From a6289c294ccffc1d53acbdd0081d14cd85cfb92b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Mar 2025 13:20:29 +0000 Subject: [PATCH 1/7] refactor: bump prettier from 2.0.5 to 3.5.3 Bumps [prettier](https://github.com/prettier/prettier) from 2.0.5 to 3.5.3. - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/2.0.5...3.5.3) --- updated-dependencies: - dependency-name: prettier dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- package-lock.json | 22 +++++++++++++--------- package.json | 2 +- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1fc3944fda..81d4eb008a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -99,7 +99,7 @@ "node-abort-controller": "3.1.1", "node-fetch": "3.2.10", "nyc": "17.1.0", - "prettier": "2.0.5", + "prettier": "3.5.3", "semantic-release": "24.2.1", "typescript": "5.7.3", "yaml": "2.7.0" @@ -18420,15 +18420,19 @@ } }, "node_modules/prettier": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", - "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, + "license": "MIT", "bin": { - "prettier": "bin-prettier.js" + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=10.13.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, "node_modules/pretty-ms": { @@ -35075,9 +35079,9 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" }, "prettier": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", - "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true }, "pretty-ms": { diff --git a/package.json b/package.json index e5e3f08599..9b05ac707a 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "node-abort-controller": "3.1.1", "node-fetch": "3.2.10", "nyc": "17.1.0", - "prettier": "2.0.5", + "prettier": "3.5.3", "semantic-release": "24.2.1", "typescript": "5.7.3", "yaml": "2.7.0" From 145cc002241c19af25a7d162e1216d1f04608af1 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 22 Mar 2025 20:33:44 +1100 Subject: [PATCH 2/7] feature: update prettier --- spec/AudienceRouter.spec.js | 107 +- spec/Auth.spec.js | 10 +- spec/AuthenticationAdapters.spec.js | 260 +- spec/AuthenticationAdaptersV2.spec.js | 14 +- spec/CLI.spec.js | 143 +- spec/CloudCode.Validator.spec.js | 123 +- spec/CloudCode.spec.js | 468 ++-- spec/CloudCodeLogger.spec.js | 127 +- spec/DefinedSchemas.spec.js | 69 +- spec/EmailVerificationToken.spec.js | 1679 +++++++------ spec/FilesController.spec.js | 25 +- spec/GridFSBucketStorageAdapter.spec.js | 4 +- spec/Idempotency.spec.js | 358 +-- spec/JobSchedule.spec.js | 14 +- spec/LoggerController.spec.js | 4 +- spec/LogsRouter.spec.js | 156 +- spec/Middlewares.spec.js | 116 +- spec/MongoSchemaCollectionAdapter.spec.js | 4 +- spec/MongoStorageAdapter.spec.js | 3 +- spec/PagesRouter.spec.js | 334 +-- spec/Parse.Push.spec.js | 123 +- spec/ParseACL.spec.js | 4 +- spec/ParseAPI.spec.js | 610 ++--- spec/ParseCloudCodePublisher.spec.js | 4 +- spec/ParseConfigKey.spec.js | 118 +- spec/ParseGeoPoint.spec.js | 197 +- spec/ParseGlobalConfig.spec.js | 37 +- spec/ParseGraphQLSchema.spec.js | 122 +- spec/ParseGraphQLServer.spec.js | 2705 +++++++++++---------- spec/ParseHooks.spec.js | 646 ++--- spec/ParseInstallation.spec.js | 236 +- spec/ParseLiveQuery.spec.js | 190 +- spec/ParseObject.spec.js | 25 +- spec/ParsePubSub.spec.js | 24 +- spec/ParseQuery.Aggregate.spec.js | 649 ++--- spec/ParseQuery.FullTextSearch.spec.js | 134 +- spec/ParseQuery.spec.js | 596 ++--- spec/ParseRole.spec.js | 127 +- spec/ParseServer.spec.js | 4 +- spec/ParseServerRESTController.spec.js | 35 +- spec/ParseUser.spec.js | 344 +-- spec/PasswordPolicy.spec.js | 648 ++--- spec/PointerPermissions.spec.js | 440 ++-- spec/PostgresInitOptions.spec.js | 4 +- spec/PostgresStorageAdapter.spec.js | 4 +- spec/PushController.spec.js | 406 ++-- spec/PushWorker.spec.js | 177 +- spec/RestQuery.spec.js | 121 +- spec/SchemaPerformance.spec.js | 35 +- spec/Uniqueness.spec.js | 41 +- spec/UserController.spec.js | 77 +- spec/Utils.spec.js | 18 +- spec/ValidationAndPasswordsReset.spec.js | 361 +-- spec/WinstonLoggerAdapter.spec.js | 91 +- spec/batch.spec.js | 4 +- spec/eslint.config.js | 88 +- spec/helper.js | 15 +- spec/index.spec.js | 4 +- spec/rest.spec.js | 33 +- spec/schemas.spec.js | 179 +- spec/support/CurrentSpecReporter.js | 43 +- spec/support/MockLdapServer.js | 5 +- spec/vulnerabilities.spec.js | 66 +- src/Auth.js | 8 +- src/Controllers/DatabaseController.js | 44 +- src/Controllers/FilesController.js | 4 +- src/Controllers/SchemaController.js | 20 +- src/Controllers/UserController.js | 9 +- src/GraphQL/parseGraphQLUtils.js | 4 +- src/Options/Definitions.js | 258 +- src/RestQuery.js | 25 +- src/RestWrite.js | 70 +- src/Routers/GlobalConfigRouter.js | 54 +- src/Routers/GraphQLRouter.js | 4 +- src/Routers/IAPValidationRouter.js | 9 +- src/Routers/PagesRouter.js | 24 +- src/Routers/PublicAPIRouter.js | 2 +- src/Routers/UsersRouter.js | 45 +- src/SchemaMigrations/DefinedSchemas.js | 18 +- src/StatusHandler.js | 3 +- src/TestUtils.js | 2 +- src/Utils.js | 7 +- src/cli/parse-server.js | 1 - src/cloud-code/Parse.Cloud.js | 2 +- src/middlewares.js | 28 +- src/vendor/mongodbUrl.js | 247 +- 86 files changed, 7745 insertions(+), 6951 deletions(-) diff --git a/spec/AudienceRouter.spec.js b/spec/AudienceRouter.spec.js index 1525147a40..da0ebdc96c 100644 --- a/spec/AudienceRouter.spec.js +++ b/spec/AudienceRouter.spec.js @@ -317,59 +317,62 @@ describe('AudiencesRouter', () => { ); }); - it_id('af1111b5-3251-4b40-8f06-fb0fc624fa91')(it_exclude_dbs(['postgres']))('should support legacy parse.com audience fields', done => { - const database = Config.get(Parse.applicationId).database.adapter.database; - const now = new Date(); - Parse._request( - 'POST', - 'push_audiences', - { name: 'My Audience', query: JSON.stringify({ deviceType: 'ios' }) }, - { useMasterKey: true } - ).then(audience => { - database - .collection('test__Audience') - .updateOne( - { _id: audience.objectId }, - { - $set: { - times_used: 1, - _last_used: now, - }, - } - ) - .then(result => { - expect(result).toBeTruthy(); + it_id('af1111b5-3251-4b40-8f06-fb0fc624fa91')(it_exclude_dbs(['postgres']))( + 'should support legacy parse.com audience fields', + done => { + const database = Config.get(Parse.applicationId).database.adapter.database; + const now = new Date(); + Parse._request( + 'POST', + 'push_audiences', + { name: 'My Audience', query: JSON.stringify({ deviceType: 'ios' }) }, + { useMasterKey: true } + ).then(audience => { + database + .collection('test__Audience') + .updateOne( + { _id: audience.objectId }, + { + $set: { + times_used: 1, + _last_used: now, + }, + } + ) + .then(result => { + expect(result).toBeTruthy(); - database - .collection('test__Audience') - .find({ _id: audience.objectId }) - .toArray() - .then(rows => { - expect(rows[0]['times_used']).toEqual(1); - expect(rows[0]['_last_used']).toEqual(now); - Parse._request( - 'GET', - 'push_audiences/' + audience.objectId, - {}, - { useMasterKey: true } - ) - .then(audience => { - expect(audience.name).toEqual('My Audience'); - expect(audience.query.deviceType).toEqual('ios'); - expect(audience.timesUsed).toEqual(1); - expect(audience.lastUsed).toEqual(now.toISOString()); - done(); - }) - .catch(error => { - done.fail(error); - }); - }) - .catch(error => { - done.fail(error); - }); - }); - }); - }); + database + .collection('test__Audience') + .find({ _id: audience.objectId }) + .toArray() + .then(rows => { + expect(rows[0]['times_used']).toEqual(1); + expect(rows[0]['_last_used']).toEqual(now); + Parse._request( + 'GET', + 'push_audiences/' + audience.objectId, + {}, + { useMasterKey: true } + ) + .then(audience => { + expect(audience.name).toEqual('My Audience'); + expect(audience.query.deviceType).toEqual('ios'); + expect(audience.timesUsed).toEqual(1); + expect(audience.lastUsed).toEqual(now.toISOString()); + done(); + }) + .catch(error => { + done.fail(error); + }); + }) + .catch(error => { + done.fail(error); + }); + }); + }); + } + ); it('should be able to search on audiences', done => { Parse._request( diff --git a/spec/Auth.spec.js b/spec/Auth.spec.js index a055cda5bc..a718a1c2d7 100644 --- a/spec/Auth.spec.js +++ b/spec/Auth.spec.js @@ -239,16 +239,10 @@ describe('extendSessionOnUse', () => { const { shouldUpdateSessionExpiry } = require('../lib/Auth'); let update = new Date(Date.now() - 86410 * 1000); - const res = shouldUpdateSessionExpiry( - { sessionLength: 86460 }, - { updatedAt: update } - ); + const res = shouldUpdateSessionExpiry({ sessionLength: 86460 }, { updatedAt: update }); update = new Date(Date.now() - 43210 * 1000); - const res2 = shouldUpdateSessionExpiry( - { sessionLength: 86460 }, - { updatedAt: update } - ); + const res2 = shouldUpdateSessionExpiry({ sessionLength: 86460 }, { updatedAt: update }); expect(res).toBe(true); expect(res2).toBe(false); diff --git a/spec/AuthenticationAdapters.spec.js b/spec/AuthenticationAdapters.spec.js index a2defde3e5..d8340c60f6 100644 --- a/spec/AuthenticationAdapters.spec.js +++ b/spec/AuthenticationAdapters.spec.js @@ -1143,9 +1143,9 @@ describe('phant auth adapter', () => { auth: { phantauth: { enableInsecureAuth: true, - } - } - }) + }, + }, + }); const authData = { id: 'fakeid', access_token: 'sometoken', @@ -1218,26 +1218,29 @@ describe('facebook limited auth adapter', () => { } }); - it_id('7bfa55ab-8fd7-4526-992e-6de3df16bf9c')(it)('should use algorithm from key header to verify id_token (facebook.com)', async () => { - const fakeClaim = { - iss: 'https://www.facebook.com', - aud: 'secret', - exp: Date.now(), - sub: 'the_user_id', - }; - const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; - const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken.header); - spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); - spyOn(jwt, 'verify').and.callFake(() => fakeClaim); + it_id('7bfa55ab-8fd7-4526-992e-6de3df16bf9c')(it)( + 'should use algorithm from key header to verify id_token (facebook.com)', + async () => { + const fakeClaim = { + iss: 'https://www.facebook.com', + aud: 'secret', + exp: Date.now(), + sub: 'the_user_id', + }; + const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; + const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken.header); + spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); + spyOn(jwt, 'verify').and.callFake(() => fakeClaim); - const result = await facebook.validateAuthData( - { id: 'the_user_id', token: 'the_token' }, - { clientId: 'secret' } - ); - expect(result).toEqual(fakeClaim); - expect(jwt.verify.calls.first().args[2].algorithms).toEqual(fakeDecodedToken.header.alg); - }); + const result = await facebook.validateAuthData( + { id: 'the_user_id', token: 'the_token' }, + { clientId: 'secret' } + ); + expect(result).toEqual(fakeClaim); + expect(jwt.verify.calls.first().args[2].algorithms).toEqual(fakeDecodedToken.header.alg); + } + ); it('should not verify invalid id_token', async () => { const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; @@ -1268,89 +1271,101 @@ describe('facebook limited auth adapter', () => { } }); - it_id('4bcb1a1a-11f8-4e12-a3f6-73f7e25e355a')(it)('using client id as string) should verify id_token (facebook.com)', async () => { - const fakeClaim = { - iss: 'https://www.facebook.com', - aud: 'secret', - exp: Date.now(), - sub: 'the_user_id', - }; - const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; - const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); - spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); - spyOn(jwt, 'verify').and.callFake(() => fakeClaim); - - const result = await facebook.validateAuthData( - { id: 'the_user_id', token: 'the_token' }, - { clientId: 'secret' } - ); - expect(result).toEqual(fakeClaim); - }); - - it_id('c521a272-2ac2-4d8b-b5ed-ea250336d8b1')(it)('(using client id as array) should verify id_token (facebook.com)', async () => { - const fakeClaim = { - iss: 'https://www.facebook.com', - aud: 'secret', - exp: Date.now(), - sub: 'the_user_id', - }; - const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; - const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); - spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); - spyOn(jwt, 'verify').and.callFake(() => fakeClaim); - - const result = await facebook.validateAuthData( - { id: 'the_user_id', token: 'the_token' }, - { clientId: ['secret'] } - ); - expect(result).toEqual(fakeClaim); - }); - - it_id('e3f16404-18e9-4a87-a555-4710cfbdac67')(it)('(using client id as array with multiple items) should verify id_token (facebook.com)', async () => { - const fakeClaim = { - iss: 'https://www.facebook.com', - aud: 'secret', - exp: Date.now(), - sub: 'the_user_id', - }; - const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; - const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); - spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); - spyOn(jwt, 'verify').and.callFake(() => fakeClaim); - - const result = await facebook.validateAuthData( - { id: 'the_user_id', token: 'the_token' }, - { clientId: ['secret', 'secret 123'] } - ); - expect(result).toEqual(fakeClaim); - }); - - it_id('549c33a1-3a6b-4732-8cf6-8f010ad4569c')(it)('(using client id as string) should throw error with with invalid jwt issuer (facebook.com)', async () => { - const fakeClaim = { - iss: 'https://not.facebook.com', - sub: 'the_user_id', - }; - const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; - const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); - spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); - spyOn(jwt, 'verify').and.callFake(() => fakeClaim); - - try { - await facebook.validateAuthData( + it_id('4bcb1a1a-11f8-4e12-a3f6-73f7e25e355a')(it)( + 'using client id as string) should verify id_token (facebook.com)', + async () => { + const fakeClaim = { + iss: 'https://www.facebook.com', + aud: 'secret', + exp: Date.now(), + sub: 'the_user_id', + }; + const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; + const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); + spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); + spyOn(jwt, 'verify').and.callFake(() => fakeClaim); + + const result = await facebook.validateAuthData( { id: 'the_user_id', token: 'the_token' }, { clientId: 'secret' } ); - fail(); - } catch (e) { - expect(e.message).toBe( - 'id token not issued by correct OpenID provider - expected: https://www.facebook.com | from: https://not.facebook.com' + expect(result).toEqual(fakeClaim); + } + ); + + it_id('c521a272-2ac2-4d8b-b5ed-ea250336d8b1')(it)( + '(using client id as array) should verify id_token (facebook.com)', + async () => { + const fakeClaim = { + iss: 'https://www.facebook.com', + aud: 'secret', + exp: Date.now(), + sub: 'the_user_id', + }; + const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; + const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); + spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); + spyOn(jwt, 'verify').and.callFake(() => fakeClaim); + + const result = await facebook.validateAuthData( + { id: 'the_user_id', token: 'the_token' }, + { clientId: ['secret'] } ); + expect(result).toEqual(fakeClaim); } - }); + ); + + it_id('e3f16404-18e9-4a87-a555-4710cfbdac67')(it)( + '(using client id as array with multiple items) should verify id_token (facebook.com)', + async () => { + const fakeClaim = { + iss: 'https://www.facebook.com', + aud: 'secret', + exp: Date.now(), + sub: 'the_user_id', + }; + const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; + const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); + spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); + spyOn(jwt, 'verify').and.callFake(() => fakeClaim); + + const result = await facebook.validateAuthData( + { id: 'the_user_id', token: 'the_token' }, + { clientId: ['secret', 'secret 123'] } + ); + expect(result).toEqual(fakeClaim); + } + ); + + it_id('549c33a1-3a6b-4732-8cf6-8f010ad4569c')(it)( + '(using client id as string) should throw error with with invalid jwt issuer (facebook.com)', + async () => { + const fakeClaim = { + iss: 'https://not.facebook.com', + sub: 'the_user_id', + }; + const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; + const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); + spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); + spyOn(jwt, 'verify').and.callFake(() => fakeClaim); + + try { + await facebook.validateAuthData( + { id: 'the_user_id', token: 'the_token' }, + { clientId: 'secret' } + ); + fail(); + } catch (e) { + expect(e.message).toBe( + 'id token not issued by correct OpenID provider - expected: https://www.facebook.com | from: https://not.facebook.com' + ); + } + } + ); // TODO: figure out a way to generate our own facebook signed tokens, perhaps with a parse facebook account // and a private key @@ -1459,28 +1474,31 @@ describe('facebook limited auth adapter', () => { } }); - it_id('c194d902-e697-46c9-a303-82c2d914473c')(it)('should throw error with with invalid user id (facebook.com)', async () => { - const fakeClaim = { - iss: 'https://www.facebook.com', - aud: 'invalid_client_id', - sub: 'a_different_user_id', - }; - const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; - const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); - spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); - spyOn(jwt, 'verify').and.callFake(() => fakeClaim); - - try { - await facebook.validateAuthData( - { id: 'the_user_id', token: 'the_token' }, - { clientId: 'secret' } - ); - fail(); - } catch (e) { - expect(e.message).toBe('auth data is invalid for this user.'); + it_id('c194d902-e697-46c9-a303-82c2d914473c')(it)( + 'should throw error with with invalid user id (facebook.com)', + async () => { + const fakeClaim = { + iss: 'https://www.facebook.com', + aud: 'invalid_client_id', + sub: 'a_different_user_id', + }; + const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; + const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); + spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); + spyOn(jwt, 'verify').and.callFake(() => fakeClaim); + + try { + await facebook.validateAuthData( + { id: 'the_user_id', token: 'the_token' }, + { clientId: 'secret' } + ); + fail(); + } catch (e) { + expect(e.message).toBe('auth data is invalid for this user.'); + } } - }); + ); }); describe('OTP TOTP auth adatper', () => { diff --git a/spec/AuthenticationAdaptersV2.spec.js b/spec/AuthenticationAdaptersV2.spec.js index 7301ab54c1..5ec30a65f6 100644 --- a/spec/AuthenticationAdaptersV2.spec.js +++ b/spec/AuthenticationAdaptersV2.spec.js @@ -361,11 +361,11 @@ describe('Auth Adapter features', () => { break; } } - expect(afterSpy).toHaveBeenCalledWith( - { id: 'modernAdapter3Data' }, - undefined, - { ip: '127.0.0.1', user, master: false }, - ); + expect(afterSpy).toHaveBeenCalledWith({ id: 'modernAdapter3Data' }, undefined, { + ip: '127.0.0.1', + user, + master: false, + }); expect(spy).toHaveBeenCalled(); }); @@ -488,7 +488,7 @@ describe('Auth Adapter features', () => { await user.save({ authData: { - baseAdapter: { id: 'baseAdapter', token: "sometoken1" }, + baseAdapter: { id: 'baseAdapter', token: 'sometoken1' }, }, }); @@ -497,7 +497,7 @@ describe('Auth Adapter features', () => { const user2 = new Parse.User(); await user2.save({ authData: { - baseAdapter: { id: 'baseAdapter', token: "sometoken2" }, + baseAdapter: { id: 'baseAdapter', token: 'sometoken2' }, }, }); diff --git a/spec/CLI.spec.js b/spec/CLI.spec.js index e131a6def5..76f8e3952d 100644 --- a/spec/CLI.spec.js +++ b/spec/CLI.spec.js @@ -219,7 +219,11 @@ describe('execution', () => { childProcess.stdout.on('data', data => { data = data.toString(); aggregatedData.push(data); - if (requiredData.every(required => aggregatedData.some(aggregated => aggregated.includes(required)))) { + if ( + requiredData.every(required => + aggregatedData.some(aggregated => aggregated.includes(required)) + ) + ) { done(); } }); @@ -267,70 +271,79 @@ describe('execution', () => { handleError(childProcess, done); }); - it_id('d7165081-b133-4cba-901b-19128ce41301')(it)('should start Parse Server with GraphQL', async done => { - const env = { ...process.env }; - env.NODE_OPTIONS = '--dns-result-order=ipv4first --trace-deprecation'; - childProcess = spawn( - binPath, - [ - '--appId', - 'test', - '--masterKey', - 'test', - '--databaseURI', - databaseURI, - '--port', - '1340', - '--mountGraphQL', - ], - { env } - ); - handleStdout(childProcess, done, aggregatedData, [ - 'parse-server running on', - 'GraphQL running on', - ]); - handleStderr(childProcess, done); - handleError(childProcess, done); - }); + it_id('d7165081-b133-4cba-901b-19128ce41301')(it)( + 'should start Parse Server with GraphQL', + async done => { + const env = { ...process.env }; + env.NODE_OPTIONS = '--dns-result-order=ipv4first --trace-deprecation'; + childProcess = spawn( + binPath, + [ + '--appId', + 'test', + '--masterKey', + 'test', + '--databaseURI', + databaseURI, + '--port', + '1340', + '--mountGraphQL', + ], + { env } + ); + handleStdout(childProcess, done, aggregatedData, [ + 'parse-server running on', + 'GraphQL running on', + ]); + handleStderr(childProcess, done); + handleError(childProcess, done); + } + ); - it_id('2769cdb4-ce8a-484d-8a91-635b5894ba7e')(it)('should start Parse Server with GraphQL and Playground', async done => { - const env = { ...process.env }; - env.NODE_OPTIONS = '--dns-result-order=ipv4first --trace-deprecation'; - childProcess = spawn( - binPath, - [ - '--appId', - 'test', - '--masterKey', - 'test', - '--databaseURI', - databaseURI, - '--port', - '1341', - '--mountGraphQL', - '--mountPlayground', - ], - { env } - ); - handleStdout(childProcess, done, aggregatedData, [ - 'parse-server running on', - 'Playground running on', - 'GraphQL running on', - ]); - handleStderr(childProcess, done); - handleError(childProcess, done); - }); + it_id('2769cdb4-ce8a-484d-8a91-635b5894ba7e')(it)( + 'should start Parse Server with GraphQL and Playground', + async done => { + const env = { ...process.env }; + env.NODE_OPTIONS = '--dns-result-order=ipv4first --trace-deprecation'; + childProcess = spawn( + binPath, + [ + '--appId', + 'test', + '--masterKey', + 'test', + '--databaseURI', + databaseURI, + '--port', + '1341', + '--mountGraphQL', + '--mountPlayground', + ], + { env } + ); + handleStdout(childProcess, done, aggregatedData, [ + 'parse-server running on', + 'Playground running on', + 'GraphQL running on', + ]); + handleStderr(childProcess, done); + handleError(childProcess, done); + } + ); - it_id('23caddd7-bfea-4869-8bd4-0f2cd283c8bd')(it)('can start Parse Server with auth via CLI', done => { - const env = { ...process.env }; - env.NODE_OPTIONS = '--dns-result-order=ipv4first --trace-deprecation'; - childProcess = spawn( - binPath, - ['--databaseURI', databaseURI, './spec/configs/CLIConfigAuth.json'], - { env } - ); - handleStdout(childProcess, done, aggregatedData, ['parse-server running on']); - handleStderr(childProcess, done); - handleError(childProcess, done); - }); + it_id('23caddd7-bfea-4869-8bd4-0f2cd283c8bd')(it)( + 'can start Parse Server with auth via CLI', + done => { + const env = { ...process.env }; + env.NODE_OPTIONS = '--dns-result-order=ipv4first --trace-deprecation'; + childProcess = spawn( + binPath, + ['--databaseURI', databaseURI, './spec/configs/CLIConfigAuth.json'], + { env } + ); + handleStdout(childProcess, done, aggregatedData, ['parse-server running on']); + handleStderr(childProcess, done); + handleError(childProcess, done); + } + ); }); diff --git a/spec/CloudCode.Validator.spec.js b/spec/CloudCode.Validator.spec.js index 11ccc82766..9b33ddc213 100644 --- a/spec/CloudCode.Validator.spec.js +++ b/spec/CloudCode.Validator.spec.js @@ -734,37 +734,43 @@ describe('cloud validator', () => { done(); }); - it_id('893eec0c-41bd-4adf-8f0a-306087ad8d61')(it)('basic beforeSave Parse.Config skipWithMasterKey', async () => { - Parse.Cloud.beforeSave( - Parse.Config, - () => { - throw 'beforeSaveFile should have resolved using master key.'; - }, - { - skipWithMasterKey: true, - } - ); - const config = await testConfig(); - expect(config.get('internal')).toBe('i'); - expect(config.get('string')).toBe('s'); - expect(config.get('number')).toBe(12); - }); - - it_id('91e739a4-6a38-405c-8f83-f36d48220734')(it)('basic afterSave Parse.Config skipWithMasterKey', async () => { - Parse.Cloud.afterSave( - Parse.Config, - () => { - throw 'beforeSaveFile should have resolved using master key.'; - }, - { - skipWithMasterKey: true, - } - ); - const config = await testConfig(); - expect(config.get('internal')).toBe('i'); - expect(config.get('string')).toBe('s'); - expect(config.get('number')).toBe(12); - }); + it_id('893eec0c-41bd-4adf-8f0a-306087ad8d61')(it)( + 'basic beforeSave Parse.Config skipWithMasterKey', + async () => { + Parse.Cloud.beforeSave( + Parse.Config, + () => { + throw 'beforeSaveFile should have resolved using master key.'; + }, + { + skipWithMasterKey: true, + } + ); + const config = await testConfig(); + expect(config.get('internal')).toBe('i'); + expect(config.get('string')).toBe('s'); + expect(config.get('number')).toBe(12); + } + ); + + it_id('91e739a4-6a38-405c-8f83-f36d48220734')(it)( + 'basic afterSave Parse.Config skipWithMasterKey', + async () => { + Parse.Cloud.afterSave( + Parse.Config, + () => { + throw 'beforeSaveFile should have resolved using master key.'; + }, + { + skipWithMasterKey: true, + } + ); + const config = await testConfig(); + expect(config.get('internal')).toBe('i'); + expect(config.get('string')).toBe('s'); + expect(config.get('number')).toBe(12); + } + ); it('beforeSave validateMasterKey and skipWithMasterKey fail', async function (done) { Parse.Cloud.beforeSave( @@ -1531,23 +1537,29 @@ describe('cloud validator', () => { } }); - it_id('32ca1a99-7f2b-429d-a7cf-62b6661d0af6')(it)('validate beforeSave Parse.Config', async () => { - Parse.Cloud.beforeSave(Parse.Config, () => {}, validatorSuccess); - const config = await testConfig(); - expect(config.get('internal')).toBe('i'); - expect(config.get('string')).toBe('s'); - expect(config.get('number')).toBe(12); - }); + it_id('32ca1a99-7f2b-429d-a7cf-62b6661d0af6')(it)( + 'validate beforeSave Parse.Config', + async () => { + Parse.Cloud.beforeSave(Parse.Config, () => {}, validatorSuccess); + const config = await testConfig(); + expect(config.get('internal')).toBe('i'); + expect(config.get('string')).toBe('s'); + expect(config.get('number')).toBe(12); + } + ); - it_id('c84d11e7-d09c-4843-ad98-f671511bf612')(it)('validate beforeSave Parse.Config fail', async () => { - Parse.Cloud.beforeSave(Parse.Config, () => {}, validatorFail); - try { - await testConfig(); - fail('cloud function should have failed.'); - } catch (e) { - expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); + it_id('c84d11e7-d09c-4843-ad98-f671511bf612')(it)( + 'validate beforeSave Parse.Config fail', + async () => { + Parse.Cloud.beforeSave(Parse.Config, () => {}, validatorFail); + try { + await testConfig(); + fail('cloud function should have failed.'); + } catch (e) { + expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); + } } - }); + ); it_id('b18b9a6a-0e35-4b60-9771-30f53501df3c')(it)('validate afterSave Parse.Config', async () => { Parse.Cloud.afterSave(Parse.Config, () => {}, validatorSuccess); @@ -1557,15 +1569,18 @@ describe('cloud validator', () => { expect(config.get('number')).toBe(12); }); - it_id('ef761222-1758-4614-b984-da84d73fc10c')(it)('validate afterSave Parse.Config fail', async () => { - Parse.Cloud.afterSave(Parse.Config, () => {}, validatorFail); - try { - await testConfig(); - fail('cloud function should have failed.'); - } catch (e) { - expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); + it_id('ef761222-1758-4614-b984-da84d73fc10c')(it)( + 'validate afterSave Parse.Config fail', + async () => { + Parse.Cloud.afterSave(Parse.Config, () => {}, validatorFail); + try { + await testConfig(); + fail('cloud function should have failed.'); + } catch (e) { + expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); + } } - }); + ); it('Should have validator', async done => { Parse.Cloud.define( diff --git a/spec/CloudCode.spec.js b/spec/CloudCode.spec.js index e26a310655..59dea5b3f3 100644 --- a/spec/CloudCode.spec.js +++ b/spec/CloudCode.spec.js @@ -3,8 +3,8 @@ const Config = require('../lib/Config'); const Parse = require('parse/node'); const ParseServer = require('../lib/index').ParseServer; const request = require('../lib/request'); -const InMemoryCacheAdapter = require('../lib/Adapters/Cache/InMemoryCacheAdapter') - .InMemoryCacheAdapter; +const InMemoryCacheAdapter = + require('../lib/Adapters/Cache/InMemoryCacheAdapter').InMemoryCacheAdapter; const mockAdapter = { createFile: async filename => ({ @@ -1588,13 +1588,13 @@ describe('Cloud Code', () => { obj.set('points', 10); obj.set('num', 10); obj.save(null, { useMasterKey: true }).then(function () { - Parse.Cloud.run('cloudIncrementClassFunction', { objectId: obj.id }).then(function ( - savedObj - ) { - expect(savedObj.get('num')).toEqual(1); - expect(savedObj.get('points')).toEqual(0); - done(); - }); + Parse.Cloud.run('cloudIncrementClassFunction', { objectId: obj.id }).then( + function (savedObj) { + expect(savedObj.get('num')).toEqual(1); + expect(savedObj.get('points')).toEqual(0); + done(); + } + ); }); }); @@ -1768,12 +1768,8 @@ describe('Cloud Code', () => { obj.increment('objectField.number', 10); await obj.save(); - const [ - , - , - , - /* className */ /* schema */ /* query */ update, - ] = adapter.findOneAndUpdate.calls.first().args; + const [, , , /* className */ /* schema */ /* query */ update] = + adapter.findOneAndUpdate.calls.first().args; expect(update).toEqual({ 'objectField.number': { __op: 'Increment', amount: 10 }, foo: 'baz', @@ -2919,55 +2915,61 @@ describe('afterFind hooks', () => { }).toThrow('Only the _Session class is allowed for the afterLogout trigger.'); }); - it_id('c16159b5-e8ee-42d5-8fe3-e2f7c006881d')(it)('should skip afterFind hooks for aggregate', done => { - const hook = { - method: function () { - return Promise.reject(); - }, - }; - spyOn(hook, 'method').and.callThrough(); - Parse.Cloud.afterFind('MyObject', hook.method); - const obj = new Parse.Object('MyObject'); - const pipeline = [ - { - $group: { _id: {} }, - }, - ]; - obj - .save() - .then(() => { - const query = new Parse.Query('MyObject'); - return query.aggregate(pipeline); - }) - .then(results => { - expect(results[0].objectId).toEqual(null); - expect(hook.method).not.toHaveBeenCalled(); - done(); - }); - }); + it_id('c16159b5-e8ee-42d5-8fe3-e2f7c006881d')(it)( + 'should skip afterFind hooks for aggregate', + done => { + const hook = { + method: function () { + return Promise.reject(); + }, + }; + spyOn(hook, 'method').and.callThrough(); + Parse.Cloud.afterFind('MyObject', hook.method); + const obj = new Parse.Object('MyObject'); + const pipeline = [ + { + $group: { _id: {} }, + }, + ]; + obj + .save() + .then(() => { + const query = new Parse.Query('MyObject'); + return query.aggregate(pipeline); + }) + .then(results => { + expect(results[0].objectId).toEqual(null); + expect(hook.method).not.toHaveBeenCalled(); + done(); + }); + } + ); - it_id('ca55c90d-36db-422c-9060-a30583ce5224')(it)('should skip afterFind hooks for distinct', done => { - const hook = { - method: function () { - return Promise.reject(); - }, - }; - spyOn(hook, 'method').and.callThrough(); - Parse.Cloud.afterFind('MyObject', hook.method); - const obj = new Parse.Object('MyObject'); - obj.set('score', 10); - obj - .save() - .then(() => { - const query = new Parse.Query('MyObject'); - return query.distinct('score'); - }) - .then(results => { - expect(results[0]).toEqual(10); - expect(hook.method).not.toHaveBeenCalled(); - done(); - }); - }); + it_id('ca55c90d-36db-422c-9060-a30583ce5224')(it)( + 'should skip afterFind hooks for distinct', + done => { + const hook = { + method: function () { + return Promise.reject(); + }, + }; + spyOn(hook, 'method').and.callThrough(); + Parse.Cloud.afterFind('MyObject', hook.method); + const obj = new Parse.Object('MyObject'); + obj.set('score', 10); + obj + .save() + .then(() => { + const query = new Parse.Query('MyObject'); + return query.distinct('score'); + }) + .then(results => { + expect(results[0]).toEqual(10); + expect(hook.method).not.toHaveBeenCalled(); + done(); + }); + } + ); it('should throw error if context header is malformed', async () => { let calledBefore = false; @@ -3033,37 +3035,40 @@ describe('afterFind hooks', () => { expect(calledAfter).toBe(false); }); - it_id('55ef1741-cf72-4a7c-a029-00cb75f53233')(it)('should expose context in beforeSave/afterSave via header', async () => { - let calledBefore = false; - let calledAfter = false; - Parse.Cloud.beforeSave('TestObject', req => { - expect(req.object.get('foo')).toEqual('bar'); - expect(req.context.otherKey).toBe(1); - expect(req.context.key).toBe('value'); - calledBefore = true; - }); - Parse.Cloud.afterSave('TestObject', req => { - expect(req.object.get('foo')).toEqual('bar'); - expect(req.context.otherKey).toBe(1); - expect(req.context.key).toBe('value'); - calledAfter = true; - }); - const req = request({ - method: 'POST', - url: 'http://localhost:8378/1/classes/TestObject', - headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Cloud-Context': '{"key":"value","otherKey":1}', - }, - body: { - foo: 'bar', - }, - }); - await req; - expect(calledBefore).toBe(true); - expect(calledAfter).toBe(true); - }); + it_id('55ef1741-cf72-4a7c-a029-00cb75f53233')(it)( + 'should expose context in beforeSave/afterSave via header', + async () => { + let calledBefore = false; + let calledAfter = false; + Parse.Cloud.beforeSave('TestObject', req => { + expect(req.object.get('foo')).toEqual('bar'); + expect(req.context.otherKey).toBe(1); + expect(req.context.key).toBe('value'); + calledBefore = true; + }); + Parse.Cloud.afterSave('TestObject', req => { + expect(req.object.get('foo')).toEqual('bar'); + expect(req.context.otherKey).toBe(1); + expect(req.context.key).toBe('value'); + calledAfter = true; + }); + const req = request({ + method: 'POST', + url: 'http://localhost:8378/1/classes/TestObject', + headers: { + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Cloud-Context': '{"key":"value","otherKey":1}', + }, + body: { + foo: 'bar', + }, + }); + await req; + expect(calledBefore).toBe(true); + expect(calledAfter).toBe(true); + } + ); it('should override header context with body context in beforeSave/afterSave', async () => { let calledBefore = false; @@ -3347,20 +3352,23 @@ describe('beforeLogin hook', () => { expect(response).toEqual(error); }); - it_id('5656d6d7-65ef-43d1-8ca6-6942ae3614d5')(it)('should have expected data in request in beforeLogin', async done => { - Parse.Cloud.beforeLogin(req => { - expect(req.object).toBeDefined(); - expect(req.user).toBeUndefined(); - expect(req.headers).toBeDefined(); - expect(req.ip).toBeDefined(); - expect(req.installationId).toBeDefined(); - expect(req.context).toBeDefined(); - }); + it_id('5656d6d7-65ef-43d1-8ca6-6942ae3614d5')(it)( + 'should have expected data in request in beforeLogin', + async done => { + Parse.Cloud.beforeLogin(req => { + expect(req.object).toBeDefined(); + expect(req.user).toBeUndefined(); + expect(req.headers).toBeDefined(); + expect(req.ip).toBeDefined(); + expect(req.installationId).toBeDefined(); + expect(req.context).toBeDefined(); + }); - await Parse.User.signUp('tupac', 'shakur'); - await Parse.User.logIn('tupac', 'shakur'); - done(); - }); + await Parse.User.signUp('tupac', 'shakur'); + await Parse.User.logIn('tupac', 'shakur'); + done(); + } + ); it('afterFind should not be triggered when saving an object', async () => { let beforeSaves = 0; @@ -3464,20 +3472,23 @@ describe('afterLogin hook', () => { done(); }); - it_id('e86155c4-62e1-4c6e-ab4a-9ac6c87c60f2')(it)('should have expected data in request in afterLogin', async done => { - Parse.Cloud.afterLogin(req => { - expect(req.object).toBeDefined(); - expect(req.user).toBeDefined(); - expect(req.headers).toBeDefined(); - expect(req.ip).toBeDefined(); - expect(req.installationId).toBeDefined(); - expect(req.context).toBeDefined(); - }); + it_id('e86155c4-62e1-4c6e-ab4a-9ac6c87c60f2')(it)( + 'should have expected data in request in afterLogin', + async done => { + Parse.Cloud.afterLogin(req => { + expect(req.object).toBeDefined(); + expect(req.user).toBeDefined(); + expect(req.headers).toBeDefined(); + expect(req.ip).toBeDefined(); + expect(req.installationId).toBeDefined(); + expect(req.context).toBeDefined(); + }); - await Parse.User.signUp('testuser', 'p@ssword'); - await Parse.User.logIn('testuser', 'p@ssword'); - done(); - }); + await Parse.User.signUp('testuser', 'p@ssword'); + await Parse.User.logIn('testuser', 'p@ssword'); + done(); + } + ); it('context options should override _context object property when saving a new object', async () => { Parse.Cloud.beforeSave('TestObject', req => { @@ -3502,9 +3513,8 @@ describe('afterLogin hook', () => { 'X-Parse-REST-API-Key': 'rest', 'X-Parse-Cloud-Context': '{"a":"a"}', }, - body: JSON.stringify({_context: { hello: 'world' }}), + body: JSON.stringify({ _context: { hello: 'world' } }), }); - }); it('should have access to context when saving a new object', async () => { @@ -3934,61 +3944,70 @@ describe('Cloud Config hooks', () => { return Parse.Config.save({ internal: 'i', string: 's', number: 12 }, { internal: true }); } - it_id('997fe20a-96f7-454a-a5b0-c155b8d02f05')(it)('beforeSave(Parse.Config) can run hook with new config', async () => { - let count = 0; - Parse.Cloud.beforeSave(Parse.Config, (req) => { - expect(req.object).toBeDefined(); - expect(req.original).toBeUndefined(); - expect(req.user).toBeUndefined(); - expect(req.headers).toBeDefined(); - expect(req.ip).toBeDefined(); - expect(req.installationId).toBeDefined(); - expect(req.context).toBeDefined(); - const config = req.object; + it_id('997fe20a-96f7-454a-a5b0-c155b8d02f05')(it)( + 'beforeSave(Parse.Config) can run hook with new config', + async () => { + let count = 0; + Parse.Cloud.beforeSave(Parse.Config, req => { + expect(req.object).toBeDefined(); + expect(req.original).toBeUndefined(); + expect(req.user).toBeUndefined(); + expect(req.headers).toBeDefined(); + expect(req.ip).toBeDefined(); + expect(req.installationId).toBeDefined(); + expect(req.context).toBeDefined(); + const config = req.object; + expect(config.get('internal')).toBe('i'); + expect(config.get('string')).toBe('s'); + expect(config.get('number')).toBe(12); + count += 1; + }); + await testConfig(); + const config = await Parse.Config.get({ useMasterKey: true }); expect(config.get('internal')).toBe('i'); expect(config.get('string')).toBe('s'); expect(config.get('number')).toBe(12); - count += 1; - }); - await testConfig(); - const config = await Parse.Config.get({ useMasterKey: true }); - expect(config.get('internal')).toBe('i'); - expect(config.get('string')).toBe('s'); - expect(config.get('number')).toBe(12); - expect(count).toBe(1); - }); + expect(count).toBe(1); + } + ); - it_id('06a9b66c-ffb4-43d1-a025-f7d2192500e7')(it)('beforeSave(Parse.Config) can run hook with existing config', async () => { - let count = 0; - Parse.Cloud.beforeSave(Parse.Config, (req) => { - if (count === 0) { - expect(req.object.get('number')).toBe(12); - expect(req.original).toBeUndefined(); - } - if (count === 1) { - expect(req.object.get('number')).toBe(13); - expect(req.original.get('number')).toBe(12); - } - count += 1; - }); - await testConfig(); - await Parse.Config.save({ number: 13 }); - expect(count).toBe(2); - }); + it_id('06a9b66c-ffb4-43d1-a025-f7d2192500e7')(it)( + 'beforeSave(Parse.Config) can run hook with existing config', + async () => { + let count = 0; + Parse.Cloud.beforeSave(Parse.Config, req => { + if (count === 0) { + expect(req.object.get('number')).toBe(12); + expect(req.original).toBeUndefined(); + } + if (count === 1) { + expect(req.object.get('number')).toBe(13); + expect(req.original.get('number')).toBe(12); + } + count += 1; + }); + await testConfig(); + await Parse.Config.save({ number: 13 }); + expect(count).toBe(2); + } + ); - it_id('ca76de8e-671b-4c2d-9535-bd28a855fa1a')(it)('beforeSave(Parse.Config) should not change config if nothing is returned', async () => { - let count = 0; - Parse.Cloud.beforeSave(Parse.Config, () => { - count += 1; - return; - }); - await testConfig(); - const config = await Parse.Config.get({ useMasterKey: true }); - expect(config.get('internal')).toBe('i'); - expect(config.get('string')).toBe('s'); - expect(config.get('number')).toBe(12); - expect(count).toBe(1); - }); + it_id('ca76de8e-671b-4c2d-9535-bd28a855fa1a')(it)( + 'beforeSave(Parse.Config) should not change config if nothing is returned', + async () => { + let count = 0; + Parse.Cloud.beforeSave(Parse.Config, () => { + count += 1; + return; + }); + await testConfig(); + const config = await Parse.Config.get({ useMasterKey: true }); + expect(config.get('internal')).toBe('i'); + expect(config.get('string')).toBe('s'); + expect(config.get('number')).toBe(12); + expect(count).toBe(1); + } + ); it('beforeSave(Parse.Config) throw custom error', async () => { Parse.Cloud.beforeSave(Parse.Config, () => { @@ -4029,60 +4048,69 @@ describe('Cloud Config hooks', () => { } }); - it_id('3e7a75c0-6c2e-4c7e-b042-6eb5f23acf94')(it)('afterSave(Parse.Config) can run hook with new config', async () => { - let count = 0; - Parse.Cloud.afterSave(Parse.Config, (req) => { - expect(req.object).toBeDefined(); - expect(req.original).toBeUndefined(); - expect(req.user).toBeUndefined(); - expect(req.headers).toBeDefined(); - expect(req.ip).toBeDefined(); - expect(req.installationId).toBeDefined(); - expect(req.context).toBeDefined(); - const config = req.object; + it_id('3e7a75c0-6c2e-4c7e-b042-6eb5f23acf94')(it)( + 'afterSave(Parse.Config) can run hook with new config', + async () => { + let count = 0; + Parse.Cloud.afterSave(Parse.Config, req => { + expect(req.object).toBeDefined(); + expect(req.original).toBeUndefined(); + expect(req.user).toBeUndefined(); + expect(req.headers).toBeDefined(); + expect(req.ip).toBeDefined(); + expect(req.installationId).toBeDefined(); + expect(req.context).toBeDefined(); + const config = req.object; + expect(config.get('internal')).toBe('i'); + expect(config.get('string')).toBe('s'); + expect(config.get('number')).toBe(12); + count += 1; + }); + await testConfig(); + const config = await Parse.Config.get({ useMasterKey: true }); expect(config.get('internal')).toBe('i'); expect(config.get('string')).toBe('s'); expect(config.get('number')).toBe(12); - count += 1; - }); - await testConfig(); - const config = await Parse.Config.get({ useMasterKey: true }); - expect(config.get('internal')).toBe('i'); - expect(config.get('string')).toBe('s'); - expect(config.get('number')).toBe(12); - expect(count).toBe(1); - }); - - it_id('5cffb28a-2924-4857-84bb-f5778d80372a')(it)('afterSave(Parse.Config) can run hook with existing config', async () => { - let count = 0; - Parse.Cloud.afterSave(Parse.Config, (req) => { - if (count === 0) { - expect(req.object.get('number')).toBe(12); - expect(req.original).toBeUndefined(); - } - if (count === 1) { - expect(req.object.get('number')).toBe(13); - expect(req.original.get('number')).toBe(12); - } - count += 1; - }); - await testConfig(); - await Parse.Config.save({ number: 13 }); - expect(count).toBe(2); - }); + expect(count).toBe(1); + } + ); - it_id('49883992-ce91-4797-85f9-7cce1f819407')(it)('afterSave(Parse.Config) should throw error', async () => { - Parse.Cloud.afterSave(Parse.Config, () => { - throw new Parse.Error(400, 'It should fail'); - }); - try { + it_id('5cffb28a-2924-4857-84bb-f5778d80372a')(it)( + 'afterSave(Parse.Config) can run hook with existing config', + async () => { + let count = 0; + Parse.Cloud.afterSave(Parse.Config, req => { + if (count === 0) { + expect(req.object.get('number')).toBe(12); + expect(req.original).toBeUndefined(); + } + if (count === 1) { + expect(req.object.get('number')).toBe(13); + expect(req.original.get('number')).toBe(12); + } + count += 1; + }); await testConfig(); - fail('error should have thrown'); - } catch (e) { - expect(e.code).toBe(400); - expect(e.message).toBe('It should fail'); + await Parse.Config.save({ number: 13 }); + expect(count).toBe(2); } - }); + ); + + it_id('49883992-ce91-4797-85f9-7cce1f819407')(it)( + 'afterSave(Parse.Config) should throw error', + async () => { + Parse.Cloud.afterSave(Parse.Config, () => { + throw new Parse.Error(400, 'It should fail'); + }); + try { + await testConfig(); + fail('error should have thrown'); + } catch (e) { + expect(e.code).toBe(400); + expect(e.message).toBe('It should fail'); + } + } + ); }); describe('sendEmail', () => { diff --git a/spec/CloudCodeLogger.spec.js b/spec/CloudCodeLogger.spec.js index a16b52365a..de59b41bb7 100644 --- a/spec/CloudCodeLogger.spec.js +++ b/spec/CloudCodeLogger.spec.js @@ -1,6 +1,6 @@ const LoggerController = require('../lib/Controllers/LoggerController').LoggerController; -const WinstonLoggerAdapter = require('../lib/Adapters/Logger/WinstonLoggerAdapter') - .WinstonLoggerAdapter; +const WinstonLoggerAdapter = + require('../lib/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter; const fs = require('fs'); const Config = require('../lib/Config'); @@ -120,23 +120,26 @@ describe('Cloud Code Logger', () => { expect(truncatedString.length).toBe(1015); // truncate length + the string '... (truncated)' }); - it_id('4a009b1f-9203-49ca-8d48-5b45f4eedbdf')(it)('should truncate input and result of long lines', done => { - const longString = fs.readFileSync(loremFile, 'utf8'); - Parse.Cloud.define('aFunction', req => { - return req.params; - }); + it_id('4a009b1f-9203-49ca-8d48-5b45f4eedbdf')(it)( + 'should truncate input and result of long lines', + done => { + const longString = fs.readFileSync(loremFile, 'utf8'); + Parse.Cloud.define('aFunction', req => { + return req.params; + }); - Parse.Cloud.run('aFunction', { longString }) - .then(() => { - const log = spy.calls.mostRecent().args; - expect(log[0]).toEqual('info'); - expect(log[1]).toMatch( - /Ran cloud function aFunction for user [^ ]* with:\n {2}Input: {.*?\(truncated\)$/m - ); - done(); - }) - .then(null, e => done.fail(e)); - }); + Parse.Cloud.run('aFunction', { longString }) + .then(() => { + const log = spy.calls.mostRecent().args; + expect(log[0]).toEqual('info'); + expect(log[1]).toMatch( + /Ran cloud function aFunction for user [^ ]* with:\n {2}Input: {.*?\(truncated\)$/m + ); + done(); + }) + .then(null, e => done.fail(e)); + } + ); it_id('9857e15d-bb18-478d-8a67-fdaad3e89565')(it)('should log an afterSave', done => { Parse.Cloud.afterSave('MyObject', () => {}); @@ -189,41 +192,44 @@ describe('Cloud Code Logger', () => { }); }); - it_id('8088de8a-7cba-4035-8b05-4a903307e674')(it)('should log cloud function execution using the custom log level', async done => { - Parse.Cloud.define('aFunction', () => { - return 'it worked!'; - }); + it_id('8088de8a-7cba-4035-8b05-4a903307e674')(it)( + 'should log cloud function execution using the custom log level', + async done => { + Parse.Cloud.define('aFunction', () => { + return 'it worked!'; + }); - Parse.Cloud.define('bFunction', () => { - throw new Error('Failed'); - }); + Parse.Cloud.define('bFunction', () => { + throw new Error('Failed'); + }); - await Parse.Cloud.run('aFunction', { foo: 'bar' }).then(() => { - const log = spy.calls.allArgs().find(log => log[1].startsWith('Ran cloud function '))?.[0]; - expect(log).toEqual('info'); - }); + await Parse.Cloud.run('aFunction', { foo: 'bar' }).then(() => { + const log = spy.calls.allArgs().find(log => log[1].startsWith('Ran cloud function '))?.[0]; + expect(log).toEqual('info'); + }); - await reconfigureServer({ - silent: true, - logLevels: { - cloudFunctionSuccess: 'warn', - cloudFunctionError: 'info', - }, - }); + await reconfigureServer({ + silent: true, + logLevels: { + cloudFunctionSuccess: 'warn', + cloudFunctionError: 'info', + }, + }); - spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough(); + spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough(); - try { - await Parse.Cloud.run('bFunction', { foo: 'bar' }); - throw new Error('bFunction should have failed'); - } catch { - const log = spy.calls - .allArgs() - .find(log => log[1].startsWith('Failed running cloud function bFunction for '))?.[0]; - expect(log).toEqual('info'); - done(); + try { + await Parse.Cloud.run('bFunction', { foo: 'bar' }); + throw new Error('bFunction should have failed'); + } catch { + const log = spy.calls + .allArgs() + .find(log => log[1].startsWith('Failed running cloud function bFunction for '))?.[0]; + expect(log).toEqual('info'); + done(); + } } - }); + ); it('should log cloud function triggers using the custom log level', async () => { Parse.Cloud.beforeSave('TestClass', () => {}); @@ -312,19 +318,22 @@ describe('Cloud Code Logger', () => { .then(null, e => done.fail(JSON.stringify(e))); }); - it_id('b86e8168-8370-4730-a4ba-24ca3016ad66')(it)('cloud function should obfuscate password', done => { - Parse.Cloud.define('testFunction', () => { - return 'verify code success'; - }); + it_id('b86e8168-8370-4730-a4ba-24ca3016ad66')(it)( + 'cloud function should obfuscate password', + done => { + Parse.Cloud.define('testFunction', () => { + return 'verify code success'; + }); - Parse.Cloud.run('testFunction', { username: 'hawk', password: '123456' }) - .then(() => { - const entry = spy.calls.mostRecent().args; - expect(entry[2].params.password).toMatch(/\*\*\*\*\*\*\*\*/); - done(); - }) - .then(null, e => done.fail(e)); - }); + Parse.Cloud.run('testFunction', { username: 'hawk', password: '123456' }) + .then(() => { + const entry = spy.calls.mostRecent().args; + expect(entry[2].params.password).toMatch(/\*\*\*\*\*\*\*\*/); + done(); + }) + .then(null, e => done.fail(e)); + } + ); it('should only log once for object not found', async () => { const config = Config.get('test'); diff --git a/spec/DefinedSchemas.spec.js b/spec/DefinedSchemas.spec.js index e3d6fd51fe..3d6f493e66 100644 --- a/spec/DefinedSchemas.spec.js +++ b/spec/DefinedSchemas.spec.js @@ -643,41 +643,44 @@ describe('DefinedSchemas', () => { expect(logger.error).toHaveBeenCalledWith(`Failed to run migrations: ${error.toString()}`); }); - it_id('a18bf4f2-25c8-4de3-b986-19cb1ab163b8')(it)('should perform migration in parallel without failing', async () => { - const server = await reconfigureServer(); - const logger = require('../lib/logger').logger; - spyOn(logger, 'error').and.callThrough(); - const migrationOptions = { - definitions: [ - { - className: 'Test', - fields: { aField: { type: 'String' } }, - indexes: { aField: { aField: 1 } }, - classLevelPermissions: { - create: { requiresAuthentication: true }, + it_id('a18bf4f2-25c8-4de3-b986-19cb1ab163b8')(it)( + 'should perform migration in parallel without failing', + async () => { + const server = await reconfigureServer(); + const logger = require('../lib/logger').logger; + spyOn(logger, 'error').and.callThrough(); + const migrationOptions = { + definitions: [ + { + className: 'Test', + fields: { aField: { type: 'String' } }, + indexes: { aField: { aField: 1 } }, + classLevelPermissions: { + create: { requiresAuthentication: true }, + }, }, - }, - ], - }; - - // Simulate parallel deployment - await Promise.all([ - new DefinedSchemas(migrationOptions, server.config).execute(), - new DefinedSchemas(migrationOptions, server.config).execute(), - new DefinedSchemas(migrationOptions, server.config).execute(), - new DefinedSchemas(migrationOptions, server.config).execute(), - new DefinedSchemas(migrationOptions, server.config).execute(), - ]); - - const testSchema = (await Parse.Schema.all()).find( - ({ className }) => className === migrationOptions.definitions[0].className - ); + ], + }; - expect(testSchema.indexes.aField).toEqual({ aField: 1 }); - expect(testSchema.fields.aField).toEqual({ type: 'String' }); - expect(testSchema.classLevelPermissions.create).toEqual({ requiresAuthentication: true }); - expect(logger.error).toHaveBeenCalledTimes(0); - }); + // Simulate parallel deployment + await Promise.all([ + new DefinedSchemas(migrationOptions, server.config).execute(), + new DefinedSchemas(migrationOptions, server.config).execute(), + new DefinedSchemas(migrationOptions, server.config).execute(), + new DefinedSchemas(migrationOptions, server.config).execute(), + new DefinedSchemas(migrationOptions, server.config).execute(), + ]); + + const testSchema = (await Parse.Schema.all()).find( + ({ className }) => className === migrationOptions.definitions[0].className + ); + + expect(testSchema.indexes.aField).toEqual({ aField: 1 }); + expect(testSchema.fields.aField).toEqual({ type: 'String' }); + expect(testSchema.classLevelPermissions.create).toEqual({ requiresAuthentication: true }); + expect(logger.error).toHaveBeenCalledTimes(0); + } + ); it('should not affect cacheAdapter', async () => { const server = await reconfigureServer(); diff --git a/spec/EmailVerificationToken.spec.js b/spec/EmailVerificationToken.spec.js index ec3d7b8ec0..c57ece9cfb 100644 --- a/spec/EmailVerificationToken.spec.js +++ b/spec/EmailVerificationToken.spec.js @@ -106,193 +106,205 @@ describe('Email Verification Token Expiration: ', () => { }); }); - it_id('f20dd3c2-87d9-4bc6-a51d-4ea2834acbcc')(it)('if user clicks on the email verify link before email verification token expiration then show the verify email success page', done => { - const user = new Parse.User(); - let sendEmailOptions; - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - reconfigureServer({ - appName: 'emailVerifyToken', - verifyUserEmails: true, - emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', - }) - .then(() => { - user.setUsername('testEmailVerifyTokenValidity'); - user.setPassword('expiringToken'); - user.set('email', 'user@parse.com'); - return user.signUp(); + it_id('f20dd3c2-87d9-4bc6-a51d-4ea2834acbcc')(it)( + 'if user clicks on the email verify link before email verification token expiration then show the verify email success page', + done => { + const user = new Parse.User(); + let sendEmailOptions; + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + reconfigureServer({ + appName: 'emailVerifyToken', + verifyUserEmails: true, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 5, // 5 seconds + publicServerURL: 'http://localhost:8378/1', }) - .then(() => jasmine.timeout()) - .then(() => { - request({ - url: sendEmailOptions.link, - followRedirects: false, - }).then(response => { - expect(response.status).toEqual(302); - expect(response.text).toEqual( - 'Found. Redirecting to http://localhost:8378/1/apps/verify_email_success.html' - ); + .then(() => { + user.setUsername('testEmailVerifyTokenValidity'); + user.setPassword('expiringToken'); + user.set('email', 'user@parse.com'); + return user.signUp(); + }) + .then(() => jasmine.timeout()) + .then(() => { + request({ + url: sendEmailOptions.link, + followRedirects: false, + }).then(response => { + expect(response.status).toEqual(302); + expect(response.text).toEqual( + 'Found. Redirecting to http://localhost:8378/1/apps/verify_email_success.html' + ); + done(); + }); + }) + .catch(error => { + jfail(error); done(); }); - }) - .catch(error => { - jfail(error); - done(); - }); - }); + } + ); - it_id('94956799-c85e-4297-b879-e2d1f985394c')(it)('if user clicks on the email verify link before email verification token expiration then emailVerified should be true', done => { - const user = new Parse.User(); - let sendEmailOptions; - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - reconfigureServer({ - appName: 'emailVerifyToken', - verifyUserEmails: true, - emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', - }) - .then(() => { - user.setUsername('testEmailVerifyTokenValidity'); - user.setPassword('expiringToken'); - user.set('email', 'user@parse.com'); - return user.signUp(); + it_id('94956799-c85e-4297-b879-e2d1f985394c')(it)( + 'if user clicks on the email verify link before email verification token expiration then emailVerified should be true', + done => { + const user = new Parse.User(); + let sendEmailOptions; + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + reconfigureServer({ + appName: 'emailVerifyToken', + verifyUserEmails: true, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 5, // 5 seconds + publicServerURL: 'http://localhost:8378/1', }) - .then(() => jasmine.timeout()) - .then(() => { - request({ - url: sendEmailOptions.link, - followRedirects: false, - }).then(response => { - expect(response.status).toEqual(302); - user - .fetch() - .then(() => { - expect(user.get('emailVerified')).toEqual(true); - done(); - }) - .catch(error => { - jfail(error); - done(); - }); + .then(() => { + user.setUsername('testEmailVerifyTokenValidity'); + user.setPassword('expiringToken'); + user.set('email', 'user@parse.com'); + return user.signUp(); + }) + .then(() => jasmine.timeout()) + .then(() => { + request({ + url: sendEmailOptions.link, + followRedirects: false, + }).then(response => { + expect(response.status).toEqual(302); + user + .fetch() + .then(() => { + expect(user.get('emailVerified')).toEqual(true); + done(); + }) + .catch(error => { + jfail(error); + done(); + }); + }); + }) + .catch(error => { + jfail(error); + done(); }); - }) - .catch(error => { - jfail(error); - done(); - }); - }); + } + ); - it_id('25f3f895-c987-431c-9841-17cb6aaf18b5')(it)('if user clicks on the email verify link before email verification token expiration then user should be able to login', done => { - const user = new Parse.User(); - let sendEmailOptions; - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - reconfigureServer({ - appName: 'emailVerifyToken', - verifyUserEmails: true, - emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', - }) - .then(() => { - user.setUsername('testEmailVerifyTokenValidity'); - user.setPassword('expiringToken'); - user.set('email', 'user@parse.com'); - return user.signUp(); + it_id('25f3f895-c987-431c-9841-17cb6aaf18b5')(it)( + 'if user clicks on the email verify link before email verification token expiration then user should be able to login', + done => { + const user = new Parse.User(); + let sendEmailOptions; + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + reconfigureServer({ + appName: 'emailVerifyToken', + verifyUserEmails: true, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 5, // 5 seconds + publicServerURL: 'http://localhost:8378/1', }) - .then(() => jasmine.timeout()) - .then(() => { - request({ - url: sendEmailOptions.link, - followRedirects: false, - }).then(response => { - expect(response.status).toEqual(302); - Parse.User.logIn('testEmailVerifyTokenValidity', 'expiringToken') - .then(user => { - expect(typeof user).toBe('object'); - expect(user.get('emailVerified')).toBe(true); - done(); - }) - .catch(error => { - jfail(error); - done(); - }); + .then(() => { + user.setUsername('testEmailVerifyTokenValidity'); + user.setPassword('expiringToken'); + user.set('email', 'user@parse.com'); + return user.signUp(); + }) + .then(() => jasmine.timeout()) + .then(() => { + request({ + url: sendEmailOptions.link, + followRedirects: false, + }).then(response => { + expect(response.status).toEqual(302); + Parse.User.logIn('testEmailVerifyTokenValidity', 'expiringToken') + .then(user => { + expect(typeof user).toBe('object'); + expect(user.get('emailVerified')).toBe(true); + done(); + }) + .catch(error => { + jfail(error); + done(); + }); + }); + }) + .catch(error => { + jfail(error); + done(); }); - }) - .catch(error => { - jfail(error); - done(); - }); - }); + } + ); - it_id('c6a3e188-9065-4f50-842d-454d1e82f289')(it)('sets the _email_verify_token_expires_at and _email_verify_token fields after user SignUp', done => { - const user = new Parse.User(); - let sendEmailOptions; - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - reconfigureServer({ - appName: 'emailVerifyToken', - verifyUserEmails: true, - emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', - }) - .then(() => { - user.setUsername('sets_email_verify_token_expires_at'); - user.setPassword('expiringToken'); - user.set('email', 'user@parse.com'); - return user.signUp(); - }) - .then(() => { - const config = Config.get('test'); - return config.database.find( - '_User', - { - username: 'sets_email_verify_token_expires_at', - }, - {}, - Auth.maintenance(config) - ); - }) - .then(results => { - expect(results.length).toBe(1); - const user = results[0]; - expect(typeof user).toBe('object'); - expect(user.emailVerified).toEqual(false); - expect(typeof user._email_verify_token).toBe('string'); - expect(typeof user._email_verify_token_expires_at).toBe('object'); - expect(sendEmailOptions).toBeDefined(); - done(); + it_id('c6a3e188-9065-4f50-842d-454d1e82f289')(it)( + 'sets the _email_verify_token_expires_at and _email_verify_token fields after user SignUp', + done => { + const user = new Parse.User(); + let sendEmailOptions; + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + reconfigureServer({ + appName: 'emailVerifyToken', + verifyUserEmails: true, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 5, // 5 seconds + publicServerURL: 'http://localhost:8378/1', }) - .catch(error => { - jfail(error); - done(); - }); - }); + .then(() => { + user.setUsername('sets_email_verify_token_expires_at'); + user.setPassword('expiringToken'); + user.set('email', 'user@parse.com'); + return user.signUp(); + }) + .then(() => { + const config = Config.get('test'); + return config.database.find( + '_User', + { + username: 'sets_email_verify_token_expires_at', + }, + {}, + Auth.maintenance(config) + ); + }) + .then(results => { + expect(results.length).toBe(1); + const user = results[0]; + expect(typeof user).toBe('object'); + expect(user.emailVerified).toEqual(false); + expect(typeof user._email_verify_token).toBe('string'); + expect(typeof user._email_verify_token_expires_at).toBe('object'); + expect(sendEmailOptions).toBeDefined(); + done(); + }) + .catch(error => { + jfail(error); + done(); + }); + } + ); it('can resend email using an expired token', async () => { const user = new Parse.User(); @@ -411,342 +423,309 @@ describe('Email Verification Token Expiration: ', () => { expect(verifySpy).toHaveBeenCalled(); }); - it_id('b3549300-bed7-4a5e-bed5-792dbfead960')(it)('can conditionally send emails and allow conditional login', async () => { - let sendEmailOptions; - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - const verifyUserEmails = { - method(req) { - expect(Object.keys(req)).toEqual(['original', 'object', 'master', 'ip', 'installationId']); - if (req.object.get('username') === 'no_email') { + it_id('b3549300-bed7-4a5e-bed5-792dbfead960')(it)( + 'can conditionally send emails and allow conditional login', + async () => { + let sendEmailOptions; + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + const verifyUserEmails = { + method(req) { + expect(Object.keys(req)).toEqual([ + 'original', + 'object', + 'master', + 'ip', + 'installationId', + ]); + if (req.object.get('username') === 'no_email') { + return false; + } + return true; + }, + }; + const verifySpy = spyOn(verifyUserEmails, 'method').and.callThrough(); + await reconfigureServer({ + appName: 'emailVerifyToken', + verifyUserEmails: verifyUserEmails.method, + preventLoginWithUnverifiedEmail: verifyUserEmails.method, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 5, // 5 seconds + publicServerURL: 'http://localhost:8378/1', + }); + const user = new Parse.User(); + user.setUsername('no_email'); + user.setPassword('expiringToken'); + user.set('email', 'user@example.com'); + await user.signUp(); + expect(sendEmailOptions).toBeUndefined(); + expect(user.getSessionToken()).toBeDefined(); + expect(verifySpy).toHaveBeenCalledTimes(2); + const user2 = new Parse.User(); + user2.setUsername('email'); + user2.setPassword('expiringToken'); + user2.set('email', 'user2@example.com'); + await user2.signUp(); + await jasmine.timeout(); + expect(user2.getSessionToken()).toBeUndefined(); + expect(sendEmailOptions).toBeDefined(); + expect(verifySpy).toHaveBeenCalledTimes(5); + } + ); + + it_id('d812de87-33d1-495e-a6e8-3485f6dc3589')(it)( + 'can conditionally send user email verification', + async () => { + const emailAdapter = { + sendVerificationEmail: () => {}, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + const sendVerificationEmail = { + method(req) { + expect(req.user).toBeDefined(); + expect(req.master).toBeDefined(); return false; - } - return true; - }, - }; - const verifySpy = spyOn(verifyUserEmails, 'method').and.callThrough(); - await reconfigureServer({ - appName: 'emailVerifyToken', - verifyUserEmails: verifyUserEmails.method, - preventLoginWithUnverifiedEmail: verifyUserEmails.method, - emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', - }); - const user = new Parse.User(); - user.setUsername('no_email'); - user.setPassword('expiringToken'); - user.set('email', 'user@example.com'); - await user.signUp(); - expect(sendEmailOptions).toBeUndefined(); - expect(user.getSessionToken()).toBeDefined(); - expect(verifySpy).toHaveBeenCalledTimes(2); - const user2 = new Parse.User(); - user2.setUsername('email'); - user2.setPassword('expiringToken'); - user2.set('email', 'user2@example.com'); - await user2.signUp(); - await jasmine.timeout(); - expect(user2.getSessionToken()).toBeUndefined(); - expect(sendEmailOptions).toBeDefined(); - expect(verifySpy).toHaveBeenCalledTimes(5); - }); + }, + }; + const sendSpy = spyOn(sendVerificationEmail, 'method').and.callThrough(); + await reconfigureServer({ + appName: 'emailVerifyToken', + verifyUserEmails: true, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 5, // 5 seconds + publicServerURL: 'http://localhost:8378/1', + sendUserEmailVerification: sendVerificationEmail.method, + }); + const emailSpy = spyOn(emailAdapter, 'sendVerificationEmail').and.callThrough(); + const newUser = new Parse.User(); + newUser.setUsername('unsets_email_verify_token_expires_at'); + newUser.setPassword('expiringToken'); + newUser.set('email', 'user@example.com'); + await newUser.signUp(); + await Parse.User.requestEmailVerification('user@example.com'); + await jasmine.timeout(); + expect(sendSpy).toHaveBeenCalledTimes(2); + expect(emailSpy).toHaveBeenCalledTimes(0); + } + ); - it_id('d812de87-33d1-495e-a6e8-3485f6dc3589')(it)('can conditionally send user email verification', async () => { - const emailAdapter = { - sendVerificationEmail: () => {}, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - const sendVerificationEmail = { - method(req) { - expect(req.user).toBeDefined(); - expect(req.master).toBeDefined(); - return false; - }, - }; - const sendSpy = spyOn(sendVerificationEmail, 'method').and.callThrough(); - await reconfigureServer({ - appName: 'emailVerifyToken', - verifyUserEmails: true, - emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', - sendUserEmailVerification: sendVerificationEmail.method, - }); - const emailSpy = spyOn(emailAdapter, 'sendVerificationEmail').and.callThrough(); - const newUser = new Parse.User(); - newUser.setUsername('unsets_email_verify_token_expires_at'); - newUser.setPassword('expiringToken'); - newUser.set('email', 'user@example.com'); - await newUser.signUp(); - await Parse.User.requestEmailVerification('user@example.com'); - await jasmine.timeout(); - expect(sendSpy).toHaveBeenCalledTimes(2); - expect(emailSpy).toHaveBeenCalledTimes(0); - }); - - it_id('d98babc1-feb8-4b5e-916c-57dc0a6ed9fb')(it)('provides full user object in email verification function on email and username change', async () => { - const emailAdapter = { - sendVerificationEmail: () => {}, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - const sendVerificationEmail = { - method(req) { - expect(req.user).toBeDefined(); - expect(req.user.id).toBeDefined(); - expect(req.user.get('createdAt')).toBeDefined(); - expect(req.user.get('updatedAt')).toBeDefined(); - expect(req.master).toBeDefined(); - return false; - }, - }; - await reconfigureServer({ - appName: 'emailVerifyToken', - verifyUserEmails: true, - emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 5, - publicServerURL: 'http://localhost:8378/1', - sendUserEmailVerification: sendVerificationEmail.method, - }); - const user = new Parse.User(); - user.setPassword('password'); - user.setUsername('new@example.com'); - user.setEmail('user@example.com'); - await user.save(null, { useMasterKey: true }); - - // Update email and username - user.setUsername('new@example.com'); - user.setEmail('new@example.com'); - await user.save(null, { useMasterKey: true }); - }); - - it_id('a8c1f820-822f-4a37-9d08-a968cac8369d')(it)('beforeSave options do not change existing behaviour', async () => { - let sendEmailOptions; - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - await reconfigureServer({ - appName: 'emailVerifyToken', - verifyUserEmails: true, - emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', - }); - const emailSpy = spyOn(emailAdapter, 'sendVerificationEmail').and.callThrough(); - const newUser = new Parse.User(); - newUser.setUsername('unsets_email_verify_token_expires_at'); - newUser.setPassword('expiringToken'); - newUser.set('email', 'user@parse.com'); - await newUser.signUp(); - await jasmine.timeout(); - const response = await request({ - url: sendEmailOptions.link, - followRedirects: false, - }); - expect(response.status).toEqual(302); - const config = Config.get('test'); - const results = await config.database.find('_User', { - username: 'unsets_email_verify_token_expires_at', - }); + it_id('d98babc1-feb8-4b5e-916c-57dc0a6ed9fb')(it)( + 'provides full user object in email verification function on email and username change', + async () => { + const emailAdapter = { + sendVerificationEmail: () => {}, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + const sendVerificationEmail = { + method(req) { + expect(req.user).toBeDefined(); + expect(req.user.id).toBeDefined(); + expect(req.user.get('createdAt')).toBeDefined(); + expect(req.user.get('updatedAt')).toBeDefined(); + expect(req.master).toBeDefined(); + return false; + }, + }; + await reconfigureServer({ + appName: 'emailVerifyToken', + verifyUserEmails: true, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 5, + publicServerURL: 'http://localhost:8378/1', + sendUserEmailVerification: sendVerificationEmail.method, + }); + const user = new Parse.User(); + user.setPassword('password'); + user.setUsername('new@example.com'); + user.setEmail('user@example.com'); + await user.save(null, { useMasterKey: true }); - expect(results.length).toBe(1); - const user = results[0]; - expect(typeof user).toBe('object'); - expect(user.emailVerified).toEqual(true); - expect(typeof user._email_verify_token).toBe('undefined'); - expect(typeof user._email_verify_token_expires_at).toBe('undefined'); - expect(emailSpy).toHaveBeenCalled(); - }); + // Update email and username + user.setUsername('new@example.com'); + user.setEmail('new@example.com'); + await user.save(null, { useMasterKey: true }); + } + ); - it_id('36d277eb-ec7c-4a39-9108-435b68228741')(it)('unsets the _email_verify_token_expires_at and _email_verify_token fields in the User class if email verification is successful', done => { - const user = new Parse.User(); - let sendEmailOptions; - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - reconfigureServer({ - appName: 'emailVerifyToken', - verifyUserEmails: true, - emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', - }) - .then(() => { - user.setUsername('unsets_email_verify_token_expires_at'); - user.setPassword('expiringToken'); - user.set('email', 'user@parse.com'); - return user.signUp(); - }) - .then(() => jasmine.timeout()) - .then(() => { - request({ - url: sendEmailOptions.link, - followRedirects: false, - }).then(response => { - expect(response.status).toEqual(302); - const config = Config.get('test'); - return config.database - .find('_User', { - username: 'unsets_email_verify_token_expires_at', - }) - .then(results => { - expect(results.length).toBe(1); - return results[0]; - }) - .then(user => { - expect(typeof user).toBe('object'); - expect(user.emailVerified).toEqual(true); - expect(typeof user._email_verify_token).toBe('undefined'); - expect(typeof user._email_verify_token_expires_at).toBe('undefined'); - done(); - }) - .catch(error => { - jfail(error); - done(); - }); - }); - }) - .catch(error => { - jfail(error); - done(); + it_id('a8c1f820-822f-4a37-9d08-a968cac8369d')(it)( + 'beforeSave options do not change existing behaviour', + async () => { + let sendEmailOptions; + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + await reconfigureServer({ + appName: 'emailVerifyToken', + verifyUserEmails: true, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 5, // 5 seconds + publicServerURL: 'http://localhost:8378/1', + }); + const emailSpy = spyOn(emailAdapter, 'sendVerificationEmail').and.callThrough(); + const newUser = new Parse.User(); + newUser.setUsername('unsets_email_verify_token_expires_at'); + newUser.setPassword('expiringToken'); + newUser.set('email', 'user@parse.com'); + await newUser.signUp(); + await jasmine.timeout(); + const response = await request({ + url: sendEmailOptions.link, + followRedirects: false, + }); + expect(response.status).toEqual(302); + const config = Config.get('test'); + const results = await config.database.find('_User', { + username: 'unsets_email_verify_token_expires_at', }); - }); - it_id('4f444704-ec4b-4dff-b947-614b1c6971c4')(it)('clicking on the email verify link by an email VERIFIED user that was setup before enabling the expire email verify token should show email verify email success', done => { - const user = new Parse.User(); - let sendEmailOptions; - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - const serverConfig = { - appName: 'emailVerifyToken', - verifyUserEmails: true, - emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', - }; + expect(results.length).toBe(1); + const user = results[0]; + expect(typeof user).toBe('object'); + expect(user.emailVerified).toEqual(true); + expect(typeof user._email_verify_token).toBe('undefined'); + expect(typeof user._email_verify_token_expires_at).toBe('undefined'); + expect(emailSpy).toHaveBeenCalled(); + } + ); - // setup server WITHOUT enabling the expire email verify token flag - reconfigureServer(serverConfig) - .then(() => { - user.setUsername('testEmailVerifyTokenValidity'); - user.setPassword('expiringToken'); - user.set('email', 'user@parse.com'); - return user.signUp(); - }) - .then(() => jasmine.timeout()) - .then(() => { - return request({ - url: sendEmailOptions.link, - followRedirects: false, - }).then(response => { - expect(response.status).toEqual(302); - return user.fetch(); - }); - }) - .then(() => { - expect(user.get('emailVerified')).toEqual(true); - // RECONFIGURE the server i.e., ENABLE the expire email verify token flag - serverConfig.emailVerifyTokenValidityDuration = 5; // 5 seconds - return reconfigureServer(serverConfig); + it_id('36d277eb-ec7c-4a39-9108-435b68228741')(it)( + 'unsets the _email_verify_token_expires_at and _email_verify_token fields in the User class if email verification is successful', + done => { + const user = new Parse.User(); + let sendEmailOptions; + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + reconfigureServer({ + appName: 'emailVerifyToken', + verifyUserEmails: true, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 5, // 5 seconds + publicServerURL: 'http://localhost:8378/1', }) - .then(() => { - request({ - url: sendEmailOptions.link, - followRedirects: false, - }).then(response => { - expect(response.status).toEqual(302); - const url = new URL(sendEmailOptions.link); - const token = url.searchParams.get('token'); - expect(response.text).toEqual( - `Found. Redirecting to http://localhost:8378/1/apps/invalid_verification_link.html?appId=test&token=${token}` - ); + .then(() => { + user.setUsername('unsets_email_verify_token_expires_at'); + user.setPassword('expiringToken'); + user.set('email', 'user@parse.com'); + return user.signUp(); + }) + .then(() => jasmine.timeout()) + .then(() => { + request({ + url: sendEmailOptions.link, + followRedirects: false, + }).then(response => { + expect(response.status).toEqual(302); + const config = Config.get('test'); + return config.database + .find('_User', { + username: 'unsets_email_verify_token_expires_at', + }) + .then(results => { + expect(results.length).toBe(1); + return results[0]; + }) + .then(user => { + expect(typeof user).toBe('object'); + expect(user.emailVerified).toEqual(true); + expect(typeof user._email_verify_token).toBe('undefined'); + expect(typeof user._email_verify_token_expires_at).toBe('undefined'); + done(); + }) + .catch(error => { + jfail(error); + done(); + }); + }); + }) + .catch(error => { + jfail(error); done(); }); - }) - .catch(error => { - jfail(error); - done(); - }); - }); + } + ); - it('clicking on the email verify link by an email UNVERIFIED user that was setup before enabling the expire email verify token should show invalid verficiation link page', done => { - const user = new Parse.User(); - let sendEmailOptions; - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - const serverConfig = { - appName: 'emailVerifyToken', - verifyUserEmails: true, - emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', - }; + it_id('4f444704-ec4b-4dff-b947-614b1c6971c4')(it)( + 'clicking on the email verify link by an email VERIFIED user that was setup before enabling the expire email verify token should show email verify email success', + done => { + const user = new Parse.User(); + let sendEmailOptions; + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + const serverConfig = { + appName: 'emailVerifyToken', + verifyUserEmails: true, + emailAdapter: emailAdapter, + publicServerURL: 'http://localhost:8378/1', + }; - // setup server WITHOUT enabling the expire email verify token flag - reconfigureServer(serverConfig) - .then(() => { - user.setUsername('testEmailVerifyTokenValidity'); - user.setPassword('expiringToken'); - user.set('email', 'user@parse.com'); - return user.signUp(); - }) - .then(() => { - // just get the user again - DO NOT email verify the user - return user.fetch(); - }) - .then(() => { - expect(user.get('emailVerified')).toEqual(false); - // RECONFIGURE the server i.e., ENABLE the expire email verify token flag - serverConfig.emailVerifyTokenValidityDuration = 5; // 5 seconds - return reconfigureServer(serverConfig); - }) - .then(() => { - request({ - url: sendEmailOptions.link, - followRedirects: false, - }).then(response => { - expect(response.status).toEqual(302); - const url = new URL(sendEmailOptions.link); - const token = url.searchParams.get('token'); - expect(response.text).toEqual( - `Found. Redirecting to http://localhost:8378/1/apps/invalid_verification_link.html?appId=test&token=${token}` - ); + // setup server WITHOUT enabling the expire email verify token flag + reconfigureServer(serverConfig) + .then(() => { + user.setUsername('testEmailVerifyTokenValidity'); + user.setPassword('expiringToken'); + user.set('email', 'user@parse.com'); + return user.signUp(); + }) + .then(() => jasmine.timeout()) + .then(() => { + return request({ + url: sendEmailOptions.link, + followRedirects: false, + }).then(response => { + expect(response.status).toEqual(302); + return user.fetch(); + }); + }) + .then(() => { + expect(user.get('emailVerified')).toEqual(true); + // RECONFIGURE the server i.e., ENABLE the expire email verify token flag + serverConfig.emailVerifyTokenValidityDuration = 5; // 5 seconds + return reconfigureServer(serverConfig); + }) + .then(() => { + request({ + url: sendEmailOptions.link, + followRedirects: false, + }).then(response => { + expect(response.status).toEqual(302); + const url = new URL(sendEmailOptions.link); + const token = url.searchParams.get('token'); + expect(response.text).toEqual( + `Found. Redirecting to http://localhost:8378/1/apps/invalid_verification_link.html?appId=test&token=${token}` + ); + done(); + }); + }) + .catch(error => { + jfail(error); done(); }); - }) - .catch(error => { - jfail(error); - done(); - }); - }); + } + ); - it_id('b6c87f35-d887-477d-bc86-a9217a424f53')(it)('setting the email on the user should set a new email verification token and new expiration date for the token when expire email verify token flag is set', done => { + it('clicking on the email verify link by an email UNVERIFIED user that was setup before enabling the expire email verify token should show invalid verficiation link page', done => { const user = new Parse.User(); - let userBeforeEmailReset; - let sendEmailOptions; const emailAdapter = { sendVerificationEmail: options => { @@ -759,153 +738,216 @@ describe('Email Verification Token Expiration: ', () => { appName: 'emailVerifyToken', verifyUserEmails: true, emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 5, // 5 seconds publicServerURL: 'http://localhost:8378/1', }; - - reconfigureServer(serverConfig) - .then(() => { - user.setUsername('newEmailVerifyTokenOnEmailReset'); - user.setPassword('expiringToken'); - user.set('email', 'user@parse.com'); - return user.signUp(); - }) - .then(() => { - const config = Config.get('test'); - return config.database - .find('_User', { username: 'newEmailVerifyTokenOnEmailReset' }) - .then(results => { - return results[0]; - }); - }) - .then(userFromDb => { - expect(typeof userFromDb).toBe('object'); - userBeforeEmailReset = userFromDb; - - // trigger another token generation by setting the email - user.set('email', 'user@parse.com'); - return new Promise(resolve => { - // wait for half a sec to get a new expiration time - setTimeout(() => resolve(user.save()), 500); - }); - }) - .then(() => { - const config = Config.get('test'); - return config.database - .find( - '_User', - { username: 'newEmailVerifyTokenOnEmailReset' }, - {}, - Auth.maintenance(config) - ) - .then(results => { - return results[0]; - }); - }) - .then(userAfterEmailReset => { - expect(typeof userAfterEmailReset).toBe('object'); - expect(userBeforeEmailReset._email_verify_token).not.toEqual( - userAfterEmailReset._email_verify_token - ); - expect(userBeforeEmailReset._email_verify_token_expires_at).not.toEqual( - userAfterEmailReset._email_verify_token_expires_at - ); - expect(sendEmailOptions).toBeDefined(); - done(); - }) - .catch(error => { - jfail(error); - done(); - }); - }); - - it_id('28f2140d-48bd-44ac-a141-ba60ea8d9713')(it)('should send a new verification email when a resend is requested and the user is UNVERIFIED', done => { - const user = new Parse.User(); - let sendEmailOptions; - let sendVerificationEmailCallCount = 0; - let userBeforeRequest; - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - sendVerificationEmailCallCount++; - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - reconfigureServer({ - appName: 'emailVerifyToken', - verifyUserEmails: true, - emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', - }) - .then(() => { - user.setUsername('resends_verification_token'); - user.setPassword('expiringToken'); - user.set('email', 'user@parse.com'); - return user.signUp(); - }) - .then(() => { - const config = Config.get('test'); - return config.database - .find('_User', { username: 'resends_verification_token' }) - .then(results => { - return results[0]; - }); - }) - .then(newUser => { - // store this user before we make our email request - userBeforeRequest = newUser; - - expect(sendVerificationEmailCallCount).toBe(1); - - return request({ - url: 'http://localhost:8378/1/verificationEmailRequest', - method: 'POST', - body: { - email: 'user@parse.com', - }, - headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', - }, - }); + + // setup server WITHOUT enabling the expire email verify token flag + reconfigureServer(serverConfig) + .then(() => { + user.setUsername('testEmailVerifyTokenValidity'); + user.setPassword('expiringToken'); + user.set('email', 'user@parse.com'); + return user.signUp(); }) - .then(response => { - expect(response.status).toBe(200); + .then(() => { + // just get the user again - DO NOT email verify the user + return user.fetch(); }) - .then(() => jasmine.timeout()) .then(() => { - expect(sendVerificationEmailCallCount).toBe(2); - expect(sendEmailOptions).toBeDefined(); - - // query for this user again - const config = Config.get('test'); - return config.database - .find('_User', { username: 'resends_verification_token' }, {}, Auth.maintenance(config)) - .then(results => { - return results[0]; - }); + expect(user.get('emailVerified')).toEqual(false); + // RECONFIGURE the server i.e., ENABLE the expire email verify token flag + serverConfig.emailVerifyTokenValidityDuration = 5; // 5 seconds + return reconfigureServer(serverConfig); }) - .then(userAfterRequest => { - // verify that our token & expiration has been changed for this new request - expect(typeof userAfterRequest).toBe('object'); - expect(userBeforeRequest._email_verify_token).not.toEqual( - userAfterRequest._email_verify_token - ); - expect(userBeforeRequest._email_verify_token_expires_at).not.toEqual( - userAfterRequest._email_verify_token_expires_at - ); - done(); + .then(() => { + request({ + url: sendEmailOptions.link, + followRedirects: false, + }).then(response => { + expect(response.status).toEqual(302); + const url = new URL(sendEmailOptions.link); + const token = url.searchParams.get('token'); + expect(response.text).toEqual( + `Found. Redirecting to http://localhost:8378/1/apps/invalid_verification_link.html?appId=test&token=${token}` + ); + done(); + }); }) .catch(error => { - console.log(error); jfail(error); done(); }); }); + it_id('b6c87f35-d887-477d-bc86-a9217a424f53')(it)( + 'setting the email on the user should set a new email verification token and new expiration date for the token when expire email verify token flag is set', + done => { + const user = new Parse.User(); + let userBeforeEmailReset; + + let sendEmailOptions; + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + const serverConfig = { + appName: 'emailVerifyToken', + verifyUserEmails: true, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 5, // 5 seconds + publicServerURL: 'http://localhost:8378/1', + }; + + reconfigureServer(serverConfig) + .then(() => { + user.setUsername('newEmailVerifyTokenOnEmailReset'); + user.setPassword('expiringToken'); + user.set('email', 'user@parse.com'); + return user.signUp(); + }) + .then(() => { + const config = Config.get('test'); + return config.database + .find('_User', { username: 'newEmailVerifyTokenOnEmailReset' }) + .then(results => { + return results[0]; + }); + }) + .then(userFromDb => { + expect(typeof userFromDb).toBe('object'); + userBeforeEmailReset = userFromDb; + + // trigger another token generation by setting the email + user.set('email', 'user@parse.com'); + return new Promise(resolve => { + // wait for half a sec to get a new expiration time + setTimeout(() => resolve(user.save()), 500); + }); + }) + .then(() => { + const config = Config.get('test'); + return config.database + .find( + '_User', + { username: 'newEmailVerifyTokenOnEmailReset' }, + {}, + Auth.maintenance(config) + ) + .then(results => { + return results[0]; + }); + }) + .then(userAfterEmailReset => { + expect(typeof userAfterEmailReset).toBe('object'); + expect(userBeforeEmailReset._email_verify_token).not.toEqual( + userAfterEmailReset._email_verify_token + ); + expect(userBeforeEmailReset._email_verify_token_expires_at).not.toEqual( + userAfterEmailReset._email_verify_token_expires_at + ); + expect(sendEmailOptions).toBeDefined(); + done(); + }) + .catch(error => { + jfail(error); + done(); + }); + } + ); + + it_id('28f2140d-48bd-44ac-a141-ba60ea8d9713')(it)( + 'should send a new verification email when a resend is requested and the user is UNVERIFIED', + done => { + const user = new Parse.User(); + let sendEmailOptions; + let sendVerificationEmailCallCount = 0; + let userBeforeRequest; + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + sendVerificationEmailCallCount++; + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + reconfigureServer({ + appName: 'emailVerifyToken', + verifyUserEmails: true, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 5, // 5 seconds + publicServerURL: 'http://localhost:8378/1', + }) + .then(() => { + user.setUsername('resends_verification_token'); + user.setPassword('expiringToken'); + user.set('email', 'user@parse.com'); + return user.signUp(); + }) + .then(() => { + const config = Config.get('test'); + return config.database + .find('_User', { username: 'resends_verification_token' }) + .then(results => { + return results[0]; + }); + }) + .then(newUser => { + // store this user before we make our email request + userBeforeRequest = newUser; + + expect(sendVerificationEmailCallCount).toBe(1); + + return request({ + url: 'http://localhost:8378/1/verificationEmailRequest', + method: 'POST', + body: { + email: 'user@parse.com', + }, + headers: { + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', + }, + }); + }) + .then(response => { + expect(response.status).toBe(200); + }) + .then(() => jasmine.timeout()) + .then(() => { + expect(sendVerificationEmailCallCount).toBe(2); + expect(sendEmailOptions).toBeDefined(); + + // query for this user again + const config = Config.get('test'); + return config.database + .find('_User', { username: 'resends_verification_token' }, {}, Auth.maintenance(config)) + .then(results => { + return results[0]; + }); + }) + .then(userAfterRequest => { + // verify that our token & expiration has been changed for this new request + expect(typeof userAfterRequest).toBe('object'); + expect(userBeforeRequest._email_verify_token).not.toEqual( + userAfterRequest._email_verify_token + ); + expect(userBeforeRequest._email_verify_token_expires_at).not.toEqual( + userAfterRequest._email_verify_token_expires_at + ); + done(); + }) + .catch(error => { + console.log(error); + jfail(error); + done(); + }); + } + ); + it('provides function arguments in verifyUserEmails on verificationEmailRequest', async () => { const user = new Parse.User(); user.setUsername('user'); @@ -914,7 +956,7 @@ describe('Email Verification Token Expiration: ', () => { await user.signUp(); const verifyUserEmails = { - method: async (params) => { + method: async params => { expect(params.object).toBeInstanceOf(Parse.User); expect(params.ip).toBeDefined(); expect(params.master).toBeDefined(); @@ -980,133 +1022,151 @@ describe('Email Verification Token Expiration: ', () => { done(); }); - it_id('0e66b7f6-2c07-4117-a8b9-605aa31a1e29')(it)('should match codes with emailVerifyTokenReuseIfValid', async done => { - let sendEmailOptions; - let sendVerificationEmailCallCount = 0; - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - sendVerificationEmailCallCount++; - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - await reconfigureServer({ - appName: 'emailVerifyToken', - verifyUserEmails: true, - emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 5 * 60, // 5 minutes - publicServerURL: 'http://localhost:8378/1', - emailVerifyTokenReuseIfValid: true, - }); - const user = new Parse.User(); - user.setUsername('resends_verification_token'); - user.setPassword('expiringToken'); - user.set('email', 'user@example.com'); - await user.signUp(); + it_id('0e66b7f6-2c07-4117-a8b9-605aa31a1e29')(it)( + 'should match codes with emailVerifyTokenReuseIfValid', + async done => { + let sendEmailOptions; + let sendVerificationEmailCallCount = 0; + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + sendVerificationEmailCallCount++; + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + await reconfigureServer({ + appName: 'emailVerifyToken', + verifyUserEmails: true, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 5 * 60, // 5 minutes + publicServerURL: 'http://localhost:8378/1', + emailVerifyTokenReuseIfValid: true, + }); + const user = new Parse.User(); + user.setUsername('resends_verification_token'); + user.setPassword('expiringToken'); + user.set('email', 'user@example.com'); + await user.signUp(); - const config = Config.get('test'); - const [userBeforeRequest] = await config.database.find('_User', { - username: 'resends_verification_token', - }, {}, Auth.maintenance(config)); - // store this user before we make our email request - expect(sendVerificationEmailCallCount).toBe(1); - await new Promise(resolve => { - setTimeout(() => { - resolve(); - }, 1000); - }); - const response = await request({ - url: 'http://localhost:8378/1/verificationEmailRequest', - method: 'POST', - body: { - email: 'user@example.com', - }, - headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', - }, - }); - await jasmine.timeout(); - expect(response.status).toBe(200); - expect(sendVerificationEmailCallCount).toBe(2); - expect(sendEmailOptions).toBeDefined(); + const config = Config.get('test'); + const [userBeforeRequest] = await config.database.find( + '_User', + { + username: 'resends_verification_token', + }, + {}, + Auth.maintenance(config) + ); + // store this user before we make our email request + expect(sendVerificationEmailCallCount).toBe(1); + await new Promise(resolve => { + setTimeout(() => { + resolve(); + }, 1000); + }); + const response = await request({ + url: 'http://localhost:8378/1/verificationEmailRequest', + method: 'POST', + body: { + email: 'user@example.com', + }, + headers: { + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', + }, + }); + await jasmine.timeout(); + expect(response.status).toBe(200); + expect(sendVerificationEmailCallCount).toBe(2); + expect(sendEmailOptions).toBeDefined(); - const [userAfterRequest] = await config.database.find('_User', { - username: 'resends_verification_token', - }, {}, Auth.maintenance(config)); + const [userAfterRequest] = await config.database.find( + '_User', + { + username: 'resends_verification_token', + }, + {}, + Auth.maintenance(config) + ); - // Verify that token & expiration haven't been changed for this new request - expect(typeof userAfterRequest).toBe('object'); - expect(userBeforeRequest._email_verify_token).toBeDefined(); - expect(userBeforeRequest._email_verify_token).toEqual(userAfterRequest._email_verify_token); - expect(userBeforeRequest._email_verify_token_expires_at).toBeDefined(); - expect(userBeforeRequest._email_verify_token_expires_at).toEqual(userAfterRequest._email_verify_token_expires_at); - done(); - }); + // Verify that token & expiration haven't been changed for this new request + expect(typeof userAfterRequest).toBe('object'); + expect(userBeforeRequest._email_verify_token).toBeDefined(); + expect(userBeforeRequest._email_verify_token).toEqual(userAfterRequest._email_verify_token); + expect(userBeforeRequest._email_verify_token_expires_at).toBeDefined(); + expect(userBeforeRequest._email_verify_token_expires_at).toEqual( + userAfterRequest._email_verify_token_expires_at + ); + done(); + } + ); - it_id('1ed9a6c2-bebc-4813-af30-4f4a212544c2')(it)('should not send a new verification email when a resend is requested and the user is VERIFIED', done => { - const user = new Parse.User(); - let sendEmailOptions; - let sendVerificationEmailCallCount = 0; - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - sendVerificationEmailCallCount++; - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - reconfigureServer({ - appName: 'emailVerifyToken', - verifyUserEmails: true, - emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', - }) - .then(() => { - user.setUsername('no_new_verification_token_once_verified'); - user.setPassword('expiringToken'); - user.set('email', 'user@parse.com'); - return user.signUp(); - }) - .then(() => jasmine.timeout()) - .then(() => { - return request({ - url: sendEmailOptions.link, - followRedirects: false, - }).then(response => { - expect(response.status).toEqual(302); - }); + it_id('1ed9a6c2-bebc-4813-af30-4f4a212544c2')(it)( + 'should not send a new verification email when a resend is requested and the user is VERIFIED', + done => { + const user = new Parse.User(); + let sendEmailOptions; + let sendVerificationEmailCallCount = 0; + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + sendVerificationEmailCallCount++; + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + reconfigureServer({ + appName: 'emailVerifyToken', + verifyUserEmails: true, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 5, // 5 seconds + publicServerURL: 'http://localhost:8378/1', }) - .then(() => { - expect(sendVerificationEmailCallCount).toBe(1); - - return request({ - url: 'http://localhost:8378/1/verificationEmailRequest', - method: 'POST', - body: { - email: 'user@parse.com', - }, - headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', - }, + .then(() => { + user.setUsername('no_new_verification_token_once_verified'); + user.setPassword('expiringToken'); + user.set('email', 'user@parse.com'); + return user.signUp(); }) - .then(fail, res => res) - .then(response => { - expect(response.status).toBe(400); - expect(sendVerificationEmailCallCount).toBe(1); - done(); + .then(() => jasmine.timeout()) + .then(() => { + return request({ + url: sendEmailOptions.link, + followRedirects: false, + }).then(response => { + expect(response.status).toEqual(302); }); - }) - .catch(error => { - jfail(error); - done(); - }); - }); + }) + .then(() => { + expect(sendVerificationEmailCallCount).toBe(1); + + return request({ + url: 'http://localhost:8378/1/verificationEmailRequest', + method: 'POST', + body: { + email: 'user@parse.com', + }, + headers: { + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', + }, + }) + .then(fail, res => res) + .then(response => { + expect(response.status).toBe(400); + expect(sendVerificationEmailCallCount).toBe(1); + done(); + }); + }) + .catch(error => { + jfail(error); + done(); + }); + } + ); it('should not send a new verification email if this user does not exist', done => { let sendEmailOptions; @@ -1287,67 +1347,70 @@ describe('Email Verification Token Expiration: ', () => { }); }); - it_id('b082d387-4974-4d45-a0d9-0c85ca2d7cbf')(it)('emailVerified should be set to false after changing from an already verified email', done => { - const user = new Parse.User(); - let sendEmailOptions; - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - reconfigureServer({ - appName: 'emailVerifyToken', - verifyUserEmails: true, - emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', - }) - .then(() => { - user.setUsername('testEmailVerifyTokenValidity'); - user.setPassword('expiringToken'); - user.set('email', 'user@parse.com'); - return user.signUp(); + it_id('b082d387-4974-4d45-a0d9-0c85ca2d7cbf')(it)( + 'emailVerified should be set to false after changing from an already verified email', + done => { + const user = new Parse.User(); + let sendEmailOptions; + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + reconfigureServer({ + appName: 'emailVerifyToken', + verifyUserEmails: true, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 5, // 5 seconds + publicServerURL: 'http://localhost:8378/1', }) - .then(() => jasmine.timeout()) - .then(() => { - request({ - url: sendEmailOptions.link, - followRedirects: false, - }).then(response => { - expect(response.status).toEqual(302); - Parse.User.logIn('testEmailVerifyTokenValidity', 'expiringToken') - .then(user => { - expect(typeof user).toBe('object'); - expect(user.get('emailVerified')).toBe(true); + .then(() => { + user.setUsername('testEmailVerifyTokenValidity'); + user.setPassword('expiringToken'); + user.set('email', 'user@parse.com'); + return user.signUp(); + }) + .then(() => jasmine.timeout()) + .then(() => { + request({ + url: sendEmailOptions.link, + followRedirects: false, + }).then(response => { + expect(response.status).toEqual(302); + Parse.User.logIn('testEmailVerifyTokenValidity', 'expiringToken') + .then(user => { + expect(typeof user).toBe('object'); + expect(user.get('emailVerified')).toBe(true); - user.set('email', 'newEmail@parse.com'); - return user.save(); - }) - .then(() => user.fetch()) - .then(user => { - expect(typeof user).toBe('object'); - expect(user.get('email')).toBe('newEmail@parse.com'); - expect(user.get('emailVerified')).toBe(false); + user.set('email', 'newEmail@parse.com'); + return user.save(); + }) + .then(() => user.fetch()) + .then(user => { + expect(typeof user).toBe('object'); + expect(user.get('email')).toBe('newEmail@parse.com'); + expect(user.get('emailVerified')).toBe(false); - request({ - url: sendEmailOptions.link, - followRedirects: false, - }).then(response => { - expect(response.status).toEqual(302); + request({ + url: sendEmailOptions.link, + followRedirects: false, + }).then(response => { + expect(response.status).toEqual(302); + done(); + }); + }) + .catch(error => { + jfail(error); done(); }); - }) - .catch(error => { - jfail(error); - done(); - }); + }); + }) + .catch(error => { + jfail(error); + done(); }); - }) - .catch(error => { - jfail(error); - done(); - }); - }); + } + ); }); diff --git a/spec/FilesController.spec.js b/spec/FilesController.spec.js index 30acf7d13c..287a89fb23 100644 --- a/spec/FilesController.spec.js +++ b/spec/FilesController.spec.js @@ -1,8 +1,8 @@ const LoggerController = require('../lib/Controllers/LoggerController').LoggerController; -const WinstonLoggerAdapter = require('../lib/Adapters/Logger/WinstonLoggerAdapter') - .WinstonLoggerAdapter; -const GridFSBucketAdapter = require('../lib/Adapters/Files/GridFSBucketAdapter') - .GridFSBucketAdapter; +const WinstonLoggerAdapter = + require('../lib/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter; +const GridFSBucketAdapter = + require('../lib/Adapters/Files/GridFSBucketAdapter').GridFSBucketAdapter; const Config = require('../lib/Config'); const FilesController = require('../lib/Controllers/FilesController').default; const databaseURI = 'mongodb://localhost:27017/parse'; @@ -26,9 +26,9 @@ describe('FilesController', () => { const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse'); gridFSAdapter.getFileLocation = (config, filename) => { return config.mount + '/files/' + config.applicationId + '/' + encodeURIComponent(filename); - } + }; const filesController = new FilesController(gridFSAdapter); - const result = await filesController.expandFilesInObject(config, function () { }); + const result = await filesController.expandFilesInObject(config, function () {}); expect(result).toBeUndefined(); @@ -50,9 +50,9 @@ describe('FilesController', () => { gridFSAdapter.getFileLocation = async (config, filename) => { await Promise.resolve(); return config.mount + '/files/' + config.applicationId + '/' + encodeURIComponent(filename); - } + }; const filesController = new FilesController(gridFSAdapter); - const result = await filesController.expandFilesInObject(config, function () { }); + const result = await filesController.expandFilesInObject(config, function () {}); expect(result).toBeUndefined(); @@ -76,7 +76,9 @@ describe('FilesController', () => { name: 'mock-name', __type: 'File', }; - gridFSAdapter.getFileLocation = jasmine.createSpy('getFileLocation').and.returnValue(Promise.resolve('mock-url')); + gridFSAdapter.getFileLocation = jasmine + .createSpy('getFileLocation') + .and.returnValue(Promise.resolve('mock-url')); const filesController = new FilesController(gridFSAdapter); const anObject = { aFile: fullFile }; @@ -93,7 +95,9 @@ describe('FilesController', () => { name: 'mock-name', __type: 'File', }; - gridFSAdapter.getFileLocation = jasmine.createSpy('getFileLocation').and.returnValue(Promise.resolve('mock-url')); + gridFSAdapter.getFileLocation = jasmine + .createSpy('getFileLocation') + .and.returnValue(Promise.resolve('mock-url')); const filesController = new FilesController(gridFSAdapter); const anObject = { aFile: fullFile }; @@ -102,7 +106,6 @@ describe('FilesController', () => { expect(anObject.aFile.url).toEqual('mock-url'); }); - it_only_db('mongo')('should pass databaseOptions to GridFSBucketAdapter', async () => { await reconfigureServer({ databaseURI: 'mongodb://localhost:27017/parse', diff --git a/spec/GridFSBucketStorageAdapter.spec.js b/spec/GridFSBucketStorageAdapter.spec.js index 7e9c84a59e..88da2bbd2d 100644 --- a/spec/GridFSBucketStorageAdapter.spec.js +++ b/spec/GridFSBucketStorageAdapter.spec.js @@ -1,5 +1,5 @@ -const GridFSBucketAdapter = require('../lib/Adapters/Files/GridFSBucketAdapter') - .GridFSBucketAdapter; +const GridFSBucketAdapter = + require('../lib/Adapters/Files/GridFSBucketAdapter').GridFSBucketAdapter; const { randomString } = require('../lib/cryptoUtils'); const databaseURI = 'mongodb://localhost:27017/parse'; const request = require('../lib/request'); diff --git a/spec/Idempotency.spec.js b/spec/Idempotency.spec.js index 14d0469b86..fd666d4360 100644 --- a/spec/Idempotency.spec.js +++ b/spec/Idempotency.spec.js @@ -50,52 +50,58 @@ describe('Idempotency', () => { }); // Tests - it_id('e25955fd-92eb-4b22-b8b7-38980e5cb223')(it)('should enforce idempotency for cloud code function', async () => { - let counter = 0; - Parse.Cloud.define('myFunction', () => { - counter++; - }); - const params = { - method: 'POST', - url: 'http://localhost:8378/1/functions/myFunction', - headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': Parse.masterKey, - 'X-Parse-Request-Id': 'abc-123', - }, - }; - expect(Config.get(Parse.applicationId).idempotencyOptions.ttl).toBe(ttl); - await request(params); - await request(params).then(fail, e => { - expect(e.status).toEqual(400); - expect(e.data.error).toEqual('Duplicate request'); - }); - expect(counter).toBe(1); - }); + it_id('e25955fd-92eb-4b22-b8b7-38980e5cb223')(it)( + 'should enforce idempotency for cloud code function', + async () => { + let counter = 0; + Parse.Cloud.define('myFunction', () => { + counter++; + }); + const params = { + method: 'POST', + url: 'http://localhost:8378/1/functions/myFunction', + headers: { + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': Parse.masterKey, + 'X-Parse-Request-Id': 'abc-123', + }, + }; + expect(Config.get(Parse.applicationId).idempotencyOptions.ttl).toBe(ttl); + await request(params); + await request(params).then(fail, e => { + expect(e.status).toEqual(400); + expect(e.data.error).toEqual('Duplicate request'); + }); + expect(counter).toBe(1); + } + ); - it_id('be2fbe16-8178-485e-9a12-6fb541096480')(it)('should delete request entry after TTL', async () => { - let counter = 0; - Parse.Cloud.define('myFunction', () => { - counter++; - }); - const params = { - method: 'POST', - url: 'http://localhost:8378/1/functions/myFunction', - headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': Parse.masterKey, - 'X-Parse-Request-Id': 'abc-123', - }, - }; - await expectAsync(request(params)).toBeResolved(); - if (SIMULATE_TTL) { - await deleteRequestEntry('abc-123'); - } else { - await new Promise(resolve => setTimeout(resolve, maxTimeOut)); + it_id('be2fbe16-8178-485e-9a12-6fb541096480')(it)( + 'should delete request entry after TTL', + async () => { + let counter = 0; + Parse.Cloud.define('myFunction', () => { + counter++; + }); + const params = { + method: 'POST', + url: 'http://localhost:8378/1/functions/myFunction', + headers: { + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': Parse.masterKey, + 'X-Parse-Request-Id': 'abc-123', + }, + }; + await expectAsync(request(params)).toBeResolved(); + if (SIMULATE_TTL) { + await deleteRequestEntry('abc-123'); + } else { + await new Promise(resolve => setTimeout(resolve, maxTimeOut)); + } + await expectAsync(request(params)).toBeResolved(); + expect(counter).toBe(2); } - await expectAsync(request(params)).toBeResolved(); - expect(counter).toBe(2); - }); + ); it_only_db('postgres')( 'should delete request entry when postgress ttl function is called', @@ -123,140 +129,158 @@ describe('Idempotency', () => { } ); - it_id('e976d0cc-a57f-45d4-9472-b9b052db6490')(it)('should enforce idempotency for cloud code jobs', async () => { - let counter = 0; - Parse.Cloud.job('myJob', () => { - counter++; - }); - const params = { - method: 'POST', - url: 'http://localhost:8378/1/jobs/myJob', - headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': Parse.masterKey, - 'X-Parse-Request-Id': 'abc-123', - }, - }; - await expectAsync(request(params)).toBeResolved(); - await request(params).then(fail, e => { - expect(e.status).toEqual(400); - expect(e.data.error).toEqual('Duplicate request'); - }); - expect(counter).toBe(1); - }); - - it_id('7c84a3d4-e1b6-4a0d-99f1-af3cf1a6b3d8')(it)('should enforce idempotency for class object creation', async () => { - let counter = 0; - Parse.Cloud.afterSave('MyClass', () => { - counter++; - }); - const params = { - method: 'POST', - url: 'http://localhost:8378/1/classes/MyClass', - headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': Parse.masterKey, - 'X-Parse-Request-Id': 'abc-123', - }, - }; - await expectAsync(request(params)).toBeResolved(); - await request(params).then(fail, e => { - expect(e.status).toEqual(400); - expect(e.data.error).toEqual('Duplicate request'); - }); - expect(counter).toBe(1); - }); + it_id('e976d0cc-a57f-45d4-9472-b9b052db6490')(it)( + 'should enforce idempotency for cloud code jobs', + async () => { + let counter = 0; + Parse.Cloud.job('myJob', () => { + counter++; + }); + const params = { + method: 'POST', + url: 'http://localhost:8378/1/jobs/myJob', + headers: { + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': Parse.masterKey, + 'X-Parse-Request-Id': 'abc-123', + }, + }; + await expectAsync(request(params)).toBeResolved(); + await request(params).then(fail, e => { + expect(e.status).toEqual(400); + expect(e.data.error).toEqual('Duplicate request'); + }); + expect(counter).toBe(1); + } + ); - it_id('a030f2dd-5d21-46ac-b53d-9d714f35d72a')(it)('should enforce idempotency for user object creation', async () => { - let counter = 0; - Parse.Cloud.afterSave('_User', () => { - counter++; - }); - const params = { - method: 'POST', - url: 'http://localhost:8378/1/users', - body: { - username: 'user', - password: 'pass', - }, - headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': Parse.masterKey, - 'X-Parse-Request-Id': 'abc-123', - }, - }; - await expectAsync(request(params)).toBeResolved(); - await request(params).then(fail, e => { - expect(e.status).toEqual(400); - expect(e.data.error).toEqual('Duplicate request'); - }); - expect(counter).toBe(1); - }); + it_id('7c84a3d4-e1b6-4a0d-99f1-af3cf1a6b3d8')(it)( + 'should enforce idempotency for class object creation', + async () => { + let counter = 0; + Parse.Cloud.afterSave('MyClass', () => { + counter++; + }); + const params = { + method: 'POST', + url: 'http://localhost:8378/1/classes/MyClass', + headers: { + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': Parse.masterKey, + 'X-Parse-Request-Id': 'abc-123', + }, + }; + await expectAsync(request(params)).toBeResolved(); + await request(params).then(fail, e => { + expect(e.status).toEqual(400); + expect(e.data.error).toEqual('Duplicate request'); + }); + expect(counter).toBe(1); + } + ); - it_id('064c469b-091c-4ba9-9043-be461f26a3eb')(it)('should enforce idempotency for installation object creation', async () => { - let counter = 0; - Parse.Cloud.afterSave('_Installation', () => { - counter++; - }); - const params = { - method: 'POST', - url: 'http://localhost:8378/1/installations', - body: { - installationId: '1', - deviceType: 'ios', - }, - headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': Parse.masterKey, - 'X-Parse-Request-Id': 'abc-123', - }, - }; - await expectAsync(request(params)).toBeResolved(); - await request(params).then(fail, e => { - expect(e.status).toEqual(400); - expect(e.data.error).toEqual('Duplicate request'); - }); - expect(counter).toBe(1); - }); + it_id('a030f2dd-5d21-46ac-b53d-9d714f35d72a')(it)( + 'should enforce idempotency for user object creation', + async () => { + let counter = 0; + Parse.Cloud.afterSave('_User', () => { + counter++; + }); + const params = { + method: 'POST', + url: 'http://localhost:8378/1/users', + body: { + username: 'user', + password: 'pass', + }, + headers: { + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': Parse.masterKey, + 'X-Parse-Request-Id': 'abc-123', + }, + }; + await expectAsync(request(params)).toBeResolved(); + await request(params).then(fail, e => { + expect(e.status).toEqual(400); + expect(e.data.error).toEqual('Duplicate request'); + }); + expect(counter).toBe(1); + } + ); - it_id('f11670b6-fa9c-4f21-a268-ae4b6bbff7fd')(it)('should not interfere with calls of different request ID', async () => { - let counter = 0; - Parse.Cloud.afterSave('MyClass', () => { - counter++; - }); - const promises = [...Array(100).keys()].map(() => { + it_id('064c469b-091c-4ba9-9043-be461f26a3eb')(it)( + 'should enforce idempotency for installation object creation', + async () => { + let counter = 0; + Parse.Cloud.afterSave('_Installation', () => { + counter++; + }); const params = { method: 'POST', - url: 'http://localhost:8378/1/classes/MyClass', + url: 'http://localhost:8378/1/installations', + body: { + installationId: '1', + deviceType: 'ios', + }, headers: { 'X-Parse-Application-Id': Parse.applicationId, 'X-Parse-Master-Key': Parse.masterKey, - 'X-Parse-Request-Id': uuid.v4(), + 'X-Parse-Request-Id': 'abc-123', }, }; - return request(params); - }); - await expectAsync(Promise.all(promises)).toBeResolved(); - expect(counter).toBe(100); - }); + await expectAsync(request(params)).toBeResolved(); + await request(params).then(fail, e => { + expect(e.status).toEqual(400); + expect(e.data.error).toEqual('Duplicate request'); + }); + expect(counter).toBe(1); + } + ); - it_id('0ecd2cd2-dafb-4a2b-bb2b-9ad4c9aca777')(it)('should re-throw any other error unchanged when writing request entry fails for any other reason', async () => { - spyOn(rest, 'create').and.rejectWith(new Parse.Error(0, 'some other error')); - Parse.Cloud.define('myFunction', () => {}); - const params = { - method: 'POST', - url: 'http://localhost:8378/1/functions/myFunction', - headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': Parse.masterKey, - 'X-Parse-Request-Id': 'abc-123', - }, - }; - await request(params).then(fail, e => { - expect(e.status).toEqual(400); - expect(e.data.error).toEqual('some other error'); - }); - }); + it_id('f11670b6-fa9c-4f21-a268-ae4b6bbff7fd')(it)( + 'should not interfere with calls of different request ID', + async () => { + let counter = 0; + Parse.Cloud.afterSave('MyClass', () => { + counter++; + }); + const promises = [...Array(100).keys()].map(() => { + const params = { + method: 'POST', + url: 'http://localhost:8378/1/classes/MyClass', + headers: { + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': Parse.masterKey, + 'X-Parse-Request-Id': uuid.v4(), + }, + }; + return request(params); + }); + await expectAsync(Promise.all(promises)).toBeResolved(); + expect(counter).toBe(100); + } + ); + + it_id('0ecd2cd2-dafb-4a2b-bb2b-9ad4c9aca777')(it)( + 'should re-throw any other error unchanged when writing request entry fails for any other reason', + async () => { + spyOn(rest, 'create').and.rejectWith(new Parse.Error(0, 'some other error')); + Parse.Cloud.define('myFunction', () => {}); + const params = { + method: 'POST', + url: 'http://localhost:8378/1/functions/myFunction', + headers: { + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': Parse.masterKey, + 'X-Parse-Request-Id': 'abc-123', + }, + }; + await request(params).then(fail, e => { + expect(e.status).toEqual(400); + expect(e.data.error).toEqual('some other error'); + }); + } + ); it('should use default configuration when none is set', async () => { await setup({}); diff --git a/spec/JobSchedule.spec.js b/spec/JobSchedule.spec.js index 853eb20143..b4a75764dd 100644 --- a/spec/JobSchedule.spec.js +++ b/spec/JobSchedule.spec.js @@ -46,15 +46,17 @@ describe('JobSchedule', () => { }); it('should reject access when not using masterKey (/jobs)', done => { - request( - Object.assign({ url: Parse.serverURL + '/cloud_code/jobs' }, defaultOptions) - ).then(done.fail, () => done()); + request(Object.assign({ url: Parse.serverURL + '/cloud_code/jobs' }, defaultOptions)).then( + done.fail, + () => done() + ); }); it('should reject access when not using masterKey (/jobs/data)', done => { - request( - Object.assign({ url: Parse.serverURL + '/cloud_code/jobs/data' }, defaultOptions) - ).then(done.fail, () => done()); + request(Object.assign({ url: Parse.serverURL + '/cloud_code/jobs/data' }, defaultOptions)).then( + done.fail, + () => done() + ); }); it('should reject access when not using masterKey (PUT /jobs/id)', done => { diff --git a/spec/LoggerController.spec.js b/spec/LoggerController.spec.js index 37d477444c..eee9bba8a8 100644 --- a/spec/LoggerController.spec.js +++ b/spec/LoggerController.spec.js @@ -1,6 +1,6 @@ const LoggerController = require('../lib/Controllers/LoggerController').LoggerController; -const WinstonLoggerAdapter = require('../lib/Adapters/Logger/WinstonLoggerAdapter') - .WinstonLoggerAdapter; +const WinstonLoggerAdapter = + require('../lib/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter; describe('LoggerController', () => { it('can process an empty query without throwing', done => { diff --git a/spec/LogsRouter.spec.js b/spec/LogsRouter.spec.js index b25ac25be5..a53072ef86 100644 --- a/spec/LogsRouter.spec.js +++ b/spec/LogsRouter.spec.js @@ -3,8 +3,8 @@ const request = require('../lib/request'); const LogsRouter = require('../lib/Routers/LogsRouter').LogsRouter; const LoggerController = require('../lib/Controllers/LoggerController').LoggerController; -const WinstonLoggerAdapter = require('../lib/Adapters/Logger/WinstonLoggerAdapter') - .WinstonLoggerAdapter; +const WinstonLoggerAdapter = + require('../lib/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter; const loggerController = new LoggerController(new WinstonLoggerAdapter()); @@ -75,50 +75,19 @@ describe_only(() => { /** * Verifies simple passwords in GET login requests with special characters are scrubbed from the verbose log */ - it_id('e36d6141-2a20-41d0-85fc-d1534c3e4bae')(it)('does scrub simple passwords on GET login', done => { - reconfigureServer({ - verbose: true, - }).then(function () { - request({ - headers: headers, - url: 'http://localhost:8378/1/login?username=test&password=simplepass.com', - }) - .catch(() => {}) - .then(() => { - request({ - url: 'http://localhost:8378/1/scriptlog?size=4&level=verbose', - headers: headers, - }).then(response => { - const body = response.data; - expect(response.status).toEqual(200); - // 4th entry is our actual GET request - expect(body[2].url).toEqual('/1/login?username=test&password=********'); - expect(body[2].message).toEqual( - 'REQUEST for [GET] /1/login?username=test&password=********: {}' - ); - done(); - }); - }); - }); - }); - - /** - * Verifies complex passwords in GET login requests with special characters are scrubbed from the verbose log - */ - it_id('24b277c5-250f-4a35-a449-2c8c519d4c03')(it)('does scrub complex passwords on GET login', done => { - reconfigureServer({ - verbose: true, - }) - .then(function () { - return request({ + it_id('e36d6141-2a20-41d0-85fc-d1534c3e4bae')(it)( + 'does scrub simple passwords on GET login', + done => { + reconfigureServer({ + verbose: true, + }).then(function () { + request({ headers: headers, - // using urlencoded password, 'simple @,/?:&=+$#pass.com' - url: - 'http://localhost:8378/1/login?username=test&password=simple%20%40%2C%2F%3F%3A%26%3D%2B%24%23pass.com', + url: 'http://localhost:8378/1/login?username=test&password=simplepass.com', }) .catch(() => {}) .then(() => { - return request({ + request({ url: 'http://localhost:8378/1/scriptlog?size=4&level=verbose', headers: headers, }).then(response => { @@ -132,42 +101,81 @@ describe_only(() => { done(); }); }); - }) - .catch(done.fail); - }); + }); + } + ); /** - * Verifies fields in POST login requests are NOT present in the verbose log + * Verifies complex passwords in GET login requests with special characters are scrubbed from the verbose log */ - it_id('33143ec9-b32d-467c-ba65-ff2bbefdaadd')(it)('does not have password field in POST login', done => { - reconfigureServer({ - verbose: true, - }).then(function () { - request({ - method: 'POST', - headers: headers, - url: 'http://localhost:8378/1/login', - body: { - username: 'test', - password: 'simplepass.com', - }, + it_id('24b277c5-250f-4a35-a449-2c8c519d4c03')(it)( + 'does scrub complex passwords on GET login', + done => { + reconfigureServer({ + verbose: true, }) - .catch(() => {}) - .then(() => { - request({ - url: 'http://localhost:8378/1/scriptlog?size=4&level=verbose', + .then(function () { + return request({ headers: headers, - }).then(response => { - const body = response.data; - expect(response.status).toEqual(200); - // 4th entry is our actual GET request - expect(body[2].url).toEqual('/1/login'); - expect(body[2].message).toEqual( - 'REQUEST for [POST] /1/login: {\n "username": "test",\n "password": "********"\n}' - ); - done(); + // using urlencoded password, 'simple @,/?:&=+$#pass.com' + url: 'http://localhost:8378/1/login?username=test&password=simple%20%40%2C%2F%3F%3A%26%3D%2B%24%23pass.com', + }) + .catch(() => {}) + .then(() => { + return request({ + url: 'http://localhost:8378/1/scriptlog?size=4&level=verbose', + headers: headers, + }).then(response => { + const body = response.data; + expect(response.status).toEqual(200); + // 4th entry is our actual GET request + expect(body[2].url).toEqual('/1/login?username=test&password=********'); + expect(body[2].message).toEqual( + 'REQUEST for [GET] /1/login?username=test&password=********: {}' + ); + done(); + }); + }); + }) + .catch(done.fail); + } + ); + + /** + * Verifies fields in POST login requests are NOT present in the verbose log + */ + it_id('33143ec9-b32d-467c-ba65-ff2bbefdaadd')(it)( + 'does not have password field in POST login', + done => { + reconfigureServer({ + verbose: true, + }).then(function () { + request({ + method: 'POST', + headers: headers, + url: 'http://localhost:8378/1/login', + body: { + username: 'test', + password: 'simplepass.com', + }, + }) + .catch(() => {}) + .then(() => { + request({ + url: 'http://localhost:8378/1/scriptlog?size=4&level=verbose', + headers: headers, + }).then(response => { + const body = response.data; + expect(response.status).toEqual(200); + // 4th entry is our actual GET request + expect(body[2].url).toEqual('/1/login'); + expect(body[2].message).toEqual( + 'REQUEST for [POST] /1/login: {\n "username": "test",\n "password": "********"\n}' + ); + done(); + }); }); - }); - }); - }); + }); + } + ); }); diff --git a/spec/Middlewares.spec.js b/spec/Middlewares.spec.js index 57dca22b0e..2ad29768bd 100644 --- a/spec/Middlewares.spec.js +++ b/spec/Middlewares.spec.js @@ -128,43 +128,49 @@ describe('middlewares', () => { const otherKeys = BodyKeys.filter( otherKey => otherKey !== infoKey && otherKey !== 'javascriptKey' ); - it_id('f9abd7ac-b1f4-4607-b9b0-365ff0559d84')(it)(`it should pull ${bodyKey} into req.info`, done => { - AppCachePut(fakeReq.body._ApplicationId, { - masterKeyIps: ['0.0.0.0/0'], - }); - fakeReq.ip = '127.0.0.1'; - fakeReq.body[bodyKey] = keyValue; - middlewares.handleParseHeaders(fakeReq, fakeRes, () => { - expect(fakeReq.body[bodyKey]).toEqual(undefined); - expect(fakeReq.info[infoKey]).toEqual(keyValue); - - otherKeys.forEach(otherKey => { - expect(fakeReq.info[otherKey]).toEqual(undefined); + it_id('f9abd7ac-b1f4-4607-b9b0-365ff0559d84')(it)( + `it should pull ${bodyKey} into req.info`, + done => { + AppCachePut(fakeReq.body._ApplicationId, { + masterKeyIps: ['0.0.0.0/0'], }); + fakeReq.ip = '127.0.0.1'; + fakeReq.body[bodyKey] = keyValue; + middlewares.handleParseHeaders(fakeReq, fakeRes, () => { + expect(fakeReq.body[bodyKey]).toEqual(undefined); + expect(fakeReq.info[infoKey]).toEqual(keyValue); - done(); - }); - }); + otherKeys.forEach(otherKey => { + expect(fakeReq.info[otherKey]).toEqual(undefined); + }); + + done(); + }); + } + ); }); - it_id('4a0bce41-c536-4482-a873-12ed023380e2')(it)('should not succeed and log if the ip does not belong to masterKeyIps list', async () => { - const logger = require('../lib/logger').logger; - spyOn(logger, 'error').and.callFake(() => {}); - AppCachePut(fakeReq.body._ApplicationId, { - masterKey: 'masterKey', - masterKeyIps: ['10.0.0.1'], - }); - fakeReq.ip = '127.0.0.1'; - fakeReq.headers['x-parse-master-key'] = 'masterKey'; + it_id('4a0bce41-c536-4482-a873-12ed023380e2')(it)( + 'should not succeed and log if the ip does not belong to masterKeyIps list', + async () => { + const logger = require('../lib/logger').logger; + spyOn(logger, 'error').and.callFake(() => {}); + AppCachePut(fakeReq.body._ApplicationId, { + masterKey: 'masterKey', + masterKeyIps: ['10.0.0.1'], + }); + fakeReq.ip = '127.0.0.1'; + fakeReq.headers['x-parse-master-key'] = 'masterKey'; - const error = await middlewares.handleParseHeaders(fakeReq, fakeRes, () => {}).catch(e => e); + const error = await middlewares.handleParseHeaders(fakeReq, fakeRes, () => {}).catch(e => e); - expect(error).toBeDefined(); - expect(error.message).toEqual(`unauthorized`); - expect(logger.error).toHaveBeenCalledWith( - `Request using master key rejected as the request IP address '127.0.0.1' is not set in Parse Server option 'masterKeyIps'.` - ); - }); + expect(error).toBeDefined(); + expect(error.message).toEqual(`unauthorized`); + expect(logger.error).toHaveBeenCalledWith( + `Request using master key rejected as the request IP address '127.0.0.1' is not set in Parse Server option 'masterKeyIps'.` + ); + } + ); it('should not succeed and log if the ip does not belong to maintenanceKeyIps list', async () => { const logger = require('../lib/logger').logger; @@ -185,27 +191,33 @@ describe('middlewares', () => { ); }); - it_id('2f7fadec-a87c-4626-90d1-65c75653aea9')(it)('should succeed if the ip does belong to masterKeyIps list', async () => { - AppCachePut(fakeReq.body._ApplicationId, { - masterKey: 'masterKey', - masterKeyIps: ['10.0.0.1'], - }); - fakeReq.ip = '10.0.0.1'; - fakeReq.headers['x-parse-master-key'] = 'masterKey'; - await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve)); - expect(fakeReq.auth.isMaster).toBe(true); - }); - - it_id('2b251fd4-d43c-48f4-ada9-c8458e40c12a')(it)('should allow any ip to use masterKey if masterKeyIps is empty', async () => { - AppCachePut(fakeReq.body._ApplicationId, { - masterKey: 'masterKey', - masterKeyIps: ['0.0.0.0/0'], - }); - fakeReq.ip = '10.0.0.1'; - fakeReq.headers['x-parse-master-key'] = 'masterKey'; - await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve)); - expect(fakeReq.auth.isMaster).toBe(true); - }); + it_id('2f7fadec-a87c-4626-90d1-65c75653aea9')(it)( + 'should succeed if the ip does belong to masterKeyIps list', + async () => { + AppCachePut(fakeReq.body._ApplicationId, { + masterKey: 'masterKey', + masterKeyIps: ['10.0.0.1'], + }); + fakeReq.ip = '10.0.0.1'; + fakeReq.headers['x-parse-master-key'] = 'masterKey'; + await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve)); + expect(fakeReq.auth.isMaster).toBe(true); + } + ); + + it_id('2b251fd4-d43c-48f4-ada9-c8458e40c12a')(it)( + 'should allow any ip to use masterKey if masterKeyIps is empty', + async () => { + AppCachePut(fakeReq.body._ApplicationId, { + masterKey: 'masterKey', + masterKeyIps: ['0.0.0.0/0'], + }); + fakeReq.ip = '10.0.0.1'; + fakeReq.headers['x-parse-master-key'] = 'masterKey'; + await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve)); + expect(fakeReq.auth.isMaster).toBe(true); + } + ); it('can set trust proxy', async () => { const server = await reconfigureServer({ trustProxy: 1 }); diff --git a/spec/MongoSchemaCollectionAdapter.spec.js b/spec/MongoSchemaCollectionAdapter.spec.js index 242466a18d..82f38935ba 100644 --- a/spec/MongoSchemaCollectionAdapter.spec.js +++ b/spec/MongoSchemaCollectionAdapter.spec.js @@ -1,7 +1,7 @@ 'use strict'; -const MongoSchemaCollection = require('../lib/Adapters/Storage/Mongo/MongoSchemaCollection') - .default; +const MongoSchemaCollection = + require('../lib/Adapters/Storage/Mongo/MongoSchemaCollection').default; describe('MongoSchemaCollection', () => { it('can transform legacy _client_permissions keys to parse format', done => { diff --git a/spec/MongoStorageAdapter.spec.js b/spec/MongoStorageAdapter.spec.js index b026fc0961..304e0c4637 100644 --- a/spec/MongoStorageAdapter.spec.js +++ b/spec/MongoStorageAdapter.spec.js @@ -46,8 +46,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { it('preserves replica sets', () => { spyOn(MongoClient, 'connect').and.returnValue(Promise.resolve(fakeClient)); new MongoStorageAdapter({ - uri: - 'mongodb://test:testpass@ds056315-a0.mongolab.com:59325,ds059315-a1.mongolab.com:59315/testDBname?replicaSet=rs-ds059415', + uri: 'mongodb://test:testpass@ds056315-a0.mongolab.com:59325,ds059315-a1.mongolab.com:59315/testDBname?replicaSet=rs-ds059415', }).connect(); expect(MongoClient.connect).toHaveBeenCalledWith( 'mongodb://test:testpass@ds056315-a0.mongolab.com:59325,ds059315-a1.mongolab.com:59315/testDBname?replicaSet=rs-ds059415', diff --git a/spec/PagesRouter.spec.js b/spec/PagesRouter.spec.js index 0aa5bb357b..896c4dbffa 100644 --- a/spec/PagesRouter.spec.js +++ b/spec/PagesRouter.spec.js @@ -607,8 +607,7 @@ describe('Pages Router', () => { it('responds to POST request with redirect response', async () => { await reconfigureServer(config); const response = await request({ - url: - 'http://localhost:8378/1/apps/test/request_password_reset?token=exampleToken&locale=de-AT', + url: 'http://localhost:8378/1/apps/test/request_password_reset?token=exampleToken&locale=de-AT', followRedirects: false, method: 'POST', }); @@ -621,8 +620,7 @@ describe('Pages Router', () => { it('responds to GET request with content response', async () => { await reconfigureServer(config); const response = await request({ - url: - 'http://localhost:8378/1/apps/test/request_password_reset?token=exampleToken&locale=de-AT', + url: 'http://localhost:8378/1/apps/test/request_password_reset?token=exampleToken&locale=de-AT', followRedirects: false, method: 'GET', }); @@ -717,144 +715,153 @@ describe('Pages Router', () => { ); }); - it_id('2845c2ea-23ba-45d2-a33f-63181d419bca')(it)('localizes end-to-end for verify email: success', async () => { - await reconfigureServer(config); - const sendVerificationEmail = spyOn( - config.emailAdapter, - 'sendVerificationEmail' - ).and.callThrough(); - const user = new Parse.User(); - user.setUsername('exampleUsername'); - user.setPassword('examplePassword'); - user.set('email', 'mail@example.com'); - await user.signUp(); - await jasmine.timeout(); - - const link = sendVerificationEmail.calls.all()[0].args[0].link; - const linkWithLocale = new URL(link); - linkWithLocale.searchParams.append(pageParams.locale, exampleLocale); - - const linkResponse = await request({ - url: linkWithLocale.toString(), - followRedirects: false, - }); - expect(linkResponse.status).toBe(200); - - const pagePath = pageResponse.calls.all()[0].args[0]; - expect(pagePath).toMatch( - new RegExp(`\/${exampleLocale}\/${pages.emailVerificationSuccess.defaultFile}`) - ); - }); - - it_id('f2272b94-b4ac-474f-8e47-1ca74de136f5')(it)('localizes end-to-end for verify email: invalid verification link - link send success', async () => { - await reconfigureServer(config); - const sendVerificationEmail = spyOn( - config.emailAdapter, - 'sendVerificationEmail' - ).and.callThrough(); - const user = new Parse.User(); - user.setUsername('exampleUsername'); - user.setPassword('examplePassword'); - user.set('email', 'mail@example.com'); - await user.signUp(); - await jasmine.timeout(); - - const link = sendVerificationEmail.calls.all()[0].args[0].link; - const linkWithLocale = new URL(link); - linkWithLocale.searchParams.append(pageParams.locale, exampleLocale); - linkWithLocale.searchParams.set(pageParams.token, 'invalidToken'); - - const linkResponse = await request({ - url: linkWithLocale.toString(), - followRedirects: false, - }); - expect(linkResponse.status).toBe(200); - - const appId = linkResponse.headers['x-parse-page-param-appid']; - const locale = linkResponse.headers['x-parse-page-param-locale']; - const publicServerUrl = linkResponse.headers['x-parse-page-param-publicserverurl']; - const invalidVerificationPagePath = pageResponse.calls.all()[0].args[0]; - expect(appId).toBeDefined(); - expect(locale).toBe(exampleLocale); - expect(publicServerUrl).toBeDefined(); - expect(invalidVerificationPagePath).toMatch( - new RegExp(`\/${exampleLocale}\/${pages.emailVerificationLinkInvalid.defaultFile}`) - ); - - const formUrl = `${publicServerUrl}/apps/${appId}/resend_verification_email`; - const formResponse = await request({ - url: formUrl, - method: 'POST', - body: { - locale, - username: 'exampleUsername', - }, - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - followRedirects: false, - }); - expect(formResponse.status).toEqual(303); - expect(formResponse.text).toContain( - `/${locale}/${pages.emailVerificationSendSuccess.defaultFile}` - ); - }); - - it_id('1d46d36a-e455-4ae7-8717-e0d286e95f02')(it)('localizes end-to-end for verify email: invalid verification link - link send fail', async () => { - await reconfigureServer(config); - const sendVerificationEmail = spyOn( - config.emailAdapter, - 'sendVerificationEmail' - ).and.callThrough(); - const user = new Parse.User(); - user.setUsername('exampleUsername'); - user.setPassword('examplePassword'); - user.set('email', 'mail@example.com'); - await user.signUp(); - await jasmine.timeout(); - - const link = sendVerificationEmail.calls.all()[0].args[0].link; - const linkWithLocale = new URL(link); - linkWithLocale.searchParams.append(pageParams.locale, exampleLocale); - linkWithLocale.searchParams.set(pageParams.token, 'invalidToken'); - - const linkResponse = await request({ - url: linkWithLocale.toString(), - followRedirects: false, - }); - expect(linkResponse.status).toBe(200); - - const appId = linkResponse.headers['x-parse-page-param-appid']; - const locale = linkResponse.headers['x-parse-page-param-locale']; - const publicServerUrl = linkResponse.headers['x-parse-page-param-publicserverurl']; - await jasmine.timeout(); + it_id('2845c2ea-23ba-45d2-a33f-63181d419bca')(it)( + 'localizes end-to-end for verify email: success', + async () => { + await reconfigureServer(config); + const sendVerificationEmail = spyOn( + config.emailAdapter, + 'sendVerificationEmail' + ).and.callThrough(); + const user = new Parse.User(); + user.setUsername('exampleUsername'); + user.setPassword('examplePassword'); + user.set('email', 'mail@example.com'); + await user.signUp(); + await jasmine.timeout(); + + const link = sendVerificationEmail.calls.all()[0].args[0].link; + const linkWithLocale = new URL(link); + linkWithLocale.searchParams.append(pageParams.locale, exampleLocale); + + const linkResponse = await request({ + url: linkWithLocale.toString(), + followRedirects: false, + }); + expect(linkResponse.status).toBe(200); + + const pagePath = pageResponse.calls.all()[0].args[0]; + expect(pagePath).toMatch( + new RegExp(`\/${exampleLocale}\/${pages.emailVerificationSuccess.defaultFile}`) + ); + } + ); + + it_id('f2272b94-b4ac-474f-8e47-1ca74de136f5')(it)( + 'localizes end-to-end for verify email: invalid verification link - link send success', + async () => { + await reconfigureServer(config); + const sendVerificationEmail = spyOn( + config.emailAdapter, + 'sendVerificationEmail' + ).and.callThrough(); + const user = new Parse.User(); + user.setUsername('exampleUsername'); + user.setPassword('examplePassword'); + user.set('email', 'mail@example.com'); + await user.signUp(); + await jasmine.timeout(); + + const link = sendVerificationEmail.calls.all()[0].args[0].link; + const linkWithLocale = new URL(link); + linkWithLocale.searchParams.append(pageParams.locale, exampleLocale); + linkWithLocale.searchParams.set(pageParams.token, 'invalidToken'); + + const linkResponse = await request({ + url: linkWithLocale.toString(), + followRedirects: false, + }); + expect(linkResponse.status).toBe(200); + + const appId = linkResponse.headers['x-parse-page-param-appid']; + const locale = linkResponse.headers['x-parse-page-param-locale']; + const publicServerUrl = linkResponse.headers['x-parse-page-param-publicserverurl']; + const invalidVerificationPagePath = pageResponse.calls.all()[0].args[0]; + expect(appId).toBeDefined(); + expect(locale).toBe(exampleLocale); + expect(publicServerUrl).toBeDefined(); + expect(invalidVerificationPagePath).toMatch( + new RegExp(`\/${exampleLocale}\/${pages.emailVerificationLinkInvalid.defaultFile}`) + ); - const invalidVerificationPagePath = pageResponse.calls.all()[0].args[0]; - expect(appId).toBeDefined(); - expect(locale).toBe(exampleLocale); - expect(publicServerUrl).toBeDefined(); - expect(invalidVerificationPagePath).toMatch( - new RegExp(`\/${exampleLocale}\/${pages.emailVerificationLinkInvalid.defaultFile}`) - ); + const formUrl = `${publicServerUrl}/apps/${appId}/resend_verification_email`; + const formResponse = await request({ + url: formUrl, + method: 'POST', + body: { + locale, + username: 'exampleUsername', + }, + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + followRedirects: false, + }); + expect(formResponse.status).toEqual(303); + expect(formResponse.text).toContain( + `/${locale}/${pages.emailVerificationSendSuccess.defaultFile}` + ); + } + ); + + it_id('1d46d36a-e455-4ae7-8717-e0d286e95f02')(it)( + 'localizes end-to-end for verify email: invalid verification link - link send fail', + async () => { + await reconfigureServer(config); + const sendVerificationEmail = spyOn( + config.emailAdapter, + 'sendVerificationEmail' + ).and.callThrough(); + const user = new Parse.User(); + user.setUsername('exampleUsername'); + user.setPassword('examplePassword'); + user.set('email', 'mail@example.com'); + await user.signUp(); + await jasmine.timeout(); + + const link = sendVerificationEmail.calls.all()[0].args[0].link; + const linkWithLocale = new URL(link); + linkWithLocale.searchParams.append(pageParams.locale, exampleLocale); + linkWithLocale.searchParams.set(pageParams.token, 'invalidToken'); + + const linkResponse = await request({ + url: linkWithLocale.toString(), + followRedirects: false, + }); + expect(linkResponse.status).toBe(200); + + const appId = linkResponse.headers['x-parse-page-param-appid']; + const locale = linkResponse.headers['x-parse-page-param-locale']; + const publicServerUrl = linkResponse.headers['x-parse-page-param-publicserverurl']; + await jasmine.timeout(); + + const invalidVerificationPagePath = pageResponse.calls.all()[0].args[0]; + expect(appId).toBeDefined(); + expect(locale).toBe(exampleLocale); + expect(publicServerUrl).toBeDefined(); + expect(invalidVerificationPagePath).toMatch( + new RegExp(`\/${exampleLocale}\/${pages.emailVerificationLinkInvalid.defaultFile}`) + ); - spyOn(UserController.prototype, 'resendVerificationEmail').and.callFake(() => - Promise.reject('failed to resend verification email') - ); + spyOn(UserController.prototype, 'resendVerificationEmail').and.callFake(() => + Promise.reject('failed to resend verification email') + ); - const formUrl = `${publicServerUrl}/apps/${appId}/resend_verification_email`; - const formResponse = await request({ - url: formUrl, - method: 'POST', - body: { - locale, - username: 'exampleUsername', - }, - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - followRedirects: false, - }); - expect(formResponse.status).toEqual(303); - expect(formResponse.text).toContain( - `/${locale}/${pages.emailVerificationSendFail.defaultFile}` - ); - }); + const formUrl = `${publicServerUrl}/apps/${appId}/resend_verification_email`; + const formResponse = await request({ + url: formUrl, + method: 'POST', + body: { + locale, + username: 'exampleUsername', + }, + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + followRedirects: false, + }); + expect(formResponse.status).toEqual(303); + expect(formResponse.text).toContain( + `/${locale}/${pages.emailVerificationSendFail.defaultFile}` + ); + } + ); it('localizes end-to-end for resend verification email: invalid link', async () => { await reconfigureServer(config); @@ -1155,29 +1162,32 @@ describe('Pages Router', () => { ); }); - it_id('81c1c28e-5dfd-4ffb-a09b-283156c08483')(it)('email verification works with custom endpoint', async () => { - config.pages.pagesEndpoint = 'customEndpoint'; - await reconfigureServer(config); - const sendVerificationEmail = spyOn( - config.emailAdapter, - 'sendVerificationEmail' - ).and.callThrough(); - const user = new Parse.User(); - user.setUsername('exampleUsername'); - user.setPassword('examplePassword'); - user.set('email', 'mail@example.com'); - await user.signUp(); - await jasmine.timeout(); - - const link = sendVerificationEmail.calls.all()[0].args[0].link; - const linkResponse = await request({ - url: link, - followRedirects: false, - }); - expect(linkResponse.status).toBe(200); - const pagePath = pageResponse.calls.all()[0].args[0]; - expect(pagePath).toMatch(new RegExp(`\/${pages.emailVerificationSuccess.defaultFile}`)); - }); + it_id('81c1c28e-5dfd-4ffb-a09b-283156c08483')(it)( + 'email verification works with custom endpoint', + async () => { + config.pages.pagesEndpoint = 'customEndpoint'; + await reconfigureServer(config); + const sendVerificationEmail = spyOn( + config.emailAdapter, + 'sendVerificationEmail' + ).and.callThrough(); + const user = new Parse.User(); + user.setUsername('exampleUsername'); + user.setPassword('examplePassword'); + user.set('email', 'mail@example.com'); + await user.signUp(); + await jasmine.timeout(); + + const link = sendVerificationEmail.calls.all()[0].args[0].link; + const linkResponse = await request({ + url: link, + followRedirects: false, + }); + expect(linkResponse.status).toBe(200); + const pagePath = pageResponse.calls.all()[0].args[0]; + expect(pagePath).toMatch(new RegExp(`\/${pages.emailVerificationSuccess.defaultFile}`)); + } + ); }); }); }); diff --git a/spec/Parse.Push.spec.js b/spec/Parse.Push.spec.js index 6303496de1..61dbdba103 100644 --- a/spec/Parse.Push.spec.js +++ b/spec/Parse.Push.spec.js @@ -126,71 +126,80 @@ describe('Parse.Push', () => { expect(sendToInstallationSpy.calls.count()).toEqual(10); }); - it_id('2a58e3c7-b6f3-4261-a384-6c893b2ac3f3')(it)('should properly send push with lowercaseIncrement', async () => { - await setup(); - const pushStatusId = await Parse.Push.send({ - where: { - deviceType: 'ios', - }, - data: { - badge: 'increment', - alert: 'Hello world!', - }, - }); - await pushCompleted(pushStatusId); - }); + it_id('2a58e3c7-b6f3-4261-a384-6c893b2ac3f3')(it)( + 'should properly send push with lowercaseIncrement', + async () => { + await setup(); + const pushStatusId = await Parse.Push.send({ + where: { + deviceType: 'ios', + }, + data: { + badge: 'increment', + alert: 'Hello world!', + }, + }); + await pushCompleted(pushStatusId); + } + ); - it_id('e21780b6-2cdd-467e-8013-81030f3288e9')(it)('should not allow clients to query _PushStatus', async () => { - await setup(); - const pushStatusId = await Parse.Push.send({ - where: { - deviceType: 'ios', - }, - data: { - badge: 'increment', - alert: 'Hello world!', - }, - }); - await pushCompleted(pushStatusId); - try { - await request({ + it_id('e21780b6-2cdd-467e-8013-81030f3288e9')(it)( + 'should not allow clients to query _PushStatus', + async () => { + await setup(); + const pushStatusId = await Parse.Push.send({ + where: { + deviceType: 'ios', + }, + data: { + badge: 'increment', + alert: 'Hello world!', + }, + }); + await pushCompleted(pushStatusId); + try { + await request({ + url: 'http://localhost:8378/1/classes/_PushStatus', + json: true, + headers: { + 'X-Parse-Application-Id': 'test', + }, + }); + fail(); + } catch (response) { + expect(response.data.error).toEqual('unauthorized'); + } + } + ); + + it_id('924cf5f5-f684-4925-978a-e52c0c457366')(it)( + 'should allow master key to query _PushStatus', + async () => { + await setup(); + const pushStatusId = await Parse.Push.send({ + where: { + deviceType: 'ios', + }, + data: { + badge: 'increment', + alert: 'Hello world!', + }, + }); + await pushCompleted(pushStatusId); + const response = await request({ url: 'http://localhost:8378/1/classes/_PushStatus', json: true, headers: { 'X-Parse-Application-Id': 'test', + 'X-Parse-Master-Key': 'test', }, }); - fail(); - } catch (response) { - expect(response.data.error).toEqual('unauthorized'); + const body = response.data; + expect(body.results.length).toEqual(1); + expect(body.results[0].query).toEqual('{"deviceType":"ios"}'); + expect(body.results[0].payload).toEqual('{"badge":"increment","alert":"Hello world!"}'); } - }); - - it_id('924cf5f5-f684-4925-978a-e52c0c457366')(it)('should allow master key to query _PushStatus', async () => { - await setup(); - const pushStatusId = await Parse.Push.send({ - where: { - deviceType: 'ios', - }, - data: { - badge: 'increment', - alert: 'Hello world!', - }, - }); - await pushCompleted(pushStatusId); - const response = await request({ - url: 'http://localhost:8378/1/classes/_PushStatus', - json: true, - headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key': 'test', - }, - }); - const body = response.data; - expect(body.results.length).toEqual(1); - expect(body.results[0].query).toEqual('{"deviceType":"ios"}'); - expect(body.results[0].payload).toEqual('{"badge":"increment","alert":"Hello world!"}'); - }); + ); it('should throw error if missing push configuration', async () => { await reconfigureServer({ push: null }); diff --git a/spec/ParseACL.spec.js b/spec/ParseACL.spec.js index 3662f99911..4e6bf4e88f 100644 --- a/spec/ParseACL.spec.js +++ b/spec/ParseACL.spec.js @@ -7,7 +7,9 @@ const auth = require('../lib/Auth'); describe('Parse.ACL', () => { it('acl must be valid', () => { const user = new Parse.User(); - expect(() => user.setACL('ACL')).toThrow(new Parse.Error(Parse.Error.OTHER_CAUSE, 'ACL must be a Parse ACL.')); + expect(() => user.setACL('ACL')).toThrow( + new Parse.Error(Parse.Error.OTHER_CAUSE, 'ACL must be a Parse ACL.') + ); }); it('refresh object with acl', async done => { diff --git a/spec/ParseAPI.spec.js b/spec/ParseAPI.spec.js index a178a1b863..1c5d5ddbc7 100644 --- a/spec/ParseAPI.spec.js +++ b/spec/ParseAPI.spec.js @@ -163,90 +163,96 @@ describe('miscellaneous', function () { expect(numCreated).toBe(1); }); - it_id('be1b9ac7-5e5f-4e91-b044-2bd8fb7622ad')(it)('ensure that if people already have duplicate users, they can still sign up new users', async done => { - try { - await Parse.User.logOut(); - } catch (e) { - /* ignore */ + it_id('be1b9ac7-5e5f-4e91-b044-2bd8fb7622ad')(it)( + 'ensure that if people already have duplicate users, they can still sign up new users', + async done => { + try { + await Parse.User.logOut(); + } catch (e) { + /* ignore */ + } + const config = Config.get('test'); + // Remove existing data to clear out unique index + TestUtils.destroyAllDataPermanently() + .then(() => config.database.adapter.performInitialization({ VolatileClassesSchemas: [] })) + .then(() => config.database.adapter.createClass('_User', userSchema)) + .then(() => + config.database.adapter + .createObject('_User', userSchema, { objectId: 'x', username: 'u' }) + .catch(fail) + ) + .then(() => + config.database.adapter + .createObject('_User', userSchema, { objectId: 'y', username: 'u' }) + .catch(fail) + ) + // Create a new server to try to recreate the unique indexes + .then(reconfigureServer) + .catch(error => { + expect(error.code).toEqual(Parse.Error.DUPLICATE_VALUE); + const user = new Parse.User(); + user.setPassword('asdf'); + user.setUsername('zxcv'); + return user.signUp().catch(fail); + }) + .then(() => { + const user = new Parse.User(); + user.setPassword('asdf'); + user.setUsername('u'); + return user.signUp(); + }) + .then(() => { + fail('should not have been able to sign up'); + done(); + }) + .catch(error => { + expect(error.code).toEqual(Parse.Error.USERNAME_TAKEN); + done(); + }); } - const config = Config.get('test'); - // Remove existing data to clear out unique index - TestUtils.destroyAllDataPermanently() - .then(() => config.database.adapter.performInitialization({ VolatileClassesSchemas: [] })) - .then(() => config.database.adapter.createClass('_User', userSchema)) - .then(() => - config.database.adapter - .createObject('_User', userSchema, { objectId: 'x', username: 'u' }) - .catch(fail) - ) - .then(() => - config.database.adapter - .createObject('_User', userSchema, { objectId: 'y', username: 'u' }) - .catch(fail) - ) - // Create a new server to try to recreate the unique indexes - .then(reconfigureServer) - .catch(error => { - expect(error.code).toEqual(Parse.Error.DUPLICATE_VALUE); - const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('zxcv'); - return user.signUp().catch(fail); - }) - .then(() => { - const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('u'); - return user.signUp(); - }) - .then(() => { - fail('should not have been able to sign up'); - done(); - }) - .catch(error => { - expect(error.code).toEqual(Parse.Error.USERNAME_TAKEN); - done(); - }); - }); - - it_id('d00f907e-41b9-40f6-8168-63e832199a8c')(it)('ensure that if people already have duplicate emails, they can still sign up new users', done => { - const config = Config.get('test'); - // Remove existing data to clear out unique index - TestUtils.destroyAllDataPermanently() - .then(() => config.database.adapter.performInitialization({ VolatileClassesSchemas: [] })) - .then(() => config.database.adapter.createClass('_User', userSchema)) - .then(() => - config.database.adapter.createObject('_User', userSchema, { - objectId: 'x', - email: 'a@b.c', + ); + + it_id('d00f907e-41b9-40f6-8168-63e832199a8c')(it)( + 'ensure that if people already have duplicate emails, they can still sign up new users', + done => { + const config = Config.get('test'); + // Remove existing data to clear out unique index + TestUtils.destroyAllDataPermanently() + .then(() => config.database.adapter.performInitialization({ VolatileClassesSchemas: [] })) + .then(() => config.database.adapter.createClass('_User', userSchema)) + .then(() => + config.database.adapter.createObject('_User', userSchema, { + objectId: 'x', + email: 'a@b.c', + }) + ) + .then(() => + config.database.adapter.createObject('_User', userSchema, { + objectId: 'y', + email: 'a@b.c', + }) + ) + .then(reconfigureServer) + .catch(() => { + const user = new Parse.User(); + user.setPassword('asdf'); + user.setUsername('qqq'); + user.setEmail('unique@unique.unique'); + return user.signUp().catch(fail); }) - ) - .then(() => - config.database.adapter.createObject('_User', userSchema, { - objectId: 'y', - email: 'a@b.c', + .then(() => { + const user = new Parse.User(); + user.setPassword('asdf'); + user.setUsername('www'); + user.setEmail('a@b.c'); + return user.signUp(); }) - ) - .then(reconfigureServer) - .catch(() => { - const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('qqq'); - user.setEmail('unique@unique.unique'); - return user.signUp().catch(fail); - }) - .then(() => { - const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('www'); - user.setEmail('a@b.c'); - return user.signUp(); - }) - .catch(error => { - expect(error.code).toEqual(Parse.Error.EMAIL_TAKEN); - done(); - }); - }); + .catch(error => { + expect(error.code).toEqual(Parse.Error.EMAIL_TAKEN); + done(); + }); + } + ); it('ensure that if you try to sign up a user with a unique username and email, but duplicates in some other field that has a uniqueness constraint, you get a regular duplicate value error', async done => { await reconfigureServer(); @@ -289,33 +295,36 @@ describe('miscellaneous', function () { }, fail); }); - it_id('33db6efe-7c02-496c-8595-0ef627a94103')(it)('increment with a user object', function (done) { - createTestUser() - .then(user => { - user.increment('foo'); - return user.save(); - }) - .then(() => { - return Parse.User.logIn('test', 'moon-y'); - }) - .then(user => { - expect(user.get('foo')).toEqual(1); - user.increment('foo'); - return user.save(); - }) - .then(() => Parse.User.logOut()) - .then(() => Parse.User.logIn('test', 'moon-y')) - .then( - user => { - expect(user.get('foo')).toEqual(2); - Parse.User.logOut().then(done); - }, - error => { - fail(JSON.stringify(error)); - done(); - } - ); - }); + it_id('33db6efe-7c02-496c-8595-0ef627a94103')(it)( + 'increment with a user object', + function (done) { + createTestUser() + .then(user => { + user.increment('foo'); + return user.save(); + }) + .then(() => { + return Parse.User.logIn('test', 'moon-y'); + }) + .then(user => { + expect(user.get('foo')).toEqual(1); + user.increment('foo'); + return user.save(); + }) + .then(() => Parse.User.logOut()) + .then(() => Parse.User.logIn('test', 'moon-y')) + .then( + user => { + expect(user.get('foo')).toEqual(2); + Parse.User.logOut().then(done); + }, + error => { + fail(JSON.stringify(error)); + done(); + } + ); + } + ); it_id('bef99522-bcfd-4f79-ba9e-3c3845550401')(it)('save various data types', function (done) { const obj = new TestObject(); @@ -951,155 +960,161 @@ describe('miscellaneous', function () { ); }); - it_id('e9e718a9-4465-4158-b13e-f146855a8892')(it)('return the updated fields on PUT', async () => { - const obj = new Parse.Object('GameScore'); - const pointer = new Parse.Object('Child'); - await pointer.save(); - obj.set( - 'point', - new Parse.GeoPoint({ - latitude: 37.4848, - longitude: -122.1483, - }) - ); - obj.set('array', ['obj1', 'obj2']); - obj.set('objects', { a: 'b' }); - obj.set('string', 'abc'); - obj.set('bool', true); - obj.set('number', 1); - obj.set('date', new Date()); - obj.set('pointer', pointer); - const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Installation-Id': 'yolo', - }; - const saveResponse = await request({ - method: 'POST', - headers: headers, - url: 'http://localhost:8378/1/classes/GameScore', - body: JSON.stringify({ - a: 'hello', - c: 1, - d: ['1'], - e: ['1'], - f: ['1', '2'], - ...obj.toJSON(), - }), - }); - expect(Object.keys(saveResponse.data).sort()).toEqual(['createdAt', 'objectId']); - obj.id = saveResponse.data.objectId; - const response = await request({ - method: 'PUT', - headers: headers, - url: 'http://localhost:8378/1/classes/GameScore/' + obj.id, - body: JSON.stringify({ - a: 'b', - c: { __op: 'Increment', amount: 2 }, - d: { __op: 'Add', objects: ['2'] }, - e: { __op: 'AddUnique', objects: ['1', '2'] }, - f: { __op: 'Remove', objects: ['2'] }, - selfThing: { - __type: 'Pointer', - className: 'GameScore', - objectId: obj.id, - }, - }), - }); - const body = response.data; - expect(Object.keys(body).sort()).toEqual(['c', 'd', 'e', 'f', 'updatedAt']); - expect(body.a).toBeUndefined(); - expect(body.c).toEqual(3); // 2+1 - expect(body.d.length).toBe(2); - expect(body.d.indexOf('1') > -1).toBe(true); - expect(body.d.indexOf('2') > -1).toBe(true); - expect(body.e.length).toBe(2); - expect(body.e.indexOf('1') > -1).toBe(true); - expect(body.e.indexOf('2') > -1).toBe(true); - expect(body.f.length).toBe(1); - expect(body.f.indexOf('1') > -1).toBe(true); - expect(body.selfThing).toBeUndefined(); - expect(body.updatedAt).not.toBeUndefined(); - }); - - it_id('ea358b59-03c0-45c9-abc7-1fdd67573029')(it)('should response should not change with triggers', async () => { - const obj = new Parse.Object('GameScore'); - const pointer = new Parse.Object('Child'); - Parse.Cloud.beforeSave('GameScore', request => { - return request.object; - }); - Parse.Cloud.afterSave('GameScore', request => { - return request.object; - }); - await pointer.save(); - obj.set( - 'point', - new Parse.GeoPoint({ - latitude: 37.4848, - longitude: -122.1483, - }) - ); - obj.set('array', ['obj1', 'obj2']); - obj.set('objects', { a: 'b' }); - obj.set('string', 'abc'); - obj.set('bool', true); - obj.set('number', 1); - obj.set('date', new Date()); - obj.set('pointer', pointer); - const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Installation-Id': 'yolo', - }; - const saveResponse = await request({ - method: 'POST', - headers: headers, - url: 'http://localhost:8378/1/classes/GameScore', - body: JSON.stringify({ - a: 'hello', - c: 1, - d: ['1'], - e: ['1'], - f: ['1', '2'], - ...obj.toJSON(), - }), - }); - expect(Object.keys(saveResponse.data).sort()).toEqual(['createdAt', 'objectId']); - obj.id = saveResponse.data.objectId; - const response = await request({ - method: 'PUT', - headers: headers, - url: 'http://localhost:8378/1/classes/GameScore/' + obj.id, - body: JSON.stringify({ - a: 'b', - c: { __op: 'Increment', amount: 2 }, - d: { __op: 'Add', objects: ['2'] }, - e: { __op: 'AddUnique', objects: ['1', '2'] }, - f: { __op: 'Remove', objects: ['2'] }, - selfThing: { - __type: 'Pointer', - className: 'GameScore', - objectId: obj.id, - }, - }), - }); - const body = response.data; - expect(Object.keys(body).sort()).toEqual(['c', 'd', 'e', 'f', 'updatedAt']); - expect(body.a).toBeUndefined(); - expect(body.c).toEqual(3); // 2+1 - expect(body.d.length).toBe(2); - expect(body.d.indexOf('1') > -1).toBe(true); - expect(body.d.indexOf('2') > -1).toBe(true); - expect(body.e.length).toBe(2); - expect(body.e.indexOf('1') > -1).toBe(true); - expect(body.e.indexOf('2') > -1).toBe(true); - expect(body.f.length).toBe(1); - expect(body.f.indexOf('1') > -1).toBe(true); - expect(body.selfThing).toBeUndefined(); - expect(body.updatedAt).not.toBeUndefined(); - }); + it_id('e9e718a9-4465-4158-b13e-f146855a8892')(it)( + 'return the updated fields on PUT', + async () => { + const obj = new Parse.Object('GameScore'); + const pointer = new Parse.Object('Child'); + await pointer.save(); + obj.set( + 'point', + new Parse.GeoPoint({ + latitude: 37.4848, + longitude: -122.1483, + }) + ); + obj.set('array', ['obj1', 'obj2']); + obj.set('objects', { a: 'b' }); + obj.set('string', 'abc'); + obj.set('bool', true); + obj.set('number', 1); + obj.set('date', new Date()); + obj.set('pointer', pointer); + const headers = { + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Installation-Id': 'yolo', + }; + const saveResponse = await request({ + method: 'POST', + headers: headers, + url: 'http://localhost:8378/1/classes/GameScore', + body: JSON.stringify({ + a: 'hello', + c: 1, + d: ['1'], + e: ['1'], + f: ['1', '2'], + ...obj.toJSON(), + }), + }); + expect(Object.keys(saveResponse.data).sort()).toEqual(['createdAt', 'objectId']); + obj.id = saveResponse.data.objectId; + const response = await request({ + method: 'PUT', + headers: headers, + url: 'http://localhost:8378/1/classes/GameScore/' + obj.id, + body: JSON.stringify({ + a: 'b', + c: { __op: 'Increment', amount: 2 }, + d: { __op: 'Add', objects: ['2'] }, + e: { __op: 'AddUnique', objects: ['1', '2'] }, + f: { __op: 'Remove', objects: ['2'] }, + selfThing: { + __type: 'Pointer', + className: 'GameScore', + objectId: obj.id, + }, + }), + }); + const body = response.data; + expect(Object.keys(body).sort()).toEqual(['c', 'd', 'e', 'f', 'updatedAt']); + expect(body.a).toBeUndefined(); + expect(body.c).toEqual(3); // 2+1 + expect(body.d.length).toBe(2); + expect(body.d.indexOf('1') > -1).toBe(true); + expect(body.d.indexOf('2') > -1).toBe(true); + expect(body.e.length).toBe(2); + expect(body.e.indexOf('1') > -1).toBe(true); + expect(body.e.indexOf('2') > -1).toBe(true); + expect(body.f.length).toBe(1); + expect(body.f.indexOf('1') > -1).toBe(true); + expect(body.selfThing).toBeUndefined(); + expect(body.updatedAt).not.toBeUndefined(); + } + ); + + it_id('ea358b59-03c0-45c9-abc7-1fdd67573029')(it)( + 'should response should not change with triggers', + async () => { + const obj = new Parse.Object('GameScore'); + const pointer = new Parse.Object('Child'); + Parse.Cloud.beforeSave('GameScore', request => { + return request.object; + }); + Parse.Cloud.afterSave('GameScore', request => { + return request.object; + }); + await pointer.save(); + obj.set( + 'point', + new Parse.GeoPoint({ + latitude: 37.4848, + longitude: -122.1483, + }) + ); + obj.set('array', ['obj1', 'obj2']); + obj.set('objects', { a: 'b' }); + obj.set('string', 'abc'); + obj.set('bool', true); + obj.set('number', 1); + obj.set('date', new Date()); + obj.set('pointer', pointer); + const headers = { + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Installation-Id': 'yolo', + }; + const saveResponse = await request({ + method: 'POST', + headers: headers, + url: 'http://localhost:8378/1/classes/GameScore', + body: JSON.stringify({ + a: 'hello', + c: 1, + d: ['1'], + e: ['1'], + f: ['1', '2'], + ...obj.toJSON(), + }), + }); + expect(Object.keys(saveResponse.data).sort()).toEqual(['createdAt', 'objectId']); + obj.id = saveResponse.data.objectId; + const response = await request({ + method: 'PUT', + headers: headers, + url: 'http://localhost:8378/1/classes/GameScore/' + obj.id, + body: JSON.stringify({ + a: 'b', + c: { __op: 'Increment', amount: 2 }, + d: { __op: 'Add', objects: ['2'] }, + e: { __op: 'AddUnique', objects: ['1', '2'] }, + f: { __op: 'Remove', objects: ['2'] }, + selfThing: { + __type: 'Pointer', + className: 'GameScore', + objectId: obj.id, + }, + }), + }); + const body = response.data; + expect(Object.keys(body).sort()).toEqual(['c', 'd', 'e', 'f', 'updatedAt']); + expect(body.a).toBeUndefined(); + expect(body.c).toEqual(3); // 2+1 + expect(body.d.length).toBe(2); + expect(body.d.indexOf('1') > -1).toBe(true); + expect(body.d.indexOf('2') > -1).toBe(true); + expect(body.e.length).toBe(2); + expect(body.e.indexOf('1') > -1).toBe(true); + expect(body.e.indexOf('2') > -1).toBe(true); + expect(body.f.length).toBe(1); + expect(body.f.indexOf('1') > -1).toBe(true); + expect(body.selfThing).toBeUndefined(); + expect(body.updatedAt).not.toBeUndefined(); + } + ); it('test cloud function error handling', done => { // Register a function which will fail @@ -1491,47 +1506,50 @@ describe('miscellaneous', function () { }); }); - it_id('b2cd9cf2-13fa-4acd-aaa9-6f81fc1858db')(it)('properly returns incremented values (#1554)', done => { - const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - }; - const requestOptions = { - headers: headers, - url: 'http://localhost:8378/1/classes/AnObject', - json: true, - }; - const object = new Parse.Object('AnObject'); - - function runIncrement(amount) { - const options = Object.assign({}, requestOptions, { - body: { - key: { - __op: 'Increment', - amount: amount, + it_id('b2cd9cf2-13fa-4acd-aaa9-6f81fc1858db')(it)( + 'properly returns incremented values (#1554)', + done => { + const headers = { + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + }; + const requestOptions = { + headers: headers, + url: 'http://localhost:8378/1/classes/AnObject', + json: true, + }; + const object = new Parse.Object('AnObject'); + + function runIncrement(amount) { + const options = Object.assign({}, requestOptions, { + body: { + key: { + __op: 'Increment', + amount: amount, + }, }, - }, - url: 'http://localhost:8378/1/classes/AnObject/' + object.id, - method: 'PUT', - }); - return request(options).then(res => res.data); - } + url: 'http://localhost:8378/1/classes/AnObject/' + object.id, + method: 'PUT', + }); + return request(options).then(res => res.data); + } - object - .save() - .then(() => { - return runIncrement(1); - }) - .then(res => { - expect(res.key).toBe(1); - return runIncrement(-1); - }) - .then(res => { - expect(res.key).toBe(0); - done(); - }); - }); + object + .save() + .then(() => { + return runIncrement(1); + }) + .then(res => { + expect(res.key).toBe(1); + return runIncrement(-1); + }) + .then(res => { + expect(res.key).toBe(0); + done(); + }); + } + ); it('ignores _RevocableSession "header" send by JS SDK', done => { const object = new Parse.Object('AnObject'); diff --git a/spec/ParseCloudCodePublisher.spec.js b/spec/ParseCloudCodePublisher.spec.js index 3435d44bde..ef00960687 100644 --- a/spec/ParseCloudCodePublisher.spec.js +++ b/spec/ParseCloudCodePublisher.spec.js @@ -1,5 +1,5 @@ -const ParseCloudCodePublisher = require('../lib/LiveQuery/ParseCloudCodePublisher') - .ParseCloudCodePublisher; +const ParseCloudCodePublisher = + require('../lib/LiveQuery/ParseCloudCodePublisher').ParseCloudCodePublisher; const Parse = require('parse/node'); describe('ParseCloudCodePublisher', function () { diff --git a/spec/ParseConfigKey.spec.js b/spec/ParseConfigKey.spec.js index fe7556123b..f3a1a42419 100644 --- a/spec/ParseConfigKey.spec.js +++ b/spec/ParseConfigKey.spec.js @@ -11,53 +11,57 @@ describe('Config Keys', () => { }); it('recognizes invalid keys in root', async () => { - await expectAsync(reconfigureServer({ - ...defaultConfiguration, - invalidKey: 1, - })).toBeResolved(); - const error = loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], ''); + await expectAsync( + reconfigureServer({ + ...defaultConfiguration, + invalidKey: 1, + }) + ).toBeResolved(); + const error = loggerErrorSpy.calls.all().reduce((s, call) => (s += call.args[0]), ''); expect(error).toMatch(invalidKeyErrorMessage); }); it('recognizes invalid keys in pages.customUrls', async () => { - await expectAsync(reconfigureServer({ - ...defaultConfiguration, - pages: { - customUrls: { - invalidKey: 1, - EmailVerificationSendFail: 1, - } - } - })).toBeResolved(); - const error = loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], ''); + await expectAsync( + reconfigureServer({ + ...defaultConfiguration, + pages: { + customUrls: { + invalidKey: 1, + EmailVerificationSendFail: 1, + }, + }, + }) + ).toBeResolved(); + const error = loggerErrorSpy.calls.all().reduce((s, call) => (s += call.args[0]), ''); expect(error).toMatch(invalidKeyErrorMessage); expect(error).toMatch(`invalidKey`); expect(error).toMatch(`EmailVerificationSendFail`); }); it('recognizes invalid keys in liveQueryServerOptions', async () => { - await expectAsync(reconfigureServer({ - ...defaultConfiguration, - liveQueryServerOptions: { - invalidKey: 1, - MasterKey: 1, - } - })).toBeResolved(); - const error = loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], ''); + await expectAsync( + reconfigureServer({ + ...defaultConfiguration, + liveQueryServerOptions: { + invalidKey: 1, + MasterKey: 1, + }, + }) + ).toBeResolved(); + const error = loggerErrorSpy.calls.all().reduce((s, call) => (s += call.args[0]), ''); expect(error).toMatch(invalidKeyErrorMessage); expect(error).toMatch(`MasterKey`); }); it('recognizes invalid keys in rateLimit', async () => { - await expectAsync(reconfigureServer({ - ...defaultConfiguration, - rateLimit: [ - { invalidKey: 1 }, - { RequestPath: 1 }, - { RequestTimeWindow: 1 }, - ] - })).toBeRejected(); - const error = loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], ''); + await expectAsync( + reconfigureServer({ + ...defaultConfiguration, + rateLimit: [{ invalidKey: 1 }, { RequestPath: 1 }, { RequestTimeWindow: 1 }], + }) + ).toBeRejected(); + const error = loggerErrorSpy.calls.all().reduce((s, call) => (s += call.args[0]), ''); expect(error).toMatch(invalidKeyErrorMessage); expect(error).toMatch('rateLimit\\[0\\]\\.invalidKey'); expect(error).toMatch('rateLimit\\[1\\]\\.RequestPath'); @@ -65,29 +69,37 @@ describe('Config Keys', () => { }); it('recognizes valid keys in default configuration', async () => { - await expectAsync(reconfigureServer({ - ...defaultConfiguration, - })).toBeResolved(); - expect(loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], '')).not.toMatch(invalidKeyErrorMessage); + await expectAsync( + reconfigureServer({ + ...defaultConfiguration, + }) + ).toBeResolved(); + expect(loggerErrorSpy.calls.all().reduce((s, call) => (s += call.args[0]), '')).not.toMatch( + invalidKeyErrorMessage + ); }); it_only_db('mongo')('recognizes valid keys in databaseOptions (MongoDB)', async () => { - await expectAsync(reconfigureServer({ - databaseURI: 'mongodb://localhost:27017/parse', - filesAdapter: null, - databaseAdapter: null, - databaseOptions: { - retryWrites: true, - maxTimeMS: 1000, - maxStalenessSeconds: 10, - maxPoolSize: 10, - minPoolSize: 5, - connectTimeoutMS: 5000, - socketTimeoutMS: 5000, - autoSelectFamily: true, - autoSelectFamilyAttemptTimeout: 3000 - }, - })).toBeResolved(); - expect(loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], '')).not.toMatch(invalidKeyErrorMessage); + await expectAsync( + reconfigureServer({ + databaseURI: 'mongodb://localhost:27017/parse', + filesAdapter: null, + databaseAdapter: null, + databaseOptions: { + retryWrites: true, + maxTimeMS: 1000, + maxStalenessSeconds: 10, + maxPoolSize: 10, + minPoolSize: 5, + connectTimeoutMS: 5000, + socketTimeoutMS: 5000, + autoSelectFamily: true, + autoSelectFamilyAttemptTimeout: 3000, + }, + }) + ).toBeResolved(); + expect(loggerErrorSpy.calls.all().reduce((s, call) => (s += call.args[0]), '')).not.toMatch( + invalidKeyErrorMessage + ); }); }); diff --git a/spec/ParseGeoPoint.spec.js b/spec/ParseGeoPoint.spec.js index f154f0048e..ac104c1a47 100644 --- a/spec/ParseGeoPoint.spec.js +++ b/spec/ParseGeoPoint.spec.js @@ -207,16 +207,19 @@ describe('Parse.GeoPoint testing', () => { done(); }); - it_id('05f1a454-56b1-4f2e-908e-408a9222cbae')(it)('geo max distance in km california', async () => { - await makeSomeGeoPoints(); - const sfo = new Parse.GeoPoint(37.6189722, -122.3748889); - const query = new Parse.Query(TestObject); - query.withinKilometers('location', sfo, 3700.0); - const results = await query.find(); - equal(results.length, 2); - equal(results[0].get('name'), 'San Francisco'); - equal(results[1].get('name'), 'Sacramento'); - }); + it_id('05f1a454-56b1-4f2e-908e-408a9222cbae')(it)( + 'geo max distance in km california', + async () => { + await makeSomeGeoPoints(); + const sfo = new Parse.GeoPoint(37.6189722, -122.3748889); + const query = new Parse.Query(TestObject); + query.withinKilometers('location', sfo, 3700.0); + const results = await query.find(); + equal(results.length, 2); + equal(results[0].get('name'), 'San Francisco'); + equal(results[1].get('name'), 'Sacramento'); + } + ); it('geo max distance in km bay area', async () => { await makeSomeGeoPoints(); @@ -246,16 +249,19 @@ describe('Parse.GeoPoint testing', () => { equal(results.length, 3); }); - it_id('9ee376ad-dd6c-4c17-ad28-c7899a4411f1')(it)('geo max distance in miles california', async () => { - await makeSomeGeoPoints(); - const sfo = new Parse.GeoPoint(37.6189722, -122.3748889); - const query = new Parse.Query(TestObject); - query.withinMiles('location', sfo, 2200.0); - const results = await query.find(); - equal(results.length, 2); - equal(results[0].get('name'), 'San Francisco'); - equal(results[1].get('name'), 'Sacramento'); - }); + it_id('9ee376ad-dd6c-4c17-ad28-c7899a4411f1')(it)( + 'geo max distance in miles california', + async () => { + await makeSomeGeoPoints(); + const sfo = new Parse.GeoPoint(37.6189722, -122.3748889); + const query = new Parse.Query(TestObject); + query.withinMiles('location', sfo, 2200.0); + const results = await query.find(); + equal(results.length, 2); + equal(results[0].get('name'), 'San Francisco'); + equal(results[1].get('name'), 'Sacramento'); + } + ); it('geo max distance in miles bay area', async () => { await makeSomeGeoPoints(); @@ -433,48 +439,51 @@ describe('Parse.GeoPoint testing', () => { }, done.fail); }); - it_id('0a248e11-3598-480a-9ab5-8a0b259258e4')(it)('supports withinPolygon Polygon object', done => { - const inbound = new Parse.GeoPoint(1.5, 1.5); - const onbound = new Parse.GeoPoint(10, 10); - const outbound = new Parse.GeoPoint(20, 20); - const obj1 = new Parse.Object('Polygon', { location: inbound }); - const obj2 = new Parse.Object('Polygon', { location: onbound }); - const obj3 = new Parse.Object('Polygon', { location: outbound }); - const polygon = { - __type: 'Polygon', - coordinates: [ - [0, 0], - [10, 0], - [10, 10], - [0, 10], - [0, 0], - ], - }; - Parse.Object.saveAll([obj1, obj2, obj3]) - .then(() => { - const where = { - location: { - $geoWithin: { - $polygon: polygon, + it_id('0a248e11-3598-480a-9ab5-8a0b259258e4')(it)( + 'supports withinPolygon Polygon object', + done => { + const inbound = new Parse.GeoPoint(1.5, 1.5); + const onbound = new Parse.GeoPoint(10, 10); + const outbound = new Parse.GeoPoint(20, 20); + const obj1 = new Parse.Object('Polygon', { location: inbound }); + const obj2 = new Parse.Object('Polygon', { location: onbound }); + const obj3 = new Parse.Object('Polygon', { location: outbound }); + const polygon = { + __type: 'Polygon', + coordinates: [ + [0, 0], + [10, 0], + [10, 10], + [0, 10], + [0, 0], + ], + }; + Parse.Object.saveAll([obj1, obj2, obj3]) + .then(() => { + const where = { + location: { + $geoWithin: { + $polygon: polygon, + }, }, - }, - }; - return request({ - method: 'POST', - url: Parse.serverURL + '/classes/Polygon', - body: { where, _method: 'GET' }, - headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, - 'Content-Type': 'application/json', - }, - }); - }) - .then(resp => { - expect(resp.data.results.length).toBe(2); - done(); - }, done.fail); - }); + }; + return request({ + method: 'POST', + url: Parse.serverURL + '/classes/Polygon', + body: { where, _method: 'GET' }, + headers: { + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, + 'Content-Type': 'application/json', + }, + }); + }) + .then(resp => { + expect(resp.data.results.length).toBe(2); + done(); + }, done.fail); + } + ); it('invalid Polygon object withinPolygon', done => { const point = new Parse.GeoPoint(1.5, 1.5); @@ -752,38 +761,44 @@ describe('Parse.GeoPoint testing', () => { equal(count, 1); }); - it_id('0b073d31-0d41-41e7-bd60-f636ffb759dc')(it)('withinKilometers complex supports count', async () => { - const inside = new Parse.GeoPoint(10, 10); - const middle = new Parse.GeoPoint(20, 20); - const outside = new Parse.GeoPoint(30, 30); - const obj1 = new Parse.Object('TestObject', { location: inside }); - const obj2 = new Parse.Object('TestObject', { location: middle }); - const obj3 = new Parse.Object('TestObject', { location: outside }); + it_id('0b073d31-0d41-41e7-bd60-f636ffb759dc')(it)( + 'withinKilometers complex supports count', + async () => { + const inside = new Parse.GeoPoint(10, 10); + const middle = new Parse.GeoPoint(20, 20); + const outside = new Parse.GeoPoint(30, 30); + const obj1 = new Parse.Object('TestObject', { location: inside }); + const obj2 = new Parse.Object('TestObject', { location: middle }); + const obj3 = new Parse.Object('TestObject', { location: outside }); - await Parse.Object.saveAll([obj1, obj2, obj3]); + await Parse.Object.saveAll([obj1, obj2, obj3]); - const q1 = new Parse.Query(TestObject).withinKilometers('location', inside, 5); - const q2 = new Parse.Query(TestObject).withinKilometers('location', middle, 5); - const query = Parse.Query.or(q1, q2); - const count = await query.count(); + const q1 = new Parse.Query(TestObject).withinKilometers('location', inside, 5); + const q2 = new Parse.Query(TestObject).withinKilometers('location', middle, 5); + const query = Parse.Query.or(q1, q2); + const count = await query.count(); - equal(count, 2); - }); + equal(count, 2); + } + ); - it_id('26c9a13d-3d71-452e-a91c-9a4589be021c')(it)('fails to fetch geopoints that are specifically not at (0,0)', async () => { - const tmp = new TestObject({ - location: new Parse.GeoPoint({ latitude: 0, longitude: 0 }), - }); - const tmp2 = new TestObject({ - location: new Parse.GeoPoint({ - latitude: 49.2577142, - longitude: -123.1941149, - }), - }); - await Parse.Object.saveAll([tmp, tmp2]); - const query = new Parse.Query(TestObject); - query.notEqualTo('location', new Parse.GeoPoint({ latitude: 0, longitude: 0 })); - const results = await query.find(); - expect(results.length).toEqual(1); - }); + it_id('26c9a13d-3d71-452e-a91c-9a4589be021c')(it)( + 'fails to fetch geopoints that are specifically not at (0,0)', + async () => { + const tmp = new TestObject({ + location: new Parse.GeoPoint({ latitude: 0, longitude: 0 }), + }); + const tmp2 = new TestObject({ + location: new Parse.GeoPoint({ + latitude: 49.2577142, + longitude: -123.1941149, + }), + }); + await Parse.Object.saveAll([tmp, tmp2]); + const query = new Parse.Query(TestObject); + query.notEqualTo('location', new Parse.GeoPoint({ latitude: 0, longitude: 0 })); + const results = await query.find(); + expect(results.length).toEqual(1); + } + ); }); diff --git a/spec/ParseGlobalConfig.spec.js b/spec/ParseGlobalConfig.spec.js index e6719433ff..1ff6dcfd93 100644 --- a/spec/ParseGlobalConfig.spec.js +++ b/spec/ParseGlobalConfig.spec.js @@ -16,22 +16,21 @@ describe('a GlobalConfig', () => { return { objectId: '1' }; } ); - await config.database.adapter - .upsertOneObject( - '_GlobalConfig', - { - fields: { - objectId: { type: 'Number' }, - params: { type: 'Object' }, - masterKeyOnly: { type: 'Object' }, - }, + await config.database.adapter.upsertOneObject( + '_GlobalConfig', + { + fields: { + objectId: { type: 'Number' }, + params: { type: 'Object' }, + masterKeyOnly: { type: 'Object' }, }, - query, - { - params: { companies: ['US', 'DK'], counter: 20, internalParam: 'internal' }, - masterKeyOnly: { internalParam: true }, - } - ); + }, + query, + { + params: { companies: ['US', 'DK'], counter: 20, internalParam: 'internal' }, + masterKeyOnly: { internalParam: true }, + } + ); }); const headers = { @@ -111,28 +110,28 @@ describe('a GlobalConfig', () => { }); it_only_db('mongo')('can addUnique', async () => { - await Parse.Config.save({ companies: { __op: 'AddUnique', objects: ['PA', 'RS', 'E'] } }); + await Parse.Config.save({ companies: { __op: 'AddUnique', objects: ['PA', 'RS', 'E'] } }); const config = await Parse.Config.get(); const companies = config.get('companies'); expect(companies).toEqual(['US', 'DK', 'PA', 'RS', 'E']); }); it_only_db('mongo')('can add to array', async () => { - await Parse.Config.save({ companies: { __op: 'Add', objects: ['PA'] } }); + await Parse.Config.save({ companies: { __op: 'Add', objects: ['PA'] } }); const config = await Parse.Config.get(); const companies = config.get('companies'); expect(companies).toEqual(['US', 'DK', 'PA']); }); it_only_db('mongo')('can remove from array', async () => { - await Parse.Config.save({ companies: { __op: 'Remove', objects: ['US'] } }); + await Parse.Config.save({ companies: { __op: 'Remove', objects: ['US'] } }); const config = await Parse.Config.get(); const companies = config.get('companies'); expect(companies).toEqual(['DK']); }); it('can increment', async () => { - await Parse.Config.save({ counter: { __op: 'Increment', amount: 49 } }); + await Parse.Config.save({ counter: { __op: 'Increment', amount: 49 } }); const config = await Parse.Config.get(); const counter = config.get('counter'); expect(counter).toEqual(69); diff --git a/spec/ParseGraphQLSchema.spec.js b/spec/ParseGraphQLSchema.spec.js index 0b3d9a9007..199c52756c 100644 --- a/spec/ParseGraphQLSchema.spec.js +++ b/spec/ParseGraphQLSchema.spec.js @@ -500,77 +500,83 @@ describe('ParseGraphQLSchema', () => { }); }); describe('alias', () => { - it_id('45282d26-f4c7-4d2d-a7b6-cd8741d5322f')(it)('Should be able to define alias for get and find query', async () => { - const parseGraphQLSchema = new ParseGraphQLSchema({ - databaseController, - parseGraphQLController, - log: defaultLogger, - appId, - }); - - await parseGraphQLSchema.parseGraphQLController.updateGraphQLConfig({ - classConfigs: [ - { - className: 'Data', - query: { - get: true, - getAlias: 'precious_data', - find: true, - findAlias: 'data_results', + it_id('45282d26-f4c7-4d2d-a7b6-cd8741d5322f')(it)( + 'Should be able to define alias for get and find query', + async () => { + const parseGraphQLSchema = new ParseGraphQLSchema({ + databaseController, + parseGraphQLController, + log: defaultLogger, + appId, + }); + + await parseGraphQLSchema.parseGraphQLController.updateGraphQLConfig({ + classConfigs: [ + { + className: 'Data', + query: { + get: true, + getAlias: 'precious_data', + find: true, + findAlias: 'data_results', + }, }, - }, - ], - }); + ], + }); - const data = new Parse.Object('Data'); + const data = new Parse.Object('Data'); - await data.save(); + await data.save(); - await parseGraphQLSchema.schemaCache.clear(); - await parseGraphQLSchema.load(); + await parseGraphQLSchema.schemaCache.clear(); + await parseGraphQLSchema.load(); - const queries1 = parseGraphQLSchema.graphQLQueries; + const queries1 = parseGraphQLSchema.graphQLQueries; - expect(Object.keys(queries1)).toContain('data_results'); - expect(Object.keys(queries1)).toContain('precious_data'); - }); + expect(Object.keys(queries1)).toContain('data_results'); + expect(Object.keys(queries1)).toContain('precious_data'); + } + ); - it_id('f04b46e3-a25d-401d-a315-3298cfee1df8')(it)('Should be able to define alias for mutation', async () => { - const parseGraphQLSchema = new ParseGraphQLSchema({ - databaseController, - parseGraphQLController, - log: defaultLogger, - appId, - }); + it_id('f04b46e3-a25d-401d-a315-3298cfee1df8')(it)( + 'Should be able to define alias for mutation', + async () => { + const parseGraphQLSchema = new ParseGraphQLSchema({ + databaseController, + parseGraphQLController, + log: defaultLogger, + appId, + }); - await parseGraphQLSchema.parseGraphQLController.updateGraphQLConfig({ - classConfigs: [ - { - className: 'Track', - mutation: { - create: true, - createAlias: 'addTrack', - update: true, - updateAlias: 'modifyTrack', - destroy: true, - destroyAlias: 'eraseTrack', + await parseGraphQLSchema.parseGraphQLController.updateGraphQLConfig({ + classConfigs: [ + { + className: 'Track', + mutation: { + create: true, + createAlias: 'addTrack', + update: true, + updateAlias: 'modifyTrack', + destroy: true, + destroyAlias: 'eraseTrack', + }, }, - }, - ], - }); + ], + }); - const data = new Parse.Object('Track'); + const data = new Parse.Object('Track'); - await data.save(); + await data.save(); - await parseGraphQLSchema.schemaCache.clear(); - await parseGraphQLSchema.load(); + await parseGraphQLSchema.schemaCache.clear(); + await parseGraphQLSchema.load(); - const mutations = parseGraphQLSchema.graphQLMutations; + const mutations = parseGraphQLSchema.graphQLMutations; - expect(Object.keys(mutations)).toContain('addTrack'); - expect(Object.keys(mutations)).toContain('modifyTrack'); - expect(Object.keys(mutations)).toContain('eraseTrack'); - }); + expect(Object.keys(mutations)).toContain('addTrack'); + expect(Object.keys(mutations)).toContain('modifyTrack'); + expect(Object.keys(mutations)).toContain('eraseTrack'); + } + ); }); }); diff --git a/spec/ParseGraphQLServer.spec.js b/spec/ParseGraphQLServer.spec.js index e1353d8db2..953919c693 100644 --- a/spec/ParseGraphQLServer.spec.js +++ b/spec/ParseGraphQLServer.spec.js @@ -9,7 +9,8 @@ const { updateCLP } = require('./support/dev'); const pluralize = require('pluralize'); const { getMainDefinition } = require('@apollo/client/utilities'); -const createUploadLink = (...args) => import('apollo-upload-client/createUploadLink.mjs').then(({ default: fn }) => fn(...args)); +const createUploadLink = (...args) => + import('apollo-upload-client/createUploadLink.mjs').then(({ default: fn }) => fn(...args)); const { SubscriptionClient } = require('subscriptions-transport-ws'); const { WebSocketLink } = require('@apollo/client/link/ws'); const { mergeSchemas } = require('@graphql-tools/schema'); @@ -130,14 +131,17 @@ describe('ParseGraphQLServer', () => { set: () => {}, }; - it_id('0696675e-060f-414f-bc77-9d57f31807f5')(it)('should return schema and context with req\'s info, config and auth', async () => { - const options = await parseGraphQLServer._getGraphQLOptions(); - expect(options.schema).toEqual(parseGraphQLServer.parseGraphQLSchema.graphQLSchema); - const contextResponse = await options.context({ req, res }); - expect(contextResponse.info).toEqual(req.info); - expect(contextResponse.config).toEqual(req.config); - expect(contextResponse.auth).toEqual(req.auth); - }); + it_id('0696675e-060f-414f-bc77-9d57f31807f5')(it)( + "should return schema and context with req's info, config and auth", + async () => { + const options = await parseGraphQLServer._getGraphQLOptions(); + expect(options.schema).toEqual(parseGraphQLServer.parseGraphQLSchema.graphQLSchema); + const contextResponse = await options.context({ req, res }); + expect(contextResponse.info).toEqual(req.info); + expect(contextResponse.config).toEqual(req.config); + expect(contextResponse.auth).toEqual(req.auth); + } + ); it('should load GraphQL schema in every call', async () => { const originalLoad = parseGraphQLServer.parseGraphQLSchema.load; @@ -1395,618 +1399,518 @@ describe('ParseGraphQLServer', () => { await resetGraphQLCache(); }); - it_id('d6a23a2f-ca18-4b15-bc73-3e636f99e6bc')(it)('should only include types in the enabledForClasses list', async () => { - const schemaController = await parseServer.config.databaseController.loadSchema(); - await schemaController.addClassIfNotExists('SuperCar', { - foo: { type: 'String' }, - }); + it_id('d6a23a2f-ca18-4b15-bc73-3e636f99e6bc')(it)( + 'should only include types in the enabledForClasses list', + async () => { + const schemaController = await parseServer.config.databaseController.loadSchema(); + await schemaController.addClassIfNotExists('SuperCar', { + foo: { type: 'String' }, + }); - const graphQLConfig = { - enabledForClasses: ['SuperCar'], - }; - await parseGraphQLServer.setGraphQLConfig(graphQLConfig); - await resetGraphQLCache(); + const graphQLConfig = { + enabledForClasses: ['SuperCar'], + }; + await parseGraphQLServer.setGraphQLConfig(graphQLConfig); + await resetGraphQLCache(); - const { data } = await apolloClient.query({ - query: gql` - query UserType { - userType: __type(name: "User") { - fields { - name + const { data } = await apolloClient.query({ + query: gql` + query UserType { + userType: __type(name: "User") { + fields { + name + } } - } - superCarType: __type(name: "SuperCar") { - fields { - name + superCarType: __type(name: "SuperCar") { + fields { + name + } } } - } - `, - }); - expect(data.userType).toBeNull(); - expect(data.superCarType).toBeTruthy(); - }); - it_id('1db2aceb-d24e-4929-ba43-8dbb5d0395e1')(it)('should not include types in the disabledForClasses list', async () => { - const schemaController = await parseServer.config.databaseController.loadSchema(); - await schemaController.addClassIfNotExists('SuperCar', { - foo: { type: 'String' }, - }); + `, + }); + expect(data.userType).toBeNull(); + expect(data.superCarType).toBeTruthy(); + } + ); + it_id('1db2aceb-d24e-4929-ba43-8dbb5d0395e1')(it)( + 'should not include types in the disabledForClasses list', + async () => { + const schemaController = await parseServer.config.databaseController.loadSchema(); + await schemaController.addClassIfNotExists('SuperCar', { + foo: { type: 'String' }, + }); - const graphQLConfig = { - disabledForClasses: ['SuperCar'], - }; - await parseGraphQLServer.setGraphQLConfig(graphQLConfig); - await resetGraphQLCache(); + const graphQLConfig = { + disabledForClasses: ['SuperCar'], + }; + await parseGraphQLServer.setGraphQLConfig(graphQLConfig); + await resetGraphQLCache(); - const { data } = await apolloClient.query({ - query: gql` - query UserType { - userType: __type(name: "User") { - fields { - name + const { data } = await apolloClient.query({ + query: gql` + query UserType { + userType: __type(name: "User") { + fields { + name + } } - } - superCarType: __type(name: "SuperCar") { - fields { - name + superCarType: __type(name: "SuperCar") { + fields { + name + } } } - } - `, - }); - expect(data.superCarType).toBeNull(); - expect(data.userType).toBeTruthy(); - }); - it_id('85c2e02f-0239-4819-b66e-392e0125f6c5')(it)('should remove query operations when disabled', async () => { - const superCar = new Parse.Object('SuperCar'); - await superCar.save({ foo: 'bar' }); - const customer = new Parse.Object('Customer'); - await customer.save({ foo: 'bar' }); + `, + }); + expect(data.superCarType).toBeNull(); + expect(data.userType).toBeTruthy(); + } + ); + it_id('85c2e02f-0239-4819-b66e-392e0125f6c5')(it)( + 'should remove query operations when disabled', + async () => { + const superCar = new Parse.Object('SuperCar'); + await superCar.save({ foo: 'bar' }); + const customer = new Parse.Object('Customer'); + await customer.save({ foo: 'bar' }); - await expectAsync( - apolloClient.query({ - query: gql` - query GetSuperCar($id: ID!) { - superCar(id: $id) { - id + await expectAsync( + apolloClient.query({ + query: gql` + query GetSuperCar($id: ID!) { + superCar(id: $id) { + id + } } - } - `, - variables: { - id: superCar.id, - }, - }) - ).toBeResolved(); + `, + variables: { + id: superCar.id, + }, + }) + ).toBeResolved(); - await expectAsync( - apolloClient.query({ - query: gql` - query FindCustomer { - customers { - count + await expectAsync( + apolloClient.query({ + query: gql` + query FindCustomer { + customers { + count + } } - } - `, - }) - ).toBeResolved(); + `, + }) + ).toBeResolved(); - const graphQLConfig = { - classConfigs: [ - { - className: 'SuperCar', - query: { - get: false, - find: true, + const graphQLConfig = { + classConfigs: [ + { + className: 'SuperCar', + query: { + get: false, + find: true, + }, }, - }, - { - className: 'Customer', - query: { - get: true, - find: false, + { + className: 'Customer', + query: { + get: true, + find: false, + }, }, - }, - ], - }; - await parseGraphQLServer.setGraphQLConfig(graphQLConfig); - await resetGraphQLCache(); + ], + }; + await parseGraphQLServer.setGraphQLConfig(graphQLConfig); + await resetGraphQLCache(); - await expectAsync( - apolloClient.query({ - query: gql` - query GetSuperCar($id: ID!) { - superCar(id: $id) { - id + await expectAsync( + apolloClient.query({ + query: gql` + query GetSuperCar($id: ID!) { + superCar(id: $id) { + id + } } - } - `, - variables: { - id: superCar.id, - }, - }) - ).toBeRejected(); - await expectAsync( - apolloClient.query({ - query: gql` - query GetCustomer($id: ID!) { - customer(id: $id) { - id + `, + variables: { + id: superCar.id, + }, + }) + ).toBeRejected(); + await expectAsync( + apolloClient.query({ + query: gql` + query GetCustomer($id: ID!) { + customer(id: $id) { + id + } } - } - `, - variables: { - id: customer.id, - }, - }) - ).toBeResolved(); - await expectAsync( - apolloClient.query({ - query: gql` - query FindSuperCar { - superCars { - count + `, + variables: { + id: customer.id, + }, + }) + ).toBeResolved(); + await expectAsync( + apolloClient.query({ + query: gql` + query FindSuperCar { + superCars { + count + } } - } - `, - }) - ).toBeResolved(); - await expectAsync( - apolloClient.query({ - query: gql` - query FindCustomer { - customers { - count + `, + }) + ).toBeResolved(); + await expectAsync( + apolloClient.query({ + query: gql` + query FindCustomer { + customers { + count + } } - } - `, - }) - ).toBeRejected(); - }); + `, + }) + ).toBeRejected(); + } + ); - it_id('972161a6-8108-4e99-a1a5-71d0267d26c2')(it)('should remove mutation operations, create, update and delete, when disabled', async () => { - const superCar1 = new Parse.Object('SuperCar'); - await superCar1.save({ foo: 'bar' }); - const customer1 = new Parse.Object('Customer'); - await customer1.save({ foo: 'bar' }); + it_id('972161a6-8108-4e99-a1a5-71d0267d26c2')(it)( + 'should remove mutation operations, create, update and delete, when disabled', + async () => { + const superCar1 = new Parse.Object('SuperCar'); + await superCar1.save({ foo: 'bar' }); + const customer1 = new Parse.Object('Customer'); + await customer1.save({ foo: 'bar' }); - await expectAsync( - apolloClient.query({ + await expectAsync( + apolloClient.query({ + query: gql` + mutation UpdateSuperCar($id: ID!, $foo: String!) { + updateSuperCar(input: { id: $id, fields: { foo: $foo } }) { + clientMutationId + } + } + `, + variables: { + id: superCar1.id, + foo: 'lah', + }, + }) + ).toBeResolved(); + + await expectAsync( + apolloClient.query({ + query: gql` + mutation DeleteCustomer($id: ID!) { + deleteCustomer(input: { id: $id }) { + clientMutationId + } + } + `, + variables: { + id: customer1.id, + }, + }) + ).toBeResolved(); + + const { data: customerData } = await apolloClient.query({ query: gql` - mutation UpdateSuperCar($id: ID!, $foo: String!) { - updateSuperCar(input: { id: $id, fields: { foo: $foo } }) { - clientMutationId + mutation CreateCustomer($foo: String!) { + createCustomer(input: { fields: { foo: $foo } }) { + customer { + id + } } } `, variables: { - id: superCar1.id, - foo: 'lah', + foo: 'rah', }, - }) - ).toBeResolved(); + }); + expect(customerData.createCustomer.customer).toBeTruthy(); - await expectAsync( - apolloClient.query({ + // used later + const customer2Id = customerData.createCustomer.customer.id; + + await parseGraphQLServer.setGraphQLConfig({ + classConfigs: [ + { + className: 'SuperCar', + mutation: { + create: true, + update: false, + destroy: true, + }, + }, + { + className: 'Customer', + mutation: { + create: false, + update: true, + destroy: false, + }, + }, + ], + }); + await resetGraphQLCache(); + + const { data: superCarData } = await apolloClient.query({ query: gql` - mutation DeleteCustomer($id: ID!) { - deleteCustomer(input: { id: $id }) { - clientMutationId + mutation CreateSuperCar($foo: String!) { + createSuperCar(input: { fields: { foo: $foo } }) { + superCar { + id + } } } `, variables: { - id: customer1.id, + foo: 'mah', }, - }) - ).toBeResolved(); + }); + expect(superCarData.createSuperCar).toBeTruthy(); + const superCar3Id = superCarData.createSuperCar.superCar.id; - const { data: customerData } = await apolloClient.query({ - query: gql` - mutation CreateCustomer($foo: String!) { - createCustomer(input: { fields: { foo: $foo } }) { - customer { - id + await expectAsync( + apolloClient.query({ + query: gql` + mutation UpdateSupercar($id: ID!, $foo: String!) { + updateSuperCar(input: { id: $id, fields: { foo: $foo } }) { + clientMutationId + } } - } - } - `, - variables: { - foo: 'rah', - }, - }); - expect(customerData.createCustomer.customer).toBeTruthy(); - - // used later - const customer2Id = customerData.createCustomer.customer.id; - - await parseGraphQLServer.setGraphQLConfig({ - classConfigs: [ - { - className: 'SuperCar', - mutation: { - create: true, - update: false, - destroy: true, - }, - }, - { - className: 'Customer', - mutation: { - create: false, - update: true, - destroy: false, + `, + variables: { + id: superCar3Id, }, - }, - ], - }); - await resetGraphQLCache(); - - const { data: superCarData } = await apolloClient.query({ - query: gql` - mutation CreateSuperCar($foo: String!) { - createSuperCar(input: { fields: { foo: $foo } }) { - superCar { - id - } - } - } - `, - variables: { - foo: 'mah', - }, - }); - expect(superCarData.createSuperCar).toBeTruthy(); - const superCar3Id = superCarData.createSuperCar.superCar.id; - - await expectAsync( - apolloClient.query({ - query: gql` - mutation UpdateSupercar($id: ID!, $foo: String!) { - updateSuperCar(input: { id: $id, fields: { foo: $foo } }) { - clientMutationId - } - } - `, - variables: { - id: superCar3Id, - }, - }) - ).toBeRejected(); + }) + ).toBeRejected(); - await expectAsync( - apolloClient.query({ - query: gql` - mutation DeleteSuperCar($id: ID!) { - deleteSuperCar(input: { id: $id }) { - clientMutationId + await expectAsync( + apolloClient.query({ + query: gql` + mutation DeleteSuperCar($id: ID!) { + deleteSuperCar(input: { id: $id }) { + clientMutationId + } } - } - `, - variables: { - id: superCar3Id, - }, - }) - ).toBeResolved(); + `, + variables: { + id: superCar3Id, + }, + }) + ).toBeResolved(); - await expectAsync( - apolloClient.query({ - query: gql` - mutation CreateCustomer($foo: String!) { - createCustomer(input: { fields: { foo: $foo } }) { - customer { - id + await expectAsync( + apolloClient.query({ + query: gql` + mutation CreateCustomer($foo: String!) { + createCustomer(input: { fields: { foo: $foo } }) { + customer { + id + } } } - } - `, - variables: { - foo: 'rah', - }, - }) - ).toBeRejected(); - await expectAsync( - apolloClient.query({ - query: gql` - mutation UpdateCustomer($id: ID!, $foo: String!) { - updateCustomer(input: { id: $id, fields: { foo: $foo } }) { - clientMutationId + `, + variables: { + foo: 'rah', + }, + }) + ).toBeRejected(); + await expectAsync( + apolloClient.query({ + query: gql` + mutation UpdateCustomer($id: ID!, $foo: String!) { + updateCustomer(input: { id: $id, fields: { foo: $foo } }) { + clientMutationId + } } - } - `, - variables: { - id: customer2Id, - foo: 'tah', - }, - }) - ).toBeResolved(); - await expectAsync( - apolloClient.query({ - query: gql` - mutation DeleteCustomer($id: ID!, $foo: String!) { - deleteCustomer(input: { id: $id }) { - clientMutationId + `, + variables: { + id: customer2Id, + foo: 'tah', + }, + }) + ).toBeResolved(); + await expectAsync( + apolloClient.query({ + query: gql` + mutation DeleteCustomer($id: ID!, $foo: String!) { + deleteCustomer(input: { id: $id }) { + clientMutationId + } } - } - `, - variables: { - id: customer2Id, - }, - }) - ).toBeRejected(); - }); + `, + variables: { + id: customer2Id, + }, + }) + ).toBeRejected(); + } + ); - it_id('4af763b1-ff86-43c7-ba30-060a1c07e730')(it)('should only allow the supplied create and update fields for a class', async () => { - const schemaController = await parseServer.config.databaseController.loadSchema(); - await schemaController.addClassIfNotExists('SuperCar', { - engine: { type: 'String' }, - doors: { type: 'Number' }, - price: { type: 'String' }, - mileage: { type: 'Number' }, - }); + it_id('4af763b1-ff86-43c7-ba30-060a1c07e730')(it)( + 'should only allow the supplied create and update fields for a class', + async () => { + const schemaController = await parseServer.config.databaseController.loadSchema(); + await schemaController.addClassIfNotExists('SuperCar', { + engine: { type: 'String' }, + doors: { type: 'Number' }, + price: { type: 'String' }, + mileage: { type: 'Number' }, + }); - await parseGraphQLServer.setGraphQLConfig({ - classConfigs: [ - { - className: 'SuperCar', - type: { - inputFields: { - create: ['engine', 'doors', 'price'], - update: ['price', 'mileage'], + await parseGraphQLServer.setGraphQLConfig({ + classConfigs: [ + { + className: 'SuperCar', + type: { + inputFields: { + create: ['engine', 'doors', 'price'], + update: ['price', 'mileage'], + }, }, }, - }, - ], - }); + ], + }); - await resetGraphQLCache(); + await resetGraphQLCache(); - await expectAsync( - apolloClient.query({ - query: gql` - mutation InvalidCreateSuperCar { - createSuperCar(input: { fields: { engine: "diesel", mileage: 1000 } }) { - superCar { - id + await expectAsync( + apolloClient.query({ + query: gql` + mutation InvalidCreateSuperCar { + createSuperCar(input: { fields: { engine: "diesel", mileage: 1000 } }) { + superCar { + id + } } } - } - `, - }) - ).toBeRejected(); - const { id: superCarId } = ( - await apolloClient.query({ - query: gql` - mutation ValidCreateSuperCar { - createSuperCar( - input: { fields: { engine: "diesel", doors: 5, price: "£10000" } } - ) { - superCar { - id + `, + }) + ).toBeRejected(); + const { id: superCarId } = ( + await apolloClient.query({ + query: gql` + mutation ValidCreateSuperCar { + createSuperCar( + input: { fields: { engine: "diesel", doors: 5, price: "£10000" } } + ) { + superCar { + id + } } } - } - `, - }) - ).data.createSuperCar.superCar; - - expect(superCarId).toBeTruthy(); - - await expectAsync( - apolloClient.query({ - query: gql` - mutation InvalidUpdateSuperCar($id: ID!) { - updateSuperCar(input: { id: $id, fields: { engine: "petrol" } }) { - clientMutationId - } - } - `, - variables: { - id: superCarId, - }, - }) - ).toBeRejected(); - - const updatedSuperCar = ( - await apolloClient.query({ - query: gql` - mutation ValidUpdateSuperCar($id: ID!) { - updateSuperCar(input: { id: $id, fields: { mileage: 2000 } }) { - clientMutationId - } - } - `, - variables: { - id: superCarId, - }, - }) - ).data.updateSuperCar; - expect(updatedSuperCar).toBeTruthy(); - }); - - it_id('fc9237e9-3e63-4b55-9c1d-e6269f613a93')(it)('should handle required fields from the Parse class', async () => { - const schemaController = await parseServer.config.databaseController.loadSchema(); - await schemaController.addClassIfNotExists('SuperCar', { - engine: { type: 'String', required: true }, - doors: { type: 'Number', required: true }, - price: { type: 'String' }, - mileage: { type: 'Number' }, - }); + `, + }) + ).data.createSuperCar.superCar; - await resetGraphQLCache(); + expect(superCarId).toBeTruthy(); - const { - data: { __type }, - } = await apolloClient.query({ - query: gql` - query requiredFields { - __type(name: "CreateSuperCarFieldsInput") { - inputFields { - name - type { - kind + await expectAsync( + apolloClient.query({ + query: gql` + mutation InvalidUpdateSuperCar($id: ID!) { + updateSuperCar(input: { id: $id, fields: { engine: "petrol" } }) { + clientMutationId } } - } - } - `, - }); - expect(__type.inputFields.find(o => o.name === 'price').type.kind).toEqual('SCALAR'); - expect(__type.inputFields.find(o => o.name === 'engine').type.kind).toEqual('NON_NULL'); - expect(__type.inputFields.find(o => o.name === 'doors').type.kind).toEqual('NON_NULL'); + `, + variables: { + id: superCarId, + }, + }) + ).toBeRejected(); - const { - data: { __type: __type2 }, - } = await apolloClient.query({ - query: gql` - query requiredFields { - __type(name: "SuperCar") { - fields { - name - type { - kind + const updatedSuperCar = ( + await apolloClient.query({ + query: gql` + mutation ValidUpdateSuperCar($id: ID!) { + updateSuperCar(input: { id: $id, fields: { mileage: 2000 } }) { + clientMutationId } } - } - } - `, - }); - expect(__type2.fields.find(o => o.name === 'price').type.kind).toEqual('SCALAR'); - expect(__type2.fields.find(o => o.name === 'engine').type.kind).toEqual('NON_NULL'); - expect(__type2.fields.find(o => o.name === 'doors').type.kind).toEqual('NON_NULL'); - }); - - it_id('83b6895a-7dfd-4e3b-a5ce-acdb1fa39705')(it)('should only allow the supplied output fields for a class', async () => { - const schemaController = await parseServer.config.databaseController.loadSchema(); - - await schemaController.addClassIfNotExists('SuperCar', { - engine: { type: 'String' }, - doors: { type: 'Number' }, - price: { type: 'String' }, - mileage: { type: 'Number' }, - insuranceClaims: { type: 'Number' }, - }); - - const superCar = await new Parse.Object('SuperCar').save({ - engine: 'petrol', - doors: 3, - price: '£7500', - mileage: 0, - insuranceCertificate: 'private-file.pdf', - }); - - await parseGraphQLServer.setGraphQLConfig({ - classConfigs: [ - { - className: 'SuperCar', - type: { - outputFields: ['engine', 'doors', 'price', 'mileage'], + `, + variables: { + id: superCarId, }, - }, - ], - }); - - await resetGraphQLCache(); + }) + ).data.updateSuperCar; + expect(updatedSuperCar).toBeTruthy(); + } + ); - await expectAsync( - apolloClient.query({ - query: gql` - query GetSuperCar($id: ID!) { - superCar(id: $id) { - id - objectId - engine - doors - price - mileage - insuranceCertificate - } - } - `, - variables: { - id: superCar.id, - }, - }) - ).toBeRejected(); - let getSuperCar = ( - await apolloClient.query({ - query: gql` - query GetSuperCar($id: ID!) { - superCar(id: $id) { - id - objectId - engine - doors - price - mileage - } - } - `, - variables: { - id: superCar.id, - }, - }) - ).data.superCar; - expect(getSuperCar).toBeTruthy(); + it_id('fc9237e9-3e63-4b55-9c1d-e6269f613a93')(it)( + 'should handle required fields from the Parse class', + async () => { + const schemaController = await parseServer.config.databaseController.loadSchema(); + await schemaController.addClassIfNotExists('SuperCar', { + engine: { type: 'String', required: true }, + doors: { type: 'Number', required: true }, + price: { type: 'String' }, + mileage: { type: 'Number' }, + }); - await parseGraphQLServer.setGraphQLConfig({ - classConfigs: [ - { - className: 'SuperCar', - type: { - outputFields: [], - }, - }, - ], - }); + await resetGraphQLCache(); - await resetGraphQLCache(); - await expectAsync( - apolloClient.query({ + const { + data: { __type }, + } = await apolloClient.query({ query: gql` - query GetSuperCar($id: ID!) { - superCar(id: $id) { - engine + query requiredFields { + __type(name: "CreateSuperCarFieldsInput") { + inputFields { + name + type { + kind + } + } } } `, - variables: { - id: superCar.id, - }, - }) - ).toBeRejected(); - getSuperCar = ( - await apolloClient.query({ + }); + expect(__type.inputFields.find(o => o.name === 'price').type.kind).toEqual('SCALAR'); + expect(__type.inputFields.find(o => o.name === 'engine').type.kind).toEqual('NON_NULL'); + expect(__type.inputFields.find(o => o.name === 'doors').type.kind).toEqual('NON_NULL'); + + const { + data: { __type: __type2 }, + } = await apolloClient.query({ query: gql` - query GetSuperCar($id: ID!) { - superCar(id: $id) { - id - objectId + query requiredFields { + __type(name: "SuperCar") { + fields { + name + type { + kind + } + } } } `, - variables: { - id: superCar.id, - }, - }) - ).data.superCar; - expect(getSuperCar.objectId).toBe(superCar.id); - }); + }); + expect(__type2.fields.find(o => o.name === 'price').type.kind).toEqual('SCALAR'); + expect(__type2.fields.find(o => o.name === 'engine').type.kind).toEqual('NON_NULL'); + expect(__type2.fields.find(o => o.name === 'doors').type.kind).toEqual('NON_NULL'); + } + ); - it_id('67dfcf94-92fb-45a3-a012-3b22c81899ba')(it)('should only allow the supplied constraint fields for a class', async () => { - try { + it_id('83b6895a-7dfd-4e3b-a5ce-acdb1fa39705')(it)( + 'should only allow the supplied output fields for a class', + async () => { const schemaController = await parseServer.config.databaseController.loadSchema(); await schemaController.addClassIfNotExists('SuperCar', { - model: { type: 'String' }, engine: { type: 'String' }, doors: { type: 'Number' }, price: { type: 'String' }, mileage: { type: 'Number' }, - insuranceCertificate: { type: 'String' }, + insuranceClaims: { type: 'Number' }, }); - await new Parse.Object('SuperCar').save({ - model: 'McLaren', + const superCar = await new Parse.Object('SuperCar').save({ engine: 'petrol', doors: 3, price: '£7500', @@ -2019,7 +1923,7 @@ describe('ParseGraphQLServer', () => { { className: 'SuperCar', type: { - constraintFields: ['engine', 'doors', 'price'], + outputFields: ['engine', 'doors', 'price', 'mileage'], }, }, ], @@ -2030,196 +1934,323 @@ describe('ParseGraphQLServer', () => { await expectAsync( apolloClient.query({ query: gql` - query FindSuperCar { - superCars(where: { insuranceCertificate: { equalTo: "private-file.pdf" } }) { - count + query GetSuperCar($id: ID!) { + superCar(id: $id) { + id + objectId + engine + doors + price + mileage + insuranceCertificate } } `, + variables: { + id: superCar.id, + }, }) ).toBeRejected(); + let getSuperCar = ( + await apolloClient.query({ + query: gql` + query GetSuperCar($id: ID!) { + superCar(id: $id) { + id + objectId + engine + doors + price + mileage + } + } + `, + variables: { + id: superCar.id, + }, + }) + ).data.superCar; + expect(getSuperCar).toBeTruthy(); + + await parseGraphQLServer.setGraphQLConfig({ + classConfigs: [ + { + className: 'SuperCar', + type: { + outputFields: [], + }, + }, + ], + }); + await resetGraphQLCache(); await expectAsync( apolloClient.query({ query: gql` - query FindSuperCar { - superCars(where: { mileage: { equalTo: 0 } }) { - count + query GetSuperCar($id: ID!) { + superCar(id: $id) { + engine } } `, + variables: { + id: superCar.id, + }, }) ).toBeRejected(); - - await expectAsync( - apolloClient.query({ + getSuperCar = ( + await apolloClient.query({ query: gql` - query FindSuperCar { - superCars(where: { engine: { equalTo: "petrol" } }) { - count + query GetSuperCar($id: ID!) { + superCar(id: $id) { + id + objectId } } `, + variables: { + id: superCar.id, + }, }) - ).toBeResolved(); - } catch (e) { - handleError(e); + ).data.superCar; + expect(getSuperCar.objectId).toBe(superCar.id); } - }); - - it_id('a3bdbd5d-8779-42fe-91a1-7a7f90a6177b')(it)('should only allow the supplied sort fields for a class', async () => { - const schemaController = await parseServer.config.databaseController.loadSchema(); + ); - await schemaController.addClassIfNotExists('SuperCar', { - engine: { type: 'String' }, - doors: { type: 'Number' }, - price: { type: 'String' }, - mileage: { type: 'Number' }, - }); + it_id('67dfcf94-92fb-45a3-a012-3b22c81899ba')(it)( + 'should only allow the supplied constraint fields for a class', + async () => { + try { + const schemaController = await parseServer.config.databaseController.loadSchema(); + + await schemaController.addClassIfNotExists('SuperCar', { + model: { type: 'String' }, + engine: { type: 'String' }, + doors: { type: 'Number' }, + price: { type: 'String' }, + mileage: { type: 'Number' }, + insuranceCertificate: { type: 'String' }, + }); - await new Parse.Object('SuperCar').save({ - engine: 'petrol', - doors: 3, - price: '£7500', - mileage: 0, - }); + await new Parse.Object('SuperCar').save({ + model: 'McLaren', + engine: 'petrol', + doors: 3, + price: '£7500', + mileage: 0, + insuranceCertificate: 'private-file.pdf', + }); - await parseGraphQLServer.setGraphQLConfig({ - classConfigs: [ - { - className: 'SuperCar', - type: { - sortFields: [ - { - field: 'doors', - asc: true, - desc: true, - }, - { - field: 'price', - asc: true, - desc: true, - }, - { - field: 'mileage', - asc: true, - desc: false, + await parseGraphQLServer.setGraphQLConfig({ + classConfigs: [ + { + className: 'SuperCar', + type: { + constraintFields: ['engine', 'doors', 'price'], }, - ], - }, - }, - ], - }); + }, + ], + }); - await resetGraphQLCache(); + await resetGraphQLCache(); - await expectAsync( - apolloClient.query({ - query: gql` - query FindSuperCar { - superCars(order: [engine_ASC]) { - edges { - node { - id + await expectAsync( + apolloClient.query({ + query: gql` + query FindSuperCar { + superCars(where: { insuranceCertificate: { equalTo: "private-file.pdf" } }) { + count } } - } - } - `, - }) - ).toBeRejected(); - await expectAsync( - apolloClient.query({ - query: gql` - query FindSuperCar { - superCars(order: [engine_DESC]) { - edges { - node { - id + `, + }) + ).toBeRejected(); + + await expectAsync( + apolloClient.query({ + query: gql` + query FindSuperCar { + superCars(where: { mileage: { equalTo: 0 } }) { + count + } + } + `, + }) + ).toBeRejected(); + + await expectAsync( + apolloClient.query({ + query: gql` + query FindSuperCar { + superCars(where: { engine: { equalTo: "petrol" } }) { + count + } + } + `, + }) + ).toBeResolved(); + } catch (e) { + handleError(e); + } + } + ); + + it_id('a3bdbd5d-8779-42fe-91a1-7a7f90a6177b')(it)( + 'should only allow the supplied sort fields for a class', + async () => { + const schemaController = await parseServer.config.databaseController.loadSchema(); + + await schemaController.addClassIfNotExists('SuperCar', { + engine: { type: 'String' }, + doors: { type: 'Number' }, + price: { type: 'String' }, + mileage: { type: 'Number' }, + }); + + await new Parse.Object('SuperCar').save({ + engine: 'petrol', + doors: 3, + price: '£7500', + mileage: 0, + }); + + await parseGraphQLServer.setGraphQLConfig({ + classConfigs: [ + { + className: 'SuperCar', + type: { + sortFields: [ + { + field: 'doors', + asc: true, + desc: true, + }, + { + field: 'price', + asc: true, + desc: true, + }, + { + field: 'mileage', + asc: true, + desc: false, + }, + ], + }, + }, + ], + }); + + await resetGraphQLCache(); + + await expectAsync( + apolloClient.query({ + query: gql` + query FindSuperCar { + superCars(order: [engine_ASC]) { + edges { + node { + id + } } } } - } - `, - }) - ).toBeRejected(); - await expectAsync( - apolloClient.query({ - query: gql` - query FindSuperCar { - superCars(order: [mileage_DESC]) { - edges { - node { - id + `, + }) + ).toBeRejected(); + await expectAsync( + apolloClient.query({ + query: gql` + query FindSuperCar { + superCars(order: [engine_DESC]) { + edges { + node { + id + } } } } - } - `, - }) - ).toBeRejected(); + `, + }) + ).toBeRejected(); + await expectAsync( + apolloClient.query({ + query: gql` + query FindSuperCar { + superCars(order: [mileage_DESC]) { + edges { + node { + id + } + } + } + } + `, + }) + ).toBeRejected(); - await expectAsync( - apolloClient.query({ - query: gql` - query FindSuperCar { - superCars(order: [mileage_ASC]) { - edges { - node { - id + await expectAsync( + apolloClient.query({ + query: gql` + query FindSuperCar { + superCars(order: [mileage_ASC]) { + edges { + node { + id + } } } } - } - `, - }) - ).toBeResolved(); - await expectAsync( - apolloClient.query({ - query: gql` - query FindSuperCar { - superCars(order: [doors_ASC]) { - edges { - node { - id + `, + }) + ).toBeResolved(); + await expectAsync( + apolloClient.query({ + query: gql` + query FindSuperCar { + superCars(order: [doors_ASC]) { + edges { + node { + id + } } } } - } - `, - }) - ).toBeResolved(); - await expectAsync( - apolloClient.query({ - query: gql` - query FindSuperCar { - superCars(order: [price_DESC]) { - edges { - node { - id + `, + }) + ).toBeResolved(); + await expectAsync( + apolloClient.query({ + query: gql` + query FindSuperCar { + superCars(order: [price_DESC]) { + edges { + node { + id + } } } } - } - `, - }) - ).toBeResolved(); - await expectAsync( - apolloClient.query({ - query: gql` - query FindSuperCar { - superCars(order: [price_ASC, doors_DESC]) { - edges { - node { - id + `, + }) + ).toBeResolved(); + await expectAsync( + apolloClient.query({ + query: gql` + query FindSuperCar { + superCars(order: [price_ASC, doors_DESC]) { + edges { + node { + id + } } } } - } - `, - }) - ).toBeResolved(); - }); + `, + }) + ).toBeResolved(); + } + ); }); describe('Relay Spec', () => { @@ -3488,12 +3519,10 @@ describe('ParseGraphQLServer', () => { }, }, }); - result.data.createClass.class.schemaFields = result.data.createClass.class.schemaFields.sort( - (a, b) => (a.name > b.name ? 1 : -1) - ); - result.data.updateClass.class.schemaFields = result.data.updateClass.class.schemaFields.sort( - (a, b) => (a.name > b.name ? 1 : -1) - ); + result.data.createClass.class.schemaFields = + result.data.createClass.class.schemaFields.sort((a, b) => (a.name > b.name ? 1 : -1)); + result.data.updateClass.class.schemaFields = + result.data.updateClass.class.schemaFields.sort((a, b) => (a.name > b.name ? 1 : -1)); expect(result).toEqual({ data: { createClass: { @@ -3824,12 +3853,10 @@ describe('ParseGraphQLServer', () => { }, }, }); - result.data.createClass.class.schemaFields = result.data.createClass.class.schemaFields.sort( - (a, b) => (a.name > b.name ? 1 : -1) - ); - result.data.deleteClass.class.schemaFields = result.data.deleteClass.class.schemaFields.sort( - (a, b) => (a.name > b.name ? 1 : -1) - ); + result.data.createClass.class.schemaFields = + result.data.createClass.class.schemaFields.sort((a, b) => (a.name > b.name ? 1 : -1)); + result.data.deleteClass.class.schemaFields = + result.data.deleteClass.class.schemaFields.sort((a, b) => (a.name > b.name ? 1 : -1)); expect(result).toEqual({ data: { createClass: { @@ -4921,50 +4948,53 @@ describe('ParseGraphQLServer', () => { ).toEqual(['someValue1', 'someValue2']); }); - it_id('accc59be-fd13-46c5-a103-ec63f2ad6670')(it)('should support full text search', async () => { - try { - const obj = new Parse.Object('FullTextSearchTest'); - obj.set('field1', 'Parse GraphQL Server'); - obj.set('field2', 'It rocks!'); - await obj.save(); + it_id('accc59be-fd13-46c5-a103-ec63f2ad6670')(it)( + 'should support full text search', + async () => { + try { + const obj = new Parse.Object('FullTextSearchTest'); + obj.set('field1', 'Parse GraphQL Server'); + obj.set('field2', 'It rocks!'); + await obj.save(); - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const result = await apolloClient.query({ - query: gql` - query FullTextSearchTests($where: FullTextSearchTestWhereInput) { - fullTextSearchTests(where: $where) { - edges { - node { - objectId + const result = await apolloClient.query({ + query: gql` + query FullTextSearchTests($where: FullTextSearchTestWhereInput) { + fullTextSearchTests(where: $where) { + edges { + node { + objectId + } } } } - } - `, - context: { - headers: { - 'X-Parse-Master-Key': 'test', + `, + context: { + headers: { + 'X-Parse-Master-Key': 'test', + }, }, - }, - variables: { - where: { - field1: { - text: { - search: { - term: 'graphql', + variables: { + where: { + field1: { + text: { + search: { + term: 'graphql', + }, }, }, }, }, - }, - }); + }); - expect(result.data.fullTextSearchTests.edges[0].node.objectId).toEqual(obj.id); - } catch (e) { - handleError(e); + expect(result.data.fullTextSearchTests.edges[0].node.objectId).toEqual(obj.id); + } catch (e) { + handleError(e); + } } - }); + ); it('should support in query key', async () => { try { @@ -5032,194 +5062,200 @@ describe('ParseGraphQLServer', () => { } }); - it_id('0fd03d3c-a2c8-4fac-95cc-2391a3032ca2')(it)('should support order, skip and first arguments', async () => { - const promises = []; - for (let i = 0; i < 100; i++) { - const obj = new Parse.Object('SomeClass'); - obj.set('someField', `someValue${i < 10 ? '0' : ''}${i}`); - obj.set('numberField', i % 3); - promises.push(obj.save()); - } - await Promise.all(promises); - - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - - const result = await apolloClient.query({ - query: gql` - query FindSomeObjects( - $where: SomeClassWhereInput - $order: [SomeClassOrder!] - $skip: Int - $first: Int - ) { - find: someClasses(where: $where, order: $order, skip: $skip, first: $first) { - edges { - node { - someField - } - } - } - } - `, - variables: { - where: { - someField: { - matchesRegex: '^someValue', - }, - }, - order: ['numberField_DESC', 'someField_ASC'], - skip: 4, - first: 2, - }, - }); - - expect(result.data.find.edges.map(obj => obj.node.someField)).toEqual([ - 'someValue14', - 'someValue17', - ]); - }); - - it_id('588a70c6-2932-4d3b-a838-a74c59d8cffb')(it)('should support pagination', async () => { - const numberArray = (first, last) => { - const array = []; - for (let i = first; i <= last; i++) { - array.push(i); + it_id('0fd03d3c-a2c8-4fac-95cc-2391a3032ca2')(it)( + 'should support order, skip and first arguments', + async () => { + const promises = []; + for (let i = 0; i < 100; i++) { + const obj = new Parse.Object('SomeClass'); + obj.set('someField', `someValue${i < 10 ? '0' : ''}${i}`); + obj.set('numberField', i % 3); + promises.push(obj.save()); } - return array; - }; - - const promises = []; - for (let i = 0; i < 100; i++) { - const obj = new Parse.Object('SomeClass'); - obj.set('numberField', i); - promises.push(obj.save()); - } - await Promise.all(promises); + await Promise.all(promises); - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const find = async ({ skip, after, first, before, last } = {}) => { - return await apolloClient.query({ + const result = await apolloClient.query({ query: gql` query FindSomeObjects( + $where: SomeClassWhereInput $order: [SomeClassOrder!] $skip: Int - $after: String $first: Int - $before: String - $last: Int ) { - someClasses( - order: $order - skip: $skip - after: $after - first: $first - before: $before - last: $last - ) { + find: someClasses(where: $where, order: $order, skip: $skip, first: $first) { edges { - cursor node { - numberField + someField } } - count - pageInfo { - hasPreviousPage - startCursor - endCursor - hasNextPage - } } } `, variables: { - order: ['numberField_ASC'], - skip, - after, - first, - before, - last, + where: { + someField: { + matchesRegex: '^someValue', + }, + }, + order: ['numberField_DESC', 'someField_ASC'], + skip: 4, + first: 2, }, }); - }; - let result = await find(); - expect(result.data.someClasses.edges.map(edge => edge.node.numberField)).toEqual( - numberArray(0, 99) - ); - expect(result.data.someClasses.count).toEqual(100); - expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual(false); - expect(result.data.someClasses.pageInfo.startCursor).toEqual( - result.data.someClasses.edges[0].cursor - ); - expect(result.data.someClasses.pageInfo.endCursor).toEqual( - result.data.someClasses.edges[99].cursor - ); - expect(result.data.someClasses.pageInfo.hasNextPage).toEqual(false); + expect(result.data.find.edges.map(obj => obj.node.someField)).toEqual([ + 'someValue14', + 'someValue17', + ]); + } + ); - result = await find({ first: 10 }); - expect(result.data.someClasses.edges.map(edge => edge.node.numberField)).toEqual( - numberArray(0, 9) - ); - expect(result.data.someClasses.count).toEqual(100); - expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual(false); - expect(result.data.someClasses.pageInfo.startCursor).toEqual( - result.data.someClasses.edges[0].cursor - ); - expect(result.data.someClasses.pageInfo.endCursor).toEqual( - result.data.someClasses.edges[9].cursor - ); - expect(result.data.someClasses.pageInfo.hasNextPage).toEqual(true); + it_id('588a70c6-2932-4d3b-a838-a74c59d8cffb')(it)( + 'should support pagination', + async () => { + const numberArray = (first, last) => { + const array = []; + for (let i = first; i <= last; i++) { + array.push(i); + } + return array; + }; + + const promises = []; + for (let i = 0; i < 100; i++) { + const obj = new Parse.Object('SomeClass'); + obj.set('numberField', i); + promises.push(obj.save()); + } + await Promise.all(promises); - result = await find({ - first: 10, - after: result.data.someClasses.pageInfo.endCursor, - }); - expect(result.data.someClasses.edges.map(edge => edge.node.numberField)).toEqual( - numberArray(10, 19) - ); - expect(result.data.someClasses.count).toEqual(100); - expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual(true); - expect(result.data.someClasses.pageInfo.startCursor).toEqual( - result.data.someClasses.edges[0].cursor - ); - expect(result.data.someClasses.pageInfo.endCursor).toEqual( - result.data.someClasses.edges[9].cursor - ); - expect(result.data.someClasses.pageInfo.hasNextPage).toEqual(true); + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - result = await find({ last: 10 }); - expect(result.data.someClasses.edges.map(edge => edge.node.numberField)).toEqual( - numberArray(90, 99) - ); - expect(result.data.someClasses.count).toEqual(100); - expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual(true); - expect(result.data.someClasses.pageInfo.startCursor).toEqual( - result.data.someClasses.edges[0].cursor - ); - expect(result.data.someClasses.pageInfo.endCursor).toEqual( - result.data.someClasses.edges[9].cursor - ); - expect(result.data.someClasses.pageInfo.hasNextPage).toEqual(false); + const find = async ({ skip, after, first, before, last } = {}) => { + return await apolloClient.query({ + query: gql` + query FindSomeObjects( + $order: [SomeClassOrder!] + $skip: Int + $after: String + $first: Int + $before: String + $last: Int + ) { + someClasses( + order: $order + skip: $skip + after: $after + first: $first + before: $before + last: $last + ) { + edges { + cursor + node { + numberField + } + } + count + pageInfo { + hasPreviousPage + startCursor + endCursor + hasNextPage + } + } + } + `, + variables: { + order: ['numberField_ASC'], + skip, + after, + first, + before, + last, + }, + }); + }; + + let result = await find(); + expect(result.data.someClasses.edges.map(edge => edge.node.numberField)).toEqual( + numberArray(0, 99) + ); + expect(result.data.someClasses.count).toEqual(100); + expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual(false); + expect(result.data.someClasses.pageInfo.startCursor).toEqual( + result.data.someClasses.edges[0].cursor + ); + expect(result.data.someClasses.pageInfo.endCursor).toEqual( + result.data.someClasses.edges[99].cursor + ); + expect(result.data.someClasses.pageInfo.hasNextPage).toEqual(false); + + result = await find({ first: 10 }); + expect(result.data.someClasses.edges.map(edge => edge.node.numberField)).toEqual( + numberArray(0, 9) + ); + expect(result.data.someClasses.count).toEqual(100); + expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual(false); + expect(result.data.someClasses.pageInfo.startCursor).toEqual( + result.data.someClasses.edges[0].cursor + ); + expect(result.data.someClasses.pageInfo.endCursor).toEqual( + result.data.someClasses.edges[9].cursor + ); + expect(result.data.someClasses.pageInfo.hasNextPage).toEqual(true); + + result = await find({ + first: 10, + after: result.data.someClasses.pageInfo.endCursor, + }); + expect(result.data.someClasses.edges.map(edge => edge.node.numberField)).toEqual( + numberArray(10, 19) + ); + expect(result.data.someClasses.count).toEqual(100); + expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual(true); + expect(result.data.someClasses.pageInfo.startCursor).toEqual( + result.data.someClasses.edges[0].cursor + ); + expect(result.data.someClasses.pageInfo.endCursor).toEqual( + result.data.someClasses.edges[9].cursor + ); + expect(result.data.someClasses.pageInfo.hasNextPage).toEqual(true); + + result = await find({ last: 10 }); + expect(result.data.someClasses.edges.map(edge => edge.node.numberField)).toEqual( + numberArray(90, 99) + ); + expect(result.data.someClasses.count).toEqual(100); + expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual(true); + expect(result.data.someClasses.pageInfo.startCursor).toEqual( + result.data.someClasses.edges[0].cursor + ); + expect(result.data.someClasses.pageInfo.endCursor).toEqual( + result.data.someClasses.edges[9].cursor + ); + expect(result.data.someClasses.pageInfo.hasNextPage).toEqual(false); - result = await find({ - last: 10, - before: result.data.someClasses.pageInfo.startCursor, - }); - expect(result.data.someClasses.edges.map(edge => edge.node.numberField)).toEqual( - numberArray(80, 89) - ); - expect(result.data.someClasses.count).toEqual(100); - expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual(true); - expect(result.data.someClasses.pageInfo.startCursor).toEqual( - result.data.someClasses.edges[0].cursor - ); - expect(result.data.someClasses.pageInfo.endCursor).toEqual( - result.data.someClasses.edges[9].cursor - ); - expect(result.data.someClasses.pageInfo.hasNextPage).toEqual(true); - }); + result = await find({ + last: 10, + before: result.data.someClasses.pageInfo.startCursor, + }); + expect(result.data.someClasses.edges.map(edge => edge.node.numberField)).toEqual( + numberArray(80, 89) + ); + expect(result.data.someClasses.count).toEqual(100); + expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual(true); + expect(result.data.someClasses.pageInfo.startCursor).toEqual( + result.data.someClasses.edges[0].cursor + ); + expect(result.data.someClasses.pageInfo.endCursor).toEqual( + result.data.someClasses.edges[9].cursor + ); + expect(result.data.someClasses.pageInfo.hasNextPage).toEqual(true); + } + ); it_id('4f6a5f20-9642-4cf0-b31d-e739672a9096')(it)('should support count', async () => { await prepareData(); @@ -5324,108 +5360,114 @@ describe('ParseGraphQLServer', () => { expect(result.data.find.count).toEqual(2); }); - it_id('942b57be-ca8a-4a5b-8104-2adef8743b1a')(it)('should respect max limit', async () => { - parseServer = await global.reconfigureServer({ - maxLimit: 10, - }); + it_id('942b57be-ca8a-4a5b-8104-2adef8743b1a')(it)( + 'should respect max limit', + async () => { + parseServer = await global.reconfigureServer({ + maxLimit: 10, + }); - const promises = []; - for (let i = 0; i < 100; i++) { - const obj = new Parse.Object('SomeClass'); - promises.push(obj.save()); - } - await Promise.all(promises); + const promises = []; + for (let i = 0; i < 100; i++) { + const obj = new Parse.Object('SomeClass'); + promises.push(obj.save()); + } + await Promise.all(promises); - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const result = await apolloClient.query({ - query: gql` - query FindSomeObjects($limit: Int) { - find: someClasses(where: { id: { exists: true } }, first: $limit) { - edges { - node { - id + const result = await apolloClient.query({ + query: gql` + query FindSomeObjects($limit: Int) { + find: someClasses(where: { id: { exists: true } }, first: $limit) { + edges { + node { + id + } } + count } - count } - } - `, - variables: { - limit: 50, - }, - context: { - headers: { - 'X-Parse-Master-Key': 'test', + `, + variables: { + limit: 50, }, - }, - }); + context: { + headers: { + 'X-Parse-Master-Key': 'test', + }, + }, + }); - expect(result.data.find.edges.length).toEqual(10); - expect(result.data.find.count).toEqual(100); - }); + expect(result.data.find.edges.length).toEqual(10); + expect(result.data.find.count).toEqual(100); + } + ); - it_id('952634f0-0ad5-4a08-8da2-187c1bd9ee94')(it)('should support keys argument', async () => { - await prepareData(); + it_id('952634f0-0ad5-4a08-8da2-187c1bd9ee94')(it)( + 'should support keys argument', + async () => { + await prepareData(); - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const result1 = await apolloClient.query({ - query: gql` - query FindSomeObject($where: GraphQLClassWhereInput) { - find: graphQLClasses(where: $where) { - edges { - node { - someField + const result1 = await apolloClient.query({ + query: gql` + query FindSomeObject($where: GraphQLClassWhereInput) { + find: graphQLClasses(where: $where) { + edges { + node { + someField + } } } } - } - `, - variables: { - where: { - id: { equalTo: object3.id }, + `, + variables: { + where: { + id: { equalTo: object3.id }, + }, }, - }, - context: { - headers: { - 'X-Parse-Session-Token': user1.getSessionToken(), + context: { + headers: { + 'X-Parse-Session-Token': user1.getSessionToken(), + }, }, - }, - }); + }); - const result2 = await apolloClient.query({ - query: gql` - query FindSomeObject($where: GraphQLClassWhereInput) { - find: graphQLClasses(where: $where) { - edges { - node { - someField - pointerToUser { - username + const result2 = await apolloClient.query({ + query: gql` + query FindSomeObject($where: GraphQLClassWhereInput) { + find: graphQLClasses(where: $where) { + edges { + node { + someField + pointerToUser { + username + } } } } } - } - `, - variables: { - where: { - id: { equalTo: object3.id }, + `, + variables: { + where: { + id: { equalTo: object3.id }, + }, }, - }, - context: { - headers: { - 'X-Parse-Session-Token': user1.getSessionToken(), + context: { + headers: { + 'X-Parse-Session-Token': user1.getSessionToken(), + }, }, - }, - }); + }); - expect(result1.data.find.edges[0].node.someField).toBeDefined(); - expect(result1.data.find.edges[0].node.pointerToUser).toBeUndefined(); - expect(result2.data.find.edges[0].node.someField).toBeDefined(); - expect(result2.data.find.edges[0].node.pointerToUser).toBeDefined(); - }); + expect(result1.data.find.edges[0].node.someField).toBeDefined(); + expect(result1.data.find.edges[0].node.pointerToUser).toBeUndefined(); + expect(result2.data.find.edges[0].node.someField).toBeDefined(); + expect(result2.data.find.edges[0].node.pointerToUser).toBeDefined(); + } + ); it('should support include argument', async () => { await prepareData(); @@ -5771,66 +5813,69 @@ describe('ParseGraphQLServer', () => { ).toEqual([object3.id, object1.id, object2.id]); }); - it_id('47a6adf3-1cb4-4d92-b74c-e480363f9cb5')(it)('should support including relation', async () => { - await prepareData(); + it_id('47a6adf3-1cb4-4d92-b74c-e480363f9cb5')(it)( + 'should support including relation', + async () => { + await prepareData(); - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const result1 = await apolloClient.query({ - query: gql` - query FindRoles { - roles { - edges { - node { - name + const result1 = await apolloClient.query({ + query: gql` + query FindRoles { + roles { + edges { + node { + name + } } } } - } - `, - variables: {}, - context: { - headers: { - 'X-Parse-Session-Token': user1.getSessionToken(), + `, + variables: {}, + context: { + headers: { + 'X-Parse-Session-Token': user1.getSessionToken(), + }, }, - }, - }); + }); - const result2 = await apolloClient.query({ - query: gql` - query FindRoles { - roles { - edges { - node { - name - users { - edges { - node { - username + const result2 = await apolloClient.query({ + query: gql` + query FindRoles { + roles { + edges { + node { + name + users { + edges { + node { + username + } } } } } } } - } - `, - variables: {}, - context: { - headers: { - 'X-Parse-Session-Token': user1.getSessionToken(), + `, + variables: {}, + context: { + headers: { + 'X-Parse-Session-Token': user1.getSessionToken(), + }, }, - }, - }); + }); - expect(result1.data.roles.edges[0].node.name).toBeDefined(); - expect(result1.data.roles.edges[0].node.users).toBeUndefined(); - expect(result1.data.roles.edges[0].node.roles).toBeUndefined(); - expect(result2.data.roles.edges[0].node.name).toBeDefined(); - expect(result2.data.roles.edges[0].node.users).toBeDefined(); - expect(result2.data.roles.edges[0].node.users.edges[0].node.username).toBeDefined(); - expect(result2.data.roles.edges[0].node.roles).toBeUndefined(); - }); + expect(result1.data.roles.edges[0].node.name).toBeDefined(); + expect(result1.data.roles.edges[0].node.users).toBeUndefined(); + expect(result1.data.roles.edges[0].node.roles).toBeUndefined(); + expect(result2.data.roles.edges[0].node.name).toBeDefined(); + expect(result2.data.roles.edges[0].node.users).toBeDefined(); + expect(result2.data.roles.edges[0].node.users.edges[0].node.username).toBeDefined(); + expect(result2.data.roles.edges[0].node.roles).toBeUndefined(); + } + ); }); }); @@ -6676,161 +6721,164 @@ describe('ParseGraphQLServer', () => { }); }); - it_id('f722e98e-1fd7-45c5-ade3-5177e3d542e8')(it)('should unset fields when null used on update/create', async () => { - const customerSchema = new Parse.Schema('Customer'); - customerSchema.addString('aString'); - customerSchema.addBoolean('aBoolean'); - customerSchema.addDate('aDate'); - customerSchema.addArray('aArray'); - customerSchema.addGeoPoint('aGeoPoint'); - customerSchema.addPointer('aPointer', 'Customer'); - customerSchema.addObject('aObject'); - customerSchema.addPolygon('aPolygon'); - await customerSchema.save(); + it_id('f722e98e-1fd7-45c5-ade3-5177e3d542e8')(it)( + 'should unset fields when null used on update/create', + async () => { + const customerSchema = new Parse.Schema('Customer'); + customerSchema.addString('aString'); + customerSchema.addBoolean('aBoolean'); + customerSchema.addDate('aDate'); + customerSchema.addArray('aArray'); + customerSchema.addGeoPoint('aGeoPoint'); + customerSchema.addPointer('aPointer', 'Customer'); + customerSchema.addObject('aObject'); + customerSchema.addPolygon('aPolygon'); + await customerSchema.save(); - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const cus = new Parse.Object('Customer'); - await cus.save({ aString: 'hello' }); - - const fields = { - aString: "i'm string", - aBoolean: true, - aDate: new Date().toISOString(), - aArray: ['hello', 1], - aGeoPoint: { latitude: 30, longitude: 30 }, - aPointer: { link: cus.id }, - aObject: { prop: { subprop: 1 }, prop2: 'test' }, - aPolygon: [ - { latitude: 30, longitude: 30 }, - { latitude: 31, longitude: 31 }, - { latitude: 32, longitude: 32 }, - { latitude: 30, longitude: 30 }, - ], - }; - const nullFields = Object.keys(fields).reduce((acc, k) => ({ ...acc, [k]: null }), {}); - const result = await apolloClient.mutate({ - mutation: gql` - mutation CreateCustomer($input: CreateCustomerInput!) { - createCustomer(input: $input) { - customer { - id - aString - aBoolean - aDate - aArray { - ... on Element { - value + const cus = new Parse.Object('Customer'); + await cus.save({ aString: 'hello' }); + + const fields = { + aString: "i'm string", + aBoolean: true, + aDate: new Date().toISOString(), + aArray: ['hello', 1], + aGeoPoint: { latitude: 30, longitude: 30 }, + aPointer: { link: cus.id }, + aObject: { prop: { subprop: 1 }, prop2: 'test' }, + aPolygon: [ + { latitude: 30, longitude: 30 }, + { latitude: 31, longitude: 31 }, + { latitude: 32, longitude: 32 }, + { latitude: 30, longitude: 30 }, + ], + }; + const nullFields = Object.keys(fields).reduce((acc, k) => ({ ...acc, [k]: null }), {}); + const result = await apolloClient.mutate({ + mutation: gql` + mutation CreateCustomer($input: CreateCustomerInput!) { + createCustomer(input: $input) { + customer { + id + aString + aBoolean + aDate + aArray { + ... on Element { + value + } } - } - aGeoPoint { - longitude - latitude - } - aPointer { - objectId - } - aObject - aPolygon { - longitude - latitude - } - } - } - } - `, - variables: { - input: { fields }, - }, - }); - const { - data: { - createCustomer: { - customer: { aPointer, aArray, id, ...otherFields }, - }, - }, - } = result; - expect(id).toBeDefined(); - delete otherFields.__typename; - delete otherFields.aGeoPoint.__typename; - otherFields.aPolygon.forEach(v => { - delete v.__typename; - }); - expect({ - ...otherFields, - aPointer: { link: aPointer.objectId }, - aArray: aArray.map(({ value }) => value), - }).toEqual(fields); - - const updated = await apolloClient.mutate({ - mutation: gql` - mutation UpdateCustomer($input: UpdateCustomerInput!) { - updateCustomer(input: $input) { - customer { - aString - aBoolean - aDate - aArray { - ... on Element { - value + aGeoPoint { + longitude + latitude + } + aPointer { + objectId + } + aObject + aPolygon { + longitude + latitude } - } - aGeoPoint { - longitude - latitude - } - aPointer { - objectId - } - aObject - aPolygon { - longitude - latitude } } } - } - `, - variables: { - input: { fields: nullFields, id }, - }, - }); - const { - data: { - updateCustomer: { customer }, - }, - } = updated; - delete customer.__typename; - expect(Object.keys(customer).length).toEqual(8); - Object.keys(customer).forEach(k => { - expect(customer[k]).toBeNull(); - }); - try { - const queryResult = await apolloClient.query({ - query: gql` - query getEmptyCustomer($where: CustomerWhereInput!) { - customers(where: $where) { - edges { - node { - id + `, + variables: { + input: { fields }, + }, + }); + const { + data: { + createCustomer: { + customer: { aPointer, aArray, id, ...otherFields }, + }, + }, + } = result; + expect(id).toBeDefined(); + delete otherFields.__typename; + delete otherFields.aGeoPoint.__typename; + otherFields.aPolygon.forEach(v => { + delete v.__typename; + }); + expect({ + ...otherFields, + aPointer: { link: aPointer.objectId }, + aArray: aArray.map(({ value }) => value), + }).toEqual(fields); + + const updated = await apolloClient.mutate({ + mutation: gql` + mutation UpdateCustomer($input: UpdateCustomerInput!) { + updateCustomer(input: $input) { + customer { + aString + aBoolean + aDate + aArray { + ... on Element { + value + } + } + aGeoPoint { + longitude + latitude + } + aPointer { + objectId + } + aObject + aPolygon { + longitude + latitude } } } } `, variables: { - where: Object.keys(fields).reduce( - (acc, k) => ({ ...acc, [k]: { exists: false } }), - {} - ), + input: { fields: nullFields, id }, + }, + }); + const { + data: { + updateCustomer: { customer }, }, + } = updated; + delete customer.__typename; + expect(Object.keys(customer).length).toEqual(8); + Object.keys(customer).forEach(k => { + expect(customer[k]).toBeNull(); }); + try { + const queryResult = await apolloClient.query({ + query: gql` + query getEmptyCustomer($where: CustomerWhereInput!) { + customers(where: $where) { + edges { + node { + id + } + } + } + } + `, + variables: { + where: Object.keys(fields).reduce( + (acc, k) => ({ ...acc, [k]: { exists: false } }), + {} + ), + }, + }); - expect(queryResult.data.customers.edges.length).toEqual(1); - } catch (e) { - console.error(JSON.stringify(e)); + expect(queryResult.data.customers.edges.length).toEqual(1); + } catch (e) { + console.error(JSON.stringify(e)); + } } - }); + ); }); describe('Files Mutations', () => { @@ -9074,232 +9122,235 @@ describe('ParseGraphQLServer', () => { expect(result2.companies.edges[0].node.objectId).toEqual(company1.id); }); - it_id('f4312f2c-90bb-4583-b033-02078ae0ce84')(it)('should support relational where query', async () => { - const president = new Parse.Object('President'); - president.set('name', 'James'); - await president.save(); + it_id('f4312f2c-90bb-4583-b033-02078ae0ce84')(it)( + 'should support relational where query', + async () => { + const president = new Parse.Object('President'); + president.set('name', 'James'); + await president.save(); - const employee = new Parse.Object('Employee'); - employee.set('name', 'John'); - await employee.save(); + const employee = new Parse.Object('Employee'); + employee.set('name', 'John'); + await employee.save(); - const company1 = new Parse.Object('Company'); - company1.set('name', 'imACompany1'); - await company1.save(); + const company1 = new Parse.Object('Company'); + company1.set('name', 'imACompany1'); + await company1.save(); - const company2 = new Parse.Object('Company'); - company2.set('name', 'imACompany2'); - company2.relation('employees').add([employee]); - await company2.save(); + const company2 = new Parse.Object('Company'); + company2.set('name', 'imACompany2'); + company2.relation('employees').add([employee]); + await company2.save(); - const country = new Parse.Object('Country'); - country.set('name', 'imACountry'); - country.relation('companies').add([company1, company2]); - await country.save(); + const country = new Parse.Object('Country'); + country.set('name', 'imACountry'); + country.relation('companies').add([company1, company2]); + await country.save(); - const country2 = new Parse.Object('Country'); - country2.set('name', 'imACountry2'); - country2.relation('companies').add([company1]); - await country2.save(); + const country2 = new Parse.Object('Country'); + country2.set('name', 'imACountry2'); + country2.relation('companies').add([company1]); + await country2.save(); - const country3 = new Parse.Object('Country'); - country3.set('name', 'imACountry3'); - country3.set('president', president); - await country3.save(); + const country3 = new Parse.Object('Country'); + country3.set('name', 'imACountry3'); + country3.set('president', president); + await country3.save(); - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - let { - data: { - countries: { edges: result }, - }, - } = await apolloClient.query({ - query: gql` - query findCountry($where: CountryWhereInput) { - countries(where: $where) { - edges { - node { - id - objectId - companies { - edges { - node { - id - objectId - name + let { + data: { + countries: { edges: result }, + }, + } = await apolloClient.query({ + query: gql` + query findCountry($where: CountryWhereInput) { + countries(where: $where) { + edges { + node { + id + objectId + companies { + edges { + node { + id + objectId + name + } } } } } } } - } - `, - variables: { - where: { - companies: { - have: { - employees: { have: { name: { equalTo: 'John' } } }, + `, + variables: { + where: { + companies: { + have: { + employees: { have: { name: { equalTo: 'John' } } }, + }, }, }, }, - }, - }); - expect(result.length).toEqual(1); - result = result[0].node; - expect(result.objectId).toEqual(country.id); - expect(result.companies.edges.length).toEqual(2); + }); + expect(result.length).toEqual(1); + result = result[0].node; + expect(result.objectId).toEqual(country.id); + expect(result.companies.edges.length).toEqual(2); - const { - data: { - countries: { edges: result2 }, - }, - } = await apolloClient.query({ - query: gql` - query findCountry($where: CountryWhereInput) { - countries(where: $where) { - edges { - node { - id - objectId - companies { - edges { - node { - id - objectId - name + const { + data: { + countries: { edges: result2 }, + }, + } = await apolloClient.query({ + query: gql` + query findCountry($where: CountryWhereInput) { + countries(where: $where) { + edges { + node { + id + objectId + companies { + edges { + node { + id + objectId + name + } } } } } } } - } - `, - variables: { - where: { - companies: { - have: { - OR: [ - { name: { equalTo: 'imACompany1' } }, - { name: { equalTo: 'imACompany2' } }, - ], + `, + variables: { + where: { + companies: { + have: { + OR: [ + { name: { equalTo: 'imACompany1' } }, + { name: { equalTo: 'imACompany2' } }, + ], + }, }, }, }, - }, - }); - expect(result2.length).toEqual(2); + }); + expect(result2.length).toEqual(2); - const { - data: { - countries: { edges: result3 }, - }, - } = await apolloClient.query({ - query: gql` - query findCountry($where: CountryWhereInput) { - countries(where: $where) { - edges { - node { - id - name + const { + data: { + countries: { edges: result3 }, + }, + } = await apolloClient.query({ + query: gql` + query findCountry($where: CountryWhereInput) { + countries(where: $where) { + edges { + node { + id + name + } } } } - } - `, - variables: { - where: { - companies: { exists: false }, + `, + variables: { + where: { + companies: { exists: false }, + }, }, - }, - }); - expect(result3.length).toEqual(1); - expect(result3[0].node.name).toEqual('imACountry3'); + }); + expect(result3.length).toEqual(1); + expect(result3[0].node.name).toEqual('imACountry3'); - const { - data: { - countries: { edges: result4 }, - }, - } = await apolloClient.query({ - query: gql` - query findCountry($where: CountryWhereInput) { - countries(where: $where) { - edges { - node { - id - name + const { + data: { + countries: { edges: result4 }, + }, + } = await apolloClient.query({ + query: gql` + query findCountry($where: CountryWhereInput) { + countries(where: $where) { + edges { + node { + id + name + } } } } - } - `, - variables: { - where: { - president: { exists: false }, + `, + variables: { + where: { + president: { exists: false }, + }, }, - }, - }); - expect(result4.length).toEqual(2); - const { - data: { - countries: { edges: result5 }, - }, - } = await apolloClient.query({ - query: gql` - query findCountry($where: CountryWhereInput) { - countries(where: $where) { - edges { - node { - id - name + }); + expect(result4.length).toEqual(2); + const { + data: { + countries: { edges: result5 }, + }, + } = await apolloClient.query({ + query: gql` + query findCountry($where: CountryWhereInput) { + countries(where: $where) { + edges { + node { + id + name + } } } } - } - `, - variables: { - where: { - president: { exists: true }, + `, + variables: { + where: { + president: { exists: true }, + }, }, - }, - }); - expect(result5.length).toEqual(1); - const { - data: { - countries: { edges: result6 }, - }, - } = await apolloClient.query({ - query: gql` - query findCountry($where: CountryWhereInput) { - countries(where: $where) { - edges { - node { - id - objectId - name + }); + expect(result5.length).toEqual(1); + const { + data: { + countries: { edges: result6 }, + }, + } = await apolloClient.query({ + query: gql` + query findCountry($where: CountryWhereInput) { + countries(where: $where) { + edges { + node { + id + objectId + name + } } } } - } - `, - variables: { - where: { - companies: { - haveNot: { - OR: [ - { name: { equalTo: 'imACompany1' } }, - { name: { equalTo: 'imACompany2' } }, - ], + `, + variables: { + where: { + companies: { + haveNot: { + OR: [ + { name: { equalTo: 'imACompany1' } }, + { name: { equalTo: 'imACompany2' } }, + ], + }, }, }, }, - }, - }); - expect(result6.length).toEqual(1); - expect(result6.length).toEqual(1); - expect(result6[0].node.name).toEqual('imACountry3'); - }); + }); + expect(result6.length).toEqual(1); + expect(result6.length).toEqual(1); + expect(result6[0].node.name).toEqual('imACountry3'); + } + ); it('should support files', async () => { try { diff --git a/spec/ParseHooks.spec.js b/spec/ParseHooks.spec.js index 8d0d0f9cdc..43d287bf6a 100644 --- a/spec/ParseHooks.spec.js +++ b/spec/ParseHooks.spec.js @@ -187,76 +187,82 @@ describe('Hooks', () => { }); }); - it_id('f7ad092f-81dc-4729-afd1-3b02db2f0948')(it)('should fail trying to create two times the same function', done => { - Parse.Hooks.createFunction('my_new_function', 'http://url.com') - .then(() => jasmine.timeout()) - .then( - () => { - return Parse.Hooks.createFunction('my_new_function', 'http://url.com'); - }, - () => { - fail('should create a new function'); - } - ) - .then( - () => { - fail('should not be able to create the same function'); - }, - err => { - expect(err).not.toBe(undefined); - expect(err).not.toBe(null); - if (err) { - expect(err.code).toBe(143); - expect(err.message).toBe('function name: my_new_function already exists'); + it_id('f7ad092f-81dc-4729-afd1-3b02db2f0948')(it)( + 'should fail trying to create two times the same function', + done => { + Parse.Hooks.createFunction('my_new_function', 'http://url.com') + .then(() => jasmine.timeout()) + .then( + () => { + return Parse.Hooks.createFunction('my_new_function', 'http://url.com'); + }, + () => { + fail('should create a new function'); } - return Parse.Hooks.removeFunction('my_new_function'); - } - ) - .then( - () => { - done(); - }, - err => { - jfail(err); - done(); - } - ); - }); + ) + .then( + () => { + fail('should not be able to create the same function'); + }, + err => { + expect(err).not.toBe(undefined); + expect(err).not.toBe(null); + if (err) { + expect(err.code).toBe(143); + expect(err.message).toBe('function name: my_new_function already exists'); + } + return Parse.Hooks.removeFunction('my_new_function'); + } + ) + .then( + () => { + done(); + }, + err => { + jfail(err); + done(); + } + ); + } + ); - it_id('4db8c249-9174-4e8e-b959-55c8ea959a02')(it)('should fail trying to create two times the same trigger', done => { - Parse.Hooks.createTrigger('MyClass', 'beforeSave', 'http://url.com') - .then( - () => { - return Parse.Hooks.createTrigger('MyClass', 'beforeSave', 'http://url.com'); - }, - () => { - fail('should create a new trigger'); - } - ) - .then( - () => { - fail('should not be able to create the same trigger'); - }, - err => { - expect(err).not.toBe(undefined); - expect(err).not.toBe(null); - if (err) { - expect(err.code).toBe(143); - expect(err.message).toBe('class MyClass already has trigger beforeSave'); + it_id('4db8c249-9174-4e8e-b959-55c8ea959a02')(it)( + 'should fail trying to create two times the same trigger', + done => { + Parse.Hooks.createTrigger('MyClass', 'beforeSave', 'http://url.com') + .then( + () => { + return Parse.Hooks.createTrigger('MyClass', 'beforeSave', 'http://url.com'); + }, + () => { + fail('should create a new trigger'); } - return Parse.Hooks.removeTrigger('MyClass', 'beforeSave'); - } - ) - .then( - () => { - done(); - }, - err => { - jfail(err); - done(); - } - ); - }); + ) + .then( + () => { + fail('should not be able to create the same trigger'); + }, + err => { + expect(err).not.toBe(undefined); + expect(err).not.toBe(null); + if (err) { + expect(err.code).toBe(143); + expect(err.message).toBe('class MyClass already has trigger beforeSave'); + } + return Parse.Hooks.removeTrigger('MyClass', 'beforeSave'); + } + ) + .then( + () => { + done(); + }, + err => { + jfail(err); + done(); + } + ); + } + ); it("should fail trying to update a function that don't exist", done => { Parse.Hooks.updateFunction('A_COOL_FUNCTION', 'http://url.com') @@ -358,164 +364,102 @@ describe('Hooks', () => { }); }); - it_id('96d99414-b739-4e36-b3f4-8135e0be83ea')(it)('should create hooks and properly preload them', done => { - const promises = []; - for (let i = 0; i < 5; i++) { - promises.push( - Parse.Hooks.createTrigger('MyClass' + i, 'beforeSave', 'http://url.com/beforeSave/' + i) - ); - promises.push(Parse.Hooks.createFunction('AFunction' + i, 'http://url.com/function' + i)); - } + it_id('96d99414-b739-4e36-b3f4-8135e0be83ea')(it)( + 'should create hooks and properly preload them', + done => { + const promises = []; + for (let i = 0; i < 5; i++) { + promises.push( + Parse.Hooks.createTrigger('MyClass' + i, 'beforeSave', 'http://url.com/beforeSave/' + i) + ); + promises.push(Parse.Hooks.createFunction('AFunction' + i, 'http://url.com/function' + i)); + } - Promise.all(promises) - .then( - function () { - for (let i = 0; i < 5; i++) { - // Delete everything from memory, as the server just started - triggers.removeTrigger('beforeSave', 'MyClass' + i, Parse.applicationId); - triggers.removeFunction('AFunction' + i, Parse.applicationId); - expect( - triggers.getTrigger('MyClass' + i, 'beforeSave', Parse.applicationId) - ).toBeUndefined(); - expect(triggers.getFunction('AFunction' + i, Parse.applicationId)).toBeUndefined(); + Promise.all(promises) + .then( + function () { + for (let i = 0; i < 5; i++) { + // Delete everything from memory, as the server just started + triggers.removeTrigger('beforeSave', 'MyClass' + i, Parse.applicationId); + triggers.removeFunction('AFunction' + i, Parse.applicationId); + expect( + triggers.getTrigger('MyClass' + i, 'beforeSave', Parse.applicationId) + ).toBeUndefined(); + expect(triggers.getFunction('AFunction' + i, Parse.applicationId)).toBeUndefined(); + } + const hooksController = new HooksController( + Parse.applicationId, + Config.get('test').database + ); + return hooksController.load(); + }, + err => { + jfail(err); + fail('Should properly create all hooks'); + done(); } - const hooksController = new HooksController( - Parse.applicationId, - Config.get('test').database - ); - return hooksController.load(); - }, - err => { - jfail(err); - fail('Should properly create all hooks'); - done(); - } - ) - .then( - function () { - for (let i = 0; i < 5; i++) { - expect( - triggers.getTrigger('MyClass' + i, 'beforeSave', Parse.applicationId) - ).not.toBeUndefined(); - expect(triggers.getFunction('AFunction' + i, Parse.applicationId)).not.toBeUndefined(); + ) + .then( + function () { + for (let i = 0; i < 5; i++) { + expect( + triggers.getTrigger('MyClass' + i, 'beforeSave', Parse.applicationId) + ).not.toBeUndefined(); + expect( + triggers.getFunction('AFunction' + i, Parse.applicationId) + ).not.toBeUndefined(); + } + done(); + }, + err => { + jfail(err); + fail('should properly load all hooks'); + done(); } - done(); - }, - err => { - jfail(err); - fail('should properly load all hooks'); - done(); - } - ); - }); - - it_id('fe7d41eb-e570-4804-ac1f-8b6c407fdafe')(it)('should run the function on the test server', done => { - app.post('/SomeFunction', function (req, res) { - res.json({ success: 'OK!' }); - }); + ); + } + ); - Parse.Hooks.createFunction('SOME_TEST_FUNCTION', hookServerURL + '/SomeFunction') - .then( - function () { - return Parse.Cloud.run('SOME_TEST_FUNCTION'); - }, - err => { - jfail(err); - fail('Should not fail creating a function'); - done(); - } - ) - .then( - function (res) { - expect(res).toBe('OK!'); - done(); - }, - err => { - jfail(err); - fail('Should not fail calling a function'); - done(); - } - ); - }); + it_id('fe7d41eb-e570-4804-ac1f-8b6c407fdafe')(it)( + 'should run the function on the test server', + done => { + app.post('/SomeFunction', function (req, res) { + res.json({ success: 'OK!' }); + }); - it_id('63985b4c-a212-4a86-aa0e-eb4600bb485b')(it)('should run the function on the test server (error handling)', done => { - app.post('/SomeFunctionError', function (req, res) { - res.json({ error: { code: 1337, error: 'hacking that one!' } }); - }); - // The function is deleted as the DB is dropped between calls - Parse.Hooks.createFunction('SOME_TEST_FUNCTION', hookServerURL + '/SomeFunctionError') - .then( - function () { - return Parse.Cloud.run('SOME_TEST_FUNCTION'); - }, - err => { - jfail(err); - fail('Should not fail creating a function'); - done(); - } - ) - .then( - function () { - fail('Should not succeed calling that function'); - done(); - }, - err => { - expect(err).not.toBe(undefined); - expect(err).not.toBe(null); - if (err) { - expect(err.code).toBe(Parse.Error.SCRIPT_FAILED); - expect(err.message.code).toEqual(1337); - expect(err.message.error).toEqual('hacking that one!'); + Parse.Hooks.createFunction('SOME_TEST_FUNCTION', hookServerURL + '/SomeFunction') + .then( + function () { + return Parse.Cloud.run('SOME_TEST_FUNCTION'); + }, + err => { + jfail(err); + fail('Should not fail creating a function'); + done(); } - done(); - } - ); - }); - - it_id('bacc1754-2a3a-4a7a-8d0e-f80af36da1ef')(it)('should provide X-Parse-Webhook-Key when defined', done => { - app.post('/ExpectingKey', function (req, res) { - if (req.get('X-Parse-Webhook-Key') === 'hook') { - res.json({ success: 'correct key provided' }); - } else { - res.json({ error: 'incorrect key provided' }); - } - }); - - Parse.Hooks.createFunction('SOME_TEST_FUNCTION', hookServerURL + '/ExpectingKey') - .then( - function () { - return Parse.Cloud.run('SOME_TEST_FUNCTION'); - }, - err => { - jfail(err); - fail('Should not fail creating a function'); - done(); - } - ) - .then( - function (res) { - expect(res).toBe('correct key provided'); - done(); - }, - err => { - jfail(err); - fail('Should not fail calling a function'); - done(); - } - ); - }); + ) + .then( + function (res) { + expect(res).toBe('OK!'); + done(); + }, + err => { + jfail(err); + fail('Should not fail calling a function'); + done(); + } + ); + } + ); - it_id('eeb67946-42c6-4581-89af-2abb4927913e')(it)('should not pass X-Parse-Webhook-Key if not provided', done => { - reconfigureServer({ webhookKey: undefined }).then(() => { - app.post('/ExpectingKeyAlso', function (req, res) { - if (req.get('X-Parse-Webhook-Key') === 'hook') { - res.json({ success: 'correct key provided' }); - } else { - res.json({ error: 'incorrect key provided' }); - } + it_id('63985b4c-a212-4a86-aa0e-eb4600bb485b')(it)( + 'should run the function on the test server (error handling)', + done => { + app.post('/SomeFunctionError', function (req, res) { + res.json({ error: { code: 1337, error: 'hacking that one!' } }); }); - - Parse.Hooks.createFunction('SOME_TEST_FUNCTION', hookServerURL + '/ExpectingKeyAlso') + // The function is deleted as the DB is dropped between calls + Parse.Hooks.createFunction('SOME_TEST_FUNCTION', hookServerURL + '/SomeFunctionError') .then( function () { return Parse.Cloud.run('SOME_TEST_FUNCTION'); @@ -536,105 +480,197 @@ describe('Hooks', () => { expect(err).not.toBe(null); if (err) { expect(err.code).toBe(Parse.Error.SCRIPT_FAILED); - expect(err.message).toEqual('incorrect key provided'); + expect(err.message.code).toEqual(1337); + expect(err.message.error).toEqual('hacking that one!'); } done(); } ); - }); - }); + } + ); - it_id('21decb65-4b93-4791-85a3-ab124a9ea3ac')(it)('should run the beforeSave hook on the test server', done => { - let triggerCount = 0; - app.post('/BeforeSaveSome', function (req, res) { - triggerCount++; - const object = req.body.object; - object.hello = 'world'; - // Would need parse cloud express to set much more - // But this should override the key upon return - res.json({ success: object }); - }); - // The function is deleted as the DB is dropped between calls - Parse.Hooks.createTrigger('SomeRandomObject', 'beforeSave', hookServerURL + '/BeforeSaveSome') - .then(function () { - const obj = new Parse.Object('SomeRandomObject'); - return obj.save(); - }) - .then(function (res) { - expect(triggerCount).toBe(1); - return res.fetch(); - }) - .then(function (res) { - expect(res.get('hello')).toEqual('world'); - done(); - }) - .catch(err => { - jfail(err); - fail('Should not fail creating a function'); - done(); + it_id('bacc1754-2a3a-4a7a-8d0e-f80af36da1ef')(it)( + 'should provide X-Parse-Webhook-Key when defined', + done => { + app.post('/ExpectingKey', function (req, res) { + if (req.get('X-Parse-Webhook-Key') === 'hook') { + res.json({ success: 'correct key provided' }); + } else { + res.json({ error: 'incorrect key provided' }); + } }); - }); - it_id('52e3152b-5514-4418-9e76-1f394368b8fb')(it)('beforeSave hooks should correctly handle responses containing entire object', done => { - app.post('/BeforeSaveSome2', function (req, res) { - const object = Parse.Object.fromJSON(req.body.object); - object.set('hello', 'world'); - res.json({ success: object }); - }); - Parse.Hooks.createTrigger('SomeRandomObject2', 'beforeSave', hookServerURL + '/BeforeSaveSome2') - .then(function () { - const obj = new Parse.Object('SomeRandomObject2'); - return obj.save(); - }) - .then(function (res) { - return res.save(); - }) - .then(function (res) { - expect(res.get('hello')).toEqual('world'); - done(); - }) - .catch(err => { - fail(`Should not fail: ${JSON.stringify(err)}`); - done(); - }); - }); + Parse.Hooks.createFunction('SOME_TEST_FUNCTION', hookServerURL + '/ExpectingKey') + .then( + function () { + return Parse.Cloud.run('SOME_TEST_FUNCTION'); + }, + err => { + jfail(err); + fail('Should not fail creating a function'); + done(); + } + ) + .then( + function (res) { + expect(res).toBe('correct key provided'); + done(); + }, + err => { + jfail(err); + fail('Should not fail calling a function'); + done(); + } + ); + } + ); + + it_id('eeb67946-42c6-4581-89af-2abb4927913e')(it)( + 'should not pass X-Parse-Webhook-Key if not provided', + done => { + reconfigureServer({ webhookKey: undefined }).then(() => { + app.post('/ExpectingKeyAlso', function (req, res) { + if (req.get('X-Parse-Webhook-Key') === 'hook') { + res.json({ success: 'correct key provided' }); + } else { + res.json({ error: 'incorrect key provided' }); + } + }); - it_id('d27a7587-abb5-40d5-9805-051ee91de474')(it)('should run the afterSave hook on the test server', done => { - let triggerCount = 0; - let newObjectId; - app.post('/AfterSaveSome', function (req, res) { - triggerCount++; - const obj = new Parse.Object('AnotherObject'); - obj.set('foo', 'bar'); - obj.save().then(function (obj) { - newObjectId = obj.id; - res.json({ success: {} }); + Parse.Hooks.createFunction('SOME_TEST_FUNCTION', hookServerURL + '/ExpectingKeyAlso') + .then( + function () { + return Parse.Cloud.run('SOME_TEST_FUNCTION'); + }, + err => { + jfail(err); + fail('Should not fail creating a function'); + done(); + } + ) + .then( + function () { + fail('Should not succeed calling that function'); + done(); + }, + err => { + expect(err).not.toBe(undefined); + expect(err).not.toBe(null); + if (err) { + expect(err.code).toBe(Parse.Error.SCRIPT_FAILED); + expect(err.message).toEqual('incorrect key provided'); + } + done(); + } + ); }); - }); - // The function is deleted as the DB is dropped between calls - Parse.Hooks.createTrigger('SomeRandomObject', 'afterSave', hookServerURL + '/AfterSaveSome') - .then(function () { - const obj = new Parse.Object('SomeRandomObject'); - return obj.save(); - }) - .then(function () { - return new Promise(resolve => { - setTimeout(() => { - expect(triggerCount).toBe(1); - new Parse.Query('AnotherObject').get(newObjectId).then(r => resolve(r)); - }, 500); + } + ); + + it_id('21decb65-4b93-4791-85a3-ab124a9ea3ac')(it)( + 'should run the beforeSave hook on the test server', + done => { + let triggerCount = 0; + app.post('/BeforeSaveSome', function (req, res) { + triggerCount++; + const object = req.body.object; + object.hello = 'world'; + // Would need parse cloud express to set much more + // But this should override the key upon return + res.json({ success: object }); + }); + // The function is deleted as the DB is dropped between calls + Parse.Hooks.createTrigger('SomeRandomObject', 'beforeSave', hookServerURL + '/BeforeSaveSome') + .then(function () { + const obj = new Parse.Object('SomeRandomObject'); + return obj.save(); + }) + .then(function (res) { + expect(triggerCount).toBe(1); + return res.fetch(); + }) + .then(function (res) { + expect(res.get('hello')).toEqual('world'); + done(); + }) + .catch(err => { + jfail(err); + fail('Should not fail creating a function'); + done(); }); - }) - .then(function (res) { - expect(res.get('foo')).toEqual('bar'); - done(); - }) - .catch(err => { - jfail(err); - fail('Should not fail creating a function'); - done(); + } + ); + + it_id('52e3152b-5514-4418-9e76-1f394368b8fb')(it)( + 'beforeSave hooks should correctly handle responses containing entire object', + done => { + app.post('/BeforeSaveSome2', function (req, res) { + const object = Parse.Object.fromJSON(req.body.object); + object.set('hello', 'world'); + res.json({ success: object }); }); - }); + Parse.Hooks.createTrigger( + 'SomeRandomObject2', + 'beforeSave', + hookServerURL + '/BeforeSaveSome2' + ) + .then(function () { + const obj = new Parse.Object('SomeRandomObject2'); + return obj.save(); + }) + .then(function (res) { + return res.save(); + }) + .then(function (res) { + expect(res.get('hello')).toEqual('world'); + done(); + }) + .catch(err => { + fail(`Should not fail: ${JSON.stringify(err)}`); + done(); + }); + } + ); + + it_id('d27a7587-abb5-40d5-9805-051ee91de474')(it)( + 'should run the afterSave hook on the test server', + done => { + let triggerCount = 0; + let newObjectId; + app.post('/AfterSaveSome', function (req, res) { + triggerCount++; + const obj = new Parse.Object('AnotherObject'); + obj.set('foo', 'bar'); + obj.save().then(function (obj) { + newObjectId = obj.id; + res.json({ success: {} }); + }); + }); + // The function is deleted as the DB is dropped between calls + Parse.Hooks.createTrigger('SomeRandomObject', 'afterSave', hookServerURL + '/AfterSaveSome') + .then(function () { + const obj = new Parse.Object('SomeRandomObject'); + return obj.save(); + }) + .then(function () { + return new Promise(resolve => { + setTimeout(() => { + expect(triggerCount).toBe(1); + new Parse.Query('AnotherObject').get(newObjectId).then(r => resolve(r)); + }, 500); + }); + }) + .then(function (res) { + expect(res.get('foo')).toEqual('bar'); + done(); + }) + .catch(err => { + jfail(err); + fail('Should not fail creating a function'); + done(); + }); + } + ); }); describe('triggers', () => { diff --git a/spec/ParseInstallation.spec.js b/spec/ParseInstallation.spec.js index c03a727b4a..8dd91c5a05 100644 --- a/spec/ParseInstallation.spec.js +++ b/spec/ParseInstallation.spec.js @@ -856,58 +856,61 @@ describe('Installations', () => { }); }); - it_id('22311bc7-3f4f-42c1-a958-57083929e80d')(it)('update is linking two existing objects w/ increment', done => { - const installId = '12345678-abcd-abcd-abcd-123456789abc'; - const t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; - let input = { - installationId: installId, - deviceType: 'ios', - }; - rest - .create(config, auth.nobody(config), '_Installation', input) - .then(() => { - input = { - deviceToken: t, - deviceType: 'ios', - }; - return rest.create(config, auth.nobody(config), '_Installation', input); - }) - .then(() => - database.adapter.find('_Installation', installationSchema, { deviceToken: t }, {}) - ) - .then(results => { - expect(results.length).toEqual(1); - input = { - deviceToken: t, - installationId: installId, - deviceType: 'ios', - score: { - __op: 'Increment', - amount: 1, - }, - }; - return rest.update( - config, - auth.nobody(config), - '_Installation', - { objectId: results[0].objectId }, - input - ); - }) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) - .then(results => { - expect(results.length).toEqual(1); - expect(results[0].installationId).toEqual(installId); - expect(results[0].deviceToken).toEqual(t); - expect(results[0].deviceType).toEqual('ios'); - expect(results[0].score).toEqual(1); - done(); - }) - .catch(error => { - jfail(error); - done(); - }); - }); + it_id('22311bc7-3f4f-42c1-a958-57083929e80d')(it)( + 'update is linking two existing objects w/ increment', + done => { + const installId = '12345678-abcd-abcd-abcd-123456789abc'; + const t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; + let input = { + installationId: installId, + deviceType: 'ios', + }; + rest + .create(config, auth.nobody(config), '_Installation', input) + .then(() => { + input = { + deviceToken: t, + deviceType: 'ios', + }; + return rest.create(config, auth.nobody(config), '_Installation', input); + }) + .then(() => + database.adapter.find('_Installation', installationSchema, { deviceToken: t }, {}) + ) + .then(results => { + expect(results.length).toEqual(1); + input = { + deviceToken: t, + installationId: installId, + deviceType: 'ios', + score: { + __op: 'Increment', + amount: 1, + }, + }; + return rest.update( + config, + auth.nobody(config), + '_Installation', + { objectId: results[0].objectId }, + input + ); + }) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .then(results => { + expect(results.length).toEqual(1); + expect(results[0].installationId).toEqual(installId); + expect(results[0].deviceToken).toEqual(t); + expect(results[0].deviceType).toEqual('ios'); + expect(results[0].score).toEqual(1); + done(); + }) + .catch(error => { + jfail(error); + done(); + }); + } + ); it('update is linking two existing with installation id', done => { const installId = '12345678-abcd-abcd-abcd-123456789abc'; @@ -969,70 +972,73 @@ describe('Installations', () => { }); }); - it_id('f2975078-eab7-4287-a932-288842e3cfb9')(it)('update is linking two existing with installation id w/ op', done => { - const installId = '12345678-abcd-abcd-abcd-123456789abc'; - const t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; - let input = { - installationId: installId, - deviceType: 'ios', - }; - let installObj; - let tokenObj; - rest - .create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) - .then(results => { - expect(results.length).toEqual(1); - installObj = results[0]; - input = { - deviceToken: t, - deviceType: 'ios', - }; - return rest.create(config, auth.nobody(config), '_Installation', input); - }) - .then(() => - database.adapter.find('_Installation', installationSchema, { deviceToken: t }, {}) - ) - .then(results => { - expect(results.length).toEqual(1); - tokenObj = results[0]; - input = { - installationId: installId, - deviceToken: t, - deviceType: 'ios', - score: { - __op: 'Increment', - amount: 1, - }, - }; - return rest.update( - config, - auth.nobody(config), - '_Installation', - { objectId: installObj.objectId }, - input - ); - }) - .then(() => - database.adapter.find( - '_Installation', - installationSchema, - { objectId: tokenObj.objectId }, - {} + it_id('f2975078-eab7-4287-a932-288842e3cfb9')(it)( + 'update is linking two existing with installation id w/ op', + done => { + const installId = '12345678-abcd-abcd-abcd-123456789abc'; + const t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; + let input = { + installationId: installId, + deviceType: 'ios', + }; + let installObj; + let tokenObj; + rest + .create(config, auth.nobody(config), '_Installation', input) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .then(results => { + expect(results.length).toEqual(1); + installObj = results[0]; + input = { + deviceToken: t, + deviceType: 'ios', + }; + return rest.create(config, auth.nobody(config), '_Installation', input); + }) + .then(() => + database.adapter.find('_Installation', installationSchema, { deviceToken: t }, {}) ) - ) - .then(results => { - expect(results.length).toEqual(1); - expect(results[0].installationId).toEqual(installId); - expect(results[0].deviceToken).toEqual(t); - expect(results[0].score).toEqual(1); - done(); - }) - .catch(error => { - jfail(error); - done(); - }); - }); + .then(results => { + expect(results.length).toEqual(1); + tokenObj = results[0]; + input = { + installationId: installId, + deviceToken: t, + deviceType: 'ios', + score: { + __op: 'Increment', + amount: 1, + }, + }; + return rest.update( + config, + auth.nobody(config), + '_Installation', + { objectId: installObj.objectId }, + input + ); + }) + .then(() => + database.adapter.find( + '_Installation', + installationSchema, + { objectId: tokenObj.objectId }, + {} + ) + ) + .then(results => { + expect(results.length).toEqual(1); + expect(results[0].installationId).toEqual(installId); + expect(results[0].deviceToken).toEqual(t); + expect(results[0].score).toEqual(1); + done(); + }) + .catch(error => { + jfail(error); + done(); + }); + } + ); it('ios merge existing same token no installation id', done => { // Test creating installation when there is an existing object with the diff --git a/spec/ParseLiveQuery.spec.js b/spec/ParseLiveQuery.spec.js index 6294c609a1..85335bb127 100644 --- a/spec/ParseLiveQuery.spec.js +++ b/spec/ParseLiveQuery.spec.js @@ -878,101 +878,107 @@ describe('ParseLiveQuery', function () { await expectAsync(query.subscribe()).toBeRejectedWith(new Error('Invalid session token')); }); - it_id('4ccc9508-ae6a-46ec-932a-9f5e49ab3b9e')(it)('handle invalid websocket payload length', async done => { - await reconfigureServer({ - liveQuery: { - classNames: ['TestObject'], - }, - startLiveQueryServer: true, - verbose: false, - silent: true, - websocketTimeout: 100, - }); - const object = new TestObject(); - await object.save(); - - const query = new Parse.Query(TestObject); - query.equalTo('objectId', object.id); - const subscription = await query.subscribe(); - - // All control frames must have a payload length of 125 bytes or less. - // https://tools.ietf.org/html/rfc6455#section-5.5 - // - // 0x89 = 10001001 = ping - // 0xfe = 11111110 = first bit is masking the remaining 7 are 1111110 or 126 the payload length - // https://tools.ietf.org/html/rfc6455#section-5.2 - const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); - client.socket._socket.write(Buffer.from([0x89, 0xfe])); - - subscription.on('update', async object => { - expect(object.get('foo')).toBe('bar'); - done(); - }); - // Wait for Websocket timeout to reconnect - setTimeout(async () => { - object.set({ foo: 'bar' }); + it_id('4ccc9508-ae6a-46ec-932a-9f5e49ab3b9e')(it)( + 'handle invalid websocket payload length', + async done => { + await reconfigureServer({ + liveQuery: { + classNames: ['TestObject'], + }, + startLiveQueryServer: true, + verbose: false, + silent: true, + websocketTimeout: 100, + }); + const object = new TestObject(); await object.save(); - }, 1000); - }); - - it_id('39a9191f-26dd-4e05-a379-297a67928de8')(it)('should execute live query update on email validation', async done => { - const emailAdapter = { - sendVerificationEmail: () => {}, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - - await reconfigureServer({ - maintenanceKey: 'test2', - liveQuery: { - classNames: [Parse.User], - }, - startLiveQueryServer: true, - verbose: false, - silent: true, - websocketTimeout: 100, - appName: 'liveQueryEmailValidation', - verifyUserEmails: true, - emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 20, // 0.5 second - publicServerURL: 'http://localhost:8378/1', - }).then(() => { - const user = new Parse.User(); - user.set('password', 'asdf'); - user.set('email', 'asdf@example.com'); - user.set('username', 'zxcv'); - user - .signUp() - .then(() => { - const config = Config.get('test'); - return config.database.find( - '_User', - { - username: 'zxcv', - }, - {}, - Auth.maintenance(config) - ); - }) - .then(async results => { - const foundUser = results[0]; - const query = new Parse.Query('_User'); - query.equalTo('objectId', foundUser.objectId); - const subscription = await query.subscribe(); - - subscription.on('update', async object => { - expect(object).toBeDefined(); - expect(object.get('emailVerified')).toBe(true); - done(); - }); - const userController = new UserController(emailAdapter, 'test', { - verifyUserEmails: true, + const query = new Parse.Query(TestObject); + query.equalTo('objectId', object.id); + const subscription = await query.subscribe(); + + // All control frames must have a payload length of 125 bytes or less. + // https://tools.ietf.org/html/rfc6455#section-5.5 + // + // 0x89 = 10001001 = ping + // 0xfe = 11111110 = first bit is masking the remaining 7 are 1111110 or 126 the payload length + // https://tools.ietf.org/html/rfc6455#section-5.2 + const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); + client.socket._socket.write(Buffer.from([0x89, 0xfe])); + + subscription.on('update', async object => { + expect(object.get('foo')).toBe('bar'); + done(); + }); + // Wait for Websocket timeout to reconnect + setTimeout(async () => { + object.set({ foo: 'bar' }); + await object.save(); + }, 1000); + } + ); + + it_id('39a9191f-26dd-4e05-a379-297a67928de8')(it)( + 'should execute live query update on email validation', + async done => { + const emailAdapter = { + sendVerificationEmail: () => {}, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + + await reconfigureServer({ + maintenanceKey: 'test2', + liveQuery: { + classNames: [Parse.User], + }, + startLiveQueryServer: true, + verbose: false, + silent: true, + websocketTimeout: 100, + appName: 'liveQueryEmailValidation', + verifyUserEmails: true, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 20, // 0.5 second + publicServerURL: 'http://localhost:8378/1', + }).then(() => { + const user = new Parse.User(); + user.set('password', 'asdf'); + user.set('email', 'asdf@example.com'); + user.set('username', 'zxcv'); + user + .signUp() + .then(() => { + const config = Config.get('test'); + return config.database.find( + '_User', + { + username: 'zxcv', + }, + {}, + Auth.maintenance(config) + ); + }) + .then(async results => { + const foundUser = results[0]; + const query = new Parse.Query('_User'); + query.equalTo('objectId', foundUser.objectId); + const subscription = await query.subscribe(); + + subscription.on('update', async object => { + expect(object).toBeDefined(); + expect(object.get('emailVerified')).toBe(true); + done(); + }); + + const userController = new UserController(emailAdapter, 'test', { + verifyUserEmails: true, + }); + userController.verifyEmail(foundUser._email_verify_token); }); - userController.verifyEmail(foundUser._email_verify_token); - }); - }); - }); + }); + } + ); it('should not broadcast event to client with invalid session token - avisory GHSA-2xm2-xj2q-qgpj', async done => { await reconfigureServer({ diff --git a/spec/ParseObject.spec.js b/spec/ParseObject.spec.js index 4c0f5bb4c2..58bb02c2d1 100644 --- a/spec/ParseObject.spec.js +++ b/spec/ParseObject.spec.js @@ -302,7 +302,9 @@ describe('Parse.Object testing', () => { it('invalid key name', function (done) { const item = new Parse.Object('Item'); - expect(() => item.set({ 'foo^bar': 'baz' })).toThrow(new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'Invalid key name: "foo^bar"')); + expect(() => item.set({ 'foo^bar': 'baz' })).toThrow( + new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'Invalid key name: "foo^bar"') + ); item.save({ 'foo^bar': 'baz' }).then(fail, () => done()); }); @@ -570,7 +572,10 @@ describe('Parse.Object testing', () => { it_only_db('mongo')('can increment array nested fields', async () => { const obj = new TestObject(); - obj.set('items', [ { value: 'a', count: 5 }, { value: 'b', count: 1 } ]); + obj.set('items', [ + { value: 'a', count: 5 }, + { value: 'b', count: 1 }, + ]); await obj.save(); obj.increment('items.0.count', 15); obj.increment('items.1.count', 4); @@ -2122,15 +2127,15 @@ describe('Parse.Object testing', () => { it('should not change the json field to array in afterSave', async () => { Parse.Cloud.beforeSave('failingJSONTestCase', req => { - expect(req.object.get('jsonField')).toEqual({ '123': 'test' }); + expect(req.object.get('jsonField')).toEqual({ 123: 'test' }); }); Parse.Cloud.afterSave('failingJSONTestCase', req => { - expect(req.object.get('jsonField')).toEqual({ '123': 'test' }); + expect(req.object.get('jsonField')).toEqual({ 123: 'test' }); }); const object = new Parse.Object('failingJSONTestCase'); - object.set('jsonField', { '123': 'test' }); + object.set('jsonField', { 123: 'test' }); await object.save(); }); @@ -2141,15 +2146,15 @@ describe('Parse.Object testing', () => { { field: 'boolean', value: true }, { field: 'array', value: [0, 1, 2] }, { field: 'array', value: [1, 2, 3] }, - { field: 'array', value: [{ '0': 'a' }, 2, 3] }, + { field: 'array', value: [{ 0: 'a' }, 2, 3] }, { field: 'object', value: { key: 'value' } }, { field: 'object', value: { key1: 'value1', key2: 'value2' } }, { field: 'object', value: { key1: 1, key2: 2 } }, { field: 'object', value: { '1x1': 1 } }, - { field: 'object', value: { '1x1': 1, '2': 2 } }, - { field: 'object', value: { '0': 0 } }, - { field: 'object', value: { '1': 1 } }, - { field: 'object', value: { '0': { '0': 'a', '1': 'b' } } }, + { field: 'object', value: { '1x1': 1, 2: 2 } }, + { field: 'object', value: { 0: 0 } }, + { field: 'object', value: { 1: 1 } }, + { field: 'object', value: { 0: { 0: 'a', 1: 'b' } } }, { field: 'date', value: new Date() }, { field: 'file', diff --git a/spec/ParsePubSub.spec.js b/spec/ParsePubSub.spec.js index 53bdd0b674..063c35728d 100644 --- a/spec/ParsePubSub.spec.js +++ b/spec/ParsePubSub.spec.js @@ -28,8 +28,8 @@ describe('ParsePubSub', function () { }); const RedisPubSub = require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; - const EventEmitterPubSub = require('../lib/Adapters/PubSub/EventEmitterPubSub') - .EventEmitterPubSub; + const EventEmitterPubSub = + require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub; expect(RedisPubSub.createPublisher).toHaveBeenCalledWith({ redisURL: 'redisURL', redisOptions: { socket_keepalive: true }, @@ -41,8 +41,8 @@ describe('ParsePubSub', function () { ParsePubSub.createPublisher({}); const RedisPubSub = require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; - const EventEmitterPubSub = require('../lib/Adapters/PubSub/EventEmitterPubSub') - .EventEmitterPubSub; + const EventEmitterPubSub = + require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub; expect(RedisPubSub.createPublisher).not.toHaveBeenCalled(); expect(EventEmitterPubSub.createPublisher).toHaveBeenCalled(); }); @@ -54,8 +54,8 @@ describe('ParsePubSub', function () { }); const RedisPubSub = require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; - const EventEmitterPubSub = require('../lib/Adapters/PubSub/EventEmitterPubSub') - .EventEmitterPubSub; + const EventEmitterPubSub = + require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub; expect(RedisPubSub.createSubscriber).toHaveBeenCalledWith({ redisURL: 'redisURL', redisOptions: { socket_keepalive: true }, @@ -67,8 +67,8 @@ describe('ParsePubSub', function () { ParsePubSub.createSubscriber({}); const RedisPubSub = require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; - const EventEmitterPubSub = require('../lib/Adapters/PubSub/EventEmitterPubSub') - .EventEmitterPubSub; + const EventEmitterPubSub = + require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub; expect(RedisPubSub.createSubscriber).not.toHaveBeenCalled(); expect(EventEmitterPubSub.createSubscriber).toHaveBeenCalled(); }); @@ -89,8 +89,8 @@ describe('ParsePubSub', function () { expect(adapter.createSubscriber).toHaveBeenCalled(); const RedisPubSub = require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; - const EventEmitterPubSub = require('../lib/Adapters/PubSub/EventEmitterPubSub') - .EventEmitterPubSub; + const EventEmitterPubSub = + require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub; expect(RedisPubSub.createSubscriber).not.toHaveBeenCalled(); expect(EventEmitterPubSub.createSubscriber).not.toHaveBeenCalled(); expect(RedisPubSub.createPublisher).not.toHaveBeenCalled(); @@ -117,8 +117,8 @@ describe('ParsePubSub', function () { expect(adapter.createSubscriber).toHaveBeenCalled(); const RedisPubSub = require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; - const EventEmitterPubSub = require('../lib/Adapters/PubSub/EventEmitterPubSub') - .EventEmitterPubSub; + const EventEmitterPubSub = + require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub; expect(RedisPubSub.createSubscriber).not.toHaveBeenCalled(); expect(EventEmitterPubSub.createSubscriber).not.toHaveBeenCalled(); expect(RedisPubSub.createPublisher).not.toHaveBeenCalled(); diff --git a/spec/ParseQuery.Aggregate.spec.js b/spec/ParseQuery.Aggregate.spec.js index 902b34d9d3..70609c39c6 100644 --- a/spec/ParseQuery.Aggregate.spec.js +++ b/spec/ParseQuery.Aggregate.spec.js @@ -296,112 +296,125 @@ describe('Parse.Query Aggregate testing', () => { .catch(done.fail); }); - it_id('c7695018-03de-49e4-8a72-d4d956f70deb')(it_exclude_dbs(['postgres']))('group and multiply transform', done => { - const obj1 = new TestObject({ name: 'item a', quantity: 2, price: 10 }); - const obj2 = new TestObject({ name: 'item b', quantity: 5, price: 5 }); - const pipeline = [ - { - $group: { - _id: null, - total: { $sum: { $multiply: ['$quantity', '$price'] } }, + it_id('c7695018-03de-49e4-8a72-d4d956f70deb')(it_exclude_dbs(['postgres']))( + 'group and multiply transform', + done => { + const obj1 = new TestObject({ name: 'item a', quantity: 2, price: 10 }); + const obj2 = new TestObject({ name: 'item b', quantity: 5, price: 5 }); + const pipeline = [ + { + $group: { + _id: null, + total: { $sum: { $multiply: ['$quantity', '$price'] } }, + }, }, - }, - ]; - Parse.Object.saveAll([obj1, obj2]) - .then(() => { - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - expect(results.length).toEqual(1); - expect(results[0].total).toEqual(45); - done(); - }); - }); + ]; + Parse.Object.saveAll([obj1, obj2]) + .then(() => { + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + expect(results.length).toEqual(1); + expect(results[0].total).toEqual(45); + done(); + }); + } + ); - it_id('2d278175-7594-4b29-bef4-04c778b7a42f')(it_exclude_dbs(['postgres']))('project and multiply transform', done => { - const obj1 = new TestObject({ name: 'item a', quantity: 2, price: 10 }); - const obj2 = new TestObject({ name: 'item b', quantity: 5, price: 5 }); - const pipeline = [ - { - $match: { quantity: { $exists: true } }, - }, - { - $project: { - name: 1, - total: { $multiply: ['$quantity', '$price'] }, + it_id('2d278175-7594-4b29-bef4-04c778b7a42f')(it_exclude_dbs(['postgres']))( + 'project and multiply transform', + done => { + const obj1 = new TestObject({ name: 'item a', quantity: 2, price: 10 }); + const obj2 = new TestObject({ name: 'item b', quantity: 5, price: 5 }); + const pipeline = [ + { + $match: { quantity: { $exists: true } }, }, - }, - ]; - Parse.Object.saveAll([obj1, obj2]) - .then(() => { - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - expect(results.length).toEqual(2); - if (results[0].name === 'item a') { + { + $project: { + name: 1, + total: { $multiply: ['$quantity', '$price'] }, + }, + }, + ]; + Parse.Object.saveAll([obj1, obj2]) + .then(() => { + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + expect(results.length).toEqual(2); + if (results[0].name === 'item a') { + expect(results[0].total).toEqual(20); + expect(results[1].total).toEqual(25); + } else { + expect(results[0].total).toEqual(25); + expect(results[1].total).toEqual(20); + } + done(); + }); + } + ); + + it_id('9c9d9318-3a9e-4c2a-8a09-d3aa52c7505b')(it_exclude_dbs(['postgres']))( + 'project without objectId transform', + done => { + const obj1 = new TestObject({ name: 'item a', quantity: 2, price: 10 }); + const obj2 = new TestObject({ name: 'item b', quantity: 5, price: 5 }); + const pipeline = [ + { + $match: { quantity: { $exists: true } }, + }, + { + $project: { + _id: 0, + total: { $multiply: ['$quantity', '$price'] }, + }, + }, + { + $sort: { total: 1 }, + }, + ]; + Parse.Object.saveAll([obj1, obj2]) + .then(() => { + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + expect(results.length).toEqual(2); expect(results[0].total).toEqual(20); + expect(results[0].objectId).toEqual(undefined); expect(results[1].total).toEqual(25); - } else { - expect(results[0].total).toEqual(25); - expect(results[1].total).toEqual(20); - } - done(); - }); - }); + expect(results[1].objectId).toEqual(undefined); + done(); + }); + } + ); - it_id('9c9d9318-3a9e-4c2a-8a09-d3aa52c7505b')(it_exclude_dbs(['postgres']))('project without objectId transform', done => { - const obj1 = new TestObject({ name: 'item a', quantity: 2, price: 10 }); - const obj2 = new TestObject({ name: 'item b', quantity: 5, price: 5 }); - const pipeline = [ - { - $match: { quantity: { $exists: true } }, - }, - { - $project: { - _id: 0, - total: { $multiply: ['$quantity', '$price'] }, + it_id('f92c82ac-1993-4758-b718-45689dfc4154')(it_exclude_dbs(['postgres']))( + 'project updatedAt only transform', + done => { + const pipeline = [ + { + $project: { _id: 0, updatedAt: 1 }, }, - }, - { - $sort: { total: 1 }, - }, - ]; - Parse.Object.saveAll([obj1, obj2]) - .then(() => { - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - expect(results.length).toEqual(2); - expect(results[0].total).toEqual(20); - expect(results[0].objectId).toEqual(undefined); - expect(results[1].total).toEqual(25); - expect(results[1].objectId).toEqual(undefined); + ]; + const query = new Parse.Query(TestObject); + query.aggregate(pipeline).then(results => { + expect(results.length).toEqual(4); + for (let i = 0; i < results.length; i++) { + const item = results[i]; + expect(Object.prototype.hasOwnProperty.call(item, 'updatedAt')).toEqual(true); + expect(Object.prototype.hasOwnProperty.call(item, 'objectId')).toEqual(false); + } done(); }); - }); - - it_id('f92c82ac-1993-4758-b718-45689dfc4154')(it_exclude_dbs(['postgres']))('project updatedAt only transform', done => { - const pipeline = [ - { - $project: { _id: 0, updatedAt: 1 }, - }, - ]; - const query = new Parse.Query(TestObject); - query.aggregate(pipeline).then(results => { - expect(results.length).toEqual(4); - for (let i = 0; i < results.length; i++) { - const item = results[i]; - expect(Object.prototype.hasOwnProperty.call(item, 'updatedAt')).toEqual(true); - expect(Object.prototype.hasOwnProperty.call(item, 'objectId')).toEqual(false); - } - done(); - }); - }); + } + ); - it_id('99566b1d-778d-4444-9deb-c398108e659d')(it_exclude_dbs(['postgres']))('can group by any date field (it does not work if you have dirty data)', + it_id('99566b1d-778d-4444-9deb-c398108e659d')(it_exclude_dbs(['postgres']))( + 'can group by any date field (it does not work if you have dirty data)', done => { // rows in your collection with non date data in the field that is supposed to be a date const obj1 = new TestObject({ dateField2019: new Date(1990, 11, 1) }); @@ -658,22 +671,25 @@ describe('Parse.Query Aggregate testing', () => { }); }); - it_id('d98c8c20-6dac-4d74-8228-85a1ae46a7d0')(it)('should aggregate with Date object (directAccess)', async () => { - const rest = require('../lib/rest'); - const auth = require('../lib/Auth'); - const TestObject = Parse.Object.extend('TestObject'); - const date = new Date(); - await new TestObject({ date: date }).save(null, { useMasterKey: true }); - const config = Config.get(Parse.applicationId); - const resp = await rest.find( - config, - auth.master(config), - 'TestObject', - {}, - { pipeline: [{ $match: { date: { $lte: new Date() } } }] } - ); - expect(resp.results.length).toBe(1); - }); + it_id('d98c8c20-6dac-4d74-8228-85a1ae46a7d0')(it)( + 'should aggregate with Date object (directAccess)', + async () => { + const rest = require('../lib/rest'); + const auth = require('../lib/Auth'); + const TestObject = Parse.Object.extend('TestObject'); + const date = new Date(); + await new TestObject({ date: date }).save(null, { useMasterKey: true }); + const config = Config.get(Parse.applicationId); + const resp = await rest.find( + config, + auth.master(config), + 'TestObject', + {}, + { pipeline: [{ $match: { date: { $lte: new Date() } } }] } + ); + expect(resp.results.length).toBe(1); + } + ); it_id('3d73d23a-fce1-4ac0-972a-50f6a550f348')(it)('match comparison query', done => { const options = Object.assign({}, masterKeyOptions, { @@ -817,14 +833,17 @@ describe('Parse.Query Aggregate testing', () => { }); }); - it_id('3a1e2cdc-52c7-4060-bc90-b06d557d85ce')(it_exclude_dbs(['postgres']))('match exists query', done => { - const pipeline = [{ $match: { score: { $exists: true } } }]; - const query = new Parse.Query(TestObject); - query.aggregate(pipeline).then(results => { - expect(results.length).toEqual(4); - done(); - }); - }); + it_id('3a1e2cdc-52c7-4060-bc90-b06d557d85ce')(it_exclude_dbs(['postgres']))( + 'match exists query', + done => { + const pipeline = [{ $match: { score: { $exists: true } } }]; + const query = new Parse.Query(TestObject); + query.aggregate(pipeline).then(results => { + expect(results.length).toEqual(4); + done(); + }); + } + ); it_id('0adea3f4-73f7-4b48-a7dd-c764ceb947ec')(it)('match date query - createdAt', done => { const obj1 = new TestObject(); @@ -882,78 +901,84 @@ describe('Parse.Query Aggregate testing', () => { }); }); - it_id('802ffc99-861b-4b72-90a6-0c666a2e3fd8')(it_exclude_dbs(['postgres']))('match pointer with operator query', done => { - const pointer = new PointerObject(); + it_id('802ffc99-861b-4b72-90a6-0c666a2e3fd8')(it_exclude_dbs(['postgres']))( + 'match pointer with operator query', + done => { + const pointer = new PointerObject(); - const obj1 = new TestObject({ pointer }); - const obj2 = new TestObject({ pointer }); - const obj3 = new TestObject(); + const obj1 = new TestObject({ pointer }); + const obj2 = new TestObject({ pointer }); + const obj3 = new TestObject(); - Parse.Object.saveAll([pointer, obj1, obj2, obj3]) - .then(() => { - const pipeline = [{ $match: { pointer: { $exists: true } } }]; - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - expect(results.length).toEqual(2); - expect(results[0].pointer.objectId).toEqual(pointer.id); - expect(results[1].pointer.objectId).toEqual(pointer.id); - expect(results.some(result => result.objectId === obj1.id)).toEqual(true); - expect(results.some(result => result.objectId === obj2.id)).toEqual(true); - done(); - }); - }); + Parse.Object.saveAll([pointer, obj1, obj2, obj3]) + .then(() => { + const pipeline = [{ $match: { pointer: { $exists: true } } }]; + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + expect(results.length).toEqual(2); + expect(results[0].pointer.objectId).toEqual(pointer.id); + expect(results[1].pointer.objectId).toEqual(pointer.id); + expect(results.some(result => result.objectId === obj1.id)).toEqual(true); + expect(results.some(result => result.objectId === obj2.id)).toEqual(true); + done(); + }); + } + ); - it_id('28090280-7c3e-47f8-8bf6-bebf8566a36c')(it_exclude_dbs(['postgres']))('match null values', async () => { - const obj1 = new Parse.Object('MyCollection'); - obj1.set('language', 'en'); - obj1.set('otherField', 1); - const obj2 = new Parse.Object('MyCollection'); - obj2.set('language', 'en'); - obj2.set('otherField', 2); - const obj3 = new Parse.Object('MyCollection'); - obj3.set('language', null); - obj3.set('otherField', 3); - const obj4 = new Parse.Object('MyCollection'); - obj4.set('language', null); - obj4.set('otherField', 4); - const obj5 = new Parse.Object('MyCollection'); - obj5.set('language', 'pt'); - obj5.set('otherField', 5); - const obj6 = new Parse.Object('MyCollection'); - obj6.set('language', 'pt'); - obj6.set('otherField', 6); - await Parse.Object.saveAll([obj1, obj2, obj3, obj4, obj5, obj6]); - - expect( - ( - await new Parse.Query('MyCollection').aggregate([ - { - $match: { - language: { $in: [null, 'en'] }, + it_id('28090280-7c3e-47f8-8bf6-bebf8566a36c')(it_exclude_dbs(['postgres']))( + 'match null values', + async () => { + const obj1 = new Parse.Object('MyCollection'); + obj1.set('language', 'en'); + obj1.set('otherField', 1); + const obj2 = new Parse.Object('MyCollection'); + obj2.set('language', 'en'); + obj2.set('otherField', 2); + const obj3 = new Parse.Object('MyCollection'); + obj3.set('language', null); + obj3.set('otherField', 3); + const obj4 = new Parse.Object('MyCollection'); + obj4.set('language', null); + obj4.set('otherField', 4); + const obj5 = new Parse.Object('MyCollection'); + obj5.set('language', 'pt'); + obj5.set('otherField', 5); + const obj6 = new Parse.Object('MyCollection'); + obj6.set('language', 'pt'); + obj6.set('otherField', 6); + await Parse.Object.saveAll([obj1, obj2, obj3, obj4, obj5, obj6]); + + expect( + ( + await new Parse.Query('MyCollection').aggregate([ + { + $match: { + language: { $in: [null, 'en'] }, + }, }, - }, - ]) - ) - .map(value => value.otherField) - .sort() - ).toEqual([1, 2, 3, 4]); - - expect( - ( - await new Parse.Query('MyCollection').aggregate([ - { - $match: { - $or: [{ language: 'en' }, { language: null }], + ]) + ) + .map(value => value.otherField) + .sort() + ).toEqual([1, 2, 3, 4]); + + expect( + ( + await new Parse.Query('MyCollection').aggregate([ + { + $match: { + $or: [{ language: 'en' }, { language: null }], + }, }, - }, - ]) - ) - .map(value => value.otherField) - .sort() - ).toEqual([1, 2, 3, 4]); - }); + ]) + ) + .map(value => value.otherField) + .sort() + ).toEqual([1, 2, 3, 4]); + } + ); it_id('df63d1f5-7c37-4ed9-8bc5-20d82f29f509')(it)('project query', done => { const options = Object.assign({}, masterKeyOptions, { @@ -1153,34 +1178,40 @@ describe('Parse.Query Aggregate testing', () => { }); }); - it_id('91e6cb94-2837-44b7-b057-0c4965057caa')(it)('distinct class does not exist return empty', done => { - const options = Object.assign({}, masterKeyOptions, { - body: { distinct: 'unknown' }, - }); - get(Parse.serverURL + '/aggregate/UnknownClass', options) - .then(resp => { - expect(resp.results.length).toBe(0); - done(); - }) - .catch(done.fail); - }); + it_id('91e6cb94-2837-44b7-b057-0c4965057caa')(it)( + 'distinct class does not exist return empty', + done => { + const options = Object.assign({}, masterKeyOptions, { + body: { distinct: 'unknown' }, + }); + get(Parse.serverURL + '/aggregate/UnknownClass', options) + .then(resp => { + expect(resp.results.length).toBe(0); + done(); + }) + .catch(done.fail); + } + ); - it_id('bd15daaf-8dc7-458c-81e2-170026f4a8a7')(it)('distinct field does not exist return empty', done => { - const options = Object.assign({}, masterKeyOptions, { - body: { distinct: 'unknown' }, - }); - const obj = new TestObject(); - obj - .save() - .then(() => { - return get(Parse.serverURL + '/aggregate/TestObject', options); - }) - .then(resp => { - expect(resp.results.length).toBe(0); - done(); - }) - .catch(done.fail); - }); + it_id('bd15daaf-8dc7-458c-81e2-170026f4a8a7')(it)( + 'distinct field does not exist return empty', + done => { + const options = Object.assign({}, masterKeyOptions, { + body: { distinct: 'unknown' }, + }); + const obj = new TestObject(); + obj + .save() + .then(() => { + return get(Parse.serverURL + '/aggregate/TestObject', options); + }) + .then(resp => { + expect(resp.results.length).toBe(0); + done(); + }) + .catch(done.fail); + } + ); it_id('21988fce-8326-425f-82f0-cd444ca3671b')(it)('distinct array', done => { const options = Object.assign({}, masterKeyOptions, { @@ -1256,108 +1287,114 @@ describe('Parse.Query Aggregate testing', () => { .catch(done.fail); }); - it_id('d9c19419-e99d-4d9f-b7f3-418e49ee47dd')(it)('does not return sensitive hidden properties', done => { - const options = Object.assign({}, masterKeyOptions, { - body: { - $match: { - score: { - $gt: 5, + it_id('d9c19419-e99d-4d9f-b7f3-418e49ee47dd')(it)( + 'does not return sensitive hidden properties', + done => { + const options = Object.assign({}, masterKeyOptions, { + body: { + $match: { + score: { + $gt: 5, + }, }, }, - }, - }); - - const username = 'leaky_user'; - const score = 10; - - const user = new Parse.User(); - user.setUsername(username); - user.setPassword('password'); - user.set('score', score); - user - .signUp() - .then(function () { - return get(Parse.serverURL + '/aggregate/_User', options); - }) - .then(function (resp) { - expect(resp.results.length).toBe(1); - const result = resp.results[0]; - - // verify server-side keys are not present... - expect(result._hashed_password).toBe(undefined); - expect(result._wperm).toBe(undefined); - expect(result._rperm).toBe(undefined); - expect(result._acl).toBe(undefined); - expect(result._created_at).toBe(undefined); - expect(result._updated_at).toBe(undefined); - - // verify createdAt, updatedAt and others are present - expect(result.createdAt).not.toBe(undefined); - expect(result.updatedAt).not.toBe(undefined); - expect(result.objectId).not.toBe(undefined); - expect(result.username).toBe(username); - expect(result.score).toBe(score); - - done(); - }) - .catch(function (err) { - fail(err); }); - }); - it_id('0a23e791-e9b5-457a-9bf9-9c5ecf406f42')(it_exclude_dbs(['postgres']))('aggregate allow multiple of same stage', async done => { - await reconfigureServer({ silent: false }); - const pointer1 = new TestObject({ value: 1 }); - const pointer2 = new TestObject({ value: 2 }); - const pointer3 = new TestObject({ value: 3 }); + const username = 'leaky_user'; + const score = 10; + + const user = new Parse.User(); + user.setUsername(username); + user.setPassword('password'); + user.set('score', score); + user + .signUp() + .then(function () { + return get(Parse.serverURL + '/aggregate/_User', options); + }) + .then(function (resp) { + expect(resp.results.length).toBe(1); + const result = resp.results[0]; + + // verify server-side keys are not present... + expect(result._hashed_password).toBe(undefined); + expect(result._wperm).toBe(undefined); + expect(result._rperm).toBe(undefined); + expect(result._acl).toBe(undefined); + expect(result._created_at).toBe(undefined); + expect(result._updated_at).toBe(undefined); + + // verify createdAt, updatedAt and others are present + expect(result.createdAt).not.toBe(undefined); + expect(result.updatedAt).not.toBe(undefined); + expect(result.objectId).not.toBe(undefined); + expect(result.username).toBe(username); + expect(result.score).toBe(score); - const obj1 = new TestObject({ pointer: pointer1, name: 'Hello' }); - const obj2 = new TestObject({ pointer: pointer2, name: 'Hello' }); - const obj3 = new TestObject({ pointer: pointer3, name: 'World' }); + done(); + }) + .catch(function (err) { + fail(err); + }); + } + ); - const options = Object.assign({}, masterKeyOptions, { - body: { - pipeline: [ - { - $match: { name: 'Hello' }, - }, - { - // Transform className$objectId to objectId and store in new field tempPointer - $project: { - tempPointer: { $substr: ['$_p_pointer', 11, -1] }, // Remove TestObject$ + it_id('0a23e791-e9b5-457a-9bf9-9c5ecf406f42')(it_exclude_dbs(['postgres']))( + 'aggregate allow multiple of same stage', + async done => { + await reconfigureServer({ silent: false }); + 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 options = Object.assign({}, masterKeyOptions, { + body: { + pipeline: [ + { + $match: { name: 'Hello' }, }, - }, - { - // Left Join, replace objectId stored in tempPointer with an actual object - $lookup: { - from: 'test_TestObject', - localField: 'tempPointer', - foreignField: '_id', - as: 'tempPointer', + { + // Transform className$objectId to objectId and store in new field tempPointer + $project: { + tempPointer: { $substr: ['$_p_pointer', 11, -1] }, // Remove TestObject$ + }, }, - }, - { - // lookup returns an array, Deconstructs an array field to objects - $unwind: { - path: '$tempPointer', + { + // Left Join, replace objectId stored in tempPointer with an actual object + $lookup: { + from: 'test_TestObject', + localField: 'tempPointer', + foreignField: '_id', + as: 'tempPointer', + }, }, - }, - { - $match: { 'tempPointer.value': 2 }, - }, - ], - }, - }); - Parse.Object.saveAll([pointer1, pointer2, pointer3, obj1, obj2, obj3]) - .then(() => { - return get(Parse.serverURL + '/aggregate/TestObject', options); - }) - .then(resp => { - expect(resp.results.length).toEqual(1); - expect(resp.results[0].tempPointer.value).toEqual(2); - done(); + { + // lookup returns an array, Deconstructs an array field to objects + $unwind: { + path: '$tempPointer', + }, + }, + { + $match: { 'tempPointer.value': 2 }, + }, + ], + }, }); - }); + Parse.Object.saveAll([pointer1, pointer2, pointer3, obj1, obj2, obj3]) + .then(() => { + return get(Parse.serverURL + '/aggregate/TestObject', options); + }) + .then(resp => { + expect(resp.results.length).toEqual(1); + expect(resp.results[0].tempPointer.value).toEqual(2); + done(); + }); + } + ); it_only_db('mongo')('aggregate geoNear with location query', async () => { // Create geo index which is required for `geoNear` query diff --git a/spec/ParseQuery.FullTextSearch.spec.js b/spec/ParseQuery.FullTextSearch.spec.js index 11760ec161..e32fab5de2 100644 --- a/spec/ParseQuery.FullTextSearch.spec.js +++ b/spec/ParseQuery.FullTextSearch.spec.js @@ -76,70 +76,88 @@ describe('Parse.Query Full Text Search testing', () => { expect(resp.length).toBe(2); }); - it_id('7d3da216-9582-40ee-a2fe-8316feaf5c0c')(it)('fullTextSearch: $diacriticSensitive', async () => { - await fullTextHelper(); - const query = new Parse.Query('TestObject'); - query.fullText('subject', 'CAFÉ', { diacriticSensitive: true }); - const resp = await query.find(); - expect(resp.length).toBe(1); - }); + it_id('7d3da216-9582-40ee-a2fe-8316feaf5c0c')(it)( + 'fullTextSearch: $diacriticSensitive', + async () => { + await fullTextHelper(); + const query = new Parse.Query('TestObject'); + query.fullText('subject', 'CAFÉ', { diacriticSensitive: true }); + const resp = await query.find(); + expect(resp.length).toBe(1); + } + ); - it_id('dade10c8-2b9c-4f43-bb3f-a13bbd82ac22')(it)('fullTextSearch: $search, invalid input', async () => { - await fullTextHelper(); - const invalidQuery = async () => { - const where = { - subject: { - $text: { - $search: true, + it_id('dade10c8-2b9c-4f43-bb3f-a13bbd82ac22')(it)( + 'fullTextSearch: $search, invalid input', + async () => { + await fullTextHelper(); + const invalidQuery = async () => { + const where = { + subject: { + $text: { + $search: true, + }, }, - }, + }; + try { + await request({ + method: 'POST', + url: 'http://localhost:8378/1/classes/TestObject', + body: { where, _method: 'GET' }, + headers: { + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'test', + 'Content-Type': 'application/json', + }, + }); + } catch (e) { + throw new Parse.Error(e.data.code, e.data.error); + } }; - try { - await request({ - method: 'POST', - url: 'http://localhost:8378/1/classes/TestObject', - body: { where, _method: 'GET' }, - headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'test', - 'Content-Type': 'application/json', - }, - }); - } catch (e) { - throw new Parse.Error(e.data.code, e.data.error); - } - }; - await expectAsync(invalidQuery()).toBeRejectedWith( - new Parse.Error(Parse.Error.INVALID_JSON, 'bad $text: $search, should be object') - ); - }); + await expectAsync(invalidQuery()).toBeRejectedWith( + new Parse.Error(Parse.Error.INVALID_JSON, 'bad $text: $search, should be object') + ); + } + ); - it_id('ff7c6b1c-4712-4847-bb76-f4e1f641f7b5')(it)('fullTextSearch: $language, invalid input', async () => { - await fullTextHelper(); - const query = new Parse.Query('TestObject'); - query.fullText('subject', 'leche', { language: true }); - await expectAsync(query.find()).toBeRejectedWith( - new Parse.Error(Parse.Error.INVALID_JSON, 'bad $text: $language, should be string') - ); - }); + it_id('ff7c6b1c-4712-4847-bb76-f4e1f641f7b5')(it)( + 'fullTextSearch: $language, invalid input', + async () => { + await fullTextHelper(); + const query = new Parse.Query('TestObject'); + query.fullText('subject', 'leche', { language: true }); + await expectAsync(query.find()).toBeRejectedWith( + new Parse.Error(Parse.Error.INVALID_JSON, 'bad $text: $language, should be string') + ); + } + ); - it_id('de262dbc-ec75-4ec6-9217-fbb90146c272')(it)('fullTextSearch: $caseSensitive, invalid input', async () => { - await fullTextHelper(); - const query = new Parse.Query('TestObject'); - query.fullText('subject', 'leche', { caseSensitive: 'string' }); - await expectAsync(query.find()).toBeRejectedWith( - new Parse.Error(Parse.Error.INVALID_JSON, 'bad $text: $caseSensitive, should be boolean') - ); - }); + it_id('de262dbc-ec75-4ec6-9217-fbb90146c272')(it)( + 'fullTextSearch: $caseSensitive, invalid input', + async () => { + await fullTextHelper(); + const query = new Parse.Query('TestObject'); + query.fullText('subject', 'leche', { caseSensitive: 'string' }); + await expectAsync(query.find()).toBeRejectedWith( + new Parse.Error(Parse.Error.INVALID_JSON, 'bad $text: $caseSensitive, should be boolean') + ); + } + ); - it_id('b7b7b3a9-8d6c-4f98-a0ff-0113593d06d4')(it)('fullTextSearch: $diacriticSensitive, invalid input', async () => { - await fullTextHelper(); - const query = new Parse.Query('TestObject'); - query.fullText('subject', 'leche', { diacriticSensitive: 'string' }); - await expectAsync(query.find()).toBeRejectedWith( - new Parse.Error(Parse.Error.INVALID_JSON, 'bad $text: $diacriticSensitive, should be boolean') - ); - }); + it_id('b7b7b3a9-8d6c-4f98-a0ff-0113593d06d4')(it)( + 'fullTextSearch: $diacriticSensitive, invalid input', + async () => { + await fullTextHelper(); + const query = new Parse.Query('TestObject'); + query.fullText('subject', 'leche', { diacriticSensitive: 'string' }); + await expectAsync(query.find()).toBeRejectedWith( + new Parse.Error( + Parse.Error.INVALID_JSON, + 'bad $text: $diacriticSensitive, should be boolean' + ) + ); + } + ); }); describe_only_db('mongo')('[mongodb] Parse.Query Full Text Search testing', () => { diff --git a/spec/ParseQuery.spec.js b/spec/ParseQuery.spec.js index e6f3b1e08a..2faeee1387 100644 --- a/spec/ParseQuery.spec.js +++ b/spec/ParseQuery.spec.js @@ -6,7 +6,8 @@ const Parse = require('parse/node'); const request = require('../lib/request'); -const ParseServerRESTController = require('../lib/ParseServerRESTController').ParseServerRESTController; +const ParseServerRESTController = + require('../lib/ParseServerRESTController').ParseServerRESTController; const ParseServer = require('../lib/ParseServer').default; const masterKeyHeaders = { @@ -592,40 +593,43 @@ describe('Parse.Query testing', () => { }); }); - it_id('25bb35a6-e953-4d6d-a31c-66324d5ae076')(it)('containsAll object array queries', function (done) { - const MessageSet = Parse.Object.extend({ className: 'MessageSet' }); + it_id('25bb35a6-e953-4d6d-a31c-66324d5ae076')(it)( + 'containsAll object array queries', + function (done) { + const MessageSet = Parse.Object.extend({ className: 'MessageSet' }); - const messageList = []; - for (let i = 0; i < 4; ++i) { - messageList.push(new TestObject({ i: i })); - } + const messageList = []; + for (let i = 0; i < 4; ++i) { + messageList.push(new TestObject({ i: i })); + } - Parse.Object.saveAll(messageList).then(function () { - equal(messageList.length, 4); + Parse.Object.saveAll(messageList).then(function () { + equal(messageList.length, 4); - const messageSetList = []; - messageSetList.push(new MessageSet({ messages: messageList })); + const messageSetList = []; + messageSetList.push(new MessageSet({ messages: messageList })); - const someList = []; - someList.push(messageList[0]); - someList.push(messageList[1]); - someList.push(messageList[3]); - messageSetList.push(new MessageSet({ messages: someList })); + const someList = []; + someList.push(messageList[0]); + someList.push(messageList[1]); + someList.push(messageList[3]); + messageSetList.push(new MessageSet({ messages: someList })); - Parse.Object.saveAll(messageSetList).then(function () { - const inList = []; - inList.push(messageList[0]); - inList.push(messageList[2]); + Parse.Object.saveAll(messageSetList).then(function () { + const inList = []; + inList.push(messageList[0]); + inList.push(messageList[2]); - const query = new Parse.Query(MessageSet); - query.containsAll('messages', inList); - query.find().then(function (results) { - equal(results.length, 1); - done(); + const query = new Parse.Query(MessageSet); + query.containsAll('messages', inList); + query.find().then(function (results) { + equal(results.length, 1); + done(); + }); }); }); - }); - }); + } + ); it('containsAllStartingWith should match all strings that starts with string', done => { const object = new Parse.Object('Object'); @@ -707,40 +711,43 @@ describe('Parse.Query testing', () => { }); }); - it_id('3ea6ae04-bcc2-453d-8817-4c64d059c2f6')(it)('containsAllStartingWith values must be all of type starting with regex', done => { - const object = new Parse.Object('Object'); - object.set('strings', ['the', 'brown', 'lazy', 'fox', 'jumps']); + it_id('3ea6ae04-bcc2-453d-8817-4c64d059c2f6')(it)( + 'containsAllStartingWith values must be all of type starting with regex', + done => { + const object = new Parse.Object('Object'); + object.set('strings', ['the', 'brown', 'lazy', 'fox', 'jumps']); - object - .save() - .then(() => { - equal(object.isNew(), false); + object + .save() + .then(() => { + equal(object.isNew(), false); - return request({ - url: Parse.serverURL + '/classes/Object', - qs: { - where: JSON.stringify({ - strings: { - $all: [ - { $regex: '^\\Qthe\\E' }, - { $regex: '^\\Qlazy\\E' }, - { $regex: '^\\Qfox\\E' }, - { $unknown: /unknown/ }, - ], - }, - }), - }, - headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, - 'Content-Type': 'application/json', - }, + return request({ + url: Parse.serverURL + '/classes/Object', + qs: { + where: JSON.stringify({ + strings: { + $all: [ + { $regex: '^\\Qthe\\E' }, + { $regex: '^\\Qlazy\\E' }, + { $regex: '^\\Qfox\\E' }, + { $unknown: /unknown/ }, + ], + }, + }), + }, + headers: { + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, + 'Content-Type': 'application/json', + }, + }); + }) + .then(done.fail, function () { + done(); }); - }) - .then(done.fail, function () { - done(); - }); - }); + } + ); it('containsAllStartingWith empty array values should return empty results', done => { const object = new Parse.Object('Object'); @@ -1670,47 +1677,53 @@ describe('Parse.Query testing', () => { .catch(done.fail); }); - it_id('65c8238d-cf02-49d0-a919-8a17f5a58280')(it)('can order on an object number field', function (done) { - const testSet = [ - { sortField: { value: 10 } }, - { sortField: { value: 1 } }, - { sortField: { value: 5 } }, - ]; - - const objects = testSet.map(e => new Parse.Object('Test', e)); - Parse.Object.saveAll(objects) - .then(() => new Parse.Query('Test').addDescending('sortField.value').first()) - .then(result => { - expect(result.get('sortField').value).toBe(10); - return new Parse.Query('Test').addAscending('sortField.value').first(); - }) - .then(result => { - expect(result.get('sortField').value).toBe(1); - done(); - }) - .catch(done.fail); - }); - - it_id('d8f0bead-b931-4d66-8b0c-28c5705e463c')(it)('can order on an object number field (level 2)', function (done) { - const testSet = [ - { sortField: { value: { field: 10 } } }, - { sortField: { value: { field: 1 } } }, - { sortField: { value: { field: 5 } } }, - ]; - - const objects = testSet.map(e => new Parse.Object('Test', e)); - Parse.Object.saveAll(objects) - .then(() => new Parse.Query('Test').addDescending('sortField.value.field').first()) - .then(result => { - expect(result.get('sortField').value.field).toBe(10); - return new Parse.Query('Test').addAscending('sortField.value.field').first(); - }) - .then(result => { - expect(result.get('sortField').value.field).toBe(1); - done(); - }) - .catch(done.fail); - }); + it_id('65c8238d-cf02-49d0-a919-8a17f5a58280')(it)( + 'can order on an object number field', + function (done) { + const testSet = [ + { sortField: { value: 10 } }, + { sortField: { value: 1 } }, + { sortField: { value: 5 } }, + ]; + + const objects = testSet.map(e => new Parse.Object('Test', e)); + Parse.Object.saveAll(objects) + .then(() => new Parse.Query('Test').addDescending('sortField.value').first()) + .then(result => { + expect(result.get('sortField').value).toBe(10); + return new Parse.Query('Test').addAscending('sortField.value').first(); + }) + .then(result => { + expect(result.get('sortField').value).toBe(1); + done(); + }) + .catch(done.fail); + } + ); + + it_id('d8f0bead-b931-4d66-8b0c-28c5705e463c')(it)( + 'can order on an object number field (level 2)', + function (done) { + const testSet = [ + { sortField: { value: { field: 10 } } }, + { sortField: { value: { field: 1 } } }, + { sortField: { value: { field: 5 } } }, + ]; + + const objects = testSet.map(e => new Parse.Object('Test', e)); + Parse.Object.saveAll(objects) + .then(() => new Parse.Query('Test').addDescending('sortField.value.field').first()) + .then(result => { + expect(result.get('sortField').value.field).toBe(10); + return new Parse.Query('Test').addAscending('sortField.value.field').first(); + }) + .then(result => { + expect(result.get('sortField').value.field).toBe(1); + done(); + }) + .catch(done.fail); + } + ); it('order by ascending number then descending string', function (done) { const strings = ['a', 'b', 'c', 'd']; @@ -2113,30 +2126,33 @@ describe('Parse.Query testing', () => { .then(done); }); - it_id('823852f6-1de5-45ba-a2b9-ed952fcc6012')(it)('Use a regex that requires all modifiers', function (done) { - const thing = new TestObject(); - thing.set('myString', 'PArSe\nCom'); - Parse.Object.saveAll([thing]).then(function () { - const query = new Parse.Query(TestObject); - query.matches( - 'myString', - "parse # First fragment. We'll write this in one case but match insensitively\n" + - '.com # Second fragment. This can be separated by any character, including newline;' + - 'however, this comment must end with a newline to recognize it as a comment\n', - 'mixs' - ); - query.find().then( - function (results) { - equal(results.length, 1); - done(); - }, - function (err) { - jfail(err); - done(); - } - ); - }); - }); + it_id('823852f6-1de5-45ba-a2b9-ed952fcc6012')(it)( + 'Use a regex that requires all modifiers', + function (done) { + const thing = new TestObject(); + thing.set('myString', 'PArSe\nCom'); + Parse.Object.saveAll([thing]).then(function () { + const query = new Parse.Query(TestObject); + query.matches( + 'myString', + "parse # First fragment. We'll write this in one case but match insensitively\n" + + '.com # Second fragment. This can be separated by any character, including newline;' + + 'however, this comment must end with a newline to recognize it as a comment\n', + 'mixs' + ); + query.find().then( + function (results) { + equal(results.length, 1); + done(); + }, + function (err) { + jfail(err); + done(); + } + ); + }); + } + ); it('Regular expression constructor includes modifiers inline', function (done) { const thing = new TestObject(); @@ -4005,51 +4021,54 @@ describe('Parse.Query testing', () => { ); }); - it_id('7079f0ef-47b3-4a1e-aac0-32654dadaa27')(it)('should properly interpret a query v2', done => { - const user = new Parse.User(); - user.set('username', 'foo'); - user.set('password', 'bar'); - return user - .save() - .then(user => { - const objIdQuery = new Parse.Query('_User').equalTo('objectId', user.id); - const blockedUserQuery = user.relation('blockedUsers').query(); - - const aResponseQuery = new Parse.Query('MatchRelationshipActivityResponse'); - aResponseQuery.equalTo('userA', user); - aResponseQuery.equalTo('userAResponse', 1); - - const bResponseQuery = new Parse.Query('MatchRelationshipActivityResponse'); - bResponseQuery.equalTo('userB', user); - bResponseQuery.equalTo('userBResponse', 1); - - const matchOr = Parse.Query.or(aResponseQuery, bResponseQuery); - const matchRelationshipA = new Parse.Query('_User'); - matchRelationshipA.matchesKeyInQuery('objectId', 'userAObjectId', matchOr); - const matchRelationshipB = new Parse.Query('_User'); - matchRelationshipB.matchesKeyInQuery('objectId', 'userBObjectId', matchOr); - - const orQuery = Parse.Query.or( - objIdQuery, - blockedUserQuery, - matchRelationshipA, - matchRelationshipB + it_id('7079f0ef-47b3-4a1e-aac0-32654dadaa27')(it)( + 'should properly interpret a query v2', + done => { + const user = new Parse.User(); + user.set('username', 'foo'); + user.set('password', 'bar'); + return user + .save() + .then(user => { + const objIdQuery = new Parse.Query('_User').equalTo('objectId', user.id); + const blockedUserQuery = user.relation('blockedUsers').query(); + + const aResponseQuery = new Parse.Query('MatchRelationshipActivityResponse'); + aResponseQuery.equalTo('userA', user); + aResponseQuery.equalTo('userAResponse', 1); + + const bResponseQuery = new Parse.Query('MatchRelationshipActivityResponse'); + bResponseQuery.equalTo('userB', user); + bResponseQuery.equalTo('userBResponse', 1); + + const matchOr = Parse.Query.or(aResponseQuery, bResponseQuery); + const matchRelationshipA = new Parse.Query('_User'); + matchRelationshipA.matchesKeyInQuery('objectId', 'userAObjectId', matchOr); + const matchRelationshipB = new Parse.Query('_User'); + matchRelationshipB.matchesKeyInQuery('objectId', 'userBObjectId', matchOr); + + const orQuery = Parse.Query.or( + objIdQuery, + blockedUserQuery, + matchRelationshipA, + matchRelationshipB + ); + const query = new Parse.Query('_User'); + query.doesNotMatchQuery('objectId', orQuery); + return query.find(); + }) + .then( + () => { + done(); + }, + err => { + jfail(err); + fail('should not fail'); + done(); + } ); - const query = new Parse.Query('_User'); - query.doesNotMatchQuery('objectId', orQuery); - return query.find(); - }) - .then( - () => { - done(); - }, - err => { - jfail(err); - fail('should not fail'); - done(); - } - ); - }); + } + ); it('should match a key in an array (#3195)', function (done) { const AuthorObject = Parse.Object.extend('Author'); @@ -4084,48 +4103,51 @@ describe('Parse.Query testing', () => { }); }); - it_id('d95818c0-9e3c-41e6-be20-e7bafb59eefb')(it)('should find objects with array of pointers', done => { - const objects = []; - while (objects.length != 5) { - const object = new Parse.Object('ContainedObject'); - object.set('index', objects.length); - objects.push(object); - } + it_id('d95818c0-9e3c-41e6-be20-e7bafb59eefb')(it)( + 'should find objects with array of pointers', + done => { + const objects = []; + while (objects.length != 5) { + const object = new Parse.Object('ContainedObject'); + object.set('index', objects.length); + objects.push(object); + } - Parse.Object.saveAll(objects) - .then(objects => { - const container = new Parse.Object('Container'); - const pointers = objects.map(obj => { - return { - __type: 'Pointer', - className: 'ContainedObject', - objectId: obj.id, - }; + Parse.Object.saveAll(objects) + .then(objects => { + const container = new Parse.Object('Container'); + const pointers = objects.map(obj => { + return { + __type: 'Pointer', + className: 'ContainedObject', + objectId: obj.id, + }; + }); + container.set('objects', pointers); + const container2 = new Parse.Object('Container'); + container2.set('objects', pointers.slice(2, 3)); + return Parse.Object.saveAll([container, container2]); + }) + .then(() => { + const inQuery = new Parse.Query('ContainedObject'); + inQuery.greaterThanOrEqualTo('index', 1); + const query = new Parse.Query('Container'); + query.matchesQuery('objects', inQuery); + return query.find(); + }) + .then(results => { + if (results) { + expect(results.length).toBe(2); + } + done(); + }) + .catch(err => { + jfail(err); + fail('should not fail'); + done(); }); - container.set('objects', pointers); - const container2 = new Parse.Object('Container'); - container2.set('objects', pointers.slice(2, 3)); - return Parse.Object.saveAll([container, container2]); - }) - .then(() => { - const inQuery = new Parse.Query('ContainedObject'); - inQuery.greaterThanOrEqualTo('index', 1); - const query = new Parse.Query('Container'); - query.matchesQuery('objects', inQuery); - return query.find(); - }) - .then(results => { - if (results) { - expect(results.length).toBe(2); - } - done(); - }) - .catch(err => { - jfail(err); - fail('should not fail'); - done(); - }); - }); + } + ); it('query with two OR subqueries (regression test #1259)', done => { const relatedObject = new Parse.Object('Class2'); @@ -5015,48 +5037,51 @@ describe('Parse.Query testing', () => { equal(results[0].get('name'), group2.get('name')); }); - it_id('8886b994-fbb8-487d-a863-43bbd2b24b73')(it)('withJSON supports geoWithin.centerSphere', done => { - const inbound = new Parse.GeoPoint(1.5, 1.5); - const onbound = new Parse.GeoPoint(10, 10); - const outbound = new Parse.GeoPoint(20, 20); - const obj1 = new Parse.Object('TestObject', { location: inbound }); - const obj2 = new Parse.Object('TestObject', { location: onbound }); - const obj3 = new Parse.Object('TestObject', { location: outbound }); - const center = new Parse.GeoPoint(0, 0); - const distanceInKilometers = 1569 + 1; // 1569km is the approximate distance between {0, 0} and {10, 10}. - Parse.Object.saveAll([obj1, obj2, obj3]) - .then(() => { - const q = new Parse.Query(TestObject); - const jsonQ = q.toJSON(); - jsonQ.where.location = { - $geoWithin: { - $centerSphere: [center, distanceInKilometers / 6371.0], - }, - }; - q.withJSON(jsonQ); - return q.find(); - }) - .then(results => { - equal(results.length, 2); - const q = new Parse.Query(TestObject); - const jsonQ = q.toJSON(); - jsonQ.where.location = { - $geoWithin: { - $centerSphere: [[0, 0], distanceInKilometers / 6371.0], - }, - }; - q.withJSON(jsonQ); - return q.find(); - }) - .then(results => { - equal(results.length, 2); - done(); - }) - .catch(error => { - fail(error); - done(); - }); - }); + it_id('8886b994-fbb8-487d-a863-43bbd2b24b73')(it)( + 'withJSON supports geoWithin.centerSphere', + done => { + const inbound = new Parse.GeoPoint(1.5, 1.5); + const onbound = new Parse.GeoPoint(10, 10); + const outbound = new Parse.GeoPoint(20, 20); + const obj1 = new Parse.Object('TestObject', { location: inbound }); + const obj2 = new Parse.Object('TestObject', { location: onbound }); + const obj3 = new Parse.Object('TestObject', { location: outbound }); + const center = new Parse.GeoPoint(0, 0); + const distanceInKilometers = 1569 + 1; // 1569km is the approximate distance between {0, 0} and {10, 10}. + Parse.Object.saveAll([obj1, obj2, obj3]) + .then(() => { + const q = new Parse.Query(TestObject); + const jsonQ = q.toJSON(); + jsonQ.where.location = { + $geoWithin: { + $centerSphere: [center, distanceInKilometers / 6371.0], + }, + }; + q.withJSON(jsonQ); + return q.find(); + }) + .then(results => { + equal(results.length, 2); + const q = new Parse.Query(TestObject); + const jsonQ = q.toJSON(); + jsonQ.where.location = { + $geoWithin: { + $centerSphere: [[0, 0], distanceInKilometers / 6371.0], + }, + }; + q.withJSON(jsonQ); + return q.find(); + }) + .then(results => { + equal(results.length, 2); + done(); + }) + .catch(error => { + fail(error); + done(); + }); + } + ); it('withJSON with geoWithin.centerSphere fails without parameters', done => { const q = new Parse.Query(TestObject); @@ -5116,37 +5141,40 @@ describe('Parse.Query testing', () => { .catch(() => done()); }); - it_id('02d4e7e6-859a-4ab6-878d-135ccc77040e')(it)('can add new config to existing config', async () => { - await request({ - method: 'PUT', - url: 'http://localhost:8378/1/config', - json: true, - body: { - params: { - files: [{ __type: 'File', name: 'name', url: 'http://url' }], + it_id('02d4e7e6-859a-4ab6-878d-135ccc77040e')(it)( + 'can add new config to existing config', + async () => { + await request({ + method: 'PUT', + url: 'http://localhost:8378/1/config', + json: true, + body: { + params: { + files: [{ __type: 'File', name: 'name', url: 'http://url' }], + }, }, - }, - headers: masterKeyHeaders, - }); + headers: masterKeyHeaders, + }); - await request({ - method: 'PUT', - url: 'http://localhost:8378/1/config', - json: true, - body: { - params: { newConfig: 'good' }, - }, - headers: masterKeyHeaders, - }); + await request({ + method: 'PUT', + url: 'http://localhost:8378/1/config', + json: true, + body: { + params: { newConfig: 'good' }, + }, + headers: masterKeyHeaders, + }); - const result = await Parse.Config.get(); - equal(result.get('files')[0].toJSON(), { - __type: 'File', - name: 'name', - url: 'http://url', - }); - equal(result.get('newConfig'), 'good'); - }); + const result = await Parse.Config.get(); + equal(result.get('files')[0].toJSON(), { + __type: 'File', + name: 'name', + url: 'http://url', + }); + equal(result.get('newConfig'), 'good'); + } + ); it('can set object type key', async () => { const data = { bar: true, baz: 100 }; @@ -5284,7 +5312,10 @@ describe('Parse.Query testing', () => { }); Parse.CoreManager.setRESTController( - ParseServerRESTController(Parse.applicationId, ParseServer.promiseRouter({ appId: Parse.applicationId })) + ParseServerRESTController( + Parse.applicationId, + ParseServer.promiseRouter({ appId: Parse.applicationId }) + ) ); const user = new Parse.User(); @@ -5297,13 +5328,14 @@ describe('Parse.Query testing', () => { score.set('score', 1); await score.save(); - await new Parse.Query('_User') - .equalTo('objectId', user.id) - .eachBatch(async ([user]) => { + await new Parse.Query('_User').equalTo('objectId', user.id).eachBatch( + async ([user]) => { const score = await new Parse.Query('Score') .equalTo('player', user) .distinct('score', { useMasterKey: true }); expect(score).toEqual([1]); - }, { useMasterKey: true }); + }, + { useMasterKey: true } + ); }); }); diff --git a/spec/ParseRole.spec.js b/spec/ParseRole.spec.js index 35a91c6c15..0a9eb6df92 100644 --- a/spec/ParseRole.spec.js +++ b/spec/ParseRole.spec.js @@ -126,76 +126,79 @@ describe('Parse Role testing', () => { ); }); - it_id('b03abe32-e8e4-4666-9b81-9c804aa53400')(it)('should not recursively load the same role multiple times', done => { - const rootRole = 'RootRole'; - const roleNames = ['FooRole', 'BarRole', 'BazRole']; - const allRoles = [rootRole].concat(roleNames); - - const roleObjs = {}; - const createAllRoles = function (user) { - const promises = allRoles.map(function (roleName) { - return createRole(roleName, null, user).then(function (roleObj) { - roleObjs[roleName] = roleObj; - return roleObj; + it_id('b03abe32-e8e4-4666-9b81-9c804aa53400')(it)( + 'should not recursively load the same role multiple times', + done => { + const rootRole = 'RootRole'; + const roleNames = ['FooRole', 'BarRole', 'BazRole']; + const allRoles = [rootRole].concat(roleNames); + + const roleObjs = {}; + const createAllRoles = function (user) { + const promises = allRoles.map(function (roleName) { + return createRole(roleName, null, user).then(function (roleObj) { + roleObjs[roleName] = roleObj; + return roleObj; + }); }); - }); - return Promise.all(promises); - }; + return Promise.all(promises); + }; - const restExecute = spyOn(RestQuery._UnsafeRestQuery.prototype, 'execute').and.callThrough(); + const restExecute = spyOn(RestQuery._UnsafeRestQuery.prototype, 'execute').and.callThrough(); - let user, auth, getAllRolesSpy; - createTestUser() - .then(newUser => { - user = newUser; - return createAllRoles(user); - }) - .then(roles => { - const rootRoleObj = roleObjs[rootRole]; - roles.forEach(function (role, i) { - // Add all roles to the RootRole - if (role.id !== rootRoleObj.id) { - role.relation('roles').add(rootRoleObj); - } - // Add all "roleNames" roles to the previous role - if (i > 0) { - role.relation('roles').add(roles[i - 1]); - } - }); + let user, auth, getAllRolesSpy; + createTestUser() + .then(newUser => { + user = newUser; + return createAllRoles(user); + }) + .then(roles => { + const rootRoleObj = roleObjs[rootRole]; + roles.forEach(function (role, i) { + // Add all roles to the RootRole + if (role.id !== rootRoleObj.id) { + role.relation('roles').add(rootRoleObj); + } + // Add all "roleNames" roles to the previous role + if (i > 0) { + role.relation('roles').add(roles[i - 1]); + } + }); - return Parse.Object.saveAll(roles, { useMasterKey: true }); - }) - .then(() => { - auth = new Auth({ - config: Config.get('test'), - isMaster: true, - user: user, - }); - getAllRolesSpy = spyOn(auth, '_getAllRolesNamesForRoleIds').and.callThrough(); + return Parse.Object.saveAll(roles, { useMasterKey: true }); + }) + .then(() => { + auth = new Auth({ + config: Config.get('test'), + isMaster: true, + user: user, + }); + getAllRolesSpy = spyOn(auth, '_getAllRolesNamesForRoleIds').and.callThrough(); - return auth._loadRoles(); - }) - .then(roles => { - expect(roles.length).toEqual(4); + return auth._loadRoles(); + }) + .then(roles => { + expect(roles.length).toEqual(4); - allRoles.forEach(function (name) { - expect(roles.indexOf('role:' + name)).not.toBe(-1); - }); + allRoles.forEach(function (name) { + expect(roles.indexOf('role:' + name)).not.toBe(-1); + }); - // 1 Query for the initial setup - // 1 query for the parent roles - expect(restExecute.calls.count()).toEqual(2); + // 1 Query for the initial setup + // 1 query for the parent roles + expect(restExecute.calls.count()).toEqual(2); - // 1 call for the 1st layer of roles - // 1 call for the 2nd layer - expect(getAllRolesSpy.calls.count()).toEqual(2); - done(); - }) - .catch(() => { - fail('should succeed'); - done(); - }); - }); + // 1 call for the 1st layer of roles + // 1 call for the 2nd layer + expect(getAllRolesSpy.calls.count()).toEqual(2); + done(); + }) + .catch(() => { + fail('should succeed'); + done(); + }); + } + ); it('should recursively load roles', done => { testLoadRoles(Config.get('test'), done); diff --git a/spec/ParseServer.spec.js b/spec/ParseServer.spec.js index b55c6fc548..7d67aaa320 100644 --- a/spec/ParseServer.spec.js +++ b/spec/ParseServer.spec.js @@ -2,8 +2,8 @@ /* Tests for ParseServer.js */ const express = require('express'); const MongoStorageAdapter = require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter').default; -const PostgresStorageAdapter = require('../lib/Adapters/Storage/Postgres/PostgresStorageAdapter') - .default; +const PostgresStorageAdapter = + require('../lib/Adapters/Storage/Postgres/PostgresStorageAdapter').default; const ParseServer = require('../lib/ParseServer').default; const path = require('path'); const { spawn } = require('child_process'); diff --git a/spec/ParseServerRESTController.spec.js b/spec/ParseServerRESTController.spec.js index fbc244ab87..e881086faa 100644 --- a/spec/ParseServerRESTController.spec.js +++ b/spec/ParseServerRESTController.spec.js @@ -1,5 +1,5 @@ -const ParseServerRESTController = require('../lib/ParseServerRESTController') - .ParseServerRESTController; +const ParseServerRESTController = + require('../lib/ParseServerRESTController').ParseServerRESTController; const ParseServer = require('../lib/ParseServer').default; const Parse = require('parse/node').Parse; @@ -163,9 +163,7 @@ describe('ParseServerRESTController', () => { const results = await query.find(); expect(createSpy.calls.count()).toBe(2); for (let i = 0; i + 1 < createSpy.calls.length; i = i + 2) { - expect(createSpy.calls.argsFor(i)[3]).toBe( - createSpy.calls.argsFor(i + 1)[3] - ); + expect(createSpy.calls.argsFor(i)[3]).toBe(createSpy.calls.argsFor(i + 1)[3]); } expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']); }); @@ -660,16 +658,23 @@ describe('ParseServerRESTController', () => { data: { alert: 'We return status!' }, where: { deviceType: 'ios' }, }; - const res = await RESTController.request('POST', 'batch', { - requests: [{ - method: 'POST', - path: '/push', - body: payload, - }], - }, { - useMasterKey: true, - returnStatus: true, - }); + const res = await RESTController.request( + 'POST', + 'batch', + { + requests: [ + { + method: 'POST', + path: '/push', + body: payload, + }, + ], + }, + { + useMasterKey: true, + returnStatus: true, + } + ); const pushStatusId = res[0]._headers['X-Parse-Push-Status-Id']; expect(pushStatusId).toBeDefined(); diff --git a/spec/ParseUser.spec.js b/spec/ParseUser.spec.js index ba34fbf6e9..e43e018e05 100644 --- a/spec/ParseUser.spec.js +++ b/spec/ParseUser.spec.js @@ -2257,68 +2257,80 @@ describe('Parse.User testing', () => { }); describe('case insensitive signup not allowed', () => { - it_id('464eddc2-7a46-413d-888e-b43b040f1511')(it)('signup should fail with duplicate case insensitive username with basic setter', async () => { - const user = new Parse.User(); - user.set('username', 'test1'); - user.set('password', 'test'); - await user.signUp(); - - const user2 = new Parse.User(); - user2.set('username', 'Test1'); - user2.set('password', 'test'); - await expectAsync(user2.signUp()).toBeRejectedWith( - new Parse.Error(Parse.Error.USERNAME_TAKEN, 'Account already exists for this username.') - ); - }); + it_id('464eddc2-7a46-413d-888e-b43b040f1511')(it)( + 'signup should fail with duplicate case insensitive username with basic setter', + async () => { + const user = new Parse.User(); + user.set('username', 'test1'); + user.set('password', 'test'); + await user.signUp(); - it_id('1cef005b-d5f0-4699-af0c-bb0af27d2437')(it)('signup should fail with duplicate case insensitive username with field specific setter', async () => { - const user = new Parse.User(); - user.setUsername('test1'); - user.setPassword('test'); - await user.signUp(); + const user2 = new Parse.User(); + user2.set('username', 'Test1'); + user2.set('password', 'test'); + await expectAsync(user2.signUp()).toBeRejectedWith( + new Parse.Error(Parse.Error.USERNAME_TAKEN, 'Account already exists for this username.') + ); + } + ); - const user2 = new Parse.User(); - user2.setUsername('Test1'); - user2.setPassword('test'); - await expectAsync(user2.signUp()).toBeRejectedWith( - new Parse.Error(Parse.Error.USERNAME_TAKEN, 'Account already exists for this username.') - ); - }); + it_id('1cef005b-d5f0-4699-af0c-bb0af27d2437')(it)( + 'signup should fail with duplicate case insensitive username with field specific setter', + async () => { + const user = new Parse.User(); + user.setUsername('test1'); + user.setPassword('test'); + await user.signUp(); - it_id('12735529-98d1-42c0-b437-3b47fe78ddde')(it)('signup should fail with duplicate case insensitive email', async () => { - const user = new Parse.User(); - user.setUsername('test1'); - user.setPassword('test'); - user.setEmail('test@example.com'); - await user.signUp(); + const user2 = new Parse.User(); + user2.setUsername('Test1'); + user2.setPassword('test'); + await expectAsync(user2.signUp()).toBeRejectedWith( + new Parse.Error(Parse.Error.USERNAME_TAKEN, 'Account already exists for this username.') + ); + } + ); - const user2 = new Parse.User(); - user2.setUsername('test2'); - user2.setPassword('test'); - user2.setEmail('Test@Example.Com'); - await expectAsync(user2.signUp()).toBeRejectedWith( - new Parse.Error(Parse.Error.EMAIL_TAKEN, 'Account already exists for this email address.') - ); - }); + it_id('12735529-98d1-42c0-b437-3b47fe78ddde')(it)( + 'signup should fail with duplicate case insensitive email', + async () => { + const user = new Parse.User(); + user.setUsername('test1'); + user.setPassword('test'); + user.setEmail('test@example.com'); + await user.signUp(); - it_id('66e51d52-2420-4b62-8a0d-c7e1b384763e')(it)('edit should fail with duplicate case insensitive email', async () => { - const user = new Parse.User(); - user.setUsername('test1'); - user.setPassword('test'); - user.setEmail('test@example.com'); - await user.signUp(); + const user2 = new Parse.User(); + user2.setUsername('test2'); + user2.setPassword('test'); + user2.setEmail('Test@Example.Com'); + await expectAsync(user2.signUp()).toBeRejectedWith( + new Parse.Error(Parse.Error.EMAIL_TAKEN, 'Account already exists for this email address.') + ); + } + ); - const user2 = new Parse.User(); - user2.setUsername('test2'); - user2.setPassword('test'); - user2.setEmail('Foo@Example.Com'); - await user2.signUp(); + it_id('66e51d52-2420-4b62-8a0d-c7e1b384763e')(it)( + 'edit should fail with duplicate case insensitive email', + async () => { + const user = new Parse.User(); + user.setUsername('test1'); + user.setPassword('test'); + user.setEmail('test@example.com'); + await user.signUp(); - user2.setEmail('Test@Example.Com'); - await expectAsync(user2.save()).toBeRejectedWith( - new Parse.Error(Parse.Error.EMAIL_TAKEN, 'Account already exists for this email address.') - ); - }); + const user2 = new Parse.User(); + user2.setUsername('test2'); + user2.setPassword('test'); + user2.setEmail('Foo@Example.Com'); + await user2.signUp(); + + user2.setEmail('Test@Example.Com'); + await expectAsync(user2.save()).toBeRejectedWith( + new Parse.Error(Parse.Error.EMAIL_TAKEN, 'Account already exists for this email address.') + ); + } + ); describe('anonymous users', () => { it('should not fail on case insensitive matches', async () => { @@ -2952,119 +2964,125 @@ describe('Parse.User testing', () => { }); }); - it_id('1be98368-19ac-4c77-8531-762a114f43fb')(it)('should send email when upgrading from anon', async done => { - await reconfigureServer(); - let emailCalled = false; - let emailOptions; - const emailAdapter = { - sendVerificationEmail: options => { - emailOptions = options; - emailCalled = true; - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => Promise.resolve(), - }; - await reconfigureServer({ - appName: 'unused', - verifyUserEmails: true, - emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', - }); - // Simulate anonymous user save - return request({ - method: 'POST', - url: 'http://localhost:8378/1/classes/_User', - headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', - }, - body: { - authData: { - anonymous: { id: '00000000-0000-0000-0000-000000000001' }, + it_id('1be98368-19ac-4c77-8531-762a114f43fb')(it)( + 'should send email when upgrading from anon', + async done => { + await reconfigureServer(); + let emailCalled = false; + let emailOptions; + const emailAdapter = { + sendVerificationEmail: options => { + emailOptions = options; + emailCalled = true; }, - }, - }) - .then(response => { - const user = response.data; - return request({ - method: 'PUT', - url: 'http://localhost:8378/1/classes/_User/' + user.objectId, - headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Session-Token': user.sessionToken, - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', - }, - body: { - authData: { anonymous: null }, - username: 'user', - email: 'user@email.com', - password: 'password', + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => Promise.resolve(), + }; + await reconfigureServer({ + appName: 'unused', + verifyUserEmails: true, + emailAdapter: emailAdapter, + publicServerURL: 'http://localhost:8378/1', + }); + // Simulate anonymous user save + return request({ + method: 'POST', + url: 'http://localhost:8378/1/classes/_User', + headers: { + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', + }, + body: { + authData: { + anonymous: { id: '00000000-0000-0000-0000-000000000001' }, }, - }); - }) - .then(() => jasmine.timeout()) - .then(() => { - expect(emailCalled).toBe(true); - expect(emailOptions).not.toBeUndefined(); - expect(emailOptions.user.get('email')).toEqual('user@email.com'); - done(); + }, }) - .catch(err => { - jfail(err); - fail('no request should fail: ' + JSON.stringify(err)); - done(); - }); - }); + .then(response => { + const user = response.data; + return request({ + method: 'PUT', + url: 'http://localhost:8378/1/classes/_User/' + user.objectId, + headers: { + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Session-Token': user.sessionToken, + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', + }, + body: { + authData: { anonymous: null }, + username: 'user', + email: 'user@email.com', + password: 'password', + }, + }); + }) + .then(() => jasmine.timeout()) + .then(() => { + expect(emailCalled).toBe(true); + expect(emailOptions).not.toBeUndefined(); + expect(emailOptions.user.get('email')).toEqual('user@email.com'); + done(); + }) + .catch(err => { + jfail(err); + fail('no request should fail: ' + JSON.stringify(err)); + done(); + }); + } + ); - it_id('bf668670-39fa-44d3-a9a9-cad52f36d272')(it)('should not send email when email is not a string', async done => { - let emailCalled = false; - let emailOptions; - const emailAdapter = { - sendVerificationEmail: options => { - emailOptions = options; - emailCalled = true; - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => Promise.resolve(), - }; - await reconfigureServer({ - appName: 'unused', - verifyUserEmails: true, - emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', - }); - const user = new Parse.User(); - user.set('username', 'asdf@jkl.com'); - user.set('password', 'zxcv'); - user.set('email', 'asdf@jkl.com'); - await user.signUp(); - request({ - method: 'POST', - url: 'http://localhost:8378/1/requestPasswordReset', - headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Session-Token': user.sessionToken, - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', - }, - body: { - email: { $regex: '^asd' }, - }, - }) - .then(res => { - fail('no request should succeed: ' + JSON.stringify(res)); - done(); - }) - .catch(err => { - expect(emailCalled).toBeTruthy(); - expect(emailOptions).toBeDefined(); - expect(err.status).toBe(400); - expect(err.text).toMatch('{"code":125,"error":"you must provide a valid email string"}'); - done(); + it_id('bf668670-39fa-44d3-a9a9-cad52f36d272')(it)( + 'should not send email when email is not a string', + async done => { + let emailCalled = false; + let emailOptions; + const emailAdapter = { + sendVerificationEmail: options => { + emailOptions = options; + emailCalled = true; + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => Promise.resolve(), + }; + await reconfigureServer({ + appName: 'unused', + verifyUserEmails: true, + emailAdapter: emailAdapter, + publicServerURL: 'http://localhost:8378/1', }); - }); + const user = new Parse.User(); + user.set('username', 'asdf@jkl.com'); + user.set('password', 'zxcv'); + user.set('email', 'asdf@jkl.com'); + await user.signUp(); + request({ + method: 'POST', + url: 'http://localhost:8378/1/requestPasswordReset', + headers: { + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Session-Token': user.sessionToken, + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', + }, + body: { + email: { $regex: '^asd' }, + }, + }) + .then(res => { + fail('no request should succeed: ' + JSON.stringify(res)); + done(); + }) + .catch(err => { + expect(emailCalled).toBeTruthy(); + expect(emailOptions).toBeDefined(); + expect(err.status).toBe(400); + expect(err.text).toMatch('{"code":125,"error":"you must provide a valid email string"}'); + done(); + }); + } + ); it('should aftersave with full object', done => { let hit = 0; diff --git a/spec/PasswordPolicy.spec.js b/spec/PasswordPolicy.spec.js index 1fd2e6aa50..b8ad67c391 100644 --- a/spec/PasswordPolicy.spec.js +++ b/spec/PasswordPolicy.spec.js @@ -3,65 +3,68 @@ const request = require('../lib/request'); describe('Password Policy: ', () => { - it_id('b400a867-9f05-496f-af79-933aa588dde5')(it)('should show the invalid link page if the user clicks on the password reset link after the token expires', done => { - const user = new Parse.User(); - let sendEmailOptions; - const emailAdapter = { - sendVerificationEmail: () => Promise.resolve(), - sendPasswordResetEmail: options => { - sendEmailOptions = options; - }, - sendMail: () => {}, - }; - reconfigureServer({ - appName: 'passwordPolicy', - emailAdapter: emailAdapter, - passwordPolicy: { - resetTokenValidityDuration: 0.5, // 0.5 second - }, - publicServerURL: 'http://localhost:8378/1', - }) - .then(() => { - user.setUsername('testResetTokenValidity'); - user.setPassword('original'); - user.set('email', 'user@parse.com'); - return user.signUp(); + it_id('b400a867-9f05-496f-af79-933aa588dde5')(it)( + 'should show the invalid link page if the user clicks on the password reset link after the token expires', + done => { + const user = new Parse.User(); + let sendEmailOptions; + const emailAdapter = { + sendVerificationEmail: () => Promise.resolve(), + sendPasswordResetEmail: options => { + sendEmailOptions = options; + }, + sendMail: () => {}, + }; + reconfigureServer({ + appName: 'passwordPolicy', + emailAdapter: emailAdapter, + passwordPolicy: { + resetTokenValidityDuration: 0.5, // 0.5 second + }, + publicServerURL: 'http://localhost:8378/1', }) - .then(() => { - Parse.User.requestPasswordReset('user@parse.com').catch(err => { + .then(() => { + user.setUsername('testResetTokenValidity'); + user.setPassword('original'); + user.set('email', 'user@parse.com'); + return user.signUp(); + }) + .then(() => { + Parse.User.requestPasswordReset('user@parse.com').catch(err => { + jfail(err); + fail('Reset password request should not fail'); + done(); + }); + }) + .then(() => { + // wait for a bit more than the validity duration set + setTimeout(() => { + expect(sendEmailOptions).not.toBeUndefined(); + + request({ + url: sendEmailOptions.link, + followRedirects: false, + simple: false, + resolveWithFullResponse: true, + }) + .then(response => { + expect(response.status).toEqual(302); + expect(response.text).toEqual( + 'Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html' + ); + done(); + }) + .catch(error => { + fail(error); + }); + }, 1000); + }) + .catch(err => { jfail(err); - fail('Reset password request should not fail'); done(); }); - }) - .then(() => { - // wait for a bit more than the validity duration set - setTimeout(() => { - expect(sendEmailOptions).not.toBeUndefined(); - - request({ - url: sendEmailOptions.link, - followRedirects: false, - simple: false, - resolveWithFullResponse: true, - }) - .then(response => { - expect(response.status).toEqual(302); - expect(response.text).toEqual( - 'Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html' - ); - done(); - }) - .catch(error => { - fail(error); - }); - }, 1000); - }) - .catch(err => { - jfail(err); - done(); - }); - }); + } + ); it('should show the reset password page if the user clicks on the password reset link before the token expires', done => { const user = new Parse.User(); @@ -107,7 +110,8 @@ describe('Password Policy: ', () => { }) .then(response => { expect(response.status).toEqual(302); - const re = /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=[a-zA-Z0-9]+\&id=test\&/; + const re = + /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=[a-zA-Z0-9]+\&id=test\&/; expect(response.text.match(re)).not.toBe(null); done(); }) @@ -150,34 +154,37 @@ describe('Password Policy: ', () => { done(); }); - it_id('7d98e1f2-ae89-4038-9ea7-5254854ea42e')(it)('should keep reset token with resetTokenReuseIfValid', async done => { - const sendEmailOptions = []; - const emailAdapter = { - sendVerificationEmail: () => Promise.resolve(), - sendPasswordResetEmail: options => { - sendEmailOptions.push(options); - }, - sendMail: () => {}, - }; - await reconfigureServer({ - appName: 'passwordPolicy', - emailAdapter: emailAdapter, - passwordPolicy: { - resetTokenValidityDuration: 5 * 60, // 5 minutes - resetTokenReuseIfValid: true, - }, - publicServerURL: 'http://localhost:8378/1', - }); - const user = new Parse.User(); - user.setUsername('testResetTokenValidity'); - user.setPassword('original'); - user.set('email', 'user@example.com'); - await user.signUp(); - await Parse.User.requestPasswordReset('user@example.com'); - await Parse.User.requestPasswordReset('user@example.com'); - expect(sendEmailOptions[0].link).toBe(sendEmailOptions[1].link); - done(); - }); + it_id('7d98e1f2-ae89-4038-9ea7-5254854ea42e')(it)( + 'should keep reset token with resetTokenReuseIfValid', + async done => { + const sendEmailOptions = []; + const emailAdapter = { + sendVerificationEmail: () => Promise.resolve(), + sendPasswordResetEmail: options => { + sendEmailOptions.push(options); + }, + sendMail: () => {}, + }; + await reconfigureServer({ + appName: 'passwordPolicy', + emailAdapter: emailAdapter, + passwordPolicy: { + resetTokenValidityDuration: 5 * 60, // 5 minutes + resetTokenReuseIfValid: true, + }, + publicServerURL: 'http://localhost:8378/1', + }); + const user = new Parse.User(); + user.setUsername('testResetTokenValidity'); + user.setPassword('original'); + user.set('email', 'user@example.com'); + await user.signUp(); + await Parse.User.requestPasswordReset('user@example.com'); + await Parse.User.requestPasswordReset('user@example.com'); + expect(sendEmailOptions[0].link).toBe(sendEmailOptions[1].link); + done(); + } + ); it('should throw with invalid resetTokenReuseIfValid', async done => { const sendEmailOptions = []; @@ -622,7 +629,8 @@ describe('Password Policy: ', () => { }) .then(response => { expect(response.status).toEqual(302); - const re = /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; + const re = + /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; const match = response.text.match(re); if (!match) { fail('should have a token'); @@ -714,7 +722,8 @@ describe('Password Policy: ', () => { }) .then(response => { expect(response.status).toEqual(302); - const re = /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; + const re = + /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; const match = response.text.match(re); if (!match) { fail('should have a token'); @@ -900,7 +909,8 @@ describe('Password Policy: ', () => { }) .then(response => { expect(response.status).toEqual(302); - const re = /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; + const re = + /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; const match = response.text.match(re); if (!match) { fail('should have a token'); @@ -991,7 +1001,8 @@ describe('Password Policy: ', () => { resolveWithFullResponse: true, }); expect(response.status).toEqual(302); - const re = /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; + const re = + /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; const match = response.text.match(re); if (!match) { fail('should have a token'); @@ -1051,7 +1062,8 @@ describe('Password Policy: ', () => { }) .then(response => { expect(response.status).toEqual(302); - const re = /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; + const re = + /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; const match = response.text.match(re); if (!match) { fail('should have a token'); @@ -1164,248 +1176,261 @@ describe('Password Policy: ', () => { }); }); - it_id('d7d0a93e-efe6-48c0-b622-0f7fb570ccc1')(it)('should succeed if logged in before password expires', done => { - const user = new Parse.User(); - reconfigureServer({ - appName: 'passwordPolicy', - passwordPolicy: { - maxPasswordAge: 1, // 1 day - }, - publicServerURL: 'http://localhost:8378/1', - }).then(() => { - user.setUsername('user1'); - user.setPassword('user1'); - user.set('email', 'user1@parse.com'); - user - .signUp() - .then(() => { - Parse.User.logIn('user1', 'user1') - .then(() => { - done(); - }) - .catch(error => { - jfail(error); - fail('Login should have succeeded before password expiry.'); - done(); - }); - }) - .catch(error => { - jfail(error); - fail('Signup failed.'); - done(); - }); - }); - }); - - it_id('22428408-8763-445d-9833-2b2d92008f62')(it)('should fail if logged in after password expires', done => { - const user = new Parse.User(); - reconfigureServer({ - appName: 'passwordPolicy', - passwordPolicy: { - maxPasswordAge: 0.5 / (24 * 60 * 60), // 0.5 sec - }, - publicServerURL: 'http://localhost:8378/1', - }).then(() => { - user.setUsername('user1'); - user.setPassword('user1'); - user.set('email', 'user1@parse.com'); - user - .signUp() - .then(() => { - // wait for a bit more than the validity duration set - setTimeout(() => { + it_id('d7d0a93e-efe6-48c0-b622-0f7fb570ccc1')(it)( + 'should succeed if logged in before password expires', + done => { + const user = new Parse.User(); + reconfigureServer({ + appName: 'passwordPolicy', + passwordPolicy: { + maxPasswordAge: 1, // 1 day + }, + publicServerURL: 'http://localhost:8378/1', + }).then(() => { + user.setUsername('user1'); + user.setPassword('user1'); + user.set('email', 'user1@parse.com'); + user + .signUp() + .then(() => { Parse.User.logIn('user1', 'user1') .then(() => { - fail('logIn should have failed'); done(); }) .catch(error => { - expect(error.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); - expect(error.message).toEqual( - 'Your password has expired. Please reset your password.' - ); + jfail(error); + fail('Login should have succeeded before password expiry.'); done(); }); - }, 1000); - }) - .catch(error => { - jfail(error); - fail('Signup failed.'); - done(); - }); - }); - }); - - it_id('cc97a109-e35f-4f94-b942-3a6134921cdd')(it)('should apply password expiry policy to existing user upon first login after policy is enabled', done => { - const user = new Parse.User(); - reconfigureServer({ - appName: 'passwordPolicy', - publicServerURL: 'http://localhost:8378/1', - }).then(() => { - user.setUsername('user1'); - user.setPassword('user1'); - user.set('email', 'user1@parse.com'); - user - .signUp() - .then(() => { - Parse.User.logOut() - .then(() => { - reconfigureServer({ - appName: 'passwordPolicy', - passwordPolicy: { - maxPasswordAge: 0.5 / (24 * 60 * 60), // 0.5 sec - }, - publicServerURL: 'http://localhost:8378/1', - }).then(() => { - Parse.User.logIn('user1', 'user1') - .then(() => { - Parse.User.logOut() - .then(() => { - // wait for a bit more than the validity duration set - setTimeout(() => { - Parse.User.logIn('user1', 'user1') - .then(() => { - fail('logIn should have failed'); - done(); - }) - .catch(error => { - expect(error.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); - expect(error.message).toEqual( - 'Your password has expired. Please reset your password.' - ); - done(); - }); - }, 2000); - }) - .catch(error => { - jfail(error); - fail('logout should have succeeded'); - done(); - }); - }) - .catch(error => { - jfail(error); - fail('Login failed.'); - done(); - }); - }); - }) - .catch(error => { - jfail(error); - fail('logout should have succeeded'); - done(); - }); - }) - .catch(error => { - jfail(error); - fail('Signup failed.'); - done(); - }); - }); - }); - - it_id('d1e6ab9d-c091-4fea-b952-08b7484bfc89')(it)('should reset password timestamp when password is reset', done => { - const user = new Parse.User(); - const emailAdapter = { - sendVerificationEmail: () => Promise.resolve(), - sendPasswordResetEmail: options => { - request({ - url: options.link, - followRedirects: false, - simple: false, - resolveWithFullResponse: true, - }) - .then(response => { - expect(response.status).toEqual(302); - const re = /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; - const match = response.text.match(re); - if (!match) { - fail('should have a token'); - done(); - return; - } - const token = match[1]; + }) + .catch(error => { + jfail(error); + fail('Signup failed.'); + done(); + }); + }); + } + ); - request({ - method: 'POST', - url: 'http://localhost:8378/1/apps/test/request_password_reset', - body: `new_password=uuser11&token=${token}`, - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - followRedirects: false, - simple: false, - resolveWithFullResponse: true, - }) - .then(response => { - expect(response.status).toEqual(302); - expect(response.text).toEqual( - 'Found. Redirecting to http://localhost:8378/1/apps/password_reset_success.html' - ); + it_id('22428408-8763-445d-9833-2b2d92008f62')(it)( + 'should fail if logged in after password expires', + done => { + const user = new Parse.User(); + reconfigureServer({ + appName: 'passwordPolicy', + passwordPolicy: { + maxPasswordAge: 0.5 / (24 * 60 * 60), // 0.5 sec + }, + publicServerURL: 'http://localhost:8378/1', + }).then(() => { + user.setUsername('user1'); + user.setPassword('user1'); + user.set('email', 'user1@parse.com'); + user + .signUp() + .then(() => { + // wait for a bit more than the validity duration set + setTimeout(() => { + Parse.User.logIn('user1', 'user1') + .then(() => { + fail('logIn should have failed'); + done(); + }) + .catch(error => { + expect(error.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); + expect(error.message).toEqual( + 'Your password has expired. Please reset your password.' + ); + done(); + }); + }, 1000); + }) + .catch(error => { + jfail(error); + fail('Signup failed.'); + done(); + }); + }); + } + ); - Parse.User.logIn('user1', 'uuser11') - .then(function () { - done(); - }) - .catch(err => { - jfail(err); - fail('should login with new password'); - done(); - }); + it_id('cc97a109-e35f-4f94-b942-3a6134921cdd')(it)( + 'should apply password expiry policy to existing user upon first login after policy is enabled', + done => { + const user = new Parse.User(); + reconfigureServer({ + appName: 'passwordPolicy', + publicServerURL: 'http://localhost:8378/1', + }).then(() => { + user.setUsername('user1'); + user.setPassword('user1'); + user.set('email', 'user1@parse.com'); + user + .signUp() + .then(() => { + Parse.User.logOut() + .then(() => { + reconfigureServer({ + appName: 'passwordPolicy', + passwordPolicy: { + maxPasswordAge: 0.5 / (24 * 60 * 60), // 0.5 sec + }, + publicServerURL: 'http://localhost:8378/1', + }).then(() => { + Parse.User.logIn('user1', 'user1') + .then(() => { + Parse.User.logOut() + .then(() => { + // wait for a bit more than the validity duration set + setTimeout(() => { + Parse.User.logIn('user1', 'user1') + .then(() => { + fail('logIn should have failed'); + done(); + }) + .catch(error => { + expect(error.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); + expect(error.message).toEqual( + 'Your password has expired. Please reset your password.' + ); + done(); + }); + }, 2000); + }) + .catch(error => { + jfail(error); + fail('logout should have succeeded'); + done(); + }); + }) + .catch(error => { + jfail(error); + fail('Login failed.'); + done(); + }); + }); }) .catch(error => { jfail(error); - fail('Failed to POST request password reset'); + fail('logout should have succeeded'); + done(); }); }) .catch(error => { jfail(error); - fail('Failed to get the reset link'); + fail('Signup failed.'); + done(); }); - }, - sendMail: () => {}, - }; - reconfigureServer({ - appName: 'passwordPolicy', - emailAdapter: emailAdapter, - passwordPolicy: { - maxPasswordAge: 0.5 / (24 * 60 * 60), // 0.5 sec - }, - publicServerURL: 'http://localhost:8378/1', - }).then(() => { - user.setUsername('user1'); - user.setPassword('user1'); - user.set('email', 'user1@parse.com'); - user - .signUp() - .then(() => { - // wait for a bit more than the validity duration set - setTimeout(() => { - Parse.User.logIn('user1', 'user1') - .then(() => { - fail('logIn should have failed'); + }); + } + ); + + it_id('d1e6ab9d-c091-4fea-b952-08b7484bfc89')(it)( + 'should reset password timestamp when password is reset', + done => { + const user = new Parse.User(); + const emailAdapter = { + sendVerificationEmail: () => Promise.resolve(), + sendPasswordResetEmail: options => { + request({ + url: options.link, + followRedirects: false, + simple: false, + resolveWithFullResponse: true, + }) + .then(response => { + expect(response.status).toEqual(302); + const re = + /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; + const match = response.text.match(re); + if (!match) { + fail('should have a token'); done(); + return; + } + const token = match[1]; + + request({ + method: 'POST', + url: 'http://localhost:8378/1/apps/test/request_password_reset', + body: `new_password=uuser11&token=${token}`, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + followRedirects: false, + simple: false, + resolveWithFullResponse: true, }) - .catch(error => { - expect(error.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); - expect(error.message).toEqual( - 'Your password has expired. Please reset your password.' - ); - Parse.User.requestPasswordReset('user1@parse.com').catch(err => { - jfail(err); - fail('Reset password request should not fail'); + .then(response => { + expect(response.status).toEqual(302); + expect(response.text).toEqual( + 'Found. Redirecting to http://localhost:8378/1/apps/password_reset_success.html' + ); + + Parse.User.logIn('user1', 'uuser11') + .then(function () { + done(); + }) + .catch(err => { + jfail(err); + fail('should login with new password'); + done(); + }); + }) + .catch(error => { + jfail(error); + fail('Failed to POST request password reset'); + }); + }) + .catch(error => { + jfail(error); + fail('Failed to get the reset link'); + }); + }, + sendMail: () => {}, + }; + reconfigureServer({ + appName: 'passwordPolicy', + emailAdapter: emailAdapter, + passwordPolicy: { + maxPasswordAge: 0.5 / (24 * 60 * 60), // 0.5 sec + }, + publicServerURL: 'http://localhost:8378/1', + }).then(() => { + user.setUsername('user1'); + user.setPassword('user1'); + user.set('email', 'user1@parse.com'); + user + .signUp() + .then(() => { + // wait for a bit more than the validity duration set + setTimeout(() => { + Parse.User.logIn('user1', 'user1') + .then(() => { + fail('logIn should have failed'); done(); + }) + .catch(error => { + expect(error.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); + expect(error.message).toEqual( + 'Your password has expired. Please reset your password.' + ); + Parse.User.requestPasswordReset('user1@parse.com').catch(err => { + jfail(err); + fail('Reset password request should not fail'); + done(); + }); }); - }); - }, 1000); - }) - .catch(error => { - jfail(error); - fail('Signup failed.'); - done(); - }); - }); - }); + }, 1000); + }) + .catch(error => { + jfail(error); + fail('Signup failed.'); + done(); + }); + }); + } + ); it('should fail if passwordPolicy.maxPasswordHistory is not a number', done => { reconfigureServer({ @@ -1472,7 +1497,8 @@ describe('Password Policy: ', () => { }) .then(response => { expect(response.status).toEqual(302); - const re = /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; + const re = + /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; const match = response.text.match(re); if (!match) { fail('should have a token'); diff --git a/spec/PointerPermissions.spec.js b/spec/PointerPermissions.spec.js index a4cf43899d..94257681c5 100644 --- a/spec/PointerPermissions.spec.js +++ b/spec/PointerPermissions.spec.js @@ -226,59 +226,62 @@ describe('Pointer Permissions', () => { }); }); - it_id('f38c35e7-d804-4d32-986d-2579e25d2461')(it)('should query on pointer permission enabled column', done => { - const config = Config.get(Parse.applicationId); - const user = new Parse.User(); - const user2 = new Parse.User(); - user.set({ - username: 'user1', - password: 'password', - }); - user2.set({ - username: 'user2', - password: 'password', - }); - const obj = new Parse.Object('AnObject'); - const obj2 = new Parse.Object('AnObject'); - user - .signUp() - .then(() => { - return user2.signUp(); - }) - .then(() => { - Parse.User.logOut(); - }) - .then(() => { - obj.set('owner', user); - return Parse.Object.saveAll([obj, obj2]); - }) - .then(() => { - return config.database.loadSchema().then(schema => { - return schema.updateClass( - 'AnObject', - {}, - { find: {}, get: {}, readUserFields: ['owner'] } - ); + it_id('f38c35e7-d804-4d32-986d-2579e25d2461')(it)( + 'should query on pointer permission enabled column', + done => { + const config = Config.get(Parse.applicationId); + const user = new Parse.User(); + const user2 = new Parse.User(); + user.set({ + username: 'user1', + password: 'password', + }); + user2.set({ + username: 'user2', + password: 'password', + }); + const obj = new Parse.Object('AnObject'); + const obj2 = new Parse.Object('AnObject'); + user + .signUp() + .then(() => { + return user2.signUp(); + }) + .then(() => { + Parse.User.logOut(); + }) + .then(() => { + obj.set('owner', user); + return Parse.Object.saveAll([obj, obj2]); + }) + .then(() => { + return config.database.loadSchema().then(schema => { + return schema.updateClass( + 'AnObject', + {}, + { find: {}, get: {}, readUserFields: ['owner'] } + ); + }); + }) + .then(() => { + return Parse.User.logIn('user1', 'password'); + }) + .then(() => { + const q = new Parse.Query('AnObject'); + q.equalTo('owner', user2); + return q.find(); + }) + .then(res => { + expect(res.length).toBe(0); + done(); + }) + .catch(err => { + jfail(err); + fail('should not fail'); + done(); }); - }) - .then(() => { - return Parse.User.logIn('user1', 'password'); - }) - .then(() => { - const q = new Parse.Query('AnObject'); - q.equalTo('owner', user2); - return q.find(); - }) - .then(res => { - expect(res.length).toBe(0); - done(); - }) - .catch(err => { - jfail(err); - fail('should not fail'); - done(); - }); - }); + } + ); it('should not allow creating objects', done => { const config = Config.get(Parse.applicationId); @@ -1203,50 +1206,53 @@ describe('Pointer Permissions', () => { done(); }); - it_id('8a7d188c-b75c-4eac-90b6-9b0b11f873ae')(it)('should query on pointer permission enabled column', async done => { - const config = Config.get(Parse.applicationId); - const user = new Parse.User(); - const user2 = new Parse.User(); - const user3 = new Parse.User(); - user.set({ - username: 'user1', - password: 'password', - }); - user2.set({ - username: 'user2', - password: 'password', - }); - user3.set({ - username: 'user3', - password: 'password', - }); - const obj = new Parse.Object('AnObject'); - const obj2 = new Parse.Object('AnObject'); + it_id('8a7d188c-b75c-4eac-90b6-9b0b11f873ae')(it)( + 'should query on pointer permission enabled column', + async done => { + const config = Config.get(Parse.applicationId); + const user = new Parse.User(); + const user2 = new Parse.User(); + const user3 = new Parse.User(); + user.set({ + username: 'user1', + password: 'password', + }); + user2.set({ + username: 'user2', + password: 'password', + }); + user3.set({ + username: 'user3', + password: 'password', + }); + const obj = new Parse.Object('AnObject'); + const obj2 = new Parse.Object('AnObject'); - await user.signUp(); - await user2.signUp(); - await user3.signUp(); - await Parse.User.logOut(); + await user.signUp(); + await user2.signUp(); + await user3.signUp(); + await Parse.User.logOut(); - obj.set('owners', [user, user2]); - await Parse.Object.saveAll([obj, obj2]); + obj.set('owners', [user, user2]); + await Parse.Object.saveAll([obj, obj2]); - const schema = await config.database.loadSchema(); - await schema.updateClass('AnObject', {}, { find: {}, get: {}, readUserFields: ['owners'] }); + const schema = await config.database.loadSchema(); + await schema.updateClass('AnObject', {}, { find: {}, get: {}, readUserFields: ['owners'] }); - for (const owner of ['user1', 'user2']) { - await Parse.User.logIn(owner, 'password'); - try { - const q = new Parse.Query('AnObject'); - q.equalTo('owners', user3); - const result = await q.find(); - expect(result.length).toBe(0); - } catch (err) { - done.fail('should not fail'); + for (const owner of ['user1', 'user2']) { + await Parse.User.logIn(owner, 'password'); + try { + const q = new Parse.Query('AnObject'); + q.equalTo('owners', user3); + const result = await q.find(); + expect(result.length).toBe(0); + } catch (err) { + done.fail('should not fail'); + } } + done(); } - done(); - }); + ); it('should not query using arrays on pointer permission enabled column', async done => { const config = Config.get(Parse.applicationId); @@ -2058,18 +2064,21 @@ describe('Pointer Permissions', () => { done(); }); - it_id('9ba681d5-59f5-4996-b36d-6647d23e6a44')(it)('should fail for user not listed', async done => { - await updateCLP({ - get: { - pointerFields: ['owner'], - }, - }); + it_id('9ba681d5-59f5-4996-b36d-6647d23e6a44')(it)( + 'should fail for user not listed', + async done => { + await updateCLP({ + get: { + pointerFields: ['owner'], + }, + }); - await logIn(user2); + await logIn(user2); - await expectAsync(actionGet(obj1.id)).toBeRejectedWith(OBJECT_NOT_FOUND); - done(); - }); + await expectAsync(actionGet(obj1.id)).toBeRejectedWith(OBJECT_NOT_FOUND); + done(); + } + ); it('should not allow other actions', async done => { await updateCLP({ @@ -2218,18 +2227,21 @@ describe('Pointer Permissions', () => { done(); }); - it_id('bcdb158d-c0b6-45e3-84ab-a3636f7cb470')(it)('should fail for user not listed', async done => { - await updateCLP({ - update: { - pointerFields: ['owner'], - }, - }); + it_id('bcdb158d-c0b6-45e3-84ab-a3636f7cb470')(it)( + 'should fail for user not listed', + async done => { + await updateCLP({ + update: { + pointerFields: ['owner'], + }, + }); - await logIn(user2); + await logIn(user2); - await expectAsync(actionUpdate(obj1)).toBeRejectedWith(OBJECT_NOT_FOUND); - done(); - }); + await expectAsync(actionUpdate(obj1)).toBeRejectedWith(OBJECT_NOT_FOUND); + done(); + } + ); it('should not allow other actions', async done => { await updateCLP({ @@ -2270,18 +2282,21 @@ describe('Pointer Permissions', () => { done(); }); - it_id('70aa3853-6e26-4c38-a927-2ddb24ced7d4')(it)('should fail for user not listed', async done => { - await updateCLP({ - delete: { - pointerFields: ['owner'], - }, - }); + it_id('70aa3853-6e26-4c38-a927-2ddb24ced7d4')(it)( + 'should fail for user not listed', + async done => { + await updateCLP({ + delete: { + pointerFields: ['owner'], + }, + }); - await logIn(user2); + await logIn(user2); - await expectAsync(actionDelete(obj1)).toBeRejectedWith(OBJECT_NOT_FOUND); - done(); - }); + await expectAsync(actionDelete(obj1)).toBeRejectedWith(OBJECT_NOT_FOUND); + done(); + } + ); it('should not allow other actions', async done => { await updateCLP({ @@ -2517,18 +2532,21 @@ describe('Pointer Permissions', () => { done(); }); - it_id('84a42339-c7b5-4735-a431-57b46535b073')(it)('should fail for user not listed', async done => { - await updateCLP({ - get: { - pointerFields: ['moderators'], - }, - }); + it_id('84a42339-c7b5-4735-a431-57b46535b073')(it)( + 'should fail for user not listed', + async done => { + await updateCLP({ + get: { + pointerFields: ['moderators'], + }, + }); - await logIn(user1); + await logIn(user1); - await expectAsync(actionGet(obj3.id)).toBeRejectedWith(OBJECT_NOT_FOUND); - done(); - }); + await expectAsync(actionGet(obj3.id)).toBeRejectedWith(OBJECT_NOT_FOUND); + done(); + } + ); it('should not allow other actions', async done => { await updateCLP({ @@ -2685,31 +2703,37 @@ describe('Pointer Permissions', () => { done(); }); - it_id('2b19234a-a471-48b4-bd1a-27bd286d066f')(it)('should be allowed (multiple users in array)', async done => { - await updateCLP({ - update: { - pointerFields: ['moderators'], - }, - }); + it_id('2b19234a-a471-48b4-bd1a-27bd286d066f')(it)( + 'should be allowed (multiple users in array)', + async done => { + await updateCLP({ + update: { + pointerFields: ['moderators'], + }, + }); - await logIn(user2); + await logIn(user2); - await expectAsync(actionUpdate(obj1)).toBeResolved(); - done(); - }); + await expectAsync(actionUpdate(obj1)).toBeResolved(); + done(); + } + ); - it_id('1abb9f4a-fb24-48c7-8025-3001d6cf8737')(it)('should fail for user not listed', async done => { - await updateCLP({ - update: { - pointerFields: ['moderators'], - }, - }); + it_id('1abb9f4a-fb24-48c7-8025-3001d6cf8737')(it)( + 'should fail for user not listed', + async done => { + await updateCLP({ + update: { + pointerFields: ['moderators'], + }, + }); - await logIn(user2); + await logIn(user2); - await expectAsync(actionUpdate(obj3)).toBeRejectedWith(OBJECT_NOT_FOUND); - done(); - }); + await expectAsync(actionUpdate(obj3)).toBeRejectedWith(OBJECT_NOT_FOUND); + done(); + } + ); it('should not allow other actions', async done => { await updateCLP({ @@ -2764,18 +2788,21 @@ describe('Pointer Permissions', () => { done(); }); - it_id('3175a0e3-e51e-4b84-a2e6-50bbcc582123')(it)('should fail for user not listed', async done => { - await updateCLP({ - delete: { - pointerFields: ['owners'], - }, - }); + it_id('3175a0e3-e51e-4b84-a2e6-50bbcc582123')(it)( + 'should fail for user not listed', + async done => { + await updateCLP({ + delete: { + pointerFields: ['owners'], + }, + }); - await logIn(user1); + await logIn(user1); - await expectAsync(actionDelete(obj3)).toBeRejectedWith(OBJECT_NOT_FOUND); - done(); - }); + await expectAsync(actionDelete(obj3)).toBeRejectedWith(OBJECT_NOT_FOUND); + done(); + } + ); it('should not allow other actions', async done => { await updateCLP({ @@ -2874,22 +2901,25 @@ describe('Pointer Permissions', () => { done(); }); - it_id('51e896e9-73b3-404f-b5ff-bdb99005a9f7')(it)('should be restricted when updating object without addField permission', async done => { - await updateCLP({ - update: { - '*': true, - }, - addField: { - pointerFields: ['moderators'], - }, - }); + it_id('51e896e9-73b3-404f-b5ff-bdb99005a9f7')(it)( + 'should be restricted when updating object without addField permission', + async done => { + await updateCLP({ + update: { + '*': true, + }, + addField: { + pointerFields: ['moderators'], + }, + }); - await logIn(user1); + await logIn(user1); - await expectAsync(actionAddFieldOnUpdate(obj2)).toBeRejectedWith(OBJECT_NOT_FOUND); + await expectAsync(actionAddFieldOnUpdate(obj2)).toBeRejectedWith(OBJECT_NOT_FOUND); - done(); - }); + done(); + } + ); }); }); @@ -2946,44 +2976,50 @@ describe('Pointer Permissions', () => { await initialize(); }); - it_id('b43db366-8cce-4a11-9cf2-eeee9603d40b')(it)('should not limit the scope of grouped read permissions', async done => { - await updateCLP({ - get: { - pointerFields: ['owner'], - }, - readUserFields: ['moderators'], - }); + it_id('b43db366-8cce-4a11-9cf2-eeee9603d40b')(it)( + 'should not limit the scope of grouped read permissions', + async done => { + await updateCLP({ + get: { + pointerFields: ['owner'], + }, + readUserFields: ['moderators'], + }); - await logIn(user2); + await logIn(user2); - await expectAsync(actionGet(obj1.id)).toBeResolved(); + await expectAsync(actionGet(obj1.id)).toBeResolved(); - const found = await actionFind(); - expect(found.length).toBe(2); + const found = await actionFind(); + expect(found.length).toBe(2); - const counted = await actionCount(); - expect(counted).toBe(2); + const counted = await actionCount(); + expect(counted).toBe(2); - done(); - }); + done(); + } + ); - it_id('bbb1686d-0e2a-4365-8b64-b5faa3e7b9cf')(it)('should not limit the scope of grouped write permissions', async done => { - await updateCLP({ - update: { - pointerFields: ['owner'], - }, - writeUserFields: ['moderators'], - }); + it_id('bbb1686d-0e2a-4365-8b64-b5faa3e7b9cf')(it)( + 'should not limit the scope of grouped write permissions', + async done => { + await updateCLP({ + update: { + pointerFields: ['owner'], + }, + writeUserFields: ['moderators'], + }); - await logIn(user2); + await logIn(user2); - await expectAsync(actionUpdate(obj1)).toBeResolved(); - await expectAsync(actionAddFieldOnUpdate(obj1)).toBeResolved(); - await expectAsync(actionDelete(obj1)).toBeResolved(); - // [create] and [addField on create] can't be enabled with pointer by design + await expectAsync(actionUpdate(obj1)).toBeResolved(); + await expectAsync(actionAddFieldOnUpdate(obj1)).toBeResolved(); + await expectAsync(actionDelete(obj1)).toBeResolved(); + // [create] and [addField on create] can't be enabled with pointer by design - done(); - }); + done(); + } + ); it('should not inherit scope of grouped read permissions from another field', async done => { await updateCLP({ diff --git a/spec/PostgresInitOptions.spec.js b/spec/PostgresInitOptions.spec.js index 1e3282ad77..2af94e9fc3 100644 --- a/spec/PostgresInitOptions.spec.js +++ b/spec/PostgresInitOptions.spec.js @@ -1,6 +1,6 @@ const Parse = require('parse/node').Parse; -const PostgresStorageAdapter = require('../lib/Adapters/Storage/Postgres/PostgresStorageAdapter') - .default; +const PostgresStorageAdapter = + require('../lib/Adapters/Storage/Postgres/PostgresStorageAdapter').default; const postgresURI = process.env.PARSE_SERVER_TEST_DATABASE_URI || 'postgres://localhost:5432/parse_server_postgres_adapter_test_database'; diff --git a/spec/PostgresStorageAdapter.spec.js b/spec/PostgresStorageAdapter.spec.js index aa5e692fe4..88a07c87d8 100644 --- a/spec/PostgresStorageAdapter.spec.js +++ b/spec/PostgresStorageAdapter.spec.js @@ -1,5 +1,5 @@ -const PostgresStorageAdapter = require('../lib/Adapters/Storage/Postgres/PostgresStorageAdapter') - .default; +const PostgresStorageAdapter = + require('../lib/Adapters/Storage/Postgres/PostgresStorageAdapter').default; const databaseURI = process.env.PARSE_SERVER_TEST_DATABASE_URI || 'postgres://localhost:5432/parse_server_postgres_adapter_test_database'; diff --git a/spec/PushController.spec.js b/spec/PushController.spec.js index af29156a16..d0f853e270 100644 --- a/spec/PushController.spec.js +++ b/spec/PushController.spec.js @@ -233,67 +233,70 @@ describe('PushController', () => { } }); - it_id('14afcedf-e65d-41cd-981e-07f32df84c14')(it)('properly increment badges by more than 1', async () => { - const pushAdapter = { - send: function (body, installations) { - const badge = body.data.badge; - installations.forEach(installation => { - expect(installation.badge).toEqual(badge); - expect(installation.originalBadge + 3).toEqual(installation.badge); - }); - return successfulTransmissions(body, installations); - }, - getValidPushTypes: function () { - return ['ios', 'android']; - }, - }; - const payload = { - data: { - alert: 'Hello World!', - badge: { __op: 'Increment', amount: 3 }, - }, - }; - const installations = []; - while (installations.length != 10) { - const installation = new Parse.Object('_Installation'); - installation.set('installationId', 'installation_' + installations.length); - installation.set('deviceToken', 'device_token_' + installations.length); - installation.set('badge', installations.length); - installation.set('originalBadge', installations.length); - installation.set('deviceType', 'ios'); - installations.push(installation); - } + it_id('14afcedf-e65d-41cd-981e-07f32df84c14')(it)( + 'properly increment badges by more than 1', + async () => { + const pushAdapter = { + send: function (body, installations) { + const badge = body.data.badge; + installations.forEach(installation => { + expect(installation.badge).toEqual(badge); + expect(installation.originalBadge + 3).toEqual(installation.badge); + }); + return successfulTransmissions(body, installations); + }, + getValidPushTypes: function () { + return ['ios', 'android']; + }, + }; + const payload = { + data: { + alert: 'Hello World!', + badge: { __op: 'Increment', amount: 3 }, + }, + }; + const installations = []; + while (installations.length != 10) { + const installation = new Parse.Object('_Installation'); + installation.set('installationId', 'installation_' + installations.length); + installation.set('deviceToken', 'device_token_' + installations.length); + installation.set('badge', installations.length); + installation.set('originalBadge', installations.length); + installation.set('deviceType', 'ios'); + installations.push(installation); + } - while (installations.length != 15) { - const installation = new Parse.Object('_Installation'); - installation.set('installationId', 'installation_' + installations.length); - installation.set('deviceToken', 'device_token_' + installations.length); - installation.set('badge', installations.length); - installation.set('originalBadge', installations.length); - installation.set('deviceType', 'android'); - installations.push(installation); - } - const config = Config.get(Parse.applicationId); - const auth = { - isMaster: true, - }; - await reconfigureServer({ - push: { adapter: pushAdapter }, - }); - await Parse.Object.saveAll(installations); - const pushStatusId = await sendPush(payload, {}, config, auth); - await pushCompleted(pushStatusId); - const pushStatus = await Parse.Push.getPushStatus(pushStatusId); - expect(pushStatus.get('numSent')).toBe(15); - // Check that the installations were actually updated. - const query = new Parse.Query('_Installation'); - const results = await query.find({ useMasterKey: true }); - expect(results.length).toBe(15); - for (let i = 0; i < 15; i++) { - const installation = results[i]; - expect(installation.get('badge')).toBe(parseInt(installation.get('originalBadge')) + 3); + while (installations.length != 15) { + const installation = new Parse.Object('_Installation'); + installation.set('installationId', 'installation_' + installations.length); + installation.set('deviceToken', 'device_token_' + installations.length); + installation.set('badge', installations.length); + installation.set('originalBadge', installations.length); + installation.set('deviceType', 'android'); + installations.push(installation); + } + const config = Config.get(Parse.applicationId); + const auth = { + isMaster: true, + }; + await reconfigureServer({ + push: { adapter: pushAdapter }, + }); + await Parse.Object.saveAll(installations); + const pushStatusId = await sendPush(payload, {}, config, auth); + await pushCompleted(pushStatusId); + const pushStatus = await Parse.Push.getPushStatus(pushStatusId); + expect(pushStatus.get('numSent')).toBe(15); + // Check that the installations were actually updated. + const query = new Parse.Query('_Installation'); + const results = await query.find({ useMasterKey: true }); + expect(results.length).toBe(15); + for (let i = 0; i < 15; i++) { + const installation = results[i]; + expect(installation.get('badge')).toBe(parseInt(installation.get('originalBadge')) + 3); + } } - }); + ); it_id('758dd579-aa91-4010-9033-8d48d3463644')(it)('properly set badges to 1', async () => { const pushAdapter = { @@ -350,61 +353,64 @@ describe('PushController', () => { } }); - it_id('75c39ae3-06ac-4354-b321-931e81c5a927')(it)('properly set badges to 1 with complex query #2903 #3022', async () => { - const payload = { - data: { - alert: 'Hello World!', - badge: 1, - }, - }; - const installations = []; - while (installations.length != 10) { - const installation = new Parse.Object('_Installation'); - installation.set('installationId', 'installation_' + installations.length); - installation.set('deviceToken', 'device_token_' + installations.length); - installation.set('badge', installations.length); - installation.set('originalBadge', installations.length); - installation.set('deviceType', 'ios'); - installations.push(installation); - } - let matchedInstallationsCount = 0; - const pushAdapter = { - send: function (body, installations) { - matchedInstallationsCount += installations.length; - const badge = body.data.badge; - installations.forEach(installation => { - expect(installation.badge).toEqual(badge); - expect(1).toEqual(installation.badge); - }); - return successfulTransmissions(body, installations); - }, - getValidPushTypes: function () { - return ['ios']; - }, - }; + it_id('75c39ae3-06ac-4354-b321-931e81c5a927')(it)( + 'properly set badges to 1 with complex query #2903 #3022', + async () => { + const payload = { + data: { + alert: 'Hello World!', + badge: 1, + }, + }; + const installations = []; + while (installations.length != 10) { + const installation = new Parse.Object('_Installation'); + installation.set('installationId', 'installation_' + installations.length); + installation.set('deviceToken', 'device_token_' + installations.length); + installation.set('badge', installations.length); + installation.set('originalBadge', installations.length); + installation.set('deviceType', 'ios'); + installations.push(installation); + } + let matchedInstallationsCount = 0; + const pushAdapter = { + send: function (body, installations) { + matchedInstallationsCount += installations.length; + const badge = body.data.badge; + installations.forEach(installation => { + expect(installation.badge).toEqual(badge); + expect(1).toEqual(installation.badge); + }); + return successfulTransmissions(body, installations); + }, + getValidPushTypes: function () { + return ['ios']; + }, + }; - const config = Config.get(Parse.applicationId); - const auth = { - isMaster: true, - }; - await reconfigureServer({ - push: { adapter: pushAdapter }, - }); - await Parse.Object.saveAll(installations); - const objectIds = installations.map(installation => { - return installation.id; - }); - const where = { - objectId: { $in: objectIds.slice(0, 5) }, - }; - const pushStatusId = await sendPush(payload, where, config, auth); - await pushCompleted(pushStatusId); - expect(matchedInstallationsCount).toBe(5); - const query = new Parse.Query(Parse.Installation); - query.equalTo('badge', 1); - const results = await query.find({ useMasterKey: true }); - expect(results.length).toBe(5); - }); + const config = Config.get(Parse.applicationId); + const auth = { + isMaster: true, + }; + await reconfigureServer({ + push: { adapter: pushAdapter }, + }); + await Parse.Object.saveAll(installations); + const objectIds = installations.map(installation => { + return installation.id; + }); + const where = { + objectId: { $in: objectIds.slice(0, 5) }, + }; + const pushStatusId = await sendPush(payload, where, config, auth); + await pushCompleted(pushStatusId); + expect(matchedInstallationsCount).toBe(5); + const query = new Parse.Query(Parse.Installation); + query.equalTo('badge', 1); + const results = await query.find({ useMasterKey: true }); + expect(results.length).toBe(5); + } + ); it_id('667f31c0-b458-4f61-ab57-668c04e3cc0b')(it)('properly creates _PushStatus', async () => { const pushStatusAfterSave = { @@ -521,51 +527,54 @@ describe('PushController', () => { expect(succeedCount).toBe(1); }); - it_id('30e0591a-56de-4720-8c60-7d72291b532a')(it)('properly creates _PushStatus without serverURL', async () => { - const pushStatusAfterSave = { - handler: function () {}, - }; - Parse.Cloud.afterSave('_PushStatus', pushStatusAfterSave.handler); - const installation = new Parse.Object('_Installation'); - installation.set('installationId', 'installation'); - installation.set('deviceToken', 'device_token'); - installation.set('badge', 0); - installation.set('originalBadge', 0); - installation.set('deviceType', 'ios'); + it_id('30e0591a-56de-4720-8c60-7d72291b532a')(it)( + 'properly creates _PushStatus without serverURL', + async () => { + const pushStatusAfterSave = { + handler: function () {}, + }; + Parse.Cloud.afterSave('_PushStatus', pushStatusAfterSave.handler); + const installation = new Parse.Object('_Installation'); + installation.set('installationId', 'installation'); + installation.set('deviceToken', 'device_token'); + installation.set('badge', 0); + installation.set('originalBadge', 0); + installation.set('deviceType', 'ios'); - const payload = { - data: { - alert: 'Hello World!', - badge: 1, - }, - }; + const payload = { + data: { + alert: 'Hello World!', + badge: 1, + }, + }; - const pushAdapter = { - send: function (body, installations) { - return successfulIOS(body, installations); - }, - getValidPushTypes: function () { - return ['ios']; - }, - }; + const pushAdapter = { + send: function (body, installations) { + return successfulIOS(body, installations); + }, + getValidPushTypes: function () { + return ['ios']; + }, + }; - const config = Config.get(Parse.applicationId); - const auth = { - isMaster: true, - }; - await installation.save(); - await reconfigureServer({ - serverURL: 'http://localhost:8378/', // server with borked URL - push: { adapter: pushAdapter }, - }); - const pushStatusId = await sendPush(payload, {}, config, auth); - // it is enqueued so it can take time - await jasmine.timeout(1000); - Parse.serverURL = 'http://localhost:8378/1'; // GOOD url - const result = await Parse.Push.getPushStatus(pushStatusId); - expect(result).toBeDefined(); - await pushCompleted(pushStatusId); - }); + const config = Config.get(Parse.applicationId); + const auth = { + isMaster: true, + }; + await installation.save(); + await reconfigureServer({ + serverURL: 'http://localhost:8378/', // server with borked URL + push: { adapter: pushAdapter }, + }); + const pushStatusId = await sendPush(payload, {}, config, auth); + // it is enqueued so it can take time + await jasmine.timeout(1000); + Parse.serverURL = 'http://localhost:8378/1'; // GOOD url + const result = await Parse.Push.getPushStatus(pushStatusId); + expect(result).toBeDefined(); + await pushCompleted(pushStatusId); + } + ); it('should properly report failures in _PushStatus', async () => { const pushAdapter = { @@ -615,51 +624,54 @@ describe('PushController', () => { } }); - it_id('53551fc3-b975-4774-92e6-7e5f3c05e105')(it)('should support full RESTQuery for increment', async () => { - const payload = { - data: { - alert: 'Hello World!', - badge: 'Increment', - }, - }; + it_id('53551fc3-b975-4774-92e6-7e5f3c05e105')(it)( + 'should support full RESTQuery for increment', + async () => { + const payload = { + data: { + alert: 'Hello World!', + badge: 'Increment', + }, + }; - const pushAdapter = { - send: function (body, installations) { - return successfulTransmissions(body, installations); - }, - getValidPushTypes: function () { - return ['ios']; - }, - }; - const config = Config.get(Parse.applicationId); - const auth = { - isMaster: true, - }; + const pushAdapter = { + send: function (body, installations) { + return successfulTransmissions(body, installations); + }, + getValidPushTypes: function () { + return ['ios']; + }, + }; + const config = Config.get(Parse.applicationId); + const auth = { + isMaster: true, + }; - const where = { - deviceToken: { - $in: ['device_token_0', 'device_token_1', 'device_token_2'], - }, - }; - await reconfigureServer({ - push: { adapter: pushAdapter }, - }); - const installations = []; - while (installations.length != 5) { - const installation = new Parse.Object('_Installation'); - installation.set('installationId', 'installation_' + installations.length); - installation.set('deviceToken', 'device_token_' + installations.length); - installation.set('badge', installations.length); - installation.set('originalBadge', installations.length); - installation.set('deviceType', 'ios'); - installations.push(installation); + const where = { + deviceToken: { + $in: ['device_token_0', 'device_token_1', 'device_token_2'], + }, + }; + await reconfigureServer({ + push: { adapter: pushAdapter }, + }); + const installations = []; + while (installations.length != 5) { + const installation = new Parse.Object('_Installation'); + installation.set('installationId', 'installation_' + installations.length); + installation.set('deviceToken', 'device_token_' + installations.length); + installation.set('badge', installations.length); + installation.set('originalBadge', installations.length); + installation.set('deviceType', 'ios'); + installations.push(installation); + } + await Parse.Object.saveAll(installations); + const pushStatusId = await sendPush(payload, where, config, auth); + await pushCompleted(pushStatusId); + const pushStatus = await Parse.Push.getPushStatus(pushStatusId); + expect(pushStatus.get('numSent')).toBe(3); } - await Parse.Object.saveAll(installations); - const pushStatusId = await sendPush(payload, where, config, auth); - await pushCompleted(pushStatusId); - const pushStatus = await Parse.Push.getPushStatus(pushStatusId); - expect(pushStatus.get('numSent')).toBe(3); - }); + ); it('should support object type for alert', async () => { const payload = { diff --git a/spec/PushWorker.spec.js b/spec/PushWorker.spec.js index 6299962d52..23e8206fe3 100644 --- a/spec/PushWorker.spec.js +++ b/spec/PushWorker.spec.js @@ -271,99 +271,102 @@ describe('PushWorker', () => { toAwait.then(done).catch(done); }); - it_id('764d28ab-241b-4b96-8ce9-e03541850e3f')(it)('tracks push status per UTC offsets', done => { - const config = Config.get('test'); - const handler = pushStatusHandler(config); - const spy = spyOn(rest, 'update').and.callThrough(); - const UTCOffset = 1; - handler - .setInitial() - .then(() => { - return handler.trackSent( - [ - { - transmitted: false, - device: { - deviceToken: 1, - deviceType: 'ios', + it_id('764d28ab-241b-4b96-8ce9-e03541850e3f')(it)( + 'tracks push status per UTC offsets', + done => { + const config = Config.get('test'); + const handler = pushStatusHandler(config); + const spy = spyOn(rest, 'update').and.callThrough(); + const UTCOffset = 1; + handler + .setInitial() + .then(() => { + return handler.trackSent( + [ + { + transmitted: false, + device: { + deviceToken: 1, + deviceType: 'ios', + }, }, - }, - { - transmitted: true, - device: { - deviceToken: 1, - deviceType: 'ios', + { + transmitted: true, + device: { + deviceToken: 1, + deviceType: 'ios', + }, }, + ], + UTCOffset + ); + }) + .then(() => { + expect(spy).toHaveBeenCalled(); + const lastCall = spy.calls.mostRecent(); + expect(lastCall.args[2]).toBe(`_PushStatus`); + expect(lastCall.args[4]).toEqual({ + numSent: { __op: 'Increment', amount: 1 }, + numFailed: { __op: 'Increment', amount: 1 }, + 'sentPerType.ios': { __op: 'Increment', amount: 1 }, + 'failedPerType.ios': { __op: 'Increment', amount: 1 }, + [`sentPerUTCOffset.${UTCOffset}`]: { __op: 'Increment', amount: 1 }, + [`failedPerUTCOffset.${UTCOffset}`]: { + __op: 'Increment', + amount: 1, }, - ], - UTCOffset - ); - }) - .then(() => { - expect(spy).toHaveBeenCalled(); - const lastCall = spy.calls.mostRecent(); - expect(lastCall.args[2]).toBe(`_PushStatus`); - expect(lastCall.args[4]).toEqual({ - numSent: { __op: 'Increment', amount: 1 }, - numFailed: { __op: 'Increment', amount: 1 }, - 'sentPerType.ios': { __op: 'Increment', amount: 1 }, - 'failedPerType.ios': { __op: 'Increment', amount: 1 }, - [`sentPerUTCOffset.${UTCOffset}`]: { __op: 'Increment', amount: 1 }, - [`failedPerUTCOffset.${UTCOffset}`]: { - __op: 'Increment', - amount: 1, - }, - count: { __op: 'Increment', amount: -1 }, - status: 'running', - }); - const query = new Parse.Query('_PushStatus'); - return query.get(handler.objectId, { useMasterKey: true }); - }) - .then(pushStatus => { - const sentPerUTCOffset = pushStatus.get('sentPerUTCOffset'); - expect(sentPerUTCOffset['1']).toBe(1); - const failedPerUTCOffset = pushStatus.get('failedPerUTCOffset'); - expect(failedPerUTCOffset['1']).toBe(1); - return handler.trackSent( - [ - { - transmitted: false, - device: { - deviceToken: 1, - deviceType: 'ios', + count: { __op: 'Increment', amount: -1 }, + status: 'running', + }); + const query = new Parse.Query('_PushStatus'); + return query.get(handler.objectId, { useMasterKey: true }); + }) + .then(pushStatus => { + const sentPerUTCOffset = pushStatus.get('sentPerUTCOffset'); + expect(sentPerUTCOffset['1']).toBe(1); + const failedPerUTCOffset = pushStatus.get('failedPerUTCOffset'); + expect(failedPerUTCOffset['1']).toBe(1); + return handler.trackSent( + [ + { + transmitted: false, + device: { + deviceToken: 1, + deviceType: 'ios', + }, }, - }, - { - transmitted: true, - device: { - deviceToken: 1, - deviceType: 'ios', + { + transmitted: true, + device: { + deviceToken: 1, + deviceType: 'ios', + }, }, - }, - { - transmitted: true, - device: { - deviceToken: 1, - deviceType: 'ios', + { + transmitted: true, + device: { + deviceToken: 1, + deviceType: 'ios', + }, }, - }, - ], - UTCOffset - ); - }) - .then(() => { - const query = new Parse.Query('_PushStatus'); - return query.get(handler.objectId, { useMasterKey: true }); - }) - .then(pushStatus => { - const sentPerUTCOffset = pushStatus.get('sentPerUTCOffset'); - expect(sentPerUTCOffset['1']).toBe(3); - const failedPerUTCOffset = pushStatus.get('failedPerUTCOffset'); - expect(failedPerUTCOffset['1']).toBe(2); - }) - .then(done) - .catch(done.fail); - }); + ], + UTCOffset + ); + }) + .then(() => { + const query = new Parse.Query('_PushStatus'); + return query.get(handler.objectId, { useMasterKey: true }); + }) + .then(pushStatus => { + const sentPerUTCOffset = pushStatus.get('sentPerUTCOffset'); + expect(sentPerUTCOffset['1']).toBe(3); + const failedPerUTCOffset = pushStatus.get('failedPerUTCOffset'); + expect(failedPerUTCOffset['1']).toBe(2); + }) + .then(done) + .catch(done.fail); + } + ); it('tracks push status per UTC offsets with negative offsets', done => { const config = Config.get('test'); diff --git a/spec/RestQuery.spec.js b/spec/RestQuery.spec.js index 6fe3c0fa18..131433e177 100644 --- a/spec/RestQuery.spec.js +++ b/spec/RestQuery.spec.js @@ -419,69 +419,72 @@ describe('RestQuery.each', () => { expect(results.length).toBe(7); }); - it_id('0fe22501-4b18-461e-b87d-82ceac4a496e')(it)('should work with query on relations', async () => { - const objectA = new Parse.Object('Letter', { value: 'A' }); - const objectB = new Parse.Object('Letter', { value: 'B' }); - - const object1 = new Parse.Object('Number', { value: '1' }); - const object2 = new Parse.Object('Number', { value: '2' }); - const object3 = new Parse.Object('Number', { value: '3' }); - const object4 = new Parse.Object('Number', { value: '4' }); - await Parse.Object.saveAll([object1, object2, object3, object4]); - - objectA.relation('numbers').add(object1); - objectB.relation('numbers').add(object2); - await Parse.Object.saveAll([objectA, objectB]); - - const config = Config.get('test'); - - /** - * Two queries needed since objectId are sorted and we can't know which one - * going to be the first and then skip by the $gt added by each - */ - const queryOne = await RestQuery({ - method: RestQuery.Method.get, - config, - auth: auth.master(config), - className: 'Letter', - restWhere: { - numbers: { - __type: 'Pointer', - className: 'Number', - objectId: object1.id, + it_id('0fe22501-4b18-461e-b87d-82ceac4a496e')(it)( + 'should work with query on relations', + async () => { + const objectA = new Parse.Object('Letter', { value: 'A' }); + const objectB = new Parse.Object('Letter', { value: 'B' }); + + const object1 = new Parse.Object('Number', { value: '1' }); + const object2 = new Parse.Object('Number', { value: '2' }); + const object3 = new Parse.Object('Number', { value: '3' }); + const object4 = new Parse.Object('Number', { value: '4' }); + await Parse.Object.saveAll([object1, object2, object3, object4]); + + objectA.relation('numbers').add(object1); + objectB.relation('numbers').add(object2); + await Parse.Object.saveAll([objectA, objectB]); + + const config = Config.get('test'); + + /** + * Two queries needed since objectId are sorted and we can't know which one + * going to be the first and then skip by the $gt added by each + */ + const queryOne = await RestQuery({ + method: RestQuery.Method.get, + config, + auth: auth.master(config), + className: 'Letter', + restWhere: { + numbers: { + __type: 'Pointer', + className: 'Number', + objectId: object1.id, + }, }, - }, - restOptions: { limit: 1 }, - }); + restOptions: { limit: 1 }, + }); - const queryTwo = await RestQuery({ - method: RestQuery.Method.get, - config, - auth: auth.master(config), - className: 'Letter', - restWhere: { - numbers: { - __type: 'Pointer', - className: 'Number', - objectId: object2.id, + const queryTwo = await RestQuery({ + method: RestQuery.Method.get, + config, + auth: auth.master(config), + className: 'Letter', + restWhere: { + numbers: { + __type: 'Pointer', + className: 'Number', + objectId: object2.id, + }, }, - }, - restOptions: { limit: 1 }, - }); + restOptions: { limit: 1 }, + }); - const classSpy = spyOn(RestQuery._UnsafeRestQuery.prototype, 'execute').and.callThrough(); - const resultsOne = []; - const resultsTwo = []; - await queryOne.each(result => { - resultsOne.push(result); - }); - await queryTwo.each(result => { - resultsTwo.push(result); - }); - expect(classSpy.calls.count()).toBe(4); - expect(resultsOne.length).toBe(1); - expect(resultsTwo.length).toBe(1); - }); + const classSpy = spyOn(RestQuery._UnsafeRestQuery.prototype, 'execute').and.callThrough(); + const resultsOne = []; + const resultsTwo = []; + await queryOne.each(result => { + resultsOne.push(result); + }); + await queryTwo.each(result => { + resultsTwo.push(result); + }); + expect(classSpy.calls.count()).toBe(4); + expect(resultsOne.length).toBe(1); + expect(resultsTwo.length).toBe(1); + } + ); it('test afterSave response object is return', done => { Parse.Cloud.beforeSave('TestObject2', function (req) { diff --git a/spec/SchemaPerformance.spec.js b/spec/SchemaPerformance.spec.js index 728b88804c..4429f9e4b0 100644 --- a/spec/SchemaPerformance.spec.js +++ b/spec/SchemaPerformance.spec.js @@ -241,21 +241,24 @@ describe('Schema Performance', function () { expect(spy.reloadCalls).toBe(1); }); - it_id('b0ae21f2-c947-48ed-a0db-e8900d45a4c8')(it)('cannot set invalid databaseOptions', async () => { - const expectError = async (key, value, expected) => - expectAsync( - reconfigureServer({ databaseAdapter: undefined, databaseOptions: { [key]: value } }) - ).toBeRejectedWith(`databaseOptions.${key} must be a ${expected}`); - for (const databaseOptions of [[], 0, 'string']) { - await expectAsync( - reconfigureServer({ databaseAdapter: undefined, databaseOptions }) - ).toBeRejectedWith(`databaseOptions must be an object`); + it_id('b0ae21f2-c947-48ed-a0db-e8900d45a4c8')(it)( + 'cannot set invalid databaseOptions', + async () => { + const expectError = async (key, value, expected) => + expectAsync( + reconfigureServer({ databaseAdapter: undefined, databaseOptions: { [key]: value } }) + ).toBeRejectedWith(`databaseOptions.${key} must be a ${expected}`); + for (const databaseOptions of [[], 0, 'string']) { + await expectAsync( + reconfigureServer({ databaseAdapter: undefined, databaseOptions }) + ).toBeRejectedWith(`databaseOptions must be an object`); + } + for (const value of [null, 0, 'string', {}, []]) { + await expectError('enableSchemaHooks', value, 'boolean'); + } + for (const value of [null, false, 'string', {}, []]) { + await expectError('schemaCacheTtl', value, 'number'); + } } - for (const value of [null, 0, 'string', {}, []]) { - await expectError('enableSchemaHooks', value, 'boolean'); - } - for (const value of [null, false, 'string', {}, []]) { - await expectError('schemaCacheTtl', value, 'number'); - } - }); + ); }); diff --git a/spec/Uniqueness.spec.js b/spec/Uniqueness.spec.js index 92ee6ea92c..7f53ac54bb 100644 --- a/spec/Uniqueness.spec.js +++ b/spec/Uniqueness.spec.js @@ -68,25 +68,28 @@ describe('Uniqueness', function () { }); }); - it_id('802650a9-a6db-447e-88d0-8aae99100088')(it)('fails when attempting to ensure uniqueness of fields that are not currently unique', done => { - const o1 = new Parse.Object('UniqueFail'); - o1.set('key', 'val'); - const o2 = new Parse.Object('UniqueFail'); - o2.set('key', 'val'); - Parse.Object.saveAll([o1, o2]) - .then(() => { - const config = Config.get('test'); - return config.database.adapter.ensureUniqueness( - 'UniqueFail', - { fields: { key: { __type: 'String' } } }, - ['key'] - ); - }) - .catch(error => { - expect(error.code).toEqual(Parse.Error.DUPLICATE_VALUE); - done(); - }); - }); + it_id('802650a9-a6db-447e-88d0-8aae99100088')(it)( + 'fails when attempting to ensure uniqueness of fields that are not currently unique', + done => { + const o1 = new Parse.Object('UniqueFail'); + o1.set('key', 'val'); + const o2 = new Parse.Object('UniqueFail'); + o2.set('key', 'val'); + Parse.Object.saveAll([o1, o2]) + .then(() => { + const config = Config.get('test'); + return config.database.adapter.ensureUniqueness( + 'UniqueFail', + { fields: { key: { __type: 'String' } } }, + ['key'] + ); + }) + .catch(error => { + expect(error.code).toEqual(Parse.Error.DUPLICATE_VALUE); + done(); + }); + } + ); it_exclude_dbs(['postgres'])('can do compound uniqueness', done => { const config = Config.get('test'); diff --git a/spec/UserController.spec.js b/spec/UserController.spec.js index 31d5051960..a4791b5101 100644 --- a/spec/UserController.spec.js +++ b/spec/UserController.spec.js @@ -29,49 +29,66 @@ describe('UserController', () => { await user.signUp(); const config = Config.get('test'); - const rawUser = await config.database.find('_User', { username }, {}, Auth.maintenance(config)); + const rawUser = await config.database.find( + '_User', + { username }, + {}, + Auth.maintenance(config) + ); const rawUsername = rawUser[0].username; const rawToken = rawUser[0]._email_verify_token; expect(rawToken).toBeDefined(); expect(rawUsername).toBe(username); - expect(emailOptions.link).toEqual(`http://www.example.com/apps/test/verify_email?token=${rawToken}`); + expect(emailOptions.link).toEqual( + `http://www.example.com/apps/test/verify_email?token=${rawToken}` + ); }); }); describe('parseFrameURL provided', () => { - it_id('673c2bb1-049e-4dda-b6be-88c866260036')(it)('uses parseFrameURL and includes the destination in the link parameter', async () => { - await reconfigureServer({ - publicServerURL: 'http://www.example.com', - customPages: { - parseFrameURL: 'http://someother.example.com/handle-parse-iframe', - }, - verifyUserEmails: true, - emailAdapter, - appName: 'test', - }); + it_id('673c2bb1-049e-4dda-b6be-88c866260036')(it)( + 'uses parseFrameURL and includes the destination in the link parameter', + async () => { + await reconfigureServer({ + publicServerURL: 'http://www.example.com', + customPages: { + parseFrameURL: 'http://someother.example.com/handle-parse-iframe', + }, + verifyUserEmails: true, + emailAdapter, + appName: 'test', + }); - let emailOptions; - emailAdapter.sendVerificationEmail = options => { - emailOptions = options; - }; + let emailOptions; + emailAdapter.sendVerificationEmail = options => { + emailOptions = options; + }; - const username = 'verificationUser'; - const user = new Parse.User(); - user.setUsername(username); - user.setPassword('pass'); - user.setEmail('verification@example.com'); - await user.signUp(); + const username = 'verificationUser'; + const user = new Parse.User(); + user.setUsername(username); + user.setPassword('pass'); + user.setEmail('verification@example.com'); + await user.signUp(); - const config = Config.get('test'); - const rawUser = await config.database.find('_User', { username }, {}, Auth.maintenance(config)); - const rawUsername = rawUser[0].username; - const rawToken = rawUser[0]._email_verify_token; - expect(rawToken).toBeDefined(); - expect(rawUsername).toBe(username); + const config = Config.get('test'); + const rawUser = await config.database.find( + '_User', + { username }, + {}, + Auth.maintenance(config) + ); + const rawUsername = rawUser[0].username; + const rawToken = rawUser[0]._email_verify_token; + expect(rawToken).toBeDefined(); + expect(rawUsername).toBe(username); - expect(emailOptions.link).toEqual(`http://someother.example.com/handle-parse-iframe?link=%2Fapps%2Ftest%2Fverify_email&token=${rawToken}`); - }); + expect(emailOptions.link).toEqual( + `http://someother.example.com/handle-parse-iframe?link=%2Fapps%2Ftest%2Fverify_email&token=${rawToken}` + ); + } + ); }); }); }); diff --git a/spec/Utils.spec.js b/spec/Utils.spec.js index fe86854e33..c4f04096fa 100644 --- a/spec/Utils.spec.js +++ b/spec/Utils.spec.js @@ -5,10 +5,10 @@ describe('Utils', () => { it('should properly escape email with all special ASCII characters for use in URLs', async () => { const values = [ { input: `!\"'),.:;<>?]^}`, output: '%21%22%27%29%2C%2E%3A%3B%3C%3E%3F%5D%5E%7D' }, - ] + ]; for (const value of values) { expect(Utils.encodeForUrl(value.input)).toBe(value.output); - } + } }); }); @@ -18,28 +18,28 @@ describe('Utils', () => { a: 1, b: { c: 2, - d: 3 + d: 3, }, - e: 4 + e: 4, }; Utils.addNestedKeysToRoot(obj, 'b'); expect(obj).toEqual({ a: 1, c: 2, d: 3, - e: 4 + e: 4, }); }); it('should not modify the object if the key does not exist', async () => { const obj = { a: 1, - e: 4 + e: 4, }; Utils.addNestedKeysToRoot(obj, 'b'); expect(obj).toEqual({ a: 1, - e: 4 + e: 4, }); }); @@ -47,13 +47,13 @@ describe('Utils', () => { const obj = { a: 1, b: 2, - e: 4 + e: 4, }; Utils.addNestedKeysToRoot(obj, 'b'); expect(obj).toEqual({ a: 1, b: 2, - e: 4 + e: 4, }); }); }); diff --git a/spec/ValidationAndPasswordsReset.spec.js b/spec/ValidationAndPasswordsReset.spec.js index 3f6d4048c5..5816217a07 100644 --- a/spec/ValidationAndPasswordsReset.spec.js +++ b/spec/ValidationAndPasswordsReset.spec.js @@ -34,32 +34,35 @@ describe('Custom Pages, Email Verification, Password Reset', () => { }); }); - it_id('5e558687-40f3-496c-9e4f-af6100bd1b2f')(it)('sends verification email if email verification is enabled', done => { - const emailAdapter = { - sendVerificationEmail: () => Promise.resolve(), - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => Promise.resolve(), - }; - reconfigureServer({ - appName: 'unused', - verifyUserEmails: true, - emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', - }).then(async () => { - spyOn(emailAdapter, 'sendVerificationEmail'); - const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('zxcv'); - user.setEmail('testIfEnabled@parse.com'); - await user.signUp(); - await jasmine.timeout(); - expect(emailAdapter.sendVerificationEmail).toHaveBeenCalled(); - user.fetch().then(() => { - expect(user.get('emailVerified')).toEqual(false); - done(); + it_id('5e558687-40f3-496c-9e4f-af6100bd1b2f')(it)( + 'sends verification email if email verification is enabled', + done => { + const emailAdapter = { + sendVerificationEmail: () => Promise.resolve(), + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => Promise.resolve(), + }; + reconfigureServer({ + appName: 'unused', + verifyUserEmails: true, + emailAdapter: emailAdapter, + publicServerURL: 'http://localhost:8378/1', + }).then(async () => { + spyOn(emailAdapter, 'sendVerificationEmail'); + const user = new Parse.User(); + user.setPassword('asdf'); + user.setUsername('zxcv'); + user.setEmail('testIfEnabled@parse.com'); + await user.signUp(); + await jasmine.timeout(); + expect(emailAdapter.sendVerificationEmail).toHaveBeenCalled(); + user.fetch().then(() => { + expect(user.get('emailVerified')).toEqual(false); + done(); + }); }); - }); - }); + } + ); it('does not send verification email when verification is enabled and email is not set', done => { const emailAdapter = { @@ -279,7 +282,7 @@ describe('Custom Pages, Email Verification, Password Reset', () => { await user.signUp(); const verifyUserEmails = { - method: async (params) => { + method: async params => { expect(params.object).toBeInstanceOf(Parse.User); expect(params.ip).toBeDefined(); expect(params.master).toBeDefined(); @@ -306,43 +309,46 @@ describe('Custom Pages, Email Verification, Password Reset', () => { expect(verifyUserEmailsSpy).toHaveBeenCalledTimes(2); }); - it_id('2a5d24be-2ca5-4385-b580-1423bd392e43')(it)('allows user to login only after user clicks on the link to confirm email address if preventLoginWithUnverifiedEmail is set to true', async () => { - let sendEmailOptions; - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - await reconfigureServer({ - appName: 'emailing app', - verifyUserEmails: true, - preventLoginWithUnverifiedEmail: true, - emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', - }); - let user = new Parse.User(); - user.setPassword('other-password'); - user.setUsername('user'); - user.set('email', 'user@example.com'); - await user.signUp(); - await jasmine.timeout(); - expect(sendEmailOptions).not.toBeUndefined(); - const response = await request({ - url: sendEmailOptions.link, - followRedirects: false, - }); - expect(response.status).toEqual(302); - expect(response.text).toEqual( - 'Found. Redirecting to http://localhost:8378/1/apps/verify_email_success.html' - ); - user = await new Parse.Query(Parse.User).first({ useMasterKey: true }); - expect(user.get('emailVerified')).toEqual(true); - user = await Parse.User.logIn('user', 'other-password'); - expect(typeof user).toBe('object'); - expect(user.get('emailVerified')).toBe(true); - }); + it_id('2a5d24be-2ca5-4385-b580-1423bd392e43')(it)( + 'allows user to login only after user clicks on the link to confirm email address if preventLoginWithUnverifiedEmail is set to true', + async () => { + let sendEmailOptions; + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + await reconfigureServer({ + appName: 'emailing app', + verifyUserEmails: true, + preventLoginWithUnverifiedEmail: true, + emailAdapter: emailAdapter, + publicServerURL: 'http://localhost:8378/1', + }); + let user = new Parse.User(); + user.setPassword('other-password'); + user.setUsername('user'); + user.set('email', 'user@example.com'); + await user.signUp(); + await jasmine.timeout(); + expect(sendEmailOptions).not.toBeUndefined(); + const response = await request({ + url: sendEmailOptions.link, + followRedirects: false, + }); + expect(response.status).toEqual(302); + expect(response.text).toEqual( + 'Found. Redirecting to http://localhost:8378/1/apps/verify_email_success.html' + ); + user = await new Parse.Query(Parse.User).first({ useMasterKey: true }); + expect(user.get('emailVerified')).toEqual(true); + user = await Parse.User.logIn('user', 'other-password'); + expect(typeof user).toBe('object'); + expect(user.get('emailVerified')).toBe(true); + } + ); it('allows user to login if email is not verified but preventLoginWithUnverifiedEmail is set to false', done => { reconfigureServer({ @@ -382,34 +388,37 @@ describe('Custom Pages, Email Verification, Password Reset', () => { }); }); - it_id('a18a07af-0319-4f15-8237-28070c5948fa')(it)('does not allow signup with preventSignupWithUnverified', async () => { - let sendEmailOptions; - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - await reconfigureServer({ - appName: 'test', - publicServerURL: 'http://localhost:1337/1', - verifyUserEmails: true, - preventLoginWithUnverifiedEmail: true, - preventSignupWithUnverifiedEmail: true, - emailAdapter, - }); - const newUser = new Parse.User(); - newUser.setPassword('asdf'); - newUser.setUsername('zxcv'); - newUser.set('email', 'test@example.com'); - await expectAsync(newUser.signUp()).toBeRejectedWith( - new Parse.Error(Parse.Error.EMAIL_NOT_FOUND, 'User email is not verified.') - ); - const user = await new Parse.Query(Parse.User).first({ useMasterKey: true }); - expect(user).toBeDefined(); - expect(sendEmailOptions).toBeDefined(); - }); + it_id('a18a07af-0319-4f15-8237-28070c5948fa')(it)( + 'does not allow signup with preventSignupWithUnverified', + async () => { + let sendEmailOptions; + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + await reconfigureServer({ + appName: 'test', + publicServerURL: 'http://localhost:1337/1', + verifyUserEmails: true, + preventLoginWithUnverifiedEmail: true, + preventSignupWithUnverifiedEmail: true, + emailAdapter, + }); + const newUser = new Parse.User(); + newUser.setPassword('asdf'); + newUser.setUsername('zxcv'); + newUser.set('email', 'test@example.com'); + await expectAsync(newUser.signUp()).toBeRejectedWith( + new Parse.Error(Parse.Error.EMAIL_NOT_FOUND, 'User email is not verified.') + ); + const user = await new Parse.Query(Parse.User).first({ useMasterKey: true }); + expect(user).toBeDefined(); + expect(sendEmailOptions).toBeDefined(); + } + ); it('fails if you include an emailAdapter, set a publicServerURL, but have no appName and send a password reset email', done => { reconfigureServer({ @@ -617,87 +626,93 @@ describe('Custom Pages, Email Verification, Password Reset', () => { }); }); - it_id('45f550a2-a2b2-4b2b-b533-ccbf96139cc9')(it)('receives the app name and user in the adapter', done => { - let emailSent = false; - const emailAdapter = { - sendVerificationEmail: options => { - expect(options.appName).toEqual('emailing app'); - expect(options.user.get('email')).toEqual('user@parse.com'); - emailSent = true; - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - reconfigureServer({ - appName: 'emailing app', - verifyUserEmails: true, - emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', - }).then(async () => { - const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('zxcv'); - user.set('email', 'user@parse.com'); - await user.signUp(); - await jasmine.timeout(); - expect(emailSent).toBe(true); - done(); - }); - }); - - it_id('ea37ef62-aad8-4a17-8dfe-35e5b2986f0f')(it)('when you click the link in the email it sets emailVerified to true and redirects you', done => { - const user = new Parse.User(); - let sendEmailOptions; - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - reconfigureServer({ - appName: 'emailing app', - verifyUserEmails: true, - emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', - }) - .then(() => { - user.setPassword('other-password'); - user.setUsername('user'); + it_id('45f550a2-a2b2-4b2b-b533-ccbf96139cc9')(it)( + 'receives the app name and user in the adapter', + done => { + let emailSent = false; + const emailAdapter = { + sendVerificationEmail: options => { + expect(options.appName).toEqual('emailing app'); + expect(options.user.get('email')).toEqual('user@parse.com'); + emailSent = true; + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + reconfigureServer({ + appName: 'emailing app', + verifyUserEmails: true, + emailAdapter: emailAdapter, + publicServerURL: 'http://localhost:8378/1', + }).then(async () => { + const user = new Parse.User(); + user.setPassword('asdf'); + user.setUsername('zxcv'); user.set('email', 'user@parse.com'); - return user.signUp(); + await user.signUp(); + await jasmine.timeout(); + expect(emailSent).toBe(true); + done(); + }); + } + ); + + it_id('ea37ef62-aad8-4a17-8dfe-35e5b2986f0f')(it)( + 'when you click the link in the email it sets emailVerified to true and redirects you', + done => { + const user = new Parse.User(); + let sendEmailOptions; + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + reconfigureServer({ + appName: 'emailing app', + verifyUserEmails: true, + emailAdapter: emailAdapter, + publicServerURL: 'http://localhost:8378/1', }) - .then(() => jasmine.timeout()) - .then(() => { - expect(sendEmailOptions).not.toBeUndefined(); - request({ - url: sendEmailOptions.link, - followRedirects: false, - }).then(response => { - expect(response.status).toEqual(302); - expect(response.text).toEqual( - 'Found. Redirecting to http://localhost:8378/1/apps/verify_email_success.html' - ); - user - .fetch() - .then( - () => { - expect(user.get('emailVerified')).toEqual(true); - done(); - }, - err => { + .then(() => { + user.setPassword('other-password'); + user.setUsername('user'); + user.set('email', 'user@parse.com'); + return user.signUp(); + }) + .then(() => jasmine.timeout()) + .then(() => { + expect(sendEmailOptions).not.toBeUndefined(); + request({ + url: sendEmailOptions.link, + followRedirects: false, + }).then(response => { + expect(response.status).toEqual(302); + expect(response.text).toEqual( + 'Found. Redirecting to http://localhost:8378/1/apps/verify_email_success.html' + ); + user + .fetch() + .then( + () => { + expect(user.get('emailVerified')).toEqual(true); + done(); + }, + err => { + jfail(err); + fail('this should not fail'); + done(); + } + ) + .catch(err => { jfail(err); - fail('this should not fail'); done(); - } - ) - .catch(err => { - jfail(err); - done(); - }); + }); + }); }); - }); - }); + } + ); it('redirects you to invalid link if you try to verify email incorrectly', done => { reconfigureServer({ @@ -825,7 +840,8 @@ describe('Custom Pages, Email Verification, Password Reset', () => { followRedirects: false, }).then(response => { expect(response.status).toEqual(302); - const re = /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=[a-zA-Z0-9]+\&id=test\&/; + const re = + /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=[a-zA-Z0-9]+\&id=test\&/; expect(response.text.match(re)).not.toBe(null); done(); }); @@ -887,7 +903,8 @@ describe('Custom Pages, Email Verification, Password Reset', () => { followRedirects: false, }).then(response => { expect(response.status).toEqual(302); - const re = /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; + const re = + /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; const match = response.text.match(re); if (!match) { fail('should have a token'); @@ -964,7 +981,8 @@ describe('Custom Pages, Email Verification, Password Reset', () => { followRedirects: false, }).then(response => { expect(response.status).toEqual(302); - const re = /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; + const re = + /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; const match = response.text.match(re); if (!match) { fail('should have a token'); @@ -1023,7 +1041,8 @@ describe('Custom Pages, Email Verification, Password Reset', () => { followRedirects: false, }); expect(response.status).toEqual(302); - const re = /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; + const re = + /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; const match = response.text.match(re); if (!match) { fail('should have a token'); diff --git a/spec/WinstonLoggerAdapter.spec.js b/spec/WinstonLoggerAdapter.spec.js index 81bdc213de..da9efd2610 100644 --- a/spec/WinstonLoggerAdapter.spec.js +++ b/spec/WinstonLoggerAdapter.spec.js @@ -1,7 +1,7 @@ 'use strict'; -const WinstonLoggerAdapter = require('../lib/Adapters/Logger/WinstonLoggerAdapter') - .WinstonLoggerAdapter; +const WinstonLoggerAdapter = + require('../lib/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter; const request = require('../lib/request'); describe_only(() => { @@ -174,50 +174,53 @@ describe_only(() => { describe_only(() => { return process.env.PARSE_SERVER_LOG_LEVEL !== 'debug'; })('verbose logs', () => { - it_id('9ca72994-d255-4c11-a5a2-693c99ee2cdb')(it)('mask sensitive information in _User class', done => { - reconfigureServer({ verbose: true }) - .then(() => createTestUser()) - .then(() => { - const winstonLoggerAdapter = new WinstonLoggerAdapter(); - return winstonLoggerAdapter.query({ - from: new Date(Date.now() - 500), - size: 100, - level: 'verbose', - }); - }) - .then(results => { - const logString = JSON.stringify(results); - expect(logString.match(/\*\*\*\*\*\*\*\*/g).length).not.toBe(0); - expect(logString.match(/moon-y/g)).toBe(null); - - const headers = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - }; - request({ - headers: headers, - url: 'http://localhost:8378/1/login?username=test&password=moon-y', - }).then(() => { + it_id('9ca72994-d255-4c11-a5a2-693c99ee2cdb')(it)( + 'mask sensitive information in _User class', + done => { + reconfigureServer({ verbose: true }) + .then(() => createTestUser()) + .then(() => { const winstonLoggerAdapter = new WinstonLoggerAdapter(); - return winstonLoggerAdapter - .query({ - from: new Date(Date.now() - 500), - size: 100, - level: 'verbose', - }) - .then(results => { - const logString = JSON.stringify(results); - expect(logString.match(/\*\*\*\*\*\*\*\*/g).length).not.toBe(0); - expect(logString.match(/moon-y/g)).toBe(null); - done(); - }); + return winstonLoggerAdapter.query({ + from: new Date(Date.now() - 500), + size: 100, + level: 'verbose', + }); + }) + .then(results => { + const logString = JSON.stringify(results); + expect(logString.match(/\*\*\*\*\*\*\*\*/g).length).not.toBe(0); + expect(logString.match(/moon-y/g)).toBe(null); + + const headers = { + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + }; + request({ + headers: headers, + url: 'http://localhost:8378/1/login?username=test&password=moon-y', + }).then(() => { + const winstonLoggerAdapter = new WinstonLoggerAdapter(); + return winstonLoggerAdapter + .query({ + from: new Date(Date.now() - 500), + size: 100, + level: 'verbose', + }) + .then(results => { + const logString = JSON.stringify(results); + expect(logString.match(/\*\*\*\*\*\*\*\*/g).length).not.toBe(0); + expect(logString.match(/moon-y/g)).toBe(null); + done(); + }); + }); + }) + .catch(err => { + fail(JSON.stringify(err)); + done(); }); - }) - .catch(err => { - fail(JSON.stringify(err)); - done(); - }); - }); + } + ); it('verbose logs should interpolate string', async () => { await reconfigureServer({ verbose: true }); diff --git a/spec/batch.spec.js b/spec/batch.spec.js index 8c9ef27a6b..66a743b1d9 100644 --- a/spec/batch.spec.js +++ b/spec/batch.spec.js @@ -242,9 +242,7 @@ describe('batch', () => { const results = await query.find(); expect(createSpy.calls.count()).toBe(2); for (let i = 0; i + 1 < createSpy.calls.length; i = i + 2) { - expect(createSpy.calls.argsFor(i)[3]).toBe( - createSpy.calls.argsFor(i + 1)[3] - ); + expect(createSpy.calls.argsFor(i)[3]).toBe(createSpy.calls.argsFor(i + 1)[3]); } expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']); }); diff --git a/spec/eslint.config.js b/spec/eslint.config.js index 0f6c0b11c5..e061f8b54b 100644 --- a/spec/eslint.config.js +++ b/spec/eslint.config.js @@ -1,56 +1,56 @@ -const js = require("@eslint/js"); -const globals = require("globals"); +const js = require('@eslint/js'); +const globals = require('globals'); module.exports = [ js.configs.recommended, { languageOptions: { - ecmaVersion: "latest", - sourceType: "module", + ecmaVersion: 'latest', + sourceType: 'module', globals: { ...globals.node, ...globals.jasmine, - mockFetch: "readonly", - Parse: "readonly", - reconfigureServer: "readonly", - createTestUser: "readonly", - jfail: "readonly", - ok: "readonly", - strictEqual: "readonly", - TestObject: "readonly", - Item: "readonly", - Container: "readonly", - equal: "readonly", - expectAsync: "readonly", - notEqual: "readonly", - it_id: "readonly", - fit_id: "readonly", - it_only_db: "readonly", - it_only_mongodb_version: "readonly", - it_only_postgres_version: "readonly", - it_only_node_version: "readonly", - fit_only_mongodb_version: "readonly", - fit_only_postgres_version: "readonly", - fit_only_node_version: "readonly", - it_exclude_dbs: "readonly", - fit_exclude_dbs: "readonly", - describe_only_db: "readonly", - fdescribe_only_db: "readonly", - describe_only: "readonly", - on_db: "readonly", - defaultConfiguration: "readonly", - range: "readonly", - jequal: "readonly", - create: "readonly", - arrayContains: "readonly", - databaseAdapter: "readonly", - databaseURI: "readonly" + mockFetch: 'readonly', + Parse: 'readonly', + reconfigureServer: 'readonly', + createTestUser: 'readonly', + jfail: 'readonly', + ok: 'readonly', + strictEqual: 'readonly', + TestObject: 'readonly', + Item: 'readonly', + Container: 'readonly', + equal: 'readonly', + expectAsync: 'readonly', + notEqual: 'readonly', + it_id: 'readonly', + fit_id: 'readonly', + it_only_db: 'readonly', + it_only_mongodb_version: 'readonly', + it_only_postgres_version: 'readonly', + it_only_node_version: 'readonly', + fit_only_mongodb_version: 'readonly', + fit_only_postgres_version: 'readonly', + fit_only_node_version: 'readonly', + it_exclude_dbs: 'readonly', + fit_exclude_dbs: 'readonly', + describe_only_db: 'readonly', + fdescribe_only_db: 'readonly', + describe_only: 'readonly', + on_db: 'readonly', + defaultConfiguration: 'readonly', + range: 'readonly', + jequal: 'readonly', + create: 'readonly', + arrayContains: 'readonly', + databaseAdapter: 'readonly', + databaseURI: 'readonly', }, }, rules: { - "no-console": "off", - "no-var": "error", - "no-unused-vars": "off", - "no-useless-escape": "off", - } + 'no-console': 'off', + 'no-var': 'error', + 'no-unused-vars': 'off', + 'no-useless-escape': 'off', + }, }, ]; diff --git a/spec/helper.js b/spec/helper.js index 7deb5c495e..c7a35c5c54 100644 --- a/spec/helper.js +++ b/spec/helper.js @@ -39,11 +39,11 @@ const ParseServer = require('../lib/index').ParseServer; const loadAdapter = require('../lib/Adapters/AdapterLoader').loadAdapter; const path = require('path'); const TestUtils = require('../lib/TestUtils'); -const GridFSBucketAdapter = require('../lib/Adapters/Files/GridFSBucketAdapter') - .GridFSBucketAdapter; +const GridFSBucketAdapter = + require('../lib/Adapters/Files/GridFSBucketAdapter').GridFSBucketAdapter; const FSAdapter = require('@parse/fs-files-adapter'); -const PostgresStorageAdapter = require('../lib/Adapters/Storage/Postgres/PostgresStorageAdapter') - .default; +const PostgresStorageAdapter = + require('../lib/Adapters/Storage/Postgres/PostgresStorageAdapter').default; const MongoStorageAdapter = require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter').default; const RedisCacheAdapter = require('../lib/Adapters/Cache/RedisCacheAdapter').default; const RESTController = require('parse/lib/node/RESTController'); @@ -274,7 +274,7 @@ global.afterEachFn = async () => { } else { await databaseAdapter.performInitialization({ VolatileClassesSchemas }); } -} +}; afterEach(global.afterEachFn); afterAll(() => { @@ -415,10 +415,10 @@ function mockShortLivedAuth() { } function mockFetch(mockResponses) { - global.fetch = jasmine.createSpy('fetch').and.callFake((url, options = { }) => { + global.fetch = jasmine.createSpy('fetch').and.callFake((url, options = {}) => { options.method ||= 'GET'; const mockResponse = mockResponses.find( - (mock) => mock.url === url && mock.method === options.method + mock => mock.url === url && mock.method === options.method ); if (mockResponse) { @@ -432,7 +432,6 @@ function mockFetch(mockResponses) { }); } - // This is polluting, but, it makes it way easier to directly port old tests. global.Parse = Parse; global.TestObject = TestObject; diff --git a/spec/index.spec.js b/spec/index.spec.js index 1a2ea889a9..6ac1ee3058 100644 --- a/spec/index.spec.js +++ b/spec/index.spec.js @@ -638,7 +638,8 @@ describe('server', () => { }); it('should reload masterKey if ttl is set and expired', async () => { - const masterKeySpy = jasmine.createSpy() + const masterKeySpy = jasmine + .createSpy() .and.returnValues(Promise.resolve('firstMasterKey'), Promise.resolve('secondMasterKey')); await reconfigureServer({ @@ -657,7 +658,6 @@ describe('server', () => { expect(config.masterKeyCache.masterKey).toEqual('secondMasterKey'); }); - it('should not fail when Google signin is introduced without the optional clientId', done => { const jwt = require('jsonwebtoken'); const authUtils = require('../lib/Adapters/Auth/utils'); diff --git a/spec/rest.spec.js b/spec/rest.spec.js index fed64c988b..92e25c3941 100644 --- a/spec/rest.spec.js +++ b/spec/rest.spec.js @@ -787,21 +787,24 @@ describe('rest create', () => { ); }); - it_id('3ce563bf-93aa-4d0b-9af9-c5fb246ac9fc')(it)('cannot get object in _GlobalConfig if not masterKey through pointer', async () => { - await Parse.Config.save({ privateData: 'secret' }, { privateData: true }); - const obj2 = new Parse.Object('TestObject'); - obj2.set('globalConfigPointer', { - __type: 'Pointer', - className: '_GlobalConfig', - objectId: 1, - }); - await obj2.save(); - const query = new Parse.Query('TestObject'); - query.include('globalConfigPointer'); - await expectAsync(query.get(obj2.id)).toBeRejectedWithError( - "Clients aren't allowed to perform the get operation on the _GlobalConfig collection." - ); - }); + it_id('3ce563bf-93aa-4d0b-9af9-c5fb246ac9fc')(it)( + 'cannot get object in _GlobalConfig if not masterKey through pointer', + async () => { + await Parse.Config.save({ privateData: 'secret' }, { privateData: true }); + const obj2 = new Parse.Object('TestObject'); + obj2.set('globalConfigPointer', { + __type: 'Pointer', + className: '_GlobalConfig', + objectId: 1, + }); + await obj2.save(); + const query = new Parse.Query('TestObject'); + query.include('globalConfigPointer'); + await expectAsync(query.get(obj2.id)).toBeRejectedWithError( + "Clients aren't allowed to perform the get operation on the _GlobalConfig collection." + ); + } + ); it('locks down session', done => { let currentUser; diff --git a/spec/schemas.spec.js b/spec/schemas.spec.js index e4697d4116..3c289da798 100644 --- a/spec/schemas.spec.js +++ b/spec/schemas.spec.js @@ -2817,7 +2817,9 @@ describe('schemas', () => { object.save({ '!12field': 'field', }) - ).toBeRejectedWith(new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'Invalid key name: "!12field"')); + ).toBeRejectedWith( + new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'Invalid key name: "!12field"') + ); done(); }); @@ -3650,93 +3652,102 @@ describe('schemas', () => { }); }); - it_id('5d0926b2-2d31-459d-a2b1-23ecc32e72a3')(it_exclude_dbs(['postgres']))('get indexes on startup', done => { - const obj = new Parse.Object('TestObject'); - obj - .save() - .then(() => { - return reconfigureServer({ - appId: 'test', - restAPIKey: 'test', - publicServerURL: 'http://localhost:8378/1', - }); - }) - .then(() => { - request({ - url: 'http://localhost:8378/1/schemas/TestObject', - headers: masterKeyHeaders, - json: true, - }).then(response => { - expect(response.data.indexes._id_).toBeDefined(); - done(); + it_id('5d0926b2-2d31-459d-a2b1-23ecc32e72a3')(it_exclude_dbs(['postgres']))( + 'get indexes on startup', + done => { + const obj = new Parse.Object('TestObject'); + obj + .save() + .then(() => { + return reconfigureServer({ + appId: 'test', + restAPIKey: 'test', + publicServerURL: 'http://localhost:8378/1', + }); + }) + .then(() => { + request({ + url: 'http://localhost:8378/1/schemas/TestObject', + headers: masterKeyHeaders, + json: true, + }).then(response => { + expect(response.data.indexes._id_).toBeDefined(); + done(); + }); }); - }); - }); + } + ); - it_id('9f2ba51a-6a9c-4b25-9da0-51c82ac65f90')(it_exclude_dbs(['postgres']))('get compound indexes on startup', done => { - const obj = new Parse.Object('TestObject'); - obj.set('subject', 'subject'); - obj.set('comment', 'comment'); - obj - .save() - .then(() => { - return config.database.adapter.createIndex('TestObject', { - subject: 'text', - comment: 'text', - }); - }) - .then(() => { - return reconfigureServer({ - appId: 'test', - restAPIKey: 'test', - publicServerURL: 'http://localhost:8378/1', + it_id('9f2ba51a-6a9c-4b25-9da0-51c82ac65f90')(it_exclude_dbs(['postgres']))( + 'get compound indexes on startup', + done => { + const obj = new Parse.Object('TestObject'); + obj.set('subject', 'subject'); + obj.set('comment', 'comment'); + obj + .save() + .then(() => { + return config.database.adapter.createIndex('TestObject', { + subject: 'text', + comment: 'text', + }); + }) + .then(() => { + return reconfigureServer({ + appId: 'test', + restAPIKey: 'test', + publicServerURL: 'http://localhost:8378/1', + }); + }) + .then(() => { + request({ + url: 'http://localhost:8378/1/schemas/TestObject', + headers: masterKeyHeaders, + json: true, + }).then(response => { + expect(response.data.indexes._id_).toBeDefined(); + expect(response.data.indexes._id_._id).toEqual(1); + expect(response.data.indexes.subject_text_comment_text).toBeDefined(); + expect(response.data.indexes.subject_text_comment_text.subject).toEqual('text'); + expect(response.data.indexes.subject_text_comment_text.comment).toEqual('text'); + done(); + }); }); - }) - .then(() => { - request({ - url: 'http://localhost:8378/1/schemas/TestObject', - headers: masterKeyHeaders, - json: true, - }).then(response => { - expect(response.data.indexes._id_).toBeDefined(); - expect(response.data.indexes._id_._id).toEqual(1); - expect(response.data.indexes.subject_text_comment_text).toBeDefined(); - expect(response.data.indexes.subject_text_comment_text.subject).toEqual('text'); - expect(response.data.indexes.subject_text_comment_text.comment).toEqual('text'); + } + ); + + it_id('cbd5d897-b938-43a4-8f5a-5d02dd2be9be')(it_exclude_dbs(['postgres']))( + 'cannot update to duplicate value on unique index', + done => { + const index = { + code: 1, + }; + const obj1 = new Parse.Object('UniqueIndexClass'); + obj1.set('code', 1); + const obj2 = new Parse.Object('UniqueIndexClass'); + obj2.set('code', 2); + const adapter = config.database.adapter; + adapter + ._adaptiveCollection('UniqueIndexClass') + .then(collection => { + return collection._ensureSparseUniqueIndexInBackground(index); + }) + .then(() => { + return obj1.save(); + }) + .then(() => { + return obj2.save(); + }) + .then(() => { + obj1.set('code', 2); + return obj1.save(); + }) + .then(done.fail) + .catch(error => { + expect(error.code).toEqual(Parse.Error.DUPLICATE_VALUE); done(); }); - }); - }); - - it_id('cbd5d897-b938-43a4-8f5a-5d02dd2be9be')(it_exclude_dbs(['postgres']))('cannot update to duplicate value on unique index', done => { - const index = { - code: 1, - }; - const obj1 = new Parse.Object('UniqueIndexClass'); - obj1.set('code', 1); - const obj2 = new Parse.Object('UniqueIndexClass'); - obj2.set('code', 2); - const adapter = config.database.adapter; - adapter - ._adaptiveCollection('UniqueIndexClass') - .then(collection => { - return collection._ensureSparseUniqueIndexInBackground(index); - }) - .then(() => { - return obj1.save(); - }) - .then(() => { - return obj2.save(); - }) - .then(() => { - obj1.set('code', 2); - return obj1.save(); - }) - .then(done.fail) - .catch(error => { - expect(error.code).toEqual(Parse.Error.DUPLICATE_VALUE); - done(); - }); - }); + } + ); }); }); diff --git a/spec/support/CurrentSpecReporter.js b/spec/support/CurrentSpecReporter.js index 8e0e0dafd1..bd45864a6b 100755 --- a/spec/support/CurrentSpecReporter.js +++ b/spec/support/CurrentSpecReporter.js @@ -11,15 +11,15 @@ global.currentSpec = null; */ const flakyTests = [ // Timeout - "ParseLiveQuery handle invalid websocket payload length", + 'ParseLiveQuery handle invalid websocket payload length', // Unhandled promise rejection: TypeError: message.split is not a function - "rest query query internal field", + 'rest query query internal field', // TypeError: Cannot read properties of undefined (reading 'link') - "UserController sendVerificationEmail parseFrameURL not provided uses publicServerURL", + 'UserController sendVerificationEmail parseFrameURL not provided uses publicServerURL', // TypeError: Cannot read properties of undefined (reading 'link') - "UserController sendVerificationEmail parseFrameURL provided uses parseFrameURL and includes the destination in the link parameter", + 'UserController sendVerificationEmail parseFrameURL provided uses parseFrameURL and includes the destination in the link parameter', // Expected undefined to be defined - "Email Verification Token Expiration: sets the _email_verify_token_expires_at and _email_verify_token fields after user SignUp", + 'Email Verification Token Expiration: sets the _email_verify_token_expires_at and _email_verify_token fields after user SignUp', ]; /** The minimum execution time in seconds for a test to be considered slow. */ @@ -50,29 +50,34 @@ class CurrentSpecReporter { } } -global.displayTestStats = function() { - const times = Object.values(timerMap).sort((a,b) => b - a).filter(time => time >= slowTestLimit); +global.displayTestStats = function () { + const times = Object.values(timerMap) + .sort((a, b) => b - a) + .filter(time => time >= slowTestLimit); if (times.length > 0) { console.log(`Slow tests with execution time >=${slowTestLimit}s:`); } - times.forEach((time) => { - console.warn(`${time.toFixed(1)}s:`, Object.keys(timerMap).find(key => timerMap[key] === time)); + times.forEach(time => { + console.warn( + `${time.toFixed(1)}s:`, + Object.keys(timerMap).find(key => timerMap[key] === time) + ); }); console.log('\n'); - duplicates.forEach((spec) => { + duplicates.forEach(spec => { console.warn('Duplicate spec: ' + spec); }); console.log('\n'); - Object.keys(retryMap).forEach((spec) => { + Object.keys(retryMap).forEach(spec => { console.warn(`Flaky test: ${spec} failed ${retryMap[spec]} times`); }); console.log('\n'); }; -global.retryFlakyTests = function() { +global.retryFlakyTests = function () { const originalSpecConstructor = jasmine.Spec; - jasmine.Spec = function(attrs) { + jasmine.Spec = function (attrs) { const spec = new originalSpecConstructor(attrs); const originalTestFn = spec.queueableFn.fn; const runOriginalTest = () => { @@ -81,12 +86,12 @@ global.retryFlakyTests = function() { return originalTestFn(); } else { // handle done() callback - return new Promise((resolve) => { + return new Promise(resolve => { originalTestFn(resolve); }); } }; - spec.queueableFn.fn = async function() { + spec.queueableFn.fn = async function () { const isFlaky = flakyTests.includes(spec.result.fullName); const runs = isFlaky ? retries : 1; let exceptionCaught; @@ -101,8 +106,8 @@ global.retryFlakyTests = function() { } catch (exception) { exceptionCaught = exception; } - const failed = !spec.markedPending && - (exceptionCaught || spec.result.failedExpectations.length != 0); + const failed = + !spec.markedPending && (exceptionCaught || spec.result.failedExpectations.length != 0); if (!failed) { break; } @@ -118,6 +123,6 @@ global.retryFlakyTests = function() { }; return spec; }; -} +}; -module.exports = CurrentSpecReporter; \ No newline at end of file +module.exports = CurrentSpecReporter; diff --git a/spec/support/MockLdapServer.js b/spec/support/MockLdapServer.js index 935f0703d6..270a9603cc 100644 --- a/spec/support/MockLdapServer.js +++ b/spec/support/MockLdapServer.js @@ -10,8 +10,9 @@ function newServer(port, dn, provokeSearchError = false, ssl = false) { const server = ssl ? ldapjs.createServer(tlsOptions) : ldapjs.createServer(); server.bind('o=example', function (req, res, next) { - if (req.dn.toString() !== dn || req.credentials !== 'secret') - { return next(new ldapjs.InvalidCredentialsError()); } + if (req.dn.toString() !== dn || req.credentials !== 'secret') { + return next(new ldapjs.InvalidCredentialsError()); + } res.end(); return next(); }); diff --git a/spec/vulnerabilities.spec.js b/spec/vulnerabilities.spec.js index f7a94cd221..858245ecc4 100644 --- a/spec/vulnerabilities.spec.js +++ b/spec/vulnerabilities.spec.js @@ -40,7 +40,9 @@ describe('Vulnerabilities', () => { it('refuses session token of user with poisoned object ID', async () => { await expectAsync( new Parse.Query(Parse.User).find({ sessionToken: poisonedUser.getSessionToken() }) - ).toBeRejectedWith(new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Invalid object ID.')); + ).toBeRejectedWith( + new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Invalid object ID.') + ); await new Parse.Query(Parse.User).find({ sessionToken: innocentUser.getSessionToken() }); }); }); @@ -248,36 +250,39 @@ describe('Vulnerabilities', () => { ); }); - it_id('e8b5f1e1-8326-4c70-b5f4-1e8678dfff8d')(it)('denies creating a hook with polluted data', async () => { - const express = require('express'); - const port = 34567; - const hookServerURL = 'http://localhost:' + port; - const app = express(); - app.use(express.json({ type: '*/*' })); - const server = await new Promise(resolve => { - const res = app.listen(port, undefined, () => resolve(res)); - }); - app.post('/BeforeSave', function (req, res) { - const object = Parse.Object.fromJSON(req.body.object); - object.set('hello', 'world'); - object.set('obj', { - constructor: { - prototype: { - dummy: 0, + it_id('e8b5f1e1-8326-4c70-b5f4-1e8678dfff8d')(it)( + 'denies creating a hook with polluted data', + async () => { + const express = require('express'); + const port = 34567; + const hookServerURL = 'http://localhost:' + port; + const app = express(); + app.use(express.json({ type: '*/*' })); + const server = await new Promise(resolve => { + const res = app.listen(port, undefined, () => resolve(res)); + }); + app.post('/BeforeSave', function (req, res) { + const object = Parse.Object.fromJSON(req.body.object); + object.set('hello', 'world'); + object.set('obj', { + constructor: { + prototype: { + dummy: 0, + }, }, - }, + }); + res.json({ success: object }); }); - res.json({ success: object }); - }); - await Parse.Hooks.createTrigger('TestObject', 'beforeSave', hookServerURL + '/BeforeSave'); - await expectAsync(new Parse.Object('TestObject').save()).toBeRejectedWith( - new Parse.Error( - Parse.Error.INVALID_KEY_NAME, - 'Prohibited keyword in request data: {"key":"constructor"}.' - ) - ); - await new Promise(resolve => server.close(resolve)); - }); + await Parse.Hooks.createTrigger('TestObject', 'beforeSave', hookServerURL + '/BeforeSave'); + await expectAsync(new Parse.Object('TestObject').save()).toBeRejectedWith( + new Parse.Error( + Parse.Error.INVALID_KEY_NAME, + 'Prohibited keyword in request data: {"key":"constructor"}.' + ) + ); + await new Promise(resolve => server.close(resolve)); + } + ); it('denies write request with custom denylist of key/value', async () => { await reconfigureServer({ @@ -488,8 +493,7 @@ describe('Postgres regex sanitizater', () => { const response = await request({ method: 'GET', - url: - "http://localhost:8378/1/classes/_User?where[username][$regex]=A'B'%3BSELECT+PG_SLEEP(3)%3B--", + url: "http://localhost:8378/1/classes/_User?where[username][$regex]=A'B'%3BSELECT+PG_SLEEP(3)%3B--", headers: { 'Content-Type': 'application/json', 'X-Parse-Application-Id': 'test', diff --git a/src/Auth.js b/src/Auth.js index d8bf7e651f..a93f49051f 100644 --- a/src/Auth.js +++ b/src/Auth.js @@ -449,11 +449,15 @@ const findUsersWithAuthData = async (config, authData, beforeFind) => { }; const hasMutatedAuthData = (authData, userAuthData) => { - if (!userAuthData) { return { hasMutatedAuthData: true, mutatedAuthData: authData }; } + if (!userAuthData) { + return { hasMutatedAuthData: true, mutatedAuthData: authData }; + } const mutatedAuthData = {}; Object.keys(authData).forEach(provider => { // Anonymous provider is not handled this way - if (provider === 'anonymous') { return; } + if (provider === 'anonymous') { + return; + } const providerData = authData[provider]; const userProviderAuthData = userAuthData[provider]; if (!isDeepStrictEqual(providerData, userProviderAuthData)) { diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index 0050216e2c..9c1fdc7033 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -142,7 +142,9 @@ const filterSensitiveData = ( object: any ) => { let userId = null; - if (auth && auth.user) { userId = auth.user.id; } + if (auth && auth.user) { + userId = auth.user.id; + } // replace protectedFields when using pointer-permissions const perms = @@ -508,9 +510,10 @@ class DatabaseController { var aclGroup = acl || []; return this.loadSchemaIfNeeded(validSchemaController).then(schemaController => { - return (isMaster - ? Promise.resolve() - : schemaController.validatePermission(className, aclGroup, 'update') + return ( + isMaster + ? Promise.resolve() + : schemaController.validatePermission(className, aclGroup, 'update') ) .then(() => { relationUpdates = this.collectRelationUpdates(className, originalQuery.objectId, update); @@ -772,9 +775,10 @@ class DatabaseController { const aclGroup = acl || []; return this.loadSchemaIfNeeded(validSchemaController).then(schemaController => { - return (isMaster - ? Promise.resolve() - : schemaController.validatePermission(className, aclGroup, 'delete') + return ( + isMaster + ? Promise.resolve() + : schemaController.validatePermission(className, aclGroup, 'delete') ).then(() => { if (!isMaster) { query = this.addPointerPermissions( @@ -852,9 +856,10 @@ class DatabaseController { return this.validateClassName(className) .then(() => this.loadSchemaIfNeeded(validSchemaController)) .then(schemaController => { - return (isMaster - ? Promise.resolve() - : schemaController.validatePermission(className, aclGroup, 'create') + return ( + isMaster + ? Promise.resolve() + : schemaController.validatePermission(className, aclGroup, 'create') ) .then(() => schemaController.enforceClassExists(className)) .then(() => schemaController.getOneSchema(className, true)) @@ -1255,9 +1260,10 @@ class DatabaseController { delete sort[fieldName]; } }); - return (isMaster - ? Promise.resolve() - : schemaController.validatePermission(className, aclGroup, op) + return ( + isMaster + ? Promise.resolve() + : schemaController.validatePermission(className, aclGroup, op) ) .then(() => this.reduceRelationKeys(className, query, queryOptions)) .then(() => this.reduceInRelation(className, query, schemaController)) @@ -1592,12 +1598,18 @@ class DatabaseController { schema && schema.getClassLevelPermissions ? schema.getClassLevelPermissions(className) : schema; - if (!perms) { return null; } + if (!perms) { + return null; + } const protectedFields = perms.protectedFields; - if (!protectedFields) { return null; } + if (!protectedFields) { + return null; + } - if (aclGroup.indexOf(query.objectId) > -1) { return null; } + if (aclGroup.indexOf(query.objectId) > -1) { + return null; + } // for queries where "keys" are set and do not include all 'userField':{field}, // we have to transparently include it, and then remove before returning to client diff --git a/src/Controllers/FilesController.js b/src/Controllers/FilesController.js index a88c527b00..21ab5efe4c 100644 --- a/src/Controllers/FilesController.js +++ b/src/Controllers/FilesController.js @@ -18,7 +18,7 @@ export class FilesController extends AdaptableController { const extname = path.extname(filename); const hasExtension = extname.length > 0; - const mime = (await import('mime')).default + const mime = (await import('mime')).default; if (!hasExtension && contentType && mime.getExtension(contentType)) { filename = filename + '.' + mime.getExtension(contentType); } else if (hasExtension && !contentType) { @@ -34,7 +34,7 @@ export class FilesController extends AdaptableController { return { url: location, name: filename, - } + }; } deleteFile(config, filename) { diff --git a/src/Controllers/SchemaController.js b/src/Controllers/SchemaController.js index 5d539055cf..ee1d4abf3b 100644 --- a/src/Controllers/SchemaController.js +++ b/src/Controllers/SchemaController.js @@ -666,10 +666,18 @@ const VolatileClassesSchemas = [ ]; const dbTypeMatchesObjectType = (dbType: SchemaField | string, objectType: SchemaField) => { - if (dbType.type !== objectType.type) { return false; } - if (dbType.targetClass !== objectType.targetClass) { return false; } - if (dbType === objectType.type) { return true; } - if (dbType.type === objectType.type) { return true; } + if (dbType.type !== objectType.type) { + return false; + } + if (dbType.targetClass !== objectType.targetClass) { + return false; + } + if (dbType === objectType.type) { + return true; + } + if (dbType.type === objectType.type) { + return true; + } return false; }; @@ -1020,7 +1028,9 @@ export default class SchemaController { } const fieldType = fields[fieldName]; const error = fieldTypeIsInvalid(fieldType); - if (error) { return { code: error.code, error: error.message }; } + if (error) { + return { code: error.code, error: error.message }; + } if (fieldType.defaultValue !== undefined) { let defaultValueType = getType(fieldType.defaultValue); if (typeof defaultValueType === 'string') { diff --git a/src/Controllers/UserController.js b/src/Controllers/UserController.js index 296b7f6868..2a6796052f 100644 --- a/src/Controllers/UserController.js +++ b/src/Controllers/UserController.js @@ -209,7 +209,7 @@ export class UserController extends AdaptableController { master, installationId, ip, - resendRequest: true + resendRequest: true, }); if (!shouldSend) { return; @@ -222,7 +222,12 @@ export class UserController extends AdaptableController { if (!aUser || aUser.emailVerified) { throw undefined; } - const generate = await this.regenerateEmailVerifyToken(aUser, req.auth?.isMaster, req.auth?.installationId, req.ip); + const generate = await this.regenerateEmailVerifyToken( + aUser, + req.auth?.isMaster, + req.auth?.installationId, + req.ip + ); if (generate) { this.sendVerificationEmail(aUser, req); } diff --git a/src/GraphQL/parseGraphQLUtils.js b/src/GraphQL/parseGraphQLUtils.js index f1194784cb..d7164525a7 100644 --- a/src/GraphQL/parseGraphQLUtils.js +++ b/src/GraphQL/parseGraphQLUtils.js @@ -23,7 +23,9 @@ export const extractKeysAndInclude = selectedFields => { selectedFields = selectedFields.filter(field => !field.includes('__typename')); // Handles "id" field for both current and included objects selectedFields = selectedFields.map(field => { - if (field === 'id') { return 'objectId'; } + if (field === 'id') { + return 'objectId'; + } return field.endsWith('.id') ? `${field.substring(0, field.lastIndexOf('.id'))}.objectId` : field; diff --git a/src/Options/Definitions.js b/src/Options/Definitions.js index ca2f7cc2ee..faffb754ac 100644 --- a/src/Options/Definitions.js +++ b/src/Options/Definitions.js @@ -15,30 +15,26 @@ module.exports.SchemaOptions = { }, definitions: { env: 'PARSE_SERVER_SCHEMA_DEFINITIONS', - help: - 'Rest representation on Parse.Schema https://docs.parseplatform.org/rest/guide/#adding-a-schema', + help: 'Rest representation on Parse.Schema https://docs.parseplatform.org/rest/guide/#adding-a-schema', required: true, action: parsers.objectParser, default: [], }, deleteExtraFields: { env: 'PARSE_SERVER_SCHEMA_DELETE_EXTRA_FIELDS', - help: - 'Is true if Parse Server should delete any fields not defined in a schema definition. This should only be used during development.', + help: 'Is true if Parse Server should delete any fields not defined in a schema definition. This should only be used during development.', action: parsers.booleanParser, default: false, }, lockSchemas: { env: 'PARSE_SERVER_SCHEMA_LOCK_SCHEMAS', - help: - 'Is true if Parse Server will reject any attempts to modify the schema while the server is running.', + help: 'Is true if Parse Server will reject any attempts to modify the schema while the server is running.', action: parsers.booleanParser, default: false, }, recreateModifiedFields: { env: 'PARSE_SERVER_SCHEMA_RECREATE_MODIFIED_FIELDS', - help: - 'Is true if Parse Server should recreate any fields that are different between the current database schema and theschema definition. This should only be used during development.', + help: 'Is true if Parse Server should recreate any fields that are different between the current database schema and theschema definition. This should only be used during development.', action: parsers.booleanParser, default: false, }, @@ -70,8 +66,7 @@ module.exports.ParseServerOptions = { }, allowExpiredAuthDataToken: { env: 'PARSE_SERVER_ALLOW_EXPIRED_AUTH_DATA_TOKEN', - help: - 'Allow a user to log in even if the 3rd party authentication token that was used to sign in to their account has expired. If this is set to `false`, then the token will be validated every time the user signs in to their account. This refers to the token that is stored in the `_User.authData` field. Defaults to `false`.', + help: 'Allow a user to log in even if the 3rd party authentication token that was used to sign in to their account has expired. If this is set to `false`, then the token will be validated every time the user signs in to their account. This refers to the token that is stored in the `_User.authData` field. Defaults to `false`.', action: parsers.booleanParser, default: false, }, @@ -82,8 +77,7 @@ module.exports.ParseServerOptions = { }, allowOrigin: { env: 'PARSE_SERVER_ALLOW_ORIGIN', - help: - 'Sets origins for Access-Control-Allow-Origin. This can be a string for a single origin or an array of strings for multiple origins.', + help: 'Sets origins for Access-Control-Allow-Origin. This can be a string for a single origin or an array of strings for multiple origins.', action: parsers.arrayParser, }, analyticsAdapter: { @@ -102,8 +96,7 @@ module.exports.ParseServerOptions = { }, auth: { env: 'PARSE_SERVER_AUTH_PROVIDERS', - help: - 'Configuration for your authentication providers, as stringified JSON. See http://docs.parseplatform.org/parse-server/guide/#oauth-and-3rd-party-authentication', + help: 'Configuration for your authentication providers, as stringified JSON. See http://docs.parseplatform.org/parse-server/guide/#oauth-and-3rd-party-authentication', }, cacheAdapter: { env: 'PARSE_SERVER_CACHE_ADAPTER', @@ -142,15 +135,13 @@ module.exports.ParseServerOptions = { }, convertEmailToLowercase: { env: 'PARSE_SERVER_CONVERT_EMAIL_TO_LOWERCASE', - help: - 'Optional. If set to `true`, the `email` property of a user is automatically converted to lowercase before being stored in the database. Consequently, queries must match the case as stored in the database, which would be lowercase in this scenario. If `false`, the `email` property is stored as set, without any case modifications. Default is `false`.', + help: 'Optional. If set to `true`, the `email` property of a user is automatically converted to lowercase before being stored in the database. Consequently, queries must match the case as stored in the database, which would be lowercase in this scenario. If `false`, the `email` property is stored as set, without any case modifications. Default is `false`.', action: parsers.booleanParser, default: false, }, convertUsernameToLowercase: { env: 'PARSE_SERVER_CONVERT_USERNAME_TO_LOWERCASE', - help: - 'Optional. If set to `true`, the `username` property of a user is automatically converted to lowercase before being stored in the database. Consequently, queries must match the case as stored in the database, which would be lowercase in this scenario. If `false`, the `username` property is stored as set, without any case modifications. Default is `false`.', + help: 'Optional. If set to `true`, the `username` property of a user is automatically converted to lowercase before being stored in the database. Consequently, queries must match the case as stored in the database, which would be lowercase in this scenario. If `false`, the `username` property is stored as set, without any case modifications. Default is `false`.', action: parsers.booleanParser, default: false, }, @@ -163,8 +154,7 @@ module.exports.ParseServerOptions = { }, databaseAdapter: { env: 'PARSE_SERVER_DATABASE_ADAPTER', - help: - 'Adapter module for the database; any options that are not explicitly described here are passed directly to the database client.', + help: 'Adapter module for the database; any options that are not explicitly described here are passed directly to the database client.', action: parsers.moduleOrObjectParser, }, databaseOptions: { @@ -187,8 +177,7 @@ module.exports.ParseServerOptions = { }, directAccess: { env: 'PARSE_SERVER_DIRECT_ACCESS', - help: - 'Set to `true` if Parse requests within the same Node.js environment as Parse Server should be routed to Parse Server directly instead of via the HTTP interface. Default is `false`.

If set to `false` then Parse requests within the same Node.js environment as Parse Server are executed as HTTP requests sent to Parse Server via the `serverURL`. For example, a `Parse.Query` in Cloud Code is calling Parse Server via a HTTP request. The server is essentially making a HTTP request to itself, unnecessarily using network resources such as network ports.

\u26A0\uFE0F In environments where multiple Parse Server instances run behind a load balancer and Parse requests within the current Node.js environment should be routed via the load balancer and distributed as HTTP requests among all instances via the `serverURL`, this should be set to `false`.', + help: 'Set to `true` if Parse requests within the same Node.js environment as Parse Server should be routed to Parse Server directly instead of via the HTTP interface. Default is `false`.

If set to `false` then Parse requests within the same Node.js environment as Parse Server are executed as HTTP requests sent to Parse Server via the `serverURL`. For example, a `Parse.Query` in Cloud Code is calling Parse Server via a HTTP request. The server is essentially making a HTTP request to itself, unnecessarily using network resources such as network ports.

\u26A0\uFE0F In environments where multiple Parse Server instances run behind a load balancer and Parse requests within the current Node.js environment should be routed via the load balancer and distributed as HTTP requests among all instances via the `serverURL`, this should be set to `false`.', action: parsers.booleanParser, default: true, }, @@ -203,15 +192,13 @@ module.exports.ParseServerOptions = { }, emailVerifyTokenReuseIfValid: { env: 'PARSE_SERVER_EMAIL_VERIFY_TOKEN_REUSE_IF_VALID', - help: - 'Set to `true` if a email verification token should be reused in case another token is requested but there is a token that is still valid, i.e. has not expired. This avoids the often observed issue that a user requests multiple emails and does not know which link contains a valid token because each newly generated token would invalidate the previous token.

Default is `false`.
Requires option `verifyUserEmails: true`.', + help: 'Set to `true` if a email verification token should be reused in case another token is requested but there is a token that is still valid, i.e. has not expired. This avoids the often observed issue that a user requests multiple emails and does not know which link contains a valid token because each newly generated token would invalidate the previous token.

Default is `false`.
Requires option `verifyUserEmails: true`.', action: parsers.booleanParser, default: false, }, emailVerifyTokenValidityDuration: { env: 'PARSE_SERVER_EMAIL_VERIFY_TOKEN_VALIDITY_DURATION', - help: - 'Set the validity duration of the email verification token in seconds after which the token expires. The token is used in the link that is set in the email. After the token expires, the link becomes invalid and a new link has to be sent. If the option is not set or set to `undefined`, then the token never expires.

For example, to expire the token after 2 hours, set a value of 7200 seconds (= 60 seconds * 60 minutes * 2 hours).

Default is `undefined`.
Requires option `verifyUserEmails: true`.', + help: 'Set the validity duration of the email verification token in seconds after which the token expires. The token is used in the link that is set in the email. After the token expires, the link becomes invalid and a new link has to be sent. If the option is not set or set to `undefined`, then the token never expires.

For example, to expire the token after 2 hours, set a value of 7200 seconds (= 60 seconds * 60 minutes * 2 hours).

Default is `undefined`.
Requires option `verifyUserEmails: true`.', action: parsers.numberParser('emailVerifyTokenValidityDuration'), }, enableAnonymousUsers: { @@ -222,8 +209,7 @@ module.exports.ParseServerOptions = { }, enableCollationCaseComparison: { env: 'PARSE_SERVER_ENABLE_COLLATION_CASE_COMPARISON', - help: - 'Optional. If set to `true`, the collation rule of case comparison for queries and indexes is enabled. Enable this option to run Parse Server with MongoDB Atlas Serverless or AWS Amazon DocumentDB. If `false`, the collation rule of case comparison is disabled. Default is `false`.', + help: 'Optional. If set to `true`, the collation rule of case comparison for queries and indexes is enabled. Enable this option to run Parse Server with MongoDB Atlas Serverless or AWS Amazon DocumentDB. If `false`, the collation rule of case comparison is disabled. Default is `false`.', action: parsers.booleanParser, default: false, }, @@ -235,15 +221,13 @@ module.exports.ParseServerOptions = { }, enableInsecureAuthAdapters: { env: 'PARSE_SERVER_ENABLE_INSECURE_AUTH_ADAPTERS', - help: - 'Enable (or disable) insecure auth adapters, defaults to true. Insecure auth adapters are deprecated and it is recommended to disable them.', + help: 'Enable (or disable) insecure auth adapters, defaults to true. Insecure auth adapters are deprecated and it is recommended to disable them.', action: parsers.booleanParser, default: true, }, encodeParseObjectInCloudFunction: { env: 'PARSE_SERVER_ENCODE_PARSE_OBJECT_IN_CLOUD_FUNCTION', - help: - 'If set to `true`, a `Parse.Object` that is in the payload when calling a Cloud Function will be converted to an instance of `Parse.Object`. If `false`, the object will not be converted and instead be a plain JavaScript object, which contains the raw data of a `Parse.Object` but is not an actual instance of `Parse.Object`. Default is `false`.

\u2139\uFE0F The expected behavior would be that the object is converted to an instance of `Parse.Object`, so you would normally set this option to `true`. The default is `false` because this is a temporary option that has been introduced to avoid a breaking change when fixing a bug where JavaScript objects are not converted to actual instances of `Parse.Object`.', + help: 'If set to `true`, a `Parse.Object` that is in the payload when calling a Cloud Function will be converted to an instance of `Parse.Object`. If `false`, the object will not be converted and instead be a plain JavaScript object, which contains the raw data of a `Parse.Object` but is not an actual instance of `Parse.Object`. Default is `false`.

\u2139\uFE0F The expected behavior would be that the object is converted to an instance of `Parse.Object`, so you would normally set this option to `true`. The default is `false` because this is a temporary option that has been introduced to avoid a breaking change when fixing a bug where JavaScript objects are not converted to actual instances of `Parse.Object`.', action: parsers.booleanParser, default: true, }, @@ -259,15 +243,13 @@ module.exports.ParseServerOptions = { }, expireInactiveSessions: { env: 'PARSE_SERVER_EXPIRE_INACTIVE_SESSIONS', - help: - 'Sets whether we should expire the inactive sessions, defaults to true. If false, all new sessions are created with no expiration date.', + help: 'Sets whether we should expire the inactive sessions, defaults to true. If false, all new sessions are created with no expiration date.', action: parsers.booleanParser, default: true, }, extendSessionOnUse: { env: 'PARSE_SERVER_EXTEND_SESSION_ON_USE', - help: - "Whether Parse Server should automatically extend a valid session by the sessionLength. In order to reduce the number of session updates in the database, a session will only be extended when a request is received after at least half of the current session's lifetime has passed.", + help: "Whether Parse Server should automatically extend a valid session by the sessionLength. In order to reduce the number of session updates in the database, a session will only be extended when a request is received after at least half of the current session's lifetime has passed.", action: parsers.booleanParser, default: false, }, @@ -303,8 +285,7 @@ module.exports.ParseServerOptions = { }, idempotencyOptions: { env: 'PARSE_SERVER_EXPERIMENTAL_IDEMPOTENCY_OPTIONS', - help: - 'Options for request idempotency to deduplicate identical requests that may be caused by network issues. Caution, this is an experimental feature that may not be appropriate for production.', + help: 'Options for request idempotency to deduplicate identical requests that may be caused by network issues. Caution, this is an experimental feature that may not be appropriate for production.', action: parsers.objectParser, type: 'IdempotencyOptions', default: {}, @@ -353,14 +334,12 @@ module.exports.ParseServerOptions = { }, maintenanceKey: { env: 'PARSE_SERVER_MAINTENANCE_KEY', - help: - '(Optional) The maintenance key is used for modifying internal and read-only fields of Parse Server.

\u26A0\uFE0F This key is not intended to be used as part of a regular operation of Parse Server. This key is intended to conduct out-of-band changes such as one-time migrations or data correction tasks. Internal fields are not officially documented and may change at any time without publication in release changelogs. We strongly advice not to rely on internal fields as part of your regular operation and to investigate the implications of any planned changes *directly in the source code* of your current version of Parse Server.', + help: '(Optional) The maintenance key is used for modifying internal and read-only fields of Parse Server.

\u26A0\uFE0F This key is not intended to be used as part of a regular operation of Parse Server. This key is intended to conduct out-of-band changes such as one-time migrations or data correction tasks. Internal fields are not officially documented and may change at any time without publication in release changelogs. We strongly advice not to rely on internal fields as part of your regular operation and to investigate the implications of any planned changes *directly in the source code* of your current version of Parse Server.', required: true, }, maintenanceKeyIps: { env: 'PARSE_SERVER_MAINTENANCE_KEY_IPS', - help: - "(Optional) Restricts the use of maintenance key permissions to a list of IP addresses or ranges.

This option accepts a list of single IP addresses, for example `['10.0.0.1', '10.0.0.2']`. You can also use CIDR notation to specify an IP address range, for example `['10.0.1.0/24']`.

Special scenarios:
- Setting an empty array `[]` means that the maintenance key cannot be used even in Parse Server Cloud Code. This value cannot be set via an environment variable as there is no way to pass an empty array to Parse Server via an environment variable.
- Setting `['0.0.0.0/0', '::0']` means to allow any IPv4 and IPv6 address to use the maintenance key and effectively disables the IP filter.

Considerations:
- IPv4 and IPv6 addresses are not compared against each other. Each IP version (IPv4 and IPv6) needs to be considered separately. For example, `['0.0.0.0/0']` allows any IPv4 address and blocks every IPv6 address. Conversely, `['::0']` allows any IPv6 address and blocks every IPv4 address.
- Keep in mind that the IP version in use depends on the network stack of the environment in which Parse Server runs. A local environment may use a different IP version than a remote environment. For example, it's possible that locally the value `['0.0.0.0/0']` allows the request IP because the environment is using IPv4, but when Parse Server is deployed remotely the request IP is blocked because the remote environment is using IPv6.
- When setting the option via an environment variable the notation is a comma-separated string, for example `\"0.0.0.0/0,::0\"`.
- IPv6 zone indices (`%` suffix) are not supported, for example `fe80::1%eth0`, `fe80::1%1` or `::1%lo`.

Defaults to `['127.0.0.1', '::1']` which means that only `localhost`, the server instance on which Parse Server runs, is allowed to use the maintenance key.", + help: "(Optional) Restricts the use of maintenance key permissions to a list of IP addresses or ranges.

This option accepts a list of single IP addresses, for example `['10.0.0.1', '10.0.0.2']`. You can also use CIDR notation to specify an IP address range, for example `['10.0.1.0/24']`.

Special scenarios:
- Setting an empty array `[]` means that the maintenance key cannot be used even in Parse Server Cloud Code. This value cannot be set via an environment variable as there is no way to pass an empty array to Parse Server via an environment variable.
- Setting `['0.0.0.0/0', '::0']` means to allow any IPv4 and IPv6 address to use the maintenance key and effectively disables the IP filter.

Considerations:
- IPv4 and IPv6 addresses are not compared against each other. Each IP version (IPv4 and IPv6) needs to be considered separately. For example, `['0.0.0.0/0']` allows any IPv4 address and blocks every IPv6 address. Conversely, `['::0']` allows any IPv6 address and blocks every IPv4 address.
- Keep in mind that the IP version in use depends on the network stack of the environment in which Parse Server runs. A local environment may use a different IP version than a remote environment. For example, it's possible that locally the value `['0.0.0.0/0']` allows the request IP because the environment is using IPv4, but when Parse Server is deployed remotely the request IP is blocked because the remote environment is using IPv6.
- When setting the option via an environment variable the notation is a comma-separated string, for example `\"0.0.0.0/0,::0\"`.
- IPv6 zone indices (`%` suffix) are not supported, for example `fe80::1%eth0`, `fe80::1%1` or `::1%lo`.

Defaults to `['127.0.0.1', '::1']` which means that only `localhost`, the server instance on which Parse Server runs, is allowed to use the maintenance key.", action: parsers.arrayParser, default: ['127.0.0.1', '::1'], }, @@ -371,15 +350,13 @@ module.exports.ParseServerOptions = { }, masterKeyIps: { env: 'PARSE_SERVER_MASTER_KEY_IPS', - help: - "(Optional) Restricts the use of master key permissions to a list of IP addresses or ranges.

This option accepts a list of single IP addresses, for example `['10.0.0.1', '10.0.0.2']`. You can also use CIDR notation to specify an IP address range, for example `['10.0.1.0/24']`.

Special scenarios:
- Setting an empty array `[]` means that the master key cannot be used even in Parse Server Cloud Code. This value cannot be set via an environment variable as there is no way to pass an empty array to Parse Server via an environment variable.
- Setting `['0.0.0.0/0', '::0']` means to allow any IPv4 and IPv6 address to use the master key and effectively disables the IP filter.

Considerations:
- IPv4 and IPv6 addresses are not compared against each other. Each IP version (IPv4 and IPv6) needs to be considered separately. For example, `['0.0.0.0/0']` allows any IPv4 address and blocks every IPv6 address. Conversely, `['::0']` allows any IPv6 address and blocks every IPv4 address.
- Keep in mind that the IP version in use depends on the network stack of the environment in which Parse Server runs. A local environment may use a different IP version than a remote environment. For example, it's possible that locally the value `['0.0.0.0/0']` allows the request IP because the environment is using IPv4, but when Parse Server is deployed remotely the request IP is blocked because the remote environment is using IPv6.
- When setting the option via an environment variable the notation is a comma-separated string, for example `\"0.0.0.0/0,::0\"`.
- IPv6 zone indices (`%` suffix) are not supported, for example `fe80::1%eth0`, `fe80::1%1` or `::1%lo`.

Defaults to `['127.0.0.1', '::1']` which means that only `localhost`, the server instance on which Parse Server runs, is allowed to use the master key.", + help: "(Optional) Restricts the use of master key permissions to a list of IP addresses or ranges.

This option accepts a list of single IP addresses, for example `['10.0.0.1', '10.0.0.2']`. You can also use CIDR notation to specify an IP address range, for example `['10.0.1.0/24']`.

Special scenarios:
- Setting an empty array `[]` means that the master key cannot be used even in Parse Server Cloud Code. This value cannot be set via an environment variable as there is no way to pass an empty array to Parse Server via an environment variable.
- Setting `['0.0.0.0/0', '::0']` means to allow any IPv4 and IPv6 address to use the master key and effectively disables the IP filter.

Considerations:
- IPv4 and IPv6 addresses are not compared against each other. Each IP version (IPv4 and IPv6) needs to be considered separately. For example, `['0.0.0.0/0']` allows any IPv4 address and blocks every IPv6 address. Conversely, `['::0']` allows any IPv6 address and blocks every IPv4 address.
- Keep in mind that the IP version in use depends on the network stack of the environment in which Parse Server runs. A local environment may use a different IP version than a remote environment. For example, it's possible that locally the value `['0.0.0.0/0']` allows the request IP because the environment is using IPv4, but when Parse Server is deployed remotely the request IP is blocked because the remote environment is using IPv6.
- When setting the option via an environment variable the notation is a comma-separated string, for example `\"0.0.0.0/0,::0\"`.
- IPv6 zone indices (`%` suffix) are not supported, for example `fe80::1%eth0`, `fe80::1%1` or `::1%lo`.

Defaults to `['127.0.0.1', '::1']` which means that only `localhost`, the server instance on which Parse Server runs, is allowed to use the master key.", action: parsers.arrayParser, default: ['127.0.0.1', '::1'], }, masterKeyTtl: { env: 'PARSE_SERVER_MASTER_KEY_TTL', - help: - '(Optional) The duration in seconds for which the current `masterKey` is being used before it is requested again if `masterKey` is set to a function. If `masterKey` is not set to a function, this option has no effect. Default is `0`, which means the master key is requested by invoking the `masterKey` function every time the master key is used internally by Parse Server.', + help: '(Optional) The duration in seconds for which the current `masterKey` is being used before it is requested again if `masterKey` is set to a function. If `masterKey` is not set to a function, this option has no effect. Default is `0`, which means the master key is requested by invoking the `masterKey` function every time the master key is used internally by Parse Server.', action: parsers.numberParser('masterKeyTtl'), }, maxLimit: { @@ -389,8 +366,7 @@ module.exports.ParseServerOptions = { }, maxLogFiles: { env: 'PARSE_SERVER_MAX_LOG_FILES', - help: - "Maximum number of logs to keep. If not set, no logs will be removed. This can be a number of files or number of days. If using days, add 'd' as the suffix. (default: null)", + help: "Maximum number of logs to keep. If not set, no logs will be removed. This can be a number of files or number of days. If using days, add 'd' as the suffix. (default: null)", action: parsers.numberOrStringParser('maxLogFiles'), }, maxUploadSize: { @@ -457,15 +433,13 @@ module.exports.ParseServerOptions = { }, preventLoginWithUnverifiedEmail: { env: 'PARSE_SERVER_PREVENT_LOGIN_WITH_UNVERIFIED_EMAIL', - help: - 'Set to `true` to prevent a user from logging in if the email has not yet been verified and email verification is required.

Default is `false`.
Requires option `verifyUserEmails: true`.', + help: 'Set to `true` to prevent a user from logging in if the email has not yet been verified and email verification is required.

Default is `false`.
Requires option `verifyUserEmails: true`.', action: parsers.booleanParser, default: false, }, preventSignupWithUnverifiedEmail: { env: 'PARSE_SERVER_PREVENT_SIGNUP_WITH_UNVERIFIED_EMAIL', - help: - "If set to `true` it prevents a user from signing up if the email has not yet been verified and email verification is required. In that case the server responds to the sign-up with HTTP status 400 and a Parse Error 205 `EMAIL_NOT_FOUND`. If set to `false` the server responds with HTTP status 200, and client SDKs return an unauthenticated Parse User without session token. In that case subsequent requests fail until the user's email address is verified.

Default is `false`.
Requires option `verifyUserEmails: true`.", + help: "If set to `true` it prevents a user from signing up if the email has not yet been verified and email verification is required. In that case the server responds to the sign-up with HTTP status 400 and a Parse Error 205 `EMAIL_NOT_FOUND`. If set to `false` the server responds with HTTP status 200, and client SDKs return an unauthenticated Parse User without session token. In that case subsequent requests fail until the user's email address is verified.

Default is `false`.
Requires option `verifyUserEmails: true`.", action: parsers.booleanParser, default: false, }, @@ -485,14 +459,12 @@ module.exports.ParseServerOptions = { }, push: { env: 'PARSE_SERVER_PUSH', - help: - 'Configuration for push, as stringified JSON. See http://docs.parseplatform.org/parse-server/guide/#push-notifications', + help: 'Configuration for push, as stringified JSON. See http://docs.parseplatform.org/parse-server/guide/#push-notifications', action: parsers.objectParser, }, rateLimit: { env: 'PARSE_SERVER_RATE_LIMIT', - help: - "Options to limit repeated requests to Parse Server APIs. This can be used to protect sensitive endpoints such as `/requestPasswordReset` from brute-force attacks or Parse Server as a whole from denial-of-service (DoS) attacks.

\u2139\uFE0F Mind the following limitations:
- rate limits applied per IP address; this limits protection against distributed denial-of-service (DDoS) attacks where many requests are coming from various IP addresses
- if multiple Parse Server instances are behind a load balancer or ran in a cluster, each instance will calculate it's own request rates, independent from other instances; this limits the applicability of this feature when using a load balancer and another rate limiting solution that takes requests across all instances into account may be more suitable
- this feature provides basic protection against denial-of-service attacks, but a more sophisticated solution works earlier in the request flow and prevents a malicious requests to even reach a server instance; it's therefore recommended to implement a solution according to architecture and user case.", + help: "Options to limit repeated requests to Parse Server APIs. This can be used to protect sensitive endpoints such as `/requestPasswordReset` from brute-force attacks or Parse Server as a whole from denial-of-service (DoS) attacks.

\u2139\uFE0F Mind the following limitations:
- rate limits applied per IP address; this limits protection against distributed denial-of-service (DDoS) attacks where many requests are coming from various IP addresses
- if multiple Parse Server instances are behind a load balancer or ran in a cluster, each instance will calculate it's own request rates, independent from other instances; this limits the applicability of this feature when using a load balancer and another rate limiting solution that takes requests across all instances into account may be more suitable
- this feature provides basic protection against denial-of-service attacks, but a more sophisticated solution works earlier in the request flow and prevents a malicious requests to even reach a server instance; it's therefore recommended to implement a solution according to architecture and user case.", action: parsers.arrayParser, type: 'RateLimitOptions[]', default: [], @@ -503,8 +475,7 @@ module.exports.ParseServerOptions = { }, requestKeywordDenylist: { env: 'PARSE_SERVER_REQUEST_KEYWORD_DENYLIST', - help: - 'An array of keys and values that are prohibited in database read and write requests to prevent potential security vulnerabilities. It is possible to specify only a key (`{"key":"..."}`), only a value (`{"value":"..."}`) or a key-value pair (`{"key":"...","value":"..."}`). The specification can use the following types: `boolean`, `numeric` or `string`, where `string` will be interpreted as a regex notation. Request data is deep-scanned for matching definitions to detect also any nested occurrences. Defaults are patterns that are likely to be used in malicious requests. Setting this option will override the default patterns.', + help: 'An array of keys and values that are prohibited in database read and write requests to prevent potential security vulnerabilities. It is possible to specify only a key (`{"key":"..."}`), only a value (`{"value":"..."}`) or a key-value pair (`{"key":"...","value":"..."}`). The specification can use the following types: `boolean`, `numeric` or `string`, where `string` will be interpreted as a regex notation. Request data is deep-scanned for matching definitions to detect also any nested occurrences. Defaults are patterns that are likely to be used in malicious requests. Setting this option will override the default patterns.', action: parsers.arrayParser, default: [ { @@ -525,8 +496,7 @@ module.exports.ParseServerOptions = { }, revokeSessionOnPasswordReset: { env: 'PARSE_SERVER_REVOKE_SESSION_ON_PASSWORD_RESET', - help: - "When a user changes their password, either through the reset password email or while logged in, all sessions are revoked if this is true. Set to false if you don't want to revoke sessions.", + help: "When a user changes their password, either through the reset password email or while logged in, all sessions are revoked if this is true. Set to false if you don't want to revoke sessions.", action: parsers.booleanParser, default: true, }, @@ -551,8 +521,7 @@ module.exports.ParseServerOptions = { }, sendUserEmailVerification: { env: 'PARSE_SERVER_SEND_USER_EMAIL_VERIFICATION', - help: - 'Set to `false` to prevent sending of verification email. Supports a function with a return value of `true` or `false` for conditional email sending.

Default is `true`.
', + help: 'Set to `false` to prevent sending of verification email. Supports a function with a return value of `true` or `false` for conditional email sending.

Default is `true`.
', default: true, }, serverCloseComplete: { @@ -582,15 +551,13 @@ module.exports.ParseServerOptions = { }, trustProxy: { env: 'PARSE_SERVER_TRUST_PROXY', - help: - 'The trust proxy settings. It is important to understand the exact setup of the reverse proxy, since this setting will trust values provided in the Parse Server API request. See the express trust proxy settings documentation. Defaults to `false`.', + help: 'The trust proxy settings. It is important to understand the exact setup of the reverse proxy, since this setting will trust values provided in the Parse Server API request. See the express trust proxy settings documentation. Defaults to `false`.', action: parsers.objectParser, default: [], }, userSensitiveFields: { env: 'PARSE_SERVER_USER_SENSITIVE_FIELDS', - help: - 'Personally identifiable information fields in the user table the should be removed for non-authorized users. Deprecated @see protectedFields', + help: 'Personally identifiable information fields in the user table the should be removed for non-authorized users. Deprecated @see protectedFields', action: parsers.arrayParser, }, verbose: { @@ -600,8 +567,7 @@ module.exports.ParseServerOptions = { }, verifyUserEmails: { env: 'PARSE_SERVER_VERIFY_USER_EMAILS', - help: - 'Set to `true` to require users to verify their email address to complete the sign-up process. Supports a function with a return value of `true` or `false` for conditional verification.

Default is `false`.', + help: 'Set to `true` to require users to verify their email address to complete the sign-up process. Supports a function with a return value of `true` or `false` for conditional verification.

Default is `false`.', default: false, }, webhookKey: { @@ -612,64 +578,54 @@ module.exports.ParseServerOptions = { module.exports.RateLimitOptions = { errorResponseMessage: { env: 'PARSE_SERVER_RATE_LIMIT_ERROR_RESPONSE_MESSAGE', - help: - 'The error message that should be returned in the body of the HTTP 429 response when the rate limit is hit. Default is `Too many requests.`.', + help: 'The error message that should be returned in the body of the HTTP 429 response when the rate limit is hit. Default is `Too many requests.`.', default: 'Too many requests.', }, includeInternalRequests: { env: 'PARSE_SERVER_RATE_LIMIT_INCLUDE_INTERNAL_REQUESTS', - help: - 'Optional, if `true` the rate limit will also apply to requests that are made in by Cloud Code, default is `false`. Note that a public Cloud Code function that triggers internal requests may circumvent rate limiting and be vulnerable to attacks.', + help: 'Optional, if `true` the rate limit will also apply to requests that are made in by Cloud Code, default is `false`. Note that a public Cloud Code function that triggers internal requests may circumvent rate limiting and be vulnerable to attacks.', action: parsers.booleanParser, default: false, }, includeMasterKey: { env: 'PARSE_SERVER_RATE_LIMIT_INCLUDE_MASTER_KEY', - help: - 'Optional, if `true` the rate limit will also apply to requests using the `masterKey`, default is `false`. Note that a public Cloud Code function that triggers internal requests using the `masterKey` may circumvent rate limiting and be vulnerable to attacks.', + help: 'Optional, if `true` the rate limit will also apply to requests using the `masterKey`, default is `false`. Note that a public Cloud Code function that triggers internal requests using the `masterKey` may circumvent rate limiting and be vulnerable to attacks.', action: parsers.booleanParser, default: false, }, redisUrl: { env: 'PARSE_SERVER_RATE_LIMIT_REDIS_URL', - help: - 'Optional, the URL of the Redis server to store rate limit data. This allows to rate limit requests for multiple servers by calculating the sum of all requests across all servers. This is useful if multiple servers are processing requests behind a load balancer. For example, the limit of 10 requests is reached if each of 2 servers processed 5 requests.', + help: 'Optional, the URL of the Redis server to store rate limit data. This allows to rate limit requests for multiple servers by calculating the sum of all requests across all servers. This is useful if multiple servers are processing requests behind a load balancer. For example, the limit of 10 requests is reached if each of 2 servers processed 5 requests.', }, requestCount: { env: 'PARSE_SERVER_RATE_LIMIT_REQUEST_COUNT', - help: - 'The number of requests that can be made per IP address within the time window set in `requestTimeWindow` before the rate limit is applied.', + help: 'The number of requests that can be made per IP address within the time window set in `requestTimeWindow` before the rate limit is applied.', action: parsers.numberParser('requestCount'), }, requestMethods: { env: 'PARSE_SERVER_RATE_LIMIT_REQUEST_METHODS', - help: - 'Optional, the HTTP request methods to which the rate limit should be applied, default is all methods.', + help: 'Optional, the HTTP request methods to which the rate limit should be applied, default is all methods.', action: parsers.arrayParser, }, requestPath: { env: 'PARSE_SERVER_RATE_LIMIT_REQUEST_PATH', - help: - 'The path of the API route to be rate limited. Route paths, in combination with a request method, define the endpoints at which requests can be made. Route paths can be strings, string patterns, or regular expression. See: https://expressjs.com/en/guide/routing.html', + help: 'The path of the API route to be rate limited. Route paths, in combination with a request method, define the endpoints at which requests can be made. Route paths can be strings, string patterns, or regular expression. See: https://expressjs.com/en/guide/routing.html', required: true, }, requestTimeWindow: { env: 'PARSE_SERVER_RATE_LIMIT_REQUEST_TIME_WINDOW', - help: - 'The window of time in milliseconds within which the number of requests set in `requestCount` can be made before the rate limit is applied.', + help: 'The window of time in milliseconds within which the number of requests set in `requestCount` can be made before the rate limit is applied.', action: parsers.numberParser('requestTimeWindow'), }, zone: { env: 'PARSE_SERVER_RATE_LIMIT_ZONE', - help: - "The type of rate limit to apply. The following types are supported:

- `global`: rate limit based on the number of requests made by all users
- `ip`: rate limit based on the IP address of the request
- `user`: rate limit based on the user ID of the request
- `session`: rate limit based on the session token of the request


:default: 'ip'", + help: "The type of rate limit to apply. The following types are supported:

- `global`: rate limit based on the number of requests made by all users
- `ip`: rate limit based on the IP address of the request
- `user`: rate limit based on the user ID of the request
- `session`: rate limit based on the session token of the request


:default: 'ip'", }, }; module.exports.SecurityOptions = { checkGroups: { env: 'PARSE_SERVER_SECURITY_CHECK_GROUPS', - help: - 'The security check groups to run. This allows to add custom security checks or override existing ones. Default are the groups defined in `CheckGroups.js`.', + help: 'The security check groups to run. This allows to add custom security checks or override existing ones. Default are the groups defined in `CheckGroups.js`.', action: parsers.arrayParser, }, enableCheck: { @@ -680,8 +636,7 @@ module.exports.SecurityOptions = { }, enableCheckLog: { env: 'PARSE_SERVER_SECURITY_ENABLE_CHECK_LOG', - help: - 'Is true if the security check report should be written to logs. This should only be enabled temporarily to not expose weak security settings in logs.', + help: 'Is true if the security check report should be written to logs. This should only be enabled temporarily to not expose weak security settings in logs.', action: parsers.booleanParser, default: false, }, @@ -709,28 +664,24 @@ module.exports.PagesOptions = { }, enableRouter: { env: 'PARSE_SERVER_PAGES_ENABLE_ROUTER', - help: - 'Is true if the pages router should be enabled; this is required for any of the pages options to take effect.', + help: 'Is true if the pages router should be enabled; this is required for any of the pages options to take effect.', action: parsers.booleanParser, default: false, }, forceRedirect: { env: 'PARSE_SERVER_PAGES_FORCE_REDIRECT', - help: - 'Is true if responses should always be redirects and never content, false if the response type should depend on the request type (GET request -> content response; POST request -> redirect response).', + help: 'Is true if responses should always be redirects and never content, false if the response type should depend on the request type (GET request -> content response; POST request -> redirect response).', action: parsers.booleanParser, default: false, }, localizationFallbackLocale: { env: 'PARSE_SERVER_PAGES_LOCALIZATION_FALLBACK_LOCALE', - help: - 'The fallback locale for localization if no matching translation is provided for the given locale. This is only relevant when providing translation resources via JSON file.', + help: 'The fallback locale for localization if no matching translation is provided for the given locale. This is only relevant when providing translation resources via JSON file.', default: 'en', }, localizationJsonPath: { env: 'PARSE_SERVER_PAGES_LOCALIZATION_JSON_PATH', - help: - 'The path to the JSON file for localization; the translations will be used to fill template placeholders according to the locale.', + help: 'The path to the JSON file for localization; the translations will be used to fill template placeholders according to the locale.', }, pagesEndpoint: { env: 'PARSE_SERVER_PAGES_PAGES_ENDPOINT', @@ -739,14 +690,12 @@ module.exports.PagesOptions = { }, pagesPath: { env: 'PARSE_SERVER_PAGES_PAGES_PATH', - help: - "The path to the pages directory; this also defines where the static endpoint '/apps' points to. Default is the './public/' directory.", + help: "The path to the pages directory; this also defines where the static endpoint '/apps' points to. Default is the './public/' directory.", default: './public', }, placeholders: { env: 'PARSE_SERVER_PAGES_PLACEHOLDERS', - help: - 'The placeholder keys and values which will be filled in pages; this can be a simple object or a callback function.', + help: 'The placeholder keys and values which will be filled in pages; this can be a simple object or a callback function.', action: parsers.objectParser, default: {}, }, @@ -873,30 +822,25 @@ module.exports.LiveQueryOptions = { module.exports.LiveQueryServerOptions = { appId: { env: 'PARSE_LIVE_QUERY_SERVER_APP_ID', - help: - 'This string should match the appId in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same appId.', + help: 'This string should match the appId in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same appId.', }, cacheTimeout: { env: 'PARSE_LIVE_QUERY_SERVER_CACHE_TIMEOUT', - help: - "Number in milliseconds. When clients provide the sessionToken to the LiveQuery server, the LiveQuery server will try to fetch its ParseUser's objectId from parse server and store it in the cache. The value defines the duration of the cache. Check the following Security section and our protocol specification for details, defaults to 5 * 1000 ms (5 seconds).", + help: "Number in milliseconds. When clients provide the sessionToken to the LiveQuery server, the LiveQuery server will try to fetch its ParseUser's objectId from parse server and store it in the cache. The value defines the duration of the cache. Check the following Security section and our protocol specification for details, defaults to 5 * 1000 ms (5 seconds).", action: parsers.numberParser('cacheTimeout'), }, keyPairs: { env: 'PARSE_LIVE_QUERY_SERVER_KEY_PAIRS', - help: - 'A JSON object that serves as a whitelist of keys. It is used for validating clients when they try to connect to the LiveQuery server. Check the following Security section and our protocol specification for details.', + help: 'A JSON object that serves as a whitelist of keys. It is used for validating clients when they try to connect to the LiveQuery server. Check the following Security section and our protocol specification for details.', action: parsers.objectParser, }, logLevel: { env: 'PARSE_LIVE_QUERY_SERVER_LOG_LEVEL', - help: - 'This string defines the log level of the LiveQuery server. We support VERBOSE, INFO, ERROR, NONE, defaults to INFO.', + help: 'This string defines the log level of the LiveQuery server. We support VERBOSE, INFO, ERROR, NONE, defaults to INFO.', }, masterKey: { env: 'PARSE_LIVE_QUERY_SERVER_MASTER_KEY', - help: - 'This string should match the masterKey in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same masterKey.', + help: 'This string should match the masterKey in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same masterKey.', }, port: { env: 'PARSE_LIVE_QUERY_SERVER_PORT', @@ -920,13 +864,11 @@ module.exports.LiveQueryServerOptions = { }, serverURL: { env: 'PARSE_LIVE_QUERY_SERVER_SERVER_URL', - help: - 'This string should match the serverURL in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same serverURL.', + help: 'This string should match the serverURL in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same serverURL.', }, websocketTimeout: { env: 'PARSE_LIVE_QUERY_SERVER_WEBSOCKET_TIMEOUT', - help: - 'Number of milliseconds between ping/pong frames. The WebSocket server sends ping/pong frames to the clients to keep the WebSocket alive. This value defines the interval of the ping/pong frame from the server to clients, defaults to 10 * 1000 ms (10 s).', + help: 'Number of milliseconds between ping/pong frames. The WebSocket server sends ping/pong frames to the clients to keep the WebSocket alive. This value defines the interval of the ping/pong frame from the server to clients, defaults to 10 * 1000 ms (10 s).', action: parsers.numberParser('websocketTimeout'), }, wssAdapter: { @@ -938,15 +880,13 @@ module.exports.LiveQueryServerOptions = { module.exports.IdempotencyOptions = { paths: { env: 'PARSE_SERVER_EXPERIMENTAL_IDEMPOTENCY_PATHS', - help: - 'An array of paths for which the feature should be enabled. The mount path must not be included, for example instead of `/parse/functions/myFunction` specifiy `functions/myFunction`. The entries are interpreted as regular expression, for example `functions/.*` matches all functions, `jobs/.*` matches all jobs, `classes/.*` matches all classes, `.*` matches all paths.', + help: 'An array of paths for which the feature should be enabled. The mount path must not be included, for example instead of `/parse/functions/myFunction` specifiy `functions/myFunction`. The entries are interpreted as regular expression, for example `functions/.*` matches all functions, `jobs/.*` matches all jobs, `classes/.*` matches all classes, `.*` matches all paths.', action: parsers.arrayParser, default: [], }, ttl: { env: 'PARSE_SERVER_EXPERIMENTAL_IDEMPOTENCY_TTL', - help: - 'The duration in seconds after which a request record is discarded from the database, defaults to 300s.', + help: 'The duration in seconds after which a request record is discarded from the database, defaults to 300s.', action: parsers.numberParser('ttl'), default: 300, }, @@ -954,20 +894,17 @@ module.exports.IdempotencyOptions = { module.exports.AccountLockoutOptions = { duration: { env: 'PARSE_SERVER_ACCOUNT_LOCKOUT_DURATION', - help: - 'Set the duration in minutes that a locked-out account remains locked out before automatically becoming unlocked.

Valid values are greater than `0` and less than `100000`.', + help: 'Set the duration in minutes that a locked-out account remains locked out before automatically becoming unlocked.

Valid values are greater than `0` and less than `100000`.', action: parsers.numberParser('duration'), }, threshold: { env: 'PARSE_SERVER_ACCOUNT_LOCKOUT_THRESHOLD', - help: - 'Set the number of failed sign-in attempts that will cause a user account to be locked. If the account is locked. The account will unlock after the duration set in the `duration` option has passed and no further login attempts have been made.

Valid values are greater than `0` and less than `1000`.', + help: 'Set the number of failed sign-in attempts that will cause a user account to be locked. If the account is locked. The account will unlock after the duration set in the `duration` option has passed and no further login attempts have been made.

Valid values are greater than `0` and less than `1000`.', action: parsers.numberParser('threshold'), }, unlockOnPasswordReset: { env: 'PARSE_SERVER_ACCOUNT_LOCKOUT_UNLOCK_ON_PASSWORD_RESET', - help: - 'Set to `true` if the account should be unlocked after a successful password reset.

Default is `false`.
Requires options `duration` and `threshold` to be set.', + help: 'Set to `true` if the account should be unlocked after a successful password reset.

Default is `false`.
Requires options `duration` and `threshold` to be set.', action: parsers.booleanParser, default: false, }, @@ -975,57 +912,48 @@ module.exports.AccountLockoutOptions = { module.exports.PasswordPolicyOptions = { doNotAllowUsername: { env: 'PARSE_SERVER_PASSWORD_POLICY_DO_NOT_ALLOW_USERNAME', - help: - 'Set to `true` to disallow the username as part of the password.

Default is `false`.', + help: 'Set to `true` to disallow the username as part of the password.

Default is `false`.', action: parsers.booleanParser, default: false, }, maxPasswordAge: { env: 'PARSE_SERVER_PASSWORD_POLICY_MAX_PASSWORD_AGE', - help: - 'Set the number of days after which a password expires. Login attempts fail if the user does not reset the password before expiration.', + help: 'Set the number of days after which a password expires. Login attempts fail if the user does not reset the password before expiration.', action: parsers.numberParser('maxPasswordAge'), }, maxPasswordHistory: { env: 'PARSE_SERVER_PASSWORD_POLICY_MAX_PASSWORD_HISTORY', - help: - 'Set the number of previous password that will not be allowed to be set as new password. If the option is not set or set to `0`, no previous passwords will be considered.

Valid values are >= `0` and <= `20`.
Default is `0`.', + help: 'Set the number of previous password that will not be allowed to be set as new password. If the option is not set or set to `0`, no previous passwords will be considered.

Valid values are >= `0` and <= `20`.
Default is `0`.', action: parsers.numberParser('maxPasswordHistory'), }, resetPasswordSuccessOnInvalidEmail: { env: 'PARSE_SERVER_PASSWORD_POLICY_RESET_PASSWORD_SUCCESS_ON_INVALID_EMAIL', - help: - 'Set to `true` if a request to reset the password should return a success response even if the provided email address is invalid, or `false` if the request should return an error response if the email address is invalid.

Default is `true`.', + help: 'Set to `true` if a request to reset the password should return a success response even if the provided email address is invalid, or `false` if the request should return an error response if the email address is invalid.

Default is `true`.', action: parsers.booleanParser, default: true, }, resetTokenReuseIfValid: { env: 'PARSE_SERVER_PASSWORD_POLICY_RESET_TOKEN_REUSE_IF_VALID', - help: - 'Set to `true` if a password reset token should be reused in case another token is requested but there is a token that is still valid, i.e. has not expired. This avoids the often observed issue that a user requests multiple emails and does not know which link contains a valid token because each newly generated token would invalidate the previous token.

Default is `false`.', + help: 'Set to `true` if a password reset token should be reused in case another token is requested but there is a token that is still valid, i.e. has not expired. This avoids the often observed issue that a user requests multiple emails and does not know which link contains a valid token because each newly generated token would invalidate the previous token.

Default is `false`.', action: parsers.booleanParser, default: false, }, resetTokenValidityDuration: { env: 'PARSE_SERVER_PASSWORD_POLICY_RESET_TOKEN_VALIDITY_DURATION', - help: - 'Set the validity duration of the password reset token in seconds after which the token expires. The token is used in the link that is set in the email. After the token expires, the link becomes invalid and a new link has to be sent. If the option is not set or set to `undefined`, then the token never expires.

For example, to expire the token after 2 hours, set a value of 7200 seconds (= 60 seconds * 60 minutes * 2 hours).

Default is `undefined`.', + help: 'Set the validity duration of the password reset token in seconds after which the token expires. The token is used in the link that is set in the email. After the token expires, the link becomes invalid and a new link has to be sent. If the option is not set or set to `undefined`, then the token never expires.

For example, to expire the token after 2 hours, set a value of 7200 seconds (= 60 seconds * 60 minutes * 2 hours).

Default is `undefined`.', action: parsers.numberParser('resetTokenValidityDuration'), }, validationError: { env: 'PARSE_SERVER_PASSWORD_POLICY_VALIDATION_ERROR', - help: - 'Set the error message to be sent.

Default is `Password does not meet the Password Policy requirements.`', + help: 'Set the error message to be sent.

Default is `Password does not meet the Password Policy requirements.`', }, validatorCallback: { env: 'PARSE_SERVER_PASSWORD_POLICY_VALIDATOR_CALLBACK', - help: - 'Set a callback function to validate a password to be accepted.

If used in combination with `validatorPattern`, the password must pass both to be accepted.', + help: 'Set a callback function to validate a password to be accepted.

If used in combination with `validatorPattern`, the password must pass both to be accepted.', }, validatorPattern: { env: 'PARSE_SERVER_PASSWORD_POLICY_VALIDATOR_PATTERN', - help: - 'Set the regular expression validation pattern a password must match to be accepted.

If used in combination with `validatorCallback`, the password must pass both to be accepted.', + help: 'Set the regular expression validation pattern a password must match to be accepted.

If used in combination with `validatorCallback`, the password must pass both to be accepted.', }, }; module.exports.FileUploadOptions = { @@ -1049,8 +977,7 @@ module.exports.FileUploadOptions = { }, fileExtensions: { env: 'PARSE_SERVER_FILE_UPLOAD_FILE_EXTENSIONS', - help: - "Sets the allowed file extensions for uploading files. The extension is defined as an array of file extensions, or a regex pattern.

It is recommended to restrict the file upload extensions as much as possible. HTML files are especially problematic as they may be used by an attacker who uploads a HTML form to look legitimate under your app's domain name, or to compromise the session token of another user via accessing the browser's local storage.

Defaults to `^(?!(h|H)(t|T)(m|M)(l|L)?$)` which allows any file extension except HTML files.", + help: "Sets the allowed file extensions for uploading files. The extension is defined as an array of file extensions, or a regex pattern.

It is recommended to restrict the file upload extensions as much as possible. HTML files are especially problematic as they may be used by an attacker who uploads a HTML form to look legitimate under your app's domain name, or to compromise the session token of another user via accessing the browser's local storage.

Defaults to `^(?!(h|H)(t|T)(m|M)(l|L)?$)` which allows any file extension except HTML files.", action: parsers.arrayParser, default: ['^(?!(h|H)(t|T)(m|M)(l|L)?$)'], }, @@ -1058,51 +985,43 @@ module.exports.FileUploadOptions = { module.exports.DatabaseOptions = { autoSelectFamily: { env: 'PARSE_SERVER_DATABASE_AUTO_SELECT_FAMILY', - help: - 'The MongoDB driver option to set whether the socket attempts to connect to IPv6 and IPv4 addresses until a connection is established. If available, the driver will select the first IPv6 address.', + help: 'The MongoDB driver option to set whether the socket attempts to connect to IPv6 and IPv4 addresses until a connection is established. If available, the driver will select the first IPv6 address.', action: parsers.booleanParser, }, autoSelectFamilyAttemptTimeout: { env: 'PARSE_SERVER_DATABASE_AUTO_SELECT_FAMILY_ATTEMPT_TIMEOUT', - help: - 'The MongoDB driver option to specify the amount of time in milliseconds to wait for a connection attempt to finish before trying the next address when using the autoSelectFamily option. If set to a positive integer less than 10, the value 10 is used instead.', + help: 'The MongoDB driver option to specify the amount of time in milliseconds to wait for a connection attempt to finish before trying the next address when using the autoSelectFamily option. If set to a positive integer less than 10, the value 10 is used instead.', action: parsers.numberParser('autoSelectFamilyAttemptTimeout'), }, connectTimeoutMS: { env: 'PARSE_SERVER_DATABASE_CONNECT_TIMEOUT_MS', - help: - 'The MongoDB driver option to specify the amount of time, in milliseconds, to wait to establish a single TCP socket connection to the server before raising an error. Specifying 0 disables the connection timeout.', + help: 'The MongoDB driver option to specify the amount of time, in milliseconds, to wait to establish a single TCP socket connection to the server before raising an error. Specifying 0 disables the connection timeout.', action: parsers.numberParser('connectTimeoutMS'), }, enableSchemaHooks: { env: 'PARSE_SERVER_DATABASE_ENABLE_SCHEMA_HOOKS', - help: - 'Enables database real-time hooks to update single schema cache. Set to `true` if using multiple Parse Servers instances connected to the same database. Failing to do so will cause a schema change to not propagate to all instances and re-syncing will only happen when the instances restart. To use this feature with MongoDB, a replica set cluster with [change stream](https://docs.mongodb.com/manual/changeStreams/#availability) support is required.', + help: 'Enables database real-time hooks to update single schema cache. Set to `true` if using multiple Parse Servers instances connected to the same database. Failing to do so will cause a schema change to not propagate to all instances and re-syncing will only happen when the instances restart. To use this feature with MongoDB, a replica set cluster with [change stream](https://docs.mongodb.com/manual/changeStreams/#availability) support is required.', action: parsers.booleanParser, default: false, }, maxPoolSize: { env: 'PARSE_SERVER_DATABASE_MAX_POOL_SIZE', - help: - 'The MongoDB driver option to set the maximum number of opened, cached, ready-to-use database connections maintained by the driver.', + help: 'The MongoDB driver option to set the maximum number of opened, cached, ready-to-use database connections maintained by the driver.', action: parsers.numberParser('maxPoolSize'), }, maxStalenessSeconds: { env: 'PARSE_SERVER_DATABASE_MAX_STALENESS_SECONDS', - help: - 'The MongoDB driver option to set the maximum replication lag for reads from secondary nodes.', + help: 'The MongoDB driver option to set the maximum replication lag for reads from secondary nodes.', action: parsers.numberParser('maxStalenessSeconds'), }, maxTimeMS: { env: 'PARSE_SERVER_DATABASE_MAX_TIME_MS', - help: - 'The MongoDB driver option to set a cumulative time limit in milliseconds for processing operations on a cursor.', + help: 'The MongoDB driver option to set a cumulative time limit in milliseconds for processing operations on a cursor.', action: parsers.numberParser('maxTimeMS'), }, minPoolSize: { env: 'PARSE_SERVER_DATABASE_MIN_POOL_SIZE', - help: - 'The MongoDB driver option to set the minimum number of opened, cached, ready-to-use database connections maintained by the driver.', + help: 'The MongoDB driver option to set the minimum number of opened, cached, ready-to-use database connections maintained by the driver.', action: parsers.numberParser('minPoolSize'), }, retryWrites: { @@ -1112,14 +1031,12 @@ module.exports.DatabaseOptions = { }, schemaCacheTtl: { env: 'PARSE_SERVER_DATABASE_SCHEMA_CACHE_TTL', - help: - 'The duration in seconds after which the schema cache expires and will be refetched from the database. Use this option if using multiple Parse Servers instances connected to the same database. A low duration will cause the schema cache to be updated too often, causing unnecessary database reads. A high duration will cause the schema to be updated too rarely, increasing the time required until schema changes propagate to all server instances. This feature can be used as an alternative or in conjunction with the option `enableSchemaHooks`. Default is infinite which means the schema cache never expires.', + help: 'The duration in seconds after which the schema cache expires and will be refetched from the database. Use this option if using multiple Parse Servers instances connected to the same database. A low duration will cause the schema cache to be updated too often, causing unnecessary database reads. A high duration will cause the schema to be updated too rarely, increasing the time required until schema changes propagate to all server instances. This feature can be used as an alternative or in conjunction with the option `enableSchemaHooks`. Default is infinite which means the schema cache never expires.', action: parsers.numberParser('schemaCacheTtl'), }, socketTimeoutMS: { env: 'PARSE_SERVER_DATABASE_SOCKET_TIMEOUT_MS', - help: - 'The MongoDB driver option to specify the amount of time, in milliseconds, spent attempting to send or receive on a socket before timing out. Specifying 0 means no timeout.', + help: 'The MongoDB driver option to specify the amount of time, in milliseconds, spent attempting to send or receive on a socket before timing out. Specifying 0 means no timeout.', action: parsers.numberParser('socketTimeoutMS'), }, }; @@ -1143,20 +1060,17 @@ module.exports.LogLevels = { }, triggerAfter: { env: 'PARSE_SERVER_LOG_LEVELS_TRIGGER_AFTER', - help: - 'Log level used by the Cloud Code Triggers `afterSave`, `afterDelete`, `afterFind`, `afterLogout`. Default is `info`.', + help: 'Log level used by the Cloud Code Triggers `afterSave`, `afterDelete`, `afterFind`, `afterLogout`. Default is `info`.', default: 'info', }, triggerBeforeError: { env: 'PARSE_SERVER_LOG_LEVELS_TRIGGER_BEFORE_ERROR', - help: - 'Log level used by the Cloud Code Triggers `beforeSave`, `beforeDelete`, `beforeFind`, `beforeLogin` on error. Default is `error`.', + help: 'Log level used by the Cloud Code Triggers `beforeSave`, `beforeDelete`, `beforeFind`, `beforeLogin` on error. Default is `error`.', default: 'error', }, triggerBeforeSuccess: { env: 'PARSE_SERVER_LOG_LEVELS_TRIGGER_BEFORE_SUCCESS', - help: - 'Log level used by the Cloud Code Triggers `beforeSave`, `beforeDelete`, `beforeFind`, `beforeLogin` on success. Default is `info`.', + help: 'Log level used by the Cloud Code Triggers `beforeSave`, `beforeDelete`, `beforeFind`, `beforeLogin` on success. Default is `info`.', default: 'info', }, }; diff --git a/src/RestQuery.js b/src/RestQuery.js index 621700984b..ec47eb2e3d 100644 --- a/src/RestQuery.js +++ b/src/RestQuery.js @@ -53,15 +53,15 @@ async function RestQuery({ enforceRoleSecurity(method, className, auth); const result = runBeforeFind ? await triggers.maybeRunQueryTrigger( - triggers.Types.beforeFind, - className, - restWhere, - restOptions, - config, - auth, - context, - method === RestQuery.Method.get - ) + triggers.Types.beforeFind, + className, + restWhere, + restOptions, + config, + auth, + context, + method === RestQuery.Method.get + ) : Promise.resolve({ restWhere, restOptions }); return new _UnsafeRestQuery( @@ -749,7 +749,12 @@ _UnsafeRestQuery.prototype.runFind = async function (options = {}) { if (options.op) { findOptions.op = options.op; } - const results = await this.config.database.find(this.className, this.restWhere, findOptions, this.auth); + const results = await this.config.database.find( + this.className, + this.restWhere, + findOptions, + this.auth + ); if (this.className === '_User' && !findOptions.explain) { for (var result of results) { this.cleanResultAuthData(result); diff --git a/src/RestWrite.js b/src/RestWrite.js index ad7aea803b..7312eb5bd0 100644 --- a/src/RestWrite.js +++ b/src/RestWrite.js @@ -504,7 +504,9 @@ RestWrite.prototype.ensureUniqueAuthDataId = async function () { key => this.data.authData[key] && this.data.authData[key].id ); - if (!hasAuthDataId) { return; } + if (!hasAuthDataId) { + return; + } const r = await Auth.findUsersWithAuthData(this.config, this.data.authData); const results = this.filteredObjectsByACL(r); @@ -547,7 +549,6 @@ RestWrite.prototype.handleAuthData = async function (authData) { // User found with provided authData if (results.length === 1) { - this.storage.authProvider = Object.keys(authData).join(','); const { hasMutatedAuthData, mutatedAuthData } = Auth.hasMutatedAuthData( @@ -809,7 +810,9 @@ RestWrite.prototype._validateEmail = function () { }; RestWrite.prototype._validatePasswordPolicy = function () { - if (!this.config.passwordPolicy) { return Promise.resolve(); } + if (!this.config.passwordPolicy) { + return Promise.resolve(); + } return this._validatePasswordRequirements().then(() => { return this._validatePasswordHistory(); }); @@ -843,18 +846,20 @@ RestWrite.prototype._validatePasswordRequirements = function () { if (this.config.passwordPolicy.doNotAllowUsername === true) { if (this.data.username) { // username is not passed during password reset - if (this.data.password.indexOf(this.data.username) >= 0) - { return Promise.reject(new Parse.Error(Parse.Error.VALIDATION_ERROR, containsUsernameError)); } + if (this.data.password.indexOf(this.data.username) >= 0) { + return Promise.reject(new Parse.Error(Parse.Error.VALIDATION_ERROR, containsUsernameError)); + } } else { // retrieve the User object using objectId during password reset return this.config.database.find('_User', { objectId: this.objectId() }).then(results => { if (results.length != 1) { throw undefined; } - if (this.data.password.indexOf(results[0].username) >= 0) - { return Promise.reject( - new Parse.Error(Parse.Error.VALIDATION_ERROR, containsUsernameError) - ); } + if (this.data.password.indexOf(results[0].username) >= 0) { + return Promise.reject( + new Parse.Error(Parse.Error.VALIDATION_ERROR, containsUsernameError) + ); + } return Promise.resolve(); }); } @@ -878,19 +883,21 @@ RestWrite.prototype._validatePasswordHistory = function () { } const user = results[0]; let oldPasswords = []; - if (user._password_history) - { oldPasswords = _.take( - user._password_history, - this.config.passwordPolicy.maxPasswordHistory - 1 - ); } + if (user._password_history) { + oldPasswords = _.take( + user._password_history, + this.config.passwordPolicy.maxPasswordHistory - 1 + ); + } oldPasswords.push(user.password); const newPassword = this.data.password; // compare the new password hash with all old password hashes const promises = oldPasswords.map(function (hash) { return passwordCrypto.compare(newPassword, hash).then(result => { - if (result) - // reject if there is a match - { return Promise.reject('REPEAT_PASSWORD'); } + if (result) { + // reject if there is a match + return Promise.reject('REPEAT_PASSWORD'); + } return Promise.resolve(); }); }); @@ -900,14 +907,15 @@ RestWrite.prototype._validatePasswordHistory = function () { return Promise.resolve(); }) .catch(err => { - if (err === 'REPEAT_PASSWORD') - // a match was found - { return Promise.reject( - new Parse.Error( - Parse.Error.VALIDATION_ERROR, - `New password should not be the same as last ${this.config.passwordPolicy.maxPasswordHistory} passwords.` - ) - ); } + if (err === 'REPEAT_PASSWORD') { + // a match was found + return Promise.reject( + new Parse.Error( + Parse.Error.VALIDATION_ERROR, + `New password should not be the same as last ${this.config.passwordPolicy.maxPasswordHistory} passwords.` + ) + ); + } throw err; }); }); @@ -941,10 +949,16 @@ RestWrite.prototype.createSessionTokenIfNeeded = async function () { // Get verification conditions which can be booleans or functions; the purpose of this async/await // structure is to avoid unnecessarily executing subsequent functions if previous ones fail in the // conditional statement below, as a developer may decide to execute expensive operations in them - const verifyUserEmails = async () => this.config.verifyUserEmails === true || (typeof this.config.verifyUserEmails === 'function' && await Promise.resolve(this.config.verifyUserEmails(request)) === true); - const preventLoginWithUnverifiedEmail = async () => this.config.preventLoginWithUnverifiedEmail === true || (typeof this.config.preventLoginWithUnverifiedEmail === 'function' && await Promise.resolve(this.config.preventLoginWithUnverifiedEmail(request)) === true); + const verifyUserEmails = async () => + this.config.verifyUserEmails === true || + (typeof this.config.verifyUserEmails === 'function' && + (await Promise.resolve(this.config.verifyUserEmails(request))) === true); + const preventLoginWithUnverifiedEmail = async () => + this.config.preventLoginWithUnverifiedEmail === true || + (typeof this.config.preventLoginWithUnverifiedEmail === 'function' && + (await Promise.resolve(this.config.preventLoginWithUnverifiedEmail(request))) === true); // If verification is required - if (await verifyUserEmails() && await preventLoginWithUnverifiedEmail()) { + if ((await verifyUserEmails()) && (await preventLoginWithUnverifiedEmail())) { this.storage.rejectSignup = true; return; } diff --git a/src/Routers/GlobalConfigRouter.js b/src/Routers/GlobalConfigRouter.js index 5a28b3bae1..31d38c8569 100644 --- a/src/Routers/GlobalConfigRouter.js +++ b/src/Routers/GlobalConfigRouter.js @@ -55,29 +55,67 @@ export class GlobalConfigRouter extends PromiseRouter { return acc; }, {}); const className = triggers.getClassName(Parse.Config); - const hasBeforeSaveHook = triggers.triggerExists(className, triggers.Types.beforeSave, req.config.applicationId); - const hasAfterSaveHook = triggers.triggerExists(className, triggers.Types.afterSave, req.config.applicationId); + const hasBeforeSaveHook = triggers.triggerExists( + className, + triggers.Types.beforeSave, + req.config.applicationId + ); + const hasAfterSaveHook = triggers.triggerExists( + className, + triggers.Types.afterSave, + req.config.applicationId + ); let originalConfigObject; let updatedConfigObject; const configObject = new Parse.Config(); configObject.attributes = params; - const results = await req.config.database.find('_GlobalConfig', { objectId: '1' }, { limit: 1 }); + const results = await req.config.database.find( + '_GlobalConfig', + { objectId: '1' }, + { limit: 1 } + ); const isNew = results.length !== 1; if (!isNew && (hasBeforeSaveHook || hasAfterSaveHook)) { originalConfigObject = getConfigFromParams(results[0].params); } try { - await triggers.maybeRunGlobalConfigTrigger(triggers.Types.beforeSave, req.auth, configObject, originalConfigObject, req.config, req.context); + await triggers.maybeRunGlobalConfigTrigger( + triggers.Types.beforeSave, + req.auth, + configObject, + originalConfigObject, + req.config, + req.context + ); if (isNew) { - await req.config.database.update('_GlobalConfig', { objectId: '1' }, update, { upsert: true }, true) + await req.config.database.update( + '_GlobalConfig', + { objectId: '1' }, + update, + { upsert: true }, + true + ); updatedConfigObject = configObject; } else { - const result = await req.config.database.update('_GlobalConfig', { objectId: '1' }, update, {}, true); + const result = await req.config.database.update( + '_GlobalConfig', + { objectId: '1' }, + update, + {}, + true + ); updatedConfigObject = getConfigFromParams(result.params); } - await triggers.maybeRunGlobalConfigTrigger(triggers.Types.afterSave, req.auth, updatedConfigObject, originalConfigObject, req.config, req.context); - return { response: { result: true } } + await triggers.maybeRunGlobalConfigTrigger( + triggers.Types.afterSave, + req.auth, + updatedConfigObject, + originalConfigObject, + req.config, + req.context + ); + return { response: { result: true } }; } catch (err) { const error = triggers.resolveError(err, { code: Parse.Error.SCRIPT_FAILED, diff --git a/src/Routers/GraphQLRouter.js b/src/Routers/GraphQLRouter.js index d472ac9df5..71ca8b3ce8 100644 --- a/src/Routers/GraphQLRouter.js +++ b/src/Routers/GraphQLRouter.js @@ -19,7 +19,9 @@ export class GraphQLRouter extends PromiseRouter { "read-only masterKey isn't allowed to update the GraphQL config." ); } - const data = await req.config.parseGraphQLController.updateGraphQLConfig(req.body?.params || {}); + const data = await req.config.parseGraphQLController.updateGraphQLConfig( + req.body?.params || {} + ); return { response: data, }; diff --git a/src/Routers/IAPValidationRouter.js b/src/Routers/IAPValidationRouter.js index bae6f593e9..17eae685d7 100644 --- a/src/Routers/IAPValidationRouter.js +++ b/src/Routers/IAPValidationRouter.js @@ -11,11 +11,14 @@ const APP_STORE_ERRORS = { 21000: 'The App Store could not read the JSON object you provided.', 21002: 'The data in the receipt-data property was malformed or missing.', 21003: 'The receipt could not be authenticated.', - 21004: 'The shared secret you provided does not match the shared secret on file for your account.', + 21004: + 'The shared secret you provided does not match the shared secret on file for your account.', 21005: 'The receipt server is not currently available.', 21006: 'This receipt is valid but the subscription has expired.', - 21007: 'This receipt is from the test environment, but it was sent to the production environment for verification. Send it to the test environment instead.', - 21008: 'This receipt is from the production environment, but it was sent to the test environment for verification. Send it to the production environment instead.', + 21007: + 'This receipt is from the test environment, but it was sent to the production environment for verification. Send it to the test environment instead.', + 21008: + 'This receipt is from the production environment, but it was sent to the test environment for verification. Send it to the production environment instead.', }; function appStoreError(status) { diff --git a/src/Routers/PagesRouter.js b/src/Routers/PagesRouter.js index 1ea3211684..184b264c01 100644 --- a/src/Routers/PagesRouter.js +++ b/src/Routers/PagesRouter.js @@ -224,11 +224,11 @@ export class PagesRouter extends PromiseRouter { const query = result.success ? {} : { - [pageParams.token]: token, - [pageParams.appId]: config.applicationId, - [pageParams.error]: result.err, - [pageParams.appName]: config.appName, - }; + [pageParams.token]: token, + [pageParams.appId]: config.applicationId, + [pageParams.error]: result.err, + [pageParams.appName]: config.appName, + }; if (result?.err === 'The password reset link has expired') { delete query[pageParams.token]; @@ -300,9 +300,9 @@ export class PagesRouter extends PromiseRouter { return Utils.getLocalizedPath(defaultPath, locale).then(({ path, subdir }) => redirect ? this.redirectResponse( - this.composePageUrl(defaultFile, config.publicServerURL, subdir), - params - ) + this.composePageUrl(defaultFile, config.publicServerURL, subdir), + params + ) : this.pageResponse(path, params, placeholders) ); } else { @@ -532,10 +532,10 @@ export class PagesRouter extends PromiseRouter { getDefaultParams(config) { return config ? { - [pageParams.appId]: config.appId, - [pageParams.appName]: config.appName, - [pageParams.publicServerUrl]: config.publicServerURL, - } + [pageParams.appId]: config.appId, + [pageParams.appName]: config.appName, + [pageParams.publicServerUrl]: config.publicServerURL, + } : {}; } diff --git a/src/Routers/PublicAPIRouter.js b/src/Routers/PublicAPIRouter.js index 2ec993f390..98da1c4542 100644 --- a/src/Routers/PublicAPIRouter.js +++ b/src/Routers/PublicAPIRouter.js @@ -15,7 +15,7 @@ export class PublicAPIRouter extends PromiseRouter { super(); Deprecator.logRuntimeDeprecation({ usage: 'PublicAPIRouter', - solution: 'pages.enableRouter' + solution: 'pages.enableRouter', }); } verifyEmail(req) { diff --git a/src/Routers/UsersRouter.js b/src/Routers/UsersRouter.js index 7668562965..4afeb7bbd2 100644 --- a/src/Routers/UsersRouter.js +++ b/src/Routers/UsersRouter.js @@ -147,13 +147,23 @@ export class UsersRouter extends ClassesRouter { // If request doesn't use master or maintenance key with ignoring email verification if (!((req.auth.isMaster || req.auth.isMaintenance) && ignoreEmailVerification)) { - // Get verification conditions which can be booleans or functions; the purpose of this async/await // structure is to avoid unnecessarily executing subsequent functions if previous ones fail in the // conditional statement below, as a developer may decide to execute expensive operations in them - const verifyUserEmails = async () => req.config.verifyUserEmails === true || (typeof req.config.verifyUserEmails === 'function' && await Promise.resolve(req.config.verifyUserEmails(request)) === true); - const preventLoginWithUnverifiedEmail = async () => req.config.preventLoginWithUnverifiedEmail === true || (typeof req.config.preventLoginWithUnverifiedEmail === 'function' && await Promise.resolve(req.config.preventLoginWithUnverifiedEmail(request)) === true); - if (await verifyUserEmails() && await preventLoginWithUnverifiedEmail() && !user.emailVerified) { + const verifyUserEmails = async () => + req.config.verifyUserEmails === true || + (typeof req.config.verifyUserEmails === 'function' && + (await Promise.resolve(req.config.verifyUserEmails(request))) === true); + const preventLoginWithUnverifiedEmail = async () => + req.config.preventLoginWithUnverifiedEmail === true || + (typeof req.config.preventLoginWithUnverifiedEmail === 'function' && + (await Promise.resolve(req.config.preventLoginWithUnverifiedEmail(request))) === + true); + if ( + (await verifyUserEmails()) && + (await preventLoginWithUnverifiedEmail()) && + !user.emailVerified + ) { throw new Parse.Error(Parse.Error.EMAIL_NOT_FOUND, 'User email is not verified.'); } } @@ -252,12 +262,13 @@ export class UsersRouter extends ClassesRouter { const expiresAt = new Date( changedAt.getTime() + 86400000 * req.config.passwordPolicy.maxPasswordAge ); - if (expiresAt < new Date()) - // fail of current time is past password expiry time - { throw new Parse.Error( - Parse.Error.OBJECT_NOT_FOUND, - 'Your password has expired. Please reset your password.' - ); } + if (expiresAt < new Date()) { + // fail of current time is past password expiry time + throw new Parse.Error( + Parse.Error.OBJECT_NOT_FOUND, + 'Your password has expired. Please reset your password.' + ); + } } } @@ -492,7 +503,12 @@ export class UsersRouter extends ClassesRouter { ); } - const results = await req.config.database.find('_User', { email: email }, {}, Auth.maintenance(req.config)); + const results = await req.config.database.find( + '_User', + { email: email }, + {}, + Auth.maintenance(req.config) + ); if (!results.length || results.length < 1) { throw new Parse.Error(Parse.Error.EMAIL_NOT_FOUND, `No user found with email ${email}`); } @@ -506,7 +522,12 @@ export class UsersRouter extends ClassesRouter { } const userController = req.config.userController; - const send = await userController.regenerateEmailVerifyToken(user, req.auth.isMaster, req.auth.installationId, req.ip); + const send = await userController.regenerateEmailVerifyToken( + user, + req.auth.isMaster, + req.auth.installationId, + req.ip + ); if (send) { userController.sendVerificationEmail(user, req); } diff --git a/src/SchemaMigrations/DefinedSchemas.js b/src/SchemaMigrations/DefinedSchemas.js index cf2b1761f4..14c975ee45 100644 --- a/src/SchemaMigrations/DefinedSchemas.js +++ b/src/SchemaMigrations/DefinedSchemas.js @@ -80,7 +80,9 @@ export class DefinedSchemas { logger.info('Running Migrations Completed'); } catch (e) { logger.error(`Failed to run migrations: ${e}`); - if (process.env.NODE_ENV === 'production') { process.exit(1); } + if (process.env.NODE_ENV === 'production') { + process.exit(1); + } } } @@ -108,7 +110,9 @@ export class DefinedSchemas { this.checkForMissingSchemas(); await this.enforceCLPForNonProvidedClass(); } catch (e) { - if (timeout) { clearTimeout(timeout); } + if (timeout) { + clearTimeout(timeout); + } if (this.retries < this.maxRetries) { this.retries++; // first retry 1sec, 2sec, 3sec total 6sec retry sequence @@ -118,7 +122,9 @@ export class DefinedSchemas { await this.executeMigrations(); } else { logger.error(`Failed to run migrations: ${e}`); - if (process.env.NODE_ENV === 'production') { process.exit(1); } + if (process.env.NODE_ENV === 'production') { + process.exit(1); + } } } } @@ -387,7 +393,7 @@ export class DefinedSchemas { logger.warn(`classLevelPermissions not provided for ${localSchema.className}.`); } // Use spread to avoid read only issue (encountered by Moumouls using directAccess) - const clp = ({ ...localSchema.classLevelPermissions || {} }: Parse.CLP.PermissionsMap); + const clp = ({ ...(localSchema.classLevelPermissions || {}) }: Parse.CLP.PermissionsMap); // To avoid inconsistency we need to remove all rights on addField clp.addField = {}; newLocalSchema.setCLP(clp); @@ -428,7 +434,9 @@ export class DefinedSchemas { const keysB: string[] = Object.keys(objB); // Check key name - if (keysA.length !== keysB.length) { return false; } + if (keysA.length !== keysB.length) { + return false; + } return keysA.every(k => objA[k] === objB[k]); } diff --git a/src/StatusHandler.js b/src/StatusHandler.js index fecfb268ec..59c77e4b95 100644 --- a/src/StatusHandler.js +++ b/src/StatusHandler.js @@ -248,7 +248,8 @@ export function pushStatusHandler(config, existingObjectId) { if ( error?.code === 'messaging/registration-token-not-registered' || error?.code === 'messaging/invalid-registration-token' || - (error?.code === 'messaging/invalid-argument' && error?.message === 'The registration token is not a valid FCM registration token') + (error?.code === 'messaging/invalid-argument' && + error?.message === 'The registration token is not a valid FCM registration token') ) { devicesToRemove.push(token); } diff --git a/src/TestUtils.js b/src/TestUtils.js index 912a459519..94eedd9034 100644 --- a/src/TestUtils.js +++ b/src/TestUtils.js @@ -40,5 +40,5 @@ export function resolvingPromise() { } export function sleep(ms) { - return new Promise((resolve) => setTimeout(resolve, ms)); + return new Promise(resolve => setTimeout(resolve, ms)); } diff --git a/src/Utils.js b/src/Utils.js index 72b49aeeb2..b676cbc248 100644 --- a/src/Utils.js +++ b/src/Utils.js @@ -389,7 +389,7 @@ class Utils { * addNestedKeysToRoot(obj, 'b'); * console.log(obj); * // Output: { a: 1, e: 4, c: 2, d: 3 } - */ + */ static addNestedKeysToRoot(obj, key) { if (obj[key] && typeof obj[key] === 'object') { // Add nested keys to root @@ -406,8 +406,9 @@ class Utils { * @returns {String} The encoded string. */ static encodeForUrl(input) { - return encodeURIComponent(input).replace(/[!'.()*]/g, char => - '%' + char.charCodeAt(0).toString(16).toUpperCase() + return encodeURIComponent(input).replace( + /[!'.()*]/g, + char => '%' + char.charCodeAt(0).toString(16).toUpperCase() ); } } diff --git a/src/cli/parse-server.js b/src/cli/parse-server.js index 7c7639b497..b0c574bfea 100755 --- a/src/cli/parse-server.js +++ b/src/cli/parse-server.js @@ -32,7 +32,6 @@ runner({ help, usage: '[options] ', start: function (program, options, logOptions) { - if (!options.appId || !options.masterKey) { program.outputHelp(); console.error(''); diff --git a/src/cloud-code/Parse.Cloud.js b/src/cloud-code/Parse.Cloud.js index 3f33e5100d..c2389b5e85 100644 --- a/src/cloud-code/Parse.Cloud.js +++ b/src/cloud-code/Parse.Cloud.js @@ -79,7 +79,7 @@ const getRoute = parseClass => { _User: 'users', _Session: 'sessions', '@File': 'files', - '@Config' : 'config', + '@Config': 'config', }[parseClass] || 'classes'; if (parseClass === '@File') { return `/${route}/:id?(.*)`; diff --git a/src/middlewares.js b/src/middlewares.js index bf8029844a..72b634219e 100644 --- a/src/middlewares.js +++ b/src/middlewares.js @@ -24,7 +24,9 @@ const getMountForRequest = function (req) { }; const getBlockList = (ipRangeList, store) => { - if (store.get('blockList')) { return store.get('blockList'); } + if (store.get('blockList')) { + return store.get('blockList'); + } const blockList = new BlockList(); ipRangeList.forEach(fullIp => { if (fullIp === '::/0' || fullIp === '::') { @@ -50,9 +52,15 @@ export const checkIp = (ip, ipRangeList, store) => { const incomingIpIsV4 = isIPv4(ip); const blockList = getBlockList(ipRangeList, store); - if (store.get(ip)) { return true; } - if (store.get('allowAllIpv4') && incomingIpIsV4) { return true; } - if (store.get('allowAllIpv6') && !incomingIpIsV4) { return true; } + if (store.get(ip)) { + return true; + } + if (store.get('allowAllIpv4') && incomingIpIsV4) { + return true; + } + if (store.get('allowAllIpv6') && !incomingIpIsV4) { + return true; + } const result = blockList.check(ip, incomingIpIsV4 ? 'ipv4' : 'ipv6'); // If the ip is in the list, we store the result in the store @@ -387,7 +395,9 @@ function getClientIp(req) { } function httpAuth(req) { - if (!(req.req || req).headers.authorization) { return; } + if (!(req.req || req).headers.authorization) { + return; + } var header = (req.req || req).headers.authorization; var appId, masterKey, javascriptKey; @@ -432,7 +442,9 @@ export function allowCrossDomain(appId) { } const baseOrigins = - typeof config?.allowOrigin === 'string' ? [config.allowOrigin] : config?.allowOrigin ?? ['*']; + typeof config?.allowOrigin === 'string' + ? [config.allowOrigin] + : (config?.allowOrigin ?? ['*']); const requestOrigin = req.headers.origin; const allowOrigins = requestOrigin && baseOrigins.includes(requestOrigin) ? requestOrigin : baseOrigins[0]; @@ -538,7 +550,9 @@ export const addRateLimit = (route, config, cloud) => { const client = createClient({ url: route.redisUrl, }); - client.on('error', err => { log.error('Middlewares addRateLimit Redis client error', { error: err }) }); + client.on('error', err => { + log.error('Middlewares addRateLimit Redis client error', { error: err }); + }); client.on('connect', () => {}); client.on('reconnecting', () => {}); client.on('ready', () => {}); diff --git a/src/vendor/mongodbUrl.js b/src/vendor/mongodbUrl.js index eaa25add02..9fdc764629 100644 --- a/src/vendor/mongodbUrl.js +++ b/src/vendor/mongodbUrl.js @@ -66,7 +66,9 @@ const querystring = require('querystring'); /* istanbul ignore next: improve coverage */ function urlParse(url, parseQueryString, slashesDenoteHost) { - if (url instanceof Url) { return url; } + if (url instanceof Url) { + return url; + } var u = new Url(); u.parse(url, parseQueryString, slashesDenoteHost); @@ -101,7 +103,9 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { code === 160 /*\u00A0*/ || code === 65279; /*\uFEFF*/ if (start === -1) { - if (isWs) { continue; } + if (isWs) { + continue; + } lastPos = start = i; } else { if (inWs) { @@ -125,7 +129,9 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { split = true; break; case 92: // '\\' - if (i - lastPos > 0) { rest += url.slice(lastPos, i); } + if (i - lastPos > 0) { + rest += url.slice(lastPos, i); + } rest += '/'; lastPos = i + 1; break; @@ -141,8 +147,11 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { // We didn't convert any backslashes if (end === -1) { - if (start === 0) { rest = url; } - else { rest = url.slice(start); } + if (start === 0) { + rest = url; + } else { + rest = url.slice(start); + } } else { rest = url.slice(start, end); } @@ -235,13 +244,17 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { case 124: // '|' case 125: // '}' // Characters that are never ever allowed in a hostname from RFC 2396 - if (nonHost === -1) { nonHost = i; } + if (nonHost === -1) { + nonHost = i; + } break; case 35: // '#' case 47: // '/' case 63: // '?' // Find the first instance of any host-ending characters - if (nonHost === -1) { nonHost = i; } + if (nonHost === -1) { + nonHost = i; + } hostEnd = i; break; case 64: // '@' @@ -251,7 +264,9 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { nonHost = -1; break; } - if (hostEnd !== -1) { break; } + if (hostEnd !== -1) { + break; + } } start = 0; if (atSign !== -1) { @@ -271,7 +286,9 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { // we've indicated that there is a hostname, // so even if it's empty, it has to be present. - if (typeof this.hostname !== 'string') { this.hostname = ''; } + if (typeof this.hostname !== 'string') { + this.hostname = ''; + } var hostname = this.hostname; @@ -283,7 +300,9 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { // validate a little. if (!ipv6Hostname) { const result = validateHostname(this, rest, hostname); - if (result !== undefined) { rest = result; } + if (result !== undefined) { + rest = result; + } } // hostnames are always lower case. @@ -318,7 +337,9 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { // escaped, even if encodeURIComponent doesn't think they // need to be. const result = autoEscapeStr(rest); - if (result !== undefined) { rest = result; } + if (result !== undefined) { + rest = result; + } } var questionIdx = -1; @@ -354,7 +375,9 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { var firstIdx = questionIdx !== -1 && (hashIdx === -1 || questionIdx < hashIdx) ? questionIdx : hashIdx; if (firstIdx === -1) { - if (rest.length > 0) { this.pathname = rest; } + if (rest.length > 0) { + this.pathname = rest; + } } else if (firstIdx > 0) { this.pathname = rest.slice(0, firstIdx); } @@ -378,7 +401,9 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { function validateHostname(self, rest, hostname) { for (var i = 0, lastPos; i <= hostname.length; ++i) { var code; - if (i < hostname.length) { code = hostname.charCodeAt(i); } + if (i < hostname.length) { + code = hostname.charCodeAt(i); + } if (code === 46 /*.*/ || i === hostname.length) { if (i - lastPos > 0) { if (i - lastPos > 63) { @@ -405,7 +430,9 @@ function validateHostname(self, rest, hostname) { } // Invalid host character self.hostname = hostname.slice(0, i); - if (i < hostname.length) { return '/' + hostname.slice(i) + rest; } + if (i < hostname.length) { + return '/' + hostname.slice(i) + rest; + } break; } } @@ -419,80 +446,113 @@ function autoEscapeStr(rest) { // Also escape single quotes in case of an XSS attack switch (rest.charCodeAt(i)) { case 9: // '\t' - if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } + if (i - lastPos > 0) { + newRest += rest.slice(lastPos, i); + } newRest += '%09'; lastPos = i + 1; break; case 10: // '\n' - if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } + if (i - lastPos > 0) { + newRest += rest.slice(lastPos, i); + } newRest += '%0A'; lastPos = i + 1; break; case 13: // '\r' - if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } + if (i - lastPos > 0) { + newRest += rest.slice(lastPos, i); + } newRest += '%0D'; lastPos = i + 1; break; case 32: // ' ' - if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } + if (i - lastPos > 0) { + newRest += rest.slice(lastPos, i); + } newRest += '%20'; lastPos = i + 1; break; case 34: // '"' - if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } + if (i - lastPos > 0) { + newRest += rest.slice(lastPos, i); + } newRest += '%22'; lastPos = i + 1; break; case 39: // '\'' - if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } + if (i - lastPos > 0) { + newRest += rest.slice(lastPos, i); + } newRest += '%27'; lastPos = i + 1; break; case 60: // '<' - if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } + if (i - lastPos > 0) { + newRest += rest.slice(lastPos, i); + } newRest += '%3C'; lastPos = i + 1; break; case 62: // '>' - if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } + if (i - lastPos > 0) { + newRest += rest.slice(lastPos, i); + } newRest += '%3E'; lastPos = i + 1; break; case 92: // '\\' - if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } + if (i - lastPos > 0) { + newRest += rest.slice(lastPos, i); + } newRest += '%5C'; lastPos = i + 1; break; case 94: // '^' - if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } + if (i - lastPos > 0) { + newRest += rest.slice(lastPos, i); + } newRest += '%5E'; lastPos = i + 1; break; case 96: // '`' - if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } + if (i - lastPos > 0) { + newRest += rest.slice(lastPos, i); + } newRest += '%60'; lastPos = i + 1; break; case 123: // '{' - if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } + if (i - lastPos > 0) { + newRest += rest.slice(lastPos, i); + } newRest += '%7B'; lastPos = i + 1; break; case 124: // '|' - if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } + if (i - lastPos > 0) { + newRest += rest.slice(lastPos, i); + } newRest += '%7C'; lastPos = i + 1; break; case 125: // '}' - if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } + if (i - lastPos > 0) { + newRest += rest.slice(lastPos, i); + } newRest += '%7D'; lastPos = i + 1; break; } } - if (lastPos === 0) { return; } - if (lastPos < rest.length) { return newRest + rest.slice(lastPos); } - else { return newRest; } + if (lastPos === 0) { + return; + } + if (lastPos < rest.length) { + return newRest + rest.slice(lastPos); + } else { + return newRest; + } } // format a parsed object into a url string @@ -502,12 +562,15 @@ function urlFormat(obj) { // If it's an obj, this is a no-op. // this way, you can call url_format() on strings // to clean up potentially wonky urls. - if (typeof obj === 'string') { obj = urlParse(obj); } - else if (typeof obj !== 'object' || obj === null) - { throw new TypeError( - 'Parameter "urlObj" must be an object, not ' + (obj === null ? 'null' : typeof obj) - ); } - else if (!(obj instanceof Url)) { return Url.prototype.format.call(obj); } + if (typeof obj === 'string') { + obj = urlParse(obj); + } else if (typeof obj !== 'object' || obj === null) { + throw new TypeError( + 'Parameter "urlObj" must be an object, not ' + (obj === null ? 'null' : typeof obj) + ); + } else if (!(obj instanceof Url)) { + return Url.prototype.format.call(obj); + } return obj.format(); } @@ -535,47 +598,63 @@ Url.prototype.format = function () { } } - if (this.query !== null && typeof this.query === 'object') - { query = querystring.stringify(this.query); } + if (this.query !== null && typeof this.query === 'object') { + query = querystring.stringify(this.query); + } var search = this.search || (query && '?' + query) || ''; - if (protocol && protocol.charCodeAt(protocol.length - 1) !== 58 /*:*/) { protocol += ':'; } + if (protocol && protocol.charCodeAt(protocol.length - 1) !== 58 /*:*/) { + protocol += ':'; + } var newPathname = ''; var lastPos = 0; for (var i = 0; i < pathname.length; ++i) { switch (pathname.charCodeAt(i)) { case 35: // '#' - if (i - lastPos > 0) { newPathname += pathname.slice(lastPos, i); } + if (i - lastPos > 0) { + newPathname += pathname.slice(lastPos, i); + } newPathname += '%23'; lastPos = i + 1; break; case 63: // '?' - if (i - lastPos > 0) { newPathname += pathname.slice(lastPos, i); } + if (i - lastPos > 0) { + newPathname += pathname.slice(lastPos, i); + } newPathname += '%3F'; lastPos = i + 1; break; } } if (lastPos > 0) { - if (lastPos !== pathname.length) { pathname = newPathname + pathname.slice(lastPos); } - else { pathname = newPathname; } + if (lastPos !== pathname.length) { + pathname = newPathname + pathname.slice(lastPos); + } else { + pathname = newPathname; + } } // only the slashedProtocols get the //. Not mailto:, xmpp:, etc. // unless they had them to begin with. if (this.slashes || ((!protocol || slashedProtocol[protocol]) && host !== false)) { host = '//' + (host || ''); - if (pathname && pathname.charCodeAt(0) !== 47 /*/*/) { pathname = '/' + pathname; } + if (pathname && pathname.charCodeAt(0) !== 47 /*/*/) { + pathname = '/' + pathname; + } } else if (!host) { host = ''; } search = search.replace('#', '%23'); - if (hash && hash.charCodeAt(0) !== 35 /*#*/) { hash = '#' + hash; } - if (search && search.charCodeAt(0) !== 63 /*?*/) { search = '?' + search; } + if (hash && hash.charCodeAt(0) !== 35 /*#*/) { + hash = '#' + hash; + } + if (search && search.charCodeAt(0) !== 63 /*?*/) { + search = '?' + search; + } return protocol + host + pathname + search + hash; }; @@ -592,7 +671,9 @@ Url.prototype.resolve = function (relative) { /* istanbul ignore next: improve coverage */ function urlResolveObject(source, relative) { - if (!source) { return relative; } + if (!source) { + return relative; + } return urlParse(source, false, true).resolveObject(relative); } @@ -627,7 +708,9 @@ Url.prototype.resolveObject = function (relative) { var rkeys = Object.keys(relative); for (var rk = 0; rk < rkeys.length; rk++) { var rkey = rkeys[rk]; - if (rkey !== 'protocol') { result[rkey] = relative[rkey]; } + if (rkey !== 'protocol') { + result[rkey] = relative[rkey]; + } } //urlParse appends trailing / to urls like http://www.example.com @@ -672,10 +755,18 @@ Url.prototype.resolveObject = function (relative) { break; } } - if (!relative.host) { relative.host = ''; } - if (!relative.hostname) { relative.hostname = ''; } - if (relPath[0] !== '') { relPath.unshift(''); } - if (relPath.length < 2) { relPath.unshift(''); } + if (!relative.host) { + relative.host = ''; + } + if (!relative.hostname) { + relative.hostname = ''; + } + if (relPath[0] !== '') { + relPath.unshift(''); + } + if (relPath.length < 2) { + relPath.unshift(''); + } result.pathname = relPath.join('/'); } else { result.pathname = relative.pathname; @@ -714,16 +805,22 @@ Url.prototype.resolveObject = function (relative) { result.hostname = ''; result.port = null; if (result.host) { - if (srcPath[0] === '') { srcPath[0] = result.host; } - else { srcPath.unshift(result.host); } + if (srcPath[0] === '') { + srcPath[0] = result.host; + } else { + srcPath.unshift(result.host); + } } result.host = ''; if (relative.protocol) { relative.hostname = null; relative.port = null; if (relative.host) { - if (relPath[0] === '') { relPath[0] = relative.host; } - else { relPath.unshift(relative.host); } + if (relPath[0] === '') { + relPath[0] = relative.host; + } else { + relPath.unshift(relative.host); + } } relative.host = null; } @@ -742,7 +839,9 @@ Url.prototype.resolveObject = function (relative) { } else if (relPath.length) { // it's relative // throw away the existing file, and take the new path instead. - if (!srcPath) { srcPath = []; } + if (!srcPath) { + srcPath = []; + } srcPath.pop(); srcPath = srcPath.concat(relPath); result.search = relative.search; @@ -879,19 +978,24 @@ Url.prototype.parseHost = function () { } host = host.slice(0, host.length - port.length); } - if (host) { this.hostname = host; } + if (host) { + this.hostname = host; + } }; // About 1.5x faster than the two-arg version of Array#splice(). /* istanbul ignore next: improve coverage */ function spliceOne(list, index) { - for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1) { list[i] = list[k]; } + for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1) { + list[i] = list[k]; + } list.pop(); } var hexTable = new Array(256); -for (var i = 0; i < 256; ++i) -{ hexTable[i] = '%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase(); } +for (var i = 0; i < 256; ++i) { + hexTable[i] = '%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase(); +} /* istanbul ignore next: improve coverage */ function encodeAuth(str) { // faster encodeURIComponent alternative for encoding auth uri components @@ -920,7 +1024,9 @@ function encodeAuth(str) { continue; } - if (i - lastPos > 0) { out += str.slice(lastPos, i); } + if (i - lastPos > 0) { + out += str.slice(lastPos, i); + } lastPos = i + 1; @@ -945,8 +1051,11 @@ function encodeAuth(str) { // Surrogate pair ++i; var c2; - if (i < str.length) { c2 = str.charCodeAt(i) & 0x3ff; } - else { c2 = 0; } + if (i < str.length) { + c2 = str.charCodeAt(i) & 0x3ff; + } else { + c2 = 0; + } c = 0x10000 + (((c & 0x3ff) << 10) | c2); out += hexTable[0xf0 | (c >> 18)] + @@ -954,7 +1063,11 @@ function encodeAuth(str) { hexTable[0x80 | ((c >> 6) & 0x3f)] + hexTable[0x80 | (c & 0x3f)]; } - if (lastPos === 0) { return str; } - if (lastPos < str.length) { return out + str.slice(lastPos); } + if (lastPos === 0) { + return str; + } + if (lastPos < str.length) { + return out + str.slice(lastPos); + } return out; } From 38a6f3736e1f6df7cd5ff16621ec6ad61db0a6ec Mon Sep 17 00:00:00 2001 From: Daniel Date: Sun, 23 Mar 2025 22:05:37 +1100 Subject: [PATCH 3/7] fix lint --- src/RestQuery.js | 18 +++++++++--------- src/Routers/PagesRouter.js | 24 ++++++++++++------------ 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/RestQuery.js b/src/RestQuery.js index ec47eb2e3d..323a83f2bf 100644 --- a/src/RestQuery.js +++ b/src/RestQuery.js @@ -53,15 +53,15 @@ async function RestQuery({ enforceRoleSecurity(method, className, auth); const result = runBeforeFind ? await triggers.maybeRunQueryTrigger( - triggers.Types.beforeFind, - className, - restWhere, - restOptions, - config, - auth, - context, - method === RestQuery.Method.get - ) + triggers.Types.beforeFind, + className, + restWhere, + restOptions, + config, + auth, + context, + method === RestQuery.Method.get + ) : Promise.resolve({ restWhere, restOptions }); return new _UnsafeRestQuery( diff --git a/src/Routers/PagesRouter.js b/src/Routers/PagesRouter.js index 184b264c01..1ea3211684 100644 --- a/src/Routers/PagesRouter.js +++ b/src/Routers/PagesRouter.js @@ -224,11 +224,11 @@ export class PagesRouter extends PromiseRouter { const query = result.success ? {} : { - [pageParams.token]: token, - [pageParams.appId]: config.applicationId, - [pageParams.error]: result.err, - [pageParams.appName]: config.appName, - }; + [pageParams.token]: token, + [pageParams.appId]: config.applicationId, + [pageParams.error]: result.err, + [pageParams.appName]: config.appName, + }; if (result?.err === 'The password reset link has expired') { delete query[pageParams.token]; @@ -300,9 +300,9 @@ export class PagesRouter extends PromiseRouter { return Utils.getLocalizedPath(defaultPath, locale).then(({ path, subdir }) => redirect ? this.redirectResponse( - this.composePageUrl(defaultFile, config.publicServerURL, subdir), - params - ) + this.composePageUrl(defaultFile, config.publicServerURL, subdir), + params + ) : this.pageResponse(path, params, placeholders) ); } else { @@ -532,10 +532,10 @@ export class PagesRouter extends PromiseRouter { getDefaultParams(config) { return config ? { - [pageParams.appId]: config.appId, - [pageParams.appName]: config.appName, - [pageParams.publicServerUrl]: config.publicServerURL, - } + [pageParams.appId]: config.appId, + [pageParams.appName]: config.appName, + [pageParams.publicServerUrl]: config.publicServerURL, + } : {}; } From e5d539ec66ac007356cdc25213c1af067ac967f7 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sun, 18 May 2025 20:43:54 +1000 Subject: [PATCH 4/7] merge conflicts --- .prettierrc | 21 +- package-lock.json | 22 +- package.json | 2 +- spec/AccountLockoutPolicy.spec.js | 309 +- spec/AdaptableController.spec.js | 26 +- spec/AdapterLoader.spec.js | 106 +- spec/AggregateRouter.spec.js | 72 +- spec/Analytics.spec.js | 32 +- spec/AudienceRouter.spec.js | 350 +- spec/Auth.spec.js | 110 +- spec/AuthenticationAdapters.spec.js | 1425 +-- spec/AuthenticationAdaptersV2.spec.js | 624 +- spec/CLI.spec.js | 341 +- spec/CacheController.spec.js | 39 +- spec/Client.spec.js | 235 +- spec/ClientSDK.spec.js | 46 +- spec/CloudCode.Validator.spec.js | 1111 ++- spec/CloudCode.spec.js | 2938 +++--- spec/CloudCodeLogger.spec.js | 506 +- spec/DatabaseController.spec.js | 443 +- spec/DefinedSchemas.spec.js | 576 +- spec/Deprecator.spec.js | 44 +- spec/EmailVerificationToken.spec.js | 1797 ++-- spec/EnableExpressErrorHandler.spec.js | 24 +- spec/EventEmitterPubSub.spec.js | 31 +- spec/FilesController.spec.js | 228 +- spec/GridFSBucketStorageAdapter.spec.js | 325 +- spec/HTTPRequest.spec.js | 144 +- spec/Idempotency.spec.js | 214 +- spec/InMemoryCache.spec.js | 18 +- spec/InMemoryCacheAdapter.spec.js | 22 +- spec/InstallationsRouter.spec.js | 244 +- spec/JobSchedule.spec.js | 174 +- spec/LdapAuth.spec.js | 238 +- spec/Logger.spec.js | 40 +- spec/LoggerController.spec.js | 97 +- spec/LogsRouter.spec.js | 79 +- spec/Middlewares.spec.js | 380 +- spec/MongoSchemaCollectionAdapter.spec.js | 112 +- spec/MongoStorageAdapter.spec.js | 465 +- spec/MongoTransform.spec.js | 416 +- spec/NullCacheAdapter.spec.js | 20 +- spec/OAuth1.spec.js | 119 +- spec/PagesRouter.spec.js | 738 +- spec/Parse.Push.spec.js | 195 +- spec/ParseACL.spec.js | 350 +- spec/ParseAPI.spec.js | 1249 +-- spec/ParseCloudCodePublisher.spec.js | 56 +- spec/ParseConfigKey.spec.js | 163 +- spec/ParseFile.spec.js | 1238 +-- spec/ParseGeoPoint.spec.js | 643 +- spec/ParseGlobalConfig.spec.js | 209 +- spec/ParseGraphQLClassNameTransformer.spec.js | 17 +- spec/ParseGraphQLController.spec.js | 308 +- spec/ParseGraphQLSchema.spec.js | 313 +- spec/ParseGraphQLServer.spec.js | 8512 +++++++++-------- spec/ParseHooks.spec.js | 591 +- spec/ParseInstallation.spec.js | 967 +- spec/ParseLiveQuery.spec.js | 970 +- spec/ParseLiveQueryRedis.spec.js | 45 +- spec/ParseLiveQueryServer.spec.js | 1188 ++- spec/ParseObject.spec.js | 1409 +-- spec/ParsePolygon.spec.js | 273 +- spec/ParsePubSub.spec.js | 87 +- spec/ParseQuery.Aggregate.spec.js | 1828 ++-- spec/ParseQuery.Comment.spec.js | 86 +- spec/ParseQuery.FullTextSearch.spec.js | 635 +- spec/ParseQuery.hint.spec.js | 570 +- spec/ParseQuery.spec.js | 3876 ++++---- spec/ParseRelation.spec.js | 466 +- spec/ParseRole.spec.js | 308 +- spec/ParseServer.spec.js | 44 +- spec/ParseServerRESTController.spec.js | 547 +- spec/ParseSession.spec.js | 78 +- spec/ParseUser.spec.js | 2756 +++--- spec/ParseWebSocket.spec.js | 23 +- spec/ParseWebSocketServer.spec.js | 90 +- spec/PasswordPolicy.spec.js | 898 +- spec/PointerPermissions.spec.js | 1538 +-- spec/PostgresConfigParser.spec.js | 42 +- spec/PostgresInitOptions.spec.js | 34 +- spec/PostgresStorageAdapter.spec.js | 432 +- spec/PromiseRouter.spec.js | 18 +- spec/ProtectedFields.spec.js | 1308 +-- spec/PublicAPI.spec.js | 180 +- spec/PurchaseValidation.spec.js | 156 +- spec/PushController.spec.js | 1431 +-- spec/PushQueue.spec.js | 35 +- spec/PushRouter.spec.js | 38 +- spec/PushWorker.spec.js | 245 +- spec/QueryTools.spec.js | 646 +- spec/RateLimit.spec.js | 450 +- spec/ReadPreferenceOption.spec.js | 920 +- spec/RedisCacheAdapter.spec.js | 47 +- spec/RedisPubSub.spec.js | 30 +- spec/RegexVulnerabilities.spec.js | 164 +- spec/RestQuery.spec.js | 441 +- spec/RevocableSessionsUpgrade.spec.js | 75 +- spec/Schema.spec.js | 1238 +-- spec/SchemaPerformance.spec.js | 201 +- spec/SecurityCheck.spec.js | 213 +- spec/SecurityCheckGroups.spec.js | 43 +- spec/SessionTokenCache.spec.js | 29 +- spec/Subscription.spec.js | 102 +- spec/Uniqueness.spec.js | 104 +- spec/UserController.spec.js | 160 +- spec/UserPII.spec.js | 679 +- spec/Utils.spec.js | 27 +- spec/ValidationAndPasswordsReset.spec.js | 823 +- spec/VerifyUserPassword.spec.js | 486 +- spec/WinstonLoggerAdapter.spec.js | 194 +- spec/batch.spec.js | 473 +- spec/cloud/cloudCodeAbsoluteFile.js | 4 +- spec/cloud/cloudCodeModuleFile.js | 4 +- spec/cloud/cloudCodeRelativeFile.js | 4 +- spec/cryptoUtils.spec.js | 58 +- spec/defaultGraphQLTypes.spec.js | 591 +- spec/eslint.config.js | 4 +- spec/features.spec.js | 30 +- spec/graphQLObjectsQueries.js | 106 +- spec/helper.js | 253 +- spec/index.spec.js | 493 +- spec/parsers.spec.js | 60 +- spec/rest.spec.js | 815 +- spec/schemas.spec.js | 2711 +++--- spec/support/CurrentSpecReporter.js | 54 +- spec/support/CustomAuth.js | 2 +- spec/support/CustomMiddleware.js | 2 +- spec/support/FailingServer.js | 16 +- spec/support/MockEmailAdapterWithOptions.js | 2 +- spec/support/MockLdapServer.js | 26 +- spec/support/dev.js | 23 +- spec/support/myoauth.js | 2 +- spec/vulnerabilities.spec.js | 348 +- src/AccountLockout.js | 59 +- src/Auth.js | 210 +- src/ClientSDK.js | 6 +- src/Controllers/AdaptableController.js | 35 +- src/Controllers/AnalyticsController.js | 10 +- src/Controllers/CacheController.js | 12 +- src/Controllers/DatabaseController.js | 1291 ++- src/Controllers/FilesController.js | 58 +- src/Controllers/HooksController.js | 114 +- src/Controllers/LoggerController.js | 82 +- src/Controllers/SchemaController.js | 803 +- src/Controllers/UserController.js | 149 +- src/Deprecator/Deprecations.js | 4 +- src/Deprecator/Deprecator.js | 24 +- src/GraphQL/parseGraphQLUtils.js | 27 +- src/LiveQuery/RequestSchema.js | 104 +- src/LiveQuery/equalObjects.js | 6 +- src/Options/Definitions.js | 880 +- src/Options/index.js | 18 +- src/Options/parsers.js | 20 +- src/ParseServerRESTController.js | 64 +- src/PromiseRouter.js | 45 +- src/Push/PushWorker.js | 69 +- src/Push/utils.js | 31 +- src/RestQuery.js | 299 +- src/RestWrite.js | 645 +- src/Routers/AggregateRouter.js | 47 +- src/Routers/AnalyticsRouter.js | 6 +- src/Routers/AudiencesRouter.js | 54 +- src/Routers/ClassesRouter.js | 145 +- src/Routers/CloudCodeRouter.js | 70 +- src/Routers/FeaturesRouter.js | 117 +- src/Routers/FilesRouter.js | 166 +- src/Routers/FunctionsRouter.js | 69 +- src/Routers/GlobalConfigRouter.js | 37 +- src/Routers/GraphQLRouter.js | 30 +- src/Routers/HooksRouter.js | 86 +- src/Routers/IAPValidationRouter.js | 56 +- src/Routers/InstallationsRouter.js | 39 +- src/Routers/LogsRouter.js | 15 +- src/Routers/PagesRouter.js | 184 +- src/Routers/PublicAPIRouter.js | 94 +- src/Routers/PurgeRouter.js | 21 +- src/Routers/PushRouter.js | 26 +- src/Routers/RolesRouter.js | 14 +- src/Routers/SchemasRouter.js | 63 +- src/Routers/SecurityRouter.js | 13 +- src/Routers/SessionsRouter.js | 46 +- src/Routers/UsersRouter.js | 318 +- src/SchemaMigrations/DefinedSchemas.js | 142 +- src/SchemaMigrations/Migrations.js | 39 +- src/Security/Check.js | 20 +- src/Security/CheckRunner.js | 46 +- src/SharedRest.js | 21 +- src/StatusHandler.js | 102 +- src/TestUtils.js | 12 +- src/Utils.js | 125 +- src/batch.js | 75 +- src/cache.js | 2 +- src/cli/parse-live-query-server.js | 6 +- src/cli/parse-server.js | 109 +- src/cloud-code/Parse.Cloud.js | 120 +- src/cloud-code/Parse.Server.js | 8 +- src/cryptoUtils.js | 17 +- src/defaults.js | 29 +- src/middlewares.js | 241 +- src/password.js | 4 +- src/request.js | 58 +- src/rest.js | 126 +- src/vendor/mongodbUrl.js | 277 +- 204 files changed, 39993 insertions(+), 33268 deletions(-) diff --git a/.prettierrc b/.prettierrc index 31fa426fac..e9e2d266c1 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,5 +1,16 @@ -semi: true -trailingComma: "es5" -singleQuote: true -arrowParens: "avoid" -printWidth: 100 \ No newline at end of file +{ + "arrowParens": "avoid", + "bracketSpacing": true, + "embeddedLanguageFormatting": "auto", + "htmlWhitespaceSensitivity": "css", + "insertPragma": false, + "jsxBracketSameLine": false, + "printWidth": 80, + "proseWrap": "preserve", + "quoteProps": "as-needed", + "semi": true, + "singleQuote": false, + "tabWidth": 2, + "trailingComma": "es5", + "useTabs": false +} diff --git a/package-lock.json b/package-lock.json index 72d5f72c7f..63b9912b7a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -100,7 +100,7 @@ "node-abort-controller": "3.1.1", "node-fetch": "3.2.10", "nyc": "17.1.0", - "prettier": "2.0.5", + "prettier": "3.5.3", "semantic-release": "24.2.3", "typescript": "5.8.3", "typescript-eslint": "8.29.0", @@ -19003,15 +19003,19 @@ } }, "node_modules/prettier": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", - "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, + "license": "MIT", "bin": { - "prettier": "bin-prettier.js" + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=10.13.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, "node_modules/pretty-ms": { @@ -35900,9 +35904,9 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" }, "prettier": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", - "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true }, "pretty-ms": { diff --git a/package.json b/package.json index 6166454f73..afc2772062 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ "node-abort-controller": "3.1.1", "node-fetch": "3.2.10", "nyc": "17.1.0", - "prettier": "2.0.5", + "prettier": "3.5.3", "semantic-release": "24.2.3", "typescript": "5.8.3", "typescript-eslint": "8.29.0", diff --git a/spec/AccountLockoutPolicy.spec.js b/spec/AccountLockoutPolicy.spec.js index da8048adab..5e9620b645 100644 --- a/spec/AccountLockoutPolicy.spec.js +++ b/spec/AccountLockoutPolicy.spec.js @@ -1,15 +1,15 @@ -'use strict'; +"use strict"; -const Config = require('../lib/Config'); -const Definitions = require('../lib/Options/Definitions'); -const request = require('../lib/request'); +const Config = require("../lib/Config"); +const Definitions = require("../lib/Options/Definitions"); +const request = require("../lib/request"); const loginWithWrongCredentialsShouldFail = function (username, password) { return new Promise((resolve, reject) => { Parse.User.logIn(username, password) - .then(() => reject('login should have failed')) + .then(() => reject("login should have failed")) .catch(err => { - if (err.message === 'Invalid username/password.') { + if (err.message === "Invalid username/password.") { resolve(); } else { reject(err); @@ -18,17 +18,22 @@ const loginWithWrongCredentialsShouldFail = function (username, password) { }); }; -const isAccountLockoutError = function (username, password, duration, waitTime) { +const isAccountLockoutError = function ( + username, + password, + duration, + waitTime +) { return new Promise((resolve, reject) => { setTimeout(() => { Parse.User.logIn(username, password) - .then(() => reject('login should have failed')) + .then(() => reject("login should have failed")) .catch(err => { if ( err.message === - 'Your account is locked due to multiple failed login attempts. Please try again after ' + + "Your account is locked due to multiple failed login attempts. Please try again after " + duration + - ' minute(s)' + " minute(s)" ) { resolve(); } else { @@ -39,292 +44,349 @@ const isAccountLockoutError = function (username, password, duration, waitTime) }); }; -describe('Account Lockout Policy: ', () => { - it('account should not be locked even after failed login attempts if account lockout policy is not set', done => { +describe("Account Lockout Policy: ", () => { + it("account should not be locked even after failed login attempts if account lockout policy is not set", done => { reconfigureServer({ - appName: 'unlimited', - publicServerURL: 'http://localhost:1337/1', + appName: "unlimited", + publicServerURL: "http://localhost:1337/1", }) .then(() => { const user = new Parse.User(); - user.setUsername('username1'); - user.setPassword('password'); + user.setUsername("username1"); + user.setPassword("password"); return user.signUp(null); }) .then(() => { - return loginWithWrongCredentialsShouldFail('username1', 'incorrect password 1'); + return loginWithWrongCredentialsShouldFail( + "username1", + "incorrect password 1" + ); }) .then(() => { - return loginWithWrongCredentialsShouldFail('username1', 'incorrect password 2'); + return loginWithWrongCredentialsShouldFail( + "username1", + "incorrect password 2" + ); }) .then(() => { - return loginWithWrongCredentialsShouldFail('username1', 'incorrect password 3'); + return loginWithWrongCredentialsShouldFail( + "username1", + "incorrect password 3" + ); }) .then(() => done()) .catch(err => { - fail('allow unlimited failed login attempts failed: ' + JSON.stringify(err)); + fail( + "allow unlimited failed login attempts failed: " + JSON.stringify(err) + ); done(); }); }); - it('throw error if duration is set to an invalid number', done => { + it("throw error if duration is set to an invalid number", done => { reconfigureServer({ - appName: 'duration', + appName: "duration", accountLockout: { - duration: 'invalid value', + duration: "invalid value", threshold: 5, }, - publicServerURL: 'https://my.public.server.com/1', + publicServerURL: "https://my.public.server.com/1", }) .then(() => { - Config.get('test'); - fail('set duration to an invalid number test failed'); + Config.get("test"); + fail("set duration to an invalid number test failed"); done(); }) .catch(err => { if ( err && - err === 'Account lockout duration should be greater than 0 and less than 100000' + err === + "Account lockout duration should be greater than 0 and less than 100000" ) { done(); } else { - fail('set duration to an invalid number test failed: ' + JSON.stringify(err)); + fail( + "set duration to an invalid number test failed: " + + JSON.stringify(err) + ); done(); } }); }); - it('throw error if threshold is set to an invalid number', done => { + it("throw error if threshold is set to an invalid number", done => { reconfigureServer({ - appName: 'threshold', + appName: "threshold", accountLockout: { duration: 5, - threshold: 'invalid number', + threshold: "invalid number", }, - publicServerURL: 'https://my.public.server.com/1', + publicServerURL: "https://my.public.server.com/1", }) .then(() => { - Config.get('test'); - fail('set threshold to an invalid number test failed'); + Config.get("test"); + fail("set threshold to an invalid number test failed"); done(); }) .catch(err => { if ( err && - err === 'Account lockout threshold should be an integer greater than 0 and less than 1000' + err === + "Account lockout threshold should be an integer greater than 0 and less than 1000" ) { done(); } else { - fail('set threshold to an invalid number test failed: ' + JSON.stringify(err)); + fail( + "set threshold to an invalid number test failed: " + + JSON.stringify(err) + ); done(); } }); }); - it('throw error if threshold is < 1', done => { + it("throw error if threshold is < 1", done => { reconfigureServer({ - appName: 'threshold', + appName: "threshold", accountLockout: { duration: 5, threshold: 0, }, - publicServerURL: 'https://my.public.server.com/1', + publicServerURL: "https://my.public.server.com/1", }) .then(() => { - Config.get('test'); - fail('threshold value < 1 is invalid test failed'); + Config.get("test"); + fail("threshold value < 1 is invalid test failed"); done(); }) .catch(err => { if ( err && - err === 'Account lockout threshold should be an integer greater than 0 and less than 1000' + err === + "Account lockout threshold should be an integer greater than 0 and less than 1000" ) { done(); } else { - fail('threshold value < 1 is invalid test failed: ' + JSON.stringify(err)); + fail( + "threshold value < 1 is invalid test failed: " + JSON.stringify(err) + ); done(); } }); }); - it('throw error if threshold is > 999', done => { + it("throw error if threshold is > 999", done => { reconfigureServer({ - appName: 'threshold', + appName: "threshold", accountLockout: { duration: 5, threshold: 1000, }, - publicServerURL: 'https://my.public.server.com/1', + publicServerURL: "https://my.public.server.com/1", }) .then(() => { - Config.get('test'); - fail('threshold value > 999 is invalid test failed'); + Config.get("test"); + fail("threshold value > 999 is invalid test failed"); done(); }) .catch(err => { if ( err && - err === 'Account lockout threshold should be an integer greater than 0 and less than 1000' + err === + "Account lockout threshold should be an integer greater than 0 and less than 1000" ) { done(); } else { - fail('threshold value > 999 is invalid test failed: ' + JSON.stringify(err)); + fail( + "threshold value > 999 is invalid test failed: " + + JSON.stringify(err) + ); done(); } }); }); - it('throw error if duration is <= 0', done => { + it("throw error if duration is <= 0", done => { reconfigureServer({ - appName: 'duration', + appName: "duration", accountLockout: { duration: 0, threshold: 5, }, - publicServerURL: 'https://my.public.server.com/1', + publicServerURL: "https://my.public.server.com/1", }) .then(() => { - Config.get('test'); - fail('duration value < 1 is invalid test failed'); + Config.get("test"); + fail("duration value < 1 is invalid test failed"); done(); }) .catch(err => { if ( err && - err === 'Account lockout duration should be greater than 0 and less than 100000' + err === + "Account lockout duration should be greater than 0 and less than 100000" ) { done(); } else { - fail('duration value < 1 is invalid test failed: ' + JSON.stringify(err)); + fail( + "duration value < 1 is invalid test failed: " + JSON.stringify(err) + ); done(); } }); }); - it('throw error if duration is > 99999', done => { + it("throw error if duration is > 99999", done => { reconfigureServer({ - appName: 'duration', + appName: "duration", accountLockout: { duration: 100000, threshold: 5, }, - publicServerURL: 'https://my.public.server.com/1', + publicServerURL: "https://my.public.server.com/1", }) .then(() => { - Config.get('test'); - fail('duration value > 99999 is invalid test failed'); + Config.get("test"); + fail("duration value > 99999 is invalid test failed"); done(); }) .catch(err => { if ( err && - err === 'Account lockout duration should be greater than 0 and less than 100000' + err === + "Account lockout duration should be greater than 0 and less than 100000" ) { done(); } else { - fail('duration value > 99999 is invalid test failed: ' + JSON.stringify(err)); + fail( + "duration value > 99999 is invalid test failed: " + + JSON.stringify(err) + ); done(); } }); }); - it('lock account if failed login attempts are above threshold', done => { + it("lock account if failed login attempts are above threshold", done => { reconfigureServer({ - appName: 'lockout threshold', + appName: "lockout threshold", accountLockout: { duration: 1, threshold: 2, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }) .then(() => { const user = new Parse.User(); - user.setUsername('username2'); - user.setPassword('failedLoginAttemptsThreshold'); + user.setUsername("username2"); + user.setPassword("failedLoginAttemptsThreshold"); return user.signUp(); }) .then(() => { - return loginWithWrongCredentialsShouldFail('username2', 'wrong password'); + return loginWithWrongCredentialsShouldFail( + "username2", + "wrong password" + ); }) .then(() => { - return loginWithWrongCredentialsShouldFail('username2', 'wrong password'); + return loginWithWrongCredentialsShouldFail( + "username2", + "wrong password" + ); }) .then(() => { - return isAccountLockoutError('username2', 'wrong password', 1, 1); + return isAccountLockoutError("username2", "wrong password", 1, 1); }) .then(() => { done(); }) .catch(err => { - fail('lock account after failed login attempts test failed: ' + JSON.stringify(err)); + fail( + "lock account after failed login attempts test failed: " + + JSON.stringify(err) + ); done(); }); }); - it('lock account for accountPolicy.duration minutes if failed login attempts are above threshold', done => { + it("lock account for accountPolicy.duration minutes if failed login attempts are above threshold", done => { reconfigureServer({ - appName: 'lockout threshold', + appName: "lockout threshold", accountLockout: { duration: 0.05, // 0.05*60 = 3 secs threshold: 2, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }) .then(() => { const user = new Parse.User(); - user.setUsername('username3'); - user.setPassword('failedLoginAttemptsThreshold'); + user.setUsername("username3"); + user.setPassword("failedLoginAttemptsThreshold"); return user.signUp(); }) .then(() => { - return loginWithWrongCredentialsShouldFail('username3', 'wrong password'); + return loginWithWrongCredentialsShouldFail( + "username3", + "wrong password" + ); }) .then(() => { - return loginWithWrongCredentialsShouldFail('username3', 'wrong password'); + return loginWithWrongCredentialsShouldFail( + "username3", + "wrong password" + ); }) .then(() => { - return isAccountLockoutError('username3', 'wrong password', 0.05, 1); + return isAccountLockoutError("username3", "wrong password", 0.05, 1); }) .then(() => { // account should still be locked even after 2 seconds. - return isAccountLockoutError('username3', 'wrong password', 0.05, 2000); + return isAccountLockoutError("username3", "wrong password", 0.05, 2000); }) .then(() => { done(); }) .catch(err => { - fail('account should be locked for duration mins test failed: ' + JSON.stringify(err)); + fail( + "account should be locked for duration mins test failed: " + + JSON.stringify(err) + ); done(); }); }); - it('allow login for locked account after accountPolicy.duration minutes', done => { + it("allow login for locked account after accountPolicy.duration minutes", done => { reconfigureServer({ - appName: 'lockout threshold', + appName: "lockout threshold", accountLockout: { duration: 0.05, // 0.05*60 = 3 secs threshold: 2, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }) .then(() => { const user = new Parse.User(); - user.setUsername('username4'); - user.setPassword('correct password'); + user.setUsername("username4"); + user.setPassword("correct password"); return user.signUp(); }) .then(() => { - return loginWithWrongCredentialsShouldFail('username4', 'wrong password'); + return loginWithWrongCredentialsShouldFail( + "username4", + "wrong password" + ); }) .then(() => { - return loginWithWrongCredentialsShouldFail('username4', 'wrong password'); + return loginWithWrongCredentialsShouldFail( + "username4", + "wrong password" + ); }) .then(() => { // allow locked user to login after 3 seconds with a valid userid and password return new Promise((resolve, reject) => { setTimeout(() => { - Parse.User.logIn('username4', 'correct password') + Parse.User.logIn("username4", "correct password") .then(() => resolve()) .catch(err => reject(err)); }, 3001); @@ -335,7 +397,7 @@ describe('Account Lockout Policy: ', () => { }) .catch(err => { fail( - 'allow login for locked account after accountPolicy.duration minutes test failed: ' + + "allow login for locked account after accountPolicy.duration minutes test failed: " + JSON.stringify(err) ); done(); @@ -343,7 +405,7 @@ describe('Account Lockout Policy: ', () => { }); }); -describe('lockout with password reset option', () => { +describe("lockout with password reset option", () => { let sendPasswordResetEmail; async function setup(options = {}) { @@ -355,9 +417,9 @@ describe('lockout with password reset option', () => { options ); const config = { - appName: 'exampleApp', + appName: "exampleApp", accountLockout: accountLockout, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", emailAdapter: { sendVerificationEmail: () => Promise.resolve(), sendPasswordResetEmail: () => Promise.resolve(), @@ -366,10 +428,13 @@ describe('lockout with password reset option', () => { }; await reconfigureServer(config); - sendPasswordResetEmail = spyOn(config.emailAdapter, 'sendPasswordResetEmail').and.callThrough(); + sendPasswordResetEmail = spyOn( + config.emailAdapter, + "sendPasswordResetEmail" + ).and.callThrough(); } - it('accepts valid unlockOnPasswordReset option', async () => { + it("accepts valid unlockOnPasswordReset option", async () => { const values = [true, false]; for (const value of values) { @@ -377,16 +442,18 @@ describe('lockout with password reset option', () => { } }); - it('rejects invalid unlockOnPasswordReset option', async () => { - const values = ['a', 0, {}, [], null]; + it("rejects invalid unlockOnPasswordReset option", async () => { + const values = ["a", 0, {}, [], null]; for (const value of values) { await expectAsync(setup({ unlockOnPasswordReset: value })).toBeRejected(); } }); - it('uses default value if unlockOnPasswordReset is not set', async () => { - await expectAsync(setup({ unlockOnPasswordReset: undefined })).toBeResolved(); + it("uses default value if unlockOnPasswordReset is not set", async () => { + await expectAsync( + setup({ unlockOnPasswordReset: undefined }) + ).toBeResolved(); const parseConfig = Config.get(Parse.applicationId); expect(parseConfig.accountLockout.unlockOnPasswordReset).toBe( @@ -394,19 +461,21 @@ describe('lockout with password reset option', () => { ); }); - it('allow login for locked account after password reset', async () => { + it("allow login for locked account after password reset", async () => { await setup({ unlockOnPasswordReset: true }); const config = Config.get(Parse.applicationId); const user = new Parse.User(); - const username = 'exampleUsername'; - const password = 'examplePassword'; + const username = "exampleUsername"; + const password = "examplePassword"; user.setUsername(username); user.setPassword(password); - user.setEmail('mail@example.com'); + user.setEmail("mail@example.com"); await user.signUp(); - await expectAsync(Parse.User.logIn(username, 'incorrectPassword')).toBeRejected(); + await expectAsync( + Parse.User.logIn(username, "incorrectPassword") + ).toBeRejected(); await expectAsync(Parse.User.logIn(username, password)).toBeRejected(); await Parse.User.requestPasswordReset(user.getEmail()); @@ -414,14 +483,14 @@ describe('lockout with password reset option', () => { const link = sendPasswordResetEmail.calls.all()[0].args[0].link; const linkUrl = new URL(link); - const token = linkUrl.searchParams.get('token'); - const newPassword = 'newPassword'; + const token = linkUrl.searchParams.get("token"); + const newPassword = "newPassword"; await request({ - method: 'POST', + method: "POST", url: `${config.publicServerURL}/apps/test/request_password_reset`, body: `new_password=${newPassword}&token=${token}`, headers: { - 'Content-Type': 'application/x-www-form-urlencoded', + "Content-Type": "application/x-www-form-urlencoded", }, followRedirects: false, }); @@ -429,19 +498,21 @@ describe('lockout with password reset option', () => { await expectAsync(Parse.User.logIn(username, newPassword)).toBeResolved(); }); - it('reject login for locked account after password reset (default)', async () => { + it("reject login for locked account after password reset (default)", async () => { await setup(); const config = Config.get(Parse.applicationId); const user = new Parse.User(); - const username = 'exampleUsername'; - const password = 'examplePassword'; + const username = "exampleUsername"; + const password = "examplePassword"; user.setUsername(username); user.setPassword(password); - user.setEmail('mail@example.com'); + user.setEmail("mail@example.com"); await user.signUp(); - await expectAsync(Parse.User.logIn(username, 'incorrectPassword')).toBeRejected(); + await expectAsync( + Parse.User.logIn(username, "incorrectPassword") + ).toBeRejected(); await expectAsync(Parse.User.logIn(username, password)).toBeRejected(); await Parse.User.requestPasswordReset(user.getEmail()); @@ -449,14 +520,14 @@ describe('lockout with password reset option', () => { const link = sendPasswordResetEmail.calls.all()[0].args[0].link; const linkUrl = new URL(link); - const token = linkUrl.searchParams.get('token'); - const newPassword = 'newPassword'; + const token = linkUrl.searchParams.get("token"); + const newPassword = "newPassword"; await request({ - method: 'POST', + method: "POST", url: `${config.publicServerURL}/apps/test/request_password_reset`, body: `new_password=${newPassword}&token=${token}`, headers: { - 'Content-Type': 'application/x-www-form-urlencoded', + "Content-Type": "application/x-www-form-urlencoded", }, followRedirects: false, }); diff --git a/spec/AdaptableController.spec.js b/spec/AdaptableController.spec.js index 4cda42e162..c0606fae6c 100644 --- a/spec/AdaptableController.spec.js +++ b/spec/AdaptableController.spec.js @@ -1,6 +1,8 @@ -const AdaptableController = require('../lib/Controllers/AdaptableController').AdaptableController; -const FilesAdapter = require('../lib/Adapters/Files/FilesAdapter').default; -const FilesController = require('../lib/Controllers/FilesController').FilesController; +const AdaptableController = + require("../lib/Controllers/AdaptableController").AdaptableController; +const FilesAdapter = require("../lib/Adapters/Files/FilesAdapter").default; +const FilesController = + require("../lib/Controllers/FilesController").FilesController; const MockController = function (options) { AdaptableController.call(this, options); @@ -8,20 +10,20 @@ const MockController = function (options) { MockController.prototype = Object.create(AdaptableController.prototype); MockController.prototype.constructor = AdaptableController; -describe('AdaptableController', () => { - it('should use the provided adapter', done => { +describe("AdaptableController", () => { + it("should use the provided adapter", done => { const adapter = new FilesAdapter(); const controller = new FilesController(adapter); expect(controller.adapter).toBe(adapter); // make sure _adapter is private expect(controller._adapter).toBe(undefined); // Override _adapter is not doing anything - controller._adapter = 'Hello'; + controller._adapter = "Hello"; expect(controller.adapter).toBe(adapter); done(); }); - it('should throw when creating a new mock controller', done => { + it("should throw when creating a new mock controller", done => { const adapter = new FilesAdapter(); expect(() => { new MockController(adapter); @@ -29,7 +31,7 @@ describe('AdaptableController', () => { done(); }); - it('should fail setting the wrong adapter to the controller', done => { + it("should fail setting the wrong adapter to the controller", done => { function WrongAdapter() {} const adapter = new FilesAdapter(); const controller = new FilesController(adapter); @@ -40,7 +42,7 @@ describe('AdaptableController', () => { done(); }); - it('should fail to instantiate a controller with wrong adapter', done => { + it("should fail to instantiate a controller with wrong adapter", done => { function WrongAdapter() {} const adapter = new WrongAdapter(); expect(() => { @@ -49,14 +51,14 @@ describe('AdaptableController', () => { done(); }); - it('should fail to instantiate a controller without an adapter', done => { + it("should fail to instantiate a controller without an adapter", done => { expect(() => { new FilesController(); }).toThrow(); done(); }); - it('should accept an object adapter', done => { + it("should accept an object adapter", done => { const adapter = { createFile: function () {}, deleteFile: function () {}, @@ -70,7 +72,7 @@ describe('AdaptableController', () => { done(); }); - it('should accept an prototype based object adapter', done => { + it("should accept an prototype based object adapter", done => { function AGoodAdapter() {} AGoodAdapter.prototype.createFile = function () {}; AGoodAdapter.prototype.deleteFile = function () {}; diff --git a/spec/AdapterLoader.spec.js b/spec/AdapterLoader.spec.js index dd726bc768..72fe7016f6 100644 --- a/spec/AdapterLoader.spec.js +++ b/spec/AdapterLoader.spec.js @@ -1,62 +1,64 @@ -const { loadAdapter, loadModule } = require('../lib/Adapters/AdapterLoader'); -const FilesAdapter = require('@parse/fs-files-adapter').default; -const MockFilesAdapter = require('mock-files-adapter'); -const Config = require('../lib/Config'); +const { loadAdapter, loadModule } = require("../lib/Adapters/AdapterLoader"); +const FilesAdapter = require("@parse/fs-files-adapter").default; +const MockFilesAdapter = require("mock-files-adapter"); +const Config = require("../lib/Config"); -describe('AdapterLoader', () => { - it('should instantiate an adapter from string in object', done => { - const adapterPath = require('path').resolve('./spec/support/MockAdapter'); +describe("AdapterLoader", () => { + it("should instantiate an adapter from string in object", done => { + const adapterPath = require("path").resolve("./spec/support/MockAdapter"); const adapter = loadAdapter({ adapter: adapterPath, options: { - key: 'value', - foo: 'bar', + key: "value", + foo: "bar", }, }); expect(adapter instanceof Object).toBe(true); - expect(adapter.options.key).toBe('value'); - expect(adapter.options.foo).toBe('bar'); + expect(adapter.options.key).toBe("value"); + expect(adapter.options.foo).toBe("bar"); done(); }); - it('should instantiate an adapter from string', done => { - const adapterPath = require('path').resolve('./spec/support/MockAdapter'); + it("should instantiate an adapter from string", done => { + const adapterPath = require("path").resolve("./spec/support/MockAdapter"); const adapter = loadAdapter(adapterPath); expect(adapter instanceof Object).toBe(true); done(); }); - it('should instantiate an adapter from string that is module', done => { - const adapterPath = require('path').resolve('./lib/Adapters/Files/FilesAdapter'); + it("should instantiate an adapter from string that is module", done => { + const adapterPath = require("path").resolve( + "./lib/Adapters/Files/FilesAdapter" + ); const adapter = loadAdapter({ adapter: adapterPath, }); - expect(typeof adapter).toBe('object'); - expect(typeof adapter.createFile).toBe('function'); - expect(typeof adapter.deleteFile).toBe('function'); - expect(typeof adapter.getFileData).toBe('function'); - expect(typeof adapter.getFileLocation).toBe('function'); + expect(typeof adapter).toBe("object"); + expect(typeof adapter.createFile).toBe("function"); + expect(typeof adapter.deleteFile).toBe("function"); + expect(typeof adapter.getFileData).toBe("function"); + expect(typeof adapter.getFileLocation).toBe("function"); done(); }); - it('should instantiate an adapter from npm module', done => { + it("should instantiate an adapter from npm module", done => { const adapter = loadAdapter({ - module: '@parse/fs-files-adapter', + module: "@parse/fs-files-adapter", }); - expect(typeof adapter).toBe('object'); - expect(typeof adapter.createFile).toBe('function'); - expect(typeof adapter.deleteFile).toBe('function'); - expect(typeof adapter.getFileData).toBe('function'); - expect(typeof adapter.getFileLocation).toBe('function'); + expect(typeof adapter).toBe("object"); + expect(typeof adapter.createFile).toBe("function"); + expect(typeof adapter.deleteFile).toBe("function"); + expect(typeof adapter.getFileData).toBe("function"); + expect(typeof adapter.getFileLocation).toBe("function"); done(); }); - it('should instantiate an adapter from function/Class', done => { + it("should instantiate an adapter from function/Class", done => { const adapter = loadAdapter({ adapter: FilesAdapter, }); @@ -64,52 +66,52 @@ describe('AdapterLoader', () => { done(); }); - it('should instantiate the default adapter from Class', done => { + it("should instantiate the default adapter from Class", done => { const adapter = loadAdapter(null, FilesAdapter); expect(adapter instanceof FilesAdapter).toBe(true); done(); }); - it('should use the default adapter', done => { + it("should use the default adapter", done => { const defaultAdapter = new FilesAdapter(); const adapter = loadAdapter(null, defaultAdapter); expect(adapter instanceof FilesAdapter).toBe(true); done(); }); - it('should use the provided adapter', done => { + it("should use the provided adapter", done => { const originalAdapter = new FilesAdapter(); const adapter = loadAdapter(originalAdapter); expect(adapter).toBe(originalAdapter); done(); }); - it('should fail loading an improperly configured adapter', done => { + it("should fail loading an improperly configured adapter", done => { const Adapter = function (options) { if (!options.foo) { - throw 'foo is required for that adapter'; + throw "foo is required for that adapter"; } }; const adapterOptions = { - param: 'key', + param: "key", doSomething: function () {}, }; expect(() => { const adapter = loadAdapter(adapterOptions, Adapter); expect(adapter).toEqual(adapterOptions); - }).not.toThrow('foo is required for that adapter'); + }).not.toThrow("foo is required for that adapter"); done(); }); - it('should load push adapter from options', async () => { + it("should load push adapter from options", async () => { const options = { android: { - senderId: 'yolo', - apiKey: 'yolo', + senderId: "yolo", + apiKey: "yolo", }, }; - const ParsePushAdapter = await loadModule('@parse/push-adapter'); + const ParsePushAdapter = await loadModule("@parse/push-adapter"); expect(() => { const adapter = loadAdapter(undefined, ParsePushAdapter, options); expect(adapter.constructor).toBe(ParsePushAdapter); @@ -117,11 +119,13 @@ describe('AdapterLoader', () => { }).not.toThrow(); }); - it('should load custom push adapter from string (#3544)', done => { - const adapterPath = require('path').resolve('./spec/support/MockPushAdapter'); + it("should load custom push adapter from string (#3544)", done => { + const adapterPath = require("path").resolve( + "./spec/support/MockPushAdapter" + ); const options = { ios: { - bundleId: 'bundle.id', + bundleId: "bundle.id", }, }; const pushAdapterOptions = { @@ -134,18 +138,20 @@ describe('AdapterLoader', () => { }).then(() => { const config = Config.get(Parse.applicationId); const pushAdapter = config.pushWorker.adapter; - expect(pushAdapter.getValidPushTypes()).toEqual(['ios']); + expect(pushAdapter.getValidPushTypes()).toEqual(["ios"]); expect(pushAdapter.options).toEqual(pushAdapterOptions); done(); }); }).not.toThrow(); }); - it('should load custom database adapter from config', done => { - const adapterPath = require('path').resolve('./spec/support/MockDatabaseAdapter'); + it("should load custom database adapter from config", done => { + const adapterPath = require("path").resolve( + "./spec/support/MockDatabaseAdapter" + ); const options = { - databaseURI: 'oracledb://user:password@localhost:1521/freepdb1', - collectionPrefix: '', + databaseURI: "oracledb://user:password@localhost:1521/freepdb1", + collectionPrefix: "", }; const databaseAdapterOptions = { adapter: adapterPath, @@ -160,9 +166,9 @@ describe('AdapterLoader', () => { done(); }); - it('should load file adapter from direct passing', done => { - spyOn(console, 'warn').and.callFake(() => {}); - const mockFilesAdapter = new MockFilesAdapter('key', 'secret', 'bucket'); + it("should load file adapter from direct passing", done => { + spyOn(console, "warn").and.callFake(() => {}); + const mockFilesAdapter = new MockFilesAdapter("key", "secret", "bucket"); expect(() => { const adapter = loadAdapter(mockFilesAdapter, FilesAdapter); expect(adapter).toBe(mockFilesAdapter); diff --git a/spec/AggregateRouter.spec.js b/spec/AggregateRouter.spec.js index 96aedcc313..a58e21cdad 100644 --- a/spec/AggregateRouter.spec.js +++ b/spec/AggregateRouter.spec.js @@ -1,7 +1,8 @@ -const AggregateRouter = require('../lib/Routers/AggregateRouter').AggregateRouter; +const AggregateRouter = + require("../lib/Routers/AggregateRouter").AggregateRouter; -describe('AggregateRouter', () => { - it('get pipeline from Array', () => { +describe("AggregateRouter", () => { + it("get pipeline from Array", () => { const body = [ { $group: { _id: {} }, @@ -12,7 +13,7 @@ describe('AggregateRouter', () => { expect(result).toEqual(expected); }); - it('get pipeline from Object', () => { + it("get pipeline from Object", () => { const body = { $group: { _id: {} }, }; @@ -21,7 +22,7 @@ describe('AggregateRouter', () => { expect(result).toEqual(expected); }); - it('get pipeline from Pipeline Operator (Array)', () => { + it("get pipeline from Pipeline Operator (Array)", () => { const body = { pipeline: [ { @@ -34,7 +35,7 @@ describe('AggregateRouter', () => { expect(result).toEqual(expected); }); - it('get pipeline from Pipeline Operator (Object)', () => { + it("get pipeline from Pipeline Operator (Object)", () => { const body = { pipeline: { $group: { _id: {} }, @@ -45,39 +46,39 @@ describe('AggregateRouter', () => { expect(result).toEqual(expected); }); - it('get pipeline fails multiple keys in Array stage ', () => { + it("get pipeline fails multiple keys in Array stage ", () => { const body = [ { $group: { _id: {} }, - $match: { name: 'Test' }, + $match: { name: "Test" }, }, ]; expect(() => AggregateRouter.getPipeline(body)).toThrow( new Parse.Error( Parse.Error.INVALID_QUERY, - 'Pipeline stages should only have one key but found $group, $match.' + "Pipeline stages should only have one key but found $group, $match." ) ); }); - it('get pipeline fails multiple keys in Pipeline Operator Array stage ', () => { + it("get pipeline fails multiple keys in Pipeline Operator Array stage ", () => { const body = { pipeline: [ { $group: { _id: {} }, - $match: { name: 'Test' }, + $match: { name: "Test" }, }, ], }; expect(() => AggregateRouter.getPipeline(body)).toThrow( new Parse.Error( Parse.Error.INVALID_QUERY, - 'Pipeline stages should only have one key but found $group, $match.' + "Pipeline stages should only have one key but found $group, $match." ) ); }); - it('get search pipeline from Pipeline Operator (Array)', () => { + it("get search pipeline from Pipeline Operator (Array)", () => { const body = { pipeline: { $search: {}, @@ -88,27 +89,27 @@ describe('AggregateRouter', () => { expect(result).toEqual(expected); }); - it('support stage name starting with `$`', () => { + it("support stage name starting with `$`", () => { const body = { - $match: { someKey: 'whatever' }, + $match: { someKey: "whatever" }, }; - const expected = [{ $match: { someKey: 'whatever' } }]; + const expected = [{ $match: { someKey: "whatever" } }]; const result = AggregateRouter.getPipeline(body); expect(result).toEqual(expected); }); - it('support nested stage names starting with `$`', () => { + it("support nested stage names starting with `$`", () => { const body = [ { $lookup: { - from: 'ACollection', - let: { id: '_id' }, - as: 'results', + from: "ACollection", + let: { id: "_id" }, + as: "results", pipeline: [ { $match: { $expr: { - $eq: ['$_id', '$$id'], + $eq: ["$_id", "$$id"], }, }, }, @@ -119,14 +120,14 @@ describe('AggregateRouter', () => { const expected = [ { $lookup: { - from: 'ACollection', - let: { id: '_id' }, - as: 'results', + from: "ACollection", + let: { id: "_id" }, + as: "results", pipeline: [ { $match: { $expr: { - $eq: ['$_id', '$$id'], + $eq: ["$_id", "$$id"], }, }, }, @@ -138,16 +139,16 @@ describe('AggregateRouter', () => { expect(result).toEqual(expected); }); - it('support the use of `_id` in stages', () => { + it("support the use of `_id` in stages", () => { const body = [ - { $match: { _id: 'randomId' } }, + { $match: { _id: "randomId" } }, { $sort: { _id: -1 } }, { $addFields: { _id: 1 } }, { $group: { _id: {} } }, { $project: { _id: 0 } }, ]; const expected = [ - { $match: { _id: 'randomId' } }, + { $match: { _id: "randomId" } }, { $sort: { _id: -1 } }, { $addFields: { _id: 1 } }, { $group: { _id: {} } }, @@ -157,14 +158,19 @@ describe('AggregateRouter', () => { expect(result).toEqual(expected); }); - it('should throw with invalid stage', () => { - expect(() => AggregateRouter.getPipeline([{ foo: 'bar' }])).toThrow( - new Parse.Error(Parse.Error.INVALID_QUERY, `Invalid aggregate stage 'foo'.`) + it("should throw with invalid stage", () => { + expect(() => AggregateRouter.getPipeline([{ foo: "bar" }])).toThrow( + new Parse.Error( + Parse.Error.INVALID_QUERY, + `Invalid aggregate stage 'foo'.` + ) ); }); - it('should throw with invalid group', () => { - expect(() => AggregateRouter.getPipeline([{ $group: { objectId: 'bar' } }])).toThrow( + it("should throw with invalid group", () => { + expect(() => + AggregateRouter.getPipeline([{ $group: { objectId: "bar" } }]) + ).toThrow( new Parse.Error( Parse.Error.INVALID_QUERY, `Cannot use 'objectId' in aggregation stage $group.` diff --git a/spec/Analytics.spec.js b/spec/Analytics.spec.js index 049a2795c8..0029ee3808 100644 --- a/spec/Analytics.spec.js +++ b/spec/Analytics.spec.js @@ -3,16 +3,16 @@ const analyticsAdapter = { trackEvent: function () {}, }; -describe('AnalyticsController', () => { - it('should track a simple event', done => { - spyOn(analyticsAdapter, 'trackEvent').and.callThrough(); +describe("AnalyticsController", () => { + it("should track a simple event", done => { + spyOn(analyticsAdapter, "trackEvent").and.callThrough(); reconfigureServer({ analyticsAdapter, }) .then(() => { - return Parse.Analytics.track('MyEvent', { - key: 'value', - count: '0', + return Parse.Analytics.track("MyEvent", { + key: "value", + count: "0", }); }) .then( @@ -20,11 +20,11 @@ describe('AnalyticsController', () => { expect(analyticsAdapter.trackEvent).toHaveBeenCalled(); const lastCall = analyticsAdapter.trackEvent.calls.first(); const args = lastCall.args; - expect(args[0]).toEqual('MyEvent'); + expect(args[0]).toEqual("MyEvent"); expect(args[1]).toEqual({ dimensions: { - key: 'value', - count: '0', + key: "value", + count: "0", }, }); done(); @@ -36,15 +36,15 @@ describe('AnalyticsController', () => { ); }); - it('should track a app opened event', done => { - spyOn(analyticsAdapter, 'appOpened').and.callThrough(); + it("should track a app opened event", done => { + spyOn(analyticsAdapter, "appOpened").and.callThrough(); reconfigureServer({ analyticsAdapter, }) .then(() => { - return Parse.Analytics.track('AppOpened', { - key: 'value', - count: '0', + return Parse.Analytics.track("AppOpened", { + key: "value", + count: "0", }); }) .then( @@ -54,8 +54,8 @@ describe('AnalyticsController', () => { const args = lastCall.args; expect(args[0]).toEqual({ dimensions: { - key: 'value', - count: '0', + key: "value", + count: "0", }, }); done(); diff --git a/spec/AudienceRouter.spec.js b/spec/AudienceRouter.spec.js index da0ebdc96c..da199ae460 100644 --- a/spec/AudienceRouter.spec.js +++ b/spec/AudienceRouter.spec.js @@ -1,18 +1,19 @@ -const auth = require('../lib/Auth'); -const Config = require('../lib/Config'); -const rest = require('../lib/rest'); -const request = require('../lib/request'); -const AudiencesRouter = require('../lib/Routers/AudiencesRouter').AudiencesRouter; +const auth = require("../lib/Auth"); +const Config = require("../lib/Config"); +const rest = require("../lib/rest"); +const request = require("../lib/request"); +const AudiencesRouter = + require("../lib/Routers/AudiencesRouter").AudiencesRouter; -describe('AudiencesRouter', () => { - it('uses find condition from request.body', done => { - const config = Config.get('test'); +describe("AudiencesRouter", () => { + it("uses find condition from request.body", done => { + const config = Config.get("test"); const androidAudienceRequest = { - name: 'Android Users', + name: "Android Users", query: '{ "test": "android" }', }; const iosAudienceRequest = { - name: 'Iphone Users', + name: "Iphone Users", query: '{ "test": "ios" }', }; const request = { @@ -29,9 +30,14 @@ describe('AudiencesRouter', () => { const router = new AudiencesRouter(); rest - .create(config, auth.nobody(config), '_Audience', androidAudienceRequest) + .create(config, auth.nobody(config), "_Audience", androidAudienceRequest) .then(() => { - return rest.create(config, auth.nobody(config), '_Audience', iosAudienceRequest); + return rest.create( + config, + auth.nobody(config), + "_Audience", + iosAudienceRequest + ); }) .then(() => { return router.handleFind(request); @@ -47,14 +53,14 @@ describe('AudiencesRouter', () => { }); }); - it('uses find condition from request.query', done => { - const config = Config.get('test'); + it("uses find condition from request.query", done => { + const config = Config.get("test"); const androidAudienceRequest = { - name: 'Android Users', + name: "Android Users", query: '{ "test": "android" }', }; const iosAudienceRequest = { - name: 'Iphone Users', + name: "Iphone Users", query: '{ "test": "ios" }', }; const request = { @@ -71,9 +77,14 @@ describe('AudiencesRouter', () => { const router = new AudiencesRouter(); rest - .create(config, auth.nobody(config), '_Audience', androidAudienceRequest) + .create(config, auth.nobody(config), "_Audience", androidAudienceRequest) .then(() => { - return rest.create(config, auth.nobody(config), '_Audience', iosAudienceRequest); + return rest.create( + config, + auth.nobody(config), + "_Audience", + iosAudienceRequest + ); }) .then(() => { return router.handleFind(request); @@ -89,14 +100,14 @@ describe('AudiencesRouter', () => { }); }); - it('query installations with limit = 0', done => { - const config = Config.get('test'); + it("query installations with limit = 0", done => { + const config = Config.get("test"); const androidAudienceRequest = { - name: 'Android Users', + name: "Android Users", query: '{ "test": "android" }', }; const iosAudienceRequest = { - name: 'Iphone Users', + name: "Iphone Users", query: '{ "test": "ios" }', }; const request = { @@ -109,12 +120,17 @@ describe('AudiencesRouter', () => { info: {}, }; - Config.get('test'); + Config.get("test"); const router = new AudiencesRouter(); rest - .create(config, auth.nobody(config), '_Audience', androidAudienceRequest) + .create(config, auth.nobody(config), "_Audience", androidAudienceRequest) .then(() => { - return rest.create(config, auth.nobody(config), '_Audience', iosAudienceRequest); + return rest.create( + config, + auth.nobody(config), + "_Audience", + iosAudienceRequest + ); }) .then(() => { return router.handleFind(request); @@ -130,14 +146,14 @@ describe('AudiencesRouter', () => { }); }); - it_exclude_dbs(['postgres'])('query installations with count = 1', done => { - const config = Config.get('test'); + it_exclude_dbs(["postgres"])("query installations with count = 1", done => { + const config = Config.get("test"); const androidAudienceRequest = { - name: 'Android Users', + name: "Android Users", query: '{ "test": "android" }', }; const iosAudienceRequest = { - name: 'Iphone Users', + name: "Iphone Users", query: '{ "test": "ios" }', }; const request = { @@ -152,8 +168,15 @@ describe('AudiencesRouter', () => { const router = new AudiencesRouter(); rest - .create(config, auth.nobody(config), '_Audience', androidAudienceRequest) - .then(() => rest.create(config, auth.nobody(config), '_Audience', iosAudienceRequest)) + .create(config, auth.nobody(config), "_Audience", androidAudienceRequest) + .then(() => + rest.create( + config, + auth.nobody(config), + "_Audience", + iosAudienceRequest + ) + ) .then(() => router.handleFind(request)) .then(res => { const response = res.response; @@ -167,169 +190,188 @@ describe('AudiencesRouter', () => { }); }); - it_exclude_dbs(['postgres'])('query installations with limit = 0 and count = 1', done => { - const config = Config.get('test'); - const androidAudienceRequest = { - name: 'Android Users', - query: '{ "test": "android" }', - }; - const iosAudienceRequest = { - name: 'Iphone Users', - query: '{ "test": "ios" }', - }; - const request = { - config: config, - auth: auth.master(config), - body: {}, - query: { - limit: 0, - count: 1, - }, - info: {}, - }; + it_exclude_dbs(["postgres"])( + "query installations with limit = 0 and count = 1", + done => { + const config = Config.get("test"); + const androidAudienceRequest = { + name: "Android Users", + query: '{ "test": "android" }', + }; + const iosAudienceRequest = { + name: "Iphone Users", + query: '{ "test": "ios" }', + }; + const request = { + config: config, + auth: auth.master(config), + body: {}, + query: { + limit: 0, + count: 1, + }, + info: {}, + }; - const router = new AudiencesRouter(); - rest - .create(config, auth.nobody(config), '_Audience', androidAudienceRequest) - .then(() => { - return rest.create(config, auth.nobody(config), '_Audience', iosAudienceRequest); - }) - .then(() => { - return router.handleFind(request); - }) - .then(res => { - const response = res.response; - expect(response.results.length).toEqual(0); - expect(response.count).toEqual(2); - done(); - }) - .catch(err => { - fail(JSON.stringify(err)); - done(); - }); - }); + const router = new AudiencesRouter(); + rest + .create( + config, + auth.nobody(config), + "_Audience", + androidAudienceRequest + ) + .then(() => { + return rest.create( + config, + auth.nobody(config), + "_Audience", + iosAudienceRequest + ); + }) + .then(() => { + return router.handleFind(request); + }) + .then(res => { + const response = res.response; + expect(response.results.length).toEqual(0); + expect(response.count).toEqual(2); + done(); + }) + .catch(err => { + fail(JSON.stringify(err)); + done(); + }); + } + ); - it('should create, read, update and delete audiences throw api', done => { + it("should create, read, update and delete audiences throw api", done => { Parse._request( - 'POST', - 'push_audiences', - { name: 'My Audience', query: JSON.stringify({ deviceType: 'ios' }) }, + "POST", + "push_audiences", + { name: "My Audience", query: JSON.stringify({ deviceType: "ios" }) }, { useMasterKey: true } ).then(() => { - Parse._request('GET', 'push_audiences', {}, { useMasterKey: true }).then(results => { - expect(results.results.length).toEqual(1); - expect(results.results[0].name).toEqual('My Audience'); - expect(results.results[0].query.deviceType).toEqual('ios'); - Parse._request( - 'GET', - `push_audiences/${results.results[0].objectId}`, - {}, - { useMasterKey: true } - ).then(results => { - expect(results.name).toEqual('My Audience'); - expect(results.query.deviceType).toEqual('ios'); + Parse._request("GET", "push_audiences", {}, { useMasterKey: true }).then( + results => { + expect(results.results.length).toEqual(1); + expect(results.results[0].name).toEqual("My Audience"); + expect(results.results[0].query.deviceType).toEqual("ios"); Parse._request( - 'PUT', - `push_audiences/${results.objectId}`, - { name: 'My Audience 2' }, + "GET", + `push_audiences/${results.results[0].objectId}`, + {}, { useMasterKey: true } - ).then(() => { + ).then(results => { + expect(results.name).toEqual("My Audience"); + expect(results.query.deviceType).toEqual("ios"); Parse._request( - 'GET', + "PUT", `push_audiences/${results.objectId}`, - {}, + { name: "My Audience 2" }, { useMasterKey: true } - ).then(results => { - expect(results.name).toEqual('My Audience 2'); - expect(results.query.deviceType).toEqual('ios'); + ).then(() => { Parse._request( - 'DELETE', + "GET", `push_audiences/${results.objectId}`, {}, { useMasterKey: true } - ).then(() => { - Parse._request('GET', 'push_audiences', {}, { useMasterKey: true }).then( - results => { + ).then(results => { + expect(results.name).toEqual("My Audience 2"); + expect(results.query.deviceType).toEqual("ios"); + Parse._request( + "DELETE", + `push_audiences/${results.objectId}`, + {}, + { useMasterKey: true } + ).then(() => { + Parse._request( + "GET", + "push_audiences", + {}, + { useMasterKey: true } + ).then(results => { expect(results.results.length).toEqual(0); done(); - } - ); + }); + }); }); }); }); - }); - }); + } + ); }); }); - it('should only create with master key', done => { - Parse._request('POST', 'push_audiences', { - name: 'My Audience', - query: JSON.stringify({ deviceType: 'ios' }), + it("should only create with master key", done => { + Parse._request("POST", "push_audiences", { + name: "My Audience", + query: JSON.stringify({ deviceType: "ios" }), }).then( () => {}, error => { - expect(error.message).toEqual('unauthorized: master key is required'); + expect(error.message).toEqual("unauthorized: master key is required"); done(); } ); }); - it('should only find with master key', done => { - Parse._request('GET', 'push_audiences', {}).then( + it("should only find with master key", done => { + Parse._request("GET", "push_audiences", {}).then( () => {}, error => { - expect(error.message).toEqual('unauthorized: master key is required'); + expect(error.message).toEqual("unauthorized: master key is required"); done(); } ); }); - it('should only get with master key', done => { - Parse._request('GET', `push_audiences/someId`, {}).then( + it("should only get with master key", done => { + Parse._request("GET", `push_audiences/someId`, {}).then( () => {}, error => { - expect(error.message).toEqual('unauthorized: master key is required'); + expect(error.message).toEqual("unauthorized: master key is required"); done(); } ); }); - it('should only update with master key', done => { - Parse._request('PUT', `push_audiences/someId`, { - name: 'My Audience 2', + it("should only update with master key", done => { + Parse._request("PUT", `push_audiences/someId`, { + name: "My Audience 2", }).then( () => {}, error => { - expect(error.message).toEqual('unauthorized: master key is required'); + expect(error.message).toEqual("unauthorized: master key is required"); done(); } ); }); - it('should only delete with master key', done => { - Parse._request('DELETE', `push_audiences/someId`, {}).then( + it("should only delete with master key", done => { + Parse._request("DELETE", `push_audiences/someId`, {}).then( () => {}, error => { - expect(error.message).toEqual('unauthorized: master key is required'); + expect(error.message).toEqual("unauthorized: master key is required"); done(); } ); }); - it_id('af1111b5-3251-4b40-8f06-fb0fc624fa91')(it_exclude_dbs(['postgres']))( - 'should support legacy parse.com audience fields', + it_id("af1111b5-3251-4b40-8f06-fb0fc624fa91")(it_exclude_dbs(["postgres"]))( + "should support legacy parse.com audience fields", done => { - const database = Config.get(Parse.applicationId).database.adapter.database; + const database = Config.get(Parse.applicationId).database.adapter + .database; const now = new Date(); Parse._request( - 'POST', - 'push_audiences', - { name: 'My Audience', query: JSON.stringify({ deviceType: 'ios' }) }, + "POST", + "push_audiences", + { name: "My Audience", query: JSON.stringify({ deviceType: "ios" }) }, { useMasterKey: true } ).then(audience => { database - .collection('test__Audience') + .collection("test__Audience") .updateOne( { _id: audience.objectId }, { @@ -343,21 +385,21 @@ describe('AudiencesRouter', () => { expect(result).toBeTruthy(); database - .collection('test__Audience') + .collection("test__Audience") .find({ _id: audience.objectId }) .toArray() .then(rows => { - expect(rows[0]['times_used']).toEqual(1); - expect(rows[0]['_last_used']).toEqual(now); + expect(rows[0]["times_used"]).toEqual(1); + expect(rows[0]["_last_used"]).toEqual(now); Parse._request( - 'GET', - 'push_audiences/' + audience.objectId, + "GET", + "push_audiences/" + audience.objectId, {}, { useMasterKey: true } ) .then(audience => { - expect(audience.name).toEqual('My Audience'); - expect(audience.query.deviceType).toEqual('ios'); + expect(audience.name).toEqual("My Audience"); + expect(audience.query.deviceType).toEqual("ios"); expect(audience.timesUsed).toEqual(1); expect(audience.lastUsed).toEqual(now.toISOString()); done(); @@ -374,11 +416,11 @@ describe('AudiencesRouter', () => { } ); - it('should be able to search on audiences', done => { + it("should be able to search on audiences", done => { Parse._request( - 'POST', - 'push_audiences', - { name: 'neverUsed', query: JSON.stringify({ deviceType: 'ios' }) }, + "POST", + "push_audiences", + { name: "neverUsed", query: JSON.stringify({ deviceType: "ios" }) }, { useMasterKey: true } ).then(() => { const query = { @@ -386,15 +428,15 @@ describe('AudiencesRouter', () => { lastUsed: { $exists: false }, }; Parse._request( - 'GET', - 'push_audiences?order=-createdAt&limit=1', + "GET", + "push_audiences?order=-createdAt&limit=1", { where: query }, { useMasterKey: true } ) .then(results => { expect(results.results.length).toEqual(1); const audience = results.results[0]; - expect(audience.name).toEqual('neverUsed'); + expect(audience.name).toEqual("neverUsed"); done(); }) .catch(error => { @@ -403,27 +445,27 @@ describe('AudiencesRouter', () => { }); }); - it('should handle _Audience invalid fields via rest', async () => { + it("should handle _Audience invalid fields via rest", async () => { await reconfigureServer({ - appId: 'test', - restAPIKey: 'test', - publicServerURL: 'http://localhost:8378/1', + appId: "test", + restAPIKey: "test", + publicServerURL: "http://localhost:8378/1", }); try { await request({ - method: 'POST', - url: 'http://localhost:8378/1/classes/_Audience', - body: { lorem: 'ipsum', _method: 'POST' }, + method: "POST", + url: "http://localhost:8378/1/classes/_Audience", + body: { lorem: "ipsum", _method: "POST" }, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'test', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "test", + "Content-Type": "application/json", }, }); expect(true).toBeFalsy(); } catch (e) { expect(e.data.code).toBe(107); - expect(e.data.error).toBe('Could not add field lorem'); + expect(e.data.error).toBe("Could not add field lorem"); } }); }); diff --git a/spec/Auth.spec.js b/spec/Auth.spec.js index a718a1c2d7..07f6daba00 100644 --- a/spec/Auth.spec.js +++ b/spec/Auth.spec.js @@ -1,26 +1,26 @@ -'use strict'; +"use strict"; -describe('Auth', () => { - const { Auth, getAuthForSessionToken } = require('../lib/Auth.js'); - const Config = require('../lib/Config'); - describe('getUserRoles', () => { +describe("Auth", () => { + const { Auth, getAuthForSessionToken } = require("../lib/Auth.js"); + const Config = require("../lib/Config"); + describe("getUserRoles", () => { let auth; let config; let currentRoles = null; - const currentUserId = 'userId'; + const currentUserId = "userId"; beforeEach(() => { - currentRoles = ['role:userId']; + currentRoles = ["role:userId"]; config = { cacheController: { role: { get: () => Promise.resolve(currentRoles), - set: jasmine.createSpy('set'), + set: jasmine.createSpy("set"), }, }, }; - spyOn(config.cacheController.role, 'get').and.callThrough(); + spyOn(config.cacheController.role, "get").and.callThrough(); auth = new Auth({ config: config, @@ -28,11 +28,11 @@ describe('Auth', () => { user: { id: currentUserId, }, - installationId: 'installationId', + installationId: "installationId", }); }); - it('should get user roles from the cache', done => { + it("should get user roles from the cache", done => { auth.getUserRoles().then(roles => { const firstSet = config.cacheController.role.set.calls.first(); expect(firstSet).toEqual(undefined); @@ -44,8 +44,8 @@ describe('Auth', () => { }); }); - it('should only query the roles once', done => { - const loadRolesSpy = spyOn(auth, '_loadRoles').and.callThrough(); + it("should only query the roles once", done => { + const loadRolesSpy = spyOn(auth, "_loadRoles").and.callThrough(); auth .getUserRoles() .then(roles => { @@ -66,7 +66,7 @@ describe('Auth', () => { }); }); - it('should not have any roles with no user', done => { + it("should not have any roles with no user", done => { auth.user = null; auth .getUserRoles() @@ -74,7 +74,7 @@ describe('Auth', () => { .then(() => done()); }); - it('should not have any user roles with master', done => { + it("should not have any user roles with master", done => { auth.isMaster = true; auth .getUserRoles() @@ -83,26 +83,26 @@ describe('Auth', () => { }); }); - it('can use extendSessionOnUse', async () => { + it("can use extendSessionOnUse", async () => { await reconfigureServer({ extendSessionOnUse: true, }); const user = new Parse.User(); await user.signUp({ - username: 'hello', - password: 'password', + username: "hello", + password: "password", }); const session = await new Parse.Query(Parse.Session).first(); - const updatedAt = new Date('2010'); + const updatedAt = new Date("2010"); const expiry = new Date(); expiry.setHours(expiry.getHours() + 1); await Parse.Server.database.update( - '_Session', + "_Session", { objectId: session.id }, { - expiresAt: { __type: 'Date', iso: expiry.toISOString() }, + expiresAt: { __type: "Date", iso: expiry.toISOString() }, updatedAt: updatedAt.toISOString(), } ); @@ -111,14 +111,14 @@ describe('Auth', () => { await session.fetch(); await new Promise(resolve => setTimeout(resolve, 1000)); await session.fetch(); - expect(session.get('expiresAt') > expiry).toBeTrue(); + expect(session.get("expiresAt") > expiry).toBeTrue(); }); - it('should load auth without a config', async () => { + it("should load auth without a config", async () => { const user = new Parse.User(); await user.signUp({ - username: 'hello', - password: 'password', + username: "hello", + password: "password", }); expect(user.getSessionToken()).not.toBeUndefined(); const userAuth = await getAuthForSessionToken({ @@ -128,29 +128,29 @@ describe('Auth', () => { expect(userAuth.user.id).toBe(user.id); }); - it('should load auth with a config', async () => { + it("should load auth with a config", async () => { const user = new Parse.User(); await user.signUp({ - username: 'hello', - password: 'password', + username: "hello", + password: "password", }); expect(user.getSessionToken()).not.toBeUndefined(); const userAuth = await getAuthForSessionToken({ sessionToken: user.getSessionToken(), - config: Config.get('test'), + config: Config.get("test"), }); expect(userAuth.user instanceof Parse.User).toBe(true); expect(userAuth.user.id).toBe(user.id); }); - describe('getRolesForUser', () => { + describe("getRolesForUser", () => { const rolesNumber = 100; - it('should load all roles without config', async () => { + it("should load all roles without config", async () => { const user = new Parse.User(); await user.signUp({ - username: 'hello', - password: 'password', + username: "hello", + password: "password", }); expect(user.getSessionToken()).not.toBeUndefined(); const userAuth = await getAuthForSessionToken({ @@ -159,7 +159,7 @@ describe('Auth', () => { const roles = []; for (let i = 0; i < rolesNumber; i++) { const acl = new Parse.ACL(); - const role = new Parse.Role('roleloadtest' + i, acl); + const role = new Parse.Role("roleloadtest" + i, acl); role.getUsers().add([user]); roles.push(role); } @@ -169,21 +169,21 @@ describe('Auth', () => { expect(cloudRoles.length).toBe(rolesNumber); }); - it('should load all roles with config', async () => { + it("should load all roles with config", async () => { const user = new Parse.User(); await user.signUp({ - username: 'hello', - password: 'password', + username: "hello", + password: "password", }); expect(user.getSessionToken()).not.toBeUndefined(); const userAuth = await getAuthForSessionToken({ sessionToken: user.getSessionToken(), - config: Config.get('test'), + config: Config.get("test"), }); const roles = []; for (let i = 0; i < rolesNumber; i++) { const acl = new Parse.ACL(); - const role = new Parse.Role('roleloadtest' + i, acl); + const role = new Parse.Role("roleloadtest" + i, acl); role.getUsers().add([user]); roles.push(role); } @@ -193,32 +193,32 @@ describe('Auth', () => { expect(cloudRoles.length).toBe(rolesNumber); }); - it('should load all roles for different users with config', async () => { + it("should load all roles for different users with config", async () => { const user = new Parse.User(); await user.signUp({ - username: 'hello', - password: 'password', + username: "hello", + password: "password", }); const user2 = new Parse.User(); await user2.signUp({ - username: 'world', - password: '1234', + username: "world", + password: "1234", }); expect(user.getSessionToken()).not.toBeUndefined(); const userAuth = await getAuthForSessionToken({ sessionToken: user.getSessionToken(), - config: Config.get('test'), + config: Config.get("test"), }); const user2Auth = await getAuthForSessionToken({ sessionToken: user2.getSessionToken(), - config: Config.get('test'), + config: Config.get("test"), }); const roles = []; for (let i = 0; i < rolesNumber; i += 1) { const acl = new Parse.ACL(); const acl2 = new Parse.ACL(); - const role = new Parse.Role('roleloadtest' + i, acl); - const role2 = new Parse.Role('role2loadtest' + i, acl2); + const role = new Parse.Role("roleloadtest" + i, acl); + const role2 = new Parse.Role("role2loadtest" + i, acl2); role.getUsers().add([user]); role2.getUsers().add([user2]); roles.push(role); @@ -234,15 +234,21 @@ describe('Auth', () => { }); }); -describe('extendSessionOnUse', () => { +describe("extendSessionOnUse", () => { it(`shouldUpdateSessionExpiry()`, async () => { - const { shouldUpdateSessionExpiry } = require('../lib/Auth'); + const { shouldUpdateSessionExpiry } = require("../lib/Auth"); let update = new Date(Date.now() - 86410 * 1000); - const res = shouldUpdateSessionExpiry({ sessionLength: 86460 }, { updatedAt: update }); + const res = shouldUpdateSessionExpiry( + { sessionLength: 86460 }, + { updatedAt: update } + ); update = new Date(Date.now() - 43210 * 1000); - const res2 = shouldUpdateSessionExpiry({ sessionLength: 86460 }, { updatedAt: update }); + const res2 = shouldUpdateSessionExpiry( + { sessionLength: 86460 }, + { updatedAt: update } + ); expect(res).toBe(true); expect(res2).toBe(false); diff --git a/spec/AuthenticationAdapters.spec.js b/spec/AuthenticationAdapters.spec.js index d8340c60f6..f5922d189d 100644 --- a/spec/AuthenticationAdapters.spec.js +++ b/spec/AuthenticationAdapters.spec.js @@ -1,15 +1,16 @@ -const request = require('../lib/request'); -const Config = require('../lib/Config'); -const defaultColumns = require('../lib/Controllers/SchemaController').defaultColumns; -const authenticationLoader = require('../lib/Adapters/Auth'); -const path = require('path'); - -describe('AuthenticationProviders', function () { +const request = require("../lib/request"); +const Config = require("../lib/Config"); +const defaultColumns = + require("../lib/Controllers/SchemaController").defaultColumns; +const authenticationLoader = require("../lib/Adapters/Auth"); +const path = require("path"); + +describe("AuthenticationProviders", function () { const getMockMyOauthProvider = function () { return { authData: { - id: '12345', - access_token: '12345', + id: "12345", + access_token: "12345", expiration_date: new Date().toJSON(), }, shouldError: false, @@ -20,7 +21,7 @@ describe('AuthenticationProviders', function () { authenticate: function (options) { if (this.shouldError) { - options.error(this, 'An error occurred'); + options.error(this, "An error occurred"); } else if (this.shouldCancel) { options.error(this, null); } else { @@ -40,7 +41,7 @@ describe('AuthenticationProviders', function () { return true; }, getAuthType: function () { - return 'myoauth'; + return "myoauth"; }, deauthenticate: function () { this.loggedOut = true; @@ -67,15 +68,15 @@ describe('AuthenticationProviders', function () { }; const options = { - method: 'POST', + method: "POST", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Installation-Id': 'yolo', - 'X-Parse-Session-Token': token, - 'Content-Type': 'application/json', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "X-Parse-Installation-Id": "yolo", + "X-Parse-Session-Token": token, + "Content-Type": "application/json", }, - url: 'http://localhost:8378/1/users', + url: "http://localhost:8378/1/users", body: jsonBody, }; return request(options) @@ -96,7 +97,7 @@ describe('AuthenticationProviders', function () { }); }; - it('should create user with REST API', done => { + it("should create user with REST API", done => { createOAuthUser((error, response, body) => { expect(error).toBe(null); const b = body; @@ -104,26 +105,26 @@ describe('AuthenticationProviders', function () { expect(b.objectId).not.toBeNull(); expect(b.objectId).not.toBeUndefined(); const sessionToken = b.sessionToken; - const q = new Parse.Query('_Session'); - q.equalTo('sessionToken', sessionToken); + const q = new Parse.Query("_Session"); + q.equalTo("sessionToken", sessionToken); q.first({ useMasterKey: true }) .then(res => { if (!res) { - fail('should not fail fetching the session'); + fail("should not fail fetching the session"); done(); return; } - expect(res.get('installationId')).toEqual('yolo'); + expect(res.get("installationId")).toEqual("yolo"); done(); }) .catch(() => { - fail('should not fail fetching the session'); + fail("should not fail fetching the session"); done(); }); }); }); - it('should only create a single user with REST API', done => { + it("should only create a single user with REST API", done => { let objectId; createOAuthUser((error, response, body) => { expect(error).toBe(null); @@ -144,7 +145,7 @@ describe('AuthenticationProviders', function () { }); it("should fail to link if session token don't match user", done => { - Parse.User.signUp('myUser', 'password') + Parse.User.signUp("myUser", "password") .then(user => { return createOAuthUserWithSessionToken(user.getSessionToken()); }) @@ -152,85 +153,92 @@ describe('AuthenticationProviders', function () { return Parse.User.logOut(); }) .then(() => { - return Parse.User.signUp('myUser2', 'password'); + return Parse.User.signUp("myUser2", "password"); }) .then(user => { return createOAuthUserWithSessionToken(user.getSessionToken()); }) .then(fail, ({ data }) => { expect(data.code).toBe(208); - expect(data.error).toBe('this auth is already used'); + expect(data.error).toBe("this auth is already used"); done(); }) .catch(done.fail); }); - it('should support loginWith with session token and with/without mutated authData', async () => { + it("should support loginWith with session token and with/without mutated authData", async () => { const fakeAuthProvider = { validateAppId: () => Promise.resolve(), validateAuthData: () => Promise.resolve(), }; - const payload = { authData: { id: 'user1', token: 'fakeToken' } }; - const payload2 = { authData: { id: 'user1', token: 'fakeToken2' } }; + const payload = { authData: { id: "user1", token: "fakeToken" } }; + const payload2 = { authData: { id: "user1", token: "fakeToken2" } }; await reconfigureServer({ auth: { fakeAuthProvider } }); - const user = await Parse.User.logInWith('fakeAuthProvider', payload); - const user2 = await Parse.User.logInWith('fakeAuthProvider', payload, { + const user = await Parse.User.logInWith("fakeAuthProvider", payload); + const user2 = await Parse.User.logInWith("fakeAuthProvider", payload, { sessionToken: user.getSessionToken(), }); - const user3 = await Parse.User.logInWith('fakeAuthProvider', payload2, { + const user3 = await Parse.User.logInWith("fakeAuthProvider", payload2, { sessionToken: user2.getSessionToken(), }); expect(user.id).toEqual(user2.id); expect(user.id).toEqual(user3.id); }); - it('should support sync/async validateAppId', async () => { + it("should support sync/async validateAppId", async () => { const syncProvider = { validateAppId: () => true, - appIds: 'test', + appIds: "test", validateAuthData: () => Promise.resolve(), }; const asyncProvider = { - appIds: 'test', + appIds: "test", validateAppId: () => Promise.resolve(true), validateAuthData: () => Promise.resolve(), }; - const payload = { authData: { id: 'user1', token: 'fakeToken' } }; - const syncSpy = spyOn(syncProvider, 'validateAppId'); - const asyncSpy = spyOn(asyncProvider, 'validateAppId'); + const payload = { authData: { id: "user1", token: "fakeToken" } }; + const syncSpy = spyOn(syncProvider, "validateAppId"); + const asyncSpy = spyOn(asyncProvider, "validateAppId"); await reconfigureServer({ auth: { asyncProvider, syncProvider } }); - const user = await Parse.User.logInWith('asyncProvider', payload); - const user2 = await Parse.User.logInWith('syncProvider', payload); + const user = await Parse.User.logInWith("asyncProvider", payload); + const user2 = await Parse.User.logInWith("syncProvider", payload); expect(user.getSessionToken()).toBeDefined(); expect(user2.getSessionToken()).toBeDefined(); expect(syncSpy).toHaveBeenCalledTimes(1); expect(asyncSpy).toHaveBeenCalledTimes(1); }); - it('unlink and link with custom provider', async () => { + it("unlink and link with custom provider", async () => { const provider = getMockMyOauthProvider(); Parse.User._registerAuthenticationProvider(provider); - const model = await Parse.User._logInWith('myoauth'); - ok(model instanceof Parse.User, 'Model should be a Parse.User'); + const model = await Parse.User._logInWith("myoauth"); + ok(model instanceof Parse.User, "Model should be a Parse.User"); strictEqual(Parse.User.current(), model); - ok(model.extended(), 'Should have used the subclass.'); + ok(model.extended(), "Should have used the subclass."); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); - strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); - ok(model._isLinked('myoauth'), 'User should be linked to myoauth'); - - await model._unlinkFrom('myoauth'); - ok(!model._isLinked('myoauth'), 'User should not be linked to myoauth'); - ok(!provider.synchronizedUserId, 'User id should be cleared'); - ok(!provider.synchronizedAuthToken, 'Auth token should be cleared'); - ok(!provider.synchronizedExpiration, 'Expiration should be cleared'); + strictEqual( + provider.authData.expiration_date, + provider.synchronizedExpiration + ); + ok(model._isLinked("myoauth"), "User should be linked to myoauth"); + + await model._unlinkFrom("myoauth"); + ok(!model._isLinked("myoauth"), "User should not be linked to myoauth"); + ok(!provider.synchronizedUserId, "User id should be cleared"); + ok(!provider.synchronizedAuthToken, "Auth token should be cleared"); + ok(!provider.synchronizedExpiration, "Expiration should be cleared"); // make sure the auth data is properly deleted const config = Config.get(Parse.applicationId); const res = await config.database.adapter.find( - '_User', + "_User", { - fields: Object.assign({}, defaultColumns._Default, defaultColumns._Installation), + fields: Object.assign( + {}, + defaultColumns._Default, + defaultColumns._Installation + ), }, { objectId: model.id }, {} @@ -239,22 +247,26 @@ describe('AuthenticationProviders', function () { expect(res[0]._auth_data_myoauth).toBeUndefined(); expect(res[0]._auth_data_myoauth).not.toBeNull(); - await model._linkWith('myoauth'); + await model._linkWith("myoauth"); - ok(provider.synchronizedUserId, 'User id should have a value'); - ok(provider.synchronizedAuthToken, 'Auth token should have a value'); - ok(provider.synchronizedExpiration, 'Expiration should have a value'); - ok(model._isLinked('myoauth'), 'User should be linked to myoauth'); + ok(provider.synchronizedUserId, "User id should have a value"); + ok(provider.synchronizedAuthToken, "Auth token should have a value"); + ok(provider.synchronizedExpiration, "Expiration should have a value"); + ok(model._isLinked("myoauth"), "User should be linked to myoauth"); }); function validateValidator(validator) { - expect(typeof validator).toBe('function'); + expect(typeof validator).toBe("function"); } function validateAuthenticationHandler(authenticationHandler) { expect(authenticationHandler).not.toBeUndefined(); - expect(typeof authenticationHandler.getValidatorForProvider).toBe('function'); - expect(typeof authenticationHandler.getValidatorForProvider).toBe('function'); + expect(typeof authenticationHandler.getValidatorForProvider).toBe( + "function" + ); + expect(typeof authenticationHandler.getValidatorForProvider).toBe( + "function" + ); } function validateAuthenticationAdapter(authAdapter) { @@ -262,36 +274,41 @@ describe('AuthenticationProviders', function () { if (!authAdapter) { return; } - expect(typeof authAdapter.validateAuthData).toBe('function'); - expect(typeof authAdapter.validateAppId).toBe('function'); + expect(typeof authAdapter.validateAuthData).toBe("function"); + expect(typeof authAdapter.validateAppId).toBe("function"); } - it('properly loads custom adapter', done => { + it("properly loads custom adapter", done => { const validAuthData = { - id: 'hello', - token: 'world', + id: "hello", + token: "world", }; const adapter = { validateAppId: function () { return Promise.resolve(); }, validateAuthData: function (authData) { - if (authData.id == validAuthData.id && authData.token == validAuthData.token) { + if ( + authData.id == validAuthData.id && + authData.token == validAuthData.token + ) { return Promise.resolve(); } return Promise.reject(); }, }; - const authDataSpy = spyOn(adapter, 'validateAuthData').and.callThrough(); - const appIdSpy = spyOn(adapter, 'validateAppId').and.callThrough(); + const authDataSpy = spyOn(adapter, "validateAuthData").and.callThrough(); + const appIdSpy = spyOn(adapter, "validateAppId").and.callThrough(); const authenticationHandler = authenticationLoader({ customAuthentication: adapter, }); validateAuthenticationHandler(authenticationHandler); - const { validator } = authenticationHandler.getValidatorForProvider('customAuthentication'); + const { validator } = authenticationHandler.getValidatorForProvider( + "customAuthentication" + ); validateValidator(validator); validator(validAuthData, {}, {}).then( @@ -308,17 +325,19 @@ describe('AuthenticationProviders', function () { ); }); - it('properly loads custom adapter module object', done => { + it("properly loads custom adapter module object", done => { const authenticationHandler = authenticationLoader({ - customAuthentication: path.resolve('./spec/support/CustomAuth.js'), + customAuthentication: path.resolve("./spec/support/CustomAuth.js"), }); validateAuthenticationHandler(authenticationHandler); - const { validator } = authenticationHandler.getValidatorForProvider('customAuthentication'); + const { validator } = authenticationHandler.getValidatorForProvider( + "customAuthentication" + ); validateValidator(validator); validator( { - token: 'my-token', + token: "my-token", }, {}, {} @@ -333,21 +352,23 @@ describe('AuthenticationProviders', function () { ); }); - it('properly loads custom adapter module object (again)', done => { + it("properly loads custom adapter module object (again)", done => { const authenticationHandler = authenticationLoader({ customAuthentication: { - module: path.resolve('./spec/support/CustomAuthFunction.js'), - options: { token: 'valid-token' }, + module: path.resolve("./spec/support/CustomAuthFunction.js"), + options: { token: "valid-token" }, }, }); validateAuthenticationHandler(authenticationHandler); - const { validator } = authenticationHandler.getValidatorForProvider('customAuthentication'); + const { validator } = authenticationHandler.getValidatorForProvider( + "customAuthentication" + ); validateValidator(validator); validator( { - token: 'valid-token', + token: "valid-token", }, {}, {} @@ -362,141 +383,148 @@ describe('AuthenticationProviders', function () { ); }); - it('properly loads a default adapter with options', () => { + it("properly loads a default adapter with options", () => { const options = { facebook: { - appIds: ['a', 'b'], - appSecret: 'secret', + appIds: ["a", "b"], + appSecret: "secret", }, }; - const { adapter, appIds, providerOptions } = authenticationLoader.loadAuthAdapter( - 'facebook', - options - ); + const { adapter, appIds, providerOptions } = + authenticationLoader.loadAuthAdapter("facebook", options); validateAuthenticationAdapter(adapter); - expect(appIds).toEqual(['a', 'b']); + expect(appIds).toEqual(["a", "b"]); expect(providerOptions).toEqual(options.facebook); }); - it('should handle Facebook appSecret for validating appIds', async () => { - const httpsRequest = require('../lib/Adapters/Auth/httpsRequest'); - spyOn(httpsRequest, 'get').and.callFake(() => { - return Promise.resolve({ id: 'a' }); + it("should handle Facebook appSecret for validating appIds", async () => { + const httpsRequest = require("../lib/Adapters/Auth/httpsRequest"); + spyOn(httpsRequest, "get").and.callFake(() => { + return Promise.resolve({ id: "a" }); }); const options = { facebook: { - appIds: ['a', 'b'], - appSecret: 'secret_sauce', + appIds: ["a", "b"], + appSecret: "secret_sauce", }, }; const authData = { - access_token: 'badtoken', + access_token: "badtoken", }; - const { adapter, appIds, providerOptions } = authenticationLoader.loadAuthAdapter( - 'facebook', - options - ); + const { adapter, appIds, providerOptions } = + authenticationLoader.loadAuthAdapter("facebook", options); await adapter.validateAppId(appIds, authData, providerOptions); - expect(httpsRequest.get.calls.first().args[0].includes('appsecret_proof')).toBe(true); + expect( + httpsRequest.get.calls.first().args[0].includes("appsecret_proof") + ).toBe(true); }); - it('should throw error when Facebook request appId is wrong data type', async () => { - const httpsRequest = require('../lib/Adapters/Auth/httpsRequest'); - spyOn(httpsRequest, 'get').and.callFake(() => { - return Promise.resolve({ id: 'a' }); + it("should throw error when Facebook request appId is wrong data type", async () => { + const httpsRequest = require("../lib/Adapters/Auth/httpsRequest"); + spyOn(httpsRequest, "get").and.callFake(() => { + return Promise.resolve({ id: "a" }); }); const options = { facebook: { - appIds: 'abcd', - appSecret: 'secret_sauce', + appIds: "abcd", + appSecret: "secret_sauce", }, }; const authData = { - access_token: 'badtoken', + access_token: "badtoken", }; - const { adapter, appIds, providerOptions } = authenticationLoader.loadAuthAdapter( - 'facebook', - options - ); - await expectAsync(adapter.validateAppId(appIds, authData, providerOptions)).toBeRejectedWith( - new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'appIds must be an array.') + const { adapter, appIds, providerOptions } = + authenticationLoader.loadAuthAdapter("facebook", options); + await expectAsync( + adapter.validateAppId(appIds, authData, providerOptions) + ).toBeRejectedWith( + new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, "appIds must be an array.") ); }); - it('should handle Facebook appSecret for validating auth data', async () => { - const httpsRequest = require('../lib/Adapters/Auth/httpsRequest'); - spyOn(httpsRequest, 'get').and.callFake(() => { + it("should handle Facebook appSecret for validating auth data", async () => { + const httpsRequest = require("../lib/Adapters/Auth/httpsRequest"); + spyOn(httpsRequest, "get").and.callFake(() => { return Promise.resolve(); }); const options = { facebook: { - appIds: ['a', 'b'], - appSecret: 'secret_sauce', + appIds: ["a", "b"], + appSecret: "secret_sauce", }, }; const authData = { - id: 'test', - access_token: 'test', + id: "test", + access_token: "test", }; - const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter('facebook', options); + const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter( + "facebook", + options + ); await adapter.validateAuthData(authData, providerOptions); - expect(httpsRequest.get.calls.first().args[0].includes('appsecret_proof')).toBe(true); + expect( + httpsRequest.get.calls.first().args[0].includes("appsecret_proof") + ).toBe(true); }); - it('properly loads a custom adapter with options', () => { + it("properly loads a custom adapter with options", () => { const options = { custom: { validateAppId: () => {}, validateAuthData: () => {}, - appIds: ['a', 'b'], + appIds: ["a", "b"], }, }; - const { adapter, appIds, providerOptions } = authenticationLoader.loadAuthAdapter( - 'custom', - options - ); + const { adapter, appIds, providerOptions } = + authenticationLoader.loadAuthAdapter("custom", options); validateAuthenticationAdapter(adapter); - expect(appIds).toEqual(['a', 'b']); + expect(appIds).toEqual(["a", "b"]); expect(providerOptions).toEqual(options.custom); }); - it('can disable provider', async () => { + it("can disable provider", async () => { await reconfigureServer({ auth: { myoauth: { enabled: false, - module: path.resolve(__dirname, 'support/myoauth'), // relative path as it's run from src + module: path.resolve(__dirname, "support/myoauth"), // relative path as it's run from src }, }, }); const provider = getMockMyOauthProvider(); Parse.User._registerAuthenticationProvider(provider); - await expectAsync(Parse.User._logInWith('myoauth')).toBeRejectedWith( - new Parse.Error(Parse.Error.UNSUPPORTED_SERVICE, 'This authentication method is unsupported.') + await expectAsync(Parse.User._logInWith("myoauth")).toBeRejectedWith( + new Parse.Error( + Parse.Error.UNSUPPORTED_SERVICE, + "This authentication method is unsupported." + ) ); }); }); -describe('google auth adapter', () => { - const google = require('../lib/Adapters/Auth/google'); - const jwt = require('jsonwebtoken'); - const authUtils = require('../lib/Adapters/Auth/utils'); +describe("google auth adapter", () => { + const google = require("../lib/Adapters/Auth/google"); + const jwt = require("jsonwebtoken"); + const authUtils = require("../lib/Adapters/Auth/utils"); - it('should throw error with missing id_token', async () => { + it("should throw error with missing id_token", async () => { try { await google.validateAuthData({}, {}); fail(); } catch (e) { - expect(e.message).toBe('id token is invalid for this user.'); + expect(e.message).toBe("id token is invalid for this user."); } }); - it('should not decode invalid id_token', async () => { + it("should not decode invalid id_token", async () => { try { - await google.validateAuthData({ id: 'the_user_id', id_token: 'the_token' }, {}); + await google.validateAuthData( + { id: "the_user_id", id_token: "the_token" }, + {} + ); fail(); } catch (e) { - expect(e.message).toBe('provided token does not decode as JWT'); + expect(e.message).toBe("provided token does not decode as JWT"); } }); @@ -514,362 +542,385 @@ describe('google auth adapter', () => { // } // }); - it('(using client id as string) should verify id_token (google.com)', async () => { + it("(using client id as string) should verify id_token (google.com)", async () => { const fakeClaim = { - iss: 'https://accounts.google.com', - aud: 'secret', + iss: "https://accounts.google.com", + aud: "secret", exp: Date.now(), - sub: 'the_user_id', + sub: "the_user_id", }; - const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); - spyOn(jwt, 'verify').and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; + spyOn(authUtils, "getHeaderFromToken").and.callFake(() => fakeDecodedToken); + spyOn(jwt, "verify").and.callFake(() => fakeClaim); const result = await google.validateAuthData( - { id: 'the_user_id', id_token: 'the_token' }, - { clientId: 'secret' } + { id: "the_user_id", id_token: "the_token" }, + { clientId: "secret" } ); expect(result).toEqual(fakeClaim); }); - it('(using client id as string) should throw error with with invalid jwt issuer (google.com)', async () => { + it("(using client id as string) should throw error with with invalid jwt issuer (google.com)", async () => { const fakeClaim = { - iss: 'https://not.google.com', - sub: 'the_user_id', + iss: "https://not.google.com", + sub: "the_user_id", }; - const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); - spyOn(jwt, 'verify').and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; + spyOn(authUtils, "getHeaderFromToken").and.callFake(() => fakeDecodedToken); + spyOn(jwt, "verify").and.callFake(() => fakeClaim); try { await google.validateAuthData( - { id: 'the_user_id', id_token: 'the_token' }, - { clientId: 'secret' } + { id: "the_user_id", id_token: "the_token" }, + { clientId: "secret" } ); fail(); } catch (e) { expect(e.message).toBe( - 'id token not issued by correct provider - expected: accounts.google.com or https://accounts.google.com | from: https://not.google.com' + "id token not issued by correct provider - expected: accounts.google.com or https://accounts.google.com | from: https://not.google.com" ); } }); - xit('(using client id as string) should throw error with invalid jwt client_id', async () => { + xit("(using client id as string) should throw error with invalid jwt client_id", async () => { const fakeClaim = { - iss: 'https://accounts.google.com', - aud: 'secret', + iss: "https://accounts.google.com", + aud: "secret", exp: Date.now(), - sub: 'the_user_id', + sub: "the_user_id", }; - const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); - spyOn(jwt, 'verify').and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; + spyOn(authUtils, "getHeaderFromToken").and.callFake(() => fakeDecodedToken); + spyOn(jwt, "verify").and.callFake(() => fakeClaim); try { await google.validateAuthData( - { id: 'INSERT ID HERE', token: 'INSERT APPLE TOKEN HERE' }, - { clientId: 'secret' } + { id: "INSERT ID HERE", token: "INSERT APPLE TOKEN HERE" }, + { clientId: "secret" } ); fail(); } catch (e) { - expect(e.message).toBe('jwt audience invalid. expected: secret'); + expect(e.message).toBe("jwt audience invalid. expected: secret"); } }); - xit('should throw error with invalid user id', async () => { + xit("should throw error with invalid user id", async () => { const fakeClaim = { - iss: 'https://accounts.google.com', - aud: 'secret', + iss: "https://accounts.google.com", + aud: "secret", exp: Date.now(), - sub: 'the_user_id', + sub: "the_user_id", }; - const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); - spyOn(jwt, 'verify').and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; + spyOn(authUtils, "getHeaderFromToken").and.callFake(() => fakeDecodedToken); + spyOn(jwt, "verify").and.callFake(() => fakeClaim); try { await google.validateAuthData( - { id: 'invalid user', token: 'INSERT APPLE TOKEN HERE' }, - { clientId: 'INSERT CLIENT ID HERE' } + { id: "invalid user", token: "INSERT APPLE TOKEN HERE" }, + { clientId: "INSERT CLIENT ID HERE" } ); fail(); } catch (e) { - expect(e.message).toBe('auth data is invalid for this user.'); + expect(e.message).toBe("auth data is invalid for this user."); } }); }); -describe('keycloak auth adapter', () => { - const keycloak = require('../lib/Adapters/Auth/keycloak'); - const httpsRequest = require('../lib/Adapters/Auth/httpsRequest'); +describe("keycloak auth adapter", () => { + const keycloak = require("../lib/Adapters/Auth/keycloak"); + const httpsRequest = require("../lib/Adapters/Auth/httpsRequest"); - it('validateAuthData should fail without access token', async () => { + it("validateAuthData should fail without access token", async () => { const authData = { - id: 'fakeid', + id: "fakeid", }; try { await keycloak.validateAuthData(authData); fail(); } catch (e) { - expect(e.message).toBe('Missing access token and/or User id'); + expect(e.message).toBe("Missing access token and/or User id"); } }); - it('validateAuthData should fail without user id', async () => { + it("validateAuthData should fail without user id", async () => { const authData = { - access_token: 'sometoken', + access_token: "sometoken", }; try { await keycloak.validateAuthData(authData); fail(); } catch (e) { - expect(e.message).toBe('Missing access token and/or User id'); + expect(e.message).toBe("Missing access token and/or User id"); } }); - it('validateAuthData should fail without config', async () => { + it("validateAuthData should fail without config", async () => { const options = { keycloak: { config: null, }, }; const authData = { - id: 'fakeid', - access_token: 'sometoken', + id: "fakeid", + access_token: "sometoken", }; - const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter('keycloak', options); + const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter( + "keycloak", + options + ); try { await adapter.validateAuthData(authData, providerOptions); fail(); } catch (e) { - expect(e.message).toBe('Missing keycloak configuration'); + expect(e.message).toBe("Missing keycloak configuration"); } }); - it('validateAuthData should fail connect error', async () => { - spyOn(httpsRequest, 'get').and.callFake(() => { + it("validateAuthData should fail connect error", async () => { + spyOn(httpsRequest, "get").and.callFake(() => { return Promise.reject({ - text: JSON.stringify({ error: 'hosting_error' }), + text: JSON.stringify({ error: "hosting_error" }), }); }); const options = { keycloak: { config: { - 'auth-server-url': 'http://example.com', - realm: 'new', + "auth-server-url": "http://example.com", + realm: "new", }, }, }; const authData = { - id: 'fakeid', - access_token: 'sometoken', + id: "fakeid", + access_token: "sometoken", }; - const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter('keycloak', options); + const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter( + "keycloak", + options + ); try { await adapter.validateAuthData(authData, providerOptions); fail(); } catch (e) { - expect(e.message).toBe('Could not connect to the authentication server'); + expect(e.message).toBe("Could not connect to the authentication server"); } }); - it('validateAuthData should fail with error description', async () => { - spyOn(httpsRequest, 'get').and.callFake(() => { + it("validateAuthData should fail with error description", async () => { + spyOn(httpsRequest, "get").and.callFake(() => { return Promise.reject({ - text: JSON.stringify({ error_description: 'custom error message' }), + text: JSON.stringify({ error_description: "custom error message" }), }); }); const options = { keycloak: { config: { - 'auth-server-url': 'http://example.com', - realm: 'new', + "auth-server-url": "http://example.com", + realm: "new", }, }, }; const authData = { - id: 'fakeid', - access_token: 'sometoken', + id: "fakeid", + access_token: "sometoken", }; - const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter('keycloak', options); + const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter( + "keycloak", + options + ); try { await adapter.validateAuthData(authData, providerOptions); fail(); } catch (e) { - expect(e.message).toBe('custom error message'); + expect(e.message).toBe("custom error message"); } }); - it('validateAuthData should fail with invalid auth', async () => { - spyOn(httpsRequest, 'get').and.callFake(() => { + it("validateAuthData should fail with invalid auth", async () => { + spyOn(httpsRequest, "get").and.callFake(() => { return Promise.resolve({}); }); const options = { keycloak: { config: { - 'auth-server-url': 'http://example.com', - realm: 'new', + "auth-server-url": "http://example.com", + realm: "new", }, }, }; const authData = { - id: 'fakeid', - access_token: 'sometoken', + id: "fakeid", + access_token: "sometoken", }; - const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter('keycloak', options); + const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter( + "keycloak", + options + ); try { await adapter.validateAuthData(authData, providerOptions); fail(); } catch (e) { - expect(e.message).toBe('Invalid authentication'); + expect(e.message).toBe("Invalid authentication"); } }); - it('validateAuthData should fail with invalid groups', async () => { - spyOn(httpsRequest, 'get').and.callFake(() => { + it("validateAuthData should fail with invalid groups", async () => { + spyOn(httpsRequest, "get").and.callFake(() => { return Promise.resolve({ data: { - sub: 'fakeid', - roles: ['role1'], - groups: ['unknown'], + sub: "fakeid", + roles: ["role1"], + groups: ["unknown"], }, }); }); const options = { keycloak: { config: { - 'auth-server-url': 'http://example.com', - realm: 'new', + "auth-server-url": "http://example.com", + realm: "new", }, }, }; const authData = { - id: 'fakeid', - access_token: 'sometoken', - roles: ['role1'], - groups: ['group1'], + id: "fakeid", + access_token: "sometoken", + roles: ["role1"], + groups: ["group1"], }; - const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter('keycloak', options); + const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter( + "keycloak", + options + ); try { await adapter.validateAuthData(authData, providerOptions); fail(); } catch (e) { - expect(e.message).toBe('Invalid authentication'); + expect(e.message).toBe("Invalid authentication"); } }); - it('validateAuthData should fail with invalid roles', async () => { - spyOn(httpsRequest, 'get').and.callFake(() => { + it("validateAuthData should fail with invalid roles", async () => { + spyOn(httpsRequest, "get").and.callFake(() => { return Promise.resolve({ data: { - sub: 'fakeid', - roles: 'unknown', - groups: ['group1'], + sub: "fakeid", + roles: "unknown", + groups: ["group1"], }, }); }); const options = { keycloak: { config: { - 'auth-server-url': 'http://example.com', - realm: 'new', + "auth-server-url": "http://example.com", + realm: "new", }, }, }; const authData = { - id: 'fakeid', - access_token: 'sometoken', - roles: ['role1'], - groups: ['group1'], + id: "fakeid", + access_token: "sometoken", + roles: ["role1"], + groups: ["group1"], }; - const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter('keycloak', options); + const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter( + "keycloak", + options + ); try { await adapter.validateAuthData(authData, providerOptions); fail(); } catch (e) { - expect(e.message).toBe('Invalid authentication'); + expect(e.message).toBe("Invalid authentication"); } }); - it('validateAuthData should handle authentication', async () => { - spyOn(httpsRequest, 'get').and.callFake(() => { + it("validateAuthData should handle authentication", async () => { + spyOn(httpsRequest, "get").and.callFake(() => { return Promise.resolve({ data: { - sub: 'fakeid', - roles: ['role1'], - groups: ['group1'], + sub: "fakeid", + roles: ["role1"], + groups: ["group1"], }, }); }); const options = { keycloak: { config: { - 'auth-server-url': 'http://example.com', - realm: 'new', + "auth-server-url": "http://example.com", + realm: "new", }, }, }; const authData = { - id: 'fakeid', - access_token: 'sometoken', - roles: ['role1'], - groups: ['group1'], + id: "fakeid", + access_token: "sometoken", + roles: ["role1"], + groups: ["group1"], }; - const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter('keycloak', options); + const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter( + "keycloak", + options + ); await adapter.validateAuthData(authData, providerOptions); expect(httpsRequest.get).toHaveBeenCalledWith({ - host: 'http://example.com', - path: '/realms/new/protocol/openid-connect/userinfo', + host: "http://example.com", + path: "/realms/new/protocol/openid-connect/userinfo", headers: { - Authorization: 'Bearer sometoken', + Authorization: "Bearer sometoken", }, }); }); }); -describe('apple signin auth adapter', () => { - const apple = require('../lib/Adapters/Auth/apple'); - const jwt = require('jsonwebtoken'); - const authUtils = require('../lib/Adapters/Auth/utils'); +describe("apple signin auth adapter", () => { + const apple = require("../lib/Adapters/Auth/apple"); + const jwt = require("jsonwebtoken"); + const authUtils = require("../lib/Adapters/Auth/utils"); - it('(using client id as string) should throw error with missing id_token', async () => { + it("(using client id as string) should throw error with missing id_token", async () => { try { - await apple.validateAuthData({}, { clientId: 'secret' }); + await apple.validateAuthData({}, { clientId: "secret" }); fail(); } catch (e) { - expect(e.message).toBe('id token is invalid for this user.'); + expect(e.message).toBe("id token is invalid for this user."); } }); - it('(using client id as array) should throw error with missing id_token', async () => { + it("(using client id as array) should throw error with missing id_token", async () => { try { - await apple.validateAuthData({}, { clientId: ['secret'] }); + await apple.validateAuthData({}, { clientId: ["secret"] }); fail(); } catch (e) { - expect(e.message).toBe('id token is invalid for this user.'); + expect(e.message).toBe("id token is invalid for this user."); } }); - it('should not decode invalid id_token', async () => { + it("should not decode invalid id_token", async () => { try { await apple.validateAuthData( - { id: 'the_user_id', token: 'the_token' }, - { clientId: 'secret' } + { id: "the_user_id", token: "the_token" }, + { clientId: "secret" } ); fail(); } catch (e) { - expect(e.message).toBe('provided token does not decode as JWT'); + expect(e.message).toBe("provided token does not decode as JWT"); } }); - it('should throw error if public key used to encode token is not available', async () => { - const fakeDecodedToken = { header: { kid: '789', alg: 'RS256' } }; + it("should throw error if public key used to encode token is not available", async () => { + const fakeDecodedToken = { header: { kid: "789", alg: "RS256" } }; try { - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken.header); + spyOn(authUtils, "getHeaderFromToken").and.callFake( + () => fakeDecodedToken.header + ); await apple.validateAuthData( - { id: 'the_user_id', token: 'the_token' }, - { clientId: 'secret' } + { id: "the_user_id", token: "the_token" }, + { clientId: "secret" } ); fail(); } catch (e) { @@ -879,266 +930,270 @@ describe('apple signin auth adapter', () => { } }); - it('should use algorithm from key header to verify id_token (apple.com)', async () => { + it("should use algorithm from key header to verify id_token (apple.com)", async () => { const fakeClaim = { - iss: 'https://appleid.apple.com', - aud: 'secret', + iss: "https://appleid.apple.com", + aud: "secret", exp: Date.now(), - sub: 'the_user_id', + sub: "the_user_id", }; - const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; - const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken.header); - spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); - spyOn(jwt, 'verify').and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; + const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; + spyOn(authUtils, "getHeaderFromToken").and.callFake( + () => fakeDecodedToken.header + ); + spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); + spyOn(jwt, "verify").and.callFake(() => fakeClaim); const result = await apple.validateAuthData( - { id: 'the_user_id', token: 'the_token' }, - { clientId: 'secret' } + { id: "the_user_id", token: "the_token" }, + { clientId: "secret" } ); expect(result).toEqual(fakeClaim); - expect(jwt.verify.calls.first().args[2].algorithms).toEqual(fakeDecodedToken.header.alg); + expect(jwt.verify.calls.first().args[2].algorithms).toEqual( + fakeDecodedToken.header.alg + ); }); - it('should not verify invalid id_token', async () => { - const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; - const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); - spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); + it("should not verify invalid id_token", async () => { + const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; + const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; + spyOn(authUtils, "getHeaderFromToken").and.callFake(() => fakeDecodedToken); + spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); try { await apple.validateAuthData( - { id: 'the_user_id', token: 'the_token' }, - { clientId: 'secret' } + { id: "the_user_id", token: "the_token" }, + { clientId: "secret" } ); fail(); } catch (e) { - expect(e.message).toBe('jwt malformed'); + expect(e.message).toBe("jwt malformed"); } }); - it('(using client id as array) should not verify invalid id_token', async () => { + it("(using client id as array) should not verify invalid id_token", async () => { try { await apple.validateAuthData( - { id: 'the_user_id', token: 'the_token' }, - { clientId: ['secret'] } + { id: "the_user_id", token: "the_token" }, + { clientId: ["secret"] } ); fail(); } catch (e) { - expect(e.message).toBe('provided token does not decode as JWT'); + expect(e.message).toBe("provided token does not decode as JWT"); } }); - it('(using client id as string) should verify id_token (apple.com)', async () => { + it("(using client id as string) should verify id_token (apple.com)", async () => { const fakeClaim = { - iss: 'https://appleid.apple.com', - aud: 'secret', + iss: "https://appleid.apple.com", + aud: "secret", exp: Date.now(), - sub: 'the_user_id', + sub: "the_user_id", }; - const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; - const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); - spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); - spyOn(jwt, 'verify').and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; + const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; + spyOn(authUtils, "getHeaderFromToken").and.callFake(() => fakeDecodedToken); + spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); + spyOn(jwt, "verify").and.callFake(() => fakeClaim); const result = await apple.validateAuthData( - { id: 'the_user_id', token: 'the_token' }, - { clientId: 'secret' } + { id: "the_user_id", token: "the_token" }, + { clientId: "secret" } ); expect(result).toEqual(fakeClaim); }); - it('(using client id as array) should verify id_token (apple.com)', async () => { + it("(using client id as array) should verify id_token (apple.com)", async () => { const fakeClaim = { - iss: 'https://appleid.apple.com', - aud: 'secret', + iss: "https://appleid.apple.com", + aud: "secret", exp: Date.now(), - sub: 'the_user_id', + sub: "the_user_id", }; - const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; - const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); - spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); - spyOn(jwt, 'verify').and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; + const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; + spyOn(authUtils, "getHeaderFromToken").and.callFake(() => fakeDecodedToken); + spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); + spyOn(jwt, "verify").and.callFake(() => fakeClaim); const result = await apple.validateAuthData( - { id: 'the_user_id', token: 'the_token' }, - { clientId: ['secret'] } + { id: "the_user_id", token: "the_token" }, + { clientId: ["secret"] } ); expect(result).toEqual(fakeClaim); }); - it('(using client id as array with multiple items) should verify id_token (apple.com)', async () => { + it("(using client id as array with multiple items) should verify id_token (apple.com)", async () => { const fakeClaim = { - iss: 'https://appleid.apple.com', - aud: 'secret', + iss: "https://appleid.apple.com", + aud: "secret", exp: Date.now(), - sub: 'the_user_id', + sub: "the_user_id", }; - const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; - const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); - spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); - spyOn(jwt, 'verify').and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; + const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; + spyOn(authUtils, "getHeaderFromToken").and.callFake(() => fakeDecodedToken); + spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); + spyOn(jwt, "verify").and.callFake(() => fakeClaim); const result = await apple.validateAuthData( - { id: 'the_user_id', token: 'the_token' }, - { clientId: ['secret', 'secret 123'] } + { id: "the_user_id", token: "the_token" }, + { clientId: ["secret", "secret 123"] } ); expect(result).toEqual(fakeClaim); }); - it('(using client id as string) should throw error with with invalid jwt issuer (apple.com)', async () => { + it("(using client id as string) should throw error with with invalid jwt issuer (apple.com)", async () => { const fakeClaim = { - iss: 'https://not.apple.com', - sub: 'the_user_id', + iss: "https://not.apple.com", + sub: "the_user_id", }; - const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; - const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); - spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); - spyOn(jwt, 'verify').and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; + const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; + spyOn(authUtils, "getHeaderFromToken").and.callFake(() => fakeDecodedToken); + spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); + spyOn(jwt, "verify").and.callFake(() => fakeClaim); try { await apple.validateAuthData( - { id: 'the_user_id', token: 'the_token' }, - { clientId: 'secret' } + { id: "the_user_id", token: "the_token" }, + { clientId: "secret" } ); fail(); } catch (e) { expect(e.message).toBe( - 'id token not issued by correct OpenID provider - expected: https://appleid.apple.com | from: https://not.apple.com' + "id token not issued by correct OpenID provider - expected: https://appleid.apple.com | from: https://not.apple.com" ); } }); // TODO: figure out a way to generate our own apple signed tokens, perhaps with a parse apple account // and a private key - xit('(using client id as array) should throw error with with invalid jwt issuer', async () => { + xit("(using client id as array) should throw error with with invalid jwt issuer", async () => { const fakeClaim = { - iss: 'https://not.apple.com', - sub: 'the_user_id', + iss: "https://not.apple.com", + sub: "the_user_id", }; - const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; - const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); - spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); - spyOn(jwt, 'verify').and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; + const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; + spyOn(authUtils, "getHeaderFromToken").and.callFake(() => fakeDecodedToken); + spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); + spyOn(jwt, "verify").and.callFake(() => fakeClaim); try { await apple.validateAuthData( { - id: 'INSERT ID HERE', - token: 'INSERT APPLE TOKEN HERE WITH INVALID JWT ISSUER', + id: "INSERT ID HERE", + token: "INSERT APPLE TOKEN HERE WITH INVALID JWT ISSUER", }, - { clientId: ['INSERT CLIENT ID HERE'] } + { clientId: ["INSERT CLIENT ID HERE"] } ); fail(); } catch (e) { expect(e.message).toBe( - 'id token not issued by correct OpenID provider - expected: https://appleid.apple.com | from: https://not.apple.com' + "id token not issued by correct OpenID provider - expected: https://appleid.apple.com | from: https://not.apple.com" ); } }); - it('(using client id as string) should throw error with with invalid jwt issuer with token (apple.com)', async () => { + it("(using client id as string) should throw error with with invalid jwt issuer with token (apple.com)", async () => { const fakeClaim = { - iss: 'https://not.apple.com', - sub: 'the_user_id', + iss: "https://not.apple.com", + sub: "the_user_id", }; - const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; - const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); - spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); - spyOn(jwt, 'verify').and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; + const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; + spyOn(authUtils, "getHeaderFromToken").and.callFake(() => fakeDecodedToken); + spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); + spyOn(jwt, "verify").and.callFake(() => fakeClaim); try { await apple.validateAuthData( { - id: 'INSERT ID HERE', - token: 'INSERT APPLE TOKEN HERE WITH INVALID JWT ISSUER', + id: "INSERT ID HERE", + token: "INSERT APPLE TOKEN HERE WITH INVALID JWT ISSUER", }, - { clientId: 'INSERT CLIENT ID HERE' } + { clientId: "INSERT CLIENT ID HERE" } ); fail(); } catch (e) { expect(e.message).toBe( - 'id token not issued by correct OpenID provider - expected: https://appleid.apple.com | from: https://not.apple.com' + "id token not issued by correct OpenID provider - expected: https://appleid.apple.com | from: https://not.apple.com" ); } }); // TODO: figure out a way to generate our own apple signed tokens, perhaps with a parse apple account // and a private key - xit('(using client id as string) should throw error with invalid jwt clientId', async () => { + xit("(using client id as string) should throw error with invalid jwt clientId", async () => { try { await apple.validateAuthData( - { id: 'INSERT ID HERE', token: 'INSERT APPLE TOKEN HERE' }, - { clientId: 'secret' } + { id: "INSERT ID HERE", token: "INSERT APPLE TOKEN HERE" }, + { clientId: "secret" } ); fail(); } catch (e) { - expect(e.message).toBe('jwt audience invalid. expected: secret'); + expect(e.message).toBe("jwt audience invalid. expected: secret"); } }); // TODO: figure out a way to generate our own apple signed tokens, perhaps with a parse apple account // and a private key - xit('(using client id as array) should throw error with invalid jwt clientId', async () => { + xit("(using client id as array) should throw error with invalid jwt clientId", async () => { try { await apple.validateAuthData( - { id: 'INSERT ID HERE', token: 'INSERT APPLE TOKEN HERE' }, - { clientId: ['secret'] } + { id: "INSERT ID HERE", token: "INSERT APPLE TOKEN HERE" }, + { clientId: ["secret"] } ); fail(); } catch (e) { - expect(e.message).toBe('jwt audience invalid. expected: secret'); + expect(e.message).toBe("jwt audience invalid. expected: secret"); } }); // TODO: figure out a way to generate our own apple signed tokens, perhaps with a parse apple account // and a private key - xit('should throw error with invalid user id', async () => { + xit("should throw error with invalid user id", async () => { try { await apple.validateAuthData( - { id: 'invalid user', token: 'INSERT APPLE TOKEN HERE' }, - { clientId: 'INSERT CLIENT ID HERE' } + { id: "invalid user", token: "INSERT APPLE TOKEN HERE" }, + { clientId: "INSERT CLIENT ID HERE" } ); fail(); } catch (e) { - expect(e.message).toBe('auth data is invalid for this user.'); + expect(e.message).toBe("auth data is invalid for this user."); } }); - it('should throw error with with invalid user id (apple.com)', async () => { + it("should throw error with with invalid user id (apple.com)", async () => { const fakeClaim = { - iss: 'https://appleid.apple.com', - aud: 'invalid_client_id', - sub: 'a_different_user_id', + iss: "https://appleid.apple.com", + aud: "invalid_client_id", + sub: "a_different_user_id", }; - const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; - const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); - spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); - spyOn(jwt, 'verify').and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; + const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; + spyOn(authUtils, "getHeaderFromToken").and.callFake(() => fakeDecodedToken); + spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); + spyOn(jwt, "verify").and.callFake(() => fakeClaim); try { await apple.validateAuthData( - { id: 'the_user_id', token: 'the_token' }, - { clientId: 'secret' } + { id: "the_user_id", token: "the_token" }, + { clientId: "secret" } ); fail(); } catch (e) { - expect(e.message).toBe('auth data is invalid for this user.'); + expect(e.message).toBe("auth data is invalid for this user."); } }); }); -describe('phant auth adapter', () => { - const httpsRequest = require('../lib/Adapters/Auth/httpsRequest'); +describe("phant auth adapter", () => { + const httpsRequest = require("../lib/Adapters/Auth/httpsRequest"); - it('validateAuthData should throw for invalid auth', async () => { + it("validateAuthData should throw for invalid auth", async () => { await reconfigureServer({ auth: { phantauth: { @@ -1147,68 +1202,72 @@ describe('phant auth adapter', () => { }, }); const authData = { - id: 'fakeid', - access_token: 'sometoken', + id: "fakeid", + access_token: "sometoken", }; - const { adapter } = authenticationLoader.loadAuthAdapter('phantauth', {}); + const { adapter } = authenticationLoader.loadAuthAdapter("phantauth", {}); - spyOn(httpsRequest, 'get').and.callFake(() => Promise.resolve({ sub: 'invalidID' })); + spyOn(httpsRequest, "get").and.callFake(() => + Promise.resolve({ sub: "invalidID" }) + ); try { await adapter.validateAuthData(authData); fail(); } catch (e) { - expect(e.message).toBe('PhantAuth auth is invalid for this user.'); + expect(e.message).toBe("PhantAuth auth is invalid for this user."); } }); }); -describe('facebook limited auth adapter', () => { - const facebook = require('../lib/Adapters/Auth/facebook'); - const jwt = require('jsonwebtoken'); - const authUtils = require('../lib/Adapters/Auth/utils'); +describe("facebook limited auth adapter", () => { + const facebook = require("../lib/Adapters/Auth/facebook"); + const jwt = require("jsonwebtoken"); + const authUtils = require("../lib/Adapters/Auth/utils"); // TODO: figure out a way to run this test alongside facebook classic tests - xit('(using client id as string) should throw error with missing id_token', async () => { + xit("(using client id as string) should throw error with missing id_token", async () => { try { - await facebook.validateAuthData({}, { clientId: 'secret' }); + await facebook.validateAuthData({}, { clientId: "secret" }); fail(); } catch (e) { - expect(e.message).toBe('Facebook auth is not configured.'); + expect(e.message).toBe("Facebook auth is not configured."); } }); // TODO: figure out a way to run this test alongside facebook classic tests - xit('(using client id as array) should throw error with missing id_token', async () => { + xit("(using client id as array) should throw error with missing id_token", async () => { try { - await facebook.validateAuthData({}, { clientId: ['secret'] }); + await facebook.validateAuthData({}, { clientId: ["secret"] }); fail(); } catch (e) { - expect(e.message).toBe('Facebook auth is not configured.'); + expect(e.message).toBe("Facebook auth is not configured."); } }); - it('should not decode invalid id_token', async () => { + it("should not decode invalid id_token", async () => { try { await facebook.validateAuthData( - { id: 'the_user_id', token: 'the_token' }, - { clientId: 'secret' } + { id: "the_user_id", token: "the_token" }, + { clientId: "secret" } ); fail(); } catch (e) { - expect(e.message).toBe('provided token does not decode as JWT'); + expect(e.message).toBe("provided token does not decode as JWT"); } }); - it('should throw error if public key used to encode token is not available', async () => { + it("should throw error if public key used to encode token is not available", async () => { const fakeDecodedToken = { - header: { kid: '789', alg: 'RS256' }, + header: { kid: "789", alg: "RS256" }, }; try { - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken.header); + spyOn(authUtils, "getHeaderFromToken").and.callFake( + () => fakeDecodedToken.header + ); await facebook.validateAuthData( - { id: 'the_user_id', token: 'the_token' }, - { clientId: 'secret' } + { id: "the_user_id", token: "the_token" }, + { clientId: "secret" } ); fail(); } catch (e) { @@ -1218,150 +1277,162 @@ describe('facebook limited auth adapter', () => { } }); - it_id('7bfa55ab-8fd7-4526-992e-6de3df16bf9c')(it)( - 'should use algorithm from key header to verify id_token (facebook.com)', + it_id("7bfa55ab-8fd7-4526-992e-6de3df16bf9c")(it)( + "should use algorithm from key header to verify id_token (facebook.com)", async () => { const fakeClaim = { - iss: 'https://www.facebook.com', - aud: 'secret', + iss: "https://www.facebook.com", + aud: "secret", exp: Date.now(), - sub: 'the_user_id', + sub: "the_user_id", }; - const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; - const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken.header); - spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); - spyOn(jwt, 'verify').and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; + const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; + spyOn(authUtils, "getHeaderFromToken").and.callFake( + () => fakeDecodedToken.header + ); + spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); + spyOn(jwt, "verify").and.callFake(() => fakeClaim); const result = await facebook.validateAuthData( - { id: 'the_user_id', token: 'the_token' }, - { clientId: 'secret' } + { id: "the_user_id", token: "the_token" }, + { clientId: "secret" } ); expect(result).toEqual(fakeClaim); - expect(jwt.verify.calls.first().args[2].algorithms).toEqual(fakeDecodedToken.header.alg); + expect(jwt.verify.calls.first().args[2].algorithms).toEqual( + fakeDecodedToken.header.alg + ); } ); - it('should not verify invalid id_token', async () => { - const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; - const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); - spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); + it("should not verify invalid id_token", async () => { + const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; + const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; + spyOn(authUtils, "getHeaderFromToken").and.callFake(() => fakeDecodedToken); + spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); try { await facebook.validateAuthData( - { id: 'the_user_id', token: 'the_token' }, - { clientId: 'secret' } + { id: "the_user_id", token: "the_token" }, + { clientId: "secret" } ); fail(); } catch (e) { - expect(e.message).toBe('jwt malformed'); + expect(e.message).toBe("jwt malformed"); } }); - it('(using client id as array) should not verify invalid id_token', async () => { + it("(using client id as array) should not verify invalid id_token", async () => { try { await facebook.validateAuthData( - { id: 'the_user_id', token: 'the_token' }, - { clientId: ['secret'] } + { id: "the_user_id", token: "the_token" }, + { clientId: ["secret"] } ); fail(); } catch (e) { - expect(e.message).toBe('provided token does not decode as JWT'); + expect(e.message).toBe("provided token does not decode as JWT"); } }); - it_id('4bcb1a1a-11f8-4e12-a3f6-73f7e25e355a')(it)( - 'using client id as string) should verify id_token (facebook.com)', + it_id("4bcb1a1a-11f8-4e12-a3f6-73f7e25e355a")(it)( + "using client id as string) should verify id_token (facebook.com)", async () => { const fakeClaim = { - iss: 'https://www.facebook.com', - aud: 'secret', + iss: "https://www.facebook.com", + aud: "secret", exp: Date.now(), - sub: 'the_user_id', + sub: "the_user_id", }; - const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; - const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); - spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); - spyOn(jwt, 'verify').and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; + const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; + spyOn(authUtils, "getHeaderFromToken").and.callFake( + () => fakeDecodedToken + ); + spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); + spyOn(jwt, "verify").and.callFake(() => fakeClaim); const result = await facebook.validateAuthData( - { id: 'the_user_id', token: 'the_token' }, - { clientId: 'secret' } + { id: "the_user_id", token: "the_token" }, + { clientId: "secret" } ); expect(result).toEqual(fakeClaim); } ); - it_id('c521a272-2ac2-4d8b-b5ed-ea250336d8b1')(it)( - '(using client id as array) should verify id_token (facebook.com)', + it_id("c521a272-2ac2-4d8b-b5ed-ea250336d8b1")(it)( + "(using client id as array) should verify id_token (facebook.com)", async () => { const fakeClaim = { - iss: 'https://www.facebook.com', - aud: 'secret', + iss: "https://www.facebook.com", + aud: "secret", exp: Date.now(), - sub: 'the_user_id', + sub: "the_user_id", }; - const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; - const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); - spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); - spyOn(jwt, 'verify').and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; + const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; + spyOn(authUtils, "getHeaderFromToken").and.callFake( + () => fakeDecodedToken + ); + spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); + spyOn(jwt, "verify").and.callFake(() => fakeClaim); const result = await facebook.validateAuthData( - { id: 'the_user_id', token: 'the_token' }, - { clientId: ['secret'] } + { id: "the_user_id", token: "the_token" }, + { clientId: ["secret"] } ); expect(result).toEqual(fakeClaim); } ); - it_id('e3f16404-18e9-4a87-a555-4710cfbdac67')(it)( - '(using client id as array with multiple items) should verify id_token (facebook.com)', + it_id("e3f16404-18e9-4a87-a555-4710cfbdac67")(it)( + "(using client id as array with multiple items) should verify id_token (facebook.com)", async () => { const fakeClaim = { - iss: 'https://www.facebook.com', - aud: 'secret', + iss: "https://www.facebook.com", + aud: "secret", exp: Date.now(), - sub: 'the_user_id', + sub: "the_user_id", }; - const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; - const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); - spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); - spyOn(jwt, 'verify').and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; + const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; + spyOn(authUtils, "getHeaderFromToken").and.callFake( + () => fakeDecodedToken + ); + spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); + spyOn(jwt, "verify").and.callFake(() => fakeClaim); const result = await facebook.validateAuthData( - { id: 'the_user_id', token: 'the_token' }, - { clientId: ['secret', 'secret 123'] } + { id: "the_user_id", token: "the_token" }, + { clientId: ["secret", "secret 123"] } ); expect(result).toEqual(fakeClaim); } ); - it_id('549c33a1-3a6b-4732-8cf6-8f010ad4569c')(it)( - '(using client id as string) should throw error with with invalid jwt issuer (facebook.com)', + it_id("549c33a1-3a6b-4732-8cf6-8f010ad4569c")(it)( + "(using client id as string) should throw error with with invalid jwt issuer (facebook.com)", async () => { const fakeClaim = { - iss: 'https://not.facebook.com', - sub: 'the_user_id', + iss: "https://not.facebook.com", + sub: "the_user_id", }; - const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; - const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); - spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); - spyOn(jwt, 'verify').and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; + const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; + spyOn(authUtils, "getHeaderFromToken").and.callFake( + () => fakeDecodedToken + ); + spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); + spyOn(jwt, "verify").and.callFake(() => fakeClaim); try { await facebook.validateAuthData( - { id: 'the_user_id', token: 'the_token' }, - { clientId: 'secret' } + { id: "the_user_id", token: "the_token" }, + { clientId: "secret" } ); fail(); } catch (e) { expect(e.message).toBe( - 'id token not issued by correct OpenID provider - expected: https://www.facebook.com | from: https://not.facebook.com' + "id token not issued by correct OpenID provider - expected: https://www.facebook.com | from: https://not.facebook.com" ); } } @@ -1369,151 +1440,153 @@ describe('facebook limited auth adapter', () => { // TODO: figure out a way to generate our own facebook signed tokens, perhaps with a parse facebook account // and a private key - xit('(using client id as array) should throw error with with invalid jwt issuer', async () => { + xit("(using client id as array) should throw error with with invalid jwt issuer", async () => { const fakeClaim = { - iss: 'https://not.facebook.com', - sub: 'the_user_id', + iss: "https://not.facebook.com", + sub: "the_user_id", }; - const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; - const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); - spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); - spyOn(jwt, 'verify').and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; + const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; + spyOn(authUtils, "getHeaderFromToken").and.callFake(() => fakeDecodedToken); + spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); + spyOn(jwt, "verify").and.callFake(() => fakeClaim); try { await facebook.validateAuthData( { - id: 'INSERT ID HERE', - token: 'INSERT FACEBOOK TOKEN HERE WITH INVALID JWT ISSUER', + id: "INSERT ID HERE", + token: "INSERT FACEBOOK TOKEN HERE WITH INVALID JWT ISSUER", }, - { clientId: ['INSERT CLIENT ID HERE'] } + { clientId: ["INSERT CLIENT ID HERE"] } ); fail(); } catch (e) { expect(e.message).toBe( - 'id token not issued by correct OpenID provider - expected: https://www.facebook.com | from: https://not.facebook.com' + "id token not issued by correct OpenID provider - expected: https://www.facebook.com | from: https://not.facebook.com" ); } }); - it('(using client id as string) with token', async () => { + it("(using client id as string) with token", async () => { const fakeClaim = { - iss: 'https://not.facebook.com', - sub: 'the_user_id', + iss: "https://not.facebook.com", + sub: "the_user_id", }; - const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; - const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); - spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); - spyOn(jwt, 'verify').and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; + const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; + spyOn(authUtils, "getHeaderFromToken").and.callFake(() => fakeDecodedToken); + spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); + spyOn(jwt, "verify").and.callFake(() => fakeClaim); try { await facebook.validateAuthData( { - id: 'INSERT ID HERE', - token: 'INSERT FACEBOOK TOKEN HERE WITH INVALID JWT ISSUER', + id: "INSERT ID HERE", + token: "INSERT FACEBOOK TOKEN HERE WITH INVALID JWT ISSUER", }, - { clientId: 'INSERT CLIENT ID HERE' } + { clientId: "INSERT CLIENT ID HERE" } ); fail(); } catch (e) { expect(e.message).toBe( - 'id token not issued by correct OpenID provider - expected: https://www.facebook.com | from: https://not.facebook.com' + "id token not issued by correct OpenID provider - expected: https://www.facebook.com | from: https://not.facebook.com" ); } }); // TODO: figure out a way to generate our own facebook signed tokens, perhaps with a parse facebook account // and a private key - xit('(using client id as string) should throw error with invalid jwt clientId', async () => { + xit("(using client id as string) should throw error with invalid jwt clientId", async () => { try { await facebook.validateAuthData( { - id: 'INSERT ID HERE', - token: 'INSERT FACEBOOK TOKEN HERE', + id: "INSERT ID HERE", + token: "INSERT FACEBOOK TOKEN HERE", }, - { clientId: 'secret' } + { clientId: "secret" } ); fail(); } catch (e) { - expect(e.message).toBe('jwt audience invalid. expected: secret'); + expect(e.message).toBe("jwt audience invalid. expected: secret"); } }); // TODO: figure out a way to generate our own facebook signed tokens, perhaps with a parse facebook account // and a private key - xit('(using client id as array) should throw error with invalid jwt clientId', async () => { + xit("(using client id as array) should throw error with invalid jwt clientId", async () => { try { await facebook.validateAuthData( { - id: 'INSERT ID HERE', - token: 'INSERT FACEBOOK TOKEN HERE', + id: "INSERT ID HERE", + token: "INSERT FACEBOOK TOKEN HERE", }, - { clientId: ['secret'] } + { clientId: ["secret"] } ); fail(); } catch (e) { - expect(e.message).toBe('jwt audience invalid. expected: secret'); + expect(e.message).toBe("jwt audience invalid. expected: secret"); } }); // TODO: figure out a way to generate our own facebook signed tokens, perhaps with a parse facebook account // and a private key - xit('should throw error with invalid user id', async () => { + xit("should throw error with invalid user id", async () => { try { await facebook.validateAuthData( { - id: 'invalid user', - token: 'INSERT FACEBOOK TOKEN HERE', + id: "invalid user", + token: "INSERT FACEBOOK TOKEN HERE", }, - { clientId: 'INSERT CLIENT ID HERE' } + { clientId: "INSERT CLIENT ID HERE" } ); fail(); } catch (e) { - expect(e.message).toBe('auth data is invalid for this user.'); + expect(e.message).toBe("auth data is invalid for this user."); } }); - it_id('c194d902-e697-46c9-a303-82c2d914473c')(it)( - 'should throw error with with invalid user id (facebook.com)', + it_id("c194d902-e697-46c9-a303-82c2d914473c")(it)( + "should throw error with with invalid user id (facebook.com)", async () => { const fakeClaim = { - iss: 'https://www.facebook.com', - aud: 'invalid_client_id', - sub: 'a_different_user_id', + iss: "https://www.facebook.com", + aud: "invalid_client_id", + sub: "a_different_user_id", }; - const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; - const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); - spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); - spyOn(jwt, 'verify').and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; + const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; + spyOn(authUtils, "getHeaderFromToken").and.callFake( + () => fakeDecodedToken + ); + spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); + spyOn(jwt, "verify").and.callFake(() => fakeClaim); try { await facebook.validateAuthData( - { id: 'the_user_id', token: 'the_token' }, - { clientId: 'secret' } + { id: "the_user_id", token: "the_token" }, + { clientId: "secret" } ); fail(); } catch (e) { - expect(e.message).toBe('auth data is invalid for this user.'); + expect(e.message).toBe("auth data is invalid for this user."); } } ); }); -describe('OTP TOTP auth adatper', () => { +describe("OTP TOTP auth adatper", () => { const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; beforeEach(async () => { await reconfigureServer({ auth: { mfa: { enabled: true, - options: ['TOTP'], - algorithm: 'SHA1', + options: ["TOTP"], + algorithm: "SHA1", digits: 6, period: 30, }, @@ -1521,12 +1594,12 @@ describe('OTP TOTP auth adatper', () => { }); }); - it('can enroll', async () => { - const user = await Parse.User.signUp('username', 'password'); - const OTPAuth = require('otpauth'); + it("can enroll", async () => { + const user = await Parse.User.signUp("username", "password"); + const OTPAuth = require("otpauth"); const secret = new OTPAuth.Secret(); const totp = new OTPAuth.TOTP({ - algorithm: 'SHA1', + algorithm: "SHA1", digits: 6, period: 30, secret, @@ -1536,20 +1609,20 @@ describe('OTP TOTP auth adatper', () => { { authData: { mfa: { secret: secret.base32, token } } }, { sessionToken: user.getSessionToken() } ); - const response = user.get('authDataResponse'); + const response = user.get("authDataResponse"); expect(response.mfa).toBeDefined(); expect(response.mfa.recovery).toBeDefined(); - expect(response.mfa.recovery.split(',').length).toEqual(2); + expect(response.mfa.recovery.split(",").length).toEqual(2); await user.fetch(); - expect(user.get('authData').mfa).toEqual({ status: 'enabled' }); + expect(user.get("authData").mfa).toEqual({ status: "enabled" }); }); - it('can login with valid token', async () => { - const user = await Parse.User.signUp('username', 'password'); - const OTPAuth = require('otpauth'); + it("can login with valid token", async () => { + const user = await Parse.User.signUp("username", "password"); + const OTPAuth = require("otpauth"); const secret = new OTPAuth.Secret(); const totp = new OTPAuth.TOTP({ - algorithm: 'SHA1', + algorithm: "SHA1", digits: 6, period: 30, secret, @@ -1561,11 +1634,11 @@ describe('OTP TOTP auth adatper', () => { ); const response = await request({ headers, - method: 'POST', - url: 'http://localhost:8378/1/login', + method: "POST", + url: "http://localhost:8378/1/login", body: JSON.stringify({ - username: 'username', - password: 'password', + username: "username", + password: "password", authData: { mfa: { token: totp.generate(), @@ -1575,27 +1648,27 @@ describe('OTP TOTP auth adatper', () => { }).then(res => res.data); expect(response.objectId).toEqual(user.id); expect(response.sessionToken).toBeDefined(); - expect(response.authData).toEqual({ mfa: { status: 'enabled' } }); + expect(response.authData).toEqual({ mfa: { status: "enabled" } }); expect(Object.keys(response).sort()).toEqual( [ - 'objectId', - 'username', - 'createdAt', - 'updatedAt', - 'authData', - 'ACL', - 'sessionToken', - 'authDataResponse', + "objectId", + "username", + "createdAt", + "updatedAt", + "authData", + "ACL", + "sessionToken", + "authDataResponse", ].sort() ); }); - it('can change OTP with valid token', async () => { - const user = await Parse.User.signUp('username', 'password'); - const OTPAuth = require('otpauth'); + it("can change OTP with valid token", async () => { + const user = await Parse.User.signUp("username", "password"); + const OTPAuth = require("otpauth"); const secret = new OTPAuth.Secret(); const totp = new OTPAuth.TOTP({ - algorithm: 'SHA1', + algorithm: "SHA1", digits: 6, period: 30, secret, @@ -1608,7 +1681,7 @@ describe('OTP TOTP auth adatper', () => { const new_secret = new OTPAuth.Secret(); const new_totp = new OTPAuth.TOTP({ - algorithm: 'SHA1', + algorithm: "SHA1", digits: 6, period: 30, secret: new_secret, @@ -1616,20 +1689,26 @@ describe('OTP TOTP auth adatper', () => { const new_token = new_totp.generate(); await user.save( { - authData: { mfa: { secret: new_secret.base32, token: new_token, old: totp.generate() } }, + authData: { + mfa: { + secret: new_secret.base32, + token: new_token, + old: totp.generate(), + }, + }, }, { sessionToken: user.getSessionToken() } ); await user.fetch({ useMasterKey: true }); - expect(user.get('authData').mfa.secret).toEqual(new_secret.base32); + expect(user.get("authData").mfa.secret).toEqual(new_secret.base32); }); - it('cannot change OTP with invalid token', async () => { - const user = await Parse.User.signUp('username', 'password'); - const OTPAuth = require('otpauth'); + it("cannot change OTP with invalid token", async () => { + const user = await Parse.User.signUp("username", "password"); + const OTPAuth = require("otpauth"); const secret = new OTPAuth.Secret(); const totp = new OTPAuth.TOTP({ - algorithm: 'SHA1', + algorithm: "SHA1", digits: 6, period: 30, secret, @@ -1642,7 +1721,7 @@ describe('OTP TOTP auth adatper', () => { const new_secret = new OTPAuth.Secret(); const new_totp = new OTPAuth.TOTP({ - algorithm: 'SHA1', + algorithm: "SHA1", digits: 6, period: 30, secret: new_secret, @@ -1651,21 +1730,25 @@ describe('OTP TOTP auth adatper', () => { await expectAsync( user.save( { - authData: { mfa: { secret: new_secret.base32, token: new_token, old: '123' } }, + authData: { + mfa: { secret: new_secret.base32, token: new_token, old: "123" }, + }, }, { sessionToken: user.getSessionToken() } ) - ).toBeRejectedWith(new Parse.Error(Parse.Error.OTHER_CAUSE, 'Invalid MFA token')); + ).toBeRejectedWith( + new Parse.Error(Parse.Error.OTHER_CAUSE, "Invalid MFA token") + ); await user.fetch({ useMasterKey: true }); - expect(user.get('authData').mfa.secret).toEqual(secret.base32); + expect(user.get("authData").mfa.secret).toEqual(secret.base32); }); - it('future logins require TOTP token', async () => { - const user = await Parse.User.signUp('username', 'password'); - const OTPAuth = require('otpauth'); + it("future logins require TOTP token", async () => { + const user = await Parse.User.signUp("username", "password"); + const OTPAuth = require("otpauth"); const secret = new OTPAuth.Secret(); const totp = new OTPAuth.TOTP({ - algorithm: 'SHA1', + algorithm: "SHA1", digits: 6, period: 30, secret, @@ -1675,17 +1758,22 @@ describe('OTP TOTP auth adatper', () => { { authData: { mfa: { secret: secret.base32, token } } }, { sessionToken: user.getSessionToken() } ); - await expectAsync(Parse.User.logIn('username', 'password')).toBeRejectedWith( - new Parse.Error(Parse.Error.OTHER_CAUSE, 'Missing additional authData mfa') + await expectAsync( + Parse.User.logIn("username", "password") + ).toBeRejectedWith( + new Parse.Error( + Parse.Error.OTHER_CAUSE, + "Missing additional authData mfa" + ) ); }); - it('future logins reject incorrect TOTP token', async () => { - const user = await Parse.User.signUp('username', 'password'); - const OTPAuth = require('otpauth'); + it("future logins reject incorrect TOTP token", async () => { + const user = await Parse.User.signUp("username", "password"); + const OTPAuth = require("otpauth"); const secret = new OTPAuth.Secret(); const totp = new OTPAuth.TOTP({ - algorithm: 'SHA1', + algorithm: "SHA1", digits: 6, period: 30, secret, @@ -1698,35 +1786,38 @@ describe('OTP TOTP auth adatper', () => { await expectAsync( request({ headers, - method: 'POST', - url: 'http://localhost:8378/1/login', + method: "POST", + url: "http://localhost:8378/1/login", body: JSON.stringify({ - username: 'username', - password: 'password', + username: "username", + password: "password", authData: { mfa: { - token: 'abcd', + token: "abcd", }, }, }), }).catch(e => { throw e.data; }) - ).toBeRejectedWith({ code: Parse.Error.SCRIPT_FAILED, error: 'Invalid MFA token' }); + ).toBeRejectedWith({ + code: Parse.Error.SCRIPT_FAILED, + error: "Invalid MFA token", + }); }); }); -describe('OTP SMS auth adatper', () => { +describe("OTP SMS auth adatper", () => { const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; let code; let mobile; const mfa = { enabled: true, - options: ['SMS'], + options: ["SMS"], sendSMS(smsCode, number) { expect(smsCode).toBeDefined(); expect(number).toBeDefined(); @@ -1738,8 +1829,8 @@ describe('OTP SMS auth adatper', () => { period: 30, }; beforeEach(async () => { - code = ''; - mobile = ''; + code = ""; + mobile = ""; await reconfigureServer({ auth: { mfa, @@ -1747,30 +1838,36 @@ describe('OTP SMS auth adatper', () => { }); }); - it('can enroll', async () => { - const user = await Parse.User.signUp('username', 'password'); + it("can enroll", async () => { + const user = await Parse.User.signUp("username", "password"); const sessionToken = user.getSessionToken(); - const spy = spyOn(mfa, 'sendSMS').and.callThrough(); - await user.save({ authData: { mfa: { mobile: '+11111111111' } } }, { sessionToken }); + const spy = spyOn(mfa, "sendSMS").and.callThrough(); + await user.save( + { authData: { mfa: { mobile: "+11111111111" } } }, + { sessionToken } + ); await user.fetch({ sessionToken }); - expect(user.get('authData')).toEqual({ mfa: { status: 'disabled' } }); - expect(spy).toHaveBeenCalledWith(code, '+11111111111'); + expect(user.get("authData")).toEqual({ mfa: { status: "disabled" } }); + expect(spy).toHaveBeenCalledWith(code, "+11111111111"); await user.fetch({ useMasterKey: true }); - const authData = user.get('authData').mfa?.pending; + const authData = user.get("authData").mfa?.pending; expect(authData).toBeDefined(); - expect(authData['+11111111111']).toBeDefined(); - expect(Object.keys(authData['+11111111111'])).toEqual(['token', 'expiry']); + expect(authData["+11111111111"]).toBeDefined(); + expect(Object.keys(authData["+11111111111"])).toEqual(["token", "expiry"]); - await user.save({ authData: { mfa: { mobile, token: code } } }, { sessionToken }); + await user.save( + { authData: { mfa: { mobile, token: code } } }, + { sessionToken } + ); await user.fetch({ sessionToken }); - expect(user.get('authData')).toEqual({ mfa: { status: 'enabled' } }); + expect(user.get("authData")).toEqual({ mfa: { status: "enabled" } }); }); - it('future logins require SMS code', async () => { - const user = await Parse.User.signUp('username', 'password'); - const spy = spyOn(mfa, 'sendSMS').and.callThrough(); + it("future logins require SMS code", async () => { + const user = await Parse.User.signUp("username", "password"); + const spy = spyOn(mfa, "sendSMS").and.callThrough(); await user.save( - { authData: { mfa: { mobile: '+11111111111' } } }, + { authData: { mfa: { mobile: "+11111111111" } } }, { sessionToken: user.getSessionToken() } ); @@ -1781,32 +1878,40 @@ describe('OTP SMS auth adatper', () => { spy.calls.reset(); - await expectAsync(Parse.User.logIn('username', 'password')).toBeRejectedWith( - new Parse.Error(Parse.Error.OTHER_CAUSE, 'Missing additional authData mfa') + await expectAsync( + Parse.User.logIn("username", "password") + ).toBeRejectedWith( + new Parse.Error( + Parse.Error.OTHER_CAUSE, + "Missing additional authData mfa" + ) ); const res = await request({ headers, - method: 'POST', - url: 'http://localhost:8378/1/login', + method: "POST", + url: "http://localhost:8378/1/login", body: JSON.stringify({ - username: 'username', - password: 'password', + username: "username", + password: "password", authData: { mfa: { - token: 'request', + token: "request", }, }, }), }).catch(e => e.data); - expect(res).toEqual({ code: Parse.Error.SCRIPT_FAILED, error: 'Please enter the token' }); - expect(spy).toHaveBeenCalledWith(code, '+11111111111'); + expect(res).toEqual({ + code: Parse.Error.SCRIPT_FAILED, + error: "Please enter the token", + }); + expect(spy).toHaveBeenCalledWith(code, "+11111111111"); const response = await request({ headers, - method: 'POST', - url: 'http://localhost:8378/1/login', + method: "POST", + url: "http://localhost:8378/1/login", body: JSON.stringify({ - username: 'username', - password: 'password', + username: "username", + password: "password", authData: { mfa: { token: code, @@ -1816,26 +1921,26 @@ describe('OTP SMS auth adatper', () => { }).then(res => res.data); expect(response.objectId).toEqual(user.id); expect(response.sessionToken).toBeDefined(); - expect(response.authData).toEqual({ mfa: { status: 'enabled' } }); + expect(response.authData).toEqual({ mfa: { status: "enabled" } }); expect(Object.keys(response).sort()).toEqual( [ - 'objectId', - 'username', - 'createdAt', - 'updatedAt', - 'authData', - 'ACL', - 'sessionToken', - 'authDataResponse', + "objectId", + "username", + "createdAt", + "updatedAt", + "authData", + "ACL", + "sessionToken", + "authDataResponse", ].sort() ); }); - it('partially enrolled users can still login', async () => { - const user = await Parse.User.signUp('username', 'password'); - await user.save({ authData: { mfa: { mobile: '+11111111111' } } }); - const spy = spyOn(mfa, 'sendSMS').and.callThrough(); - await Parse.User.logIn('username', 'password'); + it("partially enrolled users can still login", async () => { + const user = await Parse.User.signUp("username", "password"); + await user.save({ authData: { mfa: { mobile: "+11111111111" } } }); + const spy = spyOn(mfa, "sendSMS").and.callThrough(); + await Parse.User.logIn("username", "password"); expect(spy).not.toHaveBeenCalled(); }); }); diff --git a/spec/AuthenticationAdaptersV2.spec.js b/spec/AuthenticationAdaptersV2.spec.js index 5ec30a65f6..aad3359e12 100644 --- a/spec/AuthenticationAdaptersV2.spec.js +++ b/spec/AuthenticationAdaptersV2.spec.js @@ -1,5 +1,5 @@ -const request = require('../lib/request'); -const Auth = require('../lib/Auth'); +const request = require("../lib/request"); +const Auth = require("../lib/Auth"); const requestWithExpectedError = async params => { try { return await request(params); @@ -7,15 +7,16 @@ const requestWithExpectedError = async params => { throw new Error(e.data.error); } }; -describe('Auth Adapter features', () => { +describe("Auth Adapter features", () => { const baseAdapter = { validateAppId: () => Promise.resolve(), validateAuthData: () => Promise.resolve(), }; const baseAdapter2 = { - validateAppId: appIds => (appIds[0] === 'test' ? Promise.resolve() : Promise.reject()), + validateAppId: appIds => + appIds[0] === "test" ? Promise.resolve() : Promise.reject(), validateAuthData: () => Promise.resolve(), - appIds: ['test'], + appIds: ["test"], options: { anOption: true }, }; @@ -27,19 +28,19 @@ describe('Auth Adapter features', () => { const additionalAdapter = { validateAppId: () => Promise.resolve(), validateAuthData: () => Promise.resolve(), - policy: 'additional', + policy: "additional", }; const soloAdapter = { validateAppId: () => Promise.resolve(), validateAuthData: () => Promise.resolve(), - policy: 'solo', + policy: "solo", }; const challengeAdapter = { validateAppId: () => Promise.resolve(), validateAuthData: () => Promise.resolve(), - challenge: () => Promise.resolve({ token: 'test' }), + challenge: () => Promise.resolve({ token: "test" }), options: { anOption: true, }, @@ -67,7 +68,7 @@ describe('Auth Adapter features', () => { validateOptions: () => Promise.resolve(), afterFind() { return { - foo: 'bar', + foo: "bar", }; }, }; @@ -77,50 +78,50 @@ describe('Auth Adapter features', () => { }; const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; - it('should ensure no duplicate auth data id after before save', async () => { + it("should ensure no duplicate auth data id after before save", async () => { await reconfigureServer({ auth: { baseAdapter }, cloud: () => { - Parse.Cloud.beforeSave('_User', async request => { - request.object.set('authData', { baseAdapter: { id: 'test' } }); + Parse.Cloud.beforeSave("_User", async request => { + request.object.set("authData", { baseAdapter: { id: "test" } }); }); }, }); const user = new Parse.User(); - await user.save({ authData: { baseAdapter: { id: 'another' } } }); + await user.save({ authData: { baseAdapter: { id: "another" } } }); await user.fetch({ useMasterKey: true }); - expect(user.get('authData')).toEqual({ baseAdapter: { id: 'test' } }); + expect(user.get("authData")).toEqual({ baseAdapter: { id: "test" } }); const user2 = new Parse.User(); await expectAsync( - user2.save({ authData: { baseAdapter: { id: 'another' } } }) - ).toBeRejectedWithError('this auth is already used'); + user2.save({ authData: { baseAdapter: { id: "another" } } }) + ).toBeRejectedWithError("this auth is already used"); }); - it('should ensure no duplicate auth data id after before save in case of more than one result', async () => { + it("should ensure no duplicate auth data id after before save in case of more than one result", async () => { await reconfigureServer({ auth: { baseAdapter }, cloud: () => { - Parse.Cloud.beforeSave('_User', async request => { - request.object.set('authData', { baseAdapter: { id: 'test' } }); + Parse.Cloud.beforeSave("_User", async request => { + request.object.set("authData", { baseAdapter: { id: "test" } }); }); }, }); const user = new Parse.User(); - await user.save({ authData: { baseAdapter: { id: 'another' } } }); + await user.save({ authData: { baseAdapter: { id: "another" } } }); await user.fetch({ useMasterKey: true }); - expect(user.get('authData')).toEqual({ baseAdapter: { id: 'test' } }); + expect(user.get("authData")).toEqual({ baseAdapter: { id: "test" } }); let i = 0; const originalFn = Auth.findUsersWithAuthData; - spyOn(Auth, 'findUsersWithAuthData').and.callFake((...params) => { + spyOn(Auth, "findUsersWithAuthData").and.callFake((...params) => { // First call is triggered during authData validation if (i === 0) { i++; @@ -135,37 +136,37 @@ describe('Auth Adapter features', () => { }); const user2 = new Parse.User(); await expectAsync( - user2.save({ authData: { baseAdapter: { id: 'another' } } }) - ).toBeRejectedWithError('this auth is already used'); + user2.save({ authData: { baseAdapter: { id: "another" } } }) + ).toBeRejectedWithError("this auth is already used"); }); - it('should ensure no duplicate auth data id during authData validation in case of more than one result', async () => { + it("should ensure no duplicate auth data id during authData validation in case of more than one result", async () => { await reconfigureServer({ auth: { baseAdapter }, cloud: () => { - Parse.Cloud.beforeSave('_User', async request => { - request.object.set('authData', { baseAdapter: { id: 'test' } }); + Parse.Cloud.beforeSave("_User", async request => { + request.object.set("authData", { baseAdapter: { id: "test" } }); }); }, }); - spyOn(Auth, 'findUsersWithAuthData').and.resolveTo([true, true]); + spyOn(Auth, "findUsersWithAuthData").and.resolveTo([true, true]); const user = new Parse.User(); await expectAsync( - user.save({ authData: { baseAdapter: { id: 'another' } } }) - ).toBeRejectedWithError('this auth is already used'); + user.save({ authData: { baseAdapter: { id: "another" } } }) + ).toBeRejectedWithError("this auth is already used"); }); - it('should pass authData, options, request to validateAuthData', async () => { - spyOn(baseAdapter, 'validateAuthData').and.resolveTo({}); + it("should pass authData, options, request to validateAuthData", async () => { + spyOn(baseAdapter, "validateAuthData").and.resolveTo({}); await reconfigureServer({ auth: { baseAdapter } }); const user = new Parse.User(); const payload = { someData: true }; await user.save({ - username: 'test', - password: 'password', + username: "test", + password: "password", authData: { baseAdapter: payload }, }); @@ -181,11 +182,11 @@ describe('Auth Adapter features', () => { await request({ headers: headers, - method: 'POST', - url: 'http://localhost:8378/1/login', + method: "POST", + url: "http://localhost:8378/1/login", body: JSON.stringify({ - username: 'test', - password: 'password', + username: "test", + password: "password", authData: { baseAdapter: payload }, }), }); @@ -203,36 +204,36 @@ describe('Auth Adapter features', () => { expect(secondCall.length).toEqual(3); }); - it('should trigger correctly validateSetUp', async () => { - spyOn(modernAdapter, 'validateSetUp').and.resolveTo({}); - spyOn(modernAdapter, 'validateUpdate').and.resolveTo({}); - spyOn(modernAdapter, 'validateLogin').and.resolveTo({}); - spyOn(modernAdapter2, 'validateSetUp').and.resolveTo({}); - spyOn(modernAdapter2, 'validateUpdate').and.resolveTo({}); - spyOn(modernAdapter2, 'validateLogin').and.resolveTo({}); + it("should trigger correctly validateSetUp", async () => { + spyOn(modernAdapter, "validateSetUp").and.resolveTo({}); + spyOn(modernAdapter, "validateUpdate").and.resolveTo({}); + spyOn(modernAdapter, "validateLogin").and.resolveTo({}); + spyOn(modernAdapter2, "validateSetUp").and.resolveTo({}); + spyOn(modernAdapter2, "validateUpdate").and.resolveTo({}); + spyOn(modernAdapter2, "validateLogin").and.resolveTo({}); await reconfigureServer({ auth: { modernAdapter, modernAdapter2 } }); const user = new Parse.User(); - await user.save({ authData: { modernAdapter: { id: 'modernAdapter' } } }); + await user.save({ authData: { modernAdapter: { id: "modernAdapter" } } }); expect(modernAdapter.validateUpdate).toHaveBeenCalledTimes(0); expect(modernAdapter.validateLogin).toHaveBeenCalledTimes(0); expect(modernAdapter.validateSetUp).toHaveBeenCalledTimes(1); const call = modernAdapter.validateSetUp.calls.argsFor(0); - expect(call[0]).toEqual({ id: 'modernAdapter' }); + expect(call[0]).toEqual({ id: "modernAdapter" }); expect(call[1]).toEqual(modernAdapter); expect(call[2].isChallenge).toBeUndefined(); expect(call[2].master).toBeDefined(); expect(call[2].object instanceof Parse.User).toBeTruthy(); expect(call[2].user).toBeUndefined(); expect(call[2].original).toBeUndefined(); - expect(call[2].triggerName).toBe('validateSetUp'); + expect(call[2].triggerName).toBe("validateSetUp"); expect(call.length).toEqual(3); expect(user.getSessionToken()).toBeDefined(); await user.save( - { authData: { modernAdapter2: { id: 'modernAdapter2' } } }, + { authData: { modernAdapter2: { id: "modernAdapter2" } } }, { sessionToken: user.getSessionToken() } ); @@ -240,7 +241,7 @@ describe('Auth Adapter features', () => { expect(modernAdapter2.validateLogin).toHaveBeenCalledTimes(0); expect(modernAdapter2.validateSetUp).toHaveBeenCalledTimes(1); const call2 = modernAdapter2.validateSetUp.calls.argsFor(0); - expect(call2[0]).toEqual({ id: 'modernAdapter2' }); + expect(call2[0]).toEqual({ id: "modernAdapter2" }); expect(call2[1]).toEqual(modernAdapter2); expect(call2[2].isChallenge).toBeUndefined(); expect(call2[2].master).toBeDefined(); @@ -250,37 +251,40 @@ describe('Auth Adapter features', () => { expect(call2[2].original.id).toEqual(call2[2].object.id); expect(call2[2].user.id).toEqual(call2[2].object.id); expect(call2[2].object.id).toEqual(user.id); - expect(call2[2].triggerName).toBe('validateSetUp'); + expect(call2[2].triggerName).toBe("validateSetUp"); expect(call2.length).toEqual(3); const user2 = new Parse.User(); user2.id = user.id; await user2.fetch({ useMasterKey: true }); - expect(user2.get('authData')).toEqual({ - modernAdapter: { id: 'modernAdapter' }, - modernAdapter2: { id: 'modernAdapter2' }, + expect(user2.get("authData")).toEqual({ + modernAdapter: { id: "modernAdapter" }, + modernAdapter2: { id: "modernAdapter2" }, }); }); - it('should trigger correctly validateLogin', async () => { - spyOn(modernAdapter, 'validateSetUp').and.resolveTo({}); - spyOn(modernAdapter, 'validateUpdate').and.resolveTo({}); - spyOn(modernAdapter, 'validateLogin').and.resolveTo({}); + it("should trigger correctly validateLogin", async () => { + spyOn(modernAdapter, "validateSetUp").and.resolveTo({}); + spyOn(modernAdapter, "validateUpdate").and.resolveTo({}); + spyOn(modernAdapter, "validateLogin").and.resolveTo({}); - await reconfigureServer({ auth: { modernAdapter }, allowExpiredAuthDataToken: false }); + await reconfigureServer({ + auth: { modernAdapter }, + allowExpiredAuthDataToken: false, + }); const user = new Parse.User(); - await user.save({ authData: { modernAdapter: { id: 'modernAdapter' } } }); + await user.save({ authData: { modernAdapter: { id: "modernAdapter" } } }); expect(modernAdapter.validateSetUp).toHaveBeenCalledTimes(1); const user2 = new Parse.User(); - await user2.save({ authData: { modernAdapter: { id: 'modernAdapter' } } }); + await user2.save({ authData: { modernAdapter: { id: "modernAdapter" } } }); expect(modernAdapter.validateUpdate).toHaveBeenCalledTimes(0); expect(modernAdapter.validateSetUp).toHaveBeenCalledTimes(1); expect(modernAdapter.validateLogin).toHaveBeenCalledTimes(1); const call = modernAdapter.validateLogin.calls.argsFor(0); - expect(call[0]).toEqual({ id: 'modernAdapter' }); + expect(call[0]).toEqual({ id: "modernAdapter" }); expect(call[1]).toEqual(modernAdapter); expect(call[2].object instanceof Parse.User).toBeTruthy(); expect(call[2].original instanceof Parse.User).toBeTruthy(); @@ -294,26 +298,26 @@ describe('Auth Adapter features', () => { expect(user2.getSessionToken()).toBeDefined(); }); - it('should trigger correctly validateUpdate', async () => { - spyOn(modernAdapter, 'validateSetUp').and.resolveTo({}); - spyOn(modernAdapter, 'validateUpdate').and.resolveTo({}); - spyOn(modernAdapter, 'validateLogin').and.resolveTo({}); + it("should trigger correctly validateUpdate", async () => { + spyOn(modernAdapter, "validateSetUp").and.resolveTo({}); + spyOn(modernAdapter, "validateUpdate").and.resolveTo({}); + spyOn(modernAdapter, "validateLogin").and.resolveTo({}); await reconfigureServer({ auth: { modernAdapter } }); const user = new Parse.User(); - await user.save({ authData: { modernAdapter: { id: 'modernAdapter' } } }); + await user.save({ authData: { modernAdapter: { id: "modernAdapter" } } }); expect(modernAdapter.validateSetUp).toHaveBeenCalledTimes(1); // Save same data await user.save( - { authData: { modernAdapter: { id: 'modernAdapter' } } }, + { authData: { modernAdapter: { id: "modernAdapter" } } }, { sessionToken: user.getSessionToken() } ); // Save same data with master key await user.save( - { authData: { modernAdapter: { id: 'modernAdapter' } } }, + { authData: { modernAdapter: { id: "modernAdapter" } } }, { useMasterKey: true } ); @@ -323,7 +327,7 @@ describe('Auth Adapter features', () => { // Change authData await user.save( - { authData: { modernAdapter: { id: 'modernAdapter2' } } }, + { authData: { modernAdapter: { id: "modernAdapter2" } } }, { sessionToken: user.getSessionToken() } ); @@ -331,7 +335,7 @@ describe('Auth Adapter features', () => { expect(modernAdapter.validateSetUp).toHaveBeenCalledTimes(1); expect(modernAdapter.validateLogin).toHaveBeenCalledTimes(0); const call = modernAdapter.validateUpdate.calls.argsFor(0); - expect(call[0]).toEqual({ id: 'modernAdapter2' }); + expect(call[0]).toEqual({ id: "modernAdapter2" }); expect(call[1]).toEqual(modernAdapter); expect(call[2].isChallenge).toBeUndefined(); expect(call[2].master).toBeDefined(); @@ -345,15 +349,17 @@ describe('Auth Adapter features', () => { expect(user.getSessionToken()).toBeDefined(); }); - it('should strip out authData if required', async () => { - const spy = spyOn(modernAdapter3, 'validateOptions').and.callThrough(); - const afterSpy = spyOn(modernAdapter3, 'afterFind').and.callThrough(); + it("should strip out authData if required", async () => { + const spy = spyOn(modernAdapter3, "validateOptions").and.callThrough(); + const afterSpy = spyOn(modernAdapter3, "afterFind").and.callThrough(); await reconfigureServer({ auth: { modernAdapter3 } }); const user = new Parse.User(); - await user.save({ authData: { modernAdapter3: { id: 'modernAdapter3Data' } } }); + await user.save({ + authData: { modernAdapter3: { id: "modernAdapter3Data" } }, + }); await user.fetch({ sessionToken: user.getSessionToken() }); - const authData = user.get('authData').modernAdapter3; - expect(authData).toEqual({ foo: 'bar' }); + const authData = user.get("authData").modernAdapter3; + expect(authData).toEqual({ foo: "bar" }); for (const call of afterSpy.calls.all()) { const args = call.args[2]; if (args.user) { @@ -361,41 +367,49 @@ describe('Auth Adapter features', () => { break; } } - expect(afterSpy).toHaveBeenCalledWith({ id: 'modernAdapter3Data' }, undefined, { - ip: '127.0.0.1', - user, - master: false, - }); + expect(afterSpy).toHaveBeenCalledWith( + { id: "modernAdapter3Data" }, + undefined, + { + ip: "127.0.0.1", + user, + master: false, + } + ); expect(spy).toHaveBeenCalled(); }); - it('should throw if policy does not match one of default/solo/additional', async () => { + it("should throw if policy does not match one of default/solo/additional", async () => { const adapterWithBadPolicy = { validateAppId: () => Promise.resolve(), validateAuthData: () => Promise.resolve(), - policy: 'bad', + policy: "bad", }; await reconfigureServer({ auth: { adapterWithBadPolicy } }); const user = new Parse.User(); await expectAsync( - user.save({ authData: { adapterWithBadPolicy: { id: 'adapterWithBadPolicy' } } }) + user.save({ + authData: { adapterWithBadPolicy: { id: "adapterWithBadPolicy" } }, + }) ).toBeRejectedWithError( 'AuthAdapter policy is not configured correctly. The value must be either "solo", "additional", "default" or undefined (will be handled as "default")' ); }); - it('should throw if no triggers found', async () => { + it("should throw if no triggers found", async () => { await reconfigureServer({ auth: { wrongAdapter } }); const user = new Parse.User(); await expectAsync( - user.save({ authData: { wrongAdapter: { id: 'wrongAdapter' } } }) + user.save({ authData: { wrongAdapter: { id: "wrongAdapter" } } }) ).toBeRejectedWithError( - 'Adapter is not configured. Implement either validateAuthData or all of the following: validateSetUp, validateLogin and validateUpdate' + "Adapter is not configured. Implement either validateAuthData or all of the following: validateSetUp, validateLogin and validateUpdate" ); }); - it('should not update authData if provider return doNotSave', async () => { - spyOn(doNotSaveAdapter, 'validateAuthData').and.resolveTo({ doNotSave: true }); + it("should not update authData if provider return doNotSave", async () => { + spyOn(doNotSaveAdapter, "validateAuthData").and.resolveTo({ + doNotSave: true, + }); await reconfigureServer({ auth: { doNotSaveAdapter, baseAdapter }, }); @@ -403,16 +417,23 @@ describe('Auth Adapter features', () => { const user = new Parse.User(); await user.save({ - authData: { baseAdapter: { id: 'baseAdapter' }, doNotSaveAdapter: { token: true } }, + authData: { + baseAdapter: { id: "baseAdapter" }, + doNotSaveAdapter: { token: true }, + }, }); await user.fetch({ useMasterKey: true }); - expect(user.get('authData')).toEqual({ baseAdapter: { id: 'baseAdapter' } }); + expect(user.get("authData")).toEqual({ + baseAdapter: { id: "baseAdapter" }, + }); }); - it('should loginWith user with auth Adapter with do not save option, mutated authData and no additional auth adapter', async () => { - const spy = spyOn(doNotSaveAdapter, 'validateAuthData').and.resolveTo({ doNotSave: false }); + it("should loginWith user with auth Adapter with do not save option, mutated authData and no additional auth adapter", async () => { + const spy = spyOn(doNotSaveAdapter, "validateAuthData").and.resolveTo({ + doNotSave: false, + }); await reconfigureServer({ auth: { doNotSaveAdapter, baseAdapter }, }); @@ -420,26 +441,28 @@ describe('Auth Adapter features', () => { const user = new Parse.User(); await user.save({ - authData: { doNotSaveAdapter: { id: 'doNotSaveAdapter' } }, + authData: { doNotSaveAdapter: { id: "doNotSaveAdapter" } }, }); await user.fetch({ useMasterKey: true }); - expect(user.get('authData')).toEqual({ doNotSaveAdapter: { id: 'doNotSaveAdapter' } }); + expect(user.get("authData")).toEqual({ + doNotSaveAdapter: { id: "doNotSaveAdapter" }, + }); spy.and.resolveTo({ doNotSave: true }); - const user2 = await Parse.User.logInWith('doNotSaveAdapter', { - authData: { id: 'doNotSaveAdapter', example: 'example' }, + const user2 = await Parse.User.logInWith("doNotSaveAdapter", { + authData: { id: "doNotSaveAdapter", example: "example" }, }); expect(user2.getSessionToken()).toBeDefined(); expect(user2.id).toEqual(user.id); }); - it('should perform authData validation only when its required', async () => { - spyOn(baseAdapter2, 'validateAuthData').and.resolveTo({}); - spyOn(baseAdapter2, 'validateAppId').and.resolveTo({}); - spyOn(baseAdapter, 'validateAuthData').and.resolveTo({}); + it("should perform authData validation only when its required", async () => { + spyOn(baseAdapter2, "validateAuthData").and.resolveTo({}); + spyOn(baseAdapter2, "validateAppId").and.resolveTo({}); + spyOn(baseAdapter, "validateAuthData").and.resolveTo({}); await reconfigureServer({ auth: { baseAdapter2, baseAdapter }, allowExpiredAuthDataToken: false, @@ -449,7 +472,7 @@ describe('Auth Adapter features', () => { await user.save({ authData: { - baseAdapter: { id: 'baseAdapter' }, + baseAdapter: { id: "baseAdapter" }, baseAdapter2: { token: true }, }, }); @@ -460,7 +483,7 @@ describe('Auth Adapter features', () => { const user2 = new Parse.User(); await user2.save({ authData: { - baseAdapter: { id: 'baseAdapter' }, + baseAdapter: { id: "baseAdapter" }, }, }); @@ -469,7 +492,7 @@ describe('Auth Adapter features', () => { const user3 = new Parse.User(); await user3.save({ authData: { - baseAdapter: { id: 'baseAdapter' }, + baseAdapter: { id: "baseAdapter" }, baseAdapter2: { token: true }, }, }); @@ -477,8 +500,8 @@ describe('Auth Adapter features', () => { expect(baseAdapter2.validateAuthData).toHaveBeenCalledTimes(2); }); - it('should not perform authData validation twice when data mutated', async () => { - spyOn(baseAdapter, 'validateAuthData').and.resolveTo({}); + it("should not perform authData validation twice when data mutated", async () => { + spyOn(baseAdapter, "validateAuthData").and.resolveTo({}); await reconfigureServer({ auth: { baseAdapter }, allowExpiredAuthDataToken: false, @@ -488,7 +511,7 @@ describe('Auth Adapter features', () => { await user.save({ authData: { - baseAdapter: { id: 'baseAdapter', token: 'sometoken1' }, + baseAdapter: { id: "baseAdapter", token: "sometoken1" }, }, }); @@ -497,14 +520,14 @@ describe('Auth Adapter features', () => { const user2 = new Parse.User(); await user2.save({ authData: { - baseAdapter: { id: 'baseAdapter', token: 'sometoken2' }, + baseAdapter: { id: "baseAdapter", token: "sometoken2" }, }, }); expect(baseAdapter.validateAuthData).toHaveBeenCalledTimes(2); }); - it('should require additional provider if configured', async () => { + it("should require additional provider if configured", async () => { await reconfigureServer({ auth: { baseAdapter, additionalAdapter }, }); @@ -513,7 +536,7 @@ describe('Auth Adapter features', () => { await user.save({ authData: { - baseAdapter: { id: 'baseAdapter' }, + baseAdapter: { id: "baseAdapter" }, additionalAdapter: { token: true }, }, }); @@ -522,15 +545,15 @@ describe('Auth Adapter features', () => { await expectAsync( user2.save({ authData: { - baseAdapter: { id: 'baseAdapter' }, + baseAdapter: { id: "baseAdapter" }, }, }) - ).toBeRejectedWithError('Missing additional authData additionalAdapter'); + ).toBeRejectedWithError("Missing additional authData additionalAdapter"); expect(user2.getSessionToken()).toBeUndefined(); await user2.save({ authData: { - baseAdapter: { id: 'baseAdapter' }, + baseAdapter: { id: "baseAdapter" }, additionalAdapter: { token: true }, }, }); @@ -538,7 +561,7 @@ describe('Auth Adapter features', () => { expect(user2.getSessionToken()).toBeDefined(); }); - it('should skip additional provider if used provider is solo', async () => { + it("should skip additional provider if used provider is solo", async () => { await reconfigureServer({ auth: { soloAdapter, additionalAdapter }, }); @@ -547,7 +570,7 @@ describe('Auth Adapter features', () => { await user.save({ authData: { - soloAdapter: { id: 'soloAdapter' }, + soloAdapter: { id: "soloAdapter" }, additionalAdapter: { token: true }, }, }); @@ -555,17 +578,17 @@ describe('Auth Adapter features', () => { const user2 = new Parse.User(); await user2.save({ authData: { - soloAdapter: { id: 'soloAdapter' }, + soloAdapter: { id: "soloAdapter" }, }, }); expect(user2.getSessionToken()).toBeDefined(); }); - it('should return authData response and save some info on non username login', async () => { - spyOn(baseAdapter, 'validateAuthData').and.resolveTo({ + it("should return authData response and save some info on non username login", async () => { + spyOn(baseAdapter, "validateAuthData").and.resolveTo({ response: { someData: true }, }); - spyOn(baseAdapter2, 'validateAuthData').and.resolveTo({ + spyOn(baseAdapter2, "validateAuthData").and.resolveTo({ response: { someData2: true }, save: { otherData: true }, }); @@ -577,12 +600,12 @@ describe('Auth Adapter features', () => { await user.save({ authData: { - baseAdapter: { id: 'baseAdapter' }, + baseAdapter: { id: "baseAdapter" }, baseAdapter2: { test: true }, }, }); - expect(user.get('authDataResponse')).toEqual({ + expect(user.get("authDataResponse")).toEqual({ baseAdapter: { someData: true }, baseAdapter2: { someData2: true }, }); @@ -592,25 +615,27 @@ describe('Auth Adapter features', () => { await user2.save( { authData: { - baseAdapter: { id: 'baseAdapter' }, + baseAdapter: { id: "baseAdapter" }, baseAdapter2: { test: true }, }, }, { sessionToken: user.getSessionToken() } ); - expect(user2.get('authDataResponse')).toEqual({ baseAdapter2: { someData2: true } }); + expect(user2.get("authDataResponse")).toEqual({ + baseAdapter2: { someData2: true }, + }); const user3 = new Parse.User(); await user3.save({ authData: { - baseAdapter: { id: 'baseAdapter' }, + baseAdapter: { id: "baseAdapter" }, baseAdapter2: { test: true }, }, }); // On logIn all authData are revalidated - expect(user3.get('authDataResponse')).toEqual({ + expect(user3.get("authDataResponse")).toEqual({ baseAdapter: { someData: true }, baseAdapter2: { someData2: true }, }); @@ -618,17 +643,17 @@ describe('Auth Adapter features', () => { const userViaMasterKey = new Parse.User(); userViaMasterKey.id = user2.id; await userViaMasterKey.fetch({ useMasterKey: true }); - expect(userViaMasterKey.get('authData')).toEqual({ - baseAdapter: { id: 'baseAdapter' }, + expect(userViaMasterKey.get("authData")).toEqual({ + baseAdapter: { id: "baseAdapter" }, baseAdapter2: { otherData: true }, }); }); - it('should return authData response and save some info on username login', async () => { - spyOn(baseAdapter, 'validateAuthData').and.resolveTo({ + it("should return authData response and save some info on username login", async () => { + spyOn(baseAdapter, "validateAuthData").and.resolveTo({ response: { someData: true }, }); - spyOn(baseAdapter2, 'validateAuthData').and.resolveTo({ + spyOn(baseAdapter2, "validateAuthData").and.resolveTo({ response: { someData2: true }, save: { otherData: true }, }); @@ -639,29 +664,29 @@ describe('Auth Adapter features', () => { const user = new Parse.User(); await user.save({ - username: 'username', - password: 'password', + username: "username", + password: "password", authData: { - baseAdapter: { id: 'baseAdapter' }, + baseAdapter: { id: "baseAdapter" }, baseAdapter2: { test: true }, }, }); - expect(user.get('authDataResponse')).toEqual({ + expect(user.get("authDataResponse")).toEqual({ baseAdapter: { someData: true }, baseAdapter2: { someData2: true }, }); const res = await request({ headers: headers, - method: 'POST', - url: 'http://localhost:8378/1/login', + method: "POST", + url: "http://localhost:8378/1/login", body: JSON.stringify({ - username: 'username', - password: 'password', + username: "username", + password: "password", authData: { baseAdapter2: { test: true }, - baseAdapter: { id: 'baseAdapter' }, + baseAdapter: { id: "baseAdapter" }, }, }), }); @@ -672,18 +697,18 @@ describe('Auth Adapter features', () => { }); await user.fetch({ useMasterKey: true }); - expect(user.get('authData')).toEqual({ - baseAdapter: { id: 'baseAdapter' }, + expect(user.get("authData")).toEqual({ + baseAdapter: { id: "baseAdapter" }, baseAdapter2: { otherData: true }, }); }); - describe('should allow update of authData', () => { + describe("should allow update of authData", () => { beforeEach(async () => { - spyOn(baseAdapter, 'validateAuthData').and.resolveTo({ + spyOn(baseAdapter, "validateAuthData").and.resolveTo({ response: { someData: true }, }); - spyOn(baseAdapter2, 'validateAuthData').and.resolveTo({ + spyOn(baseAdapter2, "validateAuthData").and.resolveTo({ response: { someData2: true }, save: { otherData: true }, }); @@ -692,14 +717,14 @@ describe('Auth Adapter features', () => { }); }); - it('should not re validate the baseAdapter when user is already logged in and authData not changed', async () => { + it("should not re validate the baseAdapter when user is already logged in and authData not changed", async () => { const user = new Parse.User(); await user.save({ - username: 'username', - password: 'password', + username: "username", + password: "password", authData: { - baseAdapter: { id: 'baseAdapter' }, + baseAdapter: { id: "baseAdapter" }, baseAdapter2: { test: true }, }, }); @@ -711,7 +736,7 @@ describe('Auth Adapter features', () => { { authData: { baseAdapter2: { test: true }, - baseAdapter: { id: 'baseAdapter' }, + baseAdapter: { id: "baseAdapter" }, }, }, { sessionToken: user.getSessionToken() } @@ -720,13 +745,13 @@ describe('Auth Adapter features', () => { expect(baseAdapter.validateAuthData).toHaveBeenCalledTimes(1); }); - it('should not re-validate the baseAdapter when master key is used and authData has not changed', async () => { + it("should not re-validate the baseAdapter when master key is used and authData has not changed", async () => { const user = new Parse.User(); await user.save({ - username: 'username', - password: 'password', + username: "username", + password: "password", authData: { - baseAdapter: { id: 'baseAdapter' }, + baseAdapter: { id: "baseAdapter" }, baseAdapter2: { test: true }, }, }); @@ -734,7 +759,7 @@ describe('Auth Adapter features', () => { { authData: { baseAdapter2: { test: true }, - baseAdapter: { id: 'baseAdapter' }, + baseAdapter: { id: "baseAdapter" }, }, }, { useMasterKey: true } @@ -743,13 +768,13 @@ describe('Auth Adapter features', () => { expect(baseAdapter.validateAuthData).toHaveBeenCalledTimes(1); }); - it('should allow user to change authData', async () => { + it("should allow user to change authData", async () => { const user = new Parse.User(); await user.save({ - username: 'username', - password: 'password', + username: "username", + password: "password", authData: { - baseAdapter: { id: 'baseAdapter' }, + baseAdapter: { id: "baseAdapter" }, baseAdapter2: { test: true }, }, }); @@ -757,7 +782,7 @@ describe('Auth Adapter features', () => { { authData: { baseAdapter2: { test: true }, - baseAdapter: { id: 'baseAdapter2' }, + baseAdapter: { id: "baseAdapter2" }, }, }, { sessionToken: user.getSessionToken() } @@ -766,13 +791,13 @@ describe('Auth Adapter features', () => { expect(baseAdapter.validateAuthData).toHaveBeenCalledTimes(2); }); - it('should allow master key to change authData', async () => { + it("should allow master key to change authData", async () => { const user = new Parse.User(); await user.save({ - username: 'username', - password: 'password', + username: "username", + password: "password", authData: { - baseAdapter: { id: 'baseAdapter' }, + baseAdapter: { id: "baseAdapter" }, baseAdapter2: { test: true }, }, }); @@ -780,7 +805,7 @@ describe('Auth Adapter features', () => { { authData: { baseAdapter2: { test: true }, - baseAdapter: { id: 'baseAdapter3' }, + baseAdapter: { id: "baseAdapter3" }, }, }, { useMasterKey: true } @@ -789,15 +814,15 @@ describe('Auth Adapter features', () => { expect(baseAdapter.validateAuthData).toHaveBeenCalledTimes(2); await user.fetch({ useMasterKey: true }); - expect(user.get('authData')).toEqual({ - baseAdapter: { id: 'baseAdapter3' }, + expect(user.get("authData")).toEqual({ + baseAdapter: { id: "baseAdapter3" }, baseAdapter2: { otherData: true }, }); }); }); - it('should pass user to auth adapter on update by matching session', async () => { - spyOn(baseAdapter2, 'validateAuthData').and.resolveTo({}); + it("should pass user to auth adapter on update by matching session", async () => { + spyOn(baseAdapter2, "validateAuthData").and.resolveTo({}); await reconfigureServer({ auth: { baseAdapter2 } }); const user = new Parse.User(); @@ -805,8 +830,8 @@ describe('Auth Adapter features', () => { const payload = { someData: true }; await user.save({ - username: 'test', - password: 'password', + username: "test", + password: "password", }); expect(user.getSessionToken()).toBeDefined(); @@ -829,7 +854,10 @@ describe('Auth Adapter features', () => { expect(firstCall[2].user.id).toEqual(user.id); expect(firstCall.length).toEqual(3); - await user.save({ authData: { baseAdapter2: payload } }, { useMasterKey: true }); + await user.save( + { authData: { baseAdapter2: payload } }, + { useMasterKey: true } + ); const secondCall = baseAdapter2.validateAuthData.calls.argsFor(1); expect(secondCall[0]).toEqual(payload); @@ -844,18 +872,19 @@ describe('Auth Adapter features', () => { expect(secondCall.length).toEqual(3); }); - it('should return custom errors', async () => { + it("should return custom errors", async () => { const throwInChallengeAdapter = { validateAppId: () => Promise.resolve(), validateAuthData: () => Promise.resolve(), - challenge: () => Promise.reject('Invalid challenge data: yolo'), + challenge: () => Promise.reject("Invalid challenge data: yolo"), options: { anOption: true, }, }; const throwInSetup = { validateAppId: () => Promise.resolve(), - validateSetUp: () => Promise.reject('You cannot signup with that setup data.'), + validateSetUp: () => + Promise.reject("You cannot signup with that setup data."), validateUpdate: () => Promise.resolve(), validateLogin: () => Promise.resolve(), }; @@ -863,7 +892,8 @@ describe('Auth Adapter features', () => { const throwInUpdate = { validateAppId: () => Promise.resolve(), validateSetUp: () => Promise.resolve(), - validateUpdate: () => Promise.reject('You cannot update with that update data.'), + validateUpdate: () => + Promise.reject("You cannot update with that update data."), validateLogin: () => Promise.resolve(), }; @@ -871,81 +901,91 @@ describe('Auth Adapter features', () => { validateAppId: () => Promise.resolve(), validateSetUp: () => Promise.resolve(), validateUpdate: () => Promise.resolve(), - validateLogin: () => Promise.reject('You cannot login with that login data.'), + validateLogin: () => + Promise.reject("You cannot login with that login data."), }; await reconfigureServer({ auth: { challengeAdapter: throwInChallengeAdapter }, }); - let logger = require('../lib/logger').logger; - spyOn(logger, 'error').and.callFake(() => {}); + let logger = require("../lib/logger").logger; + spyOn(logger, "error").and.callFake(() => {}); await expectAsync( requestWithExpectedError({ headers: headers, - method: 'POST', - url: 'http://localhost:8378/1/challenge', + method: "POST", + url: "http://localhost:8378/1/challenge", body: JSON.stringify({ challengeData: { challengeAdapter: { someData: true }, }, }), }) - ).toBeRejectedWithError('Invalid challenge data: yolo'); + ).toBeRejectedWithError("Invalid challenge data: yolo"); expect(logger.error).toHaveBeenCalledWith( `Failed running auth step challenge for challengeAdapter for user undefined with Error: {"message":"Invalid challenge data: yolo","code":${Parse.Error.SCRIPT_FAILED}}`, { - authenticationStep: 'challenge', - error: new Parse.Error(Parse.Error.SCRIPT_FAILED, 'Invalid challenge data: yolo'), + authenticationStep: "challenge", + error: new Parse.Error( + Parse.Error.SCRIPT_FAILED, + "Invalid challenge data: yolo" + ), user: undefined, - provider: 'challengeAdapter', + provider: "challengeAdapter", } ); await reconfigureServer({ auth: { modernAdapter: throwInSetup } }); - logger = require('../lib/logger').logger; - spyOn(logger, 'error').and.callFake(() => {}); + logger = require("../lib/logger").logger; + spyOn(logger, "error").and.callFake(() => {}); let user = new Parse.User(); await expectAsync( - user.save({ authData: { modernAdapter: { id: 'modernAdapter' } } }) + user.save({ authData: { modernAdapter: { id: "modernAdapter" } } }) ).toBeRejectedWith( - new Parse.Error(Parse.Error.SCRIPT_FAILED, 'You cannot signup with that setup data.') + new Parse.Error( + Parse.Error.SCRIPT_FAILED, + "You cannot signup with that setup data." + ) ); expect(logger.error).toHaveBeenCalledWith( `Failed running auth step validateSetUp for modernAdapter for user undefined with Error: {"message":"You cannot signup with that setup data.","code":${Parse.Error.SCRIPT_FAILED}}`, { - authenticationStep: 'validateSetUp', + authenticationStep: "validateSetUp", error: new Parse.Error( Parse.Error.SCRIPT_FAILED, - 'You cannot signup with that setup data.' + "You cannot signup with that setup data." ), user: undefined, - provider: 'modernAdapter', + provider: "modernAdapter", } ); await reconfigureServer({ auth: { modernAdapter: throwInUpdate } }); - logger = require('../lib/logger').logger; - spyOn(logger, 'error').and.callFake(() => {}); + logger = require("../lib/logger").logger; + spyOn(logger, "error").and.callFake(() => {}); user = new Parse.User(); - await user.save({ authData: { modernAdapter: { id: 'updateAdapter' } } }); + await user.save({ authData: { modernAdapter: { id: "updateAdapter" } } }); await expectAsync( user.save( - { authData: { modernAdapter: { id: 'updateAdapter2' } } }, + { authData: { modernAdapter: { id: "updateAdapter2" } } }, { sessionToken: user.getSessionToken() } ) ).toBeRejectedWith( - new Parse.Error(Parse.Error.SCRIPT_FAILED, 'You cannot update with that update data.') + new Parse.Error( + Parse.Error.SCRIPT_FAILED, + "You cannot update with that update data." + ) ); expect(logger.error).toHaveBeenCalledWith( `Failed running auth step validateUpdate for modernAdapter for user ${user.id} with Error: {"message":"You cannot update with that update data.","code":${Parse.Error.SCRIPT_FAILED}}`, { - authenticationStep: 'validateUpdate', + authenticationStep: "validateUpdate", error: new Parse.Error( Parse.Error.SCRIPT_FAILED, - 'You cannot update with that update data.' + "You cannot update with that update data." ), user: user.id, - provider: 'modernAdapter', + provider: "modernAdapter", } ); @@ -953,29 +993,35 @@ describe('Auth Adapter features', () => { auth: { modernAdapter: throwInLogin }, allowExpiredAuthDataToken: false, }); - logger = require('../lib/logger').logger; - spyOn(logger, 'error').and.callFake(() => {}); + logger = require("../lib/logger").logger; + spyOn(logger, "error").and.callFake(() => {}); user = new Parse.User(); - await user.save({ authData: { modernAdapter: { id: 'modernAdapter' } } }); + await user.save({ authData: { modernAdapter: { id: "modernAdapter" } } }); const user2 = new Parse.User(); await expectAsync( - user2.save({ authData: { modernAdapter: { id: 'modernAdapter' } } }) + user2.save({ authData: { modernAdapter: { id: "modernAdapter" } } }) ).toBeRejectedWith( - new Parse.Error(Parse.Error.SCRIPT_FAILED, 'You cannot login with that login data.') + new Parse.Error( + Parse.Error.SCRIPT_FAILED, + "You cannot login with that login data." + ) ); expect(logger.error).toHaveBeenCalledWith( `Failed running auth step validateLogin for modernAdapter for user ${user.id} with Error: {"message":"You cannot login with that login data.","code":${Parse.Error.SCRIPT_FAILED}}`, { - authenticationStep: 'validateLogin', - error: new Parse.Error(Parse.Error.SCRIPT_FAILED, 'You cannot login with that login data.'), + authenticationStep: "validateLogin", + error: new Parse.Error( + Parse.Error.SCRIPT_FAILED, + "You cannot login with that login data." + ), user: user.id, - provider: 'modernAdapter', + provider: "modernAdapter", } ); }); - it('should return challenge with no logged user', async () => { - spyOn(challengeAdapter, 'challenge').and.resolveTo({ token: 'test' }); + it("should return challenge with no logged user", async () => { + spyOn(challengeAdapter, "challenge").and.resolveTo({ token: "test" }); await reconfigureServer({ auth: { challengeAdapter }, @@ -984,34 +1030,34 @@ describe('Auth Adapter features', () => { await expectAsync( requestWithExpectedError({ headers: headers, - method: 'POST', - url: 'http://localhost:8378/1/challenge', + method: "POST", + url: "http://localhost:8378/1/challenge", body: {}, }) - ).toBeRejectedWithError('Nothing to challenge.'); + ).toBeRejectedWithError("Nothing to challenge."); await expectAsync( requestWithExpectedError({ headers: headers, - method: 'POST', - url: 'http://localhost:8378/1/challenge', + method: "POST", + url: "http://localhost:8378/1/challenge", body: { challengeData: true }, }) - ).toBeRejectedWithError('challengeData should be an object.'); + ).toBeRejectedWithError("challengeData should be an object."); await expectAsync( requestWithExpectedError({ headers: headers, - method: 'POST', - url: 'http://localhost:8378/1/challenge', + method: "POST", + url: "http://localhost:8378/1/challenge", body: { challengeData: { data: true }, authData: true }, }) - ).toBeRejectedWithError('authData should be an object.'); + ).toBeRejectedWithError("authData should be an object."); const res = await request({ headers: headers, - method: 'POST', - url: 'http://localhost:8378/1/challenge', + method: "POST", + url: "http://localhost:8378/1/challenge", body: JSON.stringify({ challengeData: { challengeAdapter: { someData: true }, @@ -1022,7 +1068,7 @@ describe('Auth Adapter features', () => { expect(res.data).toEqual({ challengeData: { challengeAdapter: { - token: 'test', + token: "test", }, }, }); @@ -1040,8 +1086,8 @@ describe('Auth Adapter features', () => { expect(challengeCall.length).toEqual(4); }); - it('should return empty challenge data response if challenged provider does not exists', async () => { - spyOn(challengeAdapter, 'challenge').and.resolveTo({ token: 'test' }); + it("should return empty challenge data response if challenged provider does not exists", async () => { + spyOn(challengeAdapter, "challenge").and.resolveTo({ token: "test" }); await reconfigureServer({ auth: { challengeAdapter }, @@ -1049,8 +1095,8 @@ describe('Auth Adapter features', () => { const res = await request({ headers: headers, - method: 'POST', - url: 'http://localhost:8378/1/challenge', + method: "POST", + url: "http://localhost:8378/1/challenge", body: JSON.stringify({ challengeData: { nonExistingProvider: { someData: true }, @@ -1060,38 +1106,40 @@ describe('Auth Adapter features', () => { expect(res.data).toEqual({ challengeData: {} }); }); - it('should return challenge with username created user', async () => { - spyOn(challengeAdapter, 'challenge').and.resolveTo({ token: 'test' }); + it("should return challenge with username created user", async () => { + spyOn(challengeAdapter, "challenge").and.resolveTo({ token: "test" }); await reconfigureServer({ auth: { challengeAdapter }, }); const user = new Parse.User(); - await user.save({ username: 'username', password: 'password' }); + await user.save({ username: "username", password: "password" }); await expectAsync( requestWithExpectedError({ headers: headers, - method: 'POST', - url: 'http://localhost:8378/1/challenge', + method: "POST", + url: "http://localhost:8378/1/challenge", body: JSON.stringify({ - username: 'username', + username: "username", challengeData: { challengeAdapter: { someData: true }, }, }), }) - ).toBeRejectedWithError('You provided username or email, you need to also provide password.'); + ).toBeRejectedWithError( + "You provided username or email, you need to also provide password." + ); await expectAsync( requestWithExpectedError({ headers: headers, - method: 'POST', - url: 'http://localhost:8378/1/challenge', + method: "POST", + url: "http://localhost:8378/1/challenge", body: JSON.stringify({ - username: 'username', - password: 'password', + username: "username", + password: "password", authData: { data: true }, challengeData: { challengeAdapter: { someData: true }, @@ -1099,16 +1147,16 @@ describe('Auth Adapter features', () => { }), }) ).toBeRejectedWithError( - 'You cannot provide username/email and authData, only use one identification method.' + "You cannot provide username/email and authData, only use one identification method." ); const res = await request({ headers: headers, - method: 'POST', - url: 'http://localhost:8378/1/challenge', + method: "POST", + url: "http://localhost:8378/1/challenge", body: JSON.stringify({ - username: 'username', - password: 'password', + username: "username", + password: "password", challengeData: { challengeAdapter: { someData: true }, }, @@ -1118,7 +1166,7 @@ describe('Auth Adapter features', () => { expect(res.data).toEqual({ challengeData: { challengeAdapter: { - token: 'test', + token: "test", }, }, }); @@ -1138,9 +1186,9 @@ describe('Auth Adapter features', () => { expect(challengeCall.length).toEqual(4); }); - it('should return challenge with authData created user', async () => { - spyOn(challengeAdapter, 'challenge').and.resolveTo({ token: 'test' }); - spyOn(challengeAdapter, 'validateAuthData').and.callThrough(); + it("should return challenge with authData created user", async () => { + spyOn(challengeAdapter, "challenge").and.resolveTo({ token: "test" }); + spyOn(challengeAdapter, "validateAuthData").and.callThrough(); await reconfigureServer({ auth: { challengeAdapter, soloAdapter }, @@ -1149,52 +1197,56 @@ describe('Auth Adapter features', () => { await expectAsync( requestWithExpectedError({ headers: headers, - method: 'POST', - url: 'http://localhost:8378/1/challenge', + method: "POST", + url: "http://localhost:8378/1/challenge", body: JSON.stringify({ challengeData: { challengeAdapter: { someData: true }, }, authData: { - challengeAdapter: { id: 'challengeAdapter' }, + challengeAdapter: { id: "challengeAdapter" }, }, }), }) - ).toBeRejectedWithError('User not found.'); + ).toBeRejectedWithError("User not found."); const user = new Parse.User(); - await user.save({ authData: { challengeAdapter: { id: 'challengeAdapter' } } }); + await user.save({ + authData: { challengeAdapter: { id: "challengeAdapter" } }, + }); const user2 = new Parse.User(); - await user2.save({ authData: { soloAdapter: { id: 'soloAdapter' } } }); + await user2.save({ authData: { soloAdapter: { id: "soloAdapter" } } }); await expectAsync( requestWithExpectedError({ headers: headers, - method: 'POST', - url: 'http://localhost:8378/1/challenge', + method: "POST", + url: "http://localhost:8378/1/challenge", body: JSON.stringify({ challengeData: { challengeAdapter: { someData: true }, }, authData: { - challengeAdapter: { id: 'challengeAdapter' }, - soloAdapter: { id: 'soloAdapter' }, + challengeAdapter: { id: "challengeAdapter" }, + soloAdapter: { id: "soloAdapter" }, }, }), }) - ).toBeRejectedWithError('You cannot provide more than one authData provider with an id.'); + ).toBeRejectedWithError( + "You cannot provide more than one authData provider with an id." + ); const res = await request({ headers: headers, - method: 'POST', - url: 'http://localhost:8378/1/challenge', + method: "POST", + url: "http://localhost:8378/1/challenge", body: JSON.stringify({ challengeData: { challengeAdapter: { someData: true }, }, authData: { - challengeAdapter: { id: 'challengeAdapter' }, + challengeAdapter: { id: "challengeAdapter" }, }, }), }); @@ -1202,7 +1254,7 @@ describe('Auth Adapter features', () => { expect(res.data).toEqual({ challengeData: { challengeAdapter: { - token: 'test', + token: "test", }, }, }); @@ -1213,7 +1265,7 @@ describe('Auth Adapter features', () => { const challengeCall = challengeAdapter.challenge.calls.argsFor(0); expect(challengeAdapter.challenge).toHaveBeenCalledTimes(1); expect(challengeCall[0]).toEqual({ someData: true }); - expect(challengeCall[1]).toEqual({ id: 'challengeAdapter' }); + expect(challengeCall[1]).toEqual({ id: "challengeAdapter" }); expect(challengeCall[2]).toEqual(challengeAdapter); expect(challengeCall[3].master).toBeDefined(); expect(challengeCall[3].isChallenge).toBeTruthy(); @@ -1224,8 +1276,8 @@ describe('Auth Adapter features', () => { expect(challengeCall.length).toEqual(4); }); - it('should validate provided authData and prevent guess id attack', async () => { - spyOn(challengeAdapter, 'challenge').and.resolveTo({ token: 'test' }); + it("should validate provided authData and prevent guess id attack", async () => { + spyOn(challengeAdapter, "challenge").and.resolveTo({ token: "test" }); await reconfigureServer({ auth: { challengeAdapter, soloAdapter }, @@ -1234,43 +1286,45 @@ describe('Auth Adapter features', () => { await expectAsync( requestWithExpectedError({ headers: headers, - method: 'POST', - url: 'http://localhost:8378/1/challenge', + method: "POST", + url: "http://localhost:8378/1/challenge", body: JSON.stringify({ challengeData: { challengeAdapter: { someData: true }, }, authData: { - challengeAdapter: { id: 'challengeAdapter' }, + challengeAdapter: { id: "challengeAdapter" }, }, }), }) - ).toBeRejectedWithError('User not found.'); + ).toBeRejectedWithError("User not found."); const user = new Parse.User(); - await user.save({ authData: { challengeAdapter: { id: 'challengeAdapter' } } }); + await user.save({ + authData: { challengeAdapter: { id: "challengeAdapter" } }, + }); - spyOn(challengeAdapter, 'validateAuthData').and.rejectWith({}); + spyOn(challengeAdapter, "validateAuthData").and.rejectWith({}); await expectAsync( requestWithExpectedError({ headers: headers, - method: 'POST', - url: 'http://localhost:8378/1/challenge', + method: "POST", + url: "http://localhost:8378/1/challenge", body: JSON.stringify({ challengeData: { challengeAdapter: { someData: true }, }, authData: { - challengeAdapter: { id: 'challengeAdapter' }, + challengeAdapter: { id: "challengeAdapter" }, }, }), }) - ).toBeRejectedWithError('User not found.'); + ).toBeRejectedWithError("User not found."); const validateCall = challengeAdapter.validateAuthData.calls.argsFor(0); expect(challengeAdapter.validateAuthData).toHaveBeenCalledTimes(1); - expect(validateCall[0]).toEqual({ id: 'challengeAdapter' }); + expect(validateCall[0]).toEqual({ id: "challengeAdapter" }); expect(validateCall[1]).toEqual(challengeAdapter); expect(validateCall[2].isChallenge).toBeTruthy(); expect(validateCall[2].master).toBeDefined(); @@ -1281,7 +1335,7 @@ describe('Auth Adapter features', () => { expect(validateCall.length).toEqual(3); }); - it('should work with multiple adapters', async () => { + it("should work with multiple adapters", async () => { const adapterA = { validateAppId: () => Promise.resolve(), validateAuthData: () => Promise.resolve(), @@ -1293,13 +1347,13 @@ describe('Auth Adapter features', () => { await reconfigureServer({ auth: { adapterA, adapterB } }); const user = new Parse.User(); await user.signUp({ - username: 'test', - password: 'password', + username: "test", + password: "password", }); - await user.save({ authData: { adapterA: { id: 'testA' } } }); - expect(user.get('authData')).toEqual({ adapterA: { id: 'testA' } }); - await user.save({ authData: { adapterA: null, adapterB: { id: 'test' } } }); + await user.save({ authData: { adapterA: { id: "testA" } } }); + expect(user.get("authData")).toEqual({ adapterA: { id: "testA" } }); + await user.save({ authData: { adapterA: null, adapterB: { id: "test" } } }); await user.fetch({ useMasterKey: true }); - expect(user.get('authData')).toEqual({ adapterB: { id: 'test' } }); + expect(user.get("authData")).toEqual({ adapterB: { id: "test" } }); }); }); diff --git a/spec/CLI.spec.js b/spec/CLI.spec.js index 76f8e3952d..e3968f4946 100644 --- a/spec/CLI.spec.js +++ b/spec/CLI.spec.js @@ -1,35 +1,36 @@ -'use strict'; +"use strict"; let commander; -const definitions = require('../lib/cli/definitions/parse-server').default; -const liveQueryDefinitions = require('../lib/cli/definitions/parse-live-query-server').default; -const path = require('path'); -const { spawn } = require('child_process'); +const definitions = require("../lib/cli/definitions/parse-server").default; +const liveQueryDefinitions = + require("../lib/cli/definitions/parse-live-query-server").default; +const path = require("path"); +const { spawn } = require("child_process"); const testDefinitions = { - arg0: 'PROGRAM_ARG_0', + arg0: "PROGRAM_ARG_0", arg1: { - env: 'PROGRAM_ARG_1', + env: "PROGRAM_ARG_1", required: true, }, arg2: { - env: 'PROGRAM_ARG_2', + env: "PROGRAM_ARG_2", action: function (value) { const intValue = parseInt(value); if (!Number.isInteger(intValue)) { - throw 'arg2 is invalid'; + throw "arg2 is invalid"; } return intValue; }, }, arg3: {}, arg4: { - default: 'arg4Value', + default: "arg4Value", }, }; -describe('commander additions', () => { +describe("commander additions", () => { beforeEach(() => { - const command = require('../lib/cli/utils/commander').default; + const command = require("../lib/cli/utils/commander").default; commander = new command.constructor(); commander.storeOptionsAsProperties(); commander.allowExcessArguments(); @@ -44,179 +45,209 @@ describe('commander additions', () => { done(); }); - it('should load properly definitions from args', done => { + it("should load properly definitions from args", done => { commander.loadDefinitions(testDefinitions); commander.parse([ - 'node', - './CLI.spec.js', - '--arg0', - 'arg0Value', - '--arg1', - 'arg1Value', - '--arg2', - '2', - '--arg3', - 'some', + "node", + "./CLI.spec.js", + "--arg0", + "arg0Value", + "--arg1", + "arg1Value", + "--arg2", + "2", + "--arg3", + "some", ]); - expect(commander.arg0).toEqual('arg0Value'); - expect(commander.arg1).toEqual('arg1Value'); + expect(commander.arg0).toEqual("arg0Value"); + expect(commander.arg1).toEqual("arg1Value"); expect(commander.arg2).toEqual(2); - expect(commander.arg3).toEqual('some'); - expect(commander.arg4).toEqual('arg4Value'); + expect(commander.arg3).toEqual("some"); + expect(commander.arg4).toEqual("arg4Value"); done(); }); - it('should load properly definitions from env', done => { + it("should load properly definitions from env", done => { commander.loadDefinitions(testDefinitions); commander.parse([], { - PROGRAM_ARG_0: 'arg0ENVValue', - PROGRAM_ARG_1: 'arg1ENVValue', - PROGRAM_ARG_2: '3', + PROGRAM_ARG_0: "arg0ENVValue", + PROGRAM_ARG_1: "arg1ENVValue", + PROGRAM_ARG_2: "3", }); - expect(commander.arg0).toEqual('arg0ENVValue'); - expect(commander.arg1).toEqual('arg1ENVValue'); + expect(commander.arg0).toEqual("arg0ENVValue"); + expect(commander.arg1).toEqual("arg1ENVValue"); expect(commander.arg2).toEqual(3); - expect(commander.arg4).toEqual('arg4Value'); + expect(commander.arg4).toEqual("arg4Value"); done(); }); - it('should load properly use args over env', () => { + it("should load properly use args over env", () => { commander.loadDefinitions(testDefinitions); - commander.parse(['node', './CLI.spec.js', '--arg0', 'arg0Value', '--arg4', ''], { - PROGRAM_ARG_0: 'arg0ENVValue', - PROGRAM_ARG_1: 'arg1ENVValue', - PROGRAM_ARG_2: '4', - PROGRAM_ARG_4: 'arg4ENVValue', - }); - expect(commander.arg0).toEqual('arg0Value'); - expect(commander.arg1).toEqual('arg1ENVValue'); + commander.parse( + ["node", "./CLI.spec.js", "--arg0", "arg0Value", "--arg4", ""], + { + PROGRAM_ARG_0: "arg0ENVValue", + PROGRAM_ARG_1: "arg1ENVValue", + PROGRAM_ARG_2: "4", + PROGRAM_ARG_4: "arg4ENVValue", + } + ); + expect(commander.arg0).toEqual("arg0Value"); + expect(commander.arg1).toEqual("arg1ENVValue"); expect(commander.arg2).toEqual(4); - expect(commander.arg4).toEqual(''); + expect(commander.arg4).toEqual(""); }); - it('should fail in action as port is invalid', done => { + it("should fail in action as port is invalid", done => { commander.loadDefinitions(testDefinitions); expect(() => { - commander.parse(['node', './CLI.spec.js', '--arg0', 'arg0Value'], { - PROGRAM_ARG_0: 'arg0ENVValue', - PROGRAM_ARG_1: 'arg1ENVValue', - PROGRAM_ARG_2: 'hello', + commander.parse(["node", "./CLI.spec.js", "--arg0", "arg0Value"], { + PROGRAM_ARG_0: "arg0ENVValue", + PROGRAM_ARG_1: "arg1ENVValue", + PROGRAM_ARG_2: "hello", }); - }).toThrow('arg2 is invalid'); + }).toThrow("arg2 is invalid"); done(); }); - it('should not override config.json', done => { - spyOn(console, 'log').and.callFake(() => {}); + it("should not override config.json", done => { + spyOn(console, "log").and.callFake(() => {}); commander.loadDefinitions(testDefinitions); commander.parse( - ['node', './CLI.spec.js', '--arg0', 'arg0Value', './spec/configs/CLIConfig.json'], + [ + "node", + "./CLI.spec.js", + "--arg0", + "arg0Value", + "./spec/configs/CLIConfig.json", + ], { - PROGRAM_ARG_0: 'arg0ENVValue', - PROGRAM_ARG_1: 'arg1ENVValue', + PROGRAM_ARG_0: "arg0ENVValue", + PROGRAM_ARG_1: "arg1ENVValue", } ); const options = commander.getOptions(); expect(options.arg2).toBe(8888); - expect(options.arg3).toBe('hello'); //config value - expect(options.arg4).toBe('/1'); + expect(options.arg3).toBe("hello"); //config value + expect(options.arg4).toBe("/1"); done(); }); - it('should fail with invalid values in JSON', done => { + it("should fail with invalid values in JSON", done => { commander.loadDefinitions(testDefinitions); expect(() => { commander.parse( - ['node', './CLI.spec.js', '--arg0', 'arg0Value', './spec/configs/CLIConfigFail.json'], + [ + "node", + "./CLI.spec.js", + "--arg0", + "arg0Value", + "./spec/configs/CLIConfigFail.json", + ], { - PROGRAM_ARG_0: 'arg0ENVValue', - PROGRAM_ARG_1: 'arg1ENVValue', + PROGRAM_ARG_0: "arg0ENVValue", + PROGRAM_ARG_1: "arg1ENVValue", } ); - }).toThrow('arg2 is invalid'); + }).toThrow("arg2 is invalid"); done(); }); - it('should fail when too many apps are set', done => { + it("should fail when too many apps are set", done => { commander.loadDefinitions(testDefinitions); expect(() => { - commander.parse(['node', './CLI.spec.js', './spec/configs/CLIConfigFailTooManyApps.json']); - }).toThrow('Multiple apps are not supported'); + commander.parse([ + "node", + "./CLI.spec.js", + "./spec/configs/CLIConfigFailTooManyApps.json", + ]); + }).toThrow("Multiple apps are not supported"); done(); }); - it('should load config from apps', done => { - spyOn(console, 'log').and.callFake(() => {}); + it("should load config from apps", done => { + spyOn(console, "log").and.callFake(() => {}); commander.loadDefinitions(testDefinitions); - commander.parse(['node', './CLI.spec.js', './spec/configs/CLIConfigApps.json']); + commander.parse([ + "node", + "./CLI.spec.js", + "./spec/configs/CLIConfigApps.json", + ]); const options = commander.getOptions(); - expect(options.arg1).toBe('my_app'); + expect(options.arg1).toBe("my_app"); expect(options.arg2).toBe(8888); - expect(options.arg3).toBe('hello'); //config value - expect(options.arg4).toBe('/1'); + expect(options.arg3).toBe("hello"); //config value + expect(options.arg4).toBe("/1"); done(); }); - it('should fail when passing an invalid arguement', done => { + it("should fail when passing an invalid arguement", done => { commander.loadDefinitions(testDefinitions); expect(() => { - commander.parse(['node', './CLI.spec.js', './spec/configs/CLIConfigUnknownArg.json']); - }).toThrow('error: unknown option myArg'); + commander.parse([ + "node", + "./CLI.spec.js", + "./spec/configs/CLIConfigUnknownArg.json", + ]); + }).toThrow("error: unknown option myArg"); done(); }); }); -describe('definitions', () => { - it('should have valid types', () => { +describe("definitions", () => { + it("should have valid types", () => { for (const key in definitions) { const definition = definitions[key]; - expect(typeof definition).toBe('object'); - if (typeof definition.env !== 'undefined') { - expect(typeof definition.env).toBe('string'); + expect(typeof definition).toBe("object"); + if (typeof definition.env !== "undefined") { + expect(typeof definition.env).toBe("string"); } - expect(typeof definition.help).toBe('string'); - if (typeof definition.required !== 'undefined') { - expect(typeof definition.required).toBe('boolean'); + expect(typeof definition.help).toBe("string"); + if (typeof definition.required !== "undefined") { + expect(typeof definition.required).toBe("boolean"); } - if (typeof definition.action !== 'undefined') { - expect(typeof definition.action).toBe('function'); + if (typeof definition.action !== "undefined") { + expect(typeof definition.action).toBe("function"); } } }); - it('should throw when using deprecated facebookAppIds', () => { + it("should throw when using deprecated facebookAppIds", () => { expect(() => { definitions.facebookAppIds.action(); }).toThrow(); }); }); -describe('LiveQuery definitions', () => { - it('should have valid types', () => { +describe("LiveQuery definitions", () => { + it("should have valid types", () => { for (const key in liveQueryDefinitions) { const definition = liveQueryDefinitions[key]; - expect(typeof definition).toBe('object'); - if (typeof definition.env !== 'undefined') { - expect(typeof definition.env).toBe('string'); + expect(typeof definition).toBe("object"); + if (typeof definition.env !== "undefined") { + expect(typeof definition.env).toBe("string"); } - expect(typeof definition.help).toBe('string', `help for ${key} should be a string`); - if (typeof definition.required !== 'undefined') { - expect(typeof definition.required).toBe('boolean'); + expect(typeof definition.help).toBe( + "string", + `help for ${key} should be a string` + ); + if (typeof definition.required !== "undefined") { + expect(typeof definition.required).toBe("boolean"); } - if (typeof definition.action !== 'undefined') { - expect(typeof definition.action).toBe('function'); + if (typeof definition.action !== "undefined") { + expect(typeof definition.action).toBe("function"); } } }); }); -describe('execution', () => { - const binPath = path.resolve(__dirname, '../bin/parse-server'); +describe("execution", () => { + const binPath = path.resolve(__dirname, "../bin/parse-server"); let childProcess; let aggregatedData; function handleStdout(childProcess, done, aggregatedData, requiredData) { - childProcess.stdout.on('data', data => { + childProcess.stdout.on("data", data => { data = data.toString(); aggregatedData.push(data); if ( @@ -230,16 +261,16 @@ describe('execution', () => { } function handleStderr(childProcess, done) { - childProcess.stderr.on('data', data => { + childProcess.stderr.on("data", data => { data = data.toString(); - if (!data.includes('[DEP0040] DeprecationWarning')) { + if (!data.includes("[DEP0040] DeprecationWarning")) { done.fail(data); } }); } function handleError(childProcess, done) { - childProcess.on('error', err => { + childProcess.on("error", err => { done.fail(err); }); } @@ -250,7 +281,7 @@ describe('execution', () => { afterEach(done => { if (childProcess) { - childProcess.on('close', () => { + childProcess.on("close", () => { childProcess = undefined; done(); }); @@ -258,90 +289,106 @@ describe('execution', () => { } }); - it_id('a0ab74b4-f805-4e03-b31d-b5cd59e64495')(it)('should start Parse Server', done => { - const env = { ...process.env }; - env.NODE_OPTIONS = '--dns-result-order=ipv4first --trace-deprecation'; - childProcess = spawn( - binPath, - ['--appId', 'test', '--masterKey', 'test', '--databaseURI', databaseURI, '--port', '1339'], - { env } - ); - handleStdout(childProcess, done, aggregatedData, ['parse-server running on']); - handleStderr(childProcess, done); - handleError(childProcess, done); - }); + it_id("a0ab74b4-f805-4e03-b31d-b5cd59e64495")(it)( + "should start Parse Server", + done => { + const env = { ...process.env }; + env.NODE_OPTIONS = "--dns-result-order=ipv4first --trace-deprecation"; + childProcess = spawn( + binPath, + [ + "--appId", + "test", + "--masterKey", + "test", + "--databaseURI", + databaseURI, + "--port", + "1339", + ], + { env } + ); + handleStdout(childProcess, done, aggregatedData, [ + "parse-server running on", + ]); + handleStderr(childProcess, done); + handleError(childProcess, done); + } + ); - it_id('d7165081-b133-4cba-901b-19128ce41301')(it)( - 'should start Parse Server with GraphQL', + it_id("d7165081-b133-4cba-901b-19128ce41301")(it)( + "should start Parse Server with GraphQL", async done => { const env = { ...process.env }; - env.NODE_OPTIONS = '--dns-result-order=ipv4first --trace-deprecation'; + env.NODE_OPTIONS = "--dns-result-order=ipv4first --trace-deprecation"; childProcess = spawn( binPath, [ - '--appId', - 'test', - '--masterKey', - 'test', - '--databaseURI', + "--appId", + "test", + "--masterKey", + "test", + "--databaseURI", databaseURI, - '--port', - '1340', - '--mountGraphQL', + "--port", + "1340", + "--mountGraphQL", ], { env } ); handleStdout(childProcess, done, aggregatedData, [ - 'parse-server running on', - 'GraphQL running on', + "parse-server running on", + "GraphQL running on", ]); handleStderr(childProcess, done); handleError(childProcess, done); } ); - it_id('2769cdb4-ce8a-484d-8a91-635b5894ba7e')(it)( - 'should start Parse Server with GraphQL and Playground', + it_id("2769cdb4-ce8a-484d-8a91-635b5894ba7e")(it)( + "should start Parse Server with GraphQL and Playground", async done => { const env = { ...process.env }; - env.NODE_OPTIONS = '--dns-result-order=ipv4first --trace-deprecation'; + env.NODE_OPTIONS = "--dns-result-order=ipv4first --trace-deprecation"; childProcess = spawn( binPath, [ - '--appId', - 'test', - '--masterKey', - 'test', - '--databaseURI', + "--appId", + "test", + "--masterKey", + "test", + "--databaseURI", databaseURI, - '--port', - '1341', - '--mountGraphQL', - '--mountPlayground', + "--port", + "1341", + "--mountGraphQL", + "--mountPlayground", ], { env } ); handleStdout(childProcess, done, aggregatedData, [ - 'parse-server running on', - 'Playground running on', - 'GraphQL running on', + "parse-server running on", + "Playground running on", + "GraphQL running on", ]); handleStderr(childProcess, done); handleError(childProcess, done); } ); - it_id('23caddd7-bfea-4869-8bd4-0f2cd283c8bd')(it)( - 'can start Parse Server with auth via CLI', + it_id("23caddd7-bfea-4869-8bd4-0f2cd283c8bd")(it)( + "can start Parse Server with auth via CLI", done => { const env = { ...process.env }; - env.NODE_OPTIONS = '--dns-result-order=ipv4first --trace-deprecation'; + env.NODE_OPTIONS = "--dns-result-order=ipv4first --trace-deprecation"; childProcess = spawn( binPath, - ['--databaseURI', databaseURI, './spec/configs/CLIConfigAuth.json'], + ["--databaseURI", databaseURI, "./spec/configs/CLIConfigAuth.json"], { env } ); - handleStdout(childProcess, done, aggregatedData, ['parse-server running on']); + handleStdout(childProcess, done, aggregatedData, [ + "parse-server running on", + ]); handleStderr(childProcess, done); handleError(childProcess, done); } diff --git a/spec/CacheController.spec.js b/spec/CacheController.spec.js index de07126214..6b6678d01f 100644 --- a/spec/CacheController.spec.js +++ b/spec/CacheController.spec.js @@ -1,22 +1,23 @@ -const CacheController = require('../lib/Controllers/CacheController.js').default; +const CacheController = + require("../lib/Controllers/CacheController.js").default; -describe('CacheController', function () { +describe("CacheController", function () { let FakeCacheAdapter; - const FakeAppID = 'foo'; - const KEY = 'hello'; + const FakeAppID = "foo"; + const KEY = "hello"; beforeEach(() => { FakeCacheAdapter = { get: () => Promise.resolve(null), - put: jasmine.createSpy('put'), - del: jasmine.createSpy('del'), - clear: jasmine.createSpy('clear'), + put: jasmine.createSpy("put"), + del: jasmine.createSpy("del"), + clear: jasmine.createSpy("clear"), }; - spyOn(FakeCacheAdapter, 'get').and.callThrough(); + spyOn(FakeCacheAdapter, "get").and.callThrough(); }); - it('should expose role and user caches', done => { + it("should expose role and user caches", done => { const cache = new CacheController(FakeCacheAdapter, FakeAppID); expect(cache.role).not.toEqual(null); @@ -27,25 +28,25 @@ describe('CacheController', function () { done(); }); - ['role', 'user'].forEach(cacheName => { - it('should prefix ' + cacheName + ' cache', () => { + ["role", "user"].forEach(cacheName => { + it("should prefix " + cacheName + " cache", () => { const cache = new CacheController(FakeCacheAdapter, FakeAppID)[cacheName]; - cache.put(KEY, 'world'); + cache.put(KEY, "world"); const firstPut = FakeCacheAdapter.put.calls.first(); - expect(firstPut.args[0]).toEqual([FakeAppID, cacheName, KEY].join(':')); + expect(firstPut.args[0]).toEqual([FakeAppID, cacheName, KEY].join(":")); cache.get(KEY); const firstGet = FakeCacheAdapter.get.calls.first(); - expect(firstGet.args[0]).toEqual([FakeAppID, cacheName, KEY].join(':')); + expect(firstGet.args[0]).toEqual([FakeAppID, cacheName, KEY].join(":")); cache.del(KEY); const firstDel = FakeCacheAdapter.del.calls.first(); - expect(firstDel.args[0]).toEqual([FakeAppID, cacheName, KEY].join(':')); + expect(firstDel.args[0]).toEqual([FakeAppID, cacheName, KEY].join(":")); }); }); - it('should clear the entire cache', () => { + it("should clear the entire cache", () => { const cache = new CacheController(FakeCacheAdapter, FakeAppID); cache.clear(); @@ -58,13 +59,13 @@ describe('CacheController', function () { expect(FakeCacheAdapter.clear.calls.count()).toEqual(3); }); - it('should handle cache rejections', done => { + it("should handle cache rejections", done => { FakeCacheAdapter.get = () => Promise.reject(); const cache = new CacheController(FakeCacheAdapter, FakeAppID); - cache.get('foo').then(done, () => { - fail('Promise should not be rejected.'); + cache.get("foo").then(done, () => { + fail("Promise should not be rejected."); }); }); }); diff --git a/spec/Client.spec.js b/spec/Client.spec.js index 0de226204a..6b1e8283c7 100644 --- a/spec/Client.spec.js +++ b/spec/Client.spec.js @@ -1,8 +1,9 @@ -const Client = require('../lib/LiveQuery/Client').Client; -const ParseWebSocket = require('../lib/LiveQuery/ParseWebSocketServer').ParseWebSocket; +const Client = require("../lib/LiveQuery/Client").Client; +const ParseWebSocket = + require("../lib/LiveQuery/ParseWebSocketServer").ParseWebSocket; -describe('Client', function () { - it('can be initialized', function () { +describe("Client", function () { + it("can be initialized", function () { const parseWebSocket = new ParseWebSocket({}); const client = new Client(1, parseWebSocket); @@ -11,32 +12,32 @@ describe('Client', function () { expect(client.subscriptionInfos.size).toBe(0); }); - it('can push response', function () { + it("can push response", function () { const parseWebSocket = { - send: jasmine.createSpy('send'), + send: jasmine.createSpy("send"), }; - Client.pushResponse(parseWebSocket, 'message'); + Client.pushResponse(parseWebSocket, "message"); - expect(parseWebSocket.send).toHaveBeenCalledWith('message'); + expect(parseWebSocket.send).toHaveBeenCalledWith("message"); }); - it('can push error', function () { + it("can push error", function () { const parseWebSocket = { - send: jasmine.createSpy('send'), + send: jasmine.createSpy("send"), }; - Client.pushError(parseWebSocket, 1, 'error', true); + Client.pushError(parseWebSocket, 1, "error", true); const lastCall = parseWebSocket.send.calls.first(); const messageJSON = JSON.parse(lastCall.args[0]); - expect(messageJSON.op).toBe('error'); - expect(messageJSON.error).toBe('error'); + expect(messageJSON.op).toBe("error"); + expect(messageJSON.error).toBe("error"); expect(messageJSON.code).toBe(1); expect(messageJSON.reconnect).toBe(true); }); - it('can add subscription information', function () { + it("can add subscription information", function () { const subscription = {}; - const fields = ['test']; + const fields = ["test"]; const subscriptionInfo = { subscription: subscription, fields: fields, @@ -48,9 +49,9 @@ describe('Client', function () { expect(client.subscriptionInfos.get(1)).toBe(subscriptionInfo); }); - it('can get subscription information', function () { + it("can get subscription information", function () { const subscription = {}; - const fields = ['test']; + const fields = ["test"]; const subscriptionInfo = { subscription: subscription, fields: fields, @@ -62,9 +63,9 @@ describe('Client', function () { expect(subscriptionInfoAgain).toBe(subscriptionInfo); }); - it('can delete subscription information', function () { + it("can delete subscription information", function () { const subscription = {}; - const fields = ['test']; + const fields = ["test"]; const subscriptionInfo = { subscription: subscription, fields: fields, @@ -76,211 +77,217 @@ describe('Client', function () { expect(client.subscriptionInfos.size).toBe(0); }); - it('can generate ParseObject JSON with null selected field', function () { + it("can generate ParseObject JSON with null selected field", function () { const parseObjectJSON = { - key: 'value', - className: 'test', - objectId: 'test', - updatedAt: '2015-12-07T21:27:13.746Z', - createdAt: '2015-12-07T21:27:13.746Z', - ACL: 'test', + key: "value", + className: "test", + objectId: "test", + updatedAt: "2015-12-07T21:27:13.746Z", + createdAt: "2015-12-07T21:27:13.746Z", + ACL: "test", }; const client = new Client(1, {}); - expect(client._toJSONWithFields(parseObjectJSON, null)).toBe(parseObjectJSON); + expect(client._toJSONWithFields(parseObjectJSON, null)).toBe( + parseObjectJSON + ); }); - it('can generate ParseObject JSON with undefined selected field', function () { + it("can generate ParseObject JSON with undefined selected field", function () { const parseObjectJSON = { - key: 'value', - className: 'test', - objectId: 'test', - updatedAt: '2015-12-07T21:27:13.746Z', - createdAt: '2015-12-07T21:27:13.746Z', - ACL: 'test', + key: "value", + className: "test", + objectId: "test", + updatedAt: "2015-12-07T21:27:13.746Z", + createdAt: "2015-12-07T21:27:13.746Z", + ACL: "test", }; const client = new Client(1, {}); - expect(client._toJSONWithFields(parseObjectJSON, undefined)).toBe(parseObjectJSON); + expect(client._toJSONWithFields(parseObjectJSON, undefined)).toBe( + parseObjectJSON + ); }); - it('can generate ParseObject JSON with selected fields', function () { + it("can generate ParseObject JSON with selected fields", function () { const parseObjectJSON = { - key: 'value', - className: 'test', - objectId: 'test', - updatedAt: '2015-12-07T21:27:13.746Z', - createdAt: '2015-12-07T21:27:13.746Z', - ACL: 'test', - test: 'test', + key: "value", + className: "test", + objectId: "test", + updatedAt: "2015-12-07T21:27:13.746Z", + createdAt: "2015-12-07T21:27:13.746Z", + ACL: "test", + test: "test", }; const client = new Client(1, {}); - expect(client._toJSONWithFields(parseObjectJSON, ['test'])).toEqual({ - className: 'test', - objectId: 'test', - updatedAt: '2015-12-07T21:27:13.746Z', - createdAt: '2015-12-07T21:27:13.746Z', - ACL: 'test', - test: 'test', + expect(client._toJSONWithFields(parseObjectJSON, ["test"])).toEqual({ + className: "test", + objectId: "test", + updatedAt: "2015-12-07T21:27:13.746Z", + createdAt: "2015-12-07T21:27:13.746Z", + ACL: "test", + test: "test", }); }); - it('can generate ParseObject JSON with nonexistent selected fields', function () { + it("can generate ParseObject JSON with nonexistent selected fields", function () { const parseObjectJSON = { - key: 'value', - className: 'test', - objectId: 'test', - updatedAt: '2015-12-07T21:27:13.746Z', - createdAt: '2015-12-07T21:27:13.746Z', - ACL: 'test', - test: 'test', + key: "value", + className: "test", + objectId: "test", + updatedAt: "2015-12-07T21:27:13.746Z", + createdAt: "2015-12-07T21:27:13.746Z", + ACL: "test", + test: "test", }; const client = new Client(1, {}); - const limitedParseObject = client._toJSONWithFields(parseObjectJSON, ['name']); + const limitedParseObject = client._toJSONWithFields(parseObjectJSON, [ + "name", + ]); expect(limitedParseObject).toEqual({ - className: 'test', - objectId: 'test', - updatedAt: '2015-12-07T21:27:13.746Z', - createdAt: '2015-12-07T21:27:13.746Z', - ACL: 'test', + className: "test", + objectId: "test", + updatedAt: "2015-12-07T21:27:13.746Z", + createdAt: "2015-12-07T21:27:13.746Z", + ACL: "test", }); - expect('name' in limitedParseObject).toBe(false); + expect("name" in limitedParseObject).toBe(false); }); - it('can push connect response', function () { + it("can push connect response", function () { const parseWebSocket = { - send: jasmine.createSpy('send'), + send: jasmine.createSpy("send"), }; const client = new Client(1, parseWebSocket); client.pushConnect(); const lastCall = parseWebSocket.send.calls.first(); const messageJSON = JSON.parse(lastCall.args[0]); - expect(messageJSON.op).toBe('connected'); + expect(messageJSON.op).toBe("connected"); expect(messageJSON.clientId).toBe(1); }); - it('can push subscribe response', function () { + it("can push subscribe response", function () { const parseWebSocket = { - send: jasmine.createSpy('send'), + send: jasmine.createSpy("send"), }; const client = new Client(1, parseWebSocket); client.pushSubscribe(2); const lastCall = parseWebSocket.send.calls.first(); const messageJSON = JSON.parse(lastCall.args[0]); - expect(messageJSON.op).toBe('subscribed'); + expect(messageJSON.op).toBe("subscribed"); expect(messageJSON.clientId).toBe(1); expect(messageJSON.requestId).toBe(2); }); - it('can push unsubscribe response', function () { + it("can push unsubscribe response", function () { const parseWebSocket = { - send: jasmine.createSpy('send'), + send: jasmine.createSpy("send"), }; const client = new Client(1, parseWebSocket); client.pushUnsubscribe(2); const lastCall = parseWebSocket.send.calls.first(); const messageJSON = JSON.parse(lastCall.args[0]); - expect(messageJSON.op).toBe('unsubscribed'); + expect(messageJSON.op).toBe("unsubscribed"); expect(messageJSON.clientId).toBe(1); expect(messageJSON.requestId).toBe(2); }); - it('can push create response', function () { + it("can push create response", function () { const parseObjectJSON = { - key: 'value', - className: 'test', - objectId: 'test', - updatedAt: '2015-12-07T21:27:13.746Z', - createdAt: '2015-12-07T21:27:13.746Z', - ACL: 'test', - test: 'test', + key: "value", + className: "test", + objectId: "test", + updatedAt: "2015-12-07T21:27:13.746Z", + createdAt: "2015-12-07T21:27:13.746Z", + ACL: "test", + test: "test", }; const parseWebSocket = { - send: jasmine.createSpy('send'), + send: jasmine.createSpy("send"), }; const client = new Client(1, parseWebSocket); client.pushCreate(2, parseObjectJSON); const lastCall = parseWebSocket.send.calls.first(); const messageJSON = JSON.parse(lastCall.args[0]); - expect(messageJSON.op).toBe('create'); + expect(messageJSON.op).toBe("create"); expect(messageJSON.clientId).toBe(1); expect(messageJSON.requestId).toBe(2); expect(messageJSON.object).toEqual(parseObjectJSON); }); - it('can push enter response', function () { + it("can push enter response", function () { const parseObjectJSON = { - key: 'value', - className: 'test', - objectId: 'test', - updatedAt: '2015-12-07T21:27:13.746Z', - createdAt: '2015-12-07T21:27:13.746Z', - ACL: 'test', - test: 'test', + key: "value", + className: "test", + objectId: "test", + updatedAt: "2015-12-07T21:27:13.746Z", + createdAt: "2015-12-07T21:27:13.746Z", + ACL: "test", + test: "test", }; const parseWebSocket = { - send: jasmine.createSpy('send'), + send: jasmine.createSpy("send"), }; const client = new Client(1, parseWebSocket); client.pushEnter(2, parseObjectJSON); const lastCall = parseWebSocket.send.calls.first(); const messageJSON = JSON.parse(lastCall.args[0]); - expect(messageJSON.op).toBe('enter'); + expect(messageJSON.op).toBe("enter"); expect(messageJSON.clientId).toBe(1); expect(messageJSON.requestId).toBe(2); expect(messageJSON.object).toEqual(parseObjectJSON); }); - it('can push update response', function () { + it("can push update response", function () { const parseObjectJSON = { - key: 'value', - className: 'test', - objectId: 'test', - updatedAt: '2015-12-07T21:27:13.746Z', - createdAt: '2015-12-07T21:27:13.746Z', - ACL: 'test', - test: 'test', + key: "value", + className: "test", + objectId: "test", + updatedAt: "2015-12-07T21:27:13.746Z", + createdAt: "2015-12-07T21:27:13.746Z", + ACL: "test", + test: "test", }; const parseWebSocket = { - send: jasmine.createSpy('send'), + send: jasmine.createSpy("send"), }; const client = new Client(1, parseWebSocket); client.pushUpdate(2, parseObjectJSON); const lastCall = parseWebSocket.send.calls.first(); const messageJSON = JSON.parse(lastCall.args[0]); - expect(messageJSON.op).toBe('update'); + expect(messageJSON.op).toBe("update"); expect(messageJSON.clientId).toBe(1); expect(messageJSON.requestId).toBe(2); expect(messageJSON.object).toEqual(parseObjectJSON); }); - it('can push leave response', function () { + it("can push leave response", function () { const parseObjectJSON = { - key: 'value', - className: 'test', - objectId: 'test', - updatedAt: '2015-12-07T21:27:13.746Z', - createdAt: '2015-12-07T21:27:13.746Z', - ACL: 'test', - test: 'test', + key: "value", + className: "test", + objectId: "test", + updatedAt: "2015-12-07T21:27:13.746Z", + createdAt: "2015-12-07T21:27:13.746Z", + ACL: "test", + test: "test", }; const parseWebSocket = { - send: jasmine.createSpy('send'), + send: jasmine.createSpy("send"), }; const client = new Client(1, parseWebSocket); client.pushLeave(2, parseObjectJSON); const lastCall = parseWebSocket.send.calls.first(); const messageJSON = JSON.parse(lastCall.args[0]); - expect(messageJSON.op).toBe('leave'); + expect(messageJSON.op).toBe("leave"); expect(messageJSON.clientId).toBe(1); expect(messageJSON.requestId).toBe(2); expect(messageJSON.object).toEqual(parseObjectJSON); diff --git a/spec/ClientSDK.spec.js b/spec/ClientSDK.spec.js index 987770833c..80c0167417 100644 --- a/spec/ClientSDK.spec.js +++ b/spec/ClientSDK.spec.js @@ -1,48 +1,48 @@ -const ClientSDK = require('../lib/ClientSDK'); +const ClientSDK = require("../lib/ClientSDK"); -describe('ClientSDK', () => { - it('should properly parse the SDK versions', () => { +describe("ClientSDK", () => { + it("should properly parse the SDK versions", () => { const clientSDKFromVersion = ClientSDK.fromString; - expect(clientSDKFromVersion('i1.1.1')).toEqual({ - sdk: 'i', - version: '1.1.1', + expect(clientSDKFromVersion("i1.1.1")).toEqual({ + sdk: "i", + version: "1.1.1", }); - expect(clientSDKFromVersion('i1')).toEqual({ - sdk: 'i', - version: '1', + expect(clientSDKFromVersion("i1")).toEqual({ + sdk: "i", + version: "1", }); - expect(clientSDKFromVersion('apple-tv1.13.0')).toEqual({ - sdk: 'apple-tv', - version: '1.13.0', + expect(clientSDKFromVersion("apple-tv1.13.0")).toEqual({ + sdk: "apple-tv", + version: "1.13.0", }); - expect(clientSDKFromVersion('js1.9.0')).toEqual({ - sdk: 'js', - version: '1.9.0', + expect(clientSDKFromVersion("js1.9.0")).toEqual({ + sdk: "js", + version: "1.9.0", }); }); - it('should properly sastisfy', () => { + it("should properly sastisfy", () => { expect( ClientSDK.compatible({ - js: '>=1.9.0', - })('js1.9.0') + js: ">=1.9.0", + })("js1.9.0") ).toBe(true); expect( ClientSDK.compatible({ - js: '>=1.9.0', - })('js2.0.0') + js: ">=1.9.0", + })("js2.0.0") ).toBe(true); expect( ClientSDK.compatible({ - js: '>=1.9.0', - })('js1.8.0') + js: ">=1.9.0", + })("js1.8.0") ).toBe(false); expect( ClientSDK.compatible({ - js: '>=1.9.0', + js: ">=1.9.0", })(undefined) ).toBe(true); }); diff --git a/spec/CloudCode.Validator.spec.js b/spec/CloudCode.Validator.spec.js index 9b33ddc213..a52410e9c4 100644 --- a/spec/CloudCode.Validator.spec.js +++ b/spec/CloudCode.Validator.spec.js @@ -1,85 +1,88 @@ -'use strict'; -const Parse = require('parse/node'); +"use strict"; +const Parse = require("parse/node"); const validatorFail = () => { - throw 'you are not authorized'; + throw "you are not authorized"; }; const validatorSuccess = () => { return true; }; function testConfig() { - return Parse.Config.save({ internal: 'i', string: 's', number: 12 }, { internal: true }); + return Parse.Config.save( + { internal: "i", string: "s", number: 12 }, + { internal: true } + ); } -describe('cloud validator', () => { - it('complete validator', async done => { +describe("cloud validator", () => { + it("complete validator", async done => { Parse.Cloud.define( - 'myFunction', + "myFunction", () => { - return 'myFunc'; + return "myFunc"; }, () => {} ); try { - const result = await Parse.Cloud.run('myFunction', {}); - expect(result).toBe('myFunc'); + const result = await Parse.Cloud.run("myFunction", {}); + expect(result).toBe("myFunc"); done(); } catch (e) { - fail('should not have thrown error'); + fail("should not have thrown error"); } }); - it('Throw from validator', async done => { + it("Throw from validator", async done => { Parse.Cloud.define( - 'myFunction', + "myFunction", () => { - return 'myFunc'; + return "myFunc"; }, () => { - throw 'error'; + throw "error"; } ); try { - await Parse.Cloud.run('myFunction'); - fail('cloud function should have failed.'); + await Parse.Cloud.run("myFunction"); + fail("cloud function should have failed."); } catch (e) { expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); done(); } }); - it('validator can throw parse error', async done => { + it("validator can throw parse error", async done => { Parse.Cloud.define( - 'myFunction', + "myFunction", () => { - return 'myFunc'; + return "myFunc"; }, () => { - throw new Parse.Error(Parse.Error.SCRIPT_FAILED, 'It should fail'); + throw new Parse.Error(Parse.Error.SCRIPT_FAILED, "It should fail"); } ); try { - await Parse.Cloud.run('myFunction'); - fail('should have validation error'); + await Parse.Cloud.run("myFunction"); + fail("should have validation error"); } catch (e) { expect(e.code).toBe(Parse.Error.SCRIPT_FAILED); - expect(e.message).toBe('It should fail'); + expect(e.message).toBe("It should fail"); done(); } }); - it('validator can throw parse error with no message', async done => { + it("validator can throw parse error with no message", async done => { Parse.Cloud.define( - 'myFunction', + "myFunction", () => { - return 'myFunc'; + return "myFunc"; }, () => { throw new Parse.Error(Parse.Error.SCRIPT_FAILED); } ); try { - await Parse.Cloud.run('myFunction'); - fail('should have validation error'); + await Parse.Cloud.run("myFunction"); + fail("should have validation error"); } catch (e) { expect(e.code).toBe(Parse.Error.SCRIPT_FAILED); expect(e.message).toBeUndefined(); @@ -87,30 +90,30 @@ describe('cloud validator', () => { } }); - it('async validator', async done => { + it("async validator", async done => { Parse.Cloud.define( - 'myFunction', + "myFunction", () => { - return 'myFunc'; + return "myFunc"; }, async () => { await new Promise(resolve => { setTimeout(resolve, 1000); }); - throw 'async error'; + throw "async error"; } ); try { - await Parse.Cloud.run('myFunction'); - fail('should have validation error'); + await Parse.Cloud.run("myFunction"); + fail("should have validation error"); } catch (e) { expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); - expect(e.message).toBe('async error'); + expect(e.message).toBe("async error"); done(); } }); - it('pass function to validator', async done => { + it("pass function to validator", async done => { const validator = request => { expect(request).toBeDefined(); expect(request.params).toBeDefined(); @@ -124,104 +127,108 @@ describe('cloud validator', () => { done(); }; Parse.Cloud.define( - 'myFunction', + "myFunction", () => { - return 'myFunc'; + return "myFunc"; }, validator ); - await Parse.Cloud.run('myFunction'); + await Parse.Cloud.run("myFunction"); }); - it('require user on cloud functions', async done => { + it("require user on cloud functions", async done => { Parse.Cloud.define( - 'hello1', + "hello1", () => { - return 'Hello world!'; + return "Hello world!"; }, { requireUser: true, } ); try { - await Parse.Cloud.run('hello1', {}); - fail('function should have failed.'); + await Parse.Cloud.run("hello1", {}); + fail("function should have failed."); } catch (error) { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual('Validation failed. Please login to continue.'); + expect(error.message).toEqual( + "Validation failed. Please login to continue." + ); done(); } }); - it('require master on cloud functions', done => { + it("require master on cloud functions", done => { Parse.Cloud.define( - 'hello2', + "hello2", () => { - return 'Hello world!'; + return "Hello world!"; }, { requireMaster: true, } ); - Parse.Cloud.run('hello2', {}) + Parse.Cloud.run("hello2", {}) .then(() => { - fail('function should have failed.'); + fail("function should have failed."); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); expect(error.message).toEqual( - 'Validation failed. Master key is required to complete this request.' + "Validation failed. Master key is required to complete this request." ); done(); }); }); - it('set params on cloud functions', done => { + it("set params on cloud functions", done => { Parse.Cloud.define( - 'hello', + "hello", () => { - return 'Hello world!'; + return "Hello world!"; }, { - fields: ['a'], + fields: ["a"], } ); - Parse.Cloud.run('hello', {}) + Parse.Cloud.run("hello", {}) .then(() => { - fail('function should have failed.'); + fail("function should have failed."); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual('Validation failed. Please specify data for a.'); + expect(error.message).toEqual( + "Validation failed. Please specify data for a." + ); done(); }); }); - it('allow params on cloud functions', done => { + it("allow params on cloud functions", done => { Parse.Cloud.define( - 'hello', + "hello", req => { - expect(req.params.a).toEqual('yolo'); - return 'Hello world!'; + expect(req.params.a).toEqual("yolo"); + return "Hello world!"; }, { - fields: ['a'], + fields: ["a"], } ); - Parse.Cloud.run('hello', { a: 'yolo' }) + Parse.Cloud.run("hello", { a: "yolo" }) .then(() => { done(); }) .catch(() => { - fail('Error should not have been called.'); + fail("Error should not have been called."); }); }); - it('set params type array', done => { + it("set params type array", done => { Parse.Cloud.define( - 'hello', + "hello", () => { - return 'Hello world!'; + return "Hello world!"; }, { fields: { @@ -231,22 +238,24 @@ describe('cloud validator', () => { }, } ); - Parse.Cloud.run('hello', { data: '' }) + Parse.Cloud.run("hello", { data: "" }) .then(() => { - fail('function should have failed.'); + fail("function should have failed."); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual('Validation failed. Invalid type for data. Expected: array'); + expect(error.message).toEqual( + "Validation failed. Invalid type for data. Expected: array" + ); done(); }); }); - it('set params type allow array', async () => { + it("set params type allow array", async () => { Parse.Cloud.define( - 'hello', + "hello", () => { - return 'Hello world!'; + return "Hello world!"; }, { fields: { @@ -256,15 +265,15 @@ describe('cloud validator', () => { }, } ); - const result = await Parse.Cloud.run('hello', { data: [{ foo: 'bar' }] }); - expect(result).toBe('Hello world!'); + const result = await Parse.Cloud.run("hello", { data: [{ foo: "bar" }] }); + expect(result).toBe("Hello world!"); }); - it('set params type', done => { + it("set params type", done => { Parse.Cloud.define( - 'hello', + "hello", () => { - return 'Hello world!'; + return "Hello world!"; }, { fields: { @@ -274,48 +283,50 @@ describe('cloud validator', () => { }, } ); - Parse.Cloud.run('hello', { data: [] }) + Parse.Cloud.run("hello", { data: [] }) .then(() => { - fail('function should have failed.'); + fail("function should have failed."); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual('Validation failed. Invalid type for data. Expected: string'); + expect(error.message).toEqual( + "Validation failed. Invalid type for data. Expected: string" + ); done(); }); }); - it('set params default', done => { + it("set params default", done => { Parse.Cloud.define( - 'hello', + "hello", req => { - expect(req.params.data).toBe('yolo'); - return 'Hello world!'; + expect(req.params.data).toBe("yolo"); + return "Hello world!"; }, { fields: { data: { type: String, - default: 'yolo', + default: "yolo", }, }, } ); - Parse.Cloud.run('hello') + Parse.Cloud.run("hello") .then(() => { done(); }) .catch(() => { - fail('function should not have failed.'); + fail("function should not have failed."); }); }); - it('set params required', done => { + it("set params required", done => { Parse.Cloud.define( - 'hello', + "hello", req => { - expect(req.params.data).toBe('yolo'); - return 'Hello world!'; + expect(req.params.data).toBe("yolo"); + return "Hello world!"; }, { fields: { @@ -326,23 +337,25 @@ describe('cloud validator', () => { }, } ); - Parse.Cloud.run('hello', {}) + Parse.Cloud.run("hello", {}) .then(() => { - fail('function should have failed.'); + fail("function should have failed."); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual('Validation failed. Please specify data for data.'); + expect(error.message).toEqual( + "Validation failed. Please specify data for data." + ); done(); }); }); - it('set params not-required options data', done => { + it("set params not-required options data", done => { Parse.Cloud.define( - 'hello', + "hello", req => { - expect(req.params.data).toBe('abc'); - return 'Hello world!'; + expect(req.params.data).toBe("abc"); + return "Hello world!"; }, { fields: { @@ -352,30 +365,31 @@ describe('cloud validator', () => { options: s => { return s.length >= 4 && s.length <= 50; }, - error: 'Validation failed. Expected length of data to be between 4 and 50.', + error: + "Validation failed. Expected length of data to be between 4 and 50.", }, }, } ); - Parse.Cloud.run('hello', { data: 'abc' }) + Parse.Cloud.run("hello", { data: "abc" }) .then(() => { - fail('function should have failed.'); + fail("function should have failed."); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); expect(error.message).toEqual( - 'Validation failed. Expected length of data to be between 4 and 50.' + "Validation failed. Expected length of data to be between 4 and 50." ); done(); }); }); - it('set params not-required type', done => { + it("set params not-required type", done => { Parse.Cloud.define( - 'hello', + "hello", req => { expect(req.params.data).toBe(null); - return 'Hello world!'; + return "Hello world!"; }, { fields: { @@ -386,22 +400,24 @@ describe('cloud validator', () => { }, } ); - Parse.Cloud.run('hello', { data: null }) + Parse.Cloud.run("hello", { data: null }) .then(() => { - fail('function should have failed.'); + fail("function should have failed."); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual('Validation failed. Invalid type for data. Expected: string'); + expect(error.message).toEqual( + "Validation failed. Invalid type for data. Expected: string" + ); done(); }); }); - it('set params not-required options', done => { + it("set params not-required options", done => { Parse.Cloud.define( - 'hello', + "hello", () => { - return 'Hello world!'; + return "Hello world!"; }, { fields: { @@ -415,20 +431,20 @@ describe('cloud validator', () => { }, } ); - Parse.Cloud.run('hello', {}) + Parse.Cloud.run("hello", {}) .then(() => { done(); }) .catch(() => { - fail('function should not have failed.'); + fail("function should not have failed."); }); }); - it('set params not-required no-options', done => { + it("set params not-required no-options", done => { Parse.Cloud.define( - 'hello', + "hello", () => { - return 'Hello world!'; + return "Hello world!"; }, { fields: { @@ -439,77 +455,81 @@ describe('cloud validator', () => { }, } ); - Parse.Cloud.run('hello', {}) + Parse.Cloud.run("hello", {}) .then(() => { done(); }) .catch(() => { - fail('function should not have failed.'); + fail("function should not have failed."); }); }); - it('set params option', done => { + it("set params option", done => { Parse.Cloud.define( - 'hello', + "hello", req => { - expect(req.params.data).toBe('yolo'); - return 'Hello world!'; + expect(req.params.data).toBe("yolo"); + return "Hello world!"; }, { fields: { data: { type: String, required: true, - options: 'a', + options: "a", }, }, } ); - Parse.Cloud.run('hello', { data: 'f' }) + Parse.Cloud.run("hello", { data: "f" }) .then(() => { - fail('function should have failed.'); + fail("function should have failed."); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual('Validation failed. Invalid option for data. Expected: a'); + expect(error.message).toEqual( + "Validation failed. Invalid option for data. Expected: a" + ); done(); }); }); - it('set params options', done => { + it("set params options", done => { Parse.Cloud.define( - 'hello', + "hello", req => { - expect(req.params.data).toBe('yolo'); - return 'Hello world!'; + expect(req.params.data).toBe("yolo"); + return "Hello world!"; }, { fields: { data: { type: String, required: true, - options: ['a', 'b'], + options: ["a", "b"], }, }, } ); - Parse.Cloud.run('hello', { data: 'f' }) + Parse.Cloud.run("hello", { data: "f" }) .then(() => { - fail('function should have failed.'); + fail("function should have failed."); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual('Validation failed. Invalid option for data. Expected: a, b'); + expect(error.message).toEqual( + "Validation failed. Invalid option for data. Expected: a, b" + ); done(); }); }); - it('set params options function', done => { + it("set params options function", done => { Parse.Cloud.define( - 'hello', + "hello", () => { - fail('cloud function should not run.'); - return 'Hello world!'; + fail("cloud function should not run."); + return "Hello world!"; }, { fields: { @@ -519,28 +539,30 @@ describe('cloud validator', () => { options: val => { return val > 1 && val < 5; }, - error: 'Validation failed. Expected data to be between 1 and 5.', + error: "Validation failed. Expected data to be between 1 and 5.", }, }, } ); - Parse.Cloud.run('hello', { data: 7 }) + Parse.Cloud.run("hello", { data: 7 }) .then(() => { - fail('function should have failed.'); + fail("function should have failed."); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual('Validation failed. Expected data to be between 1 and 5.'); + expect(error.message).toEqual( + "Validation failed. Expected data to be between 1 and 5." + ); done(); }); }); - it('can run params function on null', done => { + it("can run params function on null", done => { Parse.Cloud.define( - 'hello', + "hello", () => { - fail('cloud function should not run.'); - return 'Hello world!'; + fail("cloud function should not run."); + return "Hello world!"; }, { fields: { @@ -548,56 +570,58 @@ describe('cloud validator', () => { options: val => { return val.length > 5; }, - error: 'Validation failed. String should be at least 5 characters', + error: "Validation failed. String should be at least 5 characters", }, }, } ); - Parse.Cloud.run('hello', { data: null }) + Parse.Cloud.run("hello", { data: null }) .then(() => { - fail('function should have failed.'); + fail("function should have failed."); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual('Validation failed. String should be at least 5 characters'); + expect(error.message).toEqual( + "Validation failed. String should be at least 5 characters" + ); done(); }); }); - it('can throw from options validator', done => { + it("can throw from options validator", done => { Parse.Cloud.define( - 'hello', + "hello", () => { - fail('cloud function should not run.'); - return 'Hello world!'; + fail("cloud function should not run."); + return "Hello world!"; }, { fields: { data: { options: () => { - throw 'validation failed.'; + throw "validation failed."; }, }, }, } ); - Parse.Cloud.run('hello', { data: 'a' }) + Parse.Cloud.run("hello", { data: "a" }) .then(() => { - fail('function should have failed.'); + fail("function should have failed."); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual('validation failed.'); + expect(error.message).toEqual("validation failed."); done(); }); }); - it('can throw null from options validator', done => { + it("can throw null from options validator", done => { Parse.Cloud.define( - 'hello', + "hello", () => { - fail('cloud function should not run.'); - return 'Hello world!'; + fail("cloud function should not run."); + return "Hello world!"; }, { fields: { @@ -609,22 +633,24 @@ describe('cloud validator', () => { }, } ); - Parse.Cloud.run('hello', { data: 'a' }) + Parse.Cloud.run("hello", { data: "a" }) .then(() => { - fail('function should have failed.'); + fail("function should have failed."); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual('Validation failed. Invalid value for data.'); + expect(error.message).toEqual( + "Validation failed. Invalid value for data." + ); done(); }); }); - it('can create functions', done => { + it("can create functions", done => { Parse.Cloud.define( - 'hello', + "hello", () => { - return 'Hello world!'; + return "Hello world!"; }, { requireUser: false, @@ -635,238 +661,242 @@ describe('cloud validator', () => { }, data1: { type: String, - default: 'default', + default: "default", }, }, } ); - Parse.Cloud.run('hello', { data: 'str' }).then(result => { - expect(result).toEqual('Hello world!'); + Parse.Cloud.run("hello", { data: "str" }).then(result => { + expect(result).toEqual("Hello world!"); done(); }); }); - it('basic beforeSave requireUserKey', async function (done) { - Parse.Cloud.beforeSave('BeforeSaveFail', () => {}, { + it("basic beforeSave requireUserKey", async function (done) { + Parse.Cloud.beforeSave("BeforeSaveFail", () => {}, { requireUser: true, - requireUserKeys: ['name'], + requireUserKeys: ["name"], }); - const user = await Parse.User.signUp('testuser', 'p@ssword'); - user.set('name', 'foo'); + const user = await Parse.User.signUp("testuser", "p@ssword"); + user.set("name", "foo"); await user.save(null, { sessionToken: user.getSessionToken() }); - const obj = new Parse.Object('BeforeSaveFail'); - obj.set('foo', 'bar'); + const obj = new Parse.Object("BeforeSaveFail"); + obj.set("foo", "bar"); await obj.save(null, { sessionToken: user.getSessionToken() }); - expect(obj.get('foo')).toBe('bar'); + expect(obj.get("foo")).toBe("bar"); done(); }); - it('basic beforeSave skipWithMasterKey', async function (done) { + it("basic beforeSave skipWithMasterKey", async function (done) { Parse.Cloud.beforeSave( - 'BeforeSave', + "BeforeSave", () => { - throw 'before save should have resolved using masterKey.'; + throw "before save should have resolved using masterKey."; }, { skipWithMasterKey: true, } ); - const obj = new Parse.Object('BeforeSave'); - obj.set('foo', 'bar'); + const obj = new Parse.Object("BeforeSave"); + obj.set("foo", "bar"); await obj.save(null, { useMasterKey: true }); - expect(obj.get('foo')).toBe('bar'); + expect(obj.get("foo")).toBe("bar"); done(); }); - it('basic beforeFind skipWithMasterKey', async function (done) { + it("basic beforeFind skipWithMasterKey", async function (done) { Parse.Cloud.beforeFind( - 'beforeFind', + "beforeFind", () => { - throw 'before find should have resolved using masterKey.'; + throw "before find should have resolved using masterKey."; }, { skipWithMasterKey: true, } ); - const obj = new Parse.Object('beforeFind'); - obj.set('foo', 'bar'); + const obj = new Parse.Object("beforeFind"); + obj.set("foo", "bar"); await obj.save(); - expect(obj.get('foo')).toBe('bar'); + expect(obj.get("foo")).toBe("bar"); - const query = new Parse.Query('beforeFind'); + const query = new Parse.Query("beforeFind"); const first = await query.first({ useMasterKey: true }); expect(first).toBeDefined(); expect(first.id).toBe(obj.id); done(); }); - it('basic beforeDelete skipWithMasterKey', async function (done) { + it("basic beforeDelete skipWithMasterKey", async function (done) { Parse.Cloud.beforeDelete( - 'beforeFind', + "beforeFind", () => { - throw 'before find should have resolved using masterKey.'; + throw "before find should have resolved using masterKey."; }, { skipWithMasterKey: true, } ); - const obj = new Parse.Object('beforeFind'); - obj.set('foo', 'bar'); + const obj = new Parse.Object("beforeFind"); + obj.set("foo", "bar"); await obj.save(); - expect(obj.get('foo')).toBe('bar'); + expect(obj.get("foo")).toBe("bar"); await obj.destroy({ useMasterKey: true }); done(); }); - it('basic beforeSaveFile skipWithMasterKey', async done => { + it("basic beforeSaveFile skipWithMasterKey", async done => { Parse.Cloud.beforeSave( Parse.File, () => { - throw 'beforeSaveFile should have resolved using master key.'; + throw "beforeSaveFile should have resolved using master key."; }, { skipWithMasterKey: true, } ); - const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); + const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); const result = await file.save({ useMasterKey: true }); expect(result).toBe(file); done(); }); - it_id('893eec0c-41bd-4adf-8f0a-306087ad8d61')(it)( - 'basic beforeSave Parse.Config skipWithMasterKey', + it_id("893eec0c-41bd-4adf-8f0a-306087ad8d61")(it)( + "basic beforeSave Parse.Config skipWithMasterKey", async () => { Parse.Cloud.beforeSave( Parse.Config, () => { - throw 'beforeSaveFile should have resolved using master key.'; + throw "beforeSaveFile should have resolved using master key."; }, { skipWithMasterKey: true, } ); const config = await testConfig(); - expect(config.get('internal')).toBe('i'); - expect(config.get('string')).toBe('s'); - expect(config.get('number')).toBe(12); + expect(config.get("internal")).toBe("i"); + expect(config.get("string")).toBe("s"); + expect(config.get("number")).toBe(12); } ); - it_id('91e739a4-6a38-405c-8f83-f36d48220734')(it)( - 'basic afterSave Parse.Config skipWithMasterKey', + it_id("91e739a4-6a38-405c-8f83-f36d48220734")(it)( + "basic afterSave Parse.Config skipWithMasterKey", async () => { Parse.Cloud.afterSave( Parse.Config, () => { - throw 'beforeSaveFile should have resolved using master key.'; + throw "beforeSaveFile should have resolved using master key."; }, { skipWithMasterKey: true, } ); const config = await testConfig(); - expect(config.get('internal')).toBe('i'); - expect(config.get('string')).toBe('s'); - expect(config.get('number')).toBe(12); + expect(config.get("internal")).toBe("i"); + expect(config.get("string")).toBe("s"); + expect(config.get("number")).toBe(12); } ); - it('beforeSave validateMasterKey and skipWithMasterKey fail', async function (done) { + it("beforeSave validateMasterKey and skipWithMasterKey fail", async function (done) { Parse.Cloud.beforeSave( - 'BeforeSave', + "BeforeSave", () => { - throw 'beforeSaveFile should have resolved using master key.'; + throw "beforeSaveFile should have resolved using master key."; }, { - fields: ['foo'], + fields: ["foo"], validateMasterKey: true, skipWithMasterKey: true, } ); - const obj = new Parse.Object('BeforeSave'); + const obj = new Parse.Object("BeforeSave"); try { await obj.save(null, { useMasterKey: true }); - fail('function should have failed.'); + fail("function should have failed."); } catch (error) { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual('Validation failed. Please specify data for foo.'); + expect(error.message).toEqual( + "Validation failed. Please specify data for foo." + ); done(); } }); - it('beforeSave validateMasterKey and skipWithMasterKey success', async function (done) { + it("beforeSave validateMasterKey and skipWithMasterKey success", async function (done) { Parse.Cloud.beforeSave( - 'BeforeSave', + "BeforeSave", () => { - throw 'beforeSaveFile should have resolved using master key.'; + throw "beforeSaveFile should have resolved using master key."; }, { - fields: ['foo'], + fields: ["foo"], validateMasterKey: true, skipWithMasterKey: true, } ); - const obj = new Parse.Object('BeforeSave'); - obj.set('foo', 'bar'); + const obj = new Parse.Object("BeforeSave"); + obj.set("foo", "bar"); try { await obj.save(null, { useMasterKey: true }); done(); } catch (error) { - fail('error should not have been called.'); + fail("error should not have been called."); } }); - it('basic beforeSave requireUserKey on User Class', async function (done) { + it("basic beforeSave requireUserKey on User Class", async function (done) { Parse.Cloud.beforeSave(Parse.User, () => {}, { requireUser: true, - requireUserKeys: ['name'], + requireUserKeys: ["name"], }); const user = new Parse.User(); - user.set('username', 'testuser'); - user.set('password', 'p@ssword'); - user.set('name', 'foo'); - expect(user.get('name')).toBe('foo'); + user.set("username", "testuser"); + user.set("password", "p@ssword"); + user.set("name", "foo"); + expect(user.get("name")).toBe("foo"); done(); }); - it('basic beforeSave requireUserKey rejection', async function (done) { - Parse.Cloud.beforeSave('BeforeSaveFail', () => {}, { + it("basic beforeSave requireUserKey rejection", async function (done) { + Parse.Cloud.beforeSave("BeforeSaveFail", () => {}, { requireUser: true, - requireUserKeys: ['name'], + requireUserKeys: ["name"], }); - const user = await Parse.User.signUp('testuser', 'p@ssword'); - const obj = new Parse.Object('BeforeSaveFail'); - obj.set('foo', 'bar'); + const user = await Parse.User.signUp("testuser", "p@ssword"); + const obj = new Parse.Object("BeforeSaveFail"); + obj.set("foo", "bar"); try { await obj.save(null, { sessionToken: user.getSessionToken() }); - fail('should not have been able to save without userkey'); + fail("should not have been able to save without userkey"); } catch (error) { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual('Validation failed. Please set data for name on your account.'); + expect(error.message).toEqual( + "Validation failed. Please set data for name on your account." + ); done(); } }); - it('basic beforeSave requireUserKey without user', async function (done) { - Parse.Cloud.beforeSave('BeforeSaveFail', () => {}, { - requireUserKeys: ['name'], + it("basic beforeSave requireUserKey without user", async function (done) { + Parse.Cloud.beforeSave("BeforeSaveFail", () => {}, { + requireUserKeys: ["name"], }); - const obj = new Parse.Object('BeforeSaveFail'); - obj.set('foo', 'bar'); + const obj = new Parse.Object("BeforeSaveFail"); + obj.set("foo", "bar"); try { await obj.save(); - fail('should not have been able to save without user'); + fail("should not have been able to save without user"); } catch (error) { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual('Please login to make this request.'); + expect(error.message).toEqual("Please login to make this request."); done(); } }); - it('basic beforeSave requireUserKey as admin', async function (done) { + it("basic beforeSave requireUserKey as admin", async function (done) { Parse.Cloud.beforeSave(Parse.User, () => {}, { fields: { admin: { @@ -876,7 +906,7 @@ describe('cloud validator', () => { }, }); Parse.Cloud.define( - 'secureFunction', + "secureFunction", () => { return "Here's all the secure data!"; }, @@ -884,38 +914,38 @@ describe('cloud validator', () => { requireUserKeys: { admin: { options: true, - error: 'Unauthorized.', + error: "Unauthorized.", }, }, } ); const user = new Parse.User(); - user.set('username', 'testuser'); - user.set('password', 'p@ssword'); - user.set('admin', true); + user.set("username", "testuser"); + user.set("password", "p@ssword"); + user.set("admin", true); await user.signUp(); - expect(user.get('admin')).toBe(false); + expect(user.get("admin")).toBe(false); try { - await Parse.Cloud.run('secureFunction'); - fail('function should only be available to admin users'); + await Parse.Cloud.run("secureFunction"); + fail("function should only be available to admin users"); } catch (error) { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual('Unauthorized.'); + expect(error.message).toEqual("Unauthorized."); } done(); }); - it('basic beforeSave requireUserKey as custom function', async function (done) { + it("basic beforeSave requireUserKey as custom function", async function (done) { Parse.Cloud.beforeSave(Parse.User, () => {}, { fields: { accType: { - default: 'normal', + default: "normal", constant: true, }, }, }); Parse.Cloud.define( - 'secureFunction', + "secureFunction", () => { return "Here's all the secure data!"; }, @@ -923,40 +953,40 @@ describe('cloud validator', () => { requireUserKeys: { accType: { options: val => { - return ['admin', 'admin2'].includes(val); + return ["admin", "admin2"].includes(val); }, - error: 'Unauthorized.', + error: "Unauthorized.", }, }, } ); const user = new Parse.User(); - user.set('username', 'testuser'); - user.set('password', 'p@ssword'); - user.set('accType', 'admin'); + user.set("username", "testuser"); + user.set("password", "p@ssword"); + user.set("accType", "admin"); await user.signUp(); - expect(user.get('accType')).toBe('normal'); + expect(user.get("accType")).toBe("normal"); try { - await Parse.Cloud.run('secureFunction'); - fail('function should only be available to admin users'); + await Parse.Cloud.run("secureFunction"); + fail("function should only be available to admin users"); } catch (error) { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual('Unauthorized.'); + expect(error.message).toEqual("Unauthorized."); } done(); }); - it('basic beforeSave allow requireUserKey as custom function', async function (done) { + it("basic beforeSave allow requireUserKey as custom function", async function (done) { Parse.Cloud.beforeSave(Parse.User, () => {}, { fields: { accType: { - default: 'admin', + default: "admin", constant: true, }, }, }); Parse.Cloud.define( - 'secureFunction', + "secureFunction", () => { return "Here's all the secure data!"; }, @@ -964,242 +994,254 @@ describe('cloud validator', () => { requireUserKeys: { accType: { options: val => { - return ['admin', 'admin2'].includes(val); + return ["admin", "admin2"].includes(val); }, - error: 'Unauthorized.', + error: "Unauthorized.", }, }, } ); const user = new Parse.User(); - user.set('username', 'testuser'); - user.set('password', 'p@ssword'); + user.set("username", "testuser"); + user.set("password", "p@ssword"); await user.signUp(); - expect(user.get('accType')).toBe('admin'); - const result = await Parse.Cloud.run('secureFunction'); + expect(user.get("accType")).toBe("admin"); + const result = await Parse.Cloud.run("secureFunction"); expect(result).toBe("Here's all the secure data!"); done(); }); - it('basic beforeSave requireUser', function (done) { - Parse.Cloud.beforeSave('BeforeSaveFail', () => {}, { + it("basic beforeSave requireUser", function (done) { + Parse.Cloud.beforeSave("BeforeSaveFail", () => {}, { requireUser: true, }); - const obj = new Parse.Object('BeforeSaveFail'); - obj.set('foo', 'bar'); + const obj = new Parse.Object("BeforeSaveFail"); + obj.set("foo", "bar"); obj .save() .then(() => { - fail('function should have failed.'); + fail("function should have failed."); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual('Validation failed. Please login to continue.'); + expect(error.message).toEqual( + "Validation failed. Please login to continue." + ); done(); }); }); - it('basic validator requireAnyUserRoles', async function (done) { + it("basic validator requireAnyUserRoles", async function (done) { Parse.Cloud.define( - 'cloudFunction', + "cloudFunction", () => { return true; }, { requireUser: true, - requireAnyUserRoles: ['Admin'], + requireAnyUserRoles: ["Admin"], } ); - const user = await Parse.User.signUp('testuser', 'p@ssword'); + const user = await Parse.User.signUp("testuser", "p@ssword"); try { - await Parse.Cloud.run('cloudFunction'); - fail('cloud validator should have failed.'); + await Parse.Cloud.run("cloudFunction"); + fail("cloud validator should have failed."); } catch (e) { - expect(e.message).toBe('Validation failed. User does not match the required roles.'); + expect(e.message).toBe( + "Validation failed. User does not match the required roles." + ); } const roleACL = new Parse.ACL(); roleACL.setPublicReadAccess(true); - const role = new Parse.Role('Admin', roleACL); + const role = new Parse.Role("Admin", roleACL); role.getUsers().add(user); await role.save({ useMasterKey: true }); - await Parse.Cloud.run('cloudFunction'); + await Parse.Cloud.run("cloudFunction"); done(); }); - it('basic validator requireAllUserRoles', async function (done) { + it("basic validator requireAllUserRoles", async function (done) { Parse.Cloud.define( - 'cloudFunction', + "cloudFunction", () => { return true; }, { requireUser: true, - requireAllUserRoles: ['Admin', 'Admin2'], + requireAllUserRoles: ["Admin", "Admin2"], } ); - const user = await Parse.User.signUp('testuser', 'p@ssword'); + const user = await Parse.User.signUp("testuser", "p@ssword"); try { - await Parse.Cloud.run('cloudFunction'); - fail('cloud validator should have failed.'); + await Parse.Cloud.run("cloudFunction"); + fail("cloud validator should have failed."); } catch (e) { - expect(e.message).toBe('Validation failed. User does not match all the required roles.'); + expect(e.message).toBe( + "Validation failed. User does not match all the required roles." + ); } const roleACL = new Parse.ACL(); roleACL.setPublicReadAccess(true); - const role = new Parse.Role('Admin', roleACL); + const role = new Parse.Role("Admin", roleACL); role.getUsers().add(user); - const role2 = new Parse.Role('Admin2', roleACL); + const role2 = new Parse.Role("Admin2", roleACL); role2.getUsers().add(user); await role.save({ useMasterKey: true }); await role2.save({ useMasterKey: true }); - await Parse.Cloud.run('cloudFunction'); + await Parse.Cloud.run("cloudFunction"); done(); }); - it('allow requireAnyUserRoles to be a function', async function (done) { + it("allow requireAnyUserRoles to be a function", async function (done) { Parse.Cloud.define( - 'cloudFunction', + "cloudFunction", () => { return true; }, { requireUser: true, requireAnyUserRoles: () => { - return ['Admin Func']; + return ["Admin Func"]; }, } ); - const user = await Parse.User.signUp('testuser', 'p@ssword'); + const user = await Parse.User.signUp("testuser", "p@ssword"); try { - await Parse.Cloud.run('cloudFunction'); - fail('cloud validator should have failed.'); + await Parse.Cloud.run("cloudFunction"); + fail("cloud validator should have failed."); } catch (e) { - expect(e.message).toBe('Validation failed. User does not match the required roles.'); + expect(e.message).toBe( + "Validation failed. User does not match the required roles." + ); } const roleACL = new Parse.ACL(); roleACL.setPublicReadAccess(true); - const role = new Parse.Role('Admin Func', roleACL); + const role = new Parse.Role("Admin Func", roleACL); role.getUsers().add(user); await role.save({ useMasterKey: true }); - await Parse.Cloud.run('cloudFunction'); + await Parse.Cloud.run("cloudFunction"); done(); }); - it('allow requireAllUserRoles to be a function', async function (done) { + it("allow requireAllUserRoles to be a function", async function (done) { Parse.Cloud.define( - 'cloudFunction', + "cloudFunction", () => { return true; }, { requireUser: true, requireAllUserRoles: () => { - return ['AdminA', 'AdminB']; + return ["AdminA", "AdminB"]; }, } ); - const user = await Parse.User.signUp('testuser', 'p@ssword'); + const user = await Parse.User.signUp("testuser", "p@ssword"); try { - await Parse.Cloud.run('cloudFunction'); - fail('cloud validator should have failed.'); + await Parse.Cloud.run("cloudFunction"); + fail("cloud validator should have failed."); } catch (e) { - expect(e.message).toBe('Validation failed. User does not match all the required roles.'); + expect(e.message).toBe( + "Validation failed. User does not match all the required roles." + ); } const roleACL = new Parse.ACL(); roleACL.setPublicReadAccess(true); - const role = new Parse.Role('AdminA', roleACL); + const role = new Parse.Role("AdminA", roleACL); role.getUsers().add(user); - const role2 = new Parse.Role('AdminB', roleACL); + const role2 = new Parse.Role("AdminB", roleACL); role2.getUsers().add(user); await role.save({ useMasterKey: true }); await role2.save({ useMasterKey: true }); - await Parse.Cloud.run('cloudFunction'); + await Parse.Cloud.run("cloudFunction"); done(); }); - it('basic requireAllUserRoles but no user', async function (done) { + it("basic requireAllUserRoles but no user", async function (done) { Parse.Cloud.define( - 'cloudFunction', + "cloudFunction", () => { return true; }, { - requireAllUserRoles: ['Admin'], + requireAllUserRoles: ["Admin"], } ); try { - await Parse.Cloud.run('cloudFunction'); - fail('cloud validator should have failed.'); + await Parse.Cloud.run("cloudFunction"); + fail("cloud validator should have failed."); } catch (e) { - expect(e.message).toBe('Validation failed. Please login to continue.'); + expect(e.message).toBe("Validation failed. Please login to continue."); } - const user = await Parse.User.signUp('testuser', 'p@ssword'); + const user = await Parse.User.signUp("testuser", "p@ssword"); const roleACL = new Parse.ACL(); roleACL.setPublicReadAccess(true); - const role = new Parse.Role('Admin', roleACL); + const role = new Parse.Role("Admin", roleACL); role.getUsers().add(user); await role.save({ useMasterKey: true }); - await Parse.Cloud.run('cloudFunction'); + await Parse.Cloud.run("cloudFunction"); done(); }); - it('basic beforeSave requireMaster', function (done) { - Parse.Cloud.beforeSave('BeforeSaveFail', () => {}, { + it("basic beforeSave requireMaster", function (done) { + Parse.Cloud.beforeSave("BeforeSaveFail", () => {}, { requireMaster: true, }); - const obj = new Parse.Object('BeforeSaveFail'); - obj.set('foo', 'bar'); + const obj = new Parse.Object("BeforeSaveFail"); + obj.set("foo", "bar"); obj .save() .then(() => { - fail('function should have failed.'); + fail("function should have failed."); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); expect(error.message).toEqual( - 'Validation failed. Master key is required to complete this request.' + "Validation failed. Master key is required to complete this request." ); done(); }); }); - it('basic beforeSave master', async function (done) { - Parse.Cloud.beforeSave('BeforeSaveFail', () => {}, { + it("basic beforeSave master", async function (done) { + Parse.Cloud.beforeSave("BeforeSaveFail", () => {}, { requireUser: true, }); - const obj = new Parse.Object('BeforeSaveFail'); - obj.set('foo', 'bar'); + const obj = new Parse.Object("BeforeSaveFail"); + obj.set("foo", "bar"); await obj.save(null, { useMasterKey: true }); done(); }); - it('basic beforeSave validateMasterKey', function (done) { - Parse.Cloud.beforeSave('BeforeSaveFail', () => {}, { + it("basic beforeSave validateMasterKey", function (done) { + Parse.Cloud.beforeSave("BeforeSaveFail", () => {}, { requireUser: true, validateMasterKey: true, }); - const obj = new Parse.Object('BeforeSaveFail'); - obj.set('foo', 'bar'); + const obj = new Parse.Object("BeforeSaveFail"); + obj.set("foo", "bar"); obj .save(null, { useMasterKey: true }) .then(() => { - fail('function should have failed.'); + fail("function should have failed."); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual('Validation failed. Please login to continue.'); + expect(error.message).toEqual( + "Validation failed. Please login to continue." + ); done(); }); }); - it('basic beforeSave requireKeys', function (done) { - Parse.Cloud.beforeSave('beforeSaveRequire', () => {}, { + it("basic beforeSave requireKeys", function (done) { + Parse.Cloud.beforeSave("beforeSaveRequire", () => {}, { fields: { foo: { required: true, @@ -1209,111 +1251,113 @@ describe('cloud validator', () => { }, }, }); - const obj = new Parse.Object('beforeSaveRequire'); - obj.set('foo', 'bar'); + const obj = new Parse.Object("beforeSaveRequire"); + obj.set("foo", "bar"); obj .save() .then(() => { - fail('function should have failed.'); + fail("function should have failed."); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual('Validation failed. Please specify data for bar.'); + expect(error.message).toEqual( + "Validation failed. Please specify data for bar." + ); done(); }); }); - it('basic beforeSave constantKeys', async function (done) { - Parse.Cloud.beforeSave('BeforeSave', () => {}, { + it("basic beforeSave constantKeys", async function (done) { + Parse.Cloud.beforeSave("BeforeSave", () => {}, { fields: { foo: { constant: true, - default: 'bar', + default: "bar", }, }, }); - const obj = new Parse.Object('BeforeSave'); - obj.set('foo', 'far'); + const obj = new Parse.Object("BeforeSave"); + obj.set("foo", "far"); await obj.save(); - expect(obj.get('foo')).toBe('bar'); - obj.set('foo', 'yolo'); + expect(obj.get("foo")).toBe("bar"); + obj.set("foo", "yolo"); await obj.save(); - expect(obj.get('foo')).toBe('bar'); + expect(obj.get("foo")).toBe("bar"); done(); }); - it('basic beforeSave defaultKeys', async function (done) { - Parse.Cloud.beforeSave('BeforeSave', () => {}, { + it("basic beforeSave defaultKeys", async function (done) { + Parse.Cloud.beforeSave("BeforeSave", () => {}, { fields: { foo: { - default: 'bar', + default: "bar", }, }, }); - const obj = new Parse.Object('BeforeSave'); + const obj = new Parse.Object("BeforeSave"); await obj.save(); - expect(obj.get('foo')).toBe('bar'); - obj.set('foo', 'yolo'); + expect(obj.get("foo")).toBe("bar"); + obj.set("foo", "yolo"); await obj.save(); - expect(obj.get('foo')).toBe('yolo'); + expect(obj.get("foo")).toBe("yolo"); done(); }); - it('validate beforeSave', async done => { - Parse.Cloud.beforeSave('MyObject', () => {}, validatorSuccess); + it("validate beforeSave", async done => { + Parse.Cloud.beforeSave("MyObject", () => {}, validatorSuccess); - const MyObject = Parse.Object.extend('MyObject'); + const MyObject = Parse.Object.extend("MyObject"); const myObject = new MyObject(); try { await myObject.save(); done(); } catch (e) { - fail('before save should not have failed.'); + fail("before save should not have failed."); } }); - it('validate beforeSave fail', async done => { - Parse.Cloud.beforeSave('MyObject', () => {}, validatorFail); + it("validate beforeSave fail", async done => { + Parse.Cloud.beforeSave("MyObject", () => {}, validatorFail); - const MyObject = Parse.Object.extend('MyObject'); + const MyObject = Parse.Object.extend("MyObject"); const myObject = new MyObject(); try { await myObject.save(); - fail('cloud function should have failed.'); + fail("cloud function should have failed."); } catch (e) { expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); done(); } }); - it('validate afterSave', async done => { + it("validate afterSave", async done => { Parse.Cloud.afterSave( - 'MyObject', + "MyObject", () => { done(); }, validatorSuccess ); - const MyObject = Parse.Object.extend('MyObject'); + const MyObject = Parse.Object.extend("MyObject"); const myObject = new MyObject(); try { await myObject.save(); } catch (e) { - fail('before save should not have failed.'); + fail("before save should not have failed."); } }); - it('validate afterSave fail', async done => { + it("validate afterSave fail", async done => { Parse.Cloud.afterSave( - 'MyObject', + "MyObject", () => { - fail('this should not be called.'); + fail("this should not be called."); }, validatorFail ); - const MyObject = Parse.Object.extend('MyObject'); + const MyObject = Parse.Object.extend("MyObject"); const myObject = new MyObject(); await myObject.save(); setTimeout(() => { @@ -1321,109 +1365,109 @@ describe('cloud validator', () => { }, 1000); }); - it('validate beforeDelete', async done => { - Parse.Cloud.beforeDelete('MyObject', () => {}, validatorSuccess); + it("validate beforeDelete", async done => { + Parse.Cloud.beforeDelete("MyObject", () => {}, validatorSuccess); - const MyObject = Parse.Object.extend('MyObject'); + const MyObject = Parse.Object.extend("MyObject"); const myObject = new MyObject(); await myObject.save(); try { await myObject.destroy(); done(); } catch (e) { - fail('before delete should not have failed.'); + fail("before delete should not have failed."); } }); - it('validate beforeDelete fail', async done => { + it("validate beforeDelete fail", async done => { Parse.Cloud.beforeDelete( - 'MyObject', + "MyObject", () => { - fail('this should not be called.'); + fail("this should not be called."); }, validatorFail ); - const MyObject = Parse.Object.extend('MyObject'); + const MyObject = Parse.Object.extend("MyObject"); const myObject = new MyObject(); await myObject.save(); try { await myObject.destroy(); - fail('cloud function should have failed.'); + fail("cloud function should have failed."); } catch (e) { expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); done(); } }); - it('validate afterDelete', async done => { + it("validate afterDelete", async done => { Parse.Cloud.afterDelete( - 'MyObject', + "MyObject", () => { done(); }, validatorSuccess ); - const MyObject = Parse.Object.extend('MyObject'); + const MyObject = Parse.Object.extend("MyObject"); const myObject = new MyObject(); await myObject.save(); try { await myObject.destroy(); } catch (e) { - fail('after delete should not have failed.'); + fail("after delete should not have failed."); } }); - it('validate afterDelete fail', async done => { + it("validate afterDelete fail", async done => { Parse.Cloud.afterDelete( - 'MyObject', + "MyObject", () => { - fail('this should not be called.'); + fail("this should not be called."); }, validatorFail ); - const MyObject = Parse.Object.extend('MyObject'); + const MyObject = Parse.Object.extend("MyObject"); const myObject = new MyObject(); await myObject.save(); try { await myObject.destroy(); - fail('cloud function should have failed.'); + fail("cloud function should have failed."); } catch (e) { expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); done(); } }); - it('validate beforeFind', async done => { - Parse.Cloud.beforeFind('MyObject', () => {}, validatorSuccess); + it("validate beforeFind", async done => { + Parse.Cloud.beforeFind("MyObject", () => {}, validatorSuccess); try { - const MyObject = Parse.Object.extend('MyObject'); + const MyObject = Parse.Object.extend("MyObject"); const myObjectQuery = new Parse.Query(MyObject); await myObjectQuery.find(); done(); } catch (e) { - fail('beforeFind should not have failed.'); + fail("beforeFind should not have failed."); } }); - it('validate beforeFind fail', async done => { - Parse.Cloud.beforeFind('MyObject', () => {}, validatorFail); + it("validate beforeFind fail", async done => { + Parse.Cloud.beforeFind("MyObject", () => {}, validatorFail); try { - const MyObject = Parse.Object.extend('MyObject'); + const MyObject = Parse.Object.extend("MyObject"); const myObjectQuery = new Parse.Query(MyObject); await myObjectQuery.find(); - fail('cloud function should have failed.'); + fail("cloud function should have failed."); } catch (e) { expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); done(); } }); - it('validate afterFind', async done => { - Parse.Cloud.afterFind('MyObject', () => {}, validatorSuccess); + it("validate afterFind", async done => { + Parse.Cloud.afterFind("MyObject", () => {}, validatorSuccess); - const MyObject = Parse.Object.extend('MyObject'); + const MyObject = Parse.Object.extend("MyObject"); const myObject = new MyObject(); await myObject.save(); try { @@ -1431,175 +1475,178 @@ describe('cloud validator', () => { await myObjectQuery.find(); done(); } catch (e) { - fail('beforeFind should not have failed.'); + fail("beforeFind should not have failed."); } }); - it('validate afterFind fail', async done => { - Parse.Cloud.afterFind('MyObject', () => {}, validatorFail); + it("validate afterFind fail", async done => { + Parse.Cloud.afterFind("MyObject", () => {}, validatorFail); - const MyObject = Parse.Object.extend('MyObject'); + const MyObject = Parse.Object.extend("MyObject"); const myObject = new MyObject(); await myObject.save(); try { const myObjectQuery = new Parse.Query(MyObject); await myObjectQuery.find(); - fail('cloud function should have failed.'); + fail("cloud function should have failed."); } catch (e) { expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); done(); } }); - it('validate beforeSaveFile', async done => { + it("validate beforeSaveFile", async done => { Parse.Cloud.beforeSave(Parse.File, () => {}, validatorSuccess); - const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); + const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); const result = await file.save({ useMasterKey: true }); expect(result).toBe(file); done(); }); - it('validate beforeSaveFile fail', async done => { + it("validate beforeSaveFile fail", async done => { Parse.Cloud.beforeSave(Parse.File, () => {}, validatorFail); try { - const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); + const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); await file.save({ useMasterKey: true }); - fail('cloud function should have failed.'); + fail("cloud function should have failed."); } catch (e) { expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); done(); } }); - it('validate afterSaveFile', async done => { + it("validate afterSaveFile", async done => { Parse.Cloud.afterSave(Parse.File, () => {}, validatorSuccess); - const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); + const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); const result = await file.save({ useMasterKey: true }); expect(result).toBe(file); done(); }); - it('validate afterSaveFile fail', async done => { + it("validate afterSaveFile fail", async done => { Parse.Cloud.afterSave(Parse.File, () => {}, validatorFail); try { - const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); + const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); await file.save({ useMasterKey: true }); - fail('cloud function should have failed.'); + fail("cloud function should have failed."); } catch (e) { expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); done(); } }); - it('validate beforeDeleteFile', async done => { + it("validate beforeDeleteFile", async done => { Parse.Cloud.beforeDelete(Parse.File, () => {}, validatorSuccess); - const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); + const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); await file.save(); await file.destroy(); done(); }); - it('validate beforeDeleteFile fail', async done => { + it("validate beforeDeleteFile fail", async done => { Parse.Cloud.beforeDelete(Parse.File, () => {}, validatorFail); try { - const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); + const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); await file.save(); await file.destroy(); - fail('cloud function should have failed.'); + fail("cloud function should have failed."); } catch (e) { expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); done(); } }); - it('validate afterDeleteFile', async done => { + it("validate afterDeleteFile", async done => { Parse.Cloud.afterDelete(Parse.File, () => {}, validatorSuccess); - const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); + const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); await file.save(); await file.destroy(); done(); }); - it('validate afterDeleteFile fail', async done => { + it("validate afterDeleteFile fail", async done => { Parse.Cloud.afterDelete(Parse.File, () => {}, validatorFail); try { - const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); + const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); await file.save(); await file.destroy(); - fail('cloud function should have failed.'); + fail("cloud function should have failed."); } catch (e) { expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); done(); } }); - it_id('32ca1a99-7f2b-429d-a7cf-62b6661d0af6')(it)( - 'validate beforeSave Parse.Config', + it_id("32ca1a99-7f2b-429d-a7cf-62b6661d0af6")(it)( + "validate beforeSave Parse.Config", async () => { Parse.Cloud.beforeSave(Parse.Config, () => {}, validatorSuccess); const config = await testConfig(); - expect(config.get('internal')).toBe('i'); - expect(config.get('string')).toBe('s'); - expect(config.get('number')).toBe(12); + expect(config.get("internal")).toBe("i"); + expect(config.get("string")).toBe("s"); + expect(config.get("number")).toBe(12); } ); - it_id('c84d11e7-d09c-4843-ad98-f671511bf612')(it)( - 'validate beforeSave Parse.Config fail', + it_id("c84d11e7-d09c-4843-ad98-f671511bf612")(it)( + "validate beforeSave Parse.Config fail", async () => { Parse.Cloud.beforeSave(Parse.Config, () => {}, validatorFail); try { await testConfig(); - fail('cloud function should have failed.'); + fail("cloud function should have failed."); } catch (e) { expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); } } ); - it_id('b18b9a6a-0e35-4b60-9771-30f53501df3c')(it)('validate afterSave Parse.Config', async () => { - Parse.Cloud.afterSave(Parse.Config, () => {}, validatorSuccess); - const config = await testConfig(); - expect(config.get('internal')).toBe('i'); - expect(config.get('string')).toBe('s'); - expect(config.get('number')).toBe(12); - }); + it_id("b18b9a6a-0e35-4b60-9771-30f53501df3c")(it)( + "validate afterSave Parse.Config", + async () => { + Parse.Cloud.afterSave(Parse.Config, () => {}, validatorSuccess); + const config = await testConfig(); + expect(config.get("internal")).toBe("i"); + expect(config.get("string")).toBe("s"); + expect(config.get("number")).toBe(12); + } + ); - it_id('ef761222-1758-4614-b984-da84d73fc10c')(it)( - 'validate afterSave Parse.Config fail', + it_id("ef761222-1758-4614-b984-da84d73fc10c")(it)( + "validate afterSave Parse.Config fail", async () => { Parse.Cloud.afterSave(Parse.Config, () => {}, validatorFail); try { await testConfig(); - fail('cloud function should have failed.'); + fail("cloud function should have failed."); } catch (e) { expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); } } ); - it('Should have validator', async done => { + it("Should have validator", async done => { Parse.Cloud.define( - 'myFunction', + "myFunction", () => {}, () => { - throw 'error'; + throw "error"; } ); try { - await Parse.Cloud.run('myFunction'); + await Parse.Cloud.run("myFunction"); } catch (e) { expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); done(); } }); - it('does not log on valid config', () => { - Parse.Cloud.define('myFunction', () => {}, { + it("does not log on valid config", () => { + Parse.Cloud.define("myFunction", () => {}, { requireUser: true, requireMaster: true, validateMasterKey: false, @@ -1607,152 +1654,160 @@ describe('cloud validator', () => { requireUserKeys: { Acc: { constant: true, - options: ['A', 'B'], + options: ["A", "B"], required: true, - default: 'f', - error: 'a', + default: "f", + error: "a", type: String, }, }, fields: { Acc: { constant: true, - options: ['A', 'B'], + options: ["A", "B"], required: true, - default: 'f', - error: 'a', + default: "f", + error: "a", type: String, }, }, }); }); - it('Logs on invalid config', () => { + it("Logs on invalid config", () => { const fields = [ { - field: 'requiredUser', + field: "requiredUser", value: true, - error: 'requiredUser is not a supported parameter for Cloud Function validations.', + error: + "requiredUser is not a supported parameter for Cloud Function validations.", }, { - field: 'requireUser', + field: "requireUser", value: [], error: - 'Invalid type for Cloud Function validation key requireUser. Expected boolean, actual array', + "Invalid type for Cloud Function validation key requireUser. Expected boolean, actual array", }, { - field: 'requireMaster', + field: "requireMaster", value: [], error: - 'Invalid type for Cloud Function validation key requireMaster. Expected boolean, actual array', + "Invalid type for Cloud Function validation key requireMaster. Expected boolean, actual array", }, { - field: 'validateMasterKey', + field: "validateMasterKey", value: [], error: - 'Invalid type for Cloud Function validation key validateMasterKey. Expected boolean, actual array', + "Invalid type for Cloud Function validation key validateMasterKey. Expected boolean, actual array", }, { - field: 'skipWithMasterKey', + field: "skipWithMasterKey", value: [], error: - 'Invalid type for Cloud Function validation key skipWithMasterKey. Expected boolean, actual array', + "Invalid type for Cloud Function validation key skipWithMasterKey. Expected boolean, actual array", }, { - field: 'requireAllUserRoles', + field: "requireAllUserRoles", value: true, error: - 'Invalid type for Cloud Function validation key requireAllUserRoles. Expected array|function, actual boolean', + "Invalid type for Cloud Function validation key requireAllUserRoles. Expected array|function, actual boolean", }, { - field: 'requireAnyUserRoles', + field: "requireAnyUserRoles", value: true, error: - 'Invalid type for Cloud Function validation key requireAnyUserRoles. Expected array|function, actual boolean', + "Invalid type for Cloud Function validation key requireAnyUserRoles. Expected array|function, actual boolean", }, { - field: 'fields', + field: "fields", value: true, error: - 'Invalid type for Cloud Function validation key fields. Expected array|object, actual boolean', + "Invalid type for Cloud Function validation key fields. Expected array|object, actual boolean", }, { - field: 'requireUserKeys', + field: "requireUserKeys", value: true, error: - 'Invalid type for Cloud Function validation key requireUserKeys. Expected array|object, actual boolean', + "Invalid type for Cloud Function validation key requireUserKeys. Expected array|object, actual boolean", }, ]; for (const field of fields) { try { - Parse.Cloud.define('myFunction', () => {}, { + Parse.Cloud.define("myFunction", () => {}, { [field.field]: field.value, }); - fail(`Expected error registering invalid Cloud Function validation ${field.field}.`); + fail( + `Expected error registering invalid Cloud Function validation ${field.field}.` + ); } catch (e) { expect(e).toBe(field.error); } } }); - it('Logs on multiple invalid configs', () => { + it("Logs on multiple invalid configs", () => { const fields = [ { - field: 'otherKey', + field: "otherKey", value: true, - error: 'otherKey is not a supported parameter for Cloud Function validations.', + error: + "otherKey is not a supported parameter for Cloud Function validations.", }, { - field: 'constant', + field: "constant", value: [], error: - 'Invalid type for Cloud Function validation key constant. Expected boolean, actual array', + "Invalid type for Cloud Function validation key constant. Expected boolean, actual array", }, { - field: 'required', + field: "required", value: [], error: - 'Invalid type for Cloud Function validation key required. Expected boolean, actual array', + "Invalid type for Cloud Function validation key required. Expected boolean, actual array", }, { - field: 'error', + field: "error", value: [], error: - 'Invalid type for Cloud Function validation key error. Expected string, actual array', + "Invalid type for Cloud Function validation key error. Expected string, actual array", }, ]; for (const field of fields) { try { - Parse.Cloud.define('myFunction', () => {}, { + Parse.Cloud.define("myFunction", () => {}, { fields: { name: { [field.field]: field.value, }, }, }); - fail(`Expected error registering invalid Cloud Function validation ${field.field}.`); + fail( + `Expected error registering invalid Cloud Function validation ${field.field}.` + ); } catch (e) { expect(e).toBe(field.error); } try { - Parse.Cloud.define('myFunction', () => {}, { + Parse.Cloud.define("myFunction", () => {}, { requireUserKeys: { name: { [field.field]: field.value, }, }, }); - fail(`Expected error registering invalid Cloud Function validation ${field.field}.`); + fail( + `Expected error registering invalid Cloud Function validation ${field.field}.` + ); } catch (e) { expect(e).toBe(field.error); } } }); - it('set params options function async', async () => { + it("set params options function async", async () => { Parse.Cloud.define( - 'hello', + "hello", () => { - return 'Hello world!'; + return "Hello world!"; }, { fields: { @@ -1763,35 +1818,35 @@ describe('cloud validator', () => { await new Promise(resolve => { setTimeout(resolve, 500); }); - return val === 'f'; + return val === "f"; }, - error: 'Validation failed.', + error: "Validation failed.", }, }, } ); try { - await Parse.Cloud.run('hello', { data: 'd' }); - fail('validation should have failed'); + await Parse.Cloud.run("hello", { data: "d" }); + fail("validation should have failed"); } catch (error) { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual('Validation failed.'); + expect(error.message).toEqual("Validation failed."); } - const result = await Parse.Cloud.run('hello', { data: 'f' }); - expect(result).toBe('Hello world!'); + const result = await Parse.Cloud.run("hello", { data: "f" }); + expect(result).toBe("Hello world!"); }); - it('basic beforeSave requireUserKey as custom async function', async () => { + it("basic beforeSave requireUserKey as custom async function", async () => { Parse.Cloud.beforeSave(Parse.User, () => {}, { fields: { accType: { - default: 'normal', + default: "normal", constant: true, }, }, }); Parse.Cloud.define( - 'secureFunction', + "secureFunction", () => { return "Here's all the secure data!"; }, @@ -1802,25 +1857,25 @@ describe('cloud validator', () => { await new Promise(resolve => { setTimeout(resolve, 500); }); - return ['admin', 'admin2'].includes(val); + return ["admin", "admin2"].includes(val); }, - error: 'Unauthorized.', + error: "Unauthorized.", }, }, } ); const user = new Parse.User(); - user.set('username', 'testuser'); - user.set('password', 'p@ssword'); - user.set('accType', 'admin'); + user.set("username", "testuser"); + user.set("password", "p@ssword"); + user.set("accType", "admin"); await user.signUp(); - expect(user.get('accType')).toBe('normal'); + expect(user.get("accType")).toBe("normal"); try { - await Parse.Cloud.run('secureFunction'); - fail('function should only be available to admin users'); + await Parse.Cloud.run("secureFunction"); + fail("function should only be available to admin users"); } catch (error) { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual('Unauthorized.'); + expect(error.message).toEqual("Unauthorized."); } }); }); diff --git a/spec/CloudCode.spec.js b/spec/CloudCode.spec.js index c7200ab005..576632d07a 100644 --- a/spec/CloudCode.spec.js +++ b/spec/CloudCode.spec.js @@ -1,10 +1,10 @@ -'use strict'; -const Config = require('../lib/Config'); -const Parse = require('parse/node'); -const ParseServer = require('../lib/index').ParseServer; -const request = require('../lib/request'); +"use strict"; +const Config = require("../lib/Config"); +const Parse = require("parse/node"); +const ParseServer = require("../lib/index").ParseServer; +const request = require("../lib/request"); const InMemoryCacheAdapter = - require('../lib/Adapters/Cache/InMemoryCacheAdapter').InMemoryCacheAdapter; + require("../lib/Adapters/Cache/InMemoryCacheAdapter").InMemoryCacheAdapter; const mockAdapter = { createFile: async filename => ({ @@ -19,131 +19,142 @@ const mockAdapter = { }, }; -describe('Cloud Code', () => { - it('can load absolute cloud code file', done => { +describe("Cloud Code", () => { + it("can load absolute cloud code file", done => { reconfigureServer({ - cloud: __dirname + '/cloud/cloudCodeRelativeFile.js', + cloud: __dirname + "/cloud/cloudCodeRelativeFile.js", }).then(() => { - Parse.Cloud.run('cloudCodeInFile', {}).then(result => { - expect(result).toEqual('It is possible to define cloud code in a file.'); + Parse.Cloud.run("cloudCodeInFile", {}).then(result => { + expect(result).toEqual( + "It is possible to define cloud code in a file." + ); done(); }); }); }); - it('can load relative cloud code file', done => { - reconfigureServer({ cloud: './spec/cloud/cloudCodeAbsoluteFile.js' }).then(() => { - Parse.Cloud.run('cloudCodeInFile', {}).then(result => { - expect(result).toEqual('It is possible to define cloud code in a file.'); - done(); - }); - }); + it("can load relative cloud code file", done => { + reconfigureServer({ cloud: "./spec/cloud/cloudCodeAbsoluteFile.js" }).then( + () => { + Parse.Cloud.run("cloudCodeInFile", {}).then(result => { + expect(result).toEqual( + "It is possible to define cloud code in a file." + ); + done(); + }); + } + ); }); - it('can load cloud code as a module', async () => { - process.env.npm_package_type = 'module'; - await reconfigureServer({ appId: 'test1', cloud: './spec/cloud/cloudCodeModuleFile.js' }); - const result = await Parse.Cloud.run('cloudCodeInFile'); - expect(result).toEqual('It is possible to define cloud code in a file.'); + it("can load cloud code as a module", async () => { + process.env.npm_package_type = "module"; + await reconfigureServer({ + appId: "test1", + cloud: "./spec/cloud/cloudCodeModuleFile.js", + }); + const result = await Parse.Cloud.run("cloudCodeInFile"); + expect(result).toEqual("It is possible to define cloud code in a file."); delete process.env.npm_package_type; }); - it('cloud code must be valid type', async () => { - spyOn(console, 'error').and.callFake(() => {}); + it("cloud code must be valid type", async () => { + spyOn(console, "error").and.callFake(() => {}); await expectAsync(reconfigureServer({ cloud: true })).toBeRejectedWith( "argument 'cloud' must either be a string or a function" ); }); - it('should wait for cloud code to load', async () => { - await reconfigureServer({ appId: 'test3' }); + it("should wait for cloud code to load", async () => { + await reconfigureServer({ appId: "test3" }); const initiated = new Date(); const parseServer = await new ParseServer({ ...defaultConfiguration, - appId: 'test3', - masterKey: 'test', - serverURL: 'http://localhost:12668/parse', + appId: "test3", + masterKey: "test", + serverURL: "http://localhost:12668/parse", async cloud() { await new Promise(resolve => setTimeout(resolve, 1000)); - Parse.Cloud.beforeSave('Test', () => { - throw 'Cannot save.'; + Parse.Cloud.beforeSave("Test", () => { + throw "Cannot save."; }); }, }).start(); - const express = require('express'); + const express = require("express"); const app = express(); - app.use('/parse', parseServer.app); + app.use("/parse", parseServer.app); const server = app.listen(12668); const now = new Date(); expect(now.getTime() - initiated.getTime() > 1000).toBeTrue(); - await expectAsync(new Parse.Object('Test').save()).toBeRejectedWith( - new Parse.Error(141, 'Cannot save.') + await expectAsync(new Parse.Object("Test").save()).toBeRejectedWith( + new Parse.Error(141, "Cannot save.") ); await new Promise(resolve => server.close(resolve)); }); - it('can create functions', done => { - Parse.Cloud.define('hello', () => { - return 'Hello world!'; + it("can create functions", done => { + Parse.Cloud.define("hello", () => { + return "Hello world!"; }); - Parse.Cloud.run('hello', {}).then(result => { - expect(result).toEqual('Hello world!'); + Parse.Cloud.run("hello", {}).then(result => { + expect(result).toEqual("Hello world!"); done(); }); }); - it('can get config', () => { + it("can get config", () => { const config = Parse.Server; - let currentConfig = Config.get('test'); - const server = require('../lib/cloud-code/Parse.Server'); - expect(Object.keys(config)).toEqual(Object.keys({ ...currentConfig, ...server })); + let currentConfig = Config.get("test"); + const server = require("../lib/cloud-code/Parse.Server"); + expect(Object.keys(config)).toEqual( + Object.keys({ ...currentConfig, ...server }) + ); config.silent = false; Parse.Server = config; - currentConfig = Config.get('test'); + currentConfig = Config.get("test"); expect(currentConfig.silent).toBeFalse(); }); - it('can get curent version', () => { - const version = require('../package.json').version; - const currentConfig = Config.get('test'); + it("can get curent version", () => { + const version = require("../package.json").version; + const currentConfig = Config.get("test"); expect(Parse.Server.version).toBeDefined(); expect(currentConfig.version).toBeDefined(); expect(Parse.Server.version).toEqual(version); }); - it('show warning on duplicate cloud functions', done => { - const logger = require('../lib/logger').logger; - spyOn(logger, 'warn').and.callFake(() => {}); - Parse.Cloud.define('hello', () => { - return 'Hello world!'; + it("show warning on duplicate cloud functions", done => { + const logger = require("../lib/logger").logger; + spyOn(logger, "warn").and.callFake(() => {}); + Parse.Cloud.define("hello", () => { + return "Hello world!"; }); - Parse.Cloud.define('hello', () => { - return 'Hello world!'; + Parse.Cloud.define("hello", () => { + return "Hello world!"; }); expect(logger.warn).toHaveBeenCalledWith( - 'Warning: Duplicate cloud functions exist for hello. Only the last one will be used and the others will be ignored.' + "Warning: Duplicate cloud functions exist for hello. Only the last one will be used and the others will be ignored." ); done(); }); - it('is cleared cleared after the previous test', done => { - Parse.Cloud.run('hello', {}).catch(error => { + it("is cleared cleared after the previous test", done => { + Parse.Cloud.run("hello", {}).catch(error => { expect(error.code).toEqual(Parse.Error.SCRIPT_FAILED); done(); }); }); - it('basic beforeSave rejection', function (done) { - Parse.Cloud.beforeSave('BeforeSaveFail', function () { - throw new Error('You shall not pass!'); + it("basic beforeSave rejection", function (done) { + Parse.Cloud.beforeSave("BeforeSaveFail", function () { + throw new Error("You shall not pass!"); }); - const obj = new Parse.Object('BeforeSaveFail'); - obj.set('foo', 'bar'); + const obj = new Parse.Object("BeforeSaveFail"); + obj.set("foo", "bar"); obj.save().then( () => { - fail('Should not have been able to save BeforeSaveFailure class.'); + fail("Should not have been able to save BeforeSaveFailure class."); done(); }, () => { @@ -152,82 +163,86 @@ describe('Cloud Code', () => { ); }); - it('returns an error', done => { - Parse.Cloud.define('cloudCodeWithError', () => { + it("returns an error", done => { + Parse.Cloud.define("cloudCodeWithError", () => { /* eslint-disable no-undef */ foo.bar(); /* eslint-enable no-undef */ - return 'I better throw an error.'; + return "I better throw an error."; }); - Parse.Cloud.run('cloudCodeWithError').then( - () => done.fail('should not succeed'), + Parse.Cloud.run("cloudCodeWithError").then( + () => done.fail("should not succeed"), e => { - expect(e).toEqual(new Parse.Error(Parse.Error.SCRIPT_FAILED, 'foo is not defined')); + expect(e).toEqual( + new Parse.Error(Parse.Error.SCRIPT_FAILED, "foo is not defined") + ); done(); } ); }); - it('returns an empty error', done => { - Parse.Cloud.define('cloudCodeWithError', () => { + it("returns an empty error", done => { + Parse.Cloud.define("cloudCodeWithError", () => { throw null; }); - Parse.Cloud.run('cloudCodeWithError').then( - () => done.fail('should not succeed'), + Parse.Cloud.run("cloudCodeWithError").then( + () => done.fail("should not succeed"), e => { expect(e.code).toEqual(Parse.Error.SCRIPT_FAILED); - expect(e.message).toEqual('Script failed.'); + expect(e.message).toEqual("Script failed."); done(); } ); }); - it('beforeFind can throw string', async function (done) { - Parse.Cloud.beforeFind('beforeFind', () => { - throw 'throw beforeFind'; + it("beforeFind can throw string", async function (done) { + Parse.Cloud.beforeFind("beforeFind", () => { + throw "throw beforeFind"; }); - const obj = new Parse.Object('beforeFind'); - obj.set('foo', 'bar'); + const obj = new Parse.Object("beforeFind"); + obj.set("foo", "bar"); await obj.save(); - expect(obj.get('foo')).toBe('bar'); + expect(obj.get("foo")).toBe("bar"); try { - const query = new Parse.Query('beforeFind'); + const query = new Parse.Query("beforeFind"); await query.first(); } catch (e) { expect(e.code).toBe(Parse.Error.SCRIPT_FAILED); - expect(e.message).toBe('throw beforeFind'); + expect(e.message).toBe("throw beforeFind"); done(); } }); - it('beforeSave rejection with custom error code', function (done) { - Parse.Cloud.beforeSave('BeforeSaveFailWithErrorCode', function () { - throw new Parse.Error(999, 'Nope'); + it("beforeSave rejection with custom error code", function (done) { + Parse.Cloud.beforeSave("BeforeSaveFailWithErrorCode", function () { + throw new Parse.Error(999, "Nope"); }); - const obj = new Parse.Object('BeforeSaveFailWithErrorCode'); - obj.set('foo', 'bar'); + const obj = new Parse.Object("BeforeSaveFailWithErrorCode"); + obj.set("foo", "bar"); obj.save().then( function () { - fail('Should not have been able to save BeforeSaveFailWithErrorCode class.'); + fail( + "Should not have been able to save BeforeSaveFailWithErrorCode class." + ); done(); }, function (error) { expect(error.code).toEqual(999); - expect(error.message).toEqual('Nope'); + expect(error.message).toEqual("Nope"); done(); } ); }); - it('basic beforeSave rejection via promise', function (done) { - Parse.Cloud.beforeSave('BeforeSaveFailWithPromise', function () { - const query = new Parse.Query('Yolo'); + it("basic beforeSave rejection via promise", function (done) { + Parse.Cloud.beforeSave("BeforeSaveFailWithPromise", function () { + const query = new Parse.Query("Yolo"); return query.find().then( () => { - throw 'Nope'; + throw "Nope"; }, () => { return Promise.response(); @@ -235,34 +250,34 @@ describe('Cloud Code', () => { ); }); - const obj = new Parse.Object('BeforeSaveFailWithPromise'); - obj.set('foo', 'bar'); + const obj = new Parse.Object("BeforeSaveFailWithPromise"); + obj.set("foo", "bar"); obj.save().then( function () { - fail('Should not have been able to save BeforeSaveFailure class.'); + fail("Should not have been able to save BeforeSaveFailure class."); done(); }, function (error) { expect(error.code).toEqual(Parse.Error.SCRIPT_FAILED); - expect(error.message).toEqual('Nope'); + expect(error.message).toEqual("Nope"); done(); } ); }); - it('test beforeSave changed object success', function (done) { - Parse.Cloud.beforeSave('BeforeSaveChanged', function (req) { - req.object.set('foo', 'baz'); + it("test beforeSave changed object success", function (done) { + Parse.Cloud.beforeSave("BeforeSaveChanged", function (req) { + req.object.set("foo", "baz"); }); - const obj = new Parse.Object('BeforeSaveChanged'); - obj.set('foo', 'bar'); + const obj = new Parse.Object("BeforeSaveChanged"); + obj.set("foo", "bar"); obj.save().then( function () { - const query = new Parse.Query('BeforeSaveChanged'); + const query = new Parse.Query("BeforeSaveChanged"); query.get(obj.id).then( function (objAgain) { - expect(objAgain.get('foo')).toEqual('baz'); + expect(objAgain.get("foo")).toEqual("baz"); done(); }, function (error) { @@ -278,187 +293,187 @@ describe('Cloud Code', () => { ); }); - it('test beforeSave with invalid field', async () => { - Parse.Cloud.beforeSave('BeforeSaveChanged', function (req) { - req.object.set('length', 0); + it("test beforeSave with invalid field", async () => { + Parse.Cloud.beforeSave("BeforeSaveChanged", function (req) { + req.object.set("length", 0); }); - const obj = new Parse.Object('BeforeSaveChanged'); - obj.set('foo', 'bar'); + const obj = new Parse.Object("BeforeSaveChanged"); + obj.set("foo", "bar"); try { await obj.save(); - fail('should not succeed'); + fail("should not succeed"); } catch (e) { - expect(e.message).toBe('Invalid field name: length.'); + expect(e.message).toBe("Invalid field name: length."); } }); it("test beforeSave changed object fail doesn't change object", async function () { - Parse.Cloud.beforeSave('BeforeSaveChanged', function (req) { - if (req.object.has('fail')) { - return Promise.reject(new Error('something went wrong')); + Parse.Cloud.beforeSave("BeforeSaveChanged", function (req) { + if (req.object.has("fail")) { + return Promise.reject(new Error("something went wrong")); } return Promise.resolve(); }); - const obj = new Parse.Object('BeforeSaveChanged'); - obj.set('foo', 'bar'); + const obj = new Parse.Object("BeforeSaveChanged"); + obj.set("foo", "bar"); await obj.save(); - obj.set('foo', 'baz').set('fail', true); + obj.set("foo", "baz").set("fail", true); try { await obj.save(); } catch (e) { await obj.fetch(); - expect(obj.get('foo')).toBe('bar'); + expect(obj.get("foo")).toBe("bar"); } }); - it('test beforeSave returns value on create and update', done => { - Parse.Cloud.beforeSave('BeforeSaveChanged', function (req) { - req.object.set('foo', 'baz'); + it("test beforeSave returns value on create and update", done => { + Parse.Cloud.beforeSave("BeforeSaveChanged", function (req) { + req.object.set("foo", "baz"); }); - const obj = new Parse.Object('BeforeSaveChanged'); - obj.set('foo', 'bing'); + const obj = new Parse.Object("BeforeSaveChanged"); + obj.set("foo", "bing"); obj.save().then(() => { - expect(obj.get('foo')).toEqual('baz'); - obj.set('foo', 'bar'); + expect(obj.get("foo")).toEqual("baz"); + obj.set("foo", "bar"); return obj.save().then(() => { - expect(obj.get('foo')).toEqual('baz'); + expect(obj.get("foo")).toEqual("baz"); done(); }); }); }); - it('test beforeSave applies changes when beforeSave returns true', done => { - Parse.Cloud.beforeSave('Insurance', function (req) { - req.object.set('rate', '$49.99/Month'); + it("test beforeSave applies changes when beforeSave returns true", done => { + Parse.Cloud.beforeSave("Insurance", function (req) { + req.object.set("rate", "$49.99/Month"); return true; }); - const insurance = new Parse.Object('Insurance'); - insurance.set('rate', '$5.00/Month'); + const insurance = new Parse.Object("Insurance"); + insurance.set("rate", "$5.00/Month"); insurance.save().then(insurance => { - expect(insurance.get('rate')).toEqual('$49.99/Month'); + expect(insurance.get("rate")).toEqual("$49.99/Month"); done(); }); }); - it('test beforeSave applies changes and resolves returned promise', done => { - Parse.Cloud.beforeSave('Insurance', function (req) { - req.object.set('rate', '$49.99/Month'); - return new Parse.Query('Pet').get(req.object.get('pet').id).then(pet => { - pet.set('healthy', true); + it("test beforeSave applies changes and resolves returned promise", done => { + Parse.Cloud.beforeSave("Insurance", function (req) { + req.object.set("rate", "$49.99/Month"); + return new Parse.Query("Pet").get(req.object.get("pet").id).then(pet => { + pet.set("healthy", true); return pet.save(); }); }); - const pet = new Parse.Object('Pet'); - pet.set('healthy', false); + const pet = new Parse.Object("Pet"); + pet.set("healthy", false); pet.save().then(pet => { - const insurance = new Parse.Object('Insurance'); - insurance.set('pet', pet); - insurance.set('rate', '$5.00/Month'); + const insurance = new Parse.Object("Insurance"); + insurance.set("pet", pet); + insurance.set("rate", "$5.00/Month"); insurance.save().then(insurance => { - expect(insurance.get('rate')).toEqual('$49.99/Month'); - new Parse.Query('Pet').get(insurance.get('pet').id).then(pet => { - expect(pet.get('healthy')).toEqual(true); + expect(insurance.get("rate")).toEqual("$49.99/Month"); + new Parse.Query("Pet").get(insurance.get("pet").id).then(pet => { + expect(pet.get("healthy")).toEqual(true); done(); }); }); }); }); - it('beforeSave should be called only if user fulfills permissions', async () => { + it("beforeSave should be called only if user fulfills permissions", async () => { const triggeruser = new Parse.User(); - triggeruser.setUsername('triggeruser'); - triggeruser.setPassword('triggeruser'); + triggeruser.setUsername("triggeruser"); + triggeruser.setPassword("triggeruser"); await triggeruser.signUp(); const triggeruser2 = new Parse.User(); - triggeruser2.setUsername('triggeruser2'); - triggeruser2.setPassword('triggeruser2'); + triggeruser2.setUsername("triggeruser2"); + triggeruser2.setPassword("triggeruser2"); await triggeruser2.signUp(); const triggeruser3 = new Parse.User(); - triggeruser3.setUsername('triggeruser3'); - triggeruser3.setPassword('triggeruser3'); + triggeruser3.setUsername("triggeruser3"); + triggeruser3.setPassword("triggeruser3"); await triggeruser3.signUp(); const triggeruser4 = new Parse.User(); - triggeruser4.setUsername('triggeruser4'); - triggeruser4.setPassword('triggeruser4'); + triggeruser4.setUsername("triggeruser4"); + triggeruser4.setPassword("triggeruser4"); await triggeruser4.signUp(); const triggeruser5 = new Parse.User(); - triggeruser5.setUsername('triggeruser5'); - triggeruser5.setPassword('triggeruser5'); + triggeruser5.setUsername("triggeruser5"); + triggeruser5.setPassword("triggeruser5"); await triggeruser5.signUp(); const triggerroleacl = new Parse.ACL(); triggerroleacl.setPublicReadAccess(true); const triggerrole = new Parse.Role(); - triggerrole.setName('triggerrole'); + triggerrole.setName("triggerrole"); triggerrole.setACL(triggerroleacl); triggerrole.getUsers().add(triggeruser); triggerrole.getUsers().add(triggeruser3); await triggerrole.save(); - const config = Config.get('test'); + const config = Config.get("test"); const schema = await config.database.loadSchema(); await schema.addClassIfNotExists( - 'triggerclass', + "triggerclass", { - someField: { type: 'String' }, - pointerToUser: { type: 'Pointer', targetClass: '_User' }, + someField: { type: "String" }, + pointerToUser: { type: "Pointer", targetClass: "_User" }, }, { find: { - 'role:triggerrole': true, + "role:triggerrole": true, [triggeruser.id]: true, [triggeruser2.id]: true, }, create: { - 'role:triggerrole': true, + "role:triggerrole": true, [triggeruser.id]: true, [triggeruser2.id]: true, }, get: { - 'role:triggerrole': true, + "role:triggerrole": true, [triggeruser.id]: true, [triggeruser2.id]: true, }, update: { - 'role:triggerrole': true, + "role:triggerrole": true, [triggeruser.id]: true, [triggeruser2.id]: true, }, addField: { - 'role:triggerrole': true, + "role:triggerrole": true, [triggeruser.id]: true, [triggeruser2.id]: true, }, delete: { - 'role:triggerrole': true, + "role:triggerrole": true, [triggeruser.id]: true, [triggeruser2.id]: true, }, - readUserFields: ['pointerToUser'], - writeUserFields: ['pointerToUser'], + readUserFields: ["pointerToUser"], + writeUserFields: ["pointerToUser"], }, {} ); let called = 0; - Parse.Cloud.beforeSave('triggerclass', () => { + Parse.Cloud.beforeSave("triggerclass", () => { called++; }); - const triggerobject = new Parse.Object('triggerclass'); - triggerobject.set('someField', 'someValue'); - triggerobject.set('someField2', 'someValue'); + const triggerobject = new Parse.Object("triggerclass"); + triggerobject.set("someField", "someValue"); + triggerobject.set("someField2", "someValue"); const triggerobjectacl = new Parse.ACL(); triggerobjectacl.setPublicReadAccess(false); triggerobjectacl.setPublicWriteAccess(false); @@ -487,9 +502,9 @@ describe('Cloud Code', () => { }); expect(called).toBe(4); - const triggerobject2 = new Parse.Object('triggerclass'); - triggerobject2.set('someField', 'someValue'); - triggerobject2.set('someField22', 'someValue'); + const triggerobject2 = new Parse.Object("triggerclass"); + triggerobject2.set("someField", "someValue"); + triggerobject2.set("someField22", "someValue"); const triggerobjectacl2 = new Parse.ACL(); triggerobjectacl2.setPublicReadAccess(false); triggerobjectacl2.setPublicWriteAccess(false); @@ -550,9 +565,9 @@ describe('Cloud Code', () => { expect(catched).toBe(true); expect(called).toBe(7); - const triggerobject3 = new Parse.Object('triggerclass'); - triggerobject3.set('someField', 'someValue'); - triggerobject3.set('someField33', 'someValue'); + const triggerobject3 = new Parse.Object("triggerclass"); + triggerobject3.set("someField", "someValue"); + triggerobject3.set("someField33", "someValue"); catched = false; try { @@ -579,19 +594,19 @@ describe('Cloud Code', () => { expect(called).toBe(7); }); - it('test afterSave ran and created an object', function (done) { - Parse.Cloud.afterSave('AfterSaveTest', function (req) { - const obj = new Parse.Object('AfterSaveProof'); - obj.set('proof', req.object.id); + it("test afterSave ran and created an object", function (done) { + Parse.Cloud.afterSave("AfterSaveTest", function (req) { + const obj = new Parse.Object("AfterSaveProof"); + obj.set("proof", req.object.id); obj.save().then(test); }); - const obj = new Parse.Object('AfterSaveTest'); + const obj = new Parse.Object("AfterSaveTest"); obj.save(); function test() { - const query = new Parse.Query('AfterSaveProof'); - query.equalTo('proof', obj.id); + const query = new Parse.Query("AfterSaveProof"); + query.equalTo("proof", obj.id); query.find().then( function (results) { expect(results.length).toEqual(1); @@ -605,13 +620,13 @@ describe('Cloud Code', () => { } }); - it('test afterSave ran on created object and returned a promise', function (done) { - Parse.Cloud.afterSave('AfterSaveTest2', function (req) { + it("test afterSave ran on created object and returned a promise", function (done) { + Parse.Cloud.afterSave("AfterSaveTest2", function (req) { const obj = req.object; if (!obj.existed()) { return new Promise(resolve => { setTimeout(function () { - obj.set('proof', obj.id); + obj.set("proof", obj.id); obj.save().then(function () { resolve(); }); @@ -620,15 +635,15 @@ describe('Cloud Code', () => { } }); - const obj = new Parse.Object('AfterSaveTest2'); + const obj = new Parse.Object("AfterSaveTest2"); obj.save().then(function () { - const query = new Parse.Query('AfterSaveTest2'); - query.equalTo('proof', obj.id); + const query = new Parse.Query("AfterSaveTest2"); + query.equalTo("proof", obj.id); query.find().then( function (results) { expect(results.length).toEqual(1); const savedObject = results[0]; - expect(savedObject.get('proof')).toEqual(obj.id); + expect(savedObject.get("proof")).toEqual(obj.id); done(); }, function (error) { @@ -640,13 +655,13 @@ describe('Cloud Code', () => { }); // TODO: Fails on CI randomly as racing - xit('test afterSave ignoring promise, object not found', function (done) { - Parse.Cloud.afterSave('AfterSaveTest2', function (req) { + xit("test afterSave ignoring promise, object not found", function (done) { + Parse.Cloud.afterSave("AfterSaveTest2", function (req) { const obj = req.object; if (!obj.existed()) { return new Promise(resolve => { setTimeout(function () { - obj.set('proof', obj.id); + obj.set("proof", obj.id); obj.save().then(function () { resolve(); }); @@ -655,13 +670,13 @@ describe('Cloud Code', () => { } }); - const obj = new Parse.Object('AfterSaveTest2'); + const obj = new Parse.Object("AfterSaveTest2"); obj.save().then(function () { done(); }); - const query = new Parse.Query('AfterSaveTest2'); - query.equalTo('proof', obj.id); + const query = new Parse.Query("AfterSaveTest2"); + query.equalTo("proof", obj.id); query.find().then( function (results) { expect(results.length).toEqual(0); @@ -672,16 +687,16 @@ describe('Cloud Code', () => { ); }); - it('test afterSave rejecting promise', function (done) { - Parse.Cloud.afterSave('AfterSaveTest2', function () { + it("test afterSave rejecting promise", function (done) { + Parse.Cloud.afterSave("AfterSaveTest2", function () { return new Promise((resolve, reject) => { setTimeout(function () { - reject('THIS SHOULD BE IGNORED'); + reject("THIS SHOULD BE IGNORED"); }, 1000); }); }); - const obj = new Parse.Object('AfterSaveTest2'); + const obj = new Parse.Object("AfterSaveTest2"); obj.save().then( function () { done(); @@ -693,12 +708,12 @@ describe('Cloud Code', () => { ); }); - it('test afterDelete returning promise, object is deleted when destroy resolves', function (done) { - Parse.Cloud.afterDelete('AfterDeleteTest2', function (req) { + it("test afterDelete returning promise, object is deleted when destroy resolves", function (done) { + Parse.Cloud.afterDelete("AfterDeleteTest2", function (req) { return new Promise(resolve => { setTimeout(function () { - const obj = new Parse.Object('AfterDeleteTestProof'); - obj.set('proof', req.object.id); + const obj = new Parse.Object("AfterDeleteTestProof"); + obj.set("proof", req.object.id); obj.save().then(function () { resolve(); }); @@ -711,27 +726,27 @@ describe('Cloud Code', () => { done(); }; - const obj = new Parse.Object('AfterDeleteTest2'); + const obj = new Parse.Object("AfterDeleteTest2"); obj.save().then(function () { obj.destroy().then(function () { - const query = new Parse.Query('AfterDeleteTestProof'); - query.equalTo('proof', obj.id); + const query = new Parse.Query("AfterDeleteTestProof"); + query.equalTo("proof", obj.id); query.find().then(function (results) { expect(results.length).toEqual(1); const deletedObject = results[0]; - expect(deletedObject.get('proof')).toEqual(obj.id); + expect(deletedObject.get("proof")).toEqual(obj.id); done(); }, errorHandler); }, errorHandler); }, errorHandler); }); - it('test afterDelete ignoring promise, object is not yet deleted', function (done) { - Parse.Cloud.afterDelete('AfterDeleteTest2', function (req) { + it("test afterDelete ignoring promise, object is not yet deleted", function (done) { + Parse.Cloud.afterDelete("AfterDeleteTest2", function (req) { return new Promise(resolve => { setTimeout(function () { - const obj = new Parse.Object('AfterDeleteTestProof'); - obj.set('proof', req.object.id); + const obj = new Parse.Object("AfterDeleteTestProof"); + obj.set("proof", req.object.id); obj.save().then(function () { resolve(); }); @@ -744,38 +759,38 @@ describe('Cloud Code', () => { done(); }; - const obj = new Parse.Object('AfterDeleteTest2'); + const obj = new Parse.Object("AfterDeleteTest2"); obj.save().then(function () { obj.destroy().then(function () { done(); }); - const query = new Parse.Query('AfterDeleteTestProof'); - query.equalTo('proof', obj.id); + const query = new Parse.Query("AfterDeleteTestProof"); + query.equalTo("proof", obj.id); query.find().then(function (results) { expect(results.length).toEqual(0); }, errorHandler); }, errorHandler); }); - it('test beforeSave happens on update', function (done) { - Parse.Cloud.beforeSave('BeforeSaveChanged', function (req) { - req.object.set('foo', 'baz'); + it("test beforeSave happens on update", function (done) { + Parse.Cloud.beforeSave("BeforeSaveChanged", function (req) { + req.object.set("foo", "baz"); }); - const obj = new Parse.Object('BeforeSaveChanged'); - obj.set('foo', 'bar'); + const obj = new Parse.Object("BeforeSaveChanged"); + obj.set("foo", "bar"); obj .save() .then(function () { - obj.set('foo', 'bar'); + obj.set("foo", "bar"); return obj.save(); }) .then( function () { - const query = new Parse.Query('BeforeSaveChanged'); + const query = new Parse.Query("BeforeSaveChanged"); return query.get(obj.id).then(function (objAgain) { - expect(objAgain.get('foo')).toEqual('baz'); + expect(objAgain.get("foo")).toEqual("baz"); done(); }); }, @@ -786,14 +801,14 @@ describe('Cloud Code', () => { ); }); - it('test beforeDelete failure', function (done) { - Parse.Cloud.beforeDelete('BeforeDeleteFail', function () { - throw 'Nope'; + it("test beforeDelete failure", function (done) { + Parse.Cloud.beforeDelete("BeforeDeleteFail", function () { + throw "Nope"; }); - const obj = new Parse.Object('BeforeDeleteFail'); + const obj = new Parse.Object("BeforeDeleteFail"); let id; - obj.set('foo', 'bar'); + obj.set("foo", "bar"); obj .save() .then(() => { @@ -802,14 +817,14 @@ describe('Cloud Code', () => { }) .then( () => { - fail('obj.destroy() should have failed, but it succeeded'); + fail("obj.destroy() should have failed, but it succeeded"); done(); }, error => { expect(error.code).toEqual(Parse.Error.SCRIPT_FAILED); - expect(error.message).toEqual('Nope'); + expect(error.message).toEqual("Nope"); - const objAgain = new Parse.Object('BeforeDeleteFail', { + const objAgain = new Parse.Object("BeforeDeleteFail", { objectId: id, }); return objAgain.fetch(); @@ -818,9 +833,9 @@ describe('Cloud Code', () => { .then( objAgain => { if (objAgain) { - expect(objAgain.get('foo')).toEqual('bar'); + expect(objAgain.get("foo")).toEqual("bar"); } else { - fail('unable to fetch the object ', id); + fail("unable to fetch the object ", id); } done(); }, @@ -831,45 +846,45 @@ describe('Cloud Code', () => { ); }); - it('basic beforeDelete rejection via promise', function (done) { - Parse.Cloud.beforeSave('BeforeDeleteFailWithPromise', function () { - const query = new Parse.Query('Yolo'); + it("basic beforeDelete rejection via promise", function (done) { + Parse.Cloud.beforeSave("BeforeDeleteFailWithPromise", function () { + const query = new Parse.Query("Yolo"); return query.find().then(() => { - throw 'Nope'; + throw "Nope"; }); }); - const obj = new Parse.Object('BeforeDeleteFailWithPromise'); - obj.set('foo', 'bar'); + const obj = new Parse.Object("BeforeDeleteFailWithPromise"); + obj.set("foo", "bar"); obj.save().then( function () { - fail('Should not have been able to save BeforeSaveFailure class.'); + fail("Should not have been able to save BeforeSaveFailure class."); done(); }, function (error) { expect(error.code).toEqual(Parse.Error.SCRIPT_FAILED); - expect(error.message).toEqual('Nope'); + expect(error.message).toEqual("Nope"); done(); } ); }); - it('test afterDelete ran and created an object', function (done) { - Parse.Cloud.afterDelete('AfterDeleteTest', function (req) { - const obj = new Parse.Object('AfterDeleteProof'); - obj.set('proof', req.object.id); + it("test afterDelete ran and created an object", function (done) { + Parse.Cloud.afterDelete("AfterDeleteTest", function (req) { + const obj = new Parse.Object("AfterDeleteProof"); + obj.set("proof", req.object.id); obj.save().then(test); }); - const obj = new Parse.Object('AfterDeleteTest'); + const obj = new Parse.Object("AfterDeleteTest"); obj.save().then(function () { obj.destroy(); }); function test() { - const query = new Parse.Query('AfterDeleteProof'); - query.equalTo('proof', obj.id); + const query = new Parse.Query("AfterDeleteProof"); + query.equalTo("proof", obj.id); query.find().then( function (results) { expect(results.length).toEqual(1); @@ -883,26 +898,26 @@ describe('Cloud Code', () => { } }); - it('test cloud function return types', function (done) { - Parse.Cloud.define('foo', function () { + it("test cloud function return types", function (done) { + Parse.Cloud.define("foo", function () { return { object: { - __type: 'Object', - className: 'Foo', - objectId: '123', + __type: "Object", + className: "Foo", + objectId: "123", x: 2, relation: { - __type: 'Object', - className: 'Bar', - objectId: '234', + __type: "Object", + className: "Bar", + objectId: "234", x: 3, }, }, array: [ { - __type: 'Object', - className: 'Bar', - objectId: '345', + __type: "Object", + className: "Bar", + objectId: "345", x: 2, }, ], @@ -910,47 +925,55 @@ describe('Cloud Code', () => { }; }); - Parse.Cloud.run('foo').then(result => { + Parse.Cloud.run("foo").then(result => { expect(result.object instanceof Parse.Object).toBeTruthy(); if (!result.object) { - fail('Unable to run foo'); + fail("Unable to run foo"); done(); return; } - expect(result.object.className).toEqual('Foo'); - expect(result.object.get('x')).toEqual(2); - const bar = result.object.get('relation'); + expect(result.object.className).toEqual("Foo"); + expect(result.object.get("x")).toEqual(2); + const bar = result.object.get("relation"); expect(bar instanceof Parse.Object).toBeTruthy(); - expect(bar.className).toEqual('Bar'); - expect(bar.get('x')).toEqual(3); + expect(bar.className).toEqual("Bar"); + expect(bar.get("x")).toEqual(3); expect(Array.isArray(result.array)).toEqual(true); expect(result.array[0] instanceof Parse.Object).toBeTruthy(); - expect(result.array[0].get('x')).toEqual(2); + expect(result.array[0].get("x")).toEqual(2); done(); }); }); - it('test cloud function request params types', function (done) { - Parse.Cloud.define('params', function (req) { + it("test cloud function request params types", function (done) { + Parse.Cloud.define("params", function (req) { expect(req.params.date instanceof Date).toBe(true); expect(req.params.date.getTime()).toBe(1463907600000); expect(req.params.dateList[0] instanceof Date).toBe(true); expect(req.params.dateList[0].getTime()).toBe(1463907600000); expect(req.params.complexStructure.date[0] instanceof Date).toBe(true); expect(req.params.complexStructure.date[0].getTime()).toBe(1463907600000); - expect(req.params.complexStructure.deepDate.date[0] instanceof Date).toBe(true); - expect(req.params.complexStructure.deepDate.date[0].getTime()).toBe(1463907600000); - expect(req.params.complexStructure.deepDate2[0].date instanceof Date).toBe(true); - expect(req.params.complexStructure.deepDate2[0].date.getTime()).toBe(1463907600000); + expect(req.params.complexStructure.deepDate.date[0] instanceof Date).toBe( + true + ); + expect(req.params.complexStructure.deepDate.date[0].getTime()).toBe( + 1463907600000 + ); + expect( + req.params.complexStructure.deepDate2[0].date instanceof Date + ).toBe(true); + expect(req.params.complexStructure.deepDate2[0].date.getTime()).toBe( + 1463907600000 + ); // Regression for #2294 expect(req.params.file instanceof Parse.File).toBe(true); - expect(req.params.file.url()).toEqual('https://some.url'); + expect(req.params.file.url()).toEqual("https://some.url"); // Regression for #2204 - expect(req.params.array).toEqual(['a', 'b', 'c']); + expect(req.params.array).toEqual(["a", "b", "c"]); expect(Array.isArray(req.params.array)).toBe(true); expect(req.params.arrayOfArray).toEqual([ - ['a', 'b', 'c'], - ['d', 'e', 'f'], + ["a", "b", "c"], + ["d", "e", "f"], ]); expect(Array.isArray(req.params.arrayOfArray)).toBe(true); expect(Array.isArray(req.params.arrayOfArray[0])).toBe(true); @@ -960,58 +983,58 @@ describe('Cloud Code', () => { const params = { date: { - __type: 'Date', - iso: '2016-05-22T09:00:00.000Z', + __type: "Date", + iso: "2016-05-22T09:00:00.000Z", }, dateList: [ { - __type: 'Date', - iso: '2016-05-22T09:00:00.000Z', + __type: "Date", + iso: "2016-05-22T09:00:00.000Z", }, ], - lol: 'hello', + lol: "hello", complexStructure: { date: [ { - __type: 'Date', - iso: '2016-05-22T09:00:00.000Z', + __type: "Date", + iso: "2016-05-22T09:00:00.000Z", }, ], deepDate: { date: [ { - __type: 'Date', - iso: '2016-05-22T09:00:00.000Z', + __type: "Date", + iso: "2016-05-22T09:00:00.000Z", }, ], }, deepDate2: [ { date: { - __type: 'Date', - iso: '2016-05-22T09:00:00.000Z', + __type: "Date", + iso: "2016-05-22T09:00:00.000Z", }, }, ], }, file: Parse.File.fromJSON({ - __type: 'File', - name: 'name', - url: 'https://some.url', + __type: "File", + name: "name", + url: "https://some.url", }), - array: ['a', 'b', 'c'], + array: ["a", "b", "c"], arrayOfArray: [ - ['a', 'b', 'c'], - ['d', 'e', 'f'], + ["a", "b", "c"], + ["d", "e", "f"], ], }; - Parse.Cloud.run('params', params).then(() => { + Parse.Cloud.run("params", params).then(() => { done(); }); }); - it('test cloud function should echo keys', function (done) { - Parse.Cloud.define('echoKeys', function () { + it("test cloud function should echo keys", function (done) { + Parse.Cloud.define("echoKeys", function () { return { applicationId: Parse.applicationId, masterKey: Parse.masterKey, @@ -1019,7 +1042,7 @@ describe('Cloud Code', () => { }; }); - Parse.Cloud.run('echoKeys').then(result => { + Parse.Cloud.run("echoKeys").then(result => { expect(result.applicationId).toEqual(Parse.applicationId); expect(result.masterKey).toEqual(Parse.masterKey); expect(result.javascriptKey).toEqual(Parse.javascriptKey); @@ -1027,53 +1050,53 @@ describe('Cloud Code', () => { }); }); - it('should properly create an object in before save', done => { - Parse.Cloud.beforeSave('BeforeSaveChanged', function (req) { - req.object.set('foo', 'baz'); + it("should properly create an object in before save", done => { + Parse.Cloud.beforeSave("BeforeSaveChanged", function (req) { + req.object.set("foo", "baz"); }); - Parse.Cloud.define('createBeforeSaveChangedObject', function () { - const obj = new Parse.Object('BeforeSaveChanged'); + Parse.Cloud.define("createBeforeSaveChangedObject", function () { + const obj = new Parse.Object("BeforeSaveChanged"); return obj.save().then(() => { return obj; }); }); - Parse.Cloud.run('createBeforeSaveChangedObject').then(res => { - expect(res.get('foo')).toEqual('baz'); + Parse.Cloud.run("createBeforeSaveChangedObject").then(res => { + expect(res.get("foo")).toEqual("baz"); done(); }); }); - it('dirtyKeys are set on update', done => { + it("dirtyKeys are set on update", done => { let triggerTime = 0; // Register a mock beforeSave hook - Parse.Cloud.beforeSave('GameScore', req => { + Parse.Cloud.beforeSave("GameScore", req => { const object = req.object; expect(object instanceof Parse.Object).toBeTruthy(); - expect(object.get('fooAgain')).toEqual('barAgain'); + expect(object.get("fooAgain")).toEqual("barAgain"); if (triggerTime == 0) { // Create - expect(object.get('foo')).toEqual('bar'); + expect(object.get("foo")).toEqual("bar"); } else if (triggerTime == 1) { // Update - expect(object.dirtyKeys()).toEqual(['foo']); - expect(object.dirty('foo')).toBeTruthy(); - expect(object.get('foo')).toEqual('baz'); + expect(object.dirtyKeys()).toEqual(["foo"]); + expect(object.dirty("foo")).toBeTruthy(); + expect(object.get("foo")).toEqual("baz"); } else { throw new Error(); } triggerTime++; }); - const obj = new Parse.Object('GameScore'); - obj.set('foo', 'bar'); - obj.set('fooAgain', 'barAgain'); + const obj = new Parse.Object("GameScore"); + obj.set("foo", "bar"); + obj.set("fooAgain", "barAgain"); obj .save() .then(() => { // We only update foo - obj.set('foo', 'baz'); + obj.set("foo", "baz"); return obj.save(); }) .then( @@ -1089,13 +1112,13 @@ describe('Cloud Code', () => { ); }); - it('test beforeSave unchanged success', function (done) { - Parse.Cloud.beforeSave('BeforeSaveUnchanged', function () { + it("test beforeSave unchanged success", function (done) { + Parse.Cloud.beforeSave("BeforeSaveUnchanged", function () { return; }); - const obj = new Parse.Object('BeforeSaveUnchanged'); - obj.set('foo', 'bar'); + const obj = new Parse.Object("BeforeSaveUnchanged"); + obj.set("foo", "bar"); obj.save().then( function () { done(); @@ -1107,13 +1130,13 @@ describe('Cloud Code', () => { ); }); - it('test beforeDelete success', function (done) { - Parse.Cloud.beforeDelete('BeforeDeleteTest', function () { + it("test beforeDelete success", function (done) { + Parse.Cloud.beforeDelete("BeforeDeleteTest", function () { return; }); - const obj = new Parse.Object('BeforeDeleteTest'); - obj.set('foo', 'bar'); + const obj = new Parse.Object("BeforeDeleteTest"); + obj.set("foo", "bar"); obj .save() .then(function () { @@ -1121,7 +1144,7 @@ describe('Cloud Code', () => { }) .then( function () { - const objAgain = new Parse.Object('BeforeDeleteTest', obj.id); + const objAgain = new Parse.Object("BeforeDeleteTest", obj.id); return objAgain.fetch().then(fail, () => done()); }, function (error) { @@ -1131,27 +1154,27 @@ describe('Cloud Code', () => { ); }); - it('test save triggers get user', async done => { - Parse.Cloud.beforeSave('SaveTriggerUser', function (req) { + it("test save triggers get user", async done => { + Parse.Cloud.beforeSave("SaveTriggerUser", function (req) { if (req.user && req.user.id) { return; } else { - throw new Error('No user present on request object for beforeSave.'); + throw new Error("No user present on request object for beforeSave."); } }); - Parse.Cloud.afterSave('SaveTriggerUser', function (req) { + Parse.Cloud.afterSave("SaveTriggerUser", function (req) { if (!req.user || !req.user.id) { - console.log('No user present on request object for afterSave.'); + console.log("No user present on request object for afterSave."); } }); const user = new Parse.User(); - user.set('password', 'asdf'); - user.set('email', 'asdf@example.com'); - user.set('username', 'zxcv'); + user.set("password", "asdf"); + user.set("email", "asdf@example.com"); + user.set("username", "zxcv"); await user.signUp(); - const obj = new Parse.Object('SaveTriggerUser'); + const obj = new Parse.Object("SaveTriggerUser"); obj.save().then( function () { done(); @@ -1163,40 +1186,40 @@ describe('Cloud Code', () => { ); }); - it('beforeSave change propagates through the save response', done => { - Parse.Cloud.beforeSave('ChangingObject', function (request) { - request.object.set('foo', 'baz'); + it("beforeSave change propagates through the save response", done => { + Parse.Cloud.beforeSave("ChangingObject", function (request) { + request.object.set("foo", "baz"); }); - const obj = new Parse.Object('ChangingObject'); - obj.save({ foo: 'bar' }).then( + const obj = new Parse.Object("ChangingObject"); + obj.save({ foo: "bar" }).then( objAgain => { - expect(objAgain.get('foo')).toEqual('baz'); + expect(objAgain.get("foo")).toEqual("baz"); done(); }, () => { - fail('Should not have failed to save.'); + fail("Should not have failed to save."); done(); } ); }); - it('beforeSave change propagates through the afterSave #1931', done => { - Parse.Cloud.beforeSave('ChangingObject', function (request) { - request.object.unset('file'); - request.object.unset('date'); + it("beforeSave change propagates through the afterSave #1931", done => { + Parse.Cloud.beforeSave("ChangingObject", function (request) { + request.object.unset("file"); + request.object.unset("date"); }); - Parse.Cloud.afterSave('ChangingObject', function (request) { - expect(request.object.has('file')).toBe(false); - expect(request.object.has('date')).toBe(false); - expect(request.object.get('file')).toBeUndefined(); + Parse.Cloud.afterSave("ChangingObject", function (request) { + expect(request.object.has("file")).toBe(false); + expect(request.object.has("date")).toBe(false); + expect(request.object.get("file")).toBeUndefined(); return Promise.resolve(); }); - const file = new Parse.File('yolo.txt', [1, 2, 3], 'text/plain'); + const file = new Parse.File("yolo.txt", [1, 2, 3], "text/plain"); file .save() .then(() => { - const obj = new Parse.Object('ChangingObject'); + const obj = new Parse.Object("ChangingObject"); return obj.save({ file, date: new Date() }); }) .then( @@ -1210,99 +1233,99 @@ describe('Cloud Code', () => { ); }); - it('test cloud function parameter validation success', done => { + it("test cloud function parameter validation success", done => { // Register a function with validation Parse.Cloud.define( - 'functionWithParameterValidation', + "functionWithParameterValidation", () => { - return 'works'; + return "works"; }, request => { return request.params.success === 100; } ); - Parse.Cloud.run('functionWithParameterValidation', { success: 100 }).then( + Parse.Cloud.run("functionWithParameterValidation", { success: 100 }).then( () => { done(); }, () => { - fail('Validation should not have failed.'); + fail("Validation should not have failed."); done(); } ); }); - it('doesnt receive stale user in cloud code functions after user has been updated with master key (regression test for #1836)', done => { - Parse.Cloud.define('testQuery', function (request) { - return request.user.get('data'); + it("doesnt receive stale user in cloud code functions after user has been updated with master key (regression test for #1836)", done => { + Parse.Cloud.define("testQuery", function (request) { + return request.user.get("data"); }); - Parse.User.signUp('user', 'pass') + Parse.User.signUp("user", "pass") .then(user => { - user.set('data', 'AAA'); + user.set("data", "AAA"); return user.save(); }) - .then(() => Parse.Cloud.run('testQuery')) + .then(() => Parse.Cloud.run("testQuery")) .then(result => { - expect(result).toEqual('AAA'); - Parse.User.current().set('data', 'BBB'); + expect(result).toEqual("AAA"); + Parse.User.current().set("data", "BBB"); return Parse.User.current().save(null, { useMasterKey: true }); }) - .then(() => Parse.Cloud.run('testQuery')) + .then(() => Parse.Cloud.run("testQuery")) .then(result => { - expect(result).toEqual('BBB'); + expect(result).toEqual("BBB"); done(); }); }); - it('clears out the user cache for all sessions when the user is changed', done => { + it("clears out the user cache for all sessions when the user is changed", done => { let session1; let session2; let user; const cacheAdapter = new InMemoryCacheAdapter({ ttl: 100000000 }); reconfigureServer({ cacheAdapter }) .then(() => { - Parse.Cloud.define('checkStaleUser', request => { - return request.user.get('data'); + Parse.Cloud.define("checkStaleUser", request => { + return request.user.get("data"); }); user = new Parse.User(); - user.set('username', 'test'); - user.set('password', 'moon-y'); - user.set('data', 'first data'); + user.set("username", "test"); + user.set("password", "moon-y"); + user.set("data", "first data"); return user.signUp(); }) .then(user => { session1 = user.getSessionToken(); return request({ - url: 'http://localhost:8378/1/login?username=test&password=moon-y', + url: "http://localhost:8378/1/login?username=test&password=moon-y", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }, }); }) .then(response => { session2 = response.data.sessionToken; //Ensure both session tokens are in the cache - return Parse.Cloud.run('checkStaleUser', { sessionToken: session2 }); + return Parse.Cloud.run("checkStaleUser", { sessionToken: session2 }); }) .then(() => request({ - method: 'POST', - url: 'http://localhost:8378/1/functions/checkStaleUser', + method: "POST", + url: "http://localhost:8378/1/functions/checkStaleUser", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Session-Token': session2, + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "X-Parse-Session-Token": session2, }, }) ) .then(() => Promise.all([ - cacheAdapter.get('test:user:' + session1), - cacheAdapter.get('test:user:' + session2), + cacheAdapter.get("test:user:" + session1), + cacheAdapter.get("test:user:" + session2), ]) ) .then(cachedVals => { @@ -1310,147 +1333,165 @@ describe('Cloud Code', () => { expect(cachedVals[1].objectId).toEqual(user.id); //Change with session 1 and then read with session 2. - user.set('data', 'second data'); + user.set("data", "second data"); return user.save(); }) .then(() => request({ - method: 'POST', - url: 'http://localhost:8378/1/functions/checkStaleUser', + method: "POST", + url: "http://localhost:8378/1/functions/checkStaleUser", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Session-Token': session2, + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "X-Parse-Session-Token": session2, }, }) ) .then(response => { - expect(response.data.result).toEqual('second data'); + expect(response.data.result).toEqual("second data"); done(); }) .catch(done.fail); }); - it('trivial beforeSave should not affect fetched pointers (regression test for #1238)', done => { - Parse.Cloud.beforeSave('BeforeSaveUnchanged', () => {}); + it("trivial beforeSave should not affect fetched pointers (regression test for #1238)", done => { + Parse.Cloud.beforeSave("BeforeSaveUnchanged", () => {}); - const TestObject = Parse.Object.extend('TestObject'); - const NoBeforeSaveObject = Parse.Object.extend('NoBeforeSave'); - const BeforeSaveObject = Parse.Object.extend('BeforeSaveUnchanged'); + const TestObject = Parse.Object.extend("TestObject"); + const NoBeforeSaveObject = Parse.Object.extend("NoBeforeSave"); + const BeforeSaveObject = Parse.Object.extend("BeforeSaveUnchanged"); const aTestObject = new TestObject(); - aTestObject.set('foo', 'bar'); + aTestObject.set("foo", "bar"); aTestObject .save() .then(aTestObject => { const aNoBeforeSaveObj = new NoBeforeSaveObject(); - aNoBeforeSaveObj.set('aTestObject', aTestObject); - expect(aNoBeforeSaveObj.get('aTestObject').get('foo')).toEqual('bar'); + aNoBeforeSaveObj.set("aTestObject", aTestObject); + expect(aNoBeforeSaveObj.get("aTestObject").get("foo")).toEqual("bar"); return aNoBeforeSaveObj.save(); }) .then(aNoBeforeSaveObj => { - expect(aNoBeforeSaveObj.get('aTestObject').get('foo')).toEqual('bar'); + expect(aNoBeforeSaveObj.get("aTestObject").get("foo")).toEqual("bar"); const aBeforeSaveObj = new BeforeSaveObject(); - aBeforeSaveObj.set('aTestObject', aTestObject); - expect(aBeforeSaveObj.get('aTestObject').get('foo')).toEqual('bar'); + aBeforeSaveObj.set("aTestObject", aTestObject); + expect(aBeforeSaveObj.get("aTestObject").get("foo")).toEqual("bar"); return aBeforeSaveObj.save(); }) .then(aBeforeSaveObj => { - expect(aBeforeSaveObj.get('aTestObject').get('foo')).toEqual('bar'); + expect(aBeforeSaveObj.get("aTestObject").get("foo")).toEqual("bar"); done(); }); }); - it('should not encode Parse Objects', async () => { + it("should not encode Parse Objects", async () => { await reconfigureServer({ encodeParseObjectInCloudFunction: false }); const user = new Parse.User(); - user.setUsername('username'); - user.setPassword('password'); - user.set('deleted', false); + user.setUsername("username"); + user.setPassword("password"); + user.set("deleted", false); await user.signUp(); Parse.Cloud.define( - 'deleteAccount', + "deleteAccount", async req => { expect(req.params.object instanceof Parse.Object).not.toBeTrue(); - return 'Object deleted'; + return "Object deleted"; }, { requireMaster: true, } ); - await Parse.Cloud.run('deleteAccount', { object: user.toPointer() }, { useMasterKey: true }); + await Parse.Cloud.run( + "deleteAccount", + { object: user.toPointer() }, + { useMasterKey: true } + ); }); - it('allow cloud to encode Parse Objects', async () => { + it("allow cloud to encode Parse Objects", async () => { await reconfigureServer({ encodeParseObjectInCloudFunction: true }); const user = new Parse.User(); - user.setUsername('username'); - user.setPassword('password'); - user.set('deleted', false); + user.setUsername("username"); + user.setPassword("password"); + user.set("deleted", false); await user.signUp(); Parse.Cloud.define( - 'deleteAccount', + "deleteAccount", async req => { expect(req.params.object instanceof Parse.Object).toBeTrue(); - req.params.object.set('deleted', true); + req.params.object.set("deleted", true); await req.params.object.save(null, { useMasterKey: true }); - return 'Object deleted'; + return "Object deleted"; }, { requireMaster: true, } ); - await Parse.Cloud.run('deleteAccount', { object: user.toPointer() }, { useMasterKey: true }); + await Parse.Cloud.run( + "deleteAccount", + { object: user.toPointer() }, + { useMasterKey: true } + ); }); - it('beforeSave should not affect fetched pointers', done => { - Parse.Cloud.beforeSave('BeforeSaveUnchanged', () => {}); + it("beforeSave should not affect fetched pointers", done => { + Parse.Cloud.beforeSave("BeforeSaveUnchanged", () => {}); - Parse.Cloud.beforeSave('BeforeSaveChanged', function (req) { - req.object.set('foo', 'baz'); + Parse.Cloud.beforeSave("BeforeSaveChanged", function (req) { + req.object.set("foo", "baz"); }); - const TestObject = Parse.Object.extend('TestObject'); - const BeforeSaveUnchangedObject = Parse.Object.extend('BeforeSaveUnchanged'); - const BeforeSaveChangedObject = Parse.Object.extend('BeforeSaveChanged'); + const TestObject = Parse.Object.extend("TestObject"); + const BeforeSaveUnchangedObject = Parse.Object.extend( + "BeforeSaveUnchanged" + ); + const BeforeSaveChangedObject = Parse.Object.extend("BeforeSaveChanged"); const aTestObject = new TestObject(); - aTestObject.set('foo', 'bar'); + aTestObject.set("foo", "bar"); aTestObject .save() .then(aTestObject => { const aBeforeSaveUnchangedObject = new BeforeSaveUnchangedObject(); - aBeforeSaveUnchangedObject.set('aTestObject', aTestObject); - expect(aBeforeSaveUnchangedObject.get('aTestObject').get('foo')).toEqual('bar'); + aBeforeSaveUnchangedObject.set("aTestObject", aTestObject); + expect( + aBeforeSaveUnchangedObject.get("aTestObject").get("foo") + ).toEqual("bar"); return aBeforeSaveUnchangedObject.save(); }) .then(aBeforeSaveUnchangedObject => { - expect(aBeforeSaveUnchangedObject.get('aTestObject').get('foo')).toEqual('bar'); + expect( + aBeforeSaveUnchangedObject.get("aTestObject").get("foo") + ).toEqual("bar"); const aBeforeSaveChangedObject = new BeforeSaveChangedObject(); - aBeforeSaveChangedObject.set('aTestObject', aTestObject); - expect(aBeforeSaveChangedObject.get('aTestObject').get('foo')).toEqual('bar'); + aBeforeSaveChangedObject.set("aTestObject", aTestObject); + expect(aBeforeSaveChangedObject.get("aTestObject").get("foo")).toEqual( + "bar" + ); return aBeforeSaveChangedObject.save(); }) .then(aBeforeSaveChangedObject => { - expect(aBeforeSaveChangedObject.get('aTestObject').get('foo')).toEqual('bar'); - expect(aBeforeSaveChangedObject.get('foo')).toEqual('baz'); + expect(aBeforeSaveChangedObject.get("aTestObject").get("foo")).toEqual( + "bar" + ); + expect(aBeforeSaveChangedObject.get("foo")).toEqual("baz"); done(); }); }); - it('should fully delete objects when using `unset` with beforeSave (regression test for #1840)', done => { - const TestObject = Parse.Object.extend('TestObject'); - const NoBeforeSaveObject = Parse.Object.extend('NoBeforeSave'); - const BeforeSaveObject = Parse.Object.extend('BeforeSaveChanged'); + it("should fully delete objects when using `unset` with beforeSave (regression test for #1840)", done => { + const TestObject = Parse.Object.extend("TestObject"); + const NoBeforeSaveObject = Parse.Object.extend("NoBeforeSave"); + const BeforeSaveObject = Parse.Object.extend("BeforeSaveChanged"); - Parse.Cloud.beforeSave('BeforeSaveChanged', req => { + Parse.Cloud.beforeSave("BeforeSaveChanged", req => { const object = req.object; - object.set('before', 'save'); + object.set("before", "save"); }); - Parse.Cloud.define('removeme', () => { + Parse.Cloud.define("removeme", () => { const testObject = new TestObject(); return testObject .save() @@ -1459,12 +1500,12 @@ describe('Cloud Code', () => { return object.save(); }) .then(object => { - object.unset('remove'); + object.unset("remove"); return object.save(); }); }); - Parse.Cloud.define('removeme2', () => { + Parse.Cloud.define("removeme2", () => { const testObject = new TestObject(); return testObject .save() @@ -1473,20 +1514,20 @@ describe('Cloud Code', () => { return object.save(); }) .then(object => { - object.unset('remove'); + object.unset("remove"); return object.save(); }); }); - Parse.Cloud.run('removeme') + Parse.Cloud.run("removeme") .then(aNoBeforeSaveObj => { - expect(aNoBeforeSaveObj.get('remove')).toEqual(undefined); + expect(aNoBeforeSaveObj.get("remove")).toEqual(undefined); - return Parse.Cloud.run('removeme2'); + return Parse.Cloud.run("removeme2"); }) .then(aBeforeSaveObj => { - expect(aBeforeSaveObj.get('before')).toEqual('save'); - expect(aBeforeSaveObj.get('remove')).toEqual(undefined); + expect(aBeforeSaveObj.get("before")).toEqual("save"); + expect(aBeforeSaveObj.get("remove")).toEqual(undefined); done(); }) .catch(err => { @@ -1499,20 +1540,20 @@ describe('Cloud Code', () => { TODO: fix for Postgres trying to delete a field that doesn't exists doesn't play nice */ - it_exclude_dbs(['postgres'])( - 'should fully delete objects when using `unset` and `set` with beforeSave (regression test for #1840)', + it_exclude_dbs(["postgres"])( + "should fully delete objects when using `unset` and `set` with beforeSave (regression test for #1840)", done => { - const TestObject = Parse.Object.extend('TestObject'); - const BeforeSaveObject = Parse.Object.extend('BeforeSaveChanged'); + const TestObject = Parse.Object.extend("TestObject"); + const BeforeSaveObject = Parse.Object.extend("BeforeSaveChanged"); - Parse.Cloud.beforeSave('BeforeSaveChanged', req => { + Parse.Cloud.beforeSave("BeforeSaveChanged", req => { const object = req.object; - object.set('before', 'save'); - object.unset('remove'); + object.set("before", "save"); + object.unset("remove"); }); let object; - const testObject = new TestObject({ key: 'value' }); + const testObject = new TestObject({ key: "value" }); testObject .save() .then(() => { @@ -1523,8 +1564,8 @@ describe('Cloud Code', () => { }); }) .then(objectAgain => { - expect(objectAgain.get('remove')).toBeUndefined(); - expect(object.get('remove')).toBeUndefined(); + expect(objectAgain.get("remove")).toBeUndefined(); + expect(object.get("remove")).toBeUndefined(); done(); }) .catch(err => { @@ -1534,16 +1575,16 @@ describe('Cloud Code', () => { } ); - it('should not include relation op (regression test for #1606)', done => { - const TestObject = Parse.Object.extend('TestObject'); - const BeforeSaveObject = Parse.Object.extend('BeforeSaveChanged'); + it("should not include relation op (regression test for #1606)", done => { + const TestObject = Parse.Object.extend("TestObject"); + const BeforeSaveObject = Parse.Object.extend("BeforeSaveChanged"); let testObj; - Parse.Cloud.beforeSave('BeforeSaveChanged', req => { + Parse.Cloud.beforeSave("BeforeSaveChanged", req => { const object = req.object; - object.set('before', 'save'); + object.set("before", "save"); testObj = new TestObject(); return testObj.save().then(() => { - object.relation('testsRelation').add(testObj); + object.relation("testsRelation").add(testObj); }); }); @@ -1553,7 +1594,7 @@ describe('Cloud Code', () => { .then(objectAgain => { // Originally it would throw as it would be a non-relation expect(() => { - objectAgain.relation('testsRelation'); + objectAgain.relation("testsRelation"); }).not.toThrow(); done(); }) @@ -1567,63 +1608,63 @@ describe('Cloud Code', () => { * Checks that incrementing a value to a zero in a beforeSave hook * does not result in that key being omitted from the response. */ - it('before save increment does not return undefined', done => { - Parse.Cloud.define('cloudIncrementClassFunction', function (req) { - const CloudIncrementClass = Parse.Object.extend('CloudIncrementClass'); + it("before save increment does not return undefined", done => { + Parse.Cloud.define("cloudIncrementClassFunction", function (req) { + const CloudIncrementClass = Parse.Object.extend("CloudIncrementClass"); const obj = new CloudIncrementClass(); obj.id = req.params.objectId; return obj.save(); }); - Parse.Cloud.beforeSave('CloudIncrementClass', function (req) { + Parse.Cloud.beforeSave("CloudIncrementClass", function (req) { const obj = req.object; if (!req.master) { - obj.increment('points', -10); - obj.increment('num', -9); + obj.increment("points", -10); + obj.increment("num", -9); } }); - const CloudIncrementClass = Parse.Object.extend('CloudIncrementClass'); + const CloudIncrementClass = Parse.Object.extend("CloudIncrementClass"); const obj = new CloudIncrementClass(); - obj.set('points', 10); - obj.set('num', 10); + obj.set("points", 10); + obj.set("num", 10); obj.save(null, { useMasterKey: true }).then(function () { - Parse.Cloud.run('cloudIncrementClassFunction', { objectId: obj.id }).then( + Parse.Cloud.run("cloudIncrementClassFunction", { objectId: obj.id }).then( function (savedObj) { - expect(savedObj.get('num')).toEqual(1); - expect(savedObj.get('points')).toEqual(0); + expect(savedObj.get("num")).toEqual(1); + expect(savedObj.get("points")).toEqual(0); done(); } ); }); }); - it('before save can revert fields', async () => { - Parse.Cloud.beforeSave('TestObject', ({ object }) => { - object.revert('foo'); + it("before save can revert fields", async () => { + Parse.Cloud.beforeSave("TestObject", ({ object }) => { + object.revert("foo"); return object; }); - Parse.Cloud.afterSave('TestObject', ({ object }) => { - expect(object.get('foo')).toBeUndefined(); + Parse.Cloud.afterSave("TestObject", ({ object }) => { + expect(object.get("foo")).toBeUndefined(); return object; }); const obj = new TestObject(); - obj.set('foo', 'bar'); + obj.set("foo", "bar"); await obj.save(); - expect(obj.get('foo')).toBeUndefined(); + expect(obj.get("foo")).toBeUndefined(); await obj.fetch(); - expect(obj.get('foo')).toBeUndefined(); + expect(obj.get("foo")).toBeUndefined(); }); - it('before save can revert fields with existing object', async () => { + it("before save can revert fields with existing object", async () => { Parse.Cloud.beforeSave( - 'TestObject', + "TestObject", ({ object }) => { - object.revert('foo'); + object.revert("foo"); return object; }, { @@ -1632,9 +1673,9 @@ describe('Cloud Code', () => { ); Parse.Cloud.afterSave( - 'TestObject', + "TestObject", ({ object }) => { - expect(object.get('foo')).toBe('bar'); + expect(object.get("foo")).toBe("bar"); return object; }, { @@ -1643,48 +1684,55 @@ describe('Cloud Code', () => { ); const obj = new TestObject(); - obj.set('foo', 'bar'); + obj.set("foo", "bar"); await obj.save(null, { useMasterKey: true }); - expect(obj.get('foo')).toBe('bar'); - obj.set('foo', 'yolo'); + expect(obj.get("foo")).toBe("bar"); + obj.set("foo", "yolo"); await obj.save(); - expect(obj.get('foo')).toBe('bar'); + expect(obj.get("foo")).toBe("bar"); }); - it('create role with name and ACL and a beforeSave', async () => { + it("create role with name and ACL and a beforeSave", async () => { Parse.Cloud.beforeSave(Parse.Role, ({ object }) => { return object; }); - const obj = new Parse.Role('TestRole', new Parse.ACL({ '*': { read: true, write: true } })); + const obj = new Parse.Role( + "TestRole", + new Parse.ACL({ "*": { read: true, write: true } }) + ); await obj.save(); - expect(obj.getACL()).toEqual(new Parse.ACL({ '*': { read: true, write: true } })); - expect(obj.get('name')).toEqual('TestRole'); + expect(obj.getACL()).toEqual( + new Parse.ACL({ "*": { read: true, write: true } }) + ); + expect(obj.get("name")).toEqual("TestRole"); await obj.fetch(); - expect(obj.getACL()).toEqual(new Parse.ACL({ '*': { read: true, write: true } })); - expect(obj.get('name')).toEqual('TestRole'); + expect(obj.getACL()).toEqual( + new Parse.ACL({ "*": { read: true, write: true } }) + ); + expect(obj.get("name")).toEqual("TestRole"); }); - it('can unset in afterSave', async () => { - Parse.Cloud.beforeSave('TestObject', ({ object }) => { + it("can unset in afterSave", async () => { + Parse.Cloud.beforeSave("TestObject", ({ object }) => { if (!object.existed()) { - object.set('secret', true); + object.set("secret", true); return object; } - object.revert('secret'); + object.revert("secret"); }); - Parse.Cloud.afterSave('TestObject', ({ object }) => { - object.unset('secret'); + Parse.Cloud.afterSave("TestObject", ({ object }) => { + object.unset("secret"); }); Parse.Cloud.beforeFind( - 'TestObject', + "TestObject", ({ query }) => { - query.exclude('secret'); + query.exclude("secret"); }, { skipWithMasterKey: true, @@ -1693,45 +1741,45 @@ describe('Cloud Code', () => { const obj = new TestObject(); await obj.save(); - expect(obj.get('secret')).toBeUndefined(); + expect(obj.get("secret")).toBeUndefined(); await obj.fetch(); - expect(obj.get('secret')).toBeUndefined(); + expect(obj.get("secret")).toBeUndefined(); await obj.fetch({ useMasterKey: true }); - expect(obj.get('secret')).toBe(true); + expect(obj.get("secret")).toBe(true); }); - it('should revert in beforeSave', async () => { - Parse.Cloud.beforeSave('MyObject', ({ object }) => { + it("should revert in beforeSave", async () => { + Parse.Cloud.beforeSave("MyObject", ({ object }) => { if (!object.existed()) { - object.set('count', 0); + object.set("count", 0); return object; } - object.revert('count'); + object.revert("count"); return object; }); - const obj = await new Parse.Object('MyObject').save(); - expect(obj.get('count')).toBe(0); - obj.set('count', 10); + const obj = await new Parse.Object("MyObject").save(); + expect(obj.get("count")).toBe(0); + obj.set("count", 10); await obj.save(); - expect(obj.get('count')).toBe(0); + expect(obj.get("count")).toBe(0); await obj.fetch(); - expect(obj.get('count')).toBe(0); + expect(obj.get("count")).toBe(0); }); - it('pointer should not be cleared by triggers', async () => { - Parse.Cloud.afterSave('MyObject', () => {}); - const foo = await new Parse.Object('Test', { foo: 'bar' }).save(); - const obj = await new Parse.Object('MyObject', { foo }).save(); - const foo2 = obj.get('foo'); - expect(foo2.get('foo')).toBe('bar'); + it("pointer should not be cleared by triggers", async () => { + Parse.Cloud.afterSave("MyObject", () => {}); + const foo = await new Parse.Object("Test", { foo: "bar" }).save(); + const obj = await new Parse.Object("MyObject", { foo }).save(); + const foo2 = obj.get("foo"); + expect(foo2.get("foo")).toBe("bar"); }); - it('can set a pointer in triggers', async () => { - Parse.Cloud.beforeSave('MyObject', () => {}); + it("can set a pointer in triggers", async () => { + Parse.Cloud.beforeSave("MyObject", () => {}); Parse.Cloud.afterSave( - 'MyObject', + "MyObject", async ({ object }) => { - const foo = await new Parse.Object('Test', { foo: 'bar' }).save(); + const foo = await new Parse.Object("Test", { foo: "bar" }).save(); object.set({ foo }); await object.save(null, { useMasterKey: true }); }, @@ -1739,40 +1787,40 @@ describe('Cloud Code', () => { skipWithMasterKey: true, } ); - const obj = await new Parse.Object('MyObject').save(); - const foo2 = obj.get('foo'); - expect(foo2.get('foo')).toBe('bar'); + const obj = await new Parse.Object("MyObject").save(); + const foo2 = obj.get("foo"); + expect(foo2.get("foo")).toBe("bar"); }); - it('beforeSave should not sanitize database', async done => { + it("beforeSave should not sanitize database", async done => { const { adapter } = Config.get(Parse.applicationId).database; - const spy = spyOn(adapter, 'findOneAndUpdate').and.callThrough(); + const spy = spyOn(adapter, "findOneAndUpdate").and.callThrough(); spy.calls.saveArgumentsByValue(); let count = 0; - Parse.Cloud.beforeSave('CloudIncrementNested', req => { + Parse.Cloud.beforeSave("CloudIncrementNested", req => { count += 1; - req.object.set('foo', 'baz'); - expect(typeof req.object.get('objectField').number).toBe('number'); + req.object.set("foo", "baz"); + expect(typeof req.object.get("objectField").number).toBe("number"); }); - Parse.Cloud.afterSave('CloudIncrementNested', req => { - expect(typeof req.object.get('objectField').number).toBe('number'); + Parse.Cloud.afterSave("CloudIncrementNested", req => { + expect(typeof req.object.get("objectField").number).toBe("number"); }); - const obj = new Parse.Object('CloudIncrementNested'); - obj.set('objectField', { number: 5 }); - obj.set('foo', 'bar'); + const obj = new Parse.Object("CloudIncrementNested"); + obj.set("objectField", { number: 5 }); + obj.set("foo", "bar"); await obj.save(); - obj.increment('objectField.number', 10); + obj.increment("objectField.number", 10); await obj.save(); const [, , , /* className */ /* schema */ /* query */ update] = adapter.findOneAndUpdate.calls.first().args; expect(update).toEqual({ - 'objectField.number': { __op: 'Increment', amount: 10 }, - foo: 'baz', + "objectField.number": { __op: "Increment", amount: 10 }, + foo: "baz", updatedAt: obj.updatedAt.toISOString(), }); @@ -1783,36 +1831,39 @@ describe('Cloud Code', () => { * Verifies that an afterSave hook throwing an exception * will not prevent a successful save response from being returned */ - it('should succeed on afterSave exception', done => { - Parse.Cloud.afterSave('AfterSaveTestClass', function () { - throw 'Exception'; + it("should succeed on afterSave exception", done => { + Parse.Cloud.afterSave("AfterSaveTestClass", function () { + throw "Exception"; }); - const AfterSaveTestClass = Parse.Object.extend('AfterSaveTestClass'); + const AfterSaveTestClass = Parse.Object.extend("AfterSaveTestClass"); const obj = new AfterSaveTestClass(); obj.save().then(done, done.fail); }); - describe('cloud jobs', () => { - it('should define a job', done => { + describe("cloud jobs", () => { + it("should define a job", done => { expect(() => { - Parse.Cloud.job('myJob', ({ message }) => { - message('Hello, world!!!'); + Parse.Cloud.job("myJob", ({ message }) => { + message("Hello, world!!!"); }); }).not.toThrow(); request({ - method: 'POST', - url: 'http://localhost:8378/1/jobs/myJob', + method: "POST", + url: "http://localhost:8378/1/jobs/myJob", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': Parse.masterKey, + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Master-Key": Parse.masterKey, }, }) .then(async response => { - const jobStatusId = response.headers['x-parse-job-status-id']; + const jobStatusId = response.headers["x-parse-job-status-id"]; const checkJobStatus = async () => { const jobStatus = await getJobStatus(jobStatusId); - return jobStatus.get('finishedAt') && jobStatus.get('message') === 'Hello, world!!!'; + return ( + jobStatus.get("finishedAt") && + jobStatus.get("message") === "Hello, world!!!" + ); }; while (!(await checkJobStatus())) { await new Promise(resolve => setTimeout(resolve, 100)); @@ -1822,21 +1873,21 @@ describe('Cloud Code', () => { .catch(done.fail); }); - it('should not run without master key', done => { + it("should not run without master key", done => { expect(() => { - Parse.Cloud.job('myJob', () => {}); + Parse.Cloud.job("myJob", () => {}); }).not.toThrow(); request({ - method: 'POST', - url: 'http://localhost:8378/1/jobs/myJob', + method: "POST", + url: "http://localhost:8378/1/jobs/myJob", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", }, }).then( () => { - fail('Expected to be unauthorized'); + fail("Expected to be unauthorized"); done(); }, err => { @@ -1846,30 +1897,30 @@ describe('Cloud Code', () => { ); }); - it('should run with master key', done => { + it("should run with master key", done => { expect(() => { - Parse.Cloud.job('myJob', (req, res) => { + Parse.Cloud.job("myJob", (req, res) => { expect(req.functionName).toBeUndefined(); - expect(req.jobName).toBe('myJob'); - expect(typeof req.jobId).toBe('string'); - expect(typeof req.message).toBe('function'); - expect(typeof res).toBe('undefined'); + expect(req.jobName).toBe("myJob"); + expect(typeof req.jobId).toBe("string"); + expect(typeof req.message).toBe("function"); + expect(typeof res).toBe("undefined"); }); }).not.toThrow(); request({ - method: 'POST', - url: 'http://localhost:8378/1/jobs/myJob', + method: "POST", + url: "http://localhost:8378/1/jobs/myJob", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': Parse.masterKey, + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Master-Key": Parse.masterKey, }, }) .then(async response => { - const jobStatusId = response.headers['x-parse-job-status-id']; + const jobStatusId = response.headers["x-parse-job-status-id"]; const checkJobStatus = async () => { const jobStatus = await getJobStatus(jobStatusId); - return jobStatus.get('finishedAt'); + return jobStatus.get("finishedAt"); }; while (!(await checkJobStatus())) { await new Promise(resolve => setTimeout(resolve, 100)); @@ -1879,26 +1930,26 @@ describe('Cloud Code', () => { .catch(done.fail); }); - it('should run with master key basic auth', done => { + it("should run with master key basic auth", done => { expect(() => { - Parse.Cloud.job('myJob', (req, res) => { + Parse.Cloud.job("myJob", (req, res) => { expect(req.functionName).toBeUndefined(); - expect(req.jobName).toBe('myJob'); - expect(typeof req.jobId).toBe('string'); - expect(typeof req.message).toBe('function'); - expect(typeof res).toBe('undefined'); + expect(req.jobName).toBe("myJob"); + expect(typeof req.jobId).toBe("string"); + expect(typeof req.message).toBe("function"); + expect(typeof res).toBe("undefined"); }); }).not.toThrow(); request({ - method: 'POST', + method: "POST", url: `http://${Parse.applicationId}:${Parse.masterKey}@localhost:8378/1/jobs/myJob`, }) .then(async response => { - const jobStatusId = response.headers['x-parse-job-status-id']; + const jobStatusId = response.headers["x-parse-job-status-id"]; const checkJobStatus = async () => { const jobStatus = await getJobStatus(jobStatusId); - return jobStatus.get('finishedAt'); + return jobStatus.get("finishedAt"); }; while (!(await checkJobStatus())) { await new Promise(resolve => setTimeout(resolve, 100)); @@ -1908,35 +1959,35 @@ describe('Cloud Code', () => { .catch(done.fail); }); - it('should set the message / success on the job', done => { - Parse.Cloud.job('myJob', req => { + it("should set the message / success on the job", done => { + Parse.Cloud.job("myJob", req => { return req - .message('hello') + .message("hello") .then(() => { return getJobStatus(req.jobId); }) .then(jobStatus => { - expect(jobStatus.get('message')).toEqual('hello'); - expect(jobStatus.get('status')).toEqual('running'); + expect(jobStatus.get("message")).toEqual("hello"); + expect(jobStatus.get("status")).toEqual("running"); }); }); request({ - method: 'POST', - url: 'http://localhost:8378/1/jobs/myJob', + method: "POST", + url: "http://localhost:8378/1/jobs/myJob", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': Parse.masterKey, + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Master-Key": Parse.masterKey, }, }) .then(async response => { - const jobStatusId = response.headers['x-parse-job-status-id']; + const jobStatusId = response.headers["x-parse-job-status-id"]; const checkJobStatus = async () => { const jobStatus = await getJobStatus(jobStatusId); return ( - jobStatus.get('finishedAt') && - jobStatus.get('message') === 'hello' && - jobStatus.get('status') === 'succeeded' + jobStatus.get("finishedAt") && + jobStatus.get("message") === "hello" && + jobStatus.get("status") === "succeeded" ); }; while (!(await checkJobStatus())) { @@ -1947,27 +1998,27 @@ describe('Cloud Code', () => { .catch(done.fail); }); - it('should set the failure on the job', done => { - Parse.Cloud.job('myJob', () => { - return Promise.reject('Something went wrong'); + it("should set the failure on the job", done => { + Parse.Cloud.job("myJob", () => { + return Promise.reject("Something went wrong"); }); request({ - method: 'POST', - url: 'http://localhost:8378/1/jobs/myJob', + method: "POST", + url: "http://localhost:8378/1/jobs/myJob", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': Parse.masterKey, + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Master-Key": Parse.masterKey, }, }) .then(async response => { - const jobStatusId = response.headers['x-parse-job-status-id']; + const jobStatusId = response.headers["x-parse-job-status-id"]; const checkJobStatus = async () => { const jobStatus = await getJobStatus(jobStatusId); return ( - jobStatus.get('finishedAt') && - jobStatus.get('message') === 'Something went wrong' && - jobStatus.get('status') === 'failed' + jobStatus.get("finishedAt") && + jobStatus.get("message") === "Something went wrong" && + jobStatus.get("status") === "failed" ); }; while (!(await checkJobStatus())) { @@ -1978,154 +2029,157 @@ describe('Cloud Code', () => { .catch(done.fail); }); - it('should set the failure message on the job error', async () => { - Parse.Cloud.job('myJobError', () => { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Something went wrong'); + it("should set the failure message on the job error", async () => { + Parse.Cloud.job("myJobError", () => { + throw new Parse.Error( + Parse.Error.OBJECT_NOT_FOUND, + "Something went wrong" + ); }); - const job = await Parse.Cloud.startJob('myJobError'); + const job = await Parse.Cloud.startJob("myJobError"); let jobStatus, status; - while (status !== 'failed') { + while (status !== "failed") { if (jobStatus) { await new Promise(resolve => setTimeout(resolve, 10)); } jobStatus = await Parse.Cloud.getJobStatus(job); - status = jobStatus.get('status'); + status = jobStatus.get("status"); } - expect(jobStatus.get('message')).toEqual('Something went wrong'); + expect(jobStatus.get("message")).toEqual("Something went wrong"); }); function getJobStatus(jobId) { - const q = new Parse.Query('_JobStatus'); + const q = new Parse.Query("_JobStatus"); return q.get(jobId, { useMasterKey: true }); } }); }); -describe('cloud functions', () => { - it('Should have request ip', done => { - Parse.Cloud.define('myFunction', req => { +describe("cloud functions", () => { + it("Should have request ip", done => { + Parse.Cloud.define("myFunction", req => { expect(req.ip).toBeDefined(); - return 'success'; + return "success"; }); - Parse.Cloud.run('myFunction', {}).then(() => done()); + Parse.Cloud.run("myFunction", {}).then(() => done()); }); }); -describe('beforeSave hooks', () => { - it('should have request headers', done => { - Parse.Cloud.beforeSave('MyObject', req => { +describe("beforeSave hooks", () => { + it("should have request headers", done => { + Parse.Cloud.beforeSave("MyObject", req => { expect(req.headers).toBeDefined(); }); - const MyObject = Parse.Object.extend('MyObject'); + const MyObject = Parse.Object.extend("MyObject"); const myObject = new MyObject(); myObject.save().then(() => done()); }); - it('should have request ip', done => { - Parse.Cloud.beforeSave('MyObject', req => { + it("should have request ip", done => { + Parse.Cloud.beforeSave("MyObject", req => { expect(req.ip).toBeDefined(); }); - const MyObject = Parse.Object.extend('MyObject'); + const MyObject = Parse.Object.extend("MyObject"); const myObject = new MyObject(); myObject.save().then(() => done()); }); - it('should respect custom object ids (#6733)', async () => { - Parse.Cloud.beforeSave('TestObject', req => { - expect(req.object.id).toEqual('test_6733'); + it("should respect custom object ids (#6733)", async () => { + Parse.Cloud.beforeSave("TestObject", req => { + expect(req.object.id).toEqual("test_6733"); }); await reconfigureServer({ allowCustomObjectId: true }); const req = request({ // Parse JS SDK does not currently support custom object ids (see #1097), so we do a REST request - method: 'POST', - url: 'http://localhost:8378/1/classes/TestObject', + method: "POST", + url: "http://localhost:8378/1/classes/TestObject", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }, body: { - objectId: 'test_6733', - foo: 'bar', + objectId: "test_6733", + foo: "bar", }, }); { const res = await req; - expect(res.data.objectId).toEqual('test_6733'); + expect(res.data.objectId).toEqual("test_6733"); } - const query = new Parse.Query('TestObject'); - query.equalTo('objectId', 'test_6733'); + const query = new Parse.Query("TestObject"); + query.equalTo("objectId", "test_6733"); const res = await query.find(); expect(res.length).toEqual(1); - expect(res[0].get('foo')).toEqual('bar'); + expect(res[0].get("foo")).toEqual("bar"); }); }); -describe('afterSave hooks', () => { - it('should have request headers', done => { - Parse.Cloud.afterSave('MyObject', req => { +describe("afterSave hooks", () => { + it("should have request headers", done => { + Parse.Cloud.afterSave("MyObject", req => { expect(req.headers).toBeDefined(); }); - const MyObject = Parse.Object.extend('MyObject'); + const MyObject = Parse.Object.extend("MyObject"); const myObject = new MyObject(); myObject.save().then(() => done()); }); - it('should have request ip', done => { - Parse.Cloud.afterSave('MyObject', req => { + it("should have request ip", done => { + Parse.Cloud.afterSave("MyObject", req => { expect(req.ip).toBeDefined(); }); - const MyObject = Parse.Object.extend('MyObject'); + const MyObject = Parse.Object.extend("MyObject"); const myObject = new MyObject(); myObject.save().then(() => done()); }); - it('should unset in afterSave', async () => { + it("should unset in afterSave", async () => { Parse.Cloud.afterSave( - 'MyObject', + "MyObject", ({ object }) => { - object.unset('secret'); + object.unset("secret"); }, { skipWithMasterKey: true, } ); - const obj = new Parse.Object('MyObject'); - obj.set('secret', 'bar'); + const obj = new Parse.Object("MyObject"); + obj.set("secret", "bar"); await obj.save(); - expect(obj.get('secret')).toBeUndefined(); + expect(obj.get("secret")).toBeUndefined(); await obj.fetch(); - expect(obj.get('secret')).toBe('bar'); + expect(obj.get("secret")).toBe("bar"); }); - it('should unset', async () => { - Parse.Cloud.beforeSave('MyObject', ({ object }) => { - object.set('secret', 'hidden'); + it("should unset", async () => { + Parse.Cloud.beforeSave("MyObject", ({ object }) => { + object.set("secret", "hidden"); }); - Parse.Cloud.afterSave('MyObject', ({ object }) => { - object.unset('secret'); + Parse.Cloud.afterSave("MyObject", ({ object }) => { + object.unset("secret"); }); - const obj = await new Parse.Object('MyObject').save(); - expect(obj.get('secret')).toBeUndefined(); + const obj = await new Parse.Object("MyObject").save(); + expect(obj.get("secret")).toBeUndefined(); }); }); -describe('beforeDelete hooks', () => { - it('should have request headers', done => { - Parse.Cloud.beforeDelete('MyObject', req => { +describe("beforeDelete hooks", () => { + it("should have request headers", done => { + Parse.Cloud.beforeDelete("MyObject", req => { expect(req.headers).toBeDefined(); }); - const MyObject = Parse.Object.extend('MyObject'); + const MyObject = Parse.Object.extend("MyObject"); const myObject = new MyObject(); myObject .save() @@ -2133,12 +2187,12 @@ describe('beforeDelete hooks', () => { .then(() => done()); }); - it('should have request ip', done => { - Parse.Cloud.beforeDelete('MyObject', req => { + it("should have request ip", done => { + Parse.Cloud.beforeDelete("MyObject", req => { expect(req.ip).toBeDefined(); }); - const MyObject = Parse.Object.extend('MyObject'); + const MyObject = Parse.Object.extend("MyObject"); const myObject = new MyObject(); myObject .save() @@ -2147,13 +2201,13 @@ describe('beforeDelete hooks', () => { }); }); -describe('afterDelete hooks', () => { - it('should have request headers', done => { - Parse.Cloud.afterDelete('MyObject', req => { +describe("afterDelete hooks", () => { + it("should have request headers", done => { + Parse.Cloud.afterDelete("MyObject", req => { expect(req.headers).toBeDefined(); }); - const MyObject = Parse.Object.extend('MyObject'); + const MyObject = Parse.Object.extend("MyObject"); const myObject = new MyObject(); myObject .save() @@ -2161,12 +2215,12 @@ describe('afterDelete hooks', () => { .then(() => done()); }); - it('should have request ip', done => { - Parse.Cloud.afterDelete('MyObject', req => { + it("should have request ip", done => { + Parse.Cloud.afterDelete("MyObject", req => { expect(req.ip).toBeDefined(); }); - const MyObject = Parse.Object.extend('MyObject'); + const MyObject = Parse.Object.extend("MyObject"); const myObject = new MyObject(); myObject .save() @@ -2175,80 +2229,80 @@ describe('afterDelete hooks', () => { }); }); -describe('beforeFind hooks', () => { - it('should add beforeFind trigger', done => { - Parse.Cloud.beforeFind('MyObject', req => { +describe("beforeFind hooks", () => { + it("should add beforeFind trigger", done => { + Parse.Cloud.beforeFind("MyObject", req => { const q = req.query; expect(q instanceof Parse.Query).toBe(true); const jsonQuery = q.toJSON(); - expect(jsonQuery.where.key).toEqual('value'); + expect(jsonQuery.where.key).toEqual("value"); expect(jsonQuery.where.some).toEqual({ $gt: 10 }); - expect(jsonQuery.include).toEqual('otherKey,otherValue'); - expect(jsonQuery.excludeKeys).toBe('exclude'); + expect(jsonQuery.include).toEqual("otherKey,otherValue"); + expect(jsonQuery.excludeKeys).toBe("exclude"); expect(jsonQuery.limit).toEqual(100); expect(jsonQuery.skip).toBe(undefined); - expect(jsonQuery.order).toBe('key'); - expect(jsonQuery.keys).toBe('select'); - expect(jsonQuery.readPreference).toBe('PRIMARY'); - expect(jsonQuery.includeReadPreference).toBe('SECONDARY'); - expect(jsonQuery.subqueryReadPreference).toBe('SECONDARY_PREFERRED'); + expect(jsonQuery.order).toBe("key"); + expect(jsonQuery.keys).toBe("select"); + expect(jsonQuery.readPreference).toBe("PRIMARY"); + expect(jsonQuery.includeReadPreference).toBe("SECONDARY"); + expect(jsonQuery.subqueryReadPreference).toBe("SECONDARY_PREFERRED"); expect(req.isGet).toEqual(false); }); - const query = new Parse.Query('MyObject'); - query.equalTo('key', 'value'); - query.greaterThan('some', 10); - query.include('otherKey'); - query.include('otherValue'); - query.ascending('key'); - query.select('select'); - query.exclude('exclude'); - query.readPreference('PRIMARY', 'SECONDARY', 'SECONDARY_PREFERRED'); + const query = new Parse.Query("MyObject"); + query.equalTo("key", "value"); + query.greaterThan("some", 10); + query.include("otherKey"); + query.include("otherValue"); + query.ascending("key"); + query.select("select"); + query.exclude("exclude"); + query.readPreference("PRIMARY", "SECONDARY", "SECONDARY_PREFERRED"); query.find().then(() => { done(); }); }); - it('should use modify', done => { - Parse.Cloud.beforeFind('MyObject', req => { + it("should use modify", done => { + Parse.Cloud.beforeFind("MyObject", req => { const q = req.query; - q.equalTo('forced', true); + q.equalTo("forced", true); }); - const obj0 = new Parse.Object('MyObject'); - obj0.set('forced', false); + const obj0 = new Parse.Object("MyObject"); + obj0.set("forced", false); - const obj1 = new Parse.Object('MyObject'); - obj1.set('forced', true); + const obj1 = new Parse.Object("MyObject"); + obj1.set("forced", true); Parse.Object.saveAll([obj0, obj1]).then(() => { - const query = new Parse.Query('MyObject'); - query.equalTo('forced', false); + const query = new Parse.Query("MyObject"); + query.equalTo("forced", false); query.find().then(results => { expect(results.length).toBe(1); const firstResult = results[0]; - expect(firstResult.get('forced')).toBe(true); + expect(firstResult.get("forced")).toBe(true); done(); }); }); }); - it('should use the modified the query', done => { - Parse.Cloud.beforeFind('MyObject', req => { + it("should use the modified the query", done => { + Parse.Cloud.beforeFind("MyObject", req => { const q = req.query; - const otherQuery = new Parse.Query('MyObject'); - otherQuery.equalTo('forced', true); + const otherQuery = new Parse.Query("MyObject"); + otherQuery.equalTo("forced", true); return Parse.Query.or(q, otherQuery); }); - const obj0 = new Parse.Object('MyObject'); - obj0.set('forced', false); + const obj0 = new Parse.Object("MyObject"); + obj0.set("forced", false); - const obj1 = new Parse.Object('MyObject'); - obj1.set('forced', true); + const obj1 = new Parse.Object("MyObject"); + obj1.set("forced", true); Parse.Object.saveAll([obj0, obj1]).then(() => { - const query = new Parse.Query('MyObject'); - query.equalTo('forced', false); + const query = new Parse.Query("MyObject"); + query.equalTo("forced", false); query.find().then(results => { expect(results.length).toBe(2); done(); @@ -2256,332 +2310,335 @@ describe('beforeFind hooks', () => { }); }); - it('should have object found with nested relational data query', async () => { - const obj1 = Parse.Object.extend('TestObject'); - const obj2 = Parse.Object.extend('TestObject2'); + it("should have object found with nested relational data query", async () => { + const obj1 = Parse.Object.extend("TestObject"); + const obj2 = Parse.Object.extend("TestObject2"); let item2 = new obj2(); item2 = await item2.save(); let item1 = new obj1(); - const relation = item1.relation('rel'); + const relation = item1.relation("rel"); relation.add(item2); item1 = await item1.save(); - Parse.Cloud.beforeFind('TestObject', req => { - const additionalQ = new Parse.Query('TestObject'); - additionalQ.equalTo('rel', item2); + Parse.Cloud.beforeFind("TestObject", req => { + const additionalQ = new Parse.Query("TestObject"); + additionalQ.equalTo("rel", item2); return Parse.Query.and(req.query, additionalQ); }); - const q = new Parse.Query('TestObject'); + const q = new Parse.Query("TestObject"); const res = await q.first(); expect(res.id).toEqual(item1.id); }); - it('should use the modified exclude query', async () => { - Parse.Cloud.beforeFind('MyObject', req => { + it("should use the modified exclude query", async () => { + Parse.Cloud.beforeFind("MyObject", req => { const q = req.query; - q.exclude('number'); + q.exclude("number"); }); - const obj = new Parse.Object('MyObject'); - obj.set('number', 100); - obj.set('string', 'hello'); + const obj = new Parse.Object("MyObject"); + obj.set("number", 100); + obj.set("string", "hello"); await obj.save(); - const query = new Parse.Query('MyObject'); - query.equalTo('objectId', obj.id); + const query = new Parse.Query("MyObject"); + query.equalTo("objectId", obj.id); const results = await query.find(); expect(results.length).toBe(1); - expect(results[0].get('number')).toBeUndefined(); - expect(results[0].get('string')).toBe('hello'); + expect(results[0].get("number")).toBeUndefined(); + expect(results[0].get("string")).toBe("hello"); }); - it('should reject queries', done => { - Parse.Cloud.beforeFind('MyObject', () => { - return Promise.reject('Do not run that query'); + it("should reject queries", done => { + Parse.Cloud.beforeFind("MyObject", () => { + return Promise.reject("Do not run that query"); }); - const query = new Parse.Query('MyObject'); + const query = new Parse.Query("MyObject"); query.find().then( () => { - fail('should not succeed'); + fail("should not succeed"); done(); }, err => { expect(err.code).toBe(Parse.Error.SCRIPT_FAILED); - expect(err.message).toEqual('Do not run that query'); + expect(err.message).toEqual("Do not run that query"); done(); } ); }); - it_id('6ef0d226-af30-4dfd-8306-972a1b4becd3')(it)('should handle empty where', done => { - Parse.Cloud.beforeFind('MyObject', req => { - const otherQuery = new Parse.Query('MyObject'); - otherQuery.equalTo('some', true); - return Parse.Query.or(req.query, otherQuery); - }); + it_id("6ef0d226-af30-4dfd-8306-972a1b4becd3")(it)( + "should handle empty where", + done => { + Parse.Cloud.beforeFind("MyObject", req => { + const otherQuery = new Parse.Query("MyObject"); + otherQuery.equalTo("some", true); + return Parse.Query.or(req.query, otherQuery); + }); - request({ - url: 'http://localhost:8378/1/classes/MyObject', - headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - }, - }).then( - () => { - done(); - }, - err => { - fail(err); - done(); - } - ); - }); + request({ + url: "http://localhost:8378/1/classes/MyObject", + headers: { + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", + }, + }).then( + () => { + done(); + }, + err => { + fail(err); + done(); + } + ); + } + ); - it('should handle sorting where', done => { - Parse.Cloud.beforeFind('MyObject', req => { + it("should handle sorting where", done => { + Parse.Cloud.beforeFind("MyObject", req => { const query = req.query; - query.ascending('score'); + query.ascending("score"); return query; }); const count = 20; const objects = []; while (objects.length != count) { - const object = new Parse.Object('MyObject'); - object.set('score', Math.floor(Math.random() * 100)); + const object = new Parse.Object("MyObject"); + object.set("score", Math.floor(Math.random() * 100)); objects.push(object); } Parse.Object.saveAll(objects) .then(() => { - const query = new Parse.Query('MyObject'); + const query = new Parse.Query("MyObject"); return query.find(); }) .then(objects => { let lastScore = -1; objects.forEach(element => { - expect(element.get('score') >= lastScore).toBe(true); - lastScore = element.get('score'); + expect(element.get("score") >= lastScore).toBe(true); + lastScore = element.get("score"); }); }) .then(done) .catch(done.fail); }); - it('should add beforeFind trigger using get API', done => { + it("should add beforeFind trigger using get API", done => { const hook = { method: function (req) { expect(req.isGet).toEqual(true); return Promise.resolve(); }, }; - spyOn(hook, 'method').and.callThrough(); - Parse.Cloud.beforeFind('MyObject', hook.method); - const obj = new Parse.Object('MyObject'); - obj.set('secretField', 'SSID'); + spyOn(hook, "method").and.callThrough(); + Parse.Cloud.beforeFind("MyObject", hook.method); + const obj = new Parse.Object("MyObject"); + obj.set("secretField", "SSID"); obj.save().then(function () { request({ - method: 'GET', - url: 'http://localhost:8378/1/classes/MyObject/' + obj.id, + method: "GET", + url: "http://localhost:8378/1/classes/MyObject/" + obj.id, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }, json: true, }).then(response => { const body = response.data; - expect(body.secretField).toEqual('SSID'); + expect(body.secretField).toEqual("SSID"); expect(hook.method).toHaveBeenCalled(); done(); }); }); }); - it('sets correct beforeFind trigger isGet parameter for Parse.Object.fetch request', async () => { + it("sets correct beforeFind trigger isGet parameter for Parse.Object.fetch request", async () => { const hook = { method: req => { expect(req.isGet).toEqual(true); return Promise.resolve(); }, }; - spyOn(hook, 'method').and.callThrough(); - Parse.Cloud.beforeFind('MyObject', hook.method); - const obj = new Parse.Object('MyObject'); + spyOn(hook, "method").and.callThrough(); + Parse.Cloud.beforeFind("MyObject", hook.method); + const obj = new Parse.Object("MyObject"); await obj.save(); const getObj = await obj.fetch(); expect(getObj).toBeInstanceOf(Parse.Object); expect(hook.method).toHaveBeenCalledTimes(1); }); - it('sets correct beforeFind trigger isGet parameter for Parse.Query.get request', async () => { + it("sets correct beforeFind trigger isGet parameter for Parse.Query.get request", async () => { const hook = { method: req => { expect(req.isGet).toEqual(false); return Promise.resolve(); }, }; - spyOn(hook, 'method').and.callThrough(); - Parse.Cloud.beforeFind('MyObject', hook.method); - const obj = new Parse.Object('MyObject'); + spyOn(hook, "method").and.callThrough(); + Parse.Cloud.beforeFind("MyObject", hook.method); + const obj = new Parse.Object("MyObject"); await obj.save(); - const query = new Parse.Query('MyObject'); + const query = new Parse.Query("MyObject"); const getObj = await query.get(obj.id); expect(getObj).toBeInstanceOf(Parse.Object); expect(hook.method).toHaveBeenCalledTimes(1); }); - it('sets correct beforeFind trigger isGet parameter for Parse.Query.find request', async () => { + it("sets correct beforeFind trigger isGet parameter for Parse.Query.find request", async () => { const hook = { method: req => { expect(req.isGet).toEqual(false); return Promise.resolve(); }, }; - spyOn(hook, 'method').and.callThrough(); - Parse.Cloud.beforeFind('MyObject', hook.method); - const obj = new Parse.Object('MyObject'); + spyOn(hook, "method").and.callThrough(); + Parse.Cloud.beforeFind("MyObject", hook.method); + const obj = new Parse.Object("MyObject"); await obj.save(); - const query = new Parse.Query('MyObject'); + const query = new Parse.Query("MyObject"); const findObjs = await query.find(); expect(findObjs?.[0]).toBeInstanceOf(Parse.Object); expect(hook.method).toHaveBeenCalledTimes(1); }); - it('should have request headers', done => { - Parse.Cloud.beforeFind('MyObject', req => { + it("should have request headers", done => { + Parse.Cloud.beforeFind("MyObject", req => { expect(req.headers).toBeDefined(); }); - const MyObject = Parse.Object.extend('MyObject'); + const MyObject = Parse.Object.extend("MyObject"); const myObject = new MyObject(); myObject .save() .then(myObj => { - const query = new Parse.Query('MyObject'); - query.equalTo('objectId', myObj.id); + const query = new Parse.Query("MyObject"); + query.equalTo("objectId", myObj.id); return Promise.all([query.get(myObj.id), query.first(), query.find()]); }) .then(() => done()); }); - it('should have request ip', done => { - Parse.Cloud.beforeFind('MyObject', req => { + it("should have request ip", done => { + Parse.Cloud.beforeFind("MyObject", req => { expect(req.ip).toBeDefined(); }); - const MyObject = Parse.Object.extend('MyObject'); + const MyObject = Parse.Object.extend("MyObject"); const myObject = new MyObject(); myObject .save() .then(myObj => { - const query = new Parse.Query('MyObject'); - query.equalTo('objectId', myObj.id); + const query = new Parse.Query("MyObject"); + query.equalTo("objectId", myObj.id); return Promise.all([query.get(myObj.id), query.first(), query.find()]); }) .then(() => done()); }); - it('should run beforeFind on pointers and array of pointers from an object', async () => { - const obj1 = new Parse.Object('TestObject'); - const obj2 = new Parse.Object('TestObject2'); - const obj3 = new Parse.Object('TestObject'); - obj2.set('aField', 'aFieldValue'); + it("should run beforeFind on pointers and array of pointers from an object", async () => { + const obj1 = new Parse.Object("TestObject"); + const obj2 = new Parse.Object("TestObject2"); + const obj3 = new Parse.Object("TestObject"); + obj2.set("aField", "aFieldValue"); await obj2.save(); - obj1.set('pointerField', obj2); - obj3.set('pointerFieldArray', [obj2]); + obj1.set("pointerField", obj2); + obj3.set("pointerFieldArray", [obj2]); await obj1.save(); await obj3.save(); - const spy = jasmine.createSpy('beforeFindSpy'); - Parse.Cloud.beforeFind('TestObject2', spy); - const query = new Parse.Query('TestObject'); + const spy = jasmine.createSpy("beforeFindSpy"); + Parse.Cloud.beforeFind("TestObject2", spy); + const query = new Parse.Query("TestObject"); await query.get(obj1.id); // Pointer not included in query so we don't expect beforeFind to be called expect(spy).not.toHaveBeenCalled(); - const query2 = new Parse.Query('TestObject'); - query2.include('pointerField'); + const query2 = new Parse.Query("TestObject"); + query2.include("pointerField"); const res = await query2.get(obj1.id); - expect(res.get('pointerField').get('aField')).toBe('aFieldValue'); + expect(res.get("pointerField").get("aField")).toBe("aFieldValue"); // Pointer included in query so we expect beforeFind to be called expect(spy).toHaveBeenCalledTimes(1); - const query3 = new Parse.Query('TestObject'); - query3.include('pointerFieldArray'); + const query3 = new Parse.Query("TestObject"); + query3.include("pointerFieldArray"); const res2 = await query3.get(obj3.id); - expect(res2.get('pointerFieldArray')[0].get('aField')).toBe('aFieldValue'); + expect(res2.get("pointerFieldArray")[0].get("aField")).toBe("aFieldValue"); expect(spy).toHaveBeenCalledTimes(2); }); - it('should have access to context in include query in beforeFind hook', async () => { + it("should have access to context in include query in beforeFind hook", async () => { let beforeFindTestObjectCalled = false; let beforeFindTestObject2Called = false; - const obj1 = new Parse.Object('TestObject'); - const obj2 = new Parse.Object('TestObject2'); - obj2.set('aField', 'aFieldValue'); + const obj1 = new Parse.Object("TestObject"); + const obj2 = new Parse.Object("TestObject2"); + obj2.set("aField", "aFieldValue"); await obj2.save(); - obj1.set('pointerField', obj2); + obj1.set("pointerField", obj2); await obj1.save(); - Parse.Cloud.beforeFind('TestObject', req => { + Parse.Cloud.beforeFind("TestObject", req => { expect(req.context).toBeDefined(); - expect(req.context.a).toEqual('a'); + expect(req.context.a).toEqual("a"); beforeFindTestObjectCalled = true; }); - Parse.Cloud.beforeFind('TestObject2', req => { + Parse.Cloud.beforeFind("TestObject2", req => { expect(req.context).toBeDefined(); - expect(req.context.a).toEqual('a'); + expect(req.context.a).toEqual("a"); beforeFindTestObject2Called = true; }); - const query = new Parse.Query('TestObject'); - await query.include('pointerField').find({ context: { a: 'a' } }); + const query = new Parse.Query("TestObject"); + await query.include("pointerField").find({ context: { a: "a" } }); expect(beforeFindTestObjectCalled).toBeTrue(); expect(beforeFindTestObject2Called).toBeTrue(); }); }); -describe('afterFind hooks', () => { - it('should add afterFind trigger', done => { - Parse.Cloud.afterFind('MyObject', req => { +describe("afterFind hooks", () => { + it("should add afterFind trigger", done => { + Parse.Cloud.afterFind("MyObject", req => { const q = req.query; expect(q instanceof Parse.Query).toBe(true); const jsonQuery = q.toJSON(); - expect(jsonQuery.where.key).toEqual('value'); + expect(jsonQuery.where.key).toEqual("value"); expect(jsonQuery.where.some).toEqual({ $gt: 10 }); - expect(jsonQuery.include).toEqual('otherKey,otherValue'); - expect(jsonQuery.excludeKeys).toBe('exclude'); + expect(jsonQuery.include).toEqual("otherKey,otherValue"); + expect(jsonQuery.excludeKeys).toBe("exclude"); expect(jsonQuery.limit).toEqual(100); expect(jsonQuery.skip).toBe(undefined); - expect(jsonQuery.order).toBe('key'); - expect(jsonQuery.keys).toBe('select'); - expect(jsonQuery.readPreference).toBe('PRIMARY'); - expect(jsonQuery.includeReadPreference).toBe('SECONDARY'); - expect(jsonQuery.subqueryReadPreference).toBe('SECONDARY_PREFERRED'); - }); - - const query = new Parse.Query('MyObject'); - query.equalTo('key', 'value'); - query.greaterThan('some', 10); - query.include('otherKey'); - query.include('otherValue'); - query.ascending('key'); - query.select('select'); - query.exclude('exclude'); - query.readPreference('PRIMARY', 'SECONDARY', 'SECONDARY_PREFERRED'); + expect(jsonQuery.order).toBe("key"); + expect(jsonQuery.keys).toBe("select"); + expect(jsonQuery.readPreference).toBe("PRIMARY"); + expect(jsonQuery.includeReadPreference).toBe("SECONDARY"); + expect(jsonQuery.subqueryReadPreference).toBe("SECONDARY_PREFERRED"); + }); + + const query = new Parse.Query("MyObject"); + query.equalTo("key", "value"); + query.greaterThan("some", 10); + query.include("otherKey"); + query.include("otherValue"); + query.ascending("key"); + query.select("select"); + query.exclude("exclude"); + query.readPreference("PRIMARY", "SECONDARY", "SECONDARY_PREFERRED"); query.find().then(() => { done(); }); }); - it('should add afterFind trigger using get', done => { - Parse.Cloud.afterFind('MyObject', req => { + it("should add afterFind trigger using get", done => { + Parse.Cloud.afterFind("MyObject", req => { for (let i = 0; i < req.objects.length; i++) { - req.objects[i].set('secretField', '###'); + req.objects[i].set("secretField", "###"); } return req.objects; }); - const obj = new Parse.Object('MyObject'); - obj.set('secretField', 'SSID'); + const obj = new Parse.Object("MyObject"); + obj.set("secretField", "SSID"); obj.save().then( function () { - const query = new Parse.Query('MyObject'); + const query = new Parse.Query("MyObject"); query.get(obj.id).then( function (result) { - expect(result.get('secretField')).toEqual('###'); + expect(result.get("secretField")).toEqual("###"); done(); }, function (error) { @@ -2597,22 +2654,22 @@ describe('afterFind hooks', () => { ); }); - it('should add afterFind trigger using find', done => { - Parse.Cloud.afterFind('MyObject', req => { + it("should add afterFind trigger using find", done => { + Parse.Cloud.afterFind("MyObject", req => { for (let i = 0; i < req.objects.length; i++) { - req.objects[i].set('secretField', '###'); + req.objects[i].set("secretField", "###"); } return req.objects; }); - const obj = new Parse.Object('MyObject'); - obj.set('secretField', 'SSID'); + const obj = new Parse.Object("MyObject"); + obj.set("secretField", "SSID"); obj.save().then( function () { - const query = new Parse.Query('MyObject'); - query.equalTo('objectId', obj.id); + const query = new Parse.Query("MyObject"); + query.equalTo("objectId", obj.id); query.find().then( function (results) { - expect(results[0].get('secretField')).toEqual('###'); + expect(results[0].get("secretField")).toEqual("###"); done(); }, function (error) { @@ -2628,26 +2685,26 @@ describe('afterFind hooks', () => { ); }); - it('should filter out results', done => { - Parse.Cloud.afterFind('MyObject', req => { + it("should filter out results", done => { + Parse.Cloud.afterFind("MyObject", req => { const filteredResults = []; for (let i = 0; i < req.objects.length; i++) { - if (req.objects[i].get('secretField') === 'SSID1') { + if (req.objects[i].get("secretField") === "SSID1") { filteredResults.push(req.objects[i]); } } return filteredResults; }); - const obj0 = new Parse.Object('MyObject'); - obj0.set('secretField', 'SSID1'); - const obj1 = new Parse.Object('MyObject'); - obj1.set('secretField', 'SSID2'); + const obj0 = new Parse.Object("MyObject"); + obj0.set("secretField", "SSID1"); + const obj1 = new Parse.Object("MyObject"); + obj1.set("secretField", "SSID2"); Parse.Object.saveAll([obj0, obj1]).then( function () { - const query = new Parse.Query('MyObject'); + const query = new Parse.Query("MyObject"); query.find().then( function (results) { - expect(results[0].get('secretField')).toEqual('SSID1'); + expect(results[0].get("secretField")).toEqual("SSID1"); expect(results.length).toEqual(1); done(); }, @@ -2664,19 +2721,19 @@ describe('afterFind hooks', () => { ); }); - it('should handle failures', done => { - Parse.Cloud.afterFind('MyObject', () => { - throw new Parse.Error(Parse.Error.SCRIPT_FAILED, 'It should fail'); + it("should handle failures", done => { + Parse.Cloud.afterFind("MyObject", () => { + throw new Parse.Error(Parse.Error.SCRIPT_FAILED, "It should fail"); }); - const obj = new Parse.Object('MyObject'); - obj.set('secretField', 'SSID'); + const obj = new Parse.Object("MyObject"); + obj.set("secretField", "SSID"); obj.save().then( function () { - const query = new Parse.Query('MyObject'); - query.equalTo('objectId', obj.id); + const query = new Parse.Query("MyObject"); + query.equalTo("objectId", obj.id); query.find().then( function () { - fail('AfterFind should handle response failure correctly'); + fail("AfterFind should handle response failure correctly"); done(); }, function () { @@ -2690,26 +2747,26 @@ describe('afterFind hooks', () => { ); }); - it('should also work with promise', done => { - Parse.Cloud.afterFind('MyObject', req => { + it("should also work with promise", done => { + Parse.Cloud.afterFind("MyObject", req => { return new Promise(resolve => { setTimeout(function () { for (let i = 0; i < req.objects.length; i++) { - req.objects[i].set('secretField', '###'); + req.objects[i].set("secretField", "###"); } resolve(req.objects); }, 1000); }); }); - const obj = new Parse.Object('MyObject'); - obj.set('secretField', 'SSID'); + const obj = new Parse.Object("MyObject"); + obj.set("secretField", "SSID"); obj.save().then( function () { - const query = new Parse.Query('MyObject'); - query.equalTo('objectId', obj.id); + const query = new Parse.Query("MyObject"); + query.equalTo("objectId", obj.id); query.find().then( function (results) { - expect(results[0].get('secretField')).toEqual('###'); + expect(results[0].get("secretField")).toEqual("###"); done(); }, function (error) { @@ -2723,209 +2780,237 @@ describe('afterFind hooks', () => { ); }); - it('should alter select', done => { - Parse.Cloud.beforeFind('MyObject', req => { - req.query.select('white'); + it("should alter select", done => { + Parse.Cloud.beforeFind("MyObject", req => { + req.query.select("white"); return req.query; }); - const obj0 = new Parse.Object('MyObject').set('white', true).set('black', true); + const obj0 = new Parse.Object("MyObject") + .set("white", true) + .set("black", true); obj0.save().then(() => { - new Parse.Query('MyObject').first().then(result => { - expect(result.get('white')).toBe(true); - expect(result.get('black')).toBe(undefined); + new Parse.Query("MyObject").first().then(result => { + expect(result.get("white")).toBe(true); + expect(result.get("black")).toBe(undefined); done(); }); }); }); - it('should not alter select', done => { - const obj0 = new Parse.Object('MyObject').set('white', true).set('black', true); + it("should not alter select", done => { + const obj0 = new Parse.Object("MyObject") + .set("white", true) + .set("black", true); obj0.save().then(() => { - new Parse.Query('MyObject').first().then(result => { - expect(result.get('white')).toBe(true); - expect(result.get('black')).toBe(true); + new Parse.Query("MyObject").first().then(result => { + expect(result.get("white")).toBe(true); + expect(result.get("black")).toBe(true); done(); }); }); }); - it('should set count to true on beforeFind hooks if query is count', done => { + it("should set count to true on beforeFind hooks if query is count", done => { const hook = { method: function (req) { expect(req.count).toBe(true); return Promise.resolve(); }, }; - spyOn(hook, 'method').and.callThrough(); - Parse.Cloud.beforeFind('Stuff', hook.method); - new Parse.Query('Stuff').count().then(count => { + spyOn(hook, "method").and.callThrough(); + Parse.Cloud.beforeFind("Stuff", hook.method); + new Parse.Query("Stuff").count().then(count => { expect(count).toBe(0); expect(hook.method).toHaveBeenCalled(); done(); }); }); - it('should set count to false on beforeFind hooks if query is not count', done => { + it("should set count to false on beforeFind hooks if query is not count", done => { const hook = { method: function (req) { expect(req.count).toBe(false); return Promise.resolve(); }, }; - spyOn(hook, 'method').and.callThrough(); - Parse.Cloud.beforeFind('Stuff', hook.method); - new Parse.Query('Stuff').find().then(res => { + spyOn(hook, "method").and.callThrough(); + Parse.Cloud.beforeFind("Stuff", hook.method); + new Parse.Query("Stuff").find().then(res => { expect(res.length).toBe(0); expect(hook.method).toHaveBeenCalled(); done(); }); }); - it('can set a pointer object in afterFind', async () => { - const obj = new Parse.Object('MyObject'); + it("can set a pointer object in afterFind", async () => { + const obj = new Parse.Object("MyObject"); await obj.save(); - Parse.Cloud.afterFind('MyObject', async ({ objects }) => { - const otherObject = new Parse.Object('Test'); - otherObject.set('foo', 'bar'); + Parse.Cloud.afterFind("MyObject", async ({ objects }) => { + const otherObject = new Parse.Object("Test"); + otherObject.set("foo", "bar"); await otherObject.save(); - objects[0].set('Pointer', otherObject); - objects[0].set('xyz', 'yolo'); - expect(objects[0].get('Pointer').get('foo')).toBe('bar'); + objects[0].set("Pointer", otherObject); + objects[0].set("xyz", "yolo"); + expect(objects[0].get("Pointer").get("foo")).toBe("bar"); }); - const query = new Parse.Query('MyObject'); - query.equalTo('objectId', obj.id); + const query = new Parse.Query("MyObject"); + query.equalTo("objectId", obj.id); const obj2 = await query.first(); - expect(obj2.get('xyz')).toBe('yolo'); - const pointer = obj2.get('Pointer'); - expect(pointer.get('foo')).toBe('bar'); + expect(obj2.get("xyz")).toBe("yolo"); + const pointer = obj2.get("Pointer"); + expect(pointer.get("foo")).toBe("bar"); }); - it('can set invalid object in afterFind', async () => { - const obj = new Parse.Object('MyObject'); + it("can set invalid object in afterFind", async () => { + const obj = new Parse.Object("MyObject"); await obj.save(); - Parse.Cloud.afterFind('MyObject', () => [{}]); - const query = new Parse.Query('MyObject'); - query.equalTo('objectId', obj.id); + Parse.Cloud.afterFind("MyObject", () => [{}]); + const query = new Parse.Query("MyObject"); + query.equalTo("objectId", obj.id); const obj2 = await query.first(); expect(obj2).toBeDefined(); expect(obj2.toJSON()).toEqual({}); expect(obj2.id).toBeUndefined(); }); - it('can return a unsaved object in afterFind', async () => { - const obj = new Parse.Object('MyObject'); + it("can return a unsaved object in afterFind", async () => { + const obj = new Parse.Object("MyObject"); await obj.save(); - Parse.Cloud.afterFind('MyObject', async () => { - const otherObject = new Parse.Object('Test'); - otherObject.set('foo', 'bar'); + Parse.Cloud.afterFind("MyObject", async () => { + const otherObject = new Parse.Object("Test"); + otherObject.set("foo", "bar"); return [otherObject]; }); - const query = new Parse.Query('MyObject'); + const query = new Parse.Query("MyObject"); const obj2 = await query.first(); - expect(obj2.get('foo')).toEqual('bar'); + expect(obj2.get("foo")).toEqual("bar"); expect(obj2.id).toBeUndefined(); await obj2.save(); expect(obj2.id).toBeDefined(); }); - it('should have request headers', done => { - Parse.Cloud.afterFind('MyObject', req => { + it("should have request headers", done => { + Parse.Cloud.afterFind("MyObject", req => { expect(req.headers).toBeDefined(); }); - const MyObject = Parse.Object.extend('MyObject'); + const MyObject = Parse.Object.extend("MyObject"); const myObject = new MyObject(); myObject .save() .then(myObj => { - const query = new Parse.Query('MyObject'); - query.equalTo('objectId', myObj.id); + const query = new Parse.Query("MyObject"); + query.equalTo("objectId", myObj.id); return Promise.all([query.get(myObj.id), query.first(), query.find()]); }) .then(() => done()); }); - it('should have request ip', done => { - Parse.Cloud.afterFind('MyObject', req => { + it("should have request ip", done => { + Parse.Cloud.afterFind("MyObject", req => { expect(req.ip).toBeDefined(); }); - const MyObject = Parse.Object.extend('MyObject'); + const MyObject = Parse.Object.extend("MyObject"); const myObject = new MyObject(); myObject .save() .then(myObj => { - const query = new Parse.Query('MyObject'); - query.equalTo('objectId', myObj.id); + const query = new Parse.Query("MyObject"); + query.equalTo("objectId", myObj.id); return Promise.all([query.get(myObj.id), query.first(), query.find()]); }) .then(() => done()) .catch(done.fail); }); - it('should validate triggers correctly', () => { + it("should validate triggers correctly", () => { expect(() => { - Parse.Cloud.beforeSave('_Session', () => {}); - }).toThrow('Only the afterLogout trigger is allowed for the _Session class.'); + Parse.Cloud.beforeSave("_Session", () => {}); + }).toThrow( + "Only the afterLogout trigger is allowed for the _Session class." + ); expect(() => { - Parse.Cloud.afterSave('_Session', () => {}); - }).toThrow('Only the afterLogout trigger is allowed for the _Session class.'); + Parse.Cloud.afterSave("_Session", () => {}); + }).toThrow( + "Only the afterLogout trigger is allowed for the _Session class." + ); expect(() => { - Parse.Cloud.beforeSave('_PushStatus', () => {}); - }).toThrow('Only afterSave is allowed on _PushStatus'); + Parse.Cloud.beforeSave("_PushStatus", () => {}); + }).toThrow("Only afterSave is allowed on _PushStatus"); expect(() => { - Parse.Cloud.afterSave('_PushStatus', () => {}); + Parse.Cloud.afterSave("_PushStatus", () => {}); }).not.toThrow(); expect(() => { Parse.Cloud.beforeLogin(() => {}); - }).not.toThrow('Only the _User class is allowed for the beforeLogin and afterLogin triggers'); + }).not.toThrow( + "Only the _User class is allowed for the beforeLogin and afterLogin triggers" + ); expect(() => { - Parse.Cloud.beforeLogin('_User', () => {}); - }).not.toThrow('Only the _User class is allowed for the beforeLogin and afterLogin triggers'); + Parse.Cloud.beforeLogin("_User", () => {}); + }).not.toThrow( + "Only the _User class is allowed for the beforeLogin and afterLogin triggers" + ); expect(() => { Parse.Cloud.beforeLogin(Parse.User, () => {}); - }).not.toThrow('Only the _User class is allowed for the beforeLogin and afterLogin triggers'); + }).not.toThrow( + "Only the _User class is allowed for the beforeLogin and afterLogin triggers" + ); expect(() => { - Parse.Cloud.beforeLogin('SomeClass', () => {}); - }).toThrow('Only the _User class is allowed for the beforeLogin and afterLogin triggers'); + Parse.Cloud.beforeLogin("SomeClass", () => {}); + }).toThrow( + "Only the _User class is allowed for the beforeLogin and afterLogin triggers" + ); expect(() => { Parse.Cloud.afterLogin(() => {}); - }).not.toThrow('Only the _User class is allowed for the beforeLogin and afterLogin triggers'); + }).not.toThrow( + "Only the _User class is allowed for the beforeLogin and afterLogin triggers" + ); expect(() => { - Parse.Cloud.afterLogin('_User', () => {}); - }).not.toThrow('Only the _User class is allowed for the beforeLogin and afterLogin triggers'); + Parse.Cloud.afterLogin("_User", () => {}); + }).not.toThrow( + "Only the _User class is allowed for the beforeLogin and afterLogin triggers" + ); expect(() => { Parse.Cloud.afterLogin(Parse.User, () => {}); - }).not.toThrow('Only the _User class is allowed for the beforeLogin and afterLogin triggers'); + }).not.toThrow( + "Only the _User class is allowed for the beforeLogin and afterLogin triggers" + ); expect(() => { - Parse.Cloud.afterLogin('SomeClass', () => {}); - }).toThrow('Only the _User class is allowed for the beforeLogin and afterLogin triggers'); + Parse.Cloud.afterLogin("SomeClass", () => {}); + }).toThrow( + "Only the _User class is allowed for the beforeLogin and afterLogin triggers" + ); expect(() => { Parse.Cloud.afterLogout(() => {}); }).not.toThrow(); expect(() => { - Parse.Cloud.afterLogout('_Session', () => {}); + Parse.Cloud.afterLogout("_Session", () => {}); }).not.toThrow(); expect(() => { - Parse.Cloud.afterLogout('_User', () => {}); - }).toThrow('Only the _Session class is allowed for the afterLogout trigger.'); + Parse.Cloud.afterLogout("_User", () => {}); + }).toThrow( + "Only the _Session class is allowed for the afterLogout trigger." + ); expect(() => { - Parse.Cloud.afterLogout('SomeClass', () => {}); - }).toThrow('Only the _Session class is allowed for the afterLogout trigger.'); + Parse.Cloud.afterLogout("SomeClass", () => {}); + }).toThrow( + "Only the _Session class is allowed for the afterLogout trigger." + ); }); - it_id('c16159b5-e8ee-42d5-8fe3-e2f7c006881d')(it)( - 'should skip afterFind hooks for aggregate', + it_id("c16159b5-e8ee-42d5-8fe3-e2f7c006881d")(it)( + "should skip afterFind hooks for aggregate", done => { const hook = { method: function () { return Promise.reject(); }, }; - spyOn(hook, 'method').and.callThrough(); - Parse.Cloud.afterFind('MyObject', hook.method); - const obj = new Parse.Object('MyObject'); + spyOn(hook, "method").and.callThrough(); + Parse.Cloud.afterFind("MyObject", hook.method); + const obj = new Parse.Object("MyObject"); const pipeline = [ { $group: { _id: {} }, @@ -2934,7 +3019,7 @@ describe('afterFind hooks', () => { obj .save() .then(() => { - const query = new Parse.Query('MyObject'); + const query = new Parse.Query("MyObject"); return query.aggregate(pipeline); }) .then(results => { @@ -2945,23 +3030,23 @@ describe('afterFind hooks', () => { } ); - it_id('ca55c90d-36db-422c-9060-a30583ce5224')(it)( - 'should skip afterFind hooks for distinct', + it_id("ca55c90d-36db-422c-9060-a30583ce5224")(it)( + "should skip afterFind hooks for distinct", done => { const hook = { method: function () { return Promise.reject(); }, }; - spyOn(hook, 'method').and.callThrough(); - Parse.Cloud.afterFind('MyObject', hook.method); - const obj = new Parse.Object('MyObject'); - obj.set('score', 10); + spyOn(hook, "method").and.callThrough(); + Parse.Cloud.afterFind("MyObject", hook.method); + const obj = new Parse.Object("MyObject"); + obj.set("score", 10); obj .save() .then(() => { - const query = new Parse.Query('MyObject'); - return query.distinct('score'); + const query = new Parse.Query("MyObject"); + return query.distinct("score"); }) .then(results => { expect(results[0]).toEqual(10); @@ -2971,30 +3056,30 @@ describe('afterFind hooks', () => { } ); - it('should throw error if context header is malformed', async () => { + it("should throw error if context header is malformed", async () => { let calledBefore = false; let calledAfter = false; - Parse.Cloud.beforeSave('TestObject', () => { + Parse.Cloud.beforeSave("TestObject", () => { calledBefore = true; }); - Parse.Cloud.afterSave('TestObject', () => { + Parse.Cloud.afterSave("TestObject", () => { calledAfter = true; }); const req = request({ - method: 'POST', - url: 'http://localhost:8378/1/classes/TestObject', + method: "POST", + url: "http://localhost:8378/1/classes/TestObject", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Cloud-Context': 'key', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "X-Parse-Cloud-Context": "key", }, body: { - foo: 'bar', + foo: "bar", }, }); try { await req; - fail('Should have thrown error'); + fail("Should have thrown error"); } catch (e) { expect(e).toBeDefined(); expect(e.data.code).toEqual(Parse.Error.INVALID_JSON); @@ -3006,27 +3091,27 @@ describe('afterFind hooks', () => { it('should throw error if context header is string "1"', async () => { let calledBefore = false; let calledAfter = false; - Parse.Cloud.beforeSave('TestObject', () => { + Parse.Cloud.beforeSave("TestObject", () => { calledBefore = true; }); - Parse.Cloud.afterSave('TestObject', () => { + Parse.Cloud.afterSave("TestObject", () => { calledAfter = true; }); const req = request({ - method: 'POST', - url: 'http://localhost:8378/1/classes/TestObject', + method: "POST", + url: "http://localhost:8378/1/classes/TestObject", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Cloud-Context': '1', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "X-Parse-Cloud-Context": "1", }, body: { - foo: 'bar', + foo: "bar", }, }); try { await req; - fail('Should have thrown error'); + fail("Should have thrown error"); } catch (e) { expect(e).toBeDefined(); expect(e.data.code).toEqual(Parse.Error.INVALID_JSON); @@ -3035,33 +3120,33 @@ describe('afterFind hooks', () => { expect(calledAfter).toBe(false); }); - it_id('55ef1741-cf72-4a7c-a029-00cb75f53233')(it)( - 'should expose context in beforeSave/afterSave via header', + it_id("55ef1741-cf72-4a7c-a029-00cb75f53233")(it)( + "should expose context in beforeSave/afterSave via header", async () => { let calledBefore = false; let calledAfter = false; - Parse.Cloud.beforeSave('TestObject', req => { - expect(req.object.get('foo')).toEqual('bar'); + Parse.Cloud.beforeSave("TestObject", req => { + expect(req.object.get("foo")).toEqual("bar"); expect(req.context.otherKey).toBe(1); - expect(req.context.key).toBe('value'); + expect(req.context.key).toBe("value"); calledBefore = true; }); - Parse.Cloud.afterSave('TestObject', req => { - expect(req.object.get('foo')).toEqual('bar'); + Parse.Cloud.afterSave("TestObject", req => { + expect(req.object.get("foo")).toEqual("bar"); expect(req.context.otherKey).toBe(1); - expect(req.context.key).toBe('value'); + expect(req.context.key).toBe("value"); calledAfter = true; }); const req = request({ - method: 'POST', - url: 'http://localhost:8378/1/classes/TestObject', + method: "POST", + url: "http://localhost:8378/1/classes/TestObject", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Cloud-Context': '{"key":"value","otherKey":1}', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "X-Parse-Cloud-Context": '{"key":"value","otherKey":1}', }, body: { - foo: 'bar', + foo: "bar", }, }); await req; @@ -3070,31 +3155,31 @@ describe('afterFind hooks', () => { } ); - it('should override header context with body context in beforeSave/afterSave', async () => { + it("should override header context with body context in beforeSave/afterSave", async () => { let calledBefore = false; let calledAfter = false; - Parse.Cloud.beforeSave('TestObject', req => { - expect(req.object.get('foo')).toEqual('bar'); + Parse.Cloud.beforeSave("TestObject", req => { + expect(req.object.get("foo")).toEqual("bar"); expect(req.context.otherKey).toBe(10); - expect(req.context.key).toBe('hello'); + expect(req.context.key).toBe("hello"); calledBefore = true; }); - Parse.Cloud.afterSave('TestObject', req => { - expect(req.object.get('foo')).toEqual('bar'); + Parse.Cloud.afterSave("TestObject", req => { + expect(req.object.get("foo")).toEqual("bar"); expect(req.context.otherKey).toBe(10); - expect(req.context.key).toBe('hello'); + expect(req.context.key).toBe("hello"); calledAfter = true; }); const req = request({ - method: 'POST', - url: 'http://localhost:8378/1/classes/TestObject', + method: "POST", + url: "http://localhost:8378/1/classes/TestObject", headers: { - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Cloud-Context': '{"key":"value","otherKey":1}', + "X-Parse-REST-API-Key": "rest", + "X-Parse-Cloud-Context": '{"key":"value","otherKey":1}', }, body: { - foo: 'bar', - _ApplicationId: 'test', + foo: "bar", + _ApplicationId: "test", _context: '{"key":"hello","otherKey":10}', }, }); @@ -3103,31 +3188,31 @@ describe('afterFind hooks', () => { expect(calledAfter).toBe(true); }); - it('should throw error if context body is malformed', async () => { + it("should throw error if context body is malformed", async () => { let calledBefore = false; let calledAfter = false; - Parse.Cloud.beforeSave('TestObject', () => { + Parse.Cloud.beforeSave("TestObject", () => { calledBefore = true; }); - Parse.Cloud.afterSave('TestObject', () => { + Parse.Cloud.afterSave("TestObject", () => { calledAfter = true; }); const req = request({ - method: 'POST', - url: 'http://localhost:8378/1/classes/TestObject', + method: "POST", + url: "http://localhost:8378/1/classes/TestObject", headers: { - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Cloud-Context': '{"key":"value","otherKey":1}', + "X-Parse-REST-API-Key": "rest", + "X-Parse-Cloud-Context": '{"key":"value","otherKey":1}', }, body: { - foo: 'bar', - _ApplicationId: 'test', - _context: 'key', + foo: "bar", + _ApplicationId: "test", + _context: "key", }, }); try { await req; - fail('Should have thrown error'); + fail("Should have thrown error"); } catch (e) { expect(e).toBeDefined(); expect(e.data.code).toEqual(Parse.Error.INVALID_JSON); @@ -3139,28 +3224,28 @@ describe('afterFind hooks', () => { it('should throw error if context body is string "true"', async () => { let calledBefore = false; let calledAfter = false; - Parse.Cloud.beforeSave('TestObject', () => { + Parse.Cloud.beforeSave("TestObject", () => { calledBefore = true; }); - Parse.Cloud.afterSave('TestObject', () => { + Parse.Cloud.afterSave("TestObject", () => { calledAfter = true; }); const req = request({ - method: 'POST', - url: 'http://localhost:8378/1/classes/TestObject', + method: "POST", + url: "http://localhost:8378/1/classes/TestObject", headers: { - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Cloud-Context': '{"key":"value","otherKey":1}', + "X-Parse-REST-API-Key": "rest", + "X-Parse-Cloud-Context": '{"key":"value","otherKey":1}', }, body: { - foo: 'bar', - _ApplicationId: 'test', - _context: 'true', + foo: "bar", + _ApplicationId: "test", + _context: "true", }, }); try { await req; - fail('Should have thrown error'); + fail("Should have thrown error"); } catch (e) { expect(e).toBeDefined(); expect(e.data.code).toEqual(Parse.Error.INVALID_JSON); @@ -3169,123 +3254,123 @@ describe('afterFind hooks', () => { expect(calledAfter).toBe(false); }); - it('should expose context in before and afterSave', async () => { + it("should expose context in before and afterSave", async () => { let calledBefore = false; let calledAfter = false; - Parse.Cloud.beforeSave('MyClass', req => { + Parse.Cloud.beforeSave("MyClass", req => { req.context = { - key: 'value', + key: "value", otherKey: 1, }; calledBefore = true; }); - Parse.Cloud.afterSave('MyClass', req => { + Parse.Cloud.afterSave("MyClass", req => { expect(req.context.otherKey).toBe(1); - expect(req.context.key).toBe('value'); + expect(req.context.key).toBe("value"); calledAfter = true; }); - const object = new Parse.Object('MyClass'); + const object = new Parse.Object("MyClass"); await object.save(); expect(calledBefore).toBe(true); expect(calledAfter).toBe(true); }); - it('should expose context in before and afterSave and let keys be set individually', async () => { + it("should expose context in before and afterSave and let keys be set individually", async () => { let calledBefore = false; let calledAfter = false; - Parse.Cloud.beforeSave('MyClass', req => { - req.context.some = 'value'; + Parse.Cloud.beforeSave("MyClass", req => { + req.context.some = "value"; req.context.yolo = 1; calledBefore = true; }); - Parse.Cloud.afterSave('MyClass', req => { + Parse.Cloud.afterSave("MyClass", req => { expect(req.context.yolo).toBe(1); - expect(req.context.some).toBe('value'); + expect(req.context.some).toBe("value"); calledAfter = true; }); - const object = new Parse.Object('MyClass'); + const object = new Parse.Object("MyClass"); await object.save(); expect(calledBefore).toBe(true); expect(calledAfter).toBe(true); }); }); -describe('beforeLogin hook', () => { - it('should run beforeLogin with correct credentials', async done => { +describe("beforeLogin hook", () => { + it("should run beforeLogin with correct credentials", async done => { let hit = 0; Parse.Cloud.beforeLogin(req => { hit++; - expect(req.object.get('username')).toEqual('tupac'); + expect(req.object.get("username")).toEqual("tupac"); }); - await Parse.User.signUp('tupac', 'shakur'); - const user = await Parse.User.logIn('tupac', 'shakur'); + await Parse.User.signUp("tupac", "shakur"); + const user = await Parse.User.logIn("tupac", "shakur"); expect(hit).toBe(1); expect(user).toBeDefined(); - expect(user.getUsername()).toBe('tupac'); + expect(user.getUsername()).toBe("tupac"); expect(user.getSessionToken()).toBeDefined(); done(); }); - it('should be able to block login if an error is thrown', async done => { + it("should be able to block login if an error is thrown", async done => { let hit = 0; Parse.Cloud.beforeLogin(req => { hit++; - if (req.object.get('isBanned')) { - throw new Error('banned account'); + if (req.object.get("isBanned")) { + throw new Error("banned account"); } }); - const user = await Parse.User.signUp('tupac', 'shakur'); + const user = await Parse.User.signUp("tupac", "shakur"); await user.save({ isBanned: true }); try { - await Parse.User.logIn('tupac', 'shakur'); - throw new Error('should not have been logged in.'); + await Parse.User.logIn("tupac", "shakur"); + throw new Error("should not have been logged in."); } catch (e) { - expect(e.message).toBe('banned account'); + expect(e.message).toBe("banned account"); } expect(hit).toBe(1); done(); }); - it('should be able to block login if an error is thrown even if the user has a attached file', async done => { + it("should be able to block login if an error is thrown even if the user has a attached file", async done => { let hit = 0; Parse.Cloud.beforeLogin(req => { hit++; - if (req.object.get('isBanned')) { - throw new Error('banned account'); + if (req.object.get("isBanned")) { + throw new Error("banned account"); } }); - const user = await Parse.User.signUp('tupac', 'shakur'); - const base64 = 'V29ya2luZyBhdCBQYXJzZSBpcyBncmVhdCE='; - const file = new Parse.File('myfile.txt', { base64 }); + const user = await Parse.User.signUp("tupac", "shakur"); + const base64 = "V29ya2luZyBhdCBQYXJzZSBpcyBncmVhdCE="; + const file = new Parse.File("myfile.txt", { base64 }); await file.save(); await user.save({ isBanned: true, file }); try { - await Parse.User.logIn('tupac', 'shakur'); - throw new Error('should not have been logged in.'); + await Parse.User.logIn("tupac", "shakur"); + throw new Error("should not have been logged in."); } catch (e) { - expect(e.message).toBe('banned account'); + expect(e.message).toBe("banned account"); } expect(hit).toBe(1); done(); }); - it('should not run beforeLogin with incorrect credentials', async done => { + it("should not run beforeLogin with incorrect credentials", async done => { let hit = 0; Parse.Cloud.beforeLogin(req => { hit++; - expect(req.object.get('username')).toEqual('tupac'); + expect(req.object.get("username")).toEqual("tupac"); }); - await Parse.User.signUp('tupac', 'shakur'); + await Parse.User.signUp("tupac", "shakur"); try { - await Parse.User.logIn('tony', 'shakur'); + await Parse.User.logIn("tony", "shakur"); } catch (e) { expect(e.code).toBe(Parse.Error.OBJECT_NOT_FOUND); } @@ -3293,67 +3378,67 @@ describe('beforeLogin hook', () => { done(); }); - it('should not run beforeLogin on sign up', async done => { + it("should not run beforeLogin on sign up", async done => { let hit = 0; Parse.Cloud.beforeLogin(req => { hit++; - expect(req.object.get('username')).toEqual('tupac'); + expect(req.object.get("username")).toEqual("tupac"); }); - const user = await Parse.User.signUp('tupac', 'shakur'); + const user = await Parse.User.signUp("tupac", "shakur"); expect(user).toBeDefined(); expect(hit).toBe(0); done(); }); - it('should trigger afterLogout hook on logout', async done => { + it("should trigger afterLogout hook on logout", async done => { let userId; Parse.Cloud.afterLogout(req => { - expect(req.object.className).toEqual('_Session'); + expect(req.object.className).toEqual("_Session"); expect(req.object.id).toBeDefined(); - const user = req.object.get('user'); + const user = req.object.get("user"); expect(user).toBeDefined(); userId = user.id; }); - const user = await Parse.User.signUp('user', 'pass'); + const user = await Parse.User.signUp("user", "pass"); await Parse.User.logOut(); expect(user.id).toBe(userId); done(); }); - it('does not crash server when throwing in afterLogin hook', async () => { - const error = new Parse.Error(2000, 'afterLogin error'); + it("does not crash server when throwing in afterLogin hook", async () => { + const error = new Parse.Error(2000, "afterLogin error"); const trigger = { afterLogin() { throw error; }, }; - const spy = spyOn(trigger, 'afterLogin').and.callThrough(); + const spy = spyOn(trigger, "afterLogin").and.callThrough(); Parse.Cloud.afterLogin(trigger.afterLogin); - await Parse.User.signUp('user', 'pass'); - const response = await Parse.User.logIn('user', 'pass').catch(e => e); + await Parse.User.signUp("user", "pass"); + const response = await Parse.User.logIn("user", "pass").catch(e => e); expect(spy).toHaveBeenCalled(); expect(response).toEqual(error); }); - it('does not crash server when throwing in afterLogout hook', async () => { - const error = new Parse.Error(2000, 'afterLogout error'); + it("does not crash server when throwing in afterLogout hook", async () => { + const error = new Parse.Error(2000, "afterLogout error"); const trigger = { afterLogout() { throw error; }, }; - const spy = spyOn(trigger, 'afterLogout').and.callThrough(); + const spy = spyOn(trigger, "afterLogout").and.callThrough(); Parse.Cloud.afterLogout(trigger.afterLogout); - await Parse.User.signUp('user', 'pass'); + await Parse.User.signUp("user", "pass"); const response = await Parse.User.logOut().catch(e => e); expect(spy).toHaveBeenCalled(); expect(response).toEqual(error); }); - it_id('5656d6d7-65ef-43d1-8ca6-6942ae3614d5')(it)( - 'should have expected data in request in beforeLogin', + it_id("5656d6d7-65ef-43d1-8ca6-6942ae3614d5")(it)( + "should have expected data in request in beforeLogin", async done => { Parse.Cloud.beforeLogin(req => { expect(req.object).toBeDefined(); @@ -3364,35 +3449,35 @@ describe('beforeLogin hook', () => { expect(req.context).toBeDefined(); }); - await Parse.User.signUp('tupac', 'shakur'); - await Parse.User.logIn('tupac', 'shakur'); + await Parse.User.signUp("tupac", "shakur"); + await Parse.User.logIn("tupac", "shakur"); done(); } ); - it('afterFind should not be triggered when saving an object', async () => { + it("afterFind should not be triggered when saving an object", async () => { let beforeSaves = 0; - Parse.Cloud.beforeSave('SavingTest', () => { + Parse.Cloud.beforeSave("SavingTest", () => { beforeSaves++; }); let afterSaves = 0; - Parse.Cloud.afterSave('SavingTest', () => { + Parse.Cloud.afterSave("SavingTest", () => { afterSaves++; }); let beforeFinds = 0; - Parse.Cloud.beforeFind('SavingTest', () => { + Parse.Cloud.beforeFind("SavingTest", () => { beforeFinds++; }); let afterFinds = 0; - Parse.Cloud.afterFind('SavingTest', () => { + Parse.Cloud.afterFind("SavingTest", () => { afterFinds++; }); - const obj = new Parse.Object('SavingTest'); - obj.set('someField', 'some value 1'); + const obj = new Parse.Object("SavingTest"); + obj.set("someField", "some value 1"); await obj.save(); expect(beforeSaves).toEqual(1); @@ -3400,7 +3485,7 @@ describe('beforeLogin hook', () => { expect(beforeFinds).toEqual(0); expect(afterFinds).toEqual(0); - obj.set('someField', 'some value 2'); + obj.set("someField", "some value 2"); await obj.save(); expect(beforeSaves).toEqual(2); @@ -3415,7 +3500,7 @@ describe('beforeLogin hook', () => { expect(beforeFinds).toEqual(1); expect(afterFinds).toEqual(1); - obj.set('someField', 'some value 3'); + obj.set("someField", "some value 3"); await obj.save(); expect(beforeSaves).toEqual(3); @@ -3425,33 +3510,33 @@ describe('beforeLogin hook', () => { }); }); -describe('afterLogin hook', () => { - it('should run afterLogin after successful login', async done => { +describe("afterLogin hook", () => { + it("should run afterLogin after successful login", async done => { let hit = 0; Parse.Cloud.afterLogin(req => { hit++; - expect(req.object.get('username')).toEqual('testuser'); + expect(req.object.get("username")).toEqual("testuser"); }); - await Parse.User.signUp('testuser', 'p@ssword'); - const user = await Parse.User.logIn('testuser', 'p@ssword'); + await Parse.User.signUp("testuser", "p@ssword"); + const user = await Parse.User.logIn("testuser", "p@ssword"); expect(hit).toBe(1); expect(user).toBeDefined(); - expect(user.getUsername()).toBe('testuser'); + expect(user.getUsername()).toBe("testuser"); expect(user.getSessionToken()).toBeDefined(); done(); }); - it('should not run afterLogin after unsuccessful login', async done => { + it("should not run afterLogin after unsuccessful login", async done => { let hit = 0; Parse.Cloud.afterLogin(req => { hit++; - expect(req.object.get('username')).toEqual('testuser'); + expect(req.object.get("username")).toEqual("testuser"); }); - await Parse.User.signUp('testuser', 'p@ssword'); + await Parse.User.signUp("testuser", "p@ssword"); try { - await Parse.User.logIn('testuser', 'badpassword'); + await Parse.User.logIn("testuser", "badpassword"); } catch (e) { expect(e.code).toBe(Parse.Error.OBJECT_NOT_FOUND); } @@ -3459,21 +3544,21 @@ describe('afterLogin hook', () => { done(); }); - it('should not run afterLogin on sign up', async done => { + it("should not run afterLogin on sign up", async done => { let hit = 0; Parse.Cloud.afterLogin(req => { hit++; - expect(req.object.get('username')).toEqual('testuser'); + expect(req.object.get("username")).toEqual("testuser"); }); - const user = await Parse.User.signUp('testuser', 'p@ssword'); + const user = await Parse.User.signUp("testuser", "p@ssword"); expect(user).toBeDefined(); expect(hit).toBe(0); done(); }); - it_id('e86155c4-62e1-4c6e-ab4a-9ac6c87c60f2')(it)( - 'should have expected data in request in afterLogin', + it_id("e86155c4-62e1-4c6e-ab4a-9ac6c87c60f2")(it)( + "should have expected data in request in afterLogin", async done => { Parse.Cloud.afterLogin(req => { expect(req.object).toBeDefined(); @@ -3484,257 +3569,264 @@ describe('afterLogin hook', () => { expect(req.context).toBeDefined(); }); - await Parse.User.signUp('testuser', 'p@ssword'); - await Parse.User.logIn('testuser', 'p@ssword'); + await Parse.User.signUp("testuser", "p@ssword"); + await Parse.User.logIn("testuser", "p@ssword"); done(); } ); - it('context options should override _context object property when saving a new object', async () => { - Parse.Cloud.beforeSave('TestObject', req => { - expect(req.context.a).toEqual('a'); + it("context options should override _context object property when saving a new object", async () => { + Parse.Cloud.beforeSave("TestObject", req => { + expect(req.context.a).toEqual("a"); expect(req.context.hello).not.toBeDefined(); expect(req._context).not.toBeDefined(); expect(req.object._context).not.toBeDefined(); expect(req.object.context).not.toBeDefined(); }); - Parse.Cloud.afterSave('TestObject', req => { - expect(req.context.a).toEqual('a'); + Parse.Cloud.afterSave("TestObject", req => { + expect(req.context.a).toEqual("a"); expect(req.context.hello).not.toBeDefined(); expect(req._context).not.toBeDefined(); expect(req.object._context).not.toBeDefined(); expect(req.object.context).not.toBeDefined(); }); await request({ - url: 'http://localhost:8378/1/classes/TestObject', - method: 'POST', + url: "http://localhost:8378/1/classes/TestObject", + method: "POST", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Cloud-Context': '{"a":"a"}', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "X-Parse-Cloud-Context": '{"a":"a"}', }, - body: JSON.stringify({ _context: { hello: 'world' } }), + body: JSON.stringify({ _context: { hello: "world" } }), }); }); - it('should have access to context when saving a new object', async () => { - Parse.Cloud.beforeSave('TestObject', req => { - expect(req.context.a).toEqual('a'); + it("should have access to context when saving a new object", async () => { + Parse.Cloud.beforeSave("TestObject", req => { + expect(req.context.a).toEqual("a"); }); - Parse.Cloud.afterSave('TestObject', req => { - expect(req.context.a).toEqual('a'); + Parse.Cloud.afterSave("TestObject", req => { + expect(req.context.a).toEqual("a"); }); const obj = new TestObject(); - await obj.save(null, { context: { a: 'a' } }); + await obj.save(null, { context: { a: "a" } }); }); - it('should have access to context when saving an existing object', async () => { + it("should have access to context when saving an existing object", async () => { const obj = new TestObject(); await obj.save(null); - Parse.Cloud.beforeSave('TestObject', req => { - expect(req.context.a).toEqual('a'); + Parse.Cloud.beforeSave("TestObject", req => { + expect(req.context.a).toEqual("a"); }); - Parse.Cloud.afterSave('TestObject', req => { - expect(req.context.a).toEqual('a'); + Parse.Cloud.afterSave("TestObject", req => { + expect(req.context.a).toEqual("a"); }); - await obj.save(null, { context: { a: 'a' } }); + await obj.save(null, { context: { a: "a" } }); }); - it('should have access to context when saving a new object in a trigger', async () => { - Parse.Cloud.beforeSave('TestObject', req => { - expect(req.context.a).toEqual('a'); + it("should have access to context when saving a new object in a trigger", async () => { + Parse.Cloud.beforeSave("TestObject", req => { + expect(req.context.a).toEqual("a"); }); - Parse.Cloud.afterSave('TestObject', req => { - expect(req.context.a).toEqual('a'); + Parse.Cloud.afterSave("TestObject", req => { + expect(req.context.a).toEqual("a"); }); - Parse.Cloud.afterSave('TriggerObject', async () => { + Parse.Cloud.afterSave("TriggerObject", async () => { const obj = new TestObject(); - await obj.save(null, { context: { a: 'a' } }); + await obj.save(null, { context: { a: "a" } }); }); - const obj = new Parse.Object('TriggerObject'); + const obj = new Parse.Object("TriggerObject"); await obj.save(null); }); - it('should have access to context when cascade-saving objects', async () => { - Parse.Cloud.beforeSave('TestObject', req => { - expect(req.context.a).toEqual('a'); + it("should have access to context when cascade-saving objects", async () => { + Parse.Cloud.beforeSave("TestObject", req => { + expect(req.context.a).toEqual("a"); }); - Parse.Cloud.afterSave('TestObject', req => { - expect(req.context.a).toEqual('a'); + Parse.Cloud.afterSave("TestObject", req => { + expect(req.context.a).toEqual("a"); }); - Parse.Cloud.beforeSave('TestObject2', req => { - expect(req.context.a).toEqual('a'); + Parse.Cloud.beforeSave("TestObject2", req => { + expect(req.context.a).toEqual("a"); }); - Parse.Cloud.afterSave('TestObject2', req => { - expect(req.context.a).toEqual('a'); + Parse.Cloud.afterSave("TestObject2", req => { + expect(req.context.a).toEqual("a"); }); - const obj = new Parse.Object('TestObject'); - const obj2 = new Parse.Object('TestObject2'); - obj.set('obj2', obj2); - await obj.save(null, { context: { a: 'a' } }); + const obj = new Parse.Object("TestObject"); + const obj2 = new Parse.Object("TestObject2"); + obj.set("obj2", obj2); + await obj.save(null, { context: { a: "a" } }); }); - it('should have access to context as saveAll argument', async () => { - Parse.Cloud.beforeSave('TestObject', req => { - expect(req.context.a).toEqual('a'); + it("should have access to context as saveAll argument", async () => { + Parse.Cloud.beforeSave("TestObject", req => { + expect(req.context.a).toEqual("a"); }); - Parse.Cloud.afterSave('TestObject', req => { - expect(req.context.a).toEqual('a'); + Parse.Cloud.afterSave("TestObject", req => { + expect(req.context.a).toEqual("a"); }); const obj1 = new TestObject(); const obj2 = new TestObject(); - await Parse.Object.saveAll([obj1, obj2], { context: { a: 'a' } }); + await Parse.Object.saveAll([obj1, obj2], { context: { a: "a" } }); }); - it('should have access to context as destroyAll argument', async () => { - Parse.Cloud.beforeDelete('TestObject', req => { - expect(req.context.a).toEqual('a'); + it("should have access to context as destroyAll argument", async () => { + Parse.Cloud.beforeDelete("TestObject", req => { + expect(req.context.a).toEqual("a"); }); - Parse.Cloud.afterDelete('TestObject', req => { - expect(req.context.a).toEqual('a'); + Parse.Cloud.afterDelete("TestObject", req => { + expect(req.context.a).toEqual("a"); }); const obj1 = new TestObject(); const obj2 = new TestObject(); await Parse.Object.saveAll([obj1, obj2]); - await Parse.Object.destroyAll([obj1, obj2], { context: { a: 'a' } }); + await Parse.Object.destroyAll([obj1, obj2], { context: { a: "a" } }); }); - it('should have access to context as destroy a object', async () => { - Parse.Cloud.beforeDelete('TestObject', req => { - expect(req.context.a).toEqual('a'); + it("should have access to context as destroy a object", async () => { + Parse.Cloud.beforeDelete("TestObject", req => { + expect(req.context.a).toEqual("a"); }); - Parse.Cloud.afterDelete('TestObject', req => { - expect(req.context.a).toEqual('a'); + Parse.Cloud.afterDelete("TestObject", req => { + expect(req.context.a).toEqual("a"); }); const obj = new TestObject(); await obj.save(); - await obj.destroy({ context: { a: 'a' } }); + await obj.destroy({ context: { a: "a" } }); }); - it('should have access to context in beforeFind hook', async () => { - Parse.Cloud.beforeFind('TestObject', req => { - expect(req.context.a).toEqual('a'); + it("should have access to context in beforeFind hook", async () => { + Parse.Cloud.beforeFind("TestObject", req => { + expect(req.context.a).toEqual("a"); }); - const query = new Parse.Query('TestObject'); - return query.find({ context: { a: 'a' } }); + const query = new Parse.Query("TestObject"); + return query.find({ context: { a: "a" } }); }); - it('should have access to context when cloud function is called.', async () => { - Parse.Cloud.define('contextTest', async req => { - expect(req.context.a).toEqual('a'); + it("should have access to context when cloud function is called.", async () => { + Parse.Cloud.define("contextTest", async req => { + expect(req.context.a).toEqual("a"); return {}; }); - await Parse.Cloud.run('contextTest', {}, { context: { a: 'a' } }); + await Parse.Cloud.run("contextTest", {}, { context: { a: "a" } }); }); - it('afterFind should have access to context', async () => { - Parse.Cloud.afterFind('TestObject', req => { - expect(req.context.a).toEqual('a'); + it("afterFind should have access to context", async () => { + Parse.Cloud.afterFind("TestObject", req => { + expect(req.context.a).toEqual("a"); }); const obj = new TestObject(); await obj.save(); const query = new Parse.Query(TestObject); - await query.find({ context: { a: 'a' } }); + await query.find({ context: { a: "a" } }); }); - it('beforeFind and afterFind should have access to context while making fetch call', async () => { - Parse.Cloud.beforeFind('TestObject', req => { - expect(req.context.a).toEqual('a'); + it("beforeFind and afterFind should have access to context while making fetch call", async () => { + Parse.Cloud.beforeFind("TestObject", req => { + expect(req.context.a).toEqual("a"); expect(req.context.b).toBeUndefined(); - req.context.b = 'b'; + req.context.b = "b"; }); - Parse.Cloud.afterFind('TestObject', req => { - expect(req.context.a).toEqual('a'); - expect(req.context.b).toEqual('b'); + Parse.Cloud.afterFind("TestObject", req => { + expect(req.context.a).toEqual("a"); + expect(req.context.b).toEqual("b"); }); const obj = new TestObject(); await obj.save(); - await obj.fetch({ context: { a: 'a' } }); + await obj.fetch({ context: { a: "a" } }); }); }); -describe('saveFile hooks', () => { - it('beforeSave(Parse.File) should return file that is already saved and not save anything to files adapter', async () => { +describe("saveFile hooks", () => { + it("beforeSave(Parse.File) should return file that is already saved and not save anything to files adapter", async () => { await reconfigureServer({ filesAdapter: mockAdapter }); - const createFileSpy = spyOn(mockAdapter, 'createFile').and.callThrough(); + const createFileSpy = spyOn(mockAdapter, "createFile").and.callThrough(); Parse.Cloud.beforeSave(Parse.File, () => { - const newFile = new Parse.File('some-file.txt'); - newFile._url = 'http://www.somewhere.com/parse/files/some-app-id/some-file.txt'; + const newFile = new Parse.File("some-file.txt"); + newFile._url = + "http://www.somewhere.com/parse/files/some-app-id/some-file.txt"; return newFile; }); - const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); + const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); const result = await file.save({ useMasterKey: true }); expect(result).toBe(file); - expect(result._name).toBe('some-file.txt'); - expect(result._url).toBe('http://www.somewhere.com/parse/files/some-app-id/some-file.txt'); + expect(result._name).toBe("some-file.txt"); + expect(result._url).toBe( + "http://www.somewhere.com/parse/files/some-app-id/some-file.txt" + ); expect(createFileSpy).not.toHaveBeenCalled(); }); - it('beforeSave(Parse.File) should throw error', async () => { + it("beforeSave(Parse.File) should throw error", async () => { await reconfigureServer({ filesAdapter: mockAdapter }); Parse.Cloud.beforeSave(Parse.File, () => { - throw new Parse.Error(400, 'some-error-message'); + throw new Parse.Error(400, "some-error-message"); }); - const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); + const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); try { await file.save({ useMasterKey: true }); } catch (error) { - expect(error.message).toBe('some-error-message'); + expect(error.message).toBe("some-error-message"); } }); - it('beforeSave(Parse.File) should change values of uploaded file by editing fileObject directly', async () => { + it("beforeSave(Parse.File) should change values of uploaded file by editing fileObject directly", async () => { await reconfigureServer({ filesAdapter: mockAdapter }); - const createFileSpy = spyOn(mockAdapter, 'createFile').and.callThrough(); + const createFileSpy = spyOn(mockAdapter, "createFile").and.callThrough(); Parse.Cloud.beforeSave(Parse.File, async req => { - expect(req.triggerName).toEqual('beforeSave'); + expect(req.triggerName).toEqual("beforeSave"); expect(req.master).toBe(true); - req.file.addMetadata('foo', 'bar'); - req.file.addTag('tagA', 'some-tag'); + req.file.addMetadata("foo", "bar"); + req.file.addTag("tagA", "some-tag"); }); - const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); + const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); const result = await file.save({ useMasterKey: true }); expect(result).toBe(file); const newData = new Buffer([1, 2, 3]); const newOptions = { tags: { - tagA: 'some-tag', + tagA: "some-tag", }, metadata: { - foo: 'bar', + foo: "bar", }, }; expect(createFileSpy).toHaveBeenCalledWith( jasmine.any(String), newData, - 'text/plain', + "text/plain", newOptions ); }); - it('beforeSave(Parse.File) should change values by returning new fileObject', async () => { + it("beforeSave(Parse.File) should change values by returning new fileObject", async () => { await reconfigureServer({ filesAdapter: mockAdapter }); - const createFileSpy = spyOn(mockAdapter, 'createFile').and.callThrough(); + const createFileSpy = spyOn(mockAdapter, "createFile").and.callThrough(); Parse.Cloud.beforeSave(Parse.File, async req => { - expect(req.triggerName).toEqual('beforeSave'); + expect(req.triggerName).toEqual("beforeSave"); expect(req.fileSize).toBe(3); - const newFile = new Parse.File('donald_duck.pdf', [4, 5, 6], 'application/pdf'); - newFile.setMetadata({ foo: 'bar' }); - newFile.setTags({ tagA: 'some-tag' }); + const newFile = new Parse.File( + "donald_duck.pdf", + [4, 5, 6], + "application/pdf" + ); + newFile.setMetadata({ foo: "bar" }); + newFile.setTags({ tagA: "some-tag" }); return newFile; }); - const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); + const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); const result = await file.save({ useMasterKey: true }); expect(result).toBeInstanceOf(Parse.File); const newData = new Buffer([4, 5, 6]); - const newContentType = 'application/pdf'; + const newContentType = "application/pdf"; const newOptions = { tags: { - tagA: 'some-tag', + tagA: "some-tag", }, metadata: { - foo: 'bar', + foo: "bar", }, }; expect(createFileSpy).toHaveBeenCalledWith( @@ -3743,109 +3835,118 @@ describe('saveFile hooks', () => { newContentType, newOptions ); - const expectedFileName = 'donald_duck.pdf'; - expect(file._name.indexOf(expectedFileName)).toBe(file._name.length - expectedFileName.length); + const expectedFileName = "donald_duck.pdf"; + expect(file._name.indexOf(expectedFileName)).toBe( + file._name.length - expectedFileName.length + ); }); - it('beforeSave(Parse.File) should contain metadata and tags saved from client', async () => { + it("beforeSave(Parse.File) should contain metadata and tags saved from client", async () => { await reconfigureServer({ filesAdapter: mockAdapter }); - const createFileSpy = spyOn(mockAdapter, 'createFile').and.callThrough(); + const createFileSpy = spyOn(mockAdapter, "createFile").and.callThrough(); Parse.Cloud.beforeSave(Parse.File, async req => { - expect(req.triggerName).toEqual('beforeSave'); + expect(req.triggerName).toEqual("beforeSave"); expect(req.fileSize).toBe(3); expect(req.file).toBeInstanceOf(Parse.File); - expect(req.file.name()).toBe('popeye.txt'); - expect(req.file.metadata()).toEqual({ foo: 'bar' }); - expect(req.file.tags()).toEqual({ bar: 'foo' }); + expect(req.file.name()).toBe("popeye.txt"); + expect(req.file.metadata()).toEqual({ foo: "bar" }); + expect(req.file.tags()).toEqual({ bar: "foo" }); }); - const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); - file.setMetadata({ foo: 'bar' }); - file.setTags({ bar: 'foo' }); + const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); + file.setMetadata({ foo: "bar" }); + file.setTags({ bar: "foo" }); const result = await file.save({ useMasterKey: true }); expect(result).toBeInstanceOf(Parse.File); const options = { - metadata: { foo: 'bar' }, - tags: { bar: 'foo' }, + metadata: { foo: "bar" }, + tags: { bar: "foo" }, }; expect(createFileSpy).toHaveBeenCalledWith( jasmine.any(String), jasmine.any(Buffer), - 'text/plain', + "text/plain", options ); }); - it('beforeSave(Parse.File) should return same file data with new file name', async () => { + it("beforeSave(Parse.File) should return same file data with new file name", async () => { await reconfigureServer({ filesAdapter: mockAdapter }); - const config = Config.get('test'); + const config = Config.get("test"); config.filesController.options.preserveFileName = true; Parse.Cloud.beforeSave(Parse.File, async ({ file }) => { - expect(file.name()).toBe('popeye.txt'); + expect(file.name()).toBe("popeye.txt"); const fileData = await file.getData(); - const newFile = new Parse.File('2020-04-01.txt', { base64: fileData }); + const newFile = new Parse.File("2020-04-01.txt", { base64: fileData }); return newFile; }); - const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); + const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); const result = await file.save({ useMasterKey: true }); - expect(result.name()).toBe('2020-04-01.txt'); + expect(result.name()).toBe("2020-04-01.txt"); }); - it('afterSave(Parse.File) should set fileSize to null if beforeSave returns an already saved file', async () => { + it("afterSave(Parse.File) should set fileSize to null if beforeSave returns an already saved file", async () => { await reconfigureServer({ filesAdapter: mockAdapter }); - const createFileSpy = spyOn(mockAdapter, 'createFile').and.callThrough(); + const createFileSpy = spyOn(mockAdapter, "createFile").and.callThrough(); Parse.Cloud.beforeSave(Parse.File, req => { expect(req.fileSize).toBe(3); - const newFile = new Parse.File('some-file.txt'); - newFile._url = 'http://www.somewhere.com/parse/files/some-app-id/some-file.txt'; + const newFile = new Parse.File("some-file.txt"); + newFile._url = + "http://www.somewhere.com/parse/files/some-app-id/some-file.txt"; return newFile; }); Parse.Cloud.afterSave(Parse.File, req => { expect(req.fileSize).toBe(null); }); - const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); + const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); const result = await file.save({ useMasterKey: true }); expect(result).toBe(result); - expect(result._name).toBe('some-file.txt'); - expect(result._url).toBe('http://www.somewhere.com/parse/files/some-app-id/some-file.txt'); + expect(result._name).toBe("some-file.txt"); + expect(result._url).toBe( + "http://www.somewhere.com/parse/files/some-app-id/some-file.txt" + ); expect(createFileSpy).not.toHaveBeenCalled(); }); - it('afterSave(Parse.File) should throw error', async () => { + it("afterSave(Parse.File) should throw error", async () => { await reconfigureServer({ filesAdapter: mockAdapter }); Parse.Cloud.afterSave(Parse.File, async () => { - throw new Parse.Error(400, 'some-error-message'); + throw new Parse.Error(400, "some-error-message"); }); - const filename = 'donald_duck.pdf'; - const file = new Parse.File(filename, [1, 2, 3], 'text/plain'); + const filename = "donald_duck.pdf"; + const file = new Parse.File(filename, [1, 2, 3], "text/plain"); try { await file.save({ useMasterKey: true }); } catch (error) { - expect(error.message).toBe('some-error-message'); + expect(error.message).toBe("some-error-message"); } }); - it('afterSave(Parse.File) should call with fileObject', async done => { + it("afterSave(Parse.File) should call with fileObject", async done => { await reconfigureServer({ filesAdapter: mockAdapter }); Parse.Cloud.beforeSave(Parse.File, async req => { - req.file.setTags({ tagA: 'some-tag' }); - req.file.setMetadata({ foo: 'bar' }); + req.file.setTags({ tagA: "some-tag" }); + req.file.setMetadata({ foo: "bar" }); }); Parse.Cloud.afterSave(Parse.File, async req => { expect(req.master).toBe(true); - expect(req.file._tags).toEqual({ tagA: 'some-tag' }); - expect(req.file._metadata).toEqual({ foo: 'bar' }); + expect(req.file._tags).toEqual({ tagA: "some-tag" }); + expect(req.file._metadata).toEqual({ foo: "bar" }); done(); }); - const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); + const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); await file.save({ useMasterKey: true }); }); - it('afterSave(Parse.File) should change fileSize when file data changes', async done => { + it("afterSave(Parse.File) should change fileSize when file data changes", async done => { await reconfigureServer({ filesAdapter: mockAdapter }); Parse.Cloud.beforeSave(Parse.File, async req => { expect(req.fileSize).toBe(3); expect(req.master).toBe(true); - const newFile = new Parse.File('donald_duck.pdf', [4, 5, 6, 7, 8, 9], 'application/pdf'); + const newFile = new Parse.File( + "donald_duck.pdf", + [4, 5, 6, 7, 8, 9], + "application/pdf" + ); return newFile; }); Parse.Cloud.afterSave(Parse.File, async req => { @@ -3853,85 +3954,85 @@ describe('saveFile hooks', () => { expect(req.master).toBe(true); done(); }); - const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); + const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); await file.save({ useMasterKey: true }); }); - it('beforeDelete(Parse.File) should call with fileObject', async () => { + it("beforeDelete(Parse.File) should call with fileObject", async () => { await reconfigureServer({ filesAdapter: mockAdapter }); Parse.Cloud.beforeDelete(Parse.File, req => { expect(req.file).toBeInstanceOf(Parse.File); - expect(req.file._name).toEqual('popeye.txt'); - expect(req.file._url).toEqual('http://www.somewhere.com/popeye.txt'); + expect(req.file._name).toEqual("popeye.txt"); + expect(req.file._url).toEqual("http://www.somewhere.com/popeye.txt"); expect(req.fileSize).toBe(null); }); - const file = new Parse.File('popeye.txt'); + const file = new Parse.File("popeye.txt"); await file.destroy({ useMasterKey: true }); }); - it('beforeDelete(Parse.File) should throw error', async done => { + it("beforeDelete(Parse.File) should throw error", async done => { await reconfigureServer({ filesAdapter: mockAdapter }); Parse.Cloud.beforeDelete(Parse.File, () => { - throw new Error('some error message'); + throw new Error("some error message"); }); - const file = new Parse.File('popeye.txt'); + const file = new Parse.File("popeye.txt"); try { await file.destroy({ useMasterKey: true }); } catch (error) { - expect(error.message).toBe('some error message'); + expect(error.message).toBe("some error message"); done(); } }); - it('afterDelete(Parse.File) should call with fileObject', async done => { + it("afterDelete(Parse.File) should call with fileObject", async done => { await reconfigureServer({ filesAdapter: mockAdapter }); Parse.Cloud.beforeDelete(Parse.File, req => { expect(req.file).toBeInstanceOf(Parse.File); - expect(req.file._name).toEqual('popeye.txt'); - expect(req.file._url).toEqual('http://www.somewhere.com/popeye.txt'); + expect(req.file._name).toEqual("popeye.txt"); + expect(req.file._url).toEqual("http://www.somewhere.com/popeye.txt"); }); Parse.Cloud.afterDelete(Parse.File, req => { expect(req.file).toBeInstanceOf(Parse.File); - expect(req.file._name).toEqual('popeye.txt'); - expect(req.file._url).toEqual('http://www.somewhere.com/popeye.txt'); + expect(req.file._name).toEqual("popeye.txt"); + expect(req.file._url).toEqual("http://www.somewhere.com/popeye.txt"); done(); }); - const file = new Parse.File('popeye.txt'); + const file = new Parse.File("popeye.txt"); await file.destroy({ useMasterKey: true }); }); - it('beforeSave(Parse.File) should not change file if nothing is returned', async () => { + it("beforeSave(Parse.File) should not change file if nothing is returned", async () => { await reconfigureServer({ filesAdapter: mockAdapter }); Parse.Cloud.beforeSave(Parse.File, () => { return; }); - const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); + const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); const result = await file.save({ useMasterKey: true }); expect(result).toBe(file); }); - it('throw custom error from beforeSave(Parse.File) ', async done => { + it("throw custom error from beforeSave(Parse.File) ", async done => { Parse.Cloud.beforeSave(Parse.File, () => { - throw new Parse.Error(Parse.Error.SCRIPT_FAILED, 'It should fail'); + throw new Parse.Error(Parse.Error.SCRIPT_FAILED, "It should fail"); }); try { - const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); + const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); await file.save({ useMasterKey: true }); - fail('error should have thrown'); + fail("error should have thrown"); } catch (e) { expect(e.code).toBe(Parse.Error.SCRIPT_FAILED); done(); } }); - it('throw empty error from beforeSave(Parse.File)', async done => { + it("throw empty error from beforeSave(Parse.File)", async done => { Parse.Cloud.beforeSave(Parse.File, () => { throw null; }); try { - const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); + const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); await file.save({ useMasterKey: true }); - fail('error should have thrown'); + fail("error should have thrown"); } catch (e) { expect(e.code).toBe(130); done(); @@ -3939,23 +4040,23 @@ describe('saveFile hooks', () => { }); }); -describe('Parse.File hooks', () => { - it('find hooks should run', async () => { - const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); +describe("Parse.File hooks", () => { + it("find hooks should run", async () => { + const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); await file.save({ useMasterKey: true }); - const user = await Parse.User.signUp('username', 'password'); + const user = await Parse.User.signUp("username", "password"); const hooks = { beforeFind(req) { expect(req).toBeDefined(); expect(req.file).toBeDefined(); - expect(req.triggerName).toBe('beforeFind'); + expect(req.triggerName).toBe("beforeFind"); expect(req.master).toBeFalse(); expect(req.log).toBeDefined(); }, afterFind(req) { expect(req).toBeDefined(); expect(req.file).toBeDefined(); - expect(req.triggerName).toBe('afterFind'); + expect(req.triggerName).toBe("afterFind"); expect(req.master).toBeFalse(); expect(req.log).toBeDefined(); expect(req.forceDownload).toBeFalse(); @@ -3968,9 +4069,9 @@ describe('Parse.File hooks', () => { await request({ url: file.url(), headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Session-Token': user.getSessionToken(), + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "X-Parse-Session-Token": user.getSessionToken(), }, }); for (const hook in hooks) { @@ -3978,13 +4079,13 @@ describe('Parse.File hooks', () => { } }); - it('beforeFind can throw', async () => { - const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); + it("beforeFind can throw", async () => { + const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); await file.save({ useMasterKey: true }); - const user = await Parse.User.signUp('username', 'password'); + const user = await Parse.User.signUp("username", "password"); const hooks = { beforeFind() { - throw 'unauthorized'; + throw "unauthorized"; }, afterFind() {}, }; @@ -3996,27 +4097,29 @@ describe('Parse.File hooks', () => { request({ url: file.url(), headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Session-Token': user.getSessionToken(), + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "X-Parse-Session-Token": user.getSessionToken(), }, }).catch(e => { throw new Parse.Error(e.data.code, e.data.error); }) - ).toBeRejectedWith(new Parse.Error(Parse.Error.SCRIPT_FAILED, 'unauthorized')); + ).toBeRejectedWith( + new Parse.Error(Parse.Error.SCRIPT_FAILED, "unauthorized") + ); expect(hooks.beforeFind).toHaveBeenCalled(); expect(hooks.afterFind).not.toHaveBeenCalled(); }); - it('afterFind can throw', async () => { - const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); + it("afterFind can throw", async () => { + const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); await file.save({ useMasterKey: true }); - const user = await Parse.User.signUp('username', 'password'); + const user = await Parse.User.signUp("username", "password"); const hooks = { beforeFind() {}, afterFind() { - throw 'unauthorized'; + throw "unauthorized"; }, }; for (const hook in hooks) { @@ -4027,45 +4130,52 @@ describe('Parse.File hooks', () => { request({ url: file.url(), headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Session-Token': user.getSessionToken(), + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "X-Parse-Session-Token": user.getSessionToken(), }, }).catch(e => { throw new Parse.Error(e.data.code, e.data.error); }) - ).toBeRejectedWith(new Parse.Error(Parse.Error.SCRIPT_FAILED, 'unauthorized')); + ).toBeRejectedWith( + new Parse.Error(Parse.Error.SCRIPT_FAILED, "unauthorized") + ); for (const hook in hooks) { expect(hooks[hook]).toHaveBeenCalled(); } }); - it('can force download', async () => { - const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); + it("can force download", async () => { + const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); await file.save({ useMasterKey: true }); - const user = await Parse.User.signUp('username', 'password'); + const user = await Parse.User.signUp("username", "password"); Parse.Cloud.afterFind(Parse.File, req => { req.forceDownload = true; }); const response = await request({ url: file.url(), headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Session-Token': user.getSessionToken(), + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "X-Parse-Session-Token": user.getSessionToken(), }, }); - expect(response.headers['content-disposition']).toBe(`attachment;filename=${file._name}`); + expect(response.headers["content-disposition"]).toBe( + `attachment;filename=${file._name}` + ); }); - }); +}); -describe('Cloud Config hooks', () => { +describe("Cloud Config hooks", () => { function testConfig() { - return Parse.Config.save({ internal: 'i', string: 's', number: 12 }, { internal: true }); + return Parse.Config.save( + { internal: "i", string: "s", number: 12 }, + { internal: true } + ); } - it_id('997fe20a-96f7-454a-a5b0-c155b8d02f05')(it)( - 'beforeSave(Parse.Config) can run hook with new config', + it_id("997fe20a-96f7-454a-a5b0-c155b8d02f05")(it)( + "beforeSave(Parse.Config) can run hook with new config", async () => { let count = 0; Parse.Cloud.beforeSave(Parse.Config, req => { @@ -4077,32 +4187,32 @@ describe('Cloud Config hooks', () => { expect(req.installationId).toBeDefined(); expect(req.context).toBeDefined(); const config = req.object; - expect(config.get('internal')).toBe('i'); - expect(config.get('string')).toBe('s'); - expect(config.get('number')).toBe(12); + expect(config.get("internal")).toBe("i"); + expect(config.get("string")).toBe("s"); + expect(config.get("number")).toBe(12); count += 1; }); await testConfig(); const config = await Parse.Config.get({ useMasterKey: true }); - expect(config.get('internal')).toBe('i'); - expect(config.get('string')).toBe('s'); - expect(config.get('number')).toBe(12); + expect(config.get("internal")).toBe("i"); + expect(config.get("string")).toBe("s"); + expect(config.get("number")).toBe(12); expect(count).toBe(1); } ); - it_id('06a9b66c-ffb4-43d1-a025-f7d2192500e7')(it)( - 'beforeSave(Parse.Config) can run hook with existing config', + it_id("06a9b66c-ffb4-43d1-a025-f7d2192500e7")(it)( + "beforeSave(Parse.Config) can run hook with existing config", async () => { let count = 0; Parse.Cloud.beforeSave(Parse.Config, req => { if (count === 0) { - expect(req.object.get('number')).toBe(12); + expect(req.object.get("number")).toBe(12); expect(req.original).toBeUndefined(); } if (count === 1) { - expect(req.object.get('number')).toBe(13); - expect(req.original.get('number')).toBe(12); + expect(req.object.get("number")).toBe(13); + expect(req.original.get("number")).toBe(12); } count += 1; }); @@ -4112,8 +4222,8 @@ describe('Cloud Config hooks', () => { } ); - it_id('ca76de8e-671b-4c2d-9535-bd28a855fa1a')(it)( - 'beforeSave(Parse.Config) should not change config if nothing is returned', + it_id("ca76de8e-671b-4c2d-9535-bd28a855fa1a")(it)( + "beforeSave(Parse.Config) should not change config if nothing is returned", async () => { let count = 0; Parse.Cloud.beforeSave(Parse.Config, () => { @@ -4122,54 +4232,54 @@ describe('Cloud Config hooks', () => { }); await testConfig(); const config = await Parse.Config.get({ useMasterKey: true }); - expect(config.get('internal')).toBe('i'); - expect(config.get('string')).toBe('s'); - expect(config.get('number')).toBe(12); + expect(config.get("internal")).toBe("i"); + expect(config.get("string")).toBe("s"); + expect(config.get("number")).toBe(12); expect(count).toBe(1); } ); - it('beforeSave(Parse.Config) throw custom error', async () => { + it("beforeSave(Parse.Config) throw custom error", async () => { Parse.Cloud.beforeSave(Parse.Config, () => { - throw new Parse.Error(Parse.Error.SCRIPT_FAILED, 'It should fail'); + throw new Parse.Error(Parse.Error.SCRIPT_FAILED, "It should fail"); }); try { await testConfig(); - fail('error should have thrown'); + fail("error should have thrown"); } catch (e) { expect(e.code).toBe(Parse.Error.SCRIPT_FAILED); - expect(e.message).toBe('It should fail'); + expect(e.message).toBe("It should fail"); } }); - it('beforeSave(Parse.Config) throw string error', async () => { + it("beforeSave(Parse.Config) throw string error", async () => { Parse.Cloud.beforeSave(Parse.Config, () => { - throw 'before save failed'; + throw "before save failed"; }); try { await testConfig(); - fail('error should have thrown'); + fail("error should have thrown"); } catch (e) { expect(e.code).toBe(Parse.Error.SCRIPT_FAILED); - expect(e.message).toBe('before save failed'); + expect(e.message).toBe("before save failed"); } }); - it('beforeSave(Parse.Config) throw empty error', async () => { + it("beforeSave(Parse.Config) throw empty error", async () => { Parse.Cloud.beforeSave(Parse.Config, () => { throw null; }); try { await testConfig(); - fail('error should have thrown'); + fail("error should have thrown"); } catch (e) { expect(e.code).toBe(Parse.Error.SCRIPT_FAILED); - expect(e.message).toBe('Script failed. Unknown error.'); + expect(e.message).toBe("Script failed. Unknown error."); } }); - it_id('3e7a75c0-6c2e-4c7e-b042-6eb5f23acf94')(it)( - 'afterSave(Parse.Config) can run hook with new config', + it_id("3e7a75c0-6c2e-4c7e-b042-6eb5f23acf94")(it)( + "afterSave(Parse.Config) can run hook with new config", async () => { let count = 0; Parse.Cloud.afterSave(Parse.Config, req => { @@ -4181,32 +4291,32 @@ describe('Cloud Config hooks', () => { expect(req.installationId).toBeDefined(); expect(req.context).toBeDefined(); const config = req.object; - expect(config.get('internal')).toBe('i'); - expect(config.get('string')).toBe('s'); - expect(config.get('number')).toBe(12); + expect(config.get("internal")).toBe("i"); + expect(config.get("string")).toBe("s"); + expect(config.get("number")).toBe(12); count += 1; }); await testConfig(); const config = await Parse.Config.get({ useMasterKey: true }); - expect(config.get('internal')).toBe('i'); - expect(config.get('string')).toBe('s'); - expect(config.get('number')).toBe(12); + expect(config.get("internal")).toBe("i"); + expect(config.get("string")).toBe("s"); + expect(config.get("number")).toBe(12); expect(count).toBe(1); } ); - it_id('5cffb28a-2924-4857-84bb-f5778d80372a')(it)( - 'afterSave(Parse.Config) can run hook with existing config', + it_id("5cffb28a-2924-4857-84bb-f5778d80372a")(it)( + "afterSave(Parse.Config) can run hook with existing config", async () => { let count = 0; Parse.Cloud.afterSave(Parse.Config, req => { if (count === 0) { - expect(req.object.get('number')).toBe(12); + expect(req.object.get("number")).toBe(12); expect(req.original).toBeUndefined(); } if (count === 1) { - expect(req.object.get('number')).toBe(13); - expect(req.original.get('number')).toBe(12); + expect(req.object.get("number")).toBe(13); + expect(req.original.get("number")).toBe(12); } count += 1; }); @@ -4216,45 +4326,45 @@ describe('Cloud Config hooks', () => { } ); - it_id('49883992-ce91-4797-85f9-7cce1f819407')(it)( - 'afterSave(Parse.Config) should throw error', + it_id("49883992-ce91-4797-85f9-7cce1f819407")(it)( + "afterSave(Parse.Config) should throw error", async () => { Parse.Cloud.afterSave(Parse.Config, () => { - throw new Parse.Error(400, 'It should fail'); + throw new Parse.Error(400, "It should fail"); }); try { await testConfig(); - fail('error should have thrown'); + fail("error should have thrown"); } catch (e) { expect(e.code).toBe(400); - expect(e.message).toBe('It should fail'); + expect(e.message).toBe("It should fail"); } } ); }); -describe('sendEmail', () => { - it('can send email via Parse.Cloud', async done => { +describe("sendEmail", () => { + it("can send email via Parse.Cloud", async done => { const emailAdapter = { sendMail: mailData => { expect(mailData).toBeDefined(); - expect(mailData.to).toBe('test'); + expect(mailData.to).toBe("test"); reconfigureServer().then(done, done); }, }; await reconfigureServer({ emailAdapter: emailAdapter, }); - const mailData = { to: 'test' }; + const mailData = { to: "test" }; await Parse.Cloud.sendEmail(mailData); }); - it('cannot send email without adapter', async () => { - const logger = require('../lib/logger').logger; - spyOn(logger, 'error').and.callFake(() => {}); + it("cannot send email without adapter", async () => { + const logger = require("../lib/logger").logger; + spyOn(logger, "error").and.callFake(() => {}); await Parse.Cloud.sendEmail({}); expect(logger.error).toHaveBeenCalledWith( - 'Failed to send email because no mail adapter is configured for Parse Server.' + "Failed to send email because no mail adapter is configured for Parse Server." ); }); }); diff --git a/spec/CloudCodeLogger.spec.js b/spec/CloudCodeLogger.spec.js index de59b41bb7..24f7cfc8b4 100644 --- a/spec/CloudCodeLogger.spec.js +++ b/spec/CloudCodeLogger.spec.js @@ -1,12 +1,13 @@ -const LoggerController = require('../lib/Controllers/LoggerController').LoggerController; +const LoggerController = + require("../lib/Controllers/LoggerController").LoggerController; const WinstonLoggerAdapter = - require('../lib/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter; -const fs = require('fs'); -const Config = require('../lib/Config'); + require("../lib/Adapters/Logger/WinstonLoggerAdapter").WinstonLoggerAdapter; +const fs = require("fs"); +const Config = require("../lib/Config"); -const loremFile = __dirname + '/support/lorem.txt'; +const loremFile = __dirname + "/support/lorem.txt"; -describe('Cloud Code Logger', () => { +describe("Cloud Code Logger", () => { let user; let spy; beforeEach(async () => { @@ -16,122 +17,143 @@ describe('Cloud Code Logger', () => { silent: true, logLevel: undefined, logLevels: { - cloudFunctionError: 'error', - cloudFunctionSuccess: 'info', - triggerAfter: 'info', - triggerBeforeError: 'error', - triggerBeforeSuccess: 'info', + cloudFunctionError: "error", + cloudFunctionSuccess: "info", + triggerAfter: "info", + triggerBeforeError: "error", + triggerBeforeSuccess: "info", }, }) .then(() => { - return Parse.User.signUp('tester', 'abc') + return Parse.User.signUp("tester", "abc") .catch(() => {}) .then(loggedInUser => (user = loggedInUser)) - .then(() => Parse.User.logIn(user.get('username'), 'abc')); + .then(() => Parse.User.logIn(user.get("username"), "abc")); }) .then(() => { - spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough(); + spy = spyOn( + Config.get("test").loggerController.adapter, + "log" + ).and.callThrough(); }); }); // Note that helpers takes care of logout. // see helpers.js:afterEach - it_id('02d53b97-3ec7-46fb-abb6-176fd6e85590')(it)('should expose log to functions', () => { - const spy = spyOn(Config.get('test').loggerController, 'log').and.callThrough(); - Parse.Cloud.define('loggerTest', req => { - req.log.info('logTest', 'info log', { info: 'some log' }); - req.log.error('logTest', 'error log', { error: 'there was an error' }); - return {}; - }); + it_id("02d53b97-3ec7-46fb-abb6-176fd6e85590")(it)( + "should expose log to functions", + () => { + const spy = spyOn( + Config.get("test").loggerController, + "log" + ).and.callThrough(); + Parse.Cloud.define("loggerTest", req => { + req.log.info("logTest", "info log", { info: "some log" }); + req.log.error("logTest", "error log", { error: "there was an error" }); + return {}; + }); - return Parse.Cloud.run('loggerTest').then(() => { - expect(spy).toHaveBeenCalledTimes(3); - const cloudFunctionMessage = spy.calls.all()[2]; - const errorMessage = spy.calls.all()[1]; - const infoMessage = spy.calls.all()[0]; - expect(cloudFunctionMessage.args[0]).toBe('info'); - expect(cloudFunctionMessage.args[1][1].params).toEqual({}); - expect(cloudFunctionMessage.args[1][0]).toMatch( - /Ran cloud function loggerTest for user [^ ]* with:\n {2}Input: {}\n {2}Result: {}/ - ); - expect(cloudFunctionMessage.args[1][1].functionName).toEqual('loggerTest'); - expect(errorMessage.args[0]).toBe('error'); - expect(errorMessage.args[1][2].error).toBe('there was an error'); - expect(errorMessage.args[1][0]).toBe('logTest'); - expect(errorMessage.args[1][1]).toBe('error log'); - expect(infoMessage.args[0]).toBe('info'); - expect(infoMessage.args[1][2].info).toBe('some log'); - expect(infoMessage.args[1][0]).toBe('logTest'); - expect(infoMessage.args[1][1]).toBe('info log'); - }); - }); + return Parse.Cloud.run("loggerTest").then(() => { + expect(spy).toHaveBeenCalledTimes(3); + const cloudFunctionMessage = spy.calls.all()[2]; + const errorMessage = spy.calls.all()[1]; + const infoMessage = spy.calls.all()[0]; + expect(cloudFunctionMessage.args[0]).toBe("info"); + expect(cloudFunctionMessage.args[1][1].params).toEqual({}); + expect(cloudFunctionMessage.args[1][0]).toMatch( + /Ran cloud function loggerTest for user [^ ]* with:\n {2}Input: {}\n {2}Result: {}/ + ); + expect(cloudFunctionMessage.args[1][1].functionName).toEqual( + "loggerTest" + ); + expect(errorMessage.args[0]).toBe("error"); + expect(errorMessage.args[1][2].error).toBe("there was an error"); + expect(errorMessage.args[1][0]).toBe("logTest"); + expect(errorMessage.args[1][1]).toBe("error log"); + expect(infoMessage.args[0]).toBe("info"); + expect(infoMessage.args[1][2].info).toBe("some log"); + expect(infoMessage.args[1][0]).toBe("logTest"); + expect(infoMessage.args[1][1]).toBe("info log"); + }); + } + ); - it_id('768412f5-d32f-4134-89a6-08949781a6c0')(it)('trigger should obfuscate password', done => { - Parse.Cloud.beforeSave(Parse.User, req => { - return req.object; - }); + it_id("768412f5-d32f-4134-89a6-08949781a6c0")(it)( + "trigger should obfuscate password", + done => { + Parse.Cloud.beforeSave(Parse.User, req => { + return req.object; + }); - Parse.User.signUp('tester123', 'abc') - .then(() => { - const entry = spy.calls.mostRecent().args; - expect(entry[1]).not.toMatch(/password":"abc/); - expect(entry[1]).toMatch(/\*\*\*\*\*\*\*\*/); - done(); - }) - .then(null, e => done.fail(e)); - }); + Parse.User.signUp("tester123", "abc") + .then(() => { + const entry = spy.calls.mostRecent().args; + expect(entry[1]).not.toMatch(/password":"abc/); + expect(entry[1]).toMatch(/\*\*\*\*\*\*\*\*/); + done(); + }) + .then(null, e => done.fail(e)); + } + ); - it_id('3c394047-272e-4728-9d02-9eaa660d2ed2')(it)('should expose log to trigger', done => { - Parse.Cloud.beforeSave('MyObject', req => { - req.log.info('beforeSave MyObject', 'info log', { info: 'some log' }); - req.log.error('beforeSave MyObject', 'error log', { - error: 'there was an error', + it_id("3c394047-272e-4728-9d02-9eaa660d2ed2")(it)( + "should expose log to trigger", + done => { + Parse.Cloud.beforeSave("MyObject", req => { + req.log.info("beforeSave MyObject", "info log", { info: "some log" }); + req.log.error("beforeSave MyObject", "error log", { + error: "there was an error", + }); + return {}; }); - return {}; - }); - const obj = new Parse.Object('MyObject'); - obj.save().then(() => { - const lastCalls = spy.calls.all().reverse(); - const cloudTriggerMessage = lastCalls[0].args; - const errorMessage = lastCalls[1].args; - const infoMessage = lastCalls[2].args; - expect(cloudTriggerMessage[0]).toBe('info'); - expect(cloudTriggerMessage[2].triggerType).toEqual('beforeSave'); - expect(cloudTriggerMessage[1]).toMatch( - /beforeSave triggered for MyObject for user [^ ]*\n {2}Input: {}\n {2}Result: {"object":{}}/ - ); - expect(cloudTriggerMessage[2].user).toBe(user.id); - expect(errorMessage[0]).toBe('error'); - expect(errorMessage[3].error).toBe('there was an error'); - expect(errorMessage[1] + ' ' + errorMessage[2]).toBe('beforeSave MyObject error log'); - expect(infoMessage[0]).toBe('info'); - expect(infoMessage[3].info).toBe('some log'); - expect(infoMessage[1] + ' ' + infoMessage[2]).toBe('beforeSave MyObject info log'); - done(); - }); - }); + const obj = new Parse.Object("MyObject"); + obj.save().then(() => { + const lastCalls = spy.calls.all().reverse(); + const cloudTriggerMessage = lastCalls[0].args; + const errorMessage = lastCalls[1].args; + const infoMessage = lastCalls[2].args; + expect(cloudTriggerMessage[0]).toBe("info"); + expect(cloudTriggerMessage[2].triggerType).toEqual("beforeSave"); + expect(cloudTriggerMessage[1]).toMatch( + /beforeSave triggered for MyObject for user [^ ]*\n {2}Input: {}\n {2}Result: {"object":{}}/ + ); + expect(cloudTriggerMessage[2].user).toBe(user.id); + expect(errorMessage[0]).toBe("error"); + expect(errorMessage[3].error).toBe("there was an error"); + expect(errorMessage[1] + " " + errorMessage[2]).toBe( + "beforeSave MyObject error log" + ); + expect(infoMessage[0]).toBe("info"); + expect(infoMessage[3].info).toBe("some log"); + expect(infoMessage[1] + " " + infoMessage[2]).toBe( + "beforeSave MyObject info log" + ); + done(); + }); + } + ); - it('should truncate really long lines when asked to', () => { + it("should truncate really long lines when asked to", () => { const logController = new LoggerController(new WinstonLoggerAdapter()); - const longString = fs.readFileSync(loremFile, 'utf8'); + const longString = fs.readFileSync(loremFile, "utf8"); const truncatedString = logController.truncateLogMessage(longString); expect(truncatedString.length).toBe(1015); // truncate length + the string '... (truncated)' }); - it_id('4a009b1f-9203-49ca-8d48-5b45f4eedbdf')(it)( - 'should truncate input and result of long lines', + it_id("4a009b1f-9203-49ca-8d48-5b45f4eedbdf")(it)( + "should truncate input and result of long lines", done => { - const longString = fs.readFileSync(loremFile, 'utf8'); - Parse.Cloud.define('aFunction', req => { + const longString = fs.readFileSync(loremFile, "utf8"); + Parse.Cloud.define("aFunction", req => { return req.params; }); - Parse.Cloud.run('aFunction', { longString }) + Parse.Cloud.run("aFunction", { longString }) .then(() => { const log = spy.calls.mostRecent().args; - expect(log[0]).toEqual('info'); + expect(log[0]).toEqual("info"); expect(log[1]).toMatch( /Ran cloud function aFunction for user [^ ]* with:\n {2}Input: {.*?\(truncated\)$/m ); @@ -141,99 +163,115 @@ describe('Cloud Code Logger', () => { } ); - it_id('9857e15d-bb18-478d-8a67-fdaad3e89565')(it)('should log an afterSave', done => { - Parse.Cloud.afterSave('MyObject', () => {}); - new Parse.Object('MyObject') - .save() - .then(() => { - const log = spy.calls.mostRecent().args; - expect(log[2].triggerType).toEqual('afterSave'); - done(); - }) - // catch errors - not that the error is actually useful :( - .then(null, e => done.fail(e)); - }); - - it_id('ec13a296-f8b1-4fc6-985a-3593462edd9c')(it)('should log a denied beforeSave', done => { - Parse.Cloud.beforeSave('MyObject', () => { - throw 'uh oh!'; - }); + it_id("9857e15d-bb18-478d-8a67-fdaad3e89565")(it)( + "should log an afterSave", + done => { + Parse.Cloud.afterSave("MyObject", () => {}); + new Parse.Object("MyObject") + .save() + .then(() => { + const log = spy.calls.mostRecent().args; + expect(log[2].triggerType).toEqual("afterSave"); + done(); + }) + // catch errors - not that the error is actually useful :( + .then(null, e => done.fail(e)); + } + ); - new Parse.Object('MyObject') - .save() - .then( - () => done.fail('this is not supposed to succeed'), - () => new Promise(resolve => setTimeout(resolve, 100)) - ) - .then(() => { - const logs = spy.calls.all().reverse(); - const log = logs[1].args; // 0 is the 'uh oh!' from rejection... - expect(log[0]).toEqual('error'); - const error = log[2].error; - expect(error instanceof Parse.Error).toBeTruthy(); - expect(error.code).toBe(Parse.Error.SCRIPT_FAILED); - expect(error.message).toBe('uh oh!'); - done(); + it_id("ec13a296-f8b1-4fc6-985a-3593462edd9c")(it)( + "should log a denied beforeSave", + done => { + Parse.Cloud.beforeSave("MyObject", () => { + throw "uh oh!"; }); - }); - it_id('3e0caa45-60d6-41af-829a-fd389710c132')(it)('should log cloud function success', done => { - Parse.Cloud.define('aFunction', () => { - return 'it worked!'; - }); + new Parse.Object("MyObject") + .save() + .then( + () => done.fail("this is not supposed to succeed"), + () => new Promise(resolve => setTimeout(resolve, 100)) + ) + .then(() => { + const logs = spy.calls.all().reverse(); + const log = logs[1].args; // 0 is the 'uh oh!' from rejection... + expect(log[0]).toEqual("error"); + const error = log[2].error; + expect(error instanceof Parse.Error).toBeTruthy(); + expect(error.code).toBe(Parse.Error.SCRIPT_FAILED); + expect(error.message).toBe("uh oh!"); + done(); + }); + } + ); - Parse.Cloud.run('aFunction', { foo: 'bar' }).then(() => { - const log = spy.calls.mostRecent().args; - expect(log[0]).toEqual('info'); - expect(log[1]).toMatch( - /Ran cloud function aFunction for user [^ ]* with:\n {2}Input: {"foo":"bar"}\n {2}Result: "it worked!/ - ); - done(); - }); - }); + it_id("3e0caa45-60d6-41af-829a-fd389710c132")(it)( + "should log cloud function success", + done => { + Parse.Cloud.define("aFunction", () => { + return "it worked!"; + }); + + Parse.Cloud.run("aFunction", { foo: "bar" }).then(() => { + const log = spy.calls.mostRecent().args; + expect(log[0]).toEqual("info"); + expect(log[1]).toMatch( + /Ran cloud function aFunction for user [^ ]* with:\n {2}Input: {"foo":"bar"}\n {2}Result: "it worked!/ + ); + done(); + }); + } + ); - it_id('8088de8a-7cba-4035-8b05-4a903307e674')(it)( - 'should log cloud function execution using the custom log level', + it_id("8088de8a-7cba-4035-8b05-4a903307e674")(it)( + "should log cloud function execution using the custom log level", async done => { - Parse.Cloud.define('aFunction', () => { - return 'it worked!'; + Parse.Cloud.define("aFunction", () => { + return "it worked!"; }); - Parse.Cloud.define('bFunction', () => { - throw new Error('Failed'); + Parse.Cloud.define("bFunction", () => { + throw new Error("Failed"); }); - await Parse.Cloud.run('aFunction', { foo: 'bar' }).then(() => { - const log = spy.calls.allArgs().find(log => log[1].startsWith('Ran cloud function '))?.[0]; - expect(log).toEqual('info'); + await Parse.Cloud.run("aFunction", { foo: "bar" }).then(() => { + const log = spy.calls + .allArgs() + .find(log => log[1].startsWith("Ran cloud function "))?.[0]; + expect(log).toEqual("info"); }); await reconfigureServer({ silent: true, logLevels: { - cloudFunctionSuccess: 'warn', - cloudFunctionError: 'info', + cloudFunctionSuccess: "warn", + cloudFunctionError: "info", }, }); - spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough(); + spy = spyOn( + Config.get("test").loggerController.adapter, + "log" + ).and.callThrough(); try { - await Parse.Cloud.run('bFunction', { foo: 'bar' }); - throw new Error('bFunction should have failed'); + await Parse.Cloud.run("bFunction", { foo: "bar" }); + throw new Error("bFunction should have failed"); } catch { const log = spy.calls .allArgs() - .find(log => log[1].startsWith('Failed running cloud function bFunction for '))?.[0]; - expect(log).toEqual('info'); + .find(log => + log[1].startsWith("Failed running cloud function bFunction for ") + )?.[0]; + expect(log).toEqual("info"); done(); } } ); - it('should log cloud function triggers using the custom log level', async () => { - Parse.Cloud.beforeSave('TestClass', () => {}); - Parse.Cloud.afterSave('TestClass', () => {}); + it("should log cloud function triggers using the custom log level", async () => { + Parse.Cloud.beforeSave("TestClass", () => {}); + Parse.Cloud.afterSave("TestClass", () => {}); const execTest = async (logLevel, triggerBeforeSuccess, triggerAfter) => { await reconfigureServer({ @@ -245,64 +283,74 @@ describe('Cloud Code Logger', () => { }, }); - spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough(); - const obj = new Parse.Object('TestClass'); + spy = spyOn( + Config.get("test").loggerController.adapter, + "log" + ).and.callThrough(); + const obj = new Parse.Object("TestClass"); await obj.save(); return { beforeSave: spy.calls .allArgs() - .find(log => log[1].startsWith('beforeSave triggered for TestClass for user '))?.[0], + .find(log => + log[1].startsWith("beforeSave triggered for TestClass for user ") + )?.[0], afterSave: spy.calls .allArgs() - .find(log => log[1].startsWith('afterSave triggered for TestClass for user '))?.[0], + .find(log => + log[1].startsWith("afterSave triggered for TestClass for user ") + )?.[0], }; }; - let calls = await execTest('silly', 'silly', 'debug'); - expect(calls).toEqual({ beforeSave: 'silly', afterSave: 'debug' }); + let calls = await execTest("silly", "silly", "debug"); + expect(calls).toEqual({ beforeSave: "silly", afterSave: "debug" }); - calls = await execTest('info', 'warn', 'debug'); - expect(calls).toEqual({ beforeSave: 'warn', afterSave: undefined }); + calls = await execTest("info", "warn", "debug"); + expect(calls).toEqual({ beforeSave: "warn", afterSave: undefined }); }); - it_id('97e0eafa-cde6-4a9a-9e53-7db98bacbc62')(it)('should log cloud function failure', done => { - Parse.Cloud.define('aFunction', () => { - throw 'it failed!'; - }); + it_id("97e0eafa-cde6-4a9a-9e53-7db98bacbc62")(it)( + "should log cloud function failure", + done => { + Parse.Cloud.define("aFunction", () => { + throw "it failed!"; + }); - Parse.Cloud.run('aFunction', { foo: 'bar' }) - .catch(() => {}) - .then(() => { - const logs = spy.calls.all().reverse(); - expect(logs[0].args[1]).toBe('Parse error: '); - expect(logs[0].args[2].message).toBe('it failed!'); + Parse.Cloud.run("aFunction", { foo: "bar" }) + .catch(() => {}) + .then(() => { + const logs = spy.calls.all().reverse(); + expect(logs[0].args[1]).toBe("Parse error: "); + expect(logs[0].args[2].message).toBe("it failed!"); - const log = logs[1].args; - expect(log[0]).toEqual('error'); - expect(log[1]).toMatch( - /Failed running cloud function aFunction for user [^ ]* with:\n {2}Input: {"foo":"bar"}\n {2}Error:/ - ); - const errorString = JSON.stringify( - new Parse.Error(Parse.Error.SCRIPT_FAILED, 'it failed!') - ); - expect(log[1].indexOf(errorString)).toBeGreaterThan(0); - done(); - }) - .catch(done.fail); - }); + const log = logs[1].args; + expect(log[0]).toEqual("error"); + expect(log[1]).toMatch( + /Failed running cloud function aFunction for user [^ ]* with:\n {2}Input: {"foo":"bar"}\n {2}Error:/ + ); + const errorString = JSON.stringify( + new Parse.Error(Parse.Error.SCRIPT_FAILED, "it failed!") + ); + expect(log[1].indexOf(errorString)).toBeGreaterThan(0); + done(); + }) + .catch(done.fail); + } + ); - xit('should log a changed beforeSave indicating a change', done => { - pending('needs more work.....'); + xit("should log a changed beforeSave indicating a change", done => { + pending("needs more work....."); const logController = new LoggerController(new WinstonLoggerAdapter()); - Parse.Cloud.beforeSave('MyObject', req => { + Parse.Cloud.beforeSave("MyObject", req => { const myObj = req.object; - myObj.set('aChange', true); + myObj.set("aChange", true); return myObj; }); - new Parse.Object('MyObject') + new Parse.Object("MyObject") .save() .then(() => logController.getLogs({ from: Date.now() - 500, size: 1000 })) .then(() => { @@ -318,14 +366,14 @@ describe('Cloud Code Logger', () => { .then(null, e => done.fail(JSON.stringify(e))); }); - it_id('b86e8168-8370-4730-a4ba-24ca3016ad66')(it)( - 'cloud function should obfuscate password', + it_id("b86e8168-8370-4730-a4ba-24ca3016ad66")(it)( + "cloud function should obfuscate password", done => { - Parse.Cloud.define('testFunction', () => { - return 'verify code success'; + Parse.Cloud.define("testFunction", () => { + return "verify code success"; }); - Parse.Cloud.run('testFunction', { username: 'hawk', password: '123456' }) + Parse.Cloud.run("testFunction", { username: "hawk", password: "123456" }) .then(() => { const entry = spy.calls.mostRecent().args; expect(entry[2].params.password).toMatch(/\*\*\*\*\*\*\*\*/); @@ -335,12 +383,12 @@ describe('Cloud Code Logger', () => { } ); - it('should only log once for object not found', async () => { - const config = Config.get('test'); - const spy = spyOn(config.loggerController, 'error').and.callThrough(); + it("should only log once for object not found", async () => { + const config = Config.get("test"); + const spy = spyOn(config.loggerController, "error").and.callThrough(); try { - const object = new Parse.Object('Object'); - object.id = 'invalid'; + const object = new Parse.Object("Object"); + object.id = "invalid"; await object.fetch(); } catch (e) { /**/ @@ -348,54 +396,62 @@ describe('Cloud Code Logger', () => { expect(spy).toHaveBeenCalled(); expect(spy.calls.count()).toBe(1); const { args } = spy.calls.mostRecent(); - expect(args[0]).toBe('Parse error: '); - expect(args[1].message).toBe('Object not found.'); + expect(args[0]).toBe("Parse error: "); + expect(args[1].message).toBe("Object not found."); }); - it('should log cloud function execution using the silent log level', async () => { + it("should log cloud function execution using the silent log level", async () => { await reconfigureServer({ logLevels: { - cloudFunctionSuccess: 'silent', - cloudFunctionError: 'silent', + cloudFunctionSuccess: "silent", + cloudFunctionError: "silent", }, }); - Parse.Cloud.define('aFunction', () => { - return 'it worked!'; + Parse.Cloud.define("aFunction", () => { + return "it worked!"; }); - Parse.Cloud.define('bFunction', () => { - throw new Error('Failed'); + Parse.Cloud.define("bFunction", () => { + throw new Error("Failed"); }); - spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough(); + spy = spyOn( + Config.get("test").loggerController.adapter, + "log" + ).and.callThrough(); - await Parse.Cloud.run('aFunction', { foo: 'bar' }); + await Parse.Cloud.run("aFunction", { foo: "bar" }); expect(spy).toHaveBeenCalledTimes(0); - await expectAsync(Parse.Cloud.run('bFunction', { foo: 'bar' })).toBeRejected(); + await expectAsync( + Parse.Cloud.run("bFunction", { foo: "bar" }) + ).toBeRejected(); // Not "Failed running cloud function message..." expect(spy).toHaveBeenCalledTimes(1); }); - it('should log cloud function triggers using the silent log level', async () => { + it("should log cloud function triggers using the silent log level", async () => { await reconfigureServer({ logLevels: { - triggerAfter: 'silent', - triggerBeforeSuccess: 'silent', - triggerBeforeError: 'silent', + triggerAfter: "silent", + triggerBeforeSuccess: "silent", + triggerBeforeError: "silent", }, }); - Parse.Cloud.beforeSave('TestClassError', () => { - throw new Error('Failed'); + Parse.Cloud.beforeSave("TestClassError", () => { + throw new Error("Failed"); }); - Parse.Cloud.beforeSave('TestClass', () => {}); - Parse.Cloud.afterSave('TestClass', () => {}); + Parse.Cloud.beforeSave("TestClass", () => {}); + Parse.Cloud.afterSave("TestClass", () => {}); - spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough(); + spy = spyOn( + Config.get("test").loggerController.adapter, + "log" + ).and.callThrough(); - const obj = new Parse.Object('TestClass'); + const obj = new Parse.Object("TestClass"); await obj.save(); expect(spy).toHaveBeenCalledTimes(0); - const objError = new Parse.Object('TestClassError'); + const objError = new Parse.Object("TestClassError"); await expectAsync(objError.save()).toBeRejected(); // Not "beforeSave failed for TestClassError for user ..." expect(spy).toHaveBeenCalledTimes(1); diff --git a/spec/DatabaseController.spec.js b/spec/DatabaseController.spec.js index e1b50a5a52..e5a615bf63 100644 --- a/spec/DatabaseController.spec.js +++ b/spec/DatabaseController.spec.js @@ -1,25 +1,25 @@ -const Config = require('../lib/Config'); -const DatabaseController = require('../lib/Controllers/DatabaseController.js'); +const Config = require("../lib/Config"); +const DatabaseController = require("../lib/Controllers/DatabaseController.js"); const validateQuery = DatabaseController._validateQuery; -describe('DatabaseController', function () { - describe('validateQuery', function () { - it('should not restructure simple cases of SERVER-13732', done => { +describe("DatabaseController", function () { + describe("validateQuery", function () { + it("should not restructure simple cases of SERVER-13732", done => { const query = { $or: [{ a: 1 }, { a: 2 }], - _rperm: { $in: ['a', 'b'] }, + _rperm: { $in: ["a", "b"] }, foo: 3, }; validateQuery(query); expect(query).toEqual({ $or: [{ a: 1 }, { a: 2 }], - _rperm: { $in: ['a', 'b'] }, + _rperm: { $in: ["a", "b"] }, foo: 3, }); done(); }); - it('should not restructure SERVER-13732 queries with $nears', done => { + it("should not restructure SERVER-13732 queries with $nears", done => { let query = { $or: [{ a: 1 }, { b: 1 }], c: { $nearSphere: {} } }; validateQuery(query); expect(query).toEqual({ @@ -32,7 +32,7 @@ describe('DatabaseController', function () { done(); }); - it('should not push refactored keys down a tree for SERVER-13732', done => { + it("should not push refactored keys down a tree for SERVER-13732", done => { const query = { a: 1, $or: [{ $or: [{ b: 1 }, { b: 2 }] }, { $or: [{ c: 1 }, { c: 2 }] }], @@ -46,38 +46,40 @@ describe('DatabaseController', function () { done(); }); - it('should reject invalid queries', done => { + it("should reject invalid queries", done => { expect(() => validateQuery({ $or: { a: 1 } })).toThrow(); done(); }); - it('should accept valid queries', done => { + it("should accept valid queries", done => { expect(() => validateQuery({ $or: [{ a: 1 }, { b: 2 }] })).not.toThrow(); done(); }); }); - describe('addPointerPermissions', function () { - const CLASS_NAME = 'Foo'; - const USER_ID = 'userId'; + describe("addPointerPermissions", function () { + const CLASS_NAME = "Foo"; + const USER_ID = "userId"; const ACL_GROUP = [USER_ID]; - const OPERATION = 'find'; + const OPERATION = "find"; const databaseController = new DatabaseController(); - const schemaController = jasmine.createSpyObj('SchemaController', [ - 'testPermissionsForClassName', - 'getClassLevelPermissions', - 'getExpectedType', + const schemaController = jasmine.createSpyObj("SchemaController", [ + "testPermissionsForClassName", + "getClassLevelPermissions", + "getExpectedType", ]); - it('should not decorate query if no pointer CLPs are present', done => { + it("should not decorate query if no pointer CLPs are present", done => { const clp = buildCLP(); - const query = { a: 'b' }; + const query = { a: "b" }; schemaController.testPermissionsForClassName .withArgs(CLASS_NAME, ACL_GROUP, OPERATION) .and.returnValue(true); - schemaController.getClassLevelPermissions.withArgs(CLASS_NAME).and.returnValue(clp); + schemaController.getClassLevelPermissions + .withArgs(CLASS_NAME) + .and.returnValue(clp); const output = databaseController.addPointerPermissions( schemaController, @@ -92,17 +94,19 @@ describe('DatabaseController', function () { done(); }); - it('should decorate query if a pointer CLP entry is present', done => { - const clp = buildCLP(['user']); - const query = { a: 'b' }; + it("should decorate query if a pointer CLP entry is present", done => { + const clp = buildCLP(["user"]); + const query = { a: "b" }; schemaController.testPermissionsForClassName .withArgs(CLASS_NAME, ACL_GROUP, OPERATION) .and.returnValue(false); - schemaController.getClassLevelPermissions.withArgs(CLASS_NAME).and.returnValue(clp); + schemaController.getClassLevelPermissions + .withArgs(CLASS_NAME) + .and.returnValue(clp); schemaController.getExpectedType - .withArgs(CLASS_NAME, 'user') - .and.returnValue({ type: 'Pointer' }); + .withArgs(CLASS_NAME, "user") + .and.returnValue({ type: "Pointer" }); const output = databaseController.addPointerPermissions( schemaController, @@ -117,17 +121,19 @@ describe('DatabaseController', function () { done(); }); - it('should decorate query if an array CLP entry is present', done => { - const clp = buildCLP(['users']); - const query = { a: 'b' }; + it("should decorate query if an array CLP entry is present", done => { + const clp = buildCLP(["users"]); + const query = { a: "b" }; schemaController.testPermissionsForClassName .withArgs(CLASS_NAME, ACL_GROUP, OPERATION) .and.returnValue(false); - schemaController.getClassLevelPermissions.withArgs(CLASS_NAME).and.returnValue(clp); + schemaController.getClassLevelPermissions + .withArgs(CLASS_NAME) + .and.returnValue(clp); schemaController.getExpectedType - .withArgs(CLASS_NAME, 'users') - .and.returnValue({ type: 'Array' }); + .withArgs(CLASS_NAME, "users") + .and.returnValue({ type: "Array" }); const output = databaseController.addPointerPermissions( schemaController, @@ -145,17 +151,19 @@ describe('DatabaseController', function () { done(); }); - it('should decorate query if an object CLP entry is present', done => { - const clp = buildCLP(['user']); - const query = { a: 'b' }; + it("should decorate query if an object CLP entry is present", done => { + const clp = buildCLP(["user"]); + const query = { a: "b" }; schemaController.testPermissionsForClassName .withArgs(CLASS_NAME, ACL_GROUP, OPERATION) .and.returnValue(false); - schemaController.getClassLevelPermissions.withArgs(CLASS_NAME).and.returnValue(clp); + schemaController.getClassLevelPermissions + .withArgs(CLASS_NAME) + .and.returnValue(clp); schemaController.getExpectedType - .withArgs(CLASS_NAME, 'user') - .and.returnValue({ type: 'Object' }); + .withArgs(CLASS_NAME, "user") + .and.returnValue({ type: "Object" }); const output = databaseController.addPointerPermissions( schemaController, @@ -173,17 +181,19 @@ describe('DatabaseController', function () { done(); }); - it('should decorate query if a pointer CLP is present and the same field is part of the query', done => { - const clp = buildCLP(['user']); - const query = { a: 'b', user: 'a' }; + it("should decorate query if a pointer CLP is present and the same field is part of the query", done => { + const clp = buildCLP(["user"]); + const query = { a: "b", user: "a" }; schemaController.testPermissionsForClassName .withArgs(CLASS_NAME, ACL_GROUP, OPERATION) .and.returnValue(false); - schemaController.getClassLevelPermissions.withArgs(CLASS_NAME).and.returnValue(clp); + schemaController.getClassLevelPermissions + .withArgs(CLASS_NAME) + .and.returnValue(clp); schemaController.getExpectedType - .withArgs(CLASS_NAME, 'user') - .and.returnValue({ type: 'Pointer' }); + .withArgs(CLASS_NAME, "user") + .and.returnValue({ type: "Pointer" }); const output = databaseController.addPointerPermissions( schemaController, @@ -200,23 +210,25 @@ describe('DatabaseController', function () { done(); }); - it('should transform the query to an $or query if multiple array/pointer CLPs are present', done => { - const clp = buildCLP(['user', 'users', 'userObject']); - const query = { a: 'b' }; + it("should transform the query to an $or query if multiple array/pointer CLPs are present", done => { + const clp = buildCLP(["user", "users", "userObject"]); + const query = { a: "b" }; schemaController.testPermissionsForClassName .withArgs(CLASS_NAME, ACL_GROUP, OPERATION) .and.returnValue(false); - schemaController.getClassLevelPermissions.withArgs(CLASS_NAME).and.returnValue(clp); + schemaController.getClassLevelPermissions + .withArgs(CLASS_NAME) + .and.returnValue(clp); schemaController.getExpectedType - .withArgs(CLASS_NAME, 'user') - .and.returnValue({ type: 'Pointer' }); + .withArgs(CLASS_NAME, "user") + .and.returnValue({ type: "Pointer" }); schemaController.getExpectedType - .withArgs(CLASS_NAME, 'users') - .and.returnValue({ type: 'Array' }); + .withArgs(CLASS_NAME, "users") + .and.returnValue({ type: "Array" }); schemaController.getExpectedType - .withArgs(CLASS_NAME, 'userObject') - .and.returnValue({ type: 'Object' }); + .withArgs(CLASS_NAME, "userObject") + .and.returnValue({ type: "Object" }); const output = databaseController.addPointerPermissions( schemaController, @@ -237,19 +249,21 @@ describe('DatabaseController', function () { done(); }); - it('should not return a $or operation if the query involves one of the two fields also used as array/pointer permissions', done => { - const clp = buildCLP(['users', 'user']); - const query = { a: 'b', user: createUserPointer(USER_ID) }; + it("should not return a $or operation if the query involves one of the two fields also used as array/pointer permissions", done => { + const clp = buildCLP(["users", "user"]); + const query = { a: "b", user: createUserPointer(USER_ID) }; schemaController.testPermissionsForClassName .withArgs(CLASS_NAME, ACL_GROUP, OPERATION) .and.returnValue(false); - schemaController.getClassLevelPermissions.withArgs(CLASS_NAME).and.returnValue(clp); + schemaController.getClassLevelPermissions + .withArgs(CLASS_NAME) + .and.returnValue(clp); schemaController.getExpectedType - .withArgs(CLASS_NAME, 'user') - .and.returnValue({ type: 'Pointer' }); + .withArgs(CLASS_NAME, "user") + .and.returnValue({ type: "Pointer" }); schemaController.getExpectedType - .withArgs(CLASS_NAME, 'users') - .and.returnValue({ type: 'Array' }); + .withArgs(CLASS_NAME, "users") + .and.returnValue({ type: "Array" }); const output = databaseController.addPointerPermissions( schemaController, CLASS_NAME, @@ -261,22 +275,24 @@ describe('DatabaseController', function () { done(); }); - it('should not return a $or operation if the query involves one of the fields also used as array/pointer permissions', done => { - const clp = buildCLP(['user', 'users', 'userObject']); - const query = { a: 'b', user: createUserPointer(USER_ID) }; + it("should not return a $or operation if the query involves one of the fields also used as array/pointer permissions", done => { + const clp = buildCLP(["user", "users", "userObject"]); + const query = { a: "b", user: createUserPointer(USER_ID) }; schemaController.testPermissionsForClassName .withArgs(CLASS_NAME, ACL_GROUP, OPERATION) .and.returnValue(false); - schemaController.getClassLevelPermissions.withArgs(CLASS_NAME).and.returnValue(clp); + schemaController.getClassLevelPermissions + .withArgs(CLASS_NAME) + .and.returnValue(clp); schemaController.getExpectedType - .withArgs(CLASS_NAME, 'user') - .and.returnValue({ type: 'Pointer' }); + .withArgs(CLASS_NAME, "user") + .and.returnValue({ type: "Pointer" }); schemaController.getExpectedType - .withArgs(CLASS_NAME, 'users') - .and.returnValue({ type: 'Array' }); + .withArgs(CLASS_NAME, "users") + .and.returnValue({ type: "Array" }); schemaController.getExpectedType - .withArgs(CLASS_NAME, 'userObject') - .and.returnValue({ type: 'Object' }); + .withArgs(CLASS_NAME, "userObject") + .and.returnValue({ type: "Object" }); const output = databaseController.addPointerPermissions( schemaController, CLASS_NAME, @@ -288,17 +304,19 @@ describe('DatabaseController', function () { done(); }); - it('should throw an error if for some unexpected reason the property specified in the CLP is neither a pointer nor an array', done => { - const clp = buildCLP(['user']); - const query = { a: 'b' }; + it("should throw an error if for some unexpected reason the property specified in the CLP is neither a pointer nor an array", done => { + const clp = buildCLP(["user"]); + const query = { a: "b" }; schemaController.testPermissionsForClassName .withArgs(CLASS_NAME, ACL_GROUP, OPERATION) .and.returnValue(false); - schemaController.getClassLevelPermissions.withArgs(CLASS_NAME).and.returnValue(clp); + schemaController.getClassLevelPermissions + .withArgs(CLASS_NAME) + .and.returnValue(clp); schemaController.getExpectedType - .withArgs(CLASS_NAME, 'user') - .and.returnValue({ type: 'Number' }); + .withArgs(CLASS_NAME, "user") + .and.returnValue({ type: "Number" }); expect(() => { databaseController.addPointerPermissions( @@ -318,52 +336,74 @@ describe('DatabaseController', function () { }); }); - describe('reduceOperations', function () { + describe("reduceOperations", function () { const databaseController = new DatabaseController(); - it('objectToEntriesStrings', done => { - const output = databaseController.objectToEntriesStrings({ a: 1, b: 2, c: 3 }); + it("objectToEntriesStrings", done => { + const output = databaseController.objectToEntriesStrings({ + a: 1, + b: 2, + c: 3, + }); expect(output).toEqual(['"a":1', '"b":2', '"c":3']); done(); }); - it('reduceOrOperation', done => { + it("reduceOrOperation", done => { expect(databaseController.reduceOrOperation({ a: 1 })).toEqual({ a: 1 }); - expect(databaseController.reduceOrOperation({ $or: [{ a: 1 }, { b: 2 }] })).toEqual({ + expect( + databaseController.reduceOrOperation({ $or: [{ a: 1 }, { b: 2 }] }) + ).toEqual({ $or: [{ a: 1 }, { b: 2 }], }); - expect(databaseController.reduceOrOperation({ $or: [{ a: 1 }, { a: 2 }] })).toEqual({ + expect( + databaseController.reduceOrOperation({ $or: [{ a: 1 }, { a: 2 }] }) + ).toEqual({ $or: [{ a: 1 }, { a: 2 }], }); - expect(databaseController.reduceOrOperation({ $or: [{ a: 1 }, { a: 1 }] })).toEqual({ a: 1 }); expect( - databaseController.reduceOrOperation({ $or: [{ a: 1, b: 2, c: 3 }, { a: 1 }] }) + databaseController.reduceOrOperation({ $or: [{ a: 1 }, { a: 1 }] }) + ).toEqual({ a: 1 }); + expect( + databaseController.reduceOrOperation({ + $or: [{ a: 1, b: 2, c: 3 }, { a: 1 }], + }) ).toEqual({ a: 1 }); expect( - databaseController.reduceOrOperation({ $or: [{ b: 2 }, { a: 1, b: 2, c: 3 }] }) + databaseController.reduceOrOperation({ + $or: [{ b: 2 }, { a: 1, b: 2, c: 3 }], + }) ).toEqual({ b: 2 }); done(); }); - it('reduceAndOperation', done => { + it("reduceAndOperation", done => { expect(databaseController.reduceAndOperation({ a: 1 })).toEqual({ a: 1 }); - expect(databaseController.reduceAndOperation({ $and: [{ a: 1 }, { b: 2 }] })).toEqual({ + expect( + databaseController.reduceAndOperation({ $and: [{ a: 1 }, { b: 2 }] }) + ).toEqual({ $and: [{ a: 1 }, { b: 2 }], }); - expect(databaseController.reduceAndOperation({ $and: [{ a: 1 }, { a: 2 }] })).toEqual({ + expect( + databaseController.reduceAndOperation({ $and: [{ a: 1 }, { a: 2 }] }) + ).toEqual({ $and: [{ a: 1 }, { a: 2 }], }); - expect(databaseController.reduceAndOperation({ $and: [{ a: 1 }, { a: 1 }] })).toEqual({ + expect( + databaseController.reduceAndOperation({ $and: [{ a: 1 }, { a: 1 }] }) + ).toEqual({ a: 1, }); expect( - databaseController.reduceAndOperation({ $and: [{ a: 1, b: 2, c: 3 }, { b: 2 }] }) + databaseController.reduceAndOperation({ + $and: [{ a: 1, b: 2, c: 3 }, { b: 2 }], + }) ).toEqual({ a: 1, b: 2, c: 3 }); done(); }); }); - describe('enableCollationCaseComparison', () => { + describe("enableCollationCaseComparison", () => { const dummyStorageAdapter = { find: () => Promise.resolve([]), watch: () => Promise.resolve(), @@ -374,39 +414,45 @@ describe('DatabaseController', function () { Config.get(Parse.applicationId).schemaCache.clear(); }); - it('should force caseInsensitive to false with enableCollationCaseComparison option', async () => { + it("should force caseInsensitive to false with enableCollationCaseComparison option", async () => { const databaseController = new DatabaseController(dummyStorageAdapter, { enableCollationCaseComparison: true, }); - const spy = spyOn(dummyStorageAdapter, 'find'); + const spy = spyOn(dummyStorageAdapter, "find"); spy.and.callThrough(); - await databaseController.find('SomeClass', {}, { caseInsensitive: true }); + await databaseController.find("SomeClass", {}, { caseInsensitive: true }); expect(spy.calls.all()[0].args[3].caseInsensitive).toEqual(false); }); - it('should support caseInsensitive without enableCollationCaseComparison option', async () => { - const databaseController = new DatabaseController(dummyStorageAdapter, {}); - const spy = spyOn(dummyStorageAdapter, 'find'); + it("should support caseInsensitive without enableCollationCaseComparison option", async () => { + const databaseController = new DatabaseController( + dummyStorageAdapter, + {} + ); + const spy = spyOn(dummyStorageAdapter, "find"); spy.and.callThrough(); - await databaseController.find('_User', {}, { caseInsensitive: true }); + await databaseController.find("_User", {}, { caseInsensitive: true }); expect(spy.calls.all()[0].args[3].caseInsensitive).toEqual(true); }); - it_only_db('mongo')( - 'should create insensitive indexes without enableCollationCaseComparison', + it_only_db("mongo")( + "should create insensitive indexes without enableCollationCaseComparison", async () => { await reconfigureServer({ - databaseURI: 'mongodb://localhost:27017/enableCollationCaseComparisonFalse', + databaseURI: + "mongodb://localhost:27017/enableCollationCaseComparisonFalse", databaseAdapter: undefined, }); const user = new Parse.User(); await user.save({ - username: 'example', - password: 'password', - email: 'example@example.com', + username: "example", + password: "password", + email: "example@example.com", }); const schemas = await Parse.Schema.all(); - const UserSchema = schemas.find(({ className }) => className === '_User'); + const UserSchema = schemas.find( + ({ className }) => className === "_User" + ); expect(UserSchema.indexes).toEqual({ _id_: { _id: 1 }, username_1: { username: 1 }, @@ -417,22 +463,25 @@ describe('DatabaseController', function () { } ); - it_only_db('mongo')( - 'should not create insensitive indexes with enableCollationCaseComparison', + it_only_db("mongo")( + "should not create insensitive indexes with enableCollationCaseComparison", async () => { await reconfigureServer({ enableCollationCaseComparison: true, - databaseURI: 'mongodb://localhost:27017/enableCollationCaseComparisonTrue', + databaseURI: + "mongodb://localhost:27017/enableCollationCaseComparisonTrue", databaseAdapter: undefined, }); const user = new Parse.User(); await user.save({ - username: 'example', - password: 'password', - email: 'example@example.com', + username: "example", + password: "password", + email: "example@example.com", }); const schemas = await Parse.Schema.all(); - const UserSchema = schemas.find(({ className }) => className === '_User'); + const UserSchema = schemas.find( + ({ className }) => className === "_User" + ); expect(UserSchema.indexes).toEqual({ _id_: { _id: 1 }, username_1: { username: 1 }, @@ -442,7 +491,7 @@ describe('DatabaseController', function () { ); }); - describe('convertEmailToLowercase', () => { + describe("convertEmailToLowercase", () => { const dummyStorageAdapter = { createObject: () => Promise.resolve({ ops: [{}] }), findOneAndUpdate: () => Promise.resolve({}), @@ -450,86 +499,104 @@ describe('DatabaseController', function () { getAllClasses: () => Promise.resolve([ { - className: '_User', - fields: { email: 'String' }, + className: "_User", + fields: { email: "String" }, indexes: {}, classLevelPermissions: { protectedFields: {} }, }, ]), }; const dates = { - createdAt: { iso: undefined, __type: 'Date' }, - updatedAt: { iso: undefined, __type: 'Date' }, + createdAt: { iso: undefined, __type: "Date" }, + updatedAt: { iso: undefined, __type: "Date" }, }; - it('should not transform email to lower case without convertEmailToLowercase option on create', async () => { - const databaseController = new DatabaseController(dummyStorageAdapter, {}); - const spy = spyOn(dummyStorageAdapter, 'createObject'); + it("should not transform email to lower case without convertEmailToLowercase option on create", async () => { + const databaseController = new DatabaseController( + dummyStorageAdapter, + {} + ); + const spy = spyOn(dummyStorageAdapter, "createObject"); spy.and.callThrough(); - await databaseController.create('_User', { - email: 'EXAMPLE@EXAMPLE.COM', + await databaseController.create("_User", { + email: "EXAMPLE@EXAMPLE.COM", }); expect(spy.calls.all()[0].args[2]).toEqual({ - email: 'EXAMPLE@EXAMPLE.COM', + email: "EXAMPLE@EXAMPLE.COM", ...dates, }); }); - it('should transform email to lower case with convertEmailToLowercase option on create', async () => { + it("should transform email to lower case with convertEmailToLowercase option on create", async () => { const databaseController = new DatabaseController(dummyStorageAdapter, { convertEmailToLowercase: true, }); - const spy = spyOn(dummyStorageAdapter, 'createObject'); + const spy = spyOn(dummyStorageAdapter, "createObject"); spy.and.callThrough(); - await databaseController.create('_User', { - email: 'EXAMPLE@EXAMPLE.COM', + await databaseController.create("_User", { + email: "EXAMPLE@EXAMPLE.COM", }); expect(spy.calls.all()[0].args[2]).toEqual({ - email: 'example@example.com', + email: "example@example.com", ...dates, }); }); - it('should not transform email to lower case without convertEmailToLowercase option on update', async () => { - const databaseController = new DatabaseController(dummyStorageAdapter, {}); - const spy = spyOn(dummyStorageAdapter, 'findOneAndUpdate'); + it("should not transform email to lower case without convertEmailToLowercase option on update", async () => { + const databaseController = new DatabaseController( + dummyStorageAdapter, + {} + ); + const spy = spyOn(dummyStorageAdapter, "findOneAndUpdate"); spy.and.callThrough(); - await databaseController.update('_User', { id: 'example' }, { email: 'EXAMPLE@EXAMPLE.COM' }); + await databaseController.update( + "_User", + { id: "example" }, + { email: "EXAMPLE@EXAMPLE.COM" } + ); expect(spy.calls.all()[0].args[3]).toEqual({ - email: 'EXAMPLE@EXAMPLE.COM', + email: "EXAMPLE@EXAMPLE.COM", }); }); - it('should transform email to lower case with convertEmailToLowercase option on update', async () => { + it("should transform email to lower case with convertEmailToLowercase option on update", async () => { const databaseController = new DatabaseController(dummyStorageAdapter, { convertEmailToLowercase: true, }); - const spy = spyOn(dummyStorageAdapter, 'findOneAndUpdate'); + const spy = spyOn(dummyStorageAdapter, "findOneAndUpdate"); spy.and.callThrough(); - await databaseController.update('_User', { id: 'example' }, { email: 'EXAMPLE@EXAMPLE.COM' }); + await databaseController.update( + "_User", + { id: "example" }, + { email: "EXAMPLE@EXAMPLE.COM" } + ); expect(spy.calls.all()[0].args[3]).toEqual({ - email: 'example@example.com', + email: "example@example.com", }); }); - it('should not find a case insensitive user by email with convertEmailToLowercase', async () => { + it("should not find a case insensitive user by email with convertEmailToLowercase", async () => { await reconfigureServer({ convertEmailToLowercase: true }); const user = new Parse.User(); - await user.save({ username: 'EXAMPLE', email: 'EXAMPLE@EXAMPLE.COM', password: 'password' }); + await user.save({ + username: "EXAMPLE", + email: "EXAMPLE@EXAMPLE.COM", + password: "password", + }); const query = new Parse.Query(Parse.User); - query.equalTo('email', 'EXAMPLE@EXAMPLE.COM'); + query.equalTo("email", "EXAMPLE@EXAMPLE.COM"); const result = await query.find({ useMasterKey: true }); expect(result.length).toEqual(0); const query2 = new Parse.Query(Parse.User); - query2.equalTo('email', 'example@example.com'); + query2.equalTo("email", "example@example.com"); const result2 = await query2.find({ useMasterKey: true }); expect(result2.length).toEqual(1); }); }); - describe('convertUsernameToLowercase', () => { + describe("convertUsernameToLowercase", () => { const dummyStorageAdapter = { createObject: () => Promise.resolve({ ops: [{}] }), findOneAndUpdate: () => Promise.resolve({}), @@ -537,80 +604,94 @@ describe('DatabaseController', function () { getAllClasses: () => Promise.resolve([ { - className: '_User', - fields: { username: 'String' }, + className: "_User", + fields: { username: "String" }, indexes: {}, classLevelPermissions: { protectedFields: {} }, }, ]), }; const dates = { - createdAt: { iso: undefined, __type: 'Date' }, - updatedAt: { iso: undefined, __type: 'Date' }, + createdAt: { iso: undefined, __type: "Date" }, + updatedAt: { iso: undefined, __type: "Date" }, }; - it('should not transform username to lower case without convertUsernameToLowercase option on create', async () => { - const databaseController = new DatabaseController(dummyStorageAdapter, {}); - const spy = spyOn(dummyStorageAdapter, 'createObject'); + it("should not transform username to lower case without convertUsernameToLowercase option on create", async () => { + const databaseController = new DatabaseController( + dummyStorageAdapter, + {} + ); + const spy = spyOn(dummyStorageAdapter, "createObject"); spy.and.callThrough(); - await databaseController.create('_User', { - username: 'EXAMPLE', + await databaseController.create("_User", { + username: "EXAMPLE", }); expect(spy.calls.all()[0].args[2]).toEqual({ - username: 'EXAMPLE', + username: "EXAMPLE", ...dates, }); }); - it('should transform username to lower case with convertUsernameToLowercase option on create', async () => { + it("should transform username to lower case with convertUsernameToLowercase option on create", async () => { const databaseController = new DatabaseController(dummyStorageAdapter, { convertUsernameToLowercase: true, }); - const spy = spyOn(dummyStorageAdapter, 'createObject'); + const spy = spyOn(dummyStorageAdapter, "createObject"); spy.and.callThrough(); - await databaseController.create('_User', { - username: 'EXAMPLE', + await databaseController.create("_User", { + username: "EXAMPLE", }); expect(spy.calls.all()[0].args[2]).toEqual({ - username: 'example', + username: "example", ...dates, }); }); - it('should not transform username to lower case without convertUsernameToLowercase option on update', async () => { - const databaseController = new DatabaseController(dummyStorageAdapter, {}); - const spy = spyOn(dummyStorageAdapter, 'findOneAndUpdate'); + it("should not transform username to lower case without convertUsernameToLowercase option on update", async () => { + const databaseController = new DatabaseController( + dummyStorageAdapter, + {} + ); + const spy = spyOn(dummyStorageAdapter, "findOneAndUpdate"); spy.and.callThrough(); - await databaseController.update('_User', { id: 'example' }, { username: 'EXAMPLE' }); + await databaseController.update( + "_User", + { id: "example" }, + { username: "EXAMPLE" } + ); expect(spy.calls.all()[0].args[3]).toEqual({ - username: 'EXAMPLE', + username: "EXAMPLE", }); }); - it('should transform username to lower case with convertUsernameToLowercase option on update', async () => { + it("should transform username to lower case with convertUsernameToLowercase option on update", async () => { const databaseController = new DatabaseController(dummyStorageAdapter, { convertUsernameToLowercase: true, }); - const spy = spyOn(dummyStorageAdapter, 'findOneAndUpdate'); + const spy = spyOn(dummyStorageAdapter, "findOneAndUpdate"); spy.and.callThrough(); - await databaseController.update('_User', { id: 'example' }, { username: 'EXAMPLE' }); + await databaseController.update( + "_User", + { id: "example" }, + { username: "EXAMPLE" } + ); expect(spy.calls.all()[0].args[3]).toEqual({ - username: 'example', + username: "example", }); }); - it('should not find a case insensitive user by username with convertUsernameToLowercase', async () => { + it("should not find a case insensitive user by username with convertUsernameToLowercase", async () => { await reconfigureServer({ convertUsernameToLowercase: true }); const user = new Parse.User(); - await user.save({ username: 'EXAMPLE', password: 'password' }); + await user.save({ username: "EXAMPLE", password: "password" }); const query = new Parse.Query(Parse.User); - query.equalTo('username', 'EXAMPLE'); + query.equalTo("username", "EXAMPLE"); const result = await query.find({ useMasterKey: true }); expect(result.length).toEqual(0); const query2 = new Parse.Query(Parse.User); - query2.equalTo('username', 'example'); + query2.equalTo("username", "example"); const result2 = await query2.find({ useMasterKey: true }); expect(result2.length).toEqual(1); }); @@ -618,7 +699,15 @@ describe('DatabaseController', function () { }); function buildCLP(pointerNames) { - const OPERATIONS = ['count', 'find', 'get', 'create', 'update', 'delete', 'addField']; + const OPERATIONS = [ + "count", + "find", + "get", + "create", + "update", + "delete", + "addField", + ]; const clp = OPERATIONS.reduce((acc, op) => { acc[op] = {}; @@ -639,8 +728,8 @@ function buildCLP(pointerNames) { function createUserPointer(userId) { return { - __type: 'Pointer', - className: '_User', + __type: "Pointer", + className: "_User", objectId: userId, }; } diff --git a/spec/DefinedSchemas.spec.js b/spec/DefinedSchemas.spec.js index 3d6f493e66..fa72cc97a8 100644 --- a/spec/DefinedSchemas.spec.js +++ b/spec/DefinedSchemas.spec.js @@ -1,5 +1,5 @@ -const { DefinedSchemas } = require('../lib/SchemaMigrations/DefinedSchemas'); -const Config = require('../lib/Config'); +const { DefinedSchemas } = require("../lib/SchemaMigrations/DefinedSchemas"); +const Config = require("../lib/Config"); const cleanUpIndexes = schema => { if (schema.indexes) { @@ -10,174 +10,184 @@ const cleanUpIndexes = schema => { } }; -describe('DefinedSchemas', () => { +describe("DefinedSchemas", () => { let config; afterEach(async () => { - config = Config.get('test'); + config = Config.get("test"); if (config) { await config.database.adapter.deleteAllClasses(); } }); - describe('Fields', () => { - it('should keep default fields if not provided', async () => { + describe("Fields", () => { + it("should keep default fields if not provided", async () => { const server = await reconfigureServer(); // Will perform create - await new DefinedSchemas({ definitions: [{ className: 'Test' }] }, server.config).execute(); - let schema = await new Parse.Schema('Test').get(); + await new DefinedSchemas( + { definitions: [{ className: "Test" }] }, + server.config + ).execute(); + let schema = await new Parse.Schema("Test").get(); const expectedFields = { - objectId: { type: 'String' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - ACL: { type: 'ACL' }, + objectId: { type: "String" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + ACL: { type: "ACL" }, }; expect(schema.fields).toEqual(expectedFields); await server.config.schemaCache.clear(); // Will perform update - await new DefinedSchemas({ definitions: [{ className: 'Test' }] }, server.config).execute(); - schema = await new Parse.Schema('Test').get(); + await new DefinedSchemas( + { definitions: [{ className: "Test" }] }, + server.config + ).execute(); + schema = await new Parse.Schema("Test").get(); expect(schema.fields).toEqual(expectedFields); }); - it('should protect default fields', async () => { + it("should protect default fields", async () => { const server = await reconfigureServer(); const schemas = { definitions: [ { - className: '_User', + className: "_User", fields: { - email: 'Object', + email: "Object", }, }, { - className: '_Role', + className: "_Role", fields: { - users: 'Object', + users: "Object", }, }, { - className: '_Installation', + className: "_Installation", fields: { - installationId: 'Object', + installationId: "Object", }, }, { - className: 'Test', + className: "Test", fields: { - createdAt: { type: 'Object' }, - objectId: { type: 'Number' }, - updatedAt: { type: 'String' }, - ACL: { type: 'String' }, + createdAt: { type: "Object" }, + objectId: { type: "Number" }, + updatedAt: { type: "String" }, + ACL: { type: "String" }, }, }, ], }; const expectedFields = { - objectId: { type: 'String' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - ACL: { type: 'ACL' }, + objectId: { type: "String" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + ACL: { type: "ACL" }, }; const expectedUserFields = { - objectId: { type: 'String' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - ACL: { type: 'ACL' }, - username: { type: 'String' }, - password: { type: 'String' }, - email: { type: 'String' }, - emailVerified: { type: 'Boolean' }, - authData: { type: 'Object' }, + objectId: { type: "String" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + ACL: { type: "ACL" }, + username: { type: "String" }, + password: { type: "String" }, + email: { type: "String" }, + emailVerified: { type: "Boolean" }, + authData: { type: "Object" }, }; const expectedRoleFields = { - objectId: { type: 'String' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - ACL: { type: 'ACL' }, - name: { type: 'String' }, - users: { type: 'Relation', targetClass: '_User' }, - roles: { type: 'Relation', targetClass: '_Role' }, + objectId: { type: "String" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + ACL: { type: "ACL" }, + name: { type: "String" }, + users: { type: "Relation", targetClass: "_User" }, + roles: { type: "Relation", targetClass: "_Role" }, }; const expectedInstallationFields = { - objectId: { type: 'String' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - ACL: { type: 'ACL' }, - installationId: { type: 'String' }, - deviceToken: { type: 'String' }, - channels: { type: 'Array' }, - deviceType: { type: 'String' }, - pushType: { type: 'String' }, - GCMSenderId: { type: 'String' }, - timeZone: { type: 'String' }, - localeIdentifier: { type: 'String' }, - badge: { type: 'Number' }, - appVersion: { type: 'String' }, - appName: { type: 'String' }, - appIdentifier: { type: 'String' }, - parseVersion: { type: 'String' }, + objectId: { type: "String" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + ACL: { type: "ACL" }, + installationId: { type: "String" }, + deviceToken: { type: "String" }, + channels: { type: "Array" }, + deviceType: { type: "String" }, + pushType: { type: "String" }, + GCMSenderId: { type: "String" }, + timeZone: { type: "String" }, + localeIdentifier: { type: "String" }, + badge: { type: "Number" }, + appVersion: { type: "String" }, + appName: { type: "String" }, + appIdentifier: { type: "String" }, + parseVersion: { type: "String" }, }; // Perform create await new DefinedSchemas(schemas, server.config).execute(); - let schema = await new Parse.Schema('Test').get(); + let schema = await new Parse.Schema("Test").get(); expect(schema.fields).toEqual(expectedFields); - let userSchema = await new Parse.Schema('_User').get(); + let userSchema = await new Parse.Schema("_User").get(); expect(userSchema.fields).toEqual(expectedUserFields); - let roleSchema = await new Parse.Schema('_Role').get(); + let roleSchema = await new Parse.Schema("_Role").get(); expect(roleSchema.fields).toEqual(expectedRoleFields); - let installationSchema = await new Parse.Schema('_Installation').get(); + let installationSchema = await new Parse.Schema("_Installation").get(); expect(installationSchema.fields).toEqual(expectedInstallationFields); await server.config.schemaCache.clear(); // Perform update await new DefinedSchemas(schemas, server.config).execute(); - schema = await new Parse.Schema('Test').get(); + schema = await new Parse.Schema("Test").get(); expect(schema.fields).toEqual(expectedFields); - userSchema = await new Parse.Schema('_User').get(); + userSchema = await new Parse.Schema("_User").get(); expect(userSchema.fields).toEqual(expectedUserFields); - roleSchema = await new Parse.Schema('_Role').get(); + roleSchema = await new Parse.Schema("_Role").get(); expect(roleSchema.fields).toEqual(expectedRoleFields); - installationSchema = await new Parse.Schema('_Installation').get(); + installationSchema = await new Parse.Schema("_Installation").get(); expect(installationSchema.fields).toEqual(expectedInstallationFields); }); - it('should create new fields', async () => { + it("should create new fields", async () => { const server = await reconfigureServer(); const fields = { - objectId: { type: 'String' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - ACL: { type: 'ACL' }, - aString: { type: 'String' }, - aStringWithDefault: { type: 'String', defaultValue: 'Test' }, - aStringWithRequired: { type: 'String', required: true }, - aStringWithRequiredAndDefault: { type: 'String', required: true, defaultValue: 'Test' }, - aBoolean: { type: 'Boolean' }, - aFile: { type: 'File' }, - aNumber: { type: 'Number' }, - aRelation: { type: 'Relation', targetClass: '_User' }, - aPointer: { type: 'Pointer', targetClass: '_Role' }, - aDate: { type: 'Date' }, - aGeoPoint: { type: 'GeoPoint' }, - aPolygon: { type: 'Polygon' }, - aArray: { type: 'Array' }, - aObject: { type: 'Object' }, + objectId: { type: "String" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + ACL: { type: "ACL" }, + aString: { type: "String" }, + aStringWithDefault: { type: "String", defaultValue: "Test" }, + aStringWithRequired: { type: "String", required: true }, + aStringWithRequiredAndDefault: { + type: "String", + required: true, + defaultValue: "Test", + }, + aBoolean: { type: "Boolean" }, + aFile: { type: "File" }, + aNumber: { type: "Number" }, + aRelation: { type: "Relation", targetClass: "_User" }, + aPointer: { type: "Pointer", targetClass: "_Role" }, + aDate: { type: "Date" }, + aGeoPoint: { type: "GeoPoint" }, + aPolygon: { type: "Polygon" }, + aArray: { type: "Array" }, + aObject: { type: "Object" }, }; const schemas = { definitions: [ { - className: 'Test', + className: "Test", fields, }, ], @@ -185,35 +195,42 @@ describe('DefinedSchemas', () => { // Create await new DefinedSchemas(schemas, server.config).execute(); - let schema = await new Parse.Schema('Test').get(); + let schema = await new Parse.Schema("Test").get(); expect(schema.fields).toEqual(fields); - fields.anotherObject = { type: 'Object' }; + fields.anotherObject = { type: "Object" }; // Update await new DefinedSchemas(schemas, server.config).execute(); - schema = await new Parse.Schema('Test').get(); + schema = await new Parse.Schema("Test").get(); expect(schema.fields).toEqual(fields); }); it('should not delete removed fields when "deleteExtraFields" is false', async () => { const server = await reconfigureServer(); await new DefinedSchemas( - { definitions: [{ className: 'Test', fields: { aField: { type: 'String' } } }] }, + { + definitions: [ + { className: "Test", fields: { aField: { type: "String" } } }, + ], + }, server.config ).execute(); - let schema = await new Parse.Schema('Test').get(); + let schema = await new Parse.Schema("Test").get(); expect(schema.fields.aField).toBeDefined(); - await new DefinedSchemas({ definitions: [{ className: 'Test' }] }, server.config).execute(); + await new DefinedSchemas( + { definitions: [{ className: "Test" }] }, + server.config + ).execute(); - schema = await new Parse.Schema('Test').get(); + schema = await new Parse.Schema("Test").get(); expect(schema.fields).toEqual({ - objectId: { type: 'String' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - aField: { type: 'String' }, - ACL: { type: 'ACL' }, + objectId: { type: "String" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + aField: { type: "String" }, + ACL: { type: "ACL" }, }); }); it('should delete removed fields when "deleteExtraFields" is true', async () => { @@ -221,179 +238,208 @@ describe('DefinedSchemas', () => { await new DefinedSchemas( { - definitions: [{ className: 'Test', fields: { aField: { type: 'String' } } }], + definitions: [ + { className: "Test", fields: { aField: { type: "String" } } }, + ], }, server.config ).execute(); - let schema = await new Parse.Schema('Test').get(); + let schema = await new Parse.Schema("Test").get(); expect(schema.fields.aField).toBeDefined(); await new DefinedSchemas( - { deleteExtraFields: true, definitions: [{ className: 'Test' }] }, + { deleteExtraFields: true, definitions: [{ className: "Test" }] }, server.config ).execute(); - schema = await new Parse.Schema('Test').get(); + schema = await new Parse.Schema("Test").get(); expect(schema.fields).toEqual({ - objectId: { type: 'String' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - ACL: { type: 'ACL' }, + objectId: { type: "String" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + ACL: { type: "ACL" }, }); }); it('should re create fields with changed type when "recreateModifiedFields" is true', async () => { const server = await reconfigureServer(); await new DefinedSchemas( - { definitions: [{ className: 'Test', fields: { aField: { type: 'String' } } }] }, + { + definitions: [ + { className: "Test", fields: { aField: { type: "String" } } }, + ], + }, server.config ).execute(); - let schema = await new Parse.Schema('Test').get(); - expect(schema.fields.aField).toEqual({ type: 'String' }); + let schema = await new Parse.Schema("Test").get(); + expect(schema.fields.aField).toEqual({ type: "String" }); - const object = new Parse.Object('Test'); - await object.save({ aField: 'Hello' }, { useMasterKey: true }); + const object = new Parse.Object("Test"); + await object.save({ aField: "Hello" }, { useMasterKey: true }); await new DefinedSchemas( { recreateModifiedFields: true, - definitions: [{ className: 'Test', fields: { aField: { type: 'Number' } } }], + definitions: [ + { className: "Test", fields: { aField: { type: "Number" } } }, + ], }, server.config ).execute(); - schema = await new Parse.Schema('Test').get(); - expect(schema.fields.aField).toEqual({ type: 'Number' }); + schema = await new Parse.Schema("Test").get(); + expect(schema.fields.aField).toEqual({ type: "Number" }); await object.fetch({ useMasterKey: true }); - expect(object.get('aField')).toBeUndefined(); + expect(object.get("aField")).toBeUndefined(); }); it('should not re create fields with changed type when "recreateModifiedFields" is not true', async () => { const server = await reconfigureServer(); await new DefinedSchemas( - { definitions: [{ className: 'Test', fields: { aField: { type: 'String' } } }] }, + { + definitions: [ + { className: "Test", fields: { aField: { type: "String" } } }, + ], + }, server.config ).execute(); - let schema = await new Parse.Schema('Test').get(); - expect(schema.fields.aField).toEqual({ type: 'String' }); + let schema = await new Parse.Schema("Test").get(); + expect(schema.fields.aField).toEqual({ type: "String" }); - const object = new Parse.Object('Test'); - await object.save({ aField: 'Hello' }, { useMasterKey: true }); + const object = new Parse.Object("Test"); + await object.save({ aField: "Hello" }, { useMasterKey: true }); await new DefinedSchemas( - { definitions: [{ className: 'Test', fields: { aField: { type: 'Number' } } }] }, + { + definitions: [ + { className: "Test", fields: { aField: { type: "Number" } } }, + ], + }, server.config ).execute(); - schema = await new Parse.Schema('Test').get(); - expect(schema.fields.aField).toEqual({ type: 'String' }); + schema = await new Parse.Schema("Test").get(); + expect(schema.fields.aField).toEqual({ type: "String" }); await object.fetch({ useMasterKey: true }); - expect(object.get('aField')).toBeDefined(); + expect(object.get("aField")).toBeDefined(); }); - it('should just update classic fields with changed params', async () => { + it("should just update classic fields with changed params", async () => { const server = await reconfigureServer(); await new DefinedSchemas( - { definitions: [{ className: 'Test', fields: { aField: { type: 'String' } } }] }, + { + definitions: [ + { className: "Test", fields: { aField: { type: "String" } } }, + ], + }, server.config ).execute(); - let schema = await new Parse.Schema('Test').get(); - expect(schema.fields.aField).toEqual({ type: 'String' }); + let schema = await new Parse.Schema("Test").get(); + expect(schema.fields.aField).toEqual({ type: "String" }); - const object = new Parse.Object('Test'); - await object.save({ aField: 'Hello' }, { useMasterKey: true }); + const object = new Parse.Object("Test"); + await object.save({ aField: "Hello" }, { useMasterKey: true }); await new DefinedSchemas( { definitions: [ - { className: 'Test', fields: { aField: { type: 'String', required: true } } }, + { + className: "Test", + fields: { aField: { type: "String", required: true } }, + }, ], }, server.config ).execute(); - schema = await new Parse.Schema('Test').get(); - expect(schema.fields.aField).toEqual({ type: 'String', required: true }); + schema = await new Parse.Schema("Test").get(); + expect(schema.fields.aField).toEqual({ type: "String", required: true }); await object.fetch({ useMasterKey: true }); - expect(object.get('aField')).toEqual('Hello'); + expect(object.get("aField")).toEqual("Hello"); }); }); - describe('Indexes', () => { - it('should create new indexes', async () => { + describe("Indexes", () => { + it("should create new indexes", async () => { const server = await reconfigureServer(); const indexes = { complex: { createdAt: 1, updatedAt: 1 } }; const schemas = { - definitions: [{ className: 'Test', fields: { aField: { type: 'String' } }, indexes }], + definitions: [ + { + className: "Test", + fields: { aField: { type: "String" } }, + indexes, + }, + ], }; await new DefinedSchemas(schemas, server.config).execute(); - let schema = await new Parse.Schema('Test').get(); + let schema = await new Parse.Schema("Test").get(); cleanUpIndexes(schema); expect(schema.indexes).toEqual(indexes); indexes.complex2 = { createdAt: 1, aField: 1 }; await new DefinedSchemas(schemas, server.config).execute(); - schema = await new Parse.Schema('Test').get(); + schema = await new Parse.Schema("Test").get(); cleanUpIndexes(schema); expect(schema.indexes).toEqual(indexes); }); - it('should re create changed indexes', async () => { + it("should re create changed indexes", async () => { const server = await reconfigureServer(); let indexes = { complex: { createdAt: 1, updatedAt: 1 } }; - let schemas = { definitions: [{ className: 'Test', indexes }] }; + let schemas = { definitions: [{ className: "Test", indexes }] }; await new DefinedSchemas(schemas, server.config).execute(); indexes = { complex: { createdAt: 1 } }; - schemas = { definitions: [{ className: 'Test', indexes }] }; + schemas = { definitions: [{ className: "Test", indexes }] }; // Change indexes await new DefinedSchemas(schemas, server.config).execute(); - let schema = await new Parse.Schema('Test').get(); + let schema = await new Parse.Schema("Test").get(); cleanUpIndexes(schema); expect(schema.indexes).toEqual(indexes); // Update await new DefinedSchemas(schemas, server.config).execute(); - schema = await new Parse.Schema('Test').get(); + schema = await new Parse.Schema("Test").get(); cleanUpIndexes(schema); expect(schema.indexes).toEqual(indexes); }); - it('should delete removed indexes', async () => { + it("should delete removed indexes", async () => { const server = await reconfigureServer(); let indexes = { complex: { createdAt: 1, updatedAt: 1 } }; - let schemas = { definitions: [{ className: 'Test', indexes }] }; + let schemas = { definitions: [{ className: "Test", indexes }] }; await new DefinedSchemas(schemas, server.config).execute(); indexes = {}; - schemas = { definitions: [{ className: 'Test', indexes }] }; + schemas = { definitions: [{ className: "Test", indexes }] }; // Change indexes await new DefinedSchemas(schemas, server.config).execute(); - let schema = await new Parse.Schema('Test').get(); + let schema = await new Parse.Schema("Test").get(); cleanUpIndexes(schema); expect(schema.indexes).toBeUndefined(); // Update await new DefinedSchemas(schemas, server.config).execute(); - schema = await new Parse.Schema('Test').get(); + schema = await new Parse.Schema("Test").get(); cleanUpIndexes(schema); expect(schema.indexes).toBeUndefined(); }); - xit('should keep protected indexes', async () => { + xit("should keep protected indexes", async () => { const server = await reconfigureServer(); const expectedIndexes = { @@ -405,19 +451,19 @@ describe('DefinedSchemas', () => { const schemas = { definitions: [ { - className: '_User', + className: "_User", indexes: { case_insensitive_username: { password: true }, case_insensitive_email: { password: true }, }, }, - { className: 'Test' }, + { className: "Test" }, ], }; // Create await new DefinedSchemas(schemas, server.config).execute(); - let userSchema = await new Parse.Schema('_User').get(); - let testSchema = await new Parse.Schema('Test').get(); + let userSchema = await new Parse.Schema("_User").get(); + let testSchema = await new Parse.Schema("Test").get(); cleanUpIndexes(userSchema); cleanUpIndexes(testSchema); expect(testSchema.indexes).toBeUndefined(); @@ -425,55 +471,68 @@ describe('DefinedSchemas', () => { // Update await new DefinedSchemas(schemas, server.config).execute(); - userSchema = await new Parse.Schema('_User').get(); - testSchema = await new Parse.Schema('Test').get(); + userSchema = await new Parse.Schema("_User").get(); + testSchema = await new Parse.Schema("Test").get(); cleanUpIndexes(userSchema); cleanUpIndexes(testSchema); expect(testSchema.indexes).toBeUndefined(); expect(userSchema.indexes).toEqual(expectedIndexes); }); - it('should detect protected indexes for _User class', () => { + it("should detect protected indexes for _User class", () => { const definedSchema = new DefinedSchemas({}, {}); - const protectedUserIndexes = ['_id_', 'case_insensitive_email', 'username_1', 'email_1']; + const protectedUserIndexes = [ + "_id_", + "case_insensitive_email", + "username_1", + "email_1", + ]; protectedUserIndexes.forEach(field => { - expect(definedSchema.isProtectedIndex('_User', field)).toEqual(true); + expect(definedSchema.isProtectedIndex("_User", field)).toEqual(true); }); - expect(definedSchema.isProtectedIndex('_User', 'test')).toEqual(false); + expect(definedSchema.isProtectedIndex("_User", "test")).toEqual(false); }); - it('should detect protected indexes for _Role class', () => { + it("should detect protected indexes for _Role class", () => { const definedSchema = new DefinedSchemas({}, {}); - expect(definedSchema.isProtectedIndex('_Role', 'name_1')).toEqual(true); - expect(definedSchema.isProtectedIndex('_Role', 'test')).toEqual(false); + expect(definedSchema.isProtectedIndex("_Role", "name_1")).toEqual(true); + expect(definedSchema.isProtectedIndex("_Role", "test")).toEqual(false); }); - it('should detect protected indexes for _Idempotency class', () => { + it("should detect protected indexes for _Idempotency class", () => { const definedSchema = new DefinedSchemas({}, {}); - expect(definedSchema.isProtectedIndex('_Idempotency', 'reqId_1')).toEqual(true); - expect(definedSchema.isProtectedIndex('_Idempotency', 'test')).toEqual(false); + expect(definedSchema.isProtectedIndex("_Idempotency", "reqId_1")).toEqual( + true + ); + expect(definedSchema.isProtectedIndex("_Idempotency", "test")).toEqual( + false + ); }); - it('should not detect protected indexes on user defined class', () => { + it("should not detect protected indexes on user defined class", () => { const definedSchema = new DefinedSchemas({}, {}); const protectedIndexes = [ - 'case_insensitive_email', - 'username_1', - 'email_1', - 'reqId_1', - 'name_1', + "case_insensitive_email", + "username_1", + "email_1", + "reqId_1", + "name_1", ]; protectedIndexes.forEach(field => { - expect(definedSchema.isProtectedIndex('ExampleClass', field)).toEqual(false); + expect(definedSchema.isProtectedIndex("ExampleClass", field)).toEqual( + false + ); }); - expect(definedSchema.isProtectedIndex('ExampleClass', '_id_')).toEqual(true); + expect(definedSchema.isProtectedIndex("ExampleClass", "_id_")).toEqual( + true + ); }); }); - describe('ClassLevelPermissions', () => { - it('should use default CLP', async () => { + describe("ClassLevelPermissions", () => { + it("should use default CLP", async () => { const server = await reconfigureServer(); - const schemas = { definitions: [{ className: 'Test' }] }; + const schemas = { definitions: [{ className: "Test" }] }; await new DefinedSchemas(schemas, server.config).execute(); const expectedTestCLP = { @@ -486,51 +545,59 @@ describe('DefinedSchemas', () => { addField: {}, protectedFields: {}, }; - let testSchema = await new Parse.Schema('Test').get(); + let testSchema = await new Parse.Schema("Test").get(); expect(testSchema.classLevelPermissions).toEqual(expectedTestCLP); await new DefinedSchemas(schemas, server.config).execute(); - testSchema = await new Parse.Schema('Test').get(); + testSchema = await new Parse.Schema("Test").get(); expect(testSchema.classLevelPermissions).toEqual(expectedTestCLP); }); - it('should save CLP', async () => { + it("should save CLP", async () => { const server = await reconfigureServer(); const expectedTestCLP = { find: {}, count: { requiresAuthentication: true }, - get: { 'role:Admin': true }, - create: { 'role:ARole': true, requiresAuthentication: true }, + get: { "role:Admin": true }, + create: { "role:ARole": true, requiresAuthentication: true }, update: { requiresAuthentication: true }, delete: { requiresAuthentication: true }, addField: {}, - protectedFields: { '*': ['aField'], 'role:Admin': ['anotherField'] }, + protectedFields: { "*": ["aField"], "role:Admin": ["anotherField"] }, }; const schemas = { definitions: [ { - className: 'Test', - fields: { aField: { type: 'String' }, anotherField: { type: 'Object' } }, + className: "Test", + fields: { + aField: { type: "String" }, + anotherField: { type: "Object" }, + }, classLevelPermissions: expectedTestCLP, }, ], }; await new DefinedSchemas(schemas, server.config).execute(); - let testSchema = await new Parse.Schema('Test').get(); + let testSchema = await new Parse.Schema("Test").get(); expect(testSchema.classLevelPermissions).toEqual(expectedTestCLP); expectedTestCLP.update = {}; expectedTestCLP.create = { requiresAuthentication: true }; await new DefinedSchemas(schemas, server.config).execute(); - testSchema = await new Parse.Schema('Test').get(); + testSchema = await new Parse.Schema("Test").get(); expect(testSchema.classLevelPermissions).toEqual(expectedTestCLP); }); - it('should force addField to empty', async () => { + it("should force addField to empty", async () => { const server = await reconfigureServer(); const schemas = { - definitions: [{ className: 'Test', classLevelPermissions: { addField: { '*': true } } }], + definitions: [ + { + className: "Test", + classLevelPermissions: { addField: { "*": true } }, + }, + ], }; await new DefinedSchemas(schemas, server.config).execute(); @@ -545,115 +612,131 @@ describe('DefinedSchemas', () => { protectedFields: {}, }; - let testSchema = await new Parse.Schema('Test').get(); + let testSchema = await new Parse.Schema("Test").get(); expect(testSchema.classLevelPermissions).toEqual(expectedTestCLP); await new DefinedSchemas(schemas, server.config).execute(); - testSchema = await new Parse.Schema('Test').get(); + testSchema = await new Parse.Schema("Test").get(); expect(testSchema.classLevelPermissions).toEqual(expectedTestCLP); }); }); - it('should not delete classes automatically', async () => { + it("should not delete classes automatically", async () => { await reconfigureServer({ - schema: { definitions: [{ className: '_User' }, { className: 'Test' }] }, + schema: { definitions: [{ className: "_User" }, { className: "Test" }] }, }); - await reconfigureServer({ schema: { definitions: [{ className: '_User' }] } }); + await reconfigureServer({ + schema: { definitions: [{ className: "_User" }] }, + }); - const schema = await new Parse.Schema('Test').get(); - expect(schema.className).toEqual('Test'); + const schema = await new Parse.Schema("Test").get(); + expect(schema.className).toEqual("Test"); }); - it('should disable class PUT/POST endpoint when lockSchemas provided to avoid dual source of truth', async () => { + it("should disable class PUT/POST endpoint when lockSchemas provided to avoid dual source of truth", async () => { await reconfigureServer({ schema: { lockSchemas: true, - definitions: [{ className: '_User' }, { className: 'Test' }], + definitions: [{ className: "_User" }, { className: "Test" }], }, }); - const schema = await new Parse.Schema('Test').get(); - expect(schema.className).toEqual('Test'); + const schema = await new Parse.Schema("Test").get(); + expect(schema.className).toEqual("Test"); const schemas = await Parse.Schema.all(); // Role could be flaky since all system classes are not ensured // at start up by the DefinedSchema system - expect(schemas.filter(({ className }) => className !== '_Role').length).toEqual(3); - - await expectAsync(new Parse.Schema('TheNewTest').save()).toBeRejectedWithError( - 'Cannot perform this operation when schemas options is used.' + expect( + schemas.filter(({ className }) => className !== "_Role").length + ).toEqual(3); + + await expectAsync( + new Parse.Schema("TheNewTest").save() + ).toBeRejectedWithError( + "Cannot perform this operation when schemas options is used." ); - await expectAsync(new Parse.Schema('_User').update()).toBeRejectedWithError( - 'Cannot perform this operation when schemas options is used.' + await expectAsync(new Parse.Schema("_User").update()).toBeRejectedWithError( + "Cannot perform this operation when schemas options is used." ); }); - it('should only enable delete class endpoint since', async () => { + it("should only enable delete class endpoint since", async () => { await reconfigureServer({ - schema: { definitions: [{ className: '_User' }, { className: 'Test' }] }, + schema: { definitions: [{ className: "_User" }, { className: "Test" }] }, + }); + await reconfigureServer({ + schema: { definitions: [{ className: "_User" }] }, }); - await reconfigureServer({ schema: { definitions: [{ className: '_User' }] } }); let schemas = await Parse.Schema.all(); expect(schemas.length).toEqual(4); - await new Parse.Schema('_User').delete(); + await new Parse.Schema("_User").delete(); schemas = await Parse.Schema.all(); expect(schemas.length).toEqual(3); }); - it('should run beforeMigration before execution of DefinedSchemas', async () => { + it("should run beforeMigration before execution of DefinedSchemas", async () => { const config = { schema: { - definitions: [{ className: '_User' }, { className: 'Test' }], + definitions: [{ className: "_User" }, { className: "Test" }], beforeMigration: async () => {}, }, }; - const spy = spyOn(config.schema, 'beforeMigration'); + const spy = spyOn(config.schema, "beforeMigration"); await reconfigureServer(config); expect(spy).toHaveBeenCalledTimes(1); }); - it('should run afterMigration after execution of DefinedSchemas', async () => { + it("should run afterMigration after execution of DefinedSchemas", async () => { const config = { schema: { - definitions: [{ className: '_User' }, { className: 'Test' }], + definitions: [{ className: "_User" }, { className: "Test" }], afterMigration: async () => {}, }, }; - const spy = spyOn(config.schema, 'afterMigration'); + const spy = spyOn(config.schema, "afterMigration"); await reconfigureServer(config); expect(spy).toHaveBeenCalledTimes(1); }); - it('should use logger in case of error', async () => { - const server = await reconfigureServer({ schema: { definitions: [{ className: '_User' }] } }); - const error = new Error('A test error'); - const logger = require('../lib/logger').logger; - spyOn(DefinedSchemas.prototype, 'wait').and.resolveTo(); - spyOn(logger, 'error').and.callThrough(); - spyOn(DefinedSchemas.prototype, 'createDeleteSession').and.callFake(() => { + it("should use logger in case of error", async () => { + const server = await reconfigureServer({ + schema: { definitions: [{ className: "_User" }] }, + }); + const error = new Error("A test error"); + const logger = require("../lib/logger").logger; + spyOn(DefinedSchemas.prototype, "wait").and.resolveTo(); + spyOn(logger, "error").and.callThrough(); + spyOn(DefinedSchemas.prototype, "createDeleteSession").and.callFake(() => { throw error; }); await new DefinedSchemas( - { definitions: [{ className: 'Test', fields: { aField: { type: 'String' } } }] }, + { + definitions: [ + { className: "Test", fields: { aField: { type: "String" } } }, + ], + }, server.config ).execute(); - expect(logger.error).toHaveBeenCalledWith(`Failed to run migrations: ${error.toString()}`); + expect(logger.error).toHaveBeenCalledWith( + `Failed to run migrations: ${error.toString()}` + ); }); - it_id('a18bf4f2-25c8-4de3-b986-19cb1ab163b8')(it)( - 'should perform migration in parallel without failing', + it_id("a18bf4f2-25c8-4de3-b986-19cb1ab163b8")(it)( + "should perform migration in parallel without failing", async () => { const server = await reconfigureServer(); - const logger = require('../lib/logger').logger; - spyOn(logger, 'error').and.callThrough(); + const logger = require("../lib/logger").logger; + spyOn(logger, "error").and.callThrough(); const migrationOptions = { definitions: [ { - className: 'Test', - fields: { aField: { type: 'String' } }, + className: "Test", + fields: { aField: { type: "String" } }, indexes: { aField: { aField: 1 } }, classLevelPermissions: { create: { requiresAuthentication: true }, @@ -672,25 +755,28 @@ describe('DefinedSchemas', () => { ]); const testSchema = (await Parse.Schema.all()).find( - ({ className }) => className === migrationOptions.definitions[0].className + ({ className }) => + className === migrationOptions.definitions[0].className ); expect(testSchema.indexes.aField).toEqual({ aField: 1 }); - expect(testSchema.fields.aField).toEqual({ type: 'String' }); - expect(testSchema.classLevelPermissions.create).toEqual({ requiresAuthentication: true }); + expect(testSchema.fields.aField).toEqual({ type: "String" }); + expect(testSchema.classLevelPermissions.create).toEqual({ + requiresAuthentication: true, + }); expect(logger.error).toHaveBeenCalledTimes(0); } ); - it('should not affect cacheAdapter', async () => { + it("should not affect cacheAdapter", async () => { const server = await reconfigureServer(); - const logger = require('../lib/logger').logger; - spyOn(logger, 'error').and.callThrough(); + const logger = require("../lib/logger").logger; + spyOn(logger, "error").and.callThrough(); const migrationOptions = { definitions: [ { - className: 'Test', - fields: { aField: { type: 'String' } }, + className: "Test", + fields: { aField: { type: "String" } }, indexes: { aField: { aField: 1 } }, classLevelPermissions: { create: { requiresAuthentication: true }, @@ -704,7 +790,7 @@ describe('DefinedSchemas', () => { put: () => {}, del: () => {}, clear: () => {}, - connect: jasmine.createSpy('clear'), + connect: jasmine.createSpy("clear"), }; server.config.cacheAdapter = cacheAdapter; await new DefinedSchemas(migrationOptions, server.config).execute(); diff --git a/spec/Deprecator.spec.js b/spec/Deprecator.spec.js index 3af5d10c31..261ba6fc18 100644 --- a/spec/Deprecator.spec.js +++ b/spec/Deprecator.spec.js @@ -1,24 +1,28 @@ -'use strict'; +"use strict"; -const Deprecator = require('../lib/Deprecator/Deprecator'); +const Deprecator = require("../lib/Deprecator/Deprecator"); -describe('Deprecator', () => { +describe("Deprecator", () => { let deprecations = []; beforeEach(async () => { - deprecations = [{ optionKey: 'exampleKey', changeNewDefault: 'exampleNewDefault' }]; + deprecations = [ + { optionKey: "exampleKey", changeNewDefault: "exampleNewDefault" }, + ]; }); - it('deprecations are an array', async () => { + it("deprecations are an array", async () => { expect(Deprecator._getDeprecations()).toBeInstanceOf(Array); }); - it('logs deprecation for new default', async () => { - deprecations = [{ optionKey: 'exampleKey', changeNewDefault: 'exampleNewDefault' }]; + it("logs deprecation for new default", async () => { + deprecations = [ + { optionKey: "exampleKey", changeNewDefault: "exampleNewDefault" }, + ]; - spyOn(Deprecator, '_getDeprecations').and.callFake(() => deprecations); - const logger = require('../lib/logger').logger; - const logSpy = spyOn(logger, 'warn').and.callFake(() => {}); + spyOn(Deprecator, "_getDeprecations").and.callFake(() => deprecations); + const logger = require("../lib/logger").logger; + const logSpy = spyOn(logger, "warn").and.callFake(() => {}); await reconfigureServer(); expect(logSpy.calls.all()[0].args[0]).toEqual( @@ -26,19 +30,21 @@ describe('Deprecator', () => { ); }); - it('does not log deprecation for new default if option is set manually', async () => { - deprecations = [{ optionKey: 'exampleKey', changeNewDefault: 'exampleNewDefault' }]; + it("does not log deprecation for new default if option is set manually", async () => { + deprecations = [ + { optionKey: "exampleKey", changeNewDefault: "exampleNewDefault" }, + ]; - spyOn(Deprecator, '_getDeprecations').and.callFake(() => deprecations); - const logSpy = spyOn(Deprecator, '_logOption').and.callFake(() => {}); - await reconfigureServer({ [deprecations[0].optionKey]: 'manuallySet' }); + spyOn(Deprecator, "_getDeprecations").and.callFake(() => deprecations); + const logSpy = spyOn(Deprecator, "_logOption").and.callFake(() => {}); + await reconfigureServer({ [deprecations[0].optionKey]: "manuallySet" }); expect(logSpy).not.toHaveBeenCalled(); }); - it('logs runtime deprecation', async () => { - const logger = require('../lib/logger').logger; - const logSpy = spyOn(logger, 'warn').and.callFake(() => {}); - const options = { usage: 'Doing this', solution: 'Do that instead.' }; + it("logs runtime deprecation", async () => { + const logger = require("../lib/logger").logger; + const logSpy = spyOn(logger, "warn").and.callFake(() => {}); + const options = { usage: "Doing this", solution: "Do that instead." }; Deprecator.logRuntimeDeprecation(options); expect(logSpy.calls.all()[0].args[0]).toEqual( diff --git a/spec/EmailVerificationToken.spec.js b/spec/EmailVerificationToken.spec.js index 6dd0a01966..bd95688edf 100644 --- a/spec/EmailVerificationToken.spec.js +++ b/spec/EmailVerificationToken.spec.js @@ -1,13 +1,13 @@ -'use strict'; +"use strict"; -const Auth = require('../lib/Auth'); -const Config = require('../lib/Config'); -const request = require('../lib/request'); -const { resolvingPromise, sleep } = require('../lib/TestUtils'); -const MockEmailAdapterWithOptions = require('./support/MockEmailAdapterWithOptions'); +const Auth = require("../lib/Auth"); +const Config = require("../lib/Config"); +const request = require("../lib/request"); +const { resolvingPromise, sleep } = require("../lib/TestUtils"); +const MockEmailAdapterWithOptions = require("./support/MockEmailAdapterWithOptions"); -describe('Email Verification Token Expiration:', () => { - it('show the invalid verification link page, if the user clicks on the verify email link after the email verify token expires', async () => { +describe("Email Verification Token Expiration:", () => { + it("show the invalid verification link page, if the user clicks on the verify email link after the email verify token expires", async () => { const user = new Parse.User(); let sendEmailOptions; const sendPromise = resolvingPromise(); @@ -20,15 +20,15 @@ describe('Email Verification Token Expiration:', () => { sendMail: () => {}, }; await reconfigureServer({ - appName: 'emailVerifyToken', + appName: "emailVerifyToken", verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 0.5, // 0.5 second - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }); - user.setUsername('testEmailVerifyTokenValidity'); - user.setPassword('expiringToken'); - user.set('email', 'user@parse.com'); + user.setUsername("testEmailVerifyTokenValidity"); + user.setPassword("expiringToken"); + user.set("email", "user@parse.com"); await user.signUp(); await sendPromise; // wait for 1 second - simulate user behavior to some extent @@ -42,13 +42,13 @@ describe('Email Verification Token Expiration:', () => { }); expect(response.status).toEqual(302); const url = new URL(sendEmailOptions.link); - const token = url.searchParams.get('token'); + const token = url.searchParams.get("token"); expect(response.text).toEqual( `Found. Redirecting to http://localhost:8378/1/apps/invalid_verification_link.html?appId=test&token=${token}` ); }); - it('emailVerified should set to false, if the user does not verify their email before the email verify token expires', async () => { + it("emailVerified should set to false, if the user does not verify their email before the email verify token expires", async () => { const user = new Parse.User(); let sendEmailOptions; const sendPromise = resolvingPromise(); @@ -61,15 +61,15 @@ describe('Email Verification Token Expiration:', () => { sendMail: () => {}, }; await reconfigureServer({ - appName: 'emailVerifyToken', + appName: "emailVerifyToken", verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 0.5, // 0.5 second - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }); - user.setUsername('testEmailVerifyTokenValidity'); - user.setPassword('expiringToken'); - user.set('email', 'user@parse.com'); + user.setUsername("testEmailVerifyTokenValidity"); + user.setPassword("expiringToken"); + user.set("email", "user@parse.com"); await user.signUp(); await sendPromise; // wait for 1 second - simulate user behavior to some extent @@ -83,153 +83,168 @@ describe('Email Verification Token Expiration:', () => { }); expect(response.status).toEqual(302); await user.fetch(); - expect(user.get('emailVerified')).toEqual(false); + expect(user.get("emailVerified")).toEqual(false); }); - it_id('f20dd3c2-87d9-4bc6-a51d-4ea2834acbcc')(it)('if user clicks on the email verify link before email verification token expiration then show the verify email success page', async () => { - const user = new Parse.User(); - let sendEmailOptions; - const sendPromise = resolvingPromise(); - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - sendPromise.resolve(); - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - await reconfigureServer({ - appName: 'emailVerifyToken', - verifyUserEmails: true, - emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', - }); - user.setUsername('testEmailVerifyTokenValidity'); - user.setPassword('expiringToken'); - user.set('email', 'user@parse.com'); - await user.signUp(); - await sendPromise; - const response = await request({ - url: sendEmailOptions.link, - followRedirects: false, - }); - expect(response.status).toEqual(302); - expect(response.text).toEqual( - 'Found. Redirecting to http://localhost:8378/1/apps/verify_email_success.html' - ); - }); + it_id("f20dd3c2-87d9-4bc6-a51d-4ea2834acbcc")(it)( + "if user clicks on the email verify link before email verification token expiration then show the verify email success page", + async () => { + const user = new Parse.User(); + let sendEmailOptions; + const sendPromise = resolvingPromise(); + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + sendPromise.resolve(); + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + await reconfigureServer({ + appName: "emailVerifyToken", + verifyUserEmails: true, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 5, // 5 seconds + publicServerURL: "http://localhost:8378/1", + }); + user.setUsername("testEmailVerifyTokenValidity"); + user.setPassword("expiringToken"); + user.set("email", "user@parse.com"); + await user.signUp(); + await sendPromise; + const response = await request({ + url: sendEmailOptions.link, + followRedirects: false, + }); + expect(response.status).toEqual(302); + expect(response.text).toEqual( + "Found. Redirecting to http://localhost:8378/1/apps/verify_email_success.html" + ); + } + ); - it_id('94956799-c85e-4297-b879-e2d1f985394c')(it)('if user clicks on the email verify link before email verification token expiration then emailVerified should be true', async () => { - const user = new Parse.User(); - let sendEmailOptions; - const sendPromise = resolvingPromise(); - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - sendPromise.resolve(); - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - await reconfigureServer({ - appName: 'emailVerifyToken', - verifyUserEmails: true, - emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', - }); - user.setUsername('testEmailVerifyTokenValidity'); - user.setPassword('expiringToken'); - user.set('email', 'user@parse.com'); - await user.signUp(); - await sendPromise; - const response = await request({ - url: sendEmailOptions.link, - followRedirects: false, - }); - expect(response.status).toEqual(302); - await user.fetch(); - expect(user.get('emailVerified')).toEqual(true); - }); + it_id("94956799-c85e-4297-b879-e2d1f985394c")(it)( + "if user clicks on the email verify link before email verification token expiration then emailVerified should be true", + async () => { + const user = new Parse.User(); + let sendEmailOptions; + const sendPromise = resolvingPromise(); + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + sendPromise.resolve(); + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + await reconfigureServer({ + appName: "emailVerifyToken", + verifyUserEmails: true, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 5, // 5 seconds + publicServerURL: "http://localhost:8378/1", + }); + user.setUsername("testEmailVerifyTokenValidity"); + user.setPassword("expiringToken"); + user.set("email", "user@parse.com"); + await user.signUp(); + await sendPromise; + const response = await request({ + url: sendEmailOptions.link, + followRedirects: false, + }); + expect(response.status).toEqual(302); + await user.fetch(); + expect(user.get("emailVerified")).toEqual(true); + } + ); - it_id('25f3f895-c987-431c-9841-17cb6aaf18b5')(it)('if user clicks on the email verify link before email verification token expiration then user should be able to login', async () => { - const user = new Parse.User(); - let sendEmailOptions; - const sendPromise = resolvingPromise(); - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - sendPromise.resolve(); - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - await reconfigureServer({ - appName: 'emailVerifyToken', - verifyUserEmails: true, - emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', - }); - user.setUsername('testEmailVerifyTokenValidity'); - user.setPassword('expiringToken'); - user.set('email', 'user@parse.com'); - await user.signUp(); - await sendPromise; - const response = await request({ - url: sendEmailOptions.link, - followRedirects: false, - }); - expect(response.status).toEqual(302); - const verifiedUser = await Parse.User.logIn('testEmailVerifyTokenValidity', 'expiringToken'); - expect(typeof verifiedUser).toBe('object'); - expect(verifiedUser.get('emailVerified')).toBe(true); - }); + it_id("25f3f895-c987-431c-9841-17cb6aaf18b5")(it)( + "if user clicks on the email verify link before email verification token expiration then user should be able to login", + async () => { + const user = new Parse.User(); + let sendEmailOptions; + const sendPromise = resolvingPromise(); + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + sendPromise.resolve(); + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + await reconfigureServer({ + appName: "emailVerifyToken", + verifyUserEmails: true, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 5, // 5 seconds + publicServerURL: "http://localhost:8378/1", + }); + user.setUsername("testEmailVerifyTokenValidity"); + user.setPassword("expiringToken"); + user.set("email", "user@parse.com"); + await user.signUp(); + await sendPromise; + const response = await request({ + url: sendEmailOptions.link, + followRedirects: false, + }); + expect(response.status).toEqual(302); + const verifiedUser = await Parse.User.logIn( + "testEmailVerifyTokenValidity", + "expiringToken" + ); + expect(typeof verifiedUser).toBe("object"); + expect(verifiedUser.get("emailVerified")).toBe(true); + } + ); - it_id('c6a3e188-9065-4f50-842d-454d1e82f289')(it)('sets the _email_verify_token_expires_at and _email_verify_token fields after user SignUp', async () => { - const user = new Parse.User(); - let sendEmailOptions; - const sendPromise = resolvingPromise(); - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - sendPromise.resolve(); - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - await reconfigureServer({ - appName: 'emailVerifyToken', - verifyUserEmails: true, - emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', - }); - user.setUsername('sets_email_verify_token_expires_at'); - user.setPassword('expiringToken'); - user.set('email', 'user@parse.com'); - await user.signUp(); - await sendPromise; - const config = Config.get('test'); - const results = await config.database.find( - '_User', - { - username: 'sets_email_verify_token_expires_at', - }, - {}, - Auth.maintenance(config) - ); - expect(results.length).toBe(1); - const verifiedUser = results[0]; - expect(typeof verifiedUser).toBe('object'); - expect(verifiedUser.emailVerified).toEqual(false); - expect(typeof verifiedUser._email_verify_token).toBe('string'); - expect(typeof verifiedUser._email_verify_token_expires_at).toBe('object'); - expect(sendEmailOptions).toBeDefined(); - }); + it_id("c6a3e188-9065-4f50-842d-454d1e82f289")(it)( + "sets the _email_verify_token_expires_at and _email_verify_token fields after user SignUp", + async () => { + const user = new Parse.User(); + let sendEmailOptions; + const sendPromise = resolvingPromise(); + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + sendPromise.resolve(); + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + await reconfigureServer({ + appName: "emailVerifyToken", + verifyUserEmails: true, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 5, // 5 seconds + publicServerURL: "http://localhost:8378/1", + }); + user.setUsername("sets_email_verify_token_expires_at"); + user.setPassword("expiringToken"); + user.set("email", "user@parse.com"); + await user.signUp(); + await sendPromise; + const config = Config.get("test"); + const results = await config.database.find( + "_User", + { + username: "sets_email_verify_token_expires_at", + }, + {}, + Auth.maintenance(config) + ); + expect(results.length).toBe(1); + const verifiedUser = results[0]; + expect(typeof verifiedUser).toBe("object"); + expect(verifiedUser.emailVerified).toEqual(false); + expect(typeof verifiedUser._email_verify_token).toBe("string"); + expect(typeof verifiedUser._email_verify_token_expires_at).toBe("object"); + expect(sendEmailOptions).toBeDefined(); + } + ); - it('can resend email using an expired token', async () => { + it("can resend email using an expired token", async () => { const user = new Parse.User(); const emailAdapter = { sendVerificationEmail: () => {}, @@ -237,27 +252,27 @@ describe('Email Verification Token Expiration:', () => { sendMail: () => {}, }; await reconfigureServer({ - appName: 'emailVerifyToken', + appName: "emailVerifyToken", verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }); - user.setUsername('test'); - user.setPassword('password'); - user.set('email', 'user@example.com'); + user.setUsername("test"); + user.setPassword("password"); + user.set("email", "user@example.com"); await user.signUp(); await Parse.Server.database.update( - '_User', + "_User", { objectId: user.id }, { - _email_verify_token_expires_at: Parse._encode(new Date('2000')), + _email_verify_token_expires_at: Parse._encode(new Date("2000")), } ); const obj = await Parse.Server.database.find( - '_User', + "_User", { objectId: user.id }, {}, Auth.maintenance(Parse.Server) @@ -266,340 +281,384 @@ describe('Email Verification Token Expiration:', () => { const res = await request({ url: `http://localhost:8378/1/apps/test/verify_email?token=${token}`, - method: 'GET', + method: "GET", }); expect(res.text).toEqual( - `Found. Redirecting to http://localhost:8378/1/apps/invalid_verification_link.html?appId=test&token=${token}` - ); - - const formUrl = `http://localhost:8378/1/apps/test/resend_verification_email`; - const formResponse = await request({ - url: formUrl, - method: 'POST', - body: { - token: token, - }, - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - followRedirects: false, - }); - expect(formResponse.text).toEqual( - `Found. Redirecting to http://localhost:8378/1/apps/link_send_success.html` - ); - }); - - it_id('9365c53c-b8b4-41f7-a3c1-77882f76a89c')(it)('can conditionally send emails', async () => { - let sendEmailOptions; - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - const verifyUserEmails = { - method(req) { - expect(Object.keys(req)).toEqual(['original', 'object', 'master', 'ip', 'installationId']); - return false; - }, - }; - const verifySpy = spyOn(verifyUserEmails, 'method').and.callThrough(); - await reconfigureServer({ - appName: 'emailVerifyToken', - verifyUserEmails: verifyUserEmails.method, - emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', - }); - const beforeSave = { - method(req) { - req.object.set('emailVerified', true); - }, - }; - const saveSpy = spyOn(beforeSave, 'method').and.callThrough(); - const emailSpy = spyOn(emailAdapter, 'sendVerificationEmail').and.callThrough(); - Parse.Cloud.beforeSave(Parse.User, beforeSave.method); - const user = new Parse.User(); - user.setUsername('sets_email_verify_token_expires_at'); - user.setPassword('expiringToken'); - user.set('email', 'user@example.com'); - await user.signUp(); - - const config = Config.get('test'); - const results = await config.database.find( - '_User', - { - username: 'sets_email_verify_token_expires_at', - }, - {}, - Auth.maintenance(config) - ); - - expect(results.length).toBe(1); - const user_data = results[0]; - expect(typeof user_data).toBe('object'); - expect(user_data.emailVerified).toEqual(true); - expect(user_data._email_verify_token).toBeUndefined(); - expect(user_data._email_verify_token_expires_at).toBeUndefined(); - expect(emailSpy).not.toHaveBeenCalled(); - expect(saveSpy).toHaveBeenCalled(); - expect(sendEmailOptions).toBeUndefined(); - expect(verifySpy).toHaveBeenCalled(); - }); - - it_id('b3549300-bed7-4a5e-bed5-792dbfead960')(it)('can conditionally send emails and allow conditional login', async () => { - let sendEmailOptions; - const sendPromise = resolvingPromise(); - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - sendPromise.resolve(); - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - const verifyUserEmails = { - method(req) { - expect(Object.keys(req)).toEqual(['original', 'object', 'master', 'ip', 'installationId']); - if (req.object.get('username') === 'no_email') { - return false; - } - return true; - }, - }; - const verifySpy = spyOn(verifyUserEmails, 'method').and.callThrough(); - await reconfigureServer({ - appName: 'emailVerifyToken', - verifyUserEmails: verifyUserEmails.method, - preventLoginWithUnverifiedEmail: verifyUserEmails.method, - emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', - }); - const user = new Parse.User(); - user.setUsername('no_email'); - user.setPassword('expiringToken'); - user.set('email', 'user@example.com'); - await user.signUp(); - expect(sendEmailOptions).toBeUndefined(); - expect(user.getSessionToken()).toBeDefined(); - expect(verifySpy).toHaveBeenCalledTimes(2); - const user2 = new Parse.User(); - user2.setUsername('email'); - user2.setPassword('expiringToken'); - user2.set('email', 'user2@example.com'); - await user2.signUp(); - await sendPromise; - expect(user2.getSessionToken()).toBeUndefined(); - expect(sendEmailOptions).toBeDefined(); - expect(verifySpy).toHaveBeenCalledTimes(5); - }); - - it_id('d812de87-33d1-495e-a6e8-3485f6dc3589')(it)('can conditionally send user email verification', async () => { - const emailAdapter = { - sendVerificationEmail: () => {}, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - const sendVerificationEmail = { - method(req) { - expect(req.user).toBeDefined(); - expect(req.master).toBeDefined(); - return false; - }, - }; - const sendSpy = spyOn(sendVerificationEmail, 'method').and.callThrough(); - await reconfigureServer({ - appName: 'emailVerifyToken', - verifyUserEmails: true, - emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', - sendUserEmailVerification: sendVerificationEmail.method, - }); - const emailSpy = spyOn(emailAdapter, 'sendVerificationEmail').and.callThrough(); - const newUser = new Parse.User(); - newUser.setUsername('unsets_email_verify_token_expires_at'); - newUser.setPassword('expiringToken'); - newUser.set('email', 'user@example.com'); - await newUser.signUp(); - await Parse.User.requestEmailVerification('user@example.com'); - await sleep(100); - expect(sendSpy).toHaveBeenCalledTimes(2); - expect(emailSpy).toHaveBeenCalledTimes(0); - }); - - it_id('d98babc1-feb8-4b5e-916c-57dc0a6ed9fb')(it)('provides full user object in email verification function on email and username change', async () => { - const emailAdapter = { - sendVerificationEmail: () => {}, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - const sendVerificationEmail = { - method(req) { - expect(req.user).toBeDefined(); - expect(req.user.id).toBeDefined(); - expect(req.user.get('createdAt')).toBeDefined(); - expect(req.user.get('updatedAt')).toBeDefined(); - expect(req.master).toBeDefined(); - return false; - }, - }; - await reconfigureServer({ - appName: 'emailVerifyToken', - verifyUserEmails: true, - emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 5, - publicServerURL: 'http://localhost:8378/1', - sendUserEmailVerification: sendVerificationEmail.method, - }); - const user = new Parse.User(); - user.setPassword('password'); - user.setUsername('new@example.com'); - user.setEmail('user@example.com'); - await user.save(null, { useMasterKey: true }); - - // Update email and username - user.setUsername('new@example.com'); - user.setEmail('new@example.com'); - await user.save(null, { useMasterKey: true }); - }); - - it_id('a8c1f820-822f-4a37-9d08-a968cac8369d')(it)('beforeSave options do not change existing behaviour', async () => { - let sendEmailOptions; - const sendPromise = resolvingPromise(); - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - sendPromise.resolve(); - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - await reconfigureServer({ - appName: 'emailVerifyToken', - verifyUserEmails: true, - emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', - }); - const emailSpy = spyOn(emailAdapter, 'sendVerificationEmail').and.callThrough(); - const newUser = new Parse.User(); - newUser.setUsername('unsets_email_verify_token_expires_at'); - newUser.setPassword('expiringToken'); - newUser.set('email', 'user@parse.com'); - await newUser.signUp(); - await sendPromise; - const response = await request({ - url: sendEmailOptions.link, - followRedirects: false, - }); - expect(response.status).toEqual(302); - const config = Config.get('test'); - const results = await config.database.find('_User', { - username: 'unsets_email_verify_token_expires_at', - }); - - expect(results.length).toBe(1); - const user = results[0]; - expect(typeof user).toBe('object'); - expect(user.emailVerified).toEqual(true); - expect(typeof user._email_verify_token).toBe('undefined'); - expect(typeof user._email_verify_token_expires_at).toBe('undefined'); - expect(emailSpy).toHaveBeenCalled(); - }); - - it_id('36d277eb-ec7c-4a39-9108-435b68228741')(it)('unsets the _email_verify_token_expires_at and _email_verify_token fields in the User class if email verification is successful', async () => { - const user = new Parse.User(); - let sendEmailOptions; - const sendPromise = resolvingPromise(); - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - sendPromise.resolve(); - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - await reconfigureServer({ - appName: 'emailVerifyToken', - verifyUserEmails: true, - emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', - }); - user.setUsername('unsets_email_verify_token_expires_at'); - user.setPassword('expiringToken'); - user.set('email', 'user@parse.com'); - await user.signUp(); - await sendPromise; - const response = await request({ - url: sendEmailOptions.link, - followRedirects: false, - }); - expect(response.status).toEqual(302); - const config = Config.get('test'); - const results = await config.database.find('_User', { - username: 'unsets_email_verify_token_expires_at', - }); - expect(results.length).toBe(1); - const verifiedUser = results[0]; - - expect(typeof verifiedUser).toBe('object'); - expect(verifiedUser.emailVerified).toEqual(true); - expect(typeof verifiedUser._email_verify_token).toBe('undefined'); - expect(typeof verifiedUser._email_verify_token_expires_at).toBe('undefined'); - }); - - it_id('4f444704-ec4b-4dff-b947-614b1c6971c4')(it)('clicking on the email verify link by an email VERIFIED user that was setup before enabling the expire email verify token should show email verify email success', async () => { - const user = new Parse.User(); - let sendEmailOptions; - const sendPromise = resolvingPromise(); - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - sendPromise.resolve(); - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - const serverConfig = { - appName: 'emailVerifyToken', - verifyUserEmails: true, - emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', - }; - - // setup server WITHOUT enabling the expire email verify token flag - await reconfigureServer(serverConfig); - user.setUsername('testEmailVerifyTokenValidity'); - user.setPassword('expiringToken'); - user.set('email', 'user@parse.com'); - await user.signUp(); - await sendPromise; - let response = await request({ - url: sendEmailOptions.link, - followRedirects: false, - }); - expect(response.status).toEqual(302); - await user.fetch(); - expect(user.get('emailVerified')).toEqual(true); - // RECONFIGURE the server i.e., ENABLE the expire email verify token flag - serverConfig.emailVerifyTokenValidityDuration = 5; // 5 seconds - await reconfigureServer(serverConfig); + `Found. Redirecting to http://localhost:8378/1/apps/invalid_verification_link.html?appId=test&token=${token}` + ); - response = await request({ - url: sendEmailOptions.link, + const formUrl = `http://localhost:8378/1/apps/test/resend_verification_email`; + const formResponse = await request({ + url: formUrl, + method: "POST", + body: { + token: token, + }, + headers: { "Content-Type": "application/x-www-form-urlencoded" }, followRedirects: false, }); - expect(response.status).toEqual(302); - const url = new URL(sendEmailOptions.link); - const token = url.searchParams.get('token'); - expect(response.text).toEqual( - `Found. Redirecting to http://localhost:8378/1/apps/invalid_verification_link.html?appId=test&token=${token}` + expect(formResponse.text).toEqual( + `Found. Redirecting to http://localhost:8378/1/apps/link_send_success.html` ); }); - it('clicking on the email verify link by an email UNVERIFIED user that was setup before enabling the expire email verify token should show invalid verficiation link page', async () => { + it_id("9365c53c-b8b4-41f7-a3c1-77882f76a89c")(it)( + "can conditionally send emails", + async () => { + let sendEmailOptions; + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + const verifyUserEmails = { + method(req) { + expect(Object.keys(req)).toEqual([ + "original", + "object", + "master", + "ip", + "installationId", + ]); + return false; + }, + }; + const verifySpy = spyOn(verifyUserEmails, "method").and.callThrough(); + await reconfigureServer({ + appName: "emailVerifyToken", + verifyUserEmails: verifyUserEmails.method, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 5, // 5 seconds + publicServerURL: "http://localhost:8378/1", + }); + const beforeSave = { + method(req) { + req.object.set("emailVerified", true); + }, + }; + const saveSpy = spyOn(beforeSave, "method").and.callThrough(); + const emailSpy = spyOn( + emailAdapter, + "sendVerificationEmail" + ).and.callThrough(); + Parse.Cloud.beforeSave(Parse.User, beforeSave.method); + const user = new Parse.User(); + user.setUsername("sets_email_verify_token_expires_at"); + user.setPassword("expiringToken"); + user.set("email", "user@example.com"); + await user.signUp(); + + const config = Config.get("test"); + const results = await config.database.find( + "_User", + { + username: "sets_email_verify_token_expires_at", + }, + {}, + Auth.maintenance(config) + ); + + expect(results.length).toBe(1); + const user_data = results[0]; + expect(typeof user_data).toBe("object"); + expect(user_data.emailVerified).toEqual(true); + expect(user_data._email_verify_token).toBeUndefined(); + expect(user_data._email_verify_token_expires_at).toBeUndefined(); + expect(emailSpy).not.toHaveBeenCalled(); + expect(saveSpy).toHaveBeenCalled(); + expect(sendEmailOptions).toBeUndefined(); + expect(verifySpy).toHaveBeenCalled(); + } + ); + + it_id("b3549300-bed7-4a5e-bed5-792dbfead960")(it)( + "can conditionally send emails and allow conditional login", + async () => { + let sendEmailOptions; + const sendPromise = resolvingPromise(); + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + sendPromise.resolve(); + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + const verifyUserEmails = { + method(req) { + expect(Object.keys(req)).toEqual([ + "original", + "object", + "master", + "ip", + "installationId", + ]); + if (req.object.get("username") === "no_email") { + return false; + } + return true; + }, + }; + const verifySpy = spyOn(verifyUserEmails, "method").and.callThrough(); + await reconfigureServer({ + appName: "emailVerifyToken", + verifyUserEmails: verifyUserEmails.method, + preventLoginWithUnverifiedEmail: verifyUserEmails.method, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 5, // 5 seconds + publicServerURL: "http://localhost:8378/1", + }); + const user = new Parse.User(); + user.setUsername("no_email"); + user.setPassword("expiringToken"); + user.set("email", "user@example.com"); + await user.signUp(); + expect(sendEmailOptions).toBeUndefined(); + expect(user.getSessionToken()).toBeDefined(); + expect(verifySpy).toHaveBeenCalledTimes(2); + const user2 = new Parse.User(); + user2.setUsername("email"); + user2.setPassword("expiringToken"); + user2.set("email", "user2@example.com"); + await user2.signUp(); + await sendPromise; + expect(user2.getSessionToken()).toBeUndefined(); + expect(sendEmailOptions).toBeDefined(); + expect(verifySpy).toHaveBeenCalledTimes(5); + } + ); + + it_id("d812de87-33d1-495e-a6e8-3485f6dc3589")(it)( + "can conditionally send user email verification", + async () => { + const emailAdapter = { + sendVerificationEmail: () => {}, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + const sendVerificationEmail = { + method(req) { + expect(req.user).toBeDefined(); + expect(req.master).toBeDefined(); + return false; + }, + }; + const sendSpy = spyOn(sendVerificationEmail, "method").and.callThrough(); + await reconfigureServer({ + appName: "emailVerifyToken", + verifyUserEmails: true, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 5, // 5 seconds + publicServerURL: "http://localhost:8378/1", + sendUserEmailVerification: sendVerificationEmail.method, + }); + const emailSpy = spyOn( + emailAdapter, + "sendVerificationEmail" + ).and.callThrough(); + const newUser = new Parse.User(); + newUser.setUsername("unsets_email_verify_token_expires_at"); + newUser.setPassword("expiringToken"); + newUser.set("email", "user@example.com"); + await newUser.signUp(); + await Parse.User.requestEmailVerification("user@example.com"); + await sleep(100); + expect(sendSpy).toHaveBeenCalledTimes(2); + expect(emailSpy).toHaveBeenCalledTimes(0); + } + ); + + it_id("d98babc1-feb8-4b5e-916c-57dc0a6ed9fb")(it)( + "provides full user object in email verification function on email and username change", + async () => { + const emailAdapter = { + sendVerificationEmail: () => {}, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + const sendVerificationEmail = { + method(req) { + expect(req.user).toBeDefined(); + expect(req.user.id).toBeDefined(); + expect(req.user.get("createdAt")).toBeDefined(); + expect(req.user.get("updatedAt")).toBeDefined(); + expect(req.master).toBeDefined(); + return false; + }, + }; + await reconfigureServer({ + appName: "emailVerifyToken", + verifyUserEmails: true, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 5, + publicServerURL: "http://localhost:8378/1", + sendUserEmailVerification: sendVerificationEmail.method, + }); + const user = new Parse.User(); + user.setPassword("password"); + user.setUsername("new@example.com"); + user.setEmail("user@example.com"); + await user.save(null, { useMasterKey: true }); + + // Update email and username + user.setUsername("new@example.com"); + user.setEmail("new@example.com"); + await user.save(null, { useMasterKey: true }); + } + ); + + it_id("a8c1f820-822f-4a37-9d08-a968cac8369d")(it)( + "beforeSave options do not change existing behaviour", + async () => { + let sendEmailOptions; + const sendPromise = resolvingPromise(); + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + sendPromise.resolve(); + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + await reconfigureServer({ + appName: "emailVerifyToken", + verifyUserEmails: true, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 5, // 5 seconds + publicServerURL: "http://localhost:8378/1", + }); + const emailSpy = spyOn( + emailAdapter, + "sendVerificationEmail" + ).and.callThrough(); + const newUser = new Parse.User(); + newUser.setUsername("unsets_email_verify_token_expires_at"); + newUser.setPassword("expiringToken"); + newUser.set("email", "user@parse.com"); + await newUser.signUp(); + await sendPromise; + const response = await request({ + url: sendEmailOptions.link, + followRedirects: false, + }); + expect(response.status).toEqual(302); + const config = Config.get("test"); + const results = await config.database.find("_User", { + username: "unsets_email_verify_token_expires_at", + }); + + expect(results.length).toBe(1); + const user = results[0]; + expect(typeof user).toBe("object"); + expect(user.emailVerified).toEqual(true); + expect(typeof user._email_verify_token).toBe("undefined"); + expect(typeof user._email_verify_token_expires_at).toBe("undefined"); + expect(emailSpy).toHaveBeenCalled(); + } + ); + + it_id("36d277eb-ec7c-4a39-9108-435b68228741")(it)( + "unsets the _email_verify_token_expires_at and _email_verify_token fields in the User class if email verification is successful", + async () => { + const user = new Parse.User(); + let sendEmailOptions; + const sendPromise = resolvingPromise(); + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + sendPromise.resolve(); + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + await reconfigureServer({ + appName: "emailVerifyToken", + verifyUserEmails: true, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 5, // 5 seconds + publicServerURL: "http://localhost:8378/1", + }); + user.setUsername("unsets_email_verify_token_expires_at"); + user.setPassword("expiringToken"); + user.set("email", "user@parse.com"); + await user.signUp(); + await sendPromise; + const response = await request({ + url: sendEmailOptions.link, + followRedirects: false, + }); + expect(response.status).toEqual(302); + const config = Config.get("test"); + const results = await config.database.find("_User", { + username: "unsets_email_verify_token_expires_at", + }); + expect(results.length).toBe(1); + const verifiedUser = results[0]; + + expect(typeof verifiedUser).toBe("object"); + expect(verifiedUser.emailVerified).toEqual(true); + expect(typeof verifiedUser._email_verify_token).toBe("undefined"); + expect(typeof verifiedUser._email_verify_token_expires_at).toBe( + "undefined" + ); + } + ); + + it_id("4f444704-ec4b-4dff-b947-614b1c6971c4")(it)( + "clicking on the email verify link by an email VERIFIED user that was setup before enabling the expire email verify token should show email verify email success", + async () => { + const user = new Parse.User(); + let sendEmailOptions; + const sendPromise = resolvingPromise(); + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + sendPromise.resolve(); + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + const serverConfig = { + appName: "emailVerifyToken", + verifyUserEmails: true, + emailAdapter: emailAdapter, + publicServerURL: "http://localhost:8378/1", + }; + + // setup server WITHOUT enabling the expire email verify token flag + await reconfigureServer(serverConfig); + user.setUsername("testEmailVerifyTokenValidity"); + user.setPassword("expiringToken"); + user.set("email", "user@parse.com"); + await user.signUp(); + await sendPromise; + let response = await request({ + url: sendEmailOptions.link, + followRedirects: false, + }); + expect(response.status).toEqual(302); + await user.fetch(); + expect(user.get("emailVerified")).toEqual(true); + // RECONFIGURE the server i.e., ENABLE the expire email verify token flag + serverConfig.emailVerifyTokenValidityDuration = 5; // 5 seconds + await reconfigureServer(serverConfig); + + response = await request({ + url: sendEmailOptions.link, + followRedirects: false, + }); + expect(response.status).toEqual(302); + const url = new URL(sendEmailOptions.link); + const token = url.searchParams.get("token"); + expect(response.text).toEqual( + `Found. Redirecting to http://localhost:8378/1/apps/invalid_verification_link.html?appId=test&token=${token}` + ); + } + ); + + it("clicking on the email verify link by an email UNVERIFIED user that was setup before enabling the expire email verify token should show invalid verficiation link page", async () => { const user = new Parse.User(); let sendEmailOptions; const sendPromise = resolvingPromise(); @@ -612,23 +671,23 @@ describe('Email Verification Token Expiration:', () => { sendMail: () => {}, }; const serverConfig = { - appName: 'emailVerifyToken', + appName: "emailVerifyToken", verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }; // setup server WITHOUT enabling the expire email verify token flag await reconfigureServer(serverConfig); - user.setUsername('testEmailVerifyTokenValidity'); - user.setPassword('expiringToken'); - user.set('email', 'user@parse.com'); + user.setUsername("testEmailVerifyTokenValidity"); + user.setPassword("expiringToken"); + user.set("email", "user@parse.com"); await user.signUp(); await sendPromise; // just get the user again - DO NOT email verify the user await user.fetch(); - expect(user.get('emailVerified')).toEqual(false); + expect(user.get("emailVerified")).toEqual(false); // RECONFIGURE the server i.e., ENABLE the expire email verify token flag serverConfig.emailVerifyTokenValidityDuration = 5; // 5 seconds await reconfigureServer(serverConfig); @@ -639,161 +698,172 @@ describe('Email Verification Token Expiration:', () => { }); expect(response.status).toEqual(302); const url = new URL(sendEmailOptions.link); - const token = url.searchParams.get('token'); + const token = url.searchParams.get("token"); expect(response.text).toEqual( `Found. Redirecting to http://localhost:8378/1/apps/invalid_verification_link.html?appId=test&token=${token}` ); }); - it_id('b6c87f35-d887-477d-bc86-a9217a424f53')(it)('setting the email on the user should set a new email verification token and new expiration date for the token when expire email verify token flag is set', async () => { - const user = new Parse.User(); - let userBeforeEmailReset; + it_id("b6c87f35-d887-477d-bc86-a9217a424f53")(it)( + "setting the email on the user should set a new email verification token and new expiration date for the token when expire email verify token flag is set", + async () => { + const user = new Parse.User(); + let userBeforeEmailReset; - let sendEmailOptions; - const sendPromise = resolvingPromise(); - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - sendPromise.resolve(); - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - const serverConfig = { - appName: 'emailVerifyToken', - verifyUserEmails: true, - emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', - }; + let sendEmailOptions; + const sendPromise = resolvingPromise(); + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + sendPromise.resolve(); + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + const serverConfig = { + appName: "emailVerifyToken", + verifyUserEmails: true, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 5, // 5 seconds + publicServerURL: "http://localhost:8378/1", + }; - await reconfigureServer(serverConfig); - user.setUsername('newEmailVerifyTokenOnEmailReset'); - user.setPassword('expiringToken'); - user.set('email', 'user@parse.com'); - await user.signUp(); - await sendPromise; - const config = Config.get('test'); - const userFromDb = await config.database - .find('_User', { username: 'newEmailVerifyTokenOnEmailReset' }) - .then(results => { - return results[0]; - }); - expect(typeof userFromDb).toBe('object'); - userBeforeEmailReset = userFromDb; + await reconfigureServer(serverConfig); + user.setUsername("newEmailVerifyTokenOnEmailReset"); + user.setPassword("expiringToken"); + user.set("email", "user@parse.com"); + await user.signUp(); + await sendPromise; + const config = Config.get("test"); + const userFromDb = await config.database + .find("_User", { username: "newEmailVerifyTokenOnEmailReset" }) + .then(results => { + return results[0]; + }); + expect(typeof userFromDb).toBe("object"); + userBeforeEmailReset = userFromDb; - // trigger another token generation by setting the email - user.set('email', 'user@parse.com'); - await new Promise(resolve => { - // wait for half a sec to get a new expiration time - setTimeout(() => resolve(user.save()), 500); - }); - const userAfterEmailReset = await config.database - .find( - '_User', - { username: 'newEmailVerifyTokenOnEmailReset' }, - {}, - Auth.maintenance(config) - ) - .then(results => { - return results[0]; + // trigger another token generation by setting the email + user.set("email", "user@parse.com"); + await new Promise(resolve => { + // wait for half a sec to get a new expiration time + setTimeout(() => resolve(user.save()), 500); }); + const userAfterEmailReset = await config.database + .find( + "_User", + { username: "newEmailVerifyTokenOnEmailReset" }, + {}, + Auth.maintenance(config) + ) + .then(results => { + return results[0]; + }); - expect(typeof userAfterEmailReset).toBe('object'); - expect(userBeforeEmailReset._email_verify_token).not.toEqual( - userAfterEmailReset._email_verify_token - ); - expect(userBeforeEmailReset._email_verify_token_expires_at).not.toEqual( - userAfterEmailReset._email_verify_token_expires_at - ); - expect(sendEmailOptions).toBeDefined(); - }); + expect(typeof userAfterEmailReset).toBe("object"); + expect(userBeforeEmailReset._email_verify_token).not.toEqual( + userAfterEmailReset._email_verify_token + ); + expect(userBeforeEmailReset._email_verify_token_expires_at).not.toEqual( + userAfterEmailReset._email_verify_token_expires_at + ); + expect(sendEmailOptions).toBeDefined(); + } + ); - it_id('28f2140d-48bd-44ac-a141-ba60ea8d9713')(it)('should send a new verification email when a resend is requested and the user is UNVERIFIED', async () => { - const user = new Parse.User(); - let sendEmailOptions; - let sendVerificationEmailCallCount = 0; - let userBeforeRequest; - const promise1 = resolvingPromise(); - const promise2 = resolvingPromise(); - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - sendVerificationEmailCallCount++; - if (sendVerificationEmailCallCount === 1) { - promise1.resolve(); - } else { - promise2.resolve(); - } - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - await reconfigureServer({ - appName: 'emailVerifyToken', - verifyUserEmails: true, - emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', - }); - user.setUsername('resends_verification_token'); - user.setPassword('expiringToken'); - user.set('email', 'user@parse.com'); - await user.signUp(); - await promise1; - const config = Config.get('test'); - const newUser = await config.database - .find('_User', { username: 'resends_verification_token' }) - .then(results => { - return results[0]; + it_id("28f2140d-48bd-44ac-a141-ba60ea8d9713")(it)( + "should send a new verification email when a resend is requested and the user is UNVERIFIED", + async () => { + const user = new Parse.User(); + let sendEmailOptions; + let sendVerificationEmailCallCount = 0; + let userBeforeRequest; + const promise1 = resolvingPromise(); + const promise2 = resolvingPromise(); + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + sendVerificationEmailCallCount++; + if (sendVerificationEmailCallCount === 1) { + promise1.resolve(); + } else { + promise2.resolve(); + } + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + await reconfigureServer({ + appName: "emailVerifyToken", + verifyUserEmails: true, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 5, // 5 seconds + publicServerURL: "http://localhost:8378/1", }); - // store this user before we make our email request - userBeforeRequest = newUser; + user.setUsername("resends_verification_token"); + user.setPassword("expiringToken"); + user.set("email", "user@parse.com"); + await user.signUp(); + await promise1; + const config = Config.get("test"); + const newUser = await config.database + .find("_User", { username: "resends_verification_token" }) + .then(results => { + return results[0]; + }); + // store this user before we make our email request + userBeforeRequest = newUser; - expect(sendVerificationEmailCallCount).toBe(1); - - const response = await request({ - url: 'http://localhost:8378/1/verificationEmailRequest', - method: 'POST', - body: { - email: 'user@parse.com', - }, - headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', - }, - }); - expect(response.status).toBe(200); - await promise2; - expect(sendVerificationEmailCallCount).toBe(2); - expect(sendEmailOptions).toBeDefined(); + expect(sendVerificationEmailCallCount).toBe(1); - // query for this user again - const userAfterRequest = await config.database - .find('_User', { username: 'resends_verification_token' }, {}, Auth.maintenance(config)) - .then(results => { - return results[0]; + const response = await request({ + url: "http://localhost:8378/1/verificationEmailRequest", + method: "POST", + body: { + email: "user@parse.com", + }, + headers: { + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", + }, }); - // verify that our token & expiration has been changed for this new request - expect(typeof userAfterRequest).toBe('object'); - expect(userBeforeRequest._email_verify_token).not.toEqual( - userAfterRequest._email_verify_token - ); - expect(userBeforeRequest._email_verify_token_expires_at).not.toEqual( - userAfterRequest._email_verify_token_expires_at - ); - }); + expect(response.status).toBe(200); + await promise2; + expect(sendVerificationEmailCallCount).toBe(2); + expect(sendEmailOptions).toBeDefined(); + + // query for this user again + const userAfterRequest = await config.database + .find( + "_User", + { username: "resends_verification_token" }, + {}, + Auth.maintenance(config) + ) + .then(results => { + return results[0]; + }); + // verify that our token & expiration has been changed for this new request + expect(typeof userAfterRequest).toBe("object"); + expect(userBeforeRequest._email_verify_token).not.toEqual( + userAfterRequest._email_verify_token + ); + expect(userBeforeRequest._email_verify_token_expires_at).not.toEqual( + userAfterRequest._email_verify_token_expires_at + ); + } + ); - it('provides function arguments in verifyUserEmails on verificationEmailRequest', async () => { + it("provides function arguments in verifyUserEmails on verificationEmailRequest", async () => { const user = new Parse.User(); - user.setUsername('user'); - user.setPassword('pass'); - user.set('email', 'test@example.com'); + user.setUsername("user"); + user.setPassword("pass"); + user.set("email", "test@example.com"); await user.signUp(); const verifyUserEmails = { - method: async (params) => { + method: async params => { expect(params.object).toBeInstanceOf(Parse.User); expect(params.ip).toBeDefined(); expect(params.master).toBeDefined(); @@ -802,25 +872,30 @@ describe('Email Verification Token Expiration:', () => { return true; }, }; - const verifyUserEmailsSpy = spyOn(verifyUserEmails, 'method').and.callThrough(); + const verifyUserEmailsSpy = spyOn( + verifyUserEmails, + "method" + ).and.callThrough(); await reconfigureServer({ - appName: 'test', - publicServerURL: 'http://localhost:1337/1', + appName: "test", + publicServerURL: "http://localhost:1337/1", verifyUserEmails: verifyUserEmails.method, preventLoginWithUnverifiedEmail: verifyUserEmails.method, preventSignupWithUnverifiedEmail: true, emailAdapter: MockEmailAdapterWithOptions({ - fromAddress: 'parse@example.com', - apiKey: 'k', - domain: 'd', + fromAddress: "parse@example.com", + apiKey: "k", + domain: "d", }), }); - await expectAsync(Parse.User.requestEmailVerification('test@example.com')).toBeResolved(); + await expectAsync( + Parse.User.requestEmailVerification("test@example.com") + ).toBeResolved(); expect(verifyUserEmailsSpy).toHaveBeenCalledTimes(1); }); - it('should throw with invalid emailVerifyTokenReuseIfValid', async () => { + it("should throw with invalid emailVerifyTokenReuseIfValid", async () => { const sendEmailOptions = []; const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), @@ -831,155 +906,175 @@ describe('Email Verification Token Expiration:', () => { }; try { await reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 5 * 60, // 5 minutes emailVerifyTokenReuseIfValid: [], - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }); - fail('should have thrown.'); + fail("should have thrown."); } catch (e) { - expect(e).toBe('emailVerifyTokenReuseIfValid must be a boolean value'); + expect(e).toBe("emailVerifyTokenReuseIfValid must be a boolean value"); } try { await reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenReuseIfValid: true, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }); - fail('should have thrown.'); + fail("should have thrown."); } catch (e) { expect(e).toBe( - 'You cannot use emailVerifyTokenReuseIfValid without emailVerifyTokenValidityDuration' + "You cannot use emailVerifyTokenReuseIfValid without emailVerifyTokenValidityDuration" ); } }); - it_id('0e66b7f6-2c07-4117-a8b9-605aa31a1e29')(it)('should match codes with emailVerifyTokenReuseIfValid', async () => { - let sendEmailOptions; - let sendVerificationEmailCallCount = 0; - const promise1 = resolvingPromise(); - const promise2 = resolvingPromise(); - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - sendVerificationEmailCallCount++; - if (sendVerificationEmailCallCount === 1) { - promise1.resolve(); - } else { - promise2.resolve(); - } - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - await reconfigureServer({ - appName: 'emailVerifyToken', - verifyUserEmails: true, - emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 5 * 60, // 5 minutes - publicServerURL: 'http://localhost:8378/1', - emailVerifyTokenReuseIfValid: true, - }); - const user = new Parse.User(); - user.setUsername('resends_verification_token'); - user.setPassword('expiringToken'); - user.set('email', 'user@example.com'); - await user.signUp(); - await promise1; - const config = Config.get('test'); - const [userBeforeRequest] = await config.database.find('_User', { - username: 'resends_verification_token', - }, {}, Auth.maintenance(config)); - // store this user before we make our email request - expect(sendVerificationEmailCallCount).toBe(1); - await new Promise(resolve => { - setTimeout(() => { - resolve(); - }, 1000); - }); - const response = await request({ - url: 'http://localhost:8378/1/verificationEmailRequest', - method: 'POST', - body: { - email: 'user@example.com', - }, - headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', - }, - }); - await promise2; - expect(response.status).toBe(200); - expect(sendVerificationEmailCallCount).toBe(2); - expect(sendEmailOptions).toBeDefined(); + it_id("0e66b7f6-2c07-4117-a8b9-605aa31a1e29")(it)( + "should match codes with emailVerifyTokenReuseIfValid", + async () => { + let sendEmailOptions; + let sendVerificationEmailCallCount = 0; + const promise1 = resolvingPromise(); + const promise2 = resolvingPromise(); + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + sendVerificationEmailCallCount++; + if (sendVerificationEmailCallCount === 1) { + promise1.resolve(); + } else { + promise2.resolve(); + } + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + await reconfigureServer({ + appName: "emailVerifyToken", + verifyUserEmails: true, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 5 * 60, // 5 minutes + publicServerURL: "http://localhost:8378/1", + emailVerifyTokenReuseIfValid: true, + }); + const user = new Parse.User(); + user.setUsername("resends_verification_token"); + user.setPassword("expiringToken"); + user.set("email", "user@example.com"); + await user.signUp(); + await promise1; + const config = Config.get("test"); + const [userBeforeRequest] = await config.database.find( + "_User", + { + username: "resends_verification_token", + }, + {}, + Auth.maintenance(config) + ); + // store this user before we make our email request + expect(sendVerificationEmailCallCount).toBe(1); + await new Promise(resolve => { + setTimeout(() => { + resolve(); + }, 1000); + }); + const response = await request({ + url: "http://localhost:8378/1/verificationEmailRequest", + method: "POST", + body: { + email: "user@example.com", + }, + headers: { + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", + }, + }); + await promise2; + expect(response.status).toBe(200); + expect(sendVerificationEmailCallCount).toBe(2); + expect(sendEmailOptions).toBeDefined(); - const [userAfterRequest] = await config.database.find('_User', { - username: 'resends_verification_token', - }, {}, Auth.maintenance(config)); + const [userAfterRequest] = await config.database.find( + "_User", + { + username: "resends_verification_token", + }, + {}, + Auth.maintenance(config) + ); - // Verify that token & expiration haven't been changed for this new request - expect(typeof userAfterRequest).toBe('object'); - expect(userBeforeRequest._email_verify_token).toBeDefined(); - expect(userBeforeRequest._email_verify_token).toEqual(userAfterRequest._email_verify_token); - expect(userBeforeRequest._email_verify_token_expires_at).toBeDefined(); - expect(userBeforeRequest._email_verify_token_expires_at).toEqual(userAfterRequest._email_verify_token_expires_at); - }); + // Verify that token & expiration haven't been changed for this new request + expect(typeof userAfterRequest).toBe("object"); + expect(userBeforeRequest._email_verify_token).toBeDefined(); + expect(userBeforeRequest._email_verify_token).toEqual( + userAfterRequest._email_verify_token + ); + expect(userBeforeRequest._email_verify_token_expires_at).toBeDefined(); + expect(userBeforeRequest._email_verify_token_expires_at).toEqual( + userAfterRequest._email_verify_token_expires_at + ); + } + ); - it_id('1ed9a6c2-bebc-4813-af30-4f4a212544c2')(it)('should not send a new verification email when a resend is requested and the user is VERIFIED', async () => { - const user = new Parse.User(); - let sendEmailOptions; - let sendVerificationEmailCallCount = 0; - const sendPromise = resolvingPromise(); - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - sendVerificationEmailCallCount++; - sendPromise.resolve(); - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - await reconfigureServer({ - appName: 'emailVerifyToken', - verifyUserEmails: true, - emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', - }); - user.setUsername('no_new_verification_token_once_verified'); - user.setPassword('expiringToken'); - user.set('email', 'user@parse.com'); - await user.signUp(); - await sendPromise; - let response = await request({ - url: sendEmailOptions.link, - followRedirects: false, - }); - expect(response.status).toEqual(302); - expect(sendVerificationEmailCallCount).toBe(1); + it_id("1ed9a6c2-bebc-4813-af30-4f4a212544c2")(it)( + "should not send a new verification email when a resend is requested and the user is VERIFIED", + async () => { + const user = new Parse.User(); + let sendEmailOptions; + let sendVerificationEmailCallCount = 0; + const sendPromise = resolvingPromise(); + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + sendVerificationEmailCallCount++; + sendPromise.resolve(); + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + await reconfigureServer({ + appName: "emailVerifyToken", + verifyUserEmails: true, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 5, // 5 seconds + publicServerURL: "http://localhost:8378/1", + }); + user.setUsername("no_new_verification_token_once_verified"); + user.setPassword("expiringToken"); + user.set("email", "user@parse.com"); + await user.signUp(); + await sendPromise; + let response = await request({ + url: sendEmailOptions.link, + followRedirects: false, + }); + expect(response.status).toEqual(302); + expect(sendVerificationEmailCallCount).toBe(1); - response = await request({ - url: 'http://localhost:8378/1/verificationEmailRequest', - method: 'POST', - body: { - email: 'user@parse.com', - }, - headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', - }, - }).then(fail, res => res); - expect(response.status).toBe(400); - expect(sendVerificationEmailCallCount).toBe(1); - }); + response = await request({ + url: "http://localhost:8378/1/verificationEmailRequest", + method: "POST", + body: { + email: "user@parse.com", + }, + headers: { + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", + }, + }).then(fail, res => res); + expect(response.status).toBe(400); + expect(sendVerificationEmailCallCount).toBe(1); + } + ); - it('should not send a new verification email if this user does not exist', async () => { + it("should not send a new verification email if this user does not exist", async () => { let sendEmailOptions; let sendVerificationEmailCallCount = 0; const emailAdapter = { @@ -991,33 +1086,33 @@ describe('Email Verification Token Expiration:', () => { sendMail: () => {}, }; await reconfigureServer({ - appName: 'emailVerifyToken', + appName: "emailVerifyToken", verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }); const response = await request({ - url: 'http://localhost:8378/1/verificationEmailRequest', - method: 'POST', + url: "http://localhost:8378/1/verificationEmailRequest", + method: "POST", body: { - email: 'user@parse.com', + email: "user@parse.com", }, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }, }) .then(fail) .catch(response => response); - + expect(response.status).toBe(400); expect(sendVerificationEmailCallCount).toBe(0); expect(sendEmailOptions).not.toBeDefined(); }); - it('should fail if no email is supplied', async () => { + it("should fail if no email is supplied", async () => { let sendEmailOptions; let sendVerificationEmailCallCount = 0; const emailAdapter = { @@ -1029,30 +1124,30 @@ describe('Email Verification Token Expiration:', () => { sendMail: () => {}, }; await reconfigureServer({ - appName: 'emailVerifyToken', + appName: "emailVerifyToken", verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }); const response = await request({ - url: 'http://localhost:8378/1/verificationEmailRequest', - method: 'POST', + url: "http://localhost:8378/1/verificationEmailRequest", + method: "POST", body: {}, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }, }).then(fail, response => response); expect(response.status).toBe(400); expect(response.data.code).toBe(Parse.Error.EMAIL_MISSING); - expect(response.data.error).toBe('you must provide an email'); + expect(response.data.error).toBe("you must provide an email"); expect(sendVerificationEmailCallCount).toBe(0); expect(sendEmailOptions).not.toBeDefined(); }); - it('should fail if email is not a string', async () => { + it("should fail if email is not a string", async () => { let sendEmailOptions; let sendVerificationEmailCallCount = 0; const emailAdapter = { @@ -1064,30 +1159,30 @@ describe('Email Verification Token Expiration:', () => { sendMail: () => {}, }; await reconfigureServer({ - appName: 'emailVerifyToken', + appName: "emailVerifyToken", verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }); const response = await request({ - url: 'http://localhost:8378/1/verificationEmailRequest', - method: 'POST', + url: "http://localhost:8378/1/verificationEmailRequest", + method: "POST", body: { email: 3 }, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }, }).then(fail, res => res); expect(response.status).toBe(400); expect(response.data.code).toBe(Parse.Error.INVALID_EMAIL_ADDRESS); - expect(response.data.error).toBe('you must provide a valid email string'); + expect(response.data.error).toBe("you must provide a valid email string"); expect(sendVerificationEmailCallCount).toBe(0); expect(sendEmailOptions).not.toBeDefined(); }); - it('client should not see the _email_verify_token_expires_at field', async () => { + it("client should not see the _email_verify_token_expires_at field", async () => { const user = new Parse.User(); let sendEmailOptions; const sendPromise = resolvingPromise(); @@ -1100,67 +1195,73 @@ describe('Email Verification Token Expiration:', () => { sendMail: () => {}, }; await reconfigureServer({ - appName: 'emailVerifyToken', + appName: "emailVerifyToken", verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }); - user.setUsername('testEmailVerifyTokenValidity'); - user.setPassword('expiringToken'); - user.set('email', 'user@parse.com'); + user.setUsername("testEmailVerifyTokenValidity"); + user.setPassword("expiringToken"); + user.set("email", "user@parse.com"); await user.signUp(); await sendPromise; await user.fetch(); - expect(user.get('emailVerified')).toEqual(false); - expect(typeof user.get('_email_verify_token_expires_at')).toBe('undefined'); + expect(user.get("emailVerified")).toEqual(false); + expect(typeof user.get("_email_verify_token_expires_at")).toBe("undefined"); expect(sendEmailOptions).toBeDefined(); }); - it_id('b082d387-4974-4d45-a0d9-0c85ca2d7cbf')(it)('emailVerified should be set to false after changing from an already verified email', async () => { - let user = new Parse.User(); - let sendEmailOptions; - const sendPromise = resolvingPromise(); - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - sendPromise.resolve(); - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - await reconfigureServer({ - appName: 'emailVerifyToken', - verifyUserEmails: true, - emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', - }); - user.setUsername('testEmailVerifyTokenValidity'); - user.setPassword('expiringToken'); - user.set('email', 'user@parse.com'); - await user.signUp(); - await sendPromise; - let response = await request({ - url: sendEmailOptions.link, - followRedirects: false, - }); - expect(response.status).toEqual(302); - user = await Parse.User.logIn('testEmailVerifyTokenValidity', 'expiringToken'); - expect(typeof user).toBe('object'); - expect(user.get('emailVerified')).toBe(true); + it_id("b082d387-4974-4d45-a0d9-0c85ca2d7cbf")(it)( + "emailVerified should be set to false after changing from an already verified email", + async () => { + let user = new Parse.User(); + let sendEmailOptions; + const sendPromise = resolvingPromise(); + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + sendPromise.resolve(); + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + await reconfigureServer({ + appName: "emailVerifyToken", + verifyUserEmails: true, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 5, // 5 seconds + publicServerURL: "http://localhost:8378/1", + }); + user.setUsername("testEmailVerifyTokenValidity"); + user.setPassword("expiringToken"); + user.set("email", "user@parse.com"); + await user.signUp(); + await sendPromise; + let response = await request({ + url: sendEmailOptions.link, + followRedirects: false, + }); + expect(response.status).toEqual(302); + user = await Parse.User.logIn( + "testEmailVerifyTokenValidity", + "expiringToken" + ); + expect(typeof user).toBe("object"); + expect(user.get("emailVerified")).toBe(true); - user.set('email', 'newEmail@parse.com'); - await user.save(); - await user.fetch(); - expect(typeof user).toBe('object'); - expect(user.get('email')).toBe('newEmail@parse.com'); - expect(user.get('emailVerified')).toBe(false); + user.set("email", "newEmail@parse.com"); + await user.save(); + await user.fetch(); + expect(typeof user).toBe("object"); + expect(user.get("email")).toBe("newEmail@parse.com"); + expect(user.get("emailVerified")).toBe(false); - response = await request({ - url: sendEmailOptions.link, - followRedirects: false, - }); - expect(response.status).toEqual(302); - }); + response = await request({ + url: sendEmailOptions.link, + followRedirects: false, + }); + expect(response.status).toEqual(302); + } + ); }); diff --git a/spec/EnableExpressErrorHandler.spec.js b/spec/EnableExpressErrorHandler.spec.js index 64c250628b..f3a5a56f77 100644 --- a/spec/EnableExpressErrorHandler.spec.js +++ b/spec/EnableExpressErrorHandler.spec.js @@ -1,28 +1,28 @@ -const request = require('../lib/request'); +const request = require("../lib/request"); -describe('Enable express error handler', () => { - it('should call the default handler in case of error, like updating a non existing object', async done => { - spyOn(console, 'error'); +describe("Enable express error handler", () => { + it("should call the default handler in case of error, like updating a non existing object", async done => { + spyOn(console, "error"); const parseServer = await reconfigureServer({ enableExpressErrorHandler: true, }); parseServer.app.use(function (err, req, res, next) { - expect(err.message).toBe('Object not found.'); + expect(err.message).toBe("Object not found."); next(err); }); try { await request({ - method: 'PUT', - url: defaultConfiguration.serverURL + '/classes/AnyClass/nonExistingId', + method: "PUT", + url: defaultConfiguration.serverURL + "/classes/AnyClass/nonExistingId", headers: { - 'X-Parse-Application-Id': defaultConfiguration.appId, - 'X-Parse-Master-Key': defaultConfiguration.masterKey, - 'Content-Type': 'application/json', + "X-Parse-Application-Id": defaultConfiguration.appId, + "X-Parse-Master-Key": defaultConfiguration.masterKey, + "Content-Type": "application/json", }, - body: { someField: 'blablabla' }, + body: { someField: "blablabla" }, }); - fail('Should throw error'); + fail("Should throw error"); } catch (response) { expect(response).toBeDefined(); expect(response.status).toEqual(500); diff --git a/spec/EventEmitterPubSub.spec.js b/spec/EventEmitterPubSub.spec.js index 00358646de..2d3eaf31bd 100644 --- a/spec/EventEmitterPubSub.spec.js +++ b/spec/EventEmitterPubSub.spec.js @@ -1,43 +1,44 @@ -const EventEmitterPubSub = require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub; +const EventEmitterPubSub = + require("../lib/Adapters/PubSub/EventEmitterPubSub").EventEmitterPubSub; -describe('EventEmitterPubSub', function () { - it('can publish and subscribe', function () { +describe("EventEmitterPubSub", function () { + it("can publish and subscribe", function () { const publisher = EventEmitterPubSub.createPublisher(); const subscriber = EventEmitterPubSub.createSubscriber(); - subscriber.subscribe('testChannel'); + subscriber.subscribe("testChannel"); // Register mock checked for subscriber let isChecked = false; - subscriber.on('message', function (channel, message) { + subscriber.on("message", function (channel, message) { isChecked = true; - expect(channel).toBe('testChannel'); - expect(message).toBe('testMessage'); + expect(channel).toBe("testChannel"); + expect(message).toBe("testMessage"); }); - publisher.publish('testChannel', 'testMessage'); + publisher.publish("testChannel", "testMessage"); // Make sure the callback is checked expect(isChecked).toBe(true); }); - it('can unsubscribe', function () { + it("can unsubscribe", function () { const publisher = EventEmitterPubSub.createPublisher(); const subscriber = EventEmitterPubSub.createSubscriber(); - subscriber.subscribe('testChannel'); - subscriber.unsubscribe('testChannel'); + subscriber.subscribe("testChannel"); + subscriber.unsubscribe("testChannel"); // Register mock checked for subscriber let isCalled = false; - subscriber.on('message', function () { + subscriber.on("message", function () { isCalled = true; }); - publisher.publish('testChannel', 'testMessage'); + publisher.publish("testChannel", "testMessage"); // Make sure the callback is not called expect(isCalled).toBe(false); }); - it('can unsubscribe not subscribing channel', function () { + it("can unsubscribe not subscribing channel", function () { const subscriber = EventEmitterPubSub.createSubscriber(); // Make sure subscriber does not throw exception - subscriber.unsubscribe('testChannel'); + subscriber.unsubscribe("testChannel"); }); }); diff --git a/spec/FilesController.spec.js b/spec/FilesController.spec.js index 287a89fb23..b7857c648f 100644 --- a/spec/FilesController.spec.js +++ b/spec/FilesController.spec.js @@ -1,162 +1,208 @@ -const LoggerController = require('../lib/Controllers/LoggerController').LoggerController; +const LoggerController = + require("../lib/Controllers/LoggerController").LoggerController; const WinstonLoggerAdapter = - require('../lib/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter; + require("../lib/Adapters/Logger/WinstonLoggerAdapter").WinstonLoggerAdapter; const GridFSBucketAdapter = - require('../lib/Adapters/Files/GridFSBucketAdapter').GridFSBucketAdapter; -const Config = require('../lib/Config'); -const FilesController = require('../lib/Controllers/FilesController').default; -const databaseURI = 'mongodb://localhost:27017/parse'; + require("../lib/Adapters/Files/GridFSBucketAdapter").GridFSBucketAdapter; +const Config = require("../lib/Config"); +const FilesController = require("../lib/Controllers/FilesController").default; +const databaseURI = "mongodb://localhost:27017/parse"; const mockAdapter = { createFile: () => { - return Promise.reject(new Error('it failed with xyz')); + return Promise.reject(new Error("it failed with xyz")); }, deleteFile: () => {}, getFileData: () => {}, - getFileLocation: () => 'xyz', + getFileLocation: () => "xyz", validateFilename: () => { return null; }, }; // Small additional tests to improve overall coverage -describe('FilesController', () => { - it('should properly expand objects with sync getFileLocation', async () => { +describe("FilesController", () => { + it("should properly expand objects with sync getFileLocation", async () => { const config = Config.get(Parse.applicationId); - const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse'); + const gridFSAdapter = new GridFSBucketAdapter( + "mongodb://localhost:27017/parse" + ); gridFSAdapter.getFileLocation = (config, filename) => { - return config.mount + '/files/' + config.applicationId + '/' + encodeURIComponent(filename); + return ( + config.mount + + "/files/" + + config.applicationId + + "/" + + encodeURIComponent(filename) + ); }; const filesController = new FilesController(gridFSAdapter); - const result = await filesController.expandFilesInObject(config, function () {}); + const result = await filesController.expandFilesInObject( + config, + function () {} + ); expect(result).toBeUndefined(); const fullFile = { - type: '__type', - url: 'http://an.url', + type: "__type", + url: "http://an.url", }; const anObject = { aFile: fullFile, }; await filesController.expandFilesInObject(config, anObject); - expect(anObject.aFile.url).toEqual('http://an.url'); + expect(anObject.aFile.url).toEqual("http://an.url"); }); - it('should properly expand objects with async getFileLocation', async () => { + it("should properly expand objects with async getFileLocation", async () => { const config = Config.get(Parse.applicationId); - const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse'); + const gridFSAdapter = new GridFSBucketAdapter( + "mongodb://localhost:27017/parse" + ); gridFSAdapter.getFileLocation = async (config, filename) => { await Promise.resolve(); - return config.mount + '/files/' + config.applicationId + '/' + encodeURIComponent(filename); + return ( + config.mount + + "/files/" + + config.applicationId + + "/" + + encodeURIComponent(filename) + ); }; const filesController = new FilesController(gridFSAdapter); - const result = await filesController.expandFilesInObject(config, function () {}); + const result = await filesController.expandFilesInObject( + config, + function () {} + ); expect(result).toBeUndefined(); const fullFile = { - type: '__type', - url: 'http://an.url', + type: "__type", + url: "http://an.url", }; const anObject = { aFile: fullFile, }; await filesController.expandFilesInObject(config, anObject); - expect(anObject.aFile.url).toEqual('http://an.url'); + expect(anObject.aFile.url).toEqual("http://an.url"); }); - it('should call getFileLocation when config.fileKey is undefined', async () => { + it("should call getFileLocation when config.fileKey is undefined", async () => { const config = {}; - const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse'); + const gridFSAdapter = new GridFSBucketAdapter( + "mongodb://localhost:27017/parse" + ); const fullFile = { - name: 'mock-name', - __type: 'File', + name: "mock-name", + __type: "File", }; gridFSAdapter.getFileLocation = jasmine - .createSpy('getFileLocation') - .and.returnValue(Promise.resolve('mock-url')); + .createSpy("getFileLocation") + .and.returnValue(Promise.resolve("mock-url")); const filesController = new FilesController(gridFSAdapter); const anObject = { aFile: fullFile }; await filesController.expandFilesInObject(config, anObject); - expect(gridFSAdapter.getFileLocation).toHaveBeenCalledWith(config, fullFile.name); - expect(anObject.aFile.url).toEqual('mock-url'); + expect(gridFSAdapter.getFileLocation).toHaveBeenCalledWith( + config, + fullFile.name + ); + expect(anObject.aFile.url).toEqual("mock-url"); }); - it('should call getFileLocation when config.fileKey is defined', async () => { - const config = { fileKey: 'mock-key' }; - const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse'); + it("should call getFileLocation when config.fileKey is defined", async () => { + const config = { fileKey: "mock-key" }; + const gridFSAdapter = new GridFSBucketAdapter( + "mongodb://localhost:27017/parse" + ); const fullFile = { - name: 'mock-name', - __type: 'File', + name: "mock-name", + __type: "File", }; gridFSAdapter.getFileLocation = jasmine - .createSpy('getFileLocation') - .and.returnValue(Promise.resolve('mock-url')); + .createSpy("getFileLocation") + .and.returnValue(Promise.resolve("mock-url")); const filesController = new FilesController(gridFSAdapter); const anObject = { aFile: fullFile }; await filesController.expandFilesInObject(config, anObject); - expect(gridFSAdapter.getFileLocation).toHaveBeenCalledWith(config, fullFile.name); - expect(anObject.aFile.url).toEqual('mock-url'); - }); - - it_only_db('mongo')('should pass databaseOptions to GridFSBucketAdapter', async () => { - await reconfigureServer({ - databaseURI: 'mongodb://localhost:27017/parse', - filesAdapter: null, - databaseAdapter: null, - databaseOptions: { - retryWrites: true, - }, - }); - const config = Config.get(Parse.applicationId); - expect(config.database.adapter._mongoOptions.retryWrites).toBeTrue(); - expect(config.filesController.adapter._mongoOptions.retryWrites).toBeTrue(); - expect(config.filesController.adapter._mongoOptions.enableSchemaHooks).toBeUndefined(); - expect(config.filesController.adapter._mongoOptions.schemaCacheTtl).toBeUndefined(); + expect(gridFSAdapter.getFileLocation).toHaveBeenCalledWith( + config, + fullFile.name + ); + expect(anObject.aFile.url).toEqual("mock-url"); }); - it('should create a server log on failure', done => { + it_only_db("mongo")( + "should pass databaseOptions to GridFSBucketAdapter", + async () => { + await reconfigureServer({ + databaseURI: "mongodb://localhost:27017/parse", + filesAdapter: null, + databaseAdapter: null, + databaseOptions: { + retryWrites: true, + }, + }); + const config = Config.get(Parse.applicationId); + expect(config.database.adapter._mongoOptions.retryWrites).toBeTrue(); + expect( + config.filesController.adapter._mongoOptions.retryWrites + ).toBeTrue(); + expect( + config.filesController.adapter._mongoOptions.enableSchemaHooks + ).toBeUndefined(); + expect( + config.filesController.adapter._mongoOptions.schemaCacheTtl + ).toBeUndefined(); + } + ); + + it("should create a server log on failure", done => { const logController = new LoggerController(new WinstonLoggerAdapter()); reconfigureServer({ filesAdapter: mockAdapter }) - .then(() => new Parse.File('yolo.txt', [1, 2, 3], 'text/plain').save()) + .then(() => new Parse.File("yolo.txt", [1, 2, 3], "text/plain").save()) .then( - () => done.fail('should not succeed'), - () => setImmediate(() => Promise.resolve('done')) + () => done.fail("should not succeed"), + () => setImmediate(() => Promise.resolve("done")) ) .then(() => new Promise(resolve => setTimeout(resolve, 200))) - .then(() => logController.getLogs({ from: Date.now() - 1000, size: 1000 })) + .then(() => + logController.getLogs({ from: Date.now() - 1000, size: 1000 }) + ) .then(logs => { // we get two logs here: 1. the source of the failure to save the file // and 2 the message that will be sent back to the client. - const log1 = logs.find(x => x.message === 'Error creating a file: it failed with xyz'); - expect(log1.level).toBe('error'); + const log1 = logs.find( + x => x.message === "Error creating a file: it failed with xyz" + ); + expect(log1.level).toBe("error"); - const log2 = logs.find(x => x.message === 'it failed with xyz'); - expect(log2.level).toBe('error'); + const log2 = logs.find(x => x.message === "it failed with xyz"); + expect(log2.level).toBe("error"); expect(log2.code).toBe(130); done(); }); }); - it('should create a parse error when a string is returned', done => { + it("should create a parse error when a string is returned", done => { const mock2 = mockAdapter; mock2.validateFilename = () => { - return 'Bad file! No biscuit!'; + return "Bad file! No biscuit!"; }; const filesController = new FilesController(mockAdapter); const error = filesController.validateFilename(); - expect(typeof error).toBe('object'); - expect(error.message.indexOf('biscuit')).toBe(13); + expect(typeof error).toBe("object"); + expect(error.message.indexOf("biscuit")).toBe(13); expect(error.code).toBe(Parse.Error.INVALID_FILE_NAME); mockAdapter.validateFilename = () => { return null; @@ -164,13 +210,15 @@ describe('FilesController', () => { done(); }); - it('should add a unique hash to the file name when the preserveFileName option is false', async () => { + it("should add a unique hash to the file name when the preserveFileName option is false", async () => { const config = Config.get(Parse.applicationId); - const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse'); - spyOn(gridFSAdapter, 'createFile'); + const gridFSAdapter = new GridFSBucketAdapter( + "mongodb://localhost:27017/parse" + ); + spyOn(gridFSAdapter, "createFile"); gridFSAdapter.createFile.and.returnValue(Promise.resolve()); - const fileName = 'randomFileName.pdf'; - const regexEscapedFileName = fileName.replace(/\./g, '\\$&'); + const fileName = "randomFileName.pdf"; + const regexEscapedFileName = fileName.replace(/\./g, "\\$&"); const filesController = new FilesController(gridFSAdapter, null, { preserveFileName: false, }); @@ -183,12 +231,14 @@ describe('FilesController', () => { ); }); - it('should not add a unique hash to the file name when the preserveFileName option is true', async () => { + it("should not add a unique hash to the file name when the preserveFileName option is true", async () => { const config = Config.get(Parse.applicationId); - const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse'); - spyOn(gridFSAdapter, 'createFile'); + const gridFSAdapter = new GridFSBucketAdapter( + "mongodb://localhost:27017/parse" + ); + spyOn(gridFSAdapter, "createFile"); gridFSAdapter.createFile.and.returnValue(Promise.resolve()); - const fileName = 'randomFileName.pdf'; + const fileName = "randomFileName.pdf"; const filesController = new FilesController(gridFSAdapter, null, { preserveFileName: true, }); @@ -196,10 +246,12 @@ describe('FilesController', () => { await filesController.createFile(config, fileName); expect(gridFSAdapter.createFile).toHaveBeenCalledTimes(1); - expect(gridFSAdapter.createFile.calls.mostRecent().args[0]).toEqual(fileName); + expect(gridFSAdapter.createFile.calls.mostRecent().args[0]).toEqual( + fileName + ); }); - it('should handle adapter without getMetadata', async () => { + it("should handle adapter without getMetadata", async () => { const gridFSAdapter = new GridFSBucketAdapter(databaseURI); gridFSAdapter.getMetadata = null; const filesController = new FilesController(gridFSAdapter); @@ -208,16 +260,20 @@ describe('FilesController', () => { expect(result).toEqual({}); }); - it('should reject slashes in file names', done => { - const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse'); - const fileName = 'foo/randomFileName.pdf'; + it("should reject slashes in file names", done => { + const gridFSAdapter = new GridFSBucketAdapter( + "mongodb://localhost:27017/parse" + ); + const fileName = "foo/randomFileName.pdf"; expect(gridFSAdapter.validateFilename(fileName)).not.toBe(null); done(); }); - it('should also reject slashes in file names', done => { - const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse'); - const fileName = 'foo/randomFileName.pdf'; + it("should also reject slashes in file names", done => { + const gridFSAdapter = new GridFSBucketAdapter( + "mongodb://localhost:27017/parse" + ); + const fileName = "foo/randomFileName.pdf"; expect(gridFSAdapter.validateFilename(fileName)).not.toBe(null); done(); }); diff --git a/spec/GridFSBucketStorageAdapter.spec.js b/spec/GridFSBucketStorageAdapter.spec.js index 88da2bbd2d..164c60e5be 100644 --- a/spec/GridFSBucketStorageAdapter.spec.js +++ b/spec/GridFSBucketStorageAdapter.spec.js @@ -1,27 +1,27 @@ const GridFSBucketAdapter = - require('../lib/Adapters/Files/GridFSBucketAdapter').GridFSBucketAdapter; -const { randomString } = require('../lib/cryptoUtils'); -const databaseURI = 'mongodb://localhost:27017/parse'; -const request = require('../lib/request'); + require("../lib/Adapters/Files/GridFSBucketAdapter").GridFSBucketAdapter; +const { randomString } = require("../lib/cryptoUtils"); +const databaseURI = "mongodb://localhost:27017/parse"; +const request = require("../lib/request"); async function expectMissingFile(gfsAdapter, name) { try { await gfsAdapter.getFileData(name); - fail('should have thrown'); + fail("should have thrown"); } catch (e) { - expect(e.message).toEqual('FileNotFound: file myFileName was not found'); + expect(e.message).toEqual("FileNotFound: file myFileName was not found"); } } -describe_only_db('mongo')('GridFSBucket', () => { +describe_only_db("mongo")("GridFSBucket", () => { beforeEach(async () => { const gsAdapter = new GridFSBucketAdapter(databaseURI); const db = await gsAdapter._connect(); await db.dropDatabase(); }); - it('should connect to mongo with the supported database options', async () => { - const databaseURI = 'mongodb://localhost:27017/parse'; + it("should connect to mongo with the supported database options", async () => { + const databaseURI = "mongodb://localhost:27017/parse"; const gfsAdapter = new GridFSBucketAdapter(databaseURI, { retryWrites: true, // these are not supported by the mongo client @@ -36,42 +36,44 @@ describe_only_db('mongo')('GridFSBucket', () => { expect(db.options?.retryWrites).toEqual(true); }); - it('should save an encrypted file that can only be decrypted by a GridFS adapter with the encryptionKey', async () => { + it("should save an encrypted file that can only be decrypted by a GridFS adapter with the encryptionKey", async () => { const unencryptedAdapter = new GridFSBucketAdapter(databaseURI); const encryptedAdapter = new GridFSBucketAdapter( databaseURI, {}, - '89E4AFF1-DFE4-4603-9574-BFA16BB446FD' + "89E4AFF1-DFE4-4603-9574-BFA16BB446FD" ); - await expectMissingFile(encryptedAdapter, 'myFileName'); - const originalString = 'abcdefghi'; - await encryptedAdapter.createFile('myFileName', originalString); - const unencryptedResult = await unencryptedAdapter.getFileData('myFileName'); - expect(unencryptedResult.toString('utf8')).not.toBe(originalString); - const encryptedResult = await encryptedAdapter.getFileData('myFileName'); - expect(encryptedResult.toString('utf8')).toBe(originalString); + await expectMissingFile(encryptedAdapter, "myFileName"); + const originalString = "abcdefghi"; + await encryptedAdapter.createFile("myFileName", originalString); + const unencryptedResult = + await unencryptedAdapter.getFileData("myFileName"); + expect(unencryptedResult.toString("utf8")).not.toBe(originalString); + const encryptedResult = await encryptedAdapter.getFileData("myFileName"); + expect(encryptedResult.toString("utf8")).toBe(originalString); }); - it('should rotate key of all unencrypted GridFS files to encrypted files', async () => { + it("should rotate key of all unencrypted GridFS files to encrypted files", async () => { const unencryptedAdapter = new GridFSBucketAdapter(databaseURI); const encryptedAdapter = new GridFSBucketAdapter( databaseURI, {}, - '89E4AFF1-DFE4-4603-9574-BFA16BB446FD' + "89E4AFF1-DFE4-4603-9574-BFA16BB446FD" ); - const fileName1 = 'file1.txt'; - const data1 = 'hello world'; - const fileName2 = 'file2.txt'; - const data2 = 'hello new world'; + const fileName1 = "file1.txt"; + const data1 = "hello world"; + const fileName2 = "file2.txt"; + const data2 = "hello new world"; //Store unecrypted files await unencryptedAdapter.createFile(fileName1, data1); const unencryptedResult1 = await unencryptedAdapter.getFileData(fileName1); - expect(unencryptedResult1.toString('utf8')).toBe(data1); + expect(unencryptedResult1.toString("utf8")).toBe(data1); await unencryptedAdapter.createFile(fileName2, data2); const unencryptedResult2 = await unencryptedAdapter.getFileData(fileName2); - expect(unencryptedResult2.toString('utf8')).toBe(data2); + expect(unencryptedResult2.toString("utf8")).toBe(data2); //Check if encrypted adapter can read data and make sure it's not the same as unEncrypted adapter - const { rotated, notRotated } = await encryptedAdapter.rotateEncryptionKey(); + const { rotated, notRotated } = + await encryptedAdapter.rotateEncryptionKey(); expect(rotated.length).toEqual(2); expect( rotated.filter(function (value) { @@ -86,31 +88,41 @@ describe_only_db('mongo')('GridFSBucket', () => { expect(notRotated.length).toEqual(0); let result = await encryptedAdapter.getFileData(fileName1); expect(result instanceof Buffer).toBe(true); - expect(result.toString('utf-8')).toEqual(data1); + expect(result.toString("utf-8")).toEqual(data1); const encryptedData1 = await unencryptedAdapter.getFileData(fileName1); - expect(encryptedData1.toString('utf-8')).not.toEqual(unencryptedResult1); + expect(encryptedData1.toString("utf-8")).not.toEqual(unencryptedResult1); result = await encryptedAdapter.getFileData(fileName2); expect(result instanceof Buffer).toBe(true); - expect(result.toString('utf-8')).toEqual(data2); + expect(result.toString("utf-8")).toEqual(data2); const encryptedData2 = await unencryptedAdapter.getFileData(fileName2); - expect(encryptedData2.toString('utf-8')).not.toEqual(unencryptedResult2); + expect(encryptedData2.toString("utf-8")).not.toEqual(unencryptedResult2); }); - it('should rotate key of all old encrypted GridFS files to encrypted files', async () => { - const oldEncryptionKey = 'oldKeyThatILoved'; - const oldEncryptedAdapter = new GridFSBucketAdapter(databaseURI, {}, oldEncryptionKey); - const encryptedAdapter = new GridFSBucketAdapter(databaseURI, {}, 'newKeyThatILove'); - const fileName1 = 'file1.txt'; - const data1 = 'hello world'; - const fileName2 = 'file2.txt'; - const data2 = 'hello new world'; + it("should rotate key of all old encrypted GridFS files to encrypted files", async () => { + const oldEncryptionKey = "oldKeyThatILoved"; + const oldEncryptedAdapter = new GridFSBucketAdapter( + databaseURI, + {}, + oldEncryptionKey + ); + const encryptedAdapter = new GridFSBucketAdapter( + databaseURI, + {}, + "newKeyThatILove" + ); + const fileName1 = "file1.txt"; + const data1 = "hello world"; + const fileName2 = "file2.txt"; + const data2 = "hello new world"; //Store unecrypted files await oldEncryptedAdapter.createFile(fileName1, data1); - const oldEncryptedResult1 = await oldEncryptedAdapter.getFileData(fileName1); - expect(oldEncryptedResult1.toString('utf8')).toBe(data1); + const oldEncryptedResult1 = + await oldEncryptedAdapter.getFileData(fileName1); + expect(oldEncryptedResult1.toString("utf8")).toBe(data1); await oldEncryptedAdapter.createFile(fileName2, data2); - const oldEncryptedResult2 = await oldEncryptedAdapter.getFileData(fileName2); - expect(oldEncryptedResult2.toString('utf8')).toBe(data2); + const oldEncryptedResult2 = + await oldEncryptedAdapter.getFileData(fileName2); + expect(oldEncryptedResult2.toString("utf8")).toBe(data2); //Check if encrypted adapter can read data and make sure it's not the same as unEncrypted adapter const { rotated, notRotated } = await encryptedAdapter.rotateEncryptionKey({ oldKey: oldEncryptionKey, @@ -129,7 +141,7 @@ describe_only_db('mongo')('GridFSBucket', () => { expect(notRotated.length).toEqual(0); let result = await encryptedAdapter.getFileData(fileName1); expect(result instanceof Buffer).toBe(true); - expect(result.toString('utf-8')).toEqual(data1); + expect(result.toString("utf-8")).toEqual(data1); let decryptionError1; let encryptedData1; try { @@ -137,11 +149,11 @@ describe_only_db('mongo')('GridFSBucket', () => { } catch (err) { decryptionError1 = err; } - expect(decryptionError1).toMatch('Error'); + expect(decryptionError1).toMatch("Error"); expect(encryptedData1).toBeUndefined(); result = await encryptedAdapter.getFileData(fileName2); expect(result instanceof Buffer).toBe(true); - expect(result.toString('utf-8')).toEqual(data2); + expect(result.toString("utf-8")).toEqual(data2); let decryptionError2; let encryptedData2; try { @@ -149,29 +161,36 @@ describe_only_db('mongo')('GridFSBucket', () => { } catch (err) { decryptionError2 = err; } - expect(decryptionError2).toMatch('Error'); + expect(decryptionError2).toMatch("Error"); expect(encryptedData2).toBeUndefined(); }); - it('should rotate key of all old encrypted GridFS files to unencrypted files', async () => { - const oldEncryptionKey = 'oldKeyThatILoved'; - const oldEncryptedAdapter = new GridFSBucketAdapter(databaseURI, {}, oldEncryptionKey); + it("should rotate key of all old encrypted GridFS files to unencrypted files", async () => { + const oldEncryptionKey = "oldKeyThatILoved"; + const oldEncryptedAdapter = new GridFSBucketAdapter( + databaseURI, + {}, + oldEncryptionKey + ); const unEncryptedAdapter = new GridFSBucketAdapter(databaseURI); - const fileName1 = 'file1.txt'; - const data1 = 'hello world'; - const fileName2 = 'file2.txt'; - const data2 = 'hello new world'; + const fileName1 = "file1.txt"; + const data1 = "hello world"; + const fileName2 = "file2.txt"; + const data2 = "hello new world"; //Store unecrypted files await oldEncryptedAdapter.createFile(fileName1, data1); - const oldEncryptedResult1 = await oldEncryptedAdapter.getFileData(fileName1); - expect(oldEncryptedResult1.toString('utf8')).toBe(data1); + const oldEncryptedResult1 = + await oldEncryptedAdapter.getFileData(fileName1); + expect(oldEncryptedResult1.toString("utf8")).toBe(data1); await oldEncryptedAdapter.createFile(fileName2, data2); - const oldEncryptedResult2 = await oldEncryptedAdapter.getFileData(fileName2); - expect(oldEncryptedResult2.toString('utf8')).toBe(data2); + const oldEncryptedResult2 = + await oldEncryptedAdapter.getFileData(fileName2); + expect(oldEncryptedResult2.toString("utf8")).toBe(data2); //Check if unEncrypted adapter can read data and make sure it's not the same as oldEncrypted adapter - const { rotated, notRotated } = await unEncryptedAdapter.rotateEncryptionKey({ - oldKey: oldEncryptionKey, - }); + const { rotated, notRotated } = + await unEncryptedAdapter.rotateEncryptionKey({ + oldKey: oldEncryptionKey, + }); expect(rotated.length).toEqual(2); expect( rotated.filter(function (value) { @@ -186,7 +205,7 @@ describe_only_db('mongo')('GridFSBucket', () => { expect(notRotated.length).toEqual(0); let result = await unEncryptedAdapter.getFileData(fileName1); expect(result instanceof Buffer).toBe(true); - expect(result.toString('utf-8')).toEqual(data1); + expect(result.toString("utf-8")).toEqual(data1); let decryptionError1; let encryptedData1; try { @@ -194,11 +213,11 @@ describe_only_db('mongo')('GridFSBucket', () => { } catch (err) { decryptionError1 = err; } - expect(decryptionError1).toMatch('Error'); + expect(decryptionError1).toMatch("Error"); expect(encryptedData1).toBeUndefined(); result = await unEncryptedAdapter.getFileData(fileName2); expect(result instanceof Buffer).toBe(true); - expect(result.toString('utf-8')).toEqual(data2); + expect(result.toString("utf-8")).toEqual(data2); let decryptionError2; let encryptedData2; try { @@ -206,30 +225,40 @@ describe_only_db('mongo')('GridFSBucket', () => { } catch (err) { decryptionError2 = err; } - expect(decryptionError2).toMatch('Error'); + expect(decryptionError2).toMatch("Error"); expect(encryptedData2).toBeUndefined(); }); - it('should only encrypt specified fileNames', async () => { - const oldEncryptionKey = 'oldKeyThatILoved'; - const oldEncryptedAdapter = new GridFSBucketAdapter(databaseURI, {}, oldEncryptionKey); - const encryptedAdapter = new GridFSBucketAdapter(databaseURI, {}, 'newKeyThatILove'); + it("should only encrypt specified fileNames", async () => { + const oldEncryptionKey = "oldKeyThatILoved"; + const oldEncryptedAdapter = new GridFSBucketAdapter( + databaseURI, + {}, + oldEncryptionKey + ); + const encryptedAdapter = new GridFSBucketAdapter( + databaseURI, + {}, + "newKeyThatILove" + ); const unEncryptedAdapter = new GridFSBucketAdapter(databaseURI); - const fileName1 = 'file1.txt'; - const data1 = 'hello world'; - const fileName2 = 'file2.txt'; - const data2 = 'hello new world'; + const fileName1 = "file1.txt"; + const data1 = "hello world"; + const fileName2 = "file2.txt"; + const data2 = "hello new world"; //Store unecrypted files await oldEncryptedAdapter.createFile(fileName1, data1); - const oldEncryptedResult1 = await oldEncryptedAdapter.getFileData(fileName1); - expect(oldEncryptedResult1.toString('utf8')).toBe(data1); + const oldEncryptedResult1 = + await oldEncryptedAdapter.getFileData(fileName1); + expect(oldEncryptedResult1.toString("utf8")).toBe(data1); await oldEncryptedAdapter.createFile(fileName2, data2); - const oldEncryptedResult2 = await oldEncryptedAdapter.getFileData(fileName2); - expect(oldEncryptedResult2.toString('utf8')).toBe(data2); + const oldEncryptedResult2 = + await oldEncryptedAdapter.getFileData(fileName2); + expect(oldEncryptedResult2.toString("utf8")).toBe(data2); //Inject unecrypted file to see if causes an issue - const fileName3 = 'file3.txt'; - const data3 = 'hello past world'; - await unEncryptedAdapter.createFile(fileName3, data3, 'text/utf8'); + const fileName3 = "file3.txt"; + const data3 = "hello past world"; + await unEncryptedAdapter.createFile(fileName3, data3, "text/utf8"); //Check if encrypted adapter can read data and make sure it's not the same as unEncrypted adapter const { rotated, notRotated } = await encryptedAdapter.rotateEncryptionKey({ oldKey: oldEncryptionKey, @@ -254,7 +283,7 @@ describe_only_db('mongo')('GridFSBucket', () => { ).toEqual(0); let result = await encryptedAdapter.getFileData(fileName1); expect(result instanceof Buffer).toBe(true); - expect(result.toString('utf-8')).toEqual(data1); + expect(result.toString("utf-8")).toEqual(data1); let decryptionError1; let encryptedData1; try { @@ -262,11 +291,11 @@ describe_only_db('mongo')('GridFSBucket', () => { } catch (err) { decryptionError1 = err; } - expect(decryptionError1).toMatch('Error'); + expect(decryptionError1).toMatch("Error"); expect(encryptedData1).toBeUndefined(); result = await encryptedAdapter.getFileData(fileName2); expect(result instanceof Buffer).toBe(true); - expect(result.toString('utf-8')).toEqual(data2); + expect(result.toString("utf-8")).toEqual(data2); let decryptionError2; let encryptedData2; try { @@ -274,30 +303,40 @@ describe_only_db('mongo')('GridFSBucket', () => { } catch (err) { decryptionError2 = err; } - expect(decryptionError2).toMatch('Error'); + expect(decryptionError2).toMatch("Error"); expect(encryptedData2).toBeUndefined(); }); it("should return fileNames of those it can't encrypt with the new key", async () => { - const oldEncryptionKey = 'oldKeyThatILoved'; - const oldEncryptedAdapter = new GridFSBucketAdapter(databaseURI, {}, oldEncryptionKey); - const encryptedAdapter = new GridFSBucketAdapter(databaseURI, {}, 'newKeyThatILove'); + const oldEncryptionKey = "oldKeyThatILoved"; + const oldEncryptedAdapter = new GridFSBucketAdapter( + databaseURI, + {}, + oldEncryptionKey + ); + const encryptedAdapter = new GridFSBucketAdapter( + databaseURI, + {}, + "newKeyThatILove" + ); const unEncryptedAdapter = new GridFSBucketAdapter(databaseURI); - const fileName1 = 'file1.txt'; - const data1 = 'hello world'; - const fileName2 = 'file2.txt'; - const data2 = 'hello new world'; + const fileName1 = "file1.txt"; + const data1 = "hello world"; + const fileName2 = "file2.txt"; + const data2 = "hello new world"; //Store unecrypted files await oldEncryptedAdapter.createFile(fileName1, data1); - const oldEncryptedResult1 = await oldEncryptedAdapter.getFileData(fileName1); - expect(oldEncryptedResult1.toString('utf8')).toBe(data1); + const oldEncryptedResult1 = + await oldEncryptedAdapter.getFileData(fileName1); + expect(oldEncryptedResult1.toString("utf8")).toBe(data1); await oldEncryptedAdapter.createFile(fileName2, data2); - const oldEncryptedResult2 = await oldEncryptedAdapter.getFileData(fileName2); - expect(oldEncryptedResult2.toString('utf8')).toBe(data2); + const oldEncryptedResult2 = + await oldEncryptedAdapter.getFileData(fileName2); + expect(oldEncryptedResult2.toString("utf8")).toBe(data2); //Inject unecrypted file to see if causes an issue - const fileName3 = 'file3.txt'; - const data3 = 'hello past world'; - await unEncryptedAdapter.createFile(fileName3, data3, 'text/utf8'); + const fileName3 = "file3.txt"; + const data3 = "hello past world"; + await unEncryptedAdapter.createFile(fileName3, data3, "text/utf8"); //Check if encrypted adapter can read data and make sure it's not the same as unEncrypted adapter const { rotated, notRotated } = await encryptedAdapter.rotateEncryptionKey({ oldKey: oldEncryptionKey, @@ -321,7 +360,7 @@ describe_only_db('mongo')('GridFSBucket', () => { ).toEqual(1); let result = await encryptedAdapter.getFileData(fileName1); expect(result instanceof Buffer).toBe(true); - expect(result.toString('utf-8')).toEqual(data1); + expect(result.toString("utf-8")).toEqual(data1); let decryptionError1; let encryptedData1; try { @@ -329,11 +368,11 @@ describe_only_db('mongo')('GridFSBucket', () => { } catch (err) { decryptionError1 = err; } - expect(decryptionError1).toMatch('Error'); + expect(decryptionError1).toMatch("Error"); expect(encryptedData1).toBeUndefined(); result = await encryptedAdapter.getFileData(fileName2); expect(result instanceof Buffer).toBe(true); - expect(result.toString('utf-8')).toEqual(data2); + expect(result.toString("utf-8")).toEqual(data2); let decryptionError2; let encryptedData2; try { @@ -341,54 +380,54 @@ describe_only_db('mongo')('GridFSBucket', () => { } catch (err) { decryptionError2 = err; } - expect(decryptionError2).toMatch('Error'); + expect(decryptionError2).toMatch("Error"); expect(encryptedData2).toBeUndefined(); }); - it('should save metadata', async () => { + it("should save metadata", async () => { const gfsAdapter = new GridFSBucketAdapter(databaseURI); - const originalString = 'abcdefghi'; - const metadata = { hello: 'world' }; - await gfsAdapter.createFile('myFileName', originalString, null, { + const originalString = "abcdefghi"; + const metadata = { hello: "world" }; + await gfsAdapter.createFile("myFileName", originalString, null, { metadata, }); - const gfsResult = await gfsAdapter.getFileData('myFileName'); - expect(gfsResult.toString('utf8')).toBe(originalString); - let gfsMetadata = await gfsAdapter.getMetadata('myFileName'); + const gfsResult = await gfsAdapter.getFileData("myFileName"); + expect(gfsResult.toString("utf8")).toBe(originalString); + let gfsMetadata = await gfsAdapter.getMetadata("myFileName"); expect(gfsMetadata.metadata).toEqual(metadata); // Empty json for file not found - gfsMetadata = await gfsAdapter.getMetadata('myUnknownFile'); + gfsMetadata = await gfsAdapter.getMetadata("myUnknownFile"); expect(gfsMetadata).toEqual({}); }); - it('should save metadata with file', async () => { + it("should save metadata with file", async () => { const gfsAdapter = new GridFSBucketAdapter(databaseURI); await reconfigureServer({ filesAdapter: gfsAdapter }); - const str = 'Hello World!'; + const str = "Hello World!"; const data = []; for (let i = 0; i < str.length; i++) { data.push(str.charCodeAt(i)); } - const metadata = { foo: 'bar' }; - const file = new Parse.File('hello.txt', data, 'text/plain'); - file.addMetadata('foo', 'bar'); + const metadata = { foo: "bar" }; + const file = new Parse.File("hello.txt", data, "text/plain"); + file.addMetadata("foo", "bar"); await file.save(); let fileData = await gfsAdapter.getMetadata(file.name()); expect(fileData.metadata).toEqual(metadata); // Can only add metadata on create - file.addMetadata('hello', 'world'); + file.addMetadata("hello", "world"); await file.save(); fileData = await gfsAdapter.getMetadata(file.name()); expect(fileData.metadata).toEqual(metadata); const headers = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; const response = await request({ - method: 'GET', + method: "GET", headers, url: `http://localhost:8378/1/files/test/metadata/${file.name()}`, }); @@ -396,53 +435,53 @@ describe_only_db('mongo')('GridFSBucket', () => { expect(fileData.metadata).toEqual(metadata); }); - it('should handle getMetadata error', async () => { + it("should handle getMetadata error", async () => { const gfsAdapter = new GridFSBucketAdapter(databaseURI); await reconfigureServer({ filesAdapter: gfsAdapter }); gfsAdapter.getMetadata = () => Promise.reject(); const headers = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; const response = await request({ - method: 'GET', + method: "GET", headers, url: `http://localhost:8378/1/files/test/metadata/filename.txt`, }); expect(response.data).toEqual({}); }); - it('properly fetches a large file from GridFS', async () => { + it("properly fetches a large file from GridFS", async () => { const gfsAdapter = new GridFSBucketAdapter(databaseURI); const twoMegabytesFile = randomString(2048 * 1024); - await gfsAdapter.createFile('myFileName', twoMegabytesFile); - const gfsResult = await gfsAdapter.getFileData('myFileName'); - expect(gfsResult.toString('utf8')).toBe(twoMegabytesFile); + await gfsAdapter.createFile("myFileName", twoMegabytesFile); + const gfsResult = await gfsAdapter.getFileData("myFileName"); + expect(gfsResult.toString("utf8")).toBe(twoMegabytesFile); }); - it('properly deletes a file from GridFS', async () => { + it("properly deletes a file from GridFS", async () => { const gfsAdapter = new GridFSBucketAdapter(databaseURI); - await gfsAdapter.createFile('myFileName', 'a simple file'); - await gfsAdapter.deleteFile('myFileName'); - await expectMissingFile(gfsAdapter, 'myFileName'); + await gfsAdapter.createFile("myFileName", "a simple file"); + await gfsAdapter.deleteFile("myFileName"); + await expectMissingFile(gfsAdapter, "myFileName"); }, 1000000); - it('properly overrides files', async () => { + it("properly overrides files", async () => { const gfsAdapter = new GridFSBucketAdapter(databaseURI); - await gfsAdapter.createFile('myFileName', 'a simple file'); - await gfsAdapter.createFile('myFileName', 'an overrided simple file'); - const data = await gfsAdapter.getFileData('myFileName'); - expect(data.toString('utf8')).toBe('an overrided simple file'); + await gfsAdapter.createFile("myFileName", "a simple file"); + await gfsAdapter.createFile("myFileName", "an overrided simple file"); + const data = await gfsAdapter.getFileData("myFileName"); + expect(data.toString("utf8")).toBe("an overrided simple file"); const bucket = await gfsAdapter._getBucket(); - const documents = await bucket.find({ filename: 'myFileName' }).toArray(); + const documents = await bucket.find({ filename: "myFileName" }).toArray(); expect(documents.length).toBe(2); - await gfsAdapter.deleteFile('myFileName'); - await expectMissingFile(gfsAdapter, 'myFileName'); + await gfsAdapter.deleteFile("myFileName"); + await expectMissingFile(gfsAdapter, "myFileName"); }); - it('handleShutdown, close connection', async () => { - const databaseURI = 'mongodb://localhost:27017/parse'; + it("handleShutdown, close connection", async () => { + const databaseURI = "mongodb://localhost:27017/parse"; const gfsAdapter = new GridFSBucketAdapter(databaseURI); const db = await gfsAdapter._connect(); @@ -454,7 +493,9 @@ describe_only_db('mongo')('GridFSBucket', () => { await db.admin().serverStatus(); expect(false).toBe(true); } catch (e) { - expect(e.message).toEqual('Client must be connected before running operations'); + expect(e.message).toEqual( + "Client must be connected before running operations" + ); } }); }); diff --git a/spec/HTTPRequest.spec.js b/spec/HTTPRequest.spec.js index b138a010b0..943217105e 100644 --- a/spec/HTTPRequest.spec.js +++ b/spec/HTTPRequest.spec.js @@ -1,42 +1,42 @@ -'use strict'; +"use strict"; -const httpRequest = require('../lib/request'), - HTTPResponse = require('../lib/request').HTTPResponse, - express = require('express'); +const httpRequest = require("../lib/request"), + HTTPResponse = require("../lib/request").HTTPResponse, + express = require("express"); const port = 13371; const httpRequestServer = `http://localhost:${port}`; function startServer(done) { const app = express(); - app.use(express.json({ type: '*/*' })); - app.get('/hello', function (req, res) { - res.json({ response: 'OK' }); + app.use(express.json({ type: "*/*" })); + app.get("/hello", function (req, res) { + res.json({ response: "OK" }); }); - app.get('/404', function (req, res) { + app.get("/404", function (req, res) { res.status(404); - res.send('NO'); + res.send("NO"); }); - app.get('/301', function (req, res) { + app.get("/301", function (req, res) { res.status(301); - res.location('/hello'); + res.location("/hello"); res.send(); }); - app.post('/echo', function (req, res) { + app.post("/echo", function (req, res) { res.json(req.body); }); - app.get('/qs', function (req, res) { + app.get("/qs", function (req, res) { res.json(req.query); }); return app.listen(13371, undefined, done); } -describe('httpRequest', () => { +describe("httpRequest", () => { let server; beforeEach(done => { if (!server) { @@ -50,7 +50,7 @@ describe('httpRequest', () => { server.close(done); }); - it('should do /hello', async () => { + it("should do /hello", async () => { const httpResponse = await httpRequest({ url: `${httpRequestServer}/hello`, }); @@ -58,10 +58,10 @@ describe('httpRequest', () => { expect(httpResponse.status).toBe(200); expect(httpResponse.buffer).toEqual(Buffer.from('{"response":"OK"}')); expect(httpResponse.text).toEqual('{"response":"OK"}'); - expect(httpResponse.data.response).toEqual('OK'); + expect(httpResponse.data.response).toEqual("OK"); }); - it('should do not follow redirects by default', async () => { + it("should do not follow redirects by default", async () => { const httpResponse = await httpRequest({ url: `${httpRequestServer}/301`, }); @@ -69,7 +69,7 @@ describe('httpRequest', () => { expect(httpResponse.status).toBe(301); }); - it('should follow redirects when set', async () => { + it("should follow redirects when set", async () => { const httpResponse = await httpRequest({ url: `${httpRequestServer}/301`, followRedirects: true, @@ -78,10 +78,10 @@ describe('httpRequest', () => { expect(httpResponse.status).toBe(200); expect(httpResponse.buffer).toEqual(Buffer.from('{"response":"OK"}')); expect(httpResponse.text).toEqual('{"response":"OK"}'); - expect(httpResponse.data.response).toEqual('OK'); + expect(httpResponse.data.response).toEqual("OK"); }); - it('should fail on 404', async () => { + it("should fail on 404", async () => { await expectAsync( httpRequest({ url: `${httpRequestServer}/404`, @@ -89,100 +89,102 @@ describe('httpRequest', () => { ).toBeRejectedWith( jasmine.objectContaining({ status: 404, - buffer: Buffer.from('NO'), - text: 'NO', + buffer: Buffer.from("NO"), + text: "NO", data: undefined, }) ); }); - it('should post on echo', async () => { + it("should post on echo", async () => { const httpResponse = await httpRequest({ - method: 'POST', + method: "POST", url: `${httpRequestServer}/echo`, body: { - foo: 'bar', + foo: "bar", }, headers: { - 'Content-Type': 'application/json', + "Content-Type": "application/json", }, }); expect(httpResponse.status).toBe(200); - expect(httpResponse.data).toEqual({ foo: 'bar' }); + expect(httpResponse.data).toEqual({ foo: "bar" }); }); - it('should encode a query string body by default', () => { + it("should encode a query string body by default", () => { const options = { - body: { foo: 'bar' }, + body: { foo: "bar" }, }; const result = httpRequest.encodeBody(options); - expect(result.body).toEqual('foo=bar'); - expect(result.headers['Content-Type']).toEqual('application/x-www-form-urlencoded'); + expect(result.body).toEqual("foo=bar"); + expect(result.headers["Content-Type"]).toEqual( + "application/x-www-form-urlencoded" + ); }); - it('should encode a JSON body', () => { + it("should encode a JSON body", () => { const options = { - body: { foo: 'bar' }, - headers: { 'Content-Type': 'application/json' }, + body: { foo: "bar" }, + headers: { "Content-Type": "application/json" }, }; const result = httpRequest.encodeBody(options); expect(result.body).toEqual('{"foo":"bar"}'); }); - it('should encode a www-form body', () => { + it("should encode a www-form body", () => { const options = { - body: { foo: 'bar', bar: 'baz' }, - headers: { 'cOntent-tYpe': 'application/x-www-form-urlencoded' }, + body: { foo: "bar", bar: "baz" }, + headers: { "cOntent-tYpe": "application/x-www-form-urlencoded" }, }; const result = httpRequest.encodeBody(options); - expect(result.body).toEqual('foo=bar&bar=baz'); + expect(result.body).toEqual("foo=bar&bar=baz"); }); - it('should not encode a wrong content type', () => { + it("should not encode a wrong content type", () => { const options = { - body: { foo: 'bar', bar: 'baz' }, - headers: { 'cOntent-tYpe': 'mime/jpeg' }, + body: { foo: "bar", bar: "baz" }, + headers: { "cOntent-tYpe": "mime/jpeg" }, }; const result = httpRequest.encodeBody(options); - expect(result.body).toEqual({ foo: 'bar', bar: 'baz' }); + expect(result.body).toEqual({ foo: "bar", bar: "baz" }); }); - it('should fail gracefully', async () => { + it("should fail gracefully", async () => { await expectAsync( httpRequest({ - url: 'http://not a good url', + url: "http://not a good url", }) ).toBeRejected(); }); - it('should params object to query string', async () => { + it("should params object to query string", async () => { const httpResponse = await httpRequest({ url: `${httpRequestServer}/qs`, params: { - foo: 'bar', + foo: "bar", }, }); expect(httpResponse.status).toBe(200); - expect(httpResponse.data).toEqual({ foo: 'bar' }); + expect(httpResponse.data).toEqual({ foo: "bar" }); }); - it('should params string to query string', async () => { + it("should params string to query string", async () => { const httpResponse = await httpRequest({ url: `${httpRequestServer}/qs`, - params: 'foo=bar&foo2=bar2', + params: "foo=bar&foo2=bar2", }); expect(httpResponse.status).toBe(200); - expect(httpResponse.data).toEqual({ foo: 'bar', foo2: 'bar2' }); + expect(httpResponse.data).toEqual({ foo: "bar", foo2: "bar2" }); }); - it('should not crash with undefined body', () => { + it("should not crash with undefined body", () => { const httpResponse = new HTTPResponse({}); expect(httpResponse.body).toBeUndefined(); expect(httpResponse.data).toBeUndefined(); @@ -190,58 +192,58 @@ describe('httpRequest', () => { expect(httpResponse.buffer).toBeUndefined(); }); - it('serialized httpResponse correctly with body string', () => { - const httpResponse = new HTTPResponse({}, 'hello'); - expect(httpResponse.text).toBe('hello'); + it("serialized httpResponse correctly with body string", () => { + const httpResponse = new HTTPResponse({}, "hello"); + expect(httpResponse.text).toBe("hello"); expect(httpResponse.data).toBe(undefined); - expect(httpResponse.body).toBe('hello'); + expect(httpResponse.body).toBe("hello"); const serialized = JSON.stringify(httpResponse); const result = JSON.parse(serialized); - expect(result.text).toBe('hello'); + expect(result.text).toBe("hello"); expect(result.data).toBe(undefined); expect(result.body).toBe(undefined); }); - it('serialized httpResponse correctly with body object', () => { - const httpResponse = new HTTPResponse({}, { foo: 'bar' }); + it("serialized httpResponse correctly with body object", () => { + const httpResponse = new HTTPResponse({}, { foo: "bar" }); Parse._encode(httpResponse); const serialized = JSON.stringify(httpResponse); const result = JSON.parse(serialized); expect(httpResponse.text).toEqual('{"foo":"bar"}'); - expect(httpResponse.data).toEqual({ foo: 'bar' }); - expect(httpResponse.body).toEqual({ foo: 'bar' }); + expect(httpResponse.data).toEqual({ foo: "bar" }); + expect(httpResponse.body).toEqual({ foo: "bar" }); expect(result.text).toEqual('{"foo":"bar"}'); - expect(result.data).toEqual({ foo: 'bar' }); + expect(result.data).toEqual({ foo: "bar" }); expect(result.body).toEqual(undefined); }); - it('serialized httpResponse correctly with body buffer string', () => { - const httpResponse = new HTTPResponse({}, Buffer.from('hello')); - expect(httpResponse.text).toBe('hello'); + it("serialized httpResponse correctly with body buffer string", () => { + const httpResponse = new HTTPResponse({}, Buffer.from("hello")); + expect(httpResponse.text).toBe("hello"); expect(httpResponse.data).toBe(undefined); const serialized = JSON.stringify(httpResponse); const result = JSON.parse(serialized); - expect(result.text).toBe('hello'); + expect(result.text).toBe("hello"); expect(result.data).toBe(undefined); }); - it('serialized httpResponse correctly with body buffer JSON Object', () => { + it("serialized httpResponse correctly with body buffer JSON Object", () => { const json = '{"foo":"bar"}'; const httpResponse = new HTTPResponse({}, Buffer.from(json)); const serialized = JSON.stringify(httpResponse); const result = JSON.parse(serialized); expect(result.text).toEqual('{"foo":"bar"}'); - expect(result.data).toEqual({ foo: 'bar' }); + expect(result.data).toEqual({ foo: "bar" }); }); - it('serialized httpResponse with Parse._encode should be allright', () => { + it("serialized httpResponse with Parse._encode should be allright", () => { const json = '{"foo":"bar"}'; const httpResponse = new HTTPResponse({}, Buffer.from(json)); const encoded = Parse._encode(httpResponse); @@ -250,13 +252,13 @@ describe('httpRequest', () => { foundBody = false; for (const key in encoded) { - if (key === 'data') { + if (key === "data") { foundData = true; } - if (key === 'text') { + if (key === "text") { foundText = true; } - if (key === 'body') { + if (key === "body") { foundBody = true; } } diff --git a/spec/Idempotency.spec.js b/spec/Idempotency.spec.js index fd666d4360..8dd52108fc 100644 --- a/spec/Idempotency.spec.js +++ b/spec/Idempotency.spec.js @@ -1,12 +1,12 @@ -'use strict'; -const Config = require('../lib/Config'); -const Definitions = require('../lib/Options/Definitions'); -const request = require('../lib/request'); -const rest = require('../lib/rest'); -const auth = require('../lib/Auth'); -const uuid = require('uuid'); +"use strict"; +const Config = require("../lib/Config"); +const Definitions = require("../lib/Options/Definitions"); +const request = require("../lib/request"); +const rest = require("../lib/rest"); +const auth = require("../lib/Auth"); +const uuid = require("uuid"); -describe('Idempotency', () => { +describe("Idempotency", () => { // Parameters /** Enable TTL expiration simulated by removing entry instead of waiting for MongoDB TTL monitor which runs only every 60s, so it can take up to 119s until entry removal - ain't nobody got time for that */ @@ -20,11 +20,16 @@ describe('Idempotency', () => { const res = await rest.find( config, auth.master(config), - '_Idempotency', + "_Idempotency", { reqId: reqId }, { limit: 1 } ); - await rest.del(config, auth.master(config), '_Idempotency', res.results[0].objectId); + await rest.del( + config, + auth.master(config), + "_Idempotency", + res.results[0].objectId + ); } async function setup(options) { await reconfigureServer({ @@ -40,61 +45,68 @@ describe('Idempotency', () => { jasmine.DEFAULT_TIMEOUT_INTERVAL = 200000; } await setup({ - paths: ['functions/.*', 'jobs/.*', 'classes/.*', 'users', 'installations'], + paths: [ + "functions/.*", + "jobs/.*", + "classes/.*", + "users", + "installations", + ], ttl: ttl, }); }); afterEach(() => { - jasmine.DEFAULT_TIMEOUT_INTERVAL = process.env.PARSE_SERVER_TEST_TIMEOUT || 10000; + jasmine.DEFAULT_TIMEOUT_INTERVAL = + process.env.PARSE_SERVER_TEST_TIMEOUT || 10000; }); // Tests - it_id('e25955fd-92eb-4b22-b8b7-38980e5cb223')(it)( - 'should enforce idempotency for cloud code function', + it_id("e25955fd-92eb-4b22-b8b7-38980e5cb223")(it)( + "should enforce idempotency for cloud code function", async () => { let counter = 0; - Parse.Cloud.define('myFunction', () => { + Parse.Cloud.define("myFunction", () => { counter++; }); const params = { - method: 'POST', - url: 'http://localhost:8378/1/functions/myFunction', + method: "POST", + url: "http://localhost:8378/1/functions/myFunction", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': Parse.masterKey, - 'X-Parse-Request-Id': 'abc-123', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Master-Key": Parse.masterKey, + "X-Parse-Request-Id": "abc-123", }, }; expect(Config.get(Parse.applicationId).idempotencyOptions.ttl).toBe(ttl); await request(params); await request(params).then(fail, e => { expect(e.status).toEqual(400); - expect(e.data.error).toEqual('Duplicate request'); + expect(e.data.error).toEqual("Duplicate request"); }); expect(counter).toBe(1); } ); - it_id('be2fbe16-8178-485e-9a12-6fb541096480')(it)( - 'should delete request entry after TTL', + it_id("be2fbe16-8178-485e-9a12-6fb541096480")(it)( + "should delete request entry after TTL", async () => { let counter = 0; - Parse.Cloud.define('myFunction', () => { + Parse.Cloud.define("myFunction", () => { counter++; }); const params = { - method: 'POST', - url: 'http://localhost:8378/1/functions/myFunction', + method: "POST", + url: "http://localhost:8378/1/functions/myFunction", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': Parse.masterKey, - 'X-Parse-Request-Id': 'abc-123', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Master-Key": Parse.masterKey, + "X-Parse-Request-Id": "abc-123", }, }; await expectAsync(request(params)).toBeResolved(); if (SIMULATE_TTL) { - await deleteRequestEntry('abc-123'); + await deleteRequestEntry("abc-123"); } else { await new Promise(resolve => setTimeout(resolve, maxTimeOut)); } @@ -103,155 +115,155 @@ describe('Idempotency', () => { } ); - it_only_db('postgres')( - 'should delete request entry when postgress ttl function is called', + it_only_db("postgres")( + "should delete request entry when postgress ttl function is called", async () => { const client = Config.get(Parse.applicationId).database.adapter._client; let counter = 0; - Parse.Cloud.define('myFunction', () => { + Parse.Cloud.define("myFunction", () => { counter++; }); const params = { - method: 'POST', - url: 'http://localhost:8378/1/functions/myFunction', + method: "POST", + url: "http://localhost:8378/1/functions/myFunction", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': Parse.masterKey, - 'X-Parse-Request-Id': 'abc-123', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Master-Key": Parse.masterKey, + "X-Parse-Request-Id": "abc-123", }, }; await expectAsync(request(params)).toBeResolved(); await expectAsync(request(params)).toBeRejected(); await new Promise(resolve => setTimeout(resolve, maxTimeOut)); - await client.one('SELECT idempotency_delete_expired_records()'); + await client.one("SELECT idempotency_delete_expired_records()"); await expectAsync(request(params)).toBeResolved(); expect(counter).toBe(2); } ); - it_id('e976d0cc-a57f-45d4-9472-b9b052db6490')(it)( - 'should enforce idempotency for cloud code jobs', + it_id("e976d0cc-a57f-45d4-9472-b9b052db6490")(it)( + "should enforce idempotency for cloud code jobs", async () => { let counter = 0; - Parse.Cloud.job('myJob', () => { + Parse.Cloud.job("myJob", () => { counter++; }); const params = { - method: 'POST', - url: 'http://localhost:8378/1/jobs/myJob', + method: "POST", + url: "http://localhost:8378/1/jobs/myJob", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': Parse.masterKey, - 'X-Parse-Request-Id': 'abc-123', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Master-Key": Parse.masterKey, + "X-Parse-Request-Id": "abc-123", }, }; await expectAsync(request(params)).toBeResolved(); await request(params).then(fail, e => { expect(e.status).toEqual(400); - expect(e.data.error).toEqual('Duplicate request'); + expect(e.data.error).toEqual("Duplicate request"); }); expect(counter).toBe(1); } ); - it_id('7c84a3d4-e1b6-4a0d-99f1-af3cf1a6b3d8')(it)( - 'should enforce idempotency for class object creation', + it_id("7c84a3d4-e1b6-4a0d-99f1-af3cf1a6b3d8")(it)( + "should enforce idempotency for class object creation", async () => { let counter = 0; - Parse.Cloud.afterSave('MyClass', () => { + Parse.Cloud.afterSave("MyClass", () => { counter++; }); const params = { - method: 'POST', - url: 'http://localhost:8378/1/classes/MyClass', + method: "POST", + url: "http://localhost:8378/1/classes/MyClass", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': Parse.masterKey, - 'X-Parse-Request-Id': 'abc-123', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Master-Key": Parse.masterKey, + "X-Parse-Request-Id": "abc-123", }, }; await expectAsync(request(params)).toBeResolved(); await request(params).then(fail, e => { expect(e.status).toEqual(400); - expect(e.data.error).toEqual('Duplicate request'); + expect(e.data.error).toEqual("Duplicate request"); }); expect(counter).toBe(1); } ); - it_id('a030f2dd-5d21-46ac-b53d-9d714f35d72a')(it)( - 'should enforce idempotency for user object creation', + it_id("a030f2dd-5d21-46ac-b53d-9d714f35d72a")(it)( + "should enforce idempotency for user object creation", async () => { let counter = 0; - Parse.Cloud.afterSave('_User', () => { + Parse.Cloud.afterSave("_User", () => { counter++; }); const params = { - method: 'POST', - url: 'http://localhost:8378/1/users', + method: "POST", + url: "http://localhost:8378/1/users", body: { - username: 'user', - password: 'pass', + username: "user", + password: "pass", }, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': Parse.masterKey, - 'X-Parse-Request-Id': 'abc-123', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Master-Key": Parse.masterKey, + "X-Parse-Request-Id": "abc-123", }, }; await expectAsync(request(params)).toBeResolved(); await request(params).then(fail, e => { expect(e.status).toEqual(400); - expect(e.data.error).toEqual('Duplicate request'); + expect(e.data.error).toEqual("Duplicate request"); }); expect(counter).toBe(1); } ); - it_id('064c469b-091c-4ba9-9043-be461f26a3eb')(it)( - 'should enforce idempotency for installation object creation', + it_id("064c469b-091c-4ba9-9043-be461f26a3eb")(it)( + "should enforce idempotency for installation object creation", async () => { let counter = 0; - Parse.Cloud.afterSave('_Installation', () => { + Parse.Cloud.afterSave("_Installation", () => { counter++; }); const params = { - method: 'POST', - url: 'http://localhost:8378/1/installations', + method: "POST", + url: "http://localhost:8378/1/installations", body: { - installationId: '1', - deviceType: 'ios', + installationId: "1", + deviceType: "ios", }, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': Parse.masterKey, - 'X-Parse-Request-Id': 'abc-123', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Master-Key": Parse.masterKey, + "X-Parse-Request-Id": "abc-123", }, }; await expectAsync(request(params)).toBeResolved(); await request(params).then(fail, e => { expect(e.status).toEqual(400); - expect(e.data.error).toEqual('Duplicate request'); + expect(e.data.error).toEqual("Duplicate request"); }); expect(counter).toBe(1); } ); - it_id('f11670b6-fa9c-4f21-a268-ae4b6bbff7fd')(it)( - 'should not interfere with calls of different request ID', + it_id("f11670b6-fa9c-4f21-a268-ae4b6bbff7fd")(it)( + "should not interfere with calls of different request ID", async () => { let counter = 0; - Parse.Cloud.afterSave('MyClass', () => { + Parse.Cloud.afterSave("MyClass", () => { counter++; }); const promises = [...Array(100).keys()].map(() => { const params = { - method: 'POST', - url: 'http://localhost:8378/1/classes/MyClass', + method: "POST", + url: "http://localhost:8378/1/classes/MyClass", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': Parse.masterKey, - 'X-Parse-Request-Id': uuid.v4(), + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Master-Key": Parse.masterKey, + "X-Parse-Request-Id": uuid.v4(), }, }; return request(params); @@ -261,28 +273,30 @@ describe('Idempotency', () => { } ); - it_id('0ecd2cd2-dafb-4a2b-bb2b-9ad4c9aca777')(it)( - 'should re-throw any other error unchanged when writing request entry fails for any other reason', + it_id("0ecd2cd2-dafb-4a2b-bb2b-9ad4c9aca777")(it)( + "should re-throw any other error unchanged when writing request entry fails for any other reason", async () => { - spyOn(rest, 'create').and.rejectWith(new Parse.Error(0, 'some other error')); - Parse.Cloud.define('myFunction', () => {}); + spyOn(rest, "create").and.rejectWith( + new Parse.Error(0, "some other error") + ); + Parse.Cloud.define("myFunction", () => {}); const params = { - method: 'POST', - url: 'http://localhost:8378/1/functions/myFunction', + method: "POST", + url: "http://localhost:8378/1/functions/myFunction", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': Parse.masterKey, - 'X-Parse-Request-Id': 'abc-123', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Master-Key": Parse.masterKey, + "X-Parse-Request-Id": "abc-123", }, }; await request(params).then(fail, e => { expect(e.status).toEqual(400); - expect(e.data.error).toEqual('some other error'); + expect(e.data.error).toEqual("some other error"); }); } ); - it('should use default configuration when none is set', async () => { + it("should use default configuration when none is set", async () => { await setup({}); expect(Config.get(Parse.applicationId).idempotencyOptions.ttl).toBe( Definitions.IdempotencyOptions.ttl.default @@ -292,9 +306,9 @@ describe('Idempotency', () => { ); }); - it('should throw on invalid configuration', async () => { + it("should throw on invalid configuration", async () => { await expectAsync(setup({ paths: 1 })).toBeRejected(); - await expectAsync(setup({ ttl: 'a' })).toBeRejected(); + await expectAsync(setup({ ttl: "a" })).toBeRejected(); await expectAsync(setup({ ttl: 0 })).toBeRejected(); await expectAsync(setup({ ttl: -1 })).toBeRejected(); }); diff --git a/spec/InMemoryCache.spec.js b/spec/InMemoryCache.spec.js index 4a474b7fa2..a63f388be2 100644 --- a/spec/InMemoryCache.spec.js +++ b/spec/InMemoryCache.spec.js @@ -1,16 +1,16 @@ -const InMemoryCache = require('../lib/Adapters/Cache/InMemoryCache').default; +const InMemoryCache = require("../lib/Adapters/Cache/InMemoryCache").default; -describe('InMemoryCache', function () { +describe("InMemoryCache", function () { const BASE_TTL = { ttl: 100, }; const NO_EXPIRE_TTL = { ttl: NaN, }; - const KEY = 'hello'; - const KEY_2 = KEY + '_2'; + const KEY = "hello"; + const KEY_2 = KEY + "_2"; - const VALUE = 'world'; + const VALUE = "world"; function wait(sleep) { return new Promise(function (resolve) { @@ -18,7 +18,7 @@ describe('InMemoryCache', function () { }); } - it('should destroy a expire items in the cache', done => { + it("should destroy a expire items in the cache", done => { const cache = new InMemoryCache(BASE_TTL); cache.put(KEY, VALUE); @@ -33,7 +33,7 @@ describe('InMemoryCache', function () { }); }); - it('should delete items', done => { + it("should delete items", done => { const cache = new InMemoryCache(NO_EXPIRE_TTL); cache.put(KEY, VALUE); cache.put(KEY_2, VALUE); @@ -50,7 +50,7 @@ describe('InMemoryCache', function () { done(); }); - it('should clear all items', done => { + it("should clear all items", done => { const cache = new InMemoryCache(NO_EXPIRE_TTL); cache.put(KEY, VALUE); cache.put(KEY_2, VALUE); @@ -64,7 +64,7 @@ describe('InMemoryCache', function () { done(); }); - it('should deafult TTL to 5 seconds', () => { + it("should deafult TTL to 5 seconds", () => { const cache = new InMemoryCache({}); expect(cache.ttl).toEqual(5 * 1000); }); diff --git a/spec/InMemoryCacheAdapter.spec.js b/spec/InMemoryCacheAdapter.spec.js index add976fbc9..2ed888ea3a 100644 --- a/spec/InMemoryCacheAdapter.spec.js +++ b/spec/InMemoryCacheAdapter.spec.js @@ -1,8 +1,9 @@ -const InMemoryCacheAdapter = require('../lib/Adapters/Cache/InMemoryCacheAdapter').default; +const InMemoryCacheAdapter = + require("../lib/Adapters/Cache/InMemoryCacheAdapter").default; -describe('InMemoryCacheAdapter', function () { - const KEY = 'hello'; - const VALUE = 'world'; +describe("InMemoryCacheAdapter", function () { + const KEY = "hello"; + const VALUE = "world"; function wait(sleep) { return new Promise(function (resolve) { @@ -10,18 +11,23 @@ describe('InMemoryCacheAdapter', function () { }); } - it('should expose promisifyed methods', done => { + it("should expose promisifyed methods", done => { const cache = new InMemoryCacheAdapter({ ttl: NaN, }); // Verify all methods return promises. - Promise.all([cache.put(KEY, VALUE), cache.del(KEY), cache.get(KEY), cache.clear()]).then(() => { + Promise.all([ + cache.put(KEY, VALUE), + cache.del(KEY), + cache.get(KEY), + cache.clear(), + ]).then(() => { done(); }); }); - it('should get/set/clear', done => { + it("should get/set/clear", done => { const cache = new InMemoryCacheAdapter({ ttl: NaN, }); @@ -36,7 +42,7 @@ describe('InMemoryCacheAdapter', function () { .then(done); }); - it('should expire after ttl', done => { + it("should expire after ttl", done => { const cache = new InMemoryCacheAdapter({ ttl: 10, }); diff --git a/spec/InstallationsRouter.spec.js b/spec/InstallationsRouter.spec.js index 8e5a80c135..1792c9d513 100644 --- a/spec/InstallationsRouter.spec.js +++ b/spec/InstallationsRouter.spec.js @@ -1,25 +1,26 @@ -const auth = require('../lib/Auth'); -const Config = require('../lib/Config'); -const rest = require('../lib/rest'); -const InstallationsRouter = require('../lib/Routers/InstallationsRouter').InstallationsRouter; +const auth = require("../lib/Auth"); +const Config = require("../lib/Config"); +const rest = require("../lib/rest"); +const InstallationsRouter = + require("../lib/Routers/InstallationsRouter").InstallationsRouter; -describe('InstallationsRouter', () => { - it('uses find condition from request.body', done => { - const config = Config.get('test'); +describe("InstallationsRouter", () => { + it("uses find condition from request.body", done => { + const config = Config.get("test"); const androidDeviceRequest = { - installationId: '12345678-abcd-abcd-abcd-123456789abc', - deviceType: 'android', + installationId: "12345678-abcd-abcd-abcd-123456789abc", + deviceType: "android", }; const iosDeviceRequest = { - installationId: '12345678-abcd-abcd-abcd-123456789abd', - deviceType: 'ios', + installationId: "12345678-abcd-abcd-abcd-123456789abd", + deviceType: "ios", }; const request = { config: config, auth: auth.master(config), body: { where: { - deviceType: 'android', + deviceType: "android", }, }, query: {}, @@ -28,9 +29,19 @@ describe('InstallationsRouter', () => { const router = new InstallationsRouter(); rest - .create(config, auth.nobody(config), '_Installation', androidDeviceRequest) + .create( + config, + auth.nobody(config), + "_Installation", + androidDeviceRequest + ) .then(() => { - return rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest); + return rest.create( + config, + auth.nobody(config), + "_Installation", + iosDeviceRequest + ); }) .then(() => { return router.handleFind(request); @@ -46,15 +57,15 @@ describe('InstallationsRouter', () => { }); }); - it('uses find condition from request.query', done => { - const config = Config.get('test'); + it("uses find condition from request.query", done => { + const config = Config.get("test"); const androidDeviceRequest = { - installationId: '12345678-abcd-abcd-abcd-123456789abc', - deviceType: 'android', + installationId: "12345678-abcd-abcd-abcd-123456789abc", + deviceType: "android", }; const iosDeviceRequest = { - installationId: '12345678-abcd-abcd-abcd-123456789abd', - deviceType: 'ios', + installationId: "12345678-abcd-abcd-abcd-123456789abd", + deviceType: "ios", }; const request = { config: config, @@ -62,7 +73,7 @@ describe('InstallationsRouter', () => { body: {}, query: { where: { - deviceType: 'android', + deviceType: "android", }, }, info: {}, @@ -70,9 +81,19 @@ describe('InstallationsRouter', () => { const router = new InstallationsRouter(); rest - .create(config, auth.nobody(config), '_Installation', androidDeviceRequest) + .create( + config, + auth.nobody(config), + "_Installation", + androidDeviceRequest + ) .then(() => { - return rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest); + return rest.create( + config, + auth.nobody(config), + "_Installation", + iosDeviceRequest + ); }) .then(() => { return router.handleFind(request); @@ -88,15 +109,15 @@ describe('InstallationsRouter', () => { }); }); - it('query installations with limit = 0', done => { - const config = Config.get('test'); + it("query installations with limit = 0", done => { + const config = Config.get("test"); const androidDeviceRequest = { - installationId: '12345678-abcd-abcd-abcd-123456789abc', - deviceType: 'android', + installationId: "12345678-abcd-abcd-abcd-123456789abc", + deviceType: "android", }; const iosDeviceRequest = { - installationId: '12345678-abcd-abcd-abcd-123456789abd', - deviceType: 'ios', + installationId: "12345678-abcd-abcd-abcd-123456789abd", + deviceType: "ios", }; const request = { config: config, @@ -108,12 +129,22 @@ describe('InstallationsRouter', () => { info: {}, }; - Config.get('test'); + Config.get("test"); const router = new InstallationsRouter(); rest - .create(config, auth.nobody(config), '_Installation', androidDeviceRequest) + .create( + config, + auth.nobody(config), + "_Installation", + androidDeviceRequest + ) .then(() => { - return rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest); + return rest.create( + config, + auth.nobody(config), + "_Installation", + iosDeviceRequest + ); }) .then(() => { return router.handleFind(request); @@ -129,15 +160,15 @@ describe('InstallationsRouter', () => { }); }); - it_exclude_dbs(['postgres'])('query installations with count = 1', done => { - const config = Config.get('test'); + it_exclude_dbs(["postgres"])("query installations with count = 1", done => { + const config = Config.get("test"); const androidDeviceRequest = { - installationId: '12345678-abcd-abcd-abcd-123456789abc', - deviceType: 'android', + installationId: "12345678-abcd-abcd-abcd-123456789abc", + deviceType: "android", }; const iosDeviceRequest = { - installationId: '12345678-abcd-abcd-abcd-123456789abd', - deviceType: 'ios', + installationId: "12345678-abcd-abcd-abcd-123456789abd", + deviceType: "ios", }; const request = { config: config, @@ -151,8 +182,20 @@ describe('InstallationsRouter', () => { const router = new InstallationsRouter(); rest - .create(config, auth.nobody(config), '_Installation', androidDeviceRequest) - .then(() => rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest)) + .create( + config, + auth.nobody(config), + "_Installation", + androidDeviceRequest + ) + .then(() => + rest.create( + config, + auth.nobody(config), + "_Installation", + iosDeviceRequest + ) + ) .then(() => router.handleFind(request)) .then(res => { const response = res.response; @@ -166,15 +209,15 @@ describe('InstallationsRouter', () => { }); }); - it_only_db('postgres')('query installations with count = 1', async () => { - const config = Config.get('test'); + it_only_db("postgres")("query installations with count = 1", async () => { + const config = Config.get("test"); const androidDeviceRequest = { - installationId: '12345678-abcd-abcd-abcd-123456789abc', - deviceType: 'android', + installationId: "12345678-abcd-abcd-abcd-123456789abc", + deviceType: "android", }; const iosDeviceRequest = { - installationId: '12345678-abcd-abcd-abcd-123456789abd', - deviceType: 'ios', + installationId: "12345678-abcd-abcd-abcd-123456789abd", + deviceType: "ios", }; const request = { config: config, @@ -187,15 +230,25 @@ describe('InstallationsRouter', () => { }; const router = new InstallationsRouter(); - await rest.create(config, auth.nobody(config), '_Installation', androidDeviceRequest); - await rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest); + await rest.create( + config, + auth.nobody(config), + "_Installation", + androidDeviceRequest + ); + await rest.create( + config, + auth.nobody(config), + "_Installation", + iosDeviceRequest + ); let res = await router.handleFind(request); let response = res.response; expect(response.results.length).toEqual(2); expect(response.count).toEqual(0); // estimate count is zero const pgAdapter = config.database.adapter; - await pgAdapter.updateEstimatedCount('_Installation'); + await pgAdapter.updateEstimatedCount("_Installation"); res = await router.handleFind(request); response = res.response; @@ -203,45 +256,58 @@ describe('InstallationsRouter', () => { expect(response.count).toEqual(2); }); - it_exclude_dbs(['postgres'])('query installations with limit = 0 and count = 1', done => { - const config = Config.get('test'); - const androidDeviceRequest = { - installationId: '12345678-abcd-abcd-abcd-123456789abc', - deviceType: 'android', - }; - const iosDeviceRequest = { - installationId: '12345678-abcd-abcd-abcd-123456789abd', - deviceType: 'ios', - }; - const request = { - config: config, - auth: auth.master(config), - body: {}, - query: { - limit: 0, - count: 1, - }, - info: {}, - }; + it_exclude_dbs(["postgres"])( + "query installations with limit = 0 and count = 1", + done => { + const config = Config.get("test"); + const androidDeviceRequest = { + installationId: "12345678-abcd-abcd-abcd-123456789abc", + deviceType: "android", + }; + const iosDeviceRequest = { + installationId: "12345678-abcd-abcd-abcd-123456789abd", + deviceType: "ios", + }; + const request = { + config: config, + auth: auth.master(config), + body: {}, + query: { + limit: 0, + count: 1, + }, + info: {}, + }; - const router = new InstallationsRouter(); - rest - .create(config, auth.nobody(config), '_Installation', androidDeviceRequest) - .then(() => { - return rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest); - }) - .then(() => { - return router.handleFind(request); - }) - .then(res => { - const response = res.response; - expect(response.results.length).toEqual(0); - expect(response.count).toEqual(2); - done(); - }) - .catch(err => { - fail(JSON.stringify(err)); - done(); - }); - }); + const router = new InstallationsRouter(); + rest + .create( + config, + auth.nobody(config), + "_Installation", + androidDeviceRequest + ) + .then(() => { + return rest.create( + config, + auth.nobody(config), + "_Installation", + iosDeviceRequest + ); + }) + .then(() => { + return router.handleFind(request); + }) + .then(res => { + const response = res.response; + expect(response.results.length).toEqual(0); + expect(response.count).toEqual(2); + done(); + }) + .catch(err => { + fail(JSON.stringify(err)); + done(); + }); + } + ); }); diff --git a/spec/JobSchedule.spec.js b/spec/JobSchedule.spec.js index b4a75764dd..c722ed4e3a 100644 --- a/spec/JobSchedule.spec.js +++ b/spec/JobSchedule.spec.js @@ -1,15 +1,15 @@ -const request = require('../lib/request'); +const request = require("../lib/request"); const defaultHeaders = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Rest-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": "test", + "X-Parse-Rest-API-Key": "rest", + "Content-Type": "application/json", }; const masterKeyHeaders = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Rest-API-Key': 'rest', - 'X-Parse-Master-Key': 'test', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": "test", + "X-Parse-Rest-API-Key": "rest", + "X-Parse-Master-Key": "test", + "Content-Type": "application/json", }; const defaultOptions = { headers: defaultHeaders, @@ -20,11 +20,11 @@ const masterKeyOptions = { json: true, }; -describe('JobSchedule', () => { - it('should create _JobSchedule with masterKey', done => { - const jobSchedule = new Parse.Object('_JobSchedule'); +describe("JobSchedule", () => { + it("should create _JobSchedule with masterKey", done => { + const jobSchedule = new Parse.Object("_JobSchedule"); jobSchedule.set({ - jobName: 'MY Cool Job', + jobName: "MY Cool Job", }); jobSchedule .save(null, { useMasterKey: true }) @@ -34,10 +34,10 @@ describe('JobSchedule', () => { .catch(done.fail); }); - it('should fail creating _JobSchedule without masterKey', done => { - const jobSchedule = new Parse.Object('_JobSchedule'); + it("should fail creating _JobSchedule without masterKey", done => { + const jobSchedule = new Parse.Object("_JobSchedule"); jobSchedule.set({ - jobName: 'SomeJob', + jobName: "SomeJob", }); jobSchedule .save(null) @@ -45,53 +45,59 @@ describe('JobSchedule', () => { .catch(() => done()); }); - it('should reject access when not using masterKey (/jobs)', done => { - request(Object.assign({ url: Parse.serverURL + '/cloud_code/jobs' }, defaultOptions)).then( - done.fail, - () => done() - ); + it("should reject access when not using masterKey (/jobs)", done => { + request( + Object.assign( + { url: Parse.serverURL + "/cloud_code/jobs" }, + defaultOptions + ) + ).then(done.fail, () => done()); }); - it('should reject access when not using masterKey (/jobs/data)', done => { - request(Object.assign({ url: Parse.serverURL + '/cloud_code/jobs/data' }, defaultOptions)).then( - done.fail, - () => done() - ); + it("should reject access when not using masterKey (/jobs/data)", done => { + request( + Object.assign( + { url: Parse.serverURL + "/cloud_code/jobs/data" }, + defaultOptions + ) + ).then(done.fail, () => done()); }); - it('should reject access when not using masterKey (PUT /jobs/id)', done => { + it("should reject access when not using masterKey (PUT /jobs/id)", done => { request( Object.assign( - { method: 'PUT', url: Parse.serverURL + '/cloud_code/jobs/jobId' }, + { method: "PUT", url: Parse.serverURL + "/cloud_code/jobs/jobId" }, defaultOptions ) ).then(done.fail, () => done()); }); - it('should reject access when not using masterKey (DELETE /jobs/id)', done => { + it("should reject access when not using masterKey (DELETE /jobs/id)", done => { request( Object.assign( - { method: 'DELETE', url: Parse.serverURL + '/cloud_code/jobs/jobId' }, + { method: "DELETE", url: Parse.serverURL + "/cloud_code/jobs/jobId" }, defaultOptions ) ).then(done.fail, () => done()); }); - it('should allow access when using masterKey (GET /jobs)', done => { - request(Object.assign({ url: Parse.serverURL + '/cloud_code/jobs' }, masterKeyOptions)).then( - done, - done.fail - ); + it("should allow access when using masterKey (GET /jobs)", done => { + request( + Object.assign( + { url: Parse.serverURL + "/cloud_code/jobs" }, + masterKeyOptions + ) + ).then(done, done.fail); }); - it('should create a job schedule', done => { - Parse.Cloud.job('job', () => {}); + it("should create a job schedule", done => { + Parse.Cloud.job("job", () => {}); const options = Object.assign({}, masterKeyOptions, { - method: 'POST', - url: Parse.serverURL + '/cloud_code/jobs', + method: "POST", + url: Parse.serverURL + "/cloud_code/jobs", body: { job_schedule: { - jobName: 'job', + jobName: "job", }, }, }); @@ -101,7 +107,10 @@ describe('JobSchedule', () => { }) .then(() => { return request( - Object.assign({ url: Parse.serverURL + '/cloud_code/jobs' }, masterKeyOptions) + Object.assign( + { url: Parse.serverURL + "/cloud_code/jobs" }, + masterKeyOptions + ) ); }) .then(res => { @@ -111,13 +120,13 @@ describe('JobSchedule', () => { .catch(done.fail); }); - it('should fail creating a job with an invalid name', done => { + it("should fail creating a job with an invalid name", done => { const options = Object.assign({}, masterKeyOptions, { - url: Parse.serverURL + '/cloud_code/jobs', - method: 'POST', + url: Parse.serverURL + "/cloud_code/jobs", + method: "POST", body: { job_schedule: { - jobName: 'job', + jobName: "job", }, }, }); @@ -126,15 +135,15 @@ describe('JobSchedule', () => { .catch(() => done()); }); - it('should update a job', done => { - Parse.Cloud.job('job1', () => {}); - Parse.Cloud.job('job2', () => {}); + it("should update a job", done => { + Parse.Cloud.job("job1", () => {}); + Parse.Cloud.job("job2", () => {}); const options = Object.assign({}, masterKeyOptions, { - method: 'POST', - url: Parse.serverURL + '/cloud_code/jobs', + method: "POST", + url: Parse.serverURL + "/cloud_code/jobs", body: { job_schedule: { - jobName: 'job1', + jobName: "job1", }, }, }); @@ -143,11 +152,11 @@ describe('JobSchedule', () => { expect(res.data.objectId).not.toBeUndefined(); return request( Object.assign(options, { - url: Parse.serverURL + '/cloud_code/jobs/' + res.data.objectId, - method: 'PUT', + url: Parse.serverURL + "/cloud_code/jobs/" + res.data.objectId, + method: "PUT", body: { job_schedule: { - jobName: 'job2', + jobName: "job2", }, }, }) @@ -156,26 +165,26 @@ describe('JobSchedule', () => { .then(() => { return request( Object.assign({}, masterKeyOptions, { - url: Parse.serverURL + '/cloud_code/jobs', + url: Parse.serverURL + "/cloud_code/jobs", }) ); }) .then(res => { expect(res.data.length).toBe(1); - expect(res.data[0].jobName).toBe('job2'); + expect(res.data[0].jobName).toBe("job2"); }) .then(done) .catch(done.fail); }); - it('should fail updating a job with an invalid name', done => { - Parse.Cloud.job('job1', () => {}); + it("should fail updating a job with an invalid name", done => { + Parse.Cloud.job("job1", () => {}); const options = Object.assign({}, masterKeyOptions, { - method: 'POST', - url: Parse.serverURL + '/cloud_code/jobs', + method: "POST", + url: Parse.serverURL + "/cloud_code/jobs", body: { job_schedule: { - jobName: 'job1', + jobName: "job1", }, }, }); @@ -184,11 +193,11 @@ describe('JobSchedule', () => { expect(res.data.objectId).not.toBeUndefined(); return request( Object.assign(options, { - method: 'PUT', - url: Parse.serverURL + '/cloud_code/jobs/' + res.data.objectId, + method: "PUT", + url: Parse.serverURL + "/cloud_code/jobs/" + res.data.objectId, body: { job_schedule: { - jobName: 'job2', + jobName: "job2", }, }, }) @@ -198,14 +207,14 @@ describe('JobSchedule', () => { .catch(() => done()); }); - it('should destroy a job', done => { - Parse.Cloud.job('job', () => {}); + it("should destroy a job", done => { + Parse.Cloud.job("job", () => {}); const options = Object.assign({}, masterKeyOptions, { - method: 'POST', - url: Parse.serverURL + '/cloud_code/jobs', + method: "POST", + url: Parse.serverURL + "/cloud_code/jobs", body: { job_schedule: { - jobName: 'job', + jobName: "job", }, }, }); @@ -215,8 +224,8 @@ describe('JobSchedule', () => { return request( Object.assign( { - method: 'DELETE', - url: Parse.serverURL + '/cloud_code/jobs/' + res.data.objectId, + method: "DELETE", + url: Parse.serverURL + "/cloud_code/jobs/" + res.data.objectId, }, masterKeyOptions ) @@ -226,7 +235,7 @@ describe('JobSchedule', () => { return request( Object.assign( { - url: Parse.serverURL + '/cloud_code/jobs', + url: Parse.serverURL + "/cloud_code/jobs", }, masterKeyOptions ) @@ -239,15 +248,15 @@ describe('JobSchedule', () => { .catch(done.fail); }); - it('should properly return job data', done => { - Parse.Cloud.job('job1', () => {}); - Parse.Cloud.job('job2', () => {}); + it("should properly return job data", done => { + Parse.Cloud.job("job1", () => {}); + Parse.Cloud.job("job2", () => {}); const options = Object.assign({}, masterKeyOptions, { - method: 'POST', - url: Parse.serverURL + '/cloud_code/jobs', + method: "POST", + url: Parse.serverURL + "/cloud_code/jobs", body: { job_schedule: { - jobName: 'job1', + jobName: "job1", }, }, }); @@ -258,14 +267,17 @@ describe('JobSchedule', () => { }) .then(() => { return request( - Object.assign({ url: Parse.serverURL + '/cloud_code/jobs/data' }, masterKeyOptions) + Object.assign( + { url: Parse.serverURL + "/cloud_code/jobs/data" }, + masterKeyOptions + ) ); }) .then(response => { const res = response.data; - expect(res.in_use).toEqual(['job1']); - expect(res.jobs).toContain('job1'); - expect(res.jobs).toContain('job2'); + expect(res.in_use).toEqual(["job1"]); + expect(res.jobs).toContain("job1"); + expect(res.jobs).toContain("job2"); expect(res.jobs.length).toBe(2); }) .then(done) diff --git a/spec/LdapAuth.spec.js b/spec/LdapAuth.spec.js index ea30f59f0c..576fb660c2 100644 --- a/spec/LdapAuth.spec.js +++ b/spec/LdapAuth.spec.js @@ -1,212 +1,268 @@ -const ldap = require('../lib/Adapters/Auth/ldap'); -const mockLdapServer = require('./support/MockLdapServer'); -const fs = require('fs'); +const ldap = require("../lib/Adapters/Auth/ldap"); +const mockLdapServer = require("./support/MockLdapServer"); +const fs = require("fs"); const port = 12345; const sslport = 12346; -describe('Ldap Auth', () => { - it('Should fail with missing options', done => { +describe("Ldap Auth", () => { + it("Should fail with missing options", done => { ldap - .validateAuthData({ id: 'testuser', password: 'testpw' }) + .validateAuthData({ id: "testuser", password: "testpw" }) .then(done.fail) .catch(err => { - jequal(err.message, 'LDAP auth configuration missing'); + jequal(err.message, "LDAP auth configuration missing"); done(); }); }); - it('Should return a resolved promise when validating the app id', done => { + it("Should return a resolved promise when validating the app id", done => { ldap.validateAppId().then(done).catch(done.fail); }); - it('Should succeed with right credentials', async done => { - const server = await mockLdapServer(port, 'uid=testuser, o=example'); + it("Should succeed with right credentials", async done => { + const server = await mockLdapServer(port, "uid=testuser, o=example"); const options = { - suffix: 'o=example', + suffix: "o=example", url: `ldap://localhost:${port}`, - dn: 'uid={{id}}, o=example', + dn: "uid={{id}}, o=example", }; - await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options); + await ldap.validateAuthData( + { id: "testuser", password: "secret" }, + options + ); server.close(done); }); - it('Should succeed with right credentials when LDAPS is used and certifcate is not checked', async done => { - const server = await mockLdapServer(sslport, 'uid=testuser, o=example', false, true); + it("Should succeed with right credentials when LDAPS is used and certifcate is not checked", async done => { + const server = await mockLdapServer( + sslport, + "uid=testuser, o=example", + false, + true + ); const options = { - suffix: 'o=example', + suffix: "o=example", url: `ldaps://localhost:${sslport}`, - dn: 'uid={{id}}, o=example', + dn: "uid={{id}}, o=example", tlsOptions: { rejectUnauthorized: false }, }; - await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options); + await ldap.validateAuthData( + { id: "testuser", password: "secret" }, + options + ); server.close(done); }); - it('Should succeed when LDAPS is used and the presented certificate is the expected certificate', async done => { - const server = await mockLdapServer(sslport, 'uid=testuser, o=example', false, true); + it("Should succeed when LDAPS is used and the presented certificate is the expected certificate", async done => { + const server = await mockLdapServer( + sslport, + "uid=testuser, o=example", + false, + true + ); const options = { - suffix: 'o=example', + suffix: "o=example", url: `ldaps://localhost:${sslport}`, - dn: 'uid={{id}}, o=example', + dn: "uid={{id}}, o=example", tlsOptions: { - ca: fs.readFileSync(__dirname + '/support/cert/cert.pem'), + ca: fs.readFileSync(__dirname + "/support/cert/cert.pem"), rejectUnauthorized: true, }, }; - await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options); + await ldap.validateAuthData( + { id: "testuser", password: "secret" }, + options + ); server.close(done); }); - it('Should fail when LDAPS is used and the presented certificate is not the expected certificate', async done => { - const server = await mockLdapServer(sslport, 'uid=testuser, o=example', false, true); + it("Should fail when LDAPS is used and the presented certificate is not the expected certificate", async done => { + const server = await mockLdapServer( + sslport, + "uid=testuser, o=example", + false, + true + ); const options = { - suffix: 'o=example', + suffix: "o=example", url: `ldaps://localhost:${sslport}`, - dn: 'uid={{id}}, o=example', + dn: "uid={{id}}, o=example", tlsOptions: { - ca: fs.readFileSync(__dirname + '/support/cert/anothercert.pem'), + ca: fs.readFileSync(__dirname + "/support/cert/anothercert.pem"), rejectUnauthorized: true, }, }; try { - await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options); + await ldap.validateAuthData( + { id: "testuser", password: "secret" }, + options + ); fail(); } catch (err) { - expect(err.message).toBe('LDAPS: Certificate mismatch'); + expect(err.message).toBe("LDAPS: Certificate mismatch"); } server.close(done); }); - it('Should fail when LDAPS is used certifcate matches but credentials are wrong', async done => { - const server = await mockLdapServer(sslport, 'uid=testuser, o=example', false, true); + it("Should fail when LDAPS is used certifcate matches but credentials are wrong", async done => { + const server = await mockLdapServer( + sslport, + "uid=testuser, o=example", + false, + true + ); const options = { - suffix: 'o=example', + suffix: "o=example", url: `ldaps://localhost:${sslport}`, - dn: 'uid={{id}}, o=example', + dn: "uid={{id}}, o=example", tlsOptions: { - ca: fs.readFileSync(__dirname + '/support/cert/cert.pem'), + ca: fs.readFileSync(__dirname + "/support/cert/cert.pem"), rejectUnauthorized: true, }, }; try { - await ldap.validateAuthData({ id: 'testuser', password: 'wrong!' }, options); + await ldap.validateAuthData( + { id: "testuser", password: "wrong!" }, + options + ); fail(); } catch (err) { - expect(err.message).toBe('LDAP: Wrong username or password'); + expect(err.message).toBe("LDAP: Wrong username or password"); } server.close(done); }); - it('Should fail with wrong credentials', async done => { - const server = await mockLdapServer(port, 'uid=testuser, o=example'); + it("Should fail with wrong credentials", async done => { + const server = await mockLdapServer(port, "uid=testuser, o=example"); const options = { - suffix: 'o=example', + suffix: "o=example", url: `ldap://localhost:${port}`, - dn: 'uid={{id}}, o=example', + dn: "uid={{id}}, o=example", }; try { - await ldap.validateAuthData({ id: 'testuser', password: 'wrong!' }, options); + await ldap.validateAuthData( + { id: "testuser", password: "wrong!" }, + options + ); fail(); } catch (err) { - expect(err.message).toBe('LDAP: Wrong username or password'); + expect(err.message).toBe("LDAP: Wrong username or password"); } server.close(done); }); - it('Should succeed if user is in given group', async done => { - const server = await mockLdapServer(port, 'uid=testuser, o=example'); + it("Should succeed if user is in given group", async done => { + const server = await mockLdapServer(port, "uid=testuser, o=example"); const options = { - suffix: 'o=example', + suffix: "o=example", url: `ldap://localhost:${port}`, - dn: 'uid={{id}}, o=example', - groupCn: 'powerusers', - groupFilter: '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))', + dn: "uid={{id}}, o=example", + groupCn: "powerusers", + groupFilter: + "(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))", }; - await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options); + await ldap.validateAuthData( + { id: "testuser", password: "secret" }, + options + ); server.close(done); }); - it('Should fail if user is not in given group', async done => { - const server = await mockLdapServer(port, 'uid=testuser, o=example'); + it("Should fail if user is not in given group", async done => { + const server = await mockLdapServer(port, "uid=testuser, o=example"); const options = { - suffix: 'o=example', + suffix: "o=example", url: `ldap://localhost:${port}`, - dn: 'uid={{id}}, o=example', - groupCn: 'groupTheUserIsNotIn', - groupFilter: '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))', + dn: "uid={{id}}, o=example", + groupCn: "groupTheUserIsNotIn", + groupFilter: + "(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))", }; try { - await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options); + await ldap.validateAuthData( + { id: "testuser", password: "secret" }, + options + ); fail(); } catch (err) { - expect(err.message).toBe('LDAP: User not in group'); + expect(err.message).toBe("LDAP: User not in group"); } server.close(done); }); - it('Should fail if the LDAP server does not allow searching inside the provided suffix', async done => { - const server = await mockLdapServer(port, 'uid=testuser, o=example'); + it("Should fail if the LDAP server does not allow searching inside the provided suffix", async done => { + const server = await mockLdapServer(port, "uid=testuser, o=example"); const options = { - suffix: 'o=invalid', + suffix: "o=invalid", url: `ldap://localhost:${port}`, - dn: 'uid={{id}}, o=example', - groupCn: 'powerusers', - groupFilter: '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))', + dn: "uid={{id}}, o=example", + groupCn: "powerusers", + groupFilter: + "(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))", }; try { - await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options); + await ldap.validateAuthData( + { id: "testuser", password: "secret" }, + options + ); fail(); } catch (err) { - expect(err.message).toBe('LDAP group search failed'); + expect(err.message).toBe("LDAP group search failed"); } server.close(done); }); - it('Should fail if the LDAP server encounters an error while searching', async done => { - const server = await mockLdapServer(port, 'uid=testuser, o=example', true); + it("Should fail if the LDAP server encounters an error while searching", async done => { + const server = await mockLdapServer(port, "uid=testuser, o=example", true); const options = { - suffix: 'o=example', + suffix: "o=example", url: `ldap://localhost:${port}`, - dn: 'uid={{id}}, o=example', - groupCn: 'powerusers', - groupFilter: '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))', + dn: "uid={{id}}, o=example", + groupCn: "powerusers", + groupFilter: + "(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))", }; try { - await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options); + await ldap.validateAuthData( + { id: "testuser", password: "secret" }, + options + ); fail(); } catch (err) { - expect(err.message).toBe('LDAP group search failed'); + expect(err.message).toBe("LDAP group search failed"); } server.close(done); }); - it('Should delete the password from authData after validation', async done => { - const server = await mockLdapServer(port, 'uid=testuser, o=example', true); + it("Should delete the password from authData after validation", async done => { + const server = await mockLdapServer(port, "uid=testuser, o=example", true); const options = { - suffix: 'o=example', + suffix: "o=example", url: `ldap://localhost:${port}`, - dn: 'uid={{id}}, o=example', + dn: "uid={{id}}, o=example", }; - const authData = { id: 'testuser', password: 'secret' }; + const authData = { id: "testuser", password: "secret" }; await ldap.validateAuthData(authData, options); - expect(authData).toEqual({ id: 'testuser' }); + expect(authData).toEqual({ id: "testuser" }); server.close(done); }); - it('Should not save the password in the user record after authentication', async done => { - const server = await mockLdapServer(port, 'uid=testuser, o=example', true); + it("Should not save the password in the user record after authentication", async done => { + const server = await mockLdapServer(port, "uid=testuser, o=example", true); const options = { - suffix: 'o=example', + suffix: "o=example", url: `ldap://localhost:${port}`, - dn: 'uid={{id}}, o=example', + dn: "uid={{id}}, o=example", }; await reconfigureServer({ auth: { ldap: options } }); - const authData = { authData: { id: 'testuser', password: 'secret' } }; - const returnedUser = await Parse.User.logInWith('ldap', authData); - const query = new Parse.Query('User'); - const user = await query.equalTo('objectId', returnedUser.id).first({ useMasterKey: true }); - expect(user.get('authData')).toEqual({ ldap: { id: 'testuser' } }); - expect(user.get('authData').ldap.password).toBeUndefined(); + const authData = { authData: { id: "testuser", password: "secret" } }; + const returnedUser = await Parse.User.logInWith("ldap", authData); + const query = new Parse.Query("User"); + const user = await query + .equalTo("objectId", returnedUser.id) + .first({ useMasterKey: true }); + expect(user.get("authData")).toEqual({ ldap: { id: "testuser" } }); + expect(user.get("authData").ldap.password).toBeUndefined(); server.close(done); }); }); diff --git a/spec/Logger.spec.js b/spec/Logger.spec.js index 865c5b0c5c..e49bcfa244 100644 --- a/spec/Logger.spec.js +++ b/spec/Logger.spec.js @@ -1,5 +1,5 @@ -const logging = require('../lib/Adapters/Logger/WinstonLogger'); -const Transport = require('winston-transport'); +const logging = require("../lib/Adapters/Logger/WinstonLogger"); +const Transport = require("winston-transport"); class TestTransport extends Transport { log(info, callback) { @@ -7,21 +7,21 @@ class TestTransport extends Transport { } } -describe('WinstonLogger', () => { - it('should add transport', () => { +describe("WinstonLogger", () => { + it("should add transport", () => { const testTransport = new TestTransport(); - spyOn(testTransport, 'log'); + spyOn(testTransport, "log"); logging.addTransport(testTransport); expect(logging.logger.transports.length).toBe(4); - logging.logger.info('hi'); + logging.logger.info("hi"); expect(testTransport.log).toHaveBeenCalled(); - logging.logger.error('error'); + logging.logger.error("error"); expect(testTransport.log).toHaveBeenCalled(); logging.removeTransport(testTransport); expect(logging.logger.transports.length).toBe(3); }); - it('should have files transports', done => { + it("should have files transports", done => { reconfigureServer().then(() => { const transports = logging.logger.transports; expect(transports.length).toBe(3); @@ -29,7 +29,7 @@ describe('WinstonLogger', () => { }); }); - it('should disable files logs', done => { + it("should disable files logs", done => { reconfigureServer({ logsFolder: null, }) @@ -41,29 +41,29 @@ describe('WinstonLogger', () => { .then(done); }); - it('should have a timestamp', done => { - logging.logger.info('hi'); + it("should have a timestamp", done => { + logging.logger.info("hi"); logging.logger.query({ limit: 1 }, (err, results) => { if (err) { done.fail(err); } - expect(results['parse-server'][0].timestamp).toBeDefined(); + expect(results["parse-server"][0].timestamp).toBeDefined(); done(); }); }); - it('console should not be json', done => { + it("console should not be json", done => { // Force console transport reconfigureServer({ logsFolder: null, silent: false, }) .then(() => { - spyOn(process.stdout, 'write'); - logging.logger.info('hi', { key: 'value' }); + spyOn(process.stdout, "write"); + logging.logger.info("hi", { key: "value" }); expect(process.stdout.write).toHaveBeenCalled(); const firstLog = process.stdout.write.calls.first().args[0]; - expect(firstLog).toEqual('info: hi {"key":"value"}' + '\n'); + expect(firstLog).toEqual('info: hi {"key":"value"}' + "\n"); return reconfigureServer(); }) .then(() => { @@ -71,7 +71,7 @@ describe('WinstonLogger', () => { }); }); - it('should enable JSON logs', done => { + it("should enable JSON logs", done => { // Force console transport reconfigureServer({ logsFolder: null, @@ -79,12 +79,12 @@ describe('WinstonLogger', () => { silent: false, }) .then(() => { - spyOn(process.stdout, 'write'); - logging.logger.info('hi', { key: 'value' }); + spyOn(process.stdout, "write"); + logging.logger.info("hi", { key: "value" }); expect(process.stdout.write).toHaveBeenCalled(); const firstLog = process.stdout.write.calls.first().args[0]; expect(firstLog).toEqual( - JSON.stringify({ key: 'value', level: 'info', message: 'hi' }) + '\n' + JSON.stringify({ key: "value", level: "info", message: "hi" }) + "\n" ); return reconfigureServer({ jsonLogs: false, diff --git a/spec/LoggerController.spec.js b/spec/LoggerController.spec.js index eee9bba8a8..51e8ca87bd 100644 --- a/spec/LoggerController.spec.js +++ b/spec/LoggerController.spec.js @@ -1,9 +1,10 @@ -const LoggerController = require('../lib/Controllers/LoggerController').LoggerController; +const LoggerController = + require("../lib/Controllers/LoggerController").LoggerController; const WinstonLoggerAdapter = - require('../lib/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter; + require("../lib/Adapters/Logger/WinstonLoggerAdapter").WinstonLoggerAdapter; -describe('LoggerController', () => { - it('can process an empty query without throwing', done => { +describe("LoggerController", () => { + it("can process an empty query without throwing", done => { // Make mock request const query = {}; @@ -23,32 +24,34 @@ describe('LoggerController', () => { }).not.toThrow(); }); - it('properly validates dateTimes', done => { + it("properly validates dateTimes", done => { expect(LoggerController.validDateTime()).toBe(null); - expect(LoggerController.validDateTime('String')).toBe(null); + expect(LoggerController.validDateTime("String")).toBe(null); expect(LoggerController.validDateTime(123456).getTime()).toBe(123456); - expect(LoggerController.validDateTime('2016-01-01Z00:00:00').getTime()).toBe(1451606400000); + expect( + LoggerController.validDateTime("2016-01-01Z00:00:00").getTime() + ).toBe(1451606400000); done(); }); - it('can set the proper default values', done => { + it("can set the proper default values", done => { // Make mock request const result = LoggerController.parseOptions(); expect(result.size).toEqual(10); - expect(result.order).toEqual('desc'); - expect(result.level).toEqual('info'); + expect(result.order).toEqual("desc"); + expect(result.level).toEqual("info"); done(); }); - it('can parse an ascending query without throwing', done => { + it("can parse an ascending query without throwing", done => { // Make mock request const query = { - from: '2016-01-01Z00:00:00', - until: '2016-01-01Z00:00:00', + from: "2016-01-01Z00:00:00", + until: "2016-01-01Z00:00:00", size: 5, - order: 'asc', - level: 'error', + order: "asc", + level: "error", }; const result = LoggerController.parseOptions(query); @@ -56,21 +59,21 @@ describe('LoggerController', () => { expect(result.from.getTime()).toEqual(1451606400000); expect(result.until.getTime()).toEqual(1451606400000); expect(result.size).toEqual(5); - expect(result.order).toEqual('asc'); - expect(result.level).toEqual('error'); + expect(result.order).toEqual("asc"); + expect(result.level).toEqual("error"); done(); }); - it('can process an ascending query without throwing', done => { + it("can process an ascending query without throwing", done => { const query = { size: 5, - order: 'asc', - level: 'error', + order: "asc", + level: "error", }; const loggerController = new LoggerController(new WinstonLoggerAdapter()); - loggerController.error('can process an ascending query without throwing'); + loggerController.error("can process an ascending query without throwing"); expect(() => { loggerController @@ -81,20 +84,20 @@ describe('LoggerController', () => { }) .catch(err => { jfail(err); - fail('should not fail'); + fail("should not fail"); done(); }); }).not.toThrow(); }); - it('can parse a descending query without throwing', done => { + it("can parse a descending query without throwing", done => { // Make mock request const query = { - from: '2016-01-01Z00:00:00', - until: '2016-01-01Z00:00:00', + from: "2016-01-01Z00:00:00", + until: "2016-01-01Z00:00:00", size: 5, - order: 'desc', - level: 'error', + order: "desc", + level: "error", }; const result = LoggerController.parseOptions(query); @@ -102,21 +105,21 @@ describe('LoggerController', () => { expect(result.from.getTime()).toEqual(1451606400000); expect(result.until.getTime()).toEqual(1451606400000); expect(result.size).toEqual(5); - expect(result.order).toEqual('desc'); - expect(result.level).toEqual('error'); + expect(result.order).toEqual("desc"); + expect(result.level).toEqual("error"); done(); }); - it('can process a descending query without throwing', done => { + it("can process a descending query without throwing", done => { const query = { size: 5, - order: 'desc', - level: 'error', + order: "desc", + level: "error", }; const loggerController = new LoggerController(new WinstonLoggerAdapter()); - loggerController.error('can process a descending query without throwing'); + loggerController.error("can process a descending query without throwing"); expect(() => { loggerController @@ -127,39 +130,39 @@ describe('LoggerController', () => { }) .catch(err => { jfail(err); - fail('should not fail'); + fail("should not fail"); done(); }); }).not.toThrow(); }); - it('should throw without an adapter', done => { + it("should throw without an adapter", done => { expect(() => { new LoggerController(); }).toThrow(); done(); }); - it('should replace implementations with verbose', done => { + it("should replace implementations with verbose", done => { const adapter = new WinstonLoggerAdapter(); const logger = new LoggerController(adapter, null, { verbose: true }); - spyOn(adapter, 'log'); - logger.silly('yo!'); + spyOn(adapter, "log"); + logger.silly("yo!"); expect(adapter.log).not.toHaveBeenCalled(); done(); }); - it('should replace implementations with logLevel', done => { + it("should replace implementations with logLevel", done => { const adapter = new WinstonLoggerAdapter(); - const logger = new LoggerController(adapter, null, { logLevel: 'error' }); - spyOn(adapter, 'log'); - logger.warn('yo!'); - logger.info('yo!'); - logger.debug('yo!'); - logger.verbose('yo!'); - logger.silly('yo!'); + const logger = new LoggerController(adapter, null, { logLevel: "error" }); + spyOn(adapter, "log"); + logger.warn("yo!"); + logger.info("yo!"); + logger.debug("yo!"); + logger.verbose("yo!"); + logger.silly("yo!"); expect(adapter.log).not.toHaveBeenCalled(); - logger.error('error'); + logger.error("error"); expect(adapter.log).toHaveBeenCalled(); done(); }); diff --git a/spec/LogsRouter.spec.js b/spec/LogsRouter.spec.js index a53072ef86..93121bcc82 100644 --- a/spec/LogsRouter.spec.js +++ b/spec/LogsRouter.spec.js @@ -1,17 +1,18 @@ -'use strict'; +"use strict"; -const request = require('../lib/request'); -const LogsRouter = require('../lib/Routers/LogsRouter').LogsRouter; -const LoggerController = require('../lib/Controllers/LoggerController').LoggerController; +const request = require("../lib/request"); +const LogsRouter = require("../lib/Routers/LogsRouter").LogsRouter; +const LoggerController = + require("../lib/Controllers/LoggerController").LoggerController; const WinstonLoggerAdapter = - require('../lib/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter; + require("../lib/Adapters/Logger/WinstonLoggerAdapter").WinstonLoggerAdapter; const loggerController = new LoggerController(new WinstonLoggerAdapter()); describe_only(() => { - return process.env.PARSE_SERVER_LOG_LEVEL !== 'debug'; -})('LogsRouter', () => { - it('can check valid master key of request', done => { + return process.env.PARSE_SERVER_LOG_LEVEL !== "debug"; +})("LogsRouter", () => { + it("can check valid master key of request", done => { // Make mock request const request = { auth: { @@ -31,7 +32,7 @@ describe_only(() => { done(); }); - it('can check invalid construction of controller', done => { + it("can check invalid construction of controller", done => { // Make mock request const request = { auth: { @@ -51,52 +52,54 @@ describe_only(() => { done(); }); - it('can check invalid master key of request', done => { + it("can check invalid master key of request", done => { request({ - url: 'http://localhost:8378/1/scriptlog', + url: "http://localhost:8378/1/scriptlog", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }, }).then(fail, response => { const body = response.data; expect(response.status).toEqual(403); - expect(body.error).toEqual('unauthorized: master key is required'); + expect(body.error).toEqual("unauthorized: master key is required"); done(); }); }); const headers = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Master-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "X-Parse-Master-Key": "test", }; /** * Verifies simple passwords in GET login requests with special characters are scrubbed from the verbose log */ - it_id('e36d6141-2a20-41d0-85fc-d1534c3e4bae')(it)( - 'does scrub simple passwords on GET login', + it_id("e36d6141-2a20-41d0-85fc-d1534c3e4bae")(it)( + "does scrub simple passwords on GET login", done => { reconfigureServer({ verbose: true, }).then(function () { request({ headers: headers, - url: 'http://localhost:8378/1/login?username=test&password=simplepass.com', + url: "http://localhost:8378/1/login?username=test&password=simplepass.com", }) .catch(() => {}) .then(() => { request({ - url: 'http://localhost:8378/1/scriptlog?size=4&level=verbose', + url: "http://localhost:8378/1/scriptlog?size=4&level=verbose", headers: headers, }).then(response => { const body = response.data; expect(response.status).toEqual(200); // 4th entry is our actual GET request - expect(body[2].url).toEqual('/1/login?username=test&password=********'); + expect(body[2].url).toEqual( + "/1/login?username=test&password=********" + ); expect(body[2].message).toEqual( - 'REQUEST for [GET] /1/login?username=test&password=********: {}' + "REQUEST for [GET] /1/login?username=test&password=********: {}" ); done(); }); @@ -108,8 +111,8 @@ describe_only(() => { /** * Verifies complex passwords in GET login requests with special characters are scrubbed from the verbose log */ - it_id('24b277c5-250f-4a35-a449-2c8c519d4c03')(it)( - 'does scrub complex passwords on GET login', + it_id("24b277c5-250f-4a35-a449-2c8c519d4c03")(it)( + "does scrub complex passwords on GET login", done => { reconfigureServer({ verbose: true, @@ -118,20 +121,22 @@ describe_only(() => { return request({ headers: headers, // using urlencoded password, 'simple @,/?:&=+$#pass.com' - url: 'http://localhost:8378/1/login?username=test&password=simple%20%40%2C%2F%3F%3A%26%3D%2B%24%23pass.com', + url: "http://localhost:8378/1/login?username=test&password=simple%20%40%2C%2F%3F%3A%26%3D%2B%24%23pass.com", }) .catch(() => {}) .then(() => { return request({ - url: 'http://localhost:8378/1/scriptlog?size=4&level=verbose', + url: "http://localhost:8378/1/scriptlog?size=4&level=verbose", headers: headers, }).then(response => { const body = response.data; expect(response.status).toEqual(200); // 4th entry is our actual GET request - expect(body[2].url).toEqual('/1/login?username=test&password=********'); + expect(body[2].url).toEqual( + "/1/login?username=test&password=********" + ); expect(body[2].message).toEqual( - 'REQUEST for [GET] /1/login?username=test&password=********: {}' + "REQUEST for [GET] /1/login?username=test&password=********: {}" ); done(); }); @@ -144,31 +149,31 @@ describe_only(() => { /** * Verifies fields in POST login requests are NOT present in the verbose log */ - it_id('33143ec9-b32d-467c-ba65-ff2bbefdaadd')(it)( - 'does not have password field in POST login', + it_id("33143ec9-b32d-467c-ba65-ff2bbefdaadd")(it)( + "does not have password field in POST login", done => { reconfigureServer({ verbose: true, }).then(function () { request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/login', + url: "http://localhost:8378/1/login", body: { - username: 'test', - password: 'simplepass.com', + username: "test", + password: "simplepass.com", }, }) .catch(() => {}) .then(() => { request({ - url: 'http://localhost:8378/1/scriptlog?size=4&level=verbose', + url: "http://localhost:8378/1/scriptlog?size=4&level=verbose", headers: headers, }).then(response => { const body = response.data; expect(response.status).toEqual(200); // 4th entry is our actual GET request - expect(body[2].url).toEqual('/1/login'); + expect(body[2].url).toEqual("/1/login"); expect(body[2].message).toEqual( 'REQUEST for [POST] /1/login: {\n "username": "test",\n "password": "********"\n}' ); diff --git a/spec/Middlewares.spec.js b/spec/Middlewares.spec.js index 2ad29768bd..3c25692657 100644 --- a/spec/Middlewares.spec.js +++ b/spec/Middlewares.spec.js @@ -1,6 +1,6 @@ -const middlewares = require('../lib/middlewares'); -const AppCache = require('../lib/cache').AppCache; -const { BlockList } = require('net'); +const middlewares = require("../lib/middlewares"); +const AppCache = require("../lib/cache").AppCache; +const { BlockList } = require("net"); const AppCachePut = (appId, config) => AppCache.put(appId, { @@ -9,22 +9,22 @@ const AppCachePut = (appId, config) => masterKeyIpsStore: new Map(), }); -describe('middlewares', () => { +describe("middlewares", () => { let fakeReq, fakeRes; beforeEach(() => { fakeReq = { - ip: '127.0.0.1', - originalUrl: 'http://example.com/parse/', - url: 'http://example.com/', + ip: "127.0.0.1", + originalUrl: "http://example.com/parse/", + url: "http://example.com/", body: { - _ApplicationId: 'FakeAppId', + _ApplicationId: "FakeAppId", }, headers: {}, get: key => { return fakeReq.headers[key.toLowerCase()]; }, }; - fakeRes = jasmine.createSpyObj('fakeRes', ['end', 'status']); + fakeRes = jasmine.createSpyObj("fakeRes", ["end", "status"]); AppCachePut(fakeReq.body._ApplicationId, {}); }); @@ -32,78 +32,81 @@ describe('middlewares', () => { AppCache.del(fakeReq.body._ApplicationId); }); - it_id('4cc18d90-1763-4725-97fa-f63fb4692fc4')(it)('should use _ContentType if provided', done => { - AppCachePut(fakeReq.body._ApplicationId, { - masterKeyIps: ['127.0.0.1'], - }); - expect(fakeReq.headers['content-type']).toEqual(undefined); - const contentType = 'image/jpeg'; - fakeReq.body._ContentType = contentType; - middlewares.handleParseHeaders(fakeReq, fakeRes, () => { - expect(fakeReq.headers['content-type']).toEqual(contentType); - expect(fakeReq.body._ContentType).toEqual(undefined); - done(); - }); - }); + it_id("4cc18d90-1763-4725-97fa-f63fb4692fc4")(it)( + "should use _ContentType if provided", + done => { + AppCachePut(fakeReq.body._ApplicationId, { + masterKeyIps: ["127.0.0.1"], + }); + expect(fakeReq.headers["content-type"]).toEqual(undefined); + const contentType = "image/jpeg"; + fakeReq.body._ContentType = contentType; + middlewares.handleParseHeaders(fakeReq, fakeRes, () => { + expect(fakeReq.headers["content-type"]).toEqual(contentType); + expect(fakeReq.body._ContentType).toEqual(undefined); + done(); + }); + } + ); - it('should give invalid response when keys are configured but no key supplied', async () => { + it("should give invalid response when keys are configured but no key supplied", async () => { AppCachePut(fakeReq.body._ApplicationId, { - masterKey: 'masterKey', - restAPIKey: 'restAPIKey', + masterKey: "masterKey", + restAPIKey: "restAPIKey", }); await middlewares.handleParseHeaders(fakeReq, fakeRes); expect(fakeRes.status).toHaveBeenCalledWith(403); }); - it('should give invalid response when keys are configured but supplied key is incorrect', async () => { + it("should give invalid response when keys are configured but supplied key is incorrect", async () => { AppCachePut(fakeReq.body._ApplicationId, { - masterKey: 'masterKey', - restAPIKey: 'restAPIKey', + masterKey: "masterKey", + restAPIKey: "restAPIKey", }); - fakeReq.headers['x-parse-rest-api-key'] = 'wrongKey'; + fakeReq.headers["x-parse-rest-api-key"] = "wrongKey"; await middlewares.handleParseHeaders(fakeReq, fakeRes); expect(fakeRes.status).toHaveBeenCalledWith(403); }); - it('should give invalid response when keys are configured but different key is supplied', async () => { + it("should give invalid response when keys are configured but different key is supplied", async () => { AppCachePut(fakeReq.body._ApplicationId, { - masterKey: 'masterKey', - restAPIKey: 'restAPIKey', + masterKey: "masterKey", + restAPIKey: "restAPIKey", }); - fakeReq.headers['x-parse-client-key'] = 'clientKey'; + fakeReq.headers["x-parse-client-key"] = "clientKey"; await middlewares.handleParseHeaders(fakeReq, fakeRes); expect(fakeRes.status).toHaveBeenCalledWith(403); }); - it('should succeed when any one of the configured keys supplied', done => { + it("should succeed when any one of the configured keys supplied", done => { AppCachePut(fakeReq.body._ApplicationId, { - clientKey: 'clientKey', - masterKey: 'masterKey', - restAPIKey: 'restAPIKey', + clientKey: "clientKey", + masterKey: "masterKey", + restAPIKey: "restAPIKey", }); - fakeReq.headers['x-parse-rest-api-key'] = 'restAPIKey'; + fakeReq.headers["x-parse-rest-api-key"] = "restAPIKey"; middlewares.handleParseHeaders(fakeReq, fakeRes, () => { expect(fakeRes.status).not.toHaveBeenCalled(); done(); }); }); - it('should succeed when client key supplied but empty', done => { + it("should succeed when client key supplied but empty", done => { AppCachePut(fakeReq.body._ApplicationId, { - clientKey: '', - masterKey: 'masterKey', - restAPIKey: 'restAPIKey', + clientKey: "", + masterKey: "masterKey", + restAPIKey: "restAPIKey", }); - fakeReq.headers['x-parse-client-key'] = ''; + fakeReq.headers["x-parse-client-key"] = ""; middlewares.handleParseHeaders(fakeReq, fakeRes, () => { expect(fakeRes.status).not.toHaveBeenCalled(); done(); }); }); - it('should succeed when no keys are configured and none supplied', done => { + it("should succeed when no keys are configured and none supplied", done => { AppCachePut(fakeReq.body._ApplicationId, { - masterKey: 'masterKey', + masterKey: "masterKey", }); middlewares.handleParseHeaders(fakeReq, fakeRes, () => { expect(fakeRes.status).not.toHaveBeenCalled(); @@ -112,29 +115,29 @@ describe('middlewares', () => { }); const BodyParams = { - clientVersion: '_ClientVersion', - installationId: '_InstallationId', - sessionToken: '_SessionToken', - masterKey: '_MasterKey', - javascriptKey: '_JavaScriptKey', + clientVersion: "_ClientVersion", + installationId: "_InstallationId", + sessionToken: "_SessionToken", + masterKey: "_MasterKey", + javascriptKey: "_JavaScriptKey", }; const BodyKeys = Object.keys(BodyParams); BodyKeys.forEach(infoKey => { const bodyKey = BodyParams[infoKey]; - const keyValue = 'Fake' + bodyKey; + const keyValue = "Fake" + bodyKey; // javascriptKey is the only one that gets defaulted, const otherKeys = BodyKeys.filter( - otherKey => otherKey !== infoKey && otherKey !== 'javascriptKey' + otherKey => otherKey !== infoKey && otherKey !== "javascriptKey" ); - it_id('f9abd7ac-b1f4-4607-b9b0-365ff0559d84')(it)( + it_id("f9abd7ac-b1f4-4607-b9b0-365ff0559d84")(it)( `it should pull ${bodyKey} into req.info`, done => { AppCachePut(fakeReq.body._ApplicationId, { - masterKeyIps: ['0.0.0.0/0'], + masterKeyIps: ["0.0.0.0/0"], }); - fakeReq.ip = '127.0.0.1'; + fakeReq.ip = "127.0.0.1"; fakeReq.body[bodyKey] = keyValue; middlewares.handleParseHeaders(fakeReq, fakeRes, () => { expect(fakeReq.body[bodyKey]).toEqual(undefined); @@ -150,19 +153,21 @@ describe('middlewares', () => { ); }); - it_id('4a0bce41-c536-4482-a873-12ed023380e2')(it)( - 'should not succeed and log if the ip does not belong to masterKeyIps list', + it_id("4a0bce41-c536-4482-a873-12ed023380e2")(it)( + "should not succeed and log if the ip does not belong to masterKeyIps list", async () => { - const logger = require('../lib/logger').logger; - spyOn(logger, 'error').and.callFake(() => {}); + const logger = require("../lib/logger").logger; + spyOn(logger, "error").and.callFake(() => {}); AppCachePut(fakeReq.body._ApplicationId, { - masterKey: 'masterKey', - masterKeyIps: ['10.0.0.1'], + masterKey: "masterKey", + masterKeyIps: ["10.0.0.1"], }); - fakeReq.ip = '127.0.0.1'; - fakeReq.headers['x-parse-master-key'] = 'masterKey'; + fakeReq.ip = "127.0.0.1"; + fakeReq.headers["x-parse-master-key"] = "masterKey"; - const error = await middlewares.handleParseHeaders(fakeReq, fakeRes, () => {}).catch(e => e); + const error = await middlewares + .handleParseHeaders(fakeReq, fakeRes, () => {}) + .catch(e => e); expect(error).toBeDefined(); expect(error.message).toEqual(`unauthorized`); @@ -172,17 +177,19 @@ describe('middlewares', () => { } ); - it('should not succeed and log if the ip does not belong to maintenanceKeyIps list', async () => { - const logger = require('../lib/logger').logger; - spyOn(logger, 'error').and.callFake(() => {}); + it("should not succeed and log if the ip does not belong to maintenanceKeyIps list", async () => { + const logger = require("../lib/logger").logger; + spyOn(logger, "error").and.callFake(() => {}); AppCachePut(fakeReq.body._ApplicationId, { - maintenanceKey: 'masterKey', - maintenanceKeyIps: ['10.0.0.0', '10.0.0.1'], + maintenanceKey: "masterKey", + maintenanceKeyIps: ["10.0.0.0", "10.0.0.1"], }); - fakeReq.ip = '10.0.0.2'; - fakeReq.headers['x-parse-maintenance-key'] = 'masterKey'; + fakeReq.ip = "10.0.0.2"; + fakeReq.headers["x-parse-maintenance-key"] = "masterKey"; - const error = await middlewares.handleParseHeaders(fakeReq, fakeRes, () => {}).catch(e => e); + const error = await middlewares + .handleParseHeaders(fakeReq, fakeRes, () => {}) + .catch(e => e); expect(error).toBeDefined(); expect(error.message).toEqual(`unauthorized`); @@ -191,55 +198,61 @@ describe('middlewares', () => { ); }); - it_id('2f7fadec-a87c-4626-90d1-65c75653aea9')(it)( - 'should succeed if the ip does belong to masterKeyIps list', + it_id("2f7fadec-a87c-4626-90d1-65c75653aea9")(it)( + "should succeed if the ip does belong to masterKeyIps list", async () => { AppCachePut(fakeReq.body._ApplicationId, { - masterKey: 'masterKey', - masterKeyIps: ['10.0.0.1'], + masterKey: "masterKey", + masterKeyIps: ["10.0.0.1"], }); - fakeReq.ip = '10.0.0.1'; - fakeReq.headers['x-parse-master-key'] = 'masterKey'; - await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve)); + fakeReq.ip = "10.0.0.1"; + fakeReq.headers["x-parse-master-key"] = "masterKey"; + await new Promise(resolve => + middlewares.handleParseHeaders(fakeReq, fakeRes, resolve) + ); expect(fakeReq.auth.isMaster).toBe(true); } ); - it_id('2b251fd4-d43c-48f4-ada9-c8458e40c12a')(it)( - 'should allow any ip to use masterKey if masterKeyIps is empty', + it_id("2b251fd4-d43c-48f4-ada9-c8458e40c12a")(it)( + "should allow any ip to use masterKey if masterKeyIps is empty", async () => { AppCachePut(fakeReq.body._ApplicationId, { - masterKey: 'masterKey', - masterKeyIps: ['0.0.0.0/0'], + masterKey: "masterKey", + masterKeyIps: ["0.0.0.0/0"], }); - fakeReq.ip = '10.0.0.1'; - fakeReq.headers['x-parse-master-key'] = 'masterKey'; - await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve)); + fakeReq.ip = "10.0.0.1"; + fakeReq.headers["x-parse-master-key"] = "masterKey"; + await new Promise(resolve => + middlewares.handleParseHeaders(fakeReq, fakeRes, resolve) + ); expect(fakeReq.auth.isMaster).toBe(true); } ); - it('can set trust proxy', async () => { + it("can set trust proxy", async () => { const server = await reconfigureServer({ trustProxy: 1 }); - expect(server.app.parent.settings['trust proxy']).toBe(1); + expect(server.app.parent.settings["trust proxy"]).toBe(1); }); - it('should properly expose the headers', () => { + it("should properly expose the headers", () => { const headers = {}; const res = { header: (key, value) => { headers[key] = value; }, }; - const allowCrossDomain = middlewares.allowCrossDomain(fakeReq.body._ApplicationId); + const allowCrossDomain = middlewares.allowCrossDomain( + fakeReq.body._ApplicationId + ); allowCrossDomain(fakeReq, res, () => {}); expect(Object.keys(headers).length).toBe(4); - expect(headers['Access-Control-Expose-Headers']).toBe( - 'X-Parse-Job-Status-Id, X-Parse-Push-Status-Id' + expect(headers["Access-Control-Expose-Headers"]).toBe( + "X-Parse-Job-Status-Id, X-Parse-Push-Status-Id" ); }); - it('should set default Access-Control-Allow-Headers if allowHeaders are empty', () => { + it("should set default Access-Control-Allow-Headers if allowHeaders are empty", () => { AppCachePut(fakeReq.body._ApplicationId, { allowHeaders: undefined, }); @@ -249,20 +262,26 @@ describe('middlewares', () => { headers[key] = value; }, }; - const allowCrossDomain = middlewares.allowCrossDomain(fakeReq.body._ApplicationId); + const allowCrossDomain = middlewares.allowCrossDomain( + fakeReq.body._ApplicationId + ); allowCrossDomain(fakeReq, res, () => {}); - expect(headers['Access-Control-Allow-Headers']).toContain(middlewares.DEFAULT_ALLOWED_HEADERS); + expect(headers["Access-Control-Allow-Headers"]).toContain( + middlewares.DEFAULT_ALLOWED_HEADERS + ); AppCachePut(fakeReq.body._ApplicationId, { allowHeaders: [], }); allowCrossDomain(fakeReq, res, () => {}); - expect(headers['Access-Control-Allow-Headers']).toContain(middlewares.DEFAULT_ALLOWED_HEADERS); + expect(headers["Access-Control-Allow-Headers"]).toContain( + middlewares.DEFAULT_ALLOWED_HEADERS + ); }); - it('should append custom headers to Access-Control-Allow-Headers if allowHeaders provided', () => { + it("should append custom headers to Access-Control-Allow-Headers if allowHeaders provided", () => { AppCachePut(fakeReq.body._ApplicationId, { - allowHeaders: ['Header-1', 'Header-2'], + allowHeaders: ["Header-1", "Header-2"], }); const headers = {}; const res = { @@ -270,13 +289,19 @@ describe('middlewares', () => { headers[key] = value; }, }; - const allowCrossDomain = middlewares.allowCrossDomain(fakeReq.body._ApplicationId); + const allowCrossDomain = middlewares.allowCrossDomain( + fakeReq.body._ApplicationId + ); allowCrossDomain(fakeReq, res, () => {}); - expect(headers['Access-Control-Allow-Headers']).toContain('Header-1, Header-2'); - expect(headers['Access-Control-Allow-Headers']).toContain(middlewares.DEFAULT_ALLOWED_HEADERS); + expect(headers["Access-Control-Allow-Headers"]).toContain( + "Header-1, Header-2" + ); + expect(headers["Access-Control-Allow-Headers"]).toContain( + middlewares.DEFAULT_ALLOWED_HEADERS + ); }); - it('should set default Access-Control-Allow-Origin if allowOrigin is empty', () => { + it("should set default Access-Control-Allow-Origin if allowOrigin is empty", () => { AppCachePut(fakeReq.body._ApplicationId, { allowOrigin: undefined, }); @@ -286,14 +311,16 @@ describe('middlewares', () => { headers[key] = value; }, }; - const allowCrossDomain = middlewares.allowCrossDomain(fakeReq.body._ApplicationId); + const allowCrossDomain = middlewares.allowCrossDomain( + fakeReq.body._ApplicationId + ); allowCrossDomain(fakeReq, res, () => {}); - expect(headers['Access-Control-Allow-Origin']).toEqual('*'); + expect(headers["Access-Control-Allow-Origin"]).toEqual("*"); }); - it('should set custom origin to Access-Control-Allow-Origin if allowOrigin is provided', () => { + it("should set custom origin to Access-Control-Allow-Origin if allowOrigin is provided", () => { AppCachePut(fakeReq.body._ApplicationId, { - allowOrigin: 'https://parseplatform.org/', + allowOrigin: "https://parseplatform.org/", }); const headers = {}; const res = { @@ -301,14 +328,18 @@ describe('middlewares', () => { headers[key] = value; }, }; - const allowCrossDomain = middlewares.allowCrossDomain(fakeReq.body._ApplicationId); + const allowCrossDomain = middlewares.allowCrossDomain( + fakeReq.body._ApplicationId + ); allowCrossDomain(fakeReq, res, () => {}); - expect(headers['Access-Control-Allow-Origin']).toEqual('https://parseplatform.org/'); + expect(headers["Access-Control-Allow-Origin"]).toEqual( + "https://parseplatform.org/" + ); }); - it('should support multiple origins if several are defined in allowOrigin as an array', () => { + it("should support multiple origins if several are defined in allowOrigin as an array", () => { AppCache.put(fakeReq.body._ApplicationId, { - allowOrigin: ['https://a.com', 'https://b.com', 'https://c.com'], + allowOrigin: ["https://a.com", "https://b.com", "https://c.com"], }); const headers = {}; const res = { @@ -316,118 +347,133 @@ describe('middlewares', () => { headers[key] = value; }, }; - const allowCrossDomain = middlewares.allowCrossDomain(fakeReq.body._ApplicationId); + const allowCrossDomain = middlewares.allowCrossDomain( + fakeReq.body._ApplicationId + ); // Test with the first domain - fakeReq.headers.origin = 'https://a.com'; + fakeReq.headers.origin = "https://a.com"; allowCrossDomain(fakeReq, res, () => {}); - expect(headers['Access-Control-Allow-Origin']).toEqual('https://a.com'); + expect(headers["Access-Control-Allow-Origin"]).toEqual("https://a.com"); // Test with the second domain - fakeReq.headers.origin = 'https://b.com'; + fakeReq.headers.origin = "https://b.com"; allowCrossDomain(fakeReq, res, () => {}); - expect(headers['Access-Control-Allow-Origin']).toEqual('https://b.com'); + expect(headers["Access-Control-Allow-Origin"]).toEqual("https://b.com"); // Test with the third domain - fakeReq.headers.origin = 'https://c.com'; + fakeReq.headers.origin = "https://c.com"; allowCrossDomain(fakeReq, res, () => {}); - expect(headers['Access-Control-Allow-Origin']).toEqual('https://c.com'); + expect(headers["Access-Control-Allow-Origin"]).toEqual("https://c.com"); // Test with an unauthorized domain - fakeReq.headers.origin = 'https://unauthorized.com'; + fakeReq.headers.origin = "https://unauthorized.com"; allowCrossDomain(fakeReq, res, () => {}); - expect(headers['Access-Control-Allow-Origin']).toEqual('https://a.com'); + expect(headers["Access-Control-Allow-Origin"]).toEqual("https://a.com"); }); - it('should use user provided on field userFromJWT', done => { + it("should use user provided on field userFromJWT", done => { AppCachePut(fakeReq.body._ApplicationId, { - masterKey: 'masterKey', + masterKey: "masterKey", }); - fakeReq.userFromJWT = 'fake-user'; + fakeReq.userFromJWT = "fake-user"; middlewares.handleParseHeaders(fakeReq, fakeRes, () => { - expect(fakeReq.auth.user).toEqual('fake-user'); + expect(fakeReq.auth.user).toEqual("fake-user"); done(); }); }); - it('should give invalid response when upload file without x-parse-application-id in header', () => { + it("should give invalid response when upload file without x-parse-application-id in header", () => { AppCachePut(fakeReq.body._ApplicationId, { - masterKey: 'masterKey', + masterKey: "masterKey", }); - fakeReq.body = Buffer.from('fake-file'); + fakeReq.body = Buffer.from("fake-file"); middlewares.handleParseHeaders(fakeReq, fakeRes); expect(fakeRes.status).toHaveBeenCalledWith(403); }); - it('should match address', () => { - const ipv6 = '2001:0db8:85a3:0000:0000:8a2e:0370:7334'; - const anotherIpv6 = '::ffff:101.10.0.1'; - const ipv4 = '192.168.0.101'; - const localhostV6 = '::1'; - const localhostV62 = '::ffff:127.0.0.1'; - const localhostV4 = '127.0.0.1'; + it("should match address", () => { + const ipv6 = "2001:0db8:85a3:0000:0000:8a2e:0370:7334"; + const anotherIpv6 = "::ffff:101.10.0.1"; + const ipv4 = "192.168.0.101"; + const localhostV6 = "::1"; + const localhostV62 = "::ffff:127.0.0.1"; + const localhostV4 = "127.0.0.1"; const v6 = [ipv6, anotherIpv6]; v6.forEach(ip => { - expect(middlewares.checkIp(ip, ['::/0'], new Map())).toBe(true); - expect(middlewares.checkIp(ip, ['::'], new Map())).toBe(true); - expect(middlewares.checkIp(ip, ['0.0.0.0'], new Map())).toBe(false); - expect(middlewares.checkIp(ip, ['0.0.0.0/0'], new Map())).toBe(false); - expect(middlewares.checkIp(ip, ['123.123.123.123'], new Map())).toBe(false); + expect(middlewares.checkIp(ip, ["::/0"], new Map())).toBe(true); + expect(middlewares.checkIp(ip, ["::"], new Map())).toBe(true); + expect(middlewares.checkIp(ip, ["0.0.0.0"], new Map())).toBe(false); + expect(middlewares.checkIp(ip, ["0.0.0.0/0"], new Map())).toBe(false); + expect(middlewares.checkIp(ip, ["123.123.123.123"], new Map())).toBe( + false + ); }); expect(middlewares.checkIp(ipv6, [anotherIpv6], new Map())).toBe(false); expect(middlewares.checkIp(ipv6, [ipv6], new Map())).toBe(true); - expect(middlewares.checkIp(ipv6, ['2001:db8:85a3:0:0:8a2e:0:0/100'], new Map())).toBe(true); - - expect(middlewares.checkIp(ipv4, ['::'], new Map())).toBe(false); - expect(middlewares.checkIp(ipv4, ['::/0'], new Map())).toBe(false); - expect(middlewares.checkIp(ipv4, ['0.0.0.0'], new Map())).toBe(true); - expect(middlewares.checkIp(ipv4, ['0.0.0.0/0'], new Map())).toBe(true); - expect(middlewares.checkIp(ipv4, ['123.123.123.123'], new Map())).toBe(false); + expect( + middlewares.checkIp(ipv6, ["2001:db8:85a3:0:0:8a2e:0:0/100"], new Map()) + ).toBe(true); + + expect(middlewares.checkIp(ipv4, ["::"], new Map())).toBe(false); + expect(middlewares.checkIp(ipv4, ["::/0"], new Map())).toBe(false); + expect(middlewares.checkIp(ipv4, ["0.0.0.0"], new Map())).toBe(true); + expect(middlewares.checkIp(ipv4, ["0.0.0.0/0"], new Map())).toBe(true); + expect(middlewares.checkIp(ipv4, ["123.123.123.123"], new Map())).toBe( + false + ); expect(middlewares.checkIp(ipv4, [ipv4], new Map())).toBe(true); - expect(middlewares.checkIp(ipv4, ['192.168.0.0/24'], new Map())).toBe(true); + expect(middlewares.checkIp(ipv4, ["192.168.0.0/24"], new Map())).toBe(true); - expect(middlewares.checkIp(localhostV4, ['::1'], new Map())).toBe(false); - expect(middlewares.checkIp(localhostV6, ['::1'], new Map())).toBe(true); + expect(middlewares.checkIp(localhostV4, ["::1"], new Map())).toBe(false); + expect(middlewares.checkIp(localhostV6, ["::1"], new Map())).toBe(true); // ::ffff:127.0.0.1 is a padded ipv4 address but not ::1 - expect(middlewares.checkIp(localhostV62, ['::1'], new Map())).toBe(false); + expect(middlewares.checkIp(localhostV62, ["::1"], new Map())).toBe(false); // ::ffff:127.0.0.1 is a padded ipv4 address and is a match for 127.0.0.1 - expect(middlewares.checkIp(localhostV62, ['127.0.0.1'], new Map())).toBe(true); + expect(middlewares.checkIp(localhostV62, ["127.0.0.1"], new Map())).toBe( + true + ); }); - it('should match address with cache', () => { - const ipv6 = '2001:0db8:85a3:0000:0000:8a2e:0370:7334'; + it("should match address with cache", () => { + const ipv6 = "2001:0db8:85a3:0000:0000:8a2e:0370:7334"; const cache1 = new Map(); - const spyBlockListCheck = spyOn(BlockList.prototype, 'check').and.callThrough(); - expect(middlewares.checkIp(ipv6, ['::'], cache1)).toBe(true); - expect(cache1.get('2001:0db8:85a3:0000:0000:8a2e:0370:7334')).toBe(undefined); - expect(cache1.get('allowAllIpv6')).toBe(true); + const spyBlockListCheck = spyOn( + BlockList.prototype, + "check" + ).and.callThrough(); + expect(middlewares.checkIp(ipv6, ["::"], cache1)).toBe(true); + expect(cache1.get("2001:0db8:85a3:0000:0000:8a2e:0370:7334")).toBe( + undefined + ); + expect(cache1.get("allowAllIpv6")).toBe(true); expect(spyBlockListCheck).toHaveBeenCalledTimes(0); const cache2 = new Map(); - expect(middlewares.checkIp('::1', ['::1'], cache2)).toBe(true); - expect(cache2.get('::1')).toBe(true); + expect(middlewares.checkIp("::1", ["::1"], cache2)).toBe(true); + expect(cache2.get("::1")).toBe(true); expect(spyBlockListCheck).toHaveBeenCalledTimes(1); - expect(middlewares.checkIp('::1', ['::1'], cache2)).toBe(true); + expect(middlewares.checkIp("::1", ["::1"], cache2)).toBe(true); expect(spyBlockListCheck).toHaveBeenCalledTimes(1); spyBlockListCheck.calls.reset(); const cache3 = new Map(); - expect(middlewares.checkIp('127.0.0.1', ['127.0.0.1'], cache3)).toBe(true); - expect(cache3.get('127.0.0.1')).toBe(true); + expect(middlewares.checkIp("127.0.0.1", ["127.0.0.1"], cache3)).toBe(true); + expect(cache3.get("127.0.0.1")).toBe(true); expect(spyBlockListCheck).toHaveBeenCalledTimes(1); - expect(middlewares.checkIp('127.0.0.1', ['127.0.0.1'], cache3)).toBe(true); + expect(middlewares.checkIp("127.0.0.1", ["127.0.0.1"], cache3)).toBe(true); expect(spyBlockListCheck).toHaveBeenCalledTimes(1); spyBlockListCheck.calls.reset(); const cache4 = new Map(); - const ranges = ['127.0.0.1', '192.168.0.0/24']; + const ranges = ["127.0.0.1", "192.168.0.0/24"]; // should not cache negative match - expect(middlewares.checkIp('123.123.123.123', ranges, cache4)).toBe(false); - expect(cache4.get('123.123.123.123')).toBe(undefined); + expect(middlewares.checkIp("123.123.123.123", ranges, cache4)).toBe(false); + expect(cache4.get("123.123.123.123")).toBe(undefined); expect(spyBlockListCheck).toHaveBeenCalledTimes(1); spyBlockListCheck.calls.reset(); // should not cache cidr - expect(middlewares.checkIp('192.168.0.101', ranges, cache4)).toBe(true); - expect(cache4.get('192.168.0.101')).toBe(undefined); + expect(middlewares.checkIp("192.168.0.101", ranges, cache4)).toBe(true); + expect(cache4.get("192.168.0.101")).toBe(undefined); expect(spyBlockListCheck).toHaveBeenCalledTimes(1); }); }); diff --git a/spec/MongoSchemaCollectionAdapter.spec.js b/spec/MongoSchemaCollectionAdapter.spec.js index f89151d2bd..1f63a37b4e 100644 --- a/spec/MongoSchemaCollectionAdapter.spec.js +++ b/spec/MongoSchemaCollectionAdapter.spec.js @@ -1,13 +1,13 @@ -'use strict'; +"use strict"; const MongoSchemaCollection = - require('../lib/Adapters/Storage/Mongo/MongoSchemaCollection').default; + require("../lib/Adapters/Storage/Mongo/MongoSchemaCollection").default; -describe('MongoSchemaCollection', () => { - it('can transform legacy _client_permissions keys to parse format', done => { +describe("MongoSchemaCollection", () => { + it("can transform legacy _client_permissions keys to parse format", done => { expect( MongoSchemaCollection._TESTmongoSchemaToParseSchema({ - _id: '_Installation', + _id: "_Installation", _client_permissions: { get: true, find: true, @@ -19,76 +19,76 @@ describe('MongoSchemaCollection', () => { _metadata: { class_permissions: { ACL: { - '*': { + "*": { read: true, write: true, }, }, - get: { '*': true }, - find: { '*': true }, - count: { '*': true }, - update: { '*': true }, - create: { '*': true }, - delete: { '*': true }, - addField: { '*': true }, - protectedFields: { '*': [] }, + get: { "*": true }, + find: { "*": true }, + count: { "*": true }, + update: { "*": true }, + create: { "*": true }, + delete: { "*": true }, + addField: { "*": true }, + protectedFields: { "*": [] }, }, indexes: { name1: { deviceToken: 1 }, }, }, - installationId: 'string', - deviceToken: 'string', - deviceType: 'string', - channels: 'array', - user: '*_User', - pushType: 'string', - GCMSenderId: 'string', - timeZone: 'string', - localeIdentifier: 'string', - badge: 'number', - appVersion: 'string', - appName: 'string', - appIdentifier: 'string', - parseVersion: 'string', + installationId: "string", + deviceToken: "string", + deviceType: "string", + channels: "array", + user: "*_User", + pushType: "string", + GCMSenderId: "string", + timeZone: "string", + localeIdentifier: "string", + badge: "number", + appVersion: "string", + appName: "string", + appIdentifier: "string", + parseVersion: "string", }) ).toEqual({ - className: '_Installation', + className: "_Installation", fields: { - installationId: { type: 'String' }, - deviceToken: { type: 'String' }, - deviceType: { type: 'String' }, - channels: { type: 'Array' }, - user: { type: 'Pointer', targetClass: '_User' }, - pushType: { type: 'String' }, - GCMSenderId: { type: 'String' }, - timeZone: { type: 'String' }, - localeIdentifier: { type: 'String' }, - badge: { type: 'Number' }, - appVersion: { type: 'String' }, - appName: { type: 'String' }, - appIdentifier: { type: 'String' }, - parseVersion: { type: 'String' }, - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - objectId: { type: 'String' }, + installationId: { type: "String" }, + deviceToken: { type: "String" }, + deviceType: { type: "String" }, + channels: { type: "Array" }, + user: { type: "Pointer", targetClass: "_User" }, + pushType: { type: "String" }, + GCMSenderId: { type: "String" }, + timeZone: { type: "String" }, + localeIdentifier: { type: "String" }, + badge: { type: "Number" }, + appVersion: { type: "String" }, + appName: { type: "String" }, + appIdentifier: { type: "String" }, + parseVersion: { type: "String" }, + ACL: { type: "ACL" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + objectId: { type: "String" }, }, classLevelPermissions: { ACL: { - '*': { + "*": { read: true, write: true, }, }, - find: { '*': true }, - get: { '*': true }, - count: { '*': true }, - create: { '*': true }, - update: { '*': true }, - delete: { '*': true }, - addField: { '*': true }, - protectedFields: { '*': [] }, + find: { "*": true }, + get: { "*": true }, + count: { "*": true }, + create: { "*": true }, + update: { "*": true }, + delete: { "*": true }, + addField: { "*": true }, + protectedFields: { "*": [] }, }, indexes: { name1: { deviceToken: 1 }, diff --git a/spec/MongoStorageAdapter.spec.js b/spec/MongoStorageAdapter.spec.js index 304e0c4637..9ab7d64c2e 100644 --- a/spec/MongoStorageAdapter.spec.js +++ b/spec/MongoStorageAdapter.spec.js @@ -1,11 +1,13 @@ -'use strict'; +"use strict"; -const MongoStorageAdapter = require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter').default; -const { MongoClient, Collection } = require('mongodb'); -const databaseURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase'; -const request = require('../lib/request'); -const Config = require('../lib/Config'); -const TestUtils = require('../lib/TestUtils'); +const MongoStorageAdapter = + require("../lib/Adapters/Storage/Mongo/MongoStorageAdapter").default; +const { MongoClient, Collection } = require("mongodb"); +const databaseURI = + "mongodb://localhost:27017/parseServerMongoAdapterTestDatabase"; +const request = require("../lib/request"); +const Config = require("../lib/Config"); +const TestUtils = require("../lib/TestUtils"); const fakeClient = { s: { options: { dbName: null } }, @@ -14,69 +16,71 @@ const fakeClient = { // These tests are specific to the mongo storage adapter + mongo storage format // and will eventually be moved into their own repo -describe_only_db('mongo')('MongoStorageAdapter', () => { +describe_only_db("mongo")("MongoStorageAdapter", () => { beforeEach(async () => { await new MongoStorageAdapter({ uri: databaseURI }).deleteAllClasses(); Config.get(Parse.applicationId).schemaCache.clear(); }); - it('auto-escapes symbols in auth information', () => { - spyOn(MongoClient, 'connect').and.returnValue(Promise.resolve(fakeClient)); + it("auto-escapes symbols in auth information", () => { + spyOn(MongoClient, "connect").and.returnValue(Promise.resolve(fakeClient)); new MongoStorageAdapter({ - uri: 'mongodb://user!with@+ symbols:password!with@+ symbols@localhost:1234/parse', + uri: "mongodb://user!with@+ symbols:password!with@+ symbols@localhost:1234/parse", }).connect(); expect(MongoClient.connect).toHaveBeenCalledWith( - 'mongodb://user!with%40%2B%20symbols:password!with%40%2B%20symbols@localhost:1234/parse', + "mongodb://user!with%40%2B%20symbols:password!with%40%2B%20symbols@localhost:1234/parse", jasmine.any(Object) ); }); it("doesn't double escape already URI-encoded information", () => { - spyOn(MongoClient, 'connect').and.returnValue(Promise.resolve(fakeClient)); + spyOn(MongoClient, "connect").and.returnValue(Promise.resolve(fakeClient)); new MongoStorageAdapter({ - uri: 'mongodb://user!with%40%2B%20symbols:password!with%40%2B%20symbols@localhost:1234/parse', + uri: "mongodb://user!with%40%2B%20symbols:password!with%40%2B%20symbols@localhost:1234/parse", }).connect(); expect(MongoClient.connect).toHaveBeenCalledWith( - 'mongodb://user!with%40%2B%20symbols:password!with%40%2B%20symbols@localhost:1234/parse', + "mongodb://user!with%40%2B%20symbols:password!with%40%2B%20symbols@localhost:1234/parse", jasmine.any(Object) ); }); // https://github.com/parse-community/parse-server/pull/148#issuecomment-180407057 - it('preserves replica sets', () => { - spyOn(MongoClient, 'connect').and.returnValue(Promise.resolve(fakeClient)); + it("preserves replica sets", () => { + spyOn(MongoClient, "connect").and.returnValue(Promise.resolve(fakeClient)); new MongoStorageAdapter({ - uri: 'mongodb://test:testpass@ds056315-a0.mongolab.com:59325,ds059315-a1.mongolab.com:59315/testDBname?replicaSet=rs-ds059415', + uri: "mongodb://test:testpass@ds056315-a0.mongolab.com:59325,ds059315-a1.mongolab.com:59315/testDBname?replicaSet=rs-ds059415", }).connect(); expect(MongoClient.connect).toHaveBeenCalledWith( - 'mongodb://test:testpass@ds056315-a0.mongolab.com:59325,ds059315-a1.mongolab.com:59315/testDBname?replicaSet=rs-ds059415', + "mongodb://test:testpass@ds056315-a0.mongolab.com:59325,ds059315-a1.mongolab.com:59315/testDBname?replicaSet=rs-ds059415", jasmine.any(Object) ); }); - it('stores objectId in _id', done => { + it("stores objectId in _id", done => { const adapter = new MongoStorageAdapter({ uri: databaseURI }); adapter - .createObject('Foo', { fields: {} }, { objectId: 'abcde' }) - .then(() => adapter._rawFind('Foo', {})) + .createObject("Foo", { fields: {} }, { objectId: "abcde" }) + .then(() => adapter._rawFind("Foo", {})) .then(results => { expect(results.length).toEqual(1); const obj = results[0]; - expect(obj._id).toEqual('abcde'); + expect(obj._id).toEqual("abcde"); expect(obj.objectId).toBeUndefined(); done(); }); }); - it('find succeeds when query is within maxTimeMS', done => { + it("find succeeds when query is within maxTimeMS", done => { const maxTimeMS = 250; const adapter = new MongoStorageAdapter({ uri: databaseURI, mongoOptions: { maxTimeMS }, }); adapter - .createObject('Foo', { fields: {} }, { objectId: 'abcde' }) - .then(() => adapter._rawFind('Foo', { $where: `sleep(${maxTimeMS / 2})` })) + .createObject("Foo", { fields: {} }, { objectId: "abcde" }) + .then(() => + adapter._rawFind("Foo", { $where: `sleep(${maxTimeMS / 2})` }) + ) .then( () => done(), err => { @@ -85,123 +89,125 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { ); }); - it('find fails when query exceeds maxTimeMS', done => { + it("find fails when query exceeds maxTimeMS", done => { const maxTimeMS = 250; const adapter = new MongoStorageAdapter({ uri: databaseURI, mongoOptions: { maxTimeMS }, }); adapter - .createObject('Foo', { fields: {} }, { objectId: 'abcde' }) - .then(() => adapter._rawFind('Foo', { $where: `sleep(${maxTimeMS * 2})` })) + .createObject("Foo", { fields: {} }, { objectId: "abcde" }) + .then(() => + adapter._rawFind("Foo", { $where: `sleep(${maxTimeMS * 2})` }) + ) .then( () => { - done.fail('Find succeeded despite taking too long!'); + done.fail("Find succeeded despite taking too long!"); }, err => { - expect(err.name).toEqual('MongoServerError'); + expect(err.name).toEqual("MongoServerError"); expect(err.code).toEqual(50); - expect(err.message).toMatch('operation exceeded time limit'); + expect(err.message).toMatch("operation exceeded time limit"); done(); } ); }); - it('stores pointers with a _p_ prefix', done => { + it("stores pointers with a _p_ prefix", done => { const obj = { - objectId: 'bar', + objectId: "bar", aPointer: { - __type: 'Pointer', - className: 'JustThePointer', - objectId: 'qwerty', + __type: "Pointer", + className: "JustThePointer", + objectId: "qwerty", }, }; const adapter = new MongoStorageAdapter({ uri: databaseURI }); adapter .createObject( - 'APointerDarkly', + "APointerDarkly", { fields: { - objectId: { type: 'String' }, - aPointer: { type: 'Pointer', targetClass: 'JustThePointer' }, + objectId: { type: "String" }, + aPointer: { type: "Pointer", targetClass: "JustThePointer" }, }, }, obj ) - .then(() => adapter._rawFind('APointerDarkly', {})) + .then(() => adapter._rawFind("APointerDarkly", {})) .then(results => { expect(results.length).toEqual(1); const output = results[0]; - expect(typeof output._id).toEqual('string'); - expect(typeof output._p_aPointer).toEqual('string'); - expect(output._p_aPointer).toEqual('JustThePointer$qwerty'); + expect(typeof output._id).toEqual("string"); + expect(typeof output._p_aPointer).toEqual("string"); + expect(output._p_aPointer).toEqual("JustThePointer$qwerty"); expect(output.aPointer).toBeUndefined(); done(); }); }); - it('handles object and subdocument', done => { + it("handles object and subdocument", done => { const adapter = new MongoStorageAdapter({ uri: databaseURI }); - const schema = { fields: { subdoc: { type: 'Object' } } }; - const obj = { subdoc: { foo: 'bar', wu: 'tan' } }; + const schema = { fields: { subdoc: { type: "Object" } } }; + const obj = { subdoc: { foo: "bar", wu: "tan" } }; adapter - .createObject('MyClass', schema, obj) - .then(() => adapter._rawFind('MyClass', {})) + .createObject("MyClass", schema, obj) + .then(() => adapter._rawFind("MyClass", {})) .then(results => { expect(results.length).toEqual(1); const mob = results[0]; - expect(typeof mob.subdoc).toBe('object'); - expect(mob.subdoc.foo).toBe('bar'); - expect(mob.subdoc.wu).toBe('tan'); - const obj = { 'subdoc.wu': 'clan' }; - return adapter.findOneAndUpdate('MyClass', schema, {}, obj); + expect(typeof mob.subdoc).toBe("object"); + expect(mob.subdoc.foo).toBe("bar"); + expect(mob.subdoc.wu).toBe("tan"); + const obj = { "subdoc.wu": "clan" }; + return adapter.findOneAndUpdate("MyClass", schema, {}, obj); }) - .then(() => adapter._rawFind('MyClass', {})) + .then(() => adapter._rawFind("MyClass", {})) .then(results => { expect(results.length).toEqual(1); const mob = results[0]; - expect(typeof mob.subdoc).toBe('object'); - expect(mob.subdoc.foo).toBe('bar'); - expect(mob.subdoc.wu).toBe('clan'); + expect(typeof mob.subdoc).toBe("object"); + expect(mob.subdoc.foo).toBe("bar"); + expect(mob.subdoc.wu).toBe("clan"); done(); }); }); - it('handles creating an array, object, date', done => { + it("handles creating an array, object, date", done => { const adapter = new MongoStorageAdapter({ uri: databaseURI }); const obj = { array: [1, 2, 3], - object: { foo: 'bar' }, + object: { foo: "bar" }, date: { - __type: 'Date', - iso: '2016-05-26T20:55:01.154Z', + __type: "Date", + iso: "2016-05-26T20:55:01.154Z", }, }; const schema = { fields: { - array: { type: 'Array' }, - object: { type: 'Object' }, - date: { type: 'Date' }, + array: { type: "Array" }, + object: { type: "Object" }, + date: { type: "Date" }, }, }; adapter - .createObject('MyClass', schema, obj) - .then(() => adapter._rawFind('MyClass', {})) + .createObject("MyClass", schema, obj) + .then(() => adapter._rawFind("MyClass", {})) .then(results => { expect(results.length).toEqual(1); const mob = results[0]; expect(mob.array instanceof Array).toBe(true); - expect(typeof mob.object).toBe('object'); + expect(typeof mob.object).toBe("object"); expect(mob.date instanceof Date).toBe(true); - return adapter.find('MyClass', schema, {}, {}); + return adapter.find("MyClass", schema, {}, {}); }) .then(results => { expect(results.length).toEqual(1); const mob = results[0]; expect(mob.array instanceof Array).toBe(true); - expect(typeof mob.object).toBe('object'); - expect(mob.date.__type).toBe('Date'); - expect(mob.date.iso).toBe('2016-05-26T20:55:01.154Z'); + expect(typeof mob.object).toBe("object"); + expect(mob.date.__type).toBe("Date"); + expect(mob.date.iso).toBe("2016-05-26T20:55:01.154Z"); done(); }) .catch(error => { @@ -211,8 +217,8 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { }); }); - it('handles nested dates', async () => { - await new Parse.Object('MyClass', { + it("handles nested dates", async () => { + await new Parse.Object("MyClass", { foo: { test: { date: new Date(), @@ -224,14 +230,14 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { date: new Date(), }).save(); const adapter = Config.get(Parse.applicationId).database.adapter; - const [object] = await adapter._rawFind('MyClass', {}); + const [object] = await adapter._rawFind("MyClass", {}); expect(object.date instanceof Date).toBeTrue(); expect(object.bar.date instanceof Date).toBeTrue(); expect(object.foo.test.date instanceof Date).toBeTrue(); }); - it('handles nested dates in array ', async () => { - await new Parse.Object('MyClass', { + it("handles nested dates in array ", async () => { + await new Parse.Object("MyClass", { foo: { test: { date: [new Date()], @@ -243,25 +249,25 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { date: [new Date()], }).save(); const adapter = Config.get(Parse.applicationId).database.adapter; - const [object] = await adapter._rawFind('MyClass', {}); + const [object] = await adapter._rawFind("MyClass", {}); expect(object.date[0] instanceof Date).toBeTrue(); expect(object.bar.date[0] instanceof Date).toBeTrue(); expect(object.foo.test.date[0] instanceof Date).toBeTrue(); - const obj = await new Parse.Query('MyClass').first({ useMasterKey: true }); - expect(obj.get('date')[0] instanceof Date).toBeTrue(); - expect(obj.get('bar').date[0] instanceof Date).toBeTrue(); - expect(obj.get('foo').test.date[0] instanceof Date).toBeTrue(); + const obj = await new Parse.Query("MyClass").first({ useMasterKey: true }); + expect(obj.get("date")[0] instanceof Date).toBeTrue(); + expect(obj.get("bar").date[0] instanceof Date).toBeTrue(); + expect(obj.get("foo").test.date[0] instanceof Date).toBeTrue(); }); - it('upserts with $setOnInsert', async () => { - const uuid = require('uuid'); + it("upserts with $setOnInsert", async () => { + const uuid = require("uuid"); const uuid1 = uuid.v4(); const uuid2 = uuid.v4(); const schema = { - className: 'MyClass', + className: "MyClass", fields: { - x: { type: 'Number' }, - count: { type: 'Number' }, + x: { type: "Number" }, + count: { type: "Number" }, }, classLevelPermissions: {}, }; @@ -275,17 +281,21 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { }; const update = { objectId: { - __op: 'SetOnInsert', + __op: "SetOnInsert", amount: uuid1, }, count: { - __op: 'Increment', + __op: "Increment", amount: 1, }, }; - await Parse.Server.database.update('MyClass', query, update, { upsert: true }); + await Parse.Server.database.update("MyClass", query, update, { + upsert: true, + }); update.objectId.amount = uuid2; - await Parse.Server.database.update('MyClass', query, update, { upsert: true }); + await Parse.Server.database.update("MyClass", query, update, { + upsert: true, + }); const res = await Parse.Server.database.find(schema.className, {}, {}); expect(res.length).toBe(1); @@ -294,46 +304,46 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { expect(res[0].x).toBe(1); }); - it('handles updating a single object with array, object date', done => { + it("handles updating a single object with array, object date", done => { const adapter = new MongoStorageAdapter({ uri: databaseURI }); const schema = { fields: { - array: { type: 'Array' }, - object: { type: 'Object' }, - date: { type: 'Date' }, + array: { type: "Array" }, + object: { type: "Object" }, + date: { type: "Date" }, }, }; adapter - .createObject('MyClass', schema, {}) - .then(() => adapter._rawFind('MyClass', {})) + .createObject("MyClass", schema, {}) + .then(() => adapter._rawFind("MyClass", {})) .then(results => { expect(results.length).toEqual(1); const update = { array: [1, 2, 3], - object: { foo: 'bar' }, + object: { foo: "bar" }, date: { - __type: 'Date', - iso: '2016-05-26T20:55:01.154Z', + __type: "Date", + iso: "2016-05-26T20:55:01.154Z", }, }; const query = {}; - return adapter.findOneAndUpdate('MyClass', schema, query, update); + return adapter.findOneAndUpdate("MyClass", schema, query, update); }) .then(results => { const mob = results; expect(mob.array instanceof Array).toBe(true); - expect(typeof mob.object).toBe('object'); - expect(mob.date.__type).toBe('Date'); - expect(mob.date.iso).toBe('2016-05-26T20:55:01.154Z'); - return adapter._rawFind('MyClass', {}); + expect(typeof mob.object).toBe("object"); + expect(mob.date.__type).toBe("Date"); + expect(mob.date.iso).toBe("2016-05-26T20:55:01.154Z"); + return adapter._rawFind("MyClass", {}); }) .then(results => { expect(results.length).toEqual(1); const mob = results[0]; expect(mob.array instanceof Array).toBe(true); - expect(typeof mob.object).toBe('object'); + expect(typeof mob.object).toBe("object"); expect(mob.date instanceof Date).toBe(true); done(); }) @@ -344,18 +354,18 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { }); }); - it('handleShutdown, close connection', async () => { + it("handleShutdown, close connection", async () => { const adapter = new MongoStorageAdapter({ uri: databaseURI }); const schema = { fields: { - array: { type: 'Array' }, - object: { type: 'Object' }, - date: { type: 'Date' }, + array: { type: "Array" }, + object: { type: "Object" }, + date: { type: "Date" }, }, }; - await adapter.createObject('MyClass', schema, {}); + await adapter.createObject("MyClass", schema, {}); const status = await adapter.database.admin().serverStatus(); expect(status.connections.current > 0).toEqual(true); @@ -364,123 +374,138 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { await adapter.database.admin().serverStatus(); expect(false).toBe(true); } catch (e) { - expect(e.message).toEqual('Client must be connected before running operations'); + expect(e.message).toEqual( + "Client must be connected before running operations" + ); } }); - it('getClass if exists', async () => { + it("getClass if exists", async () => { const adapter = new MongoStorageAdapter({ uri: databaseURI }); const schema = { fields: { - array: { type: 'Array' }, - object: { type: 'Object' }, - date: { type: 'Date' }, + array: { type: "Array" }, + object: { type: "Object" }, + date: { type: "Date" }, }, }; - await adapter.createClass('MyClass', schema); - const myClassSchema = await adapter.getClass('MyClass'); + await adapter.createClass("MyClass", schema); + const myClassSchema = await adapter.getClass("MyClass"); expect(myClassSchema).toBeDefined(); }); - it('getClass if not exists', async () => { + it("getClass if not exists", async () => { const adapter = new MongoStorageAdapter({ uri: databaseURI }); - await expectAsync(adapter.getClass('UnknownClass')).toBeRejectedWith(undefined); + await expectAsync(adapter.getClass("UnknownClass")).toBeRejectedWith( + undefined + ); }); - it_only_mongodb_version('<5.1 || >=6')('should use index for caseInsensitive query', async () => { - const user = new Parse.User(); - user.set('username', 'Bugs'); - user.set('password', 'Bunny'); - await user.signUp(); - - const database = Config.get(Parse.applicationId).database; - await database.adapter.dropAllIndexes('_User'); - - const preIndexPlan = await database.find( - '_User', - { username: 'bugs' }, - { caseInsensitive: true, explain: true } - ); + it_only_mongodb_version("<5.1 || >=6")( + "should use index for caseInsensitive query", + async () => { + const user = new Parse.User(); + user.set("username", "Bugs"); + user.set("password", "Bunny"); + await user.signUp(); + + const database = Config.get(Parse.applicationId).database; + await database.adapter.dropAllIndexes("_User"); + + const preIndexPlan = await database.find( + "_User", + { username: "bugs" }, + { caseInsensitive: true, explain: true } + ); - const schema = await new Parse.Schema('_User').get(); + const schema = await new Parse.Schema("_User").get(); - await database.adapter.ensureIndex( - '_User', - schema, - ['username'], - 'case_insensitive_username', - true - ); + await database.adapter.ensureIndex( + "_User", + schema, + ["username"], + "case_insensitive_username", + true + ); - const postIndexPlan = await database.find( - '_User', - { username: 'bugs' }, - { caseInsensitive: true, explain: true } - ); - expect(preIndexPlan.executionStats.executionStages.stage).toBe('COLLSCAN'); - expect(postIndexPlan.executionStats.executionStages.stage).toBe('FETCH'); - }); + const postIndexPlan = await database.find( + "_User", + { username: "bugs" }, + { caseInsensitive: true, explain: true } + ); + expect(preIndexPlan.executionStats.executionStages.stage).toBe( + "COLLSCAN" + ); + expect(postIndexPlan.executionStats.executionStages.stage).toBe("FETCH"); + } + ); - it('should delete field without index', async () => { + it("should delete field without index", async () => { const database = Config.get(Parse.applicationId).database; - const obj = new Parse.Object('MyObject'); - obj.set('test', 1); + const obj = new Parse.Object("MyObject"); + obj.set("test", 1); await obj.save(); - const schemaBeforeDeletion = await new Parse.Schema('MyObject').get(); - await database.adapter.deleteFields('MyObject', schemaBeforeDeletion, ['test']); - const schemaAfterDeletion = await new Parse.Schema('MyObject').get(); + const schemaBeforeDeletion = await new Parse.Schema("MyObject").get(); + await database.adapter.deleteFields("MyObject", schemaBeforeDeletion, [ + "test", + ]); + const schemaAfterDeletion = await new Parse.Schema("MyObject").get(); expect(schemaBeforeDeletion.fields.test).toBeDefined(); expect(schemaAfterDeletion.fields.test).toBeUndefined(); }); - it('should delete field with index', async () => { + it("should delete field with index", async () => { const database = Config.get(Parse.applicationId).database; - const obj = new Parse.Object('MyObject'); - obj.set('test', 1); + const obj = new Parse.Object("MyObject"); + obj.set("test", 1); await obj.save(); - const schemaBeforeDeletion = await new Parse.Schema('MyObject').get(); - await database.adapter.ensureIndex('MyObject', schemaBeforeDeletion, ['test']); - await database.adapter.deleteFields('MyObject', schemaBeforeDeletion, ['test']); - const schemaAfterDeletion = await new Parse.Schema('MyObject').get(); + const schemaBeforeDeletion = await new Parse.Schema("MyObject").get(); + await database.adapter.ensureIndex("MyObject", schemaBeforeDeletion, [ + "test", + ]); + await database.adapter.deleteFields("MyObject", schemaBeforeDeletion, [ + "test", + ]); + const schemaAfterDeletion = await new Parse.Schema("MyObject").get(); expect(schemaBeforeDeletion.fields.test).toBeDefined(); expect(schemaAfterDeletion.fields.test).toBeUndefined(); }); - if (process.env.MONGODB_TOPOLOGY === 'replicaset') { - describe('transactions', () => { + if (process.env.MONGODB_TOPOLOGY === "replicaset") { + describe("transactions", () => { const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; beforeEach(async () => { await reconfigureServer({ databaseAdapter: undefined, databaseURI: - 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase?replicaSet=replicaset', + "mongodb://localhost:27017/parseServerMongoAdapterTestDatabase?replicaSet=replicaset", }); await TestUtils.destroyAllDataPermanently(true); }); - it('should use transaction in a batch with transaction = true', async () => { - const myObject = new Parse.Object('MyObject'); + it("should use transaction in a batch with transaction = true", async () => { + const myObject = new Parse.Object("MyObject"); await myObject.save(); - spyOn(Collection.prototype, 'findOneAndUpdate').and.callThrough(); + spyOn(Collection.prototype, "findOneAndUpdate").and.callThrough(); await request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/batch', + url: "http://localhost:8378/1/batch", body: JSON.stringify({ requests: [ { - method: 'PUT', - path: '/1/classes/MyObject/' + myObject.id, - body: { myAttribute: 'myValue' }, + method: "PUT", + path: "/1/classes/MyObject/" + myObject.id, + body: { myAttribute: "myValue" }, }, ], transaction: true, @@ -490,27 +515,29 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { let found = false; Collection.prototype.findOneAndUpdate.calls.all().forEach(call => { found = true; - expect(call.args[2].session.transaction.state).toBe('TRANSACTION_COMMITTED'); + expect(call.args[2].session.transaction.state).toBe( + "TRANSACTION_COMMITTED" + ); }); expect(found).toBe(true); }); - it('should not use transaction in a batch with transaction = false', async () => { - const myObject = new Parse.Object('MyObject'); + it("should not use transaction in a batch with transaction = false", async () => { + const myObject = new Parse.Object("MyObject"); await myObject.save(); - spyOn(Collection.prototype, 'findOneAndUpdate').and.callThrough(); + spyOn(Collection.prototype, "findOneAndUpdate").and.callThrough(); await request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/batch', + url: "http://localhost:8378/1/batch", body: JSON.stringify({ requests: [ { - method: 'PUT', - path: '/1/classes/MyObject/' + myObject.id, - body: { myAttribute: 'myValue' }, + method: "PUT", + path: "/1/classes/MyObject/" + myObject.id, + body: { myAttribute: "myValue" }, }, ], transaction: false, @@ -525,22 +552,22 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { expect(found).toBe(true); }); - it('should not use transaction in a batch with no transaction option sent', async () => { - const myObject = new Parse.Object('MyObject'); + it("should not use transaction in a batch with no transaction option sent", async () => { + const myObject = new Parse.Object("MyObject"); await myObject.save(); - spyOn(Collection.prototype, 'findOneAndUpdate').and.callThrough(); + spyOn(Collection.prototype, "findOneAndUpdate").and.callThrough(); await request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/batch', + url: "http://localhost:8378/1/batch", body: JSON.stringify({ requests: [ { - method: 'PUT', - path: '/1/classes/MyObject/' + myObject.id, - body: { myAttribute: 'myValue' }, + method: "PUT", + path: "/1/classes/MyObject/" + myObject.id, + body: { myAttribute: "myValue" }, }, ], }), @@ -554,17 +581,17 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { expect(found).toBe(true); }); - it('should not use transaction in a put request', async () => { - const myObject = new Parse.Object('MyObject'); + it("should not use transaction in a put request", async () => { + const myObject = new Parse.Object("MyObject"); await myObject.save(); - spyOn(Collection.prototype, 'findOneAndUpdate').and.callThrough(); + spyOn(Collection.prototype, "findOneAndUpdate").and.callThrough(); await request({ - method: 'PUT', + method: "PUT", headers: headers, - url: 'http://localhost:8378/1/classes/MyObject/' + myObject.id, - body: { myAttribute: 'myValue' }, + url: "http://localhost:8378/1/classes/MyObject/" + myObject.id, + body: { myAttribute: "myValue" }, }); let found = false; @@ -575,10 +602,10 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { expect(found).toBe(true); }); - it('should not use transactions when using SDK insert', async () => { - spyOn(Collection.prototype, 'insertOne').and.callThrough(); + it("should not use transactions when using SDK insert", async () => { + spyOn(Collection.prototype, "insertOne").and.callThrough(); - const myObject = new Parse.Object('MyObject'); + const myObject = new Parse.Object("MyObject"); await myObject.save(); const calls = Collection.prototype.insertOne.calls.all(); @@ -588,13 +615,13 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { }); }); - it('should not use transactions when using SDK update', async () => { - spyOn(Collection.prototype, 'findOneAndUpdate').and.callThrough(); + it("should not use transactions when using SDK update", async () => { + spyOn(Collection.prototype, "findOneAndUpdate").and.callThrough(); - const myObject = new Parse.Object('MyObject'); + const myObject = new Parse.Object("MyObject"); await myObject.save(); - myObject.set('myAttribute', 'myValue'); + myObject.set("myAttribute", "myValue"); await myObject.save(); const calls = Collection.prototype.findOneAndUpdate.calls.all(); @@ -604,10 +631,10 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { }); }); - it('should not use transactions when using SDK delete', async () => { - spyOn(Collection.prototype, 'deleteMany').and.callThrough(); + it("should not use transactions when using SDK delete", async () => { + spyOn(Collection.prototype, "deleteMany").and.callThrough(); - const myObject = new Parse.Object('MyObject'); + const myObject = new Parse.Object("MyObject"); await myObject.save(); await myObject.destroy(); @@ -620,26 +647,26 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { }); }); - describe('watch _SCHEMA', () => { - it('should change', async done => { + describe("watch _SCHEMA", () => { + it("should change", async done => { const adapter = new MongoStorageAdapter({ uri: databaseURI, - collectionPrefix: '', + collectionPrefix: "", mongoOptions: { enableSchemaHooks: true }, }); await reconfigureServer({ databaseAdapter: adapter }); expect(adapter.enableSchemaHooks).toBe(true); - spyOn(adapter, '_onchange'); + spyOn(adapter, "_onchange"); const schema = { fields: { - array: { type: 'Array' }, - object: { type: 'Object' }, - date: { type: 'Date' }, + array: { type: "Array" }, + object: { type: "Object" }, + date: { type: "Date" }, }, }; - await adapter.createClass('Stuff', schema); - const myClassSchema = await adapter.getClass('Stuff'); + await adapter.createClass("Stuff", schema); + const myClassSchema = await adapter.getClass("Stuff"); expect(myClassSchema).toBeDefined(); setTimeout(() => { expect(adapter._onchange).toHaveBeenCalled(); diff --git a/spec/MongoTransform.spec.js b/spec/MongoTransform.spec.js index 60adb4b7f0..8491e85311 100644 --- a/spec/MongoTransform.spec.js +++ b/spec/MongoTransform.spec.js @@ -1,34 +1,34 @@ // These tests are unit tests designed to only test transform.js. -'use strict'; +"use strict"; -const transform = require('../lib/Adapters/Storage/Mongo/MongoTransform'); -const dd = require('deep-diff'); -const mongodb = require('mongodb'); -const Utils = require('../lib/Utils'); +const transform = require("../lib/Adapters/Storage/Mongo/MongoTransform"); +const dd = require("deep-diff"); +const mongodb = require("mongodb"); +const Utils = require("../lib/Utils"); -describe('parseObjectToMongoObjectForCreate', () => { - it('a basic number', done => { +describe("parseObjectToMongoObjectForCreate", () => { + it("a basic number", done => { const input = { five: 5 }; const output = transform.parseObjectToMongoObjectForCreate(null, input, { - fields: { five: { type: 'Number' } }, + fields: { five: { type: "Number" } }, }); jequal(input, output); done(); }); - it('an object with null values', done => { + it("an object with null values", done => { const input = { objectWithNullValues: { isNull: null, notNull: 3 } }; const output = transform.parseObjectToMongoObjectForCreate(null, input, { - fields: { objectWithNullValues: { type: 'object' } }, + fields: { objectWithNullValues: { type: "object" } }, }); jequal(input, output); done(); }); - it('built-in timestamps with date', done => { + it("built-in timestamps with date", done => { const input = { - createdAt: '2015-10-06T21:24:50.332Z', - updatedAt: '2015-10-06T21:24:50.332Z', + createdAt: "2015-10-06T21:24:50.332Z", + updatedAt: "2015-10-06T21:24:50.332Z", }; const output = transform.parseObjectToMongoObjectForCreate(null, input, { fields: {}, @@ -38,17 +38,17 @@ describe('parseObjectToMongoObjectForCreate', () => { done(); }); - it('array of pointers', done => { + it("array of pointers", done => { const pointer = { - __type: 'Pointer', - objectId: 'myId', - className: 'Blah', + __type: "Pointer", + objectId: "myId", + className: "Blah", }; const out = transform.parseObjectToMongoObjectForCreate( null, { pointers: [pointer] }, { - fields: { pointers: { type: 'Array' } }, + fields: { pointers: { type: "Array" } }, } ); jequal([pointer], out.pointers); @@ -57,8 +57,8 @@ describe('parseObjectToMongoObjectForCreate', () => { //TODO: object creation requests shouldn't be seeing __op delete, it makes no sense to //have __op delete in a new object. Figure out what this should actually be testing. - xit('a delete op', done => { - const input = { deleteMe: { __op: 'Delete' } }; + xit("a delete op", done => { + const input = { deleteMe: { __op: "Delete" } }; const output = transform.parseObjectToMongoObjectForCreate(null, input, { fields: {}, }); @@ -66,30 +66,30 @@ describe('parseObjectToMongoObjectForCreate', () => { done(); }); - it('Doesnt allow ACL, as Parse Server should tranform ACL to _wperm + _rperm', done => { - const input = { ACL: { '0123': { read: true, write: true } } }; + it("Doesnt allow ACL, as Parse Server should tranform ACL to _wperm + _rperm", done => { + const input = { ACL: { "0123": { read: true, write: true } } }; expect(() => transform.parseObjectToMongoObjectForCreate(null, input, { fields: {} }) ).toThrow(); done(); }); - it('parse geopoint to mongo', done => { + it("parse geopoint to mongo", done => { const lat = -45; const lng = 45; - const geoPoint = { __type: 'GeoPoint', latitude: lat, longitude: lng }; + const geoPoint = { __type: "GeoPoint", latitude: lat, longitude: lng }; const out = transform.parseObjectToMongoObjectForCreate( null, { location: geoPoint }, { - fields: { location: { type: 'GeoPoint' } }, + fields: { location: { type: "GeoPoint" } }, } ); expect(out.location).toEqual([lng, lat]); done(); }); - it('parse polygon to mongo', done => { + it("parse polygon to mongo", done => { const lat1 = -45; const lng1 = 45; const lat2 = -55; @@ -97,7 +97,7 @@ describe('parseObjectToMongoObjectForCreate', () => { const lat3 = -65; const lng3 = 65; const polygon = { - __type: 'Polygon', + __type: "Polygon", coordinates: [ [lat1, lng1], [lat2, lng2], @@ -108,7 +108,7 @@ describe('parseObjectToMongoObjectForCreate', () => { null, { location: polygon }, { - fields: { location: { type: 'Polygon' } }, + fields: { location: { type: "Polygon" } }, } ); expect(out.location.coordinates).toEqual([ @@ -122,113 +122,113 @@ describe('parseObjectToMongoObjectForCreate', () => { done(); }); - it('in array', done => { - const geoPoint = { __type: 'GeoPoint', longitude: 180, latitude: -180 }; + it("in array", done => { + const geoPoint = { __type: "GeoPoint", longitude: 180, latitude: -180 }; const out = transform.parseObjectToMongoObjectForCreate( null, { locations: [geoPoint, geoPoint] }, { - fields: { locations: { type: 'Array' } }, + fields: { locations: { type: "Array" } }, } ); expect(out.locations).toEqual([geoPoint, geoPoint]); done(); }); - it('in sub-object', done => { - const geoPoint = { __type: 'GeoPoint', longitude: 180, latitude: -180 }; + it("in sub-object", done => { + const geoPoint = { __type: "GeoPoint", longitude: 180, latitude: -180 }; const out = transform.parseObjectToMongoObjectForCreate( null, { locations: { start: geoPoint } }, { - fields: { locations: { type: 'Object' } }, + fields: { locations: { type: "Object" } }, } ); expect(out).toEqual({ locations: { start: geoPoint } }); done(); }); - it('objectId', done => { - const out = transform.transformWhere(null, { objectId: 'foo' }); - expect(out._id).toEqual('foo'); + it("objectId", done => { + const out = transform.transformWhere(null, { objectId: "foo" }); + expect(out._id).toEqual("foo"); done(); }); - it('objectId in a list', done => { + it("objectId in a list", done => { const input = { - objectId: { $in: ['one', 'two', 'three'] }, + objectId: { $in: ["one", "two", "three"] }, }; const output = transform.transformWhere(null, input); jequal(input.objectId, output._id); done(); }); - it('built-in timestamps', done => { + it("built-in timestamps", done => { const input = { createdAt: new Date(), updatedAt: new Date() }; const output = transform.mongoObjectToParseObject(null, input, { fields: {}, }); - expect(typeof output.createdAt).toEqual('string'); - expect(typeof output.updatedAt).toEqual('string'); + expect(typeof output.createdAt).toEqual("string"); + expect(typeof output.updatedAt).toEqual("string"); done(); }); - it('pointer', done => { - const input = { _p_userPointer: '_User$123' }; + it("pointer", done => { + const input = { _p_userPointer: "_User$123" }; const output = transform.mongoObjectToParseObject(null, input, { - fields: { userPointer: { type: 'Pointer', targetClass: '_User' } }, + fields: { userPointer: { type: "Pointer", targetClass: "_User" } }, }); - expect(typeof output.userPointer).toEqual('object'); + expect(typeof output.userPointer).toEqual("object"); expect(output.userPointer).toEqual({ - __type: 'Pointer', - className: '_User', - objectId: '123', + __type: "Pointer", + className: "_User", + objectId: "123", }); done(); }); - it('null pointer', done => { + it("null pointer", done => { const input = { _p_userPointer: null }; const output = transform.mongoObjectToParseObject(null, input, { - fields: { userPointer: { type: 'Pointer', targetClass: '_User' } }, + fields: { userPointer: { type: "Pointer", targetClass: "_User" } }, }); expect(output.userPointer).toBeUndefined(); done(); }); - it('file', done => { - const input = { picture: 'pic.jpg' }; + it("file", done => { + const input = { picture: "pic.jpg" }; const output = transform.mongoObjectToParseObject(null, input, { - fields: { picture: { type: 'File' } }, + fields: { picture: { type: "File" } }, }); - expect(typeof output.picture).toEqual('object'); - expect(output.picture).toEqual({ __type: 'File', name: 'pic.jpg' }); + expect(typeof output.picture).toEqual("object"); + expect(output.picture).toEqual({ __type: "File", name: "pic.jpg" }); done(); }); - it('mongo geopoint to parse', done => { + it("mongo geopoint to parse", done => { const lat = -45; const lng = 45; const input = { location: [lng, lat] }; const output = transform.mongoObjectToParseObject(null, input, { - fields: { location: { type: 'GeoPoint' } }, + fields: { location: { type: "GeoPoint" } }, }); - expect(typeof output.location).toEqual('object'); + expect(typeof output.location).toEqual("object"); expect(output.location).toEqual({ - __type: 'GeoPoint', + __type: "GeoPoint", latitude: lat, longitude: lng, }); done(); }); - it('mongo polygon to parse', done => { + it("mongo polygon to parse", done => { const lat = -45; const lng = 45; // Mongo stores polygon in WGS84 lng/lat const input = { location: { - type: 'Polygon', + type: "Polygon", coordinates: [ [ [lat, lng], @@ -238,11 +238,11 @@ describe('parseObjectToMongoObjectForCreate', () => { }, }; const output = transform.mongoObjectToParseObject(null, input, { - fields: { location: { type: 'Polygon' } }, + fields: { location: { type: "Polygon" } }, }); - expect(typeof output.location).toEqual('object'); + expect(typeof output.location).toEqual("object"); expect(output.location).toEqual({ - __type: 'Polygon', + __type: "Polygon", coordinates: [ [lng, lat], [lng, lat], @@ -251,151 +251,151 @@ describe('parseObjectToMongoObjectForCreate', () => { done(); }); - it('bytes', done => { - const input = { binaryData: 'aGVsbG8gd29ybGQ=' }; + it("bytes", done => { + const input = { binaryData: "aGVsbG8gd29ybGQ=" }; const output = transform.mongoObjectToParseObject(null, input, { - fields: { binaryData: { type: 'Bytes' } }, + fields: { binaryData: { type: "Bytes" } }, }); - expect(typeof output.binaryData).toEqual('object'); + expect(typeof output.binaryData).toEqual("object"); expect(output.binaryData).toEqual({ - __type: 'Bytes', - base64: 'aGVsbG8gd29ybGQ=', + __type: "Bytes", + base64: "aGVsbG8gd29ybGQ=", }); done(); }); - it('nested array', done => { - const input = { arr: [{ _testKey: 'testValue' }] }; + it("nested array", done => { + const input = { arr: [{ _testKey: "testValue" }] }; const output = transform.mongoObjectToParseObject(null, input, { - fields: { arr: { type: 'Array' } }, + fields: { arr: { type: "Array" } }, }); expect(Array.isArray(output.arr)).toEqual(true); - expect(output.arr).toEqual([{ _testKey: 'testValue' }]); + expect(output.arr).toEqual([{ _testKey: "testValue" }]); done(); }); - it('untransforms objects containing nested special keys', done => { + it("untransforms objects containing nested special keys", done => { const input = { array: [ { - _id: 'Test ID', + _id: "Test ID", _hashed_password: "I Don't know why you would name a key this, but if you do it should work", _tombstone: { _updated_at: "I'm sure people will nest keys like this", _acl: 7, - _id: { someString: 'str', someNumber: 7 }, + _id: { someString: "str", someNumber: 7 }, regularKey: { moreContents: [1, 2, 3] }, }, - regularKey: 'some data', + regularKey: "some data", }, ], }; const output = transform.mongoObjectToParseObject(null, input, { - fields: { array: { type: 'Array' } }, + fields: { array: { type: "Array" } }, }); expect(dd(output, input)).toEqual(undefined); done(); }); - it('changes new pointer key', done => { + it("changes new pointer key", done => { const input = { - somePointer: { __type: 'Pointer', className: 'Micro', objectId: 'oft' }, + somePointer: { __type: "Pointer", className: "Micro", objectId: "oft" }, }; const output = transform.parseObjectToMongoObjectForCreate(null, input, { - fields: { somePointer: { type: 'Pointer' } }, + fields: { somePointer: { type: "Pointer" } }, }); - expect(typeof output._p_somePointer).toEqual('string'); - expect(output._p_somePointer).toEqual('Micro$oft'); + expect(typeof output._p_somePointer).toEqual("string"); + expect(output._p_somePointer).toEqual("Micro$oft"); done(); }); - it('changes existing pointer keys', done => { + it("changes existing pointer keys", done => { const input = { userPointer: { - __type: 'Pointer', - className: '_User', - objectId: 'qwerty', + __type: "Pointer", + className: "_User", + objectId: "qwerty", }, }; const output = transform.parseObjectToMongoObjectForCreate(null, input, { - fields: { userPointer: { type: 'Pointer' } }, + fields: { userPointer: { type: "Pointer" } }, }); - expect(typeof output._p_userPointer).toEqual('string'); - expect(output._p_userPointer).toEqual('_User$qwerty'); + expect(typeof output._p_userPointer).toEqual("string"); + expect(output._p_userPointer).toEqual("_User$qwerty"); done(); }); - it('writes the old ACL format in addition to rperm and wperm on create', done => { + it("writes the old ACL format in addition to rperm and wperm on create", done => { const input = { - _rperm: ['*'], - _wperm: ['Kevin'], + _rperm: ["*"], + _wperm: ["Kevin"], }; const output = transform.parseObjectToMongoObjectForCreate(null, input, { fields: {}, }); - expect(typeof output._acl).toEqual('object'); - expect(output._acl['Kevin'].w).toBeTruthy(); - expect(output._acl['Kevin'].r).toBeUndefined(); + expect(typeof output._acl).toEqual("object"); + expect(output._acl["Kevin"].w).toBeTruthy(); + expect(output._acl["Kevin"].r).toBeUndefined(); expect(output._rperm).toEqual(input._rperm); expect(output._wperm).toEqual(input._wperm); done(); }); - it('removes Relation types', done => { + it("removes Relation types", done => { const input = { - aRelation: { __type: 'Relation', className: 'Stuff' }, + aRelation: { __type: "Relation", className: "Stuff" }, }; const output = transform.parseObjectToMongoObjectForCreate(null, input, { fields: { - aRelation: { __type: 'Relation', className: 'Stuff' }, + aRelation: { __type: "Relation", className: "Stuff" }, }, }); expect(output).toEqual({}); done(); }); - it('writes the old ACL format in addition to rperm and wperm on update', done => { + it("writes the old ACL format in addition to rperm and wperm on update", done => { const input = { - _rperm: ['*'], - _wperm: ['Kevin'], + _rperm: ["*"], + _wperm: ["Kevin"], }; const output = transform.transformUpdate(null, input, { fields: {} }); const set = output.$set; - expect(typeof set).toEqual('object'); - expect(typeof set._acl).toEqual('object'); - expect(set._acl['Kevin'].w).toBeTruthy(); - expect(set._acl['Kevin'].r).toBeUndefined(); + expect(typeof set).toEqual("object"); + expect(typeof set._acl).toEqual("object"); + expect(set._acl["Kevin"].w).toBeTruthy(); + expect(set._acl["Kevin"].r).toBeUndefined(); expect(set._rperm).toEqual(input._rperm); expect(set._wperm).toEqual(input._wperm); done(); }); - it('untransforms from _rperm and _wperm to ACL', done => { + it("untransforms from _rperm and _wperm to ACL", done => { const input = { - _rperm: ['*'], - _wperm: ['Kevin'], + _rperm: ["*"], + _wperm: ["Kevin"], }; const output = transform.mongoObjectToParseObject(null, input, { fields: {}, }); - expect(output._rperm).toEqual(['*']); - expect(output._wperm).toEqual(['Kevin']); + expect(output._rperm).toEqual(["*"]); + expect(output._wperm).toEqual(["Kevin"]); expect(output.ACL).toBeUndefined(); done(); }); - it('untransforms mongodb number types', done => { + it("untransforms mongodb number types", done => { const input = { long: mongodb.Long.fromNumber(Number.MAX_SAFE_INTEGER), double: new mongodb.Double(Number.MAX_VALUE), }; const output = transform.mongoObjectToParseObject(null, input, { fields: { - long: { type: 'Number' }, - double: { type: 'Number' }, + long: { type: "Number" }, + double: { type: "Number" }, }, }); expect(output.long).toBe(Number.MAX_SAFE_INTEGER); @@ -403,65 +403,69 @@ describe('parseObjectToMongoObjectForCreate', () => { done(); }); - it('Date object where iso attribute is of type Date', done => { + it("Date object where iso attribute is of type Date", done => { const input = { - ts: { __type: 'Date', iso: new Date('2017-01-18T00:00:00.000Z') }, + ts: { __type: "Date", iso: new Date("2017-01-18T00:00:00.000Z") }, }; const output = transform.mongoObjectToParseObject(null, input, { fields: { - ts: { type: 'Date' }, + ts: { type: "Date" }, }, }); - expect(output.ts.iso).toEqual('2017-01-18T00:00:00.000Z'); + expect(output.ts.iso).toEqual("2017-01-18T00:00:00.000Z"); done(); }); - it('Date object where iso attribute is of type String', done => { + it("Date object where iso attribute is of type String", done => { const input = { - ts: { __type: 'Date', iso: '2017-01-18T00:00:00.000Z' }, + ts: { __type: "Date", iso: "2017-01-18T00:00:00.000Z" }, }; const output = transform.mongoObjectToParseObject(null, input, { fields: { - ts: { type: 'Date' }, + ts: { type: "Date" }, }, }); - expect(output.ts.iso).toEqual('2017-01-18T00:00:00.000Z'); + expect(output.ts.iso).toEqual("2017-01-18T00:00:00.000Z"); done(); }); - it('object with undefined nested values', () => { + it("object with undefined nested values", () => { const input = { - _id: 'vQHyinCW1l', - urls: { firstUrl: 'https://', secondUrl: undefined }, + _id: "vQHyinCW1l", + urls: { firstUrl: "https://", secondUrl: undefined }, }; const output = transform.mongoObjectToParseObject(null, input, { fields: { - urls: { type: 'Object' }, + urls: { type: "Object" }, }, }); expect(output.urls).toEqual({ - firstUrl: 'https://', + firstUrl: "https://", secondUrl: undefined, }); }); - it('undefined objects', () => { + it("undefined objects", () => { const input = { - _id: 'vQHyinCW1l', + _id: "vQHyinCW1l", urls: undefined, }; const output = transform.mongoObjectToParseObject(null, input, { fields: { - urls: { type: 'Object' }, + urls: { type: "Object" }, }, }); expect(output.urls).toBeUndefined(); }); - it('$regex in $all list', done => { + it("$regex in $all list", done => { const input = { arrayField: { - $all: [{ $regex: '^\\Qone\\E' }, { $regex: '^\\Qtwo\\E' }, { $regex: '^\\Qthree\\E' }], + $all: [ + { $regex: "^\\Qone\\E" }, + { $regex: "^\\Qtwo\\E" }, + { $regex: "^\\Qthree\\E" }, + ], }, }; const outputValue = { @@ -484,10 +488,10 @@ describe('parseObjectToMongoObjectForCreate', () => { done(); }); - it('all values in $all must be $regex (start with string) or non $regex (start with string)', done => { + it("all values in $all must be $regex (start with string) or non $regex (start with string)", done => { const input = { arrayField: { - $all: [{ $regex: '^\\Qone\\E' }, { $unknown: '^\\Qtwo\\E' }], + $all: [{ $regex: "^\\Qone\\E" }, { $unknown: "^\\Qtwo\\E" }], }, }; @@ -497,39 +501,39 @@ describe('parseObjectToMongoObjectForCreate', () => { done(); }); - it('ignores User authData field in DB so it can be synthesized in code', done => { + it("ignores User authData field in DB so it can be synthesized in code", done => { const input = { - _id: '123', - _auth_data_acme: { id: 'abc' }, + _id: "123", + _auth_data_acme: { id: "abc" }, authData: null, }; - const output = transform.mongoObjectToParseObject('_User', input, { + const output = transform.mongoObjectToParseObject("_User", input, { fields: {}, }); - expect(output.authData.acme.id).toBe('abc'); + expect(output.authData.acme.id).toBe("abc"); done(); }); - it('can set authData when not User class', done => { + it("can set authData when not User class", done => { const input = { - _id: '123', - authData: 'random', + _id: "123", + authData: "random", }; - const output = transform.mongoObjectToParseObject('TestObject', input, { + const output = transform.mongoObjectToParseObject("TestObject", input, { fields: {}, }); - expect(output.authData).toBe('random'); + expect(output.authData).toBe("random"); done(); }); }); -it('cannot have a custom field name beginning with underscore', done => { +it("cannot have a custom field name beginning with underscore", done => { const input = { - _id: '123', - _thisFieldNameIs: 'invalid', + _id: "123", + _thisFieldNameIs: "invalid", }; try { - transform.mongoObjectToParseObject('TestObject', input, { + transform.mongoObjectToParseObject("TestObject", input, { fields: {}, }); } catch (e) { @@ -538,14 +542,14 @@ it('cannot have a custom field name beginning with underscore', done => { done(); }); -describe('transformUpdate', () => { - it('removes Relation types', done => { +describe("transformUpdate", () => { + it("removes Relation types", done => { const input = { - aRelation: { __type: 'Relation', className: 'Stuff' }, + aRelation: { __type: "Relation", className: "Stuff" }, }; const output = transform.transformUpdate(null, input, { fields: { - aRelation: { __type: 'Relation', className: 'Stuff' }, + aRelation: { __type: "Relation", className: "Stuff" }, }, }); expect(output).toEqual({}); @@ -553,14 +557,14 @@ describe('transformUpdate', () => { }); }); -describe('transformConstraint', () => { - describe('$relativeTime', () => { - it('should error on $eq, $ne, and $exists', () => { +describe("transformConstraint", () => { + describe("$relativeTime", () => { + it("should error on $eq, $ne, and $exists", () => { expect(() => { transform.transformConstraint({ $eq: { ttl: { - $relativeTime: '12 days ago', + $relativeTime: "12 days ago", }, }, }); @@ -570,7 +574,7 @@ describe('transformConstraint', () => { transform.transformConstraint({ $ne: { ttl: { - $relativeTime: '12 days ago', + $relativeTime: "12 days ago", }, }, }); @@ -579,7 +583,7 @@ describe('transformConstraint', () => { expect(() => { transform.transformConstraint({ $exists: { - $relativeTime: '12 days ago', + $relativeTime: "12 days ago", }, }); }).toThrow(); @@ -587,90 +591,92 @@ describe('transformConstraint', () => { }); }); -describe('relativeTimeToDate', () => { - const now = new Date('2017-09-26T13:28:16.617Z'); +describe("relativeTimeToDate", () => { + const now = new Date("2017-09-26T13:28:16.617Z"); - describe('In the future', () => { - it('should parse valid natural time', () => { - const text = 'in 1 year 2 weeks 12 days 10 hours 24 minutes 30 seconds'; + describe("In the future", () => { + it("should parse valid natural time", () => { + const text = "in 1 year 2 weeks 12 days 10 hours 24 minutes 30 seconds"; const { result, status, info } = Utils.relativeTimeToDate(text, now); - expect(result.toISOString()).toBe('2018-10-22T23:52:46.617Z'); - expect(status).toBe('success'); - expect(info).toBe('future'); + expect(result.toISOString()).toBe("2018-10-22T23:52:46.617Z"); + expect(status).toBe("success"); + expect(info).toBe("future"); }); }); - describe('In the past', () => { - it('should parse valid natural time', () => { - const text = '2 days 12 hours 1 minute 12 seconds ago'; + describe("In the past", () => { + it("should parse valid natural time", () => { + const text = "2 days 12 hours 1 minute 12 seconds ago"; const { result, status, info } = Utils.relativeTimeToDate(text, now); - expect(result.toISOString()).toBe('2017-09-24T01:27:04.617Z'); - expect(status).toBe('success'); - expect(info).toBe('past'); + expect(result.toISOString()).toBe("2017-09-24T01:27:04.617Z"); + expect(status).toBe("success"); + expect(info).toBe("past"); }); }); - describe('From now', () => { - it('should equal current time', () => { - const text = 'now'; + describe("From now", () => { + it("should equal current time", () => { + const text = "now"; const { result, status, info } = Utils.relativeTimeToDate(text, now); - expect(result.toISOString()).toBe('2017-09-26T13:28:16.617Z'); - expect(status).toBe('success'); - expect(info).toBe('present'); + expect(result.toISOString()).toBe("2017-09-26T13:28:16.617Z"); + expect(status).toBe("success"); + expect(info).toBe("present"); }); }); - describe('Error cases', () => { - it('should error if string is completely gibberish', () => { - expect(Utils.relativeTimeToDate('gibberishasdnklasdnjklasndkl123j123')).toEqual({ - status: 'error', + describe("Error cases", () => { + it("should error if string is completely gibberish", () => { + expect( + Utils.relativeTimeToDate("gibberishasdnklasdnjklasndkl123j123") + ).toEqual({ + status: "error", info: "Time should either start with 'in' or end with 'ago'", }); }); - it('should error if string contains neither `ago` nor `in`', () => { - expect(Utils.relativeTimeToDate('12 hours 1 minute')).toEqual({ - status: 'error', + it("should error if string contains neither `ago` nor `in`", () => { + expect(Utils.relativeTimeToDate("12 hours 1 minute")).toEqual({ + status: "error", info: "Time should either start with 'in' or end with 'ago'", }); }); - it('should error if there are missing units or numbers', () => { - expect(Utils.relativeTimeToDate('in 12 hours 1')).toEqual({ - status: 'error', - info: 'Invalid time string. Dangling unit or number.', + it("should error if there are missing units or numbers", () => { + expect(Utils.relativeTimeToDate("in 12 hours 1")).toEqual({ + status: "error", + info: "Invalid time string. Dangling unit or number.", }); - expect(Utils.relativeTimeToDate('12 hours minute ago')).toEqual({ - status: 'error', - info: 'Invalid time string. Dangling unit or number.', + expect(Utils.relativeTimeToDate("12 hours minute ago")).toEqual({ + status: "error", + info: "Invalid time string. Dangling unit or number.", }); }); - it('should error on floating point numbers', () => { - expect(Utils.relativeTimeToDate('in 12.3 hours')).toEqual({ - status: 'error', + it("should error on floating point numbers", () => { + expect(Utils.relativeTimeToDate("in 12.3 hours")).toEqual({ + status: "error", info: "'12.3' is not an integer.", }); }); - it('should error if numbers are invalid', () => { - expect(Utils.relativeTimeToDate('12 hours 123a minute ago')).toEqual({ - status: 'error', + it("should error if numbers are invalid", () => { + expect(Utils.relativeTimeToDate("12 hours 123a minute ago")).toEqual({ + status: "error", info: "'123a' is not an integer.", }); }); - it('should error on invalid interval units', () => { - expect(Utils.relativeTimeToDate('4 score 7 years ago')).toEqual({ - status: 'error', + it("should error on invalid interval units", () => { + expect(Utils.relativeTimeToDate("4 score 7 years ago")).toEqual({ + status: "error", info: "Invalid interval: 'score'", }); }); it("should error when string contains 'ago' and 'in'", () => { - expect(Utils.relativeTimeToDate('in 1 day 2 minutes ago')).toEqual({ - status: 'error', + expect(Utils.relativeTimeToDate("in 1 day 2 minutes ago")).toEqual({ + status: "error", info: "Time cannot have both 'in' and 'ago'", }); }); diff --git a/spec/NullCacheAdapter.spec.js b/spec/NullCacheAdapter.spec.js index f5d5e508f4..4d3dd40c62 100644 --- a/spec/NullCacheAdapter.spec.js +++ b/spec/NullCacheAdapter.spec.js @@ -1,21 +1,27 @@ -const NullCacheAdapter = require('../lib/Adapters/Cache/NullCacheAdapter').default; +const NullCacheAdapter = + require("../lib/Adapters/Cache/NullCacheAdapter").default; -describe('NullCacheAdapter', function () { - const KEY = 'hello'; - const VALUE = 'world'; +describe("NullCacheAdapter", function () { + const KEY = "hello"; + const VALUE = "world"; - it('should expose promisifyed methods', done => { + it("should expose promisifyed methods", done => { const cache = new NullCacheAdapter({ ttl: NaN, }); // Verify all methods return promises. - Promise.all([cache.put(KEY, VALUE), cache.del(KEY), cache.get(KEY), cache.clear()]).then(() => { + Promise.all([ + cache.put(KEY, VALUE), + cache.del(KEY), + cache.get(KEY), + cache.clear(), + ]).then(() => { done(); }); }); - it('should get/set/clear', done => { + it("should get/set/clear", done => { const cache = new NullCacheAdapter({ ttl: NaN, }); diff --git a/spec/OAuth1.spec.js b/spec/OAuth1.spec.js index 34dc8b6925..522b389d58 100644 --- a/spec/OAuth1.spec.js +++ b/spec/OAuth1.spec.js @@ -1,124 +1,129 @@ -const OAuth = require('../lib/Adapters/Auth/OAuth1Client'); +const OAuth = require("../lib/Adapters/Auth/OAuth1Client"); -describe('OAuth', function () { - it('Nonce should have right length', done => { +describe("OAuth", function () { + it("Nonce should have right length", done => { jequal(OAuth.nonce().length, 30); done(); }); - it('Should properly build parameter string', done => { + it("Should properly build parameter string", done => { const string = OAuth.buildParameterString({ c: 1, a: 2, b: 3 }); - jequal(string, 'a=2&b=3&c=1'); + jequal(string, "a=2&b=3&c=1"); done(); }); - it('Should properly build empty parameter string', done => { + it("Should properly build empty parameter string", done => { const string = OAuth.buildParameterString(); - jequal(string, ''); + jequal(string, ""); done(); }); - it('Should properly build signature string', done => { - const string = OAuth.buildSignatureString('get', 'http://dummy.com', ''); - jequal(string, 'GET&http%3A%2F%2Fdummy.com&'); + it("Should properly build signature string", done => { + const string = OAuth.buildSignatureString("get", "http://dummy.com", ""); + jequal(string, "GET&http%3A%2F%2Fdummy.com&"); done(); }); - it('Should properly generate request signature', done => { + it("Should properly generate request signature", done => { let request = { - host: 'dummy.com', - path: 'path', + host: "dummy.com", + path: "path", }; const oauth_params = { oauth_timestamp: 123450000, - oauth_nonce: 'AAAAAAAAAAAAAAAAA', - oauth_consumer_key: 'hello', - oauth_token: 'token', + oauth_nonce: "AAAAAAAAAAAAAAAAA", + oauth_consumer_key: "hello", + oauth_token: "token", }; - const consumer_secret = 'world'; - const auth_token_secret = 'secret'; - request = OAuth.signRequest(request, oauth_params, consumer_secret, auth_token_secret); + const consumer_secret = "world"; + const auth_token_secret = "secret"; + request = OAuth.signRequest( + request, + oauth_params, + consumer_secret, + auth_token_secret + ); jequal( - request.headers['Authorization'], + request.headers["Authorization"], 'OAuth oauth_consumer_key="hello", oauth_nonce="AAAAAAAAAAAAAAAAA", oauth_signature="8K95bpQcDi9Nd2GkhumTVcw4%2BXw%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="123450000", oauth_token="token", oauth_version="1.0"' ); done(); }); - it('Should properly build request', done => { + it("Should properly build request", done => { const options = { - host: 'dummy.com', - consumer_key: 'hello', - consumer_secret: 'world', - auth_token: 'token', - auth_token_secret: 'secret', + host: "dummy.com", + consumer_key: "hello", + consumer_secret: "world", + auth_token: "token", + auth_token_secret: "secret", // Custom oauth params for tests oauth_params: { oauth_timestamp: 123450000, - oauth_nonce: 'AAAAAAAAAAAAAAAAA', + oauth_nonce: "AAAAAAAAAAAAAAAAA", }, }; - const path = 'path'; - const method = 'get'; + const path = "path"; + const method = "get"; const oauthClient = new OAuth(options); - const req = oauthClient.buildRequest(method, path, { query: 'param' }); + const req = oauthClient.buildRequest(method, path, { query: "param" }); jequal(req.host, options.host); - jequal(req.path, '/' + path + '?query=param'); - jequal(req.method, 'GET'); - jequal(req.headers['Content-Type'], 'application/x-www-form-urlencoded'); + jequal(req.path, "/" + path + "?query=param"); + jequal(req.method, "GET"); + jequal(req.headers["Content-Type"], "application/x-www-form-urlencoded"); jequal( - req.headers['Authorization'], + req.headers["Authorization"], 'OAuth oauth_consumer_key="hello", oauth_nonce="AAAAAAAAAAAAAAAAA", oauth_signature="wNkyEkDE%2F0JZ2idmqyrgHdvC0rs%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="123450000", oauth_token="token", oauth_version="1.0"' ); done(); }); function validateCannotAuthenticateError(data, done) { - jequal(typeof data, 'object'); - jequal(typeof data.errors, 'object'); + jequal(typeof data, "object"); + jequal(typeof data.errors, "object"); const errors = data.errors; - jequal(typeof errors[0], 'object'); + jequal(typeof errors[0], "object"); // Cannot authenticate error jequal(errors[0].code, 32); done(); } - xit('GET request for a resource that requires OAuth should fail with invalid credentials', done => { + xit("GET request for a resource that requires OAuth should fail with invalid credentials", done => { /* This endpoint has been chosen to make a request to an endpoint that requires OAuth which fails due to missing authentication. Any other endpoint from the Twitter API that requires OAuth can be used instead in case the currently used endpoint deprecates. */ const options = { - host: 'api.twitter.com', - consumer_key: 'invalid_consumer_key', - consumer_secret: 'invalid_consumer_secret', + host: "api.twitter.com", + consumer_key: "invalid_consumer_key", + consumer_secret: "invalid_consumer_secret", }; - const path = '/1.1/favorites/list.json'; - const params = { lang: 'en' }; + const path = "/1.1/favorites/list.json"; + const params = { lang: "en" }; const oauthClient = new OAuth(options); oauthClient.get(path, params).then(function (data) { validateCannotAuthenticateError(data, done); }); }); - xit('POST request for a resource that requires OAuth should fail with invalid credentials', done => { + xit("POST request for a resource that requires OAuth should fail with invalid credentials", done => { /* This endpoint has been chosen to make a request to an endpoint that requires OAuth which fails due to missing authentication. Any other endpoint from the Twitter API that requires OAuth can be used instead in case the currently used endpoint deprecates. */ const options = { - host: 'api.twitter.com', - consumer_key: 'invalid_consumer_key', - consumer_secret: 'invalid_consumer_secret', + host: "api.twitter.com", + consumer_key: "invalid_consumer_key", + consumer_secret: "invalid_consumer_secret", }; const body = { - lang: 'en', + lang: "en", }; - const path = '/1.1/account/settings.json'; + const path = "/1.1/account/settings.json"; const oauthClient = new OAuth(options); oauthClient.post(path, null, body).then(function (data) { @@ -126,16 +131,16 @@ describe('OAuth', function () { }); }); - it('Should fail a request', done => { + it("Should fail a request", done => { const options = { - host: 'localhost', - consumer_key: 'XXXXXXXXXXXXXXXXXXXXXXXXX', - consumer_secret: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', + host: "localhost", + consumer_key: "XXXXXXXXXXXXXXXXXXXXXXXXX", + consumer_secret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", }; const body = { - lang: 'en', + lang: "en", }; - const path = '/'; + const path = "/"; const oauthClient = new OAuth(options); oauthClient @@ -150,12 +155,12 @@ describe('OAuth', function () { }); }); - it('Should fail with missing options', done => { + it("Should fail with missing options", done => { const options = undefined; try { new OAuth(options); } catch (error) { - jequal(error.message, 'No options passed to OAuth'); + jequal(error.message, "No options passed to OAuth"); done(); } }); diff --git a/spec/PagesRouter.spec.js b/spec/PagesRouter.spec.js index 896c4dbffa..a23b41ced1 100644 --- a/spec/PagesRouter.spec.js +++ b/spec/PagesRouter.spec.js @@ -1,41 +1,42 @@ -'use strict'; - -const request = require('../lib/request'); -const fs = require('fs').promises; -const mustache = require('mustache'); -const Utils = require('../lib/Utils'); -const { Page } = require('../lib/Page'); -const Config = require('../lib/Config'); -const Definitions = require('../lib/Options/Definitions'); -const UserController = require('../lib/Controllers/UserController').UserController; +"use strict"; + +const request = require("../lib/request"); +const fs = require("fs").promises; +const mustache = require("mustache"); +const Utils = require("../lib/Utils"); +const { Page } = require("../lib/Page"); +const Config = require("../lib/Config"); +const Definitions = require("../lib/Options/Definitions"); +const UserController = + require("../lib/Controllers/UserController").UserController; const { PagesRouter, pages, pageParams, pageParamHeaderPrefix, -} = require('../lib/Routers/PagesRouter'); +} = require("../lib/Routers/PagesRouter"); -describe('Pages Router', () => { - describe('basic request', () => { +describe("Pages Router", () => { + describe("basic request", () => { let config; beforeEach(async () => { config = { - appId: 'test', - appName: 'exampleAppname', - publicServerURL: 'http://localhost:8378/1', + appId: "test", + appName: "exampleAppname", + publicServerURL: "http://localhost:8378/1", pages: { enableRouter: true }, }; await reconfigureServer(config); }); - it('responds with file content on direct page request', async () => { + it("responds with file content on direct page request", async () => { const urls = [ - 'http://localhost:8378/1/apps/email_verification_link_invalid.html', - 'http://localhost:8378/1/apps/choose_password?appId=test', - 'http://localhost:8378/1/apps/email_verification_success.html', - 'http://localhost:8378/1/apps/password_reset_success.html', - 'http://localhost:8378/1/apps/custom_json.html', + "http://localhost:8378/1/apps/email_verification_link_invalid.html", + "http://localhost:8378/1/apps/choose_password?appId=test", + "http://localhost:8378/1/apps/email_verification_success.html", + "http://localhost:8378/1/apps/password_reset_success.html", + "http://localhost:8378/1/apps/custom_json.html", ]; for (const url of urls) { const response = await request({ url }).catch(e => e); @@ -43,18 +44,18 @@ describe('Pages Router', () => { } }); - it('can load file from custom pages path', async () => { - config.pages.pagesPath = './public'; + it("can load file from custom pages path", async () => { + config.pages.pagesPath = "./public"; await reconfigureServer(config); const response = await request({ - url: 'http://localhost:8378/1/apps/email_verification_link_invalid.html', + url: "http://localhost:8378/1/apps/email_verification_link_invalid.html", }).catch(e => e); expect(response.status).toBe(200); }); - it('can load file from custom pages endpoint', async () => { - config.pages.pagesEndpoint = 'pages'; + it("can load file from custom pages endpoint", async () => { + config.pages.pagesEndpoint = "pages"; await reconfigureServer(config); const response = await request({ @@ -63,15 +64,15 @@ describe('Pages Router', () => { expect(response.status).toBe(200); }); - it('responds with 404 if publicServerURL is not configured', async () => { + it("responds with 404 if publicServerURL is not configured", async () => { await reconfigureServer({ - appName: 'unused', + appName: "unused", pages: { enableRouter: true }, }); const urls = [ - 'http://localhost:8378/1/apps/test/verify_email', - 'http://localhost:8378/1/apps/choose_password?appId=test', - 'http://localhost:8378/1/apps/test/request_password_reset', + "http://localhost:8378/1/apps/test/verify_email", + "http://localhost:8378/1/apps/choose_password?appId=test", + "http://localhost:8378/1/apps/test/request_password_reset", ]; for (const url of urls) { const response = await request({ url }).catch(e => e); @@ -79,13 +80,28 @@ describe('Pages Router', () => { } }); - it('responds with 403 access denied with invalid appId', async () => { + it("responds with 403 access denied with invalid appId", async () => { const reqs = [ - { url: 'http://localhost:8378/1/apps/invalid/verify_email', method: 'GET' }, - { url: 'http://localhost:8378/1/apps/choose_password?id=invalid', method: 'GET' }, - { url: 'http://localhost:8378/1/apps/invalid/request_password_reset', method: 'GET' }, - { url: 'http://localhost:8378/1/apps/invalid/request_password_reset', method: 'POST' }, - { url: 'http://localhost:8378/1/apps/invalid/resend_verification_email', method: 'POST' }, + { + url: "http://localhost:8378/1/apps/invalid/verify_email", + method: "GET", + }, + { + url: "http://localhost:8378/1/apps/choose_password?id=invalid", + method: "GET", + }, + { + url: "http://localhost:8378/1/apps/invalid/request_password_reset", + method: "GET", + }, + { + url: "http://localhost:8378/1/apps/invalid/request_password_reset", + method: "POST", + }, + { + url: "http://localhost:8378/1/apps/invalid/resend_verification_email", + method: "POST", + }, ]; for (const req of reqs) { const response = await request(req).catch(e => e); @@ -94,24 +110,26 @@ describe('Pages Router', () => { }); }); - describe('AJAX requests', () => { + describe("AJAX requests", () => { beforeEach(async () => { await reconfigureServer({ - appName: 'exampleAppname', - publicServerURL: 'http://localhost:8378/1', + appName: "exampleAppname", + publicServerURL: "http://localhost:8378/1", pages: { enableRouter: true }, }); }); - it('request_password_reset: responds with AJAX success', async () => { - spyOn(UserController.prototype, 'updatePassword').and.callFake(() => Promise.resolve()); + it("request_password_reset: responds with AJAX success", async () => { + spyOn(UserController.prototype, "updatePassword").and.callFake(() => + Promise.resolve() + ); const res = await request({ - method: 'POST', - url: 'http://localhost:8378/1/apps/test/request_password_reset', + method: "POST", + url: "http://localhost:8378/1/apps/test/request_password_reset", body: `new_password=user1&token=43634643`, headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'X-Requested-With': 'XMLHttpRequest', + "Content-Type": "application/x-www-form-urlencoded", + "X-Requested-With": "XMLHttpRequest", }, followRedirects: false, }).catch(e => e); @@ -119,15 +137,15 @@ describe('Pages Router', () => { expect(res.text).toEqual('"Password successfully reset"'); }); - it('request_password_reset: responds with AJAX error on missing password', async () => { + it("request_password_reset: responds with AJAX error on missing password", async () => { try { await request({ - method: 'POST', - url: 'http://localhost:8378/1/apps/test/request_password_reset', + method: "POST", + url: "http://localhost:8378/1/apps/test/request_password_reset", body: `new_password=&token=132414`, headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'X-Requested-With': 'XMLHttpRequest', + "Content-Type": "application/x-www-form-urlencoded", + "X-Requested-With": "XMLHttpRequest", }, followRedirects: false, }); @@ -137,15 +155,15 @@ describe('Pages Router', () => { } }); - it('request_password_reset: responds with AJAX error on missing token', async () => { + it("request_password_reset: responds with AJAX error on missing token", async () => { try { await request({ - method: 'POST', - url: 'http://localhost:8378/1/apps/test/request_password_reset', + method: "POST", + url: "http://localhost:8378/1/apps/test/request_password_reset", body: `new_password=user1&token=`, headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'X-Requested-With': 'XMLHttpRequest', + "Content-Type": "application/x-www-form-urlencoded", + "X-Requested-With": "XMLHttpRequest", }, followRedirects: false, }); @@ -156,7 +174,7 @@ describe('Pages Router', () => { }); }); - describe('pages', () => { + describe("pages", () => { let router = new PagesRouter(); let req; let config; @@ -166,7 +184,8 @@ describe('Pages Router', () => { let readFile; let exampleLocale; - const fillPlaceholders = (text, fill) => text.replace(/({{2,3}.*?}{2,3})/g, fill); + const fillPlaceholders = (text, fill) => + text.replace(/({{2,3}.*?}{2,3})/g, fill); async function reconfigureServerWithPagesConfig(pagesConfig) { config.pages = pagesConfig; await reconfigureServer(config); @@ -174,21 +193,27 @@ describe('Pages Router', () => { beforeEach(async () => { router = new PagesRouter(); - readFile = spyOn(fs, 'readFile').and.callThrough(); - goToPage = spyOn(PagesRouter.prototype, 'goToPage').and.callThrough(); - pageResponse = spyOn(PagesRouter.prototype, 'pageResponse').and.callThrough(); - redirectResponse = spyOn(PagesRouter.prototype, 'redirectResponse').and.callThrough(); - exampleLocale = 'de-AT'; + readFile = spyOn(fs, "readFile").and.callThrough(); + goToPage = spyOn(PagesRouter.prototype, "goToPage").and.callThrough(); + pageResponse = spyOn( + PagesRouter.prototype, + "pageResponse" + ).and.callThrough(); + redirectResponse = spyOn( + PagesRouter.prototype, + "redirectResponse" + ).and.callThrough(); + exampleLocale = "de-AT"; config = { - appId: 'test', - appName: 'ExampleAppName', + appId: "test", + appName: "ExampleAppName", verifyUserEmails: true, emailAdapter: { sendVerificationEmail: () => Promise.resolve(), sendPasswordResetEmail: () => Promise.resolve(), sendMail: () => {}, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", pages: { enableRouter: true, enableLocalization: true, @@ -196,7 +221,7 @@ describe('Pages Router', () => { }, }; req = { - method: 'GET', + method: "GET", config, query: { locale: exampleLocale, @@ -204,8 +229,8 @@ describe('Pages Router', () => { }; }); - describe('server options', () => { - it('uses default configuration when none is set', async () => { + describe("server options", () => { + it("uses default configuration when none is set", async () => { await reconfigureServerWithPagesConfig({}); expect(Config.get(Parse.applicationId).pages.enableRouter).toBe( Definitions.PagesOptions.enableRouter.default @@ -216,9 +241,9 @@ describe('Pages Router', () => { expect(Config.get(Parse.applicationId).pages.localizationJsonPath).toBe( Definitions.PagesOptions.localizationJsonPath.default ); - expect(Config.get(Parse.applicationId).pages.localizationFallbackLocale).toBe( - Definitions.PagesOptions.localizationFallbackLocale.default - ); + expect( + Config.get(Parse.applicationId).pages.localizationFallbackLocale + ).toBe(Definitions.PagesOptions.localizationFallbackLocale.default); expect(Config.get(Parse.applicationId).pages.placeholders).toBe( Definitions.PagesOptions.placeholders.default ); @@ -239,26 +264,26 @@ describe('Pages Router', () => { ); }); - it('throws on invalid configuration', async () => { + it("throws on invalid configuration", async () => { const options = [ [], - 'a', + "a", 0, true, - { enableRouter: 'a' }, + { enableRouter: "a" }, { enableRouter: 0 }, { enableRouter: {} }, { enableRouter: [] }, - { enableLocalization: 'a' }, + { enableLocalization: "a" }, { enableLocalization: 0 }, { enableLocalization: {} }, { enableLocalization: [] }, - { forceRedirect: 'a' }, + { forceRedirect: "a" }, { forceRedirect: 0 }, { forceRedirect: {} }, { forceRedirect: [] }, { placeholders: true }, - { placeholders: 'a' }, + { placeholders: "a" }, { placeholders: 0 }, { placeholders: [] }, { pagesPath: true }, @@ -271,7 +296,7 @@ describe('Pages Router', () => { { pagesEndpoint: [] }, { customUrls: true }, { customUrls: 0 }, - { customUrls: 'a' }, + { customUrls: "a" }, { customUrls: [] }, { localizationJsonPath: true }, { localizationJsonPath: 0 }, @@ -283,176 +308,204 @@ describe('Pages Router', () => { { localizationFallbackLocale: [] }, { customRoutes: true }, { customRoutes: 0 }, - { customRoutes: 'a' }, + { customRoutes: "a" }, { customRoutes: {} }, ]; for (const option of options) { - await expectAsync(reconfigureServerWithPagesConfig(option)).toBeRejected(); + await expectAsync( + reconfigureServerWithPagesConfig(option) + ).toBeRejected(); } }); }); - describe('placeholders', () => { - it('replaces placeholder in response content', async () => { - await expectAsync(router.goToPage(req, pages.passwordResetLinkInvalid)).toBeResolved(); + describe("placeholders", () => { + it("replaces placeholder in response content", async () => { + await expectAsync( + router.goToPage(req, pages.passwordResetLinkInvalid) + ).toBeResolved(); expect(readFile.calls.all()[0].returnValue).toBeDefined(); const originalContent = await readFile.calls.all()[0].returnValue; - expect(originalContent).toContain('{{appName}}'); + expect(originalContent).toContain("{{appName}}"); expect(pageResponse.calls.all()[0].returnValue).toBeDefined(); const replacedContent = await pageResponse.calls.all()[0].returnValue; - expect(replacedContent.text).not.toContain('{{appName}}'); + expect(replacedContent.text).not.toContain("{{appName}}"); expect(replacedContent.text).toContain(req.config.appName); }); - it('removes undefined placeholder in response content', async () => { - await expectAsync(router.goToPage(req, pages.passwordReset)).toBeResolved(); + it("removes undefined placeholder in response content", async () => { + await expectAsync( + router.goToPage(req, pages.passwordReset) + ).toBeResolved(); expect(readFile.calls.all()[0].returnValue).toBeDefined(); const originalContent = await readFile.calls.all()[0].returnValue; - expect(originalContent).toContain('{{error}}'); + expect(originalContent).toContain("{{error}}"); // There is no error placeholder value set by default, so the // {{error}} placeholder should just be removed from content expect(pageResponse.calls.all()[0].returnValue).toBeDefined(); const replacedContent = await pageResponse.calls.all()[0].returnValue; - expect(replacedContent.text).not.toContain('{{error}}'); + expect(replacedContent.text).not.toContain("{{error}}"); }); - it('fills placeholders from config object', async () => { + it("fills placeholders from config object", async () => { config.pages.enableLocalization = false; config.pages.placeholders = { - title: 'setViaConfig', + title: "setViaConfig", }; await reconfigureServer(config); const response = await request({ - url: 'http://localhost:8378/1/apps/custom_json.html', + url: "http://localhost:8378/1/apps/custom_json.html", followRedirects: false, - method: 'GET', + method: "GET", }); expect(response.status).toEqual(200); expect(response.text).toContain(config.pages.placeholders.title); }); - it('fills placeholders from config function', async () => { + it("fills placeholders from config function", async () => { config.pages.enableLocalization = false; config.pages.placeholders = () => { - return { title: 'setViaConfig' }; + return { title: "setViaConfig" }; }; await reconfigureServer(config); const response = await request({ - url: 'http://localhost:8378/1/apps/custom_json.html', + url: "http://localhost:8378/1/apps/custom_json.html", followRedirects: false, - method: 'GET', + method: "GET", }); expect(response.status).toEqual(200); expect(response.text).toContain(config.pages.placeholders().title); }); - it('fills placeholders from config promise', async () => { + it("fills placeholders from config promise", async () => { config.pages.enableLocalization = false; config.pages.placeholders = async () => { - return { title: 'setViaConfig' }; + return { title: "setViaConfig" }; }; await reconfigureServer(config); const response = await request({ - url: 'http://localhost:8378/1/apps/custom_json.html', + url: "http://localhost:8378/1/apps/custom_json.html", followRedirects: false, - method: 'GET', + method: "GET", }); expect(response.status).toEqual(200); - expect(response.text).toContain((await config.pages.placeholders()).title); + expect(response.text).toContain( + (await config.pages.placeholders()).title + ); }); }); - describe('localization', () => { - it('returns default file if localization is disabled', async () => { + describe("localization", () => { + it("returns default file if localization is disabled", async () => { delete req.config.pages.enableLocalization; - await expectAsync(router.goToPage(req, pages.passwordResetLinkInvalid)).toBeResolved(); + await expectAsync( + router.goToPage(req, pages.passwordResetLinkInvalid) + ).toBeResolved(); expect(pageResponse.calls.all()[0].args[0]).toBeDefined(); expect(pageResponse.calls.all()[0].args[0]).not.toMatch( - new RegExp(`\/de(-AT)?\/${pages.passwordResetLinkInvalid.defaultFile}`) + new RegExp( + `\/de(-AT)?\/${pages.passwordResetLinkInvalid.defaultFile}` + ) ); }); - it('returns default file if no locale is specified', async () => { + it("returns default file if no locale is specified", async () => { delete req.query.locale; - await expectAsync(router.goToPage(req, pages.passwordResetLinkInvalid)).toBeResolved(); + await expectAsync( + router.goToPage(req, pages.passwordResetLinkInvalid) + ).toBeResolved(); expect(pageResponse.calls.all()[0].args[0]).toBeDefined(); expect(pageResponse.calls.all()[0].args[0]).not.toMatch( - new RegExp(`\/de(-AT)?\/${pages.passwordResetLinkInvalid.defaultFile}`) + new RegExp( + `\/de(-AT)?\/${pages.passwordResetLinkInvalid.defaultFile}` + ) ); }); - it('returns custom page regardless of localization enabled', async () => { + it("returns custom page regardless of localization enabled", async () => { req.config.pages.customUrls = { - passwordResetLinkInvalid: 'http://invalid-link.example.com', + passwordResetLinkInvalid: "http://invalid-link.example.com", }; - await expectAsync(router.goToPage(req, pages.passwordResetLinkInvalid)).toBeResolved(); + await expectAsync( + router.goToPage(req, pages.passwordResetLinkInvalid) + ).toBeResolved(); expect(pageResponse).not.toHaveBeenCalled(); expect(redirectResponse.calls.all()[0].args[0]).toBe( req.config.pages.customUrls.passwordResetLinkInvalid ); }); - it('returns file for locale match', async () => { - await expectAsync(router.goToPage(req, pages.passwordResetLinkInvalid)).toBeResolved(); + it("returns file for locale match", async () => { + await expectAsync( + router.goToPage(req, pages.passwordResetLinkInvalid) + ).toBeResolved(); expect(pageResponse.calls.all()[0].args[0]).toBeDefined(); expect(pageResponse.calls.all()[0].args[0]).toMatch( - new RegExp(`\/${req.query.locale}\/${pages.passwordResetLinkInvalid.defaultFile}`) + new RegExp( + `\/${req.query.locale}\/${pages.passwordResetLinkInvalid.defaultFile}` + ) ); }); - it('returns file for language match', async () => { + it("returns file for language match", async () => { // Pretend no locale matching file exists - spyOn(Utils, 'fileExists').and.callFake(async path => { + spyOn(Utils, "fileExists").and.callFake(async path => { return !path.includes( `/${req.query.locale}/${pages.passwordResetLinkInvalid.defaultFile}` ); }); - await expectAsync(router.goToPage(req, pages.passwordResetLinkInvalid)).toBeResolved(); + await expectAsync( + router.goToPage(req, pages.passwordResetLinkInvalid) + ).toBeResolved(); expect(pageResponse.calls.all()[0].args[0]).toBeDefined(); expect(pageResponse.calls.all()[0].args[0]).toMatch( new RegExp(`\/de\/${pages.passwordResetLinkInvalid.defaultFile}`) ); }); - it('returns default file for neither locale nor language match', async () => { - req.query.locale = 'yo-LO'; + it("returns default file for neither locale nor language match", async () => { + req.query.locale = "yo-LO"; - await expectAsync(router.goToPage(req, pages.passwordResetLinkInvalid)).toBeResolved(); + await expectAsync( + router.goToPage(req, pages.passwordResetLinkInvalid) + ).toBeResolved(); expect(pageResponse.calls.all()[0].args[0]).toBeDefined(); expect(pageResponse.calls.all()[0].args[0]).not.toMatch( - new RegExp(`\/yo(-LO)?\/${pages.passwordResetLinkInvalid.defaultFile}`) + new RegExp( + `\/yo(-LO)?\/${pages.passwordResetLinkInvalid.defaultFile}` + ) ); }); }); - describe('localization with JSON resource', () => { + describe("localization with JSON resource", () => { let jsonPageFile; let jsonPageUrl; let jsonResource; beforeEach(async () => { - jsonPageFile = 'custom_json.html'; + jsonPageFile = "custom_json.html"; jsonPageUrl = new URL(`${config.publicServerURL}/apps/${jsonPageFile}`); - jsonResource = require('../public/custom_json.json'); + jsonResource = require("../public/custom_json.json"); config.pages.enableLocalization = true; - config.pages.localizationJsonPath = './public/custom_json.json'; - config.pages.localizationFallbackLocale = 'en'; + config.pages.localizationJsonPath = "./public/custom_json.json"; + config.pages.localizationFallbackLocale = "en"; await reconfigureServer(config); }); - it('does not localize with JSON resource if localization is disabled', async () => { + it("does not localize with JSON resource if localization is disabled", async () => { config.pages.enableLocalization = false; - config.pages.localizationJsonPath = './public/custom_json.json'; - config.pages.localizationFallbackLocale = 'en'; + config.pages.localizationJsonPath = "./public/custom_json.json"; + config.pages.localizationFallbackLocale = "en"; await reconfigureServer(config); const response = await request({ @@ -472,12 +525,12 @@ describe('Pages Router', () => { // Ensure page response does not contain any translation const flattenedJson = Utils.flattenObject(jsonResource); for (const value of Object.values(flattenedJson)) { - const valueWithoutPlaceholder = fillPlaceholders(value, ''); + const valueWithoutPlaceholder = fillPlaceholders(value, ""); expect(response.text).not.toContain(valueWithoutPlaceholder); } }); - it('localizes static page with JSON resource and fallback locale', async () => { + it("localizes static page with JSON resource and fallback locale", async () => { const response = await request({ url: jsonPageUrl.toString(), followRedirects: false, @@ -485,16 +538,17 @@ describe('Pages Router', () => { expect(response.status).toBe(200); // Ensure page response contains translation of fallback locale - const translation = jsonResource[config.pages.localizationFallbackLocale].translation; + const translation = + jsonResource[config.pages.localizationFallbackLocale].translation; for (const value of Object.values(translation)) { - const valueWithoutPlaceholder = fillPlaceholders(value, ''); + const valueWithoutPlaceholder = fillPlaceholders(value, ""); expect(response.text).toContain(valueWithoutPlaceholder); } }); - it('localizes static page with JSON resource and request locale', async () => { + it("localizes static page with JSON resource and request locale", async () => { // Add locale to request URL - jsonPageUrl.searchParams.set('locale', exampleLocale); + jsonPageUrl.searchParams.set("locale", exampleLocale); const response = await request({ url: jsonPageUrl.toString(), @@ -505,15 +559,15 @@ describe('Pages Router', () => { // Ensure page response contains translations of request locale const translation = jsonResource[exampleLocale].translation; for (const value of Object.values(translation)) { - const valueWithoutPlaceholder = fillPlaceholders(value, ''); + const valueWithoutPlaceholder = fillPlaceholders(value, ""); expect(response.text).toContain(valueWithoutPlaceholder); } }); - it('localizes static page with JSON resource and language matching request locale', async () => { + it("localizes static page with JSON resource and language matching request locale", async () => { // Add locale to request URL that has no locale match but only a language // match in the JSON resource - jsonPageUrl.searchParams.set('locale', 'de-CH'); + jsonPageUrl.searchParams.set("locale", "de-CH"); const response = await request({ url: jsonPageUrl.toString(), @@ -522,19 +576,19 @@ describe('Pages Router', () => { expect(response.status).toBe(200); // Ensure page response contains translations of requst language - const translation = jsonResource['de'].translation; + const translation = jsonResource["de"].translation; for (const value of Object.values(translation)) { - const valueWithoutPlaceholder = fillPlaceholders(value, ''); + const valueWithoutPlaceholder = fillPlaceholders(value, ""); expect(response.text).toContain(valueWithoutPlaceholder); } }); - it('localizes static page with JSON resource and fills placeholders in JSON values', async () => { + it("localizes static page with JSON resource and fills placeholders in JSON values", async () => { // Add app ID to request URL so that the request is assigned to a Parse Server app // and placeholders within translations strings can be replaced with default page // parameters such as `appId` - jsonPageUrl.searchParams.set('appId', config.appId); - jsonPageUrl.searchParams.set('locale', exampleLocale); + jsonPageUrl.searchParams.set("appId", config.appId); + jsonPageUrl.searchParams.set("locale", exampleLocale); const response = await request({ url: jsonPageUrl.toString(), @@ -554,9 +608,11 @@ describe('Pages Router', () => { } }); - it('localizes feature page with JSON resource and fills placeholders in JSON values', async () => { + it("localizes feature page with JSON resource and fills placeholders in JSON values", async () => { // Fake any page to load the JSON page file - spyOnProperty(Page.prototype, 'defaultFile').and.returnValue(jsonPageFile); + spyOnProperty(Page.prototype, "defaultFile").and.returnValue( + jsonPageFile + ); const response = await request({ url: `http://localhost:8378/1/apps/test/request_password_reset?token=exampleToken&locale=${exampleLocale}`, @@ -577,69 +633,75 @@ describe('Pages Router', () => { }); }); - describe('response type', () => { - it('returns a file for GET request', async () => { - await expectAsync(router.goToPage(req, pages.passwordResetLinkInvalid)).toBeResolved(); + describe("response type", () => { + it("returns a file for GET request", async () => { + await expectAsync( + router.goToPage(req, pages.passwordResetLinkInvalid) + ).toBeResolved(); expect(pageResponse).toHaveBeenCalled(); expect(redirectResponse).not.toHaveBeenCalled(); }); - it('returns a redirect for POST request', async () => { - req.method = 'POST'; - await expectAsync(router.goToPage(req, pages.passwordResetLinkInvalid)).toBeResolved(); + it("returns a redirect for POST request", async () => { + req.method = "POST"; + await expectAsync( + router.goToPage(req, pages.passwordResetLinkInvalid) + ).toBeResolved(); expect(pageResponse).not.toHaveBeenCalled(); expect(redirectResponse).toHaveBeenCalled(); }); - it('returns a redirect for custom pages for GET and POST request', async () => { + it("returns a redirect for custom pages for GET and POST request", async () => { req.config.pages.customUrls = { - passwordResetLinkInvalid: 'http://invalid-link.example.com', + passwordResetLinkInvalid: "http://invalid-link.example.com", }; - for (const method of ['GET', 'POST']) { + for (const method of ["GET", "POST"]) { req.method = method; - await expectAsync(router.goToPage(req, pages.passwordResetLinkInvalid)).toBeResolved(); + await expectAsync( + router.goToPage(req, pages.passwordResetLinkInvalid) + ).toBeResolved(); expect(pageResponse).not.toHaveBeenCalled(); expect(redirectResponse).toHaveBeenCalled(); } }); - it('responds to POST request with redirect response', async () => { + it("responds to POST request with redirect response", async () => { await reconfigureServer(config); const response = await request({ - url: 'http://localhost:8378/1/apps/test/request_password_reset?token=exampleToken&locale=de-AT', + url: "http://localhost:8378/1/apps/test/request_password_reset?token=exampleToken&locale=de-AT", followRedirects: false, - method: 'POST', + method: "POST", }); expect(response.status).toEqual(303); expect(response.headers.location).toContain( - 'http://localhost:8378/1/apps/de-AT/password_reset_link_invalid.html' + "http://localhost:8378/1/apps/de-AT/password_reset_link_invalid.html" ); }); - it('responds to GET request with content response', async () => { + it("responds to GET request with content response", async () => { await reconfigureServer(config); const response = await request({ - url: 'http://localhost:8378/1/apps/test/request_password_reset?token=exampleToken&locale=de-AT', + url: "http://localhost:8378/1/apps/test/request_password_reset?token=exampleToken&locale=de-AT", followRedirects: false, - method: 'GET', + method: "GET", }); expect(response.status).toEqual(200); - expect(response.text).toContain(''); + expect(response.text).toContain(""); }); }); - describe('end-to-end tests', () => { - it('localizes end-to-end for password reset: success', async () => { + describe("end-to-end tests", () => { + it("localizes end-to-end for password reset: success", async () => { await reconfigureServer(config); const sendPasswordResetEmail = spyOn( config.emailAdapter, - 'sendPasswordResetEmail' + "sendPasswordResetEmail" ).and.callThrough(); const user = new Parse.User(); - user.setUsername('exampleUsername'); - user.setPassword('examplePassword'); - user.set('email', 'mail@example.com'); + user.setUsername("exampleUsername"); + user.setPassword("examplePassword"); + user.set("email", "mail@example.com"); await user.signUp(); await Parse.User.requestPasswordReset(user.getEmail()); @@ -653,10 +715,11 @@ describe('Pages Router', () => { }); expect(linkResponse.status).toBe(200); - const appId = linkResponse.headers['x-parse-page-param-appid']; - const token = linkResponse.headers['x-parse-page-param-token']; - const locale = linkResponse.headers['x-parse-page-param-locale']; - const publicServerUrl = linkResponse.headers['x-parse-page-param-publicserverurl']; + const appId = linkResponse.headers["x-parse-page-param-appid"]; + const token = linkResponse.headers["x-parse-page-param-token"]; + const locale = linkResponse.headers["x-parse-page-param-locale"]; + const publicServerUrl = + linkResponse.headers["x-parse-page-param-publicserverurl"]; const passwordResetPagePath = pageResponse.calls.all()[0].args[0]; expect(appId).toBeDefined(); expect(token).toBeDefined(); @@ -670,13 +733,13 @@ describe('Pages Router', () => { const formUrl = `${publicServerUrl}/apps/${appId}/request_password_reset`; const formResponse = await request({ url: formUrl, - method: 'POST', + method: "POST", body: { token, locale, - new_password: 'newPassword', + new_password: "newPassword", }, - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + headers: { "Content-Type": "application/x-www-form-urlencoded" }, followRedirects: false, }); expect(formResponse.status).toEqual(200); @@ -685,23 +748,23 @@ describe('Pages Router', () => { ); }); - it('localizes end-to-end for password reset: invalid link', async () => { + it("localizes end-to-end for password reset: invalid link", async () => { await reconfigureServer(config); const sendPasswordResetEmail = spyOn( config.emailAdapter, - 'sendPasswordResetEmail' + "sendPasswordResetEmail" ).and.callThrough(); const user = new Parse.User(); - user.setUsername('exampleUsername'); - user.setPassword('examplePassword'); - user.set('email', 'mail@example.com'); + user.setUsername("exampleUsername"); + user.setPassword("examplePassword"); + user.set("email", "mail@example.com"); await user.signUp(); await Parse.User.requestPasswordReset(user.getEmail()); const link = sendPasswordResetEmail.calls.all()[0].args[0].link; const linkWithLocale = new URL(link); linkWithLocale.searchParams.append(pageParams.locale, exampleLocale); - linkWithLocale.searchParams.set(pageParams.token, 'invalidToken'); + linkWithLocale.searchParams.set(pageParams.token, "invalidToken"); const linkResponse = await request({ url: linkWithLocale.toString(), @@ -711,22 +774,24 @@ describe('Pages Router', () => { const pagePath = pageResponse.calls.all()[0].args[0]; expect(pagePath).toMatch( - new RegExp(`\/${exampleLocale}\/${pages.passwordResetLinkInvalid.defaultFile}`) + new RegExp( + `\/${exampleLocale}\/${pages.passwordResetLinkInvalid.defaultFile}` + ) ); }); - it_id('2845c2ea-23ba-45d2-a33f-63181d419bca')(it)( - 'localizes end-to-end for verify email: success', + it_id("2845c2ea-23ba-45d2-a33f-63181d419bca")(it)( + "localizes end-to-end for verify email: success", async () => { await reconfigureServer(config); const sendVerificationEmail = spyOn( config.emailAdapter, - 'sendVerificationEmail' + "sendVerificationEmail" ).and.callThrough(); const user = new Parse.User(); - user.setUsername('exampleUsername'); - user.setPassword('examplePassword'); - user.set('email', 'mail@example.com'); + user.setUsername("exampleUsername"); + user.setPassword("examplePassword"); + user.set("email", "mail@example.com"); await user.signUp(); await jasmine.timeout(); @@ -742,30 +807,32 @@ describe('Pages Router', () => { const pagePath = pageResponse.calls.all()[0].args[0]; expect(pagePath).toMatch( - new RegExp(`\/${exampleLocale}\/${pages.emailVerificationSuccess.defaultFile}`) + new RegExp( + `\/${exampleLocale}\/${pages.emailVerificationSuccess.defaultFile}` + ) ); } ); - it_id('f2272b94-b4ac-474f-8e47-1ca74de136f5')(it)( - 'localizes end-to-end for verify email: invalid verification link - link send success', + it_id("f2272b94-b4ac-474f-8e47-1ca74de136f5")(it)( + "localizes end-to-end for verify email: invalid verification link - link send success", async () => { await reconfigureServer(config); const sendVerificationEmail = spyOn( config.emailAdapter, - 'sendVerificationEmail' + "sendVerificationEmail" ).and.callThrough(); const user = new Parse.User(); - user.setUsername('exampleUsername'); - user.setPassword('examplePassword'); - user.set('email', 'mail@example.com'); + user.setUsername("exampleUsername"); + user.setPassword("examplePassword"); + user.set("email", "mail@example.com"); await user.signUp(); await jasmine.timeout(); const link = sendVerificationEmail.calls.all()[0].args[0].link; const linkWithLocale = new URL(link); linkWithLocale.searchParams.append(pageParams.locale, exampleLocale); - linkWithLocale.searchParams.set(pageParams.token, 'invalidToken'); + linkWithLocale.searchParams.set(pageParams.token, "invalidToken"); const linkResponse = await request({ url: linkWithLocale.toString(), @@ -773,26 +840,30 @@ describe('Pages Router', () => { }); expect(linkResponse.status).toBe(200); - const appId = linkResponse.headers['x-parse-page-param-appid']; - const locale = linkResponse.headers['x-parse-page-param-locale']; - const publicServerUrl = linkResponse.headers['x-parse-page-param-publicserverurl']; - const invalidVerificationPagePath = pageResponse.calls.all()[0].args[0]; + const appId = linkResponse.headers["x-parse-page-param-appid"]; + const locale = linkResponse.headers["x-parse-page-param-locale"]; + const publicServerUrl = + linkResponse.headers["x-parse-page-param-publicserverurl"]; + const invalidVerificationPagePath = + pageResponse.calls.all()[0].args[0]; expect(appId).toBeDefined(); expect(locale).toBe(exampleLocale); expect(publicServerUrl).toBeDefined(); expect(invalidVerificationPagePath).toMatch( - new RegExp(`\/${exampleLocale}\/${pages.emailVerificationLinkInvalid.defaultFile}`) + new RegExp( + `\/${exampleLocale}\/${pages.emailVerificationLinkInvalid.defaultFile}` + ) ); const formUrl = `${publicServerUrl}/apps/${appId}/resend_verification_email`; const formResponse = await request({ url: formUrl, - method: 'POST', + method: "POST", body: { locale, - username: 'exampleUsername', + username: "exampleUsername", }, - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + headers: { "Content-Type": "application/x-www-form-urlencoded" }, followRedirects: false, }); expect(formResponse.status).toEqual(303); @@ -802,25 +873,25 @@ describe('Pages Router', () => { } ); - it_id('1d46d36a-e455-4ae7-8717-e0d286e95f02')(it)( - 'localizes end-to-end for verify email: invalid verification link - link send fail', + it_id("1d46d36a-e455-4ae7-8717-e0d286e95f02")(it)( + "localizes end-to-end for verify email: invalid verification link - link send fail", async () => { await reconfigureServer(config); const sendVerificationEmail = spyOn( config.emailAdapter, - 'sendVerificationEmail' + "sendVerificationEmail" ).and.callThrough(); const user = new Parse.User(); - user.setUsername('exampleUsername'); - user.setPassword('examplePassword'); - user.set('email', 'mail@example.com'); + user.setUsername("exampleUsername"); + user.setPassword("examplePassword"); + user.set("email", "mail@example.com"); await user.signUp(); await jasmine.timeout(); const link = sendVerificationEmail.calls.all()[0].args[0].link; const linkWithLocale = new URL(link); linkWithLocale.searchParams.append(pageParams.locale, exampleLocale); - linkWithLocale.searchParams.set(pageParams.token, 'invalidToken'); + linkWithLocale.searchParams.set(pageParams.token, "invalidToken"); const linkResponse = await request({ url: linkWithLocale.toString(), @@ -828,32 +899,39 @@ describe('Pages Router', () => { }); expect(linkResponse.status).toBe(200); - const appId = linkResponse.headers['x-parse-page-param-appid']; - const locale = linkResponse.headers['x-parse-page-param-locale']; - const publicServerUrl = linkResponse.headers['x-parse-page-param-publicserverurl']; + const appId = linkResponse.headers["x-parse-page-param-appid"]; + const locale = linkResponse.headers["x-parse-page-param-locale"]; + const publicServerUrl = + linkResponse.headers["x-parse-page-param-publicserverurl"]; await jasmine.timeout(); - const invalidVerificationPagePath = pageResponse.calls.all()[0].args[0]; + const invalidVerificationPagePath = + pageResponse.calls.all()[0].args[0]; expect(appId).toBeDefined(); expect(locale).toBe(exampleLocale); expect(publicServerUrl).toBeDefined(); expect(invalidVerificationPagePath).toMatch( - new RegExp(`\/${exampleLocale}\/${pages.emailVerificationLinkInvalid.defaultFile}`) + new RegExp( + `\/${exampleLocale}\/${pages.emailVerificationLinkInvalid.defaultFile}` + ) ); - spyOn(UserController.prototype, 'resendVerificationEmail').and.callFake(() => - Promise.reject('failed to resend verification email') + spyOn( + UserController.prototype, + "resendVerificationEmail" + ).and.callFake(() => + Promise.reject("failed to resend verification email") ); const formUrl = `${publicServerUrl}/apps/${appId}/resend_verification_email`; const formResponse = await request({ url: formUrl, - method: 'POST', + method: "POST", body: { locale, - username: 'exampleUsername', + username: "exampleUsername", }, - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + headers: { "Content-Type": "application/x-www-form-urlencoded" }, followRedirects: false, }); expect(formResponse.status).toEqual(303); @@ -863,16 +941,16 @@ describe('Pages Router', () => { } ); - it('localizes end-to-end for resend verification email: invalid link', async () => { + it("localizes end-to-end for resend verification email: invalid link", async () => { await reconfigureServer(config); const formUrl = `${config.publicServerURL}/apps/${config.appId}/resend_verification_email`; const formResponse = await request({ url: formUrl, - method: 'POST', + method: "POST", body: { locale: exampleLocale, }, - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + headers: { "Content-Type": "application/x-www-form-urlencoded" }, followRedirects: false, }); expect(formResponse.status).toEqual(303); @@ -882,51 +960,57 @@ describe('Pages Router', () => { }); }); - describe('failing with missing parameters', () => { - it('verifyEmail: throws on missing server configuration', async () => { + describe("failing with missing parameters", () => { + it("verifyEmail: throws on missing server configuration", async () => { delete req.config; - const verifyEmail = req => (() => new PagesRouter().verifyEmail(req)).bind(null); + const verifyEmail = req => + (() => new PagesRouter().verifyEmail(req)).bind(null); expect(verifyEmail(req)).toThrow(); }); - it('resendVerificationEmail: throws on missing server configuration', async () => { + it("resendVerificationEmail: throws on missing server configuration", async () => { delete req.config; const resendVerificationEmail = req => (() => new PagesRouter().resendVerificationEmail(req)).bind(null); expect(resendVerificationEmail(req)).toThrow(); }); - it('requestResetPassword: throws on missing server configuration', async () => { + it("requestResetPassword: throws on missing server configuration", async () => { delete req.config; const requestResetPassword = req => (() => new PagesRouter().requestResetPassword(req)).bind(null); expect(requestResetPassword(req)).toThrow(); }); - it('resetPassword: throws on missing server configuration', async () => { + it("resetPassword: throws on missing server configuration", async () => { delete req.config; - const resetPassword = req => (() => new PagesRouter().resetPassword(req)).bind(null); + const resetPassword = req => + (() => new PagesRouter().resetPassword(req)).bind(null); expect(resetPassword(req)).toThrow(); }); - it('verifyEmail: responds with invalid link on missing username', async () => { - req.query.token = 'exampleToken'; + it("verifyEmail: responds with invalid link on missing username", async () => { + req.query.token = "exampleToken"; req.params = {}; req.config.userController = { verifyEmail: () => Promise.reject() }; const verifyEmail = req => new PagesRouter().verifyEmail(req); await verifyEmail(req); - expect(goToPage.calls.all()[0].args[1]).toBe(pages.emailVerificationLinkInvalid); + expect(goToPage.calls.all()[0].args[1]).toBe( + pages.emailVerificationLinkInvalid + ); }); - it('resetPassword: responds with page choose password with error message on failed password update', async () => { + it("resetPassword: responds with page choose password with error message on failed password update", async () => { req.body = { - token: 'exampleToken', - username: 'exampleUsername', - new_password: 'examplePassword', + token: "exampleToken", + username: "exampleUsername", + new_password: "examplePassword", + }; + const error = "exampleError"; + req.config.userController = { + updatePassword: () => Promise.reject(error), }; - const error = 'exampleError'; - req.config.userController = { updatePassword: () => Promise.reject(error) }; const resetPassword = req => new PagesRouter().resetPassword(req); await resetPassword(req); @@ -934,24 +1018,27 @@ describe('Pages Router', () => { expect(goToPage.calls.all()[0].args[2].error).toBe(error); }); - it('resetPassword: responds with AJAX error with error message on failed password update', async () => { + it("resetPassword: responds with AJAX error with error message on failed password update", async () => { req.xhr = true; req.body = { - token: 'exampleToken', - username: 'exampleUsername', - new_password: 'examplePassword', + token: "exampleToken", + username: "exampleUsername", + new_password: "examplePassword", }; - const error = 'exampleError'; - req.config.userController = { updatePassword: () => Promise.reject(error) }; - const resetPassword = req => new PagesRouter().resetPassword(req).catch(e => e); + const error = "exampleError"; + req.config.userController = { + updatePassword: () => Promise.reject(error), + }; + const resetPassword = req => + new PagesRouter().resetPassword(req).catch(e => e); const response = await resetPassword(req); expect(response.code).toBe(Parse.Error.OTHER_CAUSE); }); }); - describe('exploits', () => { - it('rejects requesting file outside of pages scope with UNIX path patterns', async () => { + describe("exploits", () => { + it("rejects requesting file outside of pages scope with UNIX path patterns", async () => { await reconfigureServer(config); // Do not compose this URL with `new URL(...)` because that would normalize @@ -962,25 +1049,28 @@ describe('Pages Router', () => { followRedirects: false, }).catch(e => e); expect(response.status).toBe(404); - expect(response.text).toBe('Not found.'); + expect(response.text).toBe("Not found."); }); }); - describe('custom route', () => { - it('handles custom route with GET', async () => { + describe("custom route", () => { + it("handles custom route with GET", async () => { config.pages.customRoutes = [ { - method: 'GET', - path: 'custom_page', + method: "GET", + path: "custom_page", handler: async req => { expect(req).toBeDefined(); - expect(req.method).toBe('GET'); - return { file: 'custom_page.html' }; + expect(req.method).toBe("GET"); + return { file: "custom_page.html" }; }, }, ]; await reconfigureServer(config); - const handlerSpy = spyOn(config.pages.customRoutes[0], 'handler').and.callThrough(); + const handlerSpy = spyOn( + config.pages.customRoutes[0], + "handler" + ).and.callThrough(); const url = `${config.publicServerURL}/apps/${config.appId}/custom_page`; const response = await request({ @@ -992,62 +1082,71 @@ describe('Pages Router', () => { expect(handlerSpy).toHaveBeenCalled(); }); - it('handles custom route with POST', async () => { + it("handles custom route with POST", async () => { config.pages.customRoutes = [ { - method: 'POST', - path: 'custom_page', + method: "POST", + path: "custom_page", handler: async req => { expect(req).toBeDefined(); - expect(req.method).toBe('POST'); - return { file: 'custom_page.html' }; + expect(req.method).toBe("POST"); + return { file: "custom_page.html" }; }, }, ]; - const handlerSpy = spyOn(config.pages.customRoutes[0], 'handler').and.callThrough(); + const handlerSpy = spyOn( + config.pages.customRoutes[0], + "handler" + ).and.callThrough(); await reconfigureServer(config); const url = `${config.publicServerURL}/apps/${config.appId}/custom_page`; const response = await request({ url: url, followRedirects: false, - method: 'POST', + method: "POST", }).catch(e => e); expect(response.status).toBe(200); expect(response.text).toMatch(config.appName); expect(handlerSpy).toHaveBeenCalled(); }); - it('handles multiple custom routes', async () => { + it("handles multiple custom routes", async () => { config.pages.customRoutes = [ { - method: 'GET', - path: 'custom_page', + method: "GET", + path: "custom_page", handler: async req => { expect(req).toBeDefined(); - expect(req.method).toBe('GET'); - return { file: 'custom_page.html' }; + expect(req.method).toBe("GET"); + return { file: "custom_page.html" }; }, }, { - method: 'POST', - path: 'custom_page', + method: "POST", + path: "custom_page", handler: async req => { expect(req).toBeDefined(); - expect(req.method).toBe('POST'); - return { file: 'custom_page.html' }; + expect(req.method).toBe("POST"); + return { file: "custom_page.html" }; }, }, ]; - const getHandlerSpy = spyOn(config.pages.customRoutes[0], 'handler').and.callThrough(); - const postHandlerSpy = spyOn(config.pages.customRoutes[1], 'handler').and.callThrough(); + const getHandlerSpy = spyOn( + config.pages.customRoutes[0], + "handler" + ).and.callThrough(); + const postHandlerSpy = spyOn( + config.pages.customRoutes[1], + "handler" + ).and.callThrough(); await reconfigureServer(config); const url = `${config.publicServerURL}/apps/${config.appId}/custom_page`; const getResponse = await request({ url: url, followRedirects: false, - method: 'GET', + method: "GET", }).catch(e => e); expect(getResponse.status).toBe(200); expect(getResponse.text).toMatch(config.appName); @@ -1056,30 +1155,33 @@ describe('Pages Router', () => { const postResponse = await request({ url: url, followRedirects: false, - method: 'POST', + method: "POST", }).catch(e => e); expect(postResponse.status).toBe(200); expect(postResponse.text).toMatch(config.appName); expect(postHandlerSpy).toHaveBeenCalled(); }); - it('handles custom route with async handler', async () => { + it("handles custom route with async handler", async () => { config.pages.customRoutes = [ { - method: 'GET', - path: 'custom_page', + method: "GET", + path: "custom_page", handler: async req => { expect(req).toBeDefined(); - expect(req.method).toBe('GET'); + expect(req.method).toBe("GET"); const file = await new Promise(resolve => - setTimeout(resolve('custom_page.html'), 1000) + setTimeout(resolve("custom_page.html"), 1000) ); return { file }; }, }, ]; await reconfigureServer(config); - const handlerSpy = spyOn(config.pages.customRoutes[0], 'handler').and.callThrough(); + const handlerSpy = spyOn( + config.pages.customRoutes[0], + "handler" + ).and.callThrough(); const url = `${config.publicServerURL}/apps/${config.appId}/custom_page`; const response = await request({ @@ -1091,16 +1193,19 @@ describe('Pages Router', () => { expect(handlerSpy).toHaveBeenCalled(); }); - it('returns 404 if custom route does not return page', async () => { + it("returns 404 if custom route does not return page", async () => { config.pages.customRoutes = [ { - method: 'GET', - path: 'custom_page', + method: "GET", + path: "custom_page", handler: async () => {}, }, ]; await reconfigureServer(config); - const handlerSpy = spyOn(config.pages.customRoutes[0], 'handler').and.callThrough(); + const handlerSpy = spyOn( + config.pages.customRoutes[0], + "handler" + ).and.callThrough(); const url = `${config.publicServerURL}/apps/${config.appId}/custom_page`; const response = await request({ @@ -1108,23 +1213,23 @@ describe('Pages Router', () => { followRedirects: false, }).catch(e => e); expect(response.status).toBe(404); - expect(response.text).toMatch('Not found'); + expect(response.text).toMatch("Not found"); expect(handlerSpy).toHaveBeenCalled(); }); }); - describe('custom endpoint', () => { - it('password reset works with custom endpoint', async () => { - config.pages.pagesEndpoint = 'customEndpoint'; + describe("custom endpoint", () => { + it("password reset works with custom endpoint", async () => { + config.pages.pagesEndpoint = "customEndpoint"; await reconfigureServer(config); const sendPasswordResetEmail = spyOn( config.emailAdapter, - 'sendPasswordResetEmail' + "sendPasswordResetEmail" ).and.callThrough(); const user = new Parse.User(); - user.setUsername('exampleUsername'); - user.setPassword('examplePassword'); - user.set('email', 'mail@example.com'); + user.setUsername("exampleUsername"); + user.setPassword("examplePassword"); + user.set("email", "mail@example.com"); await user.signUp(); await Parse.User.requestPasswordReset(user.getEmail()); @@ -1135,25 +1240,28 @@ describe('Pages Router', () => { }); expect(linkResponse.status).toBe(200); - const appId = linkResponse.headers['x-parse-page-param-appid']; - const token = linkResponse.headers['x-parse-page-param-token']; - const publicServerUrl = linkResponse.headers['x-parse-page-param-publicserverurl']; + const appId = linkResponse.headers["x-parse-page-param-appid"]; + const token = linkResponse.headers["x-parse-page-param-token"]; + const publicServerUrl = + linkResponse.headers["x-parse-page-param-publicserverurl"]; const passwordResetPagePath = pageResponse.calls.all()[0].args[0]; expect(appId).toBeDefined(); expect(token).toBeDefined(); expect(publicServerUrl).toBeDefined(); - expect(passwordResetPagePath).toMatch(new RegExp(`\/${pages.passwordReset.defaultFile}`)); + expect(passwordResetPagePath).toMatch( + new RegExp(`\/${pages.passwordReset.defaultFile}`) + ); pageResponse.calls.reset(); const formUrl = `${publicServerUrl}/${config.pages.pagesEndpoint}/${appId}/request_password_reset`; const formResponse = await request({ url: formUrl, - method: 'POST', + method: "POST", body: { token, - new_password: 'newPassword', + new_password: "newPassword", }, - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + headers: { "Content-Type": "application/x-www-form-urlencoded" }, followRedirects: false, }); expect(formResponse.status).toEqual(200); @@ -1162,19 +1270,19 @@ describe('Pages Router', () => { ); }); - it_id('81c1c28e-5dfd-4ffb-a09b-283156c08483')(it)( - 'email verification works with custom endpoint', + it_id("81c1c28e-5dfd-4ffb-a09b-283156c08483")(it)( + "email verification works with custom endpoint", async () => { - config.pages.pagesEndpoint = 'customEndpoint'; + config.pages.pagesEndpoint = "customEndpoint"; await reconfigureServer(config); const sendVerificationEmail = spyOn( config.emailAdapter, - 'sendVerificationEmail' + "sendVerificationEmail" ).and.callThrough(); const user = new Parse.User(); - user.setUsername('exampleUsername'); - user.setPassword('examplePassword'); - user.set('email', 'mail@example.com'); + user.setUsername("exampleUsername"); + user.setPassword("examplePassword"); + user.set("email", "mail@example.com"); await user.signUp(); await jasmine.timeout(); @@ -1185,7 +1293,9 @@ describe('Pages Router', () => { }); expect(linkResponse.status).toBe(200); const pagePath = pageResponse.calls.all()[0].args[0]; - expect(pagePath).toMatch(new RegExp(`\/${pages.emailVerificationSuccess.defaultFile}`)); + expect(pagePath).toMatch( + new RegExp(`\/${pages.emailVerificationSuccess.defaultFile}`) + ); } ); }); diff --git a/spec/Parse.Push.spec.js b/spec/Parse.Push.spec.js index 61dbdba103..8a93332da6 100644 --- a/spec/Parse.Push.spec.js +++ b/spec/Parse.Push.spec.js @@ -1,12 +1,12 @@ -'use strict'; +"use strict"; -const request = require('../lib/request'); +const request = require("../lib/request"); const pushCompleted = async pushId => { - const query = new Parse.Query('_PushStatus'); - query.equalTo('objectId', pushId); + const query = new Parse.Query("_PushStatus"); + query.equalTo("objectId", pushId); let result = await query.first({ useMasterKey: true }); - while (!(result && result.get('status') === 'succeeded')) { + while (!(result && result.get("status") === "succeeded")) { await jasmine.timeout(); result = await query.first({ useMasterKey: true }); } @@ -31,10 +31,10 @@ const provideInstallations = function (num) { const installations = []; while (installations.length !== num) { // add Android installations - const installation = new Parse.Object('_Installation'); - installation.set('installationId', 'installation_' + installations.length); - installation.set('deviceToken', 'device_token_' + installations.length); - installation.set('deviceType', 'android'); + const installation = new Parse.Object("_Installation"); + installation.set("installationId", "installation_" + installations.length); + installation.set("deviceToken", "device_token_" + installations.length); + installation.set("deviceType", "android"); installations.push(installation); } @@ -50,7 +50,7 @@ const losingAdapter = { return successfulAny(body, installations); }, getValidPushTypes: function () { - return ['android']; + return ["android"]; }, }; @@ -63,7 +63,7 @@ const setup = function () { const promises = installations.map(installation => { sendToInstallationSpy(installation); - if (installation.deviceType == 'ios') { + if (installation.deviceType == "ios") { expect(installation.badge).toEqual(badge); expect(installation.originalBadge + 1).toEqual(installation.badge); } else { @@ -78,7 +78,7 @@ const setup = function () { return Promise.all(promises); }, getValidPushTypes: function () { - return ['ios', 'android']; + return ["ios", "android"]; }, }; @@ -93,12 +93,15 @@ const setup = function () { .then(() => { const installations = []; while (installations.length != 10) { - const installation = new Parse.Object('_Installation'); - installation.set('installationId', 'installation_' + installations.length); - installation.set('deviceToken', 'device_token_' + installations.length); - installation.set('badge', installations.length); - installation.set('originalBadge', installations.length); - installation.set('deviceType', 'ios'); + const installation = new Parse.Object("_Installation"); + installation.set( + "installationId", + "installation_" + installations.length + ); + installation.set("deviceToken", "device_token_" + installations.length); + installation.set("badge", installations.length); + installation.set("originalBadge", installations.length); + installation.set("deviceType", "ios"); installations.push(installation); } return Parse.Object.saveAll(installations); @@ -110,107 +113,112 @@ const setup = function () { }); }; -describe('Parse.Push', () => { - it_id('d1e591c4-2b21-466b-9ee2-5be467b6b771')(it)('should properly send push', async () => { - const { sendToInstallationSpy } = await setup(); - const pushStatusId = await Parse.Push.send({ - where: { - deviceType: 'ios', - }, - data: { - badge: 'Increment', - alert: 'Hello world!', - }, - }); - await pushCompleted(pushStatusId); - expect(sendToInstallationSpy.calls.count()).toEqual(10); - }); +describe("Parse.Push", () => { + it_id("d1e591c4-2b21-466b-9ee2-5be467b6b771")(it)( + "should properly send push", + async () => { + const { sendToInstallationSpy } = await setup(); + const pushStatusId = await Parse.Push.send({ + where: { + deviceType: "ios", + }, + data: { + badge: "Increment", + alert: "Hello world!", + }, + }); + await pushCompleted(pushStatusId); + expect(sendToInstallationSpy.calls.count()).toEqual(10); + } + ); - it_id('2a58e3c7-b6f3-4261-a384-6c893b2ac3f3')(it)( - 'should properly send push with lowercaseIncrement', + it_id("2a58e3c7-b6f3-4261-a384-6c893b2ac3f3")(it)( + "should properly send push with lowercaseIncrement", async () => { await setup(); const pushStatusId = await Parse.Push.send({ where: { - deviceType: 'ios', + deviceType: "ios", }, data: { - badge: 'increment', - alert: 'Hello world!', + badge: "increment", + alert: "Hello world!", }, }); await pushCompleted(pushStatusId); } ); - it_id('e21780b6-2cdd-467e-8013-81030f3288e9')(it)( - 'should not allow clients to query _PushStatus', + it_id("e21780b6-2cdd-467e-8013-81030f3288e9")(it)( + "should not allow clients to query _PushStatus", async () => { await setup(); const pushStatusId = await Parse.Push.send({ where: { - deviceType: 'ios', + deviceType: "ios", }, data: { - badge: 'increment', - alert: 'Hello world!', + badge: "increment", + alert: "Hello world!", }, }); await pushCompleted(pushStatusId); try { await request({ - url: 'http://localhost:8378/1/classes/_PushStatus', + url: "http://localhost:8378/1/classes/_PushStatus", json: true, headers: { - 'X-Parse-Application-Id': 'test', + "X-Parse-Application-Id": "test", }, }); fail(); } catch (response) { - expect(response.data.error).toEqual('unauthorized'); + expect(response.data.error).toEqual("unauthorized"); } } ); - it_id('924cf5f5-f684-4925-978a-e52c0c457366')(it)( - 'should allow master key to query _PushStatus', + it_id("924cf5f5-f684-4925-978a-e52c0c457366")(it)( + "should allow master key to query _PushStatus", async () => { await setup(); const pushStatusId = await Parse.Push.send({ where: { - deviceType: 'ios', + deviceType: "ios", }, data: { - badge: 'increment', - alert: 'Hello world!', + badge: "increment", + alert: "Hello world!", }, }); await pushCompleted(pushStatusId); const response = await request({ - url: 'http://localhost:8378/1/classes/_PushStatus', + url: "http://localhost:8378/1/classes/_PushStatus", json: true, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-Master-Key": "test", }, }); const body = response.data; expect(body.results.length).toEqual(1); expect(body.results[0].query).toEqual('{"deviceType":"ios"}'); - expect(body.results[0].payload).toEqual('{"badge":"increment","alert":"Hello world!"}'); + expect(body.results[0].payload).toEqual( + '{"badge":"increment","alert":"Hello world!"}' + ); } ); - it('should throw error if missing push configuration', async () => { + it("should throw error if missing push configuration", async () => { await reconfigureServer({ push: null }); try { await Parse.Push.send({ where: { - deviceType: 'ios', + deviceType: "ios", }, data: { - badge: 'increment', - alert: 'Hello world!', + badge: "increment", + alert: "Hello world!", }, }); fail(); @@ -230,14 +238,14 @@ describe('Parse.Push', () => { }); await Parse.Object.saveAll(provideInstallations()); const pushStatusId = await Parse.Push.send({ - data: { alert: 'We fixed our status!' }, - where: { deviceType: 'android' }, + data: { alert: "We fixed our status!" }, + where: { deviceType: "android" }, }); await pushCompleted(pushStatusId); const result = await Parse.Push.getPushStatus(pushStatusId); - expect(result.get('status')).toEqual('succeeded'); - expect(result.get('numSent')).toEqual(1); - expect(result.get('count')).toEqual(undefined); + expect(result.get("status")).toEqual("succeeded"); + expect(result.get("numSent")).toEqual(1); + expect(result.get("count")).toEqual(undefined); }); /** @@ -249,10 +257,13 @@ describe('Parse.Push', () => { const installations = provideInstallations(); // add 1 iOS installation which we will omit & add later on - const iOSInstallation = new Parse.Object('_Installation'); - iOSInstallation.set('installationId', 'installation_' + installations.length); - iOSInstallation.set('deviceToken', 'device_token_' + installations.length); - iOSInstallation.set('deviceType', 'ios'); + const iOSInstallation = new Parse.Object("_Installation"); + iOSInstallation.set( + "installationId", + "installation_" + installations.length + ); + iOSInstallation.set("deviceToken", "device_token_" + installations.length); + iOSInstallation.set("deviceType", "ios"); installations.push(iOSInstallation); await reconfigureServer({ @@ -265,21 +276,21 @@ describe('Parse.Push', () => { return successfulAny(body, installations); }, getValidPushTypes: function () { - return ['android']; + return ["android"]; }, }, }, }); await Parse.Object.saveAll(installations); const pushStatusId = await Parse.Push.send({ - data: { alert: 'We fixed our status!' }, - where: { deviceType: { $ne: 'random' } }, + data: { alert: "We fixed our status!" }, + where: { deviceType: { $ne: "random" } }, }); await pushCompleted(pushStatusId); const result = await Parse.Push.getPushStatus(pushStatusId); - expect(result.get('status')).toEqual('succeeded'); - expect(result.get('numSent')).toEqual(3); - expect(result.get('count')).toEqual(undefined); + expect(result.get("status")).toEqual("succeeded"); + expect(result.get("numSent")).toEqual(3); + expect(result.get("count")).toEqual(undefined); }); /** @@ -296,15 +307,15 @@ describe('Parse.Push', () => { }); await Parse.Object.saveAll(installations); const pushStatusId = await Parse.Push.send({ - data: { alert: 'We fixed our status!' }, - where: { deviceType: 'android' }, + data: { alert: "We fixed our status!" }, + where: { deviceType: "android" }, }); await pushCompleted(pushStatusId); const result = await Parse.Push.getPushStatus(pushStatusId); - expect(result.get('status')).toEqual('succeeded'); + expect(result.get("status")).toEqual("succeeded"); // expect # less than # of batches used, assuming each batch is 100 pushes - expect(result.get('numSent')).toEqual(devices - devices / 100); - expect(result.get('count')).toEqual(undefined); + expect(result.get("numSent")).toEqual(devices - devices / 100); + expect(result.get("count")).toEqual(undefined); }); /** @@ -319,10 +330,16 @@ describe('Parse.Push', () => { // add 1 iOS installation which we will omit & add later on const iOSInstallations = []; while (iOSInstallations.length !== devices / 100) { - const iOSInstallation = new Parse.Object('_Installation'); - iOSInstallation.set('installationId', 'installation_' + installations.length); - iOSInstallation.set('deviceToken', 'device_token_' + installations.length); - iOSInstallation.set('deviceType', 'ios'); + const iOSInstallation = new Parse.Object("_Installation"); + iOSInstallation.set( + "installationId", + "installation_" + installations.length + ); + iOSInstallation.set( + "deviceToken", + "device_token_" + installations.length + ); + iOSInstallation.set("deviceType", "ios"); installations.push(iOSInstallation); iOSInstallations.push(iOSInstallation); } @@ -336,7 +353,7 @@ describe('Parse.Push', () => { return successfulAny(body, installations); }, getValidPushTypes: function () { - return ['android']; + return ["android"]; }, }, }, @@ -344,14 +361,14 @@ describe('Parse.Push', () => { await Parse.Object.saveAll(installations); const pushStatusId = await Parse.Push.send({ - data: { alert: 'We fixed our status!' }, - where: { deviceType: { $ne: 'random' } }, + data: { alert: "We fixed our status!" }, + where: { deviceType: { $ne: "random" } }, }); await pushCompleted(pushStatusId); const result = await Parse.Push.getPushStatus(pushStatusId); - expect(result.get('status')).toEqual('succeeded'); + expect(result.get("status")).toEqual("succeeded"); // expect # less than # of batches used, assuming each batch is 100 pushes - expect(result.get('numSent')).toEqual(devices + devices / 100); - expect(result.get('count')).toEqual(undefined); + expect(result.get("numSent")).toEqual(devices + devices / 100); + expect(result.get("count")).toEqual(undefined); }); }); diff --git a/spec/ParseACL.spec.js b/spec/ParseACL.spec.js index b8a982ec45..2197199a30 100644 --- a/spec/ParseACL.spec.js +++ b/spec/ParseACL.spec.js @@ -1,22 +1,22 @@ // This is a port of the test suite: // hungry/js/test/parse_acl_test.js -const rest = require('../lib/rest'); -const Config = require('../lib/Config'); -const auth = require('../lib/Auth'); +const rest = require("../lib/rest"); +const Config = require("../lib/Config"); +const auth = require("../lib/Auth"); -describe('Parse.ACL', () => { - it('acl must be valid', () => { +describe("Parse.ACL", () => { + it("acl must be valid", () => { const user = new Parse.User(); - expect(() => user.setACL('ACL')).toThrow( - new Parse.Error(Parse.Error.OTHER_CAUSE, 'ACL must be a Parse ACL.') + expect(() => user.setACL("ACL")).toThrow( + new Parse.Error(Parse.Error.OTHER_CAUSE, "ACL must be a Parse ACL.") ); }); - it('refresh object with acl', async done => { + it("refresh object with acl", async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set('username', 'alice'); - user.set('password', 'wonderland'); + user.set("username", "alice"); + user.set("password", "wonderland"); await user.signUp(null); const object = new TestObject(); const acl = new Parse.ACL(user); @@ -26,11 +26,11 @@ describe('Parse.ACL', () => { done(); }); - it('acl an object owned by one user and public get', async done => { + it("acl an object owned by one user and public get", async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set('username', 'alice'); - user.set('password', 'wonderland'); + user.set("username", "alice"); + user.set("password", "wonderland"); await user.signUp(); const object = new TestObject(); const acl = new Parse.ACL(user); @@ -40,23 +40,23 @@ describe('Parse.ACL', () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get('ACL')); + ok(object.get("ACL")); await Parse.User.logOut(); const query = new Parse.Query(TestObject); try { await query.get(object.id); - done.fail('Should not have retrieved the object.'); + done.fail("Should not have retrieved the object."); } catch (error) { equal(error.code, Parse.Error.OBJECT_NOT_FOUND); done(); } }); - it('acl an object owned by one user and public find', async done => { + it("acl an object owned by one user and public find", async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set('username', 'alice'); - user.set('password', 'wonderland'); + user.set("username", "alice"); + user.set("password", "wonderland"); await user.signUp(); const object = new TestObject(); @@ -67,7 +67,7 @@ describe('Parse.ACL', () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get('ACL')); + ok(object.get("ACL")); // Start making requests by the public, which should all fail. await Parse.User.logOut(); @@ -78,11 +78,11 @@ describe('Parse.ACL', () => { done(); }); - it('acl an object owned by one user and public update', async done => { + it("acl an object owned by one user and public update", async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set('username', 'alice'); - user.set('password', 'wonderland'); + user.set("username", "alice"); + user.set("password", "wonderland"); await user.signUp(); const object = new TestObject(); @@ -93,26 +93,26 @@ describe('Parse.ACL', () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get('ACL')); + ok(object.get("ACL")); // Start making requests by the public, which should all fail. await Parse.User.logOut(); // Update - object.set('foo', 'bar'); + object.set("foo", "bar"); try { await object.save(); - done.fail('Should not have been able to update the object.'); + done.fail("Should not have been able to update the object."); } catch (err) { equal(err.code, Parse.Error.OBJECT_NOT_FOUND); done(); } }); - it('acl an object owned by one user and public delete', async done => { + it("acl an object owned by one user and public delete", async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set('username', 'alice'); - user.set('password', 'wonderland'); + user.set("username", "alice"); + user.set("password", "wonderland"); await user.signUp(); const object = new TestObject(); @@ -124,24 +124,24 @@ describe('Parse.ACL', () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get('ACL')); + ok(object.get("ACL")); // Start making requests by the public, which should all fail. await Parse.User.logOut(); try { await object.destroy(); - done.fail('destroy should fail'); + done.fail("destroy should fail"); } catch (error) { expect(error.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); done(); } }); - it('acl an object owned by one user and logged in get', async done => { + it("acl an object owned by one user and logged in get", async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set('username', 'alice'); - user.set('password', 'wonderland'); + user.set("username", "alice"); + user.set("password", "wonderland"); await user.signUp(); const object = new TestObject(); const acl = new Parse.ACL(user); @@ -151,10 +151,10 @@ describe('Parse.ACL', () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get('ACL')); + ok(object.get("ACL")); await Parse.User.logOut(); - await Parse.User.logIn('alice', 'wonderland'); + await Parse.User.logIn("alice", "wonderland"); // Get const query = new Parse.Query(TestObject); const result = await query.get(object.id); @@ -164,15 +164,15 @@ describe('Parse.ACL', () => { equal(result.getACL().getWriteAccess(user), true); equal(result.getACL().getPublicReadAccess(), false); equal(result.getACL().getPublicWriteAccess(), false); - ok(object.get('ACL')); + ok(object.get("ACL")); done(); }); - it('acl an object owned by one user and logged in find', async done => { + it("acl an object owned by one user and logged in find", async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set('username', 'alice'); - user.set('password', 'wonderland'); + user.set("username", "alice"); + user.set("password", "wonderland"); await user.signUp(); const object = new TestObject(); const acl = new Parse.ACL(user); @@ -182,9 +182,9 @@ describe('Parse.ACL', () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get('ACL')); + ok(object.get("ACL")); await Parse.User.logOut(); - await Parse.User.logIn('alice', 'wonderland'); + await Parse.User.logIn("alice", "wonderland"); // Find const query = new Parse.Query(TestObject); const results = await query.find(); @@ -199,15 +199,15 @@ describe('Parse.ACL', () => { equal(result.getACL().getWriteAccess(user), true); equal(result.getACL().getPublicReadAccess(), false); equal(result.getACL().getPublicWriteAccess(), false); - ok(object.get('ACL')); + ok(object.get("ACL")); done(); }); - it('acl an object owned by one user and logged in update', async done => { + it("acl an object owned by one user and logged in update", async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set('username', 'alice'); - user.set('password', 'wonderland'); + user.set("username", "alice"); + user.set("password", "wonderland"); await user.signUp(); const object = new TestObject(); const acl = new Parse.ACL(user); @@ -217,21 +217,21 @@ describe('Parse.ACL', () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get('ACL')); + ok(object.get("ACL")); await Parse.User.logOut(); - await Parse.User.logIn('alice', 'wonderland'); + await Parse.User.logIn("alice", "wonderland"); // Update - object.set('foo', 'bar'); + object.set("foo", "bar"); await object.save(); done(); }); - it('acl an object owned by one user and logged in delete', async done => { + it("acl an object owned by one user and logged in delete", async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set('username', 'alice'); - user.set('password', 'wonderland'); + user.set("username", "alice"); + user.set("password", "wonderland"); await user.signUp(); const object = new TestObject(); const acl = new Parse.ACL(user); @@ -241,19 +241,19 @@ describe('Parse.ACL', () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get('ACL')); + ok(object.get("ACL")); await Parse.User.logOut(); - await Parse.User.logIn('alice', 'wonderland'); + await Parse.User.logIn("alice", "wonderland"); // Delete await object.destroy(); done(); }); - it('acl making an object publicly readable and public get', async done => { + it("acl making an object publicly readable and public get", async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set('username', 'alice'); - user.set('password', 'wonderland'); + user.set("username", "alice"); + user.set("password", "wonderland"); await user.signUp(); const object = new TestObject(); const acl = new Parse.ACL(user); @@ -263,7 +263,7 @@ describe('Parse.ACL', () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get('ACL')); + ok(object.get("ACL")); // Now make it public. object.getACL().setPublicReadAccess(true); @@ -272,7 +272,7 @@ describe('Parse.ACL', () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), true); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get('ACL')); + ok(object.get("ACL")); await Parse.User.logOut(); // Get @@ -283,11 +283,11 @@ describe('Parse.ACL', () => { done(); }); - it('acl making an object publicly readable and public find', async done => { + it("acl making an object publicly readable and public find", async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set('username', 'alice'); - user.set('password', 'wonderland'); + user.set("username", "alice"); + user.set("password", "wonderland"); await user.signUp(); const object = new TestObject(); const acl = new Parse.ACL(user); @@ -297,7 +297,7 @@ describe('Parse.ACL', () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get('ACL')); + ok(object.get("ACL")); // Now make it public. object.getACL().setPublicReadAccess(true); @@ -306,7 +306,7 @@ describe('Parse.ACL', () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), true); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get('ACL')); + ok(object.get("ACL")); await Parse.User.logOut(); // Find @@ -319,11 +319,11 @@ describe('Parse.ACL', () => { done(); }); - it('acl making an object publicly readable and public update', async done => { + it("acl making an object publicly readable and public update", async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set('username', 'alice'); - user.set('password', 'wonderland'); + user.set("username", "alice"); + user.set("password", "wonderland"); await user.signUp(); const object = new TestObject(); const acl = new Parse.ACL(user); @@ -333,7 +333,7 @@ describe('Parse.ACL', () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get('ACL')); + ok(object.get("ACL")); // Now make it public. object.getACL().setPublicReadAccess(true); @@ -342,13 +342,13 @@ describe('Parse.ACL', () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), true); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get('ACL')); + ok(object.get("ACL")); await Parse.User.logOut(); - object.set('foo', 'bar'); + object.set("foo", "bar"); object.save().then( () => { - fail('the save should fail'); + fail("the save should fail"); }, error => { expect(error.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); @@ -357,11 +357,11 @@ describe('Parse.ACL', () => { ); }); - it('acl making an object publicly readable and public delete', async done => { + it("acl making an object publicly readable and public delete", async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set('username', 'alice'); - user.set('password', 'wonderland'); + user.set("username", "alice"); + user.set("password", "wonderland"); await user.signUp(); const object = new TestObject(); const acl = new Parse.ACL(user); @@ -371,7 +371,7 @@ describe('Parse.ACL', () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get('ACL')); + ok(object.get("ACL")); // Now make it public. object.getACL().setPublicReadAccess(true); @@ -380,13 +380,13 @@ describe('Parse.ACL', () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), true); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get('ACL')); + ok(object.get("ACL")); Parse.User.logOut() .then(() => object.destroy()) .then( () => { - fail('expected failure'); + fail("expected failure"); }, error => { expect(error.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); @@ -395,11 +395,11 @@ describe('Parse.ACL', () => { ); }); - it('acl making an object publicly writable and public get', async done => { + it("acl making an object publicly writable and public get", async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set('username', 'alice'); - user.set('password', 'wonderland'); + user.set("username", "alice"); + user.set("password", "wonderland"); await user.signUp(); const object = new TestObject(); const acl = new Parse.ACL(user); @@ -409,7 +409,7 @@ describe('Parse.ACL', () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get('ACL')); + ok(object.get("ACL")); // Now make it public. object.getACL().setPublicWriteAccess(true); @@ -418,7 +418,7 @@ describe('Parse.ACL', () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), true); - ok(object.get('ACL')); + ok(object.get("ACL")); await Parse.User.logOut(); // Get @@ -432,11 +432,11 @@ describe('Parse.ACL', () => { }); }); - it('acl making an object publicly writable and public find', async done => { + it("acl making an object publicly writable and public find", async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set('username', 'alice'); - user.set('password', 'wonderland'); + user.set("username", "alice"); + user.set("password", "wonderland"); await user.signUp(); const object = new TestObject(); const acl = new Parse.ACL(user); @@ -446,7 +446,7 @@ describe('Parse.ACL', () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get('ACL')); + ok(object.get("ACL")); // Now make it public. object.getACL().setPublicWriteAccess(true); @@ -455,7 +455,7 @@ describe('Parse.ACL', () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), true); - ok(object.get('ACL')); + ok(object.get("ACL")); await Parse.User.logOut(); // Find @@ -466,11 +466,11 @@ describe('Parse.ACL', () => { }); }); - it('acl making an object publicly writable and public update', async done => { + it("acl making an object publicly writable and public update", async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set('username', 'alice'); - user.set('password', 'wonderland'); + user.set("username", "alice"); + user.set("password", "wonderland"); await user.signUp(); const object = new TestObject(); const acl = new Parse.ACL(user); @@ -480,7 +480,7 @@ describe('Parse.ACL', () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get('ACL')); + ok(object.get("ACL")); // Now make it public. object.getACL().setPublicWriteAccess(true); @@ -489,20 +489,20 @@ describe('Parse.ACL', () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), true); - ok(object.get('ACL')); + ok(object.get("ACL")); Parse.User.logOut().then(() => { // Update - object.set('foo', 'bar'); + object.set("foo", "bar"); object.save().then(done); }); }); - it('acl making an object publicly writable and public delete', async done => { + it("acl making an object publicly writable and public delete", async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set('username', 'alice'); - user.set('password', 'wonderland'); + user.set("username", "alice"); + user.set("password", "wonderland"); await user.signUp(); const object = new TestObject(); const acl = new Parse.ACL(user); @@ -512,7 +512,7 @@ describe('Parse.ACL', () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get('ACL')); + ok(object.get("ACL")); // Now make it public. object.getACL().setPublicWriteAccess(true); @@ -521,7 +521,7 @@ describe('Parse.ACL', () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), true); - ok(object.get('ACL')); + ok(object.get("ACL")); Parse.User.logOut().then(() => { // Delete @@ -529,13 +529,13 @@ describe('Parse.ACL', () => { }); }); - it('acl making an object privately writable (#3194)', done => { + it("acl making an object privately writable (#3194)", done => { // Create an object owned by Alice. let object; let user2; const user = new Parse.User(); - user.set('username', 'alice'); - user.set('password', 'wonderland'); + user.set("username", "alice"); + user.set("password", "wonderland"); user .signUp() .then(() => { @@ -550,8 +550,8 @@ describe('Parse.ACL', () => { }) .then(() => { user2 = new Parse.User(); - user2.set('username', 'bob'); - user2.set('password', 'burger'); + user2.set("username", "bob"); + user2.set("password", "burger"); return user2.signUp(); }) .then(() => { @@ -559,7 +559,7 @@ describe('Parse.ACL', () => { }) .then( () => { - fail('should not be able to destroy the object'); + fail("should not be able to destroy the object"); done(); }, err => { @@ -569,12 +569,12 @@ describe('Parse.ACL', () => { ); }); - it('acl sharing with another user and get', async done => { + it("acl sharing with another user and get", async done => { // Sign in as Bob. - const bob = await Parse.User.signUp('bob', 'pass'); + const bob = await Parse.User.signUp("bob", "pass"); await Parse.User.logOut(); - const alice = await Parse.User.signUp('alice', 'wonderland'); + const alice = await Parse.User.signUp("alice", "wonderland"); // Create an object shared by Bob and Alice. const object = new TestObject(); const acl = new Parse.ACL(alice); @@ -590,7 +590,7 @@ describe('Parse.ACL', () => { equal(object.getACL().getPublicWriteAccess(), false); // Sign in as Bob again. - await Parse.User.logIn('bob', 'pass'); + await Parse.User.logIn("bob", "pass"); const query = new Parse.Query(TestObject); query.get(object.id).then(result => { ok(result); @@ -599,12 +599,12 @@ describe('Parse.ACL', () => { }); }); - it('acl sharing with another user and find', async done => { + it("acl sharing with another user and find", async done => { // Sign in as Bob. - const bob = await Parse.User.signUp('bob', 'pass'); + const bob = await Parse.User.signUp("bob", "pass"); await Parse.User.logOut(); // Sign in as Alice. - const alice = await Parse.User.signUp('alice', 'wonderland'); + const alice = await Parse.User.signUp("alice", "wonderland"); // Create an object shared by Bob and Alice. const object = new TestObject(); const acl = new Parse.ACL(alice); @@ -620,14 +620,14 @@ describe('Parse.ACL', () => { equal(object.getACL().getPublicWriteAccess(), false); // Sign in as Bob again. - await Parse.User.logIn('bob', 'pass'); + await Parse.User.logIn("bob", "pass"); const query = new Parse.Query(TestObject); query.find().then(results => { equal(results.length, 1); const result = results[0]; ok(result); if (!result) { - fail('should have result'); + fail("should have result"); } else { equal(result.id, object.id); } @@ -635,12 +635,12 @@ describe('Parse.ACL', () => { }); }); - it('acl sharing with another user and update', async done => { + it("acl sharing with another user and update", async done => { // Sign in as Bob. - const bob = await Parse.User.signUp('bob', 'pass'); + const bob = await Parse.User.signUp("bob", "pass"); await Parse.User.logOut(); // Sign in as Alice. - const alice = await Parse.User.signUp('alice', 'wonderland'); + const alice = await Parse.User.signUp("alice", "wonderland"); // Create an object shared by Bob and Alice. const object = new TestObject(); const acl = new Parse.ACL(alice); @@ -656,17 +656,17 @@ describe('Parse.ACL', () => { equal(object.getACL().getPublicWriteAccess(), false); // Sign in as Bob again. - await Parse.User.logIn('bob', 'pass'); - object.set('foo', 'bar'); + await Parse.User.logIn("bob", "pass"); + object.set("foo", "bar"); object.save().then(done); }); - it('acl sharing with another user and delete', async done => { + it("acl sharing with another user and delete", async done => { // Sign in as Bob. - const bob = await Parse.User.signUp('bob', 'pass'); + const bob = await Parse.User.signUp("bob", "pass"); await Parse.User.logOut(); // Sign in as Alice. - const alice = await Parse.User.signUp('alice', 'wonderland'); + const alice = await Parse.User.signUp("alice", "wonderland"); // Create an object shared by Bob and Alice. const object = new TestObject(); const acl = new Parse.ACL(alice); @@ -682,16 +682,16 @@ describe('Parse.ACL', () => { equal(object.getACL().getPublicWriteAccess(), false); // Sign in as Bob again. - await Parse.User.logIn('bob', 'pass'); - object.set('foo', 'bar'); + await Parse.User.logIn("bob", "pass"); + object.set("foo", "bar"); object.destroy().then(done); }); - it('acl sharing with another user and public get', async done => { - const bob = await Parse.User.signUp('bob', 'pass'); + it("acl sharing with another user and public get", async done => { + const bob = await Parse.User.signUp("bob", "pass"); await Parse.User.logOut(); // Sign in as Alice. - const alice = await Parse.User.signUp('alice', 'wonderland'); + const alice = await Parse.User.signUp("alice", "wonderland"); // Create an object shared by Bob and Alice. const object = new TestObject(); const acl = new Parse.ACL(alice); @@ -719,11 +719,11 @@ describe('Parse.ACL', () => { ); }); - it('acl sharing with another user and public find', async done => { - const bob = await Parse.User.signUp('bob', 'pass'); + it("acl sharing with another user and public find", async done => { + const bob = await Parse.User.signUp("bob", "pass"); await Parse.User.logOut(); // Sign in as Alice. - const alice = await Parse.User.signUp('alice', 'wonderland'); + const alice = await Parse.User.signUp("alice", "wonderland"); // Create an object shared by Bob and Alice. const object = new TestObject(); const acl = new Parse.ACL(alice); @@ -748,12 +748,12 @@ describe('Parse.ACL', () => { }); }); - it('acl sharing with another user and public update', async done => { + it("acl sharing with another user and public update", async done => { // Sign in as Bob. - const bob = await Parse.User.signUp('bob', 'pass'); + const bob = await Parse.User.signUp("bob", "pass"); await Parse.User.logOut(); // Sign in as Alice. - const alice = await Parse.User.signUp('alice', 'wonderland'); + const alice = await Parse.User.signUp("alice", "wonderland"); // Create an object shared by Bob and Alice. const object = new TestObject(); const acl = new Parse.ACL(alice); @@ -770,10 +770,10 @@ describe('Parse.ACL', () => { // Start making requests by the public. Parse.User.logOut().then(() => { - object.set('foo', 'bar'); + object.set("foo", "bar"); object.save().then( () => { - fail('expected failure'); + fail("expected failure"); }, error => { expect(error.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); @@ -783,12 +783,12 @@ describe('Parse.ACL', () => { }); }); - it('acl sharing with another user and public delete', async done => { + it("acl sharing with another user and public delete", async done => { // Sign in as Bob. - const bob = await Parse.User.signUp('bob', 'pass'); + const bob = await Parse.User.signUp("bob", "pass"); await Parse.User.logOut(); // Sign in as Alice. - const alice = await Parse.User.signUp('alice', 'wonderland'); + const alice = await Parse.User.signUp("alice", "wonderland"); // Create an object shared by Bob and Alice. const object = new TestObject(); const acl = new Parse.ACL(alice); @@ -808,7 +808,7 @@ describe('Parse.ACL', () => { .then(() => object.destroy()) .then( () => { - fail('expected failure'); + fail("expected failure"); }, error => { expect(error.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); @@ -817,8 +817,8 @@ describe('Parse.ACL', () => { ); }); - it('acl saveAll with permissions', async done => { - const alice = await Parse.User.signUp('alice', 'wonderland'); + it("acl saveAll with permissions", async done => { + const alice = await Parse.User.signUp("alice", "wonderland"); const acl = new Parse.ACL(alice); const object1 = new TestObject(); const object2 = new TestObject(); @@ -835,70 +835,70 @@ describe('Parse.ACL', () => { equal(object2.getACL().getPublicWriteAccess(), false); // Save all the objects after updating them. - object1.set('foo', 'bar'); - object2.set('foo', 'bar'); + object1.set("foo", "bar"); + object2.set("foo", "bar"); await Parse.Object.saveAll([object1, object2]); const query = new Parse.Query(TestObject); - query.equalTo('foo', 'bar'); + query.equalTo("foo", "bar"); query.find().then(function (results) { equal(results.length, 2); done(); }); }); - it('empty acl works', async done => { - await Parse.User.signUp('tdurden', 'mayhem', { + it("empty acl works", async done => { + await Parse.User.signUp("tdurden", "mayhem", { ACL: new Parse.ACL(), - foo: 'bar', + foo: "bar", }); await Parse.User.logOut(); - const user = await Parse.User.logIn('tdurden', 'mayhem'); - equal(user.get('foo'), 'bar'); + const user = await Parse.User.logIn("tdurden", "mayhem"); + equal(user.get("foo"), "bar"); done(); }); - it('query for included object with ACL works', async done => { - const obj1 = new Parse.Object('TestClass1'); - const obj2 = new Parse.Object('TestClass2'); + it("query for included object with ACL works", async done => { + const obj1 = new Parse.Object("TestClass1"); + const obj2 = new Parse.Object("TestClass2"); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); - obj2.set('ACL', acl); - obj1.set('other', obj2); + obj2.set("ACL", acl); + obj1.set("other", obj2); await obj1.save(); obj2._clearServerData(); - const query = new Parse.Query('TestClass1'); + const query = new Parse.Query("TestClass1"); const obj1Again = await query.first(); - ok(!obj1Again.get('other').get('ACL')); + ok(!obj1Again.get("other").get("ACL")); - query.include('other'); + query.include("other"); const obj1AgainWithInclude = await query.first(); - ok(obj1AgainWithInclude.get('other').get('ACL')); + ok(obj1AgainWithInclude.get("other").get("ACL")); done(); }); - it('restricted ACL does not have public access', done => { - const obj = new Parse.Object('TestClassMasterACL'); + it("restricted ACL does not have public access", done => { + const obj = new Parse.Object("TestClassMasterACL"); const acl = new Parse.ACL(); - obj.set('ACL', acl); + obj.set("ACL", acl); obj .save() .then(() => { - const query = new Parse.Query('TestClassMasterACL'); + const query = new Parse.Query("TestClassMasterACL"); return query.find(); }) .then(results => { - ok(!results.length, 'Should not have returned object with secure ACL.'); + ok(!results.length, "Should not have returned object with secure ACL."); done(); }); }); - it('regression test #701', done => { - const config = Config.get('test'); + it("regression test #701", done => { + const config = Config.get("test"); const anonUser = { authData: { anonymous: { - id: '00000000-0000-0000-0000-000000000001', + id: "00000000-0000-0000-0000-000000000001", }, }, }; @@ -909,9 +909,9 @@ describe('Parse.ACL', () => { const acl = new Parse.ACL(user); user.setACL(acl); user.save(null, { useMasterKey: true }).then(user => { - new Parse.Query('_User').get(user.objectId).then( + new Parse.Query("_User").get(user.objectId).then( () => { - fail('should not have fetched user without public read enabled'); + fail("should not have fetched user without public read enabled"); done(); }, error => { @@ -923,33 +923,33 @@ describe('Parse.ACL', () => { } }); - rest.create(config, auth.nobody(config), '_User', anonUser); + rest.create(config, auth.nobody(config), "_User", anonUser); }); - it('support defaultACL in schema', async () => { - await new Parse.Object('TestObject').save(); + it("support defaultACL in schema", async () => { + await new Parse.Object("TestObject").save(); const schema = await Parse.Server.database.loadSchema(); await schema.updateClass( - 'TestObject', + "TestObject", {}, { create: { - '*': true, + "*": true, }, ACL: { - '*': { read: true }, + "*": { read: true }, currentUser: { read: true, write: true }, }, } ); const acls = new Parse.ACL(); acls.setPublicReadAccess(true); - const user = await Parse.User.signUp('testuser', 'p@ssword'); - const obj = new Parse.Object('TestObject'); + const user = await Parse.User.signUp("testuser", "p@ssword"); + const obj = new Parse.Object("TestObject"); await obj.save(null, { sessionToken: user.getSessionToken() }); expect(obj.getACL()).toBeDefined(); const acl = obj.getACL().toJSON(); - expect(acl['*']).toEqual({ read: true }); + expect(acl["*"]).toEqual({ read: true }); expect(acl[user.id].write).toBeTrue(); expect(acl[user.id].read).toBeTrue(); }); diff --git a/spec/ParseAPI.spec.js b/spec/ParseAPI.spec.js index 4b84d6785e..c1c6b51cd0 100644 --- a/spec/ParseAPI.spec.js +++ b/spec/ParseAPI.spec.js @@ -1,15 +1,15 @@ // A bunch of different tests are in here - it isn't very thematic. // It would probably be better to refactor them into different files. -'use strict'; +"use strict"; -const request = require('../lib/request'); -const Parse = require('parse/node'); -const Config = require('../lib/Config'); -const SchemaController = require('../lib/Controllers/SchemaController'); -const TestUtils = require('../lib/TestUtils'); +const request = require("../lib/request"); +const Parse = require("parse/node"); +const Config = require("../lib/Config"); +const SchemaController = require("../lib/Controllers/SchemaController"); +const TestUtils = require("../lib/TestUtils"); const userSchema = SchemaController.convertSchemaToAdapterSchema({ - className: '_User', + className: "_User", fields: Object.assign( {}, SchemaController.defaultColumns._Default, @@ -17,64 +17,69 @@ const userSchema = SchemaController.convertSchemaToAdapterSchema({ ), }); const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Installation-Id': 'yolo', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "X-Parse-Installation-Id": "yolo", }; -describe('miscellaneous', () => { - it('db contains document after successful save', async () => { - const obj = new Parse.Object('TestObject'); - obj.set('foo', 'bar'); +describe("miscellaneous", () => { + it("db contains document after successful save", async () => { + const obj = new Parse.Object("TestObject"); + obj.set("foo", "bar"); await obj.save(); const config = Config.get(defaultConfiguration.appId); - const results = await config.database.adapter.find('TestObject', { fields: {} }, {}, {}); + const results = await config.database.adapter.find( + "TestObject", + { fields: {} }, + {}, + {} + ); expect(results.length).toEqual(1); - expect(results[0]['foo']).toEqual('bar'); + expect(results[0]["foo"]).toEqual("bar"); }); - it('create a GameScore object', function (done) { - const obj = new Parse.Object('GameScore'); - obj.set('score', 1337); + it("create a GameScore object", function (done) { + const obj = new Parse.Object("GameScore"); + obj.set("score", 1337); obj.save().then(function (obj) { - expect(typeof obj.id).toBe('string'); - expect(typeof obj.createdAt.toGMTString()).toBe('string'); + expect(typeof obj.id).toBe("string"); + expect(typeof obj.createdAt.toGMTString()).toBe("string"); done(); }, done.fail); }); - it('get a TestObject', function (done) { - create({ bloop: 'blarg' }, async function (obj) { + it("get a TestObject", function (done) { + create({ bloop: "blarg" }, async function (obj) { const t2 = new TestObject({ objectId: obj.id }); const obj2 = await t2.fetch(); - expect(obj2.get('bloop')).toEqual('blarg'); + expect(obj2.get("bloop")).toEqual("blarg"); expect(obj2.id).toBeTruthy(); expect(obj2.id).toEqual(obj.id); done(); }); }); - it('create a valid parse user', function (done) { + it("create a valid parse user", function (done) { createTestUser().then(function (data) { expect(data.id).not.toBeUndefined(); expect(data.getSessionToken()).not.toBeUndefined(); - expect(data.get('password')).toBeUndefined(); + expect(data.get("password")).toBeUndefined(); done(); }, done.fail); }); - it('fail to create a duplicate username', async () => { + it("fail to create a duplicate username", async () => { await reconfigureServer(); let numFailed = 0; let numCreated = 0; const p1 = request({ - method: 'POST', - url: Parse.serverURL + '/users', + method: "POST", + url: Parse.serverURL + "/users", body: { - password: 'asdf', - username: 'u1', - email: 'dupe@dupe.dupe', + password: "asdf", + username: "u1", + email: "dupe@dupe.dupe", }, headers, }).then( @@ -89,12 +94,12 @@ describe('miscellaneous', () => { ); const p2 = request({ - method: 'POST', - url: Parse.serverURL + '/users', + method: "POST", + url: Parse.serverURL + "/users", body: { - password: 'otherpassword', - username: 'u1', - email: 'email@other.email', + password: "otherpassword", + username: "u1", + email: "email@other.email", }, headers, }).then( @@ -112,17 +117,17 @@ describe('miscellaneous', () => { expect(numCreated).toBe(1); }); - it('ensure that email is uniquely indexed', async () => { + it("ensure that email is uniquely indexed", async () => { await reconfigureServer(); let numFailed = 0; let numCreated = 0; const p1 = request({ - method: 'POST', - url: Parse.serverURL + '/users', + method: "POST", + url: Parse.serverURL + "/users", body: { - password: 'asdf', - username: 'u1', - email: 'dupe@dupe.dupe', + password: "asdf", + username: "u1", + email: "dupe@dupe.dupe", }, headers, }).then( @@ -137,12 +142,12 @@ describe('miscellaneous', () => { ); const p2 = request({ - url: Parse.serverURL + '/users', - method: 'POST', + url: Parse.serverURL + "/users", + method: "POST", body: { - password: 'asdf', - username: 'u2', - email: 'dupe@dupe.dupe', + password: "asdf", + username: "u2", + email: "dupe@dupe.dupe", }, headers, }).then( @@ -161,27 +166,31 @@ describe('miscellaneous', () => { expect(numCreated).toBe(1); }); - it_id('be1b9ac7-5e5f-4e91-b044-2bd8fb7622ad')(it)( - 'ensure that if people already have duplicate users, they can still sign up new users', + it_id("be1b9ac7-5e5f-4e91-b044-2bd8fb7622ad")(it)( + "ensure that if people already have duplicate users, they can still sign up new users", async done => { try { await Parse.User.logOut(); } catch (e) { /* ignore */ } - const config = Config.get('test'); + const config = Config.get("test"); // Remove existing data to clear out unique index TestUtils.destroyAllDataPermanently() - .then(() => config.database.adapter.performInitialization({ VolatileClassesSchemas: [] })) - .then(() => config.database.adapter.createClass('_User', userSchema)) + .then(() => + config.database.adapter.performInitialization({ + VolatileClassesSchemas: [], + }) + ) + .then(() => config.database.adapter.createClass("_User", userSchema)) .then(() => config.database.adapter - .createObject('_User', userSchema, { objectId: 'x', username: 'u' }) + .createObject("_User", userSchema, { objectId: "x", username: "u" }) .catch(fail) ) .then(() => config.database.adapter - .createObject('_User', userSchema, { objectId: 'y', username: 'u' }) + .createObject("_User", userSchema, { objectId: "y", username: "u" }) .catch(fail) ) // Create a new server to try to recreate the unique indexes @@ -189,18 +198,18 @@ describe('miscellaneous', () => { .catch(error => { expect(error.code).toEqual(Parse.Error.DUPLICATE_VALUE); const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('zxcv'); + user.setPassword("asdf"); + user.setUsername("zxcv"); return user.signUp().catch(fail); }) .then(() => { const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('u'); + user.setPassword("asdf"); + user.setUsername("u"); return user.signUp(); }) .then(() => { - fail('should not have been able to sign up'); + fail("should not have been able to sign up"); done(); }) .catch(error => { @@ -210,39 +219,43 @@ describe('miscellaneous', () => { } ); - it_id('d00f907e-41b9-40f6-8168-63e832199a8c')(it)( - 'ensure that if people already have duplicate emails, they can still sign up new users', + it_id("d00f907e-41b9-40f6-8168-63e832199a8c")(it)( + "ensure that if people already have duplicate emails, they can still sign up new users", done => { - const config = Config.get('test'); + const config = Config.get("test"); // Remove existing data to clear out unique index TestUtils.destroyAllDataPermanently() - .then(() => config.database.adapter.performInitialization({ VolatileClassesSchemas: [] })) - .then(() => config.database.adapter.createClass('_User', userSchema)) .then(() => - config.database.adapter.createObject('_User', userSchema, { - objectId: 'x', - email: 'a@b.c', + config.database.adapter.performInitialization({ + VolatileClassesSchemas: [], + }) + ) + .then(() => config.database.adapter.createClass("_User", userSchema)) + .then(() => + config.database.adapter.createObject("_User", userSchema, { + objectId: "x", + email: "a@b.c", }) ) .then(() => - config.database.adapter.createObject('_User', userSchema, { - objectId: 'y', - email: 'a@b.c', + config.database.adapter.createObject("_User", userSchema, { + objectId: "y", + email: "a@b.c", }) ) .then(reconfigureServer) .catch(() => { const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('qqq'); - user.setEmail('unique@unique.unique'); + user.setPassword("asdf"); + user.setUsername("qqq"); + user.setEmail("unique@unique.unique"); return user.signUp().catch(fail); }) .then(() => { const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('www'); - user.setEmail('a@b.c'); + user.setPassword("asdf"); + user.setUsername("www"); + user.setEmail("a@b.c"); return user.signUp(); }) .catch(error => { @@ -252,26 +265,30 @@ describe('miscellaneous', () => { } ); - it('ensure that if you try to sign up a user with a unique username and email, but duplicates in some other field that has a uniqueness constraint, you get a regular duplicate value error', async done => { + it("ensure that if you try to sign up a user with a unique username and email, but duplicates in some other field that has a uniqueness constraint, you get a regular duplicate value error", async done => { await reconfigureServer(); - const config = Config.get('test'); + const config = Config.get("test"); config.database.adapter - .addFieldIfNotExists('_User', 'randomField', { type: 'String' }) - .then(() => config.database.adapter.ensureUniqueness('_User', userSchema, ['randomField'])) + .addFieldIfNotExists("_User", "randomField", { type: "String" }) + .then(() => + config.database.adapter.ensureUniqueness("_User", userSchema, [ + "randomField", + ]) + ) .then(() => { const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('1'); - user.setEmail('1@b.c'); - user.set('randomField', 'a'); + user.setPassword("asdf"); + user.setUsername("1"); + user.setEmail("1@b.c"); + user.set("randomField", "a"); return user.signUp(); }) .then(() => { const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('2'); - user.setEmail('2@b.c'); - user.set('randomField', 'a'); + user.setPassword("asdf"); + user.setUsername("2"); + user.setEmail("2@b.c"); + user.set("randomField", "a"); return user.signUp(); }) .catch(error => { @@ -280,40 +297,40 @@ describe('miscellaneous', () => { }); }); - it('succeed in logging in', function (done) { + it("succeed in logging in", function (done) { createTestUser().then(async function (u) { - expect(typeof u.id).toEqual('string'); + expect(typeof u.id).toEqual("string"); - const user = await Parse.User.logIn('test', 'moon-y'); - expect(typeof user.id).toEqual('string'); - expect(user.get('password')).toBeUndefined(); + const user = await Parse.User.logIn("test", "moon-y"); + expect(typeof user.id).toEqual("string"); + expect(user.get("password")).toBeUndefined(); expect(user.getSessionToken()).not.toBeUndefined(); await Parse.User.logOut(); done(); }, fail); }); - it_id('33db6efe-7c02-496c-8595-0ef627a94103')(it)( - 'increment with a user object', + it_id("33db6efe-7c02-496c-8595-0ef627a94103")(it)( + "increment with a user object", function (done) { createTestUser() .then(user => { - user.increment('foo'); + user.increment("foo"); return user.save(); }) .then(() => { - return Parse.User.logIn('test', 'moon-y'); + return Parse.User.logIn("test", "moon-y"); }) .then(user => { - expect(user.get('foo')).toEqual(1); - user.increment('foo'); + expect(user.get("foo")).toEqual(1); + user.increment("foo"); return user.save(); }) .then(() => Parse.User.logOut()) - .then(() => Parse.User.logIn('test', 'moon-y')) + .then(() => Parse.User.logIn("test", "moon-y")) .then( user => { - expect(user.get('foo')).toEqual(2); + expect(user.get("foo")).toEqual(2); Parse.User.logOut().then(done); }, error => { @@ -324,29 +341,32 @@ describe('miscellaneous', () => { } ); - it_id('bef99522-bcfd-4f79-ba9e-3c3845550401')(it)('save various data types', function (done) { - const obj = new TestObject(); - obj.set('date', new Date()); - obj.set('array', [1, 2, 3]); - obj.set('object', { one: 1, two: 2 }); - obj - .save() - .then(() => { - const obj2 = new TestObject({ objectId: obj.id }); - return obj2.fetch(); - }) - .then(obj2 => { - expect(obj2.get('date') instanceof Date).toBe(true); - expect(obj2.get('array') instanceof Array).toBe(true); - expect(obj2.get('object') instanceof Array).toBe(false); - expect(obj2.get('object') instanceof Object).toBe(true); - done(); - }); - }); + it_id("bef99522-bcfd-4f79-ba9e-3c3845550401")(it)( + "save various data types", + function (done) { + const obj = new TestObject(); + obj.set("date", new Date()); + obj.set("array", [1, 2, 3]); + obj.set("object", { one: 1, two: 2 }); + obj + .save() + .then(() => { + const obj2 = new TestObject({ objectId: obj.id }); + return obj2.fetch(); + }) + .then(obj2 => { + expect(obj2.get("date") instanceof Date).toBe(true); + expect(obj2.get("array") instanceof Array).toBe(true); + expect(obj2.get("object") instanceof Array).toBe(false); + expect(obj2.get("object") instanceof Object).toBe(true); + done(); + }); + } + ); - it('query with limit', function (done) { - const baz = new TestObject({ foo: 'baz' }); - const qux = new TestObject({ foo: 'qux' }); + it("query with limit", function (done) { + const baz = new TestObject({ foo: "baz" }); + const qux = new TestObject({ foo: "qux" }); baz .save() .then(() => { @@ -369,10 +389,10 @@ describe('miscellaneous', () => { ); }); - it('query without limit get default 100 records', function (done) { + it("query without limit get default 100 records", function (done) { const objects = []; for (let i = 0; i < 150; i++) { - objects.push(new TestObject({ name: 'name' + i })); + objects.push(new TestObject({ name: "name" + i })); } Parse.Object.saveAll(objects) .then(() => { @@ -390,9 +410,9 @@ describe('miscellaneous', () => { ); }); - it('basic saveAll', function (done) { - const alpha = new TestObject({ letter: 'alpha' }); - const beta = new TestObject({ letter: 'beta' }); + it("basic saveAll", function (done) { + const alpha = new TestObject({ letter: "alpha" }); + const beta = new TestObject({ letter: "beta" }); Parse.Object.saveAll([alpha, beta]) .then(() => { expect(alpha.id).toBeTruthy(); @@ -411,22 +431,22 @@ describe('miscellaneous', () => { ); }); - it('test beforeSave set object acl success', function (done) { + it("test beforeSave set object acl success", function (done) { const acl = new Parse.ACL({ - '*': { read: true, write: false }, + "*": { read: true, write: false }, }); - Parse.Cloud.beforeSave('BeforeSaveAddACL', function (req) { + Parse.Cloud.beforeSave("BeforeSaveAddACL", function (req) { req.object.setACL(acl); }); - const obj = new Parse.Object('BeforeSaveAddACL'); - obj.set('lol', true); + const obj = new Parse.Object("BeforeSaveAddACL"); + obj.set("lol", true); obj.save().then( function () { - const query = new Parse.Query('BeforeSaveAddACL'); + const query = new Parse.Query("BeforeSaveAddACL"); query.get(obj.id).then( function (objAgain) { - expect(objAgain.get('lol')).toBeTruthy(); + expect(objAgain.get("lol")).toBeTruthy(); expect(objAgain.getACL().equals(acl)); done(); }, @@ -443,23 +463,23 @@ describe('miscellaneous', () => { ); }); - it('object is set on create and update', done => { + it("object is set on create and update", done => { let triggerTime = 0; // Register a mock beforeSave hook - Parse.Cloud.beforeSave('GameScore', req => { + Parse.Cloud.beforeSave("GameScore", req => { const object = req.object; expect(object instanceof Parse.Object).toBeTruthy(); - expect(object.get('fooAgain')).toEqual('barAgain'); + expect(object.get("fooAgain")).toEqual("barAgain"); if (triggerTime == 0) { // Create - expect(object.get('foo')).toEqual('bar'); + expect(object.get("foo")).toEqual("bar"); // No objectId/createdAt/updatedAt expect(object.id).toBeUndefined(); expect(object.createdAt).toBeUndefined(); expect(object.updatedAt).toBeUndefined(); } else if (triggerTime == 1) { // Update - expect(object.get('foo')).toEqual('baz'); + expect(object.get("foo")).toEqual("baz"); expect(object.id).not.toBeUndefined(); expect(object.createdAt).not.toBeUndefined(); expect(object.updatedAt).not.toBeUndefined(); @@ -469,14 +489,14 @@ describe('miscellaneous', () => { triggerTime++; }); - const obj = new Parse.Object('GameScore'); - obj.set('foo', 'bar'); - obj.set('fooAgain', 'barAgain'); + const obj = new Parse.Object("GameScore"); + obj.set("foo", "bar"); + obj.set("fooAgain", "barAgain"); obj .save() .then(() => { // We only update foo - obj.set('foo', 'baz'); + obj.set("foo", "baz"); return obj.save(); }) .then( @@ -491,22 +511,22 @@ describe('miscellaneous', () => { } ); }); - it('works when object is passed to success', done => { + it("works when object is passed to success", done => { let triggerTime = 0; // Register a mock beforeSave hook - Parse.Cloud.beforeSave('GameScore', req => { + Parse.Cloud.beforeSave("GameScore", req => { const object = req.object; - object.set('foo', 'bar'); + object.set("foo", "bar"); triggerTime++; return object; }); - const obj = new Parse.Object('GameScore'); - obj.set('foo', 'baz'); + const obj = new Parse.Object("GameScore"); + obj.set("foo", "baz"); obj.save().then( () => { expect(triggerTime).toBe(1); - expect(obj.get('foo')).toEqual('bar'); + expect(obj.get("foo")).toEqual("bar"); done(); }, error => { @@ -516,13 +536,13 @@ describe('miscellaneous', () => { ); }); - it('original object is set on update', done => { + it("original object is set on update", done => { let triggerTime = 0; // Register a mock beforeSave hook - Parse.Cloud.beforeSave('GameScore', req => { + Parse.Cloud.beforeSave("GameScore", req => { const object = req.object; expect(object instanceof Parse.Object).toBeTruthy(); - expect(object.get('fooAgain')).toEqual('barAgain'); + expect(object.get("fooAgain")).toEqual("barAgain"); const originalObject = req.original; if (triggerTime == 0) { // No id/createdAt/updatedAt @@ -530,7 +550,7 @@ describe('miscellaneous', () => { expect(object.createdAt).toBeUndefined(); expect(object.updatedAt).toBeUndefined(); // Create - expect(object.get('foo')).toEqual('bar'); + expect(object.get("foo")).toEqual("bar"); // Check the originalObject is undefined expect(originalObject).toBeUndefined(); } else if (triggerTime == 1) { @@ -538,28 +558,28 @@ describe('miscellaneous', () => { expect(object.id).not.toBeUndefined(); expect(object.createdAt).not.toBeUndefined(); expect(object.updatedAt).not.toBeUndefined(); - expect(object.get('foo')).toEqual('baz'); + expect(object.get("foo")).toEqual("baz"); // Check the originalObject expect(originalObject instanceof Parse.Object).toBeTruthy(); - expect(originalObject.get('fooAgain')).toEqual('barAgain'); + expect(originalObject.get("fooAgain")).toEqual("barAgain"); expect(originalObject.id).not.toBeUndefined(); expect(originalObject.createdAt).not.toBeUndefined(); expect(originalObject.updatedAt).not.toBeUndefined(); - expect(originalObject.get('foo')).toEqual('bar'); + expect(originalObject.get("foo")).toEqual("bar"); } else { throw new Error(); } triggerTime++; }); - const obj = new Parse.Object('GameScore'); - obj.set('foo', 'bar'); - obj.set('fooAgain', 'barAgain'); + const obj = new Parse.Object("GameScore"); + obj.set("foo", "bar"); + obj.set("fooAgain", "barAgain"); obj .save() .then(() => { // We only update foo - obj.set('foo', 'baz'); + obj.set("foo", "baz"); return obj.save(); }) .then( @@ -575,38 +595,38 @@ describe('miscellaneous', () => { ); }); - it('pointer mutation properly saves object', done => { - const className = 'GameScore'; + it("pointer mutation properly saves object", done => { + const className = "GameScore"; Parse.Cloud.beforeSave(className, req => { const object = req.object; expect(object instanceof Parse.Object).toBeTruthy(); - const child = object.get('child'); + const child = object.get("child"); expect(child instanceof Parse.Object).toBeTruthy(); - child.set('a', 'b'); + child.set("a", "b"); return child.save(); }); const obj = new Parse.Object(className); - obj.set('foo', 'bar'); + obj.set("foo", "bar"); - const child = new Parse.Object('Child'); + const child = new Parse.Object("Child"); child .save() .then(() => { - obj.set('child', child); + obj.set("child", child); return obj.save(); }) .then(() => { const query = new Parse.Query(className); - query.include('child'); + query.include("child"); return query.get(obj.id).then(objAgain => { - expect(objAgain.get('foo')).toEqual('bar'); + expect(objAgain.get("foo")).toEqual("bar"); - const childAgain = objAgain.get('child'); + const childAgain = objAgain.get("child"); expect(childAgain instanceof Parse.Object).toBeTruthy(); - expect(childAgain.get('a')).toEqual('b'); + expect(childAgain.get("a")).toEqual("b"); return Promise.resolve(); }); @@ -622,89 +642,92 @@ describe('miscellaneous', () => { ); }); - it('pointer reassign is working properly (#1288)', done => { - Parse.Cloud.beforeSave('GameScore', req => { + it("pointer reassign is working properly (#1288)", done => { + Parse.Cloud.beforeSave("GameScore", req => { const obj = req.object; - if (obj.get('point')) { + if (obj.get("point")) { return; } - const TestObject1 = Parse.Object.extend('TestObject1'); + const TestObject1 = Parse.Object.extend("TestObject1"); const newObj = new TestObject1({ key1: 1 }); return newObj.save().then(newObj => { - obj.set('point', newObj); + obj.set("point", newObj); }); }); let pointId; - const obj = new Parse.Object('GameScore'); - obj.set('foo', 'bar'); + const obj = new Parse.Object("GameScore"); + obj.set("foo", "bar"); obj .save() .then(() => { - expect(obj.get('point')).not.toBeUndefined(); - pointId = obj.get('point').id; + expect(obj.get("point")).not.toBeUndefined(); + pointId = obj.get("point").id; expect(pointId).not.toBeUndefined(); - obj.set('foo', 'baz'); + obj.set("foo", "baz"); return obj.save(); }) .then(obj => { - expect(obj.get('point').id).toEqual(pointId); + expect(obj.get("point").id).toEqual(pointId); done(); }); }); - it_only_db('mongo')('pointer reassign on nested fields is working properly (#7391)', async () => { - const obj = new Parse.Object('GameScore'); // This object will include nested pointers - const ptr1 = new Parse.Object('GameScore'); - await ptr1.save(); // Obtain a unique id - const ptr2 = new Parse.Object('GameScore'); - await ptr2.save(); // Obtain a unique id - obj.set('data', { ptr: ptr1 }); - await obj.save(); - - obj.set('data.ptr', ptr2); - await obj.save(); - - const obj2 = await new Parse.Query('GameScore').get(obj.id); - expect(obj2.get('data').ptr.id).toBe(ptr2.id); - - const query = new Parse.Query('GameScore'); - query.equalTo('data.ptr', ptr2); - const res = await query.find(); - expect(res.length).toBe(1); - expect(res[0].get('data').ptr.id).toBe(ptr2.id); - }); + it_only_db("mongo")( + "pointer reassign on nested fields is working properly (#7391)", + async () => { + const obj = new Parse.Object("GameScore"); // This object will include nested pointers + const ptr1 = new Parse.Object("GameScore"); + await ptr1.save(); // Obtain a unique id + const ptr2 = new Parse.Object("GameScore"); + await ptr2.save(); // Obtain a unique id + obj.set("data", { ptr: ptr1 }); + await obj.save(); + + obj.set("data.ptr", ptr2); + await obj.save(); + + const obj2 = await new Parse.Query("GameScore").get(obj.id); + expect(obj2.get("data").ptr.id).toBe(ptr2.id); + + const query = new Parse.Query("GameScore"); + query.equalTo("data.ptr", ptr2); + const res = await query.find(); + expect(res.length).toBe(1); + expect(res[0].get("data").ptr.id).toBe(ptr2.id); + } + ); - it('test afterSave get full object on create and update', function (done) { + it("test afterSave get full object on create and update", function (done) { let triggerTime = 0; // Register a mock beforeSave hook - Parse.Cloud.afterSave('GameScore', function (req) { + Parse.Cloud.afterSave("GameScore", function (req) { const object = req.object; expect(object instanceof Parse.Object).toBeTruthy(); expect(object.id).not.toBeUndefined(); expect(object.createdAt).not.toBeUndefined(); expect(object.updatedAt).not.toBeUndefined(); - expect(object.get('fooAgain')).toEqual('barAgain'); + expect(object.get("fooAgain")).toEqual("barAgain"); if (triggerTime == 0) { // Create - expect(object.get('foo')).toEqual('bar'); + expect(object.get("foo")).toEqual("bar"); } else if (triggerTime == 1) { // Update - expect(object.get('foo')).toEqual('baz'); + expect(object.get("foo")).toEqual("baz"); } else { throw new Error(); } triggerTime++; }); - const obj = new Parse.Object('GameScore'); - obj.set('foo', 'bar'); - obj.set('fooAgain', 'barAgain'); + const obj = new Parse.Object("GameScore"); + obj.set("foo", "bar"); + obj.set("fooAgain", "barAgain"); obj .save() .then(function () { // We only update foo - obj.set('foo', 'baz'); + obj.set("foo", "baz"); return obj.save(); }) .then( @@ -720,47 +743,47 @@ describe('miscellaneous', () => { ); }); - it('test afterSave get original object on update', function (done) { + it("test afterSave get original object on update", function (done) { let triggerTime = 0; // Register a mock beforeSave hook - Parse.Cloud.afterSave('GameScore', function (req) { + Parse.Cloud.afterSave("GameScore", function (req) { const object = req.object; expect(object instanceof Parse.Object).toBeTruthy(); - expect(object.get('fooAgain')).toEqual('barAgain'); + expect(object.get("fooAgain")).toEqual("barAgain"); expect(object.id).not.toBeUndefined(); expect(object.createdAt).not.toBeUndefined(); expect(object.updatedAt).not.toBeUndefined(); const originalObject = req.original; if (triggerTime == 0) { // Create - expect(object.get('foo')).toEqual('bar'); + expect(object.get("foo")).toEqual("bar"); // Check the originalObject is undefined expect(originalObject).toBeUndefined(); } else if (triggerTime == 1) { // Update - expect(object.get('foo')).toEqual('baz'); + expect(object.get("foo")).toEqual("baz"); // Check the originalObject expect(originalObject instanceof Parse.Object).toBeTruthy(); - expect(originalObject.get('fooAgain')).toEqual('barAgain'); + expect(originalObject.get("fooAgain")).toEqual("barAgain"); expect(originalObject.id).not.toBeUndefined(); expect(originalObject.createdAt).not.toBeUndefined(); expect(originalObject.updatedAt).not.toBeUndefined(); - expect(originalObject.get('foo')).toEqual('bar'); + expect(originalObject.get("foo")).toEqual("bar"); } else { throw new Error(); } triggerTime++; }); - const obj = new Parse.Object('GameScore'); - obj.set('foo', 'bar'); - obj.set('fooAgain', 'barAgain'); + const obj = new Parse.Object("GameScore"); + obj.set("foo", "bar"); + obj.set("fooAgain", "barAgain"); obj .save() .then(function () { // We only update foo - obj.set('foo', 'baz'); + obj.set("foo", "baz"); return obj.save(); }) .then( @@ -776,33 +799,33 @@ describe('miscellaneous', () => { ); }); - it('test afterSave get full original object even req auth can not query it', done => { + it("test afterSave get full original object even req auth can not query it", done => { let triggerTime = 0; // Register a mock beforeSave hook - Parse.Cloud.afterSave('GameScore', function (req) { + Parse.Cloud.afterSave("GameScore", function (req) { const object = req.object; const originalObject = req.original; if (triggerTime == 0) { // Create } else if (triggerTime == 1) { // Update - expect(object.get('foo')).toEqual('baz'); + expect(object.get("foo")).toEqual("baz"); // Make sure we get the full originalObject expect(originalObject instanceof Parse.Object).toBeTruthy(); - expect(originalObject.get('fooAgain')).toEqual('barAgain'); + expect(originalObject.get("fooAgain")).toEqual("barAgain"); expect(originalObject.id).not.toBeUndefined(); expect(originalObject.createdAt).not.toBeUndefined(); expect(originalObject.updatedAt).not.toBeUndefined(); - expect(originalObject.get('foo')).toEqual('bar'); + expect(originalObject.get("foo")).toEqual("bar"); } else { throw new Error(); } triggerTime++; }); - const obj = new Parse.Object('GameScore'); - obj.set('foo', 'bar'); - obj.set('fooAgain', 'barAgain'); + const obj = new Parse.Object("GameScore"); + obj.set("foo", "bar"); + obj.set("fooAgain", "barAgain"); const acl = new Parse.ACL(); // Make sure our update request can not query the object acl.setPublicReadAccess(false); @@ -812,7 +835,7 @@ describe('miscellaneous', () => { .save() .then(function () { // We only update foo - obj.set('foo', 'baz'); + obj.set("foo", "baz"); return obj.save(); }) .then( @@ -828,33 +851,33 @@ describe('miscellaneous', () => { ); }); - it('afterSave flattens custom operations', done => { + it("afterSave flattens custom operations", done => { let triggerTime = 0; // Register a mock beforeSave hook - Parse.Cloud.afterSave('GameScore', function (req) { + Parse.Cloud.afterSave("GameScore", function (req) { const object = req.object; expect(object instanceof Parse.Object).toBeTruthy(); const originalObject = req.original; if (triggerTime == 0) { // Create - expect(object.get('yolo')).toEqual(1); + expect(object.get("yolo")).toEqual(1); } else if (triggerTime == 1) { // Update - expect(object.get('yolo')).toEqual(2); + expect(object.get("yolo")).toEqual(2); // Check the originalObject - expect(originalObject.get('yolo')).toEqual(1); + expect(originalObject.get("yolo")).toEqual(1); } else { throw new Error(); } triggerTime++; }); - const obj = new Parse.Object('GameScore'); - obj.increment('yolo', 1); + const obj = new Parse.Object("GameScore"); + obj.increment("yolo", 1); obj .save() .then(() => { - obj.increment('yolo', 1); + obj.increment("yolo", 1); return obj.save(); }) .then( @@ -870,10 +893,10 @@ describe('miscellaneous', () => { ); }); - it('beforeSave receives ACL', done => { + it("beforeSave receives ACL", done => { let triggerTime = 0; // Register a mock beforeSave hook - Parse.Cloud.beforeSave('GameScore', function (req) { + Parse.Cloud.beforeSave("GameScore", function (req) { const object = req.object; if (triggerTime == 0) { const acl = object.getACL(); @@ -889,7 +912,7 @@ describe('miscellaneous', () => { triggerTime++; }); - const obj = new Parse.Object('GameScore'); + const obj = new Parse.Object("GameScore"); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); acl.setPublicWriteAccess(true); @@ -914,10 +937,10 @@ describe('miscellaneous', () => { ); }); - it('afterSave receives ACL', done => { + it("afterSave receives ACL", done => { let triggerTime = 0; // Register a mock beforeSave hook - Parse.Cloud.afterSave('GameScore', function (req) { + Parse.Cloud.afterSave("GameScore", function (req) { const object = req.object; if (triggerTime == 0) { const acl = object.getACL(); @@ -933,7 +956,7 @@ describe('miscellaneous', () => { triggerTime++; }); - const obj = new Parse.Object('GameScore'); + const obj = new Parse.Object("GameScore"); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); acl.setPublicWriteAccess(true); @@ -958,275 +981,294 @@ describe('miscellaneous', () => { ); }); - it_id('e9e718a9-4465-4158-b13e-f146855a8892')(it)( - 'return the updated fields on PUT', + it_id("e9e718a9-4465-4158-b13e-f146855a8892")(it)( + "return the updated fields on PUT", async () => { - const obj = new Parse.Object('GameScore'); - const pointer = new Parse.Object('Child'); + const obj = new Parse.Object("GameScore"); + const pointer = new Parse.Object("Child"); await pointer.save(); obj.set( - 'point', + "point", new Parse.GeoPoint({ latitude: 37.4848, longitude: -122.1483, }) ); - obj.set('array', ['obj1', 'obj2']); - obj.set('objects', { a: 'b' }); - obj.set('string', 'abc'); - obj.set('bool', true); - obj.set('number', 1); - obj.set('date', new Date()); - obj.set('pointer', pointer); + obj.set("array", ["obj1", "obj2"]); + obj.set("objects", { a: "b" }); + obj.set("string", "abc"); + obj.set("bool", true); + obj.set("number", 1); + obj.set("date", new Date()); + obj.set("pointer", pointer); const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Installation-Id': 'yolo', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "X-Parse-Installation-Id": "yolo", }; const saveResponse = await request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/classes/GameScore', + url: "http://localhost:8378/1/classes/GameScore", body: JSON.stringify({ - a: 'hello', + a: "hello", c: 1, - d: ['1'], - e: ['1'], - f: ['1', '2'], + d: ["1"], + e: ["1"], + f: ["1", "2"], ...obj.toJSON(), }), }); - expect(Object.keys(saveResponse.data).sort()).toEqual(['createdAt', 'objectId']); + expect(Object.keys(saveResponse.data).sort()).toEqual([ + "createdAt", + "objectId", + ]); obj.id = saveResponse.data.objectId; const response = await request({ - method: 'PUT', + method: "PUT", headers: headers, - url: 'http://localhost:8378/1/classes/GameScore/' + obj.id, + url: "http://localhost:8378/1/classes/GameScore/" + obj.id, body: JSON.stringify({ - a: 'b', - c: { __op: 'Increment', amount: 2 }, - d: { __op: 'Add', objects: ['2'] }, - e: { __op: 'AddUnique', objects: ['1', '2'] }, - f: { __op: 'Remove', objects: ['2'] }, + a: "b", + c: { __op: "Increment", amount: 2 }, + d: { __op: "Add", objects: ["2"] }, + e: { __op: "AddUnique", objects: ["1", "2"] }, + f: { __op: "Remove", objects: ["2"] }, selfThing: { - __type: 'Pointer', - className: 'GameScore', + __type: "Pointer", + className: "GameScore", objectId: obj.id, }, }), }); const body = response.data; - expect(Object.keys(body).sort()).toEqual(['c', 'd', 'e', 'f', 'updatedAt']); + expect(Object.keys(body).sort()).toEqual([ + "c", + "d", + "e", + "f", + "updatedAt", + ]); expect(body.a).toBeUndefined(); expect(body.c).toEqual(3); // 2+1 expect(body.d.length).toBe(2); - expect(body.d.indexOf('1') > -1).toBe(true); - expect(body.d.indexOf('2') > -1).toBe(true); + expect(body.d.indexOf("1") > -1).toBe(true); + expect(body.d.indexOf("2") > -1).toBe(true); expect(body.e.length).toBe(2); - expect(body.e.indexOf('1') > -1).toBe(true); - expect(body.e.indexOf('2') > -1).toBe(true); + expect(body.e.indexOf("1") > -1).toBe(true); + expect(body.e.indexOf("2") > -1).toBe(true); expect(body.f.length).toBe(1); - expect(body.f.indexOf('1') > -1).toBe(true); + expect(body.f.indexOf("1") > -1).toBe(true); expect(body.selfThing).toBeUndefined(); expect(body.updatedAt).not.toBeUndefined(); } ); - it_id('ea358b59-03c0-45c9-abc7-1fdd67573029')(it)( - 'should response should not change with triggers', + it_id("ea358b59-03c0-45c9-abc7-1fdd67573029")(it)( + "should response should not change with triggers", async () => { - const obj = new Parse.Object('GameScore'); - const pointer = new Parse.Object('Child'); - Parse.Cloud.beforeSave('GameScore', request => { + const obj = new Parse.Object("GameScore"); + const pointer = new Parse.Object("Child"); + Parse.Cloud.beforeSave("GameScore", request => { return request.object; }); - Parse.Cloud.afterSave('GameScore', request => { + Parse.Cloud.afterSave("GameScore", request => { return request.object; }); await pointer.save(); obj.set( - 'point', + "point", new Parse.GeoPoint({ latitude: 37.4848, longitude: -122.1483, }) ); - obj.set('array', ['obj1', 'obj2']); - obj.set('objects', { a: 'b' }); - obj.set('string', 'abc'); - obj.set('bool', true); - obj.set('number', 1); - obj.set('date', new Date()); - obj.set('pointer', pointer); + obj.set("array", ["obj1", "obj2"]); + obj.set("objects", { a: "b" }); + obj.set("string", "abc"); + obj.set("bool", true); + obj.set("number", 1); + obj.set("date", new Date()); + obj.set("pointer", pointer); const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Installation-Id': 'yolo', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "X-Parse-Installation-Id": "yolo", }; const saveResponse = await request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/classes/GameScore', + url: "http://localhost:8378/1/classes/GameScore", body: JSON.stringify({ - a: 'hello', + a: "hello", c: 1, - d: ['1'], - e: ['1'], - f: ['1', '2'], + d: ["1"], + e: ["1"], + f: ["1", "2"], ...obj.toJSON(), }), }); - expect(Object.keys(saveResponse.data).sort()).toEqual(['createdAt', 'objectId']); + expect(Object.keys(saveResponse.data).sort()).toEqual([ + "createdAt", + "objectId", + ]); obj.id = saveResponse.data.objectId; const response = await request({ - method: 'PUT', + method: "PUT", headers: headers, - url: 'http://localhost:8378/1/classes/GameScore/' + obj.id, + url: "http://localhost:8378/1/classes/GameScore/" + obj.id, body: JSON.stringify({ - a: 'b', - c: { __op: 'Increment', amount: 2 }, - d: { __op: 'Add', objects: ['2'] }, - e: { __op: 'AddUnique', objects: ['1', '2'] }, - f: { __op: 'Remove', objects: ['2'] }, + a: "b", + c: { __op: "Increment", amount: 2 }, + d: { __op: "Add", objects: ["2"] }, + e: { __op: "AddUnique", objects: ["1", "2"] }, + f: { __op: "Remove", objects: ["2"] }, selfThing: { - __type: 'Pointer', - className: 'GameScore', + __type: "Pointer", + className: "GameScore", objectId: obj.id, }, }), }); const body = response.data; - expect(Object.keys(body).sort()).toEqual(['c', 'd', 'e', 'f', 'updatedAt']); + expect(Object.keys(body).sort()).toEqual([ + "c", + "d", + "e", + "f", + "updatedAt", + ]); expect(body.a).toBeUndefined(); expect(body.c).toEqual(3); // 2+1 expect(body.d.length).toBe(2); - expect(body.d.indexOf('1') > -1).toBe(true); - expect(body.d.indexOf('2') > -1).toBe(true); + expect(body.d.indexOf("1") > -1).toBe(true); + expect(body.d.indexOf("2") > -1).toBe(true); expect(body.e.length).toBe(2); - expect(body.e.indexOf('1') > -1).toBe(true); - expect(body.e.indexOf('2') > -1).toBe(true); + expect(body.e.indexOf("1") > -1).toBe(true); + expect(body.e.indexOf("2") > -1).toBe(true); expect(body.f.length).toBe(1); - expect(body.f.indexOf('1') > -1).toBe(true); + expect(body.f.indexOf("1") > -1).toBe(true); expect(body.selfThing).toBeUndefined(); expect(body.updatedAt).not.toBeUndefined(); } ); - it('test cloud function error handling', done => { + it("test cloud function error handling", done => { // Register a function which will fail - Parse.Cloud.define('willFail', () => { - throw new Error('noway'); + Parse.Cloud.define("willFail", () => { + throw new Error("noway"); }); - Parse.Cloud.run('willFail').then( + Parse.Cloud.run("willFail").then( () => { - fail('Should not have succeeded.'); + fail("Should not have succeeded."); done(); }, e => { expect(e.code).toEqual(Parse.Error.SCRIPT_FAILED); - expect(e.message).toEqual('noway'); + expect(e.message).toEqual("noway"); done(); } ); }); - it('test cloud function error handling with custom error code', done => { + it("test cloud function error handling with custom error code", done => { // Register a function which will fail - Parse.Cloud.define('willFail', () => { - throw new Parse.Error(999, 'noway'); + Parse.Cloud.define("willFail", () => { + throw new Parse.Error(999, "noway"); }); - Parse.Cloud.run('willFail').then( + Parse.Cloud.run("willFail").then( () => { - fail('Should not have succeeded.'); + fail("Should not have succeeded."); done(); }, e => { expect(e.code).toEqual(999); - expect(e.message).toEqual('noway'); + expect(e.message).toEqual("noway"); done(); } ); }); - it('test cloud function error handling with standard error code', done => { + it("test cloud function error handling with standard error code", done => { // Register a function which will fail - Parse.Cloud.define('willFail', () => { - throw new Error('noway'); + Parse.Cloud.define("willFail", () => { + throw new Error("noway"); }); - Parse.Cloud.run('willFail').then( + Parse.Cloud.run("willFail").then( () => { - fail('Should not have succeeded.'); + fail("Should not have succeeded."); done(); }, e => { expect(e.code).toEqual(Parse.Error.SCRIPT_FAILED); - expect(e.message).toEqual('noway'); + expect(e.message).toEqual("noway"); done(); } ); }); - it('test beforeSave/afterSave get installationId', function (done) { + it("test beforeSave/afterSave get installationId", function (done) { let triggerTime = 0; - Parse.Cloud.beforeSave('GameScore', function (req) { + Parse.Cloud.beforeSave("GameScore", function (req) { triggerTime++; expect(triggerTime).toEqual(1); - expect(req.installationId).toEqual('yolo'); + expect(req.installationId).toEqual("yolo"); }); - Parse.Cloud.afterSave('GameScore', function (req) { + Parse.Cloud.afterSave("GameScore", function (req) { triggerTime++; expect(triggerTime).toEqual(2); - expect(req.installationId).toEqual('yolo'); + expect(req.installationId).toEqual("yolo"); }); const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Installation-Id': 'yolo', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "X-Parse-Installation-Id": "yolo", }; request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/classes/GameScore', - body: JSON.stringify({ a: 'b' }), + url: "http://localhost:8378/1/classes/GameScore", + body: JSON.stringify({ a: "b" }), }).then(() => { expect(triggerTime).toEqual(2); done(); }); }); - it('test beforeDelete/afterDelete get installationId', function (done) { + it("test beforeDelete/afterDelete get installationId", function (done) { let triggerTime = 0; - Parse.Cloud.beforeDelete('GameScore', function (req) { + Parse.Cloud.beforeDelete("GameScore", function (req) { triggerTime++; expect(triggerTime).toEqual(1); - expect(req.installationId).toEqual('yolo'); + expect(req.installationId).toEqual("yolo"); }); - Parse.Cloud.afterDelete('GameScore', function (req) { + Parse.Cloud.afterDelete("GameScore", function (req) { triggerTime++; expect(triggerTime).toEqual(2); - expect(req.installationId).toEqual('yolo'); + expect(req.installationId).toEqual("yolo"); }); const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Installation-Id': 'yolo', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "X-Parse-Installation-Id": "yolo", }; request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/classes/GameScore', - body: JSON.stringify({ a: 'b' }), + url: "http://localhost:8378/1/classes/GameScore", + body: JSON.stringify({ a: "b" }), }).then(response => { request({ - method: 'DELETE', + method: "DELETE", headers: headers, - url: 'http://localhost:8378/1/classes/GameScore/' + response.data.objectId, + url: + "http://localhost:8378/1/classes/GameScore/" + response.data.objectId, }).then(() => { expect(triggerTime).toEqual(2); done(); @@ -1234,15 +1276,15 @@ describe('miscellaneous', () => { }); }); - it('test beforeDelete with locked down ACL', async () => { + it("test beforeDelete with locked down ACL", async () => { let called = false; - Parse.Cloud.beforeDelete('GameScore', () => { + Parse.Cloud.beforeDelete("GameScore", () => { called = true; }); - const object = new Parse.Object('GameScore'); + const object = new Parse.Object("GameScore"); object.setACL(new Parse.ACL()); await object.save(); - const objects = await new Parse.Query('GameScore').find(); + const objects = await new Parse.Query("GameScore").find(); expect(objects.length).toBe(0); try { await object.destroy(); @@ -1252,19 +1294,19 @@ describe('miscellaneous', () => { expect(called).toBe(false); }); - it('test cloud function query parameters', done => { - Parse.Cloud.define('echoParams', req => { + it("test cloud function query parameters", done => { + Parse.Cloud.define("echoParams", req => { return req.params; }); const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-Javascript-Key': 'test', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-Javascript-Key": "test", }; request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/functions/echoParams', //?option=1&other=2 + url: "http://localhost:8378/1/functions/echoParams", //?option=1&other=2 qs: { option: 1, other: 2, @@ -1272,219 +1314,221 @@ describe('miscellaneous', () => { body: '{"foo":"bar", "other": 1}', }).then(response => { const res = response.data.result; - expect(res.option).toEqual('1'); + expect(res.option).toEqual("1"); // Make sure query string params override body params - expect(res.other).toEqual('2'); - expect(res.foo).toEqual('bar'); + expect(res.other).toEqual("2"); + expect(res.foo).toEqual("bar"); done(); }); }); - it('test cloud function query parameters with array of pointers', async () => { + it("test cloud function query parameters with array of pointers", async () => { await reconfigureServer({ encodeParseObjectInCloudFunction: false }); - Parse.Cloud.define('echoParams', req => { + Parse.Cloud.define("echoParams", req => { return req.params; }); const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-Javascript-Key': 'test', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-Javascript-Key": "test", }; const response = await request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/functions/echoParams', + url: "http://localhost:8378/1/functions/echoParams", body: '{"arr": [{ "__type": "Pointer", "className": "PointerTest" }]}', }); const res = response.data.result; expect(res.arr.length).toEqual(1); }); - it('can handle null params in cloud functions (regression test for #1742)', done => { - Parse.Cloud.define('func', request => { + it("can handle null params in cloud functions (regression test for #1742)", done => { + Parse.Cloud.define("func", request => { expect(request.params.nullParam).toEqual(null); - return 'yay'; + return "yay"; }); - Parse.Cloud.run('func', { nullParam: null }).then( + Parse.Cloud.run("func", { nullParam: null }).then( () => { done(); }, () => { - fail('cloud code call failed'); + fail("cloud code call failed"); done(); } ); }); - it('can handle date params in cloud functions (#2214)', done => { + it("can handle date params in cloud functions (#2214)", done => { const date = new Date(); - Parse.Cloud.define('dateFunc', request => { - expect(request.params.date.__type).toEqual('Date'); + Parse.Cloud.define("dateFunc", request => { + expect(request.params.date.__type).toEqual("Date"); expect(request.params.date.iso).toEqual(date.toISOString()); - return 'yay'; + return "yay"; }); - Parse.Cloud.run('dateFunc', { date: date }).then( + Parse.Cloud.run("dateFunc", { date: date }).then( () => { done(); }, () => { - fail('cloud code call failed'); + fail("cloud code call failed"); done(); } ); }); - it('fails on invalid client key', done => { + it("fails on invalid client key", done => { const headers = { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - 'X-Parse-Client-Key': 'notclient', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + "X-Parse-Client-Key": "notclient", }; request({ headers: headers, - url: 'http://localhost:8378/1/classes/TestObject', + url: "http://localhost:8378/1/classes/TestObject", }).then(fail, response => { const b = response.data; - expect(b.error).toEqual('unauthorized'); + expect(b.error).toEqual("unauthorized"); done(); }); }); - it('fails on invalid windows key', done => { + it("fails on invalid windows key", done => { const headers = { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - 'X-Parse-Windows-Key': 'notwindows', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + "X-Parse-Windows-Key": "notwindows", }; request({ headers: headers, - url: 'http://localhost:8378/1/classes/TestObject', + url: "http://localhost:8378/1/classes/TestObject", }).then(fail, response => { const b = response.data; - expect(b.error).toEqual('unauthorized'); + expect(b.error).toEqual("unauthorized"); done(); }); }); - it('fails on invalid javascript key', done => { + it("fails on invalid javascript key", done => { const headers = { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - 'X-Parse-Javascript-Key': 'notjavascript', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + "X-Parse-Javascript-Key": "notjavascript", }; request({ headers: headers, - url: 'http://localhost:8378/1/classes/TestObject', + url: "http://localhost:8378/1/classes/TestObject", }).then(fail, response => { const b = response.data; - expect(b.error).toEqual('unauthorized'); + expect(b.error).toEqual("unauthorized"); done(); }); }); - it('fails on invalid rest api key', done => { + it("fails on invalid rest api key", done => { const headers = { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'notrest', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "notrest", }; request({ headers: headers, - url: 'http://localhost:8378/1/classes/TestObject', + url: "http://localhost:8378/1/classes/TestObject", }).then(fail, response => { const b = response.data; - expect(b.error).toEqual('unauthorized'); + expect(b.error).toEqual("unauthorized"); done(); }); }); - it('fails on invalid function', done => { - Parse.Cloud.run('somethingThatDoesDefinitelyNotExist').then( + it("fails on invalid function", done => { + Parse.Cloud.run("somethingThatDoesDefinitelyNotExist").then( () => { - fail('This should have never suceeded'); + fail("This should have never suceeded"); done(); }, e => { expect(e.code).toEqual(Parse.Error.SCRIPT_FAILED); - expect(e.message).toEqual('Invalid function: "somethingThatDoesDefinitelyNotExist"'); + expect(e.message).toEqual( + 'Invalid function: "somethingThatDoesDefinitelyNotExist"' + ); done(); } ); }); - it('dedupes an installation properly and returns updatedAt', done => { + it("dedupes an installation properly and returns updatedAt", done => { const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; const data = { - installationId: 'lkjsahdfkjhsdfkjhsdfkjhsdf', - deviceType: 'embedded', + installationId: "lkjsahdfkjhsdfkjhsdfkjhsdf", + deviceType: "embedded", }; const requestOptions = { headers: headers, - method: 'POST', - url: 'http://localhost:8378/1/installations', + method: "POST", + url: "http://localhost:8378/1/installations", body: JSON.stringify(data), }; request(requestOptions).then(response => { const b = response.data; - expect(typeof b.objectId).toEqual('string'); + expect(typeof b.objectId).toEqual("string"); request(requestOptions).then(response => { const b = response.data; - expect(typeof b.updatedAt).toEqual('string'); + expect(typeof b.updatedAt).toEqual("string"); done(); }); }); }); - it('android login providing empty authData block works', done => { + it("android login providing empty authData block works", done => { const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; const data = { - username: 'pulse1989', - password: 'password1234', + username: "pulse1989", + password: "password1234", authData: {}, }; const requestOptions = { - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/users', + url: "http://localhost:8378/1/users", body: JSON.stringify(data), }; request(requestOptions).then(() => { - requestOptions.url = 'http://localhost:8378/1/login'; + requestOptions.url = "http://localhost:8378/1/login"; request(requestOptions).then(response => { const b = response.data; - expect(typeof b['sessionToken']).toEqual('string'); + expect(typeof b["sessionToken"]).toEqual("string"); done(); }); }); }); - it('gets relation fields', done => { - const object = new Parse.Object('AnObject'); - const relatedObject = new Parse.Object('RelatedObject'); + it("gets relation fields", done => { + const object = new Parse.Object("AnObject"); + const relatedObject = new Parse.Object("RelatedObject"); Parse.Object.saveAll([object, relatedObject]) .then(() => { - object.relation('related').add(relatedObject); + object.relation("related").add(relatedObject); return object.save(); }) .then(() => { const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; const requestOptions = { headers: headers, - url: 'http://localhost:8378/1/classes/AnObject', + url: "http://localhost:8378/1/classes/AnObject", json: true, }; request(requestOptions).then(res => { @@ -1492,8 +1536,8 @@ describe('miscellaneous', () => { expect(body.results.length).toBe(1); const result = body.results[0]; expect(result.related).toEqual({ - __type: 'Relation', - className: 'RelatedObject', + __type: "Relation", + className: "RelatedObject", }); done(); }); @@ -1504,31 +1548,31 @@ describe('miscellaneous', () => { }); }); - it_id('b2cd9cf2-13fa-4acd-aaa9-6f81fc1858db')(it)( - 'properly returns incremented values (#1554)', + it_id("b2cd9cf2-13fa-4acd-aaa9-6f81fc1858db")(it)( + "properly returns incremented values (#1554)", done => { const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; const requestOptions = { headers: headers, - url: 'http://localhost:8378/1/classes/AnObject', + url: "http://localhost:8378/1/classes/AnObject", json: true, }; - const object = new Parse.Object('AnObject'); + const object = new Parse.Object("AnObject"); function runIncrement(amount) { const options = Object.assign({}, requestOptions, { body: { key: { - __op: 'Increment', + __op: "Increment", amount: amount, }, }, - url: 'http://localhost:8378/1/classes/AnObject/' + object.id, - method: 'PUT', + url: "http://localhost:8378/1/classes/AnObject/" + object.id, + method: "PUT", }); return request(options).then(res => res.data); } @@ -1550,20 +1594,20 @@ describe('miscellaneous', () => { ); it('ignores _RevocableSession "header" send by JS SDK', done => { - const object = new Parse.Object('AnObject'); - object.set('a', 'b'); + const object = new Parse.Object("AnObject"); + object.set("a", "b"); object.save().then(() => { request({ - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - url: 'http://localhost:8378/1/classes/AnObject', + method: "POST", + headers: { "Content-Type": "application/json" }, + url: "http://localhost:8378/1/classes/AnObject", body: { - _method: 'GET', - _ApplicationId: 'test', - _JavaScriptKey: 'test', - _ClientVersion: 'js1.8.3', - _InstallationId: 'iid', - _RevocableSession: '1', + _method: "GET", + _ApplicationId: "test", + _JavaScriptKey: "test", + _ClientVersion: "js1.8.3", + _InstallationId: "iid", + _RevocableSession: "1", }, }).then(res => { const body = res.data; @@ -1571,64 +1615,64 @@ describe('miscellaneous', () => { expect(body.results).not.toBeUndefined(); expect(body.results.length).toBe(1); const result = body.results[0]; - expect(result.a).toBe('b'); + expect(result.a).toBe("b"); done(); }); }); }); - it('doesnt convert interior keys of objects that use special names', done => { - const obj = new Parse.Object('Obj'); - obj.set('val', { createdAt: 'a', updatedAt: 1 }); + it("doesnt convert interior keys of objects that use special names", done => { + const obj = new Parse.Object("Obj"); + obj.set("val", { createdAt: "a", updatedAt: 1 }); obj .save() - .then(obj => new Parse.Query('Obj').get(obj.id)) + .then(obj => new Parse.Query("Obj").get(obj.id)) .then(obj => { - expect(obj.get('val').createdAt).toEqual('a'); - expect(obj.get('val').updatedAt).toEqual(1); + expect(obj.get("val").createdAt).toEqual("a"); + expect(obj.get("val").updatedAt).toEqual(1); done(); }); }); - it('bans interior keys containing . or $', done => { - new Parse.Object('Obj') - .save({ innerObj: { 'key with a $': 'fails' } }) + it("bans interior keys containing . or $", done => { + new Parse.Object("Obj") + .save({ innerObj: { "key with a $": "fails" } }) .then( () => { - fail('should not succeed'); + fail("should not succeed"); }, error => { expect(error.code).toEqual(Parse.Error.INVALID_NESTED_KEY); - return new Parse.Object('Obj').save({ - innerObj: { 'key with a .': 'fails' }, + return new Parse.Object("Obj").save({ + innerObj: { "key with a .": "fails" }, }); } ) .then( () => { - fail('should not succeed'); + fail("should not succeed"); }, error => { expect(error.code).toEqual(Parse.Error.INVALID_NESTED_KEY); - return new Parse.Object('Obj').save({ - innerObj: { innerInnerObj: { 'key with $': 'fails' } }, + return new Parse.Object("Obj").save({ + innerObj: { innerInnerObj: { "key with $": "fails" } }, }); } ) .then( () => { - fail('should not succeed'); + fail("should not succeed"); }, error => { expect(error.code).toEqual(Parse.Error.INVALID_NESTED_KEY); - return new Parse.Object('Obj').save({ - innerObj: { innerInnerObj: { 'key with .': 'fails' } }, + return new Parse.Object("Obj").save({ + innerObj: { innerInnerObj: { "key with .": "fails" } }, }); } ) .then( () => { - fail('should not succeed'); + fail("should not succeed"); done(); }, error => { @@ -1638,54 +1682,54 @@ describe('miscellaneous', () => { ); }); - it('does not change inner object keys named _auth_data_something', done => { - new Parse.Object('O') + it("does not change inner object keys named _auth_data_something", done => { + new Parse.Object("O") .save({ innerObj: { _auth_data_facebook: 7 } }) - .then(object => new Parse.Query('O').get(object.id)) + .then(object => new Parse.Query("O").get(object.id)) .then(object => { - expect(object.get('innerObj')).toEqual({ _auth_data_facebook: 7 }); + expect(object.get("innerObj")).toEqual({ _auth_data_facebook: 7 }); done(); }); }); - it('does not change inner object key names _p_somethign', done => { - new Parse.Object('O') + it("does not change inner object key names _p_somethign", done => { + new Parse.Object("O") .save({ innerObj: { _p_data: 7 } }) - .then(object => new Parse.Query('O').get(object.id)) + .then(object => new Parse.Query("O").get(object.id)) .then(object => { - expect(object.get('innerObj')).toEqual({ _p_data: 7 }); + expect(object.get("innerObj")).toEqual({ _p_data: 7 }); done(); }); }); - it('does not change inner object key names _rperm, _wperm', done => { - new Parse.Object('O') + it("does not change inner object key names _rperm, _wperm", done => { + new Parse.Object("O") .save({ innerObj: { _rperm: 7, _wperm: 8 } }) - .then(object => new Parse.Query('O').get(object.id)) + .then(object => new Parse.Query("O").get(object.id)) .then(object => { - expect(object.get('innerObj')).toEqual({ _rperm: 7, _wperm: 8 }); + expect(object.get("innerObj")).toEqual({ _rperm: 7, _wperm: 8 }); done(); }); }); - it('does not change inner objects if the key has the same name as a geopoint field on the class, and the value is an array of length 2, or if the key has the same name as a file field on the class, and the value is a string', done => { - const file = new Parse.File('myfile.txt', { base64: 'eAo=' }); + it("does not change inner objects if the key has the same name as a geopoint field on the class, and the value is an array of length 2, or if the key has the same name as a file field on the class, and the value is a string", done => { + const file = new Parse.File("myfile.txt", { base64: "eAo=" }); file .save() .then(f => { - const obj = new Parse.Object('O'); - obj.set('fileField', f); - obj.set('geoField', new Parse.GeoPoint(0, 0)); - obj.set('innerObj', { - fileField: 'data', + const obj = new Parse.Object("O"); + obj.set("fileField", f); + obj.set("geoField", new Parse.GeoPoint(0, 0)); + obj.set("innerObj", { + fileField: "data", geoField: [1, 2], }); return obj.save(); }) .then(object => object.fetch()) .then(object => { - expect(object.get('innerObj')).toEqual({ - fileField: 'data', + expect(object.get("innerObj")).toEqual({ + fileField: "data", geoField: [1, 2], }); done(); @@ -1696,62 +1740,67 @@ describe('miscellaneous', () => { }); }); - it_id('8f99ee20-3da7-45ec-b867-ea0eb87524a9')(it)('purge all objects in class', done => { - const object = new Parse.Object('TestObject'); - object.set('foo', 'bar'); - const object2 = new Parse.Object('TestObject'); - object2.set('alice', 'wonderland'); - Parse.Object.saveAll([object, object2]) - .then(() => { - const query = new Parse.Query(TestObject); - return query.count(); - }) - .then(count => { - expect(count).toBe(2); - const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key': 'test', - }; - request({ - method: 'DELETE', - headers: headers, - url: 'http://localhost:8378/1/purge/TestObject', - }).then(() => { + it_id("8f99ee20-3da7-45ec-b867-ea0eb87524a9")(it)( + "purge all objects in class", + done => { + const object = new Parse.Object("TestObject"); + object.set("foo", "bar"); + const object2 = new Parse.Object("TestObject"); + object2.set("alice", "wonderland"); + Parse.Object.saveAll([object, object2]) + .then(() => { const query = new Parse.Query(TestObject); - return query.count().then(count => { - expect(count).toBe(0); - done(); + return query.count(); + }) + .then(count => { + expect(count).toBe(2); + const headers = { + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-Master-Key": "test", + }; + request({ + method: "DELETE", + headers: headers, + url: "http://localhost:8378/1/purge/TestObject", + }).then(() => { + const query = new Parse.Query(TestObject); + return query.count().then(count => { + expect(count).toBe(0); + done(); + }); }); }); - }); - }); + } + ); - it('fail on purge all objects in class without master key', done => { + it("fail on purge all objects in class without master key", done => { const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; request({ - method: 'DELETE', + method: "DELETE", headers: headers, - url: 'http://localhost:8378/1/purge/TestObject', + url: "http://localhost:8378/1/purge/TestObject", }) .then(() => { - fail('Should not succeed'); + fail("Should not succeed"); }) .catch(response => { - expect(response.data.error).toEqual('unauthorized: master key is required'); + expect(response.data.error).toEqual( + "unauthorized: master key is required" + ); done(); }); }); - it('purge all objects in _Role also purge cache', done => { + it("purge all objects in _Role also purge cache", done => { const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key': 'test', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-Master-Key": "test", }; let user, object; createTestUser() @@ -1760,53 +1809,53 @@ describe('miscellaneous', () => { const acl = new Parse.ACL(); acl.setPublicReadAccess(true); acl.setPublicWriteAccess(false); - const role = new Parse.Object('_Role'); - role.set('name', 'TestRole'); + const role = new Parse.Object("_Role"); + role.set("name", "TestRole"); role.setACL(acl); - const users = role.relation('users'); + const users = role.relation("users"); users.add(user); return role.save({}, { useMasterKey: true }); }) .then(() => { - const query = new Parse.Query('_Role'); + const query = new Parse.Query("_Role"); return query.find({ useMasterKey: true }); }) .then(x => { expect(x.length).toEqual(1); - const relation = x[0].relation('users').query(); + const relation = x[0].relation("users").query(); return relation.first({ useMasterKey: true }); }) .then(x => { expect(x.id).toEqual(user.id); - object = new Parse.Object('TestObject'); + object = new Parse.Object("TestObject"); const acl = new Parse.ACL(); acl.setPublicReadAccess(false); acl.setPublicWriteAccess(false); - acl.setRoleReadAccess('TestRole', true); - acl.setRoleWriteAccess('TestRole', true); + acl.setRoleReadAccess("TestRole", true); + acl.setRoleWriteAccess("TestRole", true); object.setACL(acl); return object.save(); }) .then(() => { - const query = new Parse.Query('TestObject'); + const query = new Parse.Query("TestObject"); return query.find({ sessionToken: user.getSessionToken() }); }) .then(x => { expect(x.length).toEqual(1); return request({ - method: 'DELETE', + method: "DELETE", headers: headers, - url: 'http://localhost:8378/1/purge/_Role', + url: "http://localhost:8378/1/purge/_Role", json: true, }); }) .then(() => { - const query = new Parse.Query('TestObject'); + const query = new Parse.Query("TestObject"); return query.get(object.id, { sessionToken: user.getSessionToken() }); }) .then( () => { - fail('Should not succeed'); + fail("Should not succeed"); }, e => { expect(e.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); @@ -1815,24 +1864,24 @@ describe('miscellaneous', () => { ); }); - it('purge empty class', done => { - const testSchema = new Parse.Schema('UnknownClass'); + it("purge empty class", done => { + const testSchema = new Parse.Schema("UnknownClass"); testSchema.purge().then(done).catch(done.fail); }); - it('should not update schema beforeSave #2672', done => { - Parse.Cloud.beforeSave('MyObject', request => { - if (request.object.get('secret')) { - throw 'cannot set secret here'; + it("should not update schema beforeSave #2672", done => { + Parse.Cloud.beforeSave("MyObject", request => { + if (request.object.get("secret")) { + throw "cannot set secret here"; } }); - const object = new Parse.Object('MyObject'); - object.set('key', 'value'); + const object = new Parse.Object("MyObject"); + object.set("key", "value"); object .save() .then(() => { - return object.save({ secret: 'should not update schema' }); + return object.save({ secret: "should not update schema" }); }) .then( () => { @@ -1841,12 +1890,12 @@ describe('miscellaneous', () => { }, () => { return request({ - method: 'GET', + method: "GET", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-Master-Key": "test", }, - url: 'http://localhost:8378/1/schemas/MyObject', + url: "http://localhost:8378/1/schemas/MyObject", json: true, }); } @@ -1865,32 +1914,34 @@ describe('miscellaneous', () => { }); }); -describe_only_db('mongo')('legacy _acl', () => { - it('should have _acl when locking down (regression for #2465)', done => { +describe_only_db("mongo")("legacy _acl", () => { + it("should have _acl when locking down (regression for #2465)", done => { const headers = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }; request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/classes/Report', + url: "http://localhost:8378/1/classes/Report", body: { ACL: {}, - name: 'My Report', + name: "My Report", }, json: true, }) .then(() => { - const config = Config.get('test'); + const config = Config.get("test"); const adapter = config.database.adapter; - return adapter._adaptiveCollection('Report').then(collection => collection.find({})); + return adapter + ._adaptiveCollection("Report") + .then(collection => collection.find({})); }) .then(results => { expect(results.length).toBe(1); const result = results[0]; - expect(result.name).toEqual('My Report'); + expect(result.name).toEqual("My Report"); expect(result._wperm).toEqual([]); expect(result._rperm).toEqual([]); expect(result._acl).toEqual({}); diff --git a/spec/ParseCloudCodePublisher.spec.js b/spec/ParseCloudCodePublisher.spec.js index ef00960687..21f0ebadaf 100644 --- a/spec/ParseCloudCodePublisher.spec.js +++ b/spec/ParseCloudCodePublisher.spec.js @@ -1,76 +1,80 @@ const ParseCloudCodePublisher = - require('../lib/LiveQuery/ParseCloudCodePublisher').ParseCloudCodePublisher; -const Parse = require('parse/node'); + require("../lib/LiveQuery/ParseCloudCodePublisher").ParseCloudCodePublisher; +const Parse = require("parse/node"); -describe('ParseCloudCodePublisher', function () { +describe("ParseCloudCodePublisher", function () { beforeEach(function (done) { // Mock ParsePubSub const mockParsePubSub = { - createPublisher: jasmine.createSpy('publish').and.returnValue({ - publish: jasmine.createSpy('publish'), - on: jasmine.createSpy('on'), + createPublisher: jasmine.createSpy("publish").and.returnValue({ + publish: jasmine.createSpy("publish"), + on: jasmine.createSpy("on"), }), - createSubscriber: jasmine.createSpy('publish').and.returnValue({ - subscribe: jasmine.createSpy('subscribe'), - on: jasmine.createSpy('on'), + createSubscriber: jasmine.createSpy("publish").and.returnValue({ + subscribe: jasmine.createSpy("subscribe"), + on: jasmine.createSpy("on"), }), }; - jasmine.mockLibrary('../lib/LiveQuery/ParsePubSub', 'ParsePubSub', mockParsePubSub); + jasmine.mockLibrary( + "../lib/LiveQuery/ParsePubSub", + "ParsePubSub", + mockParsePubSub + ); done(); }); - it('can initialize', function () { + it("can initialize", function () { const config = {}; new ParseCloudCodePublisher(config); - const ParsePubSub = require('../lib/LiveQuery/ParsePubSub').ParsePubSub; + const ParsePubSub = require("../lib/LiveQuery/ParsePubSub").ParsePubSub; expect(ParsePubSub.createPublisher).toHaveBeenCalledWith(config); }); - it('can handle cloud code afterSave request', function () { + it("can handle cloud code afterSave request", function () { const publisher = new ParseCloudCodePublisher({}); - publisher._onCloudCodeMessage = jasmine.createSpy('onCloudCodeMessage'); + publisher._onCloudCodeMessage = jasmine.createSpy("onCloudCodeMessage"); const request = {}; publisher.onCloudCodeAfterSave(request); expect(publisher._onCloudCodeMessage).toHaveBeenCalledWith( - Parse.applicationId + 'afterSave', + Parse.applicationId + "afterSave", request ); }); - it('can handle cloud code afterDelete request', function () { + it("can handle cloud code afterDelete request", function () { const publisher = new ParseCloudCodePublisher({}); - publisher._onCloudCodeMessage = jasmine.createSpy('onCloudCodeMessage'); + publisher._onCloudCodeMessage = jasmine.createSpy("onCloudCodeMessage"); const request = {}; publisher.onCloudCodeAfterDelete(request); expect(publisher._onCloudCodeMessage).toHaveBeenCalledWith( - Parse.applicationId + 'afterDelete', + Parse.applicationId + "afterDelete", request ); }); - it('can handle cloud code request', function () { + it("can handle cloud code request", function () { const publisher = new ParseCloudCodePublisher({}); - const currentParseObject = new Parse.Object('Test'); - currentParseObject.set('key', 'value'); - const originalParseObject = new Parse.Object('Test'); - originalParseObject.set('key', 'originalValue'); + const currentParseObject = new Parse.Object("Test"); + currentParseObject.set("key", "value"); + const originalParseObject = new Parse.Object("Test"); + originalParseObject.set("key", "originalValue"); const request = { object: currentParseObject, original: originalParseObject, }; - publisher._onCloudCodeMessage('afterSave', request); + publisher._onCloudCodeMessage("afterSave", request); const args = publisher.parsePublisher.publish.calls.mostRecent().args; - expect(args[0]).toBe('afterSave'); + expect(args[0]).toBe("afterSave"); const message = JSON.parse(args[1]); expect(message.currentParseObject).toEqual(request.object._toFullJSON()); expect(message.originalParseObject).toEqual(request.original._toFullJSON()); }); afterEach(function () { - jasmine.restoreLibrary('../lib/LiveQuery/ParsePubSub', 'ParsePubSub'); + jasmine.restoreLibrary("../lib/LiveQuery/ParsePubSub", "ParsePubSub"); }); }); diff --git a/spec/ParseConfigKey.spec.js b/spec/ParseConfigKey.spec.js index a171032087..dea37c1dee 100644 --- a/spec/ParseConfigKey.spec.js +++ b/spec/ParseConfigKey.spec.js @@ -1,89 +1,120 @@ -const Config = require('../lib/Config'); +const Config = require("../lib/Config"); -describe('Config Keys', () => { - const invalidKeyErrorMessage = 'Invalid key\\(s\\) found in Parse Server configuration'; +describe("Config Keys", () => { + const invalidKeyErrorMessage = + "Invalid key\\(s\\) found in Parse Server configuration"; let loggerErrorSpy; beforeEach(async () => { - const logger = require('../lib/logger').logger; - loggerErrorSpy = spyOn(logger, 'error').and.callThrough(); - spyOn(Config, 'validateOptions').and.callFake(() => {}); + const logger = require("../lib/logger").logger; + loggerErrorSpy = spyOn(logger, "error").and.callThrough(); + spyOn(Config, "validateOptions").and.callFake(() => {}); }); - it('recognizes invalid keys in root', async () => { - await expectAsync(reconfigureServer({ - invalidKey: 1, - })).toBeResolved(); - const error = loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], ''); + it("recognizes invalid keys in root", async () => { + await expectAsync( + reconfigureServer({ + invalidKey: 1, + }) + ).toBeResolved(); + const error = loggerErrorSpy.calls + .all() + .reduce((s, call) => (s += call.args[0]), ""); expect(error).toMatch(invalidKeyErrorMessage); }); - it('recognizes invalid keys in pages.customUrls', async () => { - await expectAsync(reconfigureServer({ - pages: { - customUrls: { - invalidKey: 1, - EmailVerificationSendFail: 1, - } - } - })).toBeResolved(); - const error = loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], ''); + it("recognizes invalid keys in pages.customUrls", async () => { + await expectAsync( + reconfigureServer({ + pages: { + customUrls: { + invalidKey: 1, + EmailVerificationSendFail: 1, + }, + }, + }) + ).toBeResolved(); + const error = loggerErrorSpy.calls + .all() + .reduce((s, call) => (s += call.args[0]), ""); expect(error).toMatch(invalidKeyErrorMessage); expect(error).toMatch(`invalidKey`); expect(error).toMatch(`EmailVerificationSendFail`); }); - it('recognizes invalid keys in liveQueryServerOptions', async () => { - await expectAsync(reconfigureServer({ - liveQueryServerOptions: { - invalidKey: 1, - MasterKey: 1, - } - })).toBeResolved(); - const error = loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], ''); + it("recognizes invalid keys in liveQueryServerOptions", async () => { + await expectAsync( + reconfigureServer({ + liveQueryServerOptions: { + invalidKey: 1, + MasterKey: 1, + }, + }) + ).toBeResolved(); + const error = loggerErrorSpy.calls + .all() + .reduce((s, call) => (s += call.args[0]), ""); expect(error).toMatch(invalidKeyErrorMessage); expect(error).toMatch(`MasterKey`); }); - it('recognizes invalid keys in rateLimit', async () => { - await expectAsync(reconfigureServer({ - rateLimit: [ - { invalidKey: 1 }, - { RequestPath: 1 }, - { RequestTimeWindow: 1 }, - ] - })).toBeRejected(); - const error = loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], ''); + it("recognizes invalid keys in rateLimit", async () => { + await expectAsync( + reconfigureServer({ + rateLimit: [ + { invalidKey: 1 }, + { RequestPath: 1 }, + { RequestTimeWindow: 1 }, + ], + }) + ).toBeRejected(); + const error = loggerErrorSpy.calls + .all() + .reduce((s, call) => (s += call.args[0]), ""); expect(error).toMatch(invalidKeyErrorMessage); - expect(error).toMatch('rateLimit\\[0\\]\\.invalidKey'); - expect(error).toMatch('rateLimit\\[1\\]\\.RequestPath'); - expect(error).toMatch('rateLimit\\[2\\]\\.RequestTimeWindow'); + expect(error).toMatch("rateLimit\\[0\\]\\.invalidKey"); + expect(error).toMatch("rateLimit\\[1\\]\\.RequestPath"); + expect(error).toMatch("rateLimit\\[2\\]\\.RequestTimeWindow"); }); - it_only_db('mongo')('recognizes valid keys in default configuration', async () => { - await expectAsync(reconfigureServer({ - ...defaultConfiguration, - })).toBeResolved(); - expect(loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], '')).not.toMatch(invalidKeyErrorMessage); - }); + it_only_db("mongo")( + "recognizes valid keys in default configuration", + async () => { + await expectAsync( + reconfigureServer({ + ...defaultConfiguration, + }) + ).toBeResolved(); + expect( + loggerErrorSpy.calls.all().reduce((s, call) => (s += call.args[0]), "") + ).not.toMatch(invalidKeyErrorMessage); + } + ); - it_only_db('mongo')('recognizes valid keys in databaseOptions (MongoDB)', async () => { - await expectAsync(reconfigureServer({ - databaseURI: 'mongodb://localhost:27017/parse', - filesAdapter: null, - databaseAdapter: null, - databaseOptions: { - retryWrites: true, - maxTimeMS: 1000, - maxStalenessSeconds: 10, - maxPoolSize: 10, - minPoolSize: 5, - connectTimeoutMS: 5000, - socketTimeoutMS: 5000, - autoSelectFamily: true, - autoSelectFamilyAttemptTimeout: 3000 - }, - })).toBeResolved(); - expect(loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], '')).not.toMatch(invalidKeyErrorMessage); - }); + it_only_db("mongo")( + "recognizes valid keys in databaseOptions (MongoDB)", + async () => { + await expectAsync( + reconfigureServer({ + databaseURI: "mongodb://localhost:27017/parse", + filesAdapter: null, + databaseAdapter: null, + databaseOptions: { + retryWrites: true, + maxTimeMS: 1000, + maxStalenessSeconds: 10, + maxPoolSize: 10, + minPoolSize: 5, + connectTimeoutMS: 5000, + socketTimeoutMS: 5000, + autoSelectFamily: true, + autoSelectFamilyAttemptTimeout: 3000, + }, + }) + ).toBeResolved(); + expect( + loggerErrorSpy.calls.all().reduce((s, call) => (s += call.args[0]), "") + ).not.toMatch(invalidKeyErrorMessage); + } + ); }); diff --git a/spec/ParseFile.spec.js b/spec/ParseFile.spec.js index 6be506be8d..4c4628358e 100644 --- a/spec/ParseFile.spec.js +++ b/spec/ParseFile.spec.js @@ -1,125 +1,133 @@ // This is a port of the test suite: // hungry/js/test/parse_file_test.js -'use strict'; +"use strict"; -const { FilesController } = require('../lib/Controllers/FilesController'); -const request = require('../lib/request'); +const { FilesController } = require("../lib/Controllers/FilesController"); +const request = require("../lib/request"); -const str = 'Hello World!'; +const str = "Hello World!"; const data = []; for (let i = 0; i < str.length; i++) { data.push(str.charCodeAt(i)); } -describe('Parse.File testing', () => { - describe('creating files', () => { - it('works with Content-Type', done => { +describe("Parse.File testing", () => { + describe("creating files", () => { + it("works with Content-Type", done => { const headers = { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/files/file.txt', - body: 'argle bargle', + url: "http://localhost:8378/1/files/file.txt", + body: "argle bargle", }).then(response => { const b = response.data; expect(b.name).toMatch(/_file.txt$/); - expect(b.url).toMatch(/^http:\/\/localhost:8378\/1\/files\/test\/.*file.txt$/); + expect(b.url).toMatch( + /^http:\/\/localhost:8378\/1\/files\/test\/.*file.txt$/ + ); request({ url: b.url }).then(response => { const body = response.text; - expect(body).toEqual('argle bargle'); + expect(body).toEqual("argle bargle"); done(); }); }); }); - it('works with _ContentType', async () => { + it("works with _ContentType", async () => { await reconfigureServer({ fileUpload: { enableForPublic: true, - fileExtensions: ['*'], + fileExtensions: ["*"], }, }); let response = await request({ - method: 'POST', - url: 'http://localhost:8378/1/files/file', + method: "POST", + url: "http://localhost:8378/1/files/file", body: JSON.stringify({ - _ApplicationId: 'test', - _JavaScriptKey: 'test', - _ContentType: 'text/html', - base64: 'PGh0bWw+PC9odG1sPgo=', + _ApplicationId: "test", + _JavaScriptKey: "test", + _ContentType: "text/html", + base64: "PGh0bWw+PC9odG1sPgo=", }), }); const b = response.data; expect(b.name).toMatch(/_file.html/); - expect(b.url).toMatch(/^http:\/\/localhost:8378\/1\/files\/test\/.*file.html$/); + expect(b.url).toMatch( + /^http:\/\/localhost:8378\/1\/files\/test\/.*file.html$/ + ); response = await request({ url: b.url }); const body = response.text; try { - expect(response.headers['content-type']).toMatch('^text/html'); - expect(body).toEqual('\n'); + expect(response.headers["content-type"]).toMatch("^text/html"); + expect(body).toEqual("\n"); } catch (e) { jfail(e); } }); - it('works without Content-Type', done => { + it("works without Content-Type", done => { const headers = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/files/file.txt', - body: 'argle bargle', + url: "http://localhost:8378/1/files/file.txt", + body: "argle bargle", }).then(response => { const b = response.data; expect(b.name).toMatch(/_file.txt$/); - expect(b.url).toMatch(/^http:\/\/localhost:8378\/1\/files\/test\/.*file.txt$/); + expect(b.url).toMatch( + /^http:\/\/localhost:8378\/1\/files\/test\/.*file.txt$/ + ); request({ url: b.url }).then(response => { - expect(response.text).toEqual('argle bargle'); + expect(response.text).toEqual("argle bargle"); done(); }); }); }); - it('supports REST end-to-end file create, read, delete, read', done => { + it("supports REST end-to-end file create, read, delete, read", done => { const headers = { - 'Content-Type': 'image/jpeg', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "image/jpeg", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/files/testfile.txt', - body: 'check one two', + url: "http://localhost:8378/1/files/testfile.txt", + body: "check one two", }).then(response => { const b = response.data; expect(b.name).toMatch(/_testfile.txt$/); - expect(b.url).toMatch(/^http:\/\/localhost:8378\/1\/files\/test\/.*testfile.txt$/); + expect(b.url).toMatch( + /^http:\/\/localhost:8378\/1\/files\/test\/.*testfile.txt$/ + ); request({ url: b.url }).then(response => { const body = response.text; - expect(body).toEqual('check one two'); + expect(body).toEqual("check one two"); request({ - method: 'DELETE', + method: "DELETE", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Master-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "X-Parse-Master-Key": "test", }, - url: 'http://localhost:8378/1/files/' + b.name, + url: "http://localhost:8378/1/files/" + b.name, }).then(response => { expect(response.status).toEqual(200); request({ headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }, url: b.url, }).then(fail, response => { @@ -131,41 +139,43 @@ describe('Parse.File testing', () => { }); }); - it('blocks file deletions with missing or incorrect master-key header', done => { + it("blocks file deletions with missing or incorrect master-key header", done => { const headers = { - 'Content-Type': 'image/jpeg', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "image/jpeg", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/files/thefile.jpg', - body: 'the file body', + url: "http://localhost:8378/1/files/thefile.jpg", + body: "the file body", }).then(response => { const b = response.data; - expect(b.url).toMatch(/^http:\/\/localhost:8378\/1\/files\/test\/.*thefile.jpg$/); + expect(b.url).toMatch( + /^http:\/\/localhost:8378\/1\/files\/test\/.*thefile.jpg$/ + ); // missing X-Parse-Master-Key header request({ - method: 'DELETE', + method: "DELETE", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }, - url: 'http://localhost:8378/1/files/' + b.name, + url: "http://localhost:8378/1/files/" + b.name, }).then(fail, response => { const del_b = response.data; expect(response.status).toEqual(403); expect(del_b.error).toMatch(/unauthorized/); // incorrect X-Parse-Master-Key header request({ - method: 'DELETE', + method: "DELETE", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Master-Key': 'tryagain', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "X-Parse-Master-Key": "tryagain", }, - url: 'http://localhost:8378/1/files/' + b.name, + url: "http://localhost:8378/1/files/" + b.name, }).then(fail, response => { const del_b2 = response.data; expect(response.status).toEqual(403); @@ -176,43 +186,45 @@ describe('Parse.File testing', () => { }); }); - it('handles other filetypes', done => { + it("handles other filetypes", done => { const headers = { - 'Content-Type': 'image/jpeg', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "image/jpeg", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/files/file.jpg', - body: 'argle bargle', + url: "http://localhost:8378/1/files/file.jpg", + body: "argle bargle", }).then(response => { const b = response.data; expect(b.name).toMatch(/_file.jpg$/); - expect(b.url).toMatch(/^http:\/\/localhost:8378\/1\/files\/.*file.jpg$/); + expect(b.url).toMatch( + /^http:\/\/localhost:8378\/1\/files\/.*file.jpg$/ + ); request({ url: b.url }).then(response => { const body = response.text; - expect(body).toEqual('argle bargle'); + expect(body).toEqual("argle bargle"); done(); }); }); }); - it('save file', async () => { - const file = new Parse.File('hello.txt', data, 'text/plain'); + it("save file", async () => { + const file = new Parse.File("hello.txt", data, "text/plain"); ok(!file.url()); const result = await file.save(); strictEqual(result, file); ok(file.name()); ok(file.url()); - notEqual(file.name(), 'hello.txt'); + notEqual(file.name(), "hello.txt"); }); - it('saves the file with tags', async () => { - spyOn(FilesController.prototype, 'createFile').and.callThrough(); - const file = new Parse.File('hello.txt', data, 'text/plain'); - const tags = { hello: 'world' }; + it("saves the file with tags", async () => { + spyOn(FilesController.prototype, "createFile").and.callThrough(); + const file = new Parse.File("hello.txt", data, "text/plain"); + const tags = { hello: "world" }; file.setTags(tags); expect(file.url()).toBeUndefined(); const result = await file.save(); @@ -225,9 +237,9 @@ describe('Parse.File testing', () => { }); }); - it('does not pass empty file tags while saving', async () => { - spyOn(FilesController.prototype, 'createFile').and.callThrough(); - const file = new Parse.File('hello.txt', data, 'text/plain'); + it("does not pass empty file tags while saving", async () => { + spyOn(FilesController.prototype, "createFile").and.callThrough(); + const file = new Parse.File("hello.txt", data, "text/plain"); expect(file.url()).toBeUndefined(); expect(file.name()).toBeDefined(); await file.save(); @@ -237,90 +249,90 @@ describe('Parse.File testing', () => { }); }); - it('save file in object', async done => { - const file = new Parse.File('hello.txt', data, 'text/plain'); + it("save file in object", async done => { + const file = new Parse.File("hello.txt", data, "text/plain"); ok(!file.url()); const result = await file.save(); strictEqual(result, file); ok(file.name()); ok(file.url()); - notEqual(file.name(), 'hello.txt'); + notEqual(file.name(), "hello.txt"); - const object = new Parse.Object('TestObject'); + const object = new Parse.Object("TestObject"); await object.save({ file: file }); - const objectAgain = await new Parse.Query('TestObject').get(object.id); - ok(objectAgain.get('file') instanceof Parse.File); + const objectAgain = await new Parse.Query("TestObject").get(object.id); + ok(objectAgain.get("file") instanceof Parse.File); done(); }); - it('save file in object with escaped characters in filename', async () => { - const file = new Parse.File('hello . txt', data, 'text/plain'); + it("save file in object with escaped characters in filename", async () => { + const file = new Parse.File("hello . txt", data, "text/plain"); ok(!file.url()); const result = await file.save(); strictEqual(result, file); ok(file.name()); ok(file.url()); - notEqual(file.name(), 'hello . txt'); + notEqual(file.name(), "hello . txt"); - const object = new Parse.Object('TestObject'); + const object = new Parse.Object("TestObject"); await object.save({ file }); - const objectAgain = await new Parse.Query('TestObject').get(object.id); - ok(objectAgain.get('file') instanceof Parse.File); + const objectAgain = await new Parse.Query("TestObject").get(object.id); + ok(objectAgain.get("file") instanceof Parse.File); }); - it('autosave file in object', async done => { - let file = new Parse.File('hello.txt', data, 'text/plain'); + it("autosave file in object", async done => { + let file = new Parse.File("hello.txt", data, "text/plain"); ok(!file.url()); - const object = new Parse.Object('TestObject'); + const object = new Parse.Object("TestObject"); await object.save({ file }); - const objectAgain = await new Parse.Query('TestObject').get(object.id); - file = objectAgain.get('file'); + const objectAgain = await new Parse.Query("TestObject").get(object.id); + file = objectAgain.get("file"); ok(file instanceof Parse.File); ok(file.name()); ok(file.url()); - notEqual(file.name(), 'hello.txt'); + notEqual(file.name(), "hello.txt"); done(); }); - it('autosave file in object in object', async done => { - let file = new Parse.File('hello.txt', data, 'text/plain'); + it("autosave file in object in object", async done => { + let file = new Parse.File("hello.txt", data, "text/plain"); ok(!file.url()); - const child = new Parse.Object('Child'); - child.set('file', file); + const child = new Parse.Object("Child"); + child.set("file", file); - const parent = new Parse.Object('Parent'); - parent.set('child', child); + const parent = new Parse.Object("Parent"); + parent.set("child", child); await parent.save(); - const query = new Parse.Query('Parent'); - query.include('child'); + const query = new Parse.Query("Parent"); + query.include("child"); const parentAgain = await query.get(parent.id); - const childAgain = parentAgain.get('child'); - file = childAgain.get('file'); + const childAgain = parentAgain.get("child"); + file = childAgain.get("file"); ok(file instanceof Parse.File); ok(file.name()); ok(file.url()); - notEqual(file.name(), 'hello.txt'); + notEqual(file.name(), "hello.txt"); done(); }); - it('saving an already saved file', async () => { - const file = new Parse.File('hello.txt', data, 'text/plain'); + it("saving an already saved file", async () => { + const file = new Parse.File("hello.txt", data, "text/plain"); ok(!file.url()); const result = await file.save(); strictEqual(result, file); ok(file.name()); ok(file.url()); - notEqual(file.name(), 'hello.txt'); + notEqual(file.name(), "hello.txt"); const previousName = file.name(); await file.save(); equal(file.name(), previousName); }); - it('two saves at the same time', done => { - const file = new Parse.File('hello.txt', data, 'text/plain'); + it("two saves at the same time", done => { + const file = new Parse.File("hello.txt", data, "text/plain"); let firstName; let secondName; @@ -344,71 +356,71 @@ describe('Parse.File testing', () => { ); }); - it('file toJSON testing', async () => { - const file = new Parse.File('hello.txt', data, 'text/plain'); + it("file toJSON testing", async () => { + const file = new Parse.File("hello.txt", data, "text/plain"); ok(!file.url()); - const object = new Parse.Object('TestObject'); + const object = new Parse.Object("TestObject"); await object.save({ file: file, }); ok(object.toJSON().file.url); }); - it('content-type used with no extension', async () => { + it("content-type used with no extension", async () => { await reconfigureServer({ fileUpload: { enableForPublic: true, - fileExtensions: ['*'], + fileExtensions: ["*"], }, }); const headers = { - 'Content-Type': 'text/html', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "text/html", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; let response = await request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/files/file', - body: 'fee fi fo', + url: "http://localhost:8378/1/files/file", + body: "fee fi fo", }); const b = response.data; expect(b.name).toMatch(/\.html$/); response = await request({ url: b.url }); - expect(response.headers['content-type']).toMatch(/^text\/html/); + expect(response.headers["content-type"]).toMatch(/^text\/html/); }); - it('works without Content-Type and extension', async () => { + it("works without Content-Type and extension", async () => { await reconfigureServer({ fileUpload: { enableForPublic: true, }, }); const headers = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; const result = await request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/files/file', - body: '\n', + url: "http://localhost:8378/1/files/file", + body: "\n", }); - expect(result.data.url.includes('file.txt')).toBeTrue(); - expect(result.data.name.includes('file.txt')).toBeTrue(); + expect(result.data.url.includes("file.txt")).toBeTrue(); + expect(result.data.name.includes("file.txt")).toBeTrue(); }); - it('filename is url encoded', done => { + it("filename is url encoded", done => { const headers = { - 'Content-Type': 'text/html', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "text/html", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/files/hello world.txt', - body: 'oh emm gee', + url: "http://localhost:8378/1/files/hello world.txt", + body: "oh emm gee", }).then(response => { const b = response.data; expect(b.url).toMatch(/hello%20world/); @@ -416,41 +428,41 @@ describe('Parse.File testing', () => { }); }); - it('supports array of files', done => { + it("supports array of files", done => { const file = { - __type: 'File', - url: 'http://meep.meep', - name: 'meep', + __type: "File", + url: "http://meep.meep", + name: "meep", }; const files = [file, file]; - const obj = new Parse.Object('FilesArrayTest'); - obj.set('files', files); + const obj = new Parse.Object("FilesArrayTest"); + obj.set("files", files); obj .save() .then(() => { - const query = new Parse.Query('FilesArrayTest'); + const query = new Parse.Query("FilesArrayTest"); return query.first(); }) .then(result => { - const filesAgain = result.get('files'); + const filesAgain = result.get("files"); expect(filesAgain.length).toEqual(2); - expect(filesAgain[0].name()).toEqual('meep'); - expect(filesAgain[0].url()).toEqual('http://meep.meep'); + expect(filesAgain[0].name()).toEqual("meep"); + expect(filesAgain[0].url()).toEqual("http://meep.meep"); done(); }); }); - it('validates filename characters', done => { + it("validates filename characters", done => { const headers = { - 'Content-Type': 'text/plain', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "text/plain", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/files/di$avowed.txt', - body: 'will fail', + url: "http://localhost:8378/1/files/di$avowed.txt", + body: "will fail", }).then(fail, response => { const b = response.data; expect(b.code).toEqual(122); @@ -458,22 +470,22 @@ describe('Parse.File testing', () => { }); }); - it('validates filename length', done => { + it("validates filename length", done => { const headers = { - 'Content-Type': 'text/plain', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "text/plain", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; const fileName = - 'Onceuponamidnightdrearywhileiponderedweak' + - 'andwearyOveramanyquaintandcuriousvolumeof' + - 'forgottenloreWhileinoddednearlynappingsud' + - 'denlytherecameatapping'; + "Onceuponamidnightdrearywhileiponderedweak" + + "andwearyOveramanyquaintandcuriousvolumeof" + + "forgottenloreWhileinoddednearlynappingsud" + + "denlytherecameatapping"; request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/files/' + fileName, - body: 'will fail', + url: "http://localhost:8378/1/files/" + fileName, + body: "will fail", }).then(fail, response => { const b = response.data; expect(b.code).toEqual(122); @@ -481,29 +493,29 @@ describe('Parse.File testing', () => { }); }); - it('supports a dictionary with file', done => { + it("supports a dictionary with file", done => { const file = { - __type: 'File', - url: 'http://meep.meep', - name: 'meep', + __type: "File", + url: "http://meep.meep", + name: "meep", }; const dict = { file: file, }; - const obj = new Parse.Object('FileObjTest'); - obj.set('obj', dict); + const obj = new Parse.Object("FileObjTest"); + obj.set("obj", dict); obj .save() .then(() => { - const query = new Parse.Query('FileObjTest'); + const query = new Parse.Query("FileObjTest"); return query.first(); }) .then(result => { - const dictAgain = result.get('obj'); - expect(typeof dictAgain).toEqual('object'); - const fileAgain = dictAgain['file']; - expect(fileAgain.name()).toEqual('meep'); - expect(fileAgain.url()).toEqual('http://meep.meep'); + const dictAgain = result.get("obj"); + expect(typeof dictAgain).toEqual("object"); + const fileAgain = dictAgain["file"]; + expect(fileAgain.name()).toEqual("meep"); + expect(fileAgain.url()).toEqual("http://meep.meep"); done(); }) .catch(e => { @@ -512,23 +524,25 @@ describe('Parse.File testing', () => { }); }); - it('creates correct url for old files hosted on files.parsetfss.com', done => { + it("creates correct url for old files hosted on files.parsetfss.com", done => { const file = { - __type: 'File', - url: 'http://irrelevant.elephant/', - name: 'tfss-123.txt', + __type: "File", + url: "http://irrelevant.elephant/", + name: "tfss-123.txt", }; - const obj = new Parse.Object('OldFileTest'); - obj.set('oldfile', file); + const obj = new Parse.Object("OldFileTest"); + obj.set("oldfile", file); obj .save() .then(() => { - const query = new Parse.Query('OldFileTest'); + const query = new Parse.Query("OldFileTest"); return query.first(); }) .then(result => { - const fileAgain = result.get('oldfile'); - expect(fileAgain.url()).toEqual('http://files.parsetfss.com/test/tfss-123.txt'); + const fileAgain = result.get("oldfile"); + expect(fileAgain.url()).toEqual( + "http://files.parsetfss.com/test/tfss-123.txt" + ); done(); }) .catch(e => { @@ -537,24 +551,24 @@ describe('Parse.File testing', () => { }); }); - it('creates correct url for old files hosted on files.parse.com', done => { + it("creates correct url for old files hosted on files.parse.com", done => { const file = { - __type: 'File', - url: 'http://irrelevant.elephant/', - name: 'd6e80979-a128-4c57-a167-302f874700dc-123.txt', + __type: "File", + url: "http://irrelevant.elephant/", + name: "d6e80979-a128-4c57-a167-302f874700dc-123.txt", }; - const obj = new Parse.Object('OldFileTest'); - obj.set('oldfile', file); + const obj = new Parse.Object("OldFileTest"); + obj.set("oldfile", file); obj .save() .then(() => { - const query = new Parse.Query('OldFileTest'); + const query = new Parse.Query("OldFileTest"); return query.first(); }) .then(result => { - const fileAgain = result.get('oldfile'); + const fileAgain = result.get("oldfile"); expect(fileAgain.url()).toEqual( - 'http://files.parse.com/test/d6e80979-a128-4c57-a167-302f874700dc-123.txt' + "http://files.parse.com/test/d6e80979-a128-4c57-a167-302f874700dc-123.txt" ); done(); }) @@ -564,21 +578,21 @@ describe('Parse.File testing', () => { }); }); - it('supports files in objects without urls', done => { + it("supports files in objects without urls", done => { const file = { - __type: 'File', - name: '123.txt', + __type: "File", + name: "123.txt", }; - const obj = new Parse.Object('FileTest'); - obj.set('file', file); + const obj = new Parse.Object("FileTest"); + obj.set("file", file); obj .save() .then(() => { - const query = new Parse.Query('FileTest'); + const query = new Parse.Query("FileTest"); return query.first(); }) .then(result => { - const fileAgain = result.get('file'); + const fileAgain = result.get("file"); expect(fileAgain.url()).toMatch(/123.txt$/); done(); }) @@ -588,26 +602,26 @@ describe('Parse.File testing', () => { }); }); - it('return with publicServerURL when provided', done => { + it("return with publicServerURL when provided", done => { reconfigureServer({ - publicServerURL: 'https://mydomain/parse', + publicServerURL: "https://mydomain/parse", }) .then(() => { const file = { - __type: 'File', - name: '123.txt', + __type: "File", + name: "123.txt", }; - const obj = new Parse.Object('FileTest'); - obj.set('file', file); + const obj = new Parse.Object("FileTest"); + obj.set("file", file); return obj.save(); }) .then(() => { - const query = new Parse.Query('FileTest'); + const query = new Parse.Query("FileTest"); return query.first(); }) .then(result => { - const fileAgain = result.get('file'); - expect(fileAgain.url().indexOf('https://mydomain/parse')).toBe(0); + const fileAgain = result.get("file"); + expect(fileAgain.url().indexOf("https://mydomain/parse")).toBe(0); done(); }) .catch(e => { @@ -616,17 +630,17 @@ describe('Parse.File testing', () => { }); }); - it('fails to upload an empty file', done => { + it("fails to upload an empty file", done => { const headers = { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/files/file.txt', - body: '', + url: "http://localhost:8378/1/files/file.txt", + body: "", }).then(fail, response => { expect(response.status).toBe(400); const body = response.text; @@ -635,17 +649,17 @@ describe('Parse.File testing', () => { }); }); - it('fails to upload without a file name', done => { + it("fails to upload without a file name", done => { const headers = { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/files/', - body: 'yolo', + url: "http://localhost:8378/1/files/", + body: "yolo", }).then(fail, response => { expect(response.status).toBe(400); const body = response.text; @@ -655,355 +669,358 @@ describe('Parse.File testing', () => { }); }); - describe('deleting files', () => { - it('fails to delete an unkown file', done => { + describe("deleting files", () => { + it("fails to delete an unkown file", done => { const headers = { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Master-Key': 'test', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "X-Parse-Master-Key": "test", }; request({ - method: 'DELETE', + method: "DELETE", headers: headers, - url: 'http://localhost:8378/1/files/file.txt', + url: "http://localhost:8378/1/files/file.txt", }).then(fail, response => { expect(response.status).toBe(400); const body = response.text; - expect(typeof body).toBe('string'); + expect(typeof body).toBe("string"); const { code, error } = JSON.parse(body); expect(code).toBe(153); - expect(typeof error).toBe('string'); + expect(typeof error).toBe("string"); expect(error.length).toBeGreaterThan(0); done(); }); }); }); - describe('getting files', () => { - it('does not crash on file request with invalid app ID', async () => { + describe("getting files", () => { + it("does not crash on file request with invalid app ID", async () => { const res1 = await request({ - url: 'http://localhost:8378/1/files/invalid-id/invalid-file.txt', + url: "http://localhost:8378/1/files/invalid-id/invalid-file.txt", }).catch(e => e); expect(res1.status).toBe(403); - expect(res1.data).toEqual({ code: 119, error: 'Invalid application ID.' }); + expect(res1.data).toEqual({ + code: 119, + error: "Invalid application ID.", + }); // Ensure server did not crash - const res2 = await request({ url: 'http://localhost:8378/1/health' }); + const res2 = await request({ url: "http://localhost:8378/1/health" }); expect(res2.status).toEqual(200); - expect(res2.data).toEqual({ status: 'ok' }); + expect(res2.data).toEqual({ status: "ok" }); }); - it('does not crash on file request with invalid path', async () => { + it("does not crash on file request with invalid path", async () => { const res1 = await request({ - url: 'http://localhost:8378/1/files/invalid-id//invalid-path/%20/invalid-file.txt', + url: "http://localhost:8378/1/files/invalid-id//invalid-path/%20/invalid-file.txt", }).catch(e => e); expect(res1.status).toBe(403); - expect(res1.data).toEqual({ error: 'unauthorized' }); + expect(res1.data).toEqual({ error: "unauthorized" }); // Ensure server did not crash - const res2 = await request({ url: 'http://localhost:8378/1/health' }); + const res2 = await request({ url: "http://localhost:8378/1/health" }); expect(res2.status).toEqual(200); - expect(res2.data).toEqual({ status: 'ok' }); + expect(res2.data).toEqual({ status: "ok" }); }); - it('does not crash on file metadata request with invalid app ID', async () => { + it("does not crash on file metadata request with invalid app ID", async () => { const res1 = await request({ url: `http://localhost:8378/1/files/invalid-id/metadata/invalid-file.txt`, }); expect(res1.status).toBe(200); expect(res1.data).toEqual({}); // Ensure server did not crash - const res2 = await request({ url: 'http://localhost:8378/1/health' }); + const res2 = await request({ url: "http://localhost:8378/1/health" }); expect(res2.status).toEqual(200); - expect(res2.data).toEqual({ status: 'ok' }); + expect(res2.data).toEqual({ status: "ok" }); }); }); - describe_only_db('mongo')('Gridstore Range', () => { - it('supports bytes range out of range', async () => { + describe_only_db("mongo")("Gridstore Range", () => { + it("supports bytes range out of range", async () => { const headers = { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; const response = await request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1//files/file.txt ', - body: repeat('argle bargle', 100), + url: "http://localhost:8378/1//files/file.txt ", + body: repeat("argle bargle", 100), }); const b = response.data; const file = await request({ url: b.url, headers: { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - Range: 'bytes=15000-18000', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + Range: "bytes=15000-18000", }, }); - expect(file.headers['content-range']).toBe('bytes 1212-1212/1212'); + expect(file.headers["content-range"]).toBe("bytes 1212-1212/1212"); }); - it('supports bytes range if end greater than start', async () => { + it("supports bytes range if end greater than start", async () => { const headers = { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; const response = await request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1//files/file.txt ', - body: repeat('argle bargle', 100), + url: "http://localhost:8378/1//files/file.txt ", + body: repeat("argle bargle", 100), }); const b = response.data; const file = await request({ url: b.url, headers: { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - Range: 'bytes=15000-100', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + Range: "bytes=15000-100", }, }); - expect(file.headers['content-range']).toBe('bytes 100-1212/1212'); + expect(file.headers["content-range"]).toBe("bytes 100-1212/1212"); }); - it('supports bytes range if end is undefined', async () => { + it("supports bytes range if end is undefined", async () => { const headers = { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; const response = await request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1//files/file.txt ', - body: repeat('argle bargle', 100), + url: "http://localhost:8378/1//files/file.txt ", + body: repeat("argle bargle", 100), }); const b = response.data; const file = await request({ url: b.url, headers: { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - Range: 'bytes=100-', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + Range: "bytes=100-", }, }); - expect(file.headers['content-range']).toBe('bytes 100-1212/1212'); + expect(file.headers["content-range"]).toBe("bytes 100-1212/1212"); }); - it('supports bytes range if start and end undefined', async () => { + it("supports bytes range if start and end undefined", async () => { const headers = { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; const response = await request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1//files/file.txt ', - body: repeat('argle bargle', 100), + url: "http://localhost:8378/1//files/file.txt ", + body: repeat("argle bargle", 100), }); const b = response.data; const file = await request({ url: b.url, headers: { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", }, }).catch(e => e); - expect(file.headers['content-range']).toBeUndefined(); + expect(file.headers["content-range"]).toBeUndefined(); }); - it('supports bytes range if end is greater than size', async () => { + it("supports bytes range if end is greater than size", async () => { const headers = { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; const response = await request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1//files/file.txt ', - body: repeat('argle bargle', 100), + url: "http://localhost:8378/1//files/file.txt ", + body: repeat("argle bargle", 100), }); const b = response.data; const file = await request({ url: b.url, headers: { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - Range: 'bytes=0-2000', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + Range: "bytes=0-2000", }, }).catch(e => e); - expect(file.headers['content-range']).toBe('bytes 0-1212/1212'); + expect(file.headers["content-range"]).toBe("bytes 0-1212/1212"); }); - it('supports bytes range with 0 length', async () => { + it("supports bytes range with 0 length", async () => { const headers = { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; const response = await request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1//files/file.txt ', - body: 'a', + url: "http://localhost:8378/1//files/file.txt ", + body: "a", }).catch(e => e); const b = response.data; const file = await request({ url: b.url, headers: { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - Range: 'bytes=-2000', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + Range: "bytes=-2000", }, }).catch(e => e); - expect(file.headers['content-range']).toBe('bytes 0-1/1'); + expect(file.headers["content-range"]).toBe("bytes 0-1/1"); }); - it('supports range requests', done => { + it("supports range requests", done => { const headers = { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/files/file.txt', - body: 'argle bargle', + url: "http://localhost:8378/1/files/file.txt", + body: "argle bargle", }).then(response => { const b = response.data; request({ url: b.url, headers: { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - Range: 'bytes=0-5', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + Range: "bytes=0-5", }, }).then(response => { const body = response.text; - expect(body).toEqual('argle '); + expect(body).toEqual("argle "); done(); }); }); }); - it('supports small range requests', done => { + it("supports small range requests", done => { const headers = { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/files/file.txt', - body: 'argle bargle', + url: "http://localhost:8378/1/files/file.txt", + body: "argle bargle", }).then(response => { const b = response.data; request({ url: b.url, headers: { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - Range: 'bytes=0-2', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + Range: "bytes=0-2", }, }).then(response => { const body = response.text; - expect(body).toEqual('arg'); + expect(body).toEqual("arg"); done(); }); }); }); // See specs https://www.greenbytes.de/tech/webdav/draft-ietf-httpbis-p5-range-latest.html#byte.ranges - it('supports getting one byte', done => { + it("supports getting one byte", done => { const headers = { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/files/file.txt', - body: 'argle bargle', + url: "http://localhost:8378/1/files/file.txt", + body: "argle bargle", }).then(response => { const b = response.data; request({ url: b.url, headers: { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - Range: 'bytes=2-2', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + Range: "bytes=2-2", }, }).then(response => { const body = response.text; - expect(body).toEqual('g'); + expect(body).toEqual("g"); done(); }); }); }); - it('supports getting last n bytes', done => { + it("supports getting last n bytes", done => { const headers = { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/files/file.txt', - body: 'something different', + url: "http://localhost:8378/1/files/file.txt", + body: "something different", }).then(response => { const b = response.data; request({ url: b.url, headers: { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - Range: 'bytes=-4', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + Range: "bytes=-4", }, }).then(response => { const body = response.text; expect(body.length).toBe(4); - expect(body).toEqual('rent'); + expect(body).toEqual("rent"); done(); }); }); }); - it('supports getting first n bytes', done => { + it("supports getting first n bytes", done => { const headers = { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/files/file.txt', - body: 'something different', + url: "http://localhost:8378/1/files/file.txt", + body: "something different", }).then(response => { const b = response.data; request({ url: b.url, headers: { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - Range: 'bytes=10-', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + Range: "bytes=10-", }, }).then(response => { const body = response.text; - expect(body).toEqual('different'); + expect(body).toEqual("different"); done(); }); }); @@ -1018,106 +1035,116 @@ describe('Parse.File testing', () => { return s; } - it('supports large range requests', done => { + it("supports large range requests", done => { const headers = { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/files/file.txt', - body: repeat('argle bargle', 100), + url: "http://localhost:8378/1/files/file.txt", + body: repeat("argle bargle", 100), }).then(response => { const b = response.data; request({ url: b.url, headers: { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - Range: 'bytes=13-240', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + Range: "bytes=13-240", }, }).then(response => { const body = response.text; expect(body.length).toEqual(228); - expect(body.indexOf('rgle barglea')).toBe(0); + expect(body.indexOf("rgle barglea")).toBe(0); done(); }); }); }); - it('fails to stream unknown file', async () => { + it("fails to stream unknown file", async () => { const response = await request({ - url: 'http://localhost:8378/1/files/test/file.txt', + url: "http://localhost:8378/1/files/test/file.txt", headers: { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - Range: 'bytes=13-240', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + Range: "bytes=13-240", }, }).catch(e => e); expect(response.status).toBe(404); const body = response.text; - expect(body).toEqual('File not found.'); + expect(body).toEqual("File not found."); }); }); // Because GridStore is not loaded on PG, those are perfect // for fallback tests - describe_only_db('postgres')('Default Range tests', () => { - it('fallback to regular request', async done => { + describe_only_db("postgres")("Default Range tests", () => { + it("fallback to regular request", async done => { await reconfigureServer(); const headers = { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/files/file.txt', - body: 'argle bargle', + url: "http://localhost:8378/1/files/file.txt", + body: "argle bargle", }).then(response => { const b = response.data; request({ url: b.url, headers: { - 'Content-Type': 'application/octet-stream', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - Range: 'bytes=0-5', + "Content-Type": "application/octet-stream", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + Range: "bytes=0-5", }, }).then(response => { const body = response.text; - expect(body).toEqual('argle bargle'); + expect(body).toEqual("argle bargle"); done(); }); }); }); }); - describe('file upload configuration', () => { - it('allows file upload only for authenticated user by default', async () => { + describe("file upload configuration", () => { + it("allows file upload only for authenticated user by default", async () => { await reconfigureServer({ fileUpload: {}, }); - let file = new Parse.File('hello.txt', data, 'text/plain'); + let file = new Parse.File("hello.txt", data, "text/plain"); await expectAsync(file.save()).toBeRejectedWith( - new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'File upload by public is disabled.') + new Parse.Error( + Parse.Error.FILE_SAVE_ERROR, + "File upload by public is disabled." + ) ); - file = new Parse.File('hello.txt', data, 'text/plain'); + file = new Parse.File("hello.txt", data, "text/plain"); const anonUser = await Parse.AnonymousUtils.logIn(); - await expectAsync(file.save({ sessionToken: anonUser.getSessionToken() })).toBeRejectedWith( - new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'File upload by anonymous user is disabled.') + await expectAsync( + file.save({ sessionToken: anonUser.getSessionToken() }) + ).toBeRejectedWith( + new Parse.Error( + Parse.Error.FILE_SAVE_ERROR, + "File upload by anonymous user is disabled." + ) ); - file = new Parse.File('hello.txt', data, 'text/plain'); - const authUser = await Parse.User.signUp('user', 'password'); - await expectAsync(file.save({ sessionToken: authUser.getSessionToken() })).toBeResolved(); + file = new Parse.File("hello.txt", data, "text/plain"); + const authUser = await Parse.User.signUp("user", "password"); + await expectAsync( + file.save({ sessionToken: authUser.getSessionToken() }) + ).toBeResolved(); }); - it('allows file upload with master key', async () => { + it("allows file upload with master key", async () => { await reconfigureServer({ fileUpload: { enableForPublic: false, @@ -1125,11 +1152,11 @@ describe('Parse.File testing', () => { enableForAuthenticatedUser: false, }, }); - const file = new Parse.File('hello.txt', data, 'text/plain'); + const file = new Parse.File("hello.txt", data, "text/plain"); await expectAsync(file.save({ useMasterKey: true })).toBeResolved(); }); - it('rejects all file uploads', async () => { + it("rejects all file uploads", async () => { await reconfigureServer({ fileUpload: { enableForPublic: false, @@ -1137,26 +1164,36 @@ describe('Parse.File testing', () => { enableForAuthenticatedUser: false, }, }); - let file = new Parse.File('hello.txt', data, 'text/plain'); + let file = new Parse.File("hello.txt", data, "text/plain"); await expectAsync(file.save()).toBeRejectedWith( - new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'File upload by public is disabled.') + new Parse.Error( + Parse.Error.FILE_SAVE_ERROR, + "File upload by public is disabled." + ) ); - file = new Parse.File('hello.txt', data, 'text/plain'); + file = new Parse.File("hello.txt", data, "text/plain"); const anonUser = await Parse.AnonymousUtils.logIn(); - await expectAsync(file.save({ sessionToken: anonUser.getSessionToken() })).toBeRejectedWith( - new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'File upload by anonymous user is disabled.') + await expectAsync( + file.save({ sessionToken: anonUser.getSessionToken() }) + ).toBeRejectedWith( + new Parse.Error( + Parse.Error.FILE_SAVE_ERROR, + "File upload by anonymous user is disabled." + ) ); - file = new Parse.File('hello.txt', data, 'text/plain'); - const authUser = await Parse.User.signUp('user', 'password'); - await expectAsync(file.save({ sessionToken: authUser.getSessionToken() })).toBeRejectedWith( + file = new Parse.File("hello.txt", data, "text/plain"); + const authUser = await Parse.User.signUp("user", "password"); + await expectAsync( + file.save({ sessionToken: authUser.getSessionToken() }) + ).toBeRejectedWith( new Parse.Error( Parse.Error.FILE_SAVE_ERROR, - 'File upload by authenticated user is disabled.' + "File upload by authenticated user is disabled." ) ); }); - it('allows all file uploads', async () => { + it("allows all file uploads", async () => { await reconfigureServer({ fileUpload: { enableForPublic: true, @@ -1164,17 +1201,21 @@ describe('Parse.File testing', () => { enableForAuthenticatedUser: true, }, }); - let file = new Parse.File('hello.txt', data, 'text/plain'); + let file = new Parse.File("hello.txt", data, "text/plain"); await expectAsync(file.save()).toBeResolved(); - file = new Parse.File('hello.txt', data, 'text/plain'); + file = new Parse.File("hello.txt", data, "text/plain"); const anonUser = await Parse.AnonymousUtils.logIn(); - await expectAsync(file.save({ sessionToken: anonUser.getSessionToken() })).toBeResolved(); - file = new Parse.File('hello.txt', data, 'text/plain'); - const authUser = await Parse.User.signUp('user', 'password'); - await expectAsync(file.save({ sessionToken: authUser.getSessionToken() })).toBeResolved(); + await expectAsync( + file.save({ sessionToken: anonUser.getSessionToken() }) + ).toBeResolved(); + file = new Parse.File("hello.txt", data, "text/plain"); + const authUser = await Parse.User.signUp("user", "password"); + await expectAsync( + file.save({ sessionToken: authUser.getSessionToken() }) + ).toBeResolved(); }); - it('allows file upload only for public', async () => { + it("allows file upload only for public", async () => { await reconfigureServer({ fileUpload: { enableForPublic: true, @@ -1182,24 +1223,31 @@ describe('Parse.File testing', () => { enableForAuthenticatedUser: false, }, }); - let file = new Parse.File('hello.txt', data, 'text/plain'); + let file = new Parse.File("hello.txt", data, "text/plain"); await expectAsync(file.save()).toBeResolved(); - file = new Parse.File('hello.txt', data, 'text/plain'); + file = new Parse.File("hello.txt", data, "text/plain"); const anonUser = await Parse.AnonymousUtils.logIn(); - await expectAsync(file.save({ sessionToken: anonUser.getSessionToken() })).toBeRejectedWith( - new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'File upload by anonymous user is disabled.') + await expectAsync( + file.save({ sessionToken: anonUser.getSessionToken() }) + ).toBeRejectedWith( + new Parse.Error( + Parse.Error.FILE_SAVE_ERROR, + "File upload by anonymous user is disabled." + ) ); - file = new Parse.File('hello.txt', data, 'text/plain'); - const authUser = await Parse.User.signUp('user', 'password'); - await expectAsync(file.save({ sessionToken: authUser.getSessionToken() })).toBeRejectedWith( + file = new Parse.File("hello.txt", data, "text/plain"); + const authUser = await Parse.User.signUp("user", "password"); + await expectAsync( + file.save({ sessionToken: authUser.getSessionToken() }) + ).toBeRejectedWith( new Parse.Error( Parse.Error.FILE_SAVE_ERROR, - 'File upload by authenticated user is disabled.' + "File upload by authenticated user is disabled." ) ); }); - it('allows file upload only for anonymous user', async () => { + it("allows file upload only for anonymous user", async () => { await reconfigureServer({ fileUpload: { enableForPublic: false, @@ -1207,24 +1255,31 @@ describe('Parse.File testing', () => { enableForAuthenticatedUser: false, }, }); - let file = new Parse.File('hello.txt', data, 'text/plain'); + let file = new Parse.File("hello.txt", data, "text/plain"); await expectAsync(file.save()).toBeRejectedWith( - new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'File upload by public is disabled.') + new Parse.Error( + Parse.Error.FILE_SAVE_ERROR, + "File upload by public is disabled." + ) ); - file = new Parse.File('hello.txt', data, 'text/plain'); + file = new Parse.File("hello.txt", data, "text/plain"); const anonUser = await Parse.AnonymousUtils.logIn(); - await expectAsync(file.save({ sessionToken: anonUser.getSessionToken() })).toBeResolved(); - file = new Parse.File('hello.txt', data, 'text/plain'); - const authUser = await Parse.User.signUp('user', 'password'); - await expectAsync(file.save({ sessionToken: authUser.getSessionToken() })).toBeRejectedWith( + await expectAsync( + file.save({ sessionToken: anonUser.getSessionToken() }) + ).toBeResolved(); + file = new Parse.File("hello.txt", data, "text/plain"); + const authUser = await Parse.User.signUp("user", "password"); + await expectAsync( + file.save({ sessionToken: authUser.getSessionToken() }) + ).toBeRejectedWith( new Parse.Error( Parse.Error.FILE_SAVE_ERROR, - 'File upload by authenticated user is disabled.' + "File upload by authenticated user is disabled." ) ); }); - it('allows file upload only for authenticated user', async () => { + it("allows file upload only for authenticated user", async () => { await reconfigureServer({ fileUpload: { enableForPublic: false, @@ -1232,35 +1287,49 @@ describe('Parse.File testing', () => { enableForAuthenticatedUser: true, }, }); - let file = new Parse.File('hello.txt', data, 'text/plain'); + let file = new Parse.File("hello.txt", data, "text/plain"); await expectAsync(file.save()).toBeRejectedWith( - new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'File upload by public is disabled.') + new Parse.Error( + Parse.Error.FILE_SAVE_ERROR, + "File upload by public is disabled." + ) ); - file = new Parse.File('hello.txt', data, 'text/plain'); + file = new Parse.File("hello.txt", data, "text/plain"); const anonUser = await Parse.AnonymousUtils.logIn(); - await expectAsync(file.save({ sessionToken: anonUser.getSessionToken() })).toBeRejectedWith( - new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'File upload by anonymous user is disabled.') + await expectAsync( + file.save({ sessionToken: anonUser.getSessionToken() }) + ).toBeRejectedWith( + new Parse.Error( + Parse.Error.FILE_SAVE_ERROR, + "File upload by anonymous user is disabled." + ) ); - file = new Parse.File('hello.txt', data, 'text/plain'); - const authUser = await Parse.User.signUp('user', 'password'); - await expectAsync(file.save({ sessionToken: authUser.getSessionToken() })).toBeResolved(); + file = new Parse.File("hello.txt", data, "text/plain"); + const authUser = await Parse.User.signUp("user", "password"); + await expectAsync( + file.save({ sessionToken: authUser.getSessionToken() }) + ).toBeResolved(); }); - it('rejects invalid fileUpload configuration', async () => { + it("rejects invalid fileUpload configuration", async () => { const invalidConfigs = [ { fileUpload: undefined }, { fileUpload: null }, { fileUpload: [] }, { fileUpload: 1 }, - { fileUpload: 'string' }, + { fileUpload: "string" }, ]; const validConfigs = [{ fileUpload: {} }]; - const keys = ['enableForPublic', 'enableForAnonymousUser', 'enableForAuthenticatedUser']; - const invalidValues = [[], {}, 1, 'string', null]; + const keys = [ + "enableForPublic", + "enableForAnonymousUser", + "enableForAuthenticatedUser", + ]; + const invalidValues = [[], {}, 1, "string", null]; const validValues = [undefined, true, false]; for (const config of invalidConfigs) { await expectAsync(reconfigureServer(config)).toBeRejectedWith( - 'fileUpload must be an object value.' + "fileUpload must be an object value." ); } for (const config of validConfigs) { @@ -1268,12 +1337,14 @@ describe('Parse.File testing', () => { } for (const key of keys) { for (const value of invalidValues) { - await expectAsync(reconfigureServer({ fileUpload: { [key]: value } })).toBeRejectedWith( - `fileUpload.${key} must be a boolean value.` - ); + await expectAsync( + reconfigureServer({ fileUpload: { [key]: value } }) + ).toBeRejectedWith(`fileUpload.${key} must be a boolean value.`); } for (const value of validValues) { - await expectAsync(reconfigureServer({ fileUpload: { [key]: value } })).toBeResolved(); + await expectAsync( + reconfigureServer({ fileUpload: { [key]: value } }) + ).toBeResolved(); } } await expectAsync( @@ -1282,161 +1353,179 @@ describe('Parse.File testing', () => { fileExtensions: 1, }, }) - ).toBeRejectedWith('fileUpload.fileExtensions must be an array.'); + ).toBeRejectedWith("fileUpload.fileExtensions must be an array."); }); }); - describe('fileExtensions', () => { - it('works with _ContentType', async () => { + describe("fileExtensions", () => { + it("works with _ContentType", async () => { await reconfigureServer({ fileUpload: { enableForPublic: true, - fileExtensions: ['png'], + fileExtensions: ["png"], }, }); await expectAsync( request({ - method: 'POST', - url: 'http://localhost:8378/1/files/file', + method: "POST", + url: "http://localhost:8378/1/files/file", body: JSON.stringify({ - _ApplicationId: 'test', - _JavaScriptKey: 'test', - _ContentType: 'text/html', - base64: 'PGh0bWw+PC9odG1sPgo=', + _ApplicationId: "test", + _JavaScriptKey: "test", + _ContentType: "text/html", + base64: "PGh0bWw+PC9odG1sPgo=", }), }).catch(e => { throw new Error(e.data.error); }) ).toBeRejectedWith( - new Parse.Error(Parse.Error.FILE_SAVE_ERROR, `File upload of extension html is disabled.`) + new Parse.Error( + Parse.Error.FILE_SAVE_ERROR, + `File upload of extension html is disabled.` + ) ); }); - it('works without Content-Type', async () => { + it("works without Content-Type", async () => { await reconfigureServer({ fileUpload: { enableForPublic: true, }, }); const headers = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; await expectAsync( request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/files/file.html', - body: '\n', + url: "http://localhost:8378/1/files/file.html", + body: "\n", }).catch(e => { throw new Error(e.data.error); }) ).toBeRejectedWith( - new Parse.Error(Parse.Error.FILE_SAVE_ERROR, `File upload of extension html is disabled.`) + new Parse.Error( + Parse.Error.FILE_SAVE_ERROR, + `File upload of extension html is disabled.` + ) ); }); - it('default should allow common types', async () => { + it("default should allow common types", async () => { await reconfigureServer({ fileUpload: { enableForPublic: true, }, }); - for (const type of ['plain', 'txt', 'png', 'jpg', 'gif', 'doc']) { - const file = new Parse.File(`parse-server-logo.${type}`, { base64: 'ParseA==' }); + for (const type of ["plain", "txt", "png", "jpg", "gif", "doc"]) { + const file = new Parse.File(`parse-server-logo.${type}`, { + base64: "ParseA==", + }); await file.save(); } }); - it('works with a period in the file name', async () => { + it("works with a period in the file name", async () => { await reconfigureServer({ fileUpload: { enableForPublic: true, - fileExtensions: ['^[^hH][^tT][^mM][^lL]?$'], + fileExtensions: ["^[^hH][^tT][^mM][^lL]?$"], }, }); const headers = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; - const values = ['file.png.html', 'file.txt.png.html', 'file.png.txt.html']; + const values = [ + "file.png.html", + "file.txt.png.html", + "file.png.txt.html", + ]; for (const value of values) { await expectAsync( request({ - method: 'POST', + method: "POST", headers: headers, url: `http://localhost:8378/1/files/${value}`, - body: '\n', + body: "\n", }).catch(e => { throw new Error(e.data.error); }) ).toBeRejectedWith( - new Parse.Error(Parse.Error.FILE_SAVE_ERROR, `File upload of extension html is disabled.`) + new Parse.Error( + Parse.Error.FILE_SAVE_ERROR, + `File upload of extension html is disabled.` + ) ); } }); - it('works to stop invalid filenames', async () => { + it("works to stop invalid filenames", async () => { await reconfigureServer({ fileUpload: { enableForPublic: true, - fileExtensions: ['^[^hH][^tT][^mM][^lL]?$'], + fileExtensions: ["^[^hH][^tT][^mM][^lL]?$"], }, }); const headers = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; const values = [ - '!invalid.png', - '.png', - '.html', - ' .html', - '.png.html', - '~invalid.png', - '-invalid.png', + "!invalid.png", + ".png", + ".html", + " .html", + ".png.html", + "~invalid.png", + "-invalid.png", ]; for (const value of values) { await expectAsync( request({ - method: 'POST', + method: "POST", headers: headers, url: `http://localhost:8378/1/files/${value}`, - body: '\n', + body: "\n", }).catch(e => { throw new Error(e.data.error); }) ).toBeRejectedWith( - new Parse.Error(Parse.Error.INVALID_FILE_NAME, `Filename contains invalid characters.`) + new Parse.Error( + Parse.Error.INVALID_FILE_NAME, + `Filename contains invalid characters.` + ) ); } }); - it('allows file without extension', async () => { + it("allows file without extension", async () => { await reconfigureServer({ fileUpload: { enableForPublic: true, - fileExtensions: ['^[^hH][^tT][^mM][^lL]?$'], + fileExtensions: ["^[^hH][^tT][^mM][^lL]?$"], }, }); const headers = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; - const values = ['filenamewithoutextension']; + const values = ["filenamewithoutextension"]; for (const value of values) { await expectAsync( request({ - method: 'POST', + method: "POST", headers: headers, url: `http://localhost:8378/1/files/${value}`, - body: '\n', + body: "\n", }).catch(e => { throw new Error(e.data.error); }) @@ -1444,100 +1533,109 @@ describe('Parse.File testing', () => { } }); - it('works with array', async () => { + it("works with array", async () => { await reconfigureServer({ fileUpload: { enableForPublic: true, - fileExtensions: ['jpg', 'wav'], + fileExtensions: ["jpg", "wav"], }, }); await expectAsync( request({ - method: 'POST', - url: 'http://localhost:8378/1/files/file', + method: "POST", + url: "http://localhost:8378/1/files/file", body: JSON.stringify({ - _ApplicationId: 'test', - _JavaScriptKey: 'test', - _ContentType: 'text/html', - base64: 'PGh0bWw+PC9odG1sPgo=', + _ApplicationId: "test", + _JavaScriptKey: "test", + _ContentType: "text/html", + base64: "PGh0bWw+PC9odG1sPgo=", }), }).catch(e => { throw new Error(e.data.error); }) ).toBeRejectedWith( - new Parse.Error(Parse.Error.FILE_SAVE_ERROR, `File upload of extension html is disabled.`) + new Parse.Error( + Parse.Error.FILE_SAVE_ERROR, + `File upload of extension html is disabled.` + ) ); await expectAsync( request({ - method: 'POST', - url: 'http://localhost:8378/1/files/file', + method: "POST", + url: "http://localhost:8378/1/files/file", body: JSON.stringify({ - _ApplicationId: 'test', - _JavaScriptKey: 'test', - _ContentType: 'image/jpg', - base64: 'PGh0bWw+PC9odG1sPgo=', + _ApplicationId: "test", + _JavaScriptKey: "test", + _ContentType: "image/jpg", + base64: "PGh0bWw+PC9odG1sPgo=", }), }) ).toBeResolved(); await expectAsync( request({ - method: 'POST', - url: 'http://localhost:8378/1/files/file', + method: "POST", + url: "http://localhost:8378/1/files/file", body: JSON.stringify({ - _ApplicationId: 'test', - _JavaScriptKey: 'test', - _ContentType: 'audio/wav', - base64: 'UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA', + _ApplicationId: "test", + _JavaScriptKey: "test", + _ContentType: "audio/wav", + base64: + "UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA", }), }) ).toBeResolved(); }); - it('works with array without Content-Type', async () => { + it("works with array without Content-Type", async () => { await reconfigureServer({ fileUpload: { enableForPublic: true, - fileExtensions: ['jpg'], + fileExtensions: ["jpg"], }, }); const headers = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; await expectAsync( request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/files/file.html', - body: '\n', + url: "http://localhost:8378/1/files/file.html", + body: "\n", }).catch(e => { throw new Error(e.data.error); }) ).toBeRejectedWith( - new Parse.Error(Parse.Error.FILE_SAVE_ERROR, `File upload of extension html is disabled.`) + new Parse.Error( + Parse.Error.FILE_SAVE_ERROR, + `File upload of extension html is disabled.` + ) ); }); - it('works with array with correct file type', async () => { + it("works with array with correct file type", async () => { await reconfigureServer({ fileUpload: { enableForPublic: true, - fileExtensions: ['html'], + fileExtensions: ["html"], }, }); const response = await request({ - method: 'POST', - url: 'http://localhost:8378/1/files/file', + method: "POST", + url: "http://localhost:8378/1/files/file", body: JSON.stringify({ - _ApplicationId: 'test', - _JavaScriptKey: 'test', - _ContentType: 'text/html', - base64: 'PGh0bWw+PC9odG1sPgo=', + _ApplicationId: "test", + _JavaScriptKey: "test", + _ContentType: "text/html", + base64: "PGh0bWw+PC9odG1sPgo=", }), }); const b = response.data; expect(b.name).toMatch(/_file.html$/); - expect(b.url).toMatch(/^http:\/\/localhost:8378\/1\/files\/test\/.*file.html$/); + expect(b.url).toMatch( + /^http:\/\/localhost:8378\/1\/files\/test\/.*file.html$/ + ); }); }); }); diff --git a/spec/ParseGeoPoint.spec.js b/spec/ParseGeoPoint.spec.js index ac104c1a47..2ca318dbc2 100644 --- a/spec/ParseGeoPoint.spec.js +++ b/spec/ParseGeoPoint.spec.js @@ -1,32 +1,32 @@ // This is a port of the test suite: // hungry/js/test/parse_geo_point_test.js -const request = require('../lib/request'); -const TestObject = Parse.Object.extend('TestObject'); +const request = require("../lib/request"); +const TestObject = Parse.Object.extend("TestObject"); -describe('Parse.GeoPoint testing', () => { - it('geo point roundtrip', async () => { +describe("Parse.GeoPoint testing", () => { + it("geo point roundtrip", async () => { const point = new Parse.GeoPoint(44.0, -11.0); const obj = new TestObject(); - obj.set('location', point); - obj.set('name', 'Ferndale'); + obj.set("location", point); + obj.set("name", "Ferndale"); await obj.save(); const result = await new Parse.Query(TestObject).get(obj.id); - const pointAgain = result.get('location'); + const pointAgain = result.get("location"); ok(pointAgain); equal(pointAgain.latitude, 44.0); equal(pointAgain.longitude, -11.0); }); - it('update geopoint', done => { + it("update geopoint", done => { const oldPoint = new Parse.GeoPoint(44.0, -11.0); const newPoint = new Parse.GeoPoint(24.0, 19.0); const obj = new TestObject(); - obj.set('location', oldPoint); + obj.set("location", oldPoint); obj .save() .then(() => { - obj.set('location', newPoint); + obj.set("location", newPoint); return obj.save(); }) .then(() => { @@ -34,39 +34,39 @@ describe('Parse.GeoPoint testing', () => { return query.get(obj.id); }) .then(result => { - const point = result.get('location'); + const point = result.get("location"); equal(point.latitude, newPoint.latitude); equal(point.longitude, newPoint.longitude); done(); }); }); - it('has the correct __type field in the json response', async done => { + it("has the correct __type field in the json response", async done => { const point = new Parse.GeoPoint(44.0, -11.0); const obj = new TestObject(); - obj.set('location', point); - obj.set('name', 'Zhoul'); + obj.set("location", point); + obj.set("name", "Zhoul"); await obj.save(); request({ - url: 'http://localhost:8378/1/classes/TestObject/' + obj.id, + url: "http://localhost:8378/1/classes/TestObject/" + obj.id, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-Master-Key": "test", }, }).then(response => { - equal(response.data.location.__type, 'GeoPoint'); + equal(response.data.location.__type, "GeoPoint"); done(); }); }); - it('creating geo point exception two fields', done => { + it("creating geo point exception two fields", done => { const point = new Parse.GeoPoint(20, 20); const obj = new TestObject(); - obj.set('locationOne', point); - obj.set('locationTwo', point); + obj.set("locationOne", point); + obj.set("locationTwo", point); obj.save().then( () => { - fail('expected error'); + fail("expected error"); }, err => { equal(err.code, Parse.Error.INCORRECT_TYPE); @@ -76,59 +76,62 @@ describe('Parse.GeoPoint testing', () => { }); // TODO: This should also have support in postgres, or higher level database agnostic support. - it_exclude_dbs(['postgres'])('updating geo point exception two fields', async done => { - const point = new Parse.GeoPoint(20, 20); - const obj = new TestObject(); - obj.set('locationOne', point); - await obj.save(); - obj.set('locationTwo', point); - obj.save().then( - () => { - fail('expected error'); - }, - err => { - equal(err.code, Parse.Error.INCORRECT_TYPE); - done(); - } - ); - }); + it_exclude_dbs(["postgres"])( + "updating geo point exception two fields", + async done => { + const point = new Parse.GeoPoint(20, 20); + const obj = new TestObject(); + obj.set("locationOne", point); + await obj.save(); + obj.set("locationTwo", point); + obj.save().then( + () => { + fail("expected error"); + }, + err => { + equal(err.code, Parse.Error.INCORRECT_TYPE); + done(); + } + ); + } + ); - it_id('bbd9e2f6-7f61-458f-98f2-4a563586cd8d')(it)('geo line', async done => { + it_id("bbd9e2f6-7f61-458f-98f2-4a563586cd8d")(it)("geo line", async done => { const line = []; for (let i = 0; i < 10; ++i) { const obj = new TestObject(); const point = new Parse.GeoPoint(i * 4.0 - 12.0, i * 3.2 - 11.0); - obj.set('location', point); - obj.set('construct', 'line'); - obj.set('seq', i); + obj.set("location", point); + obj.set("construct", "line"); + obj.set("seq", i); line.push(obj); } await Parse.Object.saveAll(line); const query = new Parse.Query(TestObject); const point = new Parse.GeoPoint(24, 19); - query.equalTo('construct', 'line'); - query.withinMiles('location', point, 10000); + query.equalTo("construct", "line"); + query.withinMiles("location", point, 10000); const results = await query.find(); equal(results.length, 10); - equal(results[0].get('seq'), 9); - equal(results[3].get('seq'), 6); + equal(results[0].get("seq"), 9); + equal(results[3].get("seq"), 6); done(); }); - it('geo max distance large', done => { + it("geo max distance large", done => { const objects = []; [0, 1, 2].map(function (i) { const obj = new TestObject(); const point = new Parse.GeoPoint(0.0, i * 45.0); - obj.set('location', point); - obj.set('index', i); + obj.set("location", point); + obj.set("index", i); objects.push(obj); }); Parse.Object.saveAll(objects) .then(() => { const query = new Parse.Query(TestObject); const point = new Parse.GeoPoint(1.0, -1.0); - query.withinRadians('location', point, 3.14); + query.withinRadians("location", point, 3.14); return query.find(); }) .then( @@ -143,313 +146,328 @@ describe('Parse.GeoPoint testing', () => { ); }); - it_id('e1e86b38-b8a4-4109-8330-a324fe628e0c')(it)('geo max distance medium', async () => { - const objects = []; - [0, 1, 2].map(function (i) { - const obj = new TestObject(); - const point = new Parse.GeoPoint(0.0, i * 45.0); - obj.set('location', point); - obj.set('index', i); - objects.push(obj); - }); - await Parse.Object.saveAll(objects); - const query = new Parse.Query(TestObject); - const point = new Parse.GeoPoint(1.0, -1.0); - query.withinRadians('location', point, 3.14 * 0.5); - const results = await query.find(); - equal(results.length, 2); - equal(results[0].get('index'), 0); - equal(results[1].get('index'), 1); - }); + it_id("e1e86b38-b8a4-4109-8330-a324fe628e0c")(it)( + "geo max distance medium", + async () => { + const objects = []; + [0, 1, 2].map(function (i) { + const obj = new TestObject(); + const point = new Parse.GeoPoint(0.0, i * 45.0); + obj.set("location", point); + obj.set("index", i); + objects.push(obj); + }); + await Parse.Object.saveAll(objects); + const query = new Parse.Query(TestObject); + const point = new Parse.GeoPoint(1.0, -1.0); + query.withinRadians("location", point, 3.14 * 0.5); + const results = await query.find(); + equal(results.length, 2); + equal(results[0].get("index"), 0); + equal(results[1].get("index"), 1); + } + ); - it('geo max distance small', async () => { + it("geo max distance small", async () => { const objects = []; [0, 1, 2].map(function (i) { const obj = new TestObject(); const point = new Parse.GeoPoint(0.0, i * 45.0); - obj.set('location', point); - obj.set('index', i); + obj.set("location", point); + obj.set("index", i); objects.push(obj); }); await Parse.Object.saveAll(objects); const query = new Parse.Query(TestObject); const point = new Parse.GeoPoint(1.0, -1.0); - query.withinRadians('location', point, 3.14 * 0.25); + query.withinRadians("location", point, 3.14 * 0.25); const results = await query.find(); equal(results.length, 1); - equal(results[0].get('index'), 0); + equal(results[0].get("index"), 0); }); const makeSomeGeoPoints = function () { const sacramento = new TestObject(); - sacramento.set('location', new Parse.GeoPoint(38.52, -121.5)); - sacramento.set('name', 'Sacramento'); + sacramento.set("location", new Parse.GeoPoint(38.52, -121.5)); + sacramento.set("name", "Sacramento"); const honolulu = new TestObject(); - honolulu.set('location', new Parse.GeoPoint(21.35, -157.93)); - honolulu.set('name', 'Honolulu'); + honolulu.set("location", new Parse.GeoPoint(21.35, -157.93)); + honolulu.set("name", "Honolulu"); const sf = new TestObject(); - sf.set('location', new Parse.GeoPoint(37.75, -122.68)); - sf.set('name', 'San Francisco'); + sf.set("location", new Parse.GeoPoint(37.75, -122.68)); + sf.set("name", "San Francisco"); return Parse.Object.saveAll([sacramento, sf, honolulu]); }; - it('geo max distance in km everywhere', async done => { + it("geo max distance in km everywhere", async done => { await makeSomeGeoPoints(); const sfo = new Parse.GeoPoint(37.6189722, -122.3748889); const query = new Parse.Query(TestObject); // Honolulu is 4300 km away from SFO on a sphere ;) - query.withinKilometers('location', sfo, 4800.0); + query.withinKilometers("location", sfo, 4800.0); const results = await query.find(); equal(results.length, 3); done(); }); - it_id('05f1a454-56b1-4f2e-908e-408a9222cbae')(it)( - 'geo max distance in km california', + it_id("05f1a454-56b1-4f2e-908e-408a9222cbae")(it)( + "geo max distance in km california", async () => { await makeSomeGeoPoints(); const sfo = new Parse.GeoPoint(37.6189722, -122.3748889); const query = new Parse.Query(TestObject); - query.withinKilometers('location', sfo, 3700.0); + query.withinKilometers("location", sfo, 3700.0); const results = await query.find(); equal(results.length, 2); - equal(results[0].get('name'), 'San Francisco'); - equal(results[1].get('name'), 'Sacramento'); + equal(results[0].get("name"), "San Francisco"); + equal(results[1].get("name"), "Sacramento"); } ); - it('geo max distance in km bay area', async () => { + it("geo max distance in km bay area", async () => { await makeSomeGeoPoints(); const sfo = new Parse.GeoPoint(37.6189722, -122.3748889); const query = new Parse.Query(TestObject); - query.withinKilometers('location', sfo, 100.0); + query.withinKilometers("location", sfo, 100.0); const results = await query.find(); equal(results.length, 1); - equal(results[0].get('name'), 'San Francisco'); + equal(results[0].get("name"), "San Francisco"); }); - it('geo max distance in km mid peninsula', async () => { + it("geo max distance in km mid peninsula", async () => { await makeSomeGeoPoints(); const sfo = new Parse.GeoPoint(37.6189722, -122.3748889); const query = new Parse.Query(TestObject); - query.withinKilometers('location', sfo, 10.0); + query.withinKilometers("location", sfo, 10.0); const results = await query.find(); equal(results.length, 0); }); - it('geo max distance in miles everywhere', async () => { + it("geo max distance in miles everywhere", async () => { await makeSomeGeoPoints(); const sfo = new Parse.GeoPoint(37.6189722, -122.3748889); const query = new Parse.Query(TestObject); - query.withinMiles('location', sfo, 2600.0); + query.withinMiles("location", sfo, 2600.0); const results = await query.find(); equal(results.length, 3); }); - it_id('9ee376ad-dd6c-4c17-ad28-c7899a4411f1')(it)( - 'geo max distance in miles california', + it_id("9ee376ad-dd6c-4c17-ad28-c7899a4411f1")(it)( + "geo max distance in miles california", async () => { await makeSomeGeoPoints(); const sfo = new Parse.GeoPoint(37.6189722, -122.3748889); const query = new Parse.Query(TestObject); - query.withinMiles('location', sfo, 2200.0); + query.withinMiles("location", sfo, 2200.0); const results = await query.find(); equal(results.length, 2); - equal(results[0].get('name'), 'San Francisco'); - equal(results[1].get('name'), 'Sacramento'); + equal(results[0].get("name"), "San Francisco"); + equal(results[1].get("name"), "Sacramento"); } ); - it('geo max distance in miles bay area', async () => { + it("geo max distance in miles bay area", async () => { await makeSomeGeoPoints(); const sfo = new Parse.GeoPoint(37.6189722, -122.3748889); const query = new Parse.Query(TestObject); - query.withinMiles('location', sfo, 62.0); + query.withinMiles("location", sfo, 62.0); const results = await query.find(); equal(results.length, 1); - equal(results[0].get('name'), 'San Francisco'); + equal(results[0].get("name"), "San Francisco"); }); - it('geo max distance in miles mid peninsula', async () => { + it("geo max distance in miles mid peninsula", async () => { await makeSomeGeoPoints(); const sfo = new Parse.GeoPoint(37.6189722, -122.3748889); const query = new Parse.Query(TestObject); - query.withinMiles('location', sfo, 10.0); + query.withinMiles("location", sfo, 10.0); const results = await query.find(); equal(results.length, 0); }); - it_id('9e35a89e-bc2c-4ec5-b25a-8d1890a55233')(it)('returns nearest location', async () => { - await makeSomeGeoPoints(); - const sfo = new Parse.GeoPoint(37.6189722, -122.3748889); - const query = new Parse.Query(TestObject); - query.near('location', sfo); - const results = await query.find(); - equal(results[0].get('name'), 'San Francisco'); - equal(results[1].get('name'), 'Sacramento'); - }); + it_id("9e35a89e-bc2c-4ec5-b25a-8d1890a55233")(it)( + "returns nearest location", + async () => { + await makeSomeGeoPoints(); + const sfo = new Parse.GeoPoint(37.6189722, -122.3748889); + const query = new Parse.Query(TestObject); + query.near("location", sfo); + const results = await query.find(); + equal(results[0].get("name"), "San Francisco"); + equal(results[1].get("name"), "Sacramento"); + } + ); - it_id('6df434b0-142d-4302-bbc6-a6ec5a9d9c68')(it)('works with geobox queries', done => { - const inbound = new Parse.GeoPoint(1.5, 1.5); - const onbound = new Parse.GeoPoint(10, 10); - const outbound = new Parse.GeoPoint(20, 20); - const obj1 = new Parse.Object('TestObject', { location: inbound }); - const obj2 = new Parse.Object('TestObject', { location: onbound }); - const obj3 = new Parse.Object('TestObject', { location: outbound }); - Parse.Object.saveAll([obj1, obj2, obj3]) - .then(() => { - const sw = new Parse.GeoPoint(0, 0); - const ne = new Parse.GeoPoint(10, 10); - const query = new Parse.Query(TestObject); - query.withinGeoBox('location', sw, ne); - return query.find(); - }) - .then(results => { - equal(results.length, 2); - done(); - }); - }); + it_id("6df434b0-142d-4302-bbc6-a6ec5a9d9c68")(it)( + "works with geobox queries", + done => { + const inbound = new Parse.GeoPoint(1.5, 1.5); + const onbound = new Parse.GeoPoint(10, 10); + const outbound = new Parse.GeoPoint(20, 20); + const obj1 = new Parse.Object("TestObject", { location: inbound }); + const obj2 = new Parse.Object("TestObject", { location: onbound }); + const obj3 = new Parse.Object("TestObject", { location: outbound }); + Parse.Object.saveAll([obj1, obj2, obj3]) + .then(() => { + const sw = new Parse.GeoPoint(0, 0); + const ne = new Parse.GeoPoint(10, 10); + const query = new Parse.Query(TestObject); + query.withinGeoBox("location", sw, ne); + return query.find(); + }) + .then(results => { + equal(results.length, 2); + done(); + }); + } + ); - it('supports a sub-object with a geo point', async () => { + it("supports a sub-object with a geo point", async () => { const point = new Parse.GeoPoint(44.0, -11.0); const obj = new TestObject(); - obj.set('subobject', { location: point }); + obj.set("subobject", { location: point }); await obj.save(); const query = new Parse.Query(TestObject); const results = await query.find(); equal(results.length, 1); - const pointAgain = results[0].get('subobject')['location']; + const pointAgain = results[0].get("subobject")["location"]; ok(pointAgain); equal(pointAgain.latitude, 44.0); equal(pointAgain.longitude, -11.0); }); - it('supports array of geo points', async () => { + it("supports array of geo points", async () => { const point1 = new Parse.GeoPoint(44.0, -11.0); const point2 = new Parse.GeoPoint(22.0, -55.0); const obj = new TestObject(); - obj.set('locations', [point1, point2]); + obj.set("locations", [point1, point2]); await obj.save(); const query = new Parse.Query(TestObject); const results = await query.find(); equal(results.length, 1); - const locations = results[0].get('locations'); + const locations = results[0].get("locations"); expect(locations.length).toEqual(2); expect(locations[0]).toEqual(point1); expect(locations[1]).toEqual(point2); }); - it('equalTo geopoint', done => { + it("equalTo geopoint", done => { const point = new Parse.GeoPoint(44.0, -11.0); const obj = new TestObject(); - obj.set('location', point); + obj.set("location", point); obj .save() .then(() => { const query = new Parse.Query(TestObject); - query.equalTo('location', point); + query.equalTo("location", point); return query.find(); }) .then(results => { equal(results.length, 1); - const loc = results[0].get('location'); + const loc = results[0].get("location"); equal(loc.latitude, point.latitude); equal(loc.longitude, point.longitude); done(); }); }); - it_id('d9fbc5c6-f767-47d6-bb44-3858eb9df15a')(it)('supports withinPolygon open path', done => { - const inbound = new Parse.GeoPoint(1.5, 1.5); - const onbound = new Parse.GeoPoint(10, 10); - const outbound = new Parse.GeoPoint(20, 20); - const obj1 = new Parse.Object('Polygon', { location: inbound }); - const obj2 = new Parse.Object('Polygon', { location: onbound }); - const obj3 = new Parse.Object('Polygon', { location: outbound }); - Parse.Object.saveAll([obj1, obj2, obj3]) - .then(() => { - const where = { - location: { - $geoWithin: { - $polygon: [ - { __type: 'GeoPoint', latitude: 0, longitude: 0 }, - { __type: 'GeoPoint', latitude: 0, longitude: 10 }, - { __type: 'GeoPoint', latitude: 10, longitude: 10 }, - { __type: 'GeoPoint', latitude: 10, longitude: 0 }, - ], + it_id("d9fbc5c6-f767-47d6-bb44-3858eb9df15a")(it)( + "supports withinPolygon open path", + done => { + const inbound = new Parse.GeoPoint(1.5, 1.5); + const onbound = new Parse.GeoPoint(10, 10); + const outbound = new Parse.GeoPoint(20, 20); + const obj1 = new Parse.Object("Polygon", { location: inbound }); + const obj2 = new Parse.Object("Polygon", { location: onbound }); + const obj3 = new Parse.Object("Polygon", { location: outbound }); + Parse.Object.saveAll([obj1, obj2, obj3]) + .then(() => { + const where = { + location: { + $geoWithin: { + $polygon: [ + { __type: "GeoPoint", latitude: 0, longitude: 0 }, + { __type: "GeoPoint", latitude: 0, longitude: 10 }, + { __type: "GeoPoint", latitude: 10, longitude: 10 }, + { __type: "GeoPoint", latitude: 10, longitude: 0 }, + ], + }, }, - }, - }; - return request({ - method: 'POST', - url: Parse.serverURL + '/classes/Polygon', - body: { where, _method: 'GET' }, - headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, - 'Content-Type': 'application/json', - }, - }); - }) - .then(resp => { - expect(resp.data.results.length).toBe(2); - done(); - }, done.fail); - }); + }; + return request({ + method: "POST", + url: Parse.serverURL + "/classes/Polygon", + body: { where, _method: "GET" }, + headers: { + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Javascript-Key": Parse.javaScriptKey, + "Content-Type": "application/json", + }, + }); + }) + .then(resp => { + expect(resp.data.results.length).toBe(2); + done(); + }, done.fail); + } + ); - it_id('3ec537bd-839a-4c93-a48b-b4a249820074')(it)('supports withinPolygon closed path', done => { - const inbound = new Parse.GeoPoint(1.5, 1.5); - const onbound = new Parse.GeoPoint(10, 10); - const outbound = new Parse.GeoPoint(20, 20); - const obj1 = new Parse.Object('Polygon', { location: inbound }); - const obj2 = new Parse.Object('Polygon', { location: onbound }); - const obj3 = new Parse.Object('Polygon', { location: outbound }); - Parse.Object.saveAll([obj1, obj2, obj3]) - .then(() => { - const where = { - location: { - $geoWithin: { - $polygon: [ - { __type: 'GeoPoint', latitude: 0, longitude: 0 }, - { __type: 'GeoPoint', latitude: 0, longitude: 10 }, - { __type: 'GeoPoint', latitude: 10, longitude: 10 }, - { __type: 'GeoPoint', latitude: 10, longitude: 0 }, - { __type: 'GeoPoint', latitude: 0, longitude: 0 }, - ], + it_id("3ec537bd-839a-4c93-a48b-b4a249820074")(it)( + "supports withinPolygon closed path", + done => { + const inbound = new Parse.GeoPoint(1.5, 1.5); + const onbound = new Parse.GeoPoint(10, 10); + const outbound = new Parse.GeoPoint(20, 20); + const obj1 = new Parse.Object("Polygon", { location: inbound }); + const obj2 = new Parse.Object("Polygon", { location: onbound }); + const obj3 = new Parse.Object("Polygon", { location: outbound }); + Parse.Object.saveAll([obj1, obj2, obj3]) + .then(() => { + const where = { + location: { + $geoWithin: { + $polygon: [ + { __type: "GeoPoint", latitude: 0, longitude: 0 }, + { __type: "GeoPoint", latitude: 0, longitude: 10 }, + { __type: "GeoPoint", latitude: 10, longitude: 10 }, + { __type: "GeoPoint", latitude: 10, longitude: 0 }, + { __type: "GeoPoint", latitude: 0, longitude: 0 }, + ], + }, }, - }, - }; - return request({ - method: 'POST', - url: Parse.serverURL + '/classes/Polygon', - body: { where, _method: 'GET' }, - headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, - 'Content-Type': 'application/json', - }, - }); - }) - .then(resp => { - expect(resp.data.results.length).toBe(2); - done(); - }, done.fail); - }); + }; + return request({ + method: "POST", + url: Parse.serverURL + "/classes/Polygon", + body: { where, _method: "GET" }, + headers: { + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Javascript-Key": Parse.javaScriptKey, + "Content-Type": "application/json", + }, + }); + }) + .then(resp => { + expect(resp.data.results.length).toBe(2); + done(); + }, done.fail); + } + ); - it_id('0a248e11-3598-480a-9ab5-8a0b259258e4')(it)( - 'supports withinPolygon Polygon object', + it_id("0a248e11-3598-480a-9ab5-8a0b259258e4")(it)( + "supports withinPolygon Polygon object", done => { const inbound = new Parse.GeoPoint(1.5, 1.5); const onbound = new Parse.GeoPoint(10, 10); const outbound = new Parse.GeoPoint(20, 20); - const obj1 = new Parse.Object('Polygon', { location: inbound }); - const obj2 = new Parse.Object('Polygon', { location: onbound }); - const obj3 = new Parse.Object('Polygon', { location: outbound }); + const obj1 = new Parse.Object("Polygon", { location: inbound }); + const obj2 = new Parse.Object("Polygon", { location: onbound }); + const obj3 = new Parse.Object("Polygon", { location: outbound }); const polygon = { - __type: 'Polygon', + __type: "Polygon", coordinates: [ [0, 0], [10, 0], @@ -468,13 +486,13 @@ describe('Parse.GeoPoint testing', () => { }, }; return request({ - method: 'POST', - url: Parse.serverURL + '/classes/Polygon', - body: { where, _method: 'GET' }, + method: "POST", + url: Parse.serverURL + "/classes/Polygon", + body: { where, _method: "GET" }, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Javascript-Key": Parse.javaScriptKey, + "Content-Type": "application/json", }, }); }) @@ -485,11 +503,11 @@ describe('Parse.GeoPoint testing', () => { } ); - it('invalid Polygon object withinPolygon', done => { + it("invalid Polygon object withinPolygon", done => { const point = new Parse.GeoPoint(1.5, 1.5); - const obj = new Parse.Object('Polygon', { location: point }); + const obj = new Parse.Object("Polygon", { location: point }); const polygon = { - __type: 'Polygon', + __type: "Polygon", coordinates: [ [0, 0], [10, 0], @@ -506,13 +524,13 @@ describe('Parse.GeoPoint testing', () => { }, }; return request({ - method: 'POST', - url: Parse.serverURL + '/classes/Polygon', - body: { where, _method: 'GET' }, + method: "POST", + url: Parse.serverURL + "/classes/Polygon", + body: { where, _method: "GET" }, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Javascript-Key": Parse.javaScriptKey, + "Content-Type": "application/json", }, }); }) @@ -526,11 +544,11 @@ describe('Parse.GeoPoint testing', () => { }); }); - it('out of bounds Polygon object withinPolygon', done => { + it("out of bounds Polygon object withinPolygon", done => { const point = new Parse.GeoPoint(1.5, 1.5); - const obj = new Parse.Object('Polygon', { location: point }); + const obj = new Parse.Object("Polygon", { location: point }); const polygon = { - __type: 'Polygon', + __type: "Polygon", coordinates: [ [0, 0], [181, 0], @@ -548,13 +566,13 @@ describe('Parse.GeoPoint testing', () => { }, }; return request({ - method: 'POST', - url: Parse.serverURL + '/classes/Polygon', - body: { where, _method: 'GET' }, + method: "POST", + url: Parse.serverURL + "/classes/Polygon", + body: { where, _method: "GET" }, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Javascript-Key": Parse.javaScriptKey, + "Content-Type": "application/json", }, }); }) @@ -568,9 +586,9 @@ describe('Parse.GeoPoint testing', () => { }); }); - it('invalid input withinPolygon', done => { + it("invalid input withinPolygon", done => { const point = new Parse.GeoPoint(1.5, 1.5); - const obj = new Parse.Object('Polygon', { location: point }); + const obj = new Parse.Object("Polygon", { location: point }); obj .save() .then(() => { @@ -582,13 +600,13 @@ describe('Parse.GeoPoint testing', () => { }, }; return request({ - method: 'POST', - url: Parse.serverURL + '/classes/Polygon', - body: { where, _method: 'GET' }, + method: "POST", + url: Parse.serverURL + "/classes/Polygon", + body: { where, _method: "GET" }, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Javascript-Key": Parse.javaScriptKey, + "Content-Type": "application/json", }, }); }) @@ -602,9 +620,9 @@ describe('Parse.GeoPoint testing', () => { }); }); - it('invalid geoPoint withinPolygon', done => { + it("invalid geoPoint withinPolygon", done => { const point = new Parse.GeoPoint(1.5, 1.5); - const obj = new Parse.Object('Polygon', { location: point }); + const obj = new Parse.Object("Polygon", { location: point }); obj .save() .then(() => { @@ -616,13 +634,13 @@ describe('Parse.GeoPoint testing', () => { }, }; return request({ - method: 'POST', - url: Parse.serverURL + '/classes/Polygon', - body: { where, _method: 'GET' }, + method: "POST", + url: Parse.serverURL + "/classes/Polygon", + body: { where, _method: "GET" }, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Javascript-Key": Parse.javaScriptKey, + "Content-Type": "application/json", }, }); }) @@ -636,9 +654,9 @@ describe('Parse.GeoPoint testing', () => { }); }); - it('invalid latitude withinPolygon', done => { + it("invalid latitude withinPolygon", done => { const point = new Parse.GeoPoint(1.5, 1.5); - const obj = new Parse.Object('Polygon', { location: point }); + const obj = new Parse.Object("Polygon", { location: point }); obj .save() .then(() => { @@ -646,21 +664,21 @@ describe('Parse.GeoPoint testing', () => { location: { $geoWithin: { $polygon: [ - { __type: 'GeoPoint', latitude: 0, longitude: 0 }, - { __type: 'GeoPoint', latitude: 181, longitude: 0 }, - { __type: 'GeoPoint', latitude: 0, longitude: 0 }, + { __type: "GeoPoint", latitude: 0, longitude: 0 }, + { __type: "GeoPoint", latitude: 181, longitude: 0 }, + { __type: "GeoPoint", latitude: 0, longitude: 0 }, ], }, }, }; return request({ - method: 'POST', - url: Parse.serverURL + '/classes/Polygon', - body: { where, _method: 'GET' }, + method: "POST", + url: Parse.serverURL + "/classes/Polygon", + body: { where, _method: "GET" }, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Javascript-Key": Parse.javaScriptKey, + "Content-Type": "application/json", }, }); }) @@ -674,9 +692,9 @@ describe('Parse.GeoPoint testing', () => { }); }); - it('invalid longitude withinPolygon', done => { + it("invalid longitude withinPolygon", done => { const point = new Parse.GeoPoint(1.5, 1.5); - const obj = new Parse.Object('Polygon', { location: point }); + const obj = new Parse.Object("Polygon", { location: point }); obj .save() .then(() => { @@ -684,21 +702,21 @@ describe('Parse.GeoPoint testing', () => { location: { $geoWithin: { $polygon: [ - { __type: 'GeoPoint', latitude: 0, longitude: 0 }, - { __type: 'GeoPoint', latitude: 0, longitude: 181 }, - { __type: 'GeoPoint', latitude: 0, longitude: 0 }, + { __type: "GeoPoint", latitude: 0, longitude: 0 }, + { __type: "GeoPoint", latitude: 0, longitude: 181 }, + { __type: "GeoPoint", latitude: 0, longitude: 0 }, ], }, }, }; return request({ - method: 'POST', - url: Parse.serverURL + '/classes/Polygon', - body: { where, _method: 'GET' }, + method: "POST", + url: Parse.serverURL + "/classes/Polygon", + body: { where, _method: "GET" }, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Javascript-Key": Parse.javaScriptKey, + "Content-Type": "application/json", }, }); }) @@ -712,9 +730,9 @@ describe('Parse.GeoPoint testing', () => { }); }); - it('minimum 3 points withinPolygon', done => { + it("minimum 3 points withinPolygon", done => { const point = new Parse.GeoPoint(1.5, 1.5); - const obj = new Parse.Object('Polygon', { location: point }); + const obj = new Parse.Object("Polygon", { location: point }); obj .save() .then(() => { @@ -726,13 +744,13 @@ describe('Parse.GeoPoint testing', () => { }, }; return request({ - method: 'POST', - url: Parse.serverURL + '/classes/Polygon', - body: { where, _method: 'GET' }, + method: "POST", + url: Parse.serverURL + "/classes/Polygon", + body: { where, _method: "GET" }, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Javascript-Key": Parse.javaScriptKey, + "Content-Type": "application/json", }, }); }) @@ -746,35 +764,47 @@ describe('Parse.GeoPoint testing', () => { }); }); - it('withinKilometers supports count', async () => { + it("withinKilometers supports count", async () => { const inside = new Parse.GeoPoint(10, 10); const outside = new Parse.GeoPoint(20, 20); - const obj1 = new Parse.Object('TestObject', { location: inside }); - const obj2 = new Parse.Object('TestObject', { location: outside }); + const obj1 = new Parse.Object("TestObject", { location: inside }); + const obj2 = new Parse.Object("TestObject", { location: outside }); await Parse.Object.saveAll([obj1, obj2]); - const q = new Parse.Query(TestObject).withinKilometers('location', inside, 5); + const q = new Parse.Query(TestObject).withinKilometers( + "location", + inside, + 5 + ); const count = await q.count(); equal(count, 1); }); - it_id('0b073d31-0d41-41e7-bd60-f636ffb759dc')(it)( - 'withinKilometers complex supports count', + it_id("0b073d31-0d41-41e7-bd60-f636ffb759dc")(it)( + "withinKilometers complex supports count", async () => { const inside = new Parse.GeoPoint(10, 10); const middle = new Parse.GeoPoint(20, 20); const outside = new Parse.GeoPoint(30, 30); - const obj1 = new Parse.Object('TestObject', { location: inside }); - const obj2 = new Parse.Object('TestObject', { location: middle }); - const obj3 = new Parse.Object('TestObject', { location: outside }); + const obj1 = new Parse.Object("TestObject", { location: inside }); + const obj2 = new Parse.Object("TestObject", { location: middle }); + const obj3 = new Parse.Object("TestObject", { location: outside }); await Parse.Object.saveAll([obj1, obj2, obj3]); - const q1 = new Parse.Query(TestObject).withinKilometers('location', inside, 5); - const q2 = new Parse.Query(TestObject).withinKilometers('location', middle, 5); + const q1 = new Parse.Query(TestObject).withinKilometers( + "location", + inside, + 5 + ); + const q2 = new Parse.Query(TestObject).withinKilometers( + "location", + middle, + 5 + ); const query = Parse.Query.or(q1, q2); const count = await query.count(); @@ -782,8 +812,8 @@ describe('Parse.GeoPoint testing', () => { } ); - it_id('26c9a13d-3d71-452e-a91c-9a4589be021c')(it)( - 'fails to fetch geopoints that are specifically not at (0,0)', + it_id("26c9a13d-3d71-452e-a91c-9a4589be021c")(it)( + "fails to fetch geopoints that are specifically not at (0,0)", async () => { const tmp = new TestObject({ location: new Parse.GeoPoint({ latitude: 0, longitude: 0 }), @@ -796,7 +826,10 @@ describe('Parse.GeoPoint testing', () => { }); await Parse.Object.saveAll([tmp, tmp2]); const query = new Parse.Query(TestObject); - query.notEqualTo('location', new Parse.GeoPoint({ latitude: 0, longitude: 0 })); + query.notEqualTo( + "location", + new Parse.GeoPoint({ latitude: 0, longitude: 0 }) + ); const results = await query.find(); expect(results.length).toEqual(1); } diff --git a/spec/ParseGlobalConfig.spec.js b/spec/ParseGlobalConfig.spec.js index 1ff6dcfd93..9eec7a21ed 100644 --- a/spec/ParseGlobalConfig.spec.js +++ b/spec/ParseGlobalConfig.spec.js @@ -1,54 +1,58 @@ -'use strict'; +"use strict"; -const request = require('../lib/request'); -const Config = require('../lib/Config'); +const request = require("../lib/request"); +const Config = require("../lib/Config"); -describe('a GlobalConfig', () => { +describe("a GlobalConfig", () => { beforeEach(async () => { - const config = Config.get('test'); + const config = Config.get("test"); const query = on_db( - 'mongo', + "mongo", () => { // Legacy is with an int... return { objectId: 1 }; }, () => { - return { objectId: '1' }; + return { objectId: "1" }; } ); await config.database.adapter.upsertOneObject( - '_GlobalConfig', + "_GlobalConfig", { fields: { - objectId: { type: 'Number' }, - params: { type: 'Object' }, - masterKeyOnly: { type: 'Object' }, + objectId: { type: "Number" }, + params: { type: "Object" }, + masterKeyOnly: { type: "Object" }, }, }, query, { - params: { companies: ['US', 'DK'], counter: 20, internalParam: 'internal' }, + params: { + companies: ["US", "DK"], + counter: 20, + internalParam: "internal", + }, masterKeyOnly: { internalParam: true }, } ); }); const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key': 'test', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-Master-Key": "test", }; - it('can be retrieved', done => { + it("can be retrieved", done => { request({ - url: 'http://localhost:8378/1/config', + url: "http://localhost:8378/1/config", json: true, headers, }).then(response => { const body = response.data; try { expect(response.status).toEqual(200); - expect(body.params.companies).toEqual(['US', 'DK']); + expect(body.params.companies).toEqual(["US", "DK"]); } catch (e) { jfail(e); } @@ -56,16 +60,16 @@ describe('a GlobalConfig', () => { }); }); - it('internal parameter can be retrieved with master key', done => { + it("internal parameter can be retrieved with master key", done => { request({ - url: 'http://localhost:8378/1/config', + url: "http://localhost:8378/1/config", json: true, headers, }).then(response => { const body = response.data; try { expect(response.status).toEqual(200); - expect(body.params.internalParam).toEqual('internal'); + expect(body.params.internalParam).toEqual("internal"); } catch (e) { jfail(e); } @@ -73,14 +77,14 @@ describe('a GlobalConfig', () => { }); }); - it('internal parameter cannot be retrieved without master key', done => { + it("internal parameter cannot be retrieved without master key", done => { request({ - url: 'http://localhost:8378/1/config', + url: "http://localhost:8378/1/config", json: true, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }, }).then(response => { const body = response.data; @@ -94,12 +98,12 @@ describe('a GlobalConfig', () => { }); }); - it('can be updated when a master key exists', done => { + it("can be updated when a master key exists", done => { request({ - method: 'PUT', - url: 'http://localhost:8378/1/config', + method: "PUT", + url: "http://localhost:8378/1/config", json: true, - body: { params: { companies: ['US', 'DK', 'SE'] } }, + body: { params: { companies: ["US", "DK", "SE"] } }, headers, }).then(response => { const body = response.data; @@ -109,41 +113,43 @@ describe('a GlobalConfig', () => { }); }); - it_only_db('mongo')('can addUnique', async () => { - await Parse.Config.save({ companies: { __op: 'AddUnique', objects: ['PA', 'RS', 'E'] } }); + it_only_db("mongo")("can addUnique", async () => { + await Parse.Config.save({ + companies: { __op: "AddUnique", objects: ["PA", "RS", "E"] }, + }); const config = await Parse.Config.get(); - const companies = config.get('companies'); - expect(companies).toEqual(['US', 'DK', 'PA', 'RS', 'E']); + const companies = config.get("companies"); + expect(companies).toEqual(["US", "DK", "PA", "RS", "E"]); }); - it_only_db('mongo')('can add to array', async () => { - await Parse.Config.save({ companies: { __op: 'Add', objects: ['PA'] } }); + it_only_db("mongo")("can add to array", async () => { + await Parse.Config.save({ companies: { __op: "Add", objects: ["PA"] } }); const config = await Parse.Config.get(); - const companies = config.get('companies'); - expect(companies).toEqual(['US', 'DK', 'PA']); + const companies = config.get("companies"); + expect(companies).toEqual(["US", "DK", "PA"]); }); - it_only_db('mongo')('can remove from array', async () => { - await Parse.Config.save({ companies: { __op: 'Remove', objects: ['US'] } }); + it_only_db("mongo")("can remove from array", async () => { + await Parse.Config.save({ companies: { __op: "Remove", objects: ["US"] } }); const config = await Parse.Config.get(); - const companies = config.get('companies'); - expect(companies).toEqual(['DK']); + const companies = config.get("companies"); + expect(companies).toEqual(["DK"]); }); - it('can increment', async () => { - await Parse.Config.save({ counter: { __op: 'Increment', amount: 49 } }); + it("can increment", async () => { + await Parse.Config.save({ counter: { __op: "Increment", amount: 49 } }); const config = await Parse.Config.get(); - const counter = config.get('counter'); + const counter = config.get("counter"); expect(counter).toEqual(69); }); - it('can add and retrive files', done => { + it("can add and retrive files", done => { request({ - method: 'PUT', - url: 'http://localhost:8378/1/config', + method: "PUT", + url: "http://localhost:8378/1/config", json: true, body: { - params: { file: { __type: 'File', name: 'name', url: 'http://url' } }, + params: { file: { __type: "File", name: "name", url: "http://url" } }, }, headers, }).then(response => { @@ -151,19 +157,19 @@ describe('a GlobalConfig', () => { expect(response.status).toEqual(200); expect(body.result).toEqual(true); Parse.Config.get().then(res => { - const file = res.get('file'); - expect(file.name()).toBe('name'); - expect(file.url()).toBe('http://url'); + const file = res.get("file"); + expect(file.name()).toBe("name"); + expect(file.url()).toBe("http://url"); done(); }); }); }); - it('can add and retrive Geopoints', done => { + it("can add and retrive Geopoints", done => { const geopoint = new Parse.GeoPoint(10, -20); request({ - method: 'PUT', - url: 'http://localhost:8378/1/config', + method: "PUT", + url: "http://localhost:8378/1/config", json: true, body: { params: { point: geopoint.toJSON() } }, headers, @@ -172,7 +178,7 @@ describe('a GlobalConfig', () => { expect(response.status).toEqual(200); expect(body.result).toEqual(true); Parse.Config.get().then(res => { - const point = res.get('point'); + const point = res.get("point"); expect(point.latitude).toBe(10); expect(point.longitude).toBe(-20); done(); @@ -180,74 +186,77 @@ describe('a GlobalConfig', () => { }); }); - it_id('5ebbd0cf-d1a5-49d9-aac7-5216abc5cb62')(it)('properly handles delete op', done => { - request({ - method: 'PUT', - url: 'http://localhost:8378/1/config', - json: true, - body: { - params: { - companies: { __op: 'Delete' }, - counter: { __op: 'Delete' }, - internalParam: { __op: 'Delete' }, - foo: 'bar', - }, - }, - headers, - }).then(response => { - const body = response.data; - expect(response.status).toEqual(200); - expect(body.result).toEqual(true); + it_id("5ebbd0cf-d1a5-49d9-aac7-5216abc5cb62")(it)( + "properly handles delete op", + done => { request({ - url: 'http://localhost:8378/1/config', + method: "PUT", + url: "http://localhost:8378/1/config", json: true, + body: { + params: { + companies: { __op: "Delete" }, + counter: { __op: "Delete" }, + internalParam: { __op: "Delete" }, + foo: "bar", + }, + }, headers, }).then(response => { const body = response.data; - try { - expect(response.status).toEqual(200); - expect(body.params.companies).toBeUndefined(); - expect(body.params.counter).toBeUndefined(); - expect(body.params.foo).toBe('bar'); - expect(Object.keys(body.params).length).toBe(1); - } catch (e) { - jfail(e); - } - done(); + expect(response.status).toEqual(200); + expect(body.result).toEqual(true); + request({ + url: "http://localhost:8378/1/config", + json: true, + headers, + }).then(response => { + const body = response.data; + try { + expect(response.status).toEqual(200); + expect(body.params.companies).toBeUndefined(); + expect(body.params.counter).toBeUndefined(); + expect(body.params.foo).toBe("bar"); + expect(Object.keys(body.params).length).toBe(1); + } catch (e) { + jfail(e); + } + done(); + }); }); - }); - }); + } + ); - it('fail to update if master key is missing', done => { + it("fail to update if master key is missing", done => { request({ - method: 'PUT', - url: 'http://localhost:8378/1/config', + method: "PUT", + url: "http://localhost:8378/1/config", json: true, body: { params: { companies: [] } }, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }, }).then(fail, response => { const body = response.data; expect(response.status).toEqual(403); - expect(body.error).toEqual('unauthorized: master key is required'); + expect(body.error).toEqual("unauthorized: master key is required"); done(); }); }); - it('failed getting config when it is missing', done => { - const config = Config.get('test'); + it("failed getting config when it is missing", done => { + const config = Config.get("test"); config.database.adapter .deleteObjectsByQuery( - '_GlobalConfig', - { fields: { params: { __type: 'String' } } }, - { objectId: '1' } + "_GlobalConfig", + { fields: { params: { __type: "String" } } }, + { objectId: "1" } ) .then(() => { request({ - url: 'http://localhost:8378/1/config', + url: "http://localhost:8378/1/config", json: true, headers, }).then(response => { diff --git a/spec/ParseGraphQLClassNameTransformer.spec.js b/spec/ParseGraphQLClassNameTransformer.spec.js index d8a4dd6020..94a4b328ef 100644 --- a/spec/ParseGraphQLClassNameTransformer.spec.js +++ b/spec/ParseGraphQLClassNameTransformer.spec.js @@ -1,12 +1,11 @@ -const { transformClassNameToGraphQL } = require('../lib/GraphQL/transformers/className'); +const { + transformClassNameToGraphQL, +} = require("../lib/GraphQL/transformers/className"); -describe('transformClassNameToGraphQL', () => { - it('should remove starting _ and tansform first letter to upper case', () => { - expect(['_User', '_user', 'User', 'user'].map(transformClassNameToGraphQL)).toEqual([ - 'User', - 'User', - 'User', - 'User', - ]); +describe("transformClassNameToGraphQL", () => { + it("should remove starting _ and tansform first letter to upper case", () => { + expect( + ["_User", "_user", "User", "user"].map(transformClassNameToGraphQL) + ).toEqual(["User", "User", "User", "User"]); }); }); diff --git a/spec/ParseGraphQLController.spec.js b/spec/ParseGraphQLController.spec.js index 9eed8f52be..c08925ca0e 100644 --- a/spec/ParseGraphQLController.spec.js +++ b/spec/ParseGraphQLController.spec.js @@ -3,10 +3,10 @@ const { GraphQLConfigClassName, GraphQLConfigId, GraphQLConfigKey, -} = require('../lib/Controllers/ParseGraphQLController'); -const { isEqual } = require('lodash'); +} = require("../lib/Controllers/ParseGraphQLController"); +const { isEqual } = require("lodash"); -describe('ParseGraphQLController', () => { +describe("ParseGraphQLController", () => { let parseServer; let databaseController; let cacheController; @@ -36,7 +36,10 @@ describe('ParseGraphQLController', () => { const defaultFind = databaseController.find.bind(databaseController); databaseController.find = async (className, query, ...args) => { - if (className === GraphQLConfigClassName && isEqual(query, { objectId: GraphQLConfigId })) { + if ( + className === GraphQLConfigClassName && + isEqual(query, { objectId: GraphQLConfigId }) + ) { const graphQLConfigRecord = getConfigFromDb(); return graphQLConfigRecord ? [graphQLConfigRecord] : []; } else { @@ -45,7 +48,12 @@ describe('ParseGraphQLController', () => { }; const defaultUpdate = databaseController.update.bind(databaseController); - databaseController.update = async (className, query, update, fullQueryOptions) => { + databaseController.update = async ( + className, + query, + update, + fullQueryOptions + ) => { databaseUpdateArgs = [className, query, update, fullQueryOptions]; if ( className === GraphQLConfigClassName && @@ -64,8 +72,8 @@ describe('ParseGraphQLController', () => { databaseUpdateArgs = null; }); - describe('constructor', () => { - it('should require a databaseController', () => { + describe("constructor", () => { + it("should require a databaseController", () => { expect(() => new ParseGraphQLController()).toThrow( 'ParseGraphQLController requires a "databaseController" to be instantiated.' ); @@ -78,9 +86,11 @@ describe('ParseGraphQLController', () => { cacheController, mountGraphQL: false, }) - ).toThrow('ParseGraphQLController requires a "databaseController" to be instantiated.'); + ).toThrow( + 'ParseGraphQLController requires a "databaseController" to be instantiated.' + ); }); - it('should construct without a cacheController', () => { + it("should construct without a cacheController", () => { expect( () => new ParseGraphQLController({ @@ -95,7 +105,7 @@ describe('ParseGraphQLController', () => { }) ).not.toThrow(); }); - it('should set isMounted to true if config.mountGraphQL is true', () => { + it("should set isMounted to true if config.mountGraphQL is true", () => { const mountedController = new ParseGraphQLController({ databaseController, mountGraphQL: true, @@ -113,8 +123,8 @@ describe('ParseGraphQLController', () => { }); }); - describe('getGraphQLConfig', () => { - it('should return an empty graphQLConfig if collection has none', async () => { + describe("getGraphQLConfig", () => { + it("should return an empty graphQLConfig if collection has none", async () => { removeConfigFromDb(); const parseGraphQLController = new ParseGraphQLController({ @@ -125,17 +135,17 @@ describe('ParseGraphQLController', () => { const graphQLConfig = await parseGraphQLController.getGraphQLConfig(); expect(graphQLConfig).toEqual({}); }); - it('should return an existing graphQLConfig', async () => { - setConfigOnDb({ enabledForClasses: ['_User'] }); + it("should return an existing graphQLConfig", async () => { + setConfigOnDb({ enabledForClasses: ["_User"] }); const parseGraphQLController = new ParseGraphQLController({ databaseController, mountGraphQL: false, }); const graphQLConfig = await parseGraphQLController.getGraphQLConfig(); - expect(graphQLConfig).toEqual({ enabledForClasses: ['_User'] }); + expect(graphQLConfig).toEqual({ enabledForClasses: ["_User"] }); }); - it('should use the cache if mounted, and return the stored graphQLConfig', async () => { + it("should use the cache if mounted, and return the stored graphQLConfig", async () => { removeConfigFromDb(); cacheController.graphQL.clear(); const parseGraphQLController = new ParseGraphQLController({ @@ -144,14 +154,14 @@ describe('ParseGraphQLController', () => { mountGraphQL: true, }); cacheController.graphQL.put(parseGraphQLController.configCacheKey, { - enabledForClasses: ['SuperCar'], + enabledForClasses: ["SuperCar"], }); const graphQLConfig = await parseGraphQLController.getGraphQLConfig(); - expect(graphQLConfig).toEqual({ enabledForClasses: ['SuperCar'] }); + expect(graphQLConfig).toEqual({ enabledForClasses: ["SuperCar"] }); }); - it('should use the database when mounted and cache is empty', async () => { - setConfigOnDb({ disabledForClasses: ['SuperCar'] }); + it("should use the database when mounted and cache is empty", async () => { + setConfigOnDb({ disabledForClasses: ["SuperCar"] }); cacheController.graphQL.clear(); const parseGraphQLController = new ParseGraphQLController({ databaseController, @@ -159,10 +169,10 @@ describe('ParseGraphQLController', () => { mountGraphQL: true, }); const graphQLConfig = await parseGraphQLController.getGraphQLConfig(); - expect(graphQLConfig).toEqual({ disabledForClasses: ['SuperCar'] }); + expect(graphQLConfig).toEqual({ disabledForClasses: ["SuperCar"] }); }); - it('should store the graphQLConfig in cache if mounted', async () => { - setConfigOnDb({ enabledForClasses: ['SuperCar'] }); + it("should store the graphQLConfig in cache if mounted", async () => { + setConfigOnDb({ enabledForClasses: ["SuperCar"] }); cacheController.graphQL.clear(); const parseGraphQLController = new ParseGraphQLController({ databaseController, @@ -177,32 +187,32 @@ describe('ParseGraphQLController', () => { const cachedValueAfter = await cacheController.graphQL.get( parseGraphQLController.configCacheKey ); - expect(cachedValueAfter).toEqual({ enabledForClasses: ['SuperCar'] }); + expect(cachedValueAfter).toEqual({ enabledForClasses: ["SuperCar"] }); }); }); - describe('updateGraphQLConfig', () => { + describe("updateGraphQLConfig", () => { const successfulUpdateResponse = { response: { result: true } }; - it('should throw if graphQLConfig is not provided', async function () { + it("should throw if graphQLConfig is not provided", async function () { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); - expectAsync(parseGraphQLController.updateGraphQLConfig()).toBeRejectedWith( - 'You must provide a graphQLConfig!' - ); + expectAsync( + parseGraphQLController.updateGraphQLConfig() + ).toBeRejectedWith("You must provide a graphQLConfig!"); }); - it('should correct update the graphQLConfig object using the databaseController', async () => { + it("should correct update the graphQLConfig object using the databaseController", async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); const graphQLConfig = { - enabledForClasses: ['ClassA', 'ClassB'], + enabledForClasses: ["ClassA", "ClassB"], disabledForClasses: [], classConfigs: [ - { className: 'ClassA', query: { get: false } }, - { className: 'ClassB', mutation: { destroy: false }, type: {} }, + { className: "ClassA", query: { get: false } }, + { className: "ClassB", mutation: { destroy: false }, type: {} }, ], }; @@ -218,28 +228,38 @@ describe('ParseGraphQLController', () => { expect(op).toEqual({ upsert: true }); }); - it('should throw if graphQLConfig is not an object', async () => { + it("should throw if graphQLConfig is not an object", async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); - expectAsync(parseGraphQLController.updateGraphQLConfig([])).toBeRejected(); - expectAsync(parseGraphQLController.updateGraphQLConfig(function () {})).toBeRejected(); - expectAsync(parseGraphQLController.updateGraphQLConfig(Promise.resolve({}))).toBeRejected(); - expectAsync(parseGraphQLController.updateGraphQLConfig('')).toBeRejected(); - expectAsync(parseGraphQLController.updateGraphQLConfig({})).toBeResolvedTo( - successfulUpdateResponse - ); + expectAsync( + parseGraphQLController.updateGraphQLConfig([]) + ).toBeRejected(); + expectAsync( + parseGraphQLController.updateGraphQLConfig(function () {}) + ).toBeRejected(); + expectAsync( + parseGraphQLController.updateGraphQLConfig(Promise.resolve({})) + ).toBeRejected(); + expectAsync( + parseGraphQLController.updateGraphQLConfig("") + ).toBeRejected(); + expectAsync( + parseGraphQLController.updateGraphQLConfig({}) + ).toBeResolvedTo(successfulUpdateResponse); }); - it('should throw if graphQLConfig has an invalid root key', async () => { + it("should throw if graphQLConfig has an invalid root key", async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); - expectAsync(parseGraphQLController.updateGraphQLConfig({ invalidKey: true })).toBeRejected(); - expectAsync(parseGraphQLController.updateGraphQLConfig({})).toBeResolvedTo( - successfulUpdateResponse - ); + expectAsync( + parseGraphQLController.updateGraphQLConfig({ invalidKey: true }) + ).toBeRejected(); + expectAsync( + parseGraphQLController.updateGraphQLConfig({}) + ).toBeResolvedTo(successfulUpdateResponse); }); - it('should throw if graphQLConfig has invalid class filters', async () => { + it("should throw if graphQLConfig has invalid class filters", async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); @@ -258,24 +278,26 @@ describe('ParseGraphQLController', () => { ).toBeRejected(); expectAsync( parseGraphQLController.updateGraphQLConfig({ - enabledForClasses: ['_User', null], + enabledForClasses: ["_User", null], }) ).toBeRejected(); expectAsync( - parseGraphQLController.updateGraphQLConfig({ disabledForClasses: [''] }) + parseGraphQLController.updateGraphQLConfig({ disabledForClasses: [""] }) ).toBeRejected(); expectAsync( parseGraphQLController.updateGraphQLConfig({ enabledForClasses: [], - disabledForClasses: ['_User'], + disabledForClasses: ["_User"], }) ).toBeResolvedTo(successfulUpdateResponse); }); - it('should throw if classConfigs array is invalid', async () => { + it("should throw if classConfigs array is invalid", async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); - expectAsync(parseGraphQLController.updateGraphQLConfig({ classConfigs: {} })).toBeRejected(); + expectAsync( + parseGraphQLController.updateGraphQLConfig({ classConfigs: {} }) + ).toBeRejected(); expectAsync( parseGraphQLController.updateGraphQLConfig({ classConfigs: [null] }) ).toBeRejected(); @@ -286,23 +308,23 @@ describe('ParseGraphQLController', () => { ).toBeRejected(); expectAsync( parseGraphQLController.updateGraphQLConfig({ - classConfigs: [{ className: 'ValidClass' }, null], + classConfigs: [{ className: "ValidClass" }, null], }) ).toBeRejected(); - expectAsync(parseGraphQLController.updateGraphQLConfig({ classConfigs: [] })).toBeResolvedTo( - successfulUpdateResponse - ); + expectAsync( + parseGraphQLController.updateGraphQLConfig({ classConfigs: [] }) + ).toBeResolvedTo(successfulUpdateResponse); expectAsync( parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", }, ], }) ).toBeResolvedTo(successfulUpdateResponse); }); - it('should throw if a classConfig has invalid type settings', async () => { + it("should throw if a classConfig has invalid type settings", async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); @@ -310,7 +332,7 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", type: [], }, ], @@ -320,7 +342,7 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", type: { invalidKey: true, }, @@ -332,14 +354,14 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", type: {}, }, ], }) ).toBeResolvedTo(successfulUpdateResponse); }); - it('should throw if a classConfig has invalid type.inputFields settings', async () => { + it("should throw if a classConfig has invalid type.inputFields settings", async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); @@ -347,7 +369,7 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: 'SuperCar', + className: "SuperCar", type: { inputFields: [], }, @@ -359,7 +381,7 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: 'SuperCar', + className: "SuperCar", type: { inputFields: { invalidKey: true, @@ -373,7 +395,7 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: 'SuperCar', + className: "SuperCar", type: { inputFields: { create: {}, @@ -387,7 +409,7 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: 'SuperCar', + className: "SuperCar", type: { inputFields: { update: [null], @@ -401,7 +423,7 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: 'SuperCar', + className: "SuperCar", type: { inputFields: { create: [], @@ -416,10 +438,10 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: 'SuperCar', + className: "SuperCar", type: { inputFields: { - create: ['make', 'model'], + create: ["make", "model"], update: [], }, }, @@ -428,7 +450,7 @@ describe('ParseGraphQLController', () => { }) ).toBeResolvedTo(successfulUpdateResponse); }); - it('should throw if a classConfig has invalid type.outputFields settings', async () => { + it("should throw if a classConfig has invalid type.outputFields settings", async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); @@ -436,7 +458,7 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", type: { outputFields: {}, }, @@ -448,7 +470,7 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", type: { outputFields: [null], }, @@ -460,9 +482,9 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", type: { - outputFields: ['name', undefined], + outputFields: ["name", undefined], }, }, ], @@ -472,9 +494,9 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", type: { - outputFields: [''], + outputFields: [""], }, }, ], @@ -484,7 +506,7 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", type: { outputFields: [], }, @@ -496,16 +518,16 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", type: { - outputFields: ['name'], + outputFields: ["name"], }, }, ], }) ).toBeResolvedTo(successfulUpdateResponse); }); - it('should throw if a classConfig has invalid type.constraintFields settings', async () => { + it("should throw if a classConfig has invalid type.constraintFields settings", async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); @@ -513,7 +535,7 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", type: { constraintFields: {}, }, @@ -525,7 +547,7 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", type: { constraintFields: [null], }, @@ -537,9 +559,9 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", type: { - constraintFields: ['name', undefined], + constraintFields: ["name", undefined], }, }, ], @@ -549,9 +571,9 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", type: { - constraintFields: [''], + constraintFields: [""], }, }, ], @@ -561,7 +583,7 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", type: { constraintFields: [], }, @@ -573,16 +595,16 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", type: { - constraintFields: ['name'], + constraintFields: ["name"], }, }, ], }) ).toBeResolvedTo(successfulUpdateResponse); }); - it('should throw if a classConfig has invalid type.sortFields settings', async () => { + it("should throw if a classConfig has invalid type.sortFields settings", async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); @@ -590,7 +612,7 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", type: { sortFields: {}, }, @@ -602,7 +624,7 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", type: { sortFields: [null], }, @@ -614,7 +636,7 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", type: { sortFields: [ { @@ -632,11 +654,11 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", type: { sortFields: [ { - field: '', + field: "", asc: true, desc: false, }, @@ -650,13 +672,13 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", type: { sortFields: [ { - field: 'name', + field: "name", asc: true, - desc: 'false', + desc: "false", }, ], }, @@ -668,11 +690,11 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", type: { sortFields: [ { - field: 'name', + field: "name", asc: true, desc: true, }, @@ -687,7 +709,7 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", type: { sortFields: [], }, @@ -699,11 +721,11 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", type: { sortFields: [ { - field: 'name', + field: "name", asc: true, desc: true, }, @@ -714,7 +736,7 @@ describe('ParseGraphQLController', () => { }) ).toBeResolvedTo(successfulUpdateResponse); }); - it('should throw if a classConfig has invalid query params', async () => { + it("should throw if a classConfig has invalid query params", async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); @@ -722,7 +744,7 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", query: [], }, ], @@ -732,7 +754,7 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", query: { invalidKey: true, }, @@ -744,7 +766,7 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", query: { get: 1, }, @@ -756,9 +778,9 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", query: { - find: 'true', + find: "true", }, }, ], @@ -768,7 +790,7 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", query: { get: false, find: true, @@ -781,14 +803,14 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", query: {}, }, ], }) ).toBeResolvedTo(successfulUpdateResponse); }); - it('should throw if a classConfig has invalid mutation params', async () => { + it("should throw if a classConfig has invalid mutation params", async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); @@ -796,7 +818,7 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", mutation: [], }, ], @@ -806,7 +828,7 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", mutation: { invalidKey: true, }, @@ -818,7 +840,7 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", mutation: { destroy: 1, }, @@ -830,9 +852,9 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", mutation: { - update: 'true', + update: "true", }, }, ], @@ -842,7 +864,7 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", mutation: {}, }, ], @@ -852,7 +874,7 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", mutation: { create: true, update: true, @@ -864,7 +886,7 @@ describe('ParseGraphQLController', () => { ).toBeResolvedTo(successfulUpdateResponse); }); - it('should throw if _User create fields is missing username or password', async () => { + it("should throw if _User create fields is missing username or password", async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); @@ -872,10 +894,10 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", type: { inputFields: { - create: ['username', 'no-password'], + create: ["username", "no-password"], }, }, }, @@ -886,10 +908,10 @@ describe('ParseGraphQLController', () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: '_User', + className: "_User", type: { inputFields: { - create: ['username', 'password'], + create: ["username", "password"], }, }, }, @@ -897,7 +919,7 @@ describe('ParseGraphQLController', () => { }) ).toBeResolved(successfulUpdateResponse); }); - it('should update the cache if mounted', async () => { + it("should update the cache if mounted", async () => { removeConfigFromDb(); cacheController.graphQL.clear(); const mountedController = new ParseGraphQLController({ @@ -914,37 +936,45 @@ describe('ParseGraphQLController', () => { let cacheBeforeValue; let cacheAfterValue; - cacheBeforeValue = await cacheController.graphQL.get(mountedController.configCacheKey); + cacheBeforeValue = await cacheController.graphQL.get( + mountedController.configCacheKey + ); expect(cacheBeforeValue).toBeNull(); await mountedController.updateGraphQLConfig({ - enabledForClasses: ['SuperCar'], + enabledForClasses: ["SuperCar"], }); - cacheAfterValue = await cacheController.graphQL.get(mountedController.configCacheKey); - expect(cacheAfterValue).toEqual({ enabledForClasses: ['SuperCar'] }); + cacheAfterValue = await cacheController.graphQL.get( + mountedController.configCacheKey + ); + expect(cacheAfterValue).toEqual({ enabledForClasses: ["SuperCar"] }); // reset removeConfigFromDb(); cacheController.graphQL.clear(); - cacheBeforeValue = await cacheController.graphQL.get(unmountedController.configCacheKey); + cacheBeforeValue = await cacheController.graphQL.get( + unmountedController.configCacheKey + ); expect(cacheBeforeValue).toBeNull(); await unmountedController.updateGraphQLConfig({ - enabledForClasses: ['SuperCar'], + enabledForClasses: ["SuperCar"], }); - cacheAfterValue = await cacheController.graphQL.get(unmountedController.configCacheKey); + cacheAfterValue = await cacheController.graphQL.get( + unmountedController.configCacheKey + ); expect(cacheAfterValue).toBeNull(); }); }); - describe('alias', () => { - it('should fail if query alias is not a string', async () => { + describe("alias", () => { + it("should fail if query alias is not a string", async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); - const className = 'Bar'; + const className = "Bar"; expectAsync( parseGraphQLController.updateGraphQLConfig({ @@ -969,7 +999,7 @@ describe('ParseGraphQLController', () => { className, query: { find: true, - findAlias: { not: 'valid' }, + findAlias: { not: "valid" }, }, }, ], @@ -979,12 +1009,12 @@ describe('ParseGraphQLController', () => { ); }); - it('should fail if mutation alias is not a string', async () => { + it("should fail if mutation alias is not a string", async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); - const className = 'Bar'; + const className = "Bar"; expectAsync( parseGraphQLController.updateGraphQLConfig({ @@ -1025,7 +1055,7 @@ describe('ParseGraphQLController', () => { className, mutation: { destroy: true, - destroyAlias: { not: 'valid' }, + destroyAlias: { not: "valid" }, }, }, ], diff --git a/spec/ParseGraphQLSchema.spec.js b/spec/ParseGraphQLSchema.spec.js index 199c52756c..5c69eea1af 100644 --- a/spec/ParseGraphQLSchema.spec.js +++ b/spec/ParseGraphQLSchema.spec.js @@ -1,13 +1,13 @@ -const { GraphQLObjectType } = require('graphql'); -const defaultLogger = require('../lib/logger').default; -const { ParseGraphQLSchema } = require('../lib/GraphQL/ParseGraphQLSchema'); +const { GraphQLObjectType } = require("graphql"); +const defaultLogger = require("../lib/logger").default; +const { ParseGraphQLSchema } = require("../lib/GraphQL/ParseGraphQLSchema"); -describe('ParseGraphQLSchema', () => { +describe("ParseGraphQLSchema", () => { let parseServer; let databaseController; let parseGraphQLController; let parseGraphQLSchema; - const appId = 'test'; + const appId = "test"; beforeEach(async () => { parseServer = await global.reconfigureServer(); @@ -21,21 +21,21 @@ describe('ParseGraphQLSchema', () => { }); }); - describe('constructor', () => { - it('should require a parseGraphQLController, databaseController, a log instance, and the appId', () => { + describe("constructor", () => { + it("should require a parseGraphQLController, databaseController, a log instance, and the appId", () => { expect(() => new ParseGraphQLSchema()).toThrow( - 'You must provide a parseGraphQLController instance!' - ); - expect(() => new ParseGraphQLSchema({ parseGraphQLController: {} })).toThrow( - 'You must provide a databaseController instance!' + "You must provide a parseGraphQLController instance!" ); + expect( + () => new ParseGraphQLSchema({ parseGraphQLController: {} }) + ).toThrow("You must provide a databaseController instance!"); expect( () => new ParseGraphQLSchema({ parseGraphQLController: {}, databaseController: {}, }) - ).toThrow('You must provide a log instance!'); + ).toThrow("You must provide a log instance!"); expect( () => new ParseGraphQLSchema({ @@ -43,18 +43,18 @@ describe('ParseGraphQLSchema', () => { databaseController: {}, log: {}, }) - ).toThrow('You must provide the appId!'); + ).toThrow("You must provide the appId!"); }); }); - describe('load', () => { - it('should cache schema', async () => { + describe("load", () => { + it("should cache schema", async () => { const graphQLSchema = await parseGraphQLSchema.load(); const updatedGraphQLSchema = await parseGraphQLSchema.load(); expect(graphQLSchema).toBe(updatedGraphQLSchema); }); - it('should load a brand new GraphQL Schema if Parse Schema changes', async () => { + it("should load a brand new GraphQL Schema if Parse Schema changes", async () => { await parseGraphQLSchema.load(); const parseClasses = parseGraphQLSchema.parseClasses; const parseClassTypes = parseGraphQLSchema.parseClassTypes; @@ -63,7 +63,7 @@ describe('ParseGraphQLSchema', () => { const graphQLQueries = parseGraphQLSchema.graphQLQueries; const graphQLMutations = parseGraphQLSchema.graphQLMutations; const graphQLSubscriptions = parseGraphQLSchema.graphQLSubscriptions; - const newClassObject = new Parse.Object('NewClass'); + const newClassObject = new Parse.Object("NewClass"); await newClassObject.save(); await parseServer.config.schemaCache.clear(); await new Promise(resolve => setTimeout(resolve, 200)); @@ -74,10 +74,12 @@ describe('ParseGraphQLSchema', () => { expect(graphQLTypes).not.toBe(parseGraphQLSchema.graphQLTypes); expect(graphQLQueries).not.toBe(parseGraphQLSchema.graphQLQueries); expect(graphQLMutations).not.toBe(parseGraphQLSchema.graphQLMutations); - expect(graphQLSubscriptions).not.toBe(parseGraphQLSchema.graphQLSubscriptions); + expect(graphQLSubscriptions).not.toBe( + parseGraphQLSchema.graphQLSubscriptions + ); }); - it('should load a brand new GraphQL Schema if graphQLConfig changes', async () => { + it("should load a brand new GraphQL Schema if graphQLConfig changes", async () => { const parseGraphQLController = { graphQLConfig: { enabledForClasses: [] }, getGraphQLConfig() { @@ -100,7 +102,7 @@ describe('ParseGraphQLSchema', () => { const graphQLSubscriptions = parseGraphQLSchema.graphQLSubscriptions; parseGraphQLController.graphQLConfig = { - enabledForClasses: ['_User'], + enabledForClasses: ["_User"], }; await new Promise(resolve => setTimeout(resolve, 200)); @@ -111,12 +113,14 @@ describe('ParseGraphQLSchema', () => { expect(graphQLTypes).not.toBe(parseGraphQLSchema.graphQLTypes); expect(graphQLQueries).not.toBe(parseGraphQLSchema.graphQLQueries); expect(graphQLMutations).not.toBe(parseGraphQLSchema.graphQLMutations); - expect(graphQLSubscriptions).not.toBe(parseGraphQLSchema.graphQLSubscriptions); + expect(graphQLSubscriptions).not.toBe( + parseGraphQLSchema.graphQLSubscriptions + ); }); }); - describe('addGraphQLType', () => { - it('should not load and warn duplicated types', async () => { + describe("addGraphQLType", () => { + it("should not load and warn duplicated types", async () => { let logged = false; const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, @@ -125,45 +129,50 @@ describe('ParseGraphQLSchema', () => { warn: message => { logged = true; expect(message).toEqual( - 'Type SomeClass could not be added to the auto schema because it collided with an existing type.' + "Type SomeClass could not be added to the auto schema because it collided with an existing type." ); }, }, appId, }); await parseGraphQLSchema.load(); - const type = new GraphQLObjectType({ name: 'SomeClass' }); + const type = new GraphQLObjectType({ name: "SomeClass" }); expect(parseGraphQLSchema.addGraphQLType(type)).toBe(type); expect(parseGraphQLSchema.graphQLTypes).toContain(type); expect( - parseGraphQLSchema.addGraphQLType(new GraphQLObjectType({ name: 'SomeClass' })) + parseGraphQLSchema.addGraphQLType( + new GraphQLObjectType({ name: "SomeClass" }) + ) ).toBeUndefined(); expect(logged).toBeTruthy(); }); - it('should throw error when required', async () => { + it("should throw error when required", async () => { const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, parseGraphQLController, log: { warn: () => { - fail('Should not warn'); + fail("Should not warn"); }, }, appId, }); await parseGraphQLSchema.load(); - const type = new GraphQLObjectType({ name: 'SomeClass' }); + const type = new GraphQLObjectType({ name: "SomeClass" }); expect(parseGraphQLSchema.addGraphQLType(type, true)).toBe(type); expect(parseGraphQLSchema.graphQLTypes).toContain(type); expect(() => - parseGraphQLSchema.addGraphQLType(new GraphQLObjectType({ name: 'SomeClass' }), true) + parseGraphQLSchema.addGraphQLType( + new GraphQLObjectType({ name: "SomeClass" }), + true + ) ).toThrowError( - 'Type SomeClass could not be added to the auto schema because it collided with an existing type.' + "Type SomeClass could not be added to the auto schema because it collided with an existing type." ); }); - it('should warn reserved name collision', async () => { + it("should warn reserved name collision", async () => { let logged = false; const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, @@ -172,7 +181,7 @@ describe('ParseGraphQLSchema', () => { warn: message => { logged = true; expect(message).toEqual( - 'Type String could not be added to the auto schema because it collided with an existing type.' + "Type String could not be added to the auto schema because it collided with an existing type." ); }, }, @@ -180,31 +189,33 @@ describe('ParseGraphQLSchema', () => { }); await parseGraphQLSchema.load(); expect( - parseGraphQLSchema.addGraphQLType(new GraphQLObjectType({ name: 'String' })) + parseGraphQLSchema.addGraphQLType( + new GraphQLObjectType({ name: "String" }) + ) ).toBeUndefined(); expect(logged).toBeTruthy(); }); - it('should ignore collision when necessary', async () => { + it("should ignore collision when necessary", async () => { const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, parseGraphQLController, log: { warn: () => { - fail('Should not warn'); + fail("Should not warn"); }, }, appId, }); await parseGraphQLSchema.load(); - const type = new GraphQLObjectType({ name: 'String' }); + const type = new GraphQLObjectType({ name: "String" }); expect(parseGraphQLSchema.addGraphQLType(type, true, true)).toBe(type); expect(parseGraphQLSchema.graphQLTypes).toContain(type); }); }); - describe('addGraphQLQuery', () => { - it('should not load and warn duplicated queries', async () => { + describe("addGraphQLQuery", () => { + it("should not load and warn duplicated queries", async () => { let logged = false; const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, @@ -213,7 +224,7 @@ describe('ParseGraphQLSchema', () => { warn: message => { logged = true; expect(message).toEqual( - 'Query someClasses could not be added to the auto schema because it collided with an existing field.' + "Query someClasses could not be added to the auto schema because it collided with an existing field." ); }, }, @@ -221,33 +232,41 @@ describe('ParseGraphQLSchema', () => { }); await parseGraphQLSchema.load(); const field = {}; - expect(parseGraphQLSchema.addGraphQLQuery('someClasses', field)).toBe(field); - expect(parseGraphQLSchema.graphQLQueries['someClasses']).toBe(field); - expect(parseGraphQLSchema.addGraphQLQuery('someClasses', {})).toBeUndefined(); + expect(parseGraphQLSchema.addGraphQLQuery("someClasses", field)).toBe( + field + ); + expect(parseGraphQLSchema.graphQLQueries["someClasses"]).toBe(field); + expect( + parseGraphQLSchema.addGraphQLQuery("someClasses", {}) + ).toBeUndefined(); expect(logged).toBeTruthy(); }); - it('should throw error when required', async () => { + it("should throw error when required", async () => { const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, parseGraphQLController, log: { warn: () => { - fail('Should not warn'); + fail("Should not warn"); }, }, appId, }); await parseGraphQLSchema.load(); const field = {}; - expect(parseGraphQLSchema.addGraphQLQuery('someClasses', field)).toBe(field); - expect(parseGraphQLSchema.graphQLQueries['someClasses']).toBe(field); - expect(() => parseGraphQLSchema.addGraphQLQuery('someClasses', {}, true)).toThrowError( - 'Query someClasses could not be added to the auto schema because it collided with an existing field.' + expect(parseGraphQLSchema.addGraphQLQuery("someClasses", field)).toBe( + field + ); + expect(parseGraphQLSchema.graphQLQueries["someClasses"]).toBe(field); + expect(() => + parseGraphQLSchema.addGraphQLQuery("someClasses", {}, true) + ).toThrowError( + "Query someClasses could not be added to the auto schema because it collided with an existing field." ); }); - it('should warn reserved name collision', async () => { + it("should warn reserved name collision", async () => { let logged = false; const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, @@ -256,24 +275,24 @@ describe('ParseGraphQLSchema', () => { warn: message => { logged = true; expect(message).toEqual( - 'Query viewer could not be added to the auto schema because it collided with an existing field.' + "Query viewer could not be added to the auto schema because it collided with an existing field." ); }, }, appId, }); await parseGraphQLSchema.load(); - expect(parseGraphQLSchema.addGraphQLQuery('viewer', {})).toBeUndefined(); + expect(parseGraphQLSchema.addGraphQLQuery("viewer", {})).toBeUndefined(); expect(logged).toBeTruthy(); }); - it('should ignore collision when necessary', async () => { + it("should ignore collision when necessary", async () => { const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, parseGraphQLController, log: { warn: () => { - fail('Should not warn'); + fail("Should not warn"); }, }, appId, @@ -281,13 +300,15 @@ describe('ParseGraphQLSchema', () => { await parseGraphQLSchema.load(); delete parseGraphQLSchema.graphQLQueries.viewer; const field = {}; - expect(parseGraphQLSchema.addGraphQLQuery('viewer', field, true, true)).toBe(field); - expect(parseGraphQLSchema.graphQLQueries['viewer']).toBe(field); + expect( + parseGraphQLSchema.addGraphQLQuery("viewer", field, true, true) + ).toBe(field); + expect(parseGraphQLSchema.graphQLQueries["viewer"]).toBe(field); }); }); - describe('addGraphQLMutation', () => { - it('should not load and warn duplicated mutations', async () => { + describe("addGraphQLMutation", () => { + it("should not load and warn duplicated mutations", async () => { let logged = false; const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, @@ -296,7 +317,7 @@ describe('ParseGraphQLSchema', () => { warn: message => { logged = true; expect(message).toEqual( - 'Mutation createSomeClass could not be added to the auto schema because it collided with an existing field.' + "Mutation createSomeClass could not be added to the auto schema because it collided with an existing field." ); }, }, @@ -304,33 +325,45 @@ describe('ParseGraphQLSchema', () => { }); await parseGraphQLSchema.load(); const field = {}; - expect(parseGraphQLSchema.addGraphQLMutation('createSomeClass', field)).toBe(field); - expect(parseGraphQLSchema.graphQLMutations['createSomeClass']).toBe(field); - expect(parseGraphQLSchema.addGraphQLMutation('createSomeClass', {})).toBeUndefined(); + expect( + parseGraphQLSchema.addGraphQLMutation("createSomeClass", field) + ).toBe(field); + expect(parseGraphQLSchema.graphQLMutations["createSomeClass"]).toBe( + field + ); + expect( + parseGraphQLSchema.addGraphQLMutation("createSomeClass", {}) + ).toBeUndefined(); expect(logged).toBeTruthy(); }); - it('should throw error when required', async () => { + it("should throw error when required", async () => { const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, parseGraphQLController, log: { warn: () => { - fail('Should not warn'); + fail("Should not warn"); }, }, appId, }); await parseGraphQLSchema.load(); const field = {}; - expect(parseGraphQLSchema.addGraphQLMutation('createSomeClass', field)).toBe(field); - expect(parseGraphQLSchema.graphQLMutations['createSomeClass']).toBe(field); - expect(() => parseGraphQLSchema.addGraphQLMutation('createSomeClass', {}, true)).toThrowError( - 'Mutation createSomeClass could not be added to the auto schema because it collided with an existing field.' + expect( + parseGraphQLSchema.addGraphQLMutation("createSomeClass", field) + ).toBe(field); + expect(parseGraphQLSchema.graphQLMutations["createSomeClass"]).toBe( + field + ); + expect(() => + parseGraphQLSchema.addGraphQLMutation("createSomeClass", {}, true) + ).toThrowError( + "Mutation createSomeClass could not be added to the auto schema because it collided with an existing field." ); }); - it('should warn reserved name collision', async () => { + it("should warn reserved name collision", async () => { let logged = false; const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, @@ -339,24 +372,26 @@ describe('ParseGraphQLSchema', () => { warn: message => { logged = true; expect(message).toEqual( - 'Mutation signUp could not be added to the auto schema because it collided with an existing field.' + "Mutation signUp could not be added to the auto schema because it collided with an existing field." ); }, }, appId, }); await parseGraphQLSchema.load(); - expect(parseGraphQLSchema.addGraphQLMutation('signUp', {})).toBeUndefined(); + expect( + parseGraphQLSchema.addGraphQLMutation("signUp", {}) + ).toBeUndefined(); expect(logged).toBeTruthy(); }); - it('should ignore collision when necessary', async () => { + it("should ignore collision when necessary", async () => { const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, parseGraphQLController, log: { warn: () => { - fail('Should not warn'); + fail("Should not warn"); }, }, appId, @@ -364,19 +399,21 @@ describe('ParseGraphQLSchema', () => { await parseGraphQLSchema.load(); delete parseGraphQLSchema.graphQLMutations.signUp; const field = {}; - expect(parseGraphQLSchema.addGraphQLMutation('signUp', field, true, true)).toBe(field); - expect(parseGraphQLSchema.graphQLMutations['signUp']).toBe(field); + expect( + parseGraphQLSchema.addGraphQLMutation("signUp", field, true, true) + ).toBe(field); + expect(parseGraphQLSchema.graphQLMutations["signUp"]).toBe(field); }); }); - describe('_getParseClassesWithConfig', () => { - it('should sort classes', () => { + describe("_getParseClassesWithConfig", () => { + it("should sort classes", () => { const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, parseGraphQLController, log: { warn: () => { - fail('Should not warn'); + fail("Should not warn"); }, }, appId, @@ -385,14 +422,14 @@ describe('ParseGraphQLSchema', () => { parseGraphQLSchema ._getParseClassesWithConfig( [ - { className: 'b' }, - { className: '_b' }, - { className: 'B' }, - { className: '_B' }, - { className: 'a' }, - { className: '_a' }, - { className: 'A' }, - { className: '_A' }, + { className: "b" }, + { className: "_b" }, + { className: "B" }, + { className: "_B" }, + { className: "a" }, + { className: "_a" }, + { className: "A" }, + { className: "_A" }, ], { classConfigs: [], @@ -400,20 +437,20 @@ describe('ParseGraphQLSchema', () => { ) .map(item => item[0]) ).toEqual([ - { className: '_A' }, - { className: '_B' }, - { className: '_a' }, - { className: '_b' }, - { className: 'A' }, - { className: 'B' }, - { className: 'a' }, - { className: 'b' }, + { className: "_A" }, + { className: "_B" }, + { className: "_a" }, + { className: "_b" }, + { className: "A" }, + { className: "B" }, + { className: "a" }, + { className: "b" }, ]); }); }); - describe('name collision', () => { - it('should not generate duplicate types when colliding to default classes', async () => { + describe("name collision", () => { + it("should not generate duplicate types when colliding to default classes", async () => { const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, parseGraphQLController, @@ -425,7 +462,7 @@ describe('ParseGraphQLSchema', () => { const types1 = parseGraphQLSchema.graphQLTypes; const queries1 = parseGraphQLSchema.graphQLQueries; const mutations1 = parseGraphQLSchema.graphQLMutations; - const user = new Parse.Object('User'); + const user = new Parse.Object("User"); await user.save(); await parseGraphQLSchema.schemaCache.clear(); const schema2 = await parseGraphQLSchema.load(); @@ -434,28 +471,34 @@ describe('ParseGraphQLSchema', () => { const mutations2 = parseGraphQLSchema.graphQLMutations; expect(schema1).not.toBe(schema2); expect(types1).not.toBe(types2); - expect(types1.map(type => type.name).sort()).toEqual(types2.map(type => type.name).sort()); + expect(types1.map(type => type.name).sort()).toEqual( + types2.map(type => type.name).sort() + ); expect(queries1).not.toBe(queries2); - expect(Object.keys(queries1).sort()).toEqual(Object.keys(queries2).sort()); + expect(Object.keys(queries1).sort()).toEqual( + Object.keys(queries2).sort() + ); expect(mutations1).not.toBe(mutations2); - expect(Object.keys(mutations1).sort()).toEqual(Object.keys(mutations2).sort()); + expect(Object.keys(mutations1).sort()).toEqual( + Object.keys(mutations2).sort() + ); }); - it('should not generate duplicate types when colliding the same name', async () => { + it("should not generate duplicate types when colliding the same name", async () => { const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, parseGraphQLController, log: defaultLogger, appId, }); - const car1 = new Parse.Object('Car'); + const car1 = new Parse.Object("Car"); await car1.save(); await parseGraphQLSchema.schemaCache.clear(); const schema1 = await parseGraphQLSchema.load(); const types1 = parseGraphQLSchema.graphQLTypes; const queries1 = parseGraphQLSchema.graphQLQueries; const mutations1 = parseGraphQLSchema.graphQLMutations; - const car2 = new Parse.Object('car'); + const car2 = new Parse.Object("car"); await car2.save(); await parseGraphQLSchema.schemaCache.clear(); const schema2 = await parseGraphQLSchema.load(); @@ -464,27 +507,33 @@ describe('ParseGraphQLSchema', () => { const mutations2 = parseGraphQLSchema.graphQLMutations; expect(schema1).not.toBe(schema2); expect(types1).not.toBe(types2); - expect(types1.map(type => type.name).sort()).toEqual(types2.map(type => type.name).sort()); + expect(types1.map(type => type.name).sort()).toEqual( + types2.map(type => type.name).sort() + ); expect(queries1).not.toBe(queries2); - expect(Object.keys(queries1).sort()).toEqual(Object.keys(queries2).sort()); + expect(Object.keys(queries1).sort()).toEqual( + Object.keys(queries2).sort() + ); expect(mutations1).not.toBe(mutations2); - expect(Object.keys(mutations1).sort()).toEqual(Object.keys(mutations2).sort()); + expect(Object.keys(mutations1).sort()).toEqual( + Object.keys(mutations2).sort() + ); }); - it('should not generate duplicate queries when query name collide', async () => { + it("should not generate duplicate queries when query name collide", async () => { const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, parseGraphQLController, log: defaultLogger, appId, }); - const car = new Parse.Object('Car'); + const car = new Parse.Object("Car"); await car.save(); await parseGraphQLSchema.schemaCache.clear(); const schema1 = await parseGraphQLSchema.load(); const queries1 = parseGraphQLSchema.graphQLQueries; const mutations1 = parseGraphQLSchema.graphQLMutations; - const cars = new Parse.Object('cars'); + const cars = new Parse.Object("cars"); await cars.save(); await parseGraphQLSchema.schemaCache.clear(); const schema2 = await parseGraphQLSchema.load(); @@ -492,16 +541,20 @@ describe('ParseGraphQLSchema', () => { const mutations2 = parseGraphQLSchema.graphQLMutations; expect(schema1).not.toBe(schema2); expect(queries1).not.toBe(queries2); - expect(Object.keys(queries1).sort()).toEqual(Object.keys(queries2).sort()); + expect(Object.keys(queries1).sort()).toEqual( + Object.keys(queries2).sort() + ); expect(mutations1).not.toBe(mutations2); expect( - Object.keys(mutations1).concat('createCars', 'updateCars', 'deleteCars').sort() + Object.keys(mutations1) + .concat("createCars", "updateCars", "deleteCars") + .sort() ).toEqual(Object.keys(mutations2).sort()); }); }); - describe('alias', () => { - it_id('45282d26-f4c7-4d2d-a7b6-cd8741d5322f')(it)( - 'Should be able to define alias for get and find query', + describe("alias", () => { + it_id("45282d26-f4c7-4d2d-a7b6-cd8741d5322f")(it)( + "Should be able to define alias for get and find query", async () => { const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, @@ -513,18 +566,18 @@ describe('ParseGraphQLSchema', () => { await parseGraphQLSchema.parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: 'Data', + className: "Data", query: { get: true, - getAlias: 'precious_data', + getAlias: "precious_data", find: true, - findAlias: 'data_results', + findAlias: "data_results", }, }, ], }); - const data = new Parse.Object('Data'); + const data = new Parse.Object("Data"); await data.save(); @@ -533,13 +586,13 @@ describe('ParseGraphQLSchema', () => { const queries1 = parseGraphQLSchema.graphQLQueries; - expect(Object.keys(queries1)).toContain('data_results'); - expect(Object.keys(queries1)).toContain('precious_data'); + expect(Object.keys(queries1)).toContain("data_results"); + expect(Object.keys(queries1)).toContain("precious_data"); } ); - it_id('f04b46e3-a25d-401d-a315-3298cfee1df8')(it)( - 'Should be able to define alias for mutation', + it_id("f04b46e3-a25d-401d-a315-3298cfee1df8")(it)( + "Should be able to define alias for mutation", async () => { const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, @@ -551,20 +604,20 @@ describe('ParseGraphQLSchema', () => { await parseGraphQLSchema.parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: 'Track', + className: "Track", mutation: { create: true, - createAlias: 'addTrack', + createAlias: "addTrack", update: true, - updateAlias: 'modifyTrack', + updateAlias: "modifyTrack", destroy: true, - destroyAlias: 'eraseTrack', + destroyAlias: "eraseTrack", }, }, ], }); - const data = new Parse.Object('Track'); + const data = new Parse.Object("Track"); await data.save(); @@ -573,9 +626,9 @@ describe('ParseGraphQLSchema', () => { const mutations = parseGraphQLSchema.graphQLMutations; - expect(Object.keys(mutations)).toContain('addTrack'); - expect(Object.keys(mutations)).toContain('modifyTrack'); - expect(Object.keys(mutations)).toContain('eraseTrack'); + expect(Object.keys(mutations)).toContain("addTrack"); + expect(Object.keys(mutations)).toContain("modifyTrack"); + expect(Object.keys(mutations)).toContain("eraseTrack"); } ); }); diff --git a/spec/ParseGraphQLServer.spec.js b/spec/ParseGraphQLServer.spec.js index 414310d05e..20e735f743 100644 --- a/spec/ParseGraphQLServer.spec.js +++ b/spec/ParseGraphQLServer.spec.js @@ -1,27 +1,31 @@ -const http = require('http'); -const express = require('express'); -const req = require('../lib/request'); -const fetch = (...args) => import('node-fetch').then(({ default: fetch }) => fetch(...args)); -const FormData = require('form-data'); -const ws = require('ws'); -require('./helper'); -const { updateCLP } = require('./support/dev'); - -const pluralize = require('pluralize'); -const { getMainDefinition } = require('@apollo/client/utilities'); -const createUploadLink = (...args) => import('apollo-upload-client/createUploadLink.mjs').then(({ default: fn }) => fn(...args)); -const { SubscriptionClient } = require('subscriptions-transport-ws'); -const { WebSocketLink } = require('@apollo/client/link/ws'); -const { mergeSchemas } = require('@graphql-tools/schema'); +const http = require("http"); +const express = require("express"); +const req = require("../lib/request"); +const fetch = (...args) => + import("node-fetch").then(({ default: fetch }) => fetch(...args)); +const FormData = require("form-data"); +const ws = require("ws"); +require("./helper"); +const { updateCLP } = require("./support/dev"); + +const pluralize = require("pluralize"); +const { getMainDefinition } = require("@apollo/client/utilities"); +const createUploadLink = (...args) => + import("apollo-upload-client/createUploadLink.mjs").then(({ default: fn }) => + fn(...args) + ); +const { SubscriptionClient } = require("subscriptions-transport-ws"); +const { WebSocketLink } = require("@apollo/client/link/ws"); +const { mergeSchemas } = require("@graphql-tools/schema"); const { ApolloClient, InMemoryCache, ApolloLink, split, createHttpLink, -} = require('@apollo/client/core'); -const gql = require('graphql-tag'); -const { toGlobalId } = require('graphql-relay'); +} = require("@apollo/client/core"); +const gql = require("graphql-tag"); +const { toGlobalId } = require("graphql-relay"); const { GraphQLObjectType, GraphQLString, @@ -30,54 +34,61 @@ const { GraphQLInputObjectType, GraphQLSchema, GraphQLList, -} = require('graphql'); -const { ParseServer } = require('../'); -const { ParseGraphQLServer } = require('../lib/GraphQL/ParseGraphQLServer'); -const { ReadPreference, Collection } = require('mongodb'); -const { v4: uuidv4 } = require('uuid'); +} = require("graphql"); +const { ParseServer } = require("../"); +const { ParseGraphQLServer } = require("../lib/GraphQL/ParseGraphQLServer"); +const { ReadPreference, Collection } = require("mongodb"); +const { v4: uuidv4 } = require("uuid"); function handleError(e) { - if (e && e.networkError && e.networkError.result && e.networkError.result.errors) { + if ( + e && + e.networkError && + e.networkError.result && + e.networkError.result.errors + ) { fail(e.networkError.result.errors); } else { fail(e); } } -describe('ParseGraphQLServer', () => { +describe("ParseGraphQLServer", () => { let parseServer; let parseGraphQLServer; beforeEach(async () => { parseServer = await global.reconfigureServer({ - maxUploadSize: '1kb', + maxUploadSize: "1kb", }); parseGraphQLServer = new ParseGraphQLServer(parseServer, { - graphQLPath: '/graphql', - playgroundPath: '/playground', - subscriptionsPath: '/subscriptions', + graphQLPath: "/graphql", + playgroundPath: "/playground", + subscriptionsPath: "/subscriptions", }); }); - describe('constructor', () => { - it('should require a parseServer instance', () => { - expect(() => new ParseGraphQLServer()).toThrow('You must provide a parseServer instance!'); + describe("constructor", () => { + it("should require a parseServer instance", () => { + expect(() => new ParseGraphQLServer()).toThrow( + "You must provide a parseServer instance!" + ); }); - it('should require config.graphQLPath', () => { + it("should require config.graphQLPath", () => { expect(() => new ParseGraphQLServer(parseServer)).toThrow( - 'You must provide a config.graphQLPath!' + "You must provide a config.graphQLPath!" ); expect(() => new ParseGraphQLServer(parseServer, {})).toThrow( - 'You must provide a config.graphQLPath!' + "You must provide a config.graphQLPath!" ); }); - it('should only require parseServer and config.graphQLPath args', () => { + it("should only require parseServer and config.graphQLPath args", () => { let parseGraphQLServer; expect(() => { parseGraphQLServer = new ParseGraphQLServer(parseServer, { - graphQLPath: 'graphql', + graphQLPath: "graphql", }); }).not.toThrow(); expect(parseGraphQLServer.parseGraphQLSchema).toBeDefined(); @@ -86,7 +97,7 @@ describe('ParseGraphQLServer', () => { ); }); - it('should initialize parseGraphQLSchema with a log controller', async () => { + it("should initialize parseGraphQLSchema with a log controller", async () => { const loggerAdapter = { log: () => {}, error: () => {}, @@ -95,21 +106,23 @@ describe('ParseGraphQLServer', () => { loggerAdapter, }); const parseGraphQLServer = new ParseGraphQLServer(parseServer, { - graphQLPath: 'graphql', + graphQLPath: "graphql", }); - expect(parseGraphQLServer.parseGraphQLSchema.log.adapter).toBe(loggerAdapter); + expect(parseGraphQLServer.parseGraphQLSchema.log.adapter).toBe( + loggerAdapter + ); }); }); - describe('_getServer', () => { - it('should only return new server on schema changes', async () => { + describe("_getServer", () => { + it("should only return new server on schema changes", async () => { parseGraphQLServer.server = undefined; const server1 = await parseGraphQLServer._getServer(); const server2 = await parseGraphQLServer._getServer(); expect(server1).toBe(server2); // Trigger a schema change - const obj = new Parse.Object('SomeClass'); + const obj = new Parse.Object("SomeClass"); await obj.save(); const server3 = await parseGraphQLServer._getServer(); @@ -119,7 +132,7 @@ describe('ParseGraphQLServer', () => { }); }); - describe('_getGraphQLOptions', () => { + describe("_getGraphQLOptions", () => { const req = { info: new Object(), config: new Object(), @@ -130,54 +143,73 @@ describe('ParseGraphQLServer', () => { set: () => {}, }; - it_id('0696675e-060f-414f-bc77-9d57f31807f5')(it)('should return schema and context with req\'s info, config and auth', async () => { - const options = await parseGraphQLServer._getGraphQLOptions(); - expect(options.schema).toEqual(parseGraphQLServer.parseGraphQLSchema.graphQLSchema); - const contextResponse = await options.context({ req, res }); - expect(contextResponse.info).toEqual(req.info); - expect(contextResponse.config).toEqual(req.config); - expect(contextResponse.auth).toEqual(req.auth); - }); + it_id("0696675e-060f-414f-bc77-9d57f31807f5")(it)( + "should return schema and context with req's info, config and auth", + async () => { + const options = await parseGraphQLServer._getGraphQLOptions(); + expect(options.schema).toEqual( + parseGraphQLServer.parseGraphQLSchema.graphQLSchema + ); + const contextResponse = await options.context({ req, res }); + expect(contextResponse.info).toEqual(req.info); + expect(contextResponse.config).toEqual(req.config); + expect(contextResponse.auth).toEqual(req.auth); + } + ); - it('should load GraphQL schema in every call', async () => { + it("should load GraphQL schema in every call", async () => { const originalLoad = parseGraphQLServer.parseGraphQLSchema.load; let counter = 0; parseGraphQLServer.parseGraphQLSchema.load = () => ++counter; - expect((await parseGraphQLServer._getGraphQLOptions(req)).schema).toEqual(1); - expect((await parseGraphQLServer._getGraphQLOptions(req)).schema).toEqual(2); - expect((await parseGraphQLServer._getGraphQLOptions(req)).schema).toEqual(3); + expect((await parseGraphQLServer._getGraphQLOptions(req)).schema).toEqual( + 1 + ); + expect((await parseGraphQLServer._getGraphQLOptions(req)).schema).toEqual( + 2 + ); + expect((await parseGraphQLServer._getGraphQLOptions(req)).schema).toEqual( + 3 + ); parseGraphQLServer.parseGraphQLSchema.load = originalLoad; }); }); - describe('_transformMaxUploadSizeToBytes', () => { - it('should transform to bytes', () => { - expect(parseGraphQLServer._transformMaxUploadSizeToBytes('20mb')).toBe(20971520); - expect(parseGraphQLServer._transformMaxUploadSizeToBytes('333Gb')).toBe(357556027392); - expect(parseGraphQLServer._transformMaxUploadSizeToBytes('123456KB')).toBe(126418944); + describe("_transformMaxUploadSizeToBytes", () => { + it("should transform to bytes", () => { + expect(parseGraphQLServer._transformMaxUploadSizeToBytes("20mb")).toBe( + 20971520 + ); + expect(parseGraphQLServer._transformMaxUploadSizeToBytes("333Gb")).toBe( + 357556027392 + ); + expect( + parseGraphQLServer._transformMaxUploadSizeToBytes("123456KB") + ).toBe(126418944); }); }); - describe('applyGraphQL', () => { - it('should require an Express.js app instance', () => { + describe("applyGraphQL", () => { + it("should require an Express.js app instance", () => { expect(() => parseGraphQLServer.applyGraphQL()).toThrow( - 'You must provide an Express.js app instance!' + "You must provide an Express.js app instance!" ); expect(() => parseGraphQLServer.applyGraphQL({})).toThrow( - 'You must provide an Express.js app instance!' + "You must provide an Express.js app instance!" ); - expect(() => parseGraphQLServer.applyGraphQL(new express())).not.toThrow(); + expect(() => + parseGraphQLServer.applyGraphQL(new express()) + ).not.toThrow(); }); - it('should apply middlewares at config.graphQLPath', () => { + it("should apply middlewares at config.graphQLPath", () => { let useCount = 0; expect(() => new ParseGraphQLServer(parseServer, { - graphQLPath: 'somepath', + graphQLPath: "somepath", }).applyGraphQL({ use: path => { useCount++; - expect(path).toEqual('somepath'); + expect(path).toEqual("somepath"); }, }) ).not.toThrow(); @@ -185,35 +217,37 @@ describe('ParseGraphQLServer', () => { }); }); - describe('applyPlayground', () => { - it('should require an Express.js app instance', () => { + describe("applyPlayground", () => { + it("should require an Express.js app instance", () => { expect(() => parseGraphQLServer.applyPlayground()).toThrow( - 'You must provide an Express.js app instance!' + "You must provide an Express.js app instance!" ); expect(() => parseGraphQLServer.applyPlayground({})).toThrow( - 'You must provide an Express.js app instance!' + "You must provide an Express.js app instance!" ); - expect(() => parseGraphQLServer.applyPlayground(new express())).not.toThrow(); + expect(() => + parseGraphQLServer.applyPlayground(new express()) + ).not.toThrow(); }); - it('should require initialization with config.playgroundPath', () => { + it("should require initialization with config.playgroundPath", () => { expect(() => new ParseGraphQLServer(parseServer, { - graphQLPath: 'graphql', + graphQLPath: "graphql", }).applyPlayground(new express()) - ).toThrow('You must provide a config.playgroundPath to applyPlayground!'); + ).toThrow("You must provide a config.playgroundPath to applyPlayground!"); }); - it('should apply middlewares at config.playgroundPath', () => { + it("should apply middlewares at config.playgroundPath", () => { let useCount = 0; expect(() => new ParseGraphQLServer(parseServer, { - graphQLPath: 'graphQL', - playgroundPath: 'somepath', + graphQLPath: "graphQL", + playgroundPath: "somepath", }).applyPlayground({ get: path => { useCount++; - expect(path).toEqual('somepath'); + expect(path).toEqual("somepath"); }, }) ).not.toThrow(); @@ -221,24 +255,26 @@ describe('ParseGraphQLServer', () => { }); }); - describe('createSubscriptions', () => { - it('should require initialization with config.subscriptionsPath', () => { + describe("createSubscriptions", () => { + it("should require initialization with config.subscriptionsPath", () => { expect(() => new ParseGraphQLServer(parseServer, { - graphQLPath: 'graphql', + graphQLPath: "graphql", }).createSubscriptions({}) - ).toThrow('You must provide a config.subscriptionsPath to createSubscriptions!'); + ).toThrow( + "You must provide a config.subscriptionsPath to createSubscriptions!" + ); }); }); - describe('setGraphQLConfig', () => { + describe("setGraphQLConfig", () => { let parseGraphQLServer; beforeEach(() => { parseGraphQLServer = new ParseGraphQLServer(parseServer, { - graphQLPath: 'graphql', + graphQLPath: "graphql", }); }); - it('should pass the graphQLConfig onto the parseGraphQLController', async () => { + it("should pass the graphQLConfig onto the parseGraphQLController", async () => { let received; parseGraphQLServer.parseGraphQLController = { async updateGraphQLConfig(graphQLConfig) { @@ -250,34 +286,36 @@ describe('ParseGraphQLServer', () => { await parseGraphQLServer.setGraphQLConfig(graphQLConfig); expect(received).toBe(graphQLConfig); }); - it('should not absorb exceptions from parseGraphQLController', async () => { + it("should not absorb exceptions from parseGraphQLController", async () => { parseGraphQLServer.parseGraphQLController = { async updateGraphQLConfig() { - throw new Error('Network request failed'); + throw new Error("Network request failed"); }, }; - await expectAsync(parseGraphQLServer.setGraphQLConfig({})).toBeRejectedWith( - new Error('Network request failed') - ); + await expectAsync( + parseGraphQLServer.setGraphQLConfig({}) + ).toBeRejectedWith(new Error("Network request failed")); }); - it('should return the response from parseGraphQLController', async () => { + it("should return the response from parseGraphQLController", async () => { parseGraphQLServer.parseGraphQLController = { async updateGraphQLConfig() { return { response: { result: true } }; }, }; - await expectAsync(parseGraphQLServer.setGraphQLConfig({})).toBeResolvedTo({ - response: { result: true }, - }); + await expectAsync(parseGraphQLServer.setGraphQLConfig({})).toBeResolvedTo( + { + response: { result: true }, + } + ); }); }); - describe('Auto API', () => { + describe("Auto API", () => { let httpServer; let parseLiveQueryServer; const headers = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Javascript-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-Javascript-Key": "test", }; let apolloClient; @@ -298,98 +336,102 @@ describe('ParseGraphQLServer', () => { const acl = new Parse.ACL(); acl.setPublicReadAccess(true); user1 = new Parse.User(); - user1.setUsername('user1'); - user1.setPassword('user1'); - user1.setEmail('user1@user1.user1'); + user1.setUsername("user1"); + user1.setPassword("user1"); + user1.setEmail("user1@user1.user1"); user1.setACL(acl); await user1.signUp(); user2 = new Parse.User(); - user2.setUsername('user2'); - user2.setPassword('user2'); + user2.setUsername("user2"); + user2.setPassword("user2"); user2.setACL(acl); await user2.signUp(); user3 = new Parse.User(); - user3.setUsername('user3'); - user3.setPassword('user3'); + user3.setUsername("user3"); + user3.setPassword("user3"); user3.setACL(acl); await user3.signUp(); user4 = new Parse.User(); - user4.setUsername('user4'); - user4.setPassword('user4'); + user4.setUsername("user4"); + user4.setPassword("user4"); user4.setACL(acl); await user4.signUp(); user5 = new Parse.User(); - user5.setUsername('user5'); - user5.setPassword('user5'); + user5.setUsername("user5"); + user5.setPassword("user5"); user5.setACL(acl); await user5.signUp(); const roleACL = new Parse.ACL(); roleACL.setPublicReadAccess(true); role = new Parse.Role(); - role.setName('role'); + role.setName("role"); role.setACL(roleACL); role.getUsers().add(user1); role.getUsers().add(user3); role = await role.save(); - const schemaController = await parseServer.config.databaseController.loadSchema(); + const schemaController = + await parseServer.config.databaseController.loadSchema(); try { await schemaController.addClassIfNotExists( - 'GraphQLClass', + "GraphQLClass", { - someField: { type: 'String' }, - pointerToUser: { type: 'Pointer', targetClass: '_User' }, + someField: { type: "String" }, + pointerToUser: { type: "Pointer", targetClass: "_User" }, }, { find: { - 'role:role': true, + "role:role": true, [user1.id]: true, [user2.id]: true, }, create: { - 'role:role': true, + "role:role": true, [user1.id]: true, [user2.id]: true, }, get: { - 'role:role': true, + "role:role": true, [user1.id]: true, [user2.id]: true, }, update: { - 'role:role': true, + "role:role": true, [user1.id]: true, [user2.id]: true, }, addField: { - 'role:role': true, + "role:role": true, [user1.id]: true, [user2.id]: true, }, delete: { - 'role:role': true, + "role:role": true, [user1.id]: true, [user2.id]: true, }, - readUserFields: ['pointerToUser'], - writeUserFields: ['pointerToUser'], + readUserFields: ["pointerToUser"], + writeUserFields: ["pointerToUser"], }, {} ); } catch (err) { - if (!(err instanceof Parse.Error) || err.message !== 'Class GraphQLClass already exists.') { + if ( + !(err instanceof Parse.Error) || + err.message !== "Class GraphQLClass already exists." + ) { throw err; } } - object1 = new Parse.Object('GraphQLClass'); - object1.set('someField', 'someValue1'); - object1.set('someOtherField', 'A'); + object1 = new Parse.Object("GraphQLClass"); + object1.set("someField", "someValue1"); + object1.set("someOtherField", "A"); const object1ACL = new Parse.ACL(); object1ACL.setPublicReadAccess(false); object1ACL.setPublicWriteAccess(false); @@ -402,9 +444,9 @@ describe('ParseGraphQLServer', () => { object1.setACL(object1ACL); await object1.save(undefined, { useMasterKey: true }); - object2 = new Parse.Object('GraphQLClass'); - object2.set('someField', 'someValue2'); - object2.set('someOtherField', 'A'); + object2 = new Parse.Object("GraphQLClass"); + object2.set("someField", "someValue2"); + object2.set("someOtherField", "A"); const object2ACL = new Parse.ACL(); object2ACL.setPublicReadAccess(false); object2ACL.setPublicWriteAccess(false); @@ -417,14 +459,14 @@ describe('ParseGraphQLServer', () => { object2.setACL(object2ACL); await object2.save(undefined, { useMasterKey: true }); - object3 = new Parse.Object('GraphQLClass'); - object3.set('someField', 'someValue3'); - object3.set('someOtherField', 'B'); - object3.set('pointerToUser', user5); + object3 = new Parse.Object("GraphQLClass"); + object3.set("someField", "someValue3"); + object3.set("someOtherField", "B"); + object3.set("pointerToUser", user5); await object3.save(undefined, { useMasterKey: true }); - object4 = new Parse.Object('PublicClass'); - object4.set('someField', 'someValue4'); + object4 = new Parse.Object("PublicClass"); + object4.set("someField", "someValue4"); await object4.save(); objects = []; @@ -440,14 +482,17 @@ describe('ParseGraphQLServer', () => { } const expressApp = express(); httpServer = http.createServer(expressApp); - expressApp.use('/parse', _parseServer.app); - parseLiveQueryServer = await ParseServer.createLiveQueryServer(httpServer, { - port: 1338, - }); + expressApp.use("/parse", _parseServer.app); + parseLiveQueryServer = await ParseServer.createLiveQueryServer( + httpServer, + { + port: 1338, + } + ); parseGraphQLServer = new ParseGraphQLServer(_parseServer, { - graphQLPath: '/graphql', - playgroundPath: '/playground', - subscriptionsPath: '/subscriptions', + graphQLPath: "/graphql", + playgroundPath: "/playground", + subscriptionsPath: "/subscriptions", }); parseGraphQLServer.applyGraphQL(expressApp); parseGraphQLServer.applyPlayground(expressApp); @@ -456,10 +501,10 @@ describe('ParseGraphQLServer', () => { } beforeEach(async () => { - await createGQLFromParseServer(parseServer); + await createGQLFromParseServer(parseServer); const subscriptionClient = new SubscriptionClient( - 'ws://localhost:13377/subscriptions', + "ws://localhost:13377/subscriptions", { reconnect: true, connectionParams: headers, @@ -468,7 +513,7 @@ describe('ParseGraphQLServer', () => { ); const wsLink = new WebSocketLink(subscriptionClient); const httpLink = await createUploadLink({ - uri: 'http://localhost:13377/graphql', + uri: "http://localhost:13377/graphql", fetch, headers, }); @@ -476,7 +521,9 @@ describe('ParseGraphQLServer', () => { link: split( ({ query }) => { const { kind, operation } = getMainDefinition(query); - return kind === 'OperationDefinition' && operation === 'subscription'; + return ( + kind === "OperationDefinition" && operation === "subscription" + ); }, wsLink, httpLink @@ -484,12 +531,12 @@ describe('ParseGraphQLServer', () => { cache: new InMemoryCache(), defaultOptions: { query: { - fetchPolicy: 'no-cache', + fetchPolicy: "no-cache", }, }, }); - spyOn(console, 'warn').and.callFake(() => {}); - spyOn(console, 'error').and.callFake(() => {}); + spyOn(console, "warn").and.callFake(() => {}); + spyOn(console, "error").and.callFake(() => {}); }); afterEach(async () => { @@ -497,8 +544,8 @@ describe('ParseGraphQLServer', () => { await httpServer.close(); }); - describe('GraphQL', () => { - it('should be healthy', async () => { + describe("GraphQL", () => { + it("should be healthy", async () => { try { const health = ( await apolloClient.query({ @@ -515,7 +562,7 @@ describe('ParseGraphQLServer', () => { } }); - it('should be cors enabled and scope the response within the source origin', async () => { + it("should be cors enabled and scope the response within the source origin", async () => { let checked = false; const apolloClient = new ApolloClient({ link: new ApolloLink((operation, forward) => { @@ -524,17 +571,19 @@ describe('ParseGraphQLServer', () => { const { response: { headers }, } = context; - expect(headers.get('access-control-allow-origin')).toEqual('http://example.com'); + expect(headers.get("access-control-allow-origin")).toEqual( + "http://example.com" + ); checked = true; return response; }); }).concat( createHttpLink({ - uri: 'http://localhost:13377/graphql', + uri: "http://localhost:13377/graphql", fetch, headers: { ...headers, - Origin: 'http://example.com', + Origin: "http://example.com", }, }) ), @@ -551,7 +600,7 @@ describe('ParseGraphQLServer', () => { expect(checked).toBeTruthy(); }); - it('should handle Parse headers', async () => { + it("should handle Parse headers", async () => { const test = { context: ({ req: { info, config, auth } }) => { expect(req.info).toBeDefined(); @@ -564,7 +613,7 @@ describe('ParseGraphQLServer', () => { }; }, }; - const contextSpy = spyOn(test, 'context'); + const contextSpy = spyOn(test, "context"); const originalGetGraphQLOptions = parseGraphQLServer._getGraphQLOptions; parseGraphQLServer._getGraphQLOptions = async () => { return { @@ -587,17 +636,17 @@ describe('ParseGraphQLServer', () => { }); }); - describe('Playground', () => { - it('should mount playground', async () => { + describe("Playground", () => { + it("should mount playground", async () => { const res = await req({ - method: 'GET', - url: 'http://localhost:13377/playground', + method: "GET", + url: "http://localhost:13377/playground", }); expect(res.status).toEqual(200); }); }); - describe('Schema', () => { + describe("Schema", () => { const resetGraphQLCache = async () => { await Promise.all([ parseGraphQLServer.parseGraphQLController.cacheController.graphQL.clear(), @@ -605,8 +654,8 @@ describe('ParseGraphQLServer', () => { ]); }; - describe('Default Types', () => { - it('should have Object scalar type', async () => { + describe("Default Types", () => { + it("should have Object scalar type", async () => { const objectType = ( await apolloClient.query({ query: gql` @@ -617,11 +666,11 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type']; - expect(objectType.kind).toEqual('SCALAR'); + ).data["__type"]; + expect(objectType.kind).toEqual("SCALAR"); }); - it('should have Date scalar type', async () => { + it("should have Date scalar type", async () => { const dateType = ( await apolloClient.query({ query: gql` @@ -632,11 +681,11 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type']; - expect(dateType.kind).toEqual('SCALAR'); + ).data["__type"]; + expect(dateType.kind).toEqual("SCALAR"); }); - it('should have ArrayResult type', async () => { + it("should have ArrayResult type", async () => { const arrayResultType = ( await apolloClient.query({ query: gql` @@ -647,11 +696,11 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type']; - expect(arrayResultType.kind).toEqual('UNION'); + ).data["__type"]; + expect(arrayResultType.kind).toEqual("UNION"); }); - it('should have File object type', async () => { + it("should have File object type", async () => { const fileType = ( await apolloClient.query({ query: gql` @@ -665,12 +714,15 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type']; - expect(fileType.kind).toEqual('OBJECT'); - expect(fileType.fields.map(field => field.name).sort()).toEqual(['name', 'url']); + ).data["__type"]; + expect(fileType.kind).toEqual("OBJECT"); + expect(fileType.fields.map(field => field.name).sort()).toEqual([ + "name", + "url", + ]); }); - it('should have Class interface type', async () => { + it("should have Class interface type", async () => { const classType = ( await apolloClient.query({ query: gql` @@ -684,17 +736,17 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type']; - expect(classType.kind).toEqual('INTERFACE'); + ).data["__type"]; + expect(classType.kind).toEqual("INTERFACE"); expect(classType.fields.map(field => field.name).sort()).toEqual([ - 'ACL', - 'createdAt', - 'objectId', - 'updatedAt', + "ACL", + "createdAt", + "objectId", + "updatedAt", ]); }); - it('should have ReadPreference enum type', async () => { + it("should have ReadPreference enum type", async () => { const readPreferenceType = ( await apolloClient.query({ query: gql` @@ -708,18 +760,20 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type']; - expect(readPreferenceType.kind).toEqual('ENUM'); - expect(readPreferenceType.enumValues.map(value => value.name).sort()).toEqual([ - 'NEAREST', - 'PRIMARY', - 'PRIMARY_PREFERRED', - 'SECONDARY', - 'SECONDARY_PREFERRED', + ).data["__type"]; + expect(readPreferenceType.kind).toEqual("ENUM"); + expect( + readPreferenceType.enumValues.map(value => value.name).sort() + ).toEqual([ + "NEAREST", + "PRIMARY", + "PRIMARY_PREFERRED", + "SECONDARY", + "SECONDARY_PREFERRED", ]); }); - it('should have GraphQLUpload object type', async () => { + it("should have GraphQLUpload object type", async () => { const graphQLUploadType = ( await apolloClient.query({ query: gql` @@ -733,11 +787,11 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type']; - expect(graphQLUploadType.kind).toEqual('SCALAR'); + ).data["__type"]; + expect(graphQLUploadType.kind).toEqual("SCALAR"); }); - it('should have all expected types', async () => { + it("should have all expected types", async () => { const schemaTypes = ( await apolloClient.query({ query: gql` @@ -750,16 +804,22 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__schema'].types.map(type => type.name); + ).data["__schema"].types.map(type => type.name); - const expectedTypes = ['ParseObject', 'Date', 'FileInfo', 'ReadPreference', 'Upload']; - expect(expectedTypes.every(type => schemaTypes.indexOf(type) !== -1)).toBeTruthy( - JSON.stringify(schemaTypes.types) - ); + const expectedTypes = [ + "ParseObject", + "Date", + "FileInfo", + "ReadPreference", + "Upload", + ]; + expect( + expectedTypes.every(type => schemaTypes.indexOf(type) !== -1) + ).toBeTruthy(JSON.stringify(schemaTypes.types)); }); }); - describe('Relay Specific Types', () => { + describe("Relay Specific Types", () => { let clearCache; beforeEach(async () => { if (!clearCache) { @@ -768,7 +828,7 @@ describe('ParseGraphQLServer', () => { } }); - it('should have Node interface', async () => { + it("should have Node interface", async () => { const schemaTypes = ( await apolloClient.query({ query: gql` @@ -781,12 +841,12 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__schema'].types.map(type => type.name); + ).data["__schema"].types.map(type => type.name); - expect(schemaTypes).toContain('Node'); + expect(schemaTypes).toContain("Node"); }); - it('should have node query', async () => { + it("should have node query", async () => { const queryFields = ( await apolloClient.query({ query: gql` @@ -799,12 +859,12 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type'].fields.map(field => field.name); + ).data["__type"].fields.map(field => field.name); - expect(queryFields).toContain('node'); + expect(queryFields).toContain("node"); }); - it('should return global id', async () => { + it("should return global id", async () => { const userFields = ( await apolloClient.query({ query: gql` @@ -817,13 +877,13 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type'].fields.map(field => field.name); + ).data["__type"].fields.map(field => field.name); - expect(userFields).toContain('id'); - expect(userFields).toContain('objectId'); + expect(userFields).toContain("id"); + expect(userFields).toContain("objectId"); }); - it('should have clientMutationId in create file input', async () => { + it("should have clientMutationId in create file input", async () => { const createFileInputFields = ( await apolloClient.query({ query: gql` @@ -836,14 +896,14 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type'].inputFields + ).data["__type"].inputFields .map(field => field.name) .sort(); - expect(createFileInputFields).toEqual(['clientMutationId', 'upload']); + expect(createFileInputFields).toEqual(["clientMutationId", "upload"]); }); - it('should have clientMutationId in create file payload', async () => { + it("should have clientMutationId in create file payload", async () => { const createFilePayloadFields = ( await apolloClient.query({ query: gql` @@ -856,15 +916,18 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type'].fields + ).data["__type"].fields .map(field => field.name) .sort(); - expect(createFilePayloadFields).toEqual(['clientMutationId', 'fileInfo']); + expect(createFilePayloadFields).toEqual([ + "clientMutationId", + "fileInfo", + ]); }); - it('should have clientMutationId in call function input', async () => { - Parse.Cloud.define('hello', () => {}); + it("should have clientMutationId in call function input", async () => { + Parse.Cloud.define("hello", () => {}); const callFunctionInputFields = ( await apolloClient.query({ @@ -878,15 +941,19 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type'].inputFields + ).data["__type"].inputFields .map(field => field.name) .sort(); - expect(callFunctionInputFields).toEqual(['clientMutationId', 'functionName', 'params']); + expect(callFunctionInputFields).toEqual([ + "clientMutationId", + "functionName", + "params", + ]); }); - it('should have clientMutationId in call function payload', async () => { - Parse.Cloud.define('hello', () => {}); + it("should have clientMutationId in call function payload", async () => { + Parse.Cloud.define("hello", () => {}); const callFunctionPayloadFields = ( await apolloClient.query({ @@ -900,14 +967,17 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type'].fields + ).data["__type"].fields .map(field => field.name) .sort(); - expect(callFunctionPayloadFields).toEqual(['clientMutationId', 'result']); + expect(callFunctionPayloadFields).toEqual([ + "clientMutationId", + "result", + ]); }); - it('should have clientMutationId in sign up mutation input', async () => { + it("should have clientMutationId in sign up mutation input", async () => { const inputFields = ( await apolloClient.query({ query: gql` @@ -920,14 +990,14 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type'].inputFields + ).data["__type"].inputFields .map(field => field.name) .sort(); - expect(inputFields).toEqual(['clientMutationId', 'fields']); + expect(inputFields).toEqual(["clientMutationId", "fields"]); }); - it('should have clientMutationId in sign up mutation payload', async () => { + it("should have clientMutationId in sign up mutation payload", async () => { const payloadFields = ( await apolloClient.query({ query: gql` @@ -940,14 +1010,14 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type'].fields + ).data["__type"].fields .map(field => field.name) .sort(); - expect(payloadFields).toEqual(['clientMutationId', 'viewer']); + expect(payloadFields).toEqual(["clientMutationId", "viewer"]); }); - it('should have clientMutationId in log in mutation input', async () => { + it("should have clientMutationId in log in mutation input", async () => { const inputFields = ( await apolloClient.query({ query: gql` @@ -960,13 +1030,18 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type'].inputFields + ).data["__type"].inputFields .map(field => field.name) .sort(); - expect(inputFields).toEqual(['authData', 'clientMutationId', 'password', 'username']); + expect(inputFields).toEqual([ + "authData", + "clientMutationId", + "password", + "username", + ]); }); - it('should have clientMutationId in log in mutation payload', async () => { + it("should have clientMutationId in log in mutation payload", async () => { const payloadFields = ( await apolloClient.query({ query: gql` @@ -979,14 +1054,14 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type'].fields + ).data["__type"].fields .map(field => field.name) .sort(); - expect(payloadFields).toEqual(['clientMutationId', 'viewer']); + expect(payloadFields).toEqual(["clientMutationId", "viewer"]); }); - it('should have clientMutationId in log out mutation input', async () => { + it("should have clientMutationId in log out mutation input", async () => { const inputFields = ( await apolloClient.query({ query: gql` @@ -999,14 +1074,14 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type'].inputFields + ).data["__type"].inputFields .map(field => field.name) .sort(); - expect(inputFields).toEqual(['clientMutationId']); + expect(inputFields).toEqual(["clientMutationId"]); }); - it('should have clientMutationId in log out mutation payload', async () => { + it("should have clientMutationId in log out mutation payload", async () => { const payloadFields = ( await apolloClient.query({ query: gql` @@ -1019,14 +1094,14 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type'].fields + ).data["__type"].fields .map(field => field.name) .sort(); - expect(payloadFields).toEqual(['clientMutationId', 'ok']); + expect(payloadFields).toEqual(["clientMutationId", "ok"]); }); - it('should have clientMutationId in createClass mutation input', async () => { + it("should have clientMutationId in createClass mutation input", async () => { const inputFields = ( await apolloClient.query({ query: gql` @@ -1039,14 +1114,18 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type'].inputFields + ).data["__type"].inputFields .map(field => field.name) .sort(); - expect(inputFields).toEqual(['clientMutationId', 'name', 'schemaFields']); + expect(inputFields).toEqual([ + "clientMutationId", + "name", + "schemaFields", + ]); }); - it('should have clientMutationId in createClass mutation payload', async () => { + it("should have clientMutationId in createClass mutation payload", async () => { const payloadFields = ( await apolloClient.query({ query: gql` @@ -1059,14 +1138,14 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type'].fields + ).data["__type"].fields .map(field => field.name) .sort(); - expect(payloadFields).toEqual(['class', 'clientMutationId']); + expect(payloadFields).toEqual(["class", "clientMutationId"]); }); - it('should have clientMutationId in updateClass mutation input', async () => { + it("should have clientMutationId in updateClass mutation input", async () => { const inputFields = ( await apolloClient.query({ query: gql` @@ -1079,14 +1158,18 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type'].inputFields + ).data["__type"].inputFields .map(field => field.name) .sort(); - expect(inputFields).toEqual(['clientMutationId', 'name', 'schemaFields']); + expect(inputFields).toEqual([ + "clientMutationId", + "name", + "schemaFields", + ]); }); - it('should have clientMutationId in updateClass mutation payload', async () => { + it("should have clientMutationId in updateClass mutation payload", async () => { const payloadFields = ( await apolloClient.query({ query: gql` @@ -1099,14 +1182,14 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type'].fields + ).data["__type"].fields .map(field => field.name) .sort(); - expect(payloadFields).toEqual(['class', 'clientMutationId']); + expect(payloadFields).toEqual(["class", "clientMutationId"]); }); - it('should have clientMutationId in deleteClass mutation input', async () => { + it("should have clientMutationId in deleteClass mutation input", async () => { const inputFields = ( await apolloClient.query({ query: gql` @@ -1119,14 +1202,14 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type'].inputFields + ).data["__type"].inputFields .map(field => field.name) .sort(); - expect(inputFields).toEqual(['clientMutationId', 'name']); + expect(inputFields).toEqual(["clientMutationId", "name"]); }); - it('should have clientMutationId in deleteClass mutation payload', async () => { + it("should have clientMutationId in deleteClass mutation payload", async () => { const payloadFields = ( await apolloClient.query({ query: gql` @@ -1139,15 +1222,15 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type'].fields + ).data["__type"].fields .map(field => field.name) .sort(); - expect(payloadFields).toEqual(['class', 'clientMutationId']); + expect(payloadFields).toEqual(["class", "clientMutationId"]); }); - it('should have clientMutationId in custom create object mutation input', async () => { - const obj = new Parse.Object('SomeClass'); + it("should have clientMutationId in custom create object mutation input", async () => { + const obj = new Parse.Object("SomeClass"); await obj.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -1164,15 +1247,18 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type'].inputFields + ).data["__type"].inputFields .map(field => field.name) .sort(); - expect(createObjectInputFields).toEqual(['clientMutationId', 'fields']); + expect(createObjectInputFields).toEqual([ + "clientMutationId", + "fields", + ]); }); - it('should have clientMutationId in custom create object mutation payload', async () => { - const obj = new Parse.Object('SomeClass'); + it("should have clientMutationId in custom create object mutation payload", async () => { + const obj = new Parse.Object("SomeClass"); await obj.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -1189,15 +1275,18 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type'].fields + ).data["__type"].fields .map(field => field.name) .sort(); - expect(createObjectPayloadFields).toEqual(['clientMutationId', 'someClass']); + expect(createObjectPayloadFields).toEqual([ + "clientMutationId", + "someClass", + ]); }); - it('should have clientMutationId in custom update object mutation input', async () => { - const obj = new Parse.Object('SomeClass'); + it("should have clientMutationId in custom update object mutation input", async () => { + const obj = new Parse.Object("SomeClass"); await obj.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -1214,15 +1303,19 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type'].inputFields + ).data["__type"].inputFields .map(field => field.name) .sort(); - expect(createObjectInputFields).toEqual(['clientMutationId', 'fields', 'id']); + expect(createObjectInputFields).toEqual([ + "clientMutationId", + "fields", + "id", + ]); }); - it('should have clientMutationId in custom update object mutation payload', async () => { - const obj = new Parse.Object('SomeClass'); + it("should have clientMutationId in custom update object mutation payload", async () => { + const obj = new Parse.Object("SomeClass"); await obj.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -1239,15 +1332,18 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type'].fields + ).data["__type"].fields .map(field => field.name) .sort(); - expect(createObjectPayloadFields).toEqual(['clientMutationId', 'someClass']); + expect(createObjectPayloadFields).toEqual([ + "clientMutationId", + "someClass", + ]); }); - it('should have clientMutationId in custom delete object mutation input', async () => { - const obj = new Parse.Object('SomeClass'); + it("should have clientMutationId in custom delete object mutation input", async () => { + const obj = new Parse.Object("SomeClass"); await obj.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -1264,15 +1360,15 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type'].inputFields + ).data["__type"].inputFields .map(field => field.name) .sort(); - expect(createObjectInputFields).toEqual(['clientMutationId', 'id']); + expect(createObjectInputFields).toEqual(["clientMutationId", "id"]); }); - it('should have clientMutationId in custom delete object mutation payload', async () => { - const obj = new Parse.Object('SomeClass'); + it("should have clientMutationId in custom delete object mutation payload", async () => { + const obj = new Parse.Object("SomeClass"); await obj.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -1289,16 +1385,19 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type'].fields + ).data["__type"].fields .map(field => field.name) .sort(); - expect(createObjectPayloadFields).toEqual(['clientMutationId', 'someClass']); + expect(createObjectPayloadFields).toEqual([ + "clientMutationId", + "someClass", + ]); }); }); - describe('Parse Class Types', () => { - it('should have all expected types', async () => { + describe("Parse Class Types", () => { + it("should have all expected types", async () => { await parseServer.config.databaseController.loadSchema(); const schemaTypes = ( @@ -1313,26 +1412,26 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__schema'].types.map(type => type.name); + ).data["__schema"].types.map(type => type.name); const expectedTypes = [ - 'Role', - 'RoleWhereInput', - 'CreateRoleFieldsInput', - 'UpdateRoleFieldsInput', - 'RoleConnection', - 'User', - 'UserWhereInput', - 'UserConnection', - 'CreateUserFieldsInput', - 'UpdateUserFieldsInput', + "Role", + "RoleWhereInput", + "CreateRoleFieldsInput", + "UpdateRoleFieldsInput", + "RoleConnection", + "User", + "UserWhereInput", + "UserConnection", + "CreateUserFieldsInput", + "UpdateUserFieldsInput", ]; - expect(expectedTypes.every(type => schemaTypes.indexOf(type) !== -1)).toBeTruthy( - JSON.stringify(schemaTypes) - ); + expect( + expectedTypes.every(type => schemaTypes.indexOf(type) !== -1) + ).toBeTruthy(JSON.stringify(schemaTypes)); }); - it('should ArrayResult contains all types', async () => { + it("should ArrayResult contains all types", async () => { const objectType = ( await apolloClient.query({ query: gql` @@ -1346,17 +1445,18 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type']; + ).data["__type"]; const possibleTypes = objectType.possibleTypes.map(o => o.name); - expect(possibleTypes).toContain('User'); - expect(possibleTypes).toContain('Role'); - expect(possibleTypes).toContain('Element'); + expect(possibleTypes).toContain("User"); + expect(possibleTypes).toContain("Role"); + expect(possibleTypes).toContain("Element"); }); - it('should update schema when it changes', async () => { - const schemaController = await parseServer.config.databaseController.loadSchema(); - await schemaController.updateClass('_User', { - foo: { type: 'String' }, + it("should update schema when it changes", async () => { + const schemaController = + await parseServer.config.databaseController.loadSchema(); + await schemaController.updateClass("_User", { + foo: { type: "String" }, }); const userFields = ( @@ -1371,11 +1471,11 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type'].fields.map(field => field.name); - expect(userFields.indexOf('foo') !== -1).toBeTruthy(); + ).data["__type"].fields.map(field => field.name); + expect(userFields.indexOf("foo") !== -1).toBeTruthy(); }); - it('should not contain password field from _User class', async () => { + it("should not contain password field from _User class", async () => { const userFields = ( await apolloClient.query({ query: gql` @@ -1388,12 +1488,12 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type'].fields.map(field => field.name); - expect(userFields.includes('password')).toBeFalsy(); + ).data["__type"].fields.map(field => field.name); + expect(userFields.includes("password")).toBeFalsy(); }); }); - describe('Configuration', function () { + describe("Configuration", function () { const resetGraphQLCache = async () => { await Promise.all([ parseGraphQLServer.parseGraphQLController.cacheController.graphQL.clear(), @@ -1406,631 +1506,774 @@ describe('ParseGraphQLServer', () => { await resetGraphQLCache(); }); - it_id('d6a23a2f-ca18-4b15-bc73-3e636f99e6bc')(it)('should only include types in the enabledForClasses list', async () => { - const schemaController = await parseServer.config.databaseController.loadSchema(); - await schemaController.addClassIfNotExists('SuperCar', { - foo: { type: 'String' }, - }); + it_id("d6a23a2f-ca18-4b15-bc73-3e636f99e6bc")(it)( + "should only include types in the enabledForClasses list", + async () => { + const schemaController = + await parseServer.config.databaseController.loadSchema(); + await schemaController.addClassIfNotExists("SuperCar", { + foo: { type: "String" }, + }); - const graphQLConfig = { - enabledForClasses: ['SuperCar'], - }; - await parseGraphQLServer.setGraphQLConfig(graphQLConfig); - await resetGraphQLCache(); + const graphQLConfig = { + enabledForClasses: ["SuperCar"], + }; + await parseGraphQLServer.setGraphQLConfig(graphQLConfig); + await resetGraphQLCache(); - const { data } = await apolloClient.query({ - query: gql` - query UserType { - userType: __type(name: "User") { - fields { - name + const { data } = await apolloClient.query({ + query: gql` + query UserType { + userType: __type(name: "User") { + fields { + name + } } - } - superCarType: __type(name: "SuperCar") { - fields { - name + superCarType: __type(name: "SuperCar") { + fields { + name + } } } - } - `, - }); - expect(data.userType).toBeNull(); - expect(data.superCarType).toBeTruthy(); - }); - it_id('1db2aceb-d24e-4929-ba43-8dbb5d0395e1')(it)('should not include types in the disabledForClasses list', async () => { - const schemaController = await parseServer.config.databaseController.loadSchema(); - await schemaController.addClassIfNotExists('SuperCar', { - foo: { type: 'String' }, - }); + `, + }); + expect(data.userType).toBeNull(); + expect(data.superCarType).toBeTruthy(); + } + ); + it_id("1db2aceb-d24e-4929-ba43-8dbb5d0395e1")(it)( + "should not include types in the disabledForClasses list", + async () => { + const schemaController = + await parseServer.config.databaseController.loadSchema(); + await schemaController.addClassIfNotExists("SuperCar", { + foo: { type: "String" }, + }); - const graphQLConfig = { - disabledForClasses: ['SuperCar'], - }; - await parseGraphQLServer.setGraphQLConfig(graphQLConfig); - await resetGraphQLCache(); + const graphQLConfig = { + disabledForClasses: ["SuperCar"], + }; + await parseGraphQLServer.setGraphQLConfig(graphQLConfig); + await resetGraphQLCache(); - const { data } = await apolloClient.query({ - query: gql` - query UserType { - userType: __type(name: "User") { - fields { - name + const { data } = await apolloClient.query({ + query: gql` + query UserType { + userType: __type(name: "User") { + fields { + name + } } - } - superCarType: __type(name: "SuperCar") { - fields { - name + superCarType: __type(name: "SuperCar") { + fields { + name + } } } - } - `, - }); - expect(data.superCarType).toBeNull(); - expect(data.userType).toBeTruthy(); - }); - it_id('85c2e02f-0239-4819-b66e-392e0125f6c5')(it)('should remove query operations when disabled', async () => { - const superCar = new Parse.Object('SuperCar'); - await superCar.save({ foo: 'bar' }); - const customer = new Parse.Object('Customer'); - await customer.save({ foo: 'bar' }); + `, + }); + expect(data.superCarType).toBeNull(); + expect(data.userType).toBeTruthy(); + } + ); + it_id("85c2e02f-0239-4819-b66e-392e0125f6c5")(it)( + "should remove query operations when disabled", + async () => { + const superCar = new Parse.Object("SuperCar"); + await superCar.save({ foo: "bar" }); + const customer = new Parse.Object("Customer"); + await customer.save({ foo: "bar" }); - await expectAsync( - apolloClient.query({ - query: gql` - query GetSuperCar($id: ID!) { - superCar(id: $id) { - id + await expectAsync( + apolloClient.query({ + query: gql` + query GetSuperCar($id: ID!) { + superCar(id: $id) { + id + } } - } - `, - variables: { - id: superCar.id, - }, - }) - ).toBeResolved(); + `, + variables: { + id: superCar.id, + }, + }) + ).toBeResolved(); - await expectAsync( - apolloClient.query({ - query: gql` - query FindCustomer { - customers { - count + await expectAsync( + apolloClient.query({ + query: gql` + query FindCustomer { + customers { + count + } } - } - `, - }) - ).toBeResolved(); + `, + }) + ).toBeResolved(); - const graphQLConfig = { - classConfigs: [ - { - className: 'SuperCar', - query: { - get: false, - find: true, + const graphQLConfig = { + classConfigs: [ + { + className: "SuperCar", + query: { + get: false, + find: true, + }, }, - }, - { - className: 'Customer', - query: { - get: true, - find: false, + { + className: "Customer", + query: { + get: true, + find: false, + }, }, - }, - ], - }; - await parseGraphQLServer.setGraphQLConfig(graphQLConfig); - await resetGraphQLCache(); + ], + }; + await parseGraphQLServer.setGraphQLConfig(graphQLConfig); + await resetGraphQLCache(); - await expectAsync( - apolloClient.query({ - query: gql` - query GetSuperCar($id: ID!) { - superCar(id: $id) { - id + await expectAsync( + apolloClient.query({ + query: gql` + query GetSuperCar($id: ID!) { + superCar(id: $id) { + id + } } - } - `, - variables: { - id: superCar.id, - }, - }) - ).toBeRejected(); - await expectAsync( - apolloClient.query({ - query: gql` - query GetCustomer($id: ID!) { - customer(id: $id) { - id + `, + variables: { + id: superCar.id, + }, + }) + ).toBeRejected(); + await expectAsync( + apolloClient.query({ + query: gql` + query GetCustomer($id: ID!) { + customer(id: $id) { + id + } } - } - `, - variables: { - id: customer.id, - }, - }) - ).toBeResolved(); - await expectAsync( - apolloClient.query({ - query: gql` - query FindSuperCar { - superCars { - count + `, + variables: { + id: customer.id, + }, + }) + ).toBeResolved(); + await expectAsync( + apolloClient.query({ + query: gql` + query FindSuperCar { + superCars { + count + } } - } - `, - }) - ).toBeResolved(); - await expectAsync( - apolloClient.query({ - query: gql` - query FindCustomer { - customers { - count + `, + }) + ).toBeResolved(); + await expectAsync( + apolloClient.query({ + query: gql` + query FindCustomer { + customers { + count + } } - } - `, - }) - ).toBeRejected(); - }); + `, + }) + ).toBeRejected(); + } + ); - it_id('972161a6-8108-4e99-a1a5-71d0267d26c2')(it)('should remove mutation operations, create, update and delete, when disabled', async () => { - const superCar1 = new Parse.Object('SuperCar'); - await superCar1.save({ foo: 'bar' }); - const customer1 = new Parse.Object('Customer'); - await customer1.save({ foo: 'bar' }); + it_id("972161a6-8108-4e99-a1a5-71d0267d26c2")(it)( + "should remove mutation operations, create, update and delete, when disabled", + async () => { + const superCar1 = new Parse.Object("SuperCar"); + await superCar1.save({ foo: "bar" }); + const customer1 = new Parse.Object("Customer"); + await customer1.save({ foo: "bar" }); - await expectAsync( - apolloClient.query({ + await expectAsync( + apolloClient.query({ + query: gql` + mutation UpdateSuperCar($id: ID!, $foo: String!) { + updateSuperCar(input: { id: $id, fields: { foo: $foo } }) { + clientMutationId + } + } + `, + variables: { + id: superCar1.id, + foo: "lah", + }, + }) + ).toBeResolved(); + + await expectAsync( + apolloClient.query({ + query: gql` + mutation DeleteCustomer($id: ID!) { + deleteCustomer(input: { id: $id }) { + clientMutationId + } + } + `, + variables: { + id: customer1.id, + }, + }) + ).toBeResolved(); + + const { data: customerData } = await apolloClient.query({ query: gql` - mutation UpdateSuperCar($id: ID!, $foo: String!) { - updateSuperCar(input: { id: $id, fields: { foo: $foo } }) { - clientMutationId + mutation CreateCustomer($foo: String!) { + createCustomer(input: { fields: { foo: $foo } }) { + customer { + id + } } } `, variables: { - id: superCar1.id, - foo: 'lah', + foo: "rah", }, - }) - ).toBeResolved(); + }); + expect(customerData.createCustomer.customer).toBeTruthy(); - await expectAsync( - apolloClient.query({ - query: gql` - mutation DeleteCustomer($id: ID!) { - deleteCustomer(input: { id: $id }) { - clientMutationId - } - } - `, - variables: { - id: customer1.id, - }, - }) - ).toBeResolved(); + // used later + const customer2Id = customerData.createCustomer.customer.id; - const { data: customerData } = await apolloClient.query({ - query: gql` - mutation CreateCustomer($foo: String!) { - createCustomer(input: { fields: { foo: $foo } }) { - customer { - id - } - } - } - `, - variables: { - foo: 'rah', - }, - }); - expect(customerData.createCustomer.customer).toBeTruthy(); - - // used later - const customer2Id = customerData.createCustomer.customer.id; - - await parseGraphQLServer.setGraphQLConfig({ - classConfigs: [ - { - className: 'SuperCar', - mutation: { - create: true, - update: false, - destroy: true, + await parseGraphQLServer.setGraphQLConfig({ + classConfigs: [ + { + className: "SuperCar", + mutation: { + create: true, + update: false, + destroy: true, + }, }, - }, - { - className: 'Customer', - mutation: { - create: false, - update: true, - destroy: false, + { + className: "Customer", + mutation: { + create: false, + update: true, + destroy: false, + }, }, - }, - ], - }); - await resetGraphQLCache(); - - const { data: superCarData } = await apolloClient.query({ - query: gql` - mutation CreateSuperCar($foo: String!) { - createSuperCar(input: { fields: { foo: $foo } }) { - superCar { - id - } - } - } - `, - variables: { - foo: 'mah', - }, - }); - expect(superCarData.createSuperCar).toBeTruthy(); - const superCar3Id = superCarData.createSuperCar.superCar.id; + ], + }); + await resetGraphQLCache(); - await expectAsync( - apolloClient.query({ + const { data: superCarData } = await apolloClient.query({ query: gql` - mutation UpdateSupercar($id: ID!, $foo: String!) { - updateSuperCar(input: { id: $id, fields: { foo: $foo } }) { - clientMutationId + mutation CreateSuperCar($foo: String!) { + createSuperCar(input: { fields: { foo: $foo } }) { + superCar { + id + } } } `, variables: { - id: superCar3Id, + foo: "mah", }, - }) - ).toBeRejected(); + }); + expect(superCarData.createSuperCar).toBeTruthy(); + const superCar3Id = superCarData.createSuperCar.superCar.id; - await expectAsync( - apolloClient.query({ - query: gql` - mutation DeleteSuperCar($id: ID!) { - deleteSuperCar(input: { id: $id }) { - clientMutationId + await expectAsync( + apolloClient.query({ + query: gql` + mutation UpdateSupercar($id: ID!, $foo: String!) { + updateSuperCar(input: { id: $id, fields: { foo: $foo } }) { + clientMutationId + } } - } - `, - variables: { - id: superCar3Id, - }, - }) - ).toBeResolved(); + `, + variables: { + id: superCar3Id, + }, + }) + ).toBeRejected(); - await expectAsync( - apolloClient.query({ - query: gql` - mutation CreateCustomer($foo: String!) { - createCustomer(input: { fields: { foo: $foo } }) { - customer { - id + await expectAsync( + apolloClient.query({ + query: gql` + mutation DeleteSuperCar($id: ID!) { + deleteSuperCar(input: { id: $id }) { + clientMutationId } } - } - `, - variables: { - foo: 'rah', - }, - }) - ).toBeRejected(); - await expectAsync( - apolloClient.query({ - query: gql` - mutation UpdateCustomer($id: ID!, $foo: String!) { - updateCustomer(input: { id: $id, fields: { foo: $foo } }) { - clientMutationId + `, + variables: { + id: superCar3Id, + }, + }) + ).toBeResolved(); + + await expectAsync( + apolloClient.query({ + query: gql` + mutation CreateCustomer($foo: String!) { + createCustomer(input: { fields: { foo: $foo } }) { + customer { + id + } + } } - } - `, - variables: { - id: customer2Id, - foo: 'tah', - }, - }) - ).toBeResolved(); - await expectAsync( - apolloClient.query({ - query: gql` - mutation DeleteCustomer($id: ID!, $foo: String!) { - deleteCustomer(input: { id: $id }) { - clientMutationId + `, + variables: { + foo: "rah", + }, + }) + ).toBeRejected(); + await expectAsync( + apolloClient.query({ + query: gql` + mutation UpdateCustomer($id: ID!, $foo: String!) { + updateCustomer(input: { id: $id, fields: { foo: $foo } }) { + clientMutationId + } } - } - `, - variables: { - id: customer2Id, - }, - }) - ).toBeRejected(); - }); + `, + variables: { + id: customer2Id, + foo: "tah", + }, + }) + ).toBeResolved(); + await expectAsync( + apolloClient.query({ + query: gql` + mutation DeleteCustomer($id: ID!, $foo: String!) { + deleteCustomer(input: { id: $id }) { + clientMutationId + } + } + `, + variables: { + id: customer2Id, + }, + }) + ).toBeRejected(); + } + ); - it_id('4af763b1-ff86-43c7-ba30-060a1c07e730')(it)('should only allow the supplied create and update fields for a class', async () => { - const schemaController = await parseServer.config.databaseController.loadSchema(); - await schemaController.addClassIfNotExists('SuperCar', { - engine: { type: 'String' }, - doors: { type: 'Number' }, - price: { type: 'String' }, - mileage: { type: 'Number' }, - }); + it_id("4af763b1-ff86-43c7-ba30-060a1c07e730")(it)( + "should only allow the supplied create and update fields for a class", + async () => { + const schemaController = + await parseServer.config.databaseController.loadSchema(); + await schemaController.addClassIfNotExists("SuperCar", { + engine: { type: "String" }, + doors: { type: "Number" }, + price: { type: "String" }, + mileage: { type: "Number" }, + }); - await parseGraphQLServer.setGraphQLConfig({ - classConfigs: [ - { - className: 'SuperCar', - type: { - inputFields: { - create: ['engine', 'doors', 'price'], - update: ['price', 'mileage'], + await parseGraphQLServer.setGraphQLConfig({ + classConfigs: [ + { + className: "SuperCar", + type: { + inputFields: { + create: ["engine", "doors", "price"], + update: ["price", "mileage"], + }, }, }, - }, - ], - }); + ], + }); - await resetGraphQLCache(); + await resetGraphQLCache(); - await expectAsync( - apolloClient.query({ - query: gql` - mutation InvalidCreateSuperCar { - createSuperCar(input: { fields: { engine: "diesel", mileage: 1000 } }) { - superCar { - id + await expectAsync( + apolloClient.query({ + query: gql` + mutation InvalidCreateSuperCar { + createSuperCar( + input: { fields: { engine: "diesel", mileage: 1000 } } + ) { + superCar { + id + } } } - } - `, - }) - ).toBeRejected(); - const { id: superCarId } = ( - await apolloClient.query({ - query: gql` - mutation ValidCreateSuperCar { - createSuperCar( - input: { fields: { engine: "diesel", doors: 5, price: "£10000" } } - ) { - superCar { - id + `, + }) + ).toBeRejected(); + const { id: superCarId } = ( + await apolloClient.query({ + query: gql` + mutation ValidCreateSuperCar { + createSuperCar( + input: { + fields: { engine: "diesel", doors: 5, price: "£10000" } + } + ) { + superCar { + id + } } } - } - `, - }) - ).data.createSuperCar.superCar; + `, + }) + ).data.createSuperCar.superCar; - expect(superCarId).toBeTruthy(); + expect(superCarId).toBeTruthy(); - await expectAsync( - apolloClient.query({ + await expectAsync( + apolloClient.query({ + query: gql` + mutation InvalidUpdateSuperCar($id: ID!) { + updateSuperCar( + input: { id: $id, fields: { engine: "petrol" } } + ) { + clientMutationId + } + } + `, + variables: { + id: superCarId, + }, + }) + ).toBeRejected(); + + const updatedSuperCar = ( + await apolloClient.query({ + query: gql` + mutation ValidUpdateSuperCar($id: ID!) { + updateSuperCar( + input: { id: $id, fields: { mileage: 2000 } } + ) { + clientMutationId + } + } + `, + variables: { + id: superCarId, + }, + }) + ).data.updateSuperCar; + expect(updatedSuperCar).toBeTruthy(); + } + ); + + it_id("fc9237e9-3e63-4b55-9c1d-e6269f613a93")(it)( + "should handle required fields from the Parse class", + async () => { + const schemaController = + await parseServer.config.databaseController.loadSchema(); + await schemaController.addClassIfNotExists("SuperCar", { + engine: { type: "String", required: true }, + doors: { type: "Number", required: true }, + price: { type: "String" }, + mileage: { type: "Number" }, + }); + + await resetGraphQLCache(); + + const { + data: { __type }, + } = await apolloClient.query({ query: gql` - mutation InvalidUpdateSuperCar($id: ID!) { - updateSuperCar(input: { id: $id, fields: { engine: "petrol" } }) { - clientMutationId + query requiredFields { + __type(name: "CreateSuperCarFieldsInput") { + inputFields { + name + type { + kind + } + } } } `, - variables: { - id: superCarId, - }, - }) - ).toBeRejected(); + }); + expect( + __type.inputFields.find(o => o.name === "price").type.kind + ).toEqual("SCALAR"); + expect( + __type.inputFields.find(o => o.name === "engine").type.kind + ).toEqual("NON_NULL"); + expect( + __type.inputFields.find(o => o.name === "doors").type.kind + ).toEqual("NON_NULL"); - const updatedSuperCar = ( - await apolloClient.query({ + const { + data: { __type: __type2 }, + } = await apolloClient.query({ query: gql` - mutation ValidUpdateSuperCar($id: ID!) { - updateSuperCar(input: { id: $id, fields: { mileage: 2000 } }) { - clientMutationId + query requiredFields { + __type(name: "SuperCar") { + fields { + name + type { + kind + } + } } } `, - variables: { - id: superCarId, - }, - }) - ).data.updateSuperCar; - expect(updatedSuperCar).toBeTruthy(); - }); + }); + expect( + __type2.fields.find(o => o.name === "price").type.kind + ).toEqual("SCALAR"); + expect( + __type2.fields.find(o => o.name === "engine").type.kind + ).toEqual("NON_NULL"); + expect( + __type2.fields.find(o => o.name === "doors").type.kind + ).toEqual("NON_NULL"); + } + ); - it_id('fc9237e9-3e63-4b55-9c1d-e6269f613a93')(it)('should handle required fields from the Parse class', async () => { - const schemaController = await parseServer.config.databaseController.loadSchema(); - await schemaController.addClassIfNotExists('SuperCar', { - engine: { type: 'String', required: true }, - doors: { type: 'Number', required: true }, - price: { type: 'String' }, - mileage: { type: 'Number' }, - }); + it_id("83b6895a-7dfd-4e3b-a5ce-acdb1fa39705")(it)( + "should only allow the supplied output fields for a class", + async () => { + const schemaController = + await parseServer.config.databaseController.loadSchema(); - await resetGraphQLCache(); + await schemaController.addClassIfNotExists("SuperCar", { + engine: { type: "String" }, + doors: { type: "Number" }, + price: { type: "String" }, + mileage: { type: "Number" }, + insuranceClaims: { type: "Number" }, + }); - const { - data: { __type }, - } = await apolloClient.query({ - query: gql` - query requiredFields { - __type(name: "CreateSuperCarFieldsInput") { - inputFields { - name - type { - kind + const superCar = await new Parse.Object("SuperCar").save({ + engine: "petrol", + doors: 3, + price: "£7500", + mileage: 0, + insuranceCertificate: "private-file.pdf", + }); + + await parseGraphQLServer.setGraphQLConfig({ + classConfigs: [ + { + className: "SuperCar", + type: { + outputFields: ["engine", "doors", "price", "mileage"], + }, + }, + ], + }); + + await resetGraphQLCache(); + + await expectAsync( + apolloClient.query({ + query: gql` + query GetSuperCar($id: ID!) { + superCar(id: $id) { + id + objectId + engine + doors + price + mileage + insuranceCertificate } } - } - } - `, - }); - expect(__type.inputFields.find(o => o.name === 'price').type.kind).toEqual('SCALAR'); - expect(__type.inputFields.find(o => o.name === 'engine').type.kind).toEqual('NON_NULL'); - expect(__type.inputFields.find(o => o.name === 'doors').type.kind).toEqual('NON_NULL'); - - const { - data: { __type: __type2 }, - } = await apolloClient.query({ - query: gql` - query requiredFields { - __type(name: "SuperCar") { - fields { - name - type { - kind + `, + variables: { + id: superCar.id, + }, + }) + ).toBeRejected(); + let getSuperCar = ( + await apolloClient.query({ + query: gql` + query GetSuperCar($id: ID!) { + superCar(id: $id) { + id + objectId + engine + doors + price + mileage } } - } - } - `, - }); - expect(__type2.fields.find(o => o.name === 'price').type.kind).toEqual('SCALAR'); - expect(__type2.fields.find(o => o.name === 'engine').type.kind).toEqual('NON_NULL'); - expect(__type2.fields.find(o => o.name === 'doors').type.kind).toEqual('NON_NULL'); - }); + `, + variables: { + id: superCar.id, + }, + }) + ).data.superCar; + expect(getSuperCar).toBeTruthy(); - it_id('83b6895a-7dfd-4e3b-a5ce-acdb1fa39705')(it)('should only allow the supplied output fields for a class', async () => { - const schemaController = await parseServer.config.databaseController.loadSchema(); + await parseGraphQLServer.setGraphQLConfig({ + classConfigs: [ + { + className: "SuperCar", + type: { + outputFields: [], + }, + }, + ], + }); - await schemaController.addClassIfNotExists('SuperCar', { - engine: { type: 'String' }, - doors: { type: 'Number' }, - price: { type: 'String' }, - mileage: { type: 'Number' }, - insuranceClaims: { type: 'Number' }, - }); + await resetGraphQLCache(); + await expectAsync( + apolloClient.query({ + query: gql` + query GetSuperCar($id: ID!) { + superCar(id: $id) { + engine + } + } + `, + variables: { + id: superCar.id, + }, + }) + ).toBeRejected(); + getSuperCar = ( + await apolloClient.query({ + query: gql` + query GetSuperCar($id: ID!) { + superCar(id: $id) { + id + objectId + } + } + `, + variables: { + id: superCar.id, + }, + }) + ).data.superCar; + expect(getSuperCar.objectId).toBe(superCar.id); + } + ); - const superCar = await new Parse.Object('SuperCar').save({ - engine: 'petrol', - doors: 3, - price: '£7500', - mileage: 0, - insuranceCertificate: 'private-file.pdf', - }); + it_id("67dfcf94-92fb-45a3-a012-3b22c81899ba")(it)( + "should only allow the supplied constraint fields for a class", + async () => { + try { + const schemaController = + await parseServer.config.databaseController.loadSchema(); + + await schemaController.addClassIfNotExists("SuperCar", { + model: { type: "String" }, + engine: { type: "String" }, + doors: { type: "Number" }, + price: { type: "String" }, + mileage: { type: "Number" }, + insuranceCertificate: { type: "String" }, + }); - await parseGraphQLServer.setGraphQLConfig({ - classConfigs: [ - { - className: 'SuperCar', - type: { - outputFields: ['engine', 'doors', 'price', 'mileage'], - }, - }, - ], - }); + await new Parse.Object("SuperCar").save({ + model: "McLaren", + engine: "petrol", + doors: 3, + price: "£7500", + mileage: 0, + insuranceCertificate: "private-file.pdf", + }); + + await parseGraphQLServer.setGraphQLConfig({ + classConfigs: [ + { + className: "SuperCar", + type: { + constraintFields: ["engine", "doors", "price"], + }, + }, + ], + }); - await resetGraphQLCache(); + await resetGraphQLCache(); - await expectAsync( - apolloClient.query({ - query: gql` - query GetSuperCar($id: ID!) { - superCar(id: $id) { - id - objectId - engine - doors - price - mileage - insuranceCertificate - } - } - `, - variables: { - id: superCar.id, - }, - }) - ).toBeRejected(); - let getSuperCar = ( - await apolloClient.query({ - query: gql` - query GetSuperCar($id: ID!) { - superCar(id: $id) { - id - objectId - engine - doors - price - mileage - } - } - `, - variables: { - id: superCar.id, - }, - }) - ).data.superCar; - expect(getSuperCar).toBeTruthy(); + await expectAsync( + apolloClient.query({ + query: gql` + query FindSuperCar { + superCars( + where: { + insuranceCertificate: { equalTo: "private-file.pdf" } + } + ) { + count + } + } + `, + }) + ).toBeRejected(); - await parseGraphQLServer.setGraphQLConfig({ - classConfigs: [ - { - className: 'SuperCar', - type: { - outputFields: [], - }, - }, - ], - }); + await expectAsync( + apolloClient.query({ + query: gql` + query FindSuperCar { + superCars(where: { mileage: { equalTo: 0 } }) { + count + } + } + `, + }) + ).toBeRejected(); - await resetGraphQLCache(); - await expectAsync( - apolloClient.query({ - query: gql` - query GetSuperCar($id: ID!) { - superCar(id: $id) { - engine - } - } - `, - variables: { - id: superCar.id, - }, - }) - ).toBeRejected(); - getSuperCar = ( - await apolloClient.query({ - query: gql` - query GetSuperCar($id: ID!) { - superCar(id: $id) { - id - objectId - } - } - `, - variables: { - id: superCar.id, - }, - }) - ).data.superCar; - expect(getSuperCar.objectId).toBe(superCar.id); - }); + await expectAsync( + apolloClient.query({ + query: gql` + query FindSuperCar { + superCars(where: { engine: { equalTo: "petrol" } }) { + count + } + } + `, + }) + ).toBeResolved(); + } catch (e) { + handleError(e); + } + } + ); - it_id('67dfcf94-92fb-45a3-a012-3b22c81899ba')(it)('should only allow the supplied constraint fields for a class', async () => { - try { - const schemaController = await parseServer.config.databaseController.loadSchema(); + it_id("a3bdbd5d-8779-42fe-91a1-7a7f90a6177b")(it)( + "should only allow the supplied sort fields for a class", + async () => { + const schemaController = + await parseServer.config.databaseController.loadSchema(); - await schemaController.addClassIfNotExists('SuperCar', { - model: { type: 'String' }, - engine: { type: 'String' }, - doors: { type: 'Number' }, - price: { type: 'String' }, - mileage: { type: 'Number' }, - insuranceCertificate: { type: 'String' }, + await schemaController.addClassIfNotExists("SuperCar", { + engine: { type: "String" }, + doors: { type: "Number" }, + price: { type: "String" }, + mileage: { type: "Number" }, }); - await new Parse.Object('SuperCar').save({ - model: 'McLaren', - engine: 'petrol', + await new Parse.Object("SuperCar").save({ + engine: "petrol", doors: 3, - price: '£7500', + price: "£7500", mileage: 0, - insuranceCertificate: 'private-file.pdf', }); await parseGraphQLServer.setGraphQLConfig({ classConfigs: [ { - className: 'SuperCar', + className: "SuperCar", type: { - constraintFields: ['engine', 'doors', 'price'], + sortFields: [ + { + field: "doors", + asc: true, + desc: true, + }, + { + field: "price", + asc: true, + desc: true, + }, + { + field: "mileage", + asc: true, + desc: false, + }, + ], }, }, ], @@ -2042,206 +2285,121 @@ describe('ParseGraphQLServer', () => { apolloClient.query({ query: gql` query FindSuperCar { - superCars(where: { insuranceCertificate: { equalTo: "private-file.pdf" } }) { - count + superCars(order: [engine_ASC]) { + edges { + node { + id + } + } } } `, }) ).toBeRejected(); - await expectAsync( apolloClient.query({ query: gql` query FindSuperCar { - superCars(where: { mileage: { equalTo: 0 } }) { - count + superCars(order: [engine_DESC]) { + edges { + node { + id + } + } } } `, }) ).toBeRejected(); - await expectAsync( apolloClient.query({ query: gql` query FindSuperCar { - superCars(where: { engine: { equalTo: "petrol" } }) { - count + superCars(order: [mileage_DESC]) { + edges { + node { + id + } + } } } `, }) - ).toBeResolved(); - } catch (e) { - handleError(e); - } - }); - - it_id('a3bdbd5d-8779-42fe-91a1-7a7f90a6177b')(it)('should only allow the supplied sort fields for a class', async () => { - const schemaController = await parseServer.config.databaseController.loadSchema(); - - await schemaController.addClassIfNotExists('SuperCar', { - engine: { type: 'String' }, - doors: { type: 'Number' }, - price: { type: 'String' }, - mileage: { type: 'Number' }, - }); - - await new Parse.Object('SuperCar').save({ - engine: 'petrol', - doors: 3, - price: '£7500', - mileage: 0, - }); - - await parseGraphQLServer.setGraphQLConfig({ - classConfigs: [ - { - className: 'SuperCar', - type: { - sortFields: [ - { - field: 'doors', - asc: true, - desc: true, - }, - { - field: 'price', - asc: true, - desc: true, - }, - { - field: 'mileage', - asc: true, - desc: false, - }, - ], - }, - }, - ], - }); - - await resetGraphQLCache(); - - await expectAsync( - apolloClient.query({ - query: gql` - query FindSuperCar { - superCars(order: [engine_ASC]) { - edges { - node { - id - } - } - } - } - `, - }) - ).toBeRejected(); - await expectAsync( - apolloClient.query({ - query: gql` - query FindSuperCar { - superCars(order: [engine_DESC]) { - edges { - node { - id - } - } - } - } - `, - }) - ).toBeRejected(); - await expectAsync( - apolloClient.query({ - query: gql` - query FindSuperCar { - superCars(order: [mileage_DESC]) { - edges { - node { - id - } - } - } - } - `, - }) - ).toBeRejected(); + ).toBeRejected(); - await expectAsync( - apolloClient.query({ - query: gql` - query FindSuperCar { - superCars(order: [mileage_ASC]) { - edges { - node { - id + await expectAsync( + apolloClient.query({ + query: gql` + query FindSuperCar { + superCars(order: [mileage_ASC]) { + edges { + node { + id + } } } } - } - `, - }) - ).toBeResolved(); - await expectAsync( - apolloClient.query({ - query: gql` - query FindSuperCar { - superCars(order: [doors_ASC]) { - edges { - node { - id + `, + }) + ).toBeResolved(); + await expectAsync( + apolloClient.query({ + query: gql` + query FindSuperCar { + superCars(order: [doors_ASC]) { + edges { + node { + id + } } } } - } - `, - }) - ).toBeResolved(); - await expectAsync( - apolloClient.query({ - query: gql` - query FindSuperCar { - superCars(order: [price_DESC]) { - edges { - node { - id + `, + }) + ).toBeResolved(); + await expectAsync( + apolloClient.query({ + query: gql` + query FindSuperCar { + superCars(order: [price_DESC]) { + edges { + node { + id + } } } } - } - `, - }) - ).toBeResolved(); - await expectAsync( - apolloClient.query({ - query: gql` - query FindSuperCar { - superCars(order: [price_ASC, doors_DESC]) { - edges { - node { - id + `, + }) + ).toBeResolved(); + await expectAsync( + apolloClient.query({ + query: gql` + query FindSuperCar { + superCars(order: [price_ASC, doors_DESC]) { + edges { + node { + id + } } } } - } - `, - }) - ).toBeResolved(); - }); + `, + }) + ).toBeResolved(); + } + ); }); - describe('Relay Spec', () => { + describe("Relay Spec", () => { beforeEach(async () => { await resetGraphQLCache(); }); - describe('Object Identification', () => { - it('Class get custom method should return valid gobal id', async () => { - const obj = new Parse.Object('SomeClass'); - obj.set('someField', 'some value'); + describe("Object Identification", () => { + it("Class get custom method should return valid gobal id", async () => { + const obj = new Parse.Object("SomeClass"); + obj.set("someField", "some value"); await obj.save(); const getResult = await apolloClient.query({ @@ -2279,16 +2437,16 @@ describe('ParseGraphQLServer', () => { expect(nodeResult.data.node.id).toBe(getResult.data.someClass.id); expect(nodeResult.data.node.objectId).toBe(obj.id); - expect(nodeResult.data.node.someField).toBe('some value'); + expect(nodeResult.data.node.someField).toBe("some value"); }); - it('Class find custom method should return valid gobal id', async () => { - const obj1 = new Parse.Object('SomeClass'); - obj1.set('someField', 'some value 1'); + it("Class find custom method should return valid gobal id", async () => { + const obj1 = new Parse.Object("SomeClass"); + obj1.set("someField", "some value 1"); await obj1.save(); - const obj2 = new Parse.Object('SomeClass'); - obj2.set('someField', 'some value 2'); + const obj2 = new Parse.Object("SomeClass"); + obj2.set("someField", "some value 2"); await obj2.save(); const findResult = await apolloClient.query({ @@ -2306,8 +2464,12 @@ describe('ParseGraphQLServer', () => { `, }); - expect(findResult.data.someClasses.edges[0].node.objectId).toBe(obj1.id); - expect(findResult.data.someClasses.edges[1].node.objectId).toBe(obj2.id); + expect(findResult.data.someClasses.edges[0].node.objectId).toBe( + obj1.id + ); + expect(findResult.data.someClasses.edges[1].node.objectId).toBe( + obj2.id + ); const nodeResult = await apolloClient.query({ query: gql` @@ -2334,14 +2496,18 @@ describe('ParseGraphQLServer', () => { }, }); - expect(nodeResult.data.node1.id).toBe(findResult.data.someClasses.edges[0].node.id); + expect(nodeResult.data.node1.id).toBe( + findResult.data.someClasses.edges[0].node.id + ); expect(nodeResult.data.node1.objectId).toBe(obj1.id); - expect(nodeResult.data.node1.someField).toBe('some value 1'); - expect(nodeResult.data.node2.id).toBe(findResult.data.someClasses.edges[1].node.id); + expect(nodeResult.data.node1.someField).toBe("some value 1"); + expect(nodeResult.data.node2.id).toBe( + findResult.data.someClasses.edges[1].node.id + ); expect(nodeResult.data.node2.objectId).toBe(obj2.id); - expect(nodeResult.data.node2.someField).toBe('some value 2'); + expect(nodeResult.data.node2.someField).toBe("some value 2"); }); - it('Id inputs should work either with global id or object id', async () => { + it("Id inputs should work either with global id or object id", async () => { try { await apolloClient.mutate({ mutation: gql` @@ -2361,10 +2527,16 @@ describe('ParseGraphQLServer', () => { addStrings: [{ name: "stringField" }] addArrays: [{ name: "arrayField" }] addPointers: [ - { name: "pointerField", targetClassName: "SecondaryObject" } + { + name: "pointerField" + targetClassName: "SecondaryObject" + } ] addRelations: [ - { name: "relationField", targetClassName: "SecondaryObject" } + { + name: "relationField" + targetClassName: "SecondaryObject" + } ] } } @@ -2375,7 +2547,7 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); @@ -2443,7 +2615,7 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); @@ -2459,7 +2631,10 @@ describe('ParseGraphQLServer', () => { $id6: ID! ) { secondaryObject1: updateSecondaryObject( - input: { id: $id1, fields: { someField: "some value 11" } } + input: { + id: $id1 + fields: { someField: "some value 11" } + } ) { secondaryObject { id @@ -2468,7 +2643,10 @@ describe('ParseGraphQLServer', () => { } } secondaryObject2: updateSecondaryObject( - input: { id: $id2, fields: { someField: "some value 22" } } + input: { + id: $id2 + fields: { someField: "some value 22" } + } ) { secondaryObject { id @@ -2476,7 +2654,10 @@ describe('ParseGraphQLServer', () => { } } secondaryObject3: updateSecondaryObject( - input: { id: $id3, fields: { someField: "some value 33" } } + input: { + id: $id3 + fields: { someField: "some value 33" } + } ) { secondaryObject { objectId @@ -2484,7 +2665,10 @@ describe('ParseGraphQLServer', () => { } } secondaryObject4: updateSecondaryObject( - input: { id: $id4, fields: { someField: "some value 44" } } + input: { + id: $id4 + fields: { someField: "some value 44" } + } ) { secondaryObject { id @@ -2492,14 +2676,20 @@ describe('ParseGraphQLServer', () => { } } secondaryObject5: updateSecondaryObject( - input: { id: $id5, fields: { someField: "some value 55" } } + input: { + id: $id5 + fields: { someField: "some value 55" } + } ) { secondaryObject { id } } secondaryObject6: updateSecondaryObject( - input: { id: $id6, fields: { someField: "some value 66" } } + input: { + id: $id6 + fields: { someField: "some value 66" } + } ) { secondaryObject { objectId @@ -2508,42 +2698,61 @@ describe('ParseGraphQLServer', () => { } `, variables: { - id1: createSecondaryObjectsResult.data.secondaryObject1.secondaryObject.id, - id2: createSecondaryObjectsResult.data.secondaryObject2.secondaryObject.id, - id3: createSecondaryObjectsResult.data.secondaryObject3.secondaryObject.objectId, - id4: createSecondaryObjectsResult.data.secondaryObject4.secondaryObject.objectId, - id5: createSecondaryObjectsResult.data.secondaryObject5.secondaryObject.id, - id6: createSecondaryObjectsResult.data.secondaryObject6.secondaryObject.objectId, + id1: createSecondaryObjectsResult.data.secondaryObject1 + .secondaryObject.id, + id2: createSecondaryObjectsResult.data.secondaryObject2 + .secondaryObject.id, + id3: createSecondaryObjectsResult.data.secondaryObject3 + .secondaryObject.objectId, + id4: createSecondaryObjectsResult.data.secondaryObject4 + .secondaryObject.objectId, + id5: createSecondaryObjectsResult.data.secondaryObject5 + .secondaryObject.id, + id6: createSecondaryObjectsResult.data.secondaryObject6 + .secondaryObject.objectId, }, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); const deleteSecondaryObjectsResult = await apolloClient.mutate({ mutation: gql` - mutation DeleteSecondaryObjects($id1: ID!, $id3: ID!, $id5: ID!, $id6: ID!) { - secondaryObject1: deleteSecondaryObject(input: { id: $id1 }) { + mutation DeleteSecondaryObjects( + $id1: ID! + $id3: ID! + $id5: ID! + $id6: ID! + ) { + secondaryObject1: deleteSecondaryObject( + input: { id: $id1 } + ) { secondaryObject { id objectId someField } } - secondaryObject3: deleteSecondaryObject(input: { id: $id3 }) { + secondaryObject3: deleteSecondaryObject( + input: { id: $id3 } + ) { secondaryObject { objectId someField } } - secondaryObject5: deleteSecondaryObject(input: { id: $id5 }) { + secondaryObject5: deleteSecondaryObject( + input: { id: $id5 } + ) { secondaryObject { id } } - secondaryObject6: deleteSecondaryObject(input: { id: $id6 }) { + secondaryObject6: deleteSecondaryObject( + input: { id: $id6 } + ) { secondaryObject { objectId } @@ -2551,14 +2760,18 @@ describe('ParseGraphQLServer', () => { } `, variables: { - id1: updateSecondaryObjectsResult.data.secondaryObject1.secondaryObject.id, - id3: updateSecondaryObjectsResult.data.secondaryObject3.secondaryObject.objectId, - id5: updateSecondaryObjectsResult.data.secondaryObject5.secondaryObject.id, - id6: updateSecondaryObjectsResult.data.secondaryObject6.secondaryObject.objectId, + id1: updateSecondaryObjectsResult.data.secondaryObject1 + .secondaryObject.id, + id3: updateSecondaryObjectsResult.data.secondaryObject3 + .secondaryObject.objectId, + id5: updateSecondaryObjectsResult.data.secondaryObject5 + .secondaryObject.id, + id6: updateSecondaryObjectsResult.data.secondaryObject6 + .secondaryObject.objectId, }, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); @@ -2578,12 +2791,14 @@ describe('ParseGraphQLServer', () => { } `, variables: { - id2: updateSecondaryObjectsResult.data.secondaryObject2.secondaryObject.id, - id4: updateSecondaryObjectsResult.data.secondaryObject4.secondaryObject.objectId, + id2: updateSecondaryObjectsResult.data.secondaryObject2 + .secondaryObject.id, + id4: updateSecondaryObjectsResult.data.secondaryObject4 + .secondaryObject.objectId, }, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); @@ -2604,7 +2819,12 @@ describe('ParseGraphQLServer', () => { { OR: [ { id: { equalTo: $id2 } } - { AND: [{ id: { equalTo: $id4 } }, { objectId: { equalTo: $id4 } }] } + { + AND: [ + { id: { equalTo: $id4 } } + { objectId: { equalTo: $id4 } } + ] + } ] } { id: { notEqualTo: $id1 } } @@ -2628,26 +2848,32 @@ describe('ParseGraphQLServer', () => { } `, variables: { - id1: deleteSecondaryObjectsResult.data.secondaryObject1.secondaryObject.objectId, + id1: deleteSecondaryObjectsResult.data.secondaryObject1 + .secondaryObject.objectId, id2: getSecondaryObjectsResult.data.secondaryObject2.id, - id3: deleteSecondaryObjectsResult.data.secondaryObject3.secondaryObject.objectId, + id3: deleteSecondaryObjectsResult.data.secondaryObject3 + .secondaryObject.objectId, id4: getSecondaryObjectsResult.data.secondaryObject4.objectId, - id5: deleteSecondaryObjectsResult.data.secondaryObject5.secondaryObject.id, - id6: deleteSecondaryObjectsResult.data.secondaryObject6.secondaryObject.objectId, + id5: deleteSecondaryObjectsResult.data.secondaryObject5 + .secondaryObject.id, + id6: deleteSecondaryObjectsResult.data.secondaryObject6 + .secondaryObject.objectId, }, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); - expect(findSecondaryObjectsResult.data.secondaryObjects.count).toEqual(2); + expect( + findSecondaryObjectsResult.data.secondaryObjects.count + ).toEqual(2); expect( findSecondaryObjectsResult.data.secondaryObjects.edges .map(value => value.node.someField) .sort() - ).toEqual(['some value 22', 'some value 44']); + ).toEqual(["some value 22", "some value 44"]); // NOTE: Here @davimacedo tried to test RelayID order, but the test is wrong since // "objectId1" < "objectId2" do not always keep the order when objectId is transformed // to base64 by Relay @@ -2659,16 +2885,22 @@ describe('ParseGraphQLServer', () => { getSecondaryObjectsResult.data.secondaryObject4.objectId, ]; expect( - findSecondaryObjectsResult.data.secondaryObjects.edges[0].node.objectId - ).not.toBe(findSecondaryObjectsResult.data.secondaryObjects.edges[1].node.objectId); + findSecondaryObjectsResult.data.secondaryObjects.edges[0].node + .objectId + ).not.toBe( + findSecondaryObjectsResult.data.secondaryObjects.edges[1].node + .objectId + ); expect( originalIds.includes( - findSecondaryObjectsResult.data.secondaryObjects.edges[0].node.objectId + findSecondaryObjectsResult.data.secondaryObjects.edges[0].node + .objectId ) ).toBeTrue(); expect( originalIds.includes( - findSecondaryObjectsResult.data.secondaryObjects.edges[1].node.objectId + findSecondaryObjectsResult.data.secondaryObjects.edges[1].node + .objectId ) ).toBeTrue(); @@ -2685,7 +2917,9 @@ describe('ParseGraphQLServer', () => { stringField: "some value" arrayField: [1, "abc", $pointer] pointerField: { link: $secondaryObject2 } - relationField: { add: [$secondaryObject2, $secondaryObject4] } + relationField: { + add: [$secondaryObject2, $secondaryObject4] + } } } ) { @@ -2720,16 +2954,19 @@ describe('ParseGraphQLServer', () => { `, variables: { pointer: { - __type: 'Pointer', - className: 'SecondaryObject', - objectId: getSecondaryObjectsResult.data.secondaryObject4.objectId, + __type: "Pointer", + className: "SecondaryObject", + objectId: + getSecondaryObjectsResult.data.secondaryObject4.objectId, }, - secondaryObject2: getSecondaryObjectsResult.data.secondaryObject2.id, - secondaryObject4: getSecondaryObjectsResult.data.secondaryObject4.objectId, + secondaryObject2: + getSecondaryObjectsResult.data.secondaryObject2.id, + secondaryObject4: + getSecondaryObjectsResult.data.secondaryObject4.objectId, }, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); @@ -2746,7 +2983,9 @@ describe('ParseGraphQLServer', () => { id: $id fields: { pointerField: { link: $secondaryObject4 } - relationField: { remove: [$secondaryObject2, $secondaryObject4] } + relationField: { + remove: [$secondaryObject2, $secondaryObject4] + } } } ) { @@ -2780,62 +3019,70 @@ describe('ParseGraphQLServer', () => { } `, variables: { - id: createPrimaryObjectResult.data.createPrimaryObject.primaryObject.id, - secondaryObject2: getSecondaryObjectsResult.data.secondaryObject2.id, - secondaryObject4: getSecondaryObjectsResult.data.secondaryObject4.objectId, + id: createPrimaryObjectResult.data.createPrimaryObject + .primaryObject.id, + secondaryObject2: + getSecondaryObjectsResult.data.secondaryObject2.id, + secondaryObject4: + getSecondaryObjectsResult.data.secondaryObject4.objectId, }, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); expect( - createPrimaryObjectResult.data.createPrimaryObject.primaryObject.stringField - ).toEqual('some value'); + createPrimaryObjectResult.data.createPrimaryObject.primaryObject + .stringField + ).toEqual("some value"); expect( - createPrimaryObjectResult.data.createPrimaryObject.primaryObject.arrayField + createPrimaryObjectResult.data.createPrimaryObject.primaryObject + .arrayField ).toEqual([ - { __typename: 'Element', value: 1 }, - { __typename: 'Element', value: 'abc' }, - { __typename: 'SecondaryObject', someField: 'some value 44' }, + { __typename: "Element", value: 1 }, + { __typename: "Element", value: "abc" }, + { __typename: "SecondaryObject", someField: "some value 44" }, ]); expect( - createPrimaryObjectResult.data.createPrimaryObject.primaryObject.pointerField - .someField - ).toEqual('some value 22'); + createPrimaryObjectResult.data.createPrimaryObject.primaryObject + .pointerField.someField + ).toEqual("some value 22"); expect( createPrimaryObjectResult.data.createPrimaryObject.primaryObject.relationField.edges .map(value => value.node.someField) .sort() - ).toEqual(['some value 22', 'some value 44']); + ).toEqual(["some value 22", "some value 44"]); expect( - updatePrimaryObjectResult.data.updatePrimaryObject.primaryObject.stringField - ).toEqual('some value'); + updatePrimaryObjectResult.data.updatePrimaryObject.primaryObject + .stringField + ).toEqual("some value"); expect( - updatePrimaryObjectResult.data.updatePrimaryObject.primaryObject.arrayField + updatePrimaryObjectResult.data.updatePrimaryObject.primaryObject + .arrayField ).toEqual([ - { __typename: 'Element', value: 1 }, - { __typename: 'Element', value: 'abc' }, - { __typename: 'SecondaryObject', someField: 'some value 44' }, + { __typename: "Element", value: 1 }, + { __typename: "Element", value: "abc" }, + { __typename: "SecondaryObject", someField: "some value 44" }, ]); expect( - updatePrimaryObjectResult.data.updatePrimaryObject.primaryObject.pointerField - .someField - ).toEqual('some value 44'); + updatePrimaryObjectResult.data.updatePrimaryObject.primaryObject + .pointerField.someField + ).toEqual("some value 44"); expect( - updatePrimaryObjectResult.data.updatePrimaryObject.primaryObject.relationField.edges + updatePrimaryObjectResult.data.updatePrimaryObject.primaryObject + .relationField.edges ).toEqual([]); } catch (e) { handleError(e); } }); - it('Id inputs should work either with global id or object id with objectId higher than 19', async () => { + it("Id inputs should work either with global id or object id with objectId higher than 19", async () => { const parseServer = await reconfigureServer({ objectIdSize: 20 }); await createGQLFromParseServer(parseServer); - const obj = new Parse.Object('SomeClass'); - await obj.save({ name: 'aname', type: 'robot' }); + const obj = new Parse.Object("SomeClass"); + await obj.save({ name: "aname", type: "robot" }); const result = await apolloClient.query({ query: gql` query getSomeClass($id: ID!) { @@ -2852,13 +3099,15 @@ describe('ParseGraphQLServer', () => { }); }); - describe('Class Schema Mutations', () => { - it('should create a new class', async () => { + describe("Class Schema Mutations", () => { + it("should create a new class", async () => { try { const result = await apolloClient.mutate({ mutation: gql` mutation { - class1: createClass(input: { name: "Class1", clientMutationId: "cmid1" }) { + class1: createClass( + input: { name: "Class1", clientMutationId: "cmid1" } + ) { clientMutationId class { name @@ -2869,7 +3118,11 @@ describe('ParseGraphQLServer', () => { } } class2: createClass( - input: { name: "Class2", schemaFields: null, clientMutationId: "cmid2" } + input: { + name: "Class2" + schemaFields: null + clientMutationId: "cmid2" + } ) { clientMutationId class { @@ -2881,7 +3134,11 @@ describe('ParseGraphQLServer', () => { } } class3: createClass( - input: { name: "Class3", schemaFields: {}, clientMutationId: "cmid3" } + input: { + name: "Class3" + schemaFields: {} + clientMutationId: "cmid3" + } ) { clientMutationId class { @@ -3046,7 +3303,7 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); @@ -3054,8 +3311,8 @@ describe('ParseGraphQLServer', () => { clientMutationId: result.data[fieldName].clientMutationId, class: { name: result.data[fieldName].class.name, - schemaFields: result.data[fieldName].class.schemaFields.sort((a, b) => - a.name > b.name ? 1 : -1 + schemaFields: result.data[fieldName].class.schemaFields.sort( + (a, b) => (a.name > b.name ? 1 : -1) ), __typename: result.data[fieldName].class.__typename, }, @@ -3063,130 +3320,130 @@ describe('ParseGraphQLServer', () => { })); expect(classes).toEqual([ { - clientMutationId: 'cmid1', + clientMutationId: "cmid1", class: { - name: 'Class1', + name: "Class1", schemaFields: [ - { name: 'ACL', __typename: 'SchemaACLField' }, - { name: 'createdAt', __typename: 'SchemaDateField' }, - { name: 'objectId', __typename: 'SchemaStringField' }, - { name: 'updatedAt', __typename: 'SchemaDateField' }, + { name: "ACL", __typename: "SchemaACLField" }, + { name: "createdAt", __typename: "SchemaDateField" }, + { name: "objectId", __typename: "SchemaStringField" }, + { name: "updatedAt", __typename: "SchemaDateField" }, ], - __typename: 'Class', + __typename: "Class", }, - __typename: 'CreateClassPayload', + __typename: "CreateClassPayload", }, { - clientMutationId: 'cmid2', + clientMutationId: "cmid2", class: { - name: 'Class2', + name: "Class2", schemaFields: [ - { name: 'ACL', __typename: 'SchemaACLField' }, - { name: 'createdAt', __typename: 'SchemaDateField' }, - { name: 'objectId', __typename: 'SchemaStringField' }, - { name: 'updatedAt', __typename: 'SchemaDateField' }, + { name: "ACL", __typename: "SchemaACLField" }, + { name: "createdAt", __typename: "SchemaDateField" }, + { name: "objectId", __typename: "SchemaStringField" }, + { name: "updatedAt", __typename: "SchemaDateField" }, ], - __typename: 'Class', + __typename: "Class", }, - __typename: 'CreateClassPayload', + __typename: "CreateClassPayload", }, { - clientMutationId: 'cmid3', + clientMutationId: "cmid3", class: { - name: 'Class3', + name: "Class3", schemaFields: [ - { name: 'ACL', __typename: 'SchemaACLField' }, - { name: 'createdAt', __typename: 'SchemaDateField' }, - { name: 'objectId', __typename: 'SchemaStringField' }, - { name: 'updatedAt', __typename: 'SchemaDateField' }, + { name: "ACL", __typename: "SchemaACLField" }, + { name: "createdAt", __typename: "SchemaDateField" }, + { name: "objectId", __typename: "SchemaStringField" }, + { name: "updatedAt", __typename: "SchemaDateField" }, ], - __typename: 'Class', + __typename: "Class", }, - __typename: 'CreateClassPayload', + __typename: "CreateClassPayload", }, { - clientMutationId: 'cmid4', + clientMutationId: "cmid4", class: { - name: 'Class4', + name: "Class4", schemaFields: [ - { name: 'ACL', __typename: 'SchemaACLField' }, - { name: 'createdAt', __typename: 'SchemaDateField' }, - { name: 'objectId', __typename: 'SchemaStringField' }, - { name: 'updatedAt', __typename: 'SchemaDateField' }, + { name: "ACL", __typename: "SchemaACLField" }, + { name: "createdAt", __typename: "SchemaDateField" }, + { name: "objectId", __typename: "SchemaStringField" }, + { name: "updatedAt", __typename: "SchemaDateField" }, ], - __typename: 'Class', + __typename: "Class", }, - __typename: 'CreateClassPayload', + __typename: "CreateClassPayload", }, { - clientMutationId: 'cmid5', + clientMutationId: "cmid5", class: { - name: 'Class5', + name: "Class5", schemaFields: [ - { name: 'ACL', __typename: 'SchemaACLField' }, - { name: 'createdAt', __typename: 'SchemaDateField' }, - { name: 'objectId', __typename: 'SchemaStringField' }, - { name: 'updatedAt', __typename: 'SchemaDateField' }, + { name: "ACL", __typename: "SchemaACLField" }, + { name: "createdAt", __typename: "SchemaDateField" }, + { name: "objectId", __typename: "SchemaStringField" }, + { name: "updatedAt", __typename: "SchemaDateField" }, ], - __typename: 'Class', + __typename: "Class", }, - __typename: 'CreateClassPayload', + __typename: "CreateClassPayload", }, { - clientMutationId: 'cmid6', + clientMutationId: "cmid6", class: { - name: 'Class6', + name: "Class6", schemaFields: [ - { name: 'ACL', __typename: 'SchemaACLField' }, - { name: 'arrayField1', __typename: 'SchemaArrayField' }, - { name: 'arrayField2', __typename: 'SchemaArrayField' }, - { name: 'booleanField1', __typename: 'SchemaBooleanField' }, - { name: 'booleanField2', __typename: 'SchemaBooleanField' }, - { name: 'bytesField1', __typename: 'SchemaBytesField' }, - { name: 'bytesField2', __typename: 'SchemaBytesField' }, - { name: 'createdAt', __typename: 'SchemaDateField' }, - { name: 'dateField1', __typename: 'SchemaDateField' }, - { name: 'dateField2', __typename: 'SchemaDateField' }, - { name: 'fileField1', __typename: 'SchemaFileField' }, - { name: 'fileField2', __typename: 'SchemaFileField' }, + { name: "ACL", __typename: "SchemaACLField" }, + { name: "arrayField1", __typename: "SchemaArrayField" }, + { name: "arrayField2", __typename: "SchemaArrayField" }, + { name: "booleanField1", __typename: "SchemaBooleanField" }, + { name: "booleanField2", __typename: "SchemaBooleanField" }, + { name: "bytesField1", __typename: "SchemaBytesField" }, + { name: "bytesField2", __typename: "SchemaBytesField" }, + { name: "createdAt", __typename: "SchemaDateField" }, + { name: "dateField1", __typename: "SchemaDateField" }, + { name: "dateField2", __typename: "SchemaDateField" }, + { name: "fileField1", __typename: "SchemaFileField" }, + { name: "fileField2", __typename: "SchemaFileField" }, { - name: 'geoPointField', - __typename: 'SchemaGeoPointField', + name: "geoPointField", + __typename: "SchemaGeoPointField", }, - { name: 'numberField1', __typename: 'SchemaNumberField' }, - { name: 'numberField2', __typename: 'SchemaNumberField' }, - { name: 'objectField1', __typename: 'SchemaObjectField' }, - { name: 'objectField2', __typename: 'SchemaObjectField' }, - { name: 'objectId', __typename: 'SchemaStringField' }, + { name: "numberField1", __typename: "SchemaNumberField" }, + { name: "numberField2", __typename: "SchemaNumberField" }, + { name: "objectField1", __typename: "SchemaObjectField" }, + { name: "objectField2", __typename: "SchemaObjectField" }, + { name: "objectId", __typename: "SchemaStringField" }, { - name: 'pointerField1', - __typename: 'SchemaPointerField', - targetClassName: 'Class1', + name: "pointerField1", + __typename: "SchemaPointerField", + targetClassName: "Class1", }, { - name: 'pointerField2', - __typename: 'SchemaPointerField', - targetClassName: 'Class6', + name: "pointerField2", + __typename: "SchemaPointerField", + targetClassName: "Class6", }, - { name: 'polygonField1', __typename: 'SchemaPolygonField' }, - { name: 'polygonField2', __typename: 'SchemaPolygonField' }, + { name: "polygonField1", __typename: "SchemaPolygonField" }, + { name: "polygonField2", __typename: "SchemaPolygonField" }, { - name: 'relationField1', - __typename: 'SchemaRelationField', - targetClassName: 'Class1', + name: "relationField1", + __typename: "SchemaRelationField", + targetClassName: "Class1", }, { - name: 'relationField2', - __typename: 'SchemaRelationField', - targetClassName: 'Class6', + name: "relationField2", + __typename: "SchemaRelationField", + targetClassName: "Class6", }, - { name: 'stringField1', __typename: 'SchemaStringField' }, - { name: 'stringField2', __typename: 'SchemaStringField' }, - { name: 'updatedAt', __typename: 'SchemaDateField' }, + { name: "stringField1", __typename: "SchemaStringField" }, + { name: "stringField2", __typename: "SchemaStringField" }, + { name: "updatedAt", __typename: "SchemaDateField" }, ], - __typename: 'Class', + __typename: "Class", }, - __typename: 'CreateClassPayload', + __typename: "CreateClassPayload", }, ]); @@ -3210,120 +3467,120 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); findResult.data.classes = findResult.data.classes - .filter(schemaClass => !schemaClass.name.startsWith('_')) + .filter(schemaClass => !schemaClass.name.startsWith("_")) .sort((a, b) => (a.name > b.name ? 1 : -1)); findResult.data.classes.forEach(schemaClass => { - schemaClass.schemaFields = schemaClass.schemaFields.sort((a, b) => - a.name > b.name ? 1 : -1 + schemaClass.schemaFields = schemaClass.schemaFields.sort( + (a, b) => (a.name > b.name ? 1 : -1) ); }); expect(findResult.data.classes).toEqual([ { - name: 'Class1', + name: "Class1", schemaFields: [ - { name: 'ACL', __typename: 'SchemaACLField' }, - { name: 'createdAt', __typename: 'SchemaDateField' }, - { name: 'objectId', __typename: 'SchemaStringField' }, - { name: 'updatedAt', __typename: 'SchemaDateField' }, + { name: "ACL", __typename: "SchemaACLField" }, + { name: "createdAt", __typename: "SchemaDateField" }, + { name: "objectId", __typename: "SchemaStringField" }, + { name: "updatedAt", __typename: "SchemaDateField" }, ], - __typename: 'Class', + __typename: "Class", }, { - name: 'Class2', + name: "Class2", schemaFields: [ - { name: 'ACL', __typename: 'SchemaACLField' }, - { name: 'createdAt', __typename: 'SchemaDateField' }, - { name: 'objectId', __typename: 'SchemaStringField' }, - { name: 'updatedAt', __typename: 'SchemaDateField' }, + { name: "ACL", __typename: "SchemaACLField" }, + { name: "createdAt", __typename: "SchemaDateField" }, + { name: "objectId", __typename: "SchemaStringField" }, + { name: "updatedAt", __typename: "SchemaDateField" }, ], - __typename: 'Class', + __typename: "Class", }, { - name: 'Class3', + name: "Class3", schemaFields: [ - { name: 'ACL', __typename: 'SchemaACLField' }, - { name: 'createdAt', __typename: 'SchemaDateField' }, - { name: 'objectId', __typename: 'SchemaStringField' }, - { name: 'updatedAt', __typename: 'SchemaDateField' }, + { name: "ACL", __typename: "SchemaACLField" }, + { name: "createdAt", __typename: "SchemaDateField" }, + { name: "objectId", __typename: "SchemaStringField" }, + { name: "updatedAt", __typename: "SchemaDateField" }, ], - __typename: 'Class', + __typename: "Class", }, { - name: 'Class4', + name: "Class4", schemaFields: [ - { name: 'ACL', __typename: 'SchemaACLField' }, - { name: 'createdAt', __typename: 'SchemaDateField' }, - { name: 'objectId', __typename: 'SchemaStringField' }, - { name: 'updatedAt', __typename: 'SchemaDateField' }, + { name: "ACL", __typename: "SchemaACLField" }, + { name: "createdAt", __typename: "SchemaDateField" }, + { name: "objectId", __typename: "SchemaStringField" }, + { name: "updatedAt", __typename: "SchemaDateField" }, ], - __typename: 'Class', + __typename: "Class", }, { - name: 'Class5', + name: "Class5", schemaFields: [ - { name: 'ACL', __typename: 'SchemaACLField' }, - { name: 'createdAt', __typename: 'SchemaDateField' }, - { name: 'objectId', __typename: 'SchemaStringField' }, - { name: 'updatedAt', __typename: 'SchemaDateField' }, + { name: "ACL", __typename: "SchemaACLField" }, + { name: "createdAt", __typename: "SchemaDateField" }, + { name: "objectId", __typename: "SchemaStringField" }, + { name: "updatedAt", __typename: "SchemaDateField" }, ], - __typename: 'Class', + __typename: "Class", }, { - name: 'Class6', + name: "Class6", schemaFields: [ - { name: 'ACL', __typename: 'SchemaACLField' }, - { name: 'arrayField1', __typename: 'SchemaArrayField' }, - { name: 'arrayField2', __typename: 'SchemaArrayField' }, - { name: 'booleanField1', __typename: 'SchemaBooleanField' }, - { name: 'booleanField2', __typename: 'SchemaBooleanField' }, - { name: 'bytesField1', __typename: 'SchemaBytesField' }, - { name: 'bytesField2', __typename: 'SchemaBytesField' }, - { name: 'createdAt', __typename: 'SchemaDateField' }, - { name: 'dateField1', __typename: 'SchemaDateField' }, - { name: 'dateField2', __typename: 'SchemaDateField' }, - { name: 'fileField1', __typename: 'SchemaFileField' }, - { name: 'fileField2', __typename: 'SchemaFileField' }, + { name: "ACL", __typename: "SchemaACLField" }, + { name: "arrayField1", __typename: "SchemaArrayField" }, + { name: "arrayField2", __typename: "SchemaArrayField" }, + { name: "booleanField1", __typename: "SchemaBooleanField" }, + { name: "booleanField2", __typename: "SchemaBooleanField" }, + { name: "bytesField1", __typename: "SchemaBytesField" }, + { name: "bytesField2", __typename: "SchemaBytesField" }, + { name: "createdAt", __typename: "SchemaDateField" }, + { name: "dateField1", __typename: "SchemaDateField" }, + { name: "dateField2", __typename: "SchemaDateField" }, + { name: "fileField1", __typename: "SchemaFileField" }, + { name: "fileField2", __typename: "SchemaFileField" }, { - name: 'geoPointField', - __typename: 'SchemaGeoPointField', + name: "geoPointField", + __typename: "SchemaGeoPointField", }, - { name: 'numberField1', __typename: 'SchemaNumberField' }, - { name: 'numberField2', __typename: 'SchemaNumberField' }, - { name: 'objectField1', __typename: 'SchemaObjectField' }, - { name: 'objectField2', __typename: 'SchemaObjectField' }, - { name: 'objectId', __typename: 'SchemaStringField' }, + { name: "numberField1", __typename: "SchemaNumberField" }, + { name: "numberField2", __typename: "SchemaNumberField" }, + { name: "objectField1", __typename: "SchemaObjectField" }, + { name: "objectField2", __typename: "SchemaObjectField" }, + { name: "objectId", __typename: "SchemaStringField" }, { - name: 'pointerField1', - __typename: 'SchemaPointerField', - targetClassName: 'Class1', + name: "pointerField1", + __typename: "SchemaPointerField", + targetClassName: "Class1", }, { - name: 'pointerField2', - __typename: 'SchemaPointerField', - targetClassName: 'Class6', + name: "pointerField2", + __typename: "SchemaPointerField", + targetClassName: "Class6", }, - { name: 'polygonField1', __typename: 'SchemaPolygonField' }, - { name: 'polygonField2', __typename: 'SchemaPolygonField' }, + { name: "polygonField1", __typename: "SchemaPolygonField" }, + { name: "polygonField2", __typename: "SchemaPolygonField" }, { - name: 'relationField1', - __typename: 'SchemaRelationField', - targetClassName: 'Class1', + name: "relationField1", + __typename: "SchemaRelationField", + targetClassName: "Class1", }, { - name: 'relationField2', - __typename: 'SchemaRelationField', - targetClassName: 'Class6', + name: "relationField2", + __typename: "SchemaRelationField", + targetClassName: "Class6", }, - { name: 'stringField1', __typename: 'SchemaStringField' }, - { name: 'stringField2', __typename: 'SchemaStringField' }, - { name: 'updatedAt', __typename: 'SchemaDateField' }, + { name: "stringField1", __typename: "SchemaStringField" }, + { name: "stringField2", __typename: "SchemaStringField" }, + { name: "updatedAt", __typename: "SchemaDateField" }, ], - __typename: 'Class', + __typename: "Class", }, ]); } catch (e) { @@ -3331,7 +3588,7 @@ describe('ParseGraphQLServer', () => { } }); - it('should require master key to create a new class', async () => { + it("should require master key to create a new class", async () => { try { await apolloClient.mutate({ mutation: gql` @@ -3342,14 +3599,18 @@ describe('ParseGraphQLServer', () => { } `, }); - fail('should fail'); + fail("should fail"); } catch (e) { - expect(e.graphQLErrors[0].extensions.code).toEqual(Parse.Error.OPERATION_FORBIDDEN); - expect(e.graphQLErrors[0].message).toEqual('unauthorized: master key is required'); + expect(e.graphQLErrors[0].extensions.code).toEqual( + Parse.Error.OPERATION_FORBIDDEN + ); + expect(e.graphQLErrors[0].message).toEqual( + "unauthorized: master key is required" + ); } }); - it('should not allow duplicated field names when creating', async () => { + it("should not allow duplicated field names when creating", async () => { try { await apolloClient.mutate({ mutation: gql` @@ -3369,18 +3630,22 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); - fail('should fail'); + fail("should fail"); } catch (e) { - expect(e.graphQLErrors[0].extensions.code).toEqual(Parse.Error.INVALID_KEY_NAME); - expect(e.graphQLErrors[0].message).toEqual('Duplicated field name: someField'); + expect(e.graphQLErrors[0].extensions.code).toEqual( + Parse.Error.INVALID_KEY_NAME + ); + expect(e.graphQLErrors[0].message).toEqual( + "Duplicated field name: someField" + ); } }); - it('should update an existing class', async () => { + it("should update an existing class", async () => { try { const clientMutationId = uuidv4(); const result = await apolloClient.mutate({ @@ -3496,102 +3761,104 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); - result.data.createClass.class.schemaFields = result.data.createClass.class.schemaFields.sort( - (a, b) => (a.name > b.name ? 1 : -1) - ); - result.data.updateClass.class.schemaFields = result.data.updateClass.class.schemaFields.sort( - (a, b) => (a.name > b.name ? 1 : -1) - ); + result.data.createClass.class.schemaFields = + result.data.createClass.class.schemaFields.sort((a, b) => + a.name > b.name ? 1 : -1 + ); + result.data.updateClass.class.schemaFields = + result.data.updateClass.class.schemaFields.sort((a, b) => + a.name > b.name ? 1 : -1 + ); expect(result).toEqual({ data: { createClass: { class: { - name: 'MyNewClass', + name: "MyNewClass", schemaFields: [ - { name: 'ACL', __typename: 'SchemaACLField' }, - { name: 'createdAt', __typename: 'SchemaDateField' }, - { name: 'objectId', __typename: 'SchemaStringField' }, - { name: 'updatedAt', __typename: 'SchemaDateField' }, + { name: "ACL", __typename: "SchemaACLField" }, + { name: "createdAt", __typename: "SchemaDateField" }, + { name: "objectId", __typename: "SchemaStringField" }, + { name: "updatedAt", __typename: "SchemaDateField" }, { - name: 'willBeRemoved', - __typename: 'SchemaStringField', + name: "willBeRemoved", + __typename: "SchemaStringField", }, ], - __typename: 'Class', + __typename: "Class", }, - __typename: 'CreateClassPayload', + __typename: "CreateClassPayload", }, updateClass: { clientMutationId, class: { - name: 'MyNewClass', + name: "MyNewClass", schemaFields: [ - { name: 'ACL', __typename: 'SchemaACLField' }, - { name: 'arrayField1', __typename: 'SchemaArrayField' }, - { name: 'arrayField2', __typename: 'SchemaArrayField' }, + { name: "ACL", __typename: "SchemaACLField" }, + { name: "arrayField1", __typename: "SchemaArrayField" }, + { name: "arrayField2", __typename: "SchemaArrayField" }, { - name: 'booleanField1', - __typename: 'SchemaBooleanField', + name: "booleanField1", + __typename: "SchemaBooleanField", }, { - name: 'booleanField2', - __typename: 'SchemaBooleanField', + name: "booleanField2", + __typename: "SchemaBooleanField", }, - { name: 'bytesField1', __typename: 'SchemaBytesField' }, - { name: 'bytesField2', __typename: 'SchemaBytesField' }, - { name: 'createdAt', __typename: 'SchemaDateField' }, - { name: 'dateField1', __typename: 'SchemaDateField' }, - { name: 'dateField2', __typename: 'SchemaDateField' }, - { name: 'fileField1', __typename: 'SchemaFileField' }, - { name: 'fileField2', __typename: 'SchemaFileField' }, + { name: "bytesField1", __typename: "SchemaBytesField" }, + { name: "bytesField2", __typename: "SchemaBytesField" }, + { name: "createdAt", __typename: "SchemaDateField" }, + { name: "dateField1", __typename: "SchemaDateField" }, + { name: "dateField2", __typename: "SchemaDateField" }, + { name: "fileField1", __typename: "SchemaFileField" }, + { name: "fileField2", __typename: "SchemaFileField" }, { - name: 'geoPointField', - __typename: 'SchemaGeoPointField', + name: "geoPointField", + __typename: "SchemaGeoPointField", }, - { name: 'numberField1', __typename: 'SchemaNumberField' }, - { name: 'numberField2', __typename: 'SchemaNumberField' }, - { name: 'objectField1', __typename: 'SchemaObjectField' }, - { name: 'objectField2', __typename: 'SchemaObjectField' }, - { name: 'objectId', __typename: 'SchemaStringField' }, + { name: "numberField1", __typename: "SchemaNumberField" }, + { name: "numberField2", __typename: "SchemaNumberField" }, + { name: "objectField1", __typename: "SchemaObjectField" }, + { name: "objectField2", __typename: "SchemaObjectField" }, + { name: "objectId", __typename: "SchemaStringField" }, { - name: 'pointerField1', - __typename: 'SchemaPointerField', - targetClassName: 'Class1', + name: "pointerField1", + __typename: "SchemaPointerField", + targetClassName: "Class1", }, { - name: 'pointerField2', - __typename: 'SchemaPointerField', - targetClassName: 'Class6', + name: "pointerField2", + __typename: "SchemaPointerField", + targetClassName: "Class6", }, { - name: 'polygonField1', - __typename: 'SchemaPolygonField', + name: "polygonField1", + __typename: "SchemaPolygonField", }, { - name: 'polygonField2', - __typename: 'SchemaPolygonField', + name: "polygonField2", + __typename: "SchemaPolygonField", }, { - name: 'relationField1', - __typename: 'SchemaRelationField', - targetClassName: 'Class1', + name: "relationField1", + __typename: "SchemaRelationField", + targetClassName: "Class1", }, { - name: 'relationField2', - __typename: 'SchemaRelationField', - targetClassName: 'Class6', + name: "relationField2", + __typename: "SchemaRelationField", + targetClassName: "Class6", }, - { name: 'stringField1', __typename: 'SchemaStringField' }, - { name: 'stringField2', __typename: 'SchemaStringField' }, - { name: 'updatedAt', __typename: 'SchemaDateField' }, + { name: "stringField1", __typename: "SchemaStringField" }, + { name: "stringField2", __typename: "SchemaStringField" }, + { name: "updatedAt", __typename: "SchemaDateField" }, ], - __typename: 'Class', + __typename: "Class", }, - __typename: 'UpdateClassPayload', + __typename: "UpdateClassPayload", }, }, }); @@ -3616,65 +3883,66 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); - getResult.data.class.schemaFields = getResult.data.class.schemaFields.sort((a, b) => - a.name > b.name ? 1 : -1 - ); + getResult.data.class.schemaFields = + getResult.data.class.schemaFields.sort((a, b) => + a.name > b.name ? 1 : -1 + ); expect(getResult.data).toEqual({ class: { - name: 'MyNewClass', + name: "MyNewClass", schemaFields: [ - { name: 'ACL', __typename: 'SchemaACLField' }, - { name: 'arrayField1', __typename: 'SchemaArrayField' }, - { name: 'arrayField2', __typename: 'SchemaArrayField' }, - { name: 'booleanField1', __typename: 'SchemaBooleanField' }, - { name: 'booleanField2', __typename: 'SchemaBooleanField' }, - { name: 'bytesField1', __typename: 'SchemaBytesField' }, - { name: 'bytesField2', __typename: 'SchemaBytesField' }, - { name: 'createdAt', __typename: 'SchemaDateField' }, - { name: 'dateField1', __typename: 'SchemaDateField' }, - { name: 'dateField2', __typename: 'SchemaDateField' }, - { name: 'fileField1', __typename: 'SchemaFileField' }, - { name: 'fileField2', __typename: 'SchemaFileField' }, + { name: "ACL", __typename: "SchemaACLField" }, + { name: "arrayField1", __typename: "SchemaArrayField" }, + { name: "arrayField2", __typename: "SchemaArrayField" }, + { name: "booleanField1", __typename: "SchemaBooleanField" }, + { name: "booleanField2", __typename: "SchemaBooleanField" }, + { name: "bytesField1", __typename: "SchemaBytesField" }, + { name: "bytesField2", __typename: "SchemaBytesField" }, + { name: "createdAt", __typename: "SchemaDateField" }, + { name: "dateField1", __typename: "SchemaDateField" }, + { name: "dateField2", __typename: "SchemaDateField" }, + { name: "fileField1", __typename: "SchemaFileField" }, + { name: "fileField2", __typename: "SchemaFileField" }, { - name: 'geoPointField', - __typename: 'SchemaGeoPointField', + name: "geoPointField", + __typename: "SchemaGeoPointField", }, - { name: 'numberField1', __typename: 'SchemaNumberField' }, - { name: 'numberField2', __typename: 'SchemaNumberField' }, - { name: 'objectField1', __typename: 'SchemaObjectField' }, - { name: 'objectField2', __typename: 'SchemaObjectField' }, - { name: 'objectId', __typename: 'SchemaStringField' }, + { name: "numberField1", __typename: "SchemaNumberField" }, + { name: "numberField2", __typename: "SchemaNumberField" }, + { name: "objectField1", __typename: "SchemaObjectField" }, + { name: "objectField2", __typename: "SchemaObjectField" }, + { name: "objectId", __typename: "SchemaStringField" }, { - name: 'pointerField1', - __typename: 'SchemaPointerField', - targetClassName: 'Class1', + name: "pointerField1", + __typename: "SchemaPointerField", + targetClassName: "Class1", }, { - name: 'pointerField2', - __typename: 'SchemaPointerField', - targetClassName: 'Class6', + name: "pointerField2", + __typename: "SchemaPointerField", + targetClassName: "Class6", }, - { name: 'polygonField1', __typename: 'SchemaPolygonField' }, - { name: 'polygonField2', __typename: 'SchemaPolygonField' }, + { name: "polygonField1", __typename: "SchemaPolygonField" }, + { name: "polygonField2", __typename: "SchemaPolygonField" }, { - name: 'relationField1', - __typename: 'SchemaRelationField', - targetClassName: 'Class1', + name: "relationField1", + __typename: "SchemaRelationField", + targetClassName: "Class1", }, { - name: 'relationField2', - __typename: 'SchemaRelationField', - targetClassName: 'Class6', + name: "relationField2", + __typename: "SchemaRelationField", + targetClassName: "Class6", }, - { name: 'stringField1', __typename: 'SchemaStringField' }, - { name: 'stringField2', __typename: 'SchemaStringField' }, - { name: 'updatedAt', __typename: 'SchemaDateField' }, + { name: "stringField1", __typename: "SchemaStringField" }, + { name: "stringField2", __typename: "SchemaStringField" }, + { name: "updatedAt", __typename: "SchemaDateField" }, ], - __typename: 'Class', + __typename: "Class", }, }); } catch (e) { @@ -3682,7 +3950,7 @@ describe('ParseGraphQLServer', () => { } }); - it('should require master key to update an existing class', async () => { + it("should require master key to update an existing class", async () => { try { await apolloClient.mutate({ mutation: gql` @@ -3694,7 +3962,7 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); @@ -3712,14 +3980,18 @@ describe('ParseGraphQLServer', () => { } `, }); - fail('should fail'); + fail("should fail"); } catch (e) { - expect(e.graphQLErrors[0].extensions.code).toEqual(Parse.Error.OPERATION_FORBIDDEN); - expect(e.graphQLErrors[0].message).toEqual('unauthorized: master key is required'); + expect(e.graphQLErrors[0].extensions.code).toEqual( + Parse.Error.OPERATION_FORBIDDEN + ); + expect(e.graphQLErrors[0].message).toEqual( + "unauthorized: master key is required" + ); } }); - it('should not allow duplicated field names when updating', async () => { + it("should not allow duplicated field names when updating", async () => { try { await apolloClient.mutate({ mutation: gql` @@ -3736,7 +4008,7 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); @@ -3760,18 +4032,22 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); - fail('should fail'); + fail("should fail"); } catch (e) { - expect(e.graphQLErrors[0].extensions.code).toEqual(Parse.Error.INVALID_KEY_NAME); - expect(e.graphQLErrors[0].message).toEqual('Duplicated field name: someField'); + expect(e.graphQLErrors[0].extensions.code).toEqual( + Parse.Error.INVALID_KEY_NAME + ); + expect(e.graphQLErrors[0].message).toEqual( + "Duplicated field name: someField" + ); } }); - it('should fail if updating an inexistent class', async () => { + it("should fail if updating an inexistent class", async () => { try { await apolloClient.mutate({ mutation: gql` @@ -3788,18 +4064,22 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); - fail('should fail'); + fail("should fail"); } catch (e) { - expect(e.graphQLErrors[0].extensions.code).toEqual(Parse.Error.INVALID_CLASS_NAME); - expect(e.graphQLErrors[0].message).toEqual('Class SomeInexistentClass does not exist.'); + expect(e.graphQLErrors[0].extensions.code).toEqual( + Parse.Error.INVALID_CLASS_NAME + ); + expect(e.graphQLErrors[0].message).toEqual( + "Class SomeInexistentClass does not exist." + ); } }); - it('should delete an existing class', async () => { + it("should delete an existing class", async () => { try { const clientMutationId = uuidv4(); const result = await apolloClient.mutate({ @@ -3832,52 +4112,54 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); - result.data.createClass.class.schemaFields = result.data.createClass.class.schemaFields.sort( - (a, b) => (a.name > b.name ? 1 : -1) - ); - result.data.deleteClass.class.schemaFields = result.data.deleteClass.class.schemaFields.sort( - (a, b) => (a.name > b.name ? 1 : -1) - ); + result.data.createClass.class.schemaFields = + result.data.createClass.class.schemaFields.sort((a, b) => + a.name > b.name ? 1 : -1 + ); + result.data.deleteClass.class.schemaFields = + result.data.deleteClass.class.schemaFields.sort((a, b) => + a.name > b.name ? 1 : -1 + ); expect(result).toEqual({ data: { createClass: { class: { - name: 'MyNewClass', + name: "MyNewClass", schemaFields: [ - { name: 'ACL', __typename: 'SchemaACLField' }, - { name: 'createdAt', __typename: 'SchemaDateField' }, - { name: 'objectId', __typename: 'SchemaStringField' }, - { name: 'updatedAt', __typename: 'SchemaDateField' }, + { name: "ACL", __typename: "SchemaACLField" }, + { name: "createdAt", __typename: "SchemaDateField" }, + { name: "objectId", __typename: "SchemaStringField" }, + { name: "updatedAt", __typename: "SchemaDateField" }, { - name: 'willBeRemoved', - __typename: 'SchemaStringField', + name: "willBeRemoved", + __typename: "SchemaStringField", }, ], - __typename: 'Class', + __typename: "Class", }, - __typename: 'CreateClassPayload', + __typename: "CreateClassPayload", }, deleteClass: { clientMutationId, class: { - name: 'MyNewClass', + name: "MyNewClass", schemaFields: [ - { name: 'ACL', __typename: 'SchemaACLField' }, - { name: 'createdAt', __typename: 'SchemaDateField' }, - { name: 'objectId', __typename: 'SchemaStringField' }, - { name: 'updatedAt', __typename: 'SchemaDateField' }, + { name: "ACL", __typename: "SchemaACLField" }, + { name: "createdAt", __typename: "SchemaDateField" }, + { name: "objectId", __typename: "SchemaStringField" }, + { name: "updatedAt", __typename: "SchemaDateField" }, { - name: 'willBeRemoved', - __typename: 'SchemaStringField', + name: "willBeRemoved", + __typename: "SchemaStringField", }, ], - __typename: 'Class', + __typename: "Class", }, - __typename: 'DeleteClassPayload', + __typename: "DeleteClassPayload", }, }, }); @@ -3893,21 +4175,25 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); - fail('should fail'); + fail("should fail"); } catch (e) { - expect(e.graphQLErrors[0].extensions.code).toEqual(Parse.Error.INVALID_CLASS_NAME); - expect(e.graphQLErrors[0].message).toEqual('Class MyNewClass does not exist.'); + expect(e.graphQLErrors[0].extensions.code).toEqual( + Parse.Error.INVALID_CLASS_NAME + ); + expect(e.graphQLErrors[0].message).toEqual( + "Class MyNewClass does not exist." + ); } } catch (e) { handleError(e); } }); - it('should require master key to delete an existing class', async () => { + it("should require master key to delete an existing class", async () => { try { await apolloClient.mutate({ mutation: gql` @@ -3919,7 +4205,7 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); @@ -3937,14 +4223,18 @@ describe('ParseGraphQLServer', () => { } `, }); - fail('should fail'); + fail("should fail"); } catch (e) { - expect(e.graphQLErrors[0].extensions.code).toEqual(Parse.Error.OPERATION_FORBIDDEN); - expect(e.graphQLErrors[0].message).toEqual('unauthorized: master key is required'); + expect(e.graphQLErrors[0].extensions.code).toEqual( + Parse.Error.OPERATION_FORBIDDEN + ); + expect(e.graphQLErrors[0].message).toEqual( + "unauthorized: master key is required" + ); } }); - it('should fail if deleting an inexistent class', async () => { + it("should fail if deleting an inexistent class", async () => { try { await apolloClient.mutate({ mutation: gql` @@ -3956,18 +4246,22 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); - fail('should fail'); + fail("should fail"); } catch (e) { - expect(e.graphQLErrors[0].extensions.code).toEqual(Parse.Error.INVALID_CLASS_NAME); - expect(e.graphQLErrors[0].message).toEqual('Class SomeInexistentClass does not exist.'); + expect(e.graphQLErrors[0].extensions.code).toEqual( + Parse.Error.INVALID_CLASS_NAME + ); + expect(e.graphQLErrors[0].message).toEqual( + "Class SomeInexistentClass does not exist." + ); } }); - it('should require master key to get an existing class', async () => { + it("should require master key to get an existing class", async () => { try { await apolloClient.query({ query: gql` @@ -3978,14 +4272,18 @@ describe('ParseGraphQLServer', () => { } `, }); - fail('should fail'); + fail("should fail"); } catch (e) { - expect(e.graphQLErrors[0].extensions.code).toEqual(Parse.Error.OPERATION_FORBIDDEN); - expect(e.graphQLErrors[0].message).toEqual('unauthorized: master key is required'); + expect(e.graphQLErrors[0].extensions.code).toEqual( + Parse.Error.OPERATION_FORBIDDEN + ); + expect(e.graphQLErrors[0].message).toEqual( + "unauthorized: master key is required" + ); } }); - it('should require master key to find the existing classes', async () => { + it("should require master key to find the existing classes", async () => { try { await apolloClient.query({ query: gql` @@ -3996,19 +4294,23 @@ describe('ParseGraphQLServer', () => { } `, }); - fail('should fail'); + fail("should fail"); } catch (e) { - expect(e.graphQLErrors[0].extensions.code).toEqual(Parse.Error.OPERATION_FORBIDDEN); - expect(e.graphQLErrors[0].message).toEqual('unauthorized: master key is required'); + expect(e.graphQLErrors[0].extensions.code).toEqual( + Parse.Error.OPERATION_FORBIDDEN + ); + expect(e.graphQLErrors[0].message).toEqual( + "unauthorized: master key is required" + ); } }); }); - describe('Objects Queries', () => { - describe('Get', () => { - it('should return a class object using class specific query', async () => { - const obj = new Parse.Object('Customer'); - obj.set('someField', 'someValue'); + describe("Objects Queries", () => { + describe("Get", () => { + it("should return a class object using class specific query", async () => { + const obj = new Parse.Object("Customer"); + obj.set("someField", "someValue"); await obj.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -4033,98 +4335,111 @@ describe('ParseGraphQLServer', () => { ).data.customer; expect(result.objectId).toEqual(obj.id); - expect(result.someField).toEqual('someValue'); + expect(result.someField).toEqual("someValue"); expect(new Date(result.createdAt)).toEqual(obj.createdAt); expect(new Date(result.updatedAt)).toEqual(obj.updatedAt); }); - it_only_db('mongo')('should return child objects in array fields', async () => { - const obj1 = new Parse.Object('Customer'); - const obj2 = new Parse.Object('SomeClass'); - const obj3 = new Parse.Object('Customer'); + it_only_db("mongo")( + "should return child objects in array fields", + async () => { + const obj1 = new Parse.Object("Customer"); + const obj2 = new Parse.Object("SomeClass"); + const obj3 = new Parse.Object("Customer"); - obj1.set('someCustomerField', 'imCustomerOne'); - const arrayField = [42.42, 42, 'string', true]; - obj1.set('arrayField', arrayField); - await obj1.save(); + obj1.set("someCustomerField", "imCustomerOne"); + const arrayField = [42.42, 42, "string", true]; + obj1.set("arrayField", arrayField); + await obj1.save(); - obj2.set('someClassField', 'imSomeClassTwo'); - await obj2.save(); + obj2.set("someClassField", "imSomeClassTwo"); + await obj2.save(); - obj3.set('manyRelations', [obj1, obj2]); - await obj3.save(); + obj3.set("manyRelations", [obj1, obj2]); + await obj3.save(); - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const result = ( - await apolloClient.query({ - query: gql` - query GetCustomer($id: ID!) { - customer(id: $id) { - objectId - manyRelations { - ... on Customer { - objectId - someCustomerField - arrayField { - ... on Element { - value + const result = ( + await apolloClient.query({ + query: gql` + query GetCustomer($id: ID!) { + customer(id: $id) { + objectId + manyRelations { + ... on Customer { + objectId + someCustomerField + arrayField { + ... on Element { + value + } } } + ... on SomeClass { + objectId + someClassField + } } - ... on SomeClass { - objectId - someClassField - } + createdAt + updatedAt } - createdAt - updatedAt } - } - `, - variables: { - id: obj3.id, - }, - }) - ).data.customer; + `, + variables: { + id: obj3.id, + }, + }) + ).data.customer; - expect(result.objectId).toEqual(obj3.id); - expect(result.manyRelations.length).toEqual(2); + expect(result.objectId).toEqual(obj3.id); + expect(result.manyRelations.length).toEqual(2); - const customerSubObject = result.manyRelations.find(o => o.objectId === obj1.id); - const someClassSubObject = result.manyRelations.find(o => o.objectId === obj2.id); + const customerSubObject = result.manyRelations.find( + o => o.objectId === obj1.id + ); + const someClassSubObject = result.manyRelations.find( + o => o.objectId === obj2.id + ); - expect(customerSubObject).toBeDefined(); - expect(someClassSubObject).toBeDefined(); - expect(customerSubObject.someCustomerField).toEqual('imCustomerOne'); - const formatedArrayField = customerSubObject.arrayField.map(elem => elem.value); - expect(formatedArrayField).toEqual(arrayField); - expect(someClassSubObject.someClassField).toEqual('imSomeClassTwo'); - }); + expect(customerSubObject).toBeDefined(); + expect(someClassSubObject).toBeDefined(); + expect(customerSubObject.someCustomerField).toEqual( + "imCustomerOne" + ); + const formatedArrayField = customerSubObject.arrayField.map( + elem => elem.value + ); + expect(formatedArrayField).toEqual(arrayField); + expect(someClassSubObject.someClassField).toEqual( + "imSomeClassTwo" + ); + } + ); - it('should return many child objects in allow cyclic query', async () => { - const obj1 = new Parse.Object('Employee'); - const obj2 = new Parse.Object('Team'); - const obj3 = new Parse.Object('Company'); - const obj4 = new Parse.Object('Country'); + it("should return many child objects in allow cyclic query", async () => { + const obj1 = new Parse.Object("Employee"); + const obj2 = new Parse.Object("Team"); + const obj3 = new Parse.Object("Company"); + const obj4 = new Parse.Object("Country"); - obj1.set('name', 'imAnEmployee'); + obj1.set("name", "imAnEmployee"); await obj1.save(); - obj2.set('name', 'imATeam'); - obj2.set('employees', [obj1]); + obj2.set("name", "imATeam"); + obj2.set("employees", [obj1]); await obj2.save(); - obj3.set('name', 'imACompany'); - obj3.set('teams', [obj2]); - obj3.set('employees', [obj1]); + obj3.set("name", "imACompany"); + obj3.set("teams", [obj2]); + obj3.set("employees", [obj1]); await obj3.save(); - obj4.set('name', 'imACountry'); - obj4.set('companies', [obj3]); + obj4.set("name", "imACountry"); + obj4.set("companies", [obj3]); await obj4.save(); - obj1.set('country', obj4); + obj1.set("country", obj4); await obj1.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -4175,34 +4490,34 @@ describe('ParseGraphQLServer', () => { const expectedResult = { objectId: obj4.id, - name: 'imACountry', - __typename: 'Country', + name: "imACountry", + __typename: "Country", companies: [ { objectId: obj3.id, - name: 'imACompany', - __typename: 'Company', + name: "imACompany", + __typename: "Company", employees: [ { objectId: obj1.id, - name: 'imAnEmployee', - __typename: 'Employee', + name: "imAnEmployee", + __typename: "Employee", }, ], teams: [ { objectId: obj2.id, - name: 'imATeam', - __typename: 'Team', + name: "imATeam", + __typename: "Team", employees: [ { objectId: obj1.id, - name: 'imAnEmployee', - __typename: 'Employee', + name: "imAnEmployee", + __typename: "Employee", country: { objectId: obj4.id, - name: 'imACountry', - __typename: 'Country', + name: "imACountry", + __typename: "Country", }, }, ], @@ -4214,13 +4529,14 @@ describe('ParseGraphQLServer', () => { expect(result).toEqual(expectedResult); }); - it('should respect level permissions', async () => { + it("should respect level permissions", async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); async function getObject(className, id, headers) { - const alias = className.charAt(0).toLowerCase() + className.slice(1); + const alias = + className.charAt(0).toLowerCase() + className.slice(1); const specificQueryResult = await apolloClient.query({ query: gql` query GetSomeObject($id: ID!) { @@ -4246,23 +4562,24 @@ describe('ParseGraphQLServer', () => { objects .slice(0, 3) .map(obj => - expectAsync(getObject(obj.className, obj.id)).toBeRejectedWith( - jasmine.stringMatching('Object not found') - ) + expectAsync( + getObject(obj.className, obj.id) + ).toBeRejectedWith(jasmine.stringMatching("Object not found")) ) ); - expect((await getObject(object4.className, object4.id)).data.get.someField).toEqual( - 'someValue4' - ); + expect( + (await getObject(object4.className, object4.id)).data.get + .someField + ).toEqual("someValue4"); await Promise.all( objects.map(async obj => expect( ( await getObject(obj.className, obj.id, { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }) ).data.get.someField - ).toEqual(obj.get('someField')) + ).toEqual(obj.get("someField")) ) ); await Promise.all( @@ -4270,10 +4587,10 @@ describe('ParseGraphQLServer', () => { expect( ( await getObject(obj.className, obj.id, { - 'X-Parse-Session-Token': user1.getSessionToken(), + "X-Parse-Session-Token": user1.getSessionToken(), }) ).data.get.someField - ).toEqual(obj.get('someField')) + ).toEqual(obj.get("someField")) ) ); await Promise.all( @@ -4281,70 +4598,70 @@ describe('ParseGraphQLServer', () => { expect( ( await getObject(obj.className, obj.id, { - 'X-Parse-Session-Token': user2.getSessionToken(), + "X-Parse-Session-Token": user2.getSessionToken(), }) ).data.get.someField - ).toEqual(obj.get('someField')) + ).toEqual(obj.get("someField")) ) ); await expectAsync( getObject(object2.className, object2.id, { - 'X-Parse-Session-Token': user3.getSessionToken(), + "X-Parse-Session-Token": user3.getSessionToken(), }) - ).toBeRejectedWith(jasmine.stringMatching('Object not found')); + ).toBeRejectedWith(jasmine.stringMatching("Object not found")); await Promise.all( [object1, object3, object4].map(async obj => expect( ( await getObject(obj.className, obj.id, { - 'X-Parse-Session-Token': user3.getSessionToken(), + "X-Parse-Session-Token": user3.getSessionToken(), }) ).data.get.someField - ).toEqual(obj.get('someField')) + ).toEqual(obj.get("someField")) ) ); await Promise.all( objects.slice(0, 3).map(obj => expectAsync( getObject(obj.className, obj.id, { - 'X-Parse-Session-Token': user4.getSessionToken(), + "X-Parse-Session-Token": user4.getSessionToken(), }) - ).toBeRejectedWith(jasmine.stringMatching('Object not found')) + ).toBeRejectedWith(jasmine.stringMatching("Object not found")) ) ); expect( ( await getObject(object4.className, object4.id, { - 'X-Parse-Session-Token': user4.getSessionToken(), + "X-Parse-Session-Token": user4.getSessionToken(), }) ).data.get.someField - ).toEqual('someValue4'); + ).toEqual("someValue4"); await Promise.all( objects.slice(0, 2).map(obj => expectAsync( getObject(obj.className, obj.id, { - 'X-Parse-Session-Token': user5.getSessionToken(), + "X-Parse-Session-Token": user5.getSessionToken(), }) - ).toBeRejectedWith(jasmine.stringMatching('Object not found')) + ).toBeRejectedWith(jasmine.stringMatching("Object not found")) ) ); expect( ( await getObject(object3.className, object3.id, { - 'X-Parse-Session-Token': user5.getSessionToken(), + "X-Parse-Session-Token": user5.getSessionToken(), }) ).data.get.someField - ).toEqual('someValue3'); + ).toEqual("someValue3"); expect( ( await getObject(object4.className, object4.id, { - 'X-Parse-Session-Token': user5.getSessionToken(), + "X-Parse-Session-Token": user5.getSessionToken(), }) ).data.get.someField - ).toEqual('someValue4'); + ).toEqual("someValue4"); }); - it('should support keys argument', async () => { + it("should support keys argument", async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -4362,7 +4679,7 @@ describe('ParseGraphQLServer', () => { }, context: { headers: { - 'X-Parse-Session-Token': user1.getSessionToken(), + "X-Parse-Session-Token": user1.getSessionToken(), }, }, }); @@ -4383,7 +4700,7 @@ describe('ParseGraphQLServer', () => { }, context: { headers: { - 'X-Parse-Session-Token': user1.getSessionToken(), + "X-Parse-Session-Token": user1.getSessionToken(), }, }, }); @@ -4394,7 +4711,7 @@ describe('ParseGraphQLServer', () => { expect(result2.data.get.pointerToUser).toBeDefined(); }); - it('should support include argument', async () => { + it("should support include argument", async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -4414,7 +4731,7 @@ describe('ParseGraphQLServer', () => { }, context: { headers: { - 'X-Parse-Session-Token': user1.getSessionToken(), + "X-Parse-Session-Token": user1.getSessionToken(), }, }, }); @@ -4434,30 +4751,32 @@ describe('ParseGraphQLServer', () => { }, context: { headers: { - 'X-Parse-Session-Token': user1.getSessionToken(), + "X-Parse-Session-Token": user1.getSessionToken(), }, }, }); expect(result1.data.get.pointerToUser.username).toBeUndefined(); - expect(result2.data.graphQLClass.pointerToUser.username).toBeDefined(); + expect( + result2.data.graphQLClass.pointerToUser.username + ).toBeDefined(); }); - it('should respect protectedFields', async done => { + it("should respect protectedFields", async done => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const className = 'GraphQLClass'; + const className = "GraphQLClass"; await updateCLP( { - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, protectedFields: { - '*': ['someField', 'someOtherField'], - authenticated: ['someField'], - 'userField:pointerToUser': [], + "*": ["someField", "someOtherField"], + authenticated: ["someField"], + "userField:pointerToUser": [], [user2.id]: [], }, }, @@ -4466,7 +4785,7 @@ describe('ParseGraphQLServer', () => { const getObject = async (className, id, user) => { const headers = user - ? { ['X-Parse-Session-Token']: user.getSessionToken() } + ? { ["X-Parse-Session-Token"]: user.getSessionToken() } : undefined; const specificQueryResult = await apolloClient.query({ @@ -4505,30 +4824,30 @@ describe('ParseGraphQLServer', () => { const objectAuth = await getObject(className, id, user1); expect(objectAuth.someField).toBeNull(); - expect(objectAuth.someOtherField).toBe('B'); + expect(objectAuth.someOtherField).toBe("B"); /* pointer field */ const objectPointed = await getObject(className, id, user5); - expect(objectPointed.someField).toBe('someValue3'); - expect(objectPointed.someOtherField).toBe('B'); + expect(objectPointed.someField).toBe("someValue3"); + expect(objectPointed.someOtherField).toBe("B"); /* for user id */ const objectForUser = await getObject(className, id, user2); - expect(objectForUser.someField).toBe('someValue3'); - expect(objectForUser.someOtherField).toBe('B'); + expect(objectForUser.someField).toBe("someValue3"); + expect(objectForUser.someOtherField).toBe("B"); done(); }); - describe_only_db('mongo')('read preferences', () => { - it('should read from primary by default', async () => { + describe_only_db("mongo")("read preferences", () => { + it("should read from primary by default", async () => { try { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); await apolloClient.query({ query: gql` @@ -4545,7 +4864,7 @@ describe('ParseGraphQLServer', () => { }, context: { headers: { - 'X-Parse-Session-Token': user1.getSessionToken(), + "X-Parse-Session-Token": user1.getSessionToken(), }, }, }); @@ -4553,12 +4872,22 @@ describe('ParseGraphQLServer', () => { let foundGraphQLClassReadPreference = false; let foundUserClassReadPreference = false; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) { + if ( + call.object.s.namespace.collection.indexOf( + "GraphQLClass" + ) >= 0 + ) { foundGraphQLClassReadPreference = true; - expect(call.object.s.readPreference.mode).toBe(ReadPreference.PRIMARY); - } else if (call.object.s.namespace.collection.indexOf('_User') >= 0) { + expect(call.object.s.readPreference.mode).toBe( + ReadPreference.PRIMARY + ); + } else if ( + call.object.s.namespace.collection.indexOf("_User") >= 0 + ) { foundUserClassReadPreference = true; - expect(call.object.s.readPreference.mode).toBe(ReadPreference.PRIMARY); + expect(call.object.s.readPreference.mode).toBe( + ReadPreference.PRIMARY + ); } }); @@ -4569,17 +4898,20 @@ describe('ParseGraphQLServer', () => { } }); - it('should support readPreference argument', async () => { + it("should support readPreference argument", async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); await apolloClient.query({ query: gql` query GetSomeObject($id: ID!) { - graphQLClass(id: $id, options: { readPreference: SECONDARY }) { + graphQLClass( + id: $id + options: { readPreference: SECONDARY } + ) { pointerToUser { username } @@ -4591,7 +4923,7 @@ describe('ParseGraphQLServer', () => { }, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); @@ -4599,12 +4931,21 @@ describe('ParseGraphQLServer', () => { let foundGraphQLClassReadPreference = false; let foundUserClassReadPreference = false; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) { + if ( + call.object.s.namespace.collection.indexOf("GraphQLClass") >= + 0 + ) { foundGraphQLClassReadPreference = true; - expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY); - } else if (call.object.s.namespace.collection.indexOf('_User') >= 0) { + expect(call.args[1].readPreference).toBe( + ReadPreference.SECONDARY + ); + } else if ( + call.object.s.namespace.collection.indexOf("_User") >= 0 + ) { foundUserClassReadPreference = true; - expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY); + expect(call.args[1].readPreference).toBe( + ReadPreference.SECONDARY + ); } }); @@ -4612,19 +4953,22 @@ describe('ParseGraphQLServer', () => { expect(foundUserClassReadPreference).toBe(true); }); - it('should support includeReadPreference argument', async () => { + it("should support includeReadPreference argument", async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); await apolloClient.query({ query: gql` query GetSomeObject($id: ID!) { graphQLClass( id: $id - options: { readPreference: SECONDARY, includeReadPreference: NEAREST } + options: { + readPreference: SECONDARY + includeReadPreference: NEAREST + } ) { pointerToUser { username @@ -4637,7 +4981,7 @@ describe('ParseGraphQLServer', () => { }, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); @@ -4645,12 +4989,21 @@ describe('ParseGraphQLServer', () => { let foundGraphQLClassReadPreference = false; let foundUserClassReadPreference = false; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) { + if ( + call.object.s.namespace.collection.indexOf("GraphQLClass") >= + 0 + ) { foundGraphQLClassReadPreference = true; - expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY); - } else if (call.object.s.namespace.collection.indexOf('_User') >= 0) { + expect(call.args[1].readPreference).toBe( + ReadPreference.SECONDARY + ); + } else if ( + call.object.s.namespace.collection.indexOf("_User") >= 0 + ) { foundUserClassReadPreference = true; - expect(call.args[1].readPreference).toBe(ReadPreference.NEAREST); + expect(call.args[1].readPreference).toBe( + ReadPreference.NEAREST + ); } }); @@ -4660,13 +5013,13 @@ describe('ParseGraphQLServer', () => { }); }); - describe('Find', () => { - it('should return class objects using class specific query', async () => { - const obj1 = new Parse.Object('Customer'); - obj1.set('someField', 'someValue1'); + describe("Find", () => { + it("should return class objects using class specific query", async () => { + const obj1 = new Parse.Object("Customer"); + obj1.set("someField", "someValue1"); await obj1.save(); - const obj2 = new Parse.Object('Customer'); - obj2.set('someField', 'someValue1'); + const obj2 = new Parse.Object("Customer"); + obj2.set("someField", "someValue1"); await obj2.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -4693,13 +5046,13 @@ describe('ParseGraphQLServer', () => { result.data.customers.edges.forEach(resultObj => { const obj = resultObj.node.objectId === obj1.id ? obj1 : obj2; expect(resultObj.node.objectId).toEqual(obj.id); - expect(resultObj.node.someField).toEqual(obj.get('someField')); + expect(resultObj.node.someField).toEqual(obj.get("someField")); expect(new Date(resultObj.node.createdAt)).toEqual(obj.createdAt); expect(new Date(resultObj.node.updatedAt)).toEqual(obj.updatedAt); }); }); - it('should respect level permissions', async () => { + it("should respect level permissions", async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -4730,82 +5083,82 @@ describe('ParseGraphQLServer', () => { } expect( - (await findObjects('GraphQLClass')).data.find.edges.map( + (await findObjects("GraphQLClass")).data.find.edges.map( object => object.node.someField ) ).toEqual([]); expect( - (await findObjects('PublicClass')).data.find.edges.map( + (await findObjects("PublicClass")).data.find.edges.map( object => object.node.someField ) - ).toEqual(['someValue4']); + ).toEqual(["someValue4"]); expect( ( - await findObjects('GraphQLClass', { - 'X-Parse-Master-Key': 'test', + await findObjects("GraphQLClass", { + "X-Parse-Master-Key": "test", }) ).data.find.edges .map(object => object.node.someField) .sort() - ).toEqual(['someValue1', 'someValue2', 'someValue3']); + ).toEqual(["someValue1", "someValue2", "someValue3"]); expect( ( - await findObjects('PublicClass', { - 'X-Parse-Master-Key': 'test', + await findObjects("PublicClass", { + "X-Parse-Master-Key": "test", }) ).data.find.edges.map(object => object.node.someField) - ).toEqual(['someValue4']); + ).toEqual(["someValue4"]); expect( ( - await findObjects('GraphQLClass', { - 'X-Parse-Session-Token': user1.getSessionToken(), + await findObjects("GraphQLClass", { + "X-Parse-Session-Token": user1.getSessionToken(), }) ).data.find.edges .map(object => object.node.someField) .sort() - ).toEqual(['someValue1', 'someValue2', 'someValue3']); + ).toEqual(["someValue1", "someValue2", "someValue3"]); expect( ( - await findObjects('PublicClass', { - 'X-Parse-Session-Token': user1.getSessionToken(), + await findObjects("PublicClass", { + "X-Parse-Session-Token": user1.getSessionToken(), }) ).data.find.edges.map(object => object.node.someField) - ).toEqual(['someValue4']); + ).toEqual(["someValue4"]); expect( ( - await findObjects('GraphQLClass', { - 'X-Parse-Session-Token': user2.getSessionToken(), + await findObjects("GraphQLClass", { + "X-Parse-Session-Token": user2.getSessionToken(), }) ).data.find.edges .map(object => object.node.someField) .sort() - ).toEqual(['someValue1', 'someValue2', 'someValue3']); + ).toEqual(["someValue1", "someValue2", "someValue3"]); expect( ( - await findObjects('GraphQLClass', { - 'X-Parse-Session-Token': user3.getSessionToken(), + await findObjects("GraphQLClass", { + "X-Parse-Session-Token": user3.getSessionToken(), }) ).data.find.edges .map(object => object.node.someField) .sort() - ).toEqual(['someValue1', 'someValue3']); + ).toEqual(["someValue1", "someValue3"]); expect( ( - await findObjects('GraphQLClass', { - 'X-Parse-Session-Token': user4.getSessionToken(), + await findObjects("GraphQLClass", { + "X-Parse-Session-Token": user4.getSessionToken(), }) ).data.find.edges.map(object => object.node.someField) ).toEqual([]); expect( ( - await findObjects('GraphQLClass', { - 'X-Parse-Session-Token': user5.getSessionToken(), + await findObjects("GraphQLClass", { + "X-Parse-Session-Token": user5.getSessionToken(), }) ).data.find.edges.map(object => object.node.someField) - ).toEqual(['someValue3']); + ).toEqual(["someValue3"]); }); - it('should support where argument using class specific query', async () => { + it("should support where argument using class specific query", async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -4825,7 +5178,7 @@ describe('ParseGraphQLServer', () => { variables: { where: { someField: { - in: ['someValue1', 'someValue2', 'someValue3'], + in: ["someValue1", "someValue2", "someValue3"], }, OR: [ { @@ -4847,17 +5200,19 @@ describe('ParseGraphQLServer', () => { }, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); expect( - result.data.graphQLClasses.edges.map(object => object.node.someField).sort() - ).toEqual(['someValue1', 'someValue3']); + result.data.graphQLClasses.edges + .map(object => object.node.someField) + .sort() + ).toEqual(["someValue1", "someValue3"]); }); - it('should support in pointer operator using class specific query', async () => { + it("should support in pointer operator using class specific query", async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -4887,17 +5242,17 @@ describe('ParseGraphQLServer', () => { }, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); const { edges } = result.data.graphQLClasses; expect(edges.length).toBe(1); - expect(edges[0].node.someField).toEqual('someValue3'); + expect(edges[0].node.someField).toEqual("someValue3"); }); - it('should support OR operation', async () => { + it("should support OR operation", async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -4923,79 +5278,88 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); expect( - result.data.graphQLClasses.edges.map(object => object.node.someField).sort() - ).toEqual(['someValue1', 'someValue2']); + result.data.graphQLClasses.edges + .map(object => object.node.someField) + .sort() + ).toEqual(["someValue1", "someValue2"]); }); - it_id('accc59be-fd13-46c5-a103-ec63f2ad6670')(it)('should support full text search', async () => { - try { - const obj = new Parse.Object('FullTextSearchTest'); - obj.set('field1', 'Parse GraphQL Server'); - obj.set('field2', 'It rocks!'); - await obj.save(); + it_id("accc59be-fd13-46c5-a103-ec63f2ad6670")(it)( + "should support full text search", + async () => { + try { + const obj = new Parse.Object("FullTextSearchTest"); + obj.set("field1", "Parse GraphQL Server"); + obj.set("field2", "It rocks!"); + await obj.save(); - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const result = await apolloClient.query({ - query: gql` - query FullTextSearchTests($where: FullTextSearchTestWhereInput) { - fullTextSearchTests(where: $where) { - edges { - node { - objectId + const result = await apolloClient.query({ + query: gql` + query FullTextSearchTests( + $where: FullTextSearchTestWhereInput + ) { + fullTextSearchTests(where: $where) { + edges { + node { + objectId + } } } } - } - `, - context: { - headers: { - 'X-Parse-Master-Key': 'test', + `, + context: { + headers: { + "X-Parse-Master-Key": "test", + }, }, - }, - variables: { - where: { - field1: { - text: { - search: { - term: 'graphql', + variables: { + where: { + field1: { + text: { + search: { + term: "graphql", + }, }, }, }, }, - }, - }); + }); - expect(result.data.fullTextSearchTests.edges[0].node.objectId).toEqual(obj.id); - } catch (e) { - handleError(e); + expect( + result.data.fullTextSearchTests.edges[0].node.objectId + ).toEqual(obj.id); + } catch (e) { + handleError(e); + } } - }); + ); - it('should support in query key', async () => { + it("should support in query key", async () => { try { - const country = new Parse.Object('Country'); - country.set('code', 'FR'); + const country = new Parse.Object("Country"); + country.set("code", "FR"); await country.save(); - const country2 = new Parse.Object('Country'); - country2.set('code', 'US'); + const country2 = new Parse.Object("Country"); + country2.set("code", "US"); await country2.save(); - const city = new Parse.Object('City'); - city.set('country', 'FR'); - city.set('name', 'city1'); + const city = new Parse.Object("City"); + city.set("country", "FR"); + city.set("name", "city1"); await city.save(); - const city2 = new Parse.Object('City'); - city2.set('country', 'US'); - city2.set('name', 'city2'); + const city2 = new Parse.Object("City"); + city2.set("country", "US"); + city2.set("name", "city2"); await city2.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -5019,7 +5383,7 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, variables: { @@ -5027,10 +5391,10 @@ describe('ParseGraphQLServer', () => { country: { inQueryKey: { query: { - className: 'Country', - where: { code: { equalTo: 'US' } }, + className: "Country", + where: { code: { equalTo: "US" } }, }, - key: 'code', + key: "code", }, }, }, @@ -5038,263 +5402,305 @@ describe('ParseGraphQLServer', () => { }); expect(result.length).toEqual(1); - expect(result[0].node.name).toEqual('city2'); + expect(result[0].node.name).toEqual("city2"); } catch (e) { handleError(e); } }); - it_id('0fd03d3c-a2c8-4fac-95cc-2391a3032ca2')(it)('should support order, skip and first arguments', async () => { - const promises = []; - for (let i = 0; i < 100; i++) { - const obj = new Parse.Object('SomeClass'); - obj.set('someField', `someValue${i < 10 ? '0' : ''}${i}`); - obj.set('numberField', i % 3); - promises.push(obj.save()); - } - await Promise.all(promises); + it_id("0fd03d3c-a2c8-4fac-95cc-2391a3032ca2")(it)( + "should support order, skip and first arguments", + async () => { + const promises = []; + for (let i = 0; i < 100; i++) { + const obj = new Parse.Object("SomeClass"); + obj.set("someField", `someValue${i < 10 ? "0" : ""}${i}`); + obj.set("numberField", i % 3); + promises.push(obj.save()); + } + await Promise.all(promises); - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const result = await apolloClient.query({ - query: gql` - query FindSomeObjects( - $where: SomeClassWhereInput - $order: [SomeClassOrder!] - $skip: Int - $first: Int - ) { - find: someClasses(where: $where, order: $order, skip: $skip, first: $first) { - edges { - node { - someField + const result = await apolloClient.query({ + query: gql` + query FindSomeObjects( + $where: SomeClassWhereInput + $order: [SomeClassOrder!] + $skip: Int + $first: Int + ) { + find: someClasses( + where: $where + order: $order + skip: $skip + first: $first + ) { + edges { + node { + someField + } } } } - } - `, - variables: { - where: { - someField: { - matchesRegex: '^someValue', + `, + variables: { + where: { + someField: { + matchesRegex: "^someValue", + }, }, + order: ["numberField_DESC", "someField_ASC"], + skip: 4, + first: 2, }, - order: ['numberField_DESC', 'someField_ASC'], - skip: 4, - first: 2, - }, - }); + }); - expect(result.data.find.edges.map(obj => obj.node.someField)).toEqual([ - 'someValue14', - 'someValue17', - ]); - }); + expect( + result.data.find.edges.map(obj => obj.node.someField) + ).toEqual(["someValue14", "someValue17"]); + } + ); - it_id('588a70c6-2932-4d3b-a838-a74c59d8cffb')(it)('should support pagination', async () => { - const numberArray = (first, last) => { - const array = []; - for (let i = first; i <= last; i++) { - array.push(i); + it_id("588a70c6-2932-4d3b-a838-a74c59d8cffb")(it)( + "should support pagination", + async () => { + const numberArray = (first, last) => { + const array = []; + for (let i = first; i <= last; i++) { + array.push(i); + } + return array; + }; + + const promises = []; + for (let i = 0; i < 100; i++) { + const obj = new Parse.Object("SomeClass"); + obj.set("numberField", i); + promises.push(obj.save()); } - return array; - }; + await Promise.all(promises); + + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + + const find = async ({ + skip, + after, + first, + before, + last, + } = {}) => { + return await apolloClient.query({ + query: gql` + query FindSomeObjects( + $order: [SomeClassOrder!] + $skip: Int + $after: String + $first: Int + $before: String + $last: Int + ) { + someClasses( + order: $order + skip: $skip + after: $after + first: $first + before: $before + last: $last + ) { + edges { + cursor + node { + numberField + } + } + count + pageInfo { + hasPreviousPage + startCursor + endCursor + hasNextPage + } + } + } + `, + variables: { + order: ["numberField_ASC"], + skip, + after, + first, + before, + last, + }, + }); + }; + + let result = await find(); + expect( + result.data.someClasses.edges.map(edge => edge.node.numberField) + ).toEqual(numberArray(0, 99)); + expect(result.data.someClasses.count).toEqual(100); + expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual( + false + ); + expect(result.data.someClasses.pageInfo.startCursor).toEqual( + result.data.someClasses.edges[0].cursor + ); + expect(result.data.someClasses.pageInfo.endCursor).toEqual( + result.data.someClasses.edges[99].cursor + ); + expect(result.data.someClasses.pageInfo.hasNextPage).toEqual( + false + ); + + result = await find({ first: 10 }); + expect( + result.data.someClasses.edges.map(edge => edge.node.numberField) + ).toEqual(numberArray(0, 9)); + expect(result.data.someClasses.count).toEqual(100); + expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual( + false + ); + expect(result.data.someClasses.pageInfo.startCursor).toEqual( + result.data.someClasses.edges[0].cursor + ); + expect(result.data.someClasses.pageInfo.endCursor).toEqual( + result.data.someClasses.edges[9].cursor + ); + expect(result.data.someClasses.pageInfo.hasNextPage).toEqual( + true + ); + + result = await find({ + first: 10, + after: result.data.someClasses.pageInfo.endCursor, + }); + expect( + result.data.someClasses.edges.map(edge => edge.node.numberField) + ).toEqual(numberArray(10, 19)); + expect(result.data.someClasses.count).toEqual(100); + expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual( + true + ); + expect(result.data.someClasses.pageInfo.startCursor).toEqual( + result.data.someClasses.edges[0].cursor + ); + expect(result.data.someClasses.pageInfo.endCursor).toEqual( + result.data.someClasses.edges[9].cursor + ); + expect(result.data.someClasses.pageInfo.hasNextPage).toEqual( + true + ); - const promises = []; - for (let i = 0; i < 100; i++) { - const obj = new Parse.Object('SomeClass'); - obj.set('numberField', i); - promises.push(obj.save()); + result = await find({ last: 10 }); + expect( + result.data.someClasses.edges.map(edge => edge.node.numberField) + ).toEqual(numberArray(90, 99)); + expect(result.data.someClasses.count).toEqual(100); + expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual( + true + ); + expect(result.data.someClasses.pageInfo.startCursor).toEqual( + result.data.someClasses.edges[0].cursor + ); + expect(result.data.someClasses.pageInfo.endCursor).toEqual( + result.data.someClasses.edges[9].cursor + ); + expect(result.data.someClasses.pageInfo.hasNextPage).toEqual( + false + ); + + result = await find({ + last: 10, + before: result.data.someClasses.pageInfo.startCursor, + }); + expect( + result.data.someClasses.edges.map(edge => edge.node.numberField) + ).toEqual(numberArray(80, 89)); + expect(result.data.someClasses.count).toEqual(100); + expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual( + true + ); + expect(result.data.someClasses.pageInfo.startCursor).toEqual( + result.data.someClasses.edges[0].cursor + ); + expect(result.data.someClasses.pageInfo.endCursor).toEqual( + result.data.someClasses.edges[9].cursor + ); + expect(result.data.someClasses.pageInfo.hasNextPage).toEqual( + true + ); } - await Promise.all(promises); + ); + + it_id("4f6a5f20-9642-4cf0-b31d-e739672a9096")(it)( + "should support count", + async () => { + await prepareData(); + + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + const where = { + someField: { + in: ["someValue1", "someValue2", "someValue3"], + }, + OR: [ + { + pointerToUser: { + have: { + objectId: { + equalTo: user5.id, + }, + }, + }, + }, + { + id: { + equalTo: object1.id, + }, + }, + ], + }; - const find = async ({ skip, after, first, before, last } = {}) => { - return await apolloClient.query({ + const result = await apolloClient.query({ query: gql` query FindSomeObjects( - $order: [SomeClassOrder!] - $skip: Int - $after: String + $where: GraphQLClassWhereInput $first: Int - $before: String - $last: Int ) { - someClasses( - order: $order - skip: $skip - after: $after - first: $first - before: $before - last: $last - ) { + find: graphQLClasses(where: $where, first: $first) { edges { - cursor node { - numberField + id } } count - pageInfo { - hasPreviousPage - startCursor - endCursor - hasNextPage - } } } `, variables: { - order: ['numberField_ASC'], - skip, - after, - first, - before, - last, - }, - }); - }; - - let result = await find(); - expect(result.data.someClasses.edges.map(edge => edge.node.numberField)).toEqual( - numberArray(0, 99) - ); - expect(result.data.someClasses.count).toEqual(100); - expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual(false); - expect(result.data.someClasses.pageInfo.startCursor).toEqual( - result.data.someClasses.edges[0].cursor - ); - expect(result.data.someClasses.pageInfo.endCursor).toEqual( - result.data.someClasses.edges[99].cursor - ); - expect(result.data.someClasses.pageInfo.hasNextPage).toEqual(false); - - result = await find({ first: 10 }); - expect(result.data.someClasses.edges.map(edge => edge.node.numberField)).toEqual( - numberArray(0, 9) - ); - expect(result.data.someClasses.count).toEqual(100); - expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual(false); - expect(result.data.someClasses.pageInfo.startCursor).toEqual( - result.data.someClasses.edges[0].cursor - ); - expect(result.data.someClasses.pageInfo.endCursor).toEqual( - result.data.someClasses.edges[9].cursor - ); - expect(result.data.someClasses.pageInfo.hasNextPage).toEqual(true); - - result = await find({ - first: 10, - after: result.data.someClasses.pageInfo.endCursor, - }); - expect(result.data.someClasses.edges.map(edge => edge.node.numberField)).toEqual( - numberArray(10, 19) - ); - expect(result.data.someClasses.count).toEqual(100); - expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual(true); - expect(result.data.someClasses.pageInfo.startCursor).toEqual( - result.data.someClasses.edges[0].cursor - ); - expect(result.data.someClasses.pageInfo.endCursor).toEqual( - result.data.someClasses.edges[9].cursor - ); - expect(result.data.someClasses.pageInfo.hasNextPage).toEqual(true); - - result = await find({ last: 10 }); - expect(result.data.someClasses.edges.map(edge => edge.node.numberField)).toEqual( - numberArray(90, 99) - ); - expect(result.data.someClasses.count).toEqual(100); - expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual(true); - expect(result.data.someClasses.pageInfo.startCursor).toEqual( - result.data.someClasses.edges[0].cursor - ); - expect(result.data.someClasses.pageInfo.endCursor).toEqual( - result.data.someClasses.edges[9].cursor - ); - expect(result.data.someClasses.pageInfo.hasNextPage).toEqual(false); - - result = await find({ - last: 10, - before: result.data.someClasses.pageInfo.startCursor, - }); - expect(result.data.someClasses.edges.map(edge => edge.node.numberField)).toEqual( - numberArray(80, 89) - ); - expect(result.data.someClasses.count).toEqual(100); - expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual(true); - expect(result.data.someClasses.pageInfo.startCursor).toEqual( - result.data.someClasses.edges[0].cursor - ); - expect(result.data.someClasses.pageInfo.endCursor).toEqual( - result.data.someClasses.edges[9].cursor - ); - expect(result.data.someClasses.pageInfo.hasNextPage).toEqual(true); - }); - - it_id('4f6a5f20-9642-4cf0-b31d-e739672a9096')(it)('should support count', async () => { - await prepareData(); - - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - - const where = { - someField: { - in: ['someValue1', 'someValue2', 'someValue3'], - }, - OR: [ - { - pointerToUser: { - have: { - objectId: { - equalTo: user5.id, - }, - }, - }, + where, + first: 0, }, - { - id: { - equalTo: object1.id, + context: { + headers: { + "X-Parse-Master-Key": "test", }, }, - ], - }; - - const result = await apolloClient.query({ - query: gql` - query FindSomeObjects($where: GraphQLClassWhereInput, $first: Int) { - find: graphQLClasses(where: $where, first: $first) { - edges { - node { - id - } - } - count - } - } - `, - variables: { - where, - first: 0, - }, - context: { - headers: { - 'X-Parse-Master-Key': 'test', - }, - }, - }); + }); - expect(result.data.find.edges).toEqual([]); - expect(result.data.find.count).toEqual(2); - }); + expect(result.data.find.edges).toEqual([]); + expect(result.data.find.count).toEqual(2); + } + ); - it('should only count', async () => { + it("should only count", async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); const where = { someField: { - in: ['someValue1', 'someValue2', 'someValue3'], + in: ["someValue1", "someValue2", "someValue3"], }, OR: [ { @@ -5327,7 +5733,7 @@ describe('ParseGraphQLServer', () => { }, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); @@ -5336,110 +5742,123 @@ describe('ParseGraphQLServer', () => { expect(result.data.find.count).toEqual(2); }); - it_id('942b57be-ca8a-4a5b-8104-2adef8743b1a')(it)('should respect max limit', async () => { - parseServer = await global.reconfigureServer({ - maxLimit: 10, - }); - await createGQLFromParseServer(parseServer); - const promises = []; - for (let i = 0; i < 100; i++) { - const obj = new Parse.Object('SomeClass'); - promises.push(obj.save()); - } - await Promise.all(promises); + it_id("942b57be-ca8a-4a5b-8104-2adef8743b1a")(it)( + "should respect max limit", + async () => { + parseServer = await global.reconfigureServer({ + maxLimit: 10, + }); + await createGQLFromParseServer(parseServer); + const promises = []; + for (let i = 0; i < 100; i++) { + const obj = new Parse.Object("SomeClass"); + promises.push(obj.save()); + } + await Promise.all(promises); - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const result = await apolloClient.query({ - query: gql` - query FindSomeObjects($limit: Int) { - find: someClasses(where: { id: { exists: true } }, first: $limit) { - edges { - node { - id + const result = await apolloClient.query({ + query: gql` + query FindSomeObjects($limit: Int) { + find: someClasses( + where: { id: { exists: true } } + first: $limit + ) { + edges { + node { + id + } } + count } - count } - } - `, - variables: { - limit: 50, - }, - context: { - headers: { - 'X-Parse-Master-Key': 'test', + `, + variables: { + limit: 50, }, - }, - }); + context: { + headers: { + "X-Parse-Master-Key": "test", + }, + }, + }); - expect(result.data.find.edges.length).toEqual(10); - expect(result.data.find.count).toEqual(100); - }); + expect(result.data.find.edges.length).toEqual(10); + expect(result.data.find.count).toEqual(100); + } + ); - it_id('952634f0-0ad5-4a08-8da2-187c1bd9ee94')(it)('should support keys argument', async () => { - await prepareData(); + it_id("952634f0-0ad5-4a08-8da2-187c1bd9ee94")(it)( + "should support keys argument", + async () => { + await prepareData(); - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const result1 = await apolloClient.query({ - query: gql` - query FindSomeObject($where: GraphQLClassWhereInput) { - find: graphQLClasses(where: $where) { - edges { - node { - someField + const result1 = await apolloClient.query({ + query: gql` + query FindSomeObject($where: GraphQLClassWhereInput) { + find: graphQLClasses(where: $where) { + edges { + node { + someField + } } } } - } - `, - variables: { - where: { - id: { equalTo: object3.id }, + `, + variables: { + where: { + id: { equalTo: object3.id }, + }, }, - }, - context: { - headers: { - 'X-Parse-Session-Token': user1.getSessionToken(), + context: { + headers: { + "X-Parse-Session-Token": user1.getSessionToken(), + }, }, - }, - }); + }); - const result2 = await apolloClient.query({ - query: gql` - query FindSomeObject($where: GraphQLClassWhereInput) { - find: graphQLClasses(where: $where) { - edges { - node { - someField - pointerToUser { - username + const result2 = await apolloClient.query({ + query: gql` + query FindSomeObject($where: GraphQLClassWhereInput) { + find: graphQLClasses(where: $where) { + edges { + node { + someField + pointerToUser { + username + } } } } } - } - `, - variables: { - where: { - id: { equalTo: object3.id }, + `, + variables: { + where: { + id: { equalTo: object3.id }, + }, }, - }, - context: { - headers: { - 'X-Parse-Session-Token': user1.getSessionToken(), + context: { + headers: { + "X-Parse-Session-Token": user1.getSessionToken(), + }, }, - }, - }); + }); - expect(result1.data.find.edges[0].node.someField).toBeDefined(); - expect(result1.data.find.edges[0].node.pointerToUser).toBeUndefined(); - expect(result2.data.find.edges[0].node.someField).toBeDefined(); - expect(result2.data.find.edges[0].node.pointerToUser).toBeDefined(); - }); + expect(result1.data.find.edges[0].node.someField).toBeDefined(); + expect( + result1.data.find.edges[0].node.pointerToUser + ).toBeUndefined(); + expect(result2.data.find.edges[0].node.someField).toBeDefined(); + expect( + result2.data.find.edges[0].node.pointerToUser + ).toBeDefined(); + } + ); - it('should support include argument', async () => { + it("should support include argument", async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -5469,7 +5888,7 @@ describe('ParseGraphQLServer', () => { }, context: { headers: { - 'X-Parse-Session-Token': user1.getSessionToken(), + "X-Parse-Session-Token": user1.getSessionToken(), }, }, }); @@ -5493,21 +5912,25 @@ describe('ParseGraphQLServer', () => { }, context: { headers: { - 'X-Parse-Session-Token': user1.getSessionToken(), + "X-Parse-Session-Token": user1.getSessionToken(), }, }, }); - expect(result1.data.find.edges[0].node.pointerToUser.username).toBeUndefined(); - expect(result2.data.find.edges[0].node.pointerToUser.username).toBeDefined(); + expect( + result1.data.find.edges[0].node.pointerToUser.username + ).toBeUndefined(); + expect( + result2.data.find.edges[0].node.pointerToUser.username + ).toBeDefined(); }); - describe_only_db('mongo')('read preferences', () => { - it('should read from primary by default', async () => { + describe_only_db("mongo")("read preferences", () => { + it("should read from primary by default", async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); await apolloClient.query({ query: gql` @@ -5525,7 +5948,7 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Session-Token': user1.getSessionToken(), + "X-Parse-Session-Token": user1.getSessionToken(), }, }, }); @@ -5533,12 +5956,21 @@ describe('ParseGraphQLServer', () => { let foundGraphQLClassReadPreference = false; let foundUserClassReadPreference = false; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) { + if ( + call.object.s.namespace.collection.indexOf("GraphQLClass") >= + 0 + ) { foundGraphQLClassReadPreference = true; - expect(call.object.s.readPreference.mode).toBe(ReadPreference.PRIMARY); - } else if (call.object.s.namespace.collection.indexOf('_User') >= 0) { + expect(call.object.s.readPreference.mode).toBe( + ReadPreference.PRIMARY + ); + } else if ( + call.object.s.namespace.collection.indexOf("_User") >= 0 + ) { foundUserClassReadPreference = true; - expect(call.object.s.readPreference.mode).toBe(ReadPreference.PRIMARY); + expect(call.object.s.readPreference.mode).toBe( + ReadPreference.PRIMARY + ); } }); @@ -5546,17 +5978,19 @@ describe('ParseGraphQLServer', () => { expect(foundUserClassReadPreference).toBe(true); }); - it('should support readPreference argument', async () => { + it("should support readPreference argument", async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); await apolloClient.query({ query: gql` query FindSomeObjects { - find: graphQLClasses(options: { readPreference: SECONDARY }) { + find: graphQLClasses( + options: { readPreference: SECONDARY } + ) { edges { node { pointerToUser { @@ -5569,7 +6003,7 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); @@ -5577,12 +6011,21 @@ describe('ParseGraphQLServer', () => { let foundGraphQLClassReadPreference = false; let foundUserClassReadPreference = false; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) { + if ( + call.object.s.namespace.collection.indexOf("GraphQLClass") >= + 0 + ) { foundGraphQLClassReadPreference = true; - expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY); - } else if (call.object.s.namespace.collection.indexOf('_User') >= 0) { + expect(call.args[1].readPreference).toBe( + ReadPreference.SECONDARY + ); + } else if ( + call.object.s.namespace.collection.indexOf("_User") >= 0 + ) { foundUserClassReadPreference = true; - expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY); + expect(call.args[1].readPreference).toBe( + ReadPreference.SECONDARY + ); } }); @@ -5590,18 +6033,21 @@ describe('ParseGraphQLServer', () => { expect(foundUserClassReadPreference).toBe(true); }); - it('should support includeReadPreference argument', async () => { + it("should support includeReadPreference argument", async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); await apolloClient.query({ query: gql` query FindSomeObjects { graphQLClasses( - options: { readPreference: SECONDARY, includeReadPreference: NEAREST } + options: { + readPreference: SECONDARY + includeReadPreference: NEAREST + } ) { edges { node { @@ -5615,7 +6061,7 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); @@ -5623,12 +6069,21 @@ describe('ParseGraphQLServer', () => { let foundGraphQLClassReadPreference = false; let foundUserClassReadPreference = false; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) { + if ( + call.object.s.namespace.collection.indexOf("GraphQLClass") >= + 0 + ) { foundGraphQLClassReadPreference = true; - expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY); - } else if (call.object.s.namespace.collection.indexOf('_User') >= 0) { + expect(call.args[1].readPreference).toBe( + ReadPreference.SECONDARY + ); + } else if ( + call.object.s.namespace.collection.indexOf("_User") >= 0 + ) { foundUserClassReadPreference = true; - expect(call.args[1].readPreference).toBe(ReadPreference.NEAREST); + expect(call.args[1].readPreference).toBe( + ReadPreference.NEAREST + ); } }); @@ -5636,20 +6091,23 @@ describe('ParseGraphQLServer', () => { expect(foundUserClassReadPreference).toBe(true); }); - it('should support subqueryReadPreference argument', async () => { + it("should support subqueryReadPreference argument", async () => { try { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); await apolloClient.query({ query: gql` query FindSomeObjects($where: GraphQLClassWhereInput) { find: graphQLClasses( where: $where - options: { readPreference: SECONDARY, subqueryReadPreference: NEAREST } + options: { + readPreference: SECONDARY + subqueryReadPreference: NEAREST + } ) { edges { node { @@ -5664,7 +6122,7 @@ describe('ParseGraphQLServer', () => { pointerToUser: { have: { objectId: { - equalTo: 'xxxx', + equalTo: "xxxx", }, }, }, @@ -5672,7 +6130,7 @@ describe('ParseGraphQLServer', () => { }, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); @@ -5680,12 +6138,22 @@ describe('ParseGraphQLServer', () => { let foundGraphQLClassReadPreference = false; let foundUserClassReadPreference = false; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) { + if ( + call.object.s.namespace.collection.indexOf( + "GraphQLClass" + ) >= 0 + ) { foundGraphQLClassReadPreference = true; - expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY); - } else if (call.object.s.namespace.collection.indexOf('_User') >= 0) { + expect(call.args[1].readPreference).toBe( + ReadPreference.SECONDARY + ); + } else if ( + call.object.s.namespace.collection.indexOf("_User") >= 0 + ) { foundUserClassReadPreference = true; - expect(call.args[1].readPreference).toBe(ReadPreference.NEAREST); + expect(call.args[1].readPreference).toBe( + ReadPreference.NEAREST + ); } }); @@ -5697,80 +6165,31 @@ describe('ParseGraphQLServer', () => { }); }); - it('should order by multiple fields', async () => { - await prepareData(); - - await resetGraphQLCache(); - - let result; - try { - result = await apolloClient.query({ - query: gql` - query OrderByMultipleFields($order: [GraphQLClassOrder!]) { - graphQLClasses(order: $order) { - edges { - node { - objectId - } - } - } - } - `, - variables: { - order: ['someOtherField_DESC', 'someField_ASC'], - }, - context: { - headers: { - 'X-Parse-Master-Key': 'test', - }, - }, - }); - } catch (e) { - handleError(e); - } - - expect(result.data.graphQLClasses.edges.map(edge => edge.node.objectId)).toEqual([ - object3.id, - object1.id, - object2.id, - ]); - }); - - it_only_db('mongo')('should order by multiple fields on a relation field', async () => { + it("should order by multiple fields", async () => { await prepareData(); - const parentObject = new Parse.Object('ParentClass'); - const relation = parentObject.relation('graphQLClasses'); - relation.add(object1); - relation.add(object2); - relation.add(object3); - await parentObject.save(); - await resetGraphQLCache(); let result; try { result = await apolloClient.query({ query: gql` - query OrderByMultipleFieldsOnRelation($id: ID!, $order: [GraphQLClassOrder!]) { - parentClass(id: $id) { - graphQLClasses(order: $order) { - edges { - node { - objectId - } + query OrderByMultipleFields($order: [GraphQLClassOrder!]) { + graphQLClasses(order: $order) { + edges { + node { + objectId } } } } `, variables: { - id: parentObject.id, - order: ['someOtherField_DESC', 'someField_ASC'], + order: ["someOtherField_DESC", "someField_ASC"], }, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); @@ -5779,79 +6198,139 @@ describe('ParseGraphQLServer', () => { } expect( - result.data.parentClass.graphQLClasses.edges.map(edge => edge.node.objectId) + result.data.graphQLClasses.edges.map(edge => edge.node.objectId) ).toEqual([object3.id, object1.id, object2.id]); }); - it_id('47a6adf3-1cb4-4d92-b74c-e480363f9cb5')(it)('should support including relation', async () => { - await prepareData(); + it_only_db("mongo")( + "should order by multiple fields on a relation field", + async () => { + await prepareData(); - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + const parentObject = new Parse.Object("ParentClass"); + const relation = parentObject.relation("graphQLClasses"); + relation.add(object1); + relation.add(object2); + relation.add(object3); + await parentObject.save(); - const result1 = await apolloClient.query({ - query: gql` - query FindRoles { - roles { - edges { - node { - name + await resetGraphQLCache(); + + let result; + try { + result = await apolloClient.query({ + query: gql` + query OrderByMultipleFieldsOnRelation( + $id: ID! + $order: [GraphQLClassOrder!] + ) { + parentClass(id: $id) { + graphQLClasses(order: $order) { + edges { + node { + objectId + } + } + } + } + } + `, + variables: { + id: parentObject.id, + order: ["someOtherField_DESC", "someField_ASC"], + }, + context: { + headers: { + "X-Parse-Master-Key": "test", + }, + }, + }); + } catch (e) { + handleError(e); + } + + expect( + result.data.parentClass.graphQLClasses.edges.map( + edge => edge.node.objectId + ) + ).toEqual([object3.id, object1.id, object2.id]); + } + ); + + it_id("47a6adf3-1cb4-4d92-b74c-e480363f9cb5")(it)( + "should support including relation", + async () => { + await prepareData(); + + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + + const result1 = await apolloClient.query({ + query: gql` + query FindRoles { + roles { + edges { + node { + name + } } } } - } - `, - variables: {}, - context: { - headers: { - 'X-Parse-Session-Token': user1.getSessionToken(), + `, + variables: {}, + context: { + headers: { + "X-Parse-Session-Token": user1.getSessionToken(), + }, }, - }, - }); + }); - const result2 = await apolloClient.query({ - query: gql` - query FindRoles { - roles { - edges { - node { - name - users { - edges { - node { - username + const result2 = await apolloClient.query({ + query: gql` + query FindRoles { + roles { + edges { + node { + name + users { + edges { + node { + username + } } } } } } } - } - `, - variables: {}, - context: { - headers: { - 'X-Parse-Session-Token': user1.getSessionToken(), + `, + variables: {}, + context: { + headers: { + "X-Parse-Session-Token": user1.getSessionToken(), + }, }, - }, - }); + }); - expect(result1.data.roles.edges[0].node.name).toBeDefined(); - expect(result1.data.roles.edges[0].node.users).toBeUndefined(); - expect(result1.data.roles.edges[0].node.roles).toBeUndefined(); - expect(result2.data.roles.edges[0].node.name).toBeDefined(); - expect(result2.data.roles.edges[0].node.users).toBeDefined(); - expect(result2.data.roles.edges[0].node.users.edges[0].node.username).toBeDefined(); - expect(result2.data.roles.edges[0].node.roles).toBeUndefined(); - }); + expect(result1.data.roles.edges[0].node.name).toBeDefined(); + expect(result1.data.roles.edges[0].node.users).toBeUndefined(); + expect(result1.data.roles.edges[0].node.roles).toBeUndefined(); + expect(result2.data.roles.edges[0].node.name).toBeDefined(); + expect(result2.data.roles.edges[0].node.users).toBeDefined(); + expect( + result2.data.roles.edges[0].node.users.edges[0].node.username + ).toBeDefined(); + expect(result2.data.roles.edges[0].node.roles).toBeUndefined(); + } + ); }); }); - describe('Objects Mutations', () => { - describe('Create', () => { - it('should return specific type object using class specific mutation', async () => { + describe("Objects Mutations", () => { + describe("Create", () => { + it("should return specific type object using class specific mutation", async () => { const clientMutationId = uuidv4(); - const customerSchema = new Parse.Schema('Customer'); - customerSchema.addString('someField'); + const customerSchema = new Parse.Schema("Customer"); + customerSchema.addString("someField"); await customerSchema.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -5874,33 +6353,38 @@ describe('ParseGraphQLServer', () => { input: { clientMutationId, fields: { - someField: 'someValue', + someField: "someValue", }, }, }, }); - expect(result.data.createCustomer.clientMutationId).toEqual(clientMutationId); + expect(result.data.createCustomer.clientMutationId).toEqual( + clientMutationId + ); expect(result.data.createCustomer.customer.id).toBeDefined(); - expect(result.data.createCustomer.customer.someField).toEqual('someValue'); + expect(result.data.createCustomer.customer.someField).toEqual( + "someValue" + ); - const customer = await new Parse.Query('Customer').get( + const customer = await new Parse.Query("Customer").get( result.data.createCustomer.customer.objectId ); expect(customer.createdAt).toEqual( new Date(result.data.createCustomer.customer.createdAt) ); - expect(customer.get('someField')).toEqual('someValue'); + expect(customer.get("someField")).toEqual("someValue"); }); - it('should respect level permissions', async () => { + it("should respect level permissions", async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); async function createObject(className, headers) { - const getClassName = className.charAt(0).toLowerCase() + className.slice(1); + const getClassName = + className.charAt(0).toLowerCase() + className.slice(1); const result = await apolloClient.mutate({ mutation: gql` mutation CreateSomeObject { @@ -5917,64 +6401,69 @@ describe('ParseGraphQLServer', () => { }, }); - const specificCreate = result.data[`create${className}`][getClassName]; + const specificCreate = + result.data[`create${className}`][getClassName]; expect(specificCreate.id).toBeDefined(); expect(specificCreate.createdAt).toBeDefined(); return result; } - await expectAsync(createObject('GraphQLClass')).toBeRejectedWith( - jasmine.stringMatching('Permission denied for action create on class GraphQLClass') + await expectAsync(createObject("GraphQLClass")).toBeRejectedWith( + jasmine.stringMatching( + "Permission denied for action create on class GraphQLClass" + ) ); - await expectAsync(createObject('PublicClass')).toBeResolved(); + await expectAsync(createObject("PublicClass")).toBeResolved(); await expectAsync( - createObject('GraphQLClass', { 'X-Parse-Master-Key': 'test' }) + createObject("GraphQLClass", { "X-Parse-Master-Key": "test" }) ).toBeResolved(); await expectAsync( - createObject('PublicClass', { 'X-Parse-Master-Key': 'test' }) + createObject("PublicClass", { "X-Parse-Master-Key": "test" }) ).toBeResolved(); await expectAsync( - createObject('GraphQLClass', { - 'X-Parse-Session-Token': user1.getSessionToken(), + createObject("GraphQLClass", { + "X-Parse-Session-Token": user1.getSessionToken(), }) ).toBeResolved(); await expectAsync( - createObject('PublicClass', { - 'X-Parse-Session-Token': user1.getSessionToken(), + createObject("PublicClass", { + "X-Parse-Session-Token": user1.getSessionToken(), }) ).toBeResolved(); await expectAsync( - createObject('GraphQLClass', { - 'X-Parse-Session-Token': user2.getSessionToken(), + createObject("GraphQLClass", { + "X-Parse-Session-Token": user2.getSessionToken(), }) ).toBeResolved(); await expectAsync( - createObject('PublicClass', { - 'X-Parse-Session-Token': user2.getSessionToken(), + createObject("PublicClass", { + "X-Parse-Session-Token": user2.getSessionToken(), }) ).toBeResolved(); await expectAsync( - createObject('GraphQLClass', { - 'X-Parse-Session-Token': user4.getSessionToken(), + createObject("GraphQLClass", { + "X-Parse-Session-Token": user4.getSessionToken(), }) ).toBeRejectedWith( - jasmine.stringMatching('Permission denied for action create on class GraphQLClass') + jasmine.stringMatching( + "Permission denied for action create on class GraphQLClass" + ) ); await expectAsync( - createObject('PublicClass', { - 'X-Parse-Session-Token': user4.getSessionToken(), + createObject("PublicClass", { + "X-Parse-Session-Token": user4.getSessionToken(), }) ).toBeResolved(); }); }); - describe('Update', () => { - it('should return specific type object using class specific mutation', async () => { + describe("Update", () => { + it("should return specific type object using class specific mutation", async () => { const clientMutationId = uuidv4(); - const obj = new Parse.Object('Customer'); - obj.set('someField1', 'someField1Value1'); - obj.set('someField2', 'someField2Value1'); + const obj = new Parse.Object("Customer"); + obj.set("someField1", "someField1Value1"); + obj.set("someField2", "someField2Value1"); await obj.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -5997,34 +6486,43 @@ describe('ParseGraphQLServer', () => { clientMutationId, id: obj.id, fields: { - someField1: 'someField1Value2', + someField1: "someField1Value2", }, }, }, }); - expect(result.data.updateCustomer.clientMutationId).toEqual(clientMutationId); + expect(result.data.updateCustomer.clientMutationId).toEqual( + clientMutationId + ); expect(result.data.updateCustomer.customer.updatedAt).toBeDefined(); - expect(result.data.updateCustomer.customer.someField1).toEqual('someField1Value2'); - expect(result.data.updateCustomer.customer.someField2).toEqual('someField2Value1'); + expect(result.data.updateCustomer.customer.someField1).toEqual( + "someField1Value2" + ); + expect(result.data.updateCustomer.customer.someField2).toEqual( + "someField2Value1" + ); await obj.fetch(); - expect(obj.get('someField1')).toEqual('someField1Value2'); - expect(obj.get('someField2')).toEqual('someField2Value1'); + expect(obj.get("someField1")).toEqual("someField1Value2"); + expect(obj.get("someField2")).toEqual("someField2Value1"); }); - it('should return only id using class specific mutation', async () => { - const obj = new Parse.Object('Customer'); - obj.set('someField1', 'someField1Value1'); - obj.set('someField2', 'someField2Value1'); + it("should return only id using class specific mutation", async () => { + const obj = new Parse.Object("Customer"); + obj.set("someField1", "someField1Value1"); + obj.set("someField2", "someField2Value1"); await obj.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); const result = await apolloClient.mutate({ mutation: gql` - mutation UpdateCustomer($id: ID!, $fields: UpdateCustomerFieldsInput) { + mutation UpdateCustomer( + $id: ID! + $fields: UpdateCustomerFieldsInput + ) { updateCustomer(input: { id: $id, fields: $fields }) { customer { id @@ -6036,20 +6534,22 @@ describe('ParseGraphQLServer', () => { variables: { id: obj.id, fields: { - someField1: 'someField1Value2', + someField1: "someField1Value2", }, }, }); - expect(result.data.updateCustomer.customer.objectId).toEqual(obj.id); + expect(result.data.updateCustomer.customer.objectId).toEqual( + obj.id + ); await obj.fetch(); - expect(obj.get('someField1')).toEqual('someField1Value2'); - expect(obj.get('someField2')).toEqual('someField2Value1'); + expect(obj.get("someField1")).toEqual("someField1Value2"); + expect(obj.get("someField2")).toEqual("someField2Value1"); }); - it('should respect level permissions', async () => { + it("should respect level permissions", async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -6082,25 +6582,25 @@ describe('ParseGraphQLServer', () => { await Promise.all( objects.slice(0, 3).map(async obj => { - const originalFieldValue = obj.get('someField'); + const originalFieldValue = obj.get("someField"); await expectAsync( updateObject(obj.className, obj.id, { - someField: 'changedValue1', + someField: "changedValue1", }) - ).toBeRejectedWith(jasmine.stringMatching('Object not found')); + ).toBeRejectedWith(jasmine.stringMatching("Object not found")); await obj.fetch({ useMasterKey: true }); - expect(obj.get('someField')).toEqual(originalFieldValue); + expect(obj.get("someField")).toEqual(originalFieldValue); }) ); expect( ( await updateObject(object4.className, object4.id, { - someField: 'changedValue1', + someField: "changedValue1", }) ).data.update.clientMutationId ).toBeDefined(); await object4.fetch({ useMasterKey: true }); - expect(object4.get('someField')).toEqual('changedValue1'); + expect(object4.get("someField")).toEqual("changedValue1"); await Promise.all( objects.map(async obj => { expect( @@ -6108,13 +6608,13 @@ describe('ParseGraphQLServer', () => { await updateObject( obj.className, obj.id, - { someField: 'changedValue2' }, - { 'X-Parse-Master-Key': 'test' } + { someField: "changedValue2" }, + { "X-Parse-Master-Key": "test" } ) ).data.update.clientMutationId ).toBeDefined(); await obj.fetch({ useMasterKey: true }); - expect(obj.get('someField')).toEqual('changedValue2'); + expect(obj.get("someField")).toEqual("changedValue2"); }) ); await Promise.all( @@ -6124,13 +6624,13 @@ describe('ParseGraphQLServer', () => { await updateObject( obj.className, obj.id, - { someField: 'changedValue3' }, - { 'X-Parse-Session-Token': user1.getSessionToken() } + { someField: "changedValue3" }, + { "X-Parse-Session-Token": user1.getSessionToken() } ) ).data.update.clientMutationId ).toBeDefined(); await obj.fetch({ useMasterKey: true }); - expect(obj.get('someField')).toEqual('changedValue3'); + expect(obj.get("someField")).toEqual("changedValue3"); }) ); await Promise.all( @@ -6140,13 +6640,13 @@ describe('ParseGraphQLServer', () => { await updateObject( obj.className, obj.id, - { someField: 'changedValue4' }, - { 'X-Parse-Session-Token': user2.getSessionToken() } + { someField: "changedValue4" }, + { "X-Parse-Session-Token": user2.getSessionToken() } ) ).data.update.clientMutationId ).toBeDefined(); await obj.fetch({ useMasterKey: true }); - expect(obj.get('someField')).toEqual('changedValue4'); + expect(obj.get("someField")).toEqual("changedValue4"); }) ); await Promise.all( @@ -6156,39 +6656,39 @@ describe('ParseGraphQLServer', () => { await updateObject( obj.className, obj.id, - { someField: 'changedValue5' }, - { 'X-Parse-Session-Token': user3.getSessionToken() } + { someField: "changedValue5" }, + { "X-Parse-Session-Token": user3.getSessionToken() } ) ).data.update.clientMutationId ).toBeDefined(); await obj.fetch({ useMasterKey: true }); - expect(obj.get('someField')).toEqual('changedValue5'); + expect(obj.get("someField")).toEqual("changedValue5"); }) ); - const originalFieldValue = object2.get('someField'); + const originalFieldValue = object2.get("someField"); await expectAsync( updateObject( object2.className, object2.id, - { someField: 'changedValue5' }, - { 'X-Parse-Session-Token': user3.getSessionToken() } + { someField: "changedValue5" }, + { "X-Parse-Session-Token": user3.getSessionToken() } ) - ).toBeRejectedWith(jasmine.stringMatching('Object not found')); + ).toBeRejectedWith(jasmine.stringMatching("Object not found")); await object2.fetch({ useMasterKey: true }); - expect(object2.get('someField')).toEqual(originalFieldValue); + expect(object2.get("someField")).toEqual(originalFieldValue); await Promise.all( objects.slice(0, 3).map(async obj => { - const originalFieldValue = obj.get('someField'); + const originalFieldValue = obj.get("someField"); await expectAsync( updateObject( obj.className, obj.id, - { someField: 'changedValue6' }, - { 'X-Parse-Session-Token': user4.getSessionToken() } + { someField: "changedValue6" }, + { "X-Parse-Session-Token": user4.getSessionToken() } ) - ).toBeRejectedWith(jasmine.stringMatching('Object not found')); + ).toBeRejectedWith(jasmine.stringMatching("Object not found")); await obj.fetch({ useMasterKey: true }); - expect(obj.get('someField')).toEqual(originalFieldValue); + expect(obj.get("someField")).toEqual(originalFieldValue); }) ); expect( @@ -6196,26 +6696,26 @@ describe('ParseGraphQLServer', () => { await updateObject( object4.className, object4.id, - { someField: 'changedValue6' }, - { 'X-Parse-Session-Token': user4.getSessionToken() } + { someField: "changedValue6" }, + { "X-Parse-Session-Token": user4.getSessionToken() } ) ).data.update.clientMutationId ).toBeDefined(); await object4.fetch({ useMasterKey: true }); - expect(object4.get('someField')).toEqual('changedValue6'); + expect(object4.get("someField")).toEqual("changedValue6"); await Promise.all( objects.slice(0, 2).map(async obj => { - const originalFieldValue = obj.get('someField'); + const originalFieldValue = obj.get("someField"); await expectAsync( updateObject( obj.className, obj.id, - { someField: 'changedValue7' }, - { 'X-Parse-Session-Token': user5.getSessionToken() } + { someField: "changedValue7" }, + { "X-Parse-Session-Token": user5.getSessionToken() } ) - ).toBeRejectedWith(jasmine.stringMatching('Object not found')); + ).toBeRejectedWith(jasmine.stringMatching("Object not found")); await obj.fetch({ useMasterKey: true }); - expect(obj.get('someField')).toEqual(originalFieldValue); + expect(obj.get("someField")).toEqual(originalFieldValue); }) ); expect( @@ -6223,34 +6723,35 @@ describe('ParseGraphQLServer', () => { await updateObject( object3.className, object3.id, - { someField: 'changedValue7' }, - { 'X-Parse-Session-Token': user5.getSessionToken() } + { someField: "changedValue7" }, + { "X-Parse-Session-Token": user5.getSessionToken() } ) ).data.update.clientMutationId ).toBeDefined(); await object3.fetch({ useMasterKey: true }); - expect(object3.get('someField')).toEqual('changedValue7'); + expect(object3.get("someField")).toEqual("changedValue7"); expect( ( await updateObject( object4.className, object4.id, - { someField: 'changedValue7' }, - { 'X-Parse-Session-Token': user5.getSessionToken() } + { someField: "changedValue7" }, + { "X-Parse-Session-Token": user5.getSessionToken() } ) ).data.update.clientMutationId ).toBeDefined(); await object4.fetch({ useMasterKey: true }); - expect(object4.get('someField')).toEqual('changedValue7'); + expect(object4.get("someField")).toEqual("changedValue7"); }); - it('should respect level permissions with specific class mutation', async () => { + it("should respect level permissions with specific class mutation", async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); function updateObject(className, id, fields, headers) { - const mutationName = className.charAt(0).toLowerCase() + className.slice(1); + const mutationName = + className.charAt(0).toLowerCase() + className.slice(1); return apolloClient.mutate({ mutation: gql` @@ -6280,27 +6781,28 @@ describe('ParseGraphQLServer', () => { await Promise.all( objects.slice(0, 3).map(async obj => { - const originalFieldValue = obj.get('someField'); + const originalFieldValue = obj.get("someField"); await expectAsync( updateObject(obj.className, obj.id, { - someField: 'changedValue1', + someField: "changedValue1", }) - ).toBeRejectedWith(jasmine.stringMatching('Object not found')); + ).toBeRejectedWith(jasmine.stringMatching("Object not found")); await obj.fetch({ useMasterKey: true }); - expect(obj.get('someField')).toEqual(originalFieldValue); + expect(obj.get("someField")).toEqual(originalFieldValue); }) ); expect( ( await updateObject(object4.className, object4.id, { - someField: 'changedValue1', + someField: "changedValue1", }) ).data[`update${object4.className}`][ - object4.className.charAt(0).toLowerCase() + object4.className.slice(1) + object4.className.charAt(0).toLowerCase() + + object4.className.slice(1) ].updatedAt ).toBeDefined(); await object4.fetch({ useMasterKey: true }); - expect(object4.get('someField')).toEqual('changedValue1'); + expect(object4.get("someField")).toEqual("changedValue1"); await Promise.all( objects.map(async obj => { expect( @@ -6308,15 +6810,16 @@ describe('ParseGraphQLServer', () => { await updateObject( obj.className, obj.id, - { someField: 'changedValue2' }, - { 'X-Parse-Master-Key': 'test' } + { someField: "changedValue2" }, + { "X-Parse-Master-Key": "test" } ) ).data[`update${obj.className}`][ - obj.className.charAt(0).toLowerCase() + obj.className.slice(1) + obj.className.charAt(0).toLowerCase() + + obj.className.slice(1) ].updatedAt ).toBeDefined(); await obj.fetch({ useMasterKey: true }); - expect(obj.get('someField')).toEqual('changedValue2'); + expect(obj.get("someField")).toEqual("changedValue2"); }) ); await Promise.all( @@ -6326,15 +6829,16 @@ describe('ParseGraphQLServer', () => { await updateObject( obj.className, obj.id, - { someField: 'changedValue3' }, - { 'X-Parse-Session-Token': user1.getSessionToken() } + { someField: "changedValue3" }, + { "X-Parse-Session-Token": user1.getSessionToken() } ) ).data[`update${obj.className}`][ - obj.className.charAt(0).toLowerCase() + obj.className.slice(1) + obj.className.charAt(0).toLowerCase() + + obj.className.slice(1) ].updatedAt ).toBeDefined(); await obj.fetch({ useMasterKey: true }); - expect(obj.get('someField')).toEqual('changedValue3'); + expect(obj.get("someField")).toEqual("changedValue3"); }) ); await Promise.all( @@ -6344,15 +6848,16 @@ describe('ParseGraphQLServer', () => { await updateObject( obj.className, obj.id, - { someField: 'changedValue4' }, - { 'X-Parse-Session-Token': user2.getSessionToken() } + { someField: "changedValue4" }, + { "X-Parse-Session-Token": user2.getSessionToken() } ) ).data[`update${obj.className}`][ - obj.className.charAt(0).toLowerCase() + obj.className.slice(1) + obj.className.charAt(0).toLowerCase() + + obj.className.slice(1) ].updatedAt ).toBeDefined(); await obj.fetch({ useMasterKey: true }); - expect(obj.get('someField')).toEqual('changedValue4'); + expect(obj.get("someField")).toEqual("changedValue4"); }) ); await Promise.all( @@ -6362,41 +6867,42 @@ describe('ParseGraphQLServer', () => { await updateObject( obj.className, obj.id, - { someField: 'changedValue5' }, - { 'X-Parse-Session-Token': user3.getSessionToken() } + { someField: "changedValue5" }, + { "X-Parse-Session-Token": user3.getSessionToken() } ) ).data[`update${obj.className}`][ - obj.className.charAt(0).toLowerCase() + obj.className.slice(1) + obj.className.charAt(0).toLowerCase() + + obj.className.slice(1) ].updatedAt ).toBeDefined(); await obj.fetch({ useMasterKey: true }); - expect(obj.get('someField')).toEqual('changedValue5'); + expect(obj.get("someField")).toEqual("changedValue5"); }) ); - const originalFieldValue = object2.get('someField'); + const originalFieldValue = object2.get("someField"); await expectAsync( updateObject( object2.className, object2.id, - { someField: 'changedValue5' }, - { 'X-Parse-Session-Token': user3.getSessionToken() } + { someField: "changedValue5" }, + { "X-Parse-Session-Token": user3.getSessionToken() } ) - ).toBeRejectedWith(jasmine.stringMatching('Object not found')); + ).toBeRejectedWith(jasmine.stringMatching("Object not found")); await object2.fetch({ useMasterKey: true }); - expect(object2.get('someField')).toEqual(originalFieldValue); + expect(object2.get("someField")).toEqual(originalFieldValue); await Promise.all( objects.slice(0, 3).map(async obj => { - const originalFieldValue = obj.get('someField'); + const originalFieldValue = obj.get("someField"); await expectAsync( updateObject( obj.className, obj.id, - { someField: 'changedValue6' }, - { 'X-Parse-Session-Token': user4.getSessionToken() } + { someField: "changedValue6" }, + { "X-Parse-Session-Token": user4.getSessionToken() } ) - ).toBeRejectedWith(jasmine.stringMatching('Object not found')); + ).toBeRejectedWith(jasmine.stringMatching("Object not found")); await obj.fetch({ useMasterKey: true }); - expect(obj.get('someField')).toEqual(originalFieldValue); + expect(obj.get("someField")).toEqual(originalFieldValue); }) ); expect( @@ -6404,28 +6910,29 @@ describe('ParseGraphQLServer', () => { await updateObject( object4.className, object4.id, - { someField: 'changedValue6' }, - { 'X-Parse-Session-Token': user4.getSessionToken() } + { someField: "changedValue6" }, + { "X-Parse-Session-Token": user4.getSessionToken() } ) ).data[`update${object4.className}`][ - object4.className.charAt(0).toLowerCase() + object4.className.slice(1) + object4.className.charAt(0).toLowerCase() + + object4.className.slice(1) ].updatedAt ).toBeDefined(); await object4.fetch({ useMasterKey: true }); - expect(object4.get('someField')).toEqual('changedValue6'); + expect(object4.get("someField")).toEqual("changedValue6"); await Promise.all( objects.slice(0, 2).map(async obj => { - const originalFieldValue = obj.get('someField'); + const originalFieldValue = obj.get("someField"); await expectAsync( updateObject( obj.className, obj.id, - { someField: 'changedValue7' }, - { 'X-Parse-Session-Token': user5.getSessionToken() } + { someField: "changedValue7" }, + { "X-Parse-Session-Token": user5.getSessionToken() } ) - ).toBeRejectedWith(jasmine.stringMatching('Object not found')); + ).toBeRejectedWith(jasmine.stringMatching("Object not found")); await obj.fetch({ useMasterKey: true }); - expect(obj.get('someField')).toEqual(originalFieldValue); + expect(obj.get("someField")).toEqual(originalFieldValue); }) ); expect( @@ -6433,38 +6940,40 @@ describe('ParseGraphQLServer', () => { await updateObject( object3.className, object3.id, - { someField: 'changedValue7' }, - { 'X-Parse-Session-Token': user5.getSessionToken() } + { someField: "changedValue7" }, + { "X-Parse-Session-Token": user5.getSessionToken() } ) ).data[`update${object3.className}`][ - object3.className.charAt(0).toLowerCase() + object3.className.slice(1) + object3.className.charAt(0).toLowerCase() + + object3.className.slice(1) ].updatedAt ).toBeDefined(); await object3.fetch({ useMasterKey: true }); - expect(object3.get('someField')).toEqual('changedValue7'); + expect(object3.get("someField")).toEqual("changedValue7"); expect( ( await updateObject( object4.className, object4.id, - { someField: 'changedValue7' }, - { 'X-Parse-Session-Token': user5.getSessionToken() } + { someField: "changedValue7" }, + { "X-Parse-Session-Token": user5.getSessionToken() } ) ).data[`update${object4.className}`][ - object4.className.charAt(0).toLowerCase() + object4.className.slice(1) + object4.className.charAt(0).toLowerCase() + + object4.className.slice(1) ].updatedAt ).toBeDefined(); await object4.fetch({ useMasterKey: true }); - expect(object4.get('someField')).toEqual('changedValue7'); + expect(object4.get("someField")).toEqual("changedValue7"); }); }); - describe('Delete', () => { - it('should return a specific type using class specific mutation', async () => { + describe("Delete", () => { + it("should return a specific type using class specific mutation", async () => { const clientMutationId = uuidv4(); - const obj = new Parse.Object('Customer'); - obj.set('someField1', 'someField1Value1'); - obj.set('someField2', 'someField2Value1'); + const obj = new Parse.Object("Customer"); + obj.set("someField1", "someField1Value1"); + obj.set("someField2", "someField2Value1"); await obj.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -6491,23 +7000,32 @@ describe('ParseGraphQLServer', () => { }, }); - expect(result.data.deleteCustomer.clientMutationId).toEqual(clientMutationId); - expect(result.data.deleteCustomer.customer.objectId).toEqual(obj.id); - expect(result.data.deleteCustomer.customer.someField1).toEqual('someField1Value1'); - expect(result.data.deleteCustomer.customer.someField2).toEqual('someField2Value1'); - - await expectAsync(obj.fetch({ useMasterKey: true })).toBeRejectedWith( - jasmine.stringMatching('Object not found') + expect(result.data.deleteCustomer.clientMutationId).toEqual( + clientMutationId ); + expect(result.data.deleteCustomer.customer.objectId).toEqual( + obj.id + ); + expect(result.data.deleteCustomer.customer.someField1).toEqual( + "someField1Value1" + ); + expect(result.data.deleteCustomer.customer.someField2).toEqual( + "someField2Value1" + ); + + await expectAsync( + obj.fetch({ useMasterKey: true }) + ).toBeRejectedWith(jasmine.stringMatching("Object not found")); }); - it('should respect level permissions', async () => { + it("should respect level permissions", async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); function deleteObject(className, id, headers) { - const mutationName = className.charAt(0).toLowerCase() + className.slice(1); + const mutationName = + className.charAt(0).toLowerCase() + className.slice(1); return apolloClient.mutate({ mutation: gql` mutation DeleteSomeObject( @@ -6531,73 +7049,84 @@ describe('ParseGraphQLServer', () => { await Promise.all( objects.slice(0, 3).map(async obj => { - const originalFieldValue = obj.get('someField'); - await expectAsync(deleteObject(obj.className, obj.id)).toBeRejectedWith( - jasmine.stringMatching('Object not found') - ); + const originalFieldValue = obj.get("someField"); + await expectAsync( + deleteObject(obj.className, obj.id) + ).toBeRejectedWith(jasmine.stringMatching("Object not found")); await obj.fetch({ useMasterKey: true }); - expect(obj.get('someField')).toEqual(originalFieldValue); + expect(obj.get("someField")).toEqual(originalFieldValue); }) ); await Promise.all( objects.slice(0, 3).map(async obj => { - const originalFieldValue = obj.get('someField'); + const originalFieldValue = obj.get("someField"); await expectAsync( deleteObject(obj.className, obj.id, { - 'X-Parse-Session-Token': user4.getSessionToken(), + "X-Parse-Session-Token": user4.getSessionToken(), }) - ).toBeRejectedWith(jasmine.stringMatching('Object not found')); + ).toBeRejectedWith(jasmine.stringMatching("Object not found")); await obj.fetch({ useMasterKey: true }); - expect(obj.get('someField')).toEqual(originalFieldValue); + expect(obj.get("someField")).toEqual(originalFieldValue); }) ); expect( (await deleteObject(object4.className, object4.id)).data.delete[ - object4.className.charAt(0).toLowerCase() + object4.className.slice(1) + object4.className.charAt(0).toLowerCase() + + object4.className.slice(1) ] - ).toEqual({ objectId: object4.id, __typename: 'PublicClass' }); - await expectAsync(object4.fetch({ useMasterKey: true })).toBeRejectedWith( - jasmine.stringMatching('Object not found') - ); + ).toEqual({ objectId: object4.id, __typename: "PublicClass" }); + await expectAsync( + object4.fetch({ useMasterKey: true }) + ).toBeRejectedWith(jasmine.stringMatching("Object not found")); expect( ( await deleteObject(object1.className, object1.id, { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }) - ).data.delete[object1.className.charAt(0).toLowerCase() + object1.className.slice(1)] - ).toEqual({ objectId: object1.id, __typename: 'GraphQLClass' }); - await expectAsync(object1.fetch({ useMasterKey: true })).toBeRejectedWith( - jasmine.stringMatching('Object not found') - ); + ).data.delete[ + object1.className.charAt(0).toLowerCase() + + object1.className.slice(1) + ] + ).toEqual({ objectId: object1.id, __typename: "GraphQLClass" }); + await expectAsync( + object1.fetch({ useMasterKey: true }) + ).toBeRejectedWith(jasmine.stringMatching("Object not found")); expect( ( await deleteObject(object2.className, object2.id, { - 'X-Parse-Session-Token': user2.getSessionToken(), + "X-Parse-Session-Token": user2.getSessionToken(), }) - ).data.delete[object2.className.charAt(0).toLowerCase() + object2.className.slice(1)] - ).toEqual({ objectId: object2.id, __typename: 'GraphQLClass' }); - await expectAsync(object2.fetch({ useMasterKey: true })).toBeRejectedWith( - jasmine.stringMatching('Object not found') - ); + ).data.delete[ + object2.className.charAt(0).toLowerCase() + + object2.className.slice(1) + ] + ).toEqual({ objectId: object2.id, __typename: "GraphQLClass" }); + await expectAsync( + object2.fetch({ useMasterKey: true }) + ).toBeRejectedWith(jasmine.stringMatching("Object not found")); expect( ( await deleteObject(object3.className, object3.id, { - 'X-Parse-Session-Token': user5.getSessionToken(), + "X-Parse-Session-Token": user5.getSessionToken(), }) - ).data.delete[object3.className.charAt(0).toLowerCase() + object3.className.slice(1)] - ).toEqual({ objectId: object3.id, __typename: 'GraphQLClass' }); - await expectAsync(object3.fetch({ useMasterKey: true })).toBeRejectedWith( - jasmine.stringMatching('Object not found') - ); + ).data.delete[ + object3.className.charAt(0).toLowerCase() + + object3.className.slice(1) + ] + ).toEqual({ objectId: object3.id, __typename: "GraphQLClass" }); + await expectAsync( + object3.fetch({ useMasterKey: true }) + ).toBeRejectedWith(jasmine.stringMatching("Object not found")); }); - it('should respect level permissions with specific class mutation', async () => { + it("should respect level permissions with specific class mutation", async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); function deleteObject(className, id, headers) { - const mutationName = className.charAt(0).toLowerCase() + className.slice(1); + const mutationName = + className.charAt(0).toLowerCase() + className.slice(1); return apolloClient.mutate({ mutation: gql` mutation DeleteSomeObject( @@ -6621,242 +7150,254 @@ describe('ParseGraphQLServer', () => { await Promise.all( objects.slice(0, 3).map(async obj => { - const originalFieldValue = obj.get('someField'); - await expectAsync(deleteObject(obj.className, obj.id)).toBeRejectedWith( - jasmine.stringMatching('Object not found') - ); + const originalFieldValue = obj.get("someField"); + await expectAsync( + deleteObject(obj.className, obj.id) + ).toBeRejectedWith(jasmine.stringMatching("Object not found")); await obj.fetch({ useMasterKey: true }); - expect(obj.get('someField')).toEqual(originalFieldValue); + expect(obj.get("someField")).toEqual(originalFieldValue); }) ); await Promise.all( objects.slice(0, 3).map(async obj => { - const originalFieldValue = obj.get('someField'); + const originalFieldValue = obj.get("someField"); await expectAsync( deleteObject(obj.className, obj.id, { - 'X-Parse-Session-Token': user4.getSessionToken(), + "X-Parse-Session-Token": user4.getSessionToken(), }) - ).toBeRejectedWith(jasmine.stringMatching('Object not found')); + ).toBeRejectedWith(jasmine.stringMatching("Object not found")); await obj.fetch({ useMasterKey: true }); - expect(obj.get('someField')).toEqual(originalFieldValue); + expect(obj.get("someField")).toEqual(originalFieldValue); }) ); expect( (await deleteObject(object4.className, object4.id)).data[ `delete${object4.className}` - ][object4.className.charAt(0).toLowerCase() + object4.className.slice(1)].objectId + ][ + object4.className.charAt(0).toLowerCase() + + object4.className.slice(1) + ].objectId ).toEqual(object4.id); - await expectAsync(object4.fetch({ useMasterKey: true })).toBeRejectedWith( - jasmine.stringMatching('Object not found') - ); + await expectAsync( + object4.fetch({ useMasterKey: true }) + ).toBeRejectedWith(jasmine.stringMatching("Object not found")); expect( ( await deleteObject(object1.className, object1.id, { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }) ).data[`delete${object1.className}`][ - object1.className.charAt(0).toLowerCase() + object1.className.slice(1) + object1.className.charAt(0).toLowerCase() + + object1.className.slice(1) ].objectId ).toEqual(object1.id); - await expectAsync(object1.fetch({ useMasterKey: true })).toBeRejectedWith( - jasmine.stringMatching('Object not found') - ); + await expectAsync( + object1.fetch({ useMasterKey: true }) + ).toBeRejectedWith(jasmine.stringMatching("Object not found")); expect( ( await deleteObject(object2.className, object2.id, { - 'X-Parse-Session-Token': user2.getSessionToken(), + "X-Parse-Session-Token": user2.getSessionToken(), }) ).data[`delete${object2.className}`][ - object2.className.charAt(0).toLowerCase() + object2.className.slice(1) + object2.className.charAt(0).toLowerCase() + + object2.className.slice(1) ].objectId ).toEqual(object2.id); - await expectAsync(object2.fetch({ useMasterKey: true })).toBeRejectedWith( - jasmine.stringMatching('Object not found') - ); + await expectAsync( + object2.fetch({ useMasterKey: true }) + ).toBeRejectedWith(jasmine.stringMatching("Object not found")); expect( ( await deleteObject(object3.className, object3.id, { - 'X-Parse-Session-Token': user5.getSessionToken(), + "X-Parse-Session-Token": user5.getSessionToken(), }) ).data[`delete${object3.className}`][ - object3.className.charAt(0).toLowerCase() + object3.className.slice(1) + object3.className.charAt(0).toLowerCase() + + object3.className.slice(1) ].objectId ).toEqual(object3.id); - await expectAsync(object3.fetch({ useMasterKey: true })).toBeRejectedWith( - jasmine.stringMatching('Object not found') - ); - }); - }); - - it_id('f722e98e-1fd7-45c5-ade3-5177e3d542e8')(it)('should unset fields when null used on update/create', async () => { - const customerSchema = new Parse.Schema('Customer'); - customerSchema.addString('aString'); - customerSchema.addBoolean('aBoolean'); - customerSchema.addDate('aDate'); - customerSchema.addArray('aArray'); - customerSchema.addGeoPoint('aGeoPoint'); - customerSchema.addPointer('aPointer', 'Customer'); - customerSchema.addObject('aObject'); - customerSchema.addPolygon('aPolygon'); - await customerSchema.save(); + await expectAsync( + object3.fetch({ useMasterKey: true }) + ).toBeRejectedWith(jasmine.stringMatching("Object not found")); + }); + }); + + it_id("f722e98e-1fd7-45c5-ade3-5177e3d542e8")(it)( + "should unset fields when null used on update/create", + async () => { + const customerSchema = new Parse.Schema("Customer"); + customerSchema.addString("aString"); + customerSchema.addBoolean("aBoolean"); + customerSchema.addDate("aDate"); + customerSchema.addArray("aArray"); + customerSchema.addGeoPoint("aGeoPoint"); + customerSchema.addPointer("aPointer", "Customer"); + customerSchema.addObject("aObject"); + customerSchema.addPolygon("aPolygon"); + await customerSchema.save(); - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const cus = new Parse.Object('Customer'); - await cus.save({ aString: 'hello' }); - - const fields = { - aString: "i'm string", - aBoolean: true, - aDate: new Date().toISOString(), - aArray: ['hello', 1], - aGeoPoint: { latitude: 30, longitude: 30 }, - aPointer: { link: cus.id }, - aObject: { prop: { subprop: 1 }, prop2: 'test' }, - aPolygon: [ - { latitude: 30, longitude: 30 }, - { latitude: 31, longitude: 31 }, - { latitude: 32, longitude: 32 }, - { latitude: 30, longitude: 30 }, - ], - }; - const nullFields = Object.keys(fields).reduce((acc, k) => ({ ...acc, [k]: null }), {}); - const result = await apolloClient.mutate({ - mutation: gql` - mutation CreateCustomer($input: CreateCustomerInput!) { - createCustomer(input: $input) { - customer { - id - aString - aBoolean - aDate - aArray { - ... on Element { - value + const cus = new Parse.Object("Customer"); + await cus.save({ aString: "hello" }); + + const fields = { + aString: "i'm string", + aBoolean: true, + aDate: new Date().toISOString(), + aArray: ["hello", 1], + aGeoPoint: { latitude: 30, longitude: 30 }, + aPointer: { link: cus.id }, + aObject: { prop: { subprop: 1 }, prop2: "test" }, + aPolygon: [ + { latitude: 30, longitude: 30 }, + { latitude: 31, longitude: 31 }, + { latitude: 32, longitude: 32 }, + { latitude: 30, longitude: 30 }, + ], + }; + const nullFields = Object.keys(fields).reduce( + (acc, k) => ({ ...acc, [k]: null }), + {} + ); + const result = await apolloClient.mutate({ + mutation: gql` + mutation CreateCustomer($input: CreateCustomerInput!) { + createCustomer(input: $input) { + customer { + id + aString + aBoolean + aDate + aArray { + ... on Element { + value + } + } + aGeoPoint { + longitude + latitude + } + aPointer { + objectId + } + aObject + aPolygon { + longitude + latitude } - } - aGeoPoint { - longitude - latitude - } - aPointer { - objectId - } - aObject - aPolygon { - longitude - latitude } } } - } - `, - variables: { - input: { fields }, - }, - }); - const { - data: { - createCustomer: { - customer: { aPointer, aArray, id, ...otherFields }, + `, + variables: { + input: { fields }, }, - }, - } = result; - expect(id).toBeDefined(); - delete otherFields.__typename; - delete otherFields.aGeoPoint.__typename; - otherFields.aPolygon.forEach(v => { - delete v.__typename; - }); - expect({ - ...otherFields, - aPointer: { link: aPointer.objectId }, - aArray: aArray.map(({ value }) => value), - }).toEqual(fields); + }); + const { + data: { + createCustomer: { + customer: { aPointer, aArray, id, ...otherFields }, + }, + }, + } = result; + expect(id).toBeDefined(); + delete otherFields.__typename; + delete otherFields.aGeoPoint.__typename; + otherFields.aPolygon.forEach(v => { + delete v.__typename; + }); + expect({ + ...otherFields, + aPointer: { link: aPointer.objectId }, + aArray: aArray.map(({ value }) => value), + }).toEqual(fields); - const updated = await apolloClient.mutate({ - mutation: gql` - mutation UpdateCustomer($input: UpdateCustomerInput!) { - updateCustomer(input: $input) { - customer { - aString - aBoolean - aDate - aArray { - ... on Element { - value + const updated = await apolloClient.mutate({ + mutation: gql` + mutation UpdateCustomer($input: UpdateCustomerInput!) { + updateCustomer(input: $input) { + customer { + aString + aBoolean + aDate + aArray { + ... on Element { + value + } } - } - aGeoPoint { - longitude - latitude - } - aPointer { - objectId - } - aObject - aPolygon { - longitude - latitude - } - } - } - } - `, - variables: { - input: { fields: nullFields, id }, - }, - }); - const { - data: { - updateCustomer: { customer }, - }, - } = updated; - delete customer.__typename; - expect(Object.keys(customer).length).toEqual(8); - Object.keys(customer).forEach(k => { - expect(customer[k]).toBeNull(); - }); - try { - const queryResult = await apolloClient.query({ - query: gql` - query getEmptyCustomer($where: CustomerWhereInput!) { - customers(where: $where) { - edges { - node { - id + aGeoPoint { + longitude + latitude + } + aPointer { + objectId + } + aObject + aPolygon { + longitude + latitude } } } } `, variables: { - where: Object.keys(fields).reduce( - (acc, k) => ({ ...acc, [k]: { exists: false } }), - {} - ), + input: { fields: nullFields, id }, + }, + }); + const { + data: { + updateCustomer: { customer }, }, + } = updated; + delete customer.__typename; + expect(Object.keys(customer).length).toEqual(8); + Object.keys(customer).forEach(k => { + expect(customer[k]).toBeNull(); }); + try { + const queryResult = await apolloClient.query({ + query: gql` + query getEmptyCustomer($where: CustomerWhereInput!) { + customers(where: $where) { + edges { + node { + id + } + } + } + } + `, + variables: { + where: Object.keys(fields).reduce( + (acc, k) => ({ ...acc, [k]: { exists: false } }), + {} + ), + }, + }); - expect(queryResult.data.customers.edges.length).toEqual(1); - } catch (e) { - console.error(JSON.stringify(e)); + expect(queryResult.data.customers.edges.length).toEqual(1); + } catch (e) { + console.error(JSON.stringify(e)); + } } - }); + ); }); - describe('Files Mutations', () => { - describe('Create', () => { - it('should return File object', async () => { + describe("Files Mutations", () => { + describe("Create", () => { + it("should return File object", async () => { const clientMutationId = uuidv4(); parseServer = await global.reconfigureServer({ - publicServerURL: 'http://localhost:13377/parse', + publicServerURL: "http://localhost:13377/parse", }); await createGQLFromParseServer(parseServer); const body = new FormData(); body.append( - 'operations', + "operations", JSON.stringify({ query: ` mutation CreateFile($input: CreateFileInput!) { @@ -6877,14 +7418,17 @@ describe('ParseGraphQLServer', () => { }, }) ); - body.append('map', JSON.stringify({ 1: ['variables.input.upload'] })); - body.append('1', 'My File Content', { - filename: 'myFileName.txt', - contentType: 'text/plain', + body.append( + "map", + JSON.stringify({ 1: ["variables.input.upload"] }) + ); + body.append("1", "My File Content", { + filename: "myFileName.txt", + contentType: "text/plain", }); - let res = await fetch('http://localhost:13377/graphql', { - method: 'POST', + let res = await fetch("http://localhost:13377/graphql", { + method: "POST", headers, body, }); @@ -6893,7 +7437,9 @@ describe('ParseGraphQLServer', () => { const result = JSON.parse(await res.text()); - expect(result.data.createFile.clientMutationId).toEqual(clientMutationId); + expect(result.data.createFile.clientMutationId).toEqual( + clientMutationId + ); expect(result.data.createFile.fileInfo.name).toEqual( jasmine.stringMatching(/_myFileName.txt$/) ); @@ -6904,16 +7450,16 @@ describe('ParseGraphQLServer', () => { res = await fetch(result.data.createFile.fileInfo.url); expect(res.status).toEqual(200); - expect(await res.text()).toEqual('My File Content'); + expect(await res.text()).toEqual("My File Content"); }); }); }); - describe('Users Queries', () => { - it('should return current logged user', async () => { - const userName = 'user1', - password = 'user1', - email = 'emailUser1@parse.com'; + describe("Users Queries", () => { + it("should return current logged user", async () => { + const userName = "user1", + password = "user1", + email = "emailUser1@parse.com"; const user = new Parse.User(); user.setUsername(userName); @@ -6936,30 +7482,34 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Session-Token': session.getSessionToken(), + "X-Parse-Session-Token": session.getSessionToken(), }, }, }); - const { id, username: resultUserName, email: resultEmail } = result.data.viewer.user; + const { + id, + username: resultUserName, + email: resultEmail, + } = result.data.viewer.user; expect(id).toBeDefined(); expect(resultUserName).toEqual(userName); expect(resultEmail).toEqual(email); }); - it('should return logged user including pointer', async () => { - const foo = new Parse.Object('Foo'); - foo.set('bar', 'hello'); + it("should return logged user including pointer", async () => { + const foo = new Parse.Object("Foo"); + foo.set("bar", "hello"); - const userName = 'user1', - password = 'user1', - email = 'emailUser1@parse.com'; + const userName = "user1", + password = "user1", + email = "emailUser1@parse.com"; const user = new Parse.User(); user.setUsername(userName); user.setPassword(password); user.setEmail(email); - user.set('userFoo', foo); + user.set("userFoo", foo); await user.signUp(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -6982,7 +7532,7 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Session-Token': session.getSessionToken(), + "X-Parse-Session-Token": session.getSessionToken(), }, }, }); @@ -6992,25 +7542,25 @@ describe('ParseGraphQLServer', () => { expect(objectId).toEqual(user.id); expect(sessionToken).toBeDefined(); expect(resultFoo).toBeDefined(); - expect(resultFoo.bar).toEqual('hello'); + expect(resultFoo.bar).toEqual("hello"); }); - it('should return logged user and do not by pass pointer security', async () => { + it("should return logged user and do not by pass pointer security", async () => { const masterKeyOnlyACL = new Parse.ACL(); masterKeyOnlyACL.setPublicReadAccess(false); masterKeyOnlyACL.setPublicWriteAccess(false); - const foo = new Parse.Object('Foo'); + const foo = new Parse.Object("Foo"); foo.setACL(masterKeyOnlyACL); - foo.set('bar', 'hello'); + foo.set("bar", "hello"); await foo.save(null, { useMasterKey: true }); - const userName = 'userx1', - password = 'user1', - email = 'emailUserx1@parse.com'; + const userName = "userx1", + password = "user1", + email = "emailUserx1@parse.com"; const user = new Parse.User(); user.setUsername(userName); user.setPassword(password); user.setEmail(email); - user.set('userFoo', foo); + user.set("userFoo", foo); await user.signUp(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -7033,7 +7583,7 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Session-Token': session.getSessionToken(), + "X-Parse-Session-Token": session.getSessionToken(), }, }, }); @@ -7046,17 +7596,18 @@ describe('ParseGraphQLServer', () => { }); }); - describe('Users Mutations', () => { + describe("Users Mutations", () => { const challengeAdapter = { - validateAuthData: () => Promise.resolve({ response: { someData: true } }), + validateAuthData: () => + Promise.resolve({ response: { someData: true } }), validateAppId: () => Promise.resolve(), challenge: () => Promise.resolve({ someData: true }), options: { anOption: true }, }; - it('should create user and return authData response', async () => { + it("should create user and return authData response", async () => { parseServer = await global.reconfigureServer({ - publicServerURL: 'http://localhost:13377/parse', + publicServerURL: "http://localhost:13377/parse", auth: { challengeAdapter, }, @@ -7082,7 +7633,7 @@ describe('ParseGraphQLServer', () => { fields: { authData: { challengeAdapter: { - id: 'challengeAdapter', + id: "challengeAdapter", }, }, }, @@ -7090,29 +7641,31 @@ describe('ParseGraphQLServer', () => { }, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); - expect(result.data.createUser.clientMutationId).toEqual(clientMutationId); + expect(result.data.createUser.clientMutationId).toEqual( + clientMutationId + ); expect(result.data.createUser.user.authDataResponse).toEqual({ challengeAdapter: { someData: true }, }); }); - it('should sign user up', async () => { + it("should sign user up", async () => { parseServer = await global.reconfigureServer({ - publicServerURL: 'http://localhost:13377/parse', + publicServerURL: "http://localhost:13377/parse", auth: { challengeAdapter, }, }); await createGQLFromParseServer(parseServer); const clientMutationId = uuidv4(); - const userSchema = new Parse.Schema('_User'); - userSchema.addString('someField'); - userSchema.addPointer('aPointer', '_User'); + const userSchema = new Parse.Schema("_User"); + userSchema.addString("someField"); + userSchema.addPointer("aPointer", "_User"); await userSchema.update(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -7139,22 +7692,22 @@ describe('ParseGraphQLServer', () => { input: { clientMutationId, fields: { - username: 'user1', - password: 'user1', + username: "user1", + password: "user1", authData: { challengeAdapter: { - id: 'challengeAdapter', + id: "challengeAdapter", }, }, aPointer: { createAndLink: { - username: 'user2', - password: 'user2', - someField: 'someValue2', + username: "user2", + password: "user2", + someField: "someValue2", ACL: { public: { read: true, write: true } }, }, }, - someField: 'someValue', + someField: "someValue", }, }, }, @@ -7162,30 +7715,32 @@ describe('ParseGraphQLServer', () => { expect(result.data.signUp.clientMutationId).toEqual(clientMutationId); expect(result.data.signUp.viewer.sessionToken).toBeDefined(); - expect(result.data.signUp.viewer.user.someField).toEqual('someValue'); + expect(result.data.signUp.viewer.user.someField).toEqual("someValue"); expect(result.data.signUp.viewer.user.aPointer.id).toBeDefined(); - expect(result.data.signUp.viewer.user.aPointer.username).toEqual('user2'); - expect(typeof result.data.signUp.viewer.sessionToken).toBe('string'); + expect(result.data.signUp.viewer.user.aPointer.username).toEqual( + "user2" + ); + expect(typeof result.data.signUp.viewer.sessionToken).toBe("string"); expect(result.data.signUp.viewer.user.authDataResponse).toEqual({ challengeAdapter: { someData: true }, }); }); - it('should login with user', async () => { + it("should login with user", async () => { const clientMutationId = uuidv4(); - const userSchema = new Parse.Schema('_User'); + const userSchema = new Parse.Schema("_User"); parseServer = await global.reconfigureServer({ - publicServerURL: 'http://localhost:13377/parse', + publicServerURL: "http://localhost:13377/parse", auth: { challengeAdapter, myAuth: { - module: global.mockCustomAuthenticator('parse', 'graphql'), + module: global.mockCustomAuthenticator("parse", "graphql"), }, }, }); await createGQLFromParseServer(parseServer); - userSchema.addString('someField'); - userSchema.addPointer('aPointer', '_User'); + userSchema.addString("someField"); + userSchema.addPointer("aPointer", "_User"); await userSchema.update(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); const result = await apolloClient.mutate({ @@ -7211,19 +7766,19 @@ describe('ParseGraphQLServer', () => { input: { clientMutationId, authData: { - challengeAdapter: { id: 'challengeAdapter' }, + challengeAdapter: { id: "challengeAdapter" }, myAuth: { - id: 'parse', - password: 'graphql', + id: "parse", + password: "graphql", }, }, fields: { - someField: 'someValue', + someField: "someValue", aPointer: { createAndLink: { - username: 'user2', - password: 'user2', - someField: 'someValue2', + username: "user2", + password: "user2", + someField: "someValue2", ACL: { public: { read: true, write: true } }, }, }, @@ -7232,30 +7787,38 @@ describe('ParseGraphQLServer', () => { }, }); - expect(result.data.logInWith.clientMutationId).toEqual(clientMutationId); + expect(result.data.logInWith.clientMutationId).toEqual( + clientMutationId + ); expect(result.data.logInWith.viewer.sessionToken).toBeDefined(); - expect(result.data.logInWith.viewer.user.someField).toEqual('someValue'); - expect(typeof result.data.logInWith.viewer.sessionToken).toBe('string'); + expect(result.data.logInWith.viewer.user.someField).toEqual( + "someValue" + ); + expect(typeof result.data.logInWith.viewer.sessionToken).toBe( + "string" + ); expect(result.data.logInWith.viewer.user.aPointer.id).toBeDefined(); - expect(result.data.logInWith.viewer.user.aPointer.username).toEqual('user2'); + expect(result.data.logInWith.viewer.user.aPointer.username).toEqual( + "user2" + ); expect(result.data.logInWith.viewer.user.authDataResponse).toEqual({ challengeAdapter: { someData: true }, }); }); - it('should handle challenge', async () => { + it("should handle challenge", async () => { const clientMutationId = uuidv4(); - spyOn(challengeAdapter, 'challenge').and.callThrough(); + spyOn(challengeAdapter, "challenge").and.callThrough(); parseServer = await global.reconfigureServer({ - publicServerURL: 'http://localhost:13377/parse', + publicServerURL: "http://localhost:13377/parse", auth: { challengeAdapter, }, }); await createGQLFromParseServer(parseServer); const user = new Parse.User(); - await user.save({ username: 'username', password: 'password' }); + await user.save({ username: "username", password: "password" }); const result = await apolloClient.mutate({ mutation: gql` @@ -7269,8 +7832,8 @@ describe('ParseGraphQLServer', () => { variables: { input: { clientMutationId, - username: 'username', - password: 'password', + username: "username", + password: "password", challengeData: { challengeAdapter: { someChallengeData: true }, }, @@ -7288,7 +7851,9 @@ describe('ParseGraphQLServer', () => { expect(challengeCall[3].isChallenge).toBeTruthy(); expect(challengeCall[3].object.id).toEqual(user.id); expect(challengeCall[3].original.id).toEqual(user.id); - expect(result.data.challenge.clientMutationId).toEqual(clientMutationId); + expect(result.data.challenge.clientMutationId).toEqual( + clientMutationId + ); expect(result.data.challenge.challengeData).toEqual({ challengeAdapter: { someData: true }, }); @@ -7306,8 +7871,8 @@ describe('ParseGraphQLServer', () => { variables: { input: { clientMutationId, - username: 'username', - password: 'wrongPassword', + username: "username", + password: "wrongPassword", challengeData: { challengeAdapter: { someChallengeData: true }, }, @@ -7317,9 +7882,9 @@ describe('ParseGraphQLServer', () => { ).toBeRejected(); }); - it('should log the user in', async () => { + it("should log the user in", async () => { parseServer = await global.reconfigureServer({ - publicServerURL: 'http://localhost:13377/parse', + publicServerURL: "http://localhost:13377/parse", auth: { challengeAdapter, }, @@ -7327,9 +7892,9 @@ describe('ParseGraphQLServer', () => { await createGQLFromParseServer(parseServer); const clientMutationId = uuidv4(); const user = new Parse.User(); - user.setUsername('user1'); - user.setPassword('user1'); - user.set('someField', 'someValue'); + user.setUsername("user1"); + user.setPassword("user1"); + user.set("someField", "someValue"); await user.signUp(); await Parse.User.logOut(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -7351,8 +7916,8 @@ describe('ParseGraphQLServer', () => { variables: { input: { clientMutationId, - username: 'user1', - password: 'user1', + username: "user1", + password: "user1", authData: { challengeAdapter: { token: true } }, }, }, @@ -7360,18 +7925,18 @@ describe('ParseGraphQLServer', () => { expect(result.data.logIn.clientMutationId).toEqual(clientMutationId); expect(result.data.logIn.viewer.sessionToken).toBeDefined(); - expect(result.data.logIn.viewer.user.someField).toEqual('someValue'); - expect(typeof result.data.logIn.viewer.sessionToken).toBe('string'); + expect(result.data.logIn.viewer.user.someField).toEqual("someValue"); + expect(typeof result.data.logIn.viewer.sessionToken).toBe("string"); expect(result.data.logIn.viewer.user.authDataResponse).toEqual({ challengeAdapter: { someData: true }, }); }); - it('should log the user out', async () => { + it("should log the user out", async () => { const clientMutationId = uuidv4(); const user = new Parse.User(); - user.setUsername('user1'); - user.setPassword('user1'); + user.setUsername("user1"); + user.setPassword("user1"); await user.signUp(); await Parse.User.logOut(); @@ -7387,8 +7952,8 @@ describe('ParseGraphQLServer', () => { `, variables: { input: { - username: 'user1', - password: 'user1', + username: "user1", + password: "user1", }, }, }); @@ -7406,7 +7971,7 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Session-Token': sessionToken, + "X-Parse-Session-Token": sessionToken, }, }, variables: { @@ -7429,22 +7994,22 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Session-Token': sessionToken, + "X-Parse-Session-Token": sessionToken, }, }, }); - fail('should not retrieve current user due to session token'); + fail("should not retrieve current user due to session token"); } catch (err) { const { statusCode, result } = err.networkError; expect(statusCode).toBe(400); expect(result).toEqual({ code: 209, - error: 'Invalid session token', + error: "Invalid session token", }); } }); - it('should send reset password', async () => { + it("should send reset password", async () => { const clientMutationId = uuidv4(); const emailAdapter = { sendVerificationEmail: () => {}, @@ -7452,15 +8017,15 @@ describe('ParseGraphQLServer', () => { sendMail: () => {}, }; parseServer = await global.reconfigureServer({ - appName: 'test', + appName: "test", emailAdapter: emailAdapter, - publicServerURL: 'http://test.test', + publicServerURL: "http://test.test", }); await createGQLFromParseServer(parseServer); const user = new Parse.User(); - user.setUsername('user1'); - user.setPassword('user1'); - user.setEmail('user1@user1.user1'); + user.setUsername("user1"); + user.setPassword("user1"); + user.setEmail("user1@user1.user1"); await user.signUp(); await Parse.User.logOut(); const result = await apolloClient.mutate({ @@ -7475,46 +8040,50 @@ describe('ParseGraphQLServer', () => { variables: { input: { clientMutationId, - email: 'user1@user1.user1', + email: "user1@user1.user1", }, }, }); - expect(result.data.resetPassword.clientMutationId).toEqual(clientMutationId); + expect(result.data.resetPassword.clientMutationId).toEqual( + clientMutationId + ); expect(result.data.resetPassword.ok).toBeTruthy(); }); - it('should reset password', async () => { + it("should reset password", async () => { const clientMutationId = uuidv4(); let resetPasswordToken; const emailAdapter = { sendVerificationEmail: () => {}, sendPasswordResetEmail: ({ link }) => { - resetPasswordToken = link.split('token=')[1].split('&')[0]; + resetPasswordToken = link.split("token=")[1].split("&")[0]; }, sendMail: () => {}, }; parseServer = await global.reconfigureServer({ - appName: 'test', + appName: "test", emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:13377/parse', + publicServerURL: "http://localhost:13377/parse", auth: { myAuth: { - module: global.mockCustomAuthenticator('parse', 'graphql'), + module: global.mockCustomAuthenticator("parse", "graphql"), }, }, }); await createGQLFromParseServer(parseServer); const user = new Parse.User(); - user.setUsername('user1'); - user.setPassword('user1'); - user.setEmail('user1@user1.user1'); + user.setUsername("user1"); + user.setPassword("user1"); + user.setEmail("user1@user1.user1"); await user.signUp(); await Parse.User.logOut(); - await Parse.User.requestPasswordReset('user1@user1.user1'); + await Parse.User.requestPasswordReset("user1@user1.user1"); await apolloClient.mutate({ mutation: gql` - mutation ConfirmResetPassword($input: ConfirmResetPasswordInput!) { + mutation ConfirmResetPassword( + $input: ConfirmResetPasswordInput! + ) { confirmResetPassword(input: $input) { clientMutationId ok @@ -7524,8 +8093,8 @@ describe('ParseGraphQLServer', () => { variables: { input: { clientMutationId, - username: 'user1', - password: 'newPassword', + username: "user1", + password: "newPassword", token: resetPasswordToken, }, }, @@ -7544,18 +8113,18 @@ describe('ParseGraphQLServer', () => { variables: { input: { clientMutationId, - username: 'user1', - password: 'newPassword', + username: "user1", + password: "newPassword", }, }, }); expect(result.data.logIn.clientMutationId).toEqual(clientMutationId); expect(result.data.logIn.viewer.sessionToken).toBeDefined(); - expect(typeof result.data.logIn.viewer.sessionToken).toBe('string'); + expect(typeof result.data.logIn.viewer.sessionToken).toBe("string"); }); - it('should send verification email again', async () => { + it("should send verification email again", async () => { const clientMutationId = uuidv4(); const emailAdapter = { sendVerificationEmail: () => {}, @@ -7563,20 +8132,22 @@ describe('ParseGraphQLServer', () => { sendMail: () => {}, }; parseServer = await global.reconfigureServer({ - appName: 'test', + appName: "test", emailAdapter: emailAdapter, - publicServerURL: 'http://test.test', + publicServerURL: "http://test.test", }); await createGQLFromParseServer(parseServer); const user = new Parse.User(); - user.setUsername('user1'); - user.setPassword('user1'); - user.setEmail('user1@user1.user1'); + user.setUsername("user1"); + user.setPassword("user1"); + user.setEmail("user1@user1.user1"); await user.signUp(); await Parse.User.logOut(); const result = await apolloClient.mutate({ mutation: gql` - mutation SendVerificationEmail($input: SendVerificationEmailInput!) { + mutation SendVerificationEmail( + $input: SendVerificationEmailInput! + ) { sendVerificationEmail(input: $input) { clientMutationId ok @@ -7586,18 +8157,20 @@ describe('ParseGraphQLServer', () => { variables: { input: { clientMutationId, - email: 'user1@user1.user1', + email: "user1@user1.user1", }, }, }); - expect(result.data.sendVerificationEmail.clientMutationId).toEqual(clientMutationId); + expect(result.data.sendVerificationEmail.clientMutationId).toEqual( + clientMutationId + ); expect(result.data.sendVerificationEmail.ok).toBeTruthy(); }); }); - describe('Session Token', () => { - it('should fail due to invalid session token', async () => { + describe("Session Token", () => { + it("should fail due to invalid session token", async () => { try { await apolloClient.query({ query: gql` @@ -7609,22 +8182,22 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Session-Token': 'foo', + "X-Parse-Session-Token": "foo", }, }, }); - fail('should not retrieve current user due to session token'); + fail("should not retrieve current user due to session token"); } catch (err) { const { statusCode, result } = err.networkError; expect(statusCode).toBe(400); expect(result).toEqual({ code: 209, - error: 'Invalid session token', + error: "Invalid session token", }); } }); - it('should fail due to empty session token', async () => { + it("should fail due to empty session token", async () => { try { await apolloClient.query({ query: gql` @@ -7638,20 +8211,20 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Session-Token': '', + "X-Parse-Session-Token": "", }, }, }); - fail('should not retrieve current user due to session token'); + fail("should not retrieve current user due to session token"); } catch (err) { const { graphQLErrors } = err; expect(graphQLErrors.length).toBe(1); - expect(graphQLErrors[0].message).toBe('Invalid session token'); + expect(graphQLErrors[0].message).toBe("Invalid session token"); } }); - it('should find a user and fail due to empty session token', async () => { - const car = new Parse.Object('Car'); + it("should find a user and fail due to empty session token", async () => { + const car = new Parse.Object("Car"); await car.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -7676,26 +8249,26 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Session-Token': '', + "X-Parse-Session-Token": "", }, }, }); - fail('should not retrieve current user due to session token'); + fail("should not retrieve current user due to session token"); } catch (err) { const { graphQLErrors } = err; expect(graphQLErrors.length).toBe(1); - expect(graphQLErrors[0].message).toBe('Invalid session token'); + expect(graphQLErrors[0].message).toBe("Invalid session token"); } }); }); - describe('Functions Mutations', () => { - it('can be called', async () => { + describe("Functions Mutations", () => { + it("can be called", async () => { try { const clientMutationId = uuidv4(); - Parse.Cloud.define('hello', async () => { - return 'Hello world!'; + Parse.Cloud.define("hello", async () => { + return "Hello world!"; }); const result = await apolloClient.mutate({ @@ -7710,21 +8283,23 @@ describe('ParseGraphQLServer', () => { variables: { input: { clientMutationId, - functionName: 'hello', + functionName: "hello", }, }, }); - expect(result.data.callCloudCode.clientMutationId).toEqual(clientMutationId); - expect(result.data.callCloudCode.result).toEqual('Hello world!'); + expect(result.data.callCloudCode.clientMutationId).toEqual( + clientMutationId + ); + expect(result.data.callCloudCode.result).toEqual("Hello world!"); } catch (e) { handleError(e); } }); - it('can throw errors', async () => { - Parse.Cloud.define('hello', async () => { - throw new Error('Some error message.'); + it("can throw errors", async () => { + Parse.Cloud.define("hello", async () => { + throw new Error("Some error message."); }); try { @@ -7737,35 +8312,47 @@ describe('ParseGraphQLServer', () => { } `, }); - fail('Should throw an error'); + fail("Should throw an error"); } catch (e) { const { graphQLErrors } = e; expect(graphQLErrors.length).toBe(1); - expect(graphQLErrors[0].message).toBe('Some error message.'); + expect(graphQLErrors[0].message).toBe("Some error message."); } }); - it('should accept different params', done => { - Parse.Cloud.define('hello', async req => { + it("should accept different params", done => { + Parse.Cloud.define("hello", async req => { expect(req.params.date instanceof Date).toBe(true); expect(req.params.date.getTime()).toBe(1463907600000); expect(req.params.dateList[0] instanceof Date).toBe(true); expect(req.params.dateList[0].getTime()).toBe(1463907600000); - expect(req.params.complexStructure.date[0] instanceof Date).toBe(true); - expect(req.params.complexStructure.date[0].getTime()).toBe(1463907600000); - expect(req.params.complexStructure.deepDate.date[0] instanceof Date).toBe(true); - expect(req.params.complexStructure.deepDate.date[0].getTime()).toBe(1463907600000); - expect(req.params.complexStructure.deepDate2[0].date instanceof Date).toBe(true); - expect(req.params.complexStructure.deepDate2[0].date.getTime()).toBe(1463907600000); + expect(req.params.complexStructure.date[0] instanceof Date).toBe( + true + ); + expect(req.params.complexStructure.date[0].getTime()).toBe( + 1463907600000 + ); + expect( + req.params.complexStructure.deepDate.date[0] instanceof Date + ).toBe(true); + expect(req.params.complexStructure.deepDate.date[0].getTime()).toBe( + 1463907600000 + ); + expect( + req.params.complexStructure.deepDate2[0].date instanceof Date + ).toBe(true); + expect( + req.params.complexStructure.deepDate2[0].date.getTime() + ).toBe(1463907600000); // Regression for #2294 expect(req.params.file instanceof Parse.File).toBe(true); - expect(req.params.file.url()).toEqual('https://some.url'); + expect(req.params.file.url()).toEqual("https://some.url"); // Regression for #2204 - expect(req.params.array).toEqual(['a', 'b', 'c']); + expect(req.params.array).toEqual(["a", "b", "c"]); expect(Array.isArray(req.params.array)).toBe(true); expect(req.params.arrayOfArray).toEqual([ - ['a', 'b', 'c'], - ['d', 'e', 'f'], + ["a", "b", "c"], + ["d", "e", "f"], ]); expect(Array.isArray(req.params.arrayOfArray)).toBe(true); expect(Array.isArray(req.params.arrayOfArray[0])).toBe(true); @@ -7776,49 +8363,49 @@ describe('ParseGraphQLServer', () => { const params = { date: { - __type: 'Date', - iso: '2016-05-22T09:00:00.000Z', + __type: "Date", + iso: "2016-05-22T09:00:00.000Z", }, dateList: [ { - __type: 'Date', - iso: '2016-05-22T09:00:00.000Z', + __type: "Date", + iso: "2016-05-22T09:00:00.000Z", }, ], - lol: 'hello', + lol: "hello", complexStructure: { date: [ { - __type: 'Date', - iso: '2016-05-22T09:00:00.000Z', + __type: "Date", + iso: "2016-05-22T09:00:00.000Z", }, ], deepDate: { date: [ { - __type: 'Date', - iso: '2016-05-22T09:00:00.000Z', + __type: "Date", + iso: "2016-05-22T09:00:00.000Z", }, ], }, deepDate2: [ { date: { - __type: 'Date', - iso: '2016-05-22T09:00:00.000Z', + __type: "Date", + iso: "2016-05-22T09:00:00.000Z", }, }, ], }, file: Parse.File.fromJSON({ - __type: 'File', - name: 'name', - url: 'https://some.url', + __type: "File", + name: "name", + url: "https://some.url", }), - array: ['a', 'b', 'c'], + array: ["a", "b", "c"], arrayOfArray: [ - ['a', 'b', 'c'], - ['d', 'e', 'f'], + ["a", "b", "c"], + ["d", "e", "f"], ], }; @@ -7836,22 +8423,22 @@ describe('ParseGraphQLServer', () => { }); }); - it('should list all functions in the enum type', async () => { + it("should list all functions in the enum type", async () => { try { - Parse.Cloud.define('a', async () => { - return 'hello a'; + Parse.Cloud.define("a", async () => { + return "hello a"; }); - Parse.Cloud.define('b', async () => { - return 'hello b'; + Parse.Cloud.define("b", async () => { + return "hello b"; }); - Parse.Cloud.define('_underscored', async () => { - return 'hello _underscored'; + Parse.Cloud.define("_underscored", async () => { + return "hello _underscored"; }); - Parse.Cloud.define('contains1Number', async () => { - return 'hello contains1Number'; + Parse.Cloud.define("contains1Number", async () => { + return "hello contains1Number"; }); const functionEnum = ( @@ -7867,33 +8454,33 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type']; - expect(functionEnum.kind).toEqual('ENUM'); - expect(functionEnum.enumValues.map(value => value.name).sort()).toEqual([ - '_underscored', - 'a', - 'b', - 'contains1Number', - ]); + ).data["__type"]; + expect(functionEnum.kind).toEqual("ENUM"); + expect( + functionEnum.enumValues.map(value => value.name).sort() + ).toEqual(["_underscored", "a", "b", "contains1Number"]); } catch (e) { handleError(e); } }); - it('should warn functions not matching GraphQL allowed names', async () => { + it("should warn functions not matching GraphQL allowed names", async () => { try { - spyOn(parseGraphQLServer.parseGraphQLSchema.log, 'warn').and.callThrough(); + spyOn( + parseGraphQLServer.parseGraphQLSchema.log, + "warn" + ).and.callThrough(); - Parse.Cloud.define('a', async () => { - return 'hello a'; + Parse.Cloud.define("a", async () => { + return "hello a"; }); - Parse.Cloud.define('double-barrelled', async () => { - return 'hello b'; + Parse.Cloud.define("double-barrelled", async () => { + return "hello b"; }); - Parse.Cloud.define('1NumberInTheBeggning', async () => { - return 'hello contains1Number'; + Parse.Cloud.define("1NumberInTheBeggning", async () => { + return "hello contains1Number"; }); const functionEnum = ( @@ -7909,17 +8496,19 @@ describe('ParseGraphQLServer', () => { } `, }) - ).data['__type']; - expect(functionEnum.kind).toEqual('ENUM'); - expect(functionEnum.enumValues.map(value => value.name).sort()).toEqual(['a']); + ).data["__type"]; + expect(functionEnum.kind).toEqual("ENUM"); + expect( + functionEnum.enumValues.map(value => value.name).sort() + ).toEqual(["a"]); expect( parseGraphQLServer.parseGraphQLSchema.log.warn.calls .all() .map(call => call.args[0]) .sort() ).toEqual([ - 'Function 1NumberInTheBeggning could not be added to the auto schema because GraphQL names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/.', - 'Function double-barrelled could not be added to the auto schema because GraphQL names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/.', + "Function 1NumberInTheBeggning could not be added to the auto schema because GraphQL names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/.", + "Function double-barrelled could not be added to the auto schema because GraphQL names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/.", ]); } catch (e) { handleError(e); @@ -7927,35 +8516,37 @@ describe('ParseGraphQLServer', () => { }); }); - describe('Data Types', () => { - it('should support String', async () => { + describe("Data Types", () => { + it("should support String", async () => { try { - const someFieldValue = 'some string'; + const someFieldValue = "some string"; await apolloClient.mutate({ mutation: gql` mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass(input: { name: "SomeClass", schemaFields: $schemaFields }) { + createClass( + input: { name: "SomeClass", schemaFields: $schemaFields } + ) { clientMutationId } } `, variables: { schemaFields: { - addStrings: [{ name: 'someField' }], + addStrings: [{ name: "someField" }], }, }, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const schema = await new Parse.Schema('SomeClass').get(); - expect(schema.fields.someField.type).toEqual('String'); + const schema = await new Parse.Schema("SomeClass").get(); + expect(schema.fields.someField.type).toEqual("String"); const createResult = await apolloClient.mutate({ mutation: gql` @@ -7980,7 +8571,9 @@ describe('ParseGraphQLServer', () => { someClass(id: $id) { someField } - someClasses(where: { someField: { equalTo: $someFieldValue } }) { + someClasses( + where: { someField: { equalTo: $someFieldValue } } + ) { edges { node { someField @@ -7995,7 +8588,7 @@ describe('ParseGraphQLServer', () => { }, }); - expect(typeof getResult.data.someClass.someField).toEqual('string'); + expect(typeof getResult.data.someClass.someField).toEqual("string"); expect(getResult.data.someClass.someField).toEqual(someFieldValue); expect(getResult.data.someClasses.edges.length).toEqual(1); } catch (e) { @@ -8003,26 +8596,28 @@ describe('ParseGraphQLServer', () => { } }); - it('should support Int numbers', async () => { + it("should support Int numbers", async () => { try { const someFieldValue = 123; await apolloClient.mutate({ mutation: gql` mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass(input: { name: "SomeClass", schemaFields: $schemaFields }) { + createClass( + input: { name: "SomeClass", schemaFields: $schemaFields } + ) { clientMutationId } } `, variables: { schemaFields: { - addNumbers: [{ name: 'someField' }], + addNumbers: [{ name: "someField" }], }, }, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); @@ -8046,8 +8641,8 @@ describe('ParseGraphQLServer', () => { }, }); - const schema = await new Parse.Schema('SomeClass').get(); - expect(schema.fields.someField.type).toEqual('Number'); + const schema = await new Parse.Schema("SomeClass").get(); + expect(schema.fields.someField.type).toEqual("Number"); const getResult = await apolloClient.query({ query: gql` @@ -8055,7 +8650,9 @@ describe('ParseGraphQLServer', () => { someClass(id: $id) { someField } - someClasses(where: { someField: { equalTo: $someFieldValue } }) { + someClasses( + where: { someField: { equalTo: $someFieldValue } } + ) { edges { node { someField @@ -8070,7 +8667,7 @@ describe('ParseGraphQLServer', () => { }, }); - expect(typeof getResult.data.someClass.someField).toEqual('number'); + expect(typeof getResult.data.someClass.someField).toEqual("number"); expect(getResult.data.someClass.someField).toEqual(someFieldValue); expect(getResult.data.someClasses.edges.length).toEqual(1); } catch (e) { @@ -8078,34 +8675,36 @@ describe('ParseGraphQLServer', () => { } }); - it('should support Float numbers', async () => { + it("should support Float numbers", async () => { try { const someFieldValue = 123.4; await apolloClient.mutate({ mutation: gql` mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass(input: { name: "SomeClass", schemaFields: $schemaFields }) { + createClass( + input: { name: "SomeClass", schemaFields: $schemaFields } + ) { clientMutationId } } `, variables: { schemaFields: { - addNumbers: [{ name: 'someField' }], + addNumbers: [{ name: "someField" }], }, }, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const schema = await new Parse.Schema('SomeClass').get(); - expect(schema.fields.someField.type).toEqual('Number'); + const schema = await new Parse.Schema("SomeClass").get(); + expect(schema.fields.someField.type).toEqual("Number"); const createResult = await apolloClient.mutate({ mutation: gql` @@ -8130,7 +8729,9 @@ describe('ParseGraphQLServer', () => { someClass(id: $id) { someField } - someClasses(where: { someField: { equalTo: $someFieldValue } }) { + someClasses( + where: { someField: { equalTo: $someFieldValue } } + ) { edges { node { someField @@ -8145,7 +8746,7 @@ describe('ParseGraphQLServer', () => { }, }); - expect(typeof getResult.data.someClass.someField).toEqual('number'); + expect(typeof getResult.data.someClass.someField).toEqual("number"); expect(getResult.data.someClass.someField).toEqual(someFieldValue); expect(getResult.data.someClasses.edges.length).toEqual(1); } catch (e) { @@ -8153,7 +8754,7 @@ describe('ParseGraphQLServer', () => { } }); - it('should support Boolean', async () => { + it("should support Boolean", async () => { try { const someFieldValueTrue = true; const someFieldValueFalse = false; @@ -8161,28 +8762,33 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass(input: { name: "SomeClass", schemaFields: $schemaFields }) { + createClass( + input: { name: "SomeClass", schemaFields: $schemaFields } + ) { clientMutationId } } `, variables: { schemaFields: { - addBooleans: [{ name: 'someFieldTrue' }, { name: 'someFieldFalse' }], + addBooleans: [ + { name: "someFieldTrue" }, + { name: "someFieldFalse" }, + ], }, }, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const schema = await new Parse.Schema('SomeClass').get(); - expect(schema.fields.someFieldTrue.type).toEqual('Boolean'); - expect(schema.fields.someFieldFalse.type).toEqual('Boolean'); + const schema = await new Parse.Schema("SomeClass").get(); + expect(schema.fields.someFieldTrue.type).toEqual("Boolean"); + expect(schema.fields.someFieldFalse.type).toEqual("Boolean"); const createResult = await apolloClient.mutate({ mutation: gql` @@ -8234,8 +8840,12 @@ describe('ParseGraphQLServer', () => { }, }); - expect(typeof getResult.data.someClass.someFieldTrue).toEqual('boolean'); - expect(typeof getResult.data.someClass.someFieldFalse).toEqual('boolean'); + expect(typeof getResult.data.someClass.someFieldTrue).toEqual( + "boolean" + ); + expect(typeof getResult.data.someClass.someFieldFalse).toEqual( + "boolean" + ); expect(getResult.data.someClass.someFieldTrue).toEqual(true); expect(getResult.data.someClass.someFieldFalse).toEqual(false); expect(getResult.data.someClasses.edges.length).toEqual(1); @@ -8244,34 +8854,36 @@ describe('ParseGraphQLServer', () => { } }); - it('should support Date', async () => { + it("should support Date", async () => { try { const someFieldValue = new Date(); await apolloClient.mutate({ mutation: gql` mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass(input: { name: "SomeClass", schemaFields: $schemaFields }) { + createClass( + input: { name: "SomeClass", schemaFields: $schemaFields } + ) { clientMutationId } } `, variables: { schemaFields: { - addDates: [{ name: 'someField' }], + addDates: [{ name: "someField" }], }, }, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const schema = await new Parse.Schema('SomeClass').get(); - expect(schema.fields.someField.type).toEqual('Date'); + const schema = await new Parse.Schema("SomeClass").get(); + expect(schema.fields.someField.type).toEqual("Date"); const createResult = await apolloClient.mutate({ mutation: gql` @@ -8310,14 +8922,16 @@ describe('ParseGraphQLServer', () => { }, }); - expect(new Date(getResult.data.someClass.someField)).toEqual(someFieldValue); + expect(new Date(getResult.data.someClass.someField)).toEqual( + someFieldValue + ); expect(getResult.data.someClasses.edges.length).toEqual(1); } catch (e) { handleError(e); } }); - it('should support createdAt and updatedAt', async () => { + it("should support createdAt and updatedAt", async () => { await apolloClient.mutate({ mutation: gql` mutation CreateClass { @@ -8328,225 +8942,228 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); - const schema = await new Parse.Schema('SomeClass').get(); - expect(schema.fields.createdAt.type).toEqual('Date'); - expect(schema.fields.updatedAt.type).toEqual('Date'); + const schema = await new Parse.Schema("SomeClass").get(); + expect(schema.fields.createdAt.type).toEqual("Date"); + expect(schema.fields.updatedAt.type).toEqual("Date"); }); - it_id('93e748f6-ad9b-4c31-8e1e-c5685e2382fb')(it)('should support ACL', async () => { - const someClass = new Parse.Object('SomeClass'); - await someClass.save(); + it_id("93e748f6-ad9b-4c31-8e1e-c5685e2382fb")(it)( + "should support ACL", + async () => { + const someClass = new Parse.Object("SomeClass"); + await someClass.save(); - const roleACL = new Parse.ACL(); - roleACL.setPublicReadAccess(true); + const roleACL = new Parse.ACL(); + roleACL.setPublicReadAccess(true); - const user = new Parse.User(); - user.set('username', 'username'); - user.set('password', 'password'); - user.setACL(roleACL); - await user.signUp(); + const user = new Parse.User(); + user.set("username", "username"); + user.set("password", "password"); + user.setACL(roleACL); + await user.signUp(); - const user2 = new Parse.User(); - user2.set('username', 'username2'); - user2.set('password', 'password2'); - user2.setACL(roleACL); - await user2.signUp(); + const user2 = new Parse.User(); + user2.set("username", "username2"); + user2.set("password", "password2"); + user2.setACL(roleACL); + await user2.signUp(); - const role = new Parse.Role('aRole', roleACL); - await role.save(); + const role = new Parse.Role("aRole", roleACL); + await role.save(); - const role2 = new Parse.Role('aRole2', roleACL); - await role2.save(); + const role2 = new Parse.Role("aRole2", roleACL); + await role2.save(); - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const gqlUser = ( - await apolloClient.query({ - query: gql` - query getUser($id: ID!) { - user(id: $id) { - id + const gqlUser = ( + await apolloClient.query({ + query: gql` + query getUser($id: ID!) { + user(id: $id) { + id + } } - } - `, - variables: { id: user.id }, - }) - ).data.user; - const { - data: { createSomeClass }, - } = await apolloClient.mutate({ - mutation: gql` - mutation Create($fields: CreateSomeClassFieldsInput) { - createSomeClass(input: { fields: $fields }) { - someClass { - id - objectId - ACL { - users { - userId - read - write - } - roles { - roleName - read - write - } - public { - read - write + `, + variables: { id: user.id }, + }) + ).data.user; + const { + data: { createSomeClass }, + } = await apolloClient.mutate({ + mutation: gql` + mutation Create($fields: CreateSomeClassFieldsInput) { + createSomeClass(input: { fields: $fields }) { + someClass { + id + objectId + ACL { + users { + userId + read + write + } + roles { + roleName + read + write + } + public { + read + write + } } } } } - } - `, - variables: { - fields: { - ACL: { - users: [ - { userId: gqlUser.id, read: true, write: true }, - { userId: user2.id, read: true, write: false }, - ], - roles: [ - { roleName: 'aRole', read: true, write: false }, - { roleName: 'aRole2', read: false, write: true }, - ], - public: { read: true, write: true }, + `, + variables: { + fields: { + ACL: { + users: [ + { userId: gqlUser.id, read: true, write: true }, + { userId: user2.id, read: true, write: false }, + ], + roles: [ + { roleName: "aRole", read: true, write: false }, + { roleName: "aRole2", read: false, write: true }, + ], + public: { read: true, write: true }, + }, }, }, - }, - }); - - const expectedCreateACL = { - __typename: 'ACL', - users: [ - { - userId: toGlobalId('_User', user.id), - read: true, - write: true, - __typename: 'UserACL', - }, - { - userId: toGlobalId('_User', user2.id), - read: true, - write: false, - __typename: 'UserACL', - }, - ], - roles: [ - { - roleName: 'aRole', - read: true, - write: false, - __typename: 'RoleACL', - }, - { - roleName: 'aRole2', - read: false, - write: true, - __typename: 'RoleACL', - }, - ], - public: { read: true, write: true, __typename: 'PublicACL' }, - }; - const query1 = new Parse.Query('SomeClass'); - const obj1 = ( - await query1.get(createSomeClass.someClass.objectId, { - useMasterKey: true, - }) - ).toJSON(); - expect(obj1.ACL[user.id]).toEqual({ read: true, write: true }); - expect(obj1.ACL[user2.id]).toEqual({ read: true }); - expect(obj1.ACL['role:aRole']).toEqual({ read: true }); - expect(obj1.ACL['role:aRole2']).toEqual({ write: true }); - expect(obj1.ACL['*']).toEqual({ read: true, write: true }); - expect(createSomeClass.someClass.ACL).toEqual(expectedCreateACL); + }); - const { - data: { updateSomeClass }, - } = await apolloClient.mutate({ - mutation: gql` - mutation Update($id: ID!, $fields: UpdateSomeClassFieldsInput) { - updateSomeClass(input: { id: $id, fields: $fields }) { - someClass { - id - objectId - ACL { - users { - userId - read - write - } - roles { - roleName - read - write - } - public { - read - write + const expectedCreateACL = { + __typename: "ACL", + users: [ + { + userId: toGlobalId("_User", user.id), + read: true, + write: true, + __typename: "UserACL", + }, + { + userId: toGlobalId("_User", user2.id), + read: true, + write: false, + __typename: "UserACL", + }, + ], + roles: [ + { + roleName: "aRole", + read: true, + write: false, + __typename: "RoleACL", + }, + { + roleName: "aRole2", + read: false, + write: true, + __typename: "RoleACL", + }, + ], + public: { read: true, write: true, __typename: "PublicACL" }, + }; + const query1 = new Parse.Query("SomeClass"); + const obj1 = ( + await query1.get(createSomeClass.someClass.objectId, { + useMasterKey: true, + }) + ).toJSON(); + expect(obj1.ACL[user.id]).toEqual({ read: true, write: true }); + expect(obj1.ACL[user2.id]).toEqual({ read: true }); + expect(obj1.ACL["role:aRole"]).toEqual({ read: true }); + expect(obj1.ACL["role:aRole2"]).toEqual({ write: true }); + expect(obj1.ACL["*"]).toEqual({ read: true, write: true }); + expect(createSomeClass.someClass.ACL).toEqual(expectedCreateACL); + + const { + data: { updateSomeClass }, + } = await apolloClient.mutate({ + mutation: gql` + mutation Update($id: ID!, $fields: UpdateSomeClassFieldsInput) { + updateSomeClass(input: { id: $id, fields: $fields }) { + someClass { + id + objectId + ACL { + users { + userId + read + write + } + roles { + roleName + read + write + } + public { + read + write + } } } } } - } - `, - variables: { - id: createSomeClass.someClass.id, - fields: { - ACL: { - roles: [{ roleName: 'aRole', write: true, read: true }], - public: { read: true, write: false }, + `, + variables: { + id: createSomeClass.someClass.id, + fields: { + ACL: { + roles: [{ roleName: "aRole", write: true, read: true }], + public: { read: true, write: false }, + }, }, }, - }, - }); + }); - const expectedUpdateACL = { - __typename: 'ACL', - users: null, - roles: [ - { - roleName: 'aRole', - read: true, - write: true, - __typename: 'RoleACL', - }, - ], - public: { read: true, write: false, __typename: 'PublicACL' }, - }; + const expectedUpdateACL = { + __typename: "ACL", + users: null, + roles: [ + { + roleName: "aRole", + read: true, + write: true, + __typename: "RoleACL", + }, + ], + public: { read: true, write: false, __typename: "PublicACL" }, + }; - const query2 = new Parse.Query('SomeClass'); - const obj2 = ( - await query2.get(createSomeClass.someClass.objectId, { - useMasterKey: true, - }) - ).toJSON(); + const query2 = new Parse.Query("SomeClass"); + const obj2 = ( + await query2.get(createSomeClass.someClass.objectId, { + useMasterKey: true, + }) + ).toJSON(); - expect(obj2.ACL['role:aRole']).toEqual({ write: true, read: true }); - expect(obj2.ACL[user.id]).toBeUndefined(); - expect(obj2.ACL['*']).toEqual({ read: true }); - expect(updateSomeClass.someClass.ACL).toEqual(expectedUpdateACL); - }); + expect(obj2.ACL["role:aRole"]).toEqual({ write: true, read: true }); + expect(obj2.ACL[user.id]).toBeUndefined(); + expect(obj2.ACL["*"]).toEqual({ read: true }); + expect(updateSomeClass.someClass.ACL).toEqual(expectedUpdateACL); + } + ); - it('should support pointer on create', async () => { - const company = new Parse.Object('Company'); - company.set('name', 'imACompany1'); + it("should support pointer on create", async () => { + const company = new Parse.Object("Company"); + company.set("name", "imACompany1"); await company.save(); - const country = new Parse.Object('Country'); - country.set('name', 'imACountry'); - country.set('company', company); + const country = new Parse.Object("Country"); + country.set("name", "imACountry"); + country.set("company", company); await country.save(); - const company2 = new Parse.Object('Company'); - company2.set('name', 'imACompany2'); + const company2 = new Parse.Object("Company"); + company2.set("name", "imACompany2"); await company2.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -8573,7 +9190,7 @@ describe('ParseGraphQLServer', () => { `, variables: { fields: { - name: 'imCountry2', + name: "imCountry2", company: { link: company2.id }, }, }, @@ -8581,17 +9198,17 @@ describe('ParseGraphQLServer', () => { expect(result.id).toBeDefined(); expect(result.company.objectId).toEqual(company2.id); - expect(result.company.name).toEqual('imACompany2'); + expect(result.company.name).toEqual("imACompany2"); }); - it('should support nested pointer on create', async () => { - const company = new Parse.Object('Company'); - company.set('name', 'imACompany1'); + it("should support nested pointer on create", async () => { + const company = new Parse.Object("Company"); + company.set("name", "imACompany1"); await company.save(); - const country = new Parse.Object('Country'); - country.set('name', 'imACountry'); - country.set('company', company); + const country = new Parse.Object("Country"); + country.set("name", "imACountry"); + country.set("company", company); await country.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -8616,10 +9233,10 @@ describe('ParseGraphQLServer', () => { `, variables: { fields: { - name: 'imCountry2', + name: "imCountry2", company: { createAndLink: { - name: 'imACompany2', + name: "imACompany2", }, }, }, @@ -8628,21 +9245,21 @@ describe('ParseGraphQLServer', () => { expect(result.id).toBeDefined(); expect(result.company.id).toBeDefined(); - expect(result.company.name).toEqual('imACompany2'); + expect(result.company.name).toEqual("imACompany2"); }); - it('should support pointer on update', async () => { - const company = new Parse.Object('Company'); - company.set('name', 'imACompany1'); + it("should support pointer on update", async () => { + const company = new Parse.Object("Company"); + company.set("name", "imACompany1"); await company.save(); - const country = new Parse.Object('Country'); - country.set('name', 'imACountry'); - country.set('company', company); + const country = new Parse.Object("Country"); + country.set("name", "imACountry"); + country.set("company", company); await country.save(); - const company2 = new Parse.Object('Company'); - company2.set('name', 'imACompany2'); + const company2 = new Parse.Object("Company"); + company2.set("name", "imACompany2"); await company2.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -8677,17 +9294,17 @@ describe('ParseGraphQLServer', () => { expect(result.id).toBeDefined(); expect(result.company.objectId).toEqual(company2.id); - expect(result.company.name).toEqual('imACompany2'); + expect(result.company.name).toEqual("imACompany2"); }); - it('should support nested pointer on update', async () => { - const company = new Parse.Object('Company'); - company.set('name', 'imACompany1'); + it("should support nested pointer on update", async () => { + const company = new Parse.Object("Company"); + company.set("name", "imACompany1"); await company.save(); - const country = new Parse.Object('Country'); - country.set('name', 'imACountry'); - country.set('company', company); + const country = new Parse.Object("Country"); + country.set("name", "imACountry"); + country.set("company", company); await country.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -8715,7 +9332,7 @@ describe('ParseGraphQLServer', () => { fields: { company: { createAndLink: { - name: 'imACompany2', + name: "imACompany2", }, }, }, @@ -8724,85 +9341,94 @@ describe('ParseGraphQLServer', () => { expect(result.id).toBeDefined(); expect(result.company.id).toBeDefined(); - expect(result.company.name).toEqual('imACompany2'); + expect(result.company.name).toEqual("imACompany2"); }); - it_only_db('mongo')('should support relation and nested relation on create', async () => { - const company = new Parse.Object('Company'); - company.set('name', 'imACompany1'); - await company.save(); + it_only_db("mongo")( + "should support relation and nested relation on create", + async () => { + const company = new Parse.Object("Company"); + company.set("name", "imACompany1"); + await company.save(); - const country = new Parse.Object('Country'); - country.set('name', 'imACountry'); - country.relation('companies').add(company); - await country.save(); + const country = new Parse.Object("Country"); + country.set("name", "imACountry"); + country.relation("companies").add(company); + await country.save(); - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const { - data: { - createCountry: { country: result }, - }, - } = await apolloClient.mutate({ - mutation: gql` - mutation CreateCountry($fields: CreateCountryFieldsInput) { - createCountry(input: { fields: $fields }) { - country { - id - objectId - name - companies { - edges { - node { - id - objectId - name + const { + data: { + createCountry: { country: result }, + }, + } = await apolloClient.mutate({ + mutation: gql` + mutation CreateCountry($fields: CreateCountryFieldsInput) { + createCountry(input: { fields: $fields }) { + country { + id + objectId + name + companies { + edges { + node { + id + objectId + name + } } } } } } - } - `, - variables: { - fields: { - name: 'imACountry2', - companies: { - add: [company.id], - createAndAdd: [ - { - name: 'imACompany2', - }, - { - name: 'imACompany3', - }, - ], + `, + variables: { + fields: { + name: "imACountry2", + companies: { + add: [company.id], + createAndAdd: [ + { + name: "imACompany2", + }, + { + name: "imACompany3", + }, + ], + }, }, }, - }, - }); + }); - expect(result.id).toBeDefined(); - expect(result.name).toEqual('imACountry2'); - expect(result.companies.edges.length).toEqual(3); - expect(result.companies.edges.some(o => o.node.objectId === company.id)).toBeTruthy(); - expect(result.companies.edges.some(o => o.node.name === 'imACompany2')).toBeTruthy(); - expect(result.companies.edges.some(o => o.node.name === 'imACompany3')).toBeTruthy(); - }); + expect(result.id).toBeDefined(); + expect(result.name).toEqual("imACountry2"); + expect(result.companies.edges.length).toEqual(3); + expect( + result.companies.edges.some(o => o.node.objectId === company.id) + ).toBeTruthy(); + expect( + result.companies.edges.some(o => o.node.name === "imACompany2") + ).toBeTruthy(); + expect( + result.companies.edges.some(o => o.node.name === "imACompany3") + ).toBeTruthy(); + } + ); - it_only_db('mongo')('should support deep nested creation', async () => { - const team = new Parse.Object('Team'); - team.set('name', 'imATeam1'); + it_only_db("mongo")("should support deep nested creation", async () => { + const team = new Parse.Object("Team"); + team.set("name", "imATeam1"); await team.save(); - const company = new Parse.Object('Company'); - company.set('name', 'imACompany1'); - company.relation('teams').add(team); + const company = new Parse.Object("Company"); + company.set("name", "imACompany1"); + company.relation("teams").add(team); await company.save(); - const country = new Parse.Object('Country'); - country.set('name', 'imACountry'); - country.relation('companies').add(company); + const country = new Parse.Object("Country"); + country.set("name", "imACountry"); + country.relation("companies").add(company); await country.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -8840,22 +9466,22 @@ describe('ParseGraphQLServer', () => { `, variables: { fields: { - name: 'imACountry2', + name: "imACountry2", companies: { createAndAdd: [ { - name: 'imACompany2', + name: "imACompany2", teams: { createAndAdd: { - name: 'imATeam2', + name: "imATeam2", }, }, }, { - name: 'imACompany3', + name: "imACompany3", teams: { createAndAdd: { - name: 'imATeam3', + name: "imATeam3", }, }, }, @@ -8866,163 +9492,183 @@ describe('ParseGraphQLServer', () => { }); expect(result.id).toBeDefined(); - expect(result.name).toEqual('imACountry2'); + expect(result.name).toEqual("imACountry2"); expect(result.companies.edges.length).toEqual(2); expect( result.companies.edges.some( c => - c.node.name === 'imACompany2' && - c.node.teams.edges.some(t => t.node.name === 'imATeam2') + c.node.name === "imACompany2" && + c.node.teams.edges.some(t => t.node.name === "imATeam2") ) ).toBeTruthy(); expect( result.companies.edges.some( c => - c.node.name === 'imACompany3' && - c.node.teams.edges.some(t => t.node.name === 'imATeam3') + c.node.name === "imACompany3" && + c.node.teams.edges.some(t => t.node.name === "imATeam3") ) ).toBeTruthy(); }); - it_only_db('mongo')('should support relation and nested relation on update', async () => { - const company1 = new Parse.Object('Company'); - company1.set('name', 'imACompany1'); - await company1.save(); + it_only_db("mongo")( + "should support relation and nested relation on update", + async () => { + const company1 = new Parse.Object("Company"); + company1.set("name", "imACompany1"); + await company1.save(); - const company2 = new Parse.Object('Company'); - company2.set('name', 'imACompany2'); - await company2.save(); + const company2 = new Parse.Object("Company"); + company2.set("name", "imACompany2"); + await company2.save(); - const country = new Parse.Object('Country'); - country.set('name', 'imACountry'); - country.relation('companies').add(company1); - await country.save(); + const country = new Parse.Object("Country"); + country.set("name", "imACountry"); + country.relation("companies").add(company1); + await country.save(); - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const { - data: { - updateCountry: { country: result }, - }, - } = await apolloClient.mutate({ - mutation: gql` - mutation UpdateCountry($id: ID!, $fields: UpdateCountryFieldsInput) { - updateCountry(input: { id: $id, fields: $fields }) { - country { - id - objectId - companies { - edges { - node { - id - objectId - name + const { + data: { + updateCountry: { country: result }, + }, + } = await apolloClient.mutate({ + mutation: gql` + mutation UpdateCountry( + $id: ID! + $fields: UpdateCountryFieldsInput + ) { + updateCountry(input: { id: $id, fields: $fields }) { + country { + id + objectId + companies { + edges { + node { + id + objectId + name + } } } } } } - } - `, - variables: { - id: country.id, - fields: { - companies: { - add: [company2.id], - remove: [company1.id], - createAndAdd: [ - { - name: 'imACompany3', - }, - ], + `, + variables: { + id: country.id, + fields: { + companies: { + add: [company2.id], + remove: [company1.id], + createAndAdd: [ + { + name: "imACompany3", + }, + ], + }, }, }, - }, - }); + }); - expect(result.objectId).toEqual(country.id); - expect(result.companies.edges.length).toEqual(2); - expect(result.companies.edges.some(o => o.node.objectId === company2.id)).toBeTruthy(); - expect(result.companies.edges.some(o => o.node.name === 'imACompany3')).toBeTruthy(); - expect(result.companies.edges.some(o => o.node.objectId === company1.id)).toBeFalsy(); - }); + expect(result.objectId).toEqual(country.id); + expect(result.companies.edges.length).toEqual(2); + expect( + result.companies.edges.some(o => o.node.objectId === company2.id) + ).toBeTruthy(); + expect( + result.companies.edges.some(o => o.node.name === "imACompany3") + ).toBeTruthy(); + expect( + result.companies.edges.some(o => o.node.objectId === company1.id) + ).toBeFalsy(); + } + ); - it_only_db('mongo')('should support nested relation on create with filter', async () => { - const company = new Parse.Object('Company'); - company.set('name', 'imACompany1'); - await company.save(); + it_only_db("mongo")( + "should support nested relation on create with filter", + async () => { + const company = new Parse.Object("Company"); + company.set("name", "imACompany1"); + await company.save(); - const country = new Parse.Object('Country'); - country.set('name', 'imACountry'); - country.relation('companies').add(company); - await country.save(); + const country = new Parse.Object("Country"); + country.set("name", "imACountry"); + country.relation("companies").add(company); + await country.save(); - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const { - data: { - createCountry: { country: result }, - }, - } = await apolloClient.mutate({ - mutation: gql` - mutation CreateCountry($fields: CreateCountryFieldsInput, $where: CompanyWhereInput) { - createCountry(input: { fields: $fields }) { - country { - id - name - companies(where: $where) { - edges { - node { - id - name + const { + data: { + createCountry: { country: result }, + }, + } = await apolloClient.mutate({ + mutation: gql` + mutation CreateCountry( + $fields: CreateCountryFieldsInput + $where: CompanyWhereInput + ) { + createCountry(input: { fields: $fields }) { + country { + id + name + companies(where: $where) { + edges { + node { + id + name + } } } } } } - } - `, - variables: { - where: { - name: { - equalTo: 'imACompany2', + `, + variables: { + where: { + name: { + equalTo: "imACompany2", + }, }, - }, - fields: { - name: 'imACountry2', - companies: { - add: [company.id], - createAndAdd: [ - { - name: 'imACompany2', - }, - { - name: 'imACompany3', - }, - ], + fields: { + name: "imACountry2", + companies: { + add: [company.id], + createAndAdd: [ + { + name: "imACompany2", + }, + { + name: "imACompany3", + }, + ], + }, }, }, - }, - }); + }); - expect(result.id).toBeDefined(); - expect(result.name).toEqual('imACountry2'); - expect(result.companies.edges.length).toEqual(1); - expect(result.companies.edges.some(o => o.node.name === 'imACompany2')).toBeTruthy(); - }); + expect(result.id).toBeDefined(); + expect(result.name).toEqual("imACountry2"); + expect(result.companies.edges.length).toEqual(1); + expect( + result.companies.edges.some(o => o.node.name === "imACompany2") + ).toBeTruthy(); + } + ); - it_only_db('mongo')('should support relation on query', async () => { - const company1 = new Parse.Object('Company'); - company1.set('name', 'imACompany1'); + it_only_db("mongo")("should support relation on query", async () => { + const company1 = new Parse.Object("Company"); + company1.set("name", "imACompany1"); await company1.save(); - const company2 = new Parse.Object('Company'); - company2.set('name', 'imACompany2'); + const company2 = new Parse.Object("Company"); + company2.set("name", "imACompany2"); await company2.save(); - const country = new Parse.Object('Country'); - country.set('name', 'imACountry'); - country.relation('companies').add([company1, company2]); + const country = new Parse.Object("Country"); + country.set("name", "imACountry"); + country.relation("companies").add([company1, company2]); await country.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -9056,8 +9702,12 @@ describe('ParseGraphQLServer', () => { expect(result1.objectId).toEqual(country.id); expect(result1.companies.edges.length).toEqual(2); - expect(result1.companies.edges.some(o => o.node.objectId === company1.id)).toBeTruthy(); - expect(result1.companies.edges.some(o => o.node.objectId === company2.id)).toBeTruthy(); + expect( + result1.companies.edges.some(o => o.node.objectId === company1.id) + ).toBeTruthy(); + expect( + result1.companies.edges.some(o => o.node.objectId === company2.id) + ).toBeTruthy(); // With where const { @@ -9083,7 +9733,7 @@ describe('ParseGraphQLServer', () => { variables: { id: country.id, where: { - name: { equalTo: 'imACompany1' }, + name: { equalTo: "imACompany1" }, }, }, }); @@ -9092,242 +9742,245 @@ describe('ParseGraphQLServer', () => { expect(result2.companies.edges[0].node.objectId).toEqual(company1.id); }); - it_id('f4312f2c-90bb-4583-b033-02078ae0ce84')(it)('should support relational where query', async () => { - const president = new Parse.Object('President'); - president.set('name', 'James'); - await president.save(); + it_id("f4312f2c-90bb-4583-b033-02078ae0ce84")(it)( + "should support relational where query", + async () => { + const president = new Parse.Object("President"); + president.set("name", "James"); + await president.save(); - const employee = new Parse.Object('Employee'); - employee.set('name', 'John'); - await employee.save(); + const employee = new Parse.Object("Employee"); + employee.set("name", "John"); + await employee.save(); - const company1 = new Parse.Object('Company'); - company1.set('name', 'imACompany1'); - await company1.save(); + const company1 = new Parse.Object("Company"); + company1.set("name", "imACompany1"); + await company1.save(); - const company2 = new Parse.Object('Company'); - company2.set('name', 'imACompany2'); - company2.relation('employees').add([employee]); - await company2.save(); + const company2 = new Parse.Object("Company"); + company2.set("name", "imACompany2"); + company2.relation("employees").add([employee]); + await company2.save(); - const country = new Parse.Object('Country'); - country.set('name', 'imACountry'); - country.relation('companies').add([company1, company2]); - await country.save(); + const country = new Parse.Object("Country"); + country.set("name", "imACountry"); + country.relation("companies").add([company1, company2]); + await country.save(); - const country2 = new Parse.Object('Country'); - country2.set('name', 'imACountry2'); - country2.relation('companies').add([company1]); - await country2.save(); + const country2 = new Parse.Object("Country"); + country2.set("name", "imACountry2"); + country2.relation("companies").add([company1]); + await country2.save(); - const country3 = new Parse.Object('Country'); - country3.set('name', 'imACountry3'); - country3.set('president', president); - await country3.save(); + const country3 = new Parse.Object("Country"); + country3.set("name", "imACountry3"); + country3.set("president", president); + await country3.save(); - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - let { - data: { - countries: { edges: result }, - }, - } = await apolloClient.query({ - query: gql` - query findCountry($where: CountryWhereInput) { - countries(where: $where) { - edges { - node { - id - objectId - companies { - edges { - node { - id - objectId - name + let { + data: { + countries: { edges: result }, + }, + } = await apolloClient.query({ + query: gql` + query findCountry($where: CountryWhereInput) { + countries(where: $where) { + edges { + node { + id + objectId + companies { + edges { + node { + id + objectId + name + } } } } } } } - } - `, - variables: { - where: { - companies: { - have: { - employees: { have: { name: { equalTo: 'John' } } }, + `, + variables: { + where: { + companies: { + have: { + employees: { have: { name: { equalTo: "John" } } }, + }, }, }, }, - }, - }); - expect(result.length).toEqual(1); - result = result[0].node; - expect(result.objectId).toEqual(country.id); - expect(result.companies.edges.length).toEqual(2); + }); + expect(result.length).toEqual(1); + result = result[0].node; + expect(result.objectId).toEqual(country.id); + expect(result.companies.edges.length).toEqual(2); - const { - data: { - countries: { edges: result2 }, - }, - } = await apolloClient.query({ - query: gql` - query findCountry($where: CountryWhereInput) { - countries(where: $where) { - edges { - node { - id - objectId - companies { - edges { - node { - id - objectId - name + const { + data: { + countries: { edges: result2 }, + }, + } = await apolloClient.query({ + query: gql` + query findCountry($where: CountryWhereInput) { + countries(where: $where) { + edges { + node { + id + objectId + companies { + edges { + node { + id + objectId + name + } } } } } } } - } - `, - variables: { - where: { - companies: { - have: { - OR: [ - { name: { equalTo: 'imACompany1' } }, - { name: { equalTo: 'imACompany2' } }, - ], + `, + variables: { + where: { + companies: { + have: { + OR: [ + { name: { equalTo: "imACompany1" } }, + { name: { equalTo: "imACompany2" } }, + ], + }, }, }, }, - }, - }); - expect(result2.length).toEqual(2); + }); + expect(result2.length).toEqual(2); - const { - data: { - countries: { edges: result3 }, - }, - } = await apolloClient.query({ - query: gql` - query findCountry($where: CountryWhereInput) { - countries(where: $where) { - edges { - node { - id - name + const { + data: { + countries: { edges: result3 }, + }, + } = await apolloClient.query({ + query: gql` + query findCountry($where: CountryWhereInput) { + countries(where: $where) { + edges { + node { + id + name + } } } } - } - `, - variables: { - where: { - companies: { exists: false }, + `, + variables: { + where: { + companies: { exists: false }, + }, }, - }, - }); - expect(result3.length).toEqual(1); - expect(result3[0].node.name).toEqual('imACountry3'); + }); + expect(result3.length).toEqual(1); + expect(result3[0].node.name).toEqual("imACountry3"); - const { - data: { - countries: { edges: result4 }, - }, - } = await apolloClient.query({ - query: gql` - query findCountry($where: CountryWhereInput) { - countries(where: $where) { - edges { - node { - id - name + const { + data: { + countries: { edges: result4 }, + }, + } = await apolloClient.query({ + query: gql` + query findCountry($where: CountryWhereInput) { + countries(where: $where) { + edges { + node { + id + name + } } } } - } - `, - variables: { - where: { - president: { exists: false }, + `, + variables: { + where: { + president: { exists: false }, + }, }, - }, - }); - expect(result4.length).toEqual(2); - const { - data: { - countries: { edges: result5 }, - }, - } = await apolloClient.query({ - query: gql` - query findCountry($where: CountryWhereInput) { - countries(where: $where) { - edges { - node { - id - name + }); + expect(result4.length).toEqual(2); + const { + data: { + countries: { edges: result5 }, + }, + } = await apolloClient.query({ + query: gql` + query findCountry($where: CountryWhereInput) { + countries(where: $where) { + edges { + node { + id + name + } } } } - } - `, - variables: { - where: { - president: { exists: true }, + `, + variables: { + where: { + president: { exists: true }, + }, }, - }, - }); - expect(result5.length).toEqual(1); - const { - data: { - countries: { edges: result6 }, - }, - } = await apolloClient.query({ - query: gql` - query findCountry($where: CountryWhereInput) { - countries(where: $where) { - edges { - node { - id - objectId - name + }); + expect(result5.length).toEqual(1); + const { + data: { + countries: { edges: result6 }, + }, + } = await apolloClient.query({ + query: gql` + query findCountry($where: CountryWhereInput) { + countries(where: $where) { + edges { + node { + id + objectId + name + } } } } - } - `, - variables: { - where: { - companies: { - haveNot: { - OR: [ - { name: { equalTo: 'imACompany1' } }, - { name: { equalTo: 'imACompany2' } }, - ], + `, + variables: { + where: { + companies: { + haveNot: { + OR: [ + { name: { equalTo: "imACompany1" } }, + { name: { equalTo: "imACompany2" } }, + ], + }, }, }, }, - }, - }); - expect(result6.length).toEqual(1); - expect(result6.length).toEqual(1); - expect(result6[0].node.name).toEqual('imACountry3'); - }); + }); + expect(result6.length).toEqual(1); + expect(result6.length).toEqual(1); + expect(result6[0].node.name).toEqual("imACountry3"); + } + ); - it('should support files', async () => { + it("should support files", async () => { try { parseServer = await global.reconfigureServer({ - publicServerURL: 'http://localhost:13377/parse', + publicServerURL: "http://localhost:13377/parse", }); await createGQLFromParseServer(parseServer); const body = new FormData(); body.append( - 'operations', + "operations", JSON.stringify({ query: ` mutation CreateFile($input: CreateFileInput!) { @@ -9346,14 +9999,17 @@ describe('ParseGraphQLServer', () => { }, }) ); - body.append('map', JSON.stringify({ 1: ['variables.input.upload'] })); - body.append('1', 'My File Content', { - filename: 'myFileName.txt', - contentType: 'text/plain', + body.append( + "map", + JSON.stringify({ 1: ["variables.input.upload"] }) + ); + body.append("1", "My File Content", { + filename: "myFileName.txt", + contentType: "text/plain", }); - let res = await fetch('http://localhost:13377/graphql', { - method: 'POST', + let res = await fetch("http://localhost:13377/graphql", { + method: "POST", headers, body, }); @@ -9373,19 +10029,21 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass(input: { name: "SomeClass", schemaFields: $schemaFields }) { + createClass( + input: { name: "SomeClass", schemaFields: $schemaFields } + ) { clientMutationId } } `, variables: { schemaFields: { - addFiles: [{ name: 'someField' }], + addFiles: [{ name: "someField" }], }, }, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); @@ -9394,7 +10052,7 @@ describe('ParseGraphQLServer', () => { const body2 = new FormData(); body2.append( - 'operations', + "operations", JSON.stringify({ query: ` mutation CreateSomeObject( @@ -9446,7 +10104,7 @@ describe('ParseGraphQLServer', () => { file: { name: someFieldObjectValue.name, url: someFieldObjectValue.url, - __type: 'File', + __type: "File", }, }, }, @@ -9456,40 +10114,43 @@ describe('ParseGraphQLServer', () => { }, }) ); - body2.append('map', JSON.stringify({ 1: ['variables.fields3.someField.upload'] })); - body2.append('1', 'My File Content', { - filename: 'myFileName.txt', - contentType: 'text/plain', + body2.append( + "map", + JSON.stringify({ 1: ["variables.fields3.someField.upload"] }) + ); + body2.append("1", "My File Content", { + filename: "myFileName.txt", + contentType: "text/plain", }); - res = await fetch('http://localhost:13377/graphql', { - method: 'POST', + res = await fetch("http://localhost:13377/graphql", { + method: "POST", headers, body: body2, }); expect(res.status).toEqual(200); const result2 = JSON.parse(await res.text()); - expect(result2.data.createSomeClass1.someClass.someField.name).toEqual( - jasmine.stringMatching(/_myFileName.txt$/) - ); - expect(result2.data.createSomeClass1.someClass.someField.url).toEqual( - jasmine.stringMatching(/_myFileName.txt$/) - ); - expect(result2.data.createSomeClass2.someClass.someField.name).toEqual( - jasmine.stringMatching(/_myFileName.txt$/) - ); - expect(result2.data.createSomeClass2.someClass.someField.url).toEqual( - jasmine.stringMatching(/_myFileName.txt$/) - ); - expect(result2.data.createSomeClass3.someClass.someField.name).toEqual( - jasmine.stringMatching(/_myFileName.txt$/) - ); - expect(result2.data.createSomeClass3.someClass.someField.url).toEqual( - jasmine.stringMatching(/_myFileName.txt$/) - ); + expect( + result2.data.createSomeClass1.someClass.someField.name + ).toEqual(jasmine.stringMatching(/_myFileName.txt$/)); + expect( + result2.data.createSomeClass1.someClass.someField.url + ).toEqual(jasmine.stringMatching(/_myFileName.txt$/)); + expect( + result2.data.createSomeClass2.someClass.someField.name + ).toEqual(jasmine.stringMatching(/_myFileName.txt$/)); + expect( + result2.data.createSomeClass2.someClass.someField.url + ).toEqual(jasmine.stringMatching(/_myFileName.txt$/)); + expect( + result2.data.createSomeClass3.someClass.someField.name + ).toEqual(jasmine.stringMatching(/_myFileName.txt$/)); + expect( + result2.data.createSomeClass3.someClass.someField.url + ).toEqual(jasmine.stringMatching(/_myFileName.txt$/)); - const schema = await new Parse.Schema('SomeClass').get(); - expect(schema.fields.someField.type).toEqual('File'); + const schema = await new Parse.Schema("SomeClass").get(); + expect(schema.fields.someField.type).toEqual("File"); const getResult = await apolloClient.query({ query: gql` @@ -9500,7 +10161,9 @@ describe('ParseGraphQLServer', () => { url } } - findSomeClass1: someClasses(where: { someField: { exists: true } }) { + findSomeClass1: someClasses( + where: { someField: { exists: true } } + ) { edges { node { someField { @@ -9510,7 +10173,9 @@ describe('ParseGraphQLServer', () => { } } } - findSomeClass2: someClasses(where: { someField: { exists: true } }) { + findSomeClass2: someClasses( + where: { someField: { exists: true } } + ) { edges { node { someField { @@ -9527,7 +10192,7 @@ describe('ParseGraphQLServer', () => { }, }); - expect(typeof getResult.data.someClass.someField).toEqual('object'); + expect(typeof getResult.data.someClass.someField).toEqual("object"); expect(getResult.data.someClass.someField.name).toEqual( result.data.createFile.fileInfo.name ); @@ -9540,12 +10205,14 @@ describe('ParseGraphQLServer', () => { res = await fetch(getResult.data.someClass.someField.url); expect(res.status).toEqual(200); - expect(await res.text()).toEqual('My File Content'); + expect(await res.text()).toEqual("My File Content"); const mutationResult = await apolloClient.mutate({ mutation: gql` mutation UnlinkFile($id: ID!) { - updateSomeClass(input: { id: $id, fields: { someField: null } }) { + updateSomeClass( + input: { id: $id, fields: { someField: null } } + ) { someClass { someField { name @@ -9559,28 +10226,34 @@ describe('ParseGraphQLServer', () => { id: result2.data.createSomeClass3.someClass.id, }, }); - expect(mutationResult.data.updateSomeClass.someClass.someField).toEqual(null); + expect( + mutationResult.data.updateSomeClass.someClass.someField + ).toEqual(null); } catch (e) { handleError(e); } }); - it('should support files on required file', async () => { + it("should support files on required file", async () => { try { parseServer = await global.reconfigureServer({ - publicServerURL: 'http://localhost:13377/parse', + publicServerURL: "http://localhost:13377/parse", }); await createGQLFromParseServer(parseServer); - const schemaController = await parseServer.config.databaseController.loadSchema(); - await schemaController.addClassIfNotExists('SomeClassWithRequiredFile', { - someField: { type: 'File', required: true }, - }); + const schemaController = + await parseServer.config.databaseController.loadSchema(); + await schemaController.addClassIfNotExists( + "SomeClassWithRequiredFile", + { + someField: { type: "File", required: true }, + } + ); await resetGraphQLCache(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); const body = new FormData(); body.append( - 'operations', + "operations", JSON.stringify({ query: ` mutation CreateSomeObject( @@ -9606,14 +10279,17 @@ describe('ParseGraphQLServer', () => { }, }) ); - body.append('map', JSON.stringify({ 1: ['variables.fields.someField.upload'] })); - body.append('1', 'My File Content', { - filename: 'myFileName.txt', - contentType: 'text/plain', + body.append( + "map", + JSON.stringify({ 1: ["variables.fields.someField.upload"] }) + ); + body.append("1", "My File Content", { + filename: "myFileName.txt", + contentType: "text/plain", }); - const res = await fetch('http://localhost:13377/graphql', { - method: 'POST', + const res = await fetch("http://localhost:13377/graphql", { + method: "POST", headers, body, }); @@ -9621,30 +10297,32 @@ describe('ParseGraphQLServer', () => { const resText = await res.text(); const result = JSON.parse(resText); expect( - result.data.createSomeClassWithRequiredFile.someClassWithRequiredFile.someField.name + result.data.createSomeClassWithRequiredFile + .someClassWithRequiredFile.someField.name ).toEqual(jasmine.stringMatching(/_myFileName.txt$/)); expect( - result.data.createSomeClassWithRequiredFile.someClassWithRequiredFile.someField.url + result.data.createSomeClassWithRequiredFile + .someClassWithRequiredFile.someField.url ).toEqual(jasmine.stringMatching(/_myFileName.txt$/)); } catch (e) { handleError(e); } }); - it('should support file upload for on fly creation through pointer and relation', async () => { + it("should support file upload for on fly creation through pointer and relation", async () => { parseServer = await global.reconfigureServer({ - publicServerURL: 'http://localhost:13377/parse', + publicServerURL: "http://localhost:13377/parse", }); await createGQLFromParseServer(parseServer); - const schema = new Parse.Schema('SomeClass'); - schema.addFile('someFileField'); - schema.addPointer('somePointerField', 'SomeClass'); - schema.addRelation('someRelationField', 'SomeClass'); + const schema = new Parse.Schema("SomeClass"); + schema.addFile("someFileField"); + schema.addPointer("somePointerField", "SomeClass"); + schema.addRelation("someRelationField", "SomeClass"); await schema.save(); const body = new FormData(); body.append( - 'operations', + "operations", JSON.stringify({ query: ` mutation UploadFiles( @@ -9701,53 +10379,59 @@ describe('ParseGraphQLServer', () => { }) ); body.append( - 'map', + "map", JSON.stringify({ - 1: ['variables.fields.someFileField.upload'], - 2: ['variables.fields.somePointerField.createAndLink.someFileField.upload'], - 3: ['variables.fields.someRelationField.createAndAdd.0.someFileField.upload'], + 1: ["variables.fields.someFileField.upload"], + 2: [ + "variables.fields.somePointerField.createAndLink.someFileField.upload", + ], + 3: [ + "variables.fields.someRelationField.createAndAdd.0.someFileField.upload", + ], }) ); - body.append('1', 'My File Content someFileField', { - filename: 'someFileField.txt', - contentType: 'text/plain', + body.append("1", "My File Content someFileField", { + filename: "someFileField.txt", + contentType: "text/plain", }); - body.append('2', 'My File Content somePointerField', { - filename: 'somePointerField.txt', - contentType: 'text/plain', + body.append("2", "My File Content somePointerField", { + filename: "somePointerField.txt", + contentType: "text/plain", }); - body.append('3', 'My File Content someRelationField', { - filename: 'someRelationField.txt', - contentType: 'text/plain', + body.append("3", "My File Content someRelationField", { + filename: "someRelationField.txt", + contentType: "text/plain", }); - const res = await fetch('http://localhost:13377/graphql', { - method: 'POST', + const res = await fetch("http://localhost:13377/graphql", { + method: "POST", headers, body, }); expect(res.status).toEqual(200); const result = await res.json(); - expect(result.data.createSomeClass.someClass.someFileField.name).toEqual( - jasmine.stringMatching(/_someFileField.txt$/) - ); - expect(result.data.createSomeClass.someClass.somePointerField.someFileField.name).toEqual( - jasmine.stringMatching(/_somePointerField.txt$/) - ); expect( - result.data.createSomeClass.someClass.someRelationField.edges[0].node.someFileField.name + result.data.createSomeClass.someClass.someFileField.name + ).toEqual(jasmine.stringMatching(/_someFileField.txt$/)); + expect( + result.data.createSomeClass.someClass.somePointerField.someFileField + .name + ).toEqual(jasmine.stringMatching(/_somePointerField.txt$/)); + expect( + result.data.createSomeClass.someClass.someRelationField.edges[0] + .node.someFileField.name ).toEqual(jasmine.stringMatching(/_someRelationField.txt$/)); }); - it('should support files and add extension from mimetype', async () => { + it("should support files and add extension from mimetype", async () => { try { parseServer = await global.reconfigureServer({ - publicServerURL: 'http://localhost:13377/parse', + publicServerURL: "http://localhost:13377/parse", }); await createGQLFromParseServer(parseServer); const body = new FormData(); body.append( - 'operations', + "operations", JSON.stringify({ query: ` mutation CreateFile($input: CreateFileInput!) { @@ -9766,15 +10450,18 @@ describe('ParseGraphQLServer', () => { }, }) ); - body.append('map', JSON.stringify({ 1: ['variables.input.upload'] })); - body.append('1', 'My File Content', { + body.append( + "map", + JSON.stringify({ 1: ["variables.input.upload"] }) + ); + body.append("1", "My File Content", { // No extension, the system should add it from mimetype - filename: 'myFileName', - contentType: 'text/plain', + filename: "myFileName", + contentType: "text/plain", }); - const res = await fetch('http://localhost:13377/graphql', { - method: 'POST', + const res = await fetch("http://localhost:13377/graphql", { + method: "POST", headers, body, }); @@ -9793,10 +10480,10 @@ describe('ParseGraphQLServer', () => { } }); - it('should not upload if file is too large', async () => { + it("should not upload if file is too large", async () => { const body = new FormData(); body.append( - 'operations', + "operations", JSON.stringify({ query: ` mutation CreateFile($input: CreateFileInput!) { @@ -9815,19 +10502,22 @@ describe('ParseGraphQLServer', () => { }, }) ); - body.append('map', JSON.stringify({ 1: ['variables.input.upload'] })); + body.append("map", JSON.stringify({ 1: ["variables.input.upload"] })); body.append( - '1', + "1", // In this test file parse server is setup with 1kb limit - Buffer.alloc(parseGraphQLServer._transformMaxUploadSizeToBytes('2kb'), 1), + Buffer.alloc( + parseGraphQLServer._transformMaxUploadSizeToBytes("2kb"), + 1 + ), { - filename: 'myFileName.txt', - contentType: 'text/plain', + filename: "myFileName.txt", + contentType: "text/plain", } ); - const res = await fetch('http://localhost:13377/graphql', { - method: 'POST', + const res = await fetch("http://localhost:13377/graphql", { + method: "POST", headers, body, }); @@ -9835,40 +10525,42 @@ describe('ParseGraphQLServer', () => { const result = JSON.parse(await res.text()); expect(res.status).toEqual(200); expect(result.errors[0].message).toEqual( - 'File truncated as it exceeds the 1024 byte size limit.' + "File truncated as it exceeds the 1024 byte size limit." ); }); - it('should support object values', async () => { + it("should support object values", async () => { try { const someObjectFieldValue = { - foo: { bar: 'baz' }, + foo: { bar: "baz" }, number: 10, }; await apolloClient.mutate({ mutation: gql` mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass(input: { name: "SomeClass", schemaFields: $schemaFields }) { + createClass( + input: { name: "SomeClass", schemaFields: $schemaFields } + ) { clientMutationId } } `, variables: { schemaFields: { - addObjects: [{ name: 'someObjectField' }], + addObjects: [{ name: "someObjectField" }], }, }, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const schema = await new Parse.Schema('SomeClass').get(); - expect(schema.fields.someObjectField.type).toEqual('Object'); + const schema = await new Parse.Schema("SomeClass").get(); + expect(schema.fields.someObjectField.type).toEqual("Object"); const createResult = await apolloClient.mutate({ mutation: gql` @@ -9889,10 +10581,10 @@ describe('ParseGraphQLServer', () => { const where = { someObjectField: { - equalTo: { key: 'foo.bar', value: 'baz' }, - notEqualTo: { key: 'foo.bar', value: 'bat' }, - greaterThan: { key: 'number', value: 9 }, - lessThan: { key: 'number', value: 11 }, + equalTo: { key: "foo.bar", value: "baz" }, + notEqualTo: { key: "foo.bar", value: "bat" }, + greaterThan: { key: "number", value: 9 }, + lessThan: { key: "number", value: 11 }, }, }; const queryResult = await apolloClient.query({ @@ -9921,18 +10613,20 @@ describe('ParseGraphQLServer', () => { const { someClass: getResult, someClasses } = queryResult.data; const { someObjectField } = getResult; - expect(typeof someObjectField).toEqual('object'); + expect(typeof someObjectField).toEqual("object"); expect(someObjectField).toEqual(someObjectFieldValue); // Checks class query results expect(someClasses.edges.length).toEqual(1); - expect(someClasses.edges[0].node.someObjectField).toEqual(someObjectFieldValue); + expect(someClasses.edges[0].node.someObjectField).toEqual( + someObjectFieldValue + ); } catch (e) { handleError(e); } }); - it('should support where argument on object field that contains false boolean value or 0 number value', async () => { + it("should support where argument on object field that contains false boolean value or 0 number value", async () => { try { const someObjectFieldValue1 = { foo: { bar: true, baz: 100 }, @@ -9942,38 +10636,38 @@ describe('ParseGraphQLServer', () => { foo: { bar: false, baz: 0 }, }; - const object1 = new Parse.Object('SomeClass'); + const object1 = new Parse.Object("SomeClass"); await object1.save({ someObjectField: someObjectFieldValue1, }); - const object2 = new Parse.Object('SomeClass'); + const object2 = new Parse.Object("SomeClass"); await object2.save({ someObjectField: someObjectFieldValue2, }); const whereToObject1 = { someObjectField: { - equalTo: { key: 'foo.bar', value: true }, - notEqualTo: { key: 'foo.baz', value: 0 }, + equalTo: { key: "foo.bar", value: true }, + notEqualTo: { key: "foo.baz", value: 0 }, }, }; const whereToObject2 = { someObjectField: { - notEqualTo: { key: 'foo.bar', value: true }, - equalTo: { key: 'foo.baz', value: 0 }, + notEqualTo: { key: "foo.bar", value: true }, + equalTo: { key: "foo.baz", value: 0 }, }, }; const whereToAll = { someObjectField: { - lessThan: { key: 'foo.baz', value: 101 }, + lessThan: { key: "foo.baz", value: 101 }, }, }; const whereToNone = { someObjectField: { - notEqualTo: { key: 'foo.bar', value: true }, - equalTo: { key: 'foo.baz', value: 1 }, + notEqualTo: { key: "foo.bar", value: true }, + equalTo: { key: "foo.baz", value: 1 }, }, }; @@ -10039,16 +10733,21 @@ describe('ParseGraphQLServer', () => { }, }); - const { obj1, obj2, onlyObj1, onlyObj2, all, none } = queryResult.data; + const { obj1, obj2, onlyObj1, onlyObj2, all, none } = + queryResult.data; expect(obj1.someObjectField).toEqual(someObjectFieldValue1); expect(obj2.someObjectField).toEqual(someObjectFieldValue2); // Checks class query results expect(onlyObj1.edges.length).toEqual(1); - expect(onlyObj1.edges[0].node.someObjectField).toEqual(someObjectFieldValue1); + expect(onlyObj1.edges[0].node.someObjectField).toEqual( + someObjectFieldValue1 + ); expect(onlyObj2.edges.length).toEqual(1); - expect(onlyObj2.edges[0].node.someObjectField).toEqual(someObjectFieldValue2); + expect(onlyObj2.edges[0].node.someObjectField).toEqual( + someObjectFieldValue2 + ); expect(all.edges.length).toEqual(2); expect(none.edges.length).toEqual(0); } catch (e) { @@ -10056,15 +10755,15 @@ describe('ParseGraphQLServer', () => { } }); - it('should support object composed queries', async () => { + it("should support object composed queries", async () => { try { const someObjectFieldValue1 = { - lorem: 'ipsum', + lorem: "ipsum", number: 10, }; const someObjectFieldValue2 = { foo: { - test: 'bar', + test: "bar", }, number: 10, }; @@ -10075,7 +10774,9 @@ describe('ParseGraphQLServer', () => { createClass( input: { name: "SomeClass" - schemaFields: { addObjects: [{ name: "someObjectField" }] } + schemaFields: { + addObjects: [{ name: "someObjectField" }] + } } ) { clientMutationId @@ -10084,7 +10785,7 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); @@ -10123,24 +10824,24 @@ describe('ParseGraphQLServer', () => { AND: [ { someObjectField: { - greaterThan: { key: 'number', value: 9 }, + greaterThan: { key: "number", value: 9 }, }, }, { someObjectField: { - lessThan: { key: 'number', value: 11 }, + lessThan: { key: "number", value: 11 }, }, }, { OR: [ { someObjectField: { - equalTo: { key: 'lorem', value: 'ipsum' }, + equalTo: { key: "lorem", value: "ipsum" }, }, }, { someObjectField: { - equalTo: { key: 'foo.test', value: 'bar' }, + equalTo: { key: "foo.test", value: "bar" }, }, }, ], @@ -10172,44 +10873,54 @@ describe('ParseGraphQLServer', () => { const { edges } = someClasses; expect(edges.length).toEqual(2); expect( - edges.find(result => result.node.id === create1.someClass.id).node.someObjectField + edges.find(result => result.node.id === create1.someClass.id).node + .someObjectField ).toEqual(someObjectFieldValue1); expect( - edges.find(result => result.node.id === create2.someClass.id).node.someObjectField + edges.find(result => result.node.id === create2.someClass.id).node + .someObjectField ).toEqual(someObjectFieldValue2); } catch (e) { handleError(e); } }); - it('should support array values', async () => { + it("should support array values", async () => { try { - const someArrayFieldValue = [1, 'foo', ['bar'], { lorem: 'ipsum' }, true]; + const someArrayFieldValue = [ + 1, + "foo", + ["bar"], + { lorem: "ipsum" }, + true, + ]; await apolloClient.mutate({ mutation: gql` mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass(input: { name: "SomeClass", schemaFields: $schemaFields }) { + createClass( + input: { name: "SomeClass", schemaFields: $schemaFields } + ) { clientMutationId } } `, variables: { schemaFields: { - addArrays: [{ name: 'someArrayField' }], + addArrays: [{ name: "someArrayField" }], }, }, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const schema = await new Parse.Schema('SomeClass').get(); - expect(schema.fields.someArrayField.type).toEqual('Array'); + const schema = await new Parse.Schema("SomeClass").get(); + expect(schema.fields.someArrayField.type).toEqual("Array"); const createResult = await apolloClient.mutate({ mutation: gql` @@ -10259,19 +10970,21 @@ describe('ParseGraphQLServer', () => { const { someArrayField } = getResult.data.someClass; expect(Array.isArray(someArrayField)).toBeTruthy(); - expect(someArrayField.map(element => element.value)).toEqual(someArrayFieldValue); + expect(someArrayField.map(element => element.value)).toEqual( + someArrayFieldValue + ); expect(getResult.data.someClasses.edges.length).toEqual(1); } catch (e) { handleError(e); } }); - it('should support undefined array', async () => { - const schema = await new Parse.Schema('SomeClass'); - schema.addArray('someArray'); + it("should support undefined array", async () => { + const schema = await new Parse.Schema("SomeClass"); + schema.addArray("someArray"); await schema.save(); - const obj = new Parse.Object('SomeClass'); + const obj = new Parse.Object("SomeClass"); await obj.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -10296,7 +11009,7 @@ describe('ParseGraphQLServer', () => { expect(getResult.data.someClass.someArray).toEqual(null); }); - it('should support null values', async () => { + it("should support null values", async () => { try { await apolloClient.mutate({ mutation: gql` @@ -10305,7 +11018,10 @@ describe('ParseGraphQLServer', () => { input: { name: "SomeClass" schemaFields: { - addStrings: [{ name: "someStringField" }, { name: "someNullField" }] + addStrings: [ + { name: "someStringField" } + { name: "someNullField" } + ] addNumbers: [{ name: "someNumberField" }] addBooleans: [{ name: "someBooleanField" }] addObjects: [{ name: "someObjectField" }] @@ -10318,7 +11034,7 @@ describe('ParseGraphQLServer', () => { `, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); @@ -10337,10 +11053,10 @@ describe('ParseGraphQLServer', () => { `, variables: { fields: { - someStringField: 'some string', + someStringField: "some string", someNumberField: 123, someBooleanField: true, - someObjectField: { someField: 'some value' }, + someObjectField: { someField: "some value" }, someNullField: null, }, }, @@ -10348,187 +11064,23 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` - mutation UpdateSomeObject($id: ID!, $fields: UpdateSomeClassFieldsInput) { - updateSomeClass(input: { id: $id, fields: $fields }) { - clientMutationId - } - } - `, - variables: { - id: createResult.data.createSomeClass.someClass.id, - fields: { - someStringField: null, - someNumberField: null, - someBooleanField: null, - someObjectField: null, - someNullField: 'now it has a string', - }, - }, - }); - - const getResult = await apolloClient.query({ - query: gql` - query GetSomeObject($id: ID!) { - someClass(id: $id) { - someStringField - someNumberField - someBooleanField - someObjectField - someNullField - } - } - `, - variables: { - id: createResult.data.createSomeClass.someClass.id, - }, - }); - - expect(getResult.data.someClass.someStringField).toBeFalsy(); - expect(getResult.data.someClass.someNumberField).toBeFalsy(); - expect(getResult.data.someClass.someBooleanField).toBeFalsy(); - expect(getResult.data.someClass.someObjectField).toBeFalsy(); - expect(getResult.data.someClass.someNullField).toEqual('now it has a string'); - } catch (e) { - handleError(e); - } - }); - - it_id('43303db7-c5a7-4bc0-91c3-57e03fffa225')(it)('should support Bytes', async () => { - try { - const someFieldValue = 'aGVsbG8gd29ybGQ='; - - await apolloClient.mutate({ - mutation: gql` - mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass(input: { name: "SomeClass", schemaFields: $schemaFields }) { - clientMutationId - } - } - `, - variables: { - schemaFields: { - addBytes: [{ name: 'someField' }], - }, - }, - context: { - headers: { - 'X-Parse-Master-Key': 'test', - }, - }, - }); - - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - - const schema = await new Parse.Schema('SomeClass').get(); - expect(schema.fields.someField.type).toEqual('Bytes'); - - const createResult = await apolloClient.mutate({ - mutation: gql` - mutation CreateSomeObject( - $fields1: CreateSomeClassFieldsInput - $fields2: CreateSomeClassFieldsInput - ) { - createSomeClass1: createSomeClass(input: { fields: $fields1 }) { - someClass { - id - } - } - createSomeClass2: createSomeClass(input: { fields: $fields2 }) { - someClass { - id - } - } - } - `, - variables: { - fields1: { - someField: someFieldValue, - }, - fields2: { - someField: someFieldValue, - }, - }, - }); - - const getResult = await apolloClient.query({ - query: gql` - query GetSomeObject($id: ID!, $someFieldValue: Bytes) { - someClass(id: $id) { - someField - } - someClasses(where: { someField: { equalTo: $someFieldValue } }) { - edges { - node { - id - someField - } - } - } - } - `, - variables: { - id: createResult.data.createSomeClass1.someClass.id, - someFieldValue, - }, - }); - - expect(typeof getResult.data.someClass.someField).toEqual('string'); - expect(getResult.data.someClass.someField).toEqual(someFieldValue); - expect(getResult.data.someClasses.edges.length).toEqual(2); - } catch (e) { - handleError(e); - } - }); - - it_id('6a253e47-6959-4427-b841-c0c1fa77cf01')(it)('should support Geo Points', async () => { - try { - const someFieldValue = { - __typename: 'GeoPoint', - latitude: 45, - longitude: 45, - }; - - await apolloClient.mutate({ - mutation: gql` - mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass(input: { name: "SomeClass", schemaFields: $schemaFields }) { - clientMutationId - } - } - `, - variables: { - schemaFields: { - addGeoPoint: { name: 'someField' }, - }, - }, - context: { - headers: { - 'X-Parse-Master-Key': 'test', - }, - }, - }); - - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - - const schema = await new Parse.Schema('SomeClass').get(); - expect(schema.fields.someField.type).toEqual('GeoPoint'); - - const createResult = await apolloClient.mutate({ - mutation: gql` - mutation CreateSomeObject($fields: CreateSomeClassFieldsInput) { - createSomeClass(input: { fields: $fields }) { - someClass { - id - } + mutation UpdateSomeObject( + $id: ID! + $fields: UpdateSomeClassFieldsInput + ) { + updateSomeClass(input: { id: $id, fields: $fields }) { + clientMutationId } } `, variables: { + id: createResult.data.createSomeClass.someClass.id, fields: { - someField: { - latitude: someFieldValue.latitude, - longitude: someFieldValue.longitude, - }, + someStringField: null, + someNumberField: null, + someBooleanField: null, + someObjectField: null, + someNullField: "now it has a string", }, }, }); @@ -10537,21 +11089,11 @@ describe('ParseGraphQLServer', () => { query: gql` query GetSomeObject($id: ID!) { someClass(id: $id) { - someField { - latitude - longitude - } - } - someClasses(where: { someField: { exists: true } }) { - edges { - node { - id - someField { - latitude - longitude - } - } - } + someStringField + someNumberField + someBooleanField + someObjectField + someNullField } } `, @@ -10560,82 +11102,301 @@ describe('ParseGraphQLServer', () => { }, }); - expect(typeof getResult.data.someClass.someField).toEqual('object'); - expect(getResult.data.someClass.someField).toEqual(someFieldValue); - expect(getResult.data.someClasses.edges.length).toEqual(1); + expect(getResult.data.someClass.someStringField).toBeFalsy(); + expect(getResult.data.someClass.someNumberField).toBeFalsy(); + expect(getResult.data.someClass.someBooleanField).toBeFalsy(); + expect(getResult.data.someClass.someObjectField).toBeFalsy(); + expect(getResult.data.someClass.someNullField).toEqual( + "now it has a string" + ); + } catch (e) { + handleError(e); + } + }); - const getGeoWhere = await apolloClient.query({ - query: gql` - query GeoQuery($latitude: Float!, $longitude: Float!) { - nearSphere: someClasses( - where: { - someField: { nearSphere: { latitude: $latitude, longitude: $longitude } } + it_id("43303db7-c5a7-4bc0-91c3-57e03fffa225")(it)( + "should support Bytes", + async () => { + try { + const someFieldValue = "aGVsbG8gd29ybGQ="; + + await apolloClient.mutate({ + mutation: gql` + mutation CreateClass($schemaFields: SchemaFieldsInput) { + createClass( + input: { name: "SomeClass", schemaFields: $schemaFields } + ) { + clientMutationId } + } + `, + variables: { + schemaFields: { + addBytes: [{ name: "someField" }], + }, + }, + context: { + headers: { + "X-Parse-Master-Key": "test", + }, + }, + }); + + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + + const schema = await new Parse.Schema("SomeClass").get(); + expect(schema.fields.someField.type).toEqual("Bytes"); + + const createResult = await apolloClient.mutate({ + mutation: gql` + mutation CreateSomeObject( + $fields1: CreateSomeClassFieldsInput + $fields2: CreateSomeClassFieldsInput ) { - edges { - node { + createSomeClass1: createSomeClass( + input: { fields: $fields1 } + ) { + someClass { + id + } + } + createSomeClass2: createSomeClass( + input: { fields: $fields2 } + ) { + someClass { id } } } - geoWithin: someClasses( - where: { - someField: { - geoWithin: { - centerSphere: { - distance: 10 - center: { latitude: $latitude, longitude: $longitude } - } + `, + variables: { + fields1: { + someField: someFieldValue, + }, + fields2: { + someField: someFieldValue, + }, + }, + }); + + const getResult = await apolloClient.query({ + query: gql` + query GetSomeObject($id: ID!, $someFieldValue: Bytes) { + someClass(id: $id) { + someField + } + someClasses( + where: { someField: { equalTo: $someFieldValue } } + ) { + edges { + node { + id + someField } } } + } + `, + variables: { + id: createResult.data.createSomeClass1.someClass.id, + someFieldValue, + }, + }); + + expect(typeof getResult.data.someClass.someField).toEqual( + "string" + ); + expect(getResult.data.someClass.someField).toEqual( + someFieldValue + ); + expect(getResult.data.someClasses.edges.length).toEqual(2); + } catch (e) { + handleError(e); + } + } + ); + + it_id("6a253e47-6959-4427-b841-c0c1fa77cf01")(it)( + "should support Geo Points", + async () => { + try { + const someFieldValue = { + __typename: "GeoPoint", + latitude: 45, + longitude: 45, + }; + + await apolloClient.mutate({ + mutation: gql` + mutation CreateClass($schemaFields: SchemaFieldsInput) { + createClass( + input: { name: "SomeClass", schemaFields: $schemaFields } + ) { + clientMutationId + } + } + `, + variables: { + schemaFields: { + addGeoPoint: { name: "someField" }, + }, + }, + context: { + headers: { + "X-Parse-Master-Key": "test", + }, + }, + }); + + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + + const schema = await new Parse.Schema("SomeClass").get(); + expect(schema.fields.someField.type).toEqual("GeoPoint"); + + const createResult = await apolloClient.mutate({ + mutation: gql` + mutation CreateSomeObject( + $fields: CreateSomeClassFieldsInput ) { - edges { - node { + createSomeClass(input: { fields: $fields }) { + someClass { id } } } - within: someClasses( - where: { - someField: { - within: { - box: { - bottomLeft: { latitude: $latitude, longitude: $longitude } - upperRight: { latitude: $latitude, longitude: $longitude } + `, + variables: { + fields: { + someField: { + latitude: someFieldValue.latitude, + longitude: someFieldValue.longitude, + }, + }, + }, + }); + + const getResult = await apolloClient.query({ + query: gql` + query GetSomeObject($id: ID!) { + someClass(id: $id) { + someField { + latitude + longitude + } + } + someClasses(where: { someField: { exists: true } }) { + edges { + node { + id + someField { + latitude + longitude } } } } - ) { - edges { - node { - id + } + `, + variables: { + id: createResult.data.createSomeClass.someClass.id, + }, + }); + + expect(typeof getResult.data.someClass.someField).toEqual( + "object" + ); + expect(getResult.data.someClass.someField).toEqual( + someFieldValue + ); + expect(getResult.data.someClasses.edges.length).toEqual(1); + + const getGeoWhere = await apolloClient.query({ + query: gql` + query GeoQuery($latitude: Float!, $longitude: Float!) { + nearSphere: someClasses( + where: { + someField: { + nearSphere: { + latitude: $latitude + longitude: $longitude + } + } + } + ) { + edges { + node { + id + } + } + } + geoWithin: someClasses( + where: { + someField: { + geoWithin: { + centerSphere: { + distance: 10 + center: { + latitude: $latitude + longitude: $longitude + } + } + } + } + } + ) { + edges { + node { + id + } + } + } + within: someClasses( + where: { + someField: { + within: { + box: { + bottomLeft: { + latitude: $latitude + longitude: $longitude + } + upperRight: { + latitude: $latitude + longitude: $longitude + } + } + } + } + } + ) { + edges { + node { + id + } } } } - } - `, - variables: { - latitude: 45, - longitude: 45, - }, - }); - expect(getGeoWhere.data.nearSphere.edges[0].node.id).toEqual( - createResult.data.createSomeClass.someClass.id - ); - expect(getGeoWhere.data.geoWithin.edges[0].node.id).toEqual( - createResult.data.createSomeClass.someClass.id - ); - expect(getGeoWhere.data.within.edges[0].node.id).toEqual( - createResult.data.createSomeClass.someClass.id - ); - } catch (e) { - handleError(e); + `, + variables: { + latitude: 45, + longitude: 45, + }, + }); + expect(getGeoWhere.data.nearSphere.edges[0].node.id).toEqual( + createResult.data.createSomeClass.someClass.id + ); + expect(getGeoWhere.data.geoWithin.edges[0].node.id).toEqual( + createResult.data.createSomeClass.someClass.id + ); + expect(getGeoWhere.data.within.edges[0].node.id).toEqual( + createResult.data.createSomeClass.someClass.id + ); + } catch (e) { + handleError(e); + } } - }); + ); - it('should support Polygons', async () => { + it("should support Polygons", async () => { try { const somePolygonFieldValue = [ [44, 45], @@ -10650,27 +11411,29 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass(input: { name: "SomeClass", schemaFields: $schemaFields }) { + createClass( + input: { name: "SomeClass", schemaFields: $schemaFields } + ) { clientMutationId } } `, variables: { schemaFields: { - addPolygons: [{ name: 'somePolygonField' }], + addPolygons: [{ name: "somePolygonField" }], }, }, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const schema = await new Parse.Schema('SomeClass').get(); - expect(schema.fields.somePolygonField.type).toEqual('Polygon'); + const schema = await new Parse.Schema("SomeClass").get(); + expect(schema.fields.somePolygonField.type).toEqual("Polygon"); const createResult = await apolloClient.mutate({ mutation: gql` @@ -10716,18 +11479,24 @@ describe('ParseGraphQLServer', () => { }, }); - expect(typeof getResult.data.someClass.somePolygonField).toEqual('object'); + expect(typeof getResult.data.someClass.somePolygonField).toEqual( + "object" + ); expect(getResult.data.someClass.somePolygonField).toEqual( somePolygonFieldValue.map(geoPoint => ({ ...geoPoint, - __typename: 'GeoPoint', + __typename: "GeoPoint", })) ); expect(getResult.data.someClasses.edges.length).toEqual(1); const getIntersect = await apolloClient.query({ query: gql` query IntersectQuery($point: GeoPointInput!) { - someClasses(where: { somePolygonField: { geoIntersects: { point: $point } } }) { + someClasses( + where: { + somePolygonField: { geoIntersects: { point: $point } } + } + ) { edges { node { id @@ -10753,22 +11522,22 @@ describe('ParseGraphQLServer', () => { } }); - it_only_db('mongo')('should support bytes values', async () => { - const SomeClass = Parse.Object.extend('SomeClass'); + it_only_db("mongo")("should support bytes values", async () => { + const SomeClass = Parse.Object.extend("SomeClass"); const someClass = new SomeClass(); - someClass.set('someField', { - __type: 'Bytes', - base64: 'foo', + someClass.set("someField", { + __type: "Bytes", + base64: "foo", }); await someClass.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const schema = await new Parse.Schema('SomeClass').get(); - expect(schema.fields.someField.type).toEqual('Bytes'); + const schema = await new Parse.Schema("SomeClass").get(); + expect(schema.fields.someField.type).toEqual("Bytes"); const someFieldValue = { - __type: 'Bytes', - base64: 'bytesContent', + __type: "Bytes", + base64: "bytesContent", }; const createResult = await apolloClient.mutate({ @@ -10801,16 +11570,21 @@ describe('ParseGraphQLServer', () => { }, }); - expect(getResult.data.someClass.someField).toEqual(someFieldValue.base64); + expect(getResult.data.someClass.someField).toEqual( + someFieldValue.base64 + ); const updatedSomeFieldValue = { - __type: 'Bytes', - base64: 'newBytesContent', + __type: "Bytes", + base64: "newBytesContent", }; const updatedResult = await apolloClient.mutate({ mutation: gql` - mutation UpdateSomeObject($id: ID!, $fields: UpdateSomeClassFieldsInput) { + mutation UpdateSomeObject( + $id: ID! + $fields: UpdateSomeClassFieldsInput + ) { updateSomeClass(input: { id: $id, fields: $fields }) { someClass { updatedAt @@ -10851,15 +11625,17 @@ describe('ParseGraphQLServer', () => { }); const findResults = findResult.data.someClasses.edges; expect(findResults.length).toBe(1); - expect(findResults[0].node.id).toBe(createResult.data.createSomeClass.someClass.id); + expect(findResults[0].node.id).toBe( + createResult.data.createSomeClass.someClass.id + ); }); }); - describe('Special Classes', () => { - it('should support User class', async () => { + describe("Special Classes", () => { + it("should support User class", async () => { const user = new Parse.User(); - user.setUsername('user1'); - user.setPassword('user1'); + user.setUsername("user1"); + user.setPassword("user1"); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); user.setACL(acl); @@ -10883,10 +11659,10 @@ describe('ParseGraphQLServer', () => { expect(getResult.data.get.objectId).toEqual(user.id); }); - it('should support Installation class', async () => { + it("should support Installation class", async () => { const installation = new Parse.Installation(); await installation.save({ - deviceType: 'foo', + deviceType: "foo", }); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -10907,10 +11683,10 @@ describe('ParseGraphQLServer', () => { expect(getResult.data.get.objectId).toEqual(installation.id); }); - it('should support Role class', async () => { + it("should support Role class", async () => { const roleACL = new Parse.ACL(); roleACL.setPublicReadAccess(true); - const role = new Parse.Role('MyRole', roleACL); + const role = new Parse.Role("MyRole", roleACL); await role.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -10931,10 +11707,10 @@ describe('ParseGraphQLServer', () => { expect(getResult.data.get.objectId).toEqual(role.id); }); - it('should support Session class', async () => { + it("should support Session class", async () => { const user = new Parse.User(); - user.setUsername('user1'); - user.setPassword('user1'); + user.setUsername("user1"); + user.setPassword("user1"); await user.signUp(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -10954,7 +11730,7 @@ describe('ParseGraphQLServer', () => { }, context: { headers: { - 'X-Parse-Session-Token': session.getSessionToken(), + "X-Parse-Session-Token": session.getSessionToken(), }, }, }); @@ -10962,16 +11738,16 @@ describe('ParseGraphQLServer', () => { expect(getResult.data.get.objectId).toEqual(session.id); }); - it('should support Product class', async () => { - const Product = Parse.Object.extend('_Product'); + it("should support Product class", async () => { + const Product = Parse.Object.extend("_Product"); const product = new Product(); await product.save( { - productIdentifier: 'foo', - icon: new Parse.File('icon', ['foo']), + productIdentifier: "foo", + icon: new Parse.File("icon", ["foo"]), order: 1, - title: 'Foo', - subtitle: 'My product', + title: "Foo", + subtitle: "My product", }, { useMasterKey: true } ); @@ -10991,7 +11767,7 @@ describe('ParseGraphQLServer', () => { }, context: { headers: { - 'X-Parse-Master-Key': 'test', + "X-Parse-Master-Key": "test", }, }, }); @@ -11002,19 +11778,19 @@ describe('ParseGraphQLServer', () => { }); }); - describe('Custom API', () => { - describe('SDL based', () => { + describe("Custom API", () => { + describe("SDL based", () => { let httpServer; const headers = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Javascript-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-Javascript-Key": "test", }; let apolloClient; beforeEach(async () => { const expressApp = express(); httpServer = http.createServer(expressApp); parseGraphQLServer = new ParseGraphQLServer(parseServer, { - graphQLPath: '/graphql', + graphQLPath: "/graphql", graphQLCustomTypeDefs: gql` extend type Query { hello: String @resolve @@ -11026,9 +11802,11 @@ describe('ParseGraphQLServer', () => { `, }); parseGraphQLServer.applyGraphQL(expressApp); - await new Promise(resolve => httpServer.listen({ port: 13377 }, resolve)); + await new Promise(resolve => + httpServer.listen({ port: 13377 }, resolve) + ); const httpLink = await createUploadLink({ - uri: 'http://localhost:13377/graphql', + uri: "http://localhost:13377/graphql", fetch, headers, }); @@ -11037,7 +11815,7 @@ describe('ParseGraphQLServer', () => { cache: new InMemoryCache(), defaultOptions: { query: { - fetchPolicy: 'no-cache', + fetchPolicy: "no-cache", }, }, }); @@ -11047,9 +11825,9 @@ describe('ParseGraphQLServer', () => { await httpServer.close(); }); - it('can resolve a custom query using default function name', async () => { - Parse.Cloud.define('hello', async () => { - return 'Hello world!'; + it("can resolve a custom query using default function name", async () => { + Parse.Cloud.define("hello", async () => { + return "Hello world!"; }); const result = await apolloClient.query({ @@ -11060,12 +11838,12 @@ describe('ParseGraphQLServer', () => { `, }); - expect(result.data.hello).toEqual('Hello world!'); + expect(result.data.hello).toEqual("Hello world!"); }); it('can resolve a custom query using function name set by "to" argument', async () => { - Parse.Cloud.define('hello', async () => { - return 'Hello world!'; + Parse.Cloud.define("hello", async () => { + return "Hello world!"; }); const result = await apolloClient.query({ @@ -11076,30 +11854,31 @@ describe('ParseGraphQLServer', () => { `, }); - expect(result.data.hello2).toEqual('Hello world!'); + expect(result.data.hello2).toEqual("Hello world!"); }); - it('order option should continue working', async () => { - const schemaController = await parseServer.config.databaseController.loadSchema(); + it("order option should continue working", async () => { + const schemaController = + await parseServer.config.databaseController.loadSchema(); - await schemaController.addClassIfNotExists('SuperCar', { - engine: { type: 'String' }, - doors: { type: 'Number' }, - price: { type: 'String' }, - mileage: { type: 'Number' }, + await schemaController.addClassIfNotExists("SuperCar", { + engine: { type: "String" }, + doors: { type: "Number" }, + price: { type: "String" }, + mileage: { type: "Number" }, }); - await new Parse.Object('SuperCar').save({ - engine: 'petrol', + await new Parse.Object("SuperCar").save({ + engine: "petrol", doors: 3, - price: '£7500', + price: "£7500", mileage: 0, }); - await new Parse.Object('SuperCar').save({ - engine: 'petrol', + await new Parse.Object("SuperCar").save({ + engine: "petrol", doors: 3, - price: '£7500', + price: "£7500", mileage: 10000, }); @@ -11126,11 +11905,11 @@ describe('ParseGraphQLServer', () => { }); }); - describe('GraphQL Schema Based', () => { + describe("GraphQL Schema Based", () => { let httpServer; const headers = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Javascript-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-Javascript-Key": "test", }; let apolloClient; @@ -11138,26 +11917,26 @@ describe('ParseGraphQLServer', () => { const expressApp = express(); httpServer = http.createServer(expressApp); const TypeEnum = new GraphQLEnumType({ - name: 'TypeEnum', + name: "TypeEnum", values: { - human: { value: 'human' }, - robot: { value: 'robot' }, + human: { value: "human" }, + robot: { value: "robot" }, }, }); const TypeEnumWhereInput = new GraphQLInputObjectType({ - name: 'TypeEnumWhereInput', + name: "TypeEnumWhereInput", fields: { equalTo: { type: TypeEnum }, }, }); const SomeClass2WhereInput = new GraphQLInputObjectType({ - name: 'SomeClass2WhereInput', + name: "SomeClass2WhereInput", fields: { type: { type: TypeEnumWhereInput }, }, }); const SomeClassType = new GraphQLObjectType({ - name: 'SomeClass', + name: "SomeClass", fields: { nameUpperCase: { type: new GraphQLNonNull(GraphQLString), @@ -11166,21 +11945,21 @@ describe('ParseGraphQLServer', () => { type: { type: TypeEnum }, language: { type: new GraphQLEnumType({ - name: 'LanguageEnum', + name: "LanguageEnum", values: { - fr: { value: 'fr' }, - en: { value: 'en' }, + fr: { value: "fr" }, + en: { value: "en" }, }, }), - resolve: () => 'fr', + resolve: () => "fr", }, }, }), parseGraphQLServer = new ParseGraphQLServer(parseServer, { - graphQLPath: '/graphql', + graphQLPath: "/graphql", graphQLCustomTypeDefs: new GraphQLSchema({ query: new GraphQLObjectType({ - name: 'Query', + name: "Query", fields: { customQuery: { type: new GraphQLNonNull(GraphQLString), @@ -11192,7 +11971,7 @@ describe('ParseGraphQLServer', () => { errorQuery: { type: new GraphQLNonNull(GraphQLString), resolve: () => { - throw new Error('A test error'); + throw new Error("A test error"); }, }, customQueryWithAutoTypeReturn: { @@ -11201,7 +11980,7 @@ describe('ParseGraphQLServer', () => { id: { type: new GraphQLNonNull(GraphQLString) }, }, resolve: async (p, { id }) => { - const obj = new Parse.Object('SomeClass'); + const obj = new Parse.Object("SomeClass"); obj.id = id; await obj.fetch(); return obj.toJSON(); @@ -11213,7 +11992,7 @@ describe('ParseGraphQLServer', () => { id: { type: new GraphQLNonNull(GraphQLString) }, }, resolve: async (p, { id }) => { - const obj = new Parse.Object('SomeClass'); + const obj = new Parse.Object("SomeClass"); obj.id = id; await obj.fetch(); return [obj.toJSON(), obj.toJSON(), obj.toJSON()]; @@ -11223,20 +12002,20 @@ describe('ParseGraphQLServer', () => { }), types: [ new GraphQLInputObjectType({ - name: 'CreateSomeClassFieldsInput', + name: "CreateSomeClassFieldsInput", fields: { type: { type: TypeEnum }, }, }), new GraphQLInputObjectType({ - name: 'UpdateSomeClassFieldsInput', + name: "UpdateSomeClassFieldsInput", fields: { type: { type: TypeEnum }, }, }), // Enhanced where input with a extended enum new GraphQLInputObjectType({ - name: 'SomeClassWhereInput', + name: "SomeClassWhereInput", fields: { type: { type: TypeEnumWhereInput, @@ -11250,9 +12029,11 @@ describe('ParseGraphQLServer', () => { }); parseGraphQLServer.applyGraphQL(expressApp); - await new Promise(resolve => httpServer.listen({ port: 13377 }, resolve)); + await new Promise(resolve => + httpServer.listen({ port: 13377 }, resolve) + ); const httpLink = await createUploadLink({ - uri: 'http://localhost:13377/graphql', + uri: "http://localhost:13377/graphql", fetch, headers, }); @@ -11261,7 +12042,7 @@ describe('ParseGraphQLServer', () => { cache: new InMemoryCache(), defaultOptions: { query: { - fetchPolicy: 'no-cache', + fetchPolicy: "no-cache", }, }, }); @@ -11271,19 +12052,19 @@ describe('ParseGraphQLServer', () => { await httpServer.close(); }); - it('can resolve a custom query', async () => { + it("can resolve a custom query", async () => { const result = await apolloClient.query({ - variables: { message: 'hello' }, + variables: { message: "hello" }, query: gql` query CustomQuery($message: String!) { customQuery(message: $message) } `, }); - expect(result.data.customQuery).toEqual('hello'); + expect(result.data.customQuery).toEqual("hello"); }); - it('can forward original error of a custom query', async () => { + it("can forward original error of a custom query", async () => { await expectAsync( apolloClient.query({ query: gql` @@ -11292,12 +12073,12 @@ describe('ParseGraphQLServer', () => { } `, }) - ).toBeRejectedWithError('A test error'); + ).toBeRejectedWithError("A test error"); }); - it('can resolve a custom query with auto type return', async () => { - const obj = new Parse.Object('SomeClass'); - await obj.save({ name: 'aname', type: 'robot' }); + it("can resolve a custom query with auto type return", async () => { + const obj = new Parse.Object("SomeClass"); + await obj.save({ name: "aname", type: "robot" }); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); const result = await apolloClient.query({ variables: { id: obj.id }, @@ -11312,15 +12093,19 @@ describe('ParseGraphQLServer', () => { } `, }); - expect(result.data.customQueryWithAutoTypeReturn.objectId).toEqual(obj.id); - expect(result.data.customQueryWithAutoTypeReturn.name).toEqual('aname'); - expect(result.data.customQueryWithAutoTypeReturn.nameUpperCase).toEqual('ANAME'); - expect(result.data.customQueryWithAutoTypeReturn.type).toEqual('robot'); + expect(result.data.customQueryWithAutoTypeReturn.objectId).toEqual( + obj.id + ); + expect(result.data.customQueryWithAutoTypeReturn.name).toEqual("aname"); + expect(result.data.customQueryWithAutoTypeReturn.nameUpperCase).toEqual( + "ANAME" + ); + expect(result.data.customQueryWithAutoTypeReturn.type).toEqual("robot"); }); - it('can resolve a custom query with auto type list return', async () => { - const obj = new Parse.Object('SomeClass'); - await obj.save({ name: 'aname', type: 'robot' }); + it("can resolve a custom query with auto type list return", async () => { + const obj = new Parse.Object("SomeClass"); + await obj.save({ name: "aname", type: "robot" }); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); const result = await apolloClient.query({ variables: { id: obj.id }, @@ -11339,20 +12124,24 @@ describe('ParseGraphQLServer', () => { result.data.customQueryWithAutoTypeReturnList.forEach(rObj => { expect(rObj.objectId).toBeDefined(); expect(rObj.objectId).toEqual(obj.id); - expect(rObj.name).toEqual('aname'); - expect(rObj.nameUpperCase).toEqual('ANAME'); - expect(rObj.type).toEqual('robot'); + expect(rObj.name).toEqual("aname"); + expect(rObj.nameUpperCase).toEqual("ANAME"); + expect(rObj.type).toEqual("robot"); }); }); - it('can resolve a stacked query with same where variables on overloaded where input', async () => { - const objPointer = new Parse.Object('SomeClass2'); - await objPointer.save({ name: 'aname', type: 'robot' }); - const obj = new Parse.Object('SomeClass'); - await obj.save({ name: 'aname', type: 'robot', pointer: objPointer }); + it("can resolve a stacked query with same where variables on overloaded where input", async () => { + const objPointer = new Parse.Object("SomeClass2"); + await objPointer.save({ name: "aname", type: "robot" }); + const obj = new Parse.Object("SomeClass"); + await obj.save({ name: "aname", type: "robot", pointer: objPointer }); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); const result = await apolloClient.query({ - variables: { where: { OR: [{ pointer: { have: { objectId: { exists: true } } } }] } }, + variables: { + where: { + OR: [{ pointer: { have: { objectId: { exists: true } } } }], + }, + }, query: gql` query someQuery($where: SomeClassWhereInput!) { q1: someClasses(where: $where) { @@ -11374,12 +12163,14 @@ describe('ParseGraphQLServer', () => { }); expect(result.data.q1.edges.length).toEqual(1); expect(result.data.q2.edges.length).toEqual(1); - expect(result.data.q1.edges[0].node.id).toEqual(result.data.q2.edges[0].node.id); + expect(result.data.q1.edges[0].node.id).toEqual( + result.data.q2.edges[0].node.id + ); }); - it('can resolve a custom extend type', async () => { - const obj = new Parse.Object('SomeClass'); - await obj.save({ name: 'aname', type: 'robot' }); + it("can resolve a custom extend type", async () => { + const obj = new Parse.Object("SomeClass"); + await obj.save({ name: "aname", type: "robot" }); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); const result = await apolloClient.query({ variables: { id: obj.id }, @@ -11393,9 +12184,9 @@ describe('ParseGraphQLServer', () => { } `, }); - expect(result.data.someClass.nameUpperCase).toEqual('ANAME'); - expect(result.data.someClass.language).toEqual('fr'); - expect(result.data.someClass.type).toEqual('robot'); + expect(result.data.someClass.nameUpperCase).toEqual("ANAME"); + expect(result.data.someClass.language).toEqual("fr"); + expect(result.data.someClass.type).toEqual("robot"); const result2 = await apolloClient.query({ variables: { id: obj.id }, @@ -11408,13 +12199,15 @@ describe('ParseGraphQLServer', () => { } `, }); - expect(result2.data.someClass.name).toEqual('aname'); - expect(result.data.someClass.language).toEqual('fr'); + expect(result2.data.someClass.name).toEqual("aname"); + expect(result.data.someClass.language).toEqual("fr"); const result3 = await apolloClient.mutate({ - variables: { id: obj.id, name: 'anewname', type: 'human' }, + variables: { id: obj.id, name: "anewname", type: "human" }, mutation: gql` mutation someClass($id: ID!, $name: String!, $type: TypeEnum!) { - updateSomeClass(input: { id: $id, fields: { name: $name, type: $type } }) { + updateSomeClass( + input: { id: $id, fields: { name: $name, type: $type } } + ) { someClass { nameUpperCase type @@ -11423,15 +12216,17 @@ describe('ParseGraphQLServer', () => { } `, }); - expect(result3.data.updateSomeClass.someClass.nameUpperCase).toEqual('ANEWNAME'); - expect(result3.data.updateSomeClass.someClass.type).toEqual('human'); + expect(result3.data.updateSomeClass.someClass.nameUpperCase).toEqual( + "ANEWNAME" + ); + expect(result3.data.updateSomeClass.someClass.type).toEqual("human"); }); }); - describe('Async Function Based Merge', () => { + describe("Async Function Based Merge", () => { let httpServer; const headers = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Javascript-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-Javascript-Key": "test", }; let apolloClient; @@ -11440,14 +12235,17 @@ describe('ParseGraphQLServer', () => { const expressApp = express(); httpServer = http.createServer(expressApp); parseGraphQLServer = new ParseGraphQLServer(parseServer, { - graphQLPath: '/graphql', - graphQLCustomTypeDefs: ({ autoSchema }) => mergeSchemas({ schemas: [autoSchema] }), + graphQLPath: "/graphql", + graphQLCustomTypeDefs: ({ autoSchema }) => + mergeSchemas({ schemas: [autoSchema] }), }); parseGraphQLServer.applyGraphQL(expressApp); - await new Promise(resolve => httpServer.listen({ port: 13377 }, resolve)); + await new Promise(resolve => + httpServer.listen({ port: 13377 }, resolve) + ); const httpLink = await createUploadLink({ - uri: 'http://localhost:13377/graphql', + uri: "http://localhost:13377/graphql", fetch, headers, }); @@ -11456,7 +12254,7 @@ describe('ParseGraphQLServer', () => { cache: new InMemoryCache(), defaultOptions: { query: { - fetchPolicy: 'no-cache', + fetchPolicy: "no-cache", }, }, }); @@ -11467,7 +12265,7 @@ describe('ParseGraphQLServer', () => { await httpServer.close(); }); - it('can resolve a query', async () => { + it("can resolve a query", async () => { const result = await apolloClient.query({ query: gql` query Health { diff --git a/spec/ParseHooks.spec.js b/spec/ParseHooks.spec.js index 43d287bf6a..ef2b69ffc6 100644 --- a/spec/ParseHooks.spec.js +++ b/spec/ParseHooks.spec.js @@ -1,22 +1,22 @@ -'use strict'; +"use strict"; -const request = require('../lib/request'); -const triggers = require('../lib/triggers'); -const HooksController = require('../lib/Controllers/HooksController').default; -const express = require('express'); -const auth = require('../lib/Auth'); -const Config = require('../lib/Config'); +const request = require("../lib/request"); +const triggers = require("../lib/triggers"); +const HooksController = require("../lib/Controllers/HooksController").default; +const express = require("express"); +const auth = require("../lib/Auth"); +const Config = require("../lib/Config"); const port = 34567; -const hookServerURL = 'http://localhost:' + port; +const hookServerURL = "http://localhost:" + port; -describe('Hooks', () => { +describe("Hooks", () => { let server; let app; beforeEach(done => { if (!app) { app = express(); - app.use(express.json({ type: '*/*' })); + app.use(express.json({ type: "*/*" })); server = app.listen(port, undefined, done); } else { done(); @@ -27,7 +27,7 @@ describe('Hooks', () => { server.close(done); }); - it('should have no hooks registered', done => { + it("should have no hooks registered", done => { Parse.Hooks.getFunctions().then( res => { expect(res.constructor).toBe(Array.prototype.constructor); @@ -40,7 +40,7 @@ describe('Hooks', () => { ); }); - it('should have no triggers registered', done => { + it("should have no triggers registered", done => { Parse.Hooks.getTriggers().then( res => { expect(res.constructor).toBe(Array.prototype.constructor); @@ -53,165 +53,185 @@ describe('Hooks', () => { ); }); - it_id('26c9a13d-3d71-452e-a91c-9a4589be021c')(it)('should CRUD a function registration', done => { - // Create - Parse.Hooks.createFunction('My-Test-Function', 'http://someurl') - .then(response => { - expect(response.functionName).toBe('My-Test-Function'); - expect(response.url).toBe('http://someurl'); - // Find - return Parse.Hooks.getFunction('My-Test-Function'); - }) - .then(response => { - expect(response.objectId).toBeUndefined(); - expect(response.url).toBe('http://someurl'); - return Parse.Hooks.updateFunction('My-Test-Function', 'http://anotherurl'); - }) - .then(res => { - expect(res.objectId).toBeUndefined(); - expect(res.functionName).toBe('My-Test-Function'); - expect(res.url).toBe('http://anotherurl'); - // delete - return Parse.Hooks.removeFunction('My-Test-Function'); - }) - .then(() => { - // Find again! but should be deleted - return Parse.Hooks.getFunction('My-Test-Function').then( + it_id("26c9a13d-3d71-452e-a91c-9a4589be021c")(it)( + "should CRUD a function registration", + done => { + // Create + Parse.Hooks.createFunction("My-Test-Function", "http://someurl") + .then(response => { + expect(response.functionName).toBe("My-Test-Function"); + expect(response.url).toBe("http://someurl"); + // Find + return Parse.Hooks.getFunction("My-Test-Function"); + }) + .then(response => { + expect(response.objectId).toBeUndefined(); + expect(response.url).toBe("http://someurl"); + return Parse.Hooks.updateFunction( + "My-Test-Function", + "http://anotherurl" + ); + }) + .then(res => { + expect(res.objectId).toBeUndefined(); + expect(res.functionName).toBe("My-Test-Function"); + expect(res.url).toBe("http://anotherurl"); + // delete + return Parse.Hooks.removeFunction("My-Test-Function"); + }) + .then(() => { + // Find again! but should be deleted + return Parse.Hooks.getFunction("My-Test-Function").then( + res => { + fail("Failed to delete hook"); + fail(res); + done(); + return Promise.resolve(); + }, + err => { + expect(err.code).toBe(143); + expect(err.message).toBe( + "no function named: My-Test-Function is defined" + ); + done(); + return Promise.resolve(); + } + ); + }) + .catch(error => { + jfail(error); + done(); + }); + } + ); + + it_id("7a81069e-2ee9-47fb-8e27-1120eda09e99")(it)( + "should CRUD a trigger registration", + done => { + // Create + Parse.Hooks.createTrigger("MyClass", "beforeDelete", "http://someurl") + .then( res => { - fail('Failed to delete hook'); - fail(res); + expect(res.className).toBe("MyClass"); + expect(res.triggerName).toBe("beforeDelete"); + expect(res.url).toBe("http://someurl"); + // Find + return Parse.Hooks.getTrigger("MyClass", "beforeDelete"); + }, + err => { + fail(err); done(); - return Promise.resolve(); + } + ) + .then( + res => { + expect(res).not.toBe(null); + expect(res).not.toBe(undefined); + expect(res.objectId).toBeUndefined(); + expect(res.url).toBe("http://someurl"); + // delete + return Parse.Hooks.updateTrigger( + "MyClass", + "beforeDelete", + "http://anotherurl" + ); }, err => { - expect(err.code).toBe(143); - expect(err.message).toBe('no function named: My-Test-Function is defined'); + jfail(err); done(); - return Promise.resolve(); } - ); - }) - .catch(error => { - jfail(error); - done(); - }); - }); - - it_id('7a81069e-2ee9-47fb-8e27-1120eda09e99')(it)('should CRUD a trigger registration', done => { - // Create - Parse.Hooks.createTrigger('MyClass', 'beforeDelete', 'http://someurl') - .then( - res => { - expect(res.className).toBe('MyClass'); - expect(res.triggerName).toBe('beforeDelete'); - expect(res.url).toBe('http://someurl'); - // Find - return Parse.Hooks.getTrigger('MyClass', 'beforeDelete'); - }, - err => { - fail(err); - done(); - } - ) - .then( - res => { - expect(res).not.toBe(null); - expect(res).not.toBe(undefined); - expect(res.objectId).toBeUndefined(); - expect(res.url).toBe('http://someurl'); - // delete - return Parse.Hooks.updateTrigger('MyClass', 'beforeDelete', 'http://anotherurl'); - }, - err => { - jfail(err); - done(); - } - ) - .then( - res => { - expect(res.className).toBe('MyClass'); - expect(res.url).toBe('http://anotherurl'); - expect(res.objectId).toBeUndefined(); + ) + .then( + res => { + expect(res.className).toBe("MyClass"); + expect(res.url).toBe("http://anotherurl"); + expect(res.objectId).toBeUndefined(); - return Parse.Hooks.removeTrigger('MyClass', 'beforeDelete'); - }, - err => { - jfail(err); - done(); - } - ) - .then( - () => { - // Find again! but should be deleted - return Parse.Hooks.getTrigger('MyClass', 'beforeDelete'); - }, - err => { - jfail(err); - done(); - } - ) - .then( - function () { - fail('should not succeed'); - done(); - }, - err => { - if (err) { - expect(err).not.toBe(null); - expect(err).not.toBe(undefined); - expect(err.code).toBe(143); - expect(err.message).toBe('class MyClass does not exist'); - } else { - fail('should have errored'); + return Parse.Hooks.removeTrigger("MyClass", "beforeDelete"); + }, + err => { + jfail(err); + done(); } - done(); - } - ); - }); + ) + .then( + () => { + // Find again! but should be deleted + return Parse.Hooks.getTrigger("MyClass", "beforeDelete"); + }, + err => { + jfail(err); + done(); + } + ) + .then( + function () { + fail("should not succeed"); + done(); + }, + err => { + if (err) { + expect(err).not.toBe(null); + expect(err).not.toBe(undefined); + expect(err.code).toBe(143); + expect(err.message).toBe("class MyClass does not exist"); + } else { + fail("should have errored"); + } + done(); + } + ); + } + ); - it('should fail to register hooks without Master Key', done => { + it("should fail to register hooks without Master Key", done => { request({ - method: 'POST', - url: Parse.serverURL + '/hooks/functions', + method: "POST", + url: Parse.serverURL + "/hooks/functions", headers: { - 'X-Parse-Application-Id': Parse.applicationId, + "X-Parse-Application-Id": Parse.applicationId, }, body: JSON.stringify({ - url: 'http://hello.word', - functionName: 'SomeFunction', + url: "http://hello.word", + functionName: "SomeFunction", }), }).then(fail, response => { const body = response.data; - expect(body.error).toBe('unauthorized'); + expect(body.error).toBe("unauthorized"); done(); }); }); - it_id('f7ad092f-81dc-4729-afd1-3b02db2f0948')(it)( - 'should fail trying to create two times the same function', + it_id("f7ad092f-81dc-4729-afd1-3b02db2f0948")(it)( + "should fail trying to create two times the same function", done => { - Parse.Hooks.createFunction('my_new_function', 'http://url.com') + Parse.Hooks.createFunction("my_new_function", "http://url.com") .then(() => jasmine.timeout()) .then( () => { - return Parse.Hooks.createFunction('my_new_function', 'http://url.com'); + return Parse.Hooks.createFunction( + "my_new_function", + "http://url.com" + ); }, () => { - fail('should create a new function'); + fail("should create a new function"); } ) .then( () => { - fail('should not be able to create the same function'); + fail("should not be able to create the same function"); }, err => { expect(err).not.toBe(undefined); expect(err).not.toBe(null); if (err) { expect(err.code).toBe(143); - expect(err.message).toBe('function name: my_new_function already exists'); + expect(err.message).toBe( + "function name: my_new_function already exists" + ); } - return Parse.Hooks.removeFunction('my_new_function'); + return Parse.Hooks.removeFunction("my_new_function"); } ) .then( @@ -226,30 +246,36 @@ describe('Hooks', () => { } ); - it_id('4db8c249-9174-4e8e-b959-55c8ea959a02')(it)( - 'should fail trying to create two times the same trigger', + it_id("4db8c249-9174-4e8e-b959-55c8ea959a02")(it)( + "should fail trying to create two times the same trigger", done => { - Parse.Hooks.createTrigger('MyClass', 'beforeSave', 'http://url.com') + Parse.Hooks.createTrigger("MyClass", "beforeSave", "http://url.com") .then( () => { - return Parse.Hooks.createTrigger('MyClass', 'beforeSave', 'http://url.com'); + return Parse.Hooks.createTrigger( + "MyClass", + "beforeSave", + "http://url.com" + ); }, () => { - fail('should create a new trigger'); + fail("should create a new trigger"); } ) .then( () => { - fail('should not be able to create the same trigger'); + fail("should not be able to create the same trigger"); }, err => { expect(err).not.toBe(undefined); expect(err).not.toBe(null); if (err) { expect(err.code).toBe(143); - expect(err.message).toBe('class MyClass already has trigger beforeSave'); + expect(err.message).toBe( + "class MyClass already has trigger beforeSave" + ); } - return Parse.Hooks.removeTrigger('MyClass', 'beforeSave'); + return Parse.Hooks.removeTrigger("MyClass", "beforeSave"); } ) .then( @@ -265,24 +291,26 @@ describe('Hooks', () => { ); it("should fail trying to update a function that don't exist", done => { - Parse.Hooks.updateFunction('A_COOL_FUNCTION', 'http://url.com') + Parse.Hooks.updateFunction("A_COOL_FUNCTION", "http://url.com") .then( () => { - fail('Should not succeed'); + fail("Should not succeed"); }, err => { expect(err).not.toBe(undefined); expect(err).not.toBe(null); if (err) { expect(err.code).toBe(143); - expect(err.message).toBe('no function named: A_COOL_FUNCTION is defined'); + expect(err.message).toBe( + "no function named: A_COOL_FUNCTION is defined" + ); } - return Parse.Hooks.getFunction('A_COOL_FUNCTION'); + return Parse.Hooks.getFunction("A_COOL_FUNCTION"); } ) .then( () => { - fail('the function should not exist'); + fail("the function should not exist"); done(); }, err => { @@ -290,7 +318,9 @@ describe('Hooks', () => { expect(err).not.toBe(null); if (err) { expect(err.code).toBe(143); - expect(err.message).toBe('no function named: A_COOL_FUNCTION is defined'); + expect(err.message).toBe( + "no function named: A_COOL_FUNCTION is defined" + ); } done(); } @@ -298,24 +328,24 @@ describe('Hooks', () => { }); it("should fail trying to update a trigger that don't exist", done => { - Parse.Hooks.updateTrigger('AClassName', 'beforeSave', 'http://url.com') + Parse.Hooks.updateTrigger("AClassName", "beforeSave", "http://url.com") .then( () => { - fail('Should not succeed'); + fail("Should not succeed"); }, err => { expect(err).not.toBe(undefined); expect(err).not.toBe(null); if (err) { expect(err.code).toBe(143); - expect(err.message).toBe('class AClassName does not exist'); + expect(err.message).toBe("class AClassName does not exist"); } - return Parse.Hooks.getTrigger('AClassName', 'beforeSave'); + return Parse.Hooks.getTrigger("AClassName", "beforeSave"); } ) .then( () => { - fail('the function should not exist'); + fail("the function should not exist"); done(); }, err => { @@ -323,15 +353,15 @@ describe('Hooks', () => { expect(err).not.toBe(null); if (err) { expect(err.code).toBe(143); - expect(err.message).toBe('class AClassName does not exist'); + expect(err.message).toBe("class AClassName does not exist"); } done(); } ); }); - it('should fail trying to create a malformed function', done => { - Parse.Hooks.createFunction('MyFunction').then( + it("should fail trying to create a malformed function", done => { + Parse.Hooks.createFunction("MyFunction").then( res => { fail(res); }, @@ -340,39 +370,48 @@ describe('Hooks', () => { expect(err).not.toBe(null); if (err) { expect(err.code).toBe(143); - expect(err.error).toBe('invalid hook declaration'); + expect(err.error).toBe("invalid hook declaration"); } done(); } ); }); - it('should fail trying to create a malformed function (REST)', done => { + it("should fail trying to create a malformed function (REST)", done => { request({ - method: 'POST', - url: Parse.serverURL + '/hooks/functions', + method: "POST", + url: Parse.serverURL + "/hooks/functions", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': Parse.masterKey, + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Master-Key": Parse.masterKey, }, - body: JSON.stringify({ functionName: 'SomeFunction' }), + body: JSON.stringify({ functionName: "SomeFunction" }), }).then(fail, response => { const body = response.data; - expect(body.error).toBe('invalid hook declaration'); + expect(body.error).toBe("invalid hook declaration"); expect(body.code).toBe(143); done(); }); }); - it_id('96d99414-b739-4e36-b3f4-8135e0be83ea')(it)( - 'should create hooks and properly preload them', + it_id("96d99414-b739-4e36-b3f4-8135e0be83ea")(it)( + "should create hooks and properly preload them", done => { const promises = []; for (let i = 0; i < 5; i++) { promises.push( - Parse.Hooks.createTrigger('MyClass' + i, 'beforeSave', 'http://url.com/beforeSave/' + i) + Parse.Hooks.createTrigger( + "MyClass" + i, + "beforeSave", + "http://url.com/beforeSave/" + i + ) + ); + promises.push( + Parse.Hooks.createFunction( + "AFunction" + i, + "http://url.com/function" + i + ) ); - promises.push(Parse.Hooks.createFunction('AFunction' + i, 'http://url.com/function' + i)); } Promise.all(promises) @@ -380,22 +419,32 @@ describe('Hooks', () => { function () { for (let i = 0; i < 5; i++) { // Delete everything from memory, as the server just started - triggers.removeTrigger('beforeSave', 'MyClass' + i, Parse.applicationId); - triggers.removeFunction('AFunction' + i, Parse.applicationId); + triggers.removeTrigger( + "beforeSave", + "MyClass" + i, + Parse.applicationId + ); + triggers.removeFunction("AFunction" + i, Parse.applicationId); + expect( + triggers.getTrigger( + "MyClass" + i, + "beforeSave", + Parse.applicationId + ) + ).toBeUndefined(); expect( - triggers.getTrigger('MyClass' + i, 'beforeSave', Parse.applicationId) + triggers.getFunction("AFunction" + i, Parse.applicationId) ).toBeUndefined(); - expect(triggers.getFunction('AFunction' + i, Parse.applicationId)).toBeUndefined(); } const hooksController = new HooksController( Parse.applicationId, - Config.get('test').database + Config.get("test").database ); return hooksController.load(); }, err => { jfail(err); - fail('Should properly create all hooks'); + fail("Should properly create all hooks"); done(); } ) @@ -403,76 +452,86 @@ describe('Hooks', () => { function () { for (let i = 0; i < 5; i++) { expect( - triggers.getTrigger('MyClass' + i, 'beforeSave', Parse.applicationId) + triggers.getTrigger( + "MyClass" + i, + "beforeSave", + Parse.applicationId + ) ).not.toBeUndefined(); expect( - triggers.getFunction('AFunction' + i, Parse.applicationId) + triggers.getFunction("AFunction" + i, Parse.applicationId) ).not.toBeUndefined(); } done(); }, err => { jfail(err); - fail('should properly load all hooks'); + fail("should properly load all hooks"); done(); } ); } ); - it_id('fe7d41eb-e570-4804-ac1f-8b6c407fdafe')(it)( - 'should run the function on the test server', + it_id("fe7d41eb-e570-4804-ac1f-8b6c407fdafe")(it)( + "should run the function on the test server", done => { - app.post('/SomeFunction', function (req, res) { - res.json({ success: 'OK!' }); + app.post("/SomeFunction", function (req, res) { + res.json({ success: "OK!" }); }); - Parse.Hooks.createFunction('SOME_TEST_FUNCTION', hookServerURL + '/SomeFunction') + Parse.Hooks.createFunction( + "SOME_TEST_FUNCTION", + hookServerURL + "/SomeFunction" + ) .then( function () { - return Parse.Cloud.run('SOME_TEST_FUNCTION'); + return Parse.Cloud.run("SOME_TEST_FUNCTION"); }, err => { jfail(err); - fail('Should not fail creating a function'); + fail("Should not fail creating a function"); done(); } ) .then( function (res) { - expect(res).toBe('OK!'); + expect(res).toBe("OK!"); done(); }, err => { jfail(err); - fail('Should not fail calling a function'); + fail("Should not fail calling a function"); done(); } ); } ); - it_id('63985b4c-a212-4a86-aa0e-eb4600bb485b')(it)( - 'should run the function on the test server (error handling)', + it_id("63985b4c-a212-4a86-aa0e-eb4600bb485b")(it)( + "should run the function on the test server (error handling)", done => { - app.post('/SomeFunctionError', function (req, res) { - res.json({ error: { code: 1337, error: 'hacking that one!' } }); + app.post("/SomeFunctionError", function (req, res) { + res.json({ error: { code: 1337, error: "hacking that one!" } }); }); // The function is deleted as the DB is dropped between calls - Parse.Hooks.createFunction('SOME_TEST_FUNCTION', hookServerURL + '/SomeFunctionError') + Parse.Hooks.createFunction( + "SOME_TEST_FUNCTION", + hookServerURL + "/SomeFunctionError" + ) .then( function () { - return Parse.Cloud.run('SOME_TEST_FUNCTION'); + return Parse.Cloud.run("SOME_TEST_FUNCTION"); }, err => { jfail(err); - fail('Should not fail creating a function'); + fail("Should not fail creating a function"); done(); } ) .then( function () { - fail('Should not succeed calling that function'); + fail("Should not succeed calling that function"); done(); }, err => { @@ -481,7 +540,7 @@ describe('Hooks', () => { if (err) { expect(err.code).toBe(Parse.Error.SCRIPT_FAILED); expect(err.message.code).toEqual(1337); - expect(err.message.error).toEqual('hacking that one!'); + expect(err.message.error).toEqual("hacking that one!"); } done(); } @@ -489,68 +548,74 @@ describe('Hooks', () => { } ); - it_id('bacc1754-2a3a-4a7a-8d0e-f80af36da1ef')(it)( - 'should provide X-Parse-Webhook-Key when defined', + it_id("bacc1754-2a3a-4a7a-8d0e-f80af36da1ef")(it)( + "should provide X-Parse-Webhook-Key when defined", done => { - app.post('/ExpectingKey', function (req, res) { - if (req.get('X-Parse-Webhook-Key') === 'hook') { - res.json({ success: 'correct key provided' }); + app.post("/ExpectingKey", function (req, res) { + if (req.get("X-Parse-Webhook-Key") === "hook") { + res.json({ success: "correct key provided" }); } else { - res.json({ error: 'incorrect key provided' }); + res.json({ error: "incorrect key provided" }); } }); - Parse.Hooks.createFunction('SOME_TEST_FUNCTION', hookServerURL + '/ExpectingKey') + Parse.Hooks.createFunction( + "SOME_TEST_FUNCTION", + hookServerURL + "/ExpectingKey" + ) .then( function () { - return Parse.Cloud.run('SOME_TEST_FUNCTION'); + return Parse.Cloud.run("SOME_TEST_FUNCTION"); }, err => { jfail(err); - fail('Should not fail creating a function'); + fail("Should not fail creating a function"); done(); } ) .then( function (res) { - expect(res).toBe('correct key provided'); + expect(res).toBe("correct key provided"); done(); }, err => { jfail(err); - fail('Should not fail calling a function'); + fail("Should not fail calling a function"); done(); } ); } ); - it_id('eeb67946-42c6-4581-89af-2abb4927913e')(it)( - 'should not pass X-Parse-Webhook-Key if not provided', + it_id("eeb67946-42c6-4581-89af-2abb4927913e")(it)( + "should not pass X-Parse-Webhook-Key if not provided", done => { reconfigureServer({ webhookKey: undefined }).then(() => { - app.post('/ExpectingKeyAlso', function (req, res) { - if (req.get('X-Parse-Webhook-Key') === 'hook') { - res.json({ success: 'correct key provided' }); + app.post("/ExpectingKeyAlso", function (req, res) { + if (req.get("X-Parse-Webhook-Key") === "hook") { + res.json({ success: "correct key provided" }); } else { - res.json({ error: 'incorrect key provided' }); + res.json({ error: "incorrect key provided" }); } }); - Parse.Hooks.createFunction('SOME_TEST_FUNCTION', hookServerURL + '/ExpectingKeyAlso') + Parse.Hooks.createFunction( + "SOME_TEST_FUNCTION", + hookServerURL + "/ExpectingKeyAlso" + ) .then( function () { - return Parse.Cloud.run('SOME_TEST_FUNCTION'); + return Parse.Cloud.run("SOME_TEST_FUNCTION"); }, err => { jfail(err); - fail('Should not fail creating a function'); + fail("Should not fail creating a function"); done(); } ) .then( function () { - fail('Should not succeed calling that function'); + fail("Should not succeed calling that function"); done(); }, err => { @@ -558,7 +623,7 @@ describe('Hooks', () => { expect(err).not.toBe(null); if (err) { expect(err.code).toBe(Parse.Error.SCRIPT_FAILED); - expect(err.message).toEqual('incorrect key provided'); + expect(err.message).toEqual("incorrect key provided"); } done(); } @@ -567,22 +632,26 @@ describe('Hooks', () => { } ); - it_id('21decb65-4b93-4791-85a3-ab124a9ea3ac')(it)( - 'should run the beforeSave hook on the test server', + it_id("21decb65-4b93-4791-85a3-ab124a9ea3ac")(it)( + "should run the beforeSave hook on the test server", done => { let triggerCount = 0; - app.post('/BeforeSaveSome', function (req, res) { + app.post("/BeforeSaveSome", function (req, res) { triggerCount++; const object = req.body.object; - object.hello = 'world'; + object.hello = "world"; // Would need parse cloud express to set much more // But this should override the key upon return res.json({ success: object }); }); // The function is deleted as the DB is dropped between calls - Parse.Hooks.createTrigger('SomeRandomObject', 'beforeSave', hookServerURL + '/BeforeSaveSome') + Parse.Hooks.createTrigger( + "SomeRandomObject", + "beforeSave", + hookServerURL + "/BeforeSaveSome" + ) .then(function () { - const obj = new Parse.Object('SomeRandomObject'); + const obj = new Parse.Object("SomeRandomObject"); return obj.save(); }) .then(function (res) { @@ -590,39 +659,39 @@ describe('Hooks', () => { return res.fetch(); }) .then(function (res) { - expect(res.get('hello')).toEqual('world'); + expect(res.get("hello")).toEqual("world"); done(); }) .catch(err => { jfail(err); - fail('Should not fail creating a function'); + fail("Should not fail creating a function"); done(); }); } ); - it_id('52e3152b-5514-4418-9e76-1f394368b8fb')(it)( - 'beforeSave hooks should correctly handle responses containing entire object', + it_id("52e3152b-5514-4418-9e76-1f394368b8fb")(it)( + "beforeSave hooks should correctly handle responses containing entire object", done => { - app.post('/BeforeSaveSome2', function (req, res) { + app.post("/BeforeSaveSome2", function (req, res) { const object = Parse.Object.fromJSON(req.body.object); - object.set('hello', 'world'); + object.set("hello", "world"); res.json({ success: object }); }); Parse.Hooks.createTrigger( - 'SomeRandomObject2', - 'beforeSave', - hookServerURL + '/BeforeSaveSome2' + "SomeRandomObject2", + "beforeSave", + hookServerURL + "/BeforeSaveSome2" ) .then(function () { - const obj = new Parse.Object('SomeRandomObject2'); + const obj = new Parse.Object("SomeRandomObject2"); return obj.save(); }) .then(function (res) { return res.save(); }) .then(function (res) { - expect(res.get('hello')).toEqual('world'); + expect(res.get("hello")).toEqual("world"); done(); }) .catch(err => { @@ -632,53 +701,59 @@ describe('Hooks', () => { } ); - it_id('d27a7587-abb5-40d5-9805-051ee91de474')(it)( - 'should run the afterSave hook on the test server', + it_id("d27a7587-abb5-40d5-9805-051ee91de474")(it)( + "should run the afterSave hook on the test server", done => { let triggerCount = 0; let newObjectId; - app.post('/AfterSaveSome', function (req, res) { + app.post("/AfterSaveSome", function (req, res) { triggerCount++; - const obj = new Parse.Object('AnotherObject'); - obj.set('foo', 'bar'); + const obj = new Parse.Object("AnotherObject"); + obj.set("foo", "bar"); obj.save().then(function (obj) { newObjectId = obj.id; res.json({ success: {} }); }); }); // The function is deleted as the DB is dropped between calls - Parse.Hooks.createTrigger('SomeRandomObject', 'afterSave', hookServerURL + '/AfterSaveSome') + Parse.Hooks.createTrigger( + "SomeRandomObject", + "afterSave", + hookServerURL + "/AfterSaveSome" + ) .then(function () { - const obj = new Parse.Object('SomeRandomObject'); + const obj = new Parse.Object("SomeRandomObject"); return obj.save(); }) .then(function () { return new Promise(resolve => { setTimeout(() => { expect(triggerCount).toBe(1); - new Parse.Query('AnotherObject').get(newObjectId).then(r => resolve(r)); + new Parse.Query("AnotherObject") + .get(newObjectId) + .then(r => resolve(r)); }, 500); }); }) .then(function (res) { - expect(res.get('foo')).toEqual('bar'); + expect(res.get("foo")).toEqual("bar"); done(); }) .catch(err => { jfail(err); - fail('Should not fail creating a function'); + fail("Should not fail creating a function"); done(); }); } ); }); -describe('triggers', () => { - it('should produce a proper request object with context in beforeSave', () => { - const config = Config.get('test'); +describe("triggers", () => { + it("should produce a proper request object with context in beforeSave", () => { + const config = Config.get("test"); const master = auth.master(config); const context = { - originalKey: 'original', + originalKey: "original", }; const req = triggers.getRequestObject( triggers.Types.beforeSave, @@ -688,19 +763,19 @@ describe('triggers', () => { config, context ); - expect(req.context.originalKey).toBe('original'); + expect(req.context.originalKey).toBe("original"); req.context = { - key: 'value', + key: "value", }; expect(context.key).toBe(undefined); req.context = { - key: 'newValue', + key: "newValue", }; expect(context.key).toBe(undefined); }); - it('should produce a proper request object with context in afterSave', () => { - const config = Config.get('test'); + it("should produce a proper request object with context in afterSave", () => { + const config = Config.get("test"); const master = auth.master(config); const context = {}; const req = triggers.getRequestObject( @@ -714,8 +789,8 @@ describe('triggers', () => { expect(req.context).not.toBeUndefined(); }); - it('should not set context on beforeFind', () => { - const config = Config.get('test'); + it("should not set context on beforeFind", () => { + const config = Config.get("test"); const master = auth.master(config); const context = {}; const req = triggers.getRequestObject( @@ -730,13 +805,13 @@ describe('triggers', () => { }); }); -describe('sanitizing names', () => { +describe("sanitizing names", () => { const invalidNames = [ `test'%3bdeclare%20@q%20varchar(99)%3bset%20@q%3d'%5c%5cxxxxxxxxxxxxxxx.yyyyy'%2b'fy.com%5cxus'%3b%20exec%20master.dbo.xp_dirtree%20@q%3b--%20`, `test.function.name`, ]; - it('should not crash server and return error on invalid Cloud Function name', async () => { + it("should not crash server and return error on invalid Cloud Function name", async () => { for (const invalidName of invalidNames) { let error; try { @@ -749,7 +824,7 @@ describe('sanitizing names', () => { } }); - it('should not crash server and return error on invalid Cloud Job name', async () => { + it("should not crash server and return error on invalid Cloud Job name", async () => { for (const invalidName of invalidNames) { let error; try { diff --git a/spec/ParseInstallation.spec.js b/spec/ParseInstallation.spec.js index 8dd91c5a05..8de0ccc598 100644 --- a/spec/ParseInstallation.spec.js +++ b/spec/ParseInstallation.spec.js @@ -1,41 +1,48 @@ -'use strict'; +"use strict"; // These tests check the Installations functionality of the REST API. // Ported from installation_collection_test.go -const auth = require('../lib/Auth'); -const Config = require('../lib/Config'); -const Parse = require('parse/node').Parse; -const rest = require('../lib/rest'); -const request = require('../lib/request'); +const auth = require("../lib/Auth"); +const Config = require("../lib/Config"); +const Parse = require("parse/node").Parse; +const rest = require("../lib/rest"); +const request = require("../lib/request"); let config; let database; -const defaultColumns = require('../lib/Controllers/SchemaController').defaultColumns; +const defaultColumns = + require("../lib/Controllers/SchemaController").defaultColumns; const delay = function delay(delay) { return new Promise(resolve => setTimeout(resolve, delay)); }; const installationSchema = { - fields: Object.assign({}, defaultColumns._Default, defaultColumns._Installation), + fields: Object.assign( + {}, + defaultColumns._Default, + defaultColumns._Installation + ), }; -describe('Installations', () => { +describe("Installations", () => { beforeEach(() => { - config = Config.get('test'); + config = Config.get("test"); database = config.database; }); - it('creates an android installation with ids', done => { - const installId = '12345678-abcd-abcd-abcd-123456789abc'; - const device = 'android'; + it("creates an android installation with ids", done => { + const installId = "12345678-abcd-abcd-abcd-123456789abc"; + const device = "android"; const input = { installationId: installId, deviceType: device, }; rest - .create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .create(config, auth.nobody(config), "_Installation", input) + .then(() => + database.adapter.find("_Installation", installationSchema, {}, {}) + ) .then(results => { expect(results.length).toEqual(1); const obj = results[0]; @@ -50,16 +57,19 @@ describe('Installations', () => { }); }); - it('creates an ios installation with ids', done => { - const t = '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; - const device = 'ios'; + it("creates an ios installation with ids", done => { + const t = + "11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306"; + const device = "ios"; const input = { deviceToken: t, deviceType: device, }; rest - .create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .create(config, auth.nobody(config), "_Installation", input) + .then(() => + database.adapter.find("_Installation", installationSchema, {}, {}) + ) .then(results => { expect(results.length).toEqual(1); const obj = results[0]; @@ -74,16 +84,18 @@ describe('Installations', () => { }); }); - it('creates an embedded installation with ids', done => { - const installId = '12345678-abcd-abcd-abcd-123456789abc'; - const device = 'embedded'; + it("creates an embedded installation with ids", done => { + const installId = "12345678-abcd-abcd-abcd-123456789abc"; + const device = "embedded"; const input = { installationId: installId, deviceType: device, }; rest - .create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .create(config, auth.nobody(config), "_Installation", input) + .then(() => + database.adapter.find("_Installation", installationSchema, {}, {}) + ) .then(results => { expect(results.length).toEqual(1); const obj = results[0]; @@ -98,26 +110,28 @@ describe('Installations', () => { }); }); - it('creates an android installation with all fields', done => { - const installId = '12345678-abcd-abcd-abcd-123456789abc'; - const device = 'android'; + it("creates an android installation with all fields", done => { + const installId = "12345678-abcd-abcd-abcd-123456789abc"; + const device = "android"; const input = { installationId: installId, deviceType: device, - channels: ['foo', 'bar'], + channels: ["foo", "bar"], }; rest - .create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .create(config, auth.nobody(config), "_Installation", input) + .then(() => + database.adapter.find("_Installation", installationSchema, {}, {}) + ) .then(results => { expect(results.length).toEqual(1); const obj = results[0]; expect(obj.installationId).toEqual(installId); expect(obj.deviceType).toEqual(device); - expect(typeof obj.channels).toEqual('object'); + expect(typeof obj.channels).toEqual("object"); expect(obj.channels.length).toEqual(2); - expect(obj.channels[0]).toEqual('foo'); - expect(obj.channels[1]).toEqual('bar'); + expect(obj.channels[0]).toEqual("foo"); + expect(obj.channels[1]).toEqual("bar"); done(); }) .catch(error => { @@ -127,26 +141,29 @@ describe('Installations', () => { }); }); - it('creates an ios installation with all fields', done => { - const t = '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; - const device = 'ios'; + it("creates an ios installation with all fields", done => { + const t = + "11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306"; + const device = "ios"; const input = { deviceToken: t, deviceType: device, - channels: ['foo', 'bar'], + channels: ["foo", "bar"], }; rest - .create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .create(config, auth.nobody(config), "_Installation", input) + .then(() => + database.adapter.find("_Installation", installationSchema, {}, {}) + ) .then(results => { expect(results.length).toEqual(1); const obj = results[0]; expect(obj.deviceToken).toEqual(t); expect(obj.deviceType).toEqual(device); - expect(typeof obj.channels).toEqual('object'); + expect(typeof obj.channels).toEqual("object"); expect(obj.channels.length).toEqual(2); - expect(obj.channels[0]).toEqual('foo'); - expect(obj.channels[1]).toEqual('bar'); + expect(obj.channels[0]).toEqual("foo"); + expect(obj.channels[1]).toEqual("bar"); done(); }) .catch(error => { @@ -156,21 +173,21 @@ describe('Installations', () => { }); }); - it('should properly fail queying installations', done => { - const installId = '12345678-abcd-abcd-abcd-123456789abc'; - const device = 'android'; + it("should properly fail queying installations", done => { + const installId = "12345678-abcd-abcd-abcd-123456789abc"; + const device = "android"; const input = { installationId: installId, deviceType: device, }; rest - .create(config, auth.nobody(config), '_Installation', input) + .create(config, auth.nobody(config), "_Installation", input) .then(() => { const query = new Parse.Query(Parse.Installation); return query.find(); }) .then(() => { - fail('Should not succeed!'); + fail("Should not succeed!"); done(); }) .catch(error => { @@ -182,15 +199,15 @@ describe('Installations', () => { }); }); - it('should properly queying installations with masterKey', done => { - const installId = '12345678-abcd-abcd-abcd-123456789abc'; - const device = 'android'; + it("should properly queying installations with masterKey", done => { + const installId = "12345678-abcd-abcd-abcd-123456789abc"; + const device = "android"; const input = { installationId: installId, deviceType: device, }; rest - .create(config, auth.nobody(config), '_Installation', input) + .create(config, auth.nobody(config), "_Installation", input) .then(() => { const query = new Parse.Query(Parse.Installation); return query.find({ useMasterKey: true }); @@ -203,20 +220,20 @@ describe('Installations', () => { done(); }) .catch(() => { - fail('Should not fail'); + fail("Should not fail"); done(); }); }); - it('fails with missing ids', done => { + it("fails with missing ids", done => { const input = { - deviceType: 'android', - channels: ['foo', 'bar'], + deviceType: "android", + channels: ["foo", "bar"], }; rest - .create(config, auth.nobody(config), '_Installation', input) + .create(config, auth.nobody(config), "_Installation", input) .then(() => { - fail('Should not have been able to create an Installation.'); + fail("Should not have been able to create an Installation."); done(); }) .catch(error => { @@ -225,16 +242,16 @@ describe('Installations', () => { }); }); - it('fails for android with missing type', done => { - const installId = '12345678-abcd-abcd-abcd-123456789abc'; + it("fails for android with missing type", done => { + const installId = "12345678-abcd-abcd-abcd-123456789abc"; const input = { installationId: installId, - channels: ['foo', 'bar'], + channels: ["foo", "bar"], }; rest - .create(config, auth.nobody(config), '_Installation', input) + .create(config, auth.nobody(config), "_Installation", input) .then(() => { - fail('Should not have been able to create an Installation.'); + fail("Should not have been able to create an Installation."); done(); }) .catch(error => { @@ -243,21 +260,24 @@ describe('Installations', () => { }); }); - it('creates an object with custom fields', done => { - const t = '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; + it("creates an object with custom fields", done => { + const t = + "11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306"; const input = { deviceToken: t, - deviceType: 'ios', - channels: ['foo', 'bar'], - custom: 'allowed', + deviceType: "ios", + channels: ["foo", "bar"], + custom: "allowed", }; rest - .create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .create(config, auth.nobody(config), "_Installation", input) + .then(() => + database.adapter.find("_Installation", installationSchema, {}, {}) + ) .then(results => { expect(results.length).toEqual(1); const obj = results[0]; - expect(obj.custom).toEqual('allowed'); + expect(obj.custom).toEqual("allowed"); done(); }) .catch(error => { @@ -267,35 +287,40 @@ describe('Installations', () => { // Note: did not port test 'TestObjectIDForIdentifiers' - it('merging when installationId already exists', done => { - const installId1 = '12345678-abcd-abcd-abcd-123456789abc'; - const t = '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; + it("merging when installationId already exists", done => { + const installId1 = "12345678-abcd-abcd-abcd-123456789abc"; + const t = + "11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306"; const input = { deviceToken: t, - deviceType: 'ios', + deviceType: "ios", installationId: installId1, - channels: ['foo', 'bar'], + channels: ["foo", "bar"], }; let firstObject; let secondObject; rest - .create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .create(config, auth.nobody(config), "_Installation", input) + .then(() => + database.adapter.find("_Installation", installationSchema, {}, {}) + ) .then(results => { expect(results.length).toEqual(1); firstObject = results[0]; delete input.deviceToken; delete input.channels; - input['foo'] = 'bar'; - return rest.create(config, auth.nobody(config), '_Installation', input); + input["foo"] = "bar"; + return rest.create(config, auth.nobody(config), "_Installation", input); }) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .then(() => + database.adapter.find("_Installation", installationSchema, {}, {}) + ) .then(results => { expect(results.length).toEqual(1); secondObject = results[0]; expect(firstObject._id).toEqual(secondObject._id); expect(secondObject.channels.length).toEqual(2); - expect(secondObject.foo).toEqual('bar'); + expect(secondObject.foo).toEqual("bar"); done(); }) .catch(error => { @@ -303,46 +328,63 @@ describe('Installations', () => { }); }); - it('merging when two objects both only have one id', done => { - const installId = '12345678-abcd-abcd-abcd-123456789abc'; - const t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; + it("merging when two objects both only have one id", done => { + const installId = "12345678-abcd-abcd-abcd-123456789abc"; + const t = + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; const input1 = { installationId: installId, - deviceType: 'ios', + deviceType: "ios", }; const input2 = { deviceToken: t, - deviceType: 'ios', + deviceType: "ios", }; const input3 = { deviceToken: t, installationId: installId, - deviceType: 'ios', + deviceType: "ios", }; let firstObject; let secondObject; rest - .create(config, auth.nobody(config), '_Installation', input1) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .create(config, auth.nobody(config), "_Installation", input1) + .then(() => + database.adapter.find("_Installation", installationSchema, {}, {}) + ) .then(results => { expect(results.length).toEqual(1); firstObject = results[0]; - return rest.create(config, auth.nobody(config), '_Installation', input2); + return rest.create( + config, + auth.nobody(config), + "_Installation", + input2 + ); }) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .then(() => + database.adapter.find("_Installation", installationSchema, {}, {}) + ) .then(results => { expect(results.length).toEqual(2); - if (results[0]['_id'] == firstObject._id) { + if (results[0]["_id"] == firstObject._id) { secondObject = results[1]; } else { secondObject = results[0]; } - return rest.create(config, auth.nobody(config), '_Installation', input3); + return rest.create( + config, + auth.nobody(config), + "_Installation", + input3 + ); }) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .then(() => + database.adapter.find("_Installation", installationSchema, {}, {}) + ) .then(results => { expect(results.length).toEqual(1); - expect(results[0]['_id']).toEqual(secondObject._id); + expect(results[0]["_id"]).toEqual(secondObject._id); done(); }) .catch(error => { @@ -351,29 +393,30 @@ describe('Installations', () => { }); }); - xit('creating multiple devices with same device token works', done => { - const installId1 = '11111111-abcd-abcd-abcd-123456789abc'; - const installId2 = '22222222-abcd-abcd-abcd-123456789abc'; - const installId3 = '33333333-abcd-abcd-abcd-123456789abc'; - const t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; + xit("creating multiple devices with same device token works", done => { + const installId1 = "11111111-abcd-abcd-abcd-123456789abc"; + const installId2 = "22222222-abcd-abcd-abcd-123456789abc"; + const installId3 = "33333333-abcd-abcd-abcd-123456789abc"; + const t = + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; const input = { installationId: installId1, - deviceType: 'ios', + deviceType: "ios", deviceToken: t, }; rest - .create(config, auth.nobody(config), '_Installation', input) + .create(config, auth.nobody(config), "_Installation", input) .then(() => { input.installationId = installId2; - return rest.create(config, auth.nobody(config), '_Installation', input); + return rest.create(config, auth.nobody(config), "_Installation", input); }) .then(() => { input.installationId = installId3; - return rest.create(config, auth.nobody(config), '_Installation', input); + return rest.create(config, auth.nobody(config), "_Installation", input); }) .then(() => database.adapter.find( - '_Installation', + "_Installation", { installationId: installId1 }, installationSchema, {} @@ -382,7 +425,7 @@ describe('Installations', () => { .then(results => { expect(results.length).toEqual(1); return database.adapter.find( - '_Installation', + "_Installation", { installationId: installId2 }, installationSchema, {} @@ -391,7 +434,7 @@ describe('Installations', () => { .then(results => { expect(results.length).toEqual(1); return database.adapter.find( - '_Installation', + "_Installation", { installationId: installId3 }, installationSchema, {} @@ -406,60 +449,75 @@ describe('Installations', () => { }); }); - it_id('95955e90-04bc-4437-920e-b84bc30dba01')(it)('updating with new channels', done => { - const input = { - installationId: '12345678-abcd-abcd-abcd-123456789abc', - deviceType: 'android', - channels: ['foo', 'bar'], - }; - rest - .create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) - .then(results => { - expect(results.length).toEqual(1); - const objectId = results[0].objectId; - const update = { - channels: ['baz'], - }; - return rest.update(config, auth.nobody(config), '_Installation', { objectId }, update); - }) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) - .then(results => { - expect(results.length).toEqual(1); - expect(results[0].channels.length).toEqual(1); - expect(results[0].channels[0]).toEqual('baz'); - done(); - }) - .catch(error => { - jfail(error); - done(); - }); - }); + it_id("95955e90-04bc-4437-920e-b84bc30dba01")(it)( + "updating with new channels", + done => { + const input = { + installationId: "12345678-abcd-abcd-abcd-123456789abc", + deviceType: "android", + channels: ["foo", "bar"], + }; + rest + .create(config, auth.nobody(config), "_Installation", input) + .then(() => + database.adapter.find("_Installation", installationSchema, {}, {}) + ) + .then(results => { + expect(results.length).toEqual(1); + const objectId = results[0].objectId; + const update = { + channels: ["baz"], + }; + return rest.update( + config, + auth.nobody(config), + "_Installation", + { objectId }, + update + ); + }) + .then(() => + database.adapter.find("_Installation", installationSchema, {}, {}) + ) + .then(results => { + expect(results.length).toEqual(1); + expect(results[0].channels.length).toEqual(1); + expect(results[0].channels[0]).toEqual("baz"); + done(); + }) + .catch(error => { + jfail(error); + done(); + }); + } + ); - it('update android fails with new installation id', done => { - const installId1 = '12345678-abcd-abcd-abcd-123456789abc'; - const installId2 = '87654321-abcd-abcd-abcd-123456789abc'; + it("update android fails with new installation id", done => { + const installId1 = "12345678-abcd-abcd-abcd-123456789abc"; + const installId2 = "87654321-abcd-abcd-abcd-123456789abc"; let input = { installationId: installId1, - deviceType: 'android', - channels: ['foo', 'bar'], + deviceType: "android", + channels: ["foo", "bar"], }; rest - .create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .create(config, auth.nobody(config), "_Installation", input) + .then(() => + database.adapter.find("_Installation", installationSchema, {}, {}) + ) .then(results => { expect(results.length).toEqual(1); input = { installationId: installId2 }; return rest.update( config, auth.nobody(config), - '_Installation', + "_Installation", { objectId: results[0].objectId }, input ); }) .then(() => { - fail('Updating the installation should have failed.'); + fail("Updating the installation should have failed."); done(); }) .catch(error => { @@ -468,30 +526,34 @@ describe('Installations', () => { }); }); - it('update ios fails with new deviceToken and no installationId', done => { - const a = '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; - const b = '91433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; + it("update ios fails with new deviceToken and no installationId", done => { + const a = + "11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306"; + const b = + "91433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306"; let input = { deviceToken: a, - deviceType: 'ios', - channels: ['foo', 'bar'], + deviceType: "ios", + channels: ["foo", "bar"], }; rest - .create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .create(config, auth.nobody(config), "_Installation", input) + .then(() => + database.adapter.find("_Installation", installationSchema, {}, {}) + ) .then(results => { expect(results.length).toEqual(1); input = { deviceToken: b }; return rest.update( config, auth.nobody(config), - '_Installation', + "_Installation", { objectId: results[0].objectId }, input ); }) .then(() => { - fail('Updating the installation should have failed.'); + fail("Updating the installation should have failed."); }) .catch(error => { expect(error.code).toEqual(136); @@ -499,35 +561,41 @@ describe('Installations', () => { }); }); - it('update ios updates device token', done => { - const installId = '12345678-abcd-abcd-abcd-123456789abc'; - const t = '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; - const u = '91433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; + it("update ios updates device token", done => { + const installId = "12345678-abcd-abcd-abcd-123456789abc"; + const t = + "11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306"; + const u = + "91433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306"; let input = { installationId: installId, - deviceType: 'ios', + deviceType: "ios", deviceToken: t, - channels: ['foo', 'bar'], + channels: ["foo", "bar"], }; rest - .create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .create(config, auth.nobody(config), "_Installation", input) + .then(() => + database.adapter.find("_Installation", installationSchema, {}, {}) + ) .then(results => { expect(results.length).toEqual(1); input = { installationId: installId, deviceToken: u, - deviceType: 'ios', + deviceType: "ios", }; return rest.update( config, auth.nobody(config), - '_Installation', + "_Installation", { objectId: results[0].objectId }, input ); }) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .then(() => + database.adapter.find("_Installation", installationSchema, {}, {}) + ) .then(results => { expect(results.length).toEqual(1); expect(results[0].deviceToken).toEqual(u); @@ -539,31 +607,33 @@ describe('Installations', () => { }); }); - it('update fails to change deviceType', done => { - const installId = '12345678-abcd-abcd-abcd-123456789abc'; + it("update fails to change deviceType", done => { + const installId = "12345678-abcd-abcd-abcd-123456789abc"; let input = { installationId: installId, - deviceType: 'android', - channels: ['foo', 'bar'], + deviceType: "android", + channels: ["foo", "bar"], }; rest - .create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .create(config, auth.nobody(config), "_Installation", input) + .then(() => + database.adapter.find("_Installation", installationSchema, {}, {}) + ) .then(results => { expect(results.length).toEqual(1); input = { - deviceType: 'ios', + deviceType: "ios", }; return rest.update( config, auth.nobody(config), - '_Installation', + "_Installation", { objectId: results[0].objectId }, input ); }) .then(() => { - fail('Should not have been able to update Installation.'); + fail("Should not have been able to update Installation."); done(); }) .catch(error => { @@ -572,58 +642,63 @@ describe('Installations', () => { }); }); - it('update android with custom field', done => { - const installId = '12345678-abcd-abcd-abcd-123456789abc'; + it("update android with custom field", done => { + const installId = "12345678-abcd-abcd-abcd-123456789abc"; let input = { installationId: installId, - deviceType: 'android', - channels: ['foo', 'bar'], + deviceType: "android", + channels: ["foo", "bar"], }; rest - .create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .create(config, auth.nobody(config), "_Installation", input) + .then(() => + database.adapter.find("_Installation", installationSchema, {}, {}) + ) .then(results => { expect(results.length).toEqual(1); input = { - custom: 'allowed', + custom: "allowed", }; return rest.update( config, auth.nobody(config), - '_Installation', + "_Installation", { objectId: results[0].objectId }, input ); }) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .then(() => + database.adapter.find("_Installation", installationSchema, {}, {}) + ) .then(results => { expect(results.length).toEqual(1); - expect(results[0]['custom']).toEqual('allowed'); + expect(results[0]["custom"]).toEqual("allowed"); done(); }); }); - it('update android device token with duplicate device token', async () => { - const installId1 = '11111111-abcd-abcd-abcd-123456789abc'; - const installId2 = '22222222-abcd-abcd-abcd-123456789abc'; - const t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; + it("update android device token with duplicate device token", async () => { + const installId1 = "11111111-abcd-abcd-abcd-123456789abc"; + const installId2 = "22222222-abcd-abcd-abcd-123456789abc"; + const t = + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; let input = { installationId: installId1, deviceToken: t, - deviceType: 'android', + deviceType: "android", }; - await rest.create(config, auth.nobody(config), '_Installation', input); + await rest.create(config, auth.nobody(config), "_Installation", input); input = { installationId: installId2, - deviceType: 'android', + deviceType: "android", }; - await rest.create(config, auth.nobody(config), '_Installation', input); + await rest.create(config, auth.nobody(config), "_Installation", input); await delay(100); let results = await database.adapter.find( - '_Installation', + "_Installation", installationSchema, { installationId: installId1 }, {} @@ -632,7 +707,7 @@ describe('Installations', () => { const firstObject = results[0]; results = await database.adapter.find( - '_Installation', + "_Installation", installationSchema, { installationId: installId2 }, {} @@ -648,13 +723,13 @@ describe('Installations', () => { await rest.update( config, auth.nobody(config), - '_Installation', + "_Installation", { objectId: secondObject.objectId }, input ); await delay(100); results = await database.adapter.find( - '_Installation', + "_Installation", installationSchema, { objectId: firstObject.objectId }, {} @@ -662,30 +737,31 @@ describe('Installations', () => { expect(results.length).toEqual(0); }); - it('update ios device token with duplicate device token', done => { - const installId1 = '11111111-abcd-abcd-abcd-123456789abc'; - const installId2 = '22222222-abcd-abcd-abcd-123456789abc'; - const t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; + it("update ios device token with duplicate device token", done => { + const installId1 = "11111111-abcd-abcd-abcd-123456789abc"; + const installId2 = "22222222-abcd-abcd-abcd-123456789abc"; + const t = + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; let input = { installationId: installId1, deviceToken: t, - deviceType: 'ios', + deviceType: "ios", }; let firstObject; let secondObject; rest - .create(config, auth.nobody(config), '_Installation', input) + .create(config, auth.nobody(config), "_Installation", input) .then(() => { input = { installationId: installId2, - deviceType: 'ios', + deviceType: "ios", }; - return rest.create(config, auth.nobody(config), '_Installation', input); + return rest.create(config, auth.nobody(config), "_Installation", input); }) .then(() => delay(100)) .then(() => database.adapter.find( - '_Installation', + "_Installation", installationSchema, { installationId: installId1 }, {} @@ -698,7 +774,7 @@ describe('Installations', () => { .then(() => delay(100)) .then(() => database.adapter.find( - '_Installation', + "_Installation", installationSchema, { installationId: installId2 }, {} @@ -715,7 +791,7 @@ describe('Installations', () => { return rest.update( config, auth.nobody(config), - '_Installation', + "_Installation", { objectId: secondObject.objectId }, input ); @@ -723,7 +799,7 @@ describe('Installations', () => { .then(() => delay(100)) .then(() => database.adapter.find( - '_Installation', + "_Installation", installationSchema, { objectId: firstObject.objectId }, {} @@ -740,24 +816,27 @@ describe('Installations', () => { }); }); - xit('update ios device token with duplicate token different app', done => { - const installId1 = '11111111-abcd-abcd-abcd-123456789abc'; - const installId2 = '22222222-abcd-abcd-abcd-123456789abc'; - const t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; + xit("update ios device token with duplicate token different app", done => { + const installId1 = "11111111-abcd-abcd-abcd-123456789abc"; + const installId2 = "22222222-abcd-abcd-abcd-123456789abc"; + const t = + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; const input = { installationId: installId1, deviceToken: t, - deviceType: 'ios', - appIdentifier: 'foo', + deviceType: "ios", + appIdentifier: "foo", }; rest - .create(config, auth.nobody(config), '_Installation', input) + .create(config, auth.nobody(config), "_Installation", input) .then(() => { input.installationId = installId2; - input.appIdentifier = 'bar'; - return rest.create(config, auth.nobody(config), '_Installation', input); + input.appIdentifier = "bar"; + return rest.create(config, auth.nobody(config), "_Installation", input); }) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .then(() => + database.adapter.find("_Installation", installationSchema, {}, {}) + ) .then(results => { // The first object should have been deleted during merge expect(results.length).toEqual(1); @@ -770,16 +849,19 @@ describe('Installations', () => { }); }); - it('update ios token and channels', done => { - const installId = '12345678-abcd-abcd-abcd-123456789abc'; - const t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; + it("update ios token and channels", done => { + const installId = "12345678-abcd-abcd-abcd-123456789abc"; + const t = + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; let input = { installationId: installId, - deviceType: 'ios', + deviceType: "ios", }; rest - .create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .create(config, auth.nobody(config), "_Installation", input) + .then(() => + database.adapter.find("_Installation", installationSchema, {}, {}) + ) .then(results => { expect(results.length).toEqual(1); input = { @@ -789,12 +871,14 @@ describe('Installations', () => { return rest.update( config, auth.nobody(config), - '_Installation', + "_Installation", { objectId: results[0].objectId }, input ); }) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .then(() => + database.adapter.find("_Installation", installationSchema, {}, {}) + ) .then(results => { expect(results.length).toEqual(1); expect(results[0].installationId).toEqual(installId); @@ -808,46 +892,54 @@ describe('Installations', () => { }); }); - it('update ios linking two existing objects', done => { - const installId = '12345678-abcd-abcd-abcd-123456789abc'; - const t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; + it("update ios linking two existing objects", done => { + const installId = "12345678-abcd-abcd-abcd-123456789abc"; + const t = + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; let input = { installationId: installId, - deviceType: 'ios', + deviceType: "ios", }; rest - .create(config, auth.nobody(config), '_Installation', input) + .create(config, auth.nobody(config), "_Installation", input) .then(() => { input = { deviceToken: t, - deviceType: 'ios', + deviceType: "ios", }; - return rest.create(config, auth.nobody(config), '_Installation', input); + return rest.create(config, auth.nobody(config), "_Installation", input); }) .then(() => - database.adapter.find('_Installation', installationSchema, { deviceToken: t }, {}) + database.adapter.find( + "_Installation", + installationSchema, + { deviceToken: t }, + {} + ) ) .then(results => { expect(results.length).toEqual(1); input = { deviceToken: t, installationId: installId, - deviceType: 'ios', + deviceType: "ios", }; return rest.update( config, auth.nobody(config), - '_Installation', + "_Installation", { objectId: results[0].objectId }, input ); }) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .then(() => + database.adapter.find("_Installation", installationSchema, {}, {}) + ) .then(results => { expect(results.length).toEqual(1); expect(results[0].installationId).toEqual(installId); expect(results[0].deviceToken).toEqual(t); - expect(results[0].deviceType).toEqual('ios'); + expect(results[0].deviceType).toEqual("ios"); done(); }) .catch(error => { @@ -856,52 +948,65 @@ describe('Installations', () => { }); }); - it_id('22311bc7-3f4f-42c1-a958-57083929e80d')(it)( - 'update is linking two existing objects w/ increment', + it_id("22311bc7-3f4f-42c1-a958-57083929e80d")(it)( + "update is linking two existing objects w/ increment", done => { - const installId = '12345678-abcd-abcd-abcd-123456789abc'; - const t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; + const installId = "12345678-abcd-abcd-abcd-123456789abc"; + const t = + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; let input = { installationId: installId, - deviceType: 'ios', + deviceType: "ios", }; rest - .create(config, auth.nobody(config), '_Installation', input) + .create(config, auth.nobody(config), "_Installation", input) .then(() => { input = { deviceToken: t, - deviceType: 'ios', + deviceType: "ios", }; - return rest.create(config, auth.nobody(config), '_Installation', input); + return rest.create( + config, + auth.nobody(config), + "_Installation", + input + ); }) .then(() => - database.adapter.find('_Installation', installationSchema, { deviceToken: t }, {}) + database.adapter.find( + "_Installation", + installationSchema, + { deviceToken: t }, + {} + ) ) .then(results => { expect(results.length).toEqual(1); input = { deviceToken: t, installationId: installId, - deviceType: 'ios', + deviceType: "ios", score: { - __op: 'Increment', + __op: "Increment", amount: 1, }, }; return rest.update( config, auth.nobody(config), - '_Installation', + "_Installation", { objectId: results[0].objectId }, input ); }) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .then(() => + database.adapter.find("_Installation", installationSchema, {}, {}) + ) .then(results => { expect(results.length).toEqual(1); expect(results[0].installationId).toEqual(installId); expect(results[0].deviceToken).toEqual(t); - expect(results[0].deviceType).toEqual('ios'); + expect(results[0].deviceType).toEqual("ios"); expect(results[0].score).toEqual(1); done(); }) @@ -912,29 +1017,37 @@ describe('Installations', () => { } ); - it('update is linking two existing with installation id', done => { - const installId = '12345678-abcd-abcd-abcd-123456789abc'; - const t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; + it("update is linking two existing with installation id", done => { + const installId = "12345678-abcd-abcd-abcd-123456789abc"; + const t = + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; let input = { installationId: installId, - deviceType: 'ios', + deviceType: "ios", }; let installObj; let tokenObj; rest - .create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .create(config, auth.nobody(config), "_Installation", input) + .then(() => + database.adapter.find("_Installation", installationSchema, {}, {}) + ) .then(results => { expect(results.length).toEqual(1); installObj = results[0]; input = { deviceToken: t, - deviceType: 'ios', + deviceType: "ios", }; - return rest.create(config, auth.nobody(config), '_Installation', input); + return rest.create(config, auth.nobody(config), "_Installation", input); }) .then(() => - database.adapter.find('_Installation', installationSchema, { deviceToken: t }, {}) + database.adapter.find( + "_Installation", + installationSchema, + { deviceToken: t }, + {} + ) ) .then(results => { expect(results.length).toEqual(1); @@ -942,19 +1055,19 @@ describe('Installations', () => { input = { installationId: installId, deviceToken: t, - deviceType: 'ios', + deviceType: "ios", }; return rest.update( config, auth.nobody(config), - '_Installation', + "_Installation", { objectId: installObj.objectId }, input ); }) .then(() => database.adapter.find( - '_Installation', + "_Installation", installationSchema, { objectId: tokenObj.objectId }, {} @@ -972,31 +1085,44 @@ describe('Installations', () => { }); }); - it_id('f2975078-eab7-4287-a932-288842e3cfb9')(it)( - 'update is linking two existing with installation id w/ op', + it_id("f2975078-eab7-4287-a932-288842e3cfb9")(it)( + "update is linking two existing with installation id w/ op", done => { - const installId = '12345678-abcd-abcd-abcd-123456789abc'; - const t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; + const installId = "12345678-abcd-abcd-abcd-123456789abc"; + const t = + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; let input = { installationId: installId, - deviceType: 'ios', + deviceType: "ios", }; let installObj; let tokenObj; rest - .create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .create(config, auth.nobody(config), "_Installation", input) + .then(() => + database.adapter.find("_Installation", installationSchema, {}, {}) + ) .then(results => { expect(results.length).toEqual(1); installObj = results[0]; input = { deviceToken: t, - deviceType: 'ios', + deviceType: "ios", }; - return rest.create(config, auth.nobody(config), '_Installation', input); + return rest.create( + config, + auth.nobody(config), + "_Installation", + input + ); }) .then(() => - database.adapter.find('_Installation', installationSchema, { deviceToken: t }, {}) + database.adapter.find( + "_Installation", + installationSchema, + { deviceToken: t }, + {} + ) ) .then(results => { expect(results.length).toEqual(1); @@ -1004,23 +1130,23 @@ describe('Installations', () => { input = { installationId: installId, deviceToken: t, - deviceType: 'ios', + deviceType: "ios", score: { - __op: 'Increment', + __op: "Increment", amount: 1, }, }; return rest.update( config, auth.nobody(config), - '_Installation', + "_Installation", { objectId: installObj.objectId }, input ); }) .then(() => database.adapter.find( - '_Installation', + "_Installation", installationSchema, { objectId: tokenObj.objectId }, {} @@ -1040,7 +1166,7 @@ describe('Installations', () => { } ); - it('ios merge existing same token no installation id', done => { + it("ios merge existing same token no installation id", done => { // Test creating installation when there is an existing object with the // same device token but no installation ID. This is possible when // developers import device tokens from another push provider; the import @@ -1051,25 +1177,30 @@ describe('Installations', () => { // imported installation, then we should reuse the existing installation // object in case the developer already added additional fields via Data // Browser or REST API (e.g. channel targeting info). - const t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; - const installId = '12345678-abcd-abcd-abcd-123456789abc'; + const t = + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + const installId = "12345678-abcd-abcd-abcd-123456789abc"; let input = { deviceToken: t, - deviceType: 'ios', + deviceType: "ios", }; rest - .create(config, auth.nobody(config), '_Installation', input) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .create(config, auth.nobody(config), "_Installation", input) + .then(() => + database.adapter.find("_Installation", installationSchema, {}, {}) + ) .then(results => { expect(results.length).toEqual(1); input = { installationId: installId, deviceToken: t, - deviceType: 'ios', + deviceType: "ios", }; - return rest.create(config, auth.nobody(config), '_Installation', input); + return rest.create(config, auth.nobody(config), "_Installation", input); }) - .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .then(() => + database.adapter.find("_Installation", installationSchema, {}, {}) + ) .then(results => { expect(results.length).toEqual(1); expect(results[0].deviceToken).toEqual(t); @@ -1083,23 +1214,25 @@ describe('Installations', () => { }); }); - it('allows you to get your own installation (regression test for #1718)', done => { - const installId = '12345678-abcd-abcd-abcd-123456789abc'; - const device = 'android'; + it("allows you to get your own installation (regression test for #1718)", done => { + const installId = "12345678-abcd-abcd-abcd-123456789abc"; + const device = "android"; const input = { installationId: installId, deviceType: device, }; rest - .create(config, auth.nobody(config), '_Installation', input) + .create(config, auth.nobody(config), "_Installation", input) .then(createResult => { const headers = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; return request({ headers: headers, - url: 'http://localhost:8378/1/installations/' + createResult.response.objectId, + url: + "http://localhost:8378/1/installations/" + + createResult.response.objectId, }).then(response => { const body = response.data; expect(body.objectId).toEqual(createResult.response.objectId); @@ -1108,30 +1241,30 @@ describe('Installations', () => { }) .catch(error => { console.log(error); - fail('failed'); + fail("failed"); done(); }); }); - it('allows you to update installation from header (#2090)', done => { - const installId = '12345678-abcd-abcd-abcd-123456789abc'; - const device = 'android'; + it("allows you to update installation from header (#2090)", done => { + const installId = "12345678-abcd-abcd-abcd-123456789abc"; + const device = "android"; const input = { installationId: installId, deviceType: device, }; rest - .create(config, auth.nobody(config), '_Installation', input) + .create(config, auth.nobody(config), "_Installation", input) .then(() => { const headers = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Installation-Id': installId, + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "X-Parse-Installation-Id": installId, }; request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/classes/_Installation', + url: "http://localhost:8378/1/classes/_Installation", json: true, body: { date: new Date(), @@ -1145,155 +1278,167 @@ describe('Installations', () => { }) .catch(error => { console.log(error); - fail('failed'); + fail("failed"); done(); }); }); - it('allows you to update installation with masterKey', done => { - const installId = '12345678-abcd-abcd-abcd-123456789abc'; - const device = 'android'; + it("allows you to update installation with masterKey", done => { + const installId = "12345678-abcd-abcd-abcd-123456789abc"; + const device = "android"; const input = { installationId: installId, deviceType: device, }; rest - .create(config, auth.nobody(config), '_Installation', input) + .create(config, auth.nobody(config), "_Installation", input) .then(createResult => { const installationObj = Parse.Installation.createWithoutData( createResult.response.objectId ); - installationObj.set('customField', 'custom value'); + installationObj.set("customField", "custom value"); return installationObj.save(null, { useMasterKey: true }); }) .then(updateResult => { expect(updateResult).not.toBeUndefined(); - expect(updateResult.get('customField')).toEqual('custom value'); + expect(updateResult.get("customField")).toEqual("custom value"); done(); }) .catch(error => { console.log(error); - fail('failed'); + fail("failed"); done(); }); }); - it('should properly handle installation save #2780', done => { - const installId = '12345678-abcd-abcd-abcd-123456789abc'; - const device = 'android'; + it("should properly handle installation save #2780", done => { + const installId = "12345678-abcd-abcd-abcd-123456789abc"; + const device = "android"; const input = { installationId: installId, deviceType: device, }; - rest.create(config, auth.nobody(config), '_Installation', input).then(() => { - const query = new Parse.Query(Parse.Installation); - query.equalTo('installationId', installId); - query - .first({ useMasterKey: true }) - .then(installation => { - return installation.save( - { - key: 'value', + rest + .create(config, auth.nobody(config), "_Installation", input) + .then(() => { + const query = new Parse.Query(Parse.Installation); + query.equalTo("installationId", installId); + query + .first({ useMasterKey: true }) + .then(installation => { + return installation.save( + { + key: "value", + }, + { useMasterKey: true } + ); + }) + .then( + () => { + done(); }, - { useMasterKey: true } + err => { + jfail(err); + done(); + } ); - }) - .then( - () => { - done(); - }, - err => { - jfail(err); - done(); - } - ); - }); + }); }); - it('should properly reject updating installationId', done => { - const installId = '12345678-abcd-abcd-abcd-123456789abc'; - const device = 'android'; + it("should properly reject updating installationId", done => { + const installId = "12345678-abcd-abcd-abcd-123456789abc"; + const device = "android"; const input = { installationId: installId, deviceType: device, }; - rest.create(config, auth.nobody(config), '_Installation', input).then(() => { - const query = new Parse.Query(Parse.Installation); - query.equalTo('installationId', installId); - query - .first({ useMasterKey: true }) - .then(installation => { - return installation.save( - { - key: 'value', - installationId: '22222222-abcd-abcd-abcd-123456789abc', + rest + .create(config, auth.nobody(config), "_Installation", input) + .then(() => { + const query = new Parse.Query(Parse.Installation); + query.equalTo("installationId", installId); + query + .first({ useMasterKey: true }) + .then(installation => { + return installation.save( + { + key: "value", + installationId: "22222222-abcd-abcd-abcd-123456789abc", + }, + { useMasterKey: true } + ); + }) + .then( + () => { + fail("should not succeed"); + done(); }, - { useMasterKey: true } + err => { + expect(err.code).toBe(136); + expect(err.message).toBe( + "installationId may not be changed in this operation" + ); + done(); + } ); - }) - .then( - () => { - fail('should not succeed'); - done(); - }, - err => { - expect(err.code).toBe(136); - expect(err.message).toBe('installationId may not be changed in this operation'); - done(); - } - ); - }); + }); }); - it_id('e581faea-c1b4-4c64-af8c-52287ce6cd06')(it)('can use push with beforeSave', async () => { - const input = { - deviceToken: '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306', - deviceType: 'ios', - }; - await rest.create(config, auth.nobody(config), '_Installation', input); - const functions = { - beforeSave() {}, - afterSave() {}, - }; - spyOn(functions, 'beforeSave').and.callThrough(); - spyOn(functions, 'afterSave').and.callThrough(); - Parse.Cloud.beforeSave(Parse.Installation, functions.beforeSave); - Parse.Cloud.afterSave(Parse.Installation, functions.afterSave); - await Parse.Push.send({ - where: { - deviceType: 'ios', - }, - data: { - badge: 'increment', - alert: 'Hello world!', - }, - }); + it_id("e581faea-c1b4-4c64-af8c-52287ce6cd06")(it)( + "can use push with beforeSave", + async () => { + const input = { + deviceToken: + "11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306", + deviceType: "ios", + }; + await rest.create(config, auth.nobody(config), "_Installation", input); + const functions = { + beforeSave() {}, + afterSave() {}, + }; + spyOn(functions, "beforeSave").and.callThrough(); + spyOn(functions, "afterSave").and.callThrough(); + Parse.Cloud.beforeSave(Parse.Installation, functions.beforeSave); + Parse.Cloud.afterSave(Parse.Installation, functions.afterSave); + await Parse.Push.send({ + where: { + deviceType: "ios", + }, + data: { + badge: "increment", + alert: "Hello world!", + }, + }); - await Parse.Push.send({ - where: { - deviceType: 'ios', - }, - data: { - badge: 'increment', - alert: 'Hello world!', - }, - }); + await Parse.Push.send({ + where: { + deviceType: "ios", + }, + data: { + badge: "increment", + alert: "Hello world!", + }, + }); - await Parse.Push.send({ - where: { - deviceType: 'ios', - }, - data: { - badge: 'increment', - alert: 'Hello world!', - }, - }); - await new Promise(resolve => setTimeout(resolve, 1000)); - const installation = await new Parse.Query(Parse.Installation).first({ useMasterKey: true }); - expect(installation.get('badge')).toEqual(3); - expect(functions.beforeSave).not.toHaveBeenCalled(); - expect(functions.afterSave).not.toHaveBeenCalled(); - }); + await Parse.Push.send({ + where: { + deviceType: "ios", + }, + data: { + badge: "increment", + alert: "Hello world!", + }, + }); + await new Promise(resolve => setTimeout(resolve, 1000)); + const installation = await new Parse.Query(Parse.Installation).first({ + useMasterKey: true, + }); + expect(installation.get("badge")).toEqual(3); + expect(functions.beforeSave).not.toHaveBeenCalled(); + expect(functions.afterSave).not.toHaveBeenCalled(); + } + ); // TODO: Look at additional tests from installation_collection_test.go:882 // TODO: Do we need to support _tombstone disabling of installations? diff --git a/spec/ParseLiveQuery.spec.js b/spec/ParseLiveQuery.spec.js index 635432e906..e1709f1245 100644 --- a/spec/ParseLiveQuery.spec.js +++ b/spec/ParseLiveQuery.spec.js @@ -1,31 +1,37 @@ -'use strict'; -const http = require('http'); -const Auth = require('../lib/Auth'); -const UserController = require('../lib/Controllers/UserController').UserController; -const Config = require('../lib/Config'); -const ParseServer = require('../lib/index').ParseServer; -const triggers = require('../lib/triggers'); -const { resolvingPromise, sleep, getConnectionsCount } = require('../lib/TestUtils'); -const request = require('../lib/request'); +"use strict"; +const http = require("http"); +const Auth = require("../lib/Auth"); +const UserController = + require("../lib/Controllers/UserController").UserController; +const Config = require("../lib/Config"); +const ParseServer = require("../lib/index").ParseServer; +const triggers = require("../lib/triggers"); +const { + resolvingPromise, + sleep, + getConnectionsCount, +} = require("../lib/TestUtils"); +const request = require("../lib/request"); const validatorFail = () => { - throw 'you are not authorized'; + throw "you are not authorized"; }; -describe('ParseLiveQuery', function () { +describe("ParseLiveQuery", function () { beforeEach(() => { Parse.CoreManager.getLiveQueryController().setDefaultLiveQueryClient(null); }); afterEach(async () => { - const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); + const client = + await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); await client.close(); }); - it('access user on onLiveQueryEvent disconnect', async done => { + it("access user on onLiveQueryEvent disconnect", async done => { const requestedUser = new Parse.User(); - requestedUser.setUsername('username'); - requestedUser.setPassword('password'); + requestedUser.setUsername("username"); + requestedUser.setPassword("password"); Parse.Cloud.onLiveQueryEvent(req => { const { event, sessionToken } = req; - if (event === 'ws_disconnect') { + if (event === "ws_disconnect") { Parse.Cloud._removeAllHooks(); expect(sessionToken).toBeDefined(); expect(sessionToken).toBe(requestedUser.getSessionToken()); @@ -35,29 +41,30 @@ describe('ParseLiveQuery', function () { await requestedUser.signUp(); const query = new Parse.Query(TestObject); await query.subscribe(); - const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); + const client = + await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); await client.close(); }); - it('can subscribe to query', async done => { + it("can subscribe to query", async done => { const object = new TestObject(); await object.save(); const query = new Parse.Query(TestObject); - query.equalTo('objectId', object.id); + query.equalTo("objectId", object.id); const subscription = await query.subscribe(); - subscription.on('update', object => { - expect(object.get('foo')).toBe('bar'); + subscription.on("update", object => { + expect(object.get("foo")).toBe("bar"); done(); }); - object.set({ foo: 'bar' }); + object.set({ foo: "bar" }); await object.save(); }); - it('can use patterns in className', async done => { + it("can use patterns in className", async done => { await reconfigureServer({ liveQuery: { - classNames: ['Test.*'], + classNames: ["Test.*"], }, startLiveQueryServer: true, verbose: false, @@ -67,135 +74,135 @@ describe('ParseLiveQuery', function () { await object.save(); const query = new Parse.Query(TestObject); - query.equalTo('objectId', object.id); + query.equalTo("objectId", object.id); const subscription = await query.subscribe(); - subscription.on('update', object => { - expect(object.get('foo')).toBe('bar'); + subscription.on("update", object => { + expect(object.get("foo")).toBe("bar"); done(); }); - object.set({ foo: 'bar' }); + object.set({ foo: "bar" }); await object.save(); }); - it('expect afterEvent create', async done => { + it("expect afterEvent create", async done => { await reconfigureServer({ liveQuery: { - classNames: ['TestObject'], + classNames: ["TestObject"], }, startLiveQueryServer: true, verbose: false, silent: true, }); - Parse.Cloud.afterLiveQueryEvent('TestObject', req => { - expect(req.event).toBe('create'); + Parse.Cloud.afterLiveQueryEvent("TestObject", req => { + expect(req.event).toBe("create"); expect(req.user).toBeUndefined(); - expect(req.object.get('foo')).toBe('bar'); + expect(req.object.get("foo")).toBe("bar"); }); const query = new Parse.Query(TestObject); const subscription = await query.subscribe(); - subscription.on('create', object => { - expect(object.get('foo')).toBe('bar'); + subscription.on("create", object => { + expect(object.get("foo")).toBe("bar"); done(); }); const object = new TestObject(); - object.set('foo', 'bar'); + object.set("foo", "bar"); await object.save(); }); - it('expect afterEvent payload', async done => { + it("expect afterEvent payload", async done => { const object = new TestObject(); await object.save(); - Parse.Cloud.afterLiveQueryEvent('TestObject', req => { - expect(req.event).toBe('update'); + Parse.Cloud.afterLiveQueryEvent("TestObject", req => { + expect(req.event).toBe("update"); expect(req.user).toBeUndefined(); - expect(req.object.get('foo')).toBe('bar'); - expect(req.original.get('foo')).toBeUndefined(); + expect(req.object.get("foo")).toBe("bar"); + expect(req.original.get("foo")).toBeUndefined(); done(); }); const query = new Parse.Query(TestObject); - query.equalTo('objectId', object.id); + query.equalTo("objectId", object.id); await query.subscribe(); - object.set({ foo: 'bar' }); + object.set({ foo: "bar" }); await object.save(); }); - it('expect afterEvent enter', async done => { - Parse.Cloud.afterLiveQueryEvent('TestObject', req => { - expect(req.event).toBe('enter'); + it("expect afterEvent enter", async done => { + Parse.Cloud.afterLiveQueryEvent("TestObject", req => { + expect(req.event).toBe("enter"); expect(req.user).toBeUndefined(); - expect(req.object.get('foo')).toBe('bar'); - expect(req.original.get('foo')).toBeUndefined(); + expect(req.object.get("foo")).toBe("bar"); + expect(req.original.get("foo")).toBeUndefined(); }); const object = new TestObject(); await object.save(); const query = new Parse.Query(TestObject); - query.equalTo('foo', 'bar'); + query.equalTo("foo", "bar"); const subscription = await query.subscribe(); - subscription.on('enter', object => { - expect(object.get('foo')).toBe('bar'); + subscription.on("enter", object => { + expect(object.get("foo")).toBe("bar"); done(); }); - object.set('foo', 'bar'); + object.set("foo", "bar"); await object.save(); }); - it('expect afterEvent leave', async done => { - Parse.Cloud.afterLiveQueryEvent('TestObject', req => { - expect(req.event).toBe('leave'); + it("expect afterEvent leave", async done => { + Parse.Cloud.afterLiveQueryEvent("TestObject", req => { + expect(req.event).toBe("leave"); expect(req.user).toBeUndefined(); - expect(req.object.get('foo')).toBeUndefined(); - expect(req.original.get('foo')).toBe('bar'); + expect(req.object.get("foo")).toBeUndefined(); + expect(req.original.get("foo")).toBe("bar"); }); const object = new TestObject(); - object.set('foo', 'bar'); + object.set("foo", "bar"); await object.save(); const query = new Parse.Query(TestObject); - query.equalTo('foo', 'bar'); + query.equalTo("foo", "bar"); const subscription = await query.subscribe(); - subscription.on('leave', object => { - expect(object.get('foo')).toBeUndefined(); + subscription.on("leave", object => { + expect(object.get("foo")).toBeUndefined(); done(); }); - object.unset('foo'); + object.unset("foo"); await object.save(); }); - it('expect afterEvent delete', async done => { - Parse.Cloud.afterLiveQueryEvent('TestObject', req => { - expect(req.event).toBe('delete'); + it("expect afterEvent delete", async done => { + Parse.Cloud.afterLiveQueryEvent("TestObject", req => { + expect(req.event).toBe("delete"); expect(req.user).toBeUndefined(); - req.object.set('foo', 'bar'); + req.object.set("foo", "bar"); }); const object = new TestObject(); await object.save(); const query = new Parse.Query(TestObject); - query.equalTo('objectId', object.id); + query.equalTo("objectId", object.id); const subscription = await query.subscribe(); - subscription.on('delete', object => { - expect(object.get('foo')).toBe('bar'); + subscription.on("delete", object => { + expect(object.get("foo")).toBe("bar"); done(); }); await object.destroy(); }); - it('can handle afterEvent modification', async done => { + it("can handle afterEvent modification", async done => { await reconfigureServer({ liveQuery: { - classNames: ['TestObject'], + classNames: ["TestObject"], }, startLiveQueryServer: true, verbose: false, @@ -204,30 +211,30 @@ describe('ParseLiveQuery', function () { const object = new TestObject(); await object.save(); - Parse.Cloud.afterLiveQueryEvent('TestObject', req => { + Parse.Cloud.afterLiveQueryEvent("TestObject", req => { const current = req.object; - current.set('foo', 'yolo'); + current.set("foo", "yolo"); const original = req.original; - original.set('yolo', 'foo'); + original.set("yolo", "foo"); }); const query = new Parse.Query(TestObject); - query.equalTo('objectId', object.id); + query.equalTo("objectId", object.id); const subscription = await query.subscribe(); - subscription.on('update', (object, original) => { - expect(object.get('foo')).toBe('yolo'); - expect(original.get('yolo')).toBe('foo'); + subscription.on("update", (object, original) => { + expect(object.get("foo")).toBe("yolo"); + expect(original.get("yolo")).toBe("foo"); done(); }); - object.set({ foo: 'bar' }); + object.set({ foo: "bar" }); await object.save(); }); - it('can return different object in afterEvent', async done => { + it("can return different object in afterEvent", async done => { await reconfigureServer({ liveQuery: { - classNames: ['TestObject'], + classNames: ["TestObject"], }, startLiveQueryServer: true, verbose: false, @@ -236,26 +243,26 @@ describe('ParseLiveQuery', function () { const object = new TestObject(); await object.save(); - Parse.Cloud.afterLiveQueryEvent('TestObject', req => { - const object = new Parse.Object('Yolo'); + Parse.Cloud.afterLiveQueryEvent("TestObject", req => { + const object = new Parse.Object("Yolo"); req.object = object; }); const query = new Parse.Query(TestObject); - query.equalTo('objectId', object.id); + query.equalTo("objectId", object.id); const subscription = await query.subscribe(); - subscription.on('update', object => { - expect(object.className).toBe('Yolo'); + subscription.on("update", object => { + expect(object.className).toBe("Yolo"); done(); }); - object.set({ foo: 'bar' }); + object.set({ foo: "bar" }); await object.save(); }); - it('can handle afterEvent throw', async done => { + it("can handle afterEvent throw", async done => { await reconfigureServer({ liveQuery: { - classNames: ['TestObject'], + classNames: ["TestObject"], }, startLiveQueryServer: true, verbose: false, @@ -265,28 +272,28 @@ describe('ParseLiveQuery', function () { const object = new TestObject(); await object.save(); - Parse.Cloud.afterLiveQueryEvent('TestObject', () => { - throw 'Throw error from LQ afterEvent.'; + Parse.Cloud.afterLiveQueryEvent("TestObject", () => { + throw "Throw error from LQ afterEvent."; }); const query = new Parse.Query(TestObject); - query.equalTo('objectId', object.id); + query.equalTo("objectId", object.id); const subscription = await query.subscribe(); - subscription.on('update', () => { - fail('update should not have been called.'); + subscription.on("update", () => { + fail("update should not have been called."); }); - subscription.on('error', e => { - expect(e).toBe('Throw error from LQ afterEvent.'); + subscription.on("error", e => { + expect(e).toBe("Throw error from LQ afterEvent."); done(); }); - object.set({ foo: 'bar' }); + object.set({ foo: "bar" }); await object.save(); }); - it('can log on afterLiveQueryEvent throw', async () => { + it("can log on afterLiveQueryEvent throw", async () => { await reconfigureServer({ liveQuery: { - classNames: ['TestObject'], + classNames: ["TestObject"], }, startLiveQueryServer: true, verbose: false, @@ -296,11 +303,11 @@ describe('ParseLiveQuery', function () { const object = new TestObject(); await object.save(); - const logger = require('../lib/logger').logger; - spyOn(logger, 'error').and.callFake(() => {}); + const logger = require("../lib/logger").logger; + spyOn(logger, "error").and.callFake(() => {}); let session = undefined; - Parse.Cloud.afterLiveQueryEvent('TestObject', ({ sessionToken }) => { + Parse.Cloud.afterLiveQueryEvent("TestObject", ({ sessionToken }) => { session = sessionToken; /* eslint-disable no-undef */ foo.bar(); @@ -308,90 +315,90 @@ describe('ParseLiveQuery', function () { }); const query = new Parse.Query(TestObject); - query.equalTo('objectId', object.id); + query.equalTo("objectId", object.id); const subscription = await query.subscribe(); - object.set({ foo: 'bar' }); + object.set({ foo: "bar" }); await object.save(); - await new Promise(resolve => subscription.on('error', resolve)); + await new Promise(resolve => subscription.on("error", resolve)); expect(logger.error).toHaveBeenCalledWith( `Failed running afterLiveQueryEvent on class TestObject for event update with session ${session} with:\n Error: {"message":"foo is not defined","code":141}` ); }); - it('can handle afterEvent sendEvent to false', async () => { + it("can handle afterEvent sendEvent to false", async () => { const object = new TestObject(); await object.save(); const promise = resolvingPromise(); - Parse.Cloud.afterLiveQueryEvent('TestObject', req => { + Parse.Cloud.afterLiveQueryEvent("TestObject", req => { const current = req.object; const original = req.original; - if (current.get('foo') != original.get('foo')) { + if (current.get("foo") != original.get("foo")) { req.sendEvent = false; } promise.resolve(); }); const query = new Parse.Query(TestObject); - query.equalTo('objectId', object.id); + query.equalTo("objectId", object.id); const subscription = await query.subscribe(); - subscription.on('update', () => { - fail('update should not have been called.'); + subscription.on("update", () => { + fail("update should not have been called."); }); - subscription.on('error', () => { - fail('error should not have been called.'); + subscription.on("error", () => { + fail("error should not have been called."); }); - object.set({ foo: 'bar' }); + object.set({ foo: "bar" }); await object.save(); await promise; }); - it('can handle live query with fields', async () => { + it("can handle live query with fields", async () => { await reconfigureServer({ liveQuery: { - classNames: ['Test'], + classNames: ["Test"], }, startLiveQueryServer: true, }); - const query = new Parse.Query('Test'); - query.watch('yolo'); + const query = new Parse.Query("Test"); + query.watch("yolo"); const subscription = await query.subscribe(); const spy = { create(obj) { - if (!obj.get('yolo')) { - fail('create should not have been called'); + if (!obj.get("yolo")) { + fail("create should not have been called"); } }, update(object, original) { - if (object.get('yolo') === original.get('yolo')) { - fail('create should not have been called'); + if (object.get("yolo") === original.get("yolo")) { + fail("create should not have been called"); } }, }; - const createSpy = spyOn(spy, 'create').and.callThrough(); - const updateSpy = spyOn(spy, 'update').and.callThrough(); - subscription.on('create', spy.create); - subscription.on('update', spy.update); - const obj = new Parse.Object('Test'); - obj.set('foo', 'bar'); + const createSpy = spyOn(spy, "create").and.callThrough(); + const updateSpy = spyOn(spy, "update").and.callThrough(); + subscription.on("create", spy.create); + subscription.on("update", spy.update); + const obj = new Parse.Object("Test"); + obj.set("foo", "bar"); await obj.save(); - obj.set('foo', 'xyz'); - obj.set('yolo', 'xyz'); + obj.set("foo", "xyz"); + obj.set("yolo", "xyz"); await obj.save(); - const obj2 = new Parse.Object('Test'); - obj2.set('foo', 'bar'); - obj2.set('yolo', 'bar'); + const obj2 = new Parse.Object("Test"); + obj2.set("foo", "bar"); + obj2.set("yolo", "bar"); await obj2.save(); - obj2.set('foo', 'bart'); + obj2.set("foo", "bart"); await obj2.save(); expect(createSpy).toHaveBeenCalledTimes(1); expect(updateSpy).toHaveBeenCalledTimes(1); }); - it('can handle afterEvent set pointers', async done => { + it("can handle afterEvent set pointers", async done => { await reconfigureServer({ liveQuery: { - classNames: ['TestObject'], + classNames: ["TestObject"], }, startLiveQueryServer: true, verbose: false, @@ -401,35 +408,35 @@ describe('ParseLiveQuery', function () { const object = new TestObject(); await object.save(); - const secondObject = new Parse.Object('Test2'); - secondObject.set('foo', 'bar'); + const secondObject = new Parse.Object("Test2"); + secondObject.set("foo", "bar"); await secondObject.save(); - Parse.Cloud.afterLiveQueryEvent('TestObject', async ({ object }) => { - const query = new Parse.Query('Test2'); + Parse.Cloud.afterLiveQueryEvent("TestObject", async ({ object }) => { + const query = new Parse.Query("Test2"); const obj = await query.first(); - object.set('obj', obj); + object.set("obj", obj); }); const query = new Parse.Query(TestObject); - query.equalTo('objectId', object.id); + query.equalTo("objectId", object.id); const subscription = await query.subscribe(); - subscription.on('update', object => { - expect(object.get('obj')).toBeDefined(); - expect(object.get('obj').get('foo')).toBe('bar'); + subscription.on("update", object => { + expect(object.get("obj")).toBeDefined(); + expect(object.get("obj").get("foo")).toBe("bar"); done(); }); - subscription.on('error', () => { - fail('error should not have been called.'); + subscription.on("error", () => { + fail("error should not have been called."); }); - object.set({ foo: 'bar' }); + object.set({ foo: "bar" }); await object.save(); }); - it('can handle async afterEvent modification', async done => { + it("can handle async afterEvent modification", async done => { await reconfigureServer({ liveQuery: { - classNames: ['TestObject'], + classNames: ["TestObject"], }, startLiveQueryServer: true, verbose: false, @@ -437,31 +444,31 @@ describe('ParseLiveQuery', function () { }); const parent = new TestObject(); const child = new TestObject(); - child.set('bar', 'foo'); + child.set("bar", "foo"); await Parse.Object.saveAll([parent, child]); - Parse.Cloud.afterLiveQueryEvent('TestObject', async req => { + Parse.Cloud.afterLiveQueryEvent("TestObject", async req => { const current = req.object; - const pointer = current.get('child'); + const pointer = current.get("child"); await pointer.fetch(); }); const query = new Parse.Query(TestObject); - query.equalTo('objectId', parent.id); + query.equalTo("objectId", parent.id); const subscription = await query.subscribe(); - subscription.on('update', object => { - expect(object.get('child')).toBeDefined(); - expect(object.get('child').get('bar')).toBe('foo'); + subscription.on("update", object => { + expect(object.get("child")).toBeDefined(); + expect(object.get("child").get("bar")).toBe("foo"); done(); }); - parent.set('child', child); + parent.set("child", child); await parent.save(); }); - it('can handle beforeConnect / beforeSubscribe hooks', async done => { + it("can handle beforeConnect / beforeSubscribe hooks", async done => { await reconfigureServer({ liveQuery: { - classNames: ['TestObject'], + classNames: ["TestObject"], }, startLiveQueryServer: true, }); @@ -469,13 +476,13 @@ describe('ParseLiveQuery', function () { await object.save(); const hooks = { beforeSubscribe(req) { - expect(req.op).toBe('subscribe'); + expect(req.op).toBe("subscribe"); expect(req.requestId).toBe(1); expect(req.query).toBeDefined(); expect(req.user).toBeUndefined(); }, beforeConnect(req) { - expect(req.event).toBe('connect'); + expect(req.event).toBe("connect"); expect(req.clients).toBe(0); expect(req.subscriptions).toBe(0); expect(req.useMasterKey).toBe(false); @@ -484,27 +491,27 @@ describe('ParseLiveQuery', function () { expect(req.client).toBeDefined(); }, }; - spyOn(hooks, 'beforeSubscribe').and.callThrough(); - spyOn(hooks, 'beforeConnect').and.callThrough(); - Parse.Cloud.beforeSubscribe('TestObject', hooks.beforeSubscribe); + spyOn(hooks, "beforeSubscribe").and.callThrough(); + spyOn(hooks, "beforeConnect").and.callThrough(); + Parse.Cloud.beforeSubscribe("TestObject", hooks.beforeSubscribe); Parse.Cloud.beforeConnect(hooks.beforeConnect); const query = new Parse.Query(TestObject); - query.equalTo('objectId', object.id); + query.equalTo("objectId", object.id); const subscription = await query.subscribe(); - subscription.on('update', object => { - expect(object.get('foo')).toBe('bar'); + subscription.on("update", object => { + expect(object.get("foo")).toBe("bar"); expect(hooks.beforeConnect).toHaveBeenCalled(); expect(hooks.beforeSubscribe).toHaveBeenCalled(); done(); }); - object.set({ foo: 'bar' }); + object.set({ foo: "bar" }); await object.save(); }); - it('can handle beforeConnect validation function', async () => { + it("can handle beforeConnect validation function", async () => { await reconfigureServer({ liveQuery: { - classNames: ['TestObject'], + classNames: ["TestObject"], }, startLiveQueryServer: true, }); @@ -513,16 +520,16 @@ describe('ParseLiveQuery', function () { await object.save(); Parse.Cloud.beforeConnect(() => {}, validatorFail); const query = new Parse.Query(TestObject); - query.equalTo('objectId', object.id); + query.equalTo("objectId", object.id); await expectAsync(query.subscribe()).toBeRejectedWith( - new Parse.Error(Parse.Error.VALIDATION_ERROR, 'you are not authorized') + new Parse.Error(Parse.Error.VALIDATION_ERROR, "you are not authorized") ); }); - it('can handle beforeSubscribe validation function', async () => { + it("can handle beforeSubscribe validation function", async () => { await reconfigureServer({ liveQuery: { - classNames: ['TestObject'], + classNames: ["TestObject"], }, startLiveQueryServer: true, }); @@ -531,39 +538,39 @@ describe('ParseLiveQuery', function () { Parse.Cloud.beforeSubscribe(TestObject, () => {}, validatorFail); const query = new Parse.Query(TestObject); - query.equalTo('objectId', object.id); + query.equalTo("objectId", object.id); await expectAsync(query.subscribe()).toBeRejectedWith( - new Parse.Error(Parse.Error.VALIDATION_ERROR, 'you are not authorized') + new Parse.Error(Parse.Error.VALIDATION_ERROR, "you are not authorized") ); }); - it('can handle afterEvent validation function', async done => { + it("can handle afterEvent validation function", async done => { await reconfigureServer({ liveQuery: { - classNames: ['TestObject'], + classNames: ["TestObject"], }, startLiveQueryServer: true, verbose: false, silent: true, }); - Parse.Cloud.afterLiveQueryEvent('TestObject', () => {}, validatorFail); + Parse.Cloud.afterLiveQueryEvent("TestObject", () => {}, validatorFail); const query = new Parse.Query(TestObject); const subscription = await query.subscribe(); - subscription.on('error', error => { - expect(error).toBe('you are not authorized'); + subscription.on("error", error => { + expect(error).toBe("you are not authorized"); done(); }); const object = new TestObject(); - object.set('foo', 'bar'); + object.set("foo", "bar"); await object.save(); }); - it('can handle beforeConnect error', async () => { + it("can handle beforeConnect error", async () => { await reconfigureServer({ liveQuery: { - classNames: ['TestObject'], + classNames: ["TestObject"], }, startLiveQueryServer: true, }); @@ -571,23 +578,25 @@ describe('ParseLiveQuery', function () { await object.save(); Parse.Cloud.beforeConnect(() => { - throw new Error('You shall not pass!'); + throw new Error("You shall not pass!"); }); const query = new Parse.Query(TestObject); - query.equalTo('objectId', object.id); - await expectAsync(query.subscribe()).toBeRejectedWith(new Error('You shall not pass!')); + query.equalTo("objectId", object.id); + await expectAsync(query.subscribe()).toBeRejectedWith( + new Error("You shall not pass!") + ); }); - it('can log on beforeConnect throw', async () => { + it("can log on beforeConnect throw", async () => { await reconfigureServer({ liveQuery: { - classNames: ['TestObject'], + classNames: ["TestObject"], }, startLiveQueryServer: true, }); - const logger = require('../lib/logger').logger; - spyOn(logger, 'error').and.callFake(() => {}); + const logger = require("../lib/logger").logger; + spyOn(logger, "error").and.callFake(() => {}); let token = undefined; Parse.Cloud.beforeConnect(({ sessionToken }) => { token = sessionToken; @@ -596,17 +605,17 @@ describe('ParseLiveQuery', function () { /* eslint-enable no-undef */ }); await expectAsync(new Parse.Query(TestObject).subscribe()).toBeRejectedWith( - new Error('foo is not defined') + new Error("foo is not defined") ); expect(logger.error).toHaveBeenCalledWith( `Failed running beforeConnect for session ${token} with:\n Error: {"message":"foo is not defined","code":141}` ); }); - it('can handle beforeSubscribe error', async () => { + it("can handle beforeSubscribe error", async () => { await reconfigureServer({ liveQuery: { - classNames: ['TestObject'], + classNames: ["TestObject"], }, startLiveQueryServer: true, }); @@ -614,23 +623,25 @@ describe('ParseLiveQuery', function () { await object.save(); Parse.Cloud.beforeSubscribe(TestObject, () => { - throw new Error('You shall not subscribe!'); + throw new Error("You shall not subscribe!"); }); const query = new Parse.Query(TestObject); - query.equalTo('objectId', object.id); - await expectAsync(query.subscribe()).toBeRejectedWith(new Error('You shall not subscribe!')); + query.equalTo("objectId", object.id); + await expectAsync(query.subscribe()).toBeRejectedWith( + new Error("You shall not subscribe!") + ); }); - it('can log on beforeSubscribe error', async () => { + it("can log on beforeSubscribe error", async () => { await reconfigureServer({ liveQuery: { - classNames: ['TestObject'], + classNames: ["TestObject"], }, startLiveQueryServer: true, }); - const logger = require('../lib/logger').logger; - spyOn(logger, 'error').and.callFake(() => {}); + const logger = require("../lib/logger").logger; + spyOn(logger, "error").and.callFake(() => {}); Parse.Cloud.beforeSubscribe(TestObject, () => { /* eslint-disable no-undef */ @@ -639,54 +650,56 @@ describe('ParseLiveQuery', function () { }); const query = new Parse.Query(TestObject); - await expectAsync(query.subscribe()).toBeRejectedWith(new Error('foo is not defined')); + await expectAsync(query.subscribe()).toBeRejectedWith( + new Error("foo is not defined") + ); expect(logger.error).toHaveBeenCalledWith( `Failed running beforeSubscribe on TestObject for session undefined with:\n Error: {"message":"foo is not defined","code":141}` ); }); - it('can handle mutate beforeSubscribe query', async done => { + it("can handle mutate beforeSubscribe query", async done => { await reconfigureServer({ liveQuery: { - classNames: ['TestObject'], + classNames: ["TestObject"], }, startLiveQueryServer: true, }); const hook = { beforeSubscribe(request) { - request.query.equalTo('yolo', 'abc'); + request.query.equalTo("yolo", "abc"); }, }; - spyOn(hook, 'beforeSubscribe').and.callThrough(); - Parse.Cloud.beforeSubscribe('TestObject', hook.beforeSubscribe); + spyOn(hook, "beforeSubscribe").and.callThrough(); + Parse.Cloud.beforeSubscribe("TestObject", hook.beforeSubscribe); const object = new TestObject(); await object.save(); - const query = new Parse.Query('TestObject'); - query.equalTo('objectId', object.id); + const query = new Parse.Query("TestObject"); + query.equalTo("objectId", object.id); const subscription = await query.subscribe(); - subscription.on('update', () => { - fail('beforeSubscribe should restrict subscription'); + subscription.on("update", () => { + fail("beforeSubscribe should restrict subscription"); }); - subscription.on('enter', object => { - if (object.get('yolo') === 'abc') { + subscription.on("enter", object => { + if (object.get("yolo") === "abc") { done(); } else { - fail('beforeSubscribe should restrict queries'); + fail("beforeSubscribe should restrict queries"); } }); - object.set({ yolo: 'bar' }); + object.set({ yolo: "bar" }); await object.save(); - object.set({ yolo: 'abc' }); + object.set({ yolo: "abc" }); await object.save(); expect(hook.beforeSubscribe).toHaveBeenCalled(); }); - it('can return a new beforeSubscribe query', async done => { + it("can return a new beforeSubscribe query", async done => { await reconfigureServer({ liveQuery: { - classNames: ['TestObject'], + classNames: ["TestObject"], }, startLiveQueryServer: true, verbose: false, @@ -694,62 +707,62 @@ describe('ParseLiveQuery', function () { }); Parse.Cloud.beforeSubscribe(TestObject, request => { const query = new Parse.Query(TestObject); - query.equalTo('foo', 'yolo'); + query.equalTo("foo", "yolo"); request.query = query; }); const query = new Parse.Query(TestObject); - query.equalTo('foo', 'bar'); + query.equalTo("foo", "bar"); const subscription = await query.subscribe(); - subscription.on('create', object => { - expect(object.get('foo')).toBe('yolo'); + subscription.on("create", object => { + expect(object.get("foo")).toBe("yolo"); done(); }); const object = new TestObject(); - object.set({ foo: 'yolo' }); + object.set({ foo: "yolo" }); await object.save(); }); - it('can handle select beforeSubscribe query', async done => { + it("can handle select beforeSubscribe query", async done => { Parse.Cloud.beforeSubscribe(TestObject, request => { const query = request.query; - query.select('yolo'); + query.select("yolo"); }); const object = new TestObject(); await object.save(); const query = new Parse.Query(TestObject); - query.equalTo('objectId', object.id); + query.equalTo("objectId", object.id); const subscription = await query.subscribe(); - subscription.on('update', object => { - expect(object.get('foo')).toBeUndefined(); - expect(object.get('yolo')).toBe('abc'); + subscription.on("update", object => { + expect(object.get("foo")).toBeUndefined(); + expect(object.get("yolo")).toBe("abc"); done(); }); - object.set({ foo: 'bar', yolo: 'abc' }); + object.set({ foo: "bar", yolo: "abc" }); await object.save(); }); - it('LiveQuery with ACL', async () => { + it("LiveQuery with ACL", async () => { await reconfigureServer({ liveQuery: { - classNames: ['Chat'], + classNames: ["Chat"], }, startLiveQueryServer: true, verbose: false, silent: true, }); const user = new Parse.User(); - user.setUsername('username'); - user.setPassword('password'); + user.setUsername("username"); + user.setPassword("password"); await user.signUp(); const calls = { beforeConnect(req) { - expect(req.event).toBe('connect'); + expect(req.event).toBe("connect"); expect(req.clients).toBe(0); expect(req.subscriptions).toBe(0); expect(req.useMasterKey).toBe(false); @@ -757,37 +770,37 @@ describe('ParseLiveQuery', function () { expect(req.client).toBeDefined(); }, beforeSubscribe(req) { - expect(req.op).toBe('subscribe'); + expect(req.op).toBe("subscribe"); expect(req.requestId).toBe(1); expect(req.query).toBeDefined(); expect(req.user).toBeDefined(); }, afterLiveQueryEvent(req) { expect(req.user).toBeDefined(); - expect(req.object.get('foo')).toBe('bar'); + expect(req.object.get("foo")).toBe("bar"); }, create(object) { - expect(object.get('foo')).toBe('bar'); + expect(object.get("foo")).toBe("bar"); }, delete(object) { - expect(object.get('foo')).toBe('bar'); + expect(object.get("foo")).toBe("bar"); }, }; for (const key in calls) { spyOn(calls, key).and.callThrough(); } Parse.Cloud.beforeConnect(calls.beforeConnect); - Parse.Cloud.beforeSubscribe('Chat', calls.beforeSubscribe); - Parse.Cloud.afterLiveQueryEvent('Chat', calls.afterLiveQueryEvent); + Parse.Cloud.beforeSubscribe("Chat", calls.beforeSubscribe); + Parse.Cloud.afterLiveQueryEvent("Chat", calls.afterLiveQueryEvent); - const chatQuery = new Parse.Query('Chat'); + const chatQuery = new Parse.Query("Chat"); const subscription = await chatQuery.subscribe(); - subscription.on('create', calls.create); - subscription.on('delete', calls.delete); - const object = new Parse.Object('Chat'); + subscription.on("create", calls.create); + subscription.on("delete", calls.delete); + const object = new Parse.Object("Chat"); const acl = new Parse.ACL(user); object.setACL(acl); - object.set({ foo: 'bar' }); + object.set({ foo: "bar" }); await object.save(); await object.destroy(); await sleep(200); @@ -796,43 +809,45 @@ describe('ParseLiveQuery', function () { } }); - it('LiveQuery should work with changing role', async () => { + it("LiveQuery should work with changing role", async () => { await reconfigureServer({ liveQuery: { - classNames: ['Chat'], + classNames: ["Chat"], }, startLiveQueryServer: true, }); const user = new Parse.User(); - user.setUsername('username'); - user.setPassword('password'); + user.setUsername("username"); + user.setPassword("password"); await user.signUp(); - const role = new Parse.Role('Test', new Parse.ACL(user)); + const role = new Parse.Role("Test", new Parse.ACL(user)); await role.save(); - const chatQuery = new Parse.Query('Chat'); + const chatQuery = new Parse.Query("Chat"); const subscription = await chatQuery.subscribe(); - subscription.on('create', () => { - fail('should not call create as user is not part of role.'); + subscription.on("create", () => { + fail("should not call create as user is not part of role."); }); - const object = new Parse.Object('Chat'); + const object = new Parse.Object("Chat"); const acl = new Parse.ACL(); acl.setRoleReadAccess(role, true); object.setACL(acl); - object.set({ foo: 'bar' }); + object.set({ foo: "bar" }); await object.save(null, { useMasterKey: true }); role.getUsers().add(user); await sleep(1000); await role.save(); await sleep(1000); - object.set('foo', 'yolo'); + object.set("foo", "yolo"); await Promise.all([ new Promise(resolve => { - subscription.on('update', obj => { - expect(obj.get('foo')).toBe('yolo'); - expect(obj.getACL().toJSON()).toEqual({ 'role:Test': { read: true } }); + subscription.on("update", obj => { + expect(obj.get("foo")).toBe("yolo"); + expect(obj.getACL().toJSON()).toEqual({ + "role:Test": { read: true }, + }); resolve(); }); }), @@ -840,7 +855,7 @@ describe('ParseLiveQuery', function () { ]); }); - it('liveQuery on Session class', async done => { + it("liveQuery on Session class", async done => { await reconfigureServer({ liveQuery: { classNames: [Parse.Session] }, startLiveQueryServer: true, @@ -849,27 +864,30 @@ describe('ParseLiveQuery', function () { }); const user = new Parse.User(); - user.setUsername('username'); - user.setPassword('password'); + user.setUsername("username"); + user.setPassword("password"); await user.signUp(); const query = new Parse.Query(Parse.Session); const subscription = await query.subscribe(); - subscription.on('create', async obj => { - expect(obj.get('user').id).toBe(user.id); - expect(obj.get('createdWith')).toEqual({ action: 'login', authProvider: 'password' }); - expect(obj.get('expiresAt')).toBeInstanceOf(Date); - expect(obj.get('installationId')).toBeDefined(); - expect(obj.get('createdAt')).toBeInstanceOf(Date); - expect(obj.get('updatedAt')).toBeInstanceOf(Date); + subscription.on("create", async obj => { + expect(obj.get("user").id).toBe(user.id); + expect(obj.get("createdWith")).toEqual({ + action: "login", + authProvider: "password", + }); + expect(obj.get("expiresAt")).toBeInstanceOf(Date); + expect(obj.get("installationId")).toBeDefined(); + expect(obj.get("createdAt")).toBeInstanceOf(Date); + expect(obj.get("updatedAt")).toBeInstanceOf(Date); done(); }); - await Parse.User.logIn('username', 'password'); + await Parse.User.logIn("username", "password"); }); - it('prevent liveQuery on Session class when not logged in', async () => { + it("prevent liveQuery on Session class when not logged in", async () => { await reconfigureServer({ liveQuery: { classNames: [Parse.Session], @@ -877,15 +895,17 @@ describe('ParseLiveQuery', function () { startLiveQueryServer: true, }); const query = new Parse.Query(Parse.Session); - await expectAsync(query.subscribe()).toBeRejectedWith(new Error('Invalid session token')); + await expectAsync(query.subscribe()).toBeRejectedWith( + new Error("Invalid session token") + ); }); - it_id('4ccc9508-ae6a-46ec-932a-9f5e49ab3b9e')(it)( - 'handle invalid websocket payload length', + it_id("4ccc9508-ae6a-46ec-932a-9f5e49ab3b9e")(it)( + "handle invalid websocket payload length", async done => { await reconfigureServer({ liveQuery: { - classNames: ['TestObject'], + classNames: ["TestObject"], }, startLiveQueryServer: true, verbose: false, @@ -896,7 +916,7 @@ describe('ParseLiveQuery', function () { await object.save(); const query = new Parse.Query(TestObject); - query.equalTo('objectId', object.id); + query.equalTo("objectId", object.id); const subscription = await query.subscribe(); // All control frames must have a payload length of 125 bytes or less. @@ -905,23 +925,24 @@ describe('ParseLiveQuery', function () { // 0x89 = 10001001 = ping // 0xfe = 11111110 = first bit is masking the remaining 7 are 1111110 or 126 the payload length // https://tools.ietf.org/html/rfc6455#section-5.2 - const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); + const client = + await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); client.socket._socket.write(Buffer.from([0x89, 0xfe])); - subscription.on('update', async object => { - expect(object.get('foo')).toBe('bar'); + subscription.on("update", async object => { + expect(object.get("foo")).toBe("bar"); done(); }); // Wait for Websocket timeout to reconnect setTimeout(async () => { - object.set({ foo: 'bar' }); + object.set({ foo: "bar" }); await object.save(); }, 1000); } ); - it_id('39a9191f-26dd-4e05-a379-297a67928de8')(it)( - 'should execute live query update on email validation', + it_id("39a9191f-26dd-4e05-a379-297a67928de8")(it)( + "should execute live query update on email validation", async done => { const emailAdapter = { sendVerificationEmail: () => {}, @@ -930,7 +951,7 @@ describe('ParseLiveQuery', function () { }; await reconfigureServer({ - maintenanceKey: 'test2', + maintenanceKey: "test2", liveQuery: { classNames: [Parse.User], }, @@ -938,24 +959,24 @@ describe('ParseLiveQuery', function () { verbose: false, silent: true, websocketTimeout: 100, - appName: 'liveQueryEmailValidation', + appName: "liveQueryEmailValidation", verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 20, // 0.5 second - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { const user = new Parse.User(); - user.set('password', 'asdf'); - user.set('email', 'asdf@example.com'); - user.set('username', 'zxcv'); + user.set("password", "asdf"); + user.set("email", "asdf@example.com"); + user.set("username", "zxcv"); user .signUp() .then(() => { - const config = Config.get('test'); + const config = Config.get("test"); return config.database.find( - '_User', + "_User", { - username: 'zxcv', + username: "zxcv", }, {}, Auth.maintenance(config) @@ -963,17 +984,17 @@ describe('ParseLiveQuery', function () { }) .then(async results => { const foundUser = results[0]; - const query = new Parse.Query('_User'); - query.equalTo('objectId', foundUser.objectId); + const query = new Parse.Query("_User"); + query.equalTo("objectId", foundUser.objectId); const subscription = await query.subscribe(); - subscription.on('update', async object => { + subscription.on("update", async object => { expect(object).toBeDefined(); - expect(object.get('emailVerified')).toBe(true); + expect(object.get("emailVerified")).toBe(true); done(); }); - const userController = new UserController(emailAdapter, 'test', { + const userController = new UserController(emailAdapter, "test", { verifyUserEmails: true, }); userController.verifyEmail(foundUser._email_verify_token); @@ -982,10 +1003,10 @@ describe('ParseLiveQuery', function () { } ); - it('should not broadcast event to client with invalid session token - avisory GHSA-2xm2-xj2q-qgpj', async done => { + it("should not broadcast event to client with invalid session token - avisory GHSA-2xm2-xj2q-qgpj", async done => { await reconfigureServer({ liveQuery: { - classNames: ['TestObject'], + classNames: ["TestObject"], }, liveQueryServerOptions: { cacheTimeout: 100, @@ -996,24 +1017,24 @@ describe('ParseLiveQuery', function () { cacheTTL: 100, }); const user = new Parse.User(); - user.setUsername('username'); - user.setPassword('password'); + user.setUsername("username"); + user.setPassword("password"); await user.signUp(); - const obj1 = new Parse.Object('TestObject'); + const obj1 = new Parse.Object("TestObject"); const obj1ACL = new Parse.ACL(); obj1ACL.setPublicReadAccess(false); obj1ACL.setReadAccess(user, true); obj1.setACL(obj1ACL); - const obj2 = new Parse.Object('TestObject'); + const obj2 = new Parse.Object("TestObject"); const obj2ACL = new Parse.ACL(); obj2ACL.setPublicReadAccess(false); obj2ACL.setReadAccess(user, true); obj2.setACL(obj2ACL); - const query = new Parse.Query('TestObject'); + const query = new Parse.Query("TestObject"); const subscription = await query.subscribe(); - subscription.on('create', obj => { + subscription.on("create", obj => { if (obj.id !== obj1.id) { - done.fail('should not fire'); + done.fail("should not fire"); } }); await obj1.save(); @@ -1024,33 +1045,33 @@ describe('ParseLiveQuery', function () { done(); }); - it('should strip out session token in LiveQuery', async () => { + it("should strip out session token in LiveQuery", async () => { await reconfigureServer({ - liveQuery: { classNames: ['_User'] }, + liveQuery: { classNames: ["_User"] }, startLiveQueryServer: true, verbose: false, silent: true, }); const user = new Parse.User(); - user.setUsername('username'); - user.setPassword('password'); - user.set('foo', 'bar'); + user.setUsername("username"); + user.setPassword("password"); + user.set("foo", "bar"); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); user.setACL(acl); const query = new Parse.Query(Parse.User); - query.equalTo('foo', 'bar'); + query.equalTo("foo", "bar"); const subscription = await query.subscribe(); - const events = ['create', 'update', 'enter', 'leave', 'delete']; + const events = ["create", "update", "enter", "leave", "delete"]; const response = (obj, prev) => { - expect(obj.get('sessionToken')).toBeUndefined(); + expect(obj.get("sessionToken")).toBeUndefined(); expect(obj.sessionToken).toBeUndefined(); expect(prev && prev.sessionToken).toBeUndefined(); if (prev && prev.get) { - expect(prev.get('sessionToken')).toBeUndefined(); + expect(prev.get("sessionToken")).toBeUndefined(); } }; const calls = {}; @@ -1060,11 +1081,11 @@ describe('ParseLiveQuery', function () { subscription.on(key, calls[key]); } await user.signUp(); - user.unset('foo'); + user.unset("foo"); await user.save(); - user.set('foo', 'bar'); + user.set("foo", "bar"); await user.save(); - user.set('yolo', 'bar'); + user.set("yolo", "bar"); await user.save(); await user.destroy(); await new Promise(resolve => setTimeout(resolve, 10)); @@ -1073,56 +1094,56 @@ describe('ParseLiveQuery', function () { } }); - it('should strip out protected fields', async () => { + it("should strip out protected fields", async () => { await reconfigureServer({ - liveQuery: { classNames: ['Test'] }, + liveQuery: { classNames: ["Test"] }, startLiveQueryServer: true, }); - const obj1 = new Parse.Object('Test'); - obj1.set('foo', 'foo'); - obj1.set('bar', 'bar'); - obj1.set('qux', 'qux'); + const obj1 = new Parse.Object("Test"); + obj1.set("foo", "foo"); + obj1.set("bar", "bar"); + obj1.set("qux", "qux"); await obj1.save(); const config = Config.get(Parse.applicationId); const schemaController = await config.database.loadSchema(); await schemaController.updateClass( - 'Test', + "Test", {}, { - get: { '*': true }, - find: { '*': true }, - update: { '*': true }, + get: { "*": true }, + find: { "*": true }, + update: { "*": true }, protectedFields: { - '*': ['foo'], + "*": ["foo"], }, } ); const object = await obj1.fetch(); - expect(object.get('foo')).toBe(undefined); - expect(object.get('bar')).toBeDefined(); - expect(object.get('qux')).toBeDefined(); + expect(object.get("foo")).toBe(undefined); + expect(object.get("bar")).toBeDefined(); + expect(object.get("qux")).toBeDefined(); - const subscription = await new Parse.Query('Test').subscribe(); + const subscription = await new Parse.Query("Test").subscribe(); await Promise.all([ new Promise(resolve => { - subscription.on('update', (obj, original) => { - expect(obj.get('foo')).toBe(undefined); - expect(obj.get('bar')).toBeDefined(); - expect(obj.get('qux')).toBeDefined(); - expect(original.get('foo')).toBe(undefined); - expect(original.get('bar')).toBeDefined(); - expect(original.get('qux')).toBeDefined(); + subscription.on("update", (obj, original) => { + expect(obj.get("foo")).toBe(undefined); + expect(obj.get("bar")).toBeDefined(); + expect(obj.get("qux")).toBeDefined(); + expect(original.get("foo")).toBe(undefined); + expect(original.get("bar")).toBeDefined(); + expect(original.get("qux")).toBeDefined(); resolve(); }); }), - obj1.save({ foo: 'abc' }), + obj1.save({ foo: "abc" }), ]); }); - it('can subscribe to query and return object with withinKilometers with last parameter on update', async done => { + it("can subscribe to query and return object with withinKilometers with last parameter on update", async done => { await reconfigureServer({ liveQuery: { - classNames: ['TestObject'], + classNames: ["TestObject"], }, startLiveQueryServer: true, verbose: false, @@ -1137,144 +1158,163 @@ describe('ParseLiveQuery', function () { const sorted = false; const query = new Parse.Query(TestObject); query.withinKilometers( - 'location', + "location", new Parse.GeoPoint({ latitude: 40.0, longitude: -30.0 }), 2, sorted ); const subscription = await query.subscribe(); - subscription.on('update', obj => { + subscription.on("update", obj => { expect(obj.id).toBe(object.id); done(); }); - const secondPoint = new Parse.GeoPoint({ latitude: 40.0, longitude: -30.0 }); + const secondPoint = new Parse.GeoPoint({ + latitude: 40.0, + longitude: -30.0, + }); object.set({ location: secondPoint }); await object.save(); }); - it_id('2f95d8a9-7675-45ba-a4a6-e45cb7efb1fb')(it)('does shutdown liveQuery server', async () => { - await reconfigureServer({ appId: 'test_app_id' }); - const config = { - appId: 'hello_test', - masterKey: 'world', - port: 1345, - mountPath: '/1', - serverURL: 'http://localhost:1345/1', - liveQuery: { - classNames: ['Yolo'], - }, - startLiveQueryServer: true, - verbose: false, - silent: true, - }; - if (process.env.PARSE_SERVER_TEST_DB === 'postgres') { - config.databaseAdapter = new databaseAdapter.constructor({ - uri: databaseURI, - collectionPrefix: 'test_', - }); - config.filesAdapter = defaultConfiguration.filesAdapter; + it_id("2f95d8a9-7675-45ba-a4a6-e45cb7efb1fb")(it)( + "does shutdown liveQuery server", + async () => { + await reconfigureServer({ appId: "test_app_id" }); + const config = { + appId: "hello_test", + masterKey: "world", + port: 1345, + mountPath: "/1", + serverURL: "http://localhost:1345/1", + liveQuery: { + classNames: ["Yolo"], + }, + startLiveQueryServer: true, + verbose: false, + silent: true, + }; + if (process.env.PARSE_SERVER_TEST_DB === "postgres") { + config.databaseAdapter = new databaseAdapter.constructor({ + uri: databaseURI, + collectionPrefix: "test_", + }); + config.filesAdapter = defaultConfiguration.filesAdapter; + } + const server = await ParseServer.startApp(config); + const client = + await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); + client.serverURL = "ws://localhost:1345/1"; + const query = await new Parse.Query("Yolo").subscribe(); + let liveQueryConnectionCount = await getConnectionsCount( + server.liveQueryServer.server + ); + expect(liveQueryConnectionCount > 0).toBe(true); + await Promise.all([ + server.handleShutdown(), + new Promise(resolve => query.on("close", resolve)), + ]); + await sleep(100); + expect(server.liveQueryServer.server.address()).toBeNull(); + expect(server.liveQueryServer.subscriber.isOpen).toBeFalse(); + + liveQueryConnectionCount = await getConnectionsCount( + server.liveQueryServer.server + ); + expect(liveQueryConnectionCount).toBe(0); } - const server = await ParseServer.startApp(config); - const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); - client.serverURL = 'ws://localhost:1345/1'; - const query = await new Parse.Query('Yolo').subscribe(); - let liveQueryConnectionCount = await getConnectionsCount(server.liveQueryServer.server); - expect(liveQueryConnectionCount > 0).toBe(true); - await Promise.all([ - server.handleShutdown(), - new Promise(resolve => query.on('close', resolve)), - ]); - await sleep(100); - expect(server.liveQueryServer.server.address()).toBeNull(); - expect(server.liveQueryServer.subscriber.isOpen).toBeFalse(); - - liveQueryConnectionCount = await getConnectionsCount(server.liveQueryServer.server); - expect(liveQueryConnectionCount).toBe(0); - }); + ); - it_id('45655b74-716f-4fa1-a058-67eb21f3c3db')(it)('does shutdown separate liveQuery server', async () => { - await reconfigureServer({ appId: 'test_app_id' }); - let close = false; - const config = { - appId: 'hello_test', - masterKey: 'world', - port: 1345, - mountPath: '/1', - serverURL: 'http://localhost:1345/1', - liveQuery: { - classNames: ['Yolo'], - }, - startLiveQueryServer: true, - verbose: false, - silent: true, - liveQueryServerOptions: { - port: 1346, - }, - serverCloseComplete: () => { - close = true; - }, - }; - if (process.env.PARSE_SERVER_TEST_DB === 'postgres') { - config.databaseAdapter = new databaseAdapter.constructor({ - uri: databaseURI, - collectionPrefix: 'test_', - }); - config.filesAdapter = defaultConfiguration.filesAdapter; + it_id("45655b74-716f-4fa1-a058-67eb21f3c3db")(it)( + "does shutdown separate liveQuery server", + async () => { + await reconfigureServer({ appId: "test_app_id" }); + let close = false; + const config = { + appId: "hello_test", + masterKey: "world", + port: 1345, + mountPath: "/1", + serverURL: "http://localhost:1345/1", + liveQuery: { + classNames: ["Yolo"], + }, + startLiveQueryServer: true, + verbose: false, + silent: true, + liveQueryServerOptions: { + port: 1346, + }, + serverCloseComplete: () => { + close = true; + }, + }; + if (process.env.PARSE_SERVER_TEST_DB === "postgres") { + config.databaseAdapter = new databaseAdapter.constructor({ + uri: databaseURI, + collectionPrefix: "test_", + }); + config.filesAdapter = defaultConfiguration.filesAdapter; + } + const parseServer = await ParseServer.startApp(config); + expect(parseServer.liveQueryServer).toBeDefined(); + expect(parseServer.liveQueryServer.server).not.toBe(parseServer.server); + + // Open a connection to the liveQuery server + const client = + await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); + client.serverURL = "ws://localhost:1346/1"; + const query = await new Parse.Query("Yolo").subscribe(); + + // Open a connection to the parse server + const health = await request({ + method: "GET", + url: `http://localhost:1345/1/health`, + json: true, + headers: { + "X-Parse-Application-Id": "hello_test", + "X-Parse-Master-Key": "world", + "Content-Type": "application/json", + }, + agent: new http.Agent({ keepAlive: true }), + }).then(res => res.data); + expect(health.status).toBe("ok"); + + let parseConnectionCount = await getConnectionsCount(parseServer.server); + let liveQueryConnectionCount = await getConnectionsCount( + parseServer.liveQueryServer.server + ); + + expect(parseConnectionCount > 0).toBe(true); + expect(liveQueryConnectionCount > 0).toBe(true); + await Promise.all([ + parseServer.handleShutdown(), + new Promise(resolve => query.on("close", resolve)), + ]); + expect(close).toBe(true); + await sleep(100); + expect(parseServer.liveQueryServer.server.address()).toBeNull(); + expect(parseServer.liveQueryServer.subscriber.isOpen).toBeFalse(); + + parseConnectionCount = await getConnectionsCount(parseServer.server); + liveQueryConnectionCount = await getConnectionsCount( + parseServer.liveQueryServer.server + ); + expect(parseConnectionCount).toBe(0); + expect(liveQueryConnectionCount).toBe(0); } - const parseServer = await ParseServer.startApp(config); - expect(parseServer.liveQueryServer).toBeDefined(); - expect(parseServer.liveQueryServer.server).not.toBe(parseServer.server); - - // Open a connection to the liveQuery server - const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); - client.serverURL = 'ws://localhost:1346/1'; - const query = await new Parse.Query('Yolo').subscribe(); - - // Open a connection to the parse server - const health = await request({ - method: 'GET', - url: `http://localhost:1345/1/health`, - json: true, - headers: { - 'X-Parse-Application-Id': 'hello_test', - 'X-Parse-Master-Key': 'world', - 'Content-Type': 'application/json', - }, - agent: new http.Agent({ keepAlive: true }), - }).then(res => res.data); - expect(health.status).toBe('ok'); - - let parseConnectionCount = await getConnectionsCount(parseServer.server); - let liveQueryConnectionCount = await getConnectionsCount(parseServer.liveQueryServer.server); - - expect(parseConnectionCount > 0).toBe(true); - expect(liveQueryConnectionCount > 0).toBe(true); - await Promise.all([ - parseServer.handleShutdown(), - new Promise(resolve => query.on('close', resolve)), - ]); - expect(close).toBe(true); - await sleep(100); - expect(parseServer.liveQueryServer.server.address()).toBeNull(); - expect(parseServer.liveQueryServer.subscriber.isOpen).toBeFalse(); - - parseConnectionCount = await getConnectionsCount(parseServer.server); - liveQueryConnectionCount = await getConnectionsCount(parseServer.liveQueryServer.server); - expect(parseConnectionCount).toBe(0); - expect(liveQueryConnectionCount).toBe(0); - }); + ); - it('prevent afterSave trigger if not exists', async () => { + it("prevent afterSave trigger if not exists", async () => { await reconfigureServer({ liveQuery: { - classNames: ['TestObject'], + classNames: ["TestObject"], }, startLiveQueryServer: true, verbose: false, silent: true, }); - spyOn(triggers, 'maybeRunTrigger').and.callThrough(); + spyOn(triggers, "maybeRunTrigger").and.callThrough(); const object1 = new TestObject(); const object2 = new TestObject(); const object3 = new TestObject(); @@ -1286,10 +1326,10 @@ describe('ParseLiveQuery', function () { expect(object3.id).toBeDefined(); }); - it('triggers query event with constraint not equal to null', async () => { + it("triggers query event with constraint not equal to null", async () => { await reconfigureServer({ liveQuery: { - classNames: ['TestObject'], + classNames: ["TestObject"], }, startLiveQueryServer: true, verbose: false, @@ -1298,17 +1338,17 @@ describe('ParseLiveQuery', function () { const spy = { create(obj) { - expect(obj.attributes.foo).toEqual('bar'); + expect(obj.attributes.foo).toEqual("bar"); }, }; - const createSpy = spyOn(spy, 'create'); + const createSpy = spyOn(spy, "create"); const query = new Parse.Query(TestObject); - query.notEqualTo('foo', null); + query.notEqualTo("foo", null); const subscription = await query.subscribe(); - subscription.on('create', spy.create); + subscription.on("create", spy.create); const object1 = new TestObject(); - object1.set('foo', 'bar'); + object1.set("foo", "bar"); await object1.save(); await new Promise(resolve => setTimeout(resolve, 100)); diff --git a/spec/ParseLiveQueryRedis.spec.js b/spec/ParseLiveQueryRedis.spec.js index deb84bafb2..c2e44507f3 100644 --- a/spec/ParseLiveQueryRedis.spec.js +++ b/spec/ParseLiveQueryRedis.spec.js @@ -1,33 +1,34 @@ -if (process.env.PARSE_SERVER_TEST_CACHE === 'redis') { - describe('ParseLiveQuery redis', () => { +if (process.env.PARSE_SERVER_TEST_CACHE === "redis") { + describe("ParseLiveQuery redis", () => { afterEach(async () => { - const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); + const client = + await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); client.close(); }); - it('can connect', async () => { + it("can connect", async () => { await reconfigureServer({ - appId: 'redis_live_query', + appId: "redis_live_query", startLiveQueryServer: true, liveQuery: { - classNames: ['TestObject'], - redisURL: 'redis://localhost:6379', + classNames: ["TestObject"], + redisURL: "redis://localhost:6379", }, liveQueryServerOptions: { - redisURL: 'redis://localhost:6379', + redisURL: "redis://localhost:6379", }, }); - const subscription = await new Parse.Query('TestObject').subscribe(); + const subscription = await new Parse.Query("TestObject").subscribe(); const [object] = await Promise.all([ - new Parse.Object('TestObject').save(), + new Parse.Object("TestObject").save(), new Promise(resolve => - subscription.on('create', () => { + subscription.on("create", () => { resolve(); }) ), ]); await Promise.all([ new Promise(resolve => - subscription.on('delete', () => { + subscription.on("delete", () => { resolve(); }) ), @@ -35,21 +36,27 @@ if (process.env.PARSE_SERVER_TEST_CACHE === 'redis') { ]); }); - it('can call connect twice', async () => { + it("can call connect twice", async () => { const server = await reconfigureServer({ - appId: 'redis_live_query', + appId: "redis_live_query", startLiveQueryServer: true, liveQuery: { - classNames: ['TestObject'], - redisURL: 'redis://localhost:6379', + classNames: ["TestObject"], + redisURL: "redis://localhost:6379", }, liveQueryServerOptions: { - redisURL: 'redis://localhost:6379', + redisURL: "redis://localhost:6379", }, }); - expect(server.config.liveQueryController.liveQueryPublisher.parsePublisher.isOpen).toBeTrue(); + expect( + server.config.liveQueryController.liveQueryPublisher.parsePublisher + .isOpen + ).toBeTrue(); await server.config.liveQueryController.connect(); - expect(server.config.liveQueryController.liveQueryPublisher.parsePublisher.isOpen).toBeTrue(); + expect( + server.config.liveQueryController.liveQueryPublisher.parsePublisher + .isOpen + ).toBeTrue(); expect(server.liveQueryServer.subscriber.isOpen).toBe(true); await server.liveQueryServer.connect(); expect(server.liveQueryServer.subscriber.isOpen).toBe(true); diff --git a/spec/ParseLiveQueryServer.spec.js b/spec/ParseLiveQueryServer.spec.js index 9961b2503d..6dfa4e2dd4 100644 --- a/spec/ParseLiveQueryServer.spec.js +++ b/spec/ParseLiveQueryServer.spec.js @@ -1,91 +1,122 @@ -const Parse = require('parse/node'); -const ParseLiveQueryServer = require('../lib/LiveQuery/ParseLiveQueryServer').ParseLiveQueryServer; -const ParseServer = require('../lib/ParseServer').default; -const LiveQueryController = require('../lib/Controllers/LiveQueryController').LiveQueryController; -const auth = require('../lib/Auth'); +const Parse = require("parse/node"); +const ParseLiveQueryServer = + require("../lib/LiveQuery/ParseLiveQueryServer").ParseLiveQueryServer; +const ParseServer = require("../lib/ParseServer").default; +const LiveQueryController = + require("../lib/Controllers/LiveQueryController").LiveQueryController; +const auth = require("../lib/Auth"); // Global mock info -const queryHashValue = 'hash'; -const testUserId = 'userId'; -const testClassName = 'TestObject'; +const queryHashValue = "hash"; +const testUserId = "userId"; +const testClassName = "TestObject"; const timeout = () => jasmine.timeout(100); -describe('ParseLiveQueryServer', function () { +describe("ParseLiveQueryServer", function () { beforeEach(function (done) { // Mock ParseWebSocketServer - const mockParseWebSocketServer = jasmine.createSpy('ParseWebSocketServer'); + const mockParseWebSocketServer = jasmine.createSpy("ParseWebSocketServer"); jasmine.mockLibrary( - '../lib/LiveQuery/ParseWebSocketServer', - 'ParseWebSocketServer', + "../lib/LiveQuery/ParseWebSocketServer", + "ParseWebSocketServer", mockParseWebSocketServer ); // Mock Client const mockClient = function (id, socket, hasMasterKey) { - this.pushConnect = jasmine.createSpy('pushConnect'); - this.pushSubscribe = jasmine.createSpy('pushSubscribe'); - this.pushUnsubscribe = jasmine.createSpy('pushUnsubscribe'); - this.pushDelete = jasmine.createSpy('pushDelete'); - this.pushCreate = jasmine.createSpy('pushCreate'); - this.pushEnter = jasmine.createSpy('pushEnter'); - this.pushUpdate = jasmine.createSpy('pushUpdate'); - this.pushLeave = jasmine.createSpy('pushLeave'); - this.addSubscriptionInfo = jasmine.createSpy('addSubscriptionInfo'); - this.getSubscriptionInfo = jasmine.createSpy('getSubscriptionInfo'); - this.deleteSubscriptionInfo = jasmine.createSpy('deleteSubscriptionInfo'); + this.pushConnect = jasmine.createSpy("pushConnect"); + this.pushSubscribe = jasmine.createSpy("pushSubscribe"); + this.pushUnsubscribe = jasmine.createSpy("pushUnsubscribe"); + this.pushDelete = jasmine.createSpy("pushDelete"); + this.pushCreate = jasmine.createSpy("pushCreate"); + this.pushEnter = jasmine.createSpy("pushEnter"); + this.pushUpdate = jasmine.createSpy("pushUpdate"); + this.pushLeave = jasmine.createSpy("pushLeave"); + this.addSubscriptionInfo = jasmine.createSpy("addSubscriptionInfo"); + this.getSubscriptionInfo = jasmine.createSpy("getSubscriptionInfo"); + this.deleteSubscriptionInfo = jasmine.createSpy("deleteSubscriptionInfo"); this.hasMasterKey = hasMasterKey; }; - mockClient.pushError = jasmine.createSpy('pushError'); - jasmine.mockLibrary('../lib/LiveQuery/Client', 'Client', mockClient); + mockClient.pushError = jasmine.createSpy("pushError"); + jasmine.mockLibrary("../lib/LiveQuery/Client", "Client", mockClient); // Mock Subscription const mockSubscriotion = function () { - this.addClientSubscription = jasmine.createSpy('addClientSubscription'); - this.deleteClientSubscription = jasmine.createSpy('deleteClientSubscription'); + this.addClientSubscription = jasmine.createSpy("addClientSubscription"); + this.deleteClientSubscription = jasmine.createSpy( + "deleteClientSubscription" + ); }; - jasmine.mockLibrary('../lib/LiveQuery/Subscription', 'Subscription', mockSubscriotion); + jasmine.mockLibrary( + "../lib/LiveQuery/Subscription", + "Subscription", + mockSubscriotion + ); // Mock queryHash - const mockQueryHash = jasmine.createSpy('matchesQuery').and.returnValue(queryHashValue); - jasmine.mockLibrary('../lib/LiveQuery/QueryTools', 'queryHash', mockQueryHash); + const mockQueryHash = jasmine + .createSpy("matchesQuery") + .and.returnValue(queryHashValue); + jasmine.mockLibrary( + "../lib/LiveQuery/QueryTools", + "queryHash", + mockQueryHash + ); // Mock matchesQuery - const mockMatchesQuery = jasmine.createSpy('matchesQuery').and.returnValue(true); - jasmine.mockLibrary('../lib/LiveQuery/QueryTools', 'matchesQuery', mockMatchesQuery); + const mockMatchesQuery = jasmine + .createSpy("matchesQuery") + .and.returnValue(true); + jasmine.mockLibrary( + "../lib/LiveQuery/QueryTools", + "matchesQuery", + mockMatchesQuery + ); // Mock ParsePubSub const mockParsePubSub = { createPublisher: function () { return { - publish: jasmine.createSpy('publish'), - on: jasmine.createSpy('on'), + publish: jasmine.createSpy("publish"), + on: jasmine.createSpy("on"), }; }, createSubscriber: function () { return { - subscribe: jasmine.createSpy('subscribe'), - on: jasmine.createSpy('on'), + subscribe: jasmine.createSpy("subscribe"), + on: jasmine.createSpy("on"), }; }, }; - jasmine.mockLibrary('../lib/LiveQuery/ParsePubSub', 'ParsePubSub', mockParsePubSub); - spyOn(auth, 'getAuthForSessionToken').and.callFake(({ sessionToken, cacheController }) => { - if (typeof sessionToken === 'undefined') { - return Promise.reject(); - } - if (sessionToken === null) { - return Promise.reject(); - } - if (sessionToken === 'pleaseThrow') { - return Promise.reject(); - } - if (sessionToken === 'invalid') { - return Promise.reject( - new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'invalid session token') + jasmine.mockLibrary( + "../lib/LiveQuery/ParsePubSub", + "ParsePubSub", + mockParsePubSub + ); + spyOn(auth, "getAuthForSessionToken").and.callFake( + ({ sessionToken, cacheController }) => { + if (typeof sessionToken === "undefined") { + return Promise.reject(); + } + if (sessionToken === null) { + return Promise.reject(); + } + if (sessionToken === "pleaseThrow") { + return Promise.reject(); + } + if (sessionToken === "invalid") { + return Promise.reject( + new Parse.Error( + Parse.Error.INVALID_SESSION_TOKEN, + "invalid session token" + ) + ); + } + return Promise.resolve( + new auth.Auth({ cacheController, user: { id: testUserId } }) ); } - return Promise.resolve(new auth.Auth({ cacheController, user: { id: testUserId } })); - }); + ); done(); }); - it('can be initialized', function () { + it("can be initialized", function () { const httpServer = {}; const parseLiveQueryServer = new ParseLiveQueryServer(httpServer); @@ -94,19 +125,25 @@ describe('ParseLiveQueryServer', function () { expect(parseLiveQueryServer.subscriptions.size).toBe(0); }); - it('can be initialized from ParseServer', async () => { + it("can be initialized from ParseServer", async () => { const httpServer = {}; - const parseLiveQueryServer = await ParseServer.createLiveQueryServer(httpServer, {}); + const parseLiveQueryServer = await ParseServer.createLiveQueryServer( + httpServer, + {} + ); expect(parseLiveQueryServer.clientId).toBeUndefined(); expect(parseLiveQueryServer.clients.size).toBe(0); expect(parseLiveQueryServer.subscriptions.size).toBe(0); }); - it('can be initialized from ParseServer without httpServer', async () => { - const parseLiveQueryServer = await ParseServer.createLiveQueryServer(undefined, { - port: 22345, - }); + it("can be initialized from ParseServer without httpServer", async () => { + const parseLiveQueryServer = await ParseServer.createLiveQueryServer( + undefined, + { + port: 22345, + } + ); expect(parseLiveQueryServer.clientId).toBeUndefined(); expect(parseLiveQueryServer.clients.size).toBe(0); @@ -114,17 +151,17 @@ describe('ParseLiveQueryServer', function () { await new Promise(resolve => parseLiveQueryServer.server.close(resolve)); }); - describe_only_db('mongo')('initialization', () => { - beforeEach(() => reconfigureServer({ appId: 'mongo_init_test' })); - it('can be initialized through ParseServer without liveQueryServerOptions', async () => { + describe_only_db("mongo")("initialization", () => { + beforeEach(() => reconfigureServer({ appId: "mongo_init_test" })); + it("can be initialized through ParseServer without liveQueryServerOptions", async () => { const parseServer = await ParseServer.startApp({ - appId: 'hello', - masterKey: 'world', + appId: "hello", + masterKey: "world", port: 22345, - mountPath: '/1', - serverURL: 'http://localhost:12345/1', + mountPath: "/1", + serverURL: "http://localhost:12345/1", liveQuery: { - classNames: ['Yolo'], + classNames: ["Yolo"], }, startLiveQueryServer: true, }); @@ -133,15 +170,15 @@ describe('ParseLiveQueryServer', function () { await new Promise(resolve => parseServer.server.close(resolve)); }); - it('can be initialized through ParseServer with liveQueryServerOptions', async () => { + it("can be initialized through ParseServer with liveQueryServerOptions", async () => { const parseServer = await ParseServer.startApp({ - appId: 'hello', - masterKey: 'world', + appId: "hello", + masterKey: "world", port: 22346, - mountPath: '/1', - serverURL: 'http://localhost:12345/1', + mountPath: "/1", + serverURL: "http://localhost:12345/1", liveQuery: { - classNames: ['Yolo'], + classNames: ["Yolo"], }, liveQueryServerOptions: { port: 22347, @@ -153,9 +190,9 @@ describe('ParseLiveQueryServer', function () { }); }); - it('properly passes the CLP to afterSave/afterDelete hook', function (done) { + it("properly passes the CLP to afterSave/afterDelete hook", function (done) { function setPermissionsOnClass(className, permissions, doPut) { - const request = require('request'); + const request = require("request"); let op = request.post; if (doPut) { op = request.put; @@ -163,10 +200,10 @@ describe('ParseLiveQueryServer', function () { return new Promise((resolve, reject) => { op( { - url: Parse.serverURL + '/schemas/' + className, + url: Parse.serverURL + "/schemas/" + className, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': Parse.masterKey, + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Master-Key": Parse.masterKey, }, json: true, body: { @@ -190,19 +227,22 @@ describe('ParseLiveQueryServer', function () { let deleteSpy; reconfigureServer({ liveQuery: { - classNames: ['Yolo'], + classNames: ["Yolo"], }, }) .then(parseServer => { - saveSpy = spyOn(parseServer.config.liveQueryController, 'onAfterSave'); - deleteSpy = spyOn(parseServer.config.liveQueryController, 'onAfterDelete'); - return setPermissionsOnClass('Yolo', { - create: { '*': true }, - delete: { '*': true }, + saveSpy = spyOn(parseServer.config.liveQueryController, "onAfterSave"); + deleteSpy = spyOn( + parseServer.config.liveQueryController, + "onAfterDelete" + ); + return setPermissionsOnClass("Yolo", { + create: { "*": true }, + delete: { "*": true }, }); }) .then(() => { - const obj = new Parse.Object('Yolo'); + const obj = new Parse.Object("Yolo"); return obj.save(); }) .then(obj => { @@ -212,30 +252,30 @@ describe('ParseLiveQueryServer', function () { expect(saveSpy).toHaveBeenCalled(); const saveArgs = saveSpy.calls.mostRecent().args; expect(saveArgs.length).toBe(4); - expect(saveArgs[0]).toBe('Yolo'); + expect(saveArgs[0]).toBe("Yolo"); expect(saveArgs[3]).toEqual({ get: {}, count: {}, addField: {}, - create: { '*': true }, + create: { "*": true }, find: {}, update: {}, - delete: { '*': true }, + delete: { "*": true }, protectedFields: {}, }); expect(deleteSpy).toHaveBeenCalled(); const deleteArgs = deleteSpy.calls.mostRecent().args; expect(deleteArgs.length).toBe(4); - expect(deleteArgs[0]).toBe('Yolo'); + expect(deleteArgs[0]).toBe("Yolo"); expect(deleteArgs[3]).toEqual({ get: {}, count: {}, addField: {}, - create: { '*': true }, + create: { "*": true }, find: {}, update: {}, - delete: { '*': true }, + delete: { "*": true }, protectedFields: {}, }); done(); @@ -243,14 +283,16 @@ describe('ParseLiveQueryServer', function () { .catch(done.fail); }); - it('can handle connect command', async () => { + it("can handle connect command", async () => { const parseLiveQueryServer = new ParseLiveQueryServer({}); const parseWebSocket = { clientId: -1, }; - parseLiveQueryServer._validateKeys = jasmine.createSpy('validateKeys').and.returnValue(true); + parseLiveQueryServer._validateKeys = jasmine + .createSpy("validateKeys") + .and.returnValue(true); await parseLiveQueryServer._handleConnect(parseWebSocket, { - sessionToken: 'token', + sessionToken: "token", }); const clientKeys = parseLiveQueryServer.clients.keys(); @@ -263,62 +305,62 @@ describe('ParseLiveQueryServer', function () { expect(client.pushConnect).toHaveBeenCalled(); }); - it('basic beforeConnect rejection', async () => { + it("basic beforeConnect rejection", async () => { Parse.Cloud.beforeConnect(function () { - throw new Error('You shall not pass!'); + throw new Error("You shall not pass!"); }); const parseLiveQueryServer = new ParseLiveQueryServer({}); const parseWebSocket = { clientId: -1, }; await parseLiveQueryServer._handleConnect(parseWebSocket, { - sessionToken: 'token', + sessionToken: "token", }); expect(parseLiveQueryServer.clients.size).toBe(0); - const Client = require('../lib/LiveQuery/Client').Client; + const Client = require("../lib/LiveQuery/Client").Client; expect(Client.pushError).toHaveBeenCalled(); }); - it('basic beforeSubscribe rejection', async () => { - Parse.Cloud.beforeSubscribe('test', function () { - throw new Error('You shall not pass!'); + it("basic beforeSubscribe rejection", async () => { + Parse.Cloud.beforeSubscribe("test", function () { + throw new Error("You shall not pass!"); }); const parseLiveQueryServer = new ParseLiveQueryServer({}); const parseWebSocket = { clientId: -1, }; await parseLiveQueryServer._handleConnect(parseWebSocket, { - sessionToken: 'token', + sessionToken: "token", }); const query = { - className: 'test', + className: "test", where: { - key: 'value', + key: "value", }, - keys: ['test'], + keys: ["test"], }; const requestId = 2; const request = { query: query, requestId: requestId, - sessionToken: 'sessionToken', + sessionToken: "sessionToken", }; await parseLiveQueryServer._handleSubscribe(parseWebSocket, request); expect(parseLiveQueryServer.clients.size).toBe(1); - const Client = require('../lib/LiveQuery/Client').Client; + const Client = require("../lib/LiveQuery/Client").Client; expect(Client.pushError).toHaveBeenCalled(); }); - it('can handle subscribe command without clientId', async () => { + it("can handle subscribe command without clientId", async () => { const parseLiveQueryServer = new ParseLiveQueryServer({}); const incompleteParseConn = {}; await parseLiveQueryServer._handleSubscribe(incompleteParseConn, {}); - const Client = require('../lib/LiveQuery/Client').Client; + const Client = require("../lib/LiveQuery/Client").Client; expect(Client.pushError).toHaveBeenCalled(); }); - it('can handle subscribe command with new query', async () => { + it("can handle subscribe command with new query", async () => { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Add mock client const clientId = 1; @@ -328,17 +370,17 @@ describe('ParseLiveQueryServer', function () { clientId: clientId, }; const query = { - className: 'test', + className: "test", where: { - key: 'value', + key: "value", }, - keys: ['test'], + keys: ["test"], }; const requestId = 2; const request = { query: query, requestId: requestId, - sessionToken: 'sessionToken', + sessionToken: "sessionToken", }; await parseLiveQueryServer._handleSubscribe(parseWebSocket, request); @@ -348,11 +390,14 @@ describe('ParseLiveQueryServer', function () { expect(subscriptions.get(query.className)).not.toBeNull(); const classSubscriptions = subscriptions.get(query.className); expect(classSubscriptions.size).toBe(1); - expect(classSubscriptions.get('hash')).not.toBeNull(); + expect(classSubscriptions.get("hash")).not.toBeNull(); // TODO(check subscription constructor to verify we pass the right argument) // Make sure we add clientInfo to the subscription - const subscription = classSubscriptions.get('hash'); - expect(subscription.addClientSubscription).toHaveBeenCalledWith(clientId, requestId); + const subscription = classSubscriptions.get("hash"); + expect(subscription.addClientSubscription).toHaveBeenCalledWith( + clientId, + requestId + ); // Make sure we add subscriptionInfo to the client const args = client.addSubscriptionInfo.calls.first().args; expect(args[0]).toBe(requestId); @@ -362,7 +407,7 @@ describe('ParseLiveQueryServer', function () { expect(client.pushSubscribe).toHaveBeenCalledWith(requestId); }); - it('can handle subscribe command with existing query', async () => { + it("can handle subscribe command with existing query", async () => { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Add two mock clients const clientId = 1; @@ -375,23 +420,29 @@ describe('ParseLiveQueryServer', function () { }; const requestId = 2; const query = { - className: 'test', + className: "test", where: { - key: 'value', + key: "value", }, - keys: ['test'], + keys: ["test"], }; - await addMockSubscription(parseLiveQueryServer, clientId, requestId, parseWebSocket, query); + await addMockSubscription( + parseLiveQueryServer, + clientId, + requestId, + parseWebSocket, + query + ); // Add subscription for mock client 2 const parseWebSocketAgain = { clientId: clientIdAgain, }; const queryAgain = { - className: 'test', + className: "test", where: { - key: 'value', + key: "value", }, - keys: ['testAgain'], + keys: ["testAgain"], }; const requestIdAgain = 1; await addMockSubscription( @@ -408,9 +459,9 @@ describe('ParseLiveQueryServer', function () { expect(subscriptions.get(query.className)).not.toBeNull(); const classSubscriptions = subscriptions.get(query.className); expect(classSubscriptions.size).toBe(1); - expect(classSubscriptions.get('hash')).not.toBeNull(); + expect(classSubscriptions.get("hash")).not.toBeNull(); // Make sure we add clientInfo to the subscription - const subscription = classSubscriptions.get('hash'); + const subscription = classSubscriptions.get("hash"); // Make sure client 2 info has been added let args = subscription.addClientSubscription.calls.mostRecent().args; expect(args).toEqual([clientIdAgain, requestIdAgain]); @@ -420,27 +471,27 @@ describe('ParseLiveQueryServer', function () { expect(args[1].keys).toBe(queryAgain.keys); }); - it('can handle unsubscribe command without clientId', function () { + it("can handle unsubscribe command without clientId", function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); const incompleteParseConn = {}; parseLiveQueryServer._handleUnsubscribe(incompleteParseConn, {}); - const Client = require('../lib/LiveQuery/Client').Client; + const Client = require("../lib/LiveQuery/Client").Client; expect(Client.pushError).toHaveBeenCalled(); }); - it('can handle unsubscribe command without not existed client', function () { + it("can handle unsubscribe command without not existed client", function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); const parseWebSocket = { clientId: 1, }; parseLiveQueryServer._handleUnsubscribe(parseWebSocket, {}); - const Client = require('../lib/LiveQuery/Client').Client; + const Client = require("../lib/LiveQuery/Client").Client; expect(Client.pushError).toHaveBeenCalled(); }); - it('can handle unsubscribe command without not existed query', async () => { + it("can handle unsubscribe command without not existed query", async () => { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Add mock client const clientId = 1; @@ -451,11 +502,11 @@ describe('ParseLiveQueryServer', function () { }; parseLiveQueryServer._handleUnsubscribe(parseWebSocket, {}); - const Client = require('../lib/LiveQuery/Client').Client; + const Client = require("../lib/LiveQuery/Client").Client; expect(Client.pushError).toHaveBeenCalled(); }); - it('can handle unsubscribe command', async () => { + it("can handle unsubscribe command", async () => { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Add mock client const clientId = 1; @@ -472,7 +523,8 @@ describe('ParseLiveQueryServer', function () { parseWebSocket ); // Mock client.getSubscriptionInfo - const subscriptionInfo = client.addSubscriptionInfo.calls.mostRecent().args[1]; + const subscriptionInfo = + client.addSubscriptionInfo.calls.mostRecent().args[1]; client.getSubscriptionInfo = function () { return subscriptionInfo; }; @@ -485,91 +537,97 @@ describe('ParseLiveQueryServer', function () { // Make sure we delete subscription from client expect(client.deleteSubscriptionInfo).toHaveBeenCalledWith(requestId); // Make sure we delete client from subscription - expect(subscription.deleteClientSubscription).toHaveBeenCalledWith(clientId, requestId); + expect(subscription.deleteClientSubscription).toHaveBeenCalledWith( + clientId, + requestId + ); // Make sure we clear subscription in the server const subscriptions = parseLiveQueryServer.subscriptions; expect(subscriptions.size).toBe(0); }); - it('can set connect command message handler for a parseWebSocket', function () { + it("can set connect command message handler for a parseWebSocket", function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Register mock connect/subscribe/unsubscribe handler for the server - parseLiveQueryServer._handleConnect = jasmine.createSpy('_handleSubscribe'); + parseLiveQueryServer._handleConnect = jasmine.createSpy("_handleSubscribe"); // Make mock parseWebsocket - const EventEmitter = require('events'); + const EventEmitter = require("events"); const parseWebSocket = new EventEmitter(); // Register message handlers for the parseWebSocket parseLiveQueryServer._onConnect(parseWebSocket); // Check connect request const connectRequest = { - op: 'connect', - applicationId: '1', - installationId: '1234', + op: "connect", + applicationId: "1", + installationId: "1234", }; // Trigger message event - parseWebSocket.emit('message', connectRequest); + parseWebSocket.emit("message", connectRequest); // Make sure _handleConnect is called const args = parseLiveQueryServer._handleConnect.calls.mostRecent().args; expect(args[0]).toBe(parseWebSocket); }); - it('can set subscribe command message handler for a parseWebSocket', function () { + it("can set subscribe command message handler for a parseWebSocket", function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Register mock connect/subscribe/unsubscribe handler for the server - parseLiveQueryServer._handleSubscribe = jasmine.createSpy('_handleSubscribe'); + parseLiveQueryServer._handleSubscribe = + jasmine.createSpy("_handleSubscribe"); // Make mock parseWebsocket - const EventEmitter = require('events'); + const EventEmitter = require("events"); const parseWebSocket = new EventEmitter(); // Register message handlers for the parseWebSocket parseLiveQueryServer._onConnect(parseWebSocket); // Check subscribe request const subscribeRequest = JSON.stringify({ - op: 'subscribe', + op: "subscribe", requestId: 1, - query: { className: 'Test', where: {} }, + query: { className: "Test", where: {} }, }); // Trigger message event - parseWebSocket.emit('message', subscribeRequest); + parseWebSocket.emit("message", subscribeRequest); // Make sure _handleSubscribe is called const args = parseLiveQueryServer._handleSubscribe.calls.mostRecent().args; expect(args[0]).toBe(parseWebSocket); expect(JSON.stringify(args[1])).toBe(subscribeRequest); }); - it('can set unsubscribe command message handler for a parseWebSocket', function () { + it("can set unsubscribe command message handler for a parseWebSocket", function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Register mock connect/subscribe/unsubscribe handler for the server - parseLiveQueryServer._handleUnsubscribe = jasmine.createSpy('_handleSubscribe'); + parseLiveQueryServer._handleUnsubscribe = + jasmine.createSpy("_handleSubscribe"); // Make mock parseWebsocket - const EventEmitter = require('events'); + const EventEmitter = require("events"); const parseWebSocket = new EventEmitter(); // Register message handlers for the parseWebSocket parseLiveQueryServer._onConnect(parseWebSocket); // Check unsubscribe request const unsubscribeRequest = JSON.stringify({ - op: 'unsubscribe', + op: "unsubscribe", requestId: 1, }); // Trigger message event - parseWebSocket.emit('message', unsubscribeRequest); + parseWebSocket.emit("message", unsubscribeRequest); // Make sure _handleUnsubscribe is called - const args = parseLiveQueryServer._handleUnsubscribe.calls.mostRecent().args; + const args = + parseLiveQueryServer._handleUnsubscribe.calls.mostRecent().args; expect(args[0]).toBe(parseWebSocket); expect(JSON.stringify(args[1])).toBe(unsubscribeRequest); }); - it('can set update command message handler for a parseWebSocket', function () { + it("can set update command message handler for a parseWebSocket", function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Register mock connect/subscribe/unsubscribe handler for the server - spyOn(parseLiveQueryServer, '_handleUpdateSubscription').and.callThrough(); - spyOn(parseLiveQueryServer, '_handleUnsubscribe').and.callThrough(); - spyOn(parseLiveQueryServer, '_handleSubscribe').and.callThrough(); + spyOn(parseLiveQueryServer, "_handleUpdateSubscription").and.callThrough(); + spyOn(parseLiveQueryServer, "_handleUnsubscribe").and.callThrough(); + spyOn(parseLiveQueryServer, "_handleSubscribe").and.callThrough(); // Make mock parseWebsocket - const EventEmitter = require('events'); + const EventEmitter = require("events"); const parseWebSocket = new EventEmitter(); // Register message handlers for the parseWebSocket @@ -577,43 +635,45 @@ describe('ParseLiveQueryServer', function () { // Check updateRequest request const updateRequest = JSON.stringify({ - op: 'update', + op: "update", requestId: 1, - query: { className: 'Test', where: {} }, + query: { className: "Test", where: {} }, }); // Trigger message event - parseWebSocket.emit('message', updateRequest); + parseWebSocket.emit("message", updateRequest); // Make sure _handleUnsubscribe is called - const args = parseLiveQueryServer._handleUpdateSubscription.calls.mostRecent().args; + const args = + parseLiveQueryServer._handleUpdateSubscription.calls.mostRecent().args; expect(args[0]).toBe(parseWebSocket); expect(JSON.stringify(args[1])).toBe(updateRequest); expect(parseLiveQueryServer._handleUnsubscribe).toHaveBeenCalled(); - const unsubArgs = parseLiveQueryServer._handleUnsubscribe.calls.mostRecent().args; + const unsubArgs = + parseLiveQueryServer._handleUnsubscribe.calls.mostRecent().args; expect(unsubArgs.length).toBe(3); expect(unsubArgs[2]).toBe(false); expect(parseLiveQueryServer._handleSubscribe).toHaveBeenCalled(); }); - it('can set missing command message handler for a parseWebSocket', function () { + it("can set missing command message handler for a parseWebSocket", function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make mock parseWebsocket - const EventEmitter = require('events'); + const EventEmitter = require("events"); const parseWebSocket = new EventEmitter(); // Register message handlers for the parseWebSocket parseLiveQueryServer._onConnect(parseWebSocket); // Check invalid request - const invalidRequest = '{}'; + const invalidRequest = "{}"; // Trigger message event - parseWebSocket.emit('message', invalidRequest); - const Client = require('../lib/LiveQuery/Client').Client; + parseWebSocket.emit("message", invalidRequest); + const Client = require("../lib/LiveQuery/Client").Client; expect(Client.pushError).toHaveBeenCalled(); }); - it('can set unknown command message handler for a parseWebSocket', function () { + it("can set unknown command message handler for a parseWebSocket", function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make mock parseWebsocket - const EventEmitter = require('events'); + const EventEmitter = require("events"); const parseWebSocket = new EventEmitter(); // Register message handlers for the parseWebSocket parseLiveQueryServer._onConnect(parseWebSocket); @@ -621,14 +681,14 @@ describe('ParseLiveQueryServer', function () { // Check unknown request const unknownRequest = '{"op":"unknown"}'; // Trigger message event - parseWebSocket.emit('message', unknownRequest); - const Client = require('../lib/LiveQuery/Client').Client; + parseWebSocket.emit("message", unknownRequest); + const Client = require("../lib/LiveQuery/Client").Client; expect(Client.pushError).toHaveBeenCalled(); }); - it('can set disconnect command message handler for a parseWebSocket which has not registered to the server', function () { + it("can set disconnect command message handler for a parseWebSocket which has not registered to the server", function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); - const EventEmitter = require('events'); + const EventEmitter = require("events"); const parseWebSocket = new EventEmitter(); parseWebSocket.clientId = 1; // Register message handlers for the parseWebSocket @@ -636,17 +696,17 @@ describe('ParseLiveQueryServer', function () { // Make sure we do not crash // Trigger disconnect event - parseWebSocket.emit('disconnect'); + parseWebSocket.emit("disconnect"); }); - it('can forward event to cloud code', function () { + it("can forward event to cloud code", function () { const cloudCodeHandler = { handler: () => {}, }; - const spy = spyOn(cloudCodeHandler, 'handler').and.callThrough(); + const spy = spyOn(cloudCodeHandler, "handler").and.callThrough(); Parse.Cloud.onLiveQueryEvent(cloudCodeHandler.handler); const parseLiveQueryServer = new ParseLiveQueryServer({}); - const EventEmitter = require('events'); + const EventEmitter = require("events"); const parseWebSocket = new EventEmitter(); parseWebSocket.clientId = 1; // Register message handlers for the parseWebSocket @@ -654,7 +714,7 @@ describe('ParseLiveQueryServer', function () { // Make sure we do not crash // Trigger disconnect event - parseWebSocket.emit('disconnect'); + parseWebSocket.emit("disconnect"); expect(spy).toHaveBeenCalled(); // call for ws_connect, another for ws_disconnect expect(spy.calls.count()).toBe(2); @@ -662,12 +722,12 @@ describe('ParseLiveQueryServer', function () { // TODO: Test server can set disconnect command message handler for a parseWebSocket - it('has no subscription and can handle object delete command', function () { + it("has no subscription and can handle object delete command", function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make deletedParseObject const parseObject = new Parse.Object(testClassName); parseObject._finishFetch({ - key: 'value', + key: "value", className: testClassName, }); // Make mock message @@ -678,12 +738,12 @@ describe('ParseLiveQueryServer', function () { parseLiveQueryServer._onAfterDelete(message, {}); }); - it('can handle object delete command which does not match any subscription', async () => { + it("can handle object delete command which does not match any subscription", async () => { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make deletedParseObject const parseObject = new Parse.Object(testClassName); parseObject._finishFetch({ - key: 'value', + key: "value", className: testClassName, }); // Make mock message @@ -711,12 +771,12 @@ describe('ParseLiveQueryServer', function () { expect(client.pushDelete).not.toHaveBeenCalled(); }); - it('can handle object delete command which matches some subscriptions', async done => { + it("can handle object delete command which matches some subscriptions", async done => { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make deletedParseObject const parseObject = new Parse.Object(testClassName); parseObject._finishFetch({ - key: 'value', + key: "value", className: testClassName, }); // Make mock message @@ -747,7 +807,7 @@ describe('ParseLiveQueryServer', function () { done(); }); - it('has no subscription and can handle object save command', async () => { + it("has no subscription and can handle object save command", async () => { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make mock request message const message = generateMockMessage(); @@ -755,17 +815,17 @@ describe('ParseLiveQueryServer', function () { parseLiveQueryServer._onAfterSave(message); }); - it('sends correct object for dates', async () => { - jasmine.restoreLibrary('../lib/LiveQuery/QueryTools', 'matchesQuery'); + it("sends correct object for dates", async () => { + jasmine.restoreLibrary("../lib/LiveQuery/QueryTools", "matchesQuery"); const parseLiveQueryServer = new ParseLiveQueryServer({}); const date = new Date(); const message = { currentParseObject: { - date: { __type: 'Date', iso: date.toISOString() }, - __type: 'Object', - key: 'value', + date: { __type: "Date", iso: date.toISOString() }, + __type: "Object", + key: "value", className: testClassName, }, }; @@ -790,15 +850,15 @@ describe('ParseLiveQueryServer', function () { expect(client.pushCreate).toHaveBeenCalledWith( requestId2, { - className: 'TestObject', - key: 'value', - date: { __type: 'Date', iso: date.toISOString() }, + className: "TestObject", + key: "value", + date: { __type: "Date", iso: date.toISOString() }, }, null ); }); - it('can handle object save command which does not match any subscription', async done => { + it("can handle object save command which does not match any subscription", async done => { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make mock request message const message = generateMockMessage(); @@ -829,7 +889,7 @@ describe('ParseLiveQueryServer', function () { done(); }); - it('can handle object enter command which matches some subscriptions', async done => { + it("can handle object enter command which matches some subscriptions", async done => { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make mock request message const message = generateMockMessage(true); @@ -866,7 +926,7 @@ describe('ParseLiveQueryServer', function () { done(); }); - it('can handle object update command which matches some subscriptions', async done => { + it("can handle object update command which matches some subscriptions", async done => { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make mock request message const message = generateMockMessage(true); @@ -899,7 +959,7 @@ describe('ParseLiveQueryServer', function () { done(); }); - it('can handle object leave command which matches some subscriptions', async done => { + it("can handle object leave command which matches some subscriptions", async done => { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make mock request message const message = generateMockMessage(true); @@ -936,10 +996,10 @@ describe('ParseLiveQueryServer', function () { done(); }); - it('sends correct events for object with multiple subscriptions', async done => { + it("sends correct events for object with multiple subscriptions", async done => { const parseLiveQueryServer = new ParseLiveQueryServer({}); - Parse.Cloud.afterLiveQueryEvent('TestObject', () => { + Parse.Cloud.afterLiveQueryEvent("TestObject", () => { // Simulate delay due to trigger, auth, etc. return jasmine.timeout(10); }); @@ -949,21 +1009,47 @@ describe('ParseLiveQueryServer', function () { // Add mock client const clientId = 1; const client = addMockClient(parseLiveQueryServer, clientId); - client.sessionToken = 'sessionToken'; + client.sessionToken = "sessionToken"; // Mock queryHash for this special test - const mockQueryHash = jasmine.createSpy('matchesQuery').and.returnValue('hash1'); - jasmine.mockLibrary('../lib/LiveQuery/QueryTools', 'queryHash', mockQueryHash); + const mockQueryHash = jasmine + .createSpy("matchesQuery") + .and.returnValue("hash1"); + jasmine.mockLibrary( + "../lib/LiveQuery/QueryTools", + "queryHash", + mockQueryHash + ); // Add mock subscription 1 const requestId2 = 2; - await addMockSubscription(parseLiveQueryServer, clientId, requestId2, null, null, 'hash1'); + await addMockSubscription( + parseLiveQueryServer, + clientId, + requestId2, + null, + null, + "hash1" + ); // Mock queryHash for this special test - const mockQueryHash2 = jasmine.createSpy('matchesQuery').and.returnValue('hash2'); - jasmine.mockLibrary('../lib/LiveQuery/QueryTools', 'queryHash', mockQueryHash2); + const mockQueryHash2 = jasmine + .createSpy("matchesQuery") + .and.returnValue("hash2"); + jasmine.mockLibrary( + "../lib/LiveQuery/QueryTools", + "queryHash", + mockQueryHash2 + ); // Add mock subscription 2 const requestId3 = 3; - await addMockSubscription(parseLiveQueryServer, clientId, requestId3, null, null, 'hash2'); + await addMockSubscription( + parseLiveQueryServer, + clientId, + requestId3, + null, + null, + "hash2" + ); // Mock _matchesSubscription to return matching // In order to mimic a leave, then enter, we need original match return true // and the current match return false, then the other way around @@ -989,23 +1075,23 @@ describe('ParseLiveQueryServer', function () { expect(client.pushEnter).toHaveBeenCalledTimes(1); expect(client.pushEnter).toHaveBeenCalledWith( requestId3, - { key: 'value', className: 'TestObject' }, - { key: 'originalValue', className: 'TestObject' } + { key: "value", className: "TestObject" }, + { key: "originalValue", className: "TestObject" } ); expect(client.pushUpdate).not.toHaveBeenCalled(); expect(client.pushDelete).not.toHaveBeenCalled(); expect(client.pushLeave).toHaveBeenCalledTimes(1); expect(client.pushLeave).toHaveBeenCalledWith( requestId2, - { key: 'value', className: 'TestObject' }, - { key: 'originalValue', className: 'TestObject' } + { key: "value", className: "TestObject" }, + { key: "originalValue", className: "TestObject" } ); done(); }); - it('can handle update command with original object', async done => { - jasmine.restoreLibrary('../lib/LiveQuery/Client', 'Client'); - const Client = require('../lib/LiveQuery/Client').Client; + it("can handle update command with original object", async done => { + jasmine.restoreLibrary("../lib/LiveQuery/Client", "Client"); + const Client = require("../lib/LiveQuery/Client").Client; const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make mock request message const message = generateMockMessage(true); @@ -1013,16 +1099,21 @@ describe('ParseLiveQueryServer', function () { const clientId = 1; const parseWebSocket = { clientId, - send: jasmine.createSpy('send'), + send: jasmine.createSpy("send"), }; const client = new Client(clientId, parseWebSocket); - spyOn(client, 'pushUpdate').and.callThrough(); + spyOn(client, "pushUpdate").and.callThrough(); parseLiveQueryServer.clients.set(clientId, client); // Add mock subscription const requestId = 2; - await addMockSubscription(parseLiveQueryServer, clientId, requestId, parseWebSocket); + await addMockSubscription( + parseLiveQueryServer, + clientId, + requestId, + parseWebSocket + ); // Mock _matchesSubscription to return matching parseLiveQueryServer._matchesSubscription = function (parseObject) { if (!parseObject) { @@ -1048,7 +1139,7 @@ describe('ParseLiveQueryServer', function () { done(); }); - it('can handle object create command which matches some subscriptions', async done => { + it("can handle object create command which matches some subscriptions", async done => { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make mock request message const message = generateMockMessage(); @@ -1081,9 +1172,9 @@ describe('ParseLiveQueryServer', function () { done(); }); - it('can handle create command with keys', async done => { - jasmine.restoreLibrary('../lib/LiveQuery/Client', 'Client'); - const Client = require('../lib/LiveQuery/Client').Client; + it("can handle create command with keys", async done => { + jasmine.restoreLibrary("../lib/LiveQuery/Client", "Client"); + const Client = require("../lib/LiveQuery/Client").Client; const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make mock request message const message = generateMockMessage(); @@ -1091,10 +1182,10 @@ describe('ParseLiveQueryServer', function () { const clientId = 1; const parseWebSocket = { clientId, - send: jasmine.createSpy('send'), + send: jasmine.createSpy("send"), }; const client = new Client(clientId, parseWebSocket); - spyOn(client, 'pushCreate').and.callThrough(); + spyOn(client, "pushCreate").and.callThrough(); parseLiveQueryServer.clients.set(clientId, client); // Add mock subscription @@ -1102,11 +1193,17 @@ describe('ParseLiveQueryServer', function () { const query = { className: testClassName, where: { - key: 'value', + key: "value", }, - keys: ['test'], + keys: ["test"], }; - await addMockSubscription(parseLiveQueryServer, clientId, requestId, parseWebSocket, query); + await addMockSubscription( + parseLiveQueryServer, + clientId, + requestId, + parseWebSocket, + query + ); // Mock _matchesSubscription to return matching parseLiveQueryServer._matchesSubscription = function (parseObject) { if (!parseObject) { @@ -1131,9 +1228,9 @@ describe('ParseLiveQueryServer', function () { done(); }); - it('can handle create command with watch', async () => { - jasmine.restoreLibrary('../lib/LiveQuery/Client', 'Client'); - const Client = require('../lib/LiveQuery/Client').Client; + it("can handle create command with watch", async () => { + jasmine.restoreLibrary("../lib/LiveQuery/Client", "Client"); + const Client = require("../lib/LiveQuery/Client").Client; const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make mock request message const message = generateMockMessage(); @@ -1141,10 +1238,10 @@ describe('ParseLiveQueryServer', function () { const clientId = 1; const parseWebSocket = { clientId, - send: jasmine.createSpy('send'), + send: jasmine.createSpy("send"), }; const client = new Client(clientId, parseWebSocket); - spyOn(client, 'pushCreate').and.callThrough(); + spyOn(client, "pushCreate").and.callThrough(); parseLiveQueryServer.clients.set(clientId, client); // Add mock subscription @@ -1152,11 +1249,17 @@ describe('ParseLiveQueryServer', function () { const query = { className: testClassName, where: { - key: 'value', + key: "value", }, - watch: ['yolo'], + watch: ["yolo"], }; - await addMockSubscription(parseLiveQueryServer, clientId, requestId, parseWebSocket, query); + await addMockSubscription( + parseLiveQueryServer, + clientId, + requestId, + parseWebSocket, + query + ); // Mock _matchesSubscription to return matching parseLiveQueryServer._matchesSubscription = function (parseObject) { if (!parseObject) { @@ -1175,7 +1278,7 @@ describe('ParseLiveQueryServer', function () { expect(client.pushCreate).not.toHaveBeenCalled(); - message.currentParseObject.set('yolo', 'test'); + message.currentParseObject.set("yolo", "test"); parseLiveQueryServer._onAfterSave(message); await timeout(); @@ -1186,48 +1289,54 @@ describe('ParseLiveQueryServer', function () { expect(toSend.original).toBeUndefined(); }); - it('can match subscription for null or undefined parse object', function () { + it("can match subscription for null or undefined parse object", function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make mock subscription const subscription = { - match: jasmine.createSpy('match'), + match: jasmine.createSpy("match"), }; - expect(parseLiveQueryServer._matchesSubscription(null, subscription)).toBe(false); - expect(parseLiveQueryServer._matchesSubscription(undefined, subscription)).toBe(false); + expect(parseLiveQueryServer._matchesSubscription(null, subscription)).toBe( + false + ); + expect( + parseLiveQueryServer._matchesSubscription(undefined, subscription) + ).toBe(false); // Make sure subscription.match is not called expect(subscription.match).not.toHaveBeenCalled(); }); - it('can match subscription', function () { + it("can match subscription", function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make mock subscription const subscription = { query: {}, }; const parseObject = {}; - expect(parseLiveQueryServer._matchesSubscription(parseObject, subscription)).toBe(true); + expect( + parseLiveQueryServer._matchesSubscription(parseObject, subscription) + ).toBe(true); // Make sure matchesQuery is called - const matchesQuery = require('../lib/LiveQuery/QueryTools').matchesQuery; + const matchesQuery = require("../lib/LiveQuery/QueryTools").matchesQuery; expect(matchesQuery).toHaveBeenCalledWith(parseObject, subscription.query); }); - it('can inflate parse object', function () { + it("can inflate parse object", function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make mock request const objectJSON = { - className: 'testClassName', - createdAt: '2015-12-22T01:51:12.955Z', - key: 'value', - objectId: 'BfwxBCz6yW', - updatedAt: '2016-01-05T00:46:45.659Z', + className: "testClassName", + createdAt: "2015-12-22T01:51:12.955Z", + key: "value", + objectId: "BfwxBCz6yW", + updatedAt: "2016-01-05T00:46:45.659Z", }; const originalObjectJSON = { - className: 'testClassName', - createdAt: '2015-12-22T01:51:12.955Z', - key: 'originalValue', - objectId: 'BfwxBCz6yW', - updatedAt: '2016-01-05T00:46:45.659Z', + className: "testClassName", + createdAt: "2015-12-22T01:51:12.955Z", + key: "originalValue", + objectId: "BfwxBCz6yW", + updatedAt: "2016-01-05T00:46:45.659Z", }; const message = { currentParseObject: objectJSON, @@ -1239,47 +1348,47 @@ describe('ParseLiveQueryServer', function () { // Verify object const object = message.currentParseObject; expect(object instanceof Parse.Object).toBeTruthy(); - expect(object.get('key')).toEqual('value'); - expect(object.className).toEqual('testClassName'); - expect(object.id).toBe('BfwxBCz6yW'); + expect(object.get("key")).toEqual("value"); + expect(object.className).toEqual("testClassName"); + expect(object.id).toBe("BfwxBCz6yW"); expect(object.createdAt).not.toBeUndefined(); expect(object.updatedAt).not.toBeUndefined(); // Verify original object const originalObject = message.originalParseObject; expect(originalObject instanceof Parse.Object).toBeTruthy(); - expect(originalObject.get('key')).toEqual('originalValue'); - expect(originalObject.className).toEqual('testClassName'); - expect(originalObject.id).toBe('BfwxBCz6yW'); + expect(originalObject.get("key")).toEqual("originalValue"); + expect(originalObject.className).toEqual("testClassName"); + expect(originalObject.id).toBe("BfwxBCz6yW"); expect(originalObject.createdAt).not.toBeUndefined(); expect(originalObject.updatedAt).not.toBeUndefined(); }); - it('can inflate user object', async () => { + it("can inflate user object", async () => { const parseLiveQueryServer = new ParseLiveQueryServer({}); const userJSON = { - username: 'test', + username: "test", ACL: {}, - createdAt: '2018-12-21T23:09:51.784Z', - sessionToken: 'r:1234', - updatedAt: '2018-12-21T23:09:51.784Z', - objectId: 'NhF2u9n72W', - __type: 'Object', - className: '_User', - _hashed_password: '1234', - _email_verify_token: '1234', + createdAt: "2018-12-21T23:09:51.784Z", + sessionToken: "r:1234", + updatedAt: "2018-12-21T23:09:51.784Z", + objectId: "NhF2u9n72W", + __type: "Object", + className: "_User", + _hashed_password: "1234", + _email_verify_token: "1234", }; const originalUserJSON = { - username: 'test', + username: "test", ACL: {}, - createdAt: '2018-12-21T23:09:51.784Z', - sessionToken: 'r:1234', - updatedAt: '2018-12-21T23:09:51.784Z', - objectId: 'NhF2u9n72W', - __type: 'Object', - className: '_User', - _hashed_password: '12345', - _email_verify_token: '12345', + createdAt: "2018-12-21T23:09:51.784Z", + sessionToken: "r:1234", + updatedAt: "2018-12-21T23:09:51.784Z", + objectId: "NhF2u9n72W", + __type: "Object", + className: "_User", + _hashed_password: "12345", + _email_verify_token: "12345", }; const message = { @@ -1290,156 +1399,186 @@ describe('ParseLiveQueryServer', function () { const object = message.currentParseObject; expect(object instanceof Parse.Object).toBeTruthy(); - expect(object.get('_hashed_password')).toBeUndefined(); - expect(object.get('_email_verify_token')).toBeUndefined(); - expect(object.className).toEqual('_User'); - expect(object.id).toBe('NhF2u9n72W'); + expect(object.get("_hashed_password")).toBeUndefined(); + expect(object.get("_email_verify_token")).toBeUndefined(); + expect(object.className).toEqual("_User"); + expect(object.id).toBe("NhF2u9n72W"); expect(object.createdAt).not.toBeUndefined(); expect(object.updatedAt).not.toBeUndefined(); const originalObject = message.originalParseObject; expect(originalObject instanceof Parse.Object).toBeTruthy(); - expect(originalObject.get('_hashed_password')).toBeUndefined(); - expect(originalObject.get('_email_verify_token')).toBeUndefined(); - expect(originalObject.className).toEqual('_User'); - expect(originalObject.id).toBe('NhF2u9n72W'); + expect(originalObject.get("_hashed_password")).toBeUndefined(); + expect(originalObject.get("_email_verify_token")).toBeUndefined(); + expect(originalObject.className).toEqual("_User"); + expect(originalObject.id).toBe("NhF2u9n72W"); expect(originalObject.createdAt).not.toBeUndefined(); expect(originalObject.updatedAt).not.toBeUndefined(); }); - it('can match undefined ACL', function (done) { + it("can match undefined ACL", function (done) { const parseLiveQueryServer = new ParseLiveQueryServer({}); const client = {}; const requestId = 0; - parseLiveQueryServer._matchesACL(undefined, client, requestId).then(function (isMatched) { - expect(isMatched).toBe(true); - done(); - }); + parseLiveQueryServer + ._matchesACL(undefined, client, requestId) + .then(function (isMatched) { + expect(isMatched).toBe(true); + done(); + }); }); - it('can match ACL with none exist requestId', function (done) { + it("can match ACL with none exist requestId", function (done) { const parseLiveQueryServer = new ParseLiveQueryServer({}); const acl = new Parse.ACL(); const client = { - getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue(undefined), + getSubscriptionInfo: jasmine + .createSpy("getSubscriptionInfo") + .and.returnValue(undefined), }; const requestId = 0; - parseLiveQueryServer._matchesACL(acl, client, requestId).then(function (isMatched) { - expect(isMatched).toBe(false); - done(); - }); + parseLiveQueryServer + ._matchesACL(acl, client, requestId) + .then(function (isMatched) { + expect(isMatched).toBe(false); + done(); + }); }); - it('can match ACL with public read access', function (done) { + it("can match ACL with public read access", function (done) { const parseLiveQueryServer = new ParseLiveQueryServer({}); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); const client = { - getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({ - sessionToken: 'sessionToken', - }), + getSubscriptionInfo: jasmine + .createSpy("getSubscriptionInfo") + .and.returnValue({ + sessionToken: "sessionToken", + }), }; const requestId = 0; - parseLiveQueryServer._matchesACL(acl, client, requestId).then(function (isMatched) { - expect(isMatched).toBe(true); - done(); - }); + parseLiveQueryServer + ._matchesACL(acl, client, requestId) + .then(function (isMatched) { + expect(isMatched).toBe(true); + done(); + }); }); - it('can match ACL with valid subscription sessionToken', function (done) { + it("can match ACL with valid subscription sessionToken", function (done) { const parseLiveQueryServer = new ParseLiveQueryServer({}); const acl = new Parse.ACL(); acl.setReadAccess(testUserId, true); const client = { - getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({ - sessionToken: 'sessionToken', - }), + getSubscriptionInfo: jasmine + .createSpy("getSubscriptionInfo") + .and.returnValue({ + sessionToken: "sessionToken", + }), }; const requestId = 0; - parseLiveQueryServer._matchesACL(acl, client, requestId).then(function (isMatched) { - expect(isMatched).toBe(true); - done(); - }); + parseLiveQueryServer + ._matchesACL(acl, client, requestId) + .then(function (isMatched) { + expect(isMatched).toBe(true); + done(); + }); }); - it('can match ACL with valid client sessionToken', function (done) { + it("can match ACL with valid client sessionToken", function (done) { const parseLiveQueryServer = new ParseLiveQueryServer({}); const acl = new Parse.ACL(); acl.setReadAccess(testUserId, true); // Mock sessionTokenCache will return false when sessionToken is undefined const client = { - sessionToken: 'sessionToken', - getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({ - sessionToken: undefined, - }), + sessionToken: "sessionToken", + getSubscriptionInfo: jasmine + .createSpy("getSubscriptionInfo") + .and.returnValue({ + sessionToken: undefined, + }), }; const requestId = 0; - parseLiveQueryServer._matchesACL(acl, client, requestId).then(function (isMatched) { - expect(isMatched).toBe(true); - done(); - }); + parseLiveQueryServer + ._matchesACL(acl, client, requestId) + .then(function (isMatched) { + expect(isMatched).toBe(true); + done(); + }); }); - it('can match ACL with invalid subscription and client sessionToken', function (done) { + it("can match ACL with invalid subscription and client sessionToken", function (done) { const parseLiveQueryServer = new ParseLiveQueryServer({}); const acl = new Parse.ACL(); acl.setReadAccess(testUserId, true); // Mock sessionTokenCache will return false when sessionToken is undefined const client = { sessionToken: undefined, - getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({ - sessionToken: undefined, - }), + getSubscriptionInfo: jasmine + .createSpy("getSubscriptionInfo") + .and.returnValue({ + sessionToken: undefined, + }), }; const requestId = 0; - parseLiveQueryServer._matchesACL(acl, client, requestId).then(function (isMatched) { - expect(isMatched).toBe(false); - done(); - }); + parseLiveQueryServer + ._matchesACL(acl, client, requestId) + .then(function (isMatched) { + expect(isMatched).toBe(false); + done(); + }); }); - it('can match ACL with subscription sessionToken checking error', function (done) { + it("can match ACL with subscription sessionToken checking error", function (done) { const parseLiveQueryServer = new ParseLiveQueryServer({}); const acl = new Parse.ACL(); acl.setReadAccess(testUserId, true); // Mock sessionTokenCache will return error when sessionToken is null, this is just // the behaviour of our mock sessionTokenCache, not real sessionTokenCache const client = { - getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({ - sessionToken: null, - }), + getSubscriptionInfo: jasmine + .createSpy("getSubscriptionInfo") + .and.returnValue({ + sessionToken: null, + }), }; const requestId = 0; - parseLiveQueryServer._matchesACL(acl, client, requestId).then(function (isMatched) { - expect(isMatched).toBe(false); - done(); - }); + parseLiveQueryServer + ._matchesACL(acl, client, requestId) + .then(function (isMatched) { + expect(isMatched).toBe(false); + done(); + }); }); - it('can match ACL with client sessionToken checking error', function (done) { + it("can match ACL with client sessionToken checking error", function (done) { const parseLiveQueryServer = new ParseLiveQueryServer({}); const acl = new Parse.ACL(); acl.setReadAccess(testUserId, true); // Mock sessionTokenCache will return error when sessionToken is null const client = { sessionToken: null, - getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({ - sessionToken: null, - }), + getSubscriptionInfo: jasmine + .createSpy("getSubscriptionInfo") + .and.returnValue({ + sessionToken: null, + }), }; const requestId = 0; - parseLiveQueryServer._matchesACL(acl, client, requestId).then(function (isMatched) { - expect(isMatched).toBe(false); - done(); - }); + parseLiveQueryServer + ._matchesACL(acl, client, requestId) + .then(function (isMatched) { + expect(isMatched).toBe(false); + done(); + }); }); it("won't match ACL that doesn't have public read or any roles", function (done) { @@ -1447,25 +1586,31 @@ describe('ParseLiveQueryServer', function () { const acl = new Parse.ACL(); acl.setPublicReadAccess(false); const client = { - getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({ - sessionToken: 'sessionToken', - }), + getSubscriptionInfo: jasmine + .createSpy("getSubscriptionInfo") + .and.returnValue({ + sessionToken: "sessionToken", + }), }; const requestId = 0; - parseLiveQueryServer._matchesACL(acl, client, requestId).then(function (isMatched) { - expect(isMatched).toBe(false); - done(); - }); + parseLiveQueryServer + ._matchesACL(acl, client, requestId) + .then(function (isMatched) { + expect(isMatched).toBe(false); + done(); + }); }); it("won't match non-public ACL with role when there is no user", function (done) { const parseLiveQueryServer = new ParseLiveQueryServer({}); const acl = new Parse.ACL(); acl.setPublicReadAccess(false); - acl.setRoleReadAccess('livequery', true); + acl.setRoleReadAccess("livequery", true); const client = { - getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({}), + getSubscriptionInfo: jasmine + .createSpy("getSubscriptionInfo") + .and.returnValue({}), }; const requestId = 0; @@ -1482,15 +1627,17 @@ describe('ParseLiveQueryServer', function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); const acl = new Parse.ACL(); acl.setPublicReadAccess(false); - acl.setRoleReadAccess('otherLiveQueryRead', true); + acl.setRoleReadAccess("otherLiveQueryRead", true); const client = { - getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({ - sessionToken: 'sessionToken', - }), + getSubscriptionInfo: jasmine + .createSpy("getSubscriptionInfo") + .and.returnValue({ + sessionToken: "sessionToken", + }), }; const requestId = 0; - spyOn(Parse, 'Query').and.callFake(function () { + spyOn(Parse, "Query").and.callFake(function () { let shouldReturn = false; return { equalTo() { @@ -1507,37 +1654,46 @@ describe('ParseLiveQueryServer', function () { return Promise.resolve([]); } //Return a role with the name "liveQueryRead" as that is what was set on the ACL - const liveQueryRole = new Parse.Role('liveQueryRead', new Parse.ACL()); - liveQueryRole.id = 'abcdef1234'; + const liveQueryRole = new Parse.Role( + "liveQueryRead", + new Parse.ACL() + ); + liveQueryRole.id = "abcdef1234"; return Promise.resolve([liveQueryRole]); }, }; }); - parseLiveQueryServer._matchesACL(acl, client, requestId).then(function (isMatched) { - expect(isMatched).toBe(false); - done(); - }); + parseLiveQueryServer + ._matchesACL(acl, client, requestId) + .then(function (isMatched) { + expect(isMatched).toBe(false); + done(); + }); - parseLiveQueryServer._matchesACL(acl, client, requestId).then(function (isMatched) { - expect(isMatched).toBe(false); - done(); - }); + parseLiveQueryServer + ._matchesACL(acl, client, requestId) + .then(function (isMatched) { + expect(isMatched).toBe(false); + done(); + }); }); - it('will match ACL with role based read access set to true', function (done) { + it("will match ACL with role based read access set to true", function (done) { const parseLiveQueryServer = new ParseLiveQueryServer({}); const acl = new Parse.ACL(); acl.setPublicReadAccess(false); - acl.setRoleReadAccess('liveQueryRead', true); + acl.setRoleReadAccess("liveQueryRead", true); const client = { - getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({ - sessionToken: 'sessionToken', - }), + getSubscriptionInfo: jasmine + .createSpy("getSubscriptionInfo") + .and.returnValue({ + sessionToken: "sessionToken", + }), }; const requestId = 0; - spyOn(Parse, 'Query').and.callFake(function () { + spyOn(Parse, "Query").and.callFake(function () { let shouldReturn = false; return { equalTo() { @@ -1554,37 +1710,47 @@ describe('ParseLiveQueryServer', function () { return Promise.resolve([]); } //Return a role with the name "liveQueryRead" as that is what was set on the ACL - const liveQueryRole = new Parse.Role('liveQueryRead', new Parse.ACL()); - liveQueryRole.id = 'abcdef1234'; + const liveQueryRole = new Parse.Role( + "liveQueryRead", + new Parse.ACL() + ); + liveQueryRole.id = "abcdef1234"; return Promise.resolve([liveQueryRole]); }, each(callback) { //Return a role with the name "liveQueryRead" as that is what was set on the ACL - const liveQueryRole = new Parse.Role('liveQueryRead', new Parse.ACL()); - liveQueryRole.id = 'abcdef1234'; + const liveQueryRole = new Parse.Role( + "liveQueryRead", + new Parse.ACL() + ); + liveQueryRole.id = "abcdef1234"; callback(liveQueryRole); return Promise.resolve(); }, }; }); - parseLiveQueryServer._matchesACL(acl, client, requestId).then(function (isMatched) { - expect(isMatched).toBe(true); - done(); - }); + parseLiveQueryServer + ._matchesACL(acl, client, requestId) + .then(function (isMatched) { + expect(isMatched).toBe(true); + done(); + }); }); - describe('class level permissions', () => { - it('matches CLP when find is closed', done => { + describe("class level permissions", () => { + it("matches CLP when find is closed", done => { const parseLiveQueryServer = new ParseLiveQueryServer({}); const acl = new Parse.ACL(); acl.setReadAccess(testUserId, true); // Mock sessionTokenCache will return false when sessionToken is undefined const client = { - sessionToken: 'sessionToken', - getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({ - sessionToken: undefined, - }), + sessionToken: "sessionToken", + getSubscriptionInfo: jasmine + .createSpy("getSubscriptionInfo") + .and.returnValue({ + sessionToken: undefined, + }), }; const requestId = 0; @@ -1593,10 +1759,10 @@ describe('ParseLiveQueryServer', function () { { find: {}, }, - { className: 'Yolo' }, + { className: "Yolo" }, client, requestId, - 'find' + "find" ) .then(isMatched => { expect(isMatched).toBe(false); @@ -1604,28 +1770,30 @@ describe('ParseLiveQueryServer', function () { }); }); - it('matches CLP when find is open', done => { + it("matches CLP when find is open", done => { const parseLiveQueryServer = new ParseLiveQueryServer({}); const acl = new Parse.ACL(); acl.setReadAccess(testUserId, true); // Mock sessionTokenCache will return false when sessionToken is undefined const client = { - sessionToken: 'sessionToken', - getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({ - sessionToken: undefined, - }), + sessionToken: "sessionToken", + getSubscriptionInfo: jasmine + .createSpy("getSubscriptionInfo") + .and.returnValue({ + sessionToken: undefined, + }), }; const requestId = 0; parseLiveQueryServer ._matchesCLP( { - find: { '*': true }, + find: { "*": true }, }, - { className: 'Yolo' }, + { className: "Yolo" }, client, requestId, - 'find' + "find" ) .then(isMatched => { expect(isMatched).toBe(true); @@ -1633,16 +1801,18 @@ describe('ParseLiveQueryServer', function () { }); }); - it('matches CLP when find is restricted to userIds', done => { + it("matches CLP when find is restricted to userIds", done => { const parseLiveQueryServer = new ParseLiveQueryServer({}); const acl = new Parse.ACL(); acl.setReadAccess(testUserId, true); // Mock sessionTokenCache will return false when sessionToken is undefined const client = { - sessionToken: 'sessionToken', - getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({ - sessionToken: undefined, - }), + sessionToken: "sessionToken", + getSubscriptionInfo: jasmine + .createSpy("getSubscriptionInfo") + .and.returnValue({ + sessionToken: undefined, + }), }; const requestId = 0; @@ -1651,10 +1821,10 @@ describe('ParseLiveQueryServer', function () { { find: { userId: true }, }, - { className: 'Yolo' }, + { className: "Yolo" }, client, requestId, - 'find' + "find" ) .then(isMatched => { expect(isMatched).toBe(false); @@ -1663,33 +1833,35 @@ describe('ParseLiveQueryServer', function () { }); }); - it('can validate key when valid key is provided', function () { + it("can validate key when valid key is provided", function () { const parseLiveQueryServer = new ParseLiveQueryServer( {}, { keyPairs: { - clientKey: 'test', + clientKey: "test", }, } ); const request = { - clientKey: 'test', + clientKey: "test", }; - expect(parseLiveQueryServer._validateKeys(request, parseLiveQueryServer.keyPairs)).toBeTruthy(); + expect( + parseLiveQueryServer._validateKeys(request, parseLiveQueryServer.keyPairs) + ).toBeTruthy(); }); - it('can validate key when invalid key is provided', function () { + it("can validate key when invalid key is provided", function () { const parseLiveQueryServer = new ParseLiveQueryServer( {}, { keyPairs: { - clientKey: 'test', + clientKey: "test", }, } ); const request = { - clientKey: 'error', + clientKey: "error", }; expect( @@ -1697,12 +1869,12 @@ describe('ParseLiveQueryServer', function () { ).not.toBeTruthy(); }); - it('can validate key when key is not provided', function () { + it("can validate key when key is not provided", function () { const parseLiveQueryServer = new ParseLiveQueryServer( {}, { keyPairs: { - clientKey: 'test', + clientKey: "test", }, } ); @@ -1713,27 +1885,31 @@ describe('ParseLiveQueryServer', function () { ).not.toBeTruthy(); }); - it('can validate key when validKerPairs is empty', function () { + it("can validate key when validKerPairs is empty", function () { const parseLiveQueryServer = new ParseLiveQueryServer({}, {}); const request = {}; - expect(parseLiveQueryServer._validateKeys(request, parseLiveQueryServer.keyPairs)).toBeTruthy(); + expect( + parseLiveQueryServer._validateKeys(request, parseLiveQueryServer.keyPairs) + ).toBeTruthy(); }); - it('can validate client has master key when valid', function () { + it("can validate client has master key when valid", function () { const parseLiveQueryServer = new ParseLiveQueryServer( {}, { keyPairs: { - masterKey: 'test', + masterKey: "test", }, } ); const request = { - masterKey: 'test', + masterKey: "test", }; - expect(parseLiveQueryServer._hasMasterKey(request, parseLiveQueryServer.keyPairs)).toBeTruthy(); + expect( + parseLiveQueryServer._hasMasterKey(request, parseLiveQueryServer.keyPairs) + ).toBeTruthy(); }); it("can validate client doesn't have master key when invalid", function () { @@ -1741,12 +1917,12 @@ describe('ParseLiveQueryServer', function () { {}, { keyPairs: { - masterKey: 'test', + masterKey: "test", }, } ); const request = { - masterKey: 'notValid', + masterKey: "notValid", }; expect( @@ -1759,18 +1935,20 @@ describe('ParseLiveQueryServer', function () { {}, { keyPairs: { - masterKey: 'test', + masterKey: "test", }, } ); - expect(parseLiveQueryServer._hasMasterKey({}, parseLiveQueryServer.keyPairs)).not.toBeTruthy(); + expect( + parseLiveQueryServer._hasMasterKey({}, parseLiveQueryServer.keyPairs) + ).not.toBeTruthy(); }); it("can validate client doesn't have master key when validKeyPairs is empty", function () { const parseLiveQueryServer = new ParseLiveQueryServer({}, {}); const request = { - masterKey: 'test', + masterKey: "test", }; expect( @@ -1778,20 +1956,24 @@ describe('ParseLiveQueryServer', function () { ).not.toBeTruthy(); }); - it('will match non-public ACL when client has master key', function (done) { + it("will match non-public ACL when client has master key", function (done) { const parseLiveQueryServer = new ParseLiveQueryServer({}); const acl = new Parse.ACL(); acl.setPublicReadAccess(false); const client = { - getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({}), + getSubscriptionInfo: jasmine + .createSpy("getSubscriptionInfo") + .and.returnValue({}), hasMasterKey: true, }; const requestId = 0; - parseLiveQueryServer._matchesACL(acl, client, requestId).then(function (isMatched) { - expect(isMatched).toBe(true); - done(); - }); + parseLiveQueryServer + ._matchesACL(acl, client, requestId) + .then(function (isMatched) { + expect(isMatched).toBe(true); + done(); + }); }); it("won't match non-public ACL when client has no master key", function (done) { @@ -1799,61 +1981,69 @@ describe('ParseLiveQueryServer', function () { const acl = new Parse.ACL(); acl.setPublicReadAccess(false); const client = { - getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({}), + getSubscriptionInfo: jasmine + .createSpy("getSubscriptionInfo") + .and.returnValue({}), hasMasterKey: false, }; const requestId = 0; - parseLiveQueryServer._matchesACL(acl, client, requestId).then(function (isMatched) { - expect(isMatched).toBe(false); - done(); - }); + parseLiveQueryServer + ._matchesACL(acl, client, requestId) + .then(function (isMatched) { + expect(isMatched).toBe(false); + done(); + }); }); - it('should properly pull auth from cache', () => { + it("should properly pull auth from cache", () => { const parseLiveQueryServer = new ParseLiveQueryServer({}); - const promise = parseLiveQueryServer.getAuthForSessionToken('sessionToken'); - const secondPromise = parseLiveQueryServer.getAuthForSessionToken('sessionToken'); + const promise = parseLiveQueryServer.getAuthForSessionToken("sessionToken"); + const secondPromise = + parseLiveQueryServer.getAuthForSessionToken("sessionToken"); // should be in the cache - expect(parseLiveQueryServer.authCache.get('sessionToken')).toBe(promise); + expect(parseLiveQueryServer.authCache.get("sessionToken")).toBe(promise); // should be the same promise returned expect(promise).toBe(secondPromise); // the auth should be called only once expect(auth.getAuthForSessionToken.calls.count()).toBe(1); }); - it('should delete from cache throwing auth calls', async () => { + it("should delete from cache throwing auth calls", async () => { const parseLiveQueryServer = new ParseLiveQueryServer({}); - const promise = parseLiveQueryServer.getAuthForSessionToken('pleaseThrow'); - expect(parseLiveQueryServer.authCache.get('pleaseThrow')).toBe(promise); + const promise = parseLiveQueryServer.getAuthForSessionToken("pleaseThrow"); + expect(parseLiveQueryServer.authCache.get("pleaseThrow")).toBe(promise); // after the promise finishes, it should have removed it from the cache expect(await promise).toEqual({}); - expect(parseLiveQueryServer.authCache.get('pleaseThrow')).toBe(undefined); + expect(parseLiveQueryServer.authCache.get("pleaseThrow")).toBe(undefined); }); - it('should keep a cache of invalid sessions', async () => { + it("should keep a cache of invalid sessions", async () => { const parseLiveQueryServer = new ParseLiveQueryServer({}); - const promise = parseLiveQueryServer.getAuthForSessionToken('invalid'); - expect(parseLiveQueryServer.authCache.get('invalid')).toBe(promise); + const promise = parseLiveQueryServer.getAuthForSessionToken("invalid"); + expect(parseLiveQueryServer.authCache.get("invalid")).toBe(promise); // after the promise finishes, it should have removed it from the cache await promise; - const finalResult = await parseLiveQueryServer.authCache.get('invalid'); + const finalResult = await parseLiveQueryServer.authCache.get("invalid"); expect(finalResult.error).not.toBeUndefined(); - expect(parseLiveQueryServer.authCache.get('invalid')).not.toBe(undefined); + expect(parseLiveQueryServer.authCache.get("invalid")).not.toBe(undefined); }); afterEach(function () { - jasmine.restoreLibrary('../lib/LiveQuery/ParseWebSocketServer', 'ParseWebSocketServer'); - jasmine.restoreLibrary('../lib/LiveQuery/Client', 'Client'); - jasmine.restoreLibrary('../lib/LiveQuery/Subscription', 'Subscription'); - jasmine.restoreLibrary('../lib/LiveQuery/QueryTools', 'queryHash'); - jasmine.restoreLibrary('../lib/LiveQuery/QueryTools', 'matchesQuery'); - jasmine.restoreLibrary('../lib/LiveQuery/ParsePubSub', 'ParsePubSub'); + jasmine.restoreLibrary( + "../lib/LiveQuery/ParseWebSocketServer", + "ParseWebSocketServer" + ); + jasmine.restoreLibrary("../lib/LiveQuery/Client", "Client"); + jasmine.restoreLibrary("../lib/LiveQuery/Subscription", "Subscription"); + jasmine.restoreLibrary("../lib/LiveQuery/QueryTools", "queryHash"); + jasmine.restoreLibrary("../lib/LiveQuery/QueryTools", "matchesQuery"); + jasmine.restoreLibrary("../lib/LiveQuery/ParsePubSub", "ParsePubSub"); }); // Helper functions to add mock client and subscription to a liveQueryServer function addMockClient(parseLiveQueryServer, clientId) { - const Client = require('../lib/LiveQuery/Client').Client; + const Client = require("../lib/LiveQuery/Client").Client; const client = new Client(clientId, {}); parseLiveQueryServer.clients.set(clientId, client); return client; @@ -1869,7 +2059,7 @@ describe('ParseLiveQueryServer', function () { ) { // If parseWebSocket is null, we use the default one if (!parseWebSocket) { - const EventEmitter = require('events'); + const EventEmitter = require("events"); parseWebSocket = new EventEmitter(); } parseWebSocket.clientId = clientId; @@ -1878,15 +2068,15 @@ describe('ParseLiveQueryServer', function () { query = { className: testClassName, where: { - key: 'value', + key: "value", }, - keys: ['test'], + keys: ["test"], }; } const request = { query: query, requestId: requestId, - sessionToken: 'sessionToken', + sessionToken: "sessionToken", }; await parseLiveQueryServer._handleSubscribe(parseWebSocket, request); @@ -1899,7 +2089,10 @@ describe('ParseLiveQueryServer', function () { }; subscription.className = query.className; subscription.hash = customQueryHashValue || queryHashValue; - if (subscription.clientRequestIds && subscription.clientRequestIds.has(clientId)) { + if ( + subscription.clientRequestIds && + subscription.clientRequestIds.has(clientId) + ) { subscription.clientRequestIds.get(clientId).push(requestId); } else { subscription.clientRequestIds = new Map([[clientId, [requestId]]]); @@ -1912,7 +2105,7 @@ describe('ParseLiveQueryServer', function () { function generateMockMessage(hasOriginalParseObject) { const parseObject = new Parse.Object(testClassName); parseObject._finishFetch({ - key: 'value', + key: "value", className: testClassName, }); const message = { @@ -1921,7 +2114,7 @@ describe('ParseLiveQueryServer', function () { if (hasOriginalParseObject) { const originalParseObject = new Parse.Object(testClassName); originalParseObject._finishFetch({ - key: 'originalValue', + key: "originalValue", className: testClassName, }); message.originalParseObject = originalParseObject; @@ -1930,10 +2123,10 @@ describe('ParseLiveQueryServer', function () { } }); -describe('LiveQueryController', () => { - it('properly passes the CLP to afterSave/afterDelete hook', function (done) { +describe("LiveQueryController", () => { + it("properly passes the CLP to afterSave/afterDelete hook", function (done) { function setPermissionsOnClass(className, permissions, doPut) { - const request = require('request'); + const request = require("request"); let op = request.post; if (doPut) { op = request.put; @@ -1941,10 +2134,10 @@ describe('LiveQueryController', () => { return new Promise((resolve, reject) => { op( { - url: Parse.serverURL + '/schemas/' + className, + url: Parse.serverURL + "/schemas/" + className, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': Parse.masterKey, + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Master-Key": Parse.masterKey, }, json: true, body: { @@ -1968,22 +2161,25 @@ describe('LiveQueryController', () => { let deleteSpy; reconfigureServer({ liveQuery: { - classNames: ['Yolo'], + classNames: ["Yolo"], }, }) .then(parseServer => { - saveSpy = spyOn(parseServer.config.liveQueryController, 'onAfterSave').and.callThrough(); + saveSpy = spyOn( + parseServer.config.liveQueryController, + "onAfterSave" + ).and.callThrough(); deleteSpy = spyOn( parseServer.config.liveQueryController, - 'onAfterDelete' + "onAfterDelete" ).and.callThrough(); - return setPermissionsOnClass('Yolo', { - create: { '*': true }, - delete: { '*': true }, + return setPermissionsOnClass("Yolo", { + create: { "*": true }, + delete: { "*": true }, }); }) .then(() => { - const obj = new Parse.Object('Yolo'); + const obj = new Parse.Object("Yolo"); return obj.save(); }) .then(obj => { @@ -1993,30 +2189,30 @@ describe('LiveQueryController', () => { expect(saveSpy).toHaveBeenCalled(); const saveArgs = saveSpy.calls.mostRecent().args; expect(saveArgs.length).toBe(4); - expect(saveArgs[0]).toBe('Yolo'); + expect(saveArgs[0]).toBe("Yolo"); expect(saveArgs[3]).toEqual({ get: {}, count: {}, addField: {}, - create: { '*': true }, + create: { "*": true }, find: {}, update: {}, - delete: { '*': true }, + delete: { "*": true }, protectedFields: {}, }); expect(deleteSpy).toHaveBeenCalled(); const deleteArgs = deleteSpy.calls.mostRecent().args; expect(deleteArgs.length).toBe(4); - expect(deleteArgs[0]).toBe('Yolo'); + expect(deleteArgs[0]).toBe("Yolo"); expect(deleteArgs[3]).toEqual({ get: {}, count: {}, addField: {}, - create: { '*': true }, + create: { "*": true }, find: {}, update: {}, - delete: { '*': true }, + delete: { "*": true }, protectedFields: {}, }); done(); @@ -2024,12 +2220,12 @@ describe('LiveQueryController', () => { .catch(done.fail); }); - it('should properly pack message request on afterSave', () => { + it("should properly pack message request on afterSave", () => { const controller = new LiveQueryController({ - classNames: ['Yolo'], + classNames: ["Yolo"], }); - const spy = spyOn(controller.liveQueryPublisher, 'onCloudCodeAfterSave'); - controller.onAfterSave('Yolo', { o: 1 }, { o: 2 }, { yolo: true }); + const spy = spyOn(controller.liveQueryPublisher, "onCloudCodeAfterSave"); + controller.onAfterSave("Yolo", { o: 1 }, { o: 2 }, { yolo: true }); expect(spy).toHaveBeenCalled(); const args = spy.calls.mostRecent().args; expect(args.length).toBe(1); @@ -2040,12 +2236,12 @@ describe('LiveQueryController', () => { }); }); - it('should properly pack message request on afterDelete', () => { + it("should properly pack message request on afterDelete", () => { const controller = new LiveQueryController({ - classNames: ['Yolo'], + classNames: ["Yolo"], }); - const spy = spyOn(controller.liveQueryPublisher, 'onCloudCodeAfterDelete'); - controller.onAfterDelete('Yolo', { o: 1 }, { o: 2 }, { yolo: true }); + const spy = spyOn(controller.liveQueryPublisher, "onCloudCodeAfterDelete"); + controller.onAfterDelete("Yolo", { o: 1 }, { o: 2 }, { yolo: true }); expect(spy).toHaveBeenCalled(); const args = spy.calls.mostRecent().args; expect(args.length).toBe(1); @@ -2056,9 +2252,9 @@ describe('LiveQueryController', () => { }); }); - it('should properly pack message request', () => { + it("should properly pack message request", () => { const controller = new LiveQueryController({ - classNames: ['Yolo'], + classNames: ["Yolo"], }); expect(controller._makePublisherRequest({})).toEqual({ object: {}, diff --git a/spec/ParseObject.spec.js b/spec/ParseObject.spec.js index 10558b209d..8a7d6c59e2 100644 --- a/spec/ParseObject.spec.js +++ b/spec/ParseObject.spec.js @@ -1,4 +1,4 @@ -'use strict'; +"use strict"; // This is a port of the test suite: // hungry/js/test/parse_object_test.js // @@ -12,55 +12,55 @@ // single-instance mode and we don't want these tests to run in // single-instance mode. -describe('Parse.Object testing', () => { - it('create', function (done) { - create({ test: 'test' }, function (model) { - ok(model.id, 'Should have an objectId set'); - equal(model.get('test'), 'test', 'Should have the right attribute'); +describe("Parse.Object testing", () => { + it("create", function (done) { + create({ test: "test" }, function (model) { + ok(model.id, "Should have an objectId set"); + equal(model.get("test"), "test", "Should have the right attribute"); done(); }); }); - it('update', function (done) { - create({ test: 'test' }, function (model) { + it("update", function (done) { + create({ test: "test" }, function (model) { const t2 = new TestObject({ objectId: model.id }); - t2.set('test', 'changed'); + t2.set("test", "changed"); t2.save().then(function (model) { - equal(model.get('test'), 'changed', 'Update should have succeeded'); + equal(model.get("test"), "changed", "Update should have succeeded"); done(); }); }); }); - it('save without null', function (done) { + it("save without null", function (done) { const object = new TestObject(); - object.set('favoritePony', 'Rainbow Dash'); + object.set("favoritePony", "Rainbow Dash"); object.save().then( function (objectAgain) { equal(objectAgain, object); done(); }, function (objectAgain, error) { - ok(null, 'Error ' + error.code + ': ' + error.message); + ok(null, "Error " + error.code + ": " + error.message); done(); } ); }); - it('save cycle', done => { - const a = new Parse.Object('TestObject'); - const b = new Parse.Object('TestObject'); - a.set('b', b); + it("save cycle", done => { + const a = new Parse.Object("TestObject"); + const b = new Parse.Object("TestObject"); + a.set("b", b); a.save() .then(function () { - b.set('a', a); + b.set("a", a); return b.save(); }) .then(function () { ok(a.id); ok(b.id); - strictEqual(a.get('b'), b); - strictEqual(b.get('a'), a); + strictEqual(a.get("b"), b); + strictEqual(b.get("a"), a); }) .then( function () { @@ -73,21 +73,21 @@ describe('Parse.Object testing', () => { ); }); - it('get', function (done) { - create({ test: 'test' }, function (model) { + it("get", function (done) { + create({ test: "test" }, function (model) { const t2 = new TestObject({ objectId: model.id }); t2.fetch().then(function (model2) { - equal(model2.get('test'), 'test', 'Update should have succeeded'); + equal(model2.get("test"), "test", "Update should have succeeded"); ok(model2.id); - equal(model2.id, model.id, 'Ids should match'); + equal(model2.id, model.id, "Ids should match"); done(); }); }); }); - it('delete', function (done) { + it("delete", function (done) { const t = new TestObject(); - t.set('test', 'test'); + t.set("test", "test"); t.save().then(function () { t.destroy().then(function () { const t2 = new TestObject({ objectId: t.id }); @@ -96,12 +96,12 @@ describe('Parse.Object testing', () => { }); }); - it('find', function (done) { + it("find", function (done) { const t = new TestObject(); - t.set('foo', 'bar'); + t.set("foo", "bar"); t.save().then(function () { const query = new Parse.Query(TestObject); - query.equalTo('foo', 'bar'); + query.equalTo("foo", "bar"); query.find().then(function (results) { equal(results.length, 1); done(); @@ -109,27 +109,27 @@ describe('Parse.Object testing', () => { }); }); - it('relational fields', function (done) { + it("relational fields", function (done) { const item = new Item(); - item.set('property', 'x'); + item.set("property", "x"); const container = new Container(); - container.set('item', item); + container.set("item", item); Parse.Object.saveAll([item, container]).then(function () { const query = new Parse.Query(Container); query.find().then(function (results) { equal(results.length, 1); const containerAgain = results[0]; - const itemAgain = containerAgain.get('item'); + const itemAgain = containerAgain.get("item"); itemAgain.fetch().then(function () { - equal(itemAgain.get('property'), 'x'); + equal(itemAgain.get("property"), "x"); done(); }); }); }); }); - it('save adds no data keys (other than createdAt and updatedAt)', function (done) { + it("save adds no data keys (other than createdAt and updatedAt)", function (done) { const object = new TestObject(); object.save().then(function () { const keys = Object.keys(object.attributes).sort(); @@ -138,35 +138,35 @@ describe('Parse.Object testing', () => { }); }); - it('recursive save', function (done) { + it("recursive save", function (done) { const item = new Item(); - item.set('property', 'x'); + item.set("property", "x"); const container = new Container(); - container.set('item', item); + container.set("item", item); container.save().then(function () { const query = new Parse.Query(Container); query.find().then(function (results) { equal(results.length, 1); const containerAgain = results[0]; - const itemAgain = containerAgain.get('item'); + const itemAgain = containerAgain.get("item"); itemAgain.fetch().then(function () { - equal(itemAgain.get('property'), 'x'); + equal(itemAgain.get("property"), "x"); done(); }); }); }); }); - it('fetch', function (done) { - const item = new Item({ foo: 'bar' }); + it("fetch", function (done) { + const item = new Item({ foo: "bar" }); item.save().then(function () { const itemAgain = new Item(); itemAgain.id = item.id; itemAgain.fetch().then(function () { - itemAgain.save({ foo: 'baz' }).then(function () { + itemAgain.save({ foo: "baz" }).then(function () { item.fetch().then(function () { - equal(item.get('foo'), itemAgain.get('foo')); + equal(item.get("foo"), itemAgain.get("foo")); done(); }); }); @@ -175,7 +175,7 @@ describe('Parse.Object testing', () => { }); it("createdAt doesn't change", function (done) { - const object = new TestObject({ foo: 'bar' }); + const object = new TestObject({ foo: "bar" }); object.save().then(function () { const objectAgain = new TestObject(); objectAgain.id = object.id; @@ -186,8 +186,8 @@ describe('Parse.Object testing', () => { }); }); - it('createdAt and updatedAt exposed', function (done) { - const object = new TestObject({ foo: 'bar' }); + it("createdAt and updatedAt exposed", function (done) { + const object = new TestObject({ foo: "bar" }); object.save().then(function () { notEqual(object.updatedAt, undefined); notEqual(object.createdAt, undefined); @@ -195,25 +195,27 @@ describe('Parse.Object testing', () => { }); }); - it('updatedAt gets updated', function (done) { - const object = new TestObject({ foo: 'bar' }); + it("updatedAt gets updated", function (done) { + const object = new TestObject({ foo: "bar" }); object.save().then(function () { - ok(object.updatedAt, 'initial save should cause updatedAt to exist'); + ok(object.updatedAt, "initial save should cause updatedAt to exist"); const firstUpdatedAt = object.updatedAt; - object.save({ foo: 'baz' }).then(function () { - ok(object.updatedAt, 'two saves should cause updatedAt to exist'); + object.save({ foo: "baz" }).then(function () { + ok(object.updatedAt, "two saves should cause updatedAt to exist"); notEqual(firstUpdatedAt, object.updatedAt); done(); }); }); }); - it('createdAt is reasonable', function (done) { + it("createdAt is reasonable", function (done) { const startTime = new Date(); - const object = new TestObject({ foo: 'bar' }); + const object = new TestObject({ foo: "bar" }); object.save().then(function () { const endTime = new Date(); - const startDiff = Math.abs(startTime.getTime() - object.createdAt.getTime()); + const startDiff = Math.abs( + startTime.getTime() - object.createdAt.getTime() + ); ok(startDiff < 5000); const endDiff = Math.abs(endTime.getTime() - object.createdAt.getTime()); @@ -223,34 +225,34 @@ describe('Parse.Object testing', () => { }); }); - it('can set null', function (done) { - const obj = new Parse.Object('TestObject'); - obj.set('foo', null); + it("can set null", function (done) { + const obj = new Parse.Object("TestObject"); + obj.set("foo", null); obj.save().then( function (obj) { - on_db('mongo', () => { - equal(obj.get('foo'), null); + on_db("mongo", () => { + equal(obj.get("foo"), null); }); - on_db('postgres', () => { - equal(obj.get('foo'), null); + on_db("postgres", () => { + equal(obj.get("foo"), null); }); done(); }, function () { - fail('should not fail'); + fail("should not fail"); done(); } ); }); - it('can set boolean', function (done) { - const obj = new Parse.Object('TestObject'); - obj.set('yes', true); - obj.set('no', false); + it("can set boolean", function (done) { + const obj = new Parse.Object("TestObject"); + obj.set("yes", true); + obj.set("no", false); obj.save().then( function (obj) { - equal(obj.get('yes'), true); - equal(obj.get('no'), false); + equal(obj.get("yes"), true); + equal(obj.get("no"), false); done(); }, function (obj, error) { @@ -260,9 +262,9 @@ describe('Parse.Object testing', () => { ); }); - it('cannot set invalid date', async function (done) { - const obj = new Parse.Object('TestObject'); - obj.set('when', new Date(Date.parse(null))); + it("cannot set invalid date", async function (done) { + const obj = new Parse.Object("TestObject"); + obj.set("when", new Date(Date.parse(null))); try { await obj.save(); } catch (e) { @@ -270,25 +272,25 @@ describe('Parse.Object testing', () => { done(); return; } - ok(false, 'Saving an invalid date should throw'); + ok(false, "Saving an invalid date should throw"); done(); }); - it('can set authData when not user class', async () => { - const obj = new Parse.Object('TestObject'); - obj.set('authData', 'random'); + it("can set authData when not user class", async () => { + const obj = new Parse.Object("TestObject"); + obj.set("authData", "random"); await obj.save(); - expect(obj.get('authData')).toBe('random'); - const query = new Parse.Query('TestObject'); + expect(obj.get("authData")).toBe("random"); + const query = new Parse.Query("TestObject"); const object = await query.get(obj.id, { useMasterKey: true }); - expect(object.get('authData')).toBe('random'); + expect(object.get("authData")).toBe("random"); }); - it('invalid class name', function (done) { - const item = new Parse.Object('Foo^bar'); + it("invalid class name", function (done) { + const item = new Parse.Object("Foo^bar"); item.save().then( function () { - ok(false, 'The name should have been invalid.'); + ok(false, "The name should have been invalid."); done(); }, function () { @@ -300,18 +302,28 @@ describe('Parse.Object testing', () => { ); }); - it('invalid key name', function (done) { - const item = new Parse.Object('Item'); - expect(() => item.set({ 'foo^bar': 'baz' })).toThrow(new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'Invalid key name: foo^bar')); - item.save({ 'foo^bar': 'baz' }).then(fail, () => done()); - }); - - it('invalid __type', function (done) { - const item = new Parse.Object('Item'); - const types = ['Pointer', 'File', 'Date', 'GeoPoint', 'Bytes', 'Polygon', 'Relation']; + it("invalid key name", function (done) { + const item = new Parse.Object("Item"); + expect(() => item.set({ "foo^bar": "baz" })).toThrow( + new Parse.Error(Parse.Error.INVALID_KEY_NAME, "Invalid key name: foo^bar") + ); + item.save({ "foo^bar": "baz" }).then(fail, () => done()); + }); + + it("invalid __type", function (done) { + const item = new Parse.Object("Item"); + const types = [ + "Pointer", + "File", + "Date", + "GeoPoint", + "Bytes", + "Polygon", + "Relation", + ]; const tests = types.map(type => { - const test = new Parse.Object('Item'); - test.set('foo', { + const test = new Parse.Object("Item"); + test.set("foo", { __type: type, }); return test; @@ -329,226 +341,229 @@ describe('Parse.Object testing', () => { item .save({ foo: { - __type: 'IvalidName', + __type: "IvalidName", }, }) .then(fail, () => next(0)); }); - it('simple field deletion', function (done) { - const simple = new Parse.Object('SimpleObject'); + it("simple field deletion", function (done) { + const simple = new Parse.Object("SimpleObject"); simple .save({ - foo: 'bar', + foo: "bar", }) .then( function (simple) { - simple.unset('foo'); - ok(!simple.has('foo'), 'foo should have been unset.'); - ok(simple.dirty('foo'), 'foo should be dirty.'); - ok(simple.dirty(), 'the whole object should be dirty.'); + simple.unset("foo"); + ok(!simple.has("foo"), "foo should have been unset."); + ok(simple.dirty("foo"), "foo should be dirty."); + ok(simple.dirty(), "the whole object should be dirty."); simple.save().then( function (simple) { - ok(!simple.has('foo'), 'foo should have been unset.'); - ok(!simple.dirty('foo'), 'the whole object was just saved.'); - ok(!simple.dirty(), 'the whole object was just saved.'); + ok(!simple.has("foo"), "foo should have been unset."); + ok(!simple.dirty("foo"), "the whole object was just saved."); + ok(!simple.dirty(), "the whole object was just saved."); - const query = new Parse.Query('SimpleObject'); + const query = new Parse.Query("SimpleObject"); query.get(simple.id).then( function (simpleAgain) { - ok(!simpleAgain.has('foo'), 'foo should have been removed.'); + ok(!simpleAgain.has("foo"), "foo should have been removed."); done(); }, function (simpleAgain, error) { - ok(false, 'Error ' + error.code + ': ' + error.message); + ok(false, "Error " + error.code + ": " + error.message); done(); } ); }, function (simple, error) { - ok(false, 'Error ' + error.code + ': ' + error.message); + ok(false, "Error " + error.code + ": " + error.message); done(); } ); }, function (simple, error) { - ok(false, 'Error ' + error.code + ': ' + error.message); + ok(false, "Error " + error.code + ": " + error.message); done(); } ); }); - it('field deletion before first save', function (done) { - const simple = new Parse.Object('SimpleObject'); - simple.set('foo', 'bar'); - simple.unset('foo'); + it("field deletion before first save", function (done) { + const simple = new Parse.Object("SimpleObject"); + simple.set("foo", "bar"); + simple.unset("foo"); - ok(!simple.has('foo'), 'foo should have been unset.'); - ok(simple.dirty('foo'), 'foo should be dirty.'); - ok(simple.dirty(), 'the whole object should be dirty.'); + ok(!simple.has("foo"), "foo should have been unset."); + ok(simple.dirty("foo"), "foo should be dirty."); + ok(simple.dirty(), "the whole object should be dirty."); simple.save().then( function (simple) { - ok(!simple.has('foo'), 'foo should have been unset.'); - ok(!simple.dirty('foo'), 'the whole object was just saved.'); - ok(!simple.dirty(), 'the whole object was just saved.'); + ok(!simple.has("foo"), "foo should have been unset."); + ok(!simple.dirty("foo"), "the whole object was just saved."); + ok(!simple.dirty(), "the whole object was just saved."); - const query = new Parse.Query('SimpleObject'); + const query = new Parse.Query("SimpleObject"); query.get(simple.id).then( function (simpleAgain) { - ok(!simpleAgain.has('foo'), 'foo should have been removed.'); + ok(!simpleAgain.has("foo"), "foo should have been removed."); done(); }, function (simpleAgain, error) { - ok(false, 'Error ' + error.code + ': ' + error.message); + ok(false, "Error " + error.code + ": " + error.message); done(); } ); }, function (simple, error) { - ok(false, 'Error ' + error.code + ': ' + error.message); + ok(false, "Error " + error.code + ": " + error.message); done(); } ); }); - it('relation deletion', function (done) { - const simple = new Parse.Object('SimpleObject'); - const child = new Parse.Object('Child'); + it("relation deletion", function (done) { + const simple = new Parse.Object("SimpleObject"); + const child = new Parse.Object("Child"); simple .save({ child: child, }) .then( function (simple) { - simple.unset('child'); - ok(!simple.has('child'), 'child should have been unset.'); - ok(simple.dirty('child'), 'child should be dirty.'); - ok(simple.dirty(), 'the whole object should be dirty.'); + simple.unset("child"); + ok(!simple.has("child"), "child should have been unset."); + ok(simple.dirty("child"), "child should be dirty."); + ok(simple.dirty(), "the whole object should be dirty."); simple.save().then( function (simple) { - ok(!simple.has('child'), 'child should have been unset.'); - ok(!simple.dirty('child'), 'the whole object was just saved.'); - ok(!simple.dirty(), 'the whole object was just saved.'); + ok(!simple.has("child"), "child should have been unset."); + ok(!simple.dirty("child"), "the whole object was just saved."); + ok(!simple.dirty(), "the whole object was just saved."); - const query = new Parse.Query('SimpleObject'); + const query = new Parse.Query("SimpleObject"); query.get(simple.id).then( function (simpleAgain) { - ok(!simpleAgain.has('child'), 'child should have been removed.'); + ok( + !simpleAgain.has("child"), + "child should have been removed." + ); done(); }, function (simpleAgain, error) { - ok(false, 'Error ' + error.code + ': ' + error.message); + ok(false, "Error " + error.code + ": " + error.message); done(); } ); }, function (simple, error) { - ok(false, 'Error ' + error.code + ': ' + error.message); + ok(false, "Error " + error.code + ": " + error.message); done(); } ); }, function (simple, error) { - ok(false, 'Error ' + error.code + ': ' + error.message); + ok(false, "Error " + error.code + ": " + error.message); done(); } ); }); - it('deleted keys get cleared', function (done) { - const simpleObject = new Parse.Object('SimpleObject'); - simpleObject.set('foo', 'bar'); - simpleObject.unset('foo'); + it("deleted keys get cleared", function (done) { + const simpleObject = new Parse.Object("SimpleObject"); + simpleObject.set("foo", "bar"); + simpleObject.unset("foo"); simpleObject.save().then(function (simpleObject) { - simpleObject.set('foo', 'baz'); + simpleObject.set("foo", "baz"); simpleObject.save().then(function (simpleObject) { - const query = new Parse.Query('SimpleObject'); + const query = new Parse.Query("SimpleObject"); query.get(simpleObject.id).then(function (simpleObjectAgain) { - equal(simpleObjectAgain.get('foo'), 'baz'); + equal(simpleObjectAgain.get("foo"), "baz"); done(); }, done.fail); }, done.fail); }, done.fail); }); - it('setting after deleting', function (done) { - const simpleObject = new Parse.Object('SimpleObject'); - simpleObject.set('foo', 'bar'); + it("setting after deleting", function (done) { + const simpleObject = new Parse.Object("SimpleObject"); + simpleObject.set("foo", "bar"); simpleObject.save().then( function (simpleObject) { - simpleObject.unset('foo'); - simpleObject.set('foo', 'baz'); + simpleObject.unset("foo"); + simpleObject.set("foo", "baz"); simpleObject.save().then( function (simpleObject) { - const query = new Parse.Query('SimpleObject'); + const query = new Parse.Query("SimpleObject"); query.get(simpleObject.id).then( function (simpleObjectAgain) { - equal(simpleObjectAgain.get('foo'), 'baz'); + equal(simpleObjectAgain.get("foo"), "baz"); done(); }, function (error) { - ok(false, 'Error ' + error.code + ': ' + error.message); + ok(false, "Error " + error.code + ": " + error.message); done(); } ); }, function (error) { - ok(false, 'Error ' + error.code + ': ' + error.message); + ok(false, "Error " + error.code + ": " + error.message); done(); } ); }, function (error) { - ok(false, 'Error ' + error.code + ': ' + error.message); + ok(false, "Error " + error.code + ": " + error.message); done(); } ); }); - it('increment', function (done) { - const simple = new Parse.Object('SimpleObject'); + it("increment", function (done) { + const simple = new Parse.Object("SimpleObject"); simple .save({ foo: 5, }) .then(function (simple) { - simple.increment('foo'); - equal(simple.get('foo'), 6); - ok(simple.dirty('foo'), 'foo should be dirty.'); - ok(simple.dirty(), 'the whole object should be dirty.'); + simple.increment("foo"); + equal(simple.get("foo"), 6); + ok(simple.dirty("foo"), "foo should be dirty."); + ok(simple.dirty(), "the whole object should be dirty."); simple.save().then(function (simple) { - equal(simple.get('foo'), 6); - ok(!simple.dirty('foo'), 'the whole object was just saved.'); - ok(!simple.dirty(), 'the whole object was just saved.'); + equal(simple.get("foo"), 6); + ok(!simple.dirty("foo"), "the whole object was just saved."); + ok(!simple.dirty(), "the whole object was just saved."); - const query = new Parse.Query('SimpleObject'); + const query = new Parse.Query("SimpleObject"); query.get(simple.id).then(function (simpleAgain) { - equal(simpleAgain.get('foo'), 6); + equal(simpleAgain.get("foo"), 6); done(); }); }); }); }); - it('addUnique', function (done) { - const x1 = new Parse.Object('X'); - x1.set('stuff', [1, 2]); + it("addUnique", function (done) { + const x1 = new Parse.Object("X"); + x1.set("stuff", [1, 2]); x1.save() .then(() => { const objectId = x1.id; - const x2 = new Parse.Object('X', { objectId: objectId }); - x2.addUnique('stuff', 2); - x2.addUnique('stuff', 4); - expect(x2.get('stuff')).toEqual([2, 4]); + const x2 = new Parse.Object("X", { objectId: objectId }); + x2.addUnique("stuff", 2); + x2.addUnique("stuff", 4); + expect(x2.get("stuff")).toEqual([2, 4]); return x2.save(); }) .then(() => { - const query = new Parse.Query('X'); + const query = new Parse.Query("X"); return query.get(x1.id); }) .then( x3 => { - const stuff = x3.get('stuff'); + const stuff = x3.get("stuff"); const expected = [1, 2, 4]; expect(stuff.length).toBe(expected.length); for (const i of stuff) { @@ -557,101 +572,117 @@ describe('Parse.Object testing', () => { done(); }, error => { - on_db('mongo', () => { + on_db("mongo", () => { jfail(error); }); - on_db('postgres', () => { - expect(error.message).toEqual('Postgres does not support AddUnique operator.'); + on_db("postgres", () => { + expect(error.message).toEqual( + "Postgres does not support AddUnique operator." + ); }); done(); } ); }); - it_only_db('mongo')('can increment array nested fields', async () => { + it_only_db("mongo")("can increment array nested fields", async () => { const obj = new TestObject(); - obj.set('items', [ { value: 'a', count: 5 }, { value: 'b', count: 1 } ]); + obj.set("items", [ + { value: "a", count: 5 }, + { value: "b", count: 1 }, + ]); await obj.save(); - obj.increment('items.0.count', 15); - obj.increment('items.1.count', 4); + obj.increment("items.0.count", 15); + obj.increment("items.1.count", 4); await obj.save(); - expect(obj.toJSON().items[0].value).toBe('a'); - expect(obj.toJSON().items[1].value).toBe('b'); + expect(obj.toJSON().items[0].value).toBe("a"); + expect(obj.toJSON().items[1].value).toBe("b"); expect(obj.toJSON().items[0].count).toBe(20); expect(obj.toJSON().items[1].count).toBe(5); const query = new Parse.Query(TestObject); const result = await query.get(obj.id); - expect(result.get('items')[0].value).toBe('a'); - expect(result.get('items')[1].value).toBe('b'); - expect(result.get('items')[0].count).toBe(20); - expect(result.get('items')[1].count).toBe(5); - expect(result.get('items')).toEqual(obj.get('items')); + expect(result.get("items")[0].value).toBe("a"); + expect(result.get("items")[1].value).toBe("b"); + expect(result.get("items")[0].count).toBe(20); + expect(result.get("items")[1].count).toBe(5); + expect(result.get("items")).toEqual(obj.get("items")); }); - it_only_db('mongo')('can increment array nested fields missing index', async () => { - const obj = new TestObject(); - obj.set('items', []); - await obj.save(); - obj.increment('items.1.count', 15); - await obj.save(); - expect(obj.toJSON().items[0]).toBe(null); - expect(obj.toJSON().items[1].count).toBe(15); - const query = new Parse.Query(TestObject); - const result = await query.get(obj.id); - expect(result.get('items')[0]).toBe(null); - expect(result.get('items')[1].count).toBe(15); - expect(result.get('items')).toEqual(obj.get('items')); - }); - - it_id('44097c6f-d0ca-4dc5-aa8a-3dd2d9ac645a')(it)('can query array nested fields', async () => { - const objects = []; - for (let i = 0; i < 10; i++) { + it_only_db("mongo")( + "can increment array nested fields missing index", + async () => { const obj = new TestObject(); - obj.set('items', [i, { value: i }]); - objects.push(obj); + obj.set("items", []); + await obj.save(); + obj.increment("items.1.count", 15); + await obj.save(); + expect(obj.toJSON().items[0]).toBe(null); + expect(obj.toJSON().items[1].count).toBe(15); + const query = new Parse.Query(TestObject); + const result = await query.get(obj.id); + expect(result.get("items")[0]).toBe(null); + expect(result.get("items")[1].count).toBe(15); + expect(result.get("items")).toEqual(obj.get("items")); } - await Parse.Object.saveAll(objects); - let query = new Parse.Query(TestObject); - query.greaterThan('items.1.value', 5); - let result = await query.find(); - expect(result.length).toBe(4); - - query = new Parse.Query(TestObject); - query.lessThan('items.0', 3); - result = await query.find(); - expect(result.length).toBe(3); - - query = new Parse.Query(TestObject); - query.equalTo('items.0', 5); - result = await query.find(); - expect(result.length).toBe(1); - - query = new Parse.Query(TestObject); - query.notEqualTo('items.0', 5); - result = await query.find(); - expect(result.length).toBe(9); - }); - - it('addUnique with object', function (done) { - const x1 = new Parse.Object('X'); - x1.set('stuff', [1, { hello: 'world' }, { foo: 'bar' }]); + ); + + it_id("44097c6f-d0ca-4dc5-aa8a-3dd2d9ac645a")(it)( + "can query array nested fields", + async () => { + const objects = []; + for (let i = 0; i < 10; i++) { + const obj = new TestObject(); + obj.set("items", [i, { value: i }]); + objects.push(obj); + } + await Parse.Object.saveAll(objects); + let query = new Parse.Query(TestObject); + query.greaterThan("items.1.value", 5); + let result = await query.find(); + expect(result.length).toBe(4); + + query = new Parse.Query(TestObject); + query.lessThan("items.0", 3); + result = await query.find(); + expect(result.length).toBe(3); + + query = new Parse.Query(TestObject); + query.equalTo("items.0", 5); + result = await query.find(); + expect(result.length).toBe(1); + + query = new Parse.Query(TestObject); + query.notEqualTo("items.0", 5); + result = await query.find(); + expect(result.length).toBe(9); + } + ); + + it("addUnique with object", function (done) { + const x1 = new Parse.Object("X"); + x1.set("stuff", [1, { hello: "world" }, { foo: "bar" }]); x1.save() .then(() => { const objectId = x1.id; - const x2 = new Parse.Object('X', { objectId: objectId }); - x2.addUnique('stuff', { hello: 'world' }); - x2.addUnique('stuff', { bar: 'baz' }); - expect(x2.get('stuff')).toEqual([{ hello: 'world' }, { bar: 'baz' }]); + const x2 = new Parse.Object("X", { objectId: objectId }); + x2.addUnique("stuff", { hello: "world" }); + x2.addUnique("stuff", { bar: "baz" }); + expect(x2.get("stuff")).toEqual([{ hello: "world" }, { bar: "baz" }]); return x2.save(); }) .then(() => { - const query = new Parse.Query('X'); + const query = new Parse.Query("X"); return query.get(x1.id); }) .then( x3 => { - const stuff = x3.get('stuff'); - const target = [1, { hello: 'world' }, { foo: 'bar' }, { bar: 'baz' }]; + const stuff = x3.get("stuff"); + const target = [ + 1, + { hello: "world" }, + { foo: "bar" }, + { bar: "baz" }, + ]; expect(stuff.length).toEqual(target.length); let found = 0; for (const thing in target) { @@ -671,24 +702,24 @@ describe('Parse.Object testing', () => { ); }); - it('removes with object', function (done) { - const x1 = new Parse.Object('X'); - x1.set('stuff', [1, { hello: 'world' }, { foo: 'bar' }]); + it("removes with object", function (done) { + const x1 = new Parse.Object("X"); + x1.set("stuff", [1, { hello: "world" }, { foo: "bar" }]); x1.save() .then(() => { const objectId = x1.id; - const x2 = new Parse.Object('X', { objectId: objectId }); - x2.remove('stuff', { hello: 'world' }); - expect(x2.get('stuff')).toEqual([]); + const x2 = new Parse.Object("X", { objectId: objectId }); + x2.remove("stuff", { hello: "world" }); + expect(x2.get("stuff")).toEqual([]); return x2.save(); }) .then(() => { - const query = new Parse.Query('X'); + const query = new Parse.Query("X"); return query.get(x1.id); }) .then( x3 => { - expect(x3.get('stuff')).toEqual([1, { foo: 'bar' }]); + expect(x3.get("stuff")).toEqual([1, { foo: "bar" }]); done(); }, error => { @@ -698,40 +729,40 @@ describe('Parse.Object testing', () => { ); }); - it('dirty attributes', function (done) { - const object = new Parse.Object('TestObject'); - object.set('cat', 'good'); - object.set('dog', 'bad'); + it("dirty attributes", function (done) { + const object = new Parse.Object("TestObject"); + object.set("cat", "good"); + object.set("dog", "bad"); object.save().then( function (object) { ok(!object.dirty()); - ok(!object.dirty('cat')); - ok(!object.dirty('dog')); + ok(!object.dirty("cat")); + ok(!object.dirty("dog")); - object.set('dog', 'okay'); + object.set("dog", "okay"); ok(object.dirty()); - ok(!object.dirty('cat')); - ok(object.dirty('dog')); + ok(!object.dirty("cat")); + ok(object.dirty("dog")); done(); }, function () { - ok(false, 'This should have saved.'); + ok(false, "This should have saved."); done(); } ); }); - it('dirty keys', function (done) { - const object = new Parse.Object('TestObject'); - object.set('gogo', 'good'); - object.set('sito', 'sexy'); + it("dirty keys", function (done) { + const object = new Parse.Object("TestObject"); + object.set("gogo", "good"); + object.set("sito", "sexy"); ok(object.dirty()); let dirtyKeys = object.dirtyKeys(); equal(dirtyKeys.length, 2); - ok(arrayContains(dirtyKeys, 'gogo')); - ok(arrayContains(dirtyKeys, 'sito')); + ok(arrayContains(dirtyKeys, "gogo")); + ok(arrayContains(dirtyKeys, "sito")); object .save() @@ -739,51 +770,51 @@ describe('Parse.Object testing', () => { ok(!obj.dirty()); dirtyKeys = obj.dirtyKeys(); equal(dirtyKeys.length, 0); - ok(!arrayContains(dirtyKeys, 'gogo')); - ok(!arrayContains(dirtyKeys, 'sito')); + ok(!arrayContains(dirtyKeys, "gogo")); + ok(!arrayContains(dirtyKeys, "sito")); // try removing keys - obj.unset('sito'); + obj.unset("sito"); ok(obj.dirty()); dirtyKeys = obj.dirtyKeys(); equal(dirtyKeys.length, 1); - ok(!arrayContains(dirtyKeys, 'gogo')); - ok(arrayContains(dirtyKeys, 'sito')); + ok(!arrayContains(dirtyKeys, "gogo")); + ok(arrayContains(dirtyKeys, "sito")); return obj.save(); }) .then(function (obj) { ok(!obj.dirty()); - equal(obj.get('gogo'), 'good'); - equal(obj.get('sito'), undefined); + equal(obj.get("gogo"), "good"); + equal(obj.get("sito"), undefined); dirtyKeys = obj.dirtyKeys(); equal(dirtyKeys.length, 0); - ok(!arrayContains(dirtyKeys, 'gogo')); - ok(!arrayContains(dirtyKeys, 'sito')); + ok(!arrayContains(dirtyKeys, "gogo")); + ok(!arrayContains(dirtyKeys, "sito")); done(); }); }); - it('acl attribute', function (done) { - Parse.User.signUp('bob', 'password').then(function (user) { - const TestObject = Parse.Object.extend('TestObject'); + it("acl attribute", function (done) { + Parse.User.signUp("bob", "password").then(function (user) { + const TestObject = Parse.Object.extend("TestObject"); const obj = new TestObject({ ACL: new Parse.ACL(user), // ACLs cause things like validation to run }); - ok(obj.get('ACL') instanceof Parse.ACL); + ok(obj.get("ACL") instanceof Parse.ACL); obj.save().then(function (obj) { - ok(obj.get('ACL') instanceof Parse.ACL); + ok(obj.get("ACL") instanceof Parse.ACL); const query = new Parse.Query(TestObject); query.get(obj.id).then(function (obj) { - ok(obj.get('ACL') instanceof Parse.ACL); + ok(obj.get("ACL") instanceof Parse.ACL); const query = new Parse.Query(TestObject); query.find().then(function (results) { obj = results[0]; - ok(obj.get('ACL') instanceof Parse.ACL); + ok(obj.get("ACL") instanceof Parse.ACL); done(); }); @@ -792,14 +823,14 @@ describe('Parse.Object testing', () => { }); }); - it('cannot save object with invalid field', async () => { - const invalidFields = ['className', 'length']; + it("cannot save object with invalid field", async () => { + const invalidFields = ["className", "length"]; const promises = invalidFields.map(async field => { const obj = new TestObject(); - obj.set(field, 'bar'); + obj.set(field, "bar"); try { await obj.save(); - fail('should not succeed'); + fail("should not succeed"); } catch (e) { expect(e.message).toBe(`Invalid field name: ${field}.`); } @@ -807,252 +838,252 @@ describe('Parse.Object testing', () => { await Promise.all(promises); }); - it('old attribute unset then unset', function (done) { - const TestObject = Parse.Object.extend('TestObject'); + it("old attribute unset then unset", function (done) { + const TestObject = Parse.Object.extend("TestObject"); const obj = new TestObject(); - obj.set('x', 3); + obj.set("x", 3); obj.save().then(function () { - obj.unset('x'); - obj.unset('x'); + obj.unset("x"); + obj.unset("x"); obj.save().then(function () { - equal(obj.has('x'), false); - equal(obj.get('x'), undefined); + equal(obj.has("x"), false); + equal(obj.get("x"), undefined); const query = new Parse.Query(TestObject); query.get(obj.id).then(function (objAgain) { - equal(objAgain.has('x'), false); - equal(objAgain.get('x'), undefined); + equal(objAgain.has("x"), false); + equal(objAgain.get("x"), undefined); done(); }); }); }); }); - it('new attribute unset then unset', function (done) { - const TestObject = Parse.Object.extend('TestObject'); + it("new attribute unset then unset", function (done) { + const TestObject = Parse.Object.extend("TestObject"); const obj = new TestObject(); - obj.set('x', 5); - obj.unset('x'); - obj.unset('x'); + obj.set("x", 5); + obj.unset("x"); + obj.unset("x"); obj.save().then(function () { - equal(obj.has('x'), false); - equal(obj.get('x'), undefined); + equal(obj.has("x"), false); + equal(obj.get("x"), undefined); const query = new Parse.Query(TestObject); query.get(obj.id).then(function (objAgain) { - equal(objAgain.has('x'), false); - equal(objAgain.get('x'), undefined); + equal(objAgain.has("x"), false); + equal(objAgain.get("x"), undefined); done(); }); }); }); - it('unknown attribute unset then unset', function (done) { - const TestObject = Parse.Object.extend('TestObject'); + it("unknown attribute unset then unset", function (done) { + const TestObject = Parse.Object.extend("TestObject"); const obj = new TestObject(); - obj.unset('x'); - obj.unset('x'); + obj.unset("x"); + obj.unset("x"); obj.save().then(function () { - equal(obj.has('x'), false); - equal(obj.get('x'), undefined); + equal(obj.has("x"), false); + equal(obj.get("x"), undefined); const query = new Parse.Query(TestObject); query.get(obj.id).then(function (objAgain) { - equal(objAgain.has('x'), false); - equal(objAgain.get('x'), undefined); + equal(objAgain.has("x"), false); + equal(objAgain.get("x"), undefined); done(); }); }); }); - it('old attribute unset then clear', function (done) { - const TestObject = Parse.Object.extend('TestObject'); + it("old attribute unset then clear", function (done) { + const TestObject = Parse.Object.extend("TestObject"); const obj = new TestObject(); - obj.set('x', 3); + obj.set("x", 3); obj.save().then(function () { - obj.unset('x'); + obj.unset("x"); obj.clear(); obj.save().then(function () { - equal(obj.has('x'), false); - equal(obj.get('x'), undefined); + equal(obj.has("x"), false); + equal(obj.get("x"), undefined); const query = new Parse.Query(TestObject); query.get(obj.id).then(function (objAgain) { - equal(objAgain.has('x'), false); - equal(objAgain.get('x'), undefined); + equal(objAgain.has("x"), false); + equal(objAgain.get("x"), undefined); done(); }); }); }); }); - it('new attribute unset then clear', function (done) { - const TestObject = Parse.Object.extend('TestObject'); + it("new attribute unset then clear", function (done) { + const TestObject = Parse.Object.extend("TestObject"); const obj = new TestObject(); - obj.set('x', 5); - obj.unset('x'); + obj.set("x", 5); + obj.unset("x"); obj.clear(); obj.save().then(function () { - equal(obj.has('x'), false); - equal(obj.get('x'), undefined); + equal(obj.has("x"), false); + equal(obj.get("x"), undefined); const query = new Parse.Query(TestObject); query.get(obj.id).then(function (objAgain) { - equal(objAgain.has('x'), false); - equal(objAgain.get('x'), undefined); + equal(objAgain.has("x"), false); + equal(objAgain.get("x"), undefined); done(); }); }); }); - it('unknown attribute unset then clear', function (done) { - const TestObject = Parse.Object.extend('TestObject'); + it("unknown attribute unset then clear", function (done) { + const TestObject = Parse.Object.extend("TestObject"); const obj = new TestObject(); - obj.unset('x'); + obj.unset("x"); obj.clear(); obj.save().then(function () { - equal(obj.has('x'), false); - equal(obj.get('x'), undefined); + equal(obj.has("x"), false); + equal(obj.get("x"), undefined); const query = new Parse.Query(TestObject); query.get(obj.id).then(function (objAgain) { - equal(objAgain.has('x'), false); - equal(objAgain.get('x'), undefined); + equal(objAgain.has("x"), false); + equal(objAgain.get("x"), undefined); done(); }); }); }); - it('old attribute clear then unset', function (done) { - const TestObject = Parse.Object.extend('TestObject'); + it("old attribute clear then unset", function (done) { + const TestObject = Parse.Object.extend("TestObject"); const obj = new TestObject(); - obj.set('x', 3); + obj.set("x", 3); obj.save().then(function () { obj.clear(); - obj.unset('x'); + obj.unset("x"); obj.save().then(function () { - equal(obj.has('x'), false); - equal(obj.get('x'), undefined); + equal(obj.has("x"), false); + equal(obj.get("x"), undefined); const query = new Parse.Query(TestObject); query.get(obj.id).then(function (objAgain) { - equal(objAgain.has('x'), false); - equal(objAgain.get('x'), undefined); + equal(objAgain.has("x"), false); + equal(objAgain.get("x"), undefined); done(); }); }); }); }); - it('new attribute clear then unset', function (done) { - const TestObject = Parse.Object.extend('TestObject'); + it("new attribute clear then unset", function (done) { + const TestObject = Parse.Object.extend("TestObject"); const obj = new TestObject(); - obj.set('x', 5); + obj.set("x", 5); obj.clear(); - obj.unset('x'); + obj.unset("x"); obj.save().then(function () { - equal(obj.has('x'), false); - equal(obj.get('x'), undefined); + equal(obj.has("x"), false); + equal(obj.get("x"), undefined); const query = new Parse.Query(TestObject); query.get(obj.id).then(function (objAgain) { - equal(objAgain.has('x'), false); - equal(objAgain.get('x'), undefined); + equal(objAgain.has("x"), false); + equal(objAgain.get("x"), undefined); done(); }); }); }); - it('unknown attribute clear then unset', function (done) { - const TestObject = Parse.Object.extend('TestObject'); + it("unknown attribute clear then unset", function (done) { + const TestObject = Parse.Object.extend("TestObject"); const obj = new TestObject(); obj.clear(); - obj.unset('x'); + obj.unset("x"); obj.save().then(function () { - equal(obj.has('x'), false); - equal(obj.get('x'), undefined); + equal(obj.has("x"), false); + equal(obj.get("x"), undefined); const query = new Parse.Query(TestObject); query.get(obj.id).then(function (objAgain) { - equal(objAgain.has('x'), false); - equal(objAgain.get('x'), undefined); + equal(objAgain.has("x"), false); + equal(objAgain.get("x"), undefined); done(); }); }); }); - it('old attribute clear then clear', function (done) { - const TestObject = Parse.Object.extend('TestObject'); + it("old attribute clear then clear", function (done) { + const TestObject = Parse.Object.extend("TestObject"); const obj = new TestObject(); - obj.set('x', 3); + obj.set("x", 3); obj.save().then(function () { obj.clear(); obj.clear(); obj.save().then(function () { - equal(obj.has('x'), false); - equal(obj.get('x'), undefined); + equal(obj.has("x"), false); + equal(obj.get("x"), undefined); const query = new Parse.Query(TestObject); query.get(obj.id).then(function (objAgain) { - equal(objAgain.has('x'), false); - equal(objAgain.get('x'), undefined); + equal(objAgain.has("x"), false); + equal(objAgain.get("x"), undefined); done(); }); }); }); }); - it('new attribute clear then clear', function (done) { - const TestObject = Parse.Object.extend('TestObject'); + it("new attribute clear then clear", function (done) { + const TestObject = Parse.Object.extend("TestObject"); const obj = new TestObject(); - obj.set('x', 5); + obj.set("x", 5); obj.clear(); obj.clear(); obj.save().then(function () { - equal(obj.has('x'), false); - equal(obj.get('x'), undefined); + equal(obj.has("x"), false); + equal(obj.get("x"), undefined); const query = new Parse.Query(TestObject); query.get(obj.id).then(function (objAgain) { - equal(objAgain.has('x'), false); - equal(objAgain.get('x'), undefined); + equal(objAgain.has("x"), false); + equal(objAgain.get("x"), undefined); done(); }); }); }); - it('unknown attribute clear then clear', function (done) { - const TestObject = Parse.Object.extend('TestObject'); + it("unknown attribute clear then clear", function (done) { + const TestObject = Parse.Object.extend("TestObject"); const obj = new TestObject(); obj.clear(); obj.clear(); obj.save().then(function () { - equal(obj.has('x'), false); - equal(obj.get('x'), undefined); + equal(obj.has("x"), false); + equal(obj.get("x"), undefined); const query = new Parse.Query(TestObject); query.get(obj.id).then(function (objAgain) { - equal(objAgain.has('x'), false); - equal(objAgain.get('x'), undefined); + equal(objAgain.has("x"), false); + equal(objAgain.get("x"), undefined); done(); }); }); }); - it('saving children in an array', function (done) { - const Parent = Parse.Object.extend('Parent'); - const Child = Parse.Object.extend('Child'); + it("saving children in an array", function (done) { + const Parent = Parse.Object.extend("Parent"); + const Child = Parse.Object.extend("Child"); const child1 = new Child(); const child2 = new Child(); const parent = new Parent(); - child1.set('name', 'jamie'); - child2.set('name', 'cersei'); - parent.set('children', [child1, child2]); + child1.set("name", "jamie"); + child2.set("name", "cersei"); + parent.set("children", [child1, child2]); parent.save().then(function () { const query = new Parse.Query(Child); - query.ascending('name'); + query.ascending("name"); query.find().then(function (results) { equal(results.length, 2); - equal(results[0].get('name'), 'cersei'); - equal(results[1].get('name'), 'jamie'); + equal(results[0].get("name"), "cersei"); + equal(results[1].get("name"), "jamie"); done(); }); }, done.fail); }); - it('two saves at the same time', function (done) { - const object = new Parse.Object('TestObject'); + it("two saves at the same time", function (done) { + const object = new Parse.Object("TestObject"); let firstSave = true; const success = function () { @@ -1061,17 +1092,17 @@ describe('Parse.Object testing', () => { return; } - const query = new Parse.Query('TestObject'); + const query = new Parse.Query("TestObject"); query.find().then(function (results) { equal(results.length, 1); - equal(results[0].get('cat'), 'meow'); - equal(results[0].get('dog'), 'bark'); + equal(results[0].get("cat"), "meow"); + equal(results[0].get("dog"), "bark"); done(); }); }; - object.save({ cat: 'meow' }).then(success, fail); - object.save({ dog: 'bark' }).then(success, fail); + object.save({ cat: "meow" }).then(success, fail); + object.save({ dog: "bark" }).then(success, fail); }); // The schema-checking parts of this are working. @@ -1079,27 +1110,27 @@ describe('Parse.Object testing', () => { // typed field and saved okay, since that appears to be borked in // the client. // If this fails, it's probably a schema issue. - it('many saves after a failure', function (done) { + it("many saves after a failure", function (done) { // Make a class with a number in the schema. - const o1 = new Parse.Object('TestObject'); - o1.set('number', 1); + const o1 = new Parse.Object("TestObject"); + o1.set("number", 1); let object = null; o1.save() .then(() => { - object = new Parse.Object('TestObject'); - object.set('number', 'two'); + object = new Parse.Object("TestObject"); + object.set("number", "two"); return object.save(); }) .then(fail, error => { expect(error.code).toEqual(Parse.Error.INCORRECT_TYPE); - object.set('other', 'foo'); + object.set("other", "foo"); return object.save(); }) .then(fail, error => { expect(error.code).toEqual(Parse.Error.INCORRECT_TYPE); - object.set('other', 'bar'); + object.set("other", "bar"); return object.save(); }) .then(fail, error => { @@ -1109,35 +1140,35 @@ describe('Parse.Object testing', () => { }); }); - it('is not dirty after save', function (done) { - const obj = new Parse.Object('TestObject'); + it("is not dirty after save", function (done) { + const obj = new Parse.Object("TestObject"); obj.save().then(function () { - obj.set({ content: 'x' }); + obj.set({ content: "x" }); obj.fetch().then(function () { - equal(false, obj.dirty('content')); + equal(false, obj.dirty("content")); done(); }); }); }); - it('add with an object', function (done) { - const child = new Parse.Object('Person'); - const parent = new Parse.Object('Person'); + it("add with an object", function (done) { + const child = new Parse.Object("Person"); + const parent = new Parse.Object("Person"); Promise.resolve() .then(function () { return child.save(); }) .then(function () { - parent.add('children', child); + parent.add("children", child); return parent.save(); }) .then(function () { - const query = new Parse.Query('Person'); + const query = new Parse.Query("Person"); return query.get(parent.id); }) .then(function (parentAgain) { - equal(parentAgain.get('children')[0].id, child.id); + equal(parentAgain.get("children")[0].id, child.id); }) .then( function () { @@ -1150,8 +1181,8 @@ describe('Parse.Object testing', () => { ); }); - it('toJSON saved object', function (done) { - create({ foo: 'bar' }, function (model) { + it("toJSON saved object", function (done) { + create({ foo: "bar" }, function (model) { const objJSON = model.toJSON(); ok(objJSON.foo, "expected json to contain key 'foo'"); ok(objJSON.objectId, "expected json to contain key 'objectId'"); @@ -1161,36 +1192,36 @@ describe('Parse.Object testing', () => { }); }); - it('remove object from array', function (done) { + it("remove object from array", function (done) { const obj = new TestObject(); obj.save().then(function () { const container = new TestObject(); - container.add('array', obj); - equal(container.get('array').length, 1); + container.add("array", obj); + equal(container.get("array").length, 1); container.save(null).then(function () { const objAgain = new TestObject(); objAgain.id = obj.id; - container.remove('array', objAgain); - equal(container.get('array').length, 0); + container.remove("array", objAgain); + equal(container.get("array").length, 0); done(); }); }); }); - it('async methods', function (done) { + it("async methods", function (done) { const obj = new TestObject(); - obj.set('time', 'adventure'); + obj.set("time", "adventure"); obj .save() .then(function (obj) { - ok(obj.id, 'objectId should not be null.'); + ok(obj.id, "objectId should not be null."); const objAgain = new TestObject(); objAgain.id = obj.id; return objAgain.fetch(); }) .then(function (objAgain) { - equal(objAgain.get('time'), 'adventure'); + equal(objAgain.get("time"), "adventure"); return objAgain.destroy(); }) .then(function () { @@ -1205,11 +1236,11 @@ describe('Parse.Object testing', () => { }); }); - it('fail validation with promise', function (done) { - const PickyEater = Parse.Object.extend('PickyEater', { + it("fail validation with promise", function (done) { + const PickyEater = Parse.Object.extend("PickyEater", { validate: function (attrs) { - if (attrs.meal === 'tomatoes') { - return 'Ew. Tomatoes are gross.'; + if (attrs.meal === "tomatoes") { + return "Ew. Tomatoes are gross."; } return Parse.Object.prototype.validate.apply(this, arguments); }, @@ -1218,24 +1249,24 @@ describe('Parse.Object testing', () => { const bryan = new PickyEater(); bryan .save({ - meal: 'burrito', + meal: "burrito", }) .then( function () { return bryan.save({ - meal: 'tomatoes', + meal: "tomatoes", }); }, function () { - ok(false, 'Save should have succeeded.'); + ok(false, "Save should have succeeded."); } ) .then( function () { - ok(false, 'Save should have failed.'); + ok(false, "Save should have failed."); }, function (error) { - equal(error, 'Ew. Tomatoes are gross.'); + equal(error, "Ew. Tomatoes are gross."); done(); } ); @@ -1246,17 +1277,17 @@ describe('Parse.Object testing', () => { const r = restController.request; restController.request = function () { return r.apply(this, arguments).then(function (result) { - result.aDate = { __type: 'Date', iso: '2014-06-24T06:06:06.452Z' }; + result.aDate = { __type: "Date", iso: "2014-06-24T06:06:06.452Z" }; return result; }); }; - const obj = new Parse.Object('Thing'); + const obj = new Parse.Object("Thing"); obj .save() .then(function () { - ok(!obj.dirty(), 'The object should not be dirty'); - ok(obj.get('aDate')); + ok(!obj.dirty(), "The object should not be dirty"); + ok(obj.get("aDate")); }) .then(function () { restController.request = r; @@ -1269,21 +1300,21 @@ describe('Parse.Object testing', () => { const r = restController.request; restController.request = function () { return r.apply(restController, arguments).then(function (result) { - result.aDate = { __type: 'Date', iso: '2014-06-24T06:06:06.452Z' }; + result.aDate = { __type: "Date", iso: "2014-06-24T06:06:06.452Z" }; return result; }); }; const now = new Date(); - const obj = new Parse.Object('Thing'); + const obj = new Parse.Object("Thing"); const promise = obj.save(); - obj.set('aDate', now); + obj.set("aDate", now); promise .then(function () { - ok(obj.dirty(), 'The object should be dirty'); - equal(now, obj.get('aDate')); + ok(obj.dirty(), "The object should be dirty"); + equal(now, obj.get("aDate")); }) .then(function () { restController.request = r; @@ -1291,11 +1322,11 @@ describe('Parse.Object testing', () => { }); }); - it('bytes work', function (done) { + it("bytes work", function (done) { Promise.resolve() .then(function () { const obj = new TestObject(); - obj.set('bytes', { __type: 'Bytes', base64: 'ZnJveW8=' }); + obj.set("bytes", { __type: "Bytes", base64: "ZnJveW8=" }); return obj.save(); }) .then(function (obj) { @@ -1304,8 +1335,8 @@ describe('Parse.Object testing', () => { }) .then( function (obj) { - equal(obj.get('bytes').__type, 'Bytes'); - equal(obj.get('bytes').base64, 'ZnJveW8='); + equal(obj.get("bytes").__type, "Bytes"); + equal(obj.get("bytes").base64, "ZnJveW8="); done(); }, function (error) { @@ -1315,37 +1346,37 @@ describe('Parse.Object testing', () => { ); }); - it('destroyAll no objects', function (done) { + it("destroyAll no objects", function (done) { Parse.Object.destroyAll([]) .then(function (success) { - ok(success, 'Should be able to destroy no objects'); + ok(success, "Should be able to destroy no objects"); done(); }) .catch(done.fail); }); - it('destroyAll new objects only', function (done) { + it("destroyAll new objects only", function (done) { const objects = [new TestObject(), new TestObject()]; Parse.Object.destroyAll(objects) .then(function (success) { - ok(success, 'Should be able to destroy only new objects'); + ok(success, "Should be able to destroy only new objects"); done(); }) .catch(done.fail); }); - it('fetchAll', function (done) { + it("fetchAll", function (done) { const numItems = 11; const container = new Container(); const items = []; for (let i = 0; i < numItems; i++) { const item = new Item(); - item.set('x', i); + item.set("x", i); items.push(item); } Parse.Object.saveAll(items) .then(function () { - container.set('items', items); + container.set("items", items); return container.save(); }) .then(function () { @@ -1353,16 +1384,16 @@ describe('Parse.Object testing', () => { return query.get(container.id); }) .then(function (containerAgain) { - const itemsAgain = containerAgain.get('items'); + const itemsAgain = containerAgain.get("items"); if (!itemsAgain || !itemsAgain.forEach) { - fail('no itemsAgain retrieved', itemsAgain); + fail("no itemsAgain retrieved", itemsAgain); done(); return; } - equal(itemsAgain.length, numItems, 'Should get the array back'); + equal(itemsAgain.length, numItems, "Should get the array back"); itemsAgain.forEach(function (item, i) { const newValue = i * 2; - item.set('x', newValue); + item.set("x", newValue); }); return Parse.Object.saveAll(itemsAgain); }) @@ -1370,27 +1401,31 @@ describe('Parse.Object testing', () => { return Parse.Object.fetchAll(items); }) .then(function (fetchedItemsAgain) { - equal(fetchedItemsAgain.length, numItems, 'Number of items fetched should not change'); + equal( + fetchedItemsAgain.length, + numItems, + "Number of items fetched should not change" + ); fetchedItemsAgain.forEach(function (item, i) { - equal(item.get('x'), i * 2); + equal(item.get("x"), i * 2); }); done(); }); }); - it('fetchAll no objects', function (done) { + it("fetchAll no objects", function (done) { Parse.Object.fetchAll([]) .then(function (success) { - ok(Array.isArray(success), 'Should be able to fetchAll no objects'); + ok(Array.isArray(success), "Should be able to fetchAll no objects"); done(); }) .catch(done.fail); }); - it('fetchAll updates dates', function (done) { + it("fetchAll updates dates", function (done) { let updatedObject; const object = new TestObject(); - object.set('x', 7); + object.set("x", 7); object .save() .then(function () { @@ -1399,7 +1434,7 @@ describe('Parse.Object testing', () => { }) .then(function (results) { updatedObject = results[0]; - updatedObject.set('x', 11); + updatedObject.set("x", 11); return updatedObject.save(); }) .then(function () { @@ -1412,18 +1447,18 @@ describe('Parse.Object testing', () => { }); }); - xit('fetchAll backbone-style callbacks', function (done) { + xit("fetchAll backbone-style callbacks", function (done) { const numItems = 11; const container = new Container(); const items = []; for (let i = 0; i < numItems; i++) { const item = new Item(); - item.set('x', i); + item.set("x", i); items.push(item); } Parse.Object.saveAll(items) .then(function () { - container.set('items', items); + container.set("items", items); return container.save(); }) .then(function () { @@ -1431,40 +1466,44 @@ describe('Parse.Object testing', () => { return query.get(container.id); }) .then(function (containerAgain) { - const itemsAgain = containerAgain.get('items'); + const itemsAgain = containerAgain.get("items"); if (!itemsAgain || !itemsAgain.forEach) { - fail('no itemsAgain retrieved', itemsAgain); + fail("no itemsAgain retrieved", itemsAgain); done(); return; } - equal(itemsAgain.length, numItems, 'Should get the array back'); + equal(itemsAgain.length, numItems, "Should get the array back"); itemsAgain.forEach(function (item, i) { const newValue = i * 2; - item.set('x', newValue); + item.set("x", newValue); }); return Parse.Object.saveAll(itemsAgain); }) .then(function () { return Parse.Object.fetchAll(items).then( function (fetchedItemsAgain) { - equal(fetchedItemsAgain.length, numItems, 'Number of items fetched should not change'); + equal( + fetchedItemsAgain.length, + numItems, + "Number of items fetched should not change" + ); fetchedItemsAgain.forEach(function (item, i) { - equal(item.get('x'), i * 2); + equal(item.get("x"), i * 2); }); done(); }, function () { - ok(false, 'Failed to fetchAll'); + ok(false, "Failed to fetchAll"); done(); } ); }); }); - it('fetchAll error on multiple classes', function (done) { + it("fetchAll error on multiple classes", function (done) { const container = new Container(); - container.set('item', new Item()); - container.set('subcontainer', new Container()); + container.set("item", new Item()); + container.set("subcontainer", new Container()); return container .save() .then(function () { @@ -1472,8 +1511,8 @@ describe('Parse.Object testing', () => { return query.get(container.id); }) .then(function (containerAgain) { - const subContainerAgain = containerAgain.get('subcontainer'); - const itemAgain = containerAgain.get('item'); + const subContainerAgain = containerAgain.get("subcontainer"); + const itemAgain = containerAgain.get("item"); const multiClassArray = [subContainerAgain, itemAgain]; return Parse.Object.fetchAll(multiClassArray).catch(e => { expect(e.code).toBe(Parse.Error.INVALID_CLASS_NAME); @@ -1482,7 +1521,7 @@ describe('Parse.Object testing', () => { }); }); - it('fetchAll error on unsaved object', async function (done) { + it("fetchAll error on unsaved object", async function (done) { const unsavedObjectArray = [new TestObject()]; await Parse.Object.fetchAll(unsavedObjectArray).catch(e => { expect(e.code).toBe(Parse.Error.MISSING_OBJECT_ID); @@ -1490,12 +1529,12 @@ describe('Parse.Object testing', () => { }); }); - it('fetchAll error on deleted object', function (done) { + it("fetchAll error on deleted object", function (done) { const numItems = 11; const items = []; for (let i = 0; i < numItems; i++) { const item = new Item(); - item.set('x', i); + item.set("x", i); items.push(item); } Parse.Object.saveAll(items) @@ -1518,12 +1557,12 @@ describe('Parse.Object testing', () => { // TODO: Verify that with Sessions, this test is wrong... A fetch on // user should not bring down a session token. - xit('fetchAll User attributes get merged', function (done) { + xit("fetchAll User attributes get merged", function (done) { let sameUser; let user = new Parse.User(); - user.set('username', 'asdf'); - user.set('password', 'zxcv'); - user.set('foo', 'bar'); + user.set("username", "asdf"); + user.set("password", "zxcv"); + user.set("foo", "bar"); user .signUp() .then(function () { @@ -1534,14 +1573,14 @@ describe('Parse.Object testing', () => { .then(function (userAgain) { user = userAgain; sameUser = new Parse.User(); - sameUser.set('username', 'asdf'); - sameUser.set('password', 'zxcv'); + sameUser.set("username", "asdf"); + sameUser.set("password", "zxcv"); return sameUser.logIn(); }) .then(function () { - ok(!user.getSessionToken(), 'user should not have a sessionToken'); - ok(sameUser.getSessionToken(), 'sameUser should have a sessionToken'); - sameUser.set('baz', 'qux'); + ok(!user.getSessionToken(), "user should not have a sessionToken"); + ok(sameUser.getSessionToken(), "sameUser should have a sessionToken"); + sameUser.set("baz", "qux"); return sameUser.save(); }) .then(function () { @@ -1556,18 +1595,18 @@ describe('Parse.Object testing', () => { }); }); - it('fetchAllIfNeeded', function (done) { + it("fetchAllIfNeeded", function (done) { const numItems = 11; const container = new Container(); const items = []; for (let i = 0; i < numItems; i++) { const item = new Item(); - item.set('x', i); + item.set("x", i); items.push(item); } Parse.Object.saveAll(items) .then(function () { - container.set('items', items); + container.set("items", items); return container.save(); }) .then(function () { @@ -1575,14 +1614,14 @@ describe('Parse.Object testing', () => { return query.get(container.id); }) .then(function (containerAgain) { - const itemsAgain = containerAgain.get('items'); + const itemsAgain = containerAgain.get("items"); if (!itemsAgain || !itemsAgain.forEach) { - fail('no itemsAgain retrieved', itemsAgain); + fail("no itemsAgain retrieved", itemsAgain); done(); return; } itemsAgain.forEach(function (item, i) { - item.set('x', i * 2); + item.set("x", i * 2); }); return Parse.Object.saveAll(itemsAgain); }) @@ -1590,26 +1629,30 @@ describe('Parse.Object testing', () => { return Parse.Object.fetchAllIfNeeded(items); }) .then(function (fetchedItems) { - equal(fetchedItems.length, numItems, 'Number of items should not change'); + equal( + fetchedItems.length, + numItems, + "Number of items should not change" + ); fetchedItems.forEach(function (item, i) { - equal(item.get('x'), i); + equal(item.get("x"), i); }); done(); }); }); - xit('fetchAllIfNeeded backbone-style callbacks', function (done) { + xit("fetchAllIfNeeded backbone-style callbacks", function (done) { const numItems = 11; const container = new Container(); const items = []; for (let i = 0; i < numItems; i++) { const item = new Item(); - item.set('x', i); + item.set("x", i); items.push(item); } Parse.Object.saveAll(items) .then(function () { - container.set('items', items); + container.set("items", items); return container.save(); }) .then(function () { @@ -1617,45 +1660,49 @@ describe('Parse.Object testing', () => { return query.get(container.id); }) .then(function (containerAgain) { - const itemsAgain = containerAgain.get('items'); + const itemsAgain = containerAgain.get("items"); if (!itemsAgain || !itemsAgain.forEach) { - fail('no itemsAgain retrieved', itemsAgain); + fail("no itemsAgain retrieved", itemsAgain); done(); return; } itemsAgain.forEach(function (item, i) { - item.set('x', i * 2); + item.set("x", i * 2); }); return Parse.Object.saveAll(itemsAgain); }) .then(function () { - const items = container.get('items'); + const items = container.get("items"); return Parse.Object.fetchAllIfNeeded(items).then( function (fetchedItems) { - equal(fetchedItems.length, numItems, 'Number of items should not change'); + equal( + fetchedItems.length, + numItems, + "Number of items should not change" + ); fetchedItems.forEach(function (item, j) { - equal(item.get('x'), j); + equal(item.get("x"), j); }); done(); }, function () { - ok(false, 'Failed to fetchAll'); + ok(false, "Failed to fetchAll"); done(); } ); }); }); - it('fetchAllIfNeeded no objects', function (done) { + it("fetchAllIfNeeded no objects", function (done) { Parse.Object.fetchAllIfNeeded([]) .then(function (success) { - ok(Array.isArray(success), 'Should be able to fetchAll no objects'); + ok(Array.isArray(success), "Should be able to fetchAll no objects"); done(); }) .catch(done.fail); }); - it('fetchAllIfNeeded unsaved object', async function (done) { + it("fetchAllIfNeeded unsaved object", async function (done) { const unsavedObjectArray = [new TestObject()]; await Parse.Object.fetchAllIfNeeded(unsavedObjectArray).catch(e => { expect(e.code).toBe(Parse.Error.MISSING_OBJECT_ID); @@ -1663,10 +1710,10 @@ describe('Parse.Object testing', () => { }); }); - it('fetchAllIfNeeded error on multiple classes', function (done) { + it("fetchAllIfNeeded error on multiple classes", function (done) { const container = new Container(); - container.set('item', new Item()); - container.set('subcontainer', new Container()); + container.set("item", new Item()); + container.set("subcontainer", new Container()); return container .save() .then(function () { @@ -1674,8 +1721,8 @@ describe('Parse.Object testing', () => { return query.get(container.id); }) .then(function (containerAgain) { - const subContainerAgain = containerAgain.get('subcontainer'); - const itemAgain = containerAgain.get('item'); + const subContainerAgain = containerAgain.get("subcontainer"); + const itemAgain = containerAgain.get("item"); const multiClassArray = [subContainerAgain, itemAgain]; return Parse.Object.fetchAllIfNeeded(multiClassArray).catch(e => { expect(e.code).toBe(Parse.Error.INVALID_CLASS_NAME); @@ -1684,58 +1731,74 @@ describe('Parse.Object testing', () => { }); }); - it('Objects with className User', function (done) { - equal(Parse.CoreManager.get('PERFORM_USER_REWRITE'), true); + it("Objects with className User", function (done) { + equal(Parse.CoreManager.get("PERFORM_USER_REWRITE"), true); const User1 = Parse.Object.extend({ - className: 'User', + className: "User", }); - equal(User1.className, '_User', 'className is rewritten by default'); + equal(User1.className, "_User", "className is rewritten by default"); Parse.User.allowCustomUserClass(true); - equal(Parse.CoreManager.get('PERFORM_USER_REWRITE'), false); + equal(Parse.CoreManager.get("PERFORM_USER_REWRITE"), false); const User2 = Parse.Object.extend({ - className: 'User', + className: "User", }); - equal(User2.className, 'User', 'className is not rewritten when allowCustomUserClass(true)'); + equal( + User2.className, + "User", + "className is not rewritten when allowCustomUserClass(true)" + ); // Set back to default so as not to break other tests. Parse.User.allowCustomUserClass(false); - equal(Parse.CoreManager.get('PERFORM_USER_REWRITE'), true, 'PERFORM_USER_REWRITE is reset'); + equal( + Parse.CoreManager.get("PERFORM_USER_REWRITE"), + true, + "PERFORM_USER_REWRITE is reset" + ); const user = new User2(); - user.set('name', 'Me'); + user.set("name", "Me"); user.save({ height: 181 }).then(function (user) { - equal(user.get('name'), 'Me'); - equal(user.get('height'), 181); + equal(user.get("name"), "Me"); + equal(user.get("height"), 181); const query = new Parse.Query(User2); query.get(user.id).then(function (user) { - equal(user.className, 'User'); - equal(user.get('name'), 'Me'); - equal(user.get('height'), 181); + equal(user.className, "User"); + equal(user.get("name"), "Me"); + equal(user.get("height"), 181); done(); }); }); }); - it('create without data', function (done) { - const t1 = new TestObject({ test: 'test' }); + it("create without data", function (done) { + const t1 = new TestObject({ test: "test" }); t1.save() .then(function (t1) { const t2 = TestObject.createWithoutData(t1.id); return t2.fetch(); }) .then(function (t2) { - equal(t2.get('test'), 'test', 'Fetch should have grabbed ' + "'test' property."); + equal( + t2.get("test"), + "test", + "Fetch should have grabbed " + "'test' property." + ); const t3 = TestObject.createWithoutData(t2.id); - t3.set('test', 'not test'); + t3.set("test", "not test"); return t3.fetch(); }) .then( function (t3) { - equal(t3.get('test'), 'test', "Fetch should have grabbed server 'test' property."); + equal( + t3.get("test"), + "test", + "Fetch should have grabbed server 'test' property." + ); done(); }, function (error) { @@ -1745,36 +1808,36 @@ describe('Parse.Object testing', () => { ); }); - it('remove from new field creates array key', done => { + it("remove from new field creates array key", done => { const obj = new TestObject(); - obj.remove('shouldBeArray', 'foo'); + obj.remove("shouldBeArray", "foo"); obj .save() .then(() => { - const query = new Parse.Query('TestObject'); + const query = new Parse.Query("TestObject"); return query.get(obj.id); }) .then(objAgain => { - const arr = objAgain.get('shouldBeArray'); - ok(Array.isArray(arr), 'Should have created array key'); - ok(!arr || arr.length === 0, 'Should have an empty array.'); + const arr = objAgain.get("shouldBeArray"); + ok(Array.isArray(arr), "Should have created array key"); + ok(!arr || arr.length === 0, "Should have an empty array."); done(); }); }); - it('increment with type conflict fails', done => { + it("increment with type conflict fails", done => { const obj = new TestObject(); - obj.set('astring', 'foo'); + obj.set("astring", "foo"); obj .save() .then(() => { const obj2 = new TestObject(); - obj2.increment('astring'); + obj2.increment("astring"); return obj2.save(); }) .then( () => { - fail('Should not have saved.'); + fail("Should not have saved."); done(); }, error => { @@ -1784,19 +1847,19 @@ describe('Parse.Object testing', () => { ); }); - it('increment with empty field solidifies type', done => { + it("increment with empty field solidifies type", done => { const obj = new TestObject(); - obj.increment('aninc'); + obj.increment("aninc"); obj .save() .then(() => { const obj2 = new TestObject(); - obj2.set('aninc', 'foo'); + obj2.set("aninc", "foo"); return obj2.save(); }) .then( () => { - fail('Should not have saved.'); + fail("Should not have saved."); done(); }, error => { @@ -1806,20 +1869,20 @@ describe('Parse.Object testing', () => { ); }); - it('increment update with type conflict fails', done => { + it("increment update with type conflict fails", done => { const obj = new TestObject(); - obj.set('someString', 'foo'); + obj.set("someString", "foo"); obj .save() .then(objAgain => { const obj2 = new TestObject(); obj2.id = objAgain.id; - obj2.increment('someString'); + obj2.increment("someString"); return obj2.save(); }) .then( () => { - fail('Should not have saved.'); + fail("Should not have saved."); done(); }, error => { @@ -1829,48 +1892,48 @@ describe('Parse.Object testing', () => { ); }); - it('dictionary fetched pointers do not lose data on fetch', done => { - const parent = new Parse.Object('Parent'); + it("dictionary fetched pointers do not lose data on fetch", done => { + const parent = new Parse.Object("Parent"); const dict = {}; for (let i = 0; i < 5; i++) { const proc = iter => { - const child = new Parse.Object('Child'); - child.set('name', 'testname' + i); + const child = new Parse.Object("Child"); + child.set("name", "testname" + i); dict[iter] = child; }; proc(i); } - parent.set('childDict', dict); + parent.set("childDict", dict); parent .save() .then(() => { return parent.fetch(); }) .then(parentAgain => { - const dictAgain = parentAgain.get('childDict'); + const dictAgain = parentAgain.get("childDict"); if (!dictAgain) { - fail('Should have been a dictionary.'); + fail("Should have been a dictionary."); return done(); } - expect(typeof dictAgain).toEqual('object'); - expect(typeof dictAgain['0']).toEqual('object'); - expect(typeof dictAgain['1']).toEqual('object'); - expect(typeof dictAgain['2']).toEqual('object'); - expect(typeof dictAgain['3']).toEqual('object'); - expect(typeof dictAgain['4']).toEqual('object'); + expect(typeof dictAgain).toEqual("object"); + expect(typeof dictAgain["0"]).toEqual("object"); + expect(typeof dictAgain["1"]).toEqual("object"); + expect(typeof dictAgain["2"]).toEqual("object"); + expect(typeof dictAgain["3"]).toEqual("object"); + expect(typeof dictAgain["4"]).toEqual("object"); done(); }); }); - it('should create nested keys with _', done => { - const object = new Parse.Object('AnObject'); - object.set('foo', { - _bar: '_', + it("should create nested keys with _", done => { + const object = new Parse.Object("AnObject"); + object.set("foo", { + _bar: "_", baz_bar: 1, __foo_bar: true, - _0: 'underscore_zero', + _0: "underscore_zero", _more: { - _nested: 'key', + _nested: "key", }, }); object @@ -1880,58 +1943,58 @@ describe('Parse.Object testing', () => { return res.fetch(); }) .then(res => { - const foo = res.get('foo'); - expect(foo['_bar']).toEqual('_'); - expect(foo['baz_bar']).toEqual(1); - expect(foo['__foo_bar']).toBe(true); - expect(foo['_0']).toEqual('underscore_zero'); - expect(foo['_more']['_nested']).toEqual('key'); + const foo = res.get("foo"); + expect(foo["_bar"]).toEqual("_"); + expect(foo["baz_bar"]).toEqual(1); + expect(foo["__foo_bar"]).toBe(true); + expect(foo["_0"]).toEqual("underscore_zero"); + expect(foo["_more"]["_nested"]).toEqual("key"); done(); }) .catch(err => { jfail(err); - fail('should not fail'); + fail("should not fail"); done(); }); }); - it('should have undefined includes when object is missing', done => { - const obj1 = new Parse.Object('AnObject'); - const obj2 = new Parse.Object('AnObject'); + it("should have undefined includes when object is missing", done => { + const obj1 = new Parse.Object("AnObject"); + const obj2 = new Parse.Object("AnObject"); Parse.Object.saveAll([obj1, obj2]) .then(() => { - obj1.set('obj', obj2); + obj1.set("obj", obj2); // Save the pointer, delete the pointee return obj1.save().then(() => { return obj2.destroy(); }); }) .then(() => { - const query = new Parse.Query('AnObject'); - query.include('obj'); + const query = new Parse.Query("AnObject"); + query.include("obj"); return query.find(); }) .then(res => { expect(res.length).toBe(1); if (res[0]) { - expect(res[0].get('obj')).toBe(undefined); + expect(res[0].get("obj")).toBe(undefined); } - const query = new Parse.Query('AnObject'); + const query = new Parse.Query("AnObject"); return query.find(); }) .then(res => { expect(res.length).toBe(1); if (res[0]) { - expect(res[0].get('obj')).not.toBe(undefined); - return res[0].get('obj').fetch(); + expect(res[0].get("obj")).not.toBe(undefined); + return res[0].get("obj").fetch(); } else { done(); } }) .then( () => { - fail('Should not fetch a deleted object'); + fail("Should not fetch a deleted object"); }, err => { expect(err.code).toBe(Parse.Error.OBJECT_NOT_FOUND); @@ -1940,27 +2003,27 @@ describe('Parse.Object testing', () => { ); }); - it('should have undefined includes when object is missing on deeper path', done => { - const obj1 = new Parse.Object('AnObject'); - const obj2 = new Parse.Object('AnObject'); - const obj3 = new Parse.Object('AnObject'); + it("should have undefined includes when object is missing on deeper path", done => { + const obj1 = new Parse.Object("AnObject"); + const obj2 = new Parse.Object("AnObject"); + const obj3 = new Parse.Object("AnObject"); Parse.Object.saveAll([obj1, obj2, obj3]) .then(() => { - obj1.set('obj', obj2); - obj2.set('obj', obj3); + obj1.set("obj", obj2); + obj2.set("obj", obj3); // Save the pointer, delete the pointee return Parse.Object.saveAll([obj1, obj2]).then(() => { return obj3.destroy(); }); }) .then(() => { - const query = new Parse.Query('AnObject'); - query.include('obj.obj'); + const query = new Parse.Query("AnObject"); + query.include("obj.obj"); return query.get(obj1.id); }) .then(res => { - expect(res.get('obj')).not.toBe(undefined); - expect(res.get('obj').get('obj')).toBe(undefined); + expect(res.get("obj")).not.toBe(undefined); + expect(res.get("obj").get("obj")).toBe(undefined); done(); }) .catch(err => { @@ -1969,12 +2032,12 @@ describe('Parse.Object testing', () => { }); }); - it('should handle includes on null arrays #2752', done => { - const obj1 = new Parse.Object('AnObject'); - const obj2 = new Parse.Object('AnotherObject'); - const obj3 = new Parse.Object('NestedObject'); + it("should handle includes on null arrays #2752", done => { + const obj1 = new Parse.Object("AnObject"); + const obj2 = new Parse.Object("AnotherObject"); + const obj3 = new Parse.Object("NestedObject"); obj3.set({ - foo: 'bar', + foo: "bar", }); obj2.set({ key: obj3, @@ -1982,22 +2045,22 @@ describe('Parse.Object testing', () => { Parse.Object.saveAll([obj1, obj2]) .then(() => { - obj1.set('objects', [null, null, obj2]); + obj1.set("objects", [null, null, obj2]); return obj1.save(); }) .then(() => { - const query = new Parse.Query('AnObject'); - query.include('objects.key'); + const query = new Parse.Query("AnObject"); + query.include("objects.key"); return query.find(); }) .then(res => { const obj = res[0]; - expect(obj.get('objects')).not.toBe(undefined); - const array = obj.get('objects'); + expect(obj.get("objects")).not.toBe(undefined); + const array = obj.get("objects"); expect(Array.isArray(array)).toBe(true); expect(array[0]).toBe(null); expect(array[1]).toBe(null); - expect(array[2].get('key').get('foo')).toEqual('bar'); + expect(array[2].get("key").get("foo")).toEqual("bar"); done(); }) .catch(err => { @@ -2006,9 +2069,9 @@ describe('Parse.Object testing', () => { }); }); - it('should handle select and include #2786', done => { - const score = new Parse.Object('GameScore'); - const player = new Parse.Object('Player'); + it("should handle select and include #2786", done => { + const score = new Parse.Object("GameScore"); + const player = new Parse.Object("Player"); score.set({ score: 1234, }); @@ -2016,23 +2079,23 @@ describe('Parse.Object testing', () => { score .save() .then(() => { - player.set('gameScore', score); - player.set('other', 'value'); + player.set("gameScore", score); + player.set("other", "value"); return player.save(); }) .then(() => { - const query = new Parse.Query('Player'); - query.include('gameScore'); - query.select('gameScore'); + const query = new Parse.Query("Player"); + query.include("gameScore"); + query.select("gameScore"); return query.find(); }) .then(res => { const obj = res[0]; - const gameScore = obj.get('gameScore'); - const other = obj.get('other'); + const gameScore = obj.get("gameScore"); + const other = obj.get("other"); expect(other).toBeUndefined(); expect(gameScore).not.toBeUndefined(); - expect(gameScore.get('score')).toBe(1234); + expect(gameScore.get("score")).toBe(1234); done(); }) .catch(err => { @@ -2041,9 +2104,9 @@ describe('Parse.Object testing', () => { }); }); - it('should include ACLs with select', done => { - const score = new Parse.Object('GameScore'); - const player = new Parse.Object('Player'); + it("should include ACLs with select", done => { + const score = new Parse.Object("GameScore"); + const player = new Parse.Object("Player"); score.set({ score: 1234, }); @@ -2054,24 +2117,24 @@ describe('Parse.Object testing', () => { score .save() .then(() => { - player.set('gameScore', score); - player.set('other', 'value'); + player.set("gameScore", score); + player.set("other", "value"); player.setACL(acl); return player.save(); }) .then(() => { - const query = new Parse.Query('Player'); - query.include('gameScore'); - query.select('gameScore'); + const query = new Parse.Query("Player"); + query.include("gameScore"); + query.select("gameScore"); return query.find(); }) .then(res => { const obj = res[0]; - const gameScore = obj.get('gameScore'); - const other = obj.get('other'); + const gameScore = obj.get("gameScore"); + const other = obj.get("other"); expect(other).toBeUndefined(); expect(gameScore).not.toBeUndefined(); - expect(gameScore.get('score')).toBe(1234); + expect(gameScore.get("score")).toBe(1234); expect(obj.getACL().getPublicReadAccess()).toBe(true); expect(obj.getACL().getPublicWriteAccess()).toBe(false); }) @@ -2079,88 +2142,88 @@ describe('Parse.Object testing', () => { .catch(done.fail); }); - it('Update object field should store exactly same sent object', async done => { + it("Update object field should store exactly same sent object", async done => { let object = new TestObject(); // Set initial data - object.set('jsonData', { a: 'b' }); + object.set("jsonData", { a: "b" }); object = await object.save(); - equal(object.get('jsonData'), { a: 'b' }); + equal(object.get("jsonData"), { a: "b" }); // Set empty JSON - object.set('jsonData', {}); + object.set("jsonData", {}); object = await object.save(); - equal(object.get('jsonData'), {}); + equal(object.get("jsonData"), {}); // Set new JSON data - object.unset('jsonData'); - object.set('jsonData', { c: 'd' }); + object.unset("jsonData"); + object.set("jsonData", { c: "d" }); object = await object.save(); - equal(object.get('jsonData'), { c: 'd' }); + equal(object.get("jsonData"), { c: "d" }); // Fetch object from server object = await object.fetch(); - equal(object.get('jsonData'), { c: 'd' }); + equal(object.get("jsonData"), { c: "d" }); done(); }); - it('isNew in cloud code', async () => { - Parse.Cloud.beforeSave('CloudCodeIsNew', req => { + it("isNew in cloud code", async () => { + Parse.Cloud.beforeSave("CloudCodeIsNew", req => { expect(req.object.isNew()).toBeTruthy(); expect(req.object.id).toBeUndefined(); }); - Parse.Cloud.afterSave('CloudCodeIsNew', req => { + Parse.Cloud.afterSave("CloudCodeIsNew", req => { expect(req.object.isNew()).toBeFalsy(); expect(req.object.id).toBeDefined(); }); - const object = new Parse.Object('CloudCodeIsNew'); + const object = new Parse.Object("CloudCodeIsNew"); await object.save(); }); - it('should not change the json field to array in afterSave', async () => { - Parse.Cloud.beforeSave('failingJSONTestCase', req => { - expect(req.object.get('jsonField')).toEqual({ '123': 'test' }); + it("should not change the json field to array in afterSave", async () => { + Parse.Cloud.beforeSave("failingJSONTestCase", req => { + expect(req.object.get("jsonField")).toEqual({ 123: "test" }); }); - Parse.Cloud.afterSave('failingJSONTestCase', req => { - expect(req.object.get('jsonField')).toEqual({ '123': 'test' }); + Parse.Cloud.afterSave("failingJSONTestCase", req => { + expect(req.object.get("jsonField")).toEqual({ 123: "test" }); }); - const object = new Parse.Object('failingJSONTestCase'); - object.set('jsonField', { '123': 'test' }); + const object = new Parse.Object("failingJSONTestCase"); + object.set("jsonField", { 123: "test" }); await object.save(); }); - it('returns correct field values', async () => { + it("returns correct field values", async () => { const values = [ - { field: 'string', value: 'string' }, - { field: 'number', value: 1 }, - { field: 'boolean', value: true }, - { field: 'array', value: [0, 1, 2] }, - { field: 'array', value: [1, 2, 3] }, - { field: 'array', value: [{ '0': 'a' }, 2, 3] }, - { field: 'object', value: { key: 'value' } }, - { field: 'object', value: { key1: 'value1', key2: 'value2' } }, - { field: 'object', value: { key1: 1, key2: 2 } }, - { field: 'object', value: { '1x1': 1 } }, - { field: 'object', value: { '1x1': 1, '2': 2 } }, - { field: 'object', value: { '0': 0 } }, - { field: 'object', value: { '1': 1 } }, - { field: 'object', value: { '0': { '0': 'a', '1': 'b' } } }, - { field: 'date', value: new Date() }, + { field: "string", value: "string" }, + { field: "number", value: 1 }, + { field: "boolean", value: true }, + { field: "array", value: [0, 1, 2] }, + { field: "array", value: [1, 2, 3] }, + { field: "array", value: [{ 0: "a" }, 2, 3] }, + { field: "object", value: { key: "value" } }, + { field: "object", value: { key1: "value1", key2: "value2" } }, + { field: "object", value: { key1: 1, key2: 2 } }, + { field: "object", value: { "1x1": 1 } }, + { field: "object", value: { "1x1": 1, 2: 2 } }, + { field: "object", value: { 0: 0 } }, + { field: "object", value: { 1: 1 } }, + { field: "object", value: { 0: { 0: "a", 1: "b" } } }, + { field: "date", value: new Date() }, { - field: 'file', + field: "file", value: Parse.File.fromJSON({ - __type: 'File', - name: 'name', - url: 'http://localhost:8378/1/files/test/name', + __type: "File", + name: "name", + url: "http://localhost:8378/1/files/test/name", }), }, - { field: 'geoPoint', value: new Parse.GeoPoint(40, -30) }, - { field: 'bytes', value: { __type: 'Bytes', base64: 'ZnJveW8=' } }, + { field: "geoPoint", value: new Parse.GeoPoint(40, -30) }, + { field: "bytes", value: { __type: "Bytes", base64: "ZnJveW8=" } }, ]; for (const value of values) { const object = new TestObject(); diff --git a/spec/ParsePolygon.spec.js b/spec/ParsePolygon.spec.js index b53846d4ba..a8bec05f1a 100644 --- a/spec/ParsePolygon.spec.js +++ b/spec/ParsePolygon.spec.js @@ -1,14 +1,14 @@ -const TestObject = Parse.Object.extend('TestObject'); -const request = require('../lib/request'); -const TestUtils = require('../lib/TestUtils'); +const TestObject = Parse.Object.extend("TestObject"); +const request = require("../lib/request"); +const TestUtils = require("../lib/TestUtils"); const defaultHeaders = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Rest-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": "test", + "X-Parse-Rest-API-Key": "rest", + "Content-Type": "application/json", }; -describe('Parse.Polygon testing', () => { - it('polygon save open path', done => { +describe("Parse.Polygon testing", () => { + it("polygon save open path", done => { const coords = [ [0, 0], [0, 1], @@ -23,7 +23,7 @@ describe('Parse.Polygon testing', () => { [0, 0], ]; const obj = new TestObject(); - obj.set('polygon', new Parse.Polygon(coords)); + obj.set("polygon", new Parse.Polygon(coords)); return obj .save() .then(() => { @@ -31,14 +31,14 @@ describe('Parse.Polygon testing', () => { return query.get(obj.id); }) .then(result => { - const polygon = result.get('polygon'); + const polygon = result.get("polygon"); equal(polygon instanceof Parse.Polygon, true); equal(polygon.coordinates, closed); done(); }, done.fail); }); - it('polygon save closed path', done => { + it("polygon save closed path", done => { const coords = [ [0, 0], [0, 1], @@ -47,7 +47,7 @@ describe('Parse.Polygon testing', () => { [0, 0], ]; const obj = new TestObject(); - obj.set('polygon', new Parse.Polygon(coords)); + obj.set("polygon", new Parse.Polygon(coords)); return obj .save() .then(() => { @@ -55,55 +55,58 @@ describe('Parse.Polygon testing', () => { return query.get(obj.id); }) .then(result => { - const polygon = result.get('polygon'); + const polygon = result.get("polygon"); equal(polygon instanceof Parse.Polygon, true); equal(polygon.coordinates, coords); done(); }, done.fail); }); - it_id('3019353b-d5b3-4e53-bcb1-716418328bdd')(it)('polygon equalTo (open/closed) path', done => { - const openPoints = [ - [0, 0], - [0, 1], - [1, 1], - [1, 0], - ]; - const closedPoints = [ - [0, 0], - [0, 1], - [1, 1], - [1, 0], - [0, 0], - ]; - const openPolygon = new Parse.Polygon(openPoints); - const closedPolygon = new Parse.Polygon(closedPoints); - const obj = new TestObject(); - obj.set('polygon', openPolygon); - return obj - .save() - .then(() => { - const query = new Parse.Query(TestObject); - query.equalTo('polygon', openPolygon); - return query.find(); - }) - .then(results => { - const polygon = results[0].get('polygon'); - equal(polygon instanceof Parse.Polygon, true); - equal(polygon.coordinates, closedPoints); - const query = new Parse.Query(TestObject); - query.equalTo('polygon', closedPolygon); - return query.find(); - }) - .then(results => { - const polygon = results[0].get('polygon'); - equal(polygon instanceof Parse.Polygon, true); - equal(polygon.coordinates, closedPoints); - done(); - }, done.fail); - }); + it_id("3019353b-d5b3-4e53-bcb1-716418328bdd")(it)( + "polygon equalTo (open/closed) path", + done => { + const openPoints = [ + [0, 0], + [0, 1], + [1, 1], + [1, 0], + ]; + const closedPoints = [ + [0, 0], + [0, 1], + [1, 1], + [1, 0], + [0, 0], + ]; + const openPolygon = new Parse.Polygon(openPoints); + const closedPolygon = new Parse.Polygon(closedPoints); + const obj = new TestObject(); + obj.set("polygon", openPolygon); + return obj + .save() + .then(() => { + const query = new Parse.Query(TestObject); + query.equalTo("polygon", openPolygon); + return query.find(); + }) + .then(results => { + const polygon = results[0].get("polygon"); + equal(polygon instanceof Parse.Polygon, true); + equal(polygon.coordinates, closedPoints); + const query = new Parse.Query(TestObject); + query.equalTo("polygon", closedPolygon); + return query.find(); + }) + .then(results => { + const polygon = results[0].get("polygon"); + equal(polygon instanceof Parse.Polygon, true); + equal(polygon.coordinates, closedPoints); + done(); + }, done.fail); + } + ); - it('polygon update', done => { + it("polygon update", done => { const oldCoords = [ [0, 0], [0, 1], @@ -119,11 +122,11 @@ describe('Parse.Polygon testing', () => { ]; const newPolygon = new Parse.Polygon(newCoords); const obj = new TestObject(); - obj.set('polygon', oldPolygon); + obj.set("polygon", oldPolygon); return obj .save() .then(() => { - obj.set('polygon', newPolygon); + obj.set("polygon", newPolygon); return obj.save(); }) .then(() => { @@ -131,7 +134,7 @@ describe('Parse.Polygon testing', () => { return query.get(obj.id); }) .then(result => { - const polygon = result.get('polygon'); + const polygon = result.get("polygon"); newCoords.push(newCoords[0]); equal(polygon instanceof Parse.Polygon, true); equal(polygon.coordinates, newCoords); @@ -139,16 +142,16 @@ describe('Parse.Polygon testing', () => { }, done.fail); }); - it('polygon invalid value', done => { + it("polygon invalid value", done => { const coords = [ - ['foo', 'bar'], + ["foo", "bar"], [0, 1], [1, 0], [1, 1], [0, 0], ]; const obj = new TestObject(); - obj.set('polygon', { __type: 'Polygon', coordinates: coords }); + obj.set("polygon", { __type: "Polygon", coordinates: coords }); return obj .save() .then(() => { @@ -158,26 +161,26 @@ describe('Parse.Polygon testing', () => { .then(done.fail, () => done()); }); - it('polygon three points minimum', done => { + it("polygon three points minimum", done => { const coords = [[0, 0]]; const obj = new TestObject(); // use raw so we test the server validates properly - obj.set('polygon', { __type: 'Polygon', coordinates: coords }); + obj.set("polygon", { __type: "Polygon", coordinates: coords }); obj.save().then(done.fail, () => done()); }); - it('polygon three different points minimum', done => { + it("polygon three different points minimum", done => { const coords = [ [0, 0], [0, 1], [0, 0], ]; const obj = new TestObject(); - obj.set('polygon', new Parse.Polygon(coords)); + obj.set("polygon", new Parse.Polygon(coords)); obj.save().then(done.fail, () => done()); }); - it('polygon counterclockwise', done => { + it("polygon counterclockwise", done => { const coords = [ [1, 1], [0, 1], @@ -192,7 +195,7 @@ describe('Parse.Polygon testing', () => { [1, 1], ]; const obj = new TestObject(); - obj.set('polygon', new Parse.Polygon(coords)); + obj.set("polygon", new Parse.Polygon(coords)); obj .save() .then(() => { @@ -200,19 +203,19 @@ describe('Parse.Polygon testing', () => { return query.get(obj.id); }) .then(result => { - const polygon = result.get('polygon'); + const polygon = result.get("polygon"); equal(polygon instanceof Parse.Polygon, true); equal(polygon.coordinates, closed); done(); }, done.fail); }); - describe('with location', () => { - if (process.env.PARSE_SERVER_TEST_DB !== 'postgres') { + describe("with location", () => { + if (process.env.PARSE_SERVER_TEST_DB !== "postgres") { beforeEach(async () => await TestUtils.destroyAllDataPermanently()); } - it('polygonContain query', done => { + it("polygonContain query", done => { const points1 = [ [0, 0], [0, 1], @@ -243,18 +246,18 @@ describe('Parse.Polygon testing', () => { const where = { boundary: { $geoIntersects: { - $point: { __type: 'GeoPoint', latitude: 0.5, longitude: 0.5 }, + $point: { __type: "GeoPoint", latitude: 0.5, longitude: 0.5 }, }, }, }; return request({ - method: 'POST', - url: Parse.serverURL + '/classes/TestObject', - body: { where, _method: 'GET' }, + method: "POST", + url: Parse.serverURL + "/classes/TestObject", + body: { where, _method: "GET" }, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Javascript-Key": Parse.javaScriptKey, + "Content-Type": "application/json", }, }); }) @@ -264,7 +267,7 @@ describe('Parse.Polygon testing', () => { }, done.fail); }); - it('polygonContain query no reverse input (Regression test for #4608)', done => { + it("polygonContain query no reverse input (Regression test for #4608)", done => { const points1 = [ [0.25, 0], [0.25, 1.25], @@ -295,18 +298,18 @@ describe('Parse.Polygon testing', () => { const where = { boundary: { $geoIntersects: { - $point: { __type: 'GeoPoint', latitude: 0.5, longitude: 1.0 }, + $point: { __type: "GeoPoint", latitude: 0.5, longitude: 1.0 }, }, }, }; return request({ - method: 'POST', - url: Parse.serverURL + '/classes/TestObject', - body: { where, _method: 'GET' }, + method: "POST", + url: Parse.serverURL + "/classes/TestObject", + body: { where, _method: "GET" }, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Javascript-Key": Parse.javaScriptKey, + "Content-Type": "application/json", }, }); }) @@ -316,7 +319,7 @@ describe('Parse.Polygon testing', () => { }, done.fail); }); - it('polygonContain query real data (Regression test for #4608)', done => { + it("polygonContain query real data (Regression test for #4608)", done => { const detroit = [ [42.631655189280224, -83.78406753121705], [42.633047793854814, -83.75333640366955], @@ -333,7 +336,7 @@ describe('Parse.Polygon testing', () => { boundary: { $geoIntersects: { $point: { - __type: 'GeoPoint', + __type: "GeoPoint", latitude: 42.624599, longitude: -83.770162, }, @@ -341,13 +344,13 @@ describe('Parse.Polygon testing', () => { }, }; return request({ - method: 'POST', - url: Parse.serverURL + '/classes/TestObject', - body: { where, _method: 'GET' }, + method: "POST", + url: Parse.serverURL + "/classes/TestObject", + body: { where, _method: "GET" }, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Javascript-Key": Parse.javaScriptKey, + "Content-Type": "application/json", }, }); }) @@ -357,7 +360,7 @@ describe('Parse.Polygon testing', () => { }, done.fail); }); - it('polygonContain invalid input', done => { + it("polygonContain invalid input", done => { const points = [ [0, 0], [0, 1], @@ -372,24 +375,24 @@ describe('Parse.Polygon testing', () => { const where = { boundary: { $geoIntersects: { - $point: { __type: 'GeoPoint', latitude: 181, longitude: 181 }, + $point: { __type: "GeoPoint", latitude: 181, longitude: 181 }, }, }, }; return request({ - method: 'POST', - url: Parse.serverURL + '/classes/TestObject', - body: { where, _method: 'GET' }, + method: "POST", + url: Parse.serverURL + "/classes/TestObject", + body: { where, _method: "GET" }, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Javascript-Key": Parse.javaScriptKey, }, }); }) .then(done.fail, () => done()); }); - it('polygonContain invalid geoPoint', done => { + it("polygonContain invalid geoPoint", done => { const points = [ [0, 0], [0, 1], @@ -409,12 +412,12 @@ describe('Parse.Polygon testing', () => { }, }; return request({ - method: 'POST', - url: Parse.serverURL + '/classes/TestObject', - body: { where, _method: 'GET' }, + method: "POST", + url: Parse.serverURL + "/classes/TestObject", + body: { where, _method: "GET" }, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Javascript-Key": Parse.javaScriptKey, }, }); }) @@ -423,17 +426,17 @@ describe('Parse.Polygon testing', () => { }); }); -describe_only_db('mongo')('Parse.Polygon testing', () => { - const Config = require('../lib/Config'); +describe_only_db("mongo")("Parse.Polygon testing", () => { + const Config = require("../lib/Config"); let config; beforeEach(async () => { - if (process.env.PARSE_SERVER_TEST_DB !== 'postgres') { + if (process.env.PARSE_SERVER_TEST_DB !== "postgres") { await TestUtils.destroyAllDataPermanently(); } - config = Config.get('test'); + config = Config.get("test"); config.schemaCache.clear(); }); - it('support 2d and 2dsphere', done => { + it("support 2d and 2dsphere", done => { const coords = [ [0, 0], [0, 1], @@ -442,29 +445,29 @@ describe_only_db('mongo')('Parse.Polygon testing', () => { [0, 0], ]; // testings against REST API, use raw formats - const polygon = { __type: 'Polygon', coordinates: coords }; - const location = { __type: 'GeoPoint', latitude: 10, longitude: 10 }; + const polygon = { __type: "Polygon", coordinates: coords }; + const location = { __type: "GeoPoint", latitude: 10, longitude: 10 }; const databaseAdapter = config.database.adapter; return reconfigureServer({ - appId: 'test', - restAPIKey: 'rest', - publicServerURL: 'http://localhost:8378/1', + appId: "test", + restAPIKey: "rest", + publicServerURL: "http://localhost:8378/1", databaseAdapter, }) .then(() => { - return databaseAdapter.createIndex('TestObject', { location: '2d' }); + return databaseAdapter.createIndex("TestObject", { location: "2d" }); }) .then(() => { - return databaseAdapter.createIndex('TestObject', { - polygon: '2dsphere', + return databaseAdapter.createIndex("TestObject", { + polygon: "2dsphere", }); }) .then(() => { return request({ - method: 'POST', - url: 'http://localhost:8378/1/classes/TestObject', + method: "POST", + url: "http://localhost:8378/1/classes/TestObject", body: { - _method: 'POST', + _method: "POST", location, polygon, polygon2: polygon, @@ -474,9 +477,9 @@ describe_only_db('mongo')('Parse.Polygon testing', () => { }) .then(resp => { return request({ - method: 'POST', + method: "POST", url: `http://localhost:8378/1/classes/TestObject/${resp.data.objectId}`, - body: { _method: 'GET' }, + body: { _method: "GET" }, headers: defaultHeaders, }); }) @@ -484,21 +487,21 @@ describe_only_db('mongo')('Parse.Polygon testing', () => { equal(resp.data.location, location); equal(resp.data.polygon, polygon); equal(resp.data.polygon2, polygon); - return databaseAdapter.getIndexes('TestObject'); + return databaseAdapter.getIndexes("TestObject"); }) .then(indexes => { equal(indexes.length, 4); equal(indexes[0].key, { _id: 1 }); - equal(indexes[1].key, { location: '2d' }); - equal(indexes[2].key, { polygon: '2dsphere' }); - equal(indexes[3].key, { polygon2: '2dsphere' }); + equal(indexes[1].key, { location: "2d" }); + equal(indexes[2].key, { polygon: "2dsphere" }); + equal(indexes[3].key, { polygon2: "2dsphere" }); done(); }, done.fail); }); - it('polygon coordinates reverse input', done => { - const Config = require('../lib/Config'); - const config = Config.get('test'); + it("polygon coordinates reverse input", done => { + const Config = require("../lib/Config"); + const config = Config.get("test"); // When stored the first point should be the last point const input = [ @@ -517,11 +520,11 @@ describe_only_db('mongo')('Parse.Polygon testing', () => { ], ]; const obj = new TestObject(); - obj.set('polygon', new Parse.Polygon(input)); + obj.set("polygon", new Parse.Polygon(input)); obj .save() .then(() => { - return config.database.adapter._rawFind('TestObject', { _id: obj.id }); + return config.database.adapter._rawFind("TestObject", { _id: obj.id }); }) .then(results => { expect(results.length).toBe(1); @@ -530,7 +533,7 @@ describe_only_db('mongo')('Parse.Polygon testing', () => { }); }); - it('polygon loop is not valid', done => { + it("polygon loop is not valid", done => { const coords = [ [0, 0], [0, 1], @@ -538,7 +541,7 @@ describe_only_db('mongo')('Parse.Polygon testing', () => { [1, 1], ]; const obj = new TestObject(); - obj.set('polygon', new Parse.Polygon(coords)); + obj.set("polygon", new Parse.Polygon(coords)); obj.save().then(done.fail, () => done()); }); }); diff --git a/spec/ParsePubSub.spec.js b/spec/ParsePubSub.spec.js index 063c35728d..148ce226c3 100644 --- a/spec/ParsePubSub.spec.js +++ b/spec/ParsePubSub.spec.js @@ -1,82 +1,90 @@ -const ParsePubSub = require('../lib/LiveQuery/ParsePubSub').ParsePubSub; +const ParsePubSub = require("../lib/LiveQuery/ParsePubSub").ParsePubSub; -describe('ParsePubSub', function () { +describe("ParsePubSub", function () { beforeEach(function (done) { // Mock RedisPubSub const mockRedisPubSub = { - createPublisher: jasmine.createSpy('createPublisherRedis'), - createSubscriber: jasmine.createSpy('createSubscriberRedis'), + createPublisher: jasmine.createSpy("createPublisherRedis"), + createSubscriber: jasmine.createSpy("createSubscriberRedis"), }; - jasmine.mockLibrary('../lib/Adapters/PubSub/RedisPubSub', 'RedisPubSub', mockRedisPubSub); + jasmine.mockLibrary( + "../lib/Adapters/PubSub/RedisPubSub", + "RedisPubSub", + mockRedisPubSub + ); // Mock EventEmitterPubSub const mockEventEmitterPubSub = { - createPublisher: jasmine.createSpy('createPublisherEventEmitter'), - createSubscriber: jasmine.createSpy('createSubscriberEventEmitter'), + createPublisher: jasmine.createSpy("createPublisherEventEmitter"), + createSubscriber: jasmine.createSpy("createSubscriberEventEmitter"), }; jasmine.mockLibrary( - '../lib/Adapters/PubSub/EventEmitterPubSub', - 'EventEmitterPubSub', + "../lib/Adapters/PubSub/EventEmitterPubSub", + "EventEmitterPubSub", mockEventEmitterPubSub ); done(); }); - it('can create redis publisher', function () { + it("can create redis publisher", function () { ParsePubSub.createPublisher({ - redisURL: 'redisURL', + redisURL: "redisURL", redisOptions: { socket_keepalive: true }, }); - const RedisPubSub = require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; + const RedisPubSub = + require("../lib/Adapters/PubSub/RedisPubSub").RedisPubSub; const EventEmitterPubSub = - require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub; + require("../lib/Adapters/PubSub/EventEmitterPubSub").EventEmitterPubSub; expect(RedisPubSub.createPublisher).toHaveBeenCalledWith({ - redisURL: 'redisURL', + redisURL: "redisURL", redisOptions: { socket_keepalive: true }, }); expect(EventEmitterPubSub.createPublisher).not.toHaveBeenCalled(); }); - it('can create event emitter publisher', function () { + it("can create event emitter publisher", function () { ParsePubSub.createPublisher({}); - const RedisPubSub = require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; + const RedisPubSub = + require("../lib/Adapters/PubSub/RedisPubSub").RedisPubSub; const EventEmitterPubSub = - require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub; + require("../lib/Adapters/PubSub/EventEmitterPubSub").EventEmitterPubSub; expect(RedisPubSub.createPublisher).not.toHaveBeenCalled(); expect(EventEmitterPubSub.createPublisher).toHaveBeenCalled(); }); - it('can create redis subscriber', function () { + it("can create redis subscriber", function () { ParsePubSub.createSubscriber({ - redisURL: 'redisURL', + redisURL: "redisURL", redisOptions: { socket_keepalive: true }, }); - const RedisPubSub = require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; + const RedisPubSub = + require("../lib/Adapters/PubSub/RedisPubSub").RedisPubSub; const EventEmitterPubSub = - require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub; + require("../lib/Adapters/PubSub/EventEmitterPubSub").EventEmitterPubSub; expect(RedisPubSub.createSubscriber).toHaveBeenCalledWith({ - redisURL: 'redisURL', + redisURL: "redisURL", redisOptions: { socket_keepalive: true }, }); expect(EventEmitterPubSub.createSubscriber).not.toHaveBeenCalled(); }); - it('can create event emitter subscriber', function () { + it("can create event emitter subscriber", function () { ParsePubSub.createSubscriber({}); - const RedisPubSub = require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; + const RedisPubSub = + require("../lib/Adapters/PubSub/RedisPubSub").RedisPubSub; const EventEmitterPubSub = - require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub; + require("../lib/Adapters/PubSub/EventEmitterPubSub").EventEmitterPubSub; expect(RedisPubSub.createSubscriber).not.toHaveBeenCalled(); expect(EventEmitterPubSub.createSubscriber).toHaveBeenCalled(); }); - it('can create publisher/sub with custom adapter', function () { + it("can create publisher/sub with custom adapter", function () { const adapter = { - createPublisher: jasmine.createSpy('createPublisher'), - createSubscriber: jasmine.createSpy('createSubscriber'), + createPublisher: jasmine.createSpy("createPublisher"), + createSubscriber: jasmine.createSpy("createSubscriber"), }; ParsePubSub.createPublisher({ pubSubAdapter: adapter, @@ -88,19 +96,20 @@ describe('ParsePubSub', function () { }); expect(adapter.createSubscriber).toHaveBeenCalled(); - const RedisPubSub = require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; + const RedisPubSub = + require("../lib/Adapters/PubSub/RedisPubSub").RedisPubSub; const EventEmitterPubSub = - require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub; + require("../lib/Adapters/PubSub/EventEmitterPubSub").EventEmitterPubSub; expect(RedisPubSub.createSubscriber).not.toHaveBeenCalled(); expect(EventEmitterPubSub.createSubscriber).not.toHaveBeenCalled(); expect(RedisPubSub.createPublisher).not.toHaveBeenCalled(); expect(EventEmitterPubSub.createPublisher).not.toHaveBeenCalled(); }); - it('can create publisher/sub with custom function adapter', function () { + it("can create publisher/sub with custom function adapter", function () { const adapter = { - createPublisher: jasmine.createSpy('createPublisher'), - createSubscriber: jasmine.createSpy('createSubscriber'), + createPublisher: jasmine.createSpy("createPublisher"), + createSubscriber: jasmine.createSpy("createSubscriber"), }; ParsePubSub.createPublisher({ pubSubAdapter: function () { @@ -116,9 +125,10 @@ describe('ParsePubSub', function () { }); expect(adapter.createSubscriber).toHaveBeenCalled(); - const RedisPubSub = require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; + const RedisPubSub = + require("../lib/Adapters/PubSub/RedisPubSub").RedisPubSub; const EventEmitterPubSub = - require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub; + require("../lib/Adapters/PubSub/EventEmitterPubSub").EventEmitterPubSub; expect(RedisPubSub.createSubscriber).not.toHaveBeenCalled(); expect(EventEmitterPubSub.createSubscriber).not.toHaveBeenCalled(); expect(RedisPubSub.createPublisher).not.toHaveBeenCalled(); @@ -126,7 +136,10 @@ describe('ParsePubSub', function () { }); afterEach(function () { - jasmine.restoreLibrary('../lib/Adapters/PubSub/RedisPubSub', 'RedisPubSub'); - jasmine.restoreLibrary('../lib/Adapters/PubSub/EventEmitterPubSub', 'EventEmitterPubSub'); + jasmine.restoreLibrary("../lib/Adapters/PubSub/RedisPubSub", "RedisPubSub"); + jasmine.restoreLibrary( + "../lib/Adapters/PubSub/EventEmitterPubSub", + "EventEmitterPubSub" + ); }); }); diff --git a/spec/ParseQuery.Aggregate.spec.js b/spec/ParseQuery.Aggregate.spec.js index deb123d58b..a977519967 100644 --- a/spec/ParseQuery.Aggregate.spec.js +++ b/spec/ParseQuery.Aggregate.spec.js @@ -1,13 +1,13 @@ -'use strict'; -const Parse = require('parse/node'); -const request = require('../lib/request'); -const Config = require('../lib/Config'); +"use strict"; +const Parse = require("parse/node"); +const request = require("../lib/request"); +const Config = require("../lib/Config"); const masterKeyHeaders = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Rest-API-Key': 'test', - 'X-Parse-Master-Key': 'test', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": "test", + "X-Parse-Rest-API-Key": "test", + "X-Parse-Master-Key": "test", + "Content-Type": "application/json", }; const masterKeyOptions = { @@ -16,37 +16,37 @@ const masterKeyOptions = { }; const PointerObject = Parse.Object.extend({ - className: 'PointerObject', + className: "PointerObject", }); const loadTestData = () => { const data1 = { score: 10, - name: 'foo', - sender: { group: 'A' }, + name: "foo", + sender: { group: "A" }, views: 900, - size: ['S', 'M'], + size: ["S", "M"], }; const data2 = { score: 10, - name: 'foo', - sender: { group: 'A' }, + name: "foo", + sender: { group: "A" }, views: 800, - size: ['M', 'L'], + size: ["M", "L"], }; const data3 = { score: 10, - name: 'bar', - sender: { group: 'B' }, + name: "bar", + sender: { group: "B" }, views: 700, - size: ['S'], + size: ["S"], }; const data4 = { score: 20, - name: 'dpl', - sender: { group: 'B' }, + name: "dpl", + sender: { group: "B" }, views: 700, - size: ['S'], + size: ["S"], }; const obj1 = new TestObject(data1); const obj2 = new TestObject(data2); @@ -68,45 +68,51 @@ const get = function (url, options) { }); }; -describe('Parse.Query Aggregate testing', () => { +describe("Parse.Query Aggregate testing", () => { beforeEach(async () => { await loadTestData(); }); - it('should only query aggregate with master key', done => { - Parse._request('GET', `aggregate/someClass`, {}).then( + it("should only query aggregate with master key", done => { + Parse._request("GET", `aggregate/someClass`, {}).then( () => {}, error => { - expect(error.message).toEqual('unauthorized: master key is required'); + expect(error.message).toEqual("unauthorized: master key is required"); done(); } ); }); - it('invalid query group _id required', done => { + it("invalid query group _id required", done => { const options = Object.assign({}, masterKeyOptions, { body: { $group: {}, }, }); - get(Parse.serverURL + '/aggregate/TestObject', options).catch(error => { + get(Parse.serverURL + "/aggregate/TestObject", options).catch(error => { expect(error.error.code).toEqual(Parse.Error.INVALID_QUERY); done(); }); }); - it_id('add7050f-65d5-4a13-b526-5bd1ee09c7f1')(it)('group by field', done => { + it_id("add7050f-65d5-4a13-b526-5bd1ee09c7f1")(it)("group by field", done => { const options = Object.assign({}, masterKeyOptions, { body: { - $group: { _id: '$name' }, + $group: { _id: "$name" }, }, }); - get(Parse.serverURL + '/aggregate/TestObject', options) + get(Parse.serverURL + "/aggregate/TestObject", options) .then(resp => { expect(resp.results.length).toBe(3); - expect(Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId')).toBe(true); - expect(Object.prototype.hasOwnProperty.call(resp.results[1], 'objectId')).toBe(true); - expect(Object.prototype.hasOwnProperty.call(resp.results[2], 'objectId')).toBe(true); + expect( + Object.prototype.hasOwnProperty.call(resp.results[0], "objectId") + ).toBe(true); + expect( + Object.prototype.hasOwnProperty.call(resp.results[1], "objectId") + ).toBe(true); + expect( + Object.prototype.hasOwnProperty.call(resp.results[2], "objectId") + ).toBe(true); expect(resp.results[0].objectId).not.toBe(undefined); expect(resp.results[1].objectId).not.toBe(undefined); expect(resp.results[2].objectId).not.toBe(undefined); @@ -115,197 +121,234 @@ describe('Parse.Query Aggregate testing', () => { .catch(done.fail); }); - it_id('0ab0d776-e45d-419a-9b35-3d11933b77d1')(it)('group by pipeline operator', async () => { - const options = Object.assign({}, masterKeyOptions, { - body: { - pipeline: { - $group: { _id: '$name' }, + it_id("0ab0d776-e45d-419a-9b35-3d11933b77d1")(it)( + "group by pipeline operator", + async () => { + const options = Object.assign({}, masterKeyOptions, { + body: { + pipeline: { + $group: { _id: "$name" }, + }, }, - }, - }); - const resp = await get(Parse.serverURL + '/aggregate/TestObject', options); - expect(resp.results.length).toBe(3); - expect(Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId')).toBe(true); - expect(Object.prototype.hasOwnProperty.call(resp.results[1], 'objectId')).toBe(true); - expect(Object.prototype.hasOwnProperty.call(resp.results[2], 'objectId')).toBe(true); - expect(resp.results[0].objectId).not.toBe(undefined); - expect(resp.results[1].objectId).not.toBe(undefined); - expect(resp.results[2].objectId).not.toBe(undefined); - }); - - it_id('b6b42145-7eb4-47aa-ada6-8c1444420e07')(it)('group by empty object', done => { - const obj = new TestObject(); - const pipeline = [ - { - $group: { _id: {} }, - }, - ]; - obj - .save() - .then(() => { - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - expect(results[0].objectId).toEqual(null); - done(); }); - }); + const resp = await get( + Parse.serverURL + "/aggregate/TestObject", + options + ); + expect(resp.results.length).toBe(3); + expect( + Object.prototype.hasOwnProperty.call(resp.results[0], "objectId") + ).toBe(true); + expect( + Object.prototype.hasOwnProperty.call(resp.results[1], "objectId") + ).toBe(true); + expect( + Object.prototype.hasOwnProperty.call(resp.results[2], "objectId") + ).toBe(true); + expect(resp.results[0].objectId).not.toBe(undefined); + expect(resp.results[1].objectId).not.toBe(undefined); + expect(resp.results[2].objectId).not.toBe(undefined); + } + ); - it_id('0f5f6869-e675-41b9-9ad2-52b201124fb0')(it)('group by empty string', done => { - const obj = new TestObject(); - const pipeline = [ - { - $group: { _id: '' }, - }, - ]; - obj - .save() - .then(() => { - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - expect(results[0].objectId).toEqual(null); - done(); - }); - }); + it_id("b6b42145-7eb4-47aa-ada6-8c1444420e07")(it)( + "group by empty object", + done => { + const obj = new TestObject(); + const pipeline = [ + { + $group: { _id: {} }, + }, + ]; + obj + .save() + .then(() => { + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + expect(results[0].objectId).toEqual(null); + done(); + }); + } + ); - it_id('b9c4f1b4-47f4-4ff4-88fb-586711f57e4a')(it)('group by empty array', done => { - const obj = new TestObject(); - const pipeline = [ - { - $group: { _id: [] }, - }, - ]; - obj - .save() - .then(() => { - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - expect(results[0].objectId).toEqual(null); - done(); - }); - }); + it_id("0f5f6869-e675-41b9-9ad2-52b201124fb0")(it)( + "group by empty string", + done => { + const obj = new TestObject(); + const pipeline = [ + { + $group: { _id: "" }, + }, + ]; + obj + .save() + .then(() => { + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + expect(results[0].objectId).toEqual(null); + done(); + }); + } + ); - it_id('bf5ee3e5-986c-4994-9c8d-79310283f602')(it)('group by multiple columns ', done => { - const obj1 = new TestObject(); - const obj2 = new TestObject(); - const obj3 = new TestObject(); - const pipeline = [ - { - $group: { - _id: { - score: '$score', - views: '$views', + it_id("b9c4f1b4-47f4-4ff4-88fb-586711f57e4a")(it)( + "group by empty array", + done => { + const obj = new TestObject(); + const pipeline = [ + { + $group: { _id: [] }, + }, + ]; + obj + .save() + .then(() => { + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + expect(results[0].objectId).toEqual(null); + done(); + }); + } + ); + + it_id("bf5ee3e5-986c-4994-9c8d-79310283f602")(it)( + "group by multiple columns ", + done => { + const obj1 = new TestObject(); + const obj2 = new TestObject(); + const obj3 = new TestObject(); + const pipeline = [ + { + $group: { + _id: { + score: "$score", + views: "$views", + }, + count: { $sum: 1 }, }, - count: { $sum: 1 }, }, - }, - ]; - Parse.Object.saveAll([obj1, obj2, obj3]) - .then(() => { - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - expect(results.length).toEqual(5); - done(); - }); - }); + ]; + Parse.Object.saveAll([obj1, obj2, obj3]) + .then(() => { + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + expect(results.length).toEqual(5); + done(); + }); + } + ); - it_id('3e652c61-78e1-4541-83ac-51ad1def9874')(it)('group by date object', done => { - const obj1 = new TestObject(); - const obj2 = new TestObject(); - const obj3 = new TestObject(); - const pipeline = [ - { - $group: { - _id: { - day: { $dayOfMonth: '$_updated_at' }, - month: { $month: '$_created_at' }, - year: { $year: '$_created_at' }, + it_id("3e652c61-78e1-4541-83ac-51ad1def9874")(it)( + "group by date object", + done => { + const obj1 = new TestObject(); + const obj2 = new TestObject(); + const obj3 = new TestObject(); + const pipeline = [ + { + $group: { + _id: { + day: { $dayOfMonth: "$_updated_at" }, + month: { $month: "$_created_at" }, + year: { $year: "$_created_at" }, + }, + count: { $sum: 1 }, }, - count: { $sum: 1 }, }, - }, - ]; - Parse.Object.saveAll([obj1, obj2, obj3]) - .then(() => { - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - const createdAt = new Date(obj1.createdAt); - expect(results[0].objectId.day).toEqual(createdAt.getUTCDate()); - expect(results[0].objectId.month).toEqual(createdAt.getUTCMonth() + 1); - expect(results[0].objectId.year).toEqual(createdAt.getUTCFullYear()); - done(); - }); - }); + ]; + Parse.Object.saveAll([obj1, obj2, obj3]) + .then(() => { + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + const createdAt = new Date(obj1.createdAt); + expect(results[0].objectId.day).toEqual(createdAt.getUTCDate()); + expect(results[0].objectId.month).toEqual( + createdAt.getUTCMonth() + 1 + ); + expect(results[0].objectId.year).toEqual(createdAt.getUTCFullYear()); + done(); + }); + } + ); - it_id('5d3a0f73-1f49-46f3-9be5-caf1eaefec79')(it)('group by date object transform', done => { - const obj1 = new TestObject(); - const obj2 = new TestObject(); - const obj3 = new TestObject(); - const pipeline = [ - { - $group: { - _id: { - day: { $dayOfMonth: '$updatedAt' }, - month: { $month: '$createdAt' }, - year: { $year: '$createdAt' }, + it_id("5d3a0f73-1f49-46f3-9be5-caf1eaefec79")(it)( + "group by date object transform", + done => { + const obj1 = new TestObject(); + const obj2 = new TestObject(); + const obj3 = new TestObject(); + const pipeline = [ + { + $group: { + _id: { + day: { $dayOfMonth: "$updatedAt" }, + month: { $month: "$createdAt" }, + year: { $year: "$createdAt" }, + }, + count: { $sum: 1 }, }, - count: { $sum: 1 }, }, - }, - ]; - Parse.Object.saveAll([obj1, obj2, obj3]) - .then(() => { - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - const createdAt = new Date(obj1.createdAt); - expect(results[0].objectId.day).toEqual(createdAt.getUTCDate()); - expect(results[0].objectId.month).toEqual(createdAt.getUTCMonth() + 1); - expect(results[0].objectId.year).toEqual(createdAt.getUTCFullYear()); - done(); - }); - }); + ]; + Parse.Object.saveAll([obj1, obj2, obj3]) + .then(() => { + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + const createdAt = new Date(obj1.createdAt); + expect(results[0].objectId.day).toEqual(createdAt.getUTCDate()); + expect(results[0].objectId.month).toEqual( + createdAt.getUTCMonth() + 1 + ); + expect(results[0].objectId.year).toEqual(createdAt.getUTCFullYear()); + done(); + }); + } + ); - it_id('1f9b10f7-dc0e-467f-b506-a303b9c36258')(it)('group by number', done => { + it_id("1f9b10f7-dc0e-467f-b506-a303b9c36258")(it)("group by number", done => { const options = Object.assign({}, masterKeyOptions, { body: { - $group: { _id: '$score' }, + $group: { _id: "$score" }, }, }); - get(Parse.serverURL + '/aggregate/TestObject', options) + get(Parse.serverURL + "/aggregate/TestObject", options) .then(resp => { expect(resp.results.length).toBe(2); - expect(Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId')).toBe(true); - expect(Object.prototype.hasOwnProperty.call(resp.results[1], 'objectId')).toBe(true); - expect(resp.results.sort((a, b) => (a.objectId > b.objectId ? 1 : -1))).toEqual([ - { objectId: 10 }, - { objectId: 20 }, - ]); + expect( + Object.prototype.hasOwnProperty.call(resp.results[0], "objectId") + ).toBe(true); + expect( + Object.prototype.hasOwnProperty.call(resp.results[1], "objectId") + ).toBe(true); + expect( + resp.results.sort((a, b) => (a.objectId > b.objectId ? 1 : -1)) + ).toEqual([{ objectId: 10 }, { objectId: 20 }]); done(); }) .catch(done.fail); }); - it_id('c7695018-03de-49e4-8a72-d4d956f70deb')(it_exclude_dbs(['postgres']))( - 'group and multiply transform', + it_id("c7695018-03de-49e4-8a72-d4d956f70deb")(it_exclude_dbs(["postgres"]))( + "group and multiply transform", done => { - const obj1 = new TestObject({ name: 'item a', quantity: 2, price: 10 }); - const obj2 = new TestObject({ name: 'item b', quantity: 5, price: 5 }); + const obj1 = new TestObject({ name: "item a", quantity: 2, price: 10 }); + const obj2 = new TestObject({ name: "item b", quantity: 5, price: 5 }); const pipeline = [ { $group: { _id: null, - total: { $sum: { $multiply: ['$quantity', '$price'] } }, + total: { $sum: { $multiply: ["$quantity", "$price"] } }, }, }, ]; @@ -322,11 +365,11 @@ describe('Parse.Query Aggregate testing', () => { } ); - it_id('2d278175-7594-4b29-bef4-04c778b7a42f')(it_exclude_dbs(['postgres']))( - 'project and multiply transform', + it_id("2d278175-7594-4b29-bef4-04c778b7a42f")(it_exclude_dbs(["postgres"]))( + "project and multiply transform", done => { - const obj1 = new TestObject({ name: 'item a', quantity: 2, price: 10 }); - const obj2 = new TestObject({ name: 'item b', quantity: 5, price: 5 }); + const obj1 = new TestObject({ name: "item a", quantity: 2, price: 10 }); + const obj2 = new TestObject({ name: "item b", quantity: 5, price: 5 }); const pipeline = [ { $match: { quantity: { $exists: true } }, @@ -334,7 +377,7 @@ describe('Parse.Query Aggregate testing', () => { { $project: { name: 1, - total: { $multiply: ['$quantity', '$price'] }, + total: { $multiply: ["$quantity", "$price"] }, }, }, ]; @@ -345,7 +388,7 @@ describe('Parse.Query Aggregate testing', () => { }) .then(results => { expect(results.length).toEqual(2); - if (results[0].name === 'item a') { + if (results[0].name === "item a") { expect(results[0].total).toEqual(20); expect(results[1].total).toEqual(25); } else { @@ -357,11 +400,11 @@ describe('Parse.Query Aggregate testing', () => { } ); - it_id('9c9d9318-3a9e-4c2a-8a09-d3aa52c7505b')(it_exclude_dbs(['postgres']))( - 'project without objectId transform', + it_id("9c9d9318-3a9e-4c2a-8a09-d3aa52c7505b")(it_exclude_dbs(["postgres"]))( + "project without objectId transform", done => { - const obj1 = new TestObject({ name: 'item a', quantity: 2, price: 10 }); - const obj2 = new TestObject({ name: 'item b', quantity: 5, price: 5 }); + const obj1 = new TestObject({ name: "item a", quantity: 2, price: 10 }); + const obj2 = new TestObject({ name: "item b", quantity: 5, price: 5 }); const pipeline = [ { $match: { quantity: { $exists: true } }, @@ -369,7 +412,7 @@ describe('Parse.Query Aggregate testing', () => { { $project: { _id: 0, - total: { $multiply: ['$quantity', '$price'] }, + total: { $multiply: ["$quantity", "$price"] }, }, }, { @@ -392,8 +435,8 @@ describe('Parse.Query Aggregate testing', () => { } ); - it_id('f92c82ac-1993-4758-b718-45689dfc4154')(it_exclude_dbs(['postgres']))( - 'project updatedAt only transform', + it_id("f92c82ac-1993-4758-b718-45689dfc4154")(it_exclude_dbs(["postgres"]))( + "project updatedAt only transform", done => { const pipeline = [ { @@ -405,16 +448,20 @@ describe('Parse.Query Aggregate testing', () => { expect(results.length).toEqual(4); for (let i = 0; i < results.length; i++) { const item = results[i]; - expect(Object.prototype.hasOwnProperty.call(item, 'updatedAt')).toEqual(true); - expect(Object.prototype.hasOwnProperty.call(item, 'objectId')).toEqual(false); + expect( + Object.prototype.hasOwnProperty.call(item, "updatedAt") + ).toEqual(true); + expect( + Object.prototype.hasOwnProperty.call(item, "objectId") + ).toEqual(false); } done(); }); } ); - it_id('99566b1d-778d-4444-9deb-c398108e659d')(it_exclude_dbs(['postgres']))( - 'can group by any date field (it does not work if you have dirty data)', + it_id("99566b1d-778d-4444-9deb-c398108e659d")(it_exclude_dbs(["postgres"]))( + "can group by any date field (it does not work if you have dirty data)", done => { // rows in your collection with non date data in the field that is supposed to be a date const obj1 = new TestObject({ dateField2019: new Date(1990, 11, 1) }); @@ -429,9 +476,9 @@ describe('Parse.Query Aggregate testing', () => { { $group: { _id: { - day: { $dayOfMonth: '$dateField2019' }, - month: { $month: '$dateField2019' }, - year: { $year: '$dateField2019' }, + day: { $dayOfMonth: "$dateField2019" }, + month: { $month: "$dateField2019" }, + year: { $year: "$dateField2019" }, }, count: { $sum: 1 }, }, @@ -452,8 +499,8 @@ describe('Parse.Query Aggregate testing', () => { } ); - it_only_db('postgres')( - 'can group by any date field (it does not work if you have dirty data)', // rows in your collection with non date data in the field that is supposed to be a date + it_only_db("postgres")( + "can group by any date field (it does not work if you have dirty data)", // rows in your collection with non date data in the field that is supposed to be a date done => { const obj1 = new TestObject({ dateField2019: new Date(1990, 11, 1) }); const obj2 = new TestObject({ dateField2019: new Date(1990, 5, 1) }); @@ -462,9 +509,9 @@ describe('Parse.Query Aggregate testing', () => { { $group: { _id: { - day: { $dayOfMonth: '$dateField2019' }, - month: { $month: '$dateField2019' }, - year: { $year: '$dateField2019' }, + day: { $dayOfMonth: "$dateField2019" }, + month: { $month: "$dateField2019" }, + year: { $year: "$dateField2019" }, }, count: { $sum: 1 }, }, @@ -485,36 +532,47 @@ describe('Parse.Query Aggregate testing', () => { } ); - it_id('bf3c2704-b721-4b1b-92fa-e1b129ae4aff')(it)('group by pointer', done => { - const pointer1 = new TestObject(); - const pointer2 = new TestObject(); - const obj1 = new TestObject({ pointer: pointer1 }); - const obj2 = new TestObject({ pointer: pointer2 }); - const obj3 = new TestObject({ pointer: pointer1 }); - const pipeline = [{ $group: { _id: '$pointer' } }]; - Parse.Object.saveAll([pointer1, pointer2, obj1, obj2, obj3]) - .then(() => { - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - expect(results.length).toEqual(3); - expect(results.some(result => result.objectId === pointer1.id)).toEqual(true); - expect(results.some(result => result.objectId === pointer2.id)).toEqual(true); - expect(results.some(result => result.objectId === null)).toEqual(true); - done(); - }); - }); + it_id("bf3c2704-b721-4b1b-92fa-e1b129ae4aff")(it)( + "group by pointer", + done => { + const pointer1 = new TestObject(); + const pointer2 = new TestObject(); + const obj1 = new TestObject({ pointer: pointer1 }); + const obj2 = new TestObject({ pointer: pointer2 }); + const obj3 = new TestObject({ pointer: pointer1 }); + const pipeline = [{ $group: { _id: "$pointer" } }]; + Parse.Object.saveAll([pointer1, pointer2, obj1, obj2, obj3]) + .then(() => { + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + expect(results.length).toEqual(3); + expect( + results.some(result => result.objectId === pointer1.id) + ).toEqual(true); + expect( + results.some(result => result.objectId === pointer2.id) + ).toEqual(true); + expect(results.some(result => result.objectId === null)).toEqual( + true + ); + done(); + }); + } + ); - it_id('9ee9e8c0-a590-4af9-97a9-4b8e5080ffae')(it)('group sum query', done => { + it_id("9ee9e8c0-a590-4af9-97a9-4b8e5080ffae")(it)("group sum query", done => { const options = Object.assign({}, masterKeyOptions, { body: { - $group: { _id: null, total: { $sum: '$score' } }, + $group: { _id: null, total: { $sum: "$score" } }, }, }); - get(Parse.serverURL + '/aggregate/TestObject', options) + get(Parse.serverURL + "/aggregate/TestObject", options) .then(resp => { - expect(Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId')).toBe(true); + expect( + Object.prototype.hasOwnProperty.call(resp.results[0], "objectId") + ).toBe(true); expect(resp.results[0].objectId).toBe(null); expect(resp.results[0].total).toBe(50); done(); @@ -522,31 +580,38 @@ describe('Parse.Query Aggregate testing', () => { .catch(done.fail); }); - it_id('39133cd6-5bdf-4917-b672-a9d7a9157b6f')(it)('group count query', done => { - const options = Object.assign({}, masterKeyOptions, { - body: { - $group: { _id: null, total: { $sum: 1 } }, - }, - }); - get(Parse.serverURL + '/aggregate/TestObject', options) - .then(resp => { - expect(Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId')).toBe(true); - expect(resp.results[0].objectId).toBe(null); - expect(resp.results[0].total).toBe(4); - done(); - }) - .catch(done.fail); - }); + it_id("39133cd6-5bdf-4917-b672-a9d7a9157b6f")(it)( + "group count query", + done => { + const options = Object.assign({}, masterKeyOptions, { + body: { + $group: { _id: null, total: { $sum: 1 } }, + }, + }); + get(Parse.serverURL + "/aggregate/TestObject", options) + .then(resp => { + expect( + Object.prototype.hasOwnProperty.call(resp.results[0], "objectId") + ).toBe(true); + expect(resp.results[0].objectId).toBe(null); + expect(resp.results[0].total).toBe(4); + done(); + }) + .catch(done.fail); + } + ); - it_id('48685ff3-066f-4353-82e7-87f39d812ff7')(it)('group min query', done => { + it_id("48685ff3-066f-4353-82e7-87f39d812ff7")(it)("group min query", done => { const options = Object.assign({}, masterKeyOptions, { body: { - $group: { _id: null, minScore: { $min: '$score' } }, + $group: { _id: null, minScore: { $min: "$score" } }, }, }); - get(Parse.serverURL + '/aggregate/TestObject', options) + get(Parse.serverURL + "/aggregate/TestObject", options) .then(resp => { - expect(Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId')).toBe(true); + expect( + Object.prototype.hasOwnProperty.call(resp.results[0], "objectId") + ).toBe(true); expect(resp.results[0].objectId).toBe(null); expect(resp.results[0].minScore).toBe(10); done(); @@ -554,15 +619,17 @@ describe('Parse.Query Aggregate testing', () => { .catch(done.fail); }); - it_id('581efea6-6525-4e10-96d9-76d32c73e7a9')(it)('group max query', done => { + it_id("581efea6-6525-4e10-96d9-76d32c73e7a9")(it)("group max query", done => { const options = Object.assign({}, masterKeyOptions, { body: { - $group: { _id: null, maxScore: { $max: '$score' } }, + $group: { _id: null, maxScore: { $max: "$score" } }, }, }); - get(Parse.serverURL + '/aggregate/TestObject', options) + get(Parse.serverURL + "/aggregate/TestObject", options) .then(resp => { - expect(Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId')).toBe(true); + expect( + Object.prototype.hasOwnProperty.call(resp.results[0], "objectId") + ).toBe(true); expect(resp.results[0].objectId).toBe(null); expect(resp.results[0].maxScore).toBe(20); done(); @@ -570,15 +637,17 @@ describe('Parse.Query Aggregate testing', () => { .catch(done.fail); }); - it_id('5f880de2-b97f-43d1-89b7-ad903a4be4e2')(it)('group avg query', done => { + it_id("5f880de2-b97f-43d1-89b7-ad903a4be4e2")(it)("group avg query", done => { const options = Object.assign({}, masterKeyOptions, { body: { - $group: { _id: null, avgScore: { $avg: '$score' } }, + $group: { _id: null, avgScore: { $avg: "$score" } }, }, }); - get(Parse.serverURL + '/aggregate/TestObject', options) + get(Parse.serverURL + "/aggregate/TestObject", options) .then(resp => { - expect(Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId')).toBe(true); + expect( + Object.prototype.hasOwnProperty.call(resp.results[0], "objectId") + ).toBe(true); expect(resp.results[0].objectId).toBe(null); expect(resp.results[0].avgScore).toBe(12.5); done(); @@ -586,13 +655,13 @@ describe('Parse.Query Aggregate testing', () => { .catch(done.fail); }); - it_id('58e7a1a0-fae1-4993-b336-7bcbd5b7c786')(it)('limit query', done => { + it_id("58e7a1a0-fae1-4993-b336-7bcbd5b7c786")(it)("limit query", done => { const options = Object.assign({}, masterKeyOptions, { body: { $limit: 2, }, }); - get(Parse.serverURL + '/aggregate/TestObject', options) + get(Parse.serverURL + "/aggregate/TestObject", options) .then(resp => { expect(resp.results.length).toBe(2); done(); @@ -600,49 +669,55 @@ describe('Parse.Query Aggregate testing', () => { .catch(done.fail); }); - it_id('c892a3d2-8ae8-4b88-bf2b-3c958e1cacd8')(it)('sort ascending query', done => { - const options = Object.assign({}, masterKeyOptions, { - body: { - $sort: { name: 1 }, - }, - }); - get(Parse.serverURL + '/aggregate/TestObject', options) - .then(resp => { - expect(resp.results.length).toBe(4); - expect(resp.results[0].name).toBe('bar'); - expect(resp.results[1].name).toBe('dpl'); - expect(resp.results[2].name).toBe('foo'); - expect(resp.results[3].name).toBe('foo'); - done(); - }) - .catch(done.fail); - }); + it_id("c892a3d2-8ae8-4b88-bf2b-3c958e1cacd8")(it)( + "sort ascending query", + done => { + const options = Object.assign({}, masterKeyOptions, { + body: { + $sort: { name: 1 }, + }, + }); + get(Parse.serverURL + "/aggregate/TestObject", options) + .then(resp => { + expect(resp.results.length).toBe(4); + expect(resp.results[0].name).toBe("bar"); + expect(resp.results[1].name).toBe("dpl"); + expect(resp.results[2].name).toBe("foo"); + expect(resp.results[3].name).toBe("foo"); + done(); + }) + .catch(done.fail); + } + ); - it_id('79d4bc2e-8b69-42ec-8526-20d17e968ab3')(it)('sort decending query', done => { - const options = Object.assign({}, masterKeyOptions, { - body: { - $sort: { name: -1 }, - }, - }); - get(Parse.serverURL + '/aggregate/TestObject', options) - .then(resp => { - expect(resp.results.length).toBe(4); - expect(resp.results[0].name).toBe('foo'); - expect(resp.results[1].name).toBe('foo'); - expect(resp.results[2].name).toBe('dpl'); - expect(resp.results[3].name).toBe('bar'); - done(); - }) - .catch(done.fail); - }); + it_id("79d4bc2e-8b69-42ec-8526-20d17e968ab3")(it)( + "sort decending query", + done => { + const options = Object.assign({}, masterKeyOptions, { + body: { + $sort: { name: -1 }, + }, + }); + get(Parse.serverURL + "/aggregate/TestObject", options) + .then(resp => { + expect(resp.results.length).toBe(4); + expect(resp.results[0].name).toBe("foo"); + expect(resp.results[1].name).toBe("foo"); + expect(resp.results[2].name).toBe("dpl"); + expect(resp.results[3].name).toBe("bar"); + done(); + }) + .catch(done.fail); + } + ); - it_id('b3d97d48-bd6b-444d-be64-cc1fd4738266')(it)('skip query', done => { + it_id("b3d97d48-bd6b-444d-be64-cc1fd4738266")(it)("skip query", done => { const options = Object.assign({}, masterKeyOptions, { body: { $skip: 2, }, }); - get(Parse.serverURL + '/aggregate/TestObject', options) + get(Parse.serverURL + "/aggregate/TestObject", options) .then(resp => { expect(resp.results.length).toBe(2); done(); @@ -650,40 +725,43 @@ describe('Parse.Query Aggregate testing', () => { .catch(done.fail); }); - it_id('4a7daee3-5ba1-4c8b-b406-1846a73a64c8')(it)('match comparison date query', done => { - const today = new Date(); - const yesterday = new Date(); - const tomorrow = new Date(); - yesterday.setDate(today.getDate() - 1); - tomorrow.setDate(today.getDate() + 1); - const obj1 = new TestObject({ dateField: yesterday }); - const obj2 = new TestObject({ dateField: today }); - const obj3 = new TestObject({ dateField: tomorrow }); - const pipeline = [{ $match: { dateField: { $lt: tomorrow } } }]; - Parse.Object.saveAll([obj1, obj2, obj3]) - .then(() => { - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - expect(results.length).toBe(2); - done(); - }); - }); + it_id("4a7daee3-5ba1-4c8b-b406-1846a73a64c8")(it)( + "match comparison date query", + done => { + const today = new Date(); + const yesterday = new Date(); + const tomorrow = new Date(); + yesterday.setDate(today.getDate() - 1); + tomorrow.setDate(today.getDate() + 1); + const obj1 = new TestObject({ dateField: yesterday }); + const obj2 = new TestObject({ dateField: today }); + const obj3 = new TestObject({ dateField: tomorrow }); + const pipeline = [{ $match: { dateField: { $lt: tomorrow } } }]; + Parse.Object.saveAll([obj1, obj2, obj3]) + .then(() => { + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + expect(results.length).toBe(2); + done(); + }); + } + ); - it_id('d98c8c20-6dac-4d74-8228-85a1ae46a7d0')(it)( - 'should aggregate with Date object (directAccess)', + it_id("d98c8c20-6dac-4d74-8228-85a1ae46a7d0")(it)( + "should aggregate with Date object (directAccess)", async () => { - const rest = require('../lib/rest'); - const auth = require('../lib/Auth'); - const TestObject = Parse.Object.extend('TestObject'); + const rest = require("../lib/rest"); + const auth = require("../lib/Auth"); + const TestObject = Parse.Object.extend("TestObject"); const date = new Date(); await new TestObject({ date: date }).save(null, { useMasterKey: true }); const config = Config.get(Parse.applicationId); const resp = await rest.find( config, auth.master(config), - 'TestObject', + "TestObject", {}, { pipeline: [{ $match: { date: { $lte: new Date() } } }] } ); @@ -691,79 +769,97 @@ describe('Parse.Query Aggregate testing', () => { } ); - it_id('3d73d23a-fce1-4ac0-972a-50f6a550f348')(it)('match comparison query', done => { - const options = Object.assign({}, masterKeyOptions, { - body: { - $match: { score: { $gt: 15 } }, - }, - }); - get(Parse.serverURL + '/aggregate/TestObject', options) - .then(resp => { - expect(resp.results.length).toBe(1); - expect(resp.results[0].score).toBe(20); - done(); - }) - .catch(done.fail); - }); + it_id("3d73d23a-fce1-4ac0-972a-50f6a550f348")(it)( + "match comparison query", + done => { + const options = Object.assign({}, masterKeyOptions, { + body: { + $match: { score: { $gt: 15 } }, + }, + }); + get(Parse.serverURL + "/aggregate/TestObject", options) + .then(resp => { + expect(resp.results.length).toBe(1); + expect(resp.results[0].score).toBe(20); + done(); + }) + .catch(done.fail); + } + ); - it_id('11772059-6c93-41ac-8dfe-e55b6c97e16f')(it)('match multiple comparison query', done => { - const options = Object.assign({}, masterKeyOptions, { - body: { - $match: { score: { $gt: 5, $lt: 15 } }, - }, - }); - get(Parse.serverURL + '/aggregate/TestObject', options) - .then(resp => { - expect(resp.results.length).toBe(3); - expect(resp.results[0].score).toBe(10); - expect(resp.results[1].score).toBe(10); - expect(resp.results[2].score).toBe(10); - done(); - }) - .catch(done.fail); - }); + it_id("11772059-6c93-41ac-8dfe-e55b6c97e16f")(it)( + "match multiple comparison query", + done => { + const options = Object.assign({}, masterKeyOptions, { + body: { + $match: { score: { $gt: 5, $lt: 15 } }, + }, + }); + get(Parse.serverURL + "/aggregate/TestObject", options) + .then(resp => { + expect(resp.results.length).toBe(3); + expect(resp.results[0].score).toBe(10); + expect(resp.results[1].score).toBe(10); + expect(resp.results[2].score).toBe(10); + done(); + }) + .catch(done.fail); + } + ); - it_id('ca2efb04-8f73-40ca-a5fc-79d0032bc398')(it)('match complex comparison query', done => { - const options = Object.assign({}, masterKeyOptions, { - body: { - $match: { score: { $gt: 5, $lt: 15 }, views: { $gt: 850, $lt: 1000 } }, - }, - }); - get(Parse.serverURL + '/aggregate/TestObject', options) - .then(resp => { - expect(resp.results.length).toBe(1); - expect(resp.results[0].score).toBe(10); - expect(resp.results[0].views).toBe(900); - done(); - }) - .catch(done.fail); - }); + it_id("ca2efb04-8f73-40ca-a5fc-79d0032bc398")(it)( + "match complex comparison query", + done => { + const options = Object.assign({}, masterKeyOptions, { + body: { + $match: { + score: { $gt: 5, $lt: 15 }, + views: { $gt: 850, $lt: 1000 }, + }, + }, + }); + get(Parse.serverURL + "/aggregate/TestObject", options) + .then(resp => { + expect(resp.results.length).toBe(1); + expect(resp.results[0].score).toBe(10); + expect(resp.results[0].views).toBe(900); + done(); + }) + .catch(done.fail); + } + ); - it_id('5ef9dcbe-fe54-4db2-b8fb-58c87c6ff072')(it)('match comparison and equality query', done => { - const options = Object.assign({}, masterKeyOptions, { - body: { - $match: { score: { $gt: 5, $lt: 15 }, views: 900 }, - }, - }); - get(Parse.serverURL + '/aggregate/TestObject', options) - .then(resp => { - expect(resp.results.length).toBe(1); - expect(resp.results[0].score).toBe(10); - expect(resp.results[0].views).toBe(900); - done(); - }) - .catch(done.fail); - }); + it_id("5ef9dcbe-fe54-4db2-b8fb-58c87c6ff072")(it)( + "match comparison and equality query", + done => { + const options = Object.assign({}, masterKeyOptions, { + body: { + $match: { score: { $gt: 5, $lt: 15 }, views: 900 }, + }, + }); + get(Parse.serverURL + "/aggregate/TestObject", options) + .then(resp => { + expect(resp.results.length).toBe(1); + expect(resp.results[0].score).toBe(10); + expect(resp.results[0].views).toBe(900); + done(); + }) + .catch(done.fail); + } + ); - it_id('c910a6af-58df-46aa-bbf8-da014a04cdcd')(it)('match $or query', done => { + it_id("c910a6af-58df-46aa-bbf8-da014a04cdcd")(it)("match $or query", done => { const options = Object.assign({}, masterKeyOptions, { body: { $match: { - $or: [{ score: { $gt: 15, $lt: 25 } }, { views: { $gt: 750, $lt: 850 } }], + $or: [ + { score: { $gt: 15, $lt: 25 } }, + { views: { $gt: 750, $lt: 850 } }, + ], }, }, }); - get(Parse.serverURL + '/aggregate/TestObject', options) + get(Parse.serverURL + "/aggregate/TestObject", options) .then(resp => { expect(resp.results.length).toBe(2); // Match score { $gt: 15, $lt: 25 } @@ -778,131 +874,165 @@ describe('Parse.Query Aggregate testing', () => { .catch(done.fail); }); - it_id('0f768dc2-0675-4e45-a763-5ca9c895fa5f')(it)('match objectId query', done => { - const obj1 = new TestObject(); - const obj2 = new TestObject(); - Parse.Object.saveAll([obj1, obj2]) - .then(() => { - const pipeline = [{ $match: { _id: obj1.id } }]; - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - expect(results.length).toEqual(1); - expect(results[0].objectId).toEqual(obj1.id); - done(); - }); - }); + it_id("0f768dc2-0675-4e45-a763-5ca9c895fa5f")(it)( + "match objectId query", + done => { + const obj1 = new TestObject(); + const obj2 = new TestObject(); + Parse.Object.saveAll([obj1, obj2]) + .then(() => { + const pipeline = [{ $match: { _id: obj1.id } }]; + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + expect(results.length).toEqual(1); + expect(results[0].objectId).toEqual(obj1.id); + done(); + }); + } + ); - it_id('27349e04-0d9d-453f-ad85-1a811631582d')(it)('match field query', done => { - const obj1 = new TestObject({ name: 'TestObject1' }); - const obj2 = new TestObject({ name: 'TestObject2' }); - Parse.Object.saveAll([obj1, obj2]) - .then(() => { - const pipeline = [{ $match: { name: 'TestObject1' } }]; - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - expect(results.length).toEqual(1); - expect(results[0].objectId).toEqual(obj1.id); - done(); - }); - }); + it_id("27349e04-0d9d-453f-ad85-1a811631582d")(it)( + "match field query", + done => { + const obj1 = new TestObject({ name: "TestObject1" }); + const obj2 = new TestObject({ name: "TestObject2" }); + Parse.Object.saveAll([obj1, obj2]) + .then(() => { + const pipeline = [{ $match: { name: "TestObject1" } }]; + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + expect(results.length).toEqual(1); + expect(results[0].objectId).toEqual(obj1.id); + done(); + }); + } + ); - it_id('9222e025-d450-4699-8d5b-c5cf9a64fb24')(it)('match pointer query', done => { - const pointer1 = new PointerObject(); - const pointer2 = new PointerObject(); - const obj1 = new TestObject({ pointer: pointer1 }); - const obj2 = new TestObject({ pointer: pointer2 }); - const obj3 = new TestObject({ pointer: pointer1 }); - - Parse.Object.saveAll([pointer1, pointer2, obj1, obj2, obj3]) - .then(() => { - const pipeline = [{ $match: { pointer: pointer1.id } }]; - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - expect(results.length).toEqual(2); - expect(results[0].pointer.objectId).toEqual(pointer1.id); - expect(results[1].pointer.objectId).toEqual(pointer1.id); - expect(results.some(result => result.objectId === obj1.id)).toEqual(true); - expect(results.some(result => result.objectId === obj3.id)).toEqual(true); + it_id("9222e025-d450-4699-8d5b-c5cf9a64fb24")(it)( + "match pointer query", + done => { + const pointer1 = new PointerObject(); + const pointer2 = new PointerObject(); + const obj1 = new TestObject({ pointer: pointer1 }); + const obj2 = new TestObject({ pointer: pointer2 }); + const obj3 = new TestObject({ pointer: pointer1 }); + + Parse.Object.saveAll([pointer1, pointer2, obj1, obj2, obj3]) + .then(() => { + const pipeline = [{ $match: { pointer: pointer1.id } }]; + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + expect(results.length).toEqual(2); + expect(results[0].pointer.objectId).toEqual(pointer1.id); + expect(results[1].pointer.objectId).toEqual(pointer1.id); + expect(results.some(result => result.objectId === obj1.id)).toEqual( + true + ); + expect(results.some(result => result.objectId === obj3.id)).toEqual( + true + ); + done(); + }); + } + ); + + it_id("3a1e2cdc-52c7-4060-bc90-b06d557d85ce")(it_exclude_dbs(["postgres"]))( + "match exists query", + done => { + const pipeline = [{ $match: { score: { $exists: true } } }]; + const query = new Parse.Query(TestObject); + query.aggregate(pipeline).then(results => { + expect(results.length).toEqual(4); done(); }); - }); + } + ); + + it_id("0adea3f4-73f7-4b48-a7dd-c764ceb947ec")(it)( + "match date query - createdAt", + done => { + const obj1 = new TestObject(); + const obj2 = new TestObject(); + + Parse.Object.saveAll([obj1, obj2]) + .then(() => { + const now = new Date(); + const today = new Date( + now.getFullYear(), + now.getMonth(), + now.getDate() + ); + const pipeline = [{ $match: { createdAt: { $gte: today } } }]; + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + // Four objects were created initially, we added two more. + expect(results.length).toEqual(6); + done(); + }); + } + ); - it_id('3a1e2cdc-52c7-4060-bc90-b06d557d85ce')(it_exclude_dbs(['postgres']))( - 'match exists query', + it_id("cdc0eecb-f547-4881-84cc-c06fb46a636a")(it)( + "match date query - updatedAt", done => { - const pipeline = [{ $match: { score: { $exists: true } } }]; - const query = new Parse.Query(TestObject); - query.aggregate(pipeline).then(results => { - expect(results.length).toEqual(4); - done(); - }); + const obj1 = new TestObject(); + const obj2 = new TestObject(); + + Parse.Object.saveAll([obj1, obj2]) + .then(() => { + const now = new Date(); + const today = new Date( + now.getFullYear(), + now.getMonth(), + now.getDate() + ); + const pipeline = [{ $match: { updatedAt: { $gte: today } } }]; + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + // Four objects were added initially, we added two more. + expect(results.length).toEqual(6); + done(); + }); } ); - it_id('0adea3f4-73f7-4b48-a7dd-c764ceb947ec')(it)('match date query - createdAt', done => { - const obj1 = new TestObject(); - const obj2 = new TestObject(); - - Parse.Object.saveAll([obj1, obj2]) - .then(() => { - const now = new Date(); - const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); - const pipeline = [{ $match: { createdAt: { $gte: today } } }]; - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - // Four objects were created initially, we added two more. - expect(results.length).toEqual(6); - done(); - }); - }); - - it_id('cdc0eecb-f547-4881-84cc-c06fb46a636a')(it)('match date query - updatedAt', done => { - const obj1 = new TestObject(); - const obj2 = new TestObject(); - - Parse.Object.saveAll([obj1, obj2]) - .then(() => { - const now = new Date(); - const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); - const pipeline = [{ $match: { updatedAt: { $gte: today } } }]; - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - // Four objects were added initially, we added two more. - expect(results.length).toEqual(6); - done(); - }); - }); + it_id("621fe00a-1127-4341-a8e1-fc579b7ed8bd")(it)( + "match date query - empty", + done => { + const obj1 = new TestObject(); + const obj2 = new TestObject(); - it_id('621fe00a-1127-4341-a8e1-fc579b7ed8bd')(it)('match date query - empty', done => { - const obj1 = new TestObject(); - const obj2 = new TestObject(); - - Parse.Object.saveAll([obj1, obj2]) - .then(() => { - const now = new Date(); - const future = new Date(now.getFullYear(), now.getMonth() + 1, now.getDate()); - const pipeline = [{ $match: { createdAt: future } }]; - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - expect(results.length).toEqual(0); - done(); - }); - }); + Parse.Object.saveAll([obj1, obj2]) + .then(() => { + const now = new Date(); + const future = new Date( + now.getFullYear(), + now.getMonth() + 1, + now.getDate() + ); + const pipeline = [{ $match: { createdAt: future } }]; + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + expect(results.length).toEqual(0); + done(); + }); + } + ); - it_id('802ffc99-861b-4b72-90a6-0c666a2e3fd8')(it_exclude_dbs(['postgres']))( - 'match pointer with operator query', + it_id("802ffc99-861b-4b72-90a6-0c666a2e3fd8")(it_exclude_dbs(["postgres"]))( + "match pointer with operator query", done => { const pointer = new PointerObject(); @@ -920,42 +1050,46 @@ describe('Parse.Query Aggregate testing', () => { expect(results.length).toEqual(2); expect(results[0].pointer.objectId).toEqual(pointer.id); expect(results[1].pointer.objectId).toEqual(pointer.id); - expect(results.some(result => result.objectId === obj1.id)).toEqual(true); - expect(results.some(result => result.objectId === obj2.id)).toEqual(true); + expect(results.some(result => result.objectId === obj1.id)).toEqual( + true + ); + expect(results.some(result => result.objectId === obj2.id)).toEqual( + true + ); done(); }); } ); - it_id('28090280-7c3e-47f8-8bf6-bebf8566a36c')(it_exclude_dbs(['postgres']))( - 'match null values', + it_id("28090280-7c3e-47f8-8bf6-bebf8566a36c")(it_exclude_dbs(["postgres"]))( + "match null values", async () => { - const obj1 = new Parse.Object('MyCollection'); - obj1.set('language', 'en'); - obj1.set('otherField', 1); - const obj2 = new Parse.Object('MyCollection'); - obj2.set('language', 'en'); - obj2.set('otherField', 2); - const obj3 = new Parse.Object('MyCollection'); - obj3.set('language', null); - obj3.set('otherField', 3); - const obj4 = new Parse.Object('MyCollection'); - obj4.set('language', null); - obj4.set('otherField', 4); - const obj5 = new Parse.Object('MyCollection'); - obj5.set('language', 'pt'); - obj5.set('otherField', 5); - const obj6 = new Parse.Object('MyCollection'); - obj6.set('language', 'pt'); - obj6.set('otherField', 6); + const obj1 = new Parse.Object("MyCollection"); + obj1.set("language", "en"); + obj1.set("otherField", 1); + const obj2 = new Parse.Object("MyCollection"); + obj2.set("language", "en"); + obj2.set("otherField", 2); + const obj3 = new Parse.Object("MyCollection"); + obj3.set("language", null); + obj3.set("otherField", 3); + const obj4 = new Parse.Object("MyCollection"); + obj4.set("language", null); + obj4.set("otherField", 4); + const obj5 = new Parse.Object("MyCollection"); + obj5.set("language", "pt"); + obj5.set("otherField", 5); + const obj6 = new Parse.Object("MyCollection"); + obj6.set("language", "pt"); + obj6.set("otherField", 6); await Parse.Object.saveAll([obj1, obj2, obj3, obj4, obj5, obj6]); expect( ( - await new Parse.Query('MyCollection').aggregate([ + await new Parse.Query("MyCollection").aggregate([ { $match: { - language: { $in: [null, 'en'] }, + language: { $in: [null, "en"] }, }, }, ]) @@ -966,10 +1100,10 @@ describe('Parse.Query Aggregate testing', () => { expect( ( - await new Parse.Query('MyCollection').aggregate([ + await new Parse.Query("MyCollection").aggregate([ { $match: { - $or: [{ language: 'en' }, { language: null }], + $or: [{ language: "en" }, { language: null }], }, }, ]) @@ -980,13 +1114,13 @@ describe('Parse.Query Aggregate testing', () => { } ); - it_id('df63d1f5-7c37-4ed9-8bc5-20d82f29f509')(it)('project query', done => { + it_id("df63d1f5-7c37-4ed9-8bc5-20d82f29f509")(it)("project query", done => { const options = Object.assign({}, masterKeyOptions, { body: { $project: { name: 1 }, }, }); - get(Parse.serverURL + '/aggregate/TestObject', options) + get(Parse.serverURL + "/aggregate/TestObject", options) .then(resp => { resp.results.forEach(result => { expect(result.objectId).not.toBe(undefined); @@ -1000,84 +1134,95 @@ describe('Parse.Query Aggregate testing', () => { .catch(done.fail); }); - it_id('69224bbb-8ea0-4ab4-af23-398b6432f668')(it)('multiple project query', done => { - const options = Object.assign({}, masterKeyOptions, { - body: { - $project: { name: 1, score: 1, sender: 1 }, - }, - }); - get(Parse.serverURL + '/aggregate/TestObject', options) - .then(resp => { - resp.results.forEach(result => { - expect(result.objectId).not.toBe(undefined); - expect(result.name).not.toBe(undefined); - expect(result.score).not.toBe(undefined); - expect(result.sender).not.toBe(undefined); - expect(result.size).toBe(undefined); - }); - done(); - }) - .catch(done.fail); - }); - - it_id('97ce4c7c-8d9f-4ffd-9352-394bc9867bab')(it)('project pointer query', done => { - const pointer = new PointerObject(); - const obj = new TestObject({ pointer, name: 'hello' }); - - obj - .save() - .then(() => { - const pipeline = [ - { $match: { _id: obj.id } }, - { $project: { pointer: 1, name: 1, createdAt: 1 } }, - ]; - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - expect(results.length).toEqual(1); - expect(results[0].name).toEqual('hello'); - expect(results[0].createdAt).not.toBe(undefined); - expect(results[0].pointer.objectId).toEqual(pointer.id); - done(); + it_id("69224bbb-8ea0-4ab4-af23-398b6432f668")(it)( + "multiple project query", + done => { + const options = Object.assign({}, masterKeyOptions, { + body: { + $project: { name: 1, score: 1, sender: 1 }, + }, }); - }); + get(Parse.serverURL + "/aggregate/TestObject", options) + .then(resp => { + resp.results.forEach(result => { + expect(result.objectId).not.toBe(undefined); + expect(result.name).not.toBe(undefined); + expect(result.score).not.toBe(undefined); + expect(result.sender).not.toBe(undefined); + expect(result.size).toBe(undefined); + }); + done(); + }) + .catch(done.fail); + } + ); - it_id('3940aac3-ac49-4279-8083-af9096de636f')(it)('project with group query', done => { - const options = Object.assign({}, masterKeyOptions, { - body: { - $project: { score: 1 }, - $group: { _id: '$score', score: { $sum: '$score' } }, - }, - }); - get(Parse.serverURL + '/aggregate/TestObject', options) - .then(resp => { - expect(resp.results.length).toBe(2); - resp.results.forEach(result => { - expect(Object.prototype.hasOwnProperty.call(result, 'objectId')).toBe(true); - expect(result.name).toBe(undefined); - expect(result.sender).toBe(undefined); - expect(result.size).toBe(undefined); - expect(result.score).not.toBe(undefined); - if (result.objectId === 10) { - expect(result.score).toBe(30); - } - if (result.objectId === 20) { - expect(result.score).toBe(20); - } + it_id("97ce4c7c-8d9f-4ffd-9352-394bc9867bab")(it)( + "project pointer query", + done => { + const pointer = new PointerObject(); + const obj = new TestObject({ pointer, name: "hello" }); + + obj + .save() + .then(() => { + const pipeline = [ + { $match: { _id: obj.id } }, + { $project: { pointer: 1, name: 1, createdAt: 1 } }, + ]; + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + expect(results.length).toEqual(1); + expect(results[0].name).toEqual("hello"); + expect(results[0].createdAt).not.toBe(undefined); + expect(results[0].pointer.objectId).toEqual(pointer.id); + done(); }); - done(); - }) - .catch(done.fail); - }); + } + ); + + it_id("3940aac3-ac49-4279-8083-af9096de636f")(it)( + "project with group query", + done => { + const options = Object.assign({}, masterKeyOptions, { + body: { + $project: { score: 1 }, + $group: { _id: "$score", score: { $sum: "$score" } }, + }, + }); + get(Parse.serverURL + "/aggregate/TestObject", options) + .then(resp => { + expect(resp.results.length).toBe(2); + resp.results.forEach(result => { + expect( + Object.prototype.hasOwnProperty.call(result, "objectId") + ).toBe(true); + expect(result.name).toBe(undefined); + expect(result.sender).toBe(undefined); + expect(result.size).toBe(undefined); + expect(result.score).not.toBe(undefined); + if (result.objectId === 10) { + expect(result.score).toBe(30); + } + if (result.objectId === 20) { + expect(result.score).toBe(20); + } + }); + done(); + }) + .catch(done.fail); + } + ); - it('class does not exist return empty', done => { + it("class does not exist return empty", done => { const options = Object.assign({}, masterKeyOptions, { body: { - $group: { _id: null, total: { $sum: '$score' } }, + $group: { _id: null, total: { $sum: "$score" } }, }, }); - get(Parse.serverURL + '/aggregate/UnknownClass', options) + get(Parse.serverURL + "/aggregate/UnknownClass", options) .then(resp => { expect(resp.results.length).toBe(0); done(); @@ -1085,13 +1230,13 @@ describe('Parse.Query Aggregate testing', () => { .catch(done.fail); }); - it('field does not exist return empty', done => { + it("field does not exist return empty", done => { const options = Object.assign({}, masterKeyOptions, { body: { - $group: { _id: null, total: { $sum: '$unknownfield' } }, + $group: { _id: null, total: { $sum: "$unknownfield" } }, }, }); - get(Parse.serverURL + '/aggregate/UnknownClass', options) + get(Parse.serverURL + "/aggregate/UnknownClass", options) .then(resp => { expect(resp.results.length).toBe(0); done(); @@ -1099,11 +1244,11 @@ describe('Parse.Query Aggregate testing', () => { .catch(done.fail); }); - it_id('985e7a66-d4f5-4f72-bd54-ee44670e0ab0')(it)('distinct query', done => { + it_id("985e7a66-d4f5-4f72-bd54-ee44670e0ab0")(it)("distinct query", done => { const options = Object.assign({}, masterKeyOptions, { - body: { distinct: 'score' }, + body: { distinct: "score" }, }); - get(Parse.serverURL + '/aggregate/TestObject', options) + get(Parse.serverURL + "/aggregate/TestObject", options) .then(resp => { expect(resp.results.length).toBe(2); expect(resp.results.includes(10)).toBe(true); @@ -1113,78 +1258,91 @@ describe('Parse.Query Aggregate testing', () => { .catch(done.fail); }); - it_id('ef157f86-c456-4a4c-8dac-81910bd0f716')(it)('distinct query with where', done => { - const options = Object.assign({}, masterKeyOptions, { - body: { - distinct: 'score', - $where: { - name: 'bar', + it_id("ef157f86-c456-4a4c-8dac-81910bd0f716")(it)( + "distinct query with where", + done => { + const options = Object.assign({}, masterKeyOptions, { + body: { + distinct: "score", + $where: { + name: "bar", + }, }, - }, - }); - get(Parse.serverURL + '/aggregate/TestObject', options) - .then(resp => { - expect(resp.results[0]).toBe(10); - done(); - }) - .catch(done.fail); - }); + }); + get(Parse.serverURL + "/aggregate/TestObject", options) + .then(resp => { + expect(resp.results[0]).toBe(10); + done(); + }) + .catch(done.fail); + } + ); - it_id('7f5275cc-2c34-42bc-8a09-43378419c326')(it)('distinct query with where string', done => { - const options = Object.assign({}, masterKeyOptions, { - body: { - distinct: 'score', - $where: JSON.stringify({ name: 'bar' }), - }, - }); - get(Parse.serverURL + '/aggregate/TestObject', options) - .then(resp => { - expect(resp.results[0]).toBe(10); - done(); - }) - .catch(done.fail); - }); + it_id("7f5275cc-2c34-42bc-8a09-43378419c326")(it)( + "distinct query with where string", + done => { + const options = Object.assign({}, masterKeyOptions, { + body: { + distinct: "score", + $where: JSON.stringify({ name: "bar" }), + }, + }); + get(Parse.serverURL + "/aggregate/TestObject", options) + .then(resp => { + expect(resp.results[0]).toBe(10); + done(); + }) + .catch(done.fail); + } + ); - it_id('383b7248-e457-4373-8d5c-f9359384347e')(it)('distinct nested', done => { + it_id("383b7248-e457-4373-8d5c-f9359384347e")(it)("distinct nested", done => { const options = Object.assign({}, masterKeyOptions, { - body: { distinct: 'sender.group' }, + body: { distinct: "sender.group" }, }); - get(Parse.serverURL + '/aggregate/TestObject', options) + get(Parse.serverURL + "/aggregate/TestObject", options) .then(resp => { expect(resp.results.length).toBe(2); - expect(resp.results.includes('A')).toBe(true); - expect(resp.results.includes('B')).toBe(true); + expect(resp.results.includes("A")).toBe(true); + expect(resp.results.includes("B")).toBe(true); done(); }) .catch(done.fail); }); - it_id('20f14464-adb7-428c-ac7a-5a91a1952a64')(it)('distinct pointer', done => { - const pointer1 = new PointerObject(); - const pointer2 = new PointerObject(); - const obj1 = new TestObject({ pointer: pointer1 }); - const obj2 = new TestObject({ pointer: pointer2 }); - const obj3 = new TestObject({ pointer: pointer1 }); - Parse.Object.saveAll([pointer1, pointer2, obj1, obj2, obj3]) - .then(() => { - const query = new Parse.Query(TestObject); - return query.distinct('pointer'); - }) - .then(results => { - expect(results.length).toEqual(2); - expect(results.some(result => result.objectId === pointer1.id)).toEqual(true); - expect(results.some(result => result.objectId === pointer2.id)).toEqual(true); - done(); - }); - }); + it_id("20f14464-adb7-428c-ac7a-5a91a1952a64")(it)( + "distinct pointer", + done => { + const pointer1 = new PointerObject(); + const pointer2 = new PointerObject(); + const obj1 = new TestObject({ pointer: pointer1 }); + const obj2 = new TestObject({ pointer: pointer2 }); + const obj3 = new TestObject({ pointer: pointer1 }); + Parse.Object.saveAll([pointer1, pointer2, obj1, obj2, obj3]) + .then(() => { + const query = new Parse.Query(TestObject); + return query.distinct("pointer"); + }) + .then(results => { + expect(results.length).toEqual(2); + expect( + results.some(result => result.objectId === pointer1.id) + ).toEqual(true); + expect( + results.some(result => result.objectId === pointer2.id) + ).toEqual(true); + done(); + }); + } + ); - it_id('91e6cb94-2837-44b7-b057-0c4965057caa')(it)( - 'distinct class does not exist return empty', + it_id("91e6cb94-2837-44b7-b057-0c4965057caa")(it)( + "distinct class does not exist return empty", done => { const options = Object.assign({}, masterKeyOptions, { - body: { distinct: 'unknown' }, + body: { distinct: "unknown" }, }); - get(Parse.serverURL + '/aggregate/UnknownClass', options) + get(Parse.serverURL + "/aggregate/UnknownClass", options) .then(resp => { expect(resp.results.length).toBe(0); done(); @@ -1193,17 +1351,17 @@ describe('Parse.Query Aggregate testing', () => { } ); - it_id('bd15daaf-8dc7-458c-81e2-170026f4a8a7')(it)( - 'distinct field does not exist return empty', + it_id("bd15daaf-8dc7-458c-81e2-170026f4a8a7")(it)( + "distinct field does not exist return empty", done => { const options = Object.assign({}, masterKeyOptions, { - body: { distinct: 'unknown' }, + body: { distinct: "unknown" }, }); const obj = new TestObject(); obj .save() .then(() => { - return get(Parse.serverURL + '/aggregate/TestObject', options); + return get(Parse.serverURL + "/aggregate/TestObject", options); }) .then(resp => { expect(resp.results.length).toBe(0); @@ -1213,82 +1371,94 @@ describe('Parse.Query Aggregate testing', () => { } ); - it_id('21988fce-8326-425f-82f0-cd444ca3671b')(it)('distinct array', done => { + it_id("21988fce-8326-425f-82f0-cd444ca3671b")(it)("distinct array", done => { const options = Object.assign({}, masterKeyOptions, { - body: { distinct: 'size' }, + body: { distinct: "size" }, }); - get(Parse.serverURL + '/aggregate/TestObject', options) + get(Parse.serverURL + "/aggregate/TestObject", options) .then(resp => { expect(resp.results.length).toBe(3); - expect(resp.results.includes('S')).toBe(true); - expect(resp.results.includes('M')).toBe(true); - expect(resp.results.includes('L')).toBe(true); + expect(resp.results.includes("S")).toBe(true); + expect(resp.results.includes("M")).toBe(true); + expect(resp.results.includes("L")).toBe(true); done(); }) .catch(done.fail); }); - it_id('633fde06-c4af-474b-9841-3ccabc24dd4f')(it)('distinct objectId', async () => { - const query = new Parse.Query(TestObject); - const results = await query.distinct('objectId'); - expect(results.length).toBe(4); - }); + it_id("633fde06-c4af-474b-9841-3ccabc24dd4f")(it)( + "distinct objectId", + async () => { + const query = new Parse.Query(TestObject); + const results = await query.distinct("objectId"); + expect(results.length).toBe(4); + } + ); - it_id('8f9706f4-2703-42f1-b524-f2f7e72bbfe7')(it)('distinct createdAt', async () => { - const object1 = new TestObject({ createdAt_test: true }); - await object1.save(); - const object2 = new TestObject({ createdAt_test: true }); - await object2.save(); - const query = new Parse.Query(TestObject); - query.equalTo('createdAt_test', true); - const results = await query.distinct('createdAt'); - expect(results.length).toBe(2); - }); + it_id("8f9706f4-2703-42f1-b524-f2f7e72bbfe7")(it)( + "distinct createdAt", + async () => { + const object1 = new TestObject({ createdAt_test: true }); + await object1.save(); + const object2 = new TestObject({ createdAt_test: true }); + await object2.save(); + const query = new Parse.Query(TestObject); + query.equalTo("createdAt_test", true); + const results = await query.distinct("createdAt"); + expect(results.length).toBe(2); + } + ); - it_id('3562e600-8ce5-4d6d-96df-8ff969e81421')(it)('distinct updatedAt', async () => { - const object1 = new TestObject({ updatedAt_test: true }); - await object1.save(); - const object2 = new TestObject(); - await object2.save(); - object2.set('updatedAt_test', true); - await object2.save(); - const query = new Parse.Query(TestObject); - query.equalTo('updatedAt_test', true); - const results = await query.distinct('updatedAt'); - expect(results.length).toBe(2); - }); + it_id("3562e600-8ce5-4d6d-96df-8ff969e81421")(it)( + "distinct updatedAt", + async () => { + const object1 = new TestObject({ updatedAt_test: true }); + await object1.save(); + const object2 = new TestObject(); + await object2.save(); + object2.set("updatedAt_test", true); + await object2.save(); + const query = new Parse.Query(TestObject); + query.equalTo("updatedAt_test", true); + const results = await query.distinct("updatedAt"); + expect(results.length).toBe(2); + } + ); - it_id('5012cfb1-b0aa-429d-a94f-d32d8aa0b7f9')(it)('distinct null field', done => { - const options = Object.assign({}, masterKeyOptions, { - body: { distinct: 'distinctField' }, - }); - const user1 = new Parse.User(); - user1.setUsername('distinct_1'); - user1.setPassword('password'); - user1.set('distinctField', 'one'); - - const user2 = new Parse.User(); - user2.setUsername('distinct_2'); - user2.setPassword('password'); - user2.set('distinctField', null); - user1 - .signUp() - .then(() => { - return user2.signUp(); - }) - .then(() => { - return get(Parse.serverURL + '/aggregate/_User', options); - }) - .then(resp => { - expect(resp.results.length).toEqual(1); - expect(resp.results).toEqual(['one']); - done(); - }) - .catch(done.fail); - }); + it_id("5012cfb1-b0aa-429d-a94f-d32d8aa0b7f9")(it)( + "distinct null field", + done => { + const options = Object.assign({}, masterKeyOptions, { + body: { distinct: "distinctField" }, + }); + const user1 = new Parse.User(); + user1.setUsername("distinct_1"); + user1.setPassword("password"); + user1.set("distinctField", "one"); + + const user2 = new Parse.User(); + user2.setUsername("distinct_2"); + user2.setPassword("password"); + user2.set("distinctField", null); + user1 + .signUp() + .then(() => { + return user2.signUp(); + }) + .then(() => { + return get(Parse.serverURL + "/aggregate/_User", options); + }) + .then(resp => { + expect(resp.results.length).toEqual(1); + expect(resp.results).toEqual(["one"]); + done(); + }) + .catch(done.fail); + } + ); - it_id('d9c19419-e99d-4d9f-b7f3-418e49ee47dd')(it)( - 'does not return sensitive hidden properties', + it_id("d9c19419-e99d-4d9f-b7f3-418e49ee47dd")(it)( + "does not return sensitive hidden properties", done => { const options = Object.assign({}, masterKeyOptions, { body: { @@ -1300,17 +1470,17 @@ describe('Parse.Query Aggregate testing', () => { }, }); - const username = 'leaky_user'; + const username = "leaky_user"; const score = 10; const user = new Parse.User(); user.setUsername(username); - user.setPassword('password'); - user.set('score', score); + user.setPassword("password"); + user.set("score", score); user .signUp() .then(function () { - return get(Parse.serverURL + '/aggregate/_User', options); + return get(Parse.serverURL + "/aggregate/_User", options); }) .then(function (resp) { expect(resp.results.length).toBe(1); @@ -1339,54 +1509,54 @@ describe('Parse.Query Aggregate testing', () => { } ); - it_id('0a23e791-e9b5-457a-9bf9-9c5ecf406f42')(it_exclude_dbs(['postgres']))( - 'aggregate allow multiple of same stage', + it_id("0a23e791-e9b5-457a-9bf9-9c5ecf406f42")(it_exclude_dbs(["postgres"]))( + "aggregate allow multiple of same stage", async done => { await reconfigureServer({ silent: false }); 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 obj1 = new TestObject({ pointer: pointer1, name: "Hello" }); + const obj2 = new TestObject({ pointer: pointer2, name: "Hello" }); + const obj3 = new TestObject({ pointer: pointer3, name: "World" }); const options = Object.assign({}, masterKeyOptions, { body: { pipeline: [ { - $match: { name: 'Hello' }, + $match: { name: "Hello" }, }, { // Transform className$objectId to objectId and store in new field tempPointer $project: { - tempPointer: { $substr: ['$_p_pointer', 11, -1] }, // Remove TestObject$ + tempPointer: { $substr: ["$_p_pointer", 11, -1] }, // Remove TestObject$ }, }, { // Left Join, replace objectId stored in tempPointer with an actual object $lookup: { - from: 'test_TestObject', - localField: 'tempPointer', - foreignField: '_id', - as: 'tempPointer', + from: "test_TestObject", + localField: "tempPointer", + foreignField: "_id", + as: "tempPointer", }, }, { // lookup returns an array, Deconstructs an array field to objects $unwind: { - path: '$tempPointer', + path: "$tempPointer", }, }, { - $match: { 'tempPointer.value': 2 }, + $match: { "tempPointer.value": 2 }, }, ], }, }); Parse.Object.saveAll([pointer1, pointer2, pointer3, obj1, obj2, obj3]) .then(() => { - return get(Parse.serverURL + '/aggregate/TestObject', options); + return get(Parse.serverURL + "/aggregate/TestObject", options); }) .then(resp => { expect(resp.results.length).toEqual(1); @@ -1396,15 +1566,22 @@ describe('Parse.Query Aggregate testing', () => { } ); - it_only_db('mongo')('aggregate geoNear with location query', async () => { + it_only_db("mongo")("aggregate geoNear with location query", async () => { // Create geo index which is required for `geoNear` query const database = Config.get(Parse.applicationId).database; - const schema = await new Parse.Schema('GeoObject').save(); - await database.adapter.ensureIndex('GeoObject', schema, ['location'], undefined, false, { - indexType: '2dsphere', - }); + const schema = await new Parse.Schema("GeoObject").save(); + await database.adapter.ensureIndex( + "GeoObject", + schema, + ["location"], + undefined, + false, + { + indexType: "2dsphere", + } + ); // Create objects - const GeoObject = Parse.Object.extend('GeoObject'); + const GeoObject = Parse.Object.extend("GeoObject"); const obj1 = new GeoObject({ value: 1, location: new Parse.GeoPoint(1, 1), @@ -1426,12 +1603,12 @@ describe('Parse.Query Aggregate testing', () => { { $geoNear: { near: { - type: 'Point', + type: "Point", coordinates: [1, 1], }, - key: 'location', + key: "location", spherical: true, - distanceField: 'dist', + distanceField: "dist", query: { date: { $gte: new Date(2), @@ -1449,15 +1626,22 @@ describe('Parse.Query Aggregate testing', () => { await database.adapter.deleteAllClasses(false); }); - it_only_db('mongo')('aggregate geoNear with near GeoJSON point', async () => { + it_only_db("mongo")("aggregate geoNear with near GeoJSON point", async () => { // Create geo index which is required for `geoNear` query const database = Config.get(Parse.applicationId).database; - const schema = await new Parse.Schema('GeoObject').save(); - await database.adapter.ensureIndex('GeoObject', schema, ['location'], undefined, false, { - indexType: '2dsphere', - }); + const schema = await new Parse.Schema("GeoObject").save(); + await database.adapter.ensureIndex( + "GeoObject", + schema, + ["location"], + undefined, + false, + { + indexType: "2dsphere", + } + ); // Create objects - const GeoObject = Parse.Object.extend('GeoObject'); + const GeoObject = Parse.Object.extend("GeoObject"); const obj1 = new GeoObject({ value: 1, location: new Parse.GeoPoint(1, 1), @@ -1479,12 +1663,12 @@ describe('Parse.Query Aggregate testing', () => { { $geoNear: { near: { - type: 'Point', + type: "Point", coordinates: [1, 1], }, - key: 'location', + key: "location", spherical: true, - distanceField: 'dist', + distanceField: "dist", }, }, ]; @@ -1495,57 +1679,67 @@ describe('Parse.Query Aggregate testing', () => { await database.adapter.deleteAllClasses(false); }); - it_only_db('mongo')('aggregate geoNear with near legacy coordinate pair', async () => { - // Create geo index which is required for `geoNear` query - const database = Config.get(Parse.applicationId).database; - const schema = await new Parse.Schema('GeoObject').save(); - await database.adapter.ensureIndex('GeoObject', schema, ['location'], undefined, false, { - indexType: '2dsphere', - }); - // Create objects - const GeoObject = Parse.Object.extend('GeoObject'); - const obj1 = new GeoObject({ - value: 1, - location: new Parse.GeoPoint(1, 1), - date: new Date(1), - }); - const obj2 = new GeoObject({ - value: 2, - location: new Parse.GeoPoint(2, 1), - date: new Date(2), - }); - const obj3 = new GeoObject({ - value: 3, - location: new Parse.GeoPoint(3, 1), - date: new Date(3), - }); - await Parse.Object.saveAll([obj1, obj2, obj3]); - // Create query - const pipeline = [ - { - $geoNear: { - near: [1, 1], - key: 'location', - spherical: true, - distanceField: 'dist', + it_only_db("mongo")( + "aggregate geoNear with near legacy coordinate pair", + async () => { + // Create geo index which is required for `geoNear` query + const database = Config.get(Parse.applicationId).database; + const schema = await new Parse.Schema("GeoObject").save(); + await database.adapter.ensureIndex( + "GeoObject", + schema, + ["location"], + undefined, + false, + { + indexType: "2dsphere", + } + ); + // Create objects + const GeoObject = Parse.Object.extend("GeoObject"); + const obj1 = new GeoObject({ + value: 1, + location: new Parse.GeoPoint(1, 1), + date: new Date(1), + }); + const obj2 = new GeoObject({ + value: 2, + location: new Parse.GeoPoint(2, 1), + date: new Date(2), + }); + const obj3 = new GeoObject({ + value: 3, + location: new Parse.GeoPoint(3, 1), + date: new Date(3), + }); + await Parse.Object.saveAll([obj1, obj2, obj3]); + // Create query + const pipeline = [ + { + $geoNear: { + near: [1, 1], + key: "location", + spherical: true, + distanceField: "dist", + }, }, - }, - ]; - const query = new Parse.Query(GeoObject); - const results = await query.aggregate(pipeline); - // Check results - expect(results.length).toEqual(3); - await database.adapter.deleteAllClasses(false); - }); + ]; + const query = new Parse.Query(GeoObject); + const results = await query.aggregate(pipeline); + // Check results + expect(results.length).toEqual(3); + await database.adapter.deleteAllClasses(false); + } + ); - it_only_db('mongo')('aggregate handle mongodb errors', async () => { + it_only_db("mongo")("aggregate handle mongodb errors", async () => { const pipeline = [ { $search: { index: "default", text: { path: ["name"], - query: 'foo', + query: "foo", }, }, }, diff --git a/spec/ParseQuery.Comment.spec.js b/spec/ParseQuery.Comment.spec.js index 7b37f2a2c2..111e06ea39 100644 --- a/spec/ParseQuery.Comment.spec.js +++ b/spec/ParseQuery.Comment.spec.js @@ -1,17 +1,17 @@ -'use strict'; +"use strict"; -const Config = require('../lib/Config'); -const { MongoClient } = require('mongodb'); -const databaseURI = 'mongodb://localhost:27017/'; -const request = require('../lib/request'); +const Config = require("../lib/Config"); +const { MongoClient } = require("mongodb"); +const databaseURI = "mongodb://localhost:27017/"; +const request = require("../lib/request"); let config, client, database; const masterKeyHeaders = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Rest-API-Key': 'rest', - 'X-Parse-Master-Key': 'test', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": "test", + "X-Parse-Rest-API-Key": "rest", + "X-Parse-Master-Key": "test", + "Content-Type": "application/json", }; const masterKeyOptions = { @@ -20,11 +20,11 @@ const masterKeyOptions = { }; const profileLevel = 2; -describe_only_db('mongo')('Parse.Query with comment testing', () => { +describe_only_db("mongo")("Parse.Query with comment testing", () => { beforeAll(async () => { - config = Config.get('test'); + config = Config.get("test"); client = await MongoClient.connect(databaseURI); - database = client.db('parseServerMongoAdapterTestDatabase'); + database = client.db("parseServerMongoAdapterTestDatabase"); let profiler = await database.command({ profile: 0 }); expect(profiler.was).toEqual(0); // console.log(`Disabling profiler : ${profiler.was}`); @@ -45,62 +45,76 @@ describe_only_db('mongo')('Parse.Query with comment testing', () => { await client.close(); }); - it('send comment with query through REST', async () => { - const comment = 'Hello Parse'; + it("send comment with query through REST", async () => { + const comment = "Hello Parse"; const object = new TestObject(); - object.set('name', 'object'); + object.set("name", "object"); await object.save(); const options = Object.assign({}, masterKeyOptions, { - url: Parse.serverURL + '/classes/TestObject', + url: Parse.serverURL + "/classes/TestObject", qs: { explain: true, comment: comment, }, }); await request(options); - const result = await database.collection('system.profile').findOne({}, { sort: { ts: -1 } }); + const result = await database + .collection("system.profile") + .findOne({}, { sort: { ts: -1 } }); expect(result.command.explain.comment).toBe(comment); }); - it('send comment with query', async () => { - const comment = 'Hello Parse'; + it("send comment with query", async () => { + const comment = "Hello Parse"; const object = new TestObject(); - object.set('name', 'object'); + object.set("name", "object"); await object.save(); - const collection = await config.database.adapter._adaptiveCollection('TestObject'); - await collection._rawFind({ name: 'object' }, { comment: comment }); - const result = await database.collection('system.profile').findOne({}, { sort: { ts: -1 } }); + const collection = + await config.database.adapter._adaptiveCollection("TestObject"); + await collection._rawFind({ name: "object" }, { comment: comment }); + const result = await database + .collection("system.profile") + .findOne({}, { sort: { ts: -1 } }); expect(result.command.comment).toBe(comment); }); - it('send a comment with a count query', async () => { - const comment = 'Hello Parse'; + it("send a comment with a count query", async () => { + const comment = "Hello Parse"; const object = new TestObject(); - object.set('name', 'object'); + object.set("name", "object"); await object.save(); const object2 = new TestObject(); - object2.set('name', 'object'); + object2.set("name", "object"); await object2.save(); - const collection = await config.database.adapter._adaptiveCollection('TestObject'); - const countResult = await collection.count({ name: 'object' }, { comment: comment }); + const collection = + await config.database.adapter._adaptiveCollection("TestObject"); + const countResult = await collection.count( + { name: "object" }, + { comment: comment } + ); expect(countResult).toEqual(2); - const result = await database.collection('system.profile').findOne({}, { sort: { ts: -1 } }); + const result = await database + .collection("system.profile") + .findOne({}, { sort: { ts: -1 } }); expect(result.command.comment).toBe(comment); }); - it('attach a comment to an aggregation', async () => { - const comment = 'Hello Parse'; + it("attach a comment to an aggregation", async () => { + const comment = "Hello Parse"; const object = new TestObject(); - object.set('name', 'object'); + object.set("name", "object"); await object.save(); - const collection = await config.database.adapter._adaptiveCollection('TestObject'); - await collection.aggregate([{ $group: { _id: '$name' } }], { + const collection = + await config.database.adapter._adaptiveCollection("TestObject"); + await collection.aggregate([{ $group: { _id: "$name" } }], { explain: true, comment: comment, }); - const result = await database.collection('system.profile').findOne({}, { sort: { ts: -1 } }); + const result = await database + .collection("system.profile") + .findOne({}, { sort: { ts: -1 } }); expect(result.command.explain.comment).toBe(comment); }); }); diff --git a/spec/ParseQuery.FullTextSearch.spec.js b/spec/ParseQuery.FullTextSearch.spec.js index d11d1ba86a..4443e1d500 100644 --- a/spec/ParseQuery.FullTextSearch.spec.js +++ b/spec/ParseQuery.FullTextSearch.spec.js @@ -1,330 +1,379 @@ -'use strict'; +"use strict"; -const Config = require('../lib/Config'); -const Parse = require('parse/node'); -const request = require('../lib/request'); +const Config = require("../lib/Config"); +const Parse = require("parse/node"); +const request = require("../lib/request"); const fullTextHelper = async () => { const subjects = [ - 'coffee', - 'Coffee Shopping', - 'Baking a cake', - 'baking', - 'Café Con Leche', - 'Сырники', - 'coffee and cream', - 'Cafe con Leche', + "coffee", + "Coffee Shopping", + "Baking a cake", + "baking", + "Café Con Leche", + "Сырники", + "coffee and cream", + "Cafe con Leche", ]; await Parse.Object.saveAll( - subjects.map(subject => new Parse.Object('TestObject').set({ subject, comment: subject })) + subjects.map(subject => + new Parse.Object("TestObject").set({ subject, comment: subject }) + ) ); }; -describe('Parse.Query Full Text Search testing', () => { - it_id('77ba6779-6584-4e09-8e7e-31f89e741d6a')(it)('fullTextSearch: $search', async () => { - await fullTextHelper(); - const query = new Parse.Query('TestObject'); - query.fullText('subject', 'coffee'); - const results = await query.find(); - expect(results.length).toBe(3); - }); +describe("Parse.Query Full Text Search testing", () => { + it_id("77ba6779-6584-4e09-8e7e-31f89e741d6a")(it)( + "fullTextSearch: $search", + async () => { + await fullTextHelper(); + const query = new Parse.Query("TestObject"); + query.fullText("subject", "coffee"); + const results = await query.find(); + expect(results.length).toBe(3); + } + ); - it_id('d1992ea6-6d92-4bfa-a487-2a49fbcf8f0d')(it)('fullTextSearch: $search, sort', async () => { - await fullTextHelper(); - const query = new Parse.Query('TestObject'); - query.fullText('subject', 'coffee'); - query.select('$score'); - query.ascending('$score'); - const results = await query.find(); - expect(results.length).toBe(3); - expect(results[0].get('score')); - expect(results[1].get('score')); - expect(results[2].get('score')); - }); + it_id("d1992ea6-6d92-4bfa-a487-2a49fbcf8f0d")(it)( + "fullTextSearch: $search, sort", + async () => { + await fullTextHelper(); + const query = new Parse.Query("TestObject"); + query.fullText("subject", "coffee"); + query.select("$score"); + query.ascending("$score"); + const results = await query.find(); + expect(results.length).toBe(3); + expect(results[0].get("score")); + expect(results[1].get("score")); + expect(results[2].get("score")); + } + ); - it_id('07172595-50de-4be2-984a-d3136bebb22e')(it)('fulltext descending by $score', async () => { - await fullTextHelper(); - const query = new Parse.Query('TestObject'); - query.fullText('subject', 'coffee'); - query.descending('$score'); - query.select('$score'); - const [first, second, third] = await query.find(); - expect(first).toBeDefined(); - expect(second).toBeDefined(); - expect(third).toBeDefined(); - expect(first.get('score')); - expect(second.get('score')); - expect(third.get('score')); - expect(first.get('score') >= second.get('score')).toBeTrue(); - expect(second.get('score') >= third.get('score')).toBeTrue(); - }); + it_id("07172595-50de-4be2-984a-d3136bebb22e")(it)( + "fulltext descending by $score", + async () => { + await fullTextHelper(); + const query = new Parse.Query("TestObject"); + query.fullText("subject", "coffee"); + query.descending("$score"); + query.select("$score"); + const [first, second, third] = await query.find(); + expect(first).toBeDefined(); + expect(second).toBeDefined(); + expect(third).toBeDefined(); + expect(first.get("score")); + expect(second.get("score")); + expect(third.get("score")); + expect(first.get("score") >= second.get("score")).toBeTrue(); + expect(second.get("score") >= third.get("score")).toBeTrue(); + } + ); - it_id('8e821973-3fae-4e7c-8152-766228a18cdd')(it)('fullTextSearch: $language', async () => { - await fullTextHelper(); - const query = new Parse.Query('TestObject'); - query.fullText('subject', 'leche', { language: 'spanish' }); - const resp = await query.find(); - expect(resp.length).toBe(2); - }); + it_id("8e821973-3fae-4e7c-8152-766228a18cdd")(it)( + "fullTextSearch: $language", + async () => { + await fullTextHelper(); + const query = new Parse.Query("TestObject"); + query.fullText("subject", "leche", { language: "spanish" }); + const resp = await query.find(); + expect(resp.length).toBe(2); + } + ); - it_id('7d3da216-9582-40ee-a2fe-8316feaf5c0c')(it)('fullTextSearch: $diacriticSensitive', async () => { - await fullTextHelper(); - const query = new Parse.Query('TestObject'); - query.fullText('subject', 'CAFÉ', { diacriticSensitive: true }); - const resp = await query.find(); - expect(resp.length).toBe(1); - }); + it_id("7d3da216-9582-40ee-a2fe-8316feaf5c0c")(it)( + "fullTextSearch: $diacriticSensitive", + async () => { + await fullTextHelper(); + const query = new Parse.Query("TestObject"); + query.fullText("subject", "CAFÉ", { diacriticSensitive: true }); + const resp = await query.find(); + expect(resp.length).toBe(1); + } + ); - it_id('dade10c8-2b9c-4f43-bb3f-a13bbd82ac22')(it)('fullTextSearch: $search, invalid input', async () => { - await fullTextHelper(); - const invalidQuery = async () => { - const where = { - subject: { - $text: { - $search: true, + it_id("dade10c8-2b9c-4f43-bb3f-a13bbd82ac22")(it)( + "fullTextSearch: $search, invalid input", + async () => { + await fullTextHelper(); + const invalidQuery = async () => { + const where = { + subject: { + $text: { + $search: true, + }, }, - }, + }; + try { + await request({ + method: "POST", + url: "http://localhost:8378/1/classes/TestObject", + body: { where, _method: "GET" }, + headers: { + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", + }, + }); + } catch (e) { + throw new Parse.Error(e.data.code, e.data.error); + } }; - try { - await request({ - method: 'POST', - url: 'http://localhost:8378/1/classes/TestObject', - body: { where, _method: 'GET' }, - headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', - }, - }); - } catch (e) { - throw new Parse.Error(e.data.code, e.data.error); - } - }; - await expectAsync(invalidQuery()).toBeRejectedWith( - new Parse.Error(Parse.Error.INVALID_JSON, 'bad $text: $search, should be object') - ); - }); + await expectAsync(invalidQuery()).toBeRejectedWith( + new Parse.Error( + Parse.Error.INVALID_JSON, + "bad $text: $search, should be object" + ) + ); + } + ); - it_id('ff7c6b1c-4712-4847-bb76-f4e1f641f7b5')(it)('fullTextSearch: $language, invalid input', async () => { - await fullTextHelper(); - const query = new Parse.Query('TestObject'); - query.fullText('subject', 'leche', { language: true }); - await expectAsync(query.find()).toBeRejectedWith( - new Parse.Error(Parse.Error.INVALID_JSON, 'bad $text: $language, should be string') - ); - }); + it_id("ff7c6b1c-4712-4847-bb76-f4e1f641f7b5")(it)( + "fullTextSearch: $language, invalid input", + async () => { + await fullTextHelper(); + const query = new Parse.Query("TestObject"); + query.fullText("subject", "leche", { language: true }); + await expectAsync(query.find()).toBeRejectedWith( + new Parse.Error( + Parse.Error.INVALID_JSON, + "bad $text: $language, should be string" + ) + ); + } + ); - it_id('de262dbc-ec75-4ec6-9217-fbb90146c272')(it)('fullTextSearch: $caseSensitive, invalid input', async () => { - await fullTextHelper(); - const query = new Parse.Query('TestObject'); - query.fullText('subject', 'leche', { caseSensitive: 'string' }); - await expectAsync(query.find()).toBeRejectedWith( - new Parse.Error(Parse.Error.INVALID_JSON, 'bad $text: $caseSensitive, should be boolean') - ); - }); + it_id("de262dbc-ec75-4ec6-9217-fbb90146c272")(it)( + "fullTextSearch: $caseSensitive, invalid input", + async () => { + await fullTextHelper(); + const query = new Parse.Query("TestObject"); + query.fullText("subject", "leche", { caseSensitive: "string" }); + await expectAsync(query.find()).toBeRejectedWith( + new Parse.Error( + Parse.Error.INVALID_JSON, + "bad $text: $caseSensitive, should be boolean" + ) + ); + } + ); - it_id('b7b7b3a9-8d6c-4f98-a0ff-0113593d06d4')(it)('fullTextSearch: $diacriticSensitive, invalid input', async () => { - await fullTextHelper(); - const query = new Parse.Query('TestObject'); - query.fullText('subject', 'leche', { diacriticSensitive: 'string' }); - await expectAsync(query.find()).toBeRejectedWith( - new Parse.Error(Parse.Error.INVALID_JSON, 'bad $text: $diacriticSensitive, should be boolean') - ); - }); + it_id("b7b7b3a9-8d6c-4f98-a0ff-0113593d06d4")(it)( + "fullTextSearch: $diacriticSensitive, invalid input", + async () => { + await fullTextHelper(); + const query = new Parse.Query("TestObject"); + query.fullText("subject", "leche", { diacriticSensitive: "string" }); + await expectAsync(query.find()).toBeRejectedWith( + new Parse.Error( + Parse.Error.INVALID_JSON, + "bad $text: $diacriticSensitive, should be boolean" + ) + ); + } + ); }); -describe_only_db('mongo')('[mongodb] Parse.Query Full Text Search testing', () => { - it('fullTextSearch: does not create text index if compound index exist', async () => { - await fullTextHelper(); - await databaseAdapter.dropAllIndexes('TestObject'); - let indexes = await databaseAdapter.getIndexes('TestObject'); - expect(indexes.length).toEqual(1); - await databaseAdapter.createIndex('TestObject', { - subject: 'text', - comment: 'text', - }); - indexes = await databaseAdapter.getIndexes('TestObject'); - const query = new Parse.Query('TestObject'); - query.fullText('subject', 'coffee'); - query.select('$score'); - query.ascending('$score'); - const results = await query.find(); - expect(results.length).toBe(3); - expect(results[0].get('score')); - expect(results[1].get('score')); - expect(results[2].get('score')); +describe_only_db("mongo")( + "[mongodb] Parse.Query Full Text Search testing", + () => { + it("fullTextSearch: does not create text index if compound index exist", async () => { + await fullTextHelper(); + await databaseAdapter.dropAllIndexes("TestObject"); + let indexes = await databaseAdapter.getIndexes("TestObject"); + expect(indexes.length).toEqual(1); + await databaseAdapter.createIndex("TestObject", { + subject: "text", + comment: "text", + }); + indexes = await databaseAdapter.getIndexes("TestObject"); + const query = new Parse.Query("TestObject"); + query.fullText("subject", "coffee"); + query.select("$score"); + query.ascending("$score"); + const results = await query.find(); + expect(results.length).toBe(3); + expect(results[0].get("score")); + expect(results[1].get("score")); + expect(results[2].get("score")); - indexes = await databaseAdapter.getIndexes('TestObject'); - expect(indexes.length).toEqual(2); + indexes = await databaseAdapter.getIndexes("TestObject"); + expect(indexes.length).toEqual(2); - const schemas = await new Parse.Schema('TestObject').get(); - expect(schemas.indexes._id_).toBeDefined(); - expect(schemas.indexes._id_._id).toEqual(1); - expect(schemas.indexes.subject_text_comment_text).toBeDefined(); - expect(schemas.indexes.subject_text_comment_text.subject).toEqual('text'); - expect(schemas.indexes.subject_text_comment_text.comment).toEqual('text'); - }); + const schemas = await new Parse.Schema("TestObject").get(); + expect(schemas.indexes._id_).toBeDefined(); + expect(schemas.indexes._id_._id).toEqual(1); + expect(schemas.indexes.subject_text_comment_text).toBeDefined(); + expect(schemas.indexes.subject_text_comment_text.subject).toEqual("text"); + expect(schemas.indexes.subject_text_comment_text.comment).toEqual("text"); + }); - it('fullTextSearch: does not create text index if schema compound index exist', done => { - fullTextHelper() - .then(() => { - return databaseAdapter.dropAllIndexes('TestObject'); - }) - .then(() => { - return databaseAdapter.getIndexes('TestObject'); - }) - .then(indexes => { - expect(indexes.length).toEqual(1); - return request({ - method: 'PUT', - url: 'http://localhost:8378/1/schemas/TestObject', - headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Master-Key': 'test', - 'Content-Type': 'application/json', - }, - body: { - indexes: { - text_test: { subject: 'text', comment: 'text' }, + it("fullTextSearch: does not create text index if schema compound index exist", done => { + fullTextHelper() + .then(() => { + return databaseAdapter.dropAllIndexes("TestObject"); + }) + .then(() => { + return databaseAdapter.getIndexes("TestObject"); + }) + .then(indexes => { + expect(indexes.length).toEqual(1); + return request({ + method: "PUT", + url: "http://localhost:8378/1/schemas/TestObject", + headers: { + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "X-Parse-Master-Key": "test", + "Content-Type": "application/json", }, - }, - }); - }) - .then(() => { - return databaseAdapter.getIndexes('TestObject'); - }) - .then(indexes => { - expect(indexes.length).toEqual(2); - const where = { - subject: { - $text: { - $search: { - $term: 'coffee', + body: { + indexes: { + text_test: { subject: "text", comment: "text" }, }, }, - }, - }; - return request({ - method: 'POST', - url: 'http://localhost:8378/1/classes/TestObject', - body: { where, _method: 'GET' }, - headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', - }, - }); - }) - .then(resp => { - expect(resp.data.results.length).toEqual(3); - return databaseAdapter.getIndexes('TestObject'); - }) - .then(indexes => { - expect(indexes.length).toEqual(2); - request({ - url: 'http://localhost:8378/1/schemas/TestObject', - headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key': 'test', - 'Content-Type': 'application/json', - }, - }).then(response => { - const body = response.data; - expect(body.indexes._id_).toBeDefined(); - expect(body.indexes._id_._id).toEqual(1); - expect(body.indexes.text_test).toBeDefined(); - expect(body.indexes.text_test.subject).toEqual('text'); - expect(body.indexes.text_test.comment).toEqual('text'); - done(); - }); - }) - .catch(done.fail); - }); + }); + }) + .then(() => { + return databaseAdapter.getIndexes("TestObject"); + }) + .then(indexes => { + expect(indexes.length).toEqual(2); + const where = { + subject: { + $text: { + $search: { + $term: "coffee", + }, + }, + }, + }; + return request({ + method: "POST", + url: "http://localhost:8378/1/classes/TestObject", + body: { where, _method: "GET" }, + headers: { + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", + }, + }); + }) + .then(resp => { + expect(resp.data.results.length).toEqual(3); + return databaseAdapter.getIndexes("TestObject"); + }) + .then(indexes => { + expect(indexes.length).toEqual(2); + request({ + url: "http://localhost:8378/1/schemas/TestObject", + headers: { + "X-Parse-Application-Id": "test", + "X-Parse-Master-Key": "test", + "Content-Type": "application/json", + }, + }).then(response => { + const body = response.data; + expect(body.indexes._id_).toBeDefined(); + expect(body.indexes._id_._id).toEqual(1); + expect(body.indexes.text_test).toBeDefined(); + expect(body.indexes.text_test.subject).toEqual("text"); + expect(body.indexes.text_test.comment).toEqual("text"); + done(); + }); + }) + .catch(done.fail); + }); - it('fullTextSearch: $diacriticSensitive - false', async () => { - await fullTextHelper(); - const query = new Parse.Query('TestObject'); - query.fullText('subject', 'CAFÉ', { diacriticSensitive: false }); - const resp = await query.find(); - expect(resp.length).toBe(2); - }); + it("fullTextSearch: $diacriticSensitive - false", async () => { + await fullTextHelper(); + const query = new Parse.Query("TestObject"); + query.fullText("subject", "CAFÉ", { diacriticSensitive: false }); + const resp = await query.find(); + expect(resp.length).toBe(2); + }); - it('fullTextSearch: $caseSensitive', async () => { - await fullTextHelper(); - const query = new Parse.Query('TestObject'); - query.fullText('subject', 'Coffee', { caseSensitive: true }); - const results = await query.find(); - expect(results.length).toBe(1); - }); -}); + it("fullTextSearch: $caseSensitive", async () => { + await fullTextHelper(); + const query = new Parse.Query("TestObject"); + query.fullText("subject", "Coffee", { caseSensitive: true }); + const results = await query.find(); + expect(results.length).toBe(1); + }); + } +); -describe_only_db('postgres')('[postgres] Parse.Query Full Text Search testing', () => { - it('fullTextSearch: $diacriticSensitive - false', done => { - fullTextHelper() - .then(() => { - const where = { - subject: { - $text: { - $search: { - $term: 'CAFÉ', - $diacriticSensitive: false, +describe_only_db("postgres")( + "[postgres] Parse.Query Full Text Search testing", + () => { + it("fullTextSearch: $diacriticSensitive - false", done => { + fullTextHelper() + .then(() => { + const where = { + subject: { + $text: { + $search: { + $term: "CAFÉ", + $diacriticSensitive: false, + }, }, }, - }, - }; - return request({ - method: 'POST', - url: 'http://localhost:8378/1/classes/TestObject', - body: { where, _method: 'GET' }, - headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', - }, + }; + return request({ + method: "POST", + url: "http://localhost:8378/1/classes/TestObject", + body: { where, _method: "GET" }, + headers: { + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", + }, + }); + }) + .then(resp => { + fail( + `$diacriticSensitive - false should not supported: ${JSON.stringify(resp)}` + ); + done(); + }) + .catch(err => { + expect(err.data.code).toEqual(Parse.Error.INVALID_JSON); + done(); }); - }) - .then(resp => { - fail(`$diacriticSensitive - false should not supported: ${JSON.stringify(resp)}`); - done(); - }) - .catch(err => { - expect(err.data.code).toEqual(Parse.Error.INVALID_JSON); - done(); - }); - }); + }); - it('fullTextSearch: $caseSensitive', done => { - fullTextHelper() - .then(() => { - const where = { - subject: { - $text: { - $search: { - $term: 'Coffee', - $caseSensitive: true, + it("fullTextSearch: $caseSensitive", done => { + fullTextHelper() + .then(() => { + const where = { + subject: { + $text: { + $search: { + $term: "Coffee", + $caseSensitive: true, + }, }, }, - }, - }; - return request({ - method: 'POST', - url: 'http://localhost:8378/1/classes/TestObject', - body: { where, _method: 'GET' }, - headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', - }, + }; + return request({ + method: "POST", + url: "http://localhost:8378/1/classes/TestObject", + body: { where, _method: "GET" }, + headers: { + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", + }, + }); + }) + .then(resp => { + fail(`$caseSensitive should not supported: ${JSON.stringify(resp)}`); + done(); + }) + .catch(err => { + expect(err.data.code).toEqual(Parse.Error.INVALID_JSON); + done(); }); - }) - .then(resp => { - fail(`$caseSensitive should not supported: ${JSON.stringify(resp)}`); - done(); - }) - .catch(err => { - expect(err.data.code).toEqual(Parse.Error.INVALID_JSON); - done(); - }); - }); -}); + }); + } +); diff --git a/spec/ParseQuery.hint.spec.js b/spec/ParseQuery.hint.spec.js index 0905eb7d32..d349c730e3 100644 --- a/spec/ParseQuery.hint.spec.js +++ b/spec/ParseQuery.hint.spec.js @@ -1,16 +1,16 @@ -'use strict'; +"use strict"; -const Config = require('../lib/Config'); -const TestUtils = require('../lib/TestUtils'); -const request = require('../lib/request'); +const Config = require("../lib/Config"); +const TestUtils = require("../lib/TestUtils"); +const request = require("../lib/request"); let config; const masterKeyHeaders = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Rest-API-Key': 'rest', - 'X-Parse-Master-Key': 'test', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": "test", + "X-Parse-Rest-API-Key": "rest", + "X-Parse-Master-Key": "test", + "Content-Type": "application/json", }; const masterKeyOptions = { @@ -18,253 +18,363 @@ const masterKeyOptions = { json: true, }; -describe_only_db('mongo')('Parse.Query hint', () => { +describe_only_db("mongo")("Parse.Query hint", () => { beforeEach(() => { - config = Config.get('test'); + config = Config.get("test"); }); afterEach(async () => { await TestUtils.destroyAllDataPermanently(false); }); - it_only_mongodb_version('<5.1 || >=6 <8')('query find with hint string', async () => { + it_only_mongodb_version("<5.1 || >=6 <8")( + "query find with hint string", + async () => { + const object = new TestObject(); + await object.save(); + + const collection = + await config.database.adapter._adaptiveCollection("TestObject"); + let explain = await collection._rawFind( + { _id: object.id }, + { explain: true } + ); + expect(explain.queryPlanner.winningPlan.stage).toBe("IDHACK"); + explain = await collection._rawFind( + { _id: object.id }, + { hint: "_id_", explain: true } + ); + expect(explain.queryPlanner.winningPlan.stage).toBe("FETCH"); + expect(explain.queryPlanner.winningPlan.inputStage.indexName).toBe( + "_id_" + ); + } + ); + + it_only_mongodb_version(">=8")("query find with hint string", async () => { const object = new TestObject(); await object.save(); - const collection = await config.database.adapter._adaptiveCollection('TestObject'); - let explain = await collection._rawFind({ _id: object.id }, { explain: true }); - expect(explain.queryPlanner.winningPlan.stage).toBe('IDHACK'); - explain = await collection._rawFind({ _id: object.id }, { hint: '_id_', explain: true }); - expect(explain.queryPlanner.winningPlan.stage).toBe('FETCH'); - expect(explain.queryPlanner.winningPlan.inputStage.indexName).toBe('_id_'); + const collection = + await config.database.adapter._adaptiveCollection("TestObject"); + let explain = await collection._rawFind( + { _id: object.id }, + { explain: true } + ); + expect(explain.queryPlanner.winningPlan.stage).toBe("EXPRESS_IXSCAN"); + explain = await collection._rawFind( + { _id: object.id }, + { hint: "_id_", explain: true } + ); + expect(explain.queryPlanner.winningPlan.stage).toBe("FETCH"); + expect(explain.queryPlanner.winningPlan.inputStage.indexName).toBe("_id_"); }); - it_only_mongodb_version('>=8')('query find with hint string', async () => { + it_only_mongodb_version("<5.1 || >=6 <8")( + "query find with hint object", + async () => { + const object = new TestObject(); + await object.save(); + + const collection = + await config.database.adapter._adaptiveCollection("TestObject"); + let explain = await collection._rawFind( + { _id: object.id }, + { explain: true } + ); + expect(explain.queryPlanner.winningPlan.stage).toBe("IDHACK"); + explain = await collection._rawFind( + { _id: object.id }, + { hint: { _id: 1 }, explain: true } + ); + expect(explain.queryPlanner.winningPlan.stage).toBe("FETCH"); + expect(explain.queryPlanner.winningPlan.inputStage.keyPattern).toEqual({ + _id: 1, + }); + } + ); + + it_only_mongodb_version(">=8")("query find with hint object", async () => { const object = new TestObject(); await object.save(); - const collection = await config.database.adapter._adaptiveCollection('TestObject'); - let explain = await collection._rawFind({ _id: object.id }, { explain: true }); - expect(explain.queryPlanner.winningPlan.stage).toBe('EXPRESS_IXSCAN'); - explain = await collection._rawFind({ _id: object.id }, { hint: '_id_', explain: true }); - expect(explain.queryPlanner.winningPlan.stage).toBe('FETCH'); - expect(explain.queryPlanner.winningPlan.inputStage.indexName).toBe('_id_'); - }); - - it_only_mongodb_version('<5.1 || >=6 <8')('query find with hint object', async () => { - const object = new TestObject(); - await object.save(); - - const collection = await config.database.adapter._adaptiveCollection('TestObject'); - let explain = await collection._rawFind({ _id: object.id }, { explain: true }); - expect(explain.queryPlanner.winningPlan.stage).toBe('IDHACK'); - explain = await collection._rawFind({ _id: object.id }, { hint: { _id: 1 }, explain: true }); - expect(explain.queryPlanner.winningPlan.stage).toBe('FETCH'); - expect(explain.queryPlanner.winningPlan.inputStage.keyPattern).toEqual({ - _id: 1, - }); - }); - - it_only_mongodb_version('>=8')('query find with hint object', async () => { - const object = new TestObject(); - await object.save(); - - const collection = await config.database.adapter._adaptiveCollection('TestObject'); - let explain = await collection._rawFind({ _id: object.id }, { explain: true }); - expect(explain.queryPlanner.winningPlan.stage).toBe('EXPRESS_IXSCAN'); - explain = await collection._rawFind({ _id: object.id }, { hint: { _id: 1 }, explain: true }); - expect(explain.queryPlanner.winningPlan.stage).toBe('FETCH'); + const collection = + await config.database.adapter._adaptiveCollection("TestObject"); + let explain = await collection._rawFind( + { _id: object.id }, + { explain: true } + ); + expect(explain.queryPlanner.winningPlan.stage).toBe("EXPRESS_IXSCAN"); + explain = await collection._rawFind( + { _id: object.id }, + { hint: { _id: 1 }, explain: true } + ); + expect(explain.queryPlanner.winningPlan.stage).toBe("FETCH"); expect(explain.queryPlanner.winningPlan.inputStage.keyPattern).toEqual({ _id: 1, }); }); - it_only_mongodb_version('<7')('query aggregate with hint string', async () => { - const object = new TestObject({ foo: 'bar' }); - await object.save(); - - const collection = await config.database.adapter._adaptiveCollection('TestObject'); - let result = await collection.aggregate([{ $group: { _id: '$foo' } }], { - explain: true, - }); - let queryPlanner = result[0].stages[0].$cursor.queryPlanner; - expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); - expect(queryPlanner.winningPlan.inputStage.stage).toBe('COLLSCAN'); - expect(queryPlanner.winningPlan.inputStage.inputStage).toBeUndefined(); - - result = await collection.aggregate([{ $group: { _id: '$foo' } }], { - hint: '_id_', - explain: true, - }); - queryPlanner = result[0].stages[0].$cursor.queryPlanner; - expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); - expect(queryPlanner.winningPlan.inputStage.stage).toBe('FETCH'); - expect(queryPlanner.winningPlan.inputStage.inputStage.stage).toBe('IXSCAN'); - expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); - }); - - it_only_mongodb_version('>=7')('query aggregate with hint string', async () => { - const object = new TestObject({ foo: 'bar' }); - await object.save(); - - const collection = await config.database.adapter._adaptiveCollection('TestObject'); - let result = await collection.aggregate([{ $group: { _id: '$foo' } }], { - explain: true, - }); - let queryPlanner = result[0].queryPlanner; - expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); - expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('COLLSCAN'); - expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage).toBeUndefined(); - - result = await collection.aggregate([{ $group: { _id: '$foo' } }], { - hint: '_id_', - explain: true, - }); - queryPlanner = result[0].queryPlanner; - expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); - expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('FETCH'); - expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.stage).toBe('IXSCAN'); - expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName).toBe('_id_'); - }); - - it_only_mongodb_version('<7')('query aggregate with hint object', async () => { - const object = new TestObject({ foo: 'bar' }); - await object.save(); - - const collection = await config.database.adapter._adaptiveCollection('TestObject'); - let result = await collection.aggregate([{ $group: { _id: '$foo' } }], { - explain: true, - }); - let queryPlanner = result[0].stages[0].$cursor.queryPlanner; - expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); - expect(queryPlanner.winningPlan.inputStage.stage).toBe('COLLSCAN'); - expect(queryPlanner.winningPlan.inputStage.inputStage).toBeUndefined(); - - result = await collection.aggregate([{ $group: { _id: '$foo' } }], { - hint: { _id: 1 }, - explain: true, - }); - queryPlanner = result[0].stages[0].$cursor.queryPlanner; - expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); - expect(queryPlanner.winningPlan.inputStage.stage).toBe('FETCH'); - expect(queryPlanner.winningPlan.inputStage.inputStage.stage).toBe('IXSCAN'); - expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); - expect(queryPlanner.winningPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); - }); - - it_only_mongodb_version('>=7')('query aggregate with hint object', async () => { - const object = new TestObject({ foo: 'bar' }); - await object.save(); - - const collection = await config.database.adapter._adaptiveCollection('TestObject'); - let result = await collection.aggregate([{ $group: { _id: '$foo' } }], { - explain: true, - }); - let queryPlanner = result[0].queryPlanner; - expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); - expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('COLLSCAN'); - expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage).toBeUndefined(); - - result = await collection.aggregate([{ $group: { _id: '$foo' } }], { - hint: { _id: 1 }, - explain: true, - }); - queryPlanner = result[0].queryPlanner; - expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); - expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('FETCH'); - expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.stage).toBe('IXSCAN'); - expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName).toBe('_id_'); - expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); - }); + it_only_mongodb_version("<7")( + "query aggregate with hint string", + async () => { + const object = new TestObject({ foo: "bar" }); + await object.save(); - it_only_mongodb_version('<5.1 || >=6')('query find with hint (rest)', async () => { - const object = new TestObject(); - await object.save(); - let options = Object.assign({}, masterKeyOptions, { - url: Parse.serverURL + '/classes/TestObject', - qs: { + const collection = + await config.database.adapter._adaptiveCollection("TestObject"); + let result = await collection.aggregate([{ $group: { _id: "$foo" } }], { explain: true, - }, - }); - let response = await request(options); - let explain = response.data.results; - expect(explain.queryPlanner.winningPlan.inputStage.stage).toBe('COLLSCAN'); - - options = Object.assign({}, masterKeyOptions, { - url: Parse.serverURL + '/classes/TestObject', - qs: { + }); + let queryPlanner = result[0].stages[0].$cursor.queryPlanner; + expect(queryPlanner.winningPlan.stage).toBe("PROJECTION_SIMPLE"); + expect(queryPlanner.winningPlan.inputStage.stage).toBe("COLLSCAN"); + expect(queryPlanner.winningPlan.inputStage.inputStage).toBeUndefined(); + + result = await collection.aggregate([{ $group: { _id: "$foo" } }], { + hint: "_id_", explain: true, - hint: '_id_', - }, - }); - response = await request(options); - explain = response.data.results; - expect(explain.queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); - }); - - it_only_mongodb_version('<7')('query aggregate with hint (rest)', async () => { - const object = new TestObject({ foo: 'bar' }); - await object.save(); - let options = Object.assign({}, masterKeyOptions, { - url: Parse.serverURL + '/aggregate/TestObject', - qs: { + }); + queryPlanner = result[0].stages[0].$cursor.queryPlanner; + expect(queryPlanner.winningPlan.stage).toBe("PROJECTION_SIMPLE"); + expect(queryPlanner.winningPlan.inputStage.stage).toBe("FETCH"); + expect(queryPlanner.winningPlan.inputStage.inputStage.stage).toBe( + "IXSCAN" + ); + expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe( + "_id_" + ); + } + ); + + it_only_mongodb_version(">=7")( + "query aggregate with hint string", + async () => { + const object = new TestObject({ foo: "bar" }); + await object.save(); + + const collection = + await config.database.adapter._adaptiveCollection("TestObject"); + let result = await collection.aggregate([{ $group: { _id: "$foo" } }], { explain: true, - $group: JSON.stringify({ _id: '$foo' }), - }, - }); - let response = await request(options); - let queryPlanner = response.data.results[0].stages[0].$cursor.queryPlanner; - expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); - expect(queryPlanner.winningPlan.inputStage.stage).toBe('COLLSCAN'); - expect(queryPlanner.winningPlan.inputStage.inputStage).toBeUndefined(); - - options = Object.assign({}, masterKeyOptions, { - url: Parse.serverURL + '/aggregate/TestObject', - qs: { + }); + let queryPlanner = result[0].queryPlanner; + expect(queryPlanner.winningPlan.queryPlan.stage).toBe("GROUP"); + expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe( + "COLLSCAN" + ); + expect( + queryPlanner.winningPlan.queryPlan.inputStage.inputStage + ).toBeUndefined(); + + result = await collection.aggregate([{ $group: { _id: "$foo" } }], { + hint: "_id_", explain: true, - hint: '_id_', - $group: JSON.stringify({ _id: '$foo' }), - }, - }); - response = await request(options); - queryPlanner = response.data.results[0].stages[0].$cursor.queryPlanner; - expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); - expect(queryPlanner.winningPlan.inputStage.stage).toBe('FETCH'); - expect(queryPlanner.winningPlan.inputStage.inputStage.stage).toBe('IXSCAN'); - expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); - expect(queryPlanner.winningPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); - }); - - it_only_mongodb_version('>=7')('query aggregate with hint (rest)', async () => { - const object = new TestObject({ foo: 'bar' }); - await object.save(); - let options = Object.assign({}, masterKeyOptions, { - url: Parse.serverURL + '/aggregate/TestObject', - qs: { + }); + queryPlanner = result[0].queryPlanner; + expect(queryPlanner.winningPlan.queryPlan.stage).toBe("GROUP"); + expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe("FETCH"); + expect( + queryPlanner.winningPlan.queryPlan.inputStage.inputStage.stage + ).toBe("IXSCAN"); + expect( + queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName + ).toBe("_id_"); + } + ); + + it_only_mongodb_version("<7")( + "query aggregate with hint object", + async () => { + const object = new TestObject({ foo: "bar" }); + await object.save(); + + const collection = + await config.database.adapter._adaptiveCollection("TestObject"); + let result = await collection.aggregate([{ $group: { _id: "$foo" } }], { explain: true, - $group: JSON.stringify({ _id: '$foo' }), - }, - }); - let response = await request(options); - let queryPlanner = response.data.results[0].queryPlanner; - expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); - expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('COLLSCAN'); - expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage).toBeUndefined(); - - options = Object.assign({}, masterKeyOptions, { - url: Parse.serverURL + '/aggregate/TestObject', - qs: { + }); + let queryPlanner = result[0].stages[0].$cursor.queryPlanner; + expect(queryPlanner.winningPlan.stage).toBe("PROJECTION_SIMPLE"); + expect(queryPlanner.winningPlan.inputStage.stage).toBe("COLLSCAN"); + expect(queryPlanner.winningPlan.inputStage.inputStage).toBeUndefined(); + + result = await collection.aggregate([{ $group: { _id: "$foo" } }], { + hint: { _id: 1 }, explain: true, - hint: '_id_', - $group: JSON.stringify({ _id: '$foo' }), - }, - }); - response = await request(options); - queryPlanner = response.data.results[0].queryPlanner; - expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); - expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('FETCH'); - expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.stage).toBe('IXSCAN'); - expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName).toBe('_id_'); - expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); - }); + }); + queryPlanner = result[0].stages[0].$cursor.queryPlanner; + expect(queryPlanner.winningPlan.stage).toBe("PROJECTION_SIMPLE"); + expect(queryPlanner.winningPlan.inputStage.stage).toBe("FETCH"); + expect(queryPlanner.winningPlan.inputStage.inputStage.stage).toBe( + "IXSCAN" + ); + expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe( + "_id_" + ); + expect(queryPlanner.winningPlan.inputStage.inputStage.keyPattern).toEqual( + { _id: 1 } + ); + } + ); + + it_only_mongodb_version(">=7")( + "query aggregate with hint object", + async () => { + const object = new TestObject({ foo: "bar" }); + await object.save(); + + const collection = + await config.database.adapter._adaptiveCollection("TestObject"); + let result = await collection.aggregate([{ $group: { _id: "$foo" } }], { + explain: true, + }); + let queryPlanner = result[0].queryPlanner; + expect(queryPlanner.winningPlan.queryPlan.stage).toBe("GROUP"); + expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe( + "COLLSCAN" + ); + expect( + queryPlanner.winningPlan.queryPlan.inputStage.inputStage + ).toBeUndefined(); + + result = await collection.aggregate([{ $group: { _id: "$foo" } }], { + hint: { _id: 1 }, + explain: true, + }); + queryPlanner = result[0].queryPlanner; + expect(queryPlanner.winningPlan.queryPlan.stage).toBe("GROUP"); + expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe("FETCH"); + expect( + queryPlanner.winningPlan.queryPlan.inputStage.inputStage.stage + ).toBe("IXSCAN"); + expect( + queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName + ).toBe("_id_"); + expect( + queryPlanner.winningPlan.queryPlan.inputStage.inputStage.keyPattern + ).toEqual({ _id: 1 }); + } + ); + + it_only_mongodb_version("<5.1 || >=6")( + "query find with hint (rest)", + async () => { + const object = new TestObject(); + await object.save(); + let options = Object.assign({}, masterKeyOptions, { + url: Parse.serverURL + "/classes/TestObject", + qs: { + explain: true, + }, + }); + let response = await request(options); + let explain = response.data.results; + expect(explain.queryPlanner.winningPlan.inputStage.stage).toBe( + "COLLSCAN" + ); + + options = Object.assign({}, masterKeyOptions, { + url: Parse.serverURL + "/classes/TestObject", + qs: { + explain: true, + hint: "_id_", + }, + }); + response = await request(options); + explain = response.data.results; + expect( + explain.queryPlanner.winningPlan.inputStage.inputStage.indexName + ).toBe("_id_"); + } + ); + + it_only_mongodb_version("<7")( + "query aggregate with hint (rest)", + async () => { + const object = new TestObject({ foo: "bar" }); + await object.save(); + let options = Object.assign({}, masterKeyOptions, { + url: Parse.serverURL + "/aggregate/TestObject", + qs: { + explain: true, + $group: JSON.stringify({ _id: "$foo" }), + }, + }); + let response = await request(options); + let queryPlanner = + response.data.results[0].stages[0].$cursor.queryPlanner; + expect(queryPlanner.winningPlan.stage).toBe("PROJECTION_SIMPLE"); + expect(queryPlanner.winningPlan.inputStage.stage).toBe("COLLSCAN"); + expect(queryPlanner.winningPlan.inputStage.inputStage).toBeUndefined(); + + options = Object.assign({}, masterKeyOptions, { + url: Parse.serverURL + "/aggregate/TestObject", + qs: { + explain: true, + hint: "_id_", + $group: JSON.stringify({ _id: "$foo" }), + }, + }); + response = await request(options); + queryPlanner = response.data.results[0].stages[0].$cursor.queryPlanner; + expect(queryPlanner.winningPlan.stage).toBe("PROJECTION_SIMPLE"); + expect(queryPlanner.winningPlan.inputStage.stage).toBe("FETCH"); + expect(queryPlanner.winningPlan.inputStage.inputStage.stage).toBe( + "IXSCAN" + ); + expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe( + "_id_" + ); + expect(queryPlanner.winningPlan.inputStage.inputStage.keyPattern).toEqual( + { _id: 1 } + ); + } + ); + + it_only_mongodb_version(">=7")( + "query aggregate with hint (rest)", + async () => { + const object = new TestObject({ foo: "bar" }); + await object.save(); + let options = Object.assign({}, masterKeyOptions, { + url: Parse.serverURL + "/aggregate/TestObject", + qs: { + explain: true, + $group: JSON.stringify({ _id: "$foo" }), + }, + }); + let response = await request(options); + let queryPlanner = response.data.results[0].queryPlanner; + expect(queryPlanner.winningPlan.queryPlan.stage).toBe("GROUP"); + expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe( + "COLLSCAN" + ); + expect( + queryPlanner.winningPlan.queryPlan.inputStage.inputStage + ).toBeUndefined(); + + options = Object.assign({}, masterKeyOptions, { + url: Parse.serverURL + "/aggregate/TestObject", + qs: { + explain: true, + hint: "_id_", + $group: JSON.stringify({ _id: "$foo" }), + }, + }); + response = await request(options); + queryPlanner = response.data.results[0].queryPlanner; + expect(queryPlanner.winningPlan.queryPlan.stage).toBe("GROUP"); + expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe("FETCH"); + expect( + queryPlanner.winningPlan.queryPlan.inputStage.inputStage.stage + ).toBe("IXSCAN"); + expect( + queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName + ).toBe("_id_"); + expect( + queryPlanner.winningPlan.queryPlan.inputStage.inputStage.keyPattern + ).toEqual({ _id: 1 }); + } + ); }); diff --git a/spec/ParseQuery.spec.js b/spec/ParseQuery.spec.js index 80c677dbf3..0799718ae8 100644 --- a/spec/ParseQuery.spec.js +++ b/spec/ParseQuery.spec.js @@ -2,19 +2,19 @@ // hungry/js/test/parse_query_test.js // // Some new tests are added. -'use strict'; +"use strict"; -const Parse = require('parse/node'); -const request = require('../lib/request'); +const Parse = require("parse/node"); +const request = require("../lib/request"); const ParseServerRESTController = - require('../lib/ParseServerRESTController').ParseServerRESTController; -const ParseServer = require('../lib/ParseServer').default; + require("../lib/ParseServerRESTController").ParseServerRESTController; +const ParseServer = require("../lib/ParseServer").default; const masterKeyHeaders = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Rest-API-Key': 'test', - 'X-Parse-Master-Key': 'test', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": "test", + "X-Parse-Rest-API-Key": "test", + "X-Parse-Master-Key": "test", + "Content-Type": "application/json", }; const masterKeyOptions = { @@ -22,61 +22,61 @@ const masterKeyOptions = { }; const BoxedNumber = Parse.Object.extend({ - className: 'BoxedNumber', + className: "BoxedNumber", }); -describe('Parse.Query testing', () => { - it('basic query', function (done) { - const baz = new TestObject({ foo: 'baz' }); - const qux = new TestObject({ foo: 'qux' }); +describe("Parse.Query testing", () => { + it("basic query", function (done) { + const baz = new TestObject({ foo: "baz" }); + const qux = new TestObject({ foo: "qux" }); Parse.Object.saveAll([baz, qux]).then(function () { const query = new Parse.Query(TestObject); - query.equalTo('foo', 'baz'); + query.equalTo("foo", "baz"); query.find().then(function (results) { equal(results.length, 1); - equal(results[0].get('foo'), 'baz'); + equal(results[0].get("foo"), "baz"); done(); }); }); }); - it_only_db('mongo')('gracefully handles invalid explain values', async () => { + it_only_db("mongo")("gracefully handles invalid explain values", async () => { // Note that anything that is not truthy (like 0) does not cause an exception, as they get swallowed up by ClassesRouter::optionsFromBody - const values = [1, 'yolo', { a: 1 }, [1, 2, 3]]; + const values = [1, "yolo", { a: 1 }, [1, 2, 3]]; for (const value of values) { try { await request({ - method: 'GET', + method: "GET", url: `http://localhost:8378/1/classes/_User?explain=${value}`, json: true, headers: masterKeyHeaders, }); - fail('request did not throw'); + fail("request did not throw"); } catch (e) { // Expect that Parse Server did not crash - expect(e.code).not.toEqual('ECONNRESET'); + expect(e.code).not.toEqual("ECONNRESET"); // Expect that Parse Server validates the explain value and does not crash; // see https://jira.mongodb.org/browse/NODE-3463 equal(e.data.code, Parse.Error.INVALID_QUERY); - equal(e.data.error, 'Invalid value for explain'); + equal(e.data.error, "Invalid value for explain"); } // get queries (of the form '/classes/:className/:objectId' cannot have the explain key, see ClassesRouter.js) // so it is enough that we test find queries } }); - it_only_db('mongo')('supports valid explain values', async () => { + it_only_db("mongo")("supports valid explain values", async () => { const values = [ false, true, - 'queryPlanner', - 'executionStats', - 'allPlansExecution', + "queryPlanner", + "executionStats", + "allPlansExecution", // 'queryPlannerExtended' is excluded as it only applies to MongoDB Data Lake which is currently not available in our CI environment ]; for (const value of values) { const response = await request({ - method: 'GET', + method: "GET", url: `http://localhost:8378/1/classes/_User?explain=${value}`, json: true, headers: masterKeyHeaders, @@ -88,16 +88,16 @@ describe('Parse.Query testing', () => { } }); - it('searching for null', function (done) { + it("searching for null", function (done) { const baz = new TestObject({ foo: null }); - const qux = new TestObject({ foo: 'qux' }); + const qux = new TestObject({ foo: "qux" }); const qux2 = new TestObject({}); Parse.Object.saveAll([baz, qux, qux2]).then(function () { const query = new Parse.Query(TestObject); - query.equalTo('foo', null); + query.equalTo("foo", null); query.find().then(function (results) { equal(results.length, 2); - qux.set('foo', null); + qux.set("foo", null); qux.save().then(function () { query.find().then(function (results) { equal(results.length, 3); @@ -108,16 +108,16 @@ describe('Parse.Query testing', () => { }); }); - it('searching for not null', function (done) { + it("searching for not null", function (done) { const baz = new TestObject({ foo: null }); - const qux = new TestObject({ foo: 'qux' }); + const qux = new TestObject({ foo: "qux" }); const qux2 = new TestObject({}); Parse.Object.saveAll([baz, qux, qux2]).then(function () { const query = new Parse.Query(TestObject); - query.notEqualTo('foo', null); + query.notEqualTo("foo", null); query.find().then(function (results) { equal(results.length, 1); - qux.set('foo', null); + qux.set("foo", null); qux.save().then(function () { query.find().then(function (results) { equal(results.length, 0); @@ -128,20 +128,20 @@ describe('Parse.Query testing', () => { }); }); - it('notEqualTo with Relation is working', function (done) { + it("notEqualTo with Relation is working", function (done) { const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('zxcv'); + user.setPassword("asdf"); + user.setUsername("zxcv"); const user1 = new Parse.User(); - user1.setPassword('asdf'); - user1.setUsername('qwerty'); + user1.setPassword("asdf"); + user1.setUsername("qwerty"); const user2 = new Parse.User(); - user2.setPassword('asdf'); - user2.setUsername('asdf'); + user2.setPassword("asdf"); + user2.setUsername("asdf"); - const Cake = Parse.Object.extend('Cake'); + const Cake = Parse.Object.extend("Cake"); const cake1 = new Cake(); const cake2 = new Cake(); const cake3 = new Cake(); @@ -155,38 +155,38 @@ describe('Parse.Query testing', () => { return user2.signUp(); }) .then(function () { - const relLike1 = cake1.relation('liker'); + const relLike1 = cake1.relation("liker"); relLike1.add([user, user1]); - const relDislike1 = cake1.relation('hater'); + const relDislike1 = cake1.relation("hater"); relDislike1.add(user2); return cake1.save(); }) .then(function () { - const rellike2 = cake2.relation('liker'); + const rellike2 = cake2.relation("liker"); rellike2.add([user, user1]); - const relDislike2 = cake2.relation('hater'); + const relDislike2 = cake2.relation("hater"); relDislike2.add(user2); - const relSomething = cake2.relation('something'); + const relSomething = cake2.relation("something"); relSomething.add(user); return cake2.save(); }) .then(function () { - const rellike3 = cake3.relation('liker'); + const rellike3 = cake3.relation("liker"); rellike3.add(user); - const relDislike3 = cake3.relation('hater'); + const relDislike3 = cake3.relation("hater"); relDislike3.add([user1, user2]); return cake3.save(); }) .then(function () { const query = new Parse.Query(Cake); // User2 likes nothing so we should receive 0 - query.equalTo('liker', user2); + query.equalTo("liker", user2); return query.find().then(function (results) { equal(results.length, 0); }); @@ -194,7 +194,7 @@ describe('Parse.Query testing', () => { .then(function () { const query = new Parse.Query(Cake); // User1 likes two of three cakes - query.equalTo('liker', user1); + query.equalTo("liker", user1); return query.find().then(function (results) { // It should return 2 -> cake 1 and cake 2 equal(results.length, 2); @@ -203,7 +203,7 @@ describe('Parse.Query testing', () => { .then(function () { const query = new Parse.Query(Cake); // We want to know which cake the user1 is not appreciating -> cake3 - query.notEqualTo('liker', user1); + query.notEqualTo("liker", user1); return query.find().then(function (results) { // Should return 1 -> the cake 3 equal(results.length, 1); @@ -212,7 +212,7 @@ describe('Parse.Query testing', () => { .then(function () { const query = new Parse.Query(Cake); // User2 is a hater of everything so we should receive 0 - query.notEqualTo('hater', user2); + query.notEqualTo("hater", user2); return query.find().then(function (results) { equal(results.length, 0); }); @@ -220,7 +220,7 @@ describe('Parse.Query testing', () => { .then(function () { const query = new Parse.Query(Cake); // Only cake3 is liked by user - query.notContainedIn('liker', [user1]); + query.notContainedIn("liker", [user1]); return query.find().then(function (results) { equal(results.length, 1); }); @@ -228,9 +228,9 @@ describe('Parse.Query testing', () => { .then(function () { const query = new Parse.Query(Cake); // All the users - query.containedIn('liker', [user, user1, user2]); + query.containedIn("liker", [user, user1, user2]); // Exclude user 1 - query.notEqualTo('liker', user1); + query.notEqualTo("liker", user1); // Only cake3 is liked only by user1 return query.find().then(function (results) { equal(results.length, 1); @@ -241,9 +241,9 @@ describe('Parse.Query testing', () => { .then(function () { const query = new Parse.Query(Cake); // Exclude user1 - query.notEqualTo('liker', user1); + query.notEqualTo("liker", user1); // Only cake1 - query.equalTo('objectId', cake1.id); + query.equalTo("objectId", cake1.id); // user1 likes cake1 so this should return no results return query.find().then(function (results) { equal(results.length, 0); @@ -251,8 +251,8 @@ describe('Parse.Query testing', () => { }) .then(function () { const query = new Parse.Query(Cake); - query.notEqualTo('hater', user2); - query.notEqualTo('liker', user2); + query.notEqualTo("hater", user2); + query.notEqualTo("liker", user2); // user2 doesn't like any cake so this should be 0 return query.find().then(function (results) { equal(results.length, 0); @@ -260,8 +260,8 @@ describe('Parse.Query testing', () => { }) .then(function () { const query = new Parse.Query(Cake); - query.equalTo('hater', user); - query.equalTo('liker', user); + query.equalTo("hater", user); + query.equalTo("liker", user); // user doesn't hate any cake so this should be 0 return query.find().then(function (results) { equal(results.length, 0); @@ -269,8 +269,8 @@ describe('Parse.Query testing', () => { }) .then(function () { const query = new Parse.Query(Cake); - query.equalTo('hater', null); - query.equalTo('liker', null); + query.equalTo("hater", null); + query.equalTo("liker", null); // user doesn't hate any cake so this should be 0 return query.find().then(function (results) { equal(results.length, 0); @@ -278,7 +278,7 @@ describe('Parse.Query testing', () => { }) .then(function () { const query = new Parse.Query(Cake); - query.equalTo('something', null); + query.equalTo("something", null); // user doesn't hate any cake so this should be 0 return query.find().then(function (results) { equal(results.length, 0); @@ -293,43 +293,43 @@ describe('Parse.Query testing', () => { }); }); - it('query notContainedIn on empty array', async () => { + it("query notContainedIn on empty array", async () => { const object = new TestObject(); - object.set('value', 100); + object.set("value", 100); await object.save(); const query = new Parse.Query(TestObject); - query.notContainedIn('value', []); + query.notContainedIn("value", []); const results = await query.find(); equal(results.length, 1); }); - it('query containedIn on empty array', async () => { + it("query containedIn on empty array", async () => { const object = new TestObject(); - object.set('value', 100); + object.set("value", 100); await object.save(); const query = new Parse.Query(TestObject); - query.containedIn('value', []); + query.containedIn("value", []); const results = await query.find(); equal(results.length, 0); }); - it('query without limit respects default limit', async () => { + it("query without limit respects default limit", async () => { await reconfigureServer({ defaultLimit: 1 }); - const obj1 = new TestObject({ foo: 'baz' }); - const obj2 = new TestObject({ foo: 'qux' }); + const obj1 = new TestObject({ foo: "baz" }); + const obj2 = new TestObject({ foo: "qux" }); await Parse.Object.saveAll([obj1, obj2]); const query = new Parse.Query(TestObject); const result = await query.find(); expect(result.length).toBe(1); }); - it('query with limit', async () => { - const obj1 = new TestObject({ foo: 'baz' }); - const obj2 = new TestObject({ foo: 'qux' }); + it("query with limit", async () => { + const obj1 = new TestObject({ foo: "baz" }); + const obj2 = new TestObject({ foo: "qux" }); await Parse.Object.saveAll([obj1, obj2]); const query = new Parse.Query(TestObject); query.limit(1); @@ -337,10 +337,10 @@ describe('Parse.Query testing', () => { expect(result.length).toBe(1); }); - it('query with limit overrides default limit', async () => { + it("query with limit overrides default limit", async () => { await reconfigureServer({ defaultLimit: 2 }); - const obj1 = new TestObject({ foo: 'baz' }); - const obj2 = new TestObject({ foo: 'qux' }); + const obj1 = new TestObject({ foo: "baz" }); + const obj2 = new TestObject({ foo: "qux" }); await Parse.Object.saveAll([obj1, obj2]); const query = new Parse.Query(TestObject); query.limit(1); @@ -348,10 +348,10 @@ describe('Parse.Query testing', () => { expect(result.length).toBe(1); }); - it('query with limit equal to maxlimit', async () => { + it("query with limit equal to maxlimit", async () => { await reconfigureServer({ maxLimit: 1 }); - const obj1 = new TestObject({ foo: 'baz' }); - const obj2 = new TestObject({ foo: 'qux' }); + const obj1 = new TestObject({ foo: "baz" }); + const obj2 = new TestObject({ foo: "qux" }); await Parse.Object.saveAll([obj1, obj2]); const query = new Parse.Query(TestObject); query.limit(1); @@ -359,10 +359,10 @@ describe('Parse.Query testing', () => { expect(result.length).toBe(1); }); - it('query with limit exceeding maxlimit', async () => { + it("query with limit exceeding maxlimit", async () => { await reconfigureServer({ maxLimit: 1 }); - const obj1 = new TestObject({ foo: 'baz' }); - const obj2 = new TestObject({ foo: 'qux' }); + const obj1 = new TestObject({ foo: "baz" }); + const obj2 = new TestObject({ foo: "qux" }); await Parse.Object.saveAll([obj1, obj2]); const query = new Parse.Query(TestObject); query.limit(2); @@ -370,12 +370,12 @@ describe('Parse.Query testing', () => { expect(result.length).toBe(1); }); - it('containedIn object array queries', function (done) { + it("containedIn object array queries", function (done) { const messageList = []; for (let i = 0; i < 4; ++i) { const message = new TestObject({}); if (i > 0) { - message.set('prior', messageList[i - 1]); + message.set("prior", messageList[i - 1]); } messageList.push(message); } @@ -389,7 +389,7 @@ describe('Parse.Query testing', () => { inList.push(messageList[2]); const query = new Parse.Query(TestObject); - query.containedIn('prior', inList); + query.containedIn("prior", inList); query.find().then( function (results) { equal(results.length, 2); @@ -408,16 +408,16 @@ describe('Parse.Query testing', () => { ); }); - it('containedIn null array', done => { - const emails = ['contact@xyz.com', 'contact@zyx.com', null]; + it("containedIn null array", done => { + const emails = ["contact@xyz.com", "contact@zyx.com", null]; const user = new Parse.User(); user.setUsername(emails[0]); - user.setPassword('asdf'); + user.setPassword("asdf"); user .signUp() .then(() => { const query = new Parse.Query(Parse.User); - query.containedIn('username', emails); + query.containedIn("username", emails); return query.find({ useMasterKey: true }); }) .then(results => { @@ -426,35 +426,35 @@ describe('Parse.Query testing', () => { }, done.fail); }); - it('nested equalTo string with single quote', async () => { + it("nested equalTo string with single quote", async () => { const obj = new TestObject({ nested: { foo: "single'quote" } }); await obj.save(); const query = new Parse.Query(TestObject); - query.equalTo('nested.foo', "single'quote"); + query.equalTo("nested.foo", "single'quote"); const result = await query.get(obj.id); - equal(result.get('nested').foo, "single'quote"); + equal(result.get("nested").foo, "single'quote"); }); - it('nested containedIn string with single quote', async () => { + it("nested containedIn string with single quote", async () => { const obj = new TestObject({ nested: { foo: ["single'quote"] } }); await obj.save(); const query = new Parse.Query(TestObject); - query.containedIn('nested.foo', ["single'quote"]); + query.containedIn("nested.foo", ["single'quote"]); const result = await query.get(obj.id); - equal(result.get('nested').foo[0], "single'quote"); + equal(result.get("nested").foo[0], "single'quote"); }); - it('nested containedIn string', done => { - const sender1 = { group: ['A', 'B'] }; - const sender2 = { group: ['A', 'C'] }; - const sender3 = { group: ['B', 'C'] }; + it("nested containedIn string", done => { + const sender1 = { group: ["A", "B"] }; + const sender2 = { group: ["A", "C"] }; + const sender3 = { group: ["B", "C"] }; const obj1 = new TestObject({ sender: sender1 }); const obj2 = new TestObject({ sender: sender2 }); const obj3 = new TestObject({ sender: sender3 }); Parse.Object.saveAll([obj1, obj2, obj3]) .then(() => { const query = new Parse.Query(TestObject); - query.containedIn('sender.group', ['A']); + query.containedIn("sender.group", ["A"]); return query.find(); }) .then(results => { @@ -463,7 +463,7 @@ describe('Parse.Query testing', () => { }, done.fail); }); - it('nested containedIn number', done => { + it("nested containedIn number", done => { const sender1 = { group: [1, 2] }; const sender2 = { group: [1, 3] }; const sender3 = { group: [2, 3] }; @@ -473,7 +473,7 @@ describe('Parse.Query testing', () => { Parse.Object.saveAll([obj1, obj2, obj3]) .then(() => { const query = new Parse.Query(TestObject); - query.containedIn('sender.group', [1]); + query.containedIn("sender.group", [1]); return query.find(); }) .then(results => { @@ -482,8 +482,8 @@ describe('Parse.Query testing', () => { }, done.fail); }); - it('containsAll number array queries', function (done) { - const NumberSet = Parse.Object.extend({ className: 'NumberSet' }); + it("containsAll number array queries", function (done) { + const NumberSet = Parse.Object.extend({ className: "NumberSet" }); const objectsList = []; objectsList.push(new NumberSet({ numbers: [1, 2, 3, 4, 5] })); @@ -492,7 +492,7 @@ describe('Parse.Query testing', () => { Parse.Object.saveAll(objectsList) .then(function () { const query = new Parse.Query(NumberSet); - query.containsAll('numbers', [1, 2, 3]); + query.containsAll("numbers", [1, 2, 3]); query.find().then( function (results) { equal(results.length, 1); @@ -510,17 +510,17 @@ describe('Parse.Query testing', () => { }); }); - it('containsAll string array queries', function (done) { - const StringSet = Parse.Object.extend({ className: 'StringSet' }); + it("containsAll string array queries", function (done) { + const StringSet = Parse.Object.extend({ className: "StringSet" }); const objectsList = []; - objectsList.push(new StringSet({ strings: ['a', 'b', 'c', 'd', 'e'] })); - objectsList.push(new StringSet({ strings: ['a', 'c', 'd', 'e'] })); + objectsList.push(new StringSet({ strings: ["a", "b", "c", "d", "e"] })); + objectsList.push(new StringSet({ strings: ["a", "c", "d", "e"] })); Parse.Object.saveAll(objectsList) .then(function () { const query = new Parse.Query(StringSet); - query.containsAll('strings', ['a', 'b', 'c']); + query.containsAll("strings", ["a", "b", "c"]); query.find().then(function (results) { equal(results.length, 1); done(); @@ -532,16 +532,16 @@ describe('Parse.Query testing', () => { }); }); - it('containsAll date array queries', function (done) { - const DateSet = Parse.Object.extend({ className: 'DateSet' }); + it("containsAll date array queries", function (done) { + const DateSet = Parse.Object.extend({ className: "DateSet" }); function parseDate(iso8601) { const regexp = new RegExp( - '^([0-9]{1,4})-([0-9]{1,2})-([0-9]{1,2})' + - 'T' + - '([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2})' + - '(.([0-9]+))?' + - 'Z$' + "^([0-9]{1,4})-([0-9]{1,2})-([0-9]{1,2})" + + "T" + + "([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2})" + + "(.([0-9]+))?" + + "Z$" ); const match = regexp.exec(iso8601); if (!match) { @@ -561,25 +561,33 @@ describe('Parse.Query testing', () => { const makeDates = function (stringArray) { return stringArray.map(function (dateStr) { - return parseDate(dateStr + 'T00:00:00Z'); + return parseDate(dateStr + "T00:00:00Z"); }); }; const objectsList = []; objectsList.push( new DateSet({ - dates: makeDates(['2013-02-01', '2013-02-02', '2013-02-03', '2013-02-04']), + dates: makeDates([ + "2013-02-01", + "2013-02-02", + "2013-02-03", + "2013-02-04", + ]), }) ); objectsList.push( new DateSet({ - dates: makeDates(['2013-02-01', '2013-02-03', '2013-02-04']), + dates: makeDates(["2013-02-01", "2013-02-03", "2013-02-04"]), }) ); Parse.Object.saveAll(objectsList).then(function () { const query = new Parse.Query(DateSet); - query.containsAll('dates', makeDates(['2013-02-01', '2013-02-02', '2013-02-03'])); + query.containsAll( + "dates", + makeDates(["2013-02-01", "2013-02-02", "2013-02-03"]) + ); query.find().then( function (results) { equal(results.length, 1); @@ -593,10 +601,10 @@ describe('Parse.Query testing', () => { }); }); - it_id('25bb35a6-e953-4d6d-a31c-66324d5ae076')(it)( - 'containsAll object array queries', + it_id("25bb35a6-e953-4d6d-a31c-66324d5ae076")(it)( + "containsAll object array queries", function (done) { - const MessageSet = Parse.Object.extend({ className: 'MessageSet' }); + const MessageSet = Parse.Object.extend({ className: "MessageSet" }); const messageList = []; for (let i = 0; i < 4; ++i) { @@ -621,7 +629,7 @@ describe('Parse.Query testing', () => { inList.push(messageList[2]); const query = new Parse.Query(MessageSet); - query.containsAll('messages', inList); + query.containsAll("messages", inList); query.find().then(function (results) { equal(results.length, 1); done(); @@ -631,13 +639,13 @@ describe('Parse.Query testing', () => { } ); - it('containsAllStartingWith should match all strings that starts with string', done => { - const object = new Parse.Object('Object'); - object.set('strings', ['the', 'brown', 'lazy', 'fox', 'jumps']); - const object2 = new Parse.Object('Object'); - object2.set('strings', ['the', 'brown', 'fox', 'jumps']); - const object3 = new Parse.Object('Object'); - object3.set('strings', ['over', 'the', 'lazy', 'dog']); + it("containsAllStartingWith should match all strings that starts with string", done => { + const object = new Parse.Object("Object"); + object.set("strings", ["the", "brown", "lazy", "fox", "jumps"]); + const object2 = new Parse.Object("Object"); + object2.set("strings", ["the", "brown", "fox", "jumps"]); + const object3 = new Parse.Object("Object"); + object3.set("strings", ["over", "the", "lazy", "dog"]); const objectList = [object, object2, object3]; @@ -645,18 +653,22 @@ describe('Parse.Query testing', () => { equal(objectList.length, results.length); return request({ - url: Parse.serverURL + '/classes/Object', + url: Parse.serverURL + "/classes/Object", qs: { where: JSON.stringify({ strings: { - $all: [{ $regex: '^\\Qthe\\E' }, { $regex: '^\\Qfox\\E' }, { $regex: '^\\Qlazy\\E' }], + $all: [ + { $regex: "^\\Qthe\\E" }, + { $regex: "^\\Qfox\\E" }, + { $regex: "^\\Qlazy\\E" }, + ], }, }), }, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Javascript-Key": Parse.javaScriptKey, + "Content-Type": "application/json", }, }) .then(function (response) { @@ -665,18 +677,18 @@ describe('Parse.Query testing', () => { arrayContains(results.results, object); return request({ - url: Parse.serverURL + '/classes/Object', + url: Parse.serverURL + "/classes/Object", qs: { where: JSON.stringify({ strings: { - $all: [{ $regex: '^\\Qthe\\E' }, { $regex: '^\\Qlazy\\E' }], + $all: [{ $regex: "^\\Qthe\\E" }, { $regex: "^\\Qlazy\\E" }], }, }), }, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Javascript-Key": Parse.javaScriptKey, + "Content-Type": "application/json", }, }); }) @@ -687,18 +699,18 @@ describe('Parse.Query testing', () => { arrayContains(results.results, object3); return request({ - url: Parse.serverURL + '/classes/Object', + url: Parse.serverURL + "/classes/Object", qs: { where: JSON.stringify({ strings: { - $all: [{ $regex: '^\\Qhe\\E' }, { $regex: '^\\Qlazy\\E' }], + $all: [{ $regex: "^\\Qhe\\E" }, { $regex: "^\\Qlazy\\E" }], }, }), }, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Javascript-Key": Parse.javaScriptKey, + "Content-Type": "application/json", }, }); }) @@ -711,11 +723,11 @@ describe('Parse.Query testing', () => { }); }); - it_id('3ea6ae04-bcc2-453d-8817-4c64d059c2f6')(it)( - 'containsAllStartingWith values must be all of type starting with regex', + it_id("3ea6ae04-bcc2-453d-8817-4c64d059c2f6")(it)( + "containsAllStartingWith values must be all of type starting with regex", done => { - const object = new Parse.Object('Object'); - object.set('strings', ['the', 'brown', 'lazy', 'fox', 'jumps']); + const object = new Parse.Object("Object"); + object.set("strings", ["the", "brown", "lazy", "fox", "jumps"]); object .save() @@ -723,23 +735,23 @@ describe('Parse.Query testing', () => { equal(object.isNew(), false); return request({ - url: Parse.serverURL + '/classes/Object', + url: Parse.serverURL + "/classes/Object", qs: { where: JSON.stringify({ strings: { $all: [ - { $regex: '^\\Qthe\\E' }, - { $regex: '^\\Qlazy\\E' }, - { $regex: '^\\Qfox\\E' }, + { $regex: "^\\Qthe\\E" }, + { $regex: "^\\Qlazy\\E" }, + { $regex: "^\\Qfox\\E" }, { $unknown: /unknown/ }, ], }, }), }, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Javascript-Key": Parse.javaScriptKey, + "Content-Type": "application/json", }, }); }) @@ -749,9 +761,9 @@ describe('Parse.Query testing', () => { } ); - it('containsAllStartingWith empty array values should return empty results', done => { - const object = new Parse.Object('Object'); - object.set('strings', ['the', 'brown', 'lazy', 'fox', 'jumps']); + it("containsAllStartingWith empty array values should return empty results", done => { + const object = new Parse.Object("Object"); + object.set("strings", ["the", "brown", "lazy", "fox", "jumps"]); object .save() @@ -759,7 +771,7 @@ describe('Parse.Query testing', () => { equal(object.isNew(), false); return request({ - url: Parse.serverURL + '/classes/Object', + url: Parse.serverURL + "/classes/Object", qs: { where: JSON.stringify({ strings: { @@ -768,9 +780,9 @@ describe('Parse.Query testing', () => { }), }, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Javascript-Key": Parse.javaScriptKey, + "Content-Type": "application/json", }, }); }) @@ -784,9 +796,9 @@ describe('Parse.Query testing', () => { ); }); - it('containsAllStartingWith single empty value returns empty results', done => { - const object = new Parse.Object('Object'); - object.set('strings', ['the', 'brown', 'lazy', 'fox', 'jumps']); + it("containsAllStartingWith single empty value returns empty results", done => { + const object = new Parse.Object("Object"); + object.set("strings", ["the", "brown", "lazy", "fox", "jumps"]); object .save() @@ -794,7 +806,7 @@ describe('Parse.Query testing', () => { equal(object.isNew(), false); return request({ - url: Parse.serverURL + '/classes/Object', + url: Parse.serverURL + "/classes/Object", qs: { where: JSON.stringify({ strings: { @@ -803,9 +815,9 @@ describe('Parse.Query testing', () => { }), }, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Javascript-Key": Parse.javaScriptKey, + "Content-Type": "application/json", }, }); }) @@ -819,13 +831,13 @@ describe('Parse.Query testing', () => { ); }); - it('containsAllStartingWith single regex value should return corresponding matching results', done => { - const object = new Parse.Object('Object'); - object.set('strings', ['the', 'brown', 'lazy', 'fox', 'jumps']); - const object2 = new Parse.Object('Object'); - object2.set('strings', ['the', 'brown', 'fox', 'jumps']); - const object3 = new Parse.Object('Object'); - object3.set('strings', ['over', 'the', 'lazy', 'dog']); + it("containsAllStartingWith single regex value should return corresponding matching results", done => { + const object = new Parse.Object("Object"); + object.set("strings", ["the", "brown", "lazy", "fox", "jumps"]); + const object2 = new Parse.Object("Object"); + object2.set("strings", ["the", "brown", "fox", "jumps"]); + const object3 = new Parse.Object("Object"); + object3.set("strings", ["over", "the", "lazy", "dog"]); const objectList = [object, object2, object3]; @@ -834,18 +846,18 @@ describe('Parse.Query testing', () => { equal(objectList.length, results.length); return request({ - url: Parse.serverURL + '/classes/Object', + url: Parse.serverURL + "/classes/Object", qs: { where: JSON.stringify({ strings: { - $all: [{ $regex: '^\\Qlazy\\E' }], + $all: [{ $regex: "^\\Qlazy\\E" }], }, }), }, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Javascript-Key": Parse.javaScriptKey, + "Content-Type": "application/json", }, }); }) @@ -859,9 +871,9 @@ describe('Parse.Query testing', () => { ); }); - it('containsAllStartingWith single invalid regex returns empty results', done => { - const object = new Parse.Object('Object'); - object.set('strings', ['the', 'brown', 'lazy', 'fox', 'jumps']); + it("containsAllStartingWith single invalid regex returns empty results", done => { + const object = new Parse.Object("Object"); + object.set("strings", ["the", "brown", "lazy", "fox", "jumps"]); object .save() @@ -869,17 +881,17 @@ describe('Parse.Query testing', () => { equal(object.isNew(), false); return request({ - url: Parse.serverURL + '/classes/Object', + url: Parse.serverURL + "/classes/Object", qs: { where: JSON.stringify({ strings: { - $all: [{ $unknown: '^\\Qlazy\\E' }], + $all: [{ $unknown: "^\\Qlazy\\E" }], }, }), }, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Javascript-Key": Parse.javaScriptKey, }, }); }) @@ -893,62 +905,65 @@ describe('Parse.Query testing', () => { ); }); - it_id('01a15195-dde2-4368-b996-d746a4ede3a1')(it)('containedBy pointer array', done => { - const objects = Array.from(Array(10).keys()).map(idx => { - const obj = new Parse.Object('Object'); - obj.set('key', idx); - return obj; - }); + it_id("01a15195-dde2-4368-b996-d746a4ede3a1")(it)( + "containedBy pointer array", + done => { + const objects = Array.from(Array(10).keys()).map(idx => { + const obj = new Parse.Object("Object"); + obj.set("key", idx); + return obj; + }); - const parent = new Parse.Object('Parent'); - const parent2 = new Parse.Object('Parent'); - const parent3 = new Parse.Object('Parent'); + const parent = new Parse.Object("Parent"); + const parent2 = new Parse.Object("Parent"); + const parent3 = new Parse.Object("Parent"); - Parse.Object.saveAll(objects) - .then(() => { - // [0, 1, 2] - parent.set('objects', objects.slice(0, 3)); + Parse.Object.saveAll(objects) + .then(() => { + // [0, 1, 2] + parent.set("objects", objects.slice(0, 3)); - const shift = objects.shift(); - // [2, 0] - parent2.set('objects', [objects[1], shift]); + const shift = objects.shift(); + // [2, 0] + parent2.set("objects", [objects[1], shift]); - // [1, 2, 3, 4] - parent3.set('objects', objects.slice(1, 4)); + // [1, 2, 3, 4] + parent3.set("objects", objects.slice(1, 4)); - return Parse.Object.saveAll([parent, parent2, parent3]); - }) - .then(() => { - // [1, 2, 3, 4, 5, 6, 7, 8, 9] - const pointers = objects.map(object => object.toPointer()); + return Parse.Object.saveAll([parent, parent2, parent3]); + }) + .then(() => { + // [1, 2, 3, 4, 5, 6, 7, 8, 9] + const pointers = objects.map(object => object.toPointer()); - // Return all Parent where all parent.objects are contained in objects - return request({ - url: Parse.serverURL + '/classes/Parent', - qs: { - where: JSON.stringify({ - objects: { - $containedBy: pointers, - }, - }), - }, - headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, - 'Content-Type': 'application/json', - }, + // Return all Parent where all parent.objects are contained in objects + return request({ + url: Parse.serverURL + "/classes/Parent", + qs: { + where: JSON.stringify({ + objects: { + $containedBy: pointers, + }, + }), + }, + headers: { + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Javascript-Key": Parse.javaScriptKey, + "Content-Type": "application/json", + }, + }); + }) + .then(response => { + const results = response.data; + expect(results.results[0].objectId).not.toBeUndefined(); + expect(results.results[0].objectId).toBe(parent3.id); + expect(results.results.length).toBe(1); + done(); }); - }) - .then(response => { - const results = response.data; - expect(results.results[0].objectId).not.toBeUndefined(); - expect(results.results[0].objectId).toBe(parent3.id); - expect(results.results.length).toBe(1); - done(); - }); - }); + } + ); - it('containedBy number array', done => { + it("containedBy number array", done => { const options = Object.assign({}, masterKeyOptions, { qs: { where: JSON.stringify({ @@ -961,7 +976,12 @@ describe('Parse.Query testing', () => { const obj3 = new TestObject({ numbers: [1, 2, 3, 4] }); Parse.Object.saveAll([obj1, obj2, obj3]) .then(() => { - return request(Object.assign({ url: Parse.serverURL + '/classes/TestObject' }, options)); + return request( + Object.assign( + { url: Parse.serverURL + "/classes/TestObject" }, + options + ) + ); }) .then(response => { const results = response.data; @@ -972,7 +992,7 @@ describe('Parse.Query testing', () => { }); }); - it('containedBy empty array', done => { + it("containedBy empty array", done => { const options = Object.assign({}, masterKeyOptions, { qs: { where: JSON.stringify({ numbers: { $containedBy: [] } }), @@ -983,7 +1003,12 @@ describe('Parse.Query testing', () => { const obj3 = new TestObject({ numbers: [1, 2, 3, 4] }); Parse.Object.saveAll([obj1, obj2, obj3]) .then(() => { - return request(Object.assign({ url: Parse.serverURL + '/classes/TestObject' }, options)); + return request( + Object.assign( + { url: Parse.serverURL + "/classes/TestObject" }, + options + ) + ); }) .then(response => { const results = response.data; @@ -992,7 +1017,7 @@ describe('Parse.Query testing', () => { }); }); - it('containedBy invalid query', done => { + it("containedBy invalid query", done => { const options = Object.assign({}, masterKeyOptions, { qs: { where: JSON.stringify({ objects: { $containedBy: 1234 } }), @@ -1002,23 +1027,30 @@ describe('Parse.Query testing', () => { obj .save() .then(() => { - return request(Object.assign({ url: Parse.serverURL + '/classes/TestObject' }, options)); + return request( + Object.assign( + { url: Parse.serverURL + "/classes/TestObject" }, + options + ) + ); }) .then(done.fail) .catch(response => { equal(response.data.code, Parse.Error.INVALID_JSON); - equal(response.data.error, 'bad $containedBy: should be an array'); + equal(response.data.error, "bad $containedBy: should be an array"); done(); }); }); - it('equalTo queries', function (done) { + it("equalTo queries", function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; - Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber)).then(function () { + Parse.Object.saveAll( + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) + ).then(function () { const query = new Parse.Query(BoxedNumber); - query.equalTo('number', 3); + query.equalTo("number", 3); query.find().then(function (results) { equal(results.length, 1); done(); @@ -1026,13 +1058,15 @@ describe('Parse.Query testing', () => { }); }); - it('equalTo undefined', function (done) { + it("equalTo undefined", function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; - Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber)).then(function () { + Parse.Object.saveAll( + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) + ).then(function () { const query = new Parse.Query(BoxedNumber); - query.equalTo('number', undefined); + query.equalTo("number", undefined); query.find().then(function (results) { equal(results.length, 0); done(); @@ -1040,13 +1074,15 @@ describe('Parse.Query testing', () => { }); }); - it('lessThan queries', function (done) { + it("lessThan queries", function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; - Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber)).then(function () { + Parse.Object.saveAll( + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) + ).then(function () { const query = new Parse.Query(BoxedNumber); - query.lessThan('number', 7); + query.lessThan("number", 7); query.find().then(function (results) { equal(results.length, 7); done(); @@ -1054,13 +1090,15 @@ describe('Parse.Query testing', () => { }); }); - it('lessThanOrEqualTo queries', function (done) { + it("lessThanOrEqualTo queries", function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; - Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber)).then(function () { + Parse.Object.saveAll( + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) + ).then(function () { const query = new Parse.Query(BoxedNumber); - query.lessThanOrEqualTo('number', 7); + query.lessThanOrEqualTo("number", 7); query.find().then(function (results) { equal(results.length, 8); done(); @@ -1068,7 +1106,7 @@ describe('Parse.Query testing', () => { }); }); - it('lessThan zero queries', done => { + it("lessThan zero queries", done => { const makeBoxedNumber = i => { return new BoxedNumber({ number: i }); }; @@ -1077,7 +1115,7 @@ describe('Parse.Query testing', () => { Parse.Object.saveAll(boxedNumbers) .then(() => { const query = new Parse.Query(BoxedNumber); - query.lessThan('number', 0); + query.lessThan("number", 0); return query.find(); }) .then(results => { @@ -1086,7 +1124,7 @@ describe('Parse.Query testing', () => { }); }); - it('lessThanOrEqualTo zero queries', done => { + it("lessThanOrEqualTo zero queries", done => { const makeBoxedNumber = i => { return new BoxedNumber({ number: i }); }; @@ -1095,7 +1133,7 @@ describe('Parse.Query testing', () => { Parse.Object.saveAll(boxedNumbers) .then(() => { const query = new Parse.Query(BoxedNumber); - query.lessThanOrEqualTo('number', 0); + query.lessThanOrEqualTo("number", 0); return query.find(); }) .then(results => { @@ -1104,13 +1142,15 @@ describe('Parse.Query testing', () => { }); }); - it('greaterThan queries', function (done) { + it("greaterThan queries", function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; - Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber)).then(function () { + Parse.Object.saveAll( + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) + ).then(function () { const query = new Parse.Query(BoxedNumber); - query.greaterThan('number', 7); + query.greaterThan("number", 7); query.find().then(function (results) { equal(results.length, 2); done(); @@ -1118,13 +1158,15 @@ describe('Parse.Query testing', () => { }); }); - it('greaterThanOrEqualTo queries', function (done) { + it("greaterThanOrEqualTo queries", function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; - Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber)).then(function () { + Parse.Object.saveAll( + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) + ).then(function () { const query = new Parse.Query(BoxedNumber); - query.greaterThanOrEqualTo('number', 7); + query.greaterThanOrEqualTo("number", 7); query.find().then(function (results) { equal(results.length, 3); done(); @@ -1132,7 +1174,7 @@ describe('Parse.Query testing', () => { }); }); - it('greaterThan zero queries', done => { + it("greaterThan zero queries", done => { const makeBoxedNumber = i => { return new BoxedNumber({ number: i }); }; @@ -1141,7 +1183,7 @@ describe('Parse.Query testing', () => { Parse.Object.saveAll(boxedNumbers) .then(() => { const query = new Parse.Query(BoxedNumber); - query.greaterThan('number', 0); + query.greaterThan("number", 0); return query.find(); }) .then(results => { @@ -1150,7 +1192,7 @@ describe('Parse.Query testing', () => { }); }); - it('greaterThanOrEqualTo zero queries', done => { + it("greaterThanOrEqualTo zero queries", done => { const makeBoxedNumber = i => { return new BoxedNumber({ number: i }); }; @@ -1159,7 +1201,7 @@ describe('Parse.Query testing', () => { Parse.Object.saveAll(boxedNumbers) .then(() => { const query = new Parse.Query(BoxedNumber); - query.greaterThanOrEqualTo('number', 0); + query.greaterThanOrEqualTo("number", 0); return query.find(); }) .then(results => { @@ -1168,14 +1210,16 @@ describe('Parse.Query testing', () => { }); }); - it('lessThanOrEqualTo greaterThanOrEqualTo queries', function (done) { + it("lessThanOrEqualTo greaterThanOrEqualTo queries", function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; - Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber)).then(function () { + Parse.Object.saveAll( + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) + ).then(function () { const query = new Parse.Query(BoxedNumber); - query.lessThanOrEqualTo('number', 7); - query.greaterThanOrEqualTo('number', 7); + query.lessThanOrEqualTo("number", 7); + query.greaterThanOrEqualTo("number", 7); query.find().then(function (results) { equal(results.length, 1); done(); @@ -1183,14 +1227,16 @@ describe('Parse.Query testing', () => { }); }); - it('lessThan greaterThan queries', function (done) { + it("lessThan greaterThan queries", function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; - Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber)).then(function () { + Parse.Object.saveAll( + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) + ).then(function () { const query = new Parse.Query(BoxedNumber); - query.lessThan('number', 9); - query.greaterThan('number', 3); + query.lessThan("number", 9); + query.greaterThan("number", 3); query.find().then(function (results) { equal(results.length, 5); done(); @@ -1198,13 +1244,15 @@ describe('Parse.Query testing', () => { }); }); - it('notEqualTo queries', function (done) { + it("notEqualTo queries", function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; - Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber)).then(function () { + Parse.Object.saveAll( + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) + ).then(function () { const query = new Parse.Query(BoxedNumber); - query.notEqualTo('number', 5); + query.notEqualTo("number", 5); query.find().then(function (results) { equal(results.length, 9); done(); @@ -1212,7 +1260,7 @@ describe('Parse.Query testing', () => { }); }); - it('notEqualTo zero queries', done => { + it("notEqualTo zero queries", done => { const makeBoxedNumber = i => { return new BoxedNumber({ number: i }); }; @@ -1221,7 +1269,7 @@ describe('Parse.Query testing', () => { Parse.Object.saveAll(boxedNumbers) .then(() => { const query = new Parse.Query(BoxedNumber); - query.notEqualTo('number', 0); + query.notEqualTo("number", 0); return query.find(); }) .then(results => { @@ -1230,7 +1278,7 @@ describe('Parse.Query testing', () => { }); }); - it('equalTo zero queries', done => { + it("equalTo zero queries", done => { const makeBoxedNumber = i => { return new BoxedNumber({ number: i }); }; @@ -1239,7 +1287,7 @@ describe('Parse.Query testing', () => { Parse.Object.saveAll(boxedNumbers) .then(() => { const query = new Parse.Query(BoxedNumber); - query.equalTo('number', 0); + query.equalTo("number", 0); return query.find(); }) .then(results => { @@ -1248,7 +1296,7 @@ describe('Parse.Query testing', () => { }); }); - it('number equalTo boolean queries', done => { + it("number equalTo boolean queries", done => { const makeBoxedNumber = i => { return new BoxedNumber({ number: i }); }; @@ -1257,7 +1305,7 @@ describe('Parse.Query testing', () => { Parse.Object.saveAll(boxedNumbers) .then(() => { const query = new Parse.Query(BoxedNumber); - query.equalTo('number', false); + query.equalTo("number", false); return query.find(); }) .then(results => { @@ -1266,13 +1314,13 @@ describe('Parse.Query testing', () => { }); }); - it('equalTo false queries', done => { + it("equalTo false queries", done => { const obj1 = new TestObject({ field: false }); const obj2 = new TestObject({ field: true }); Parse.Object.saveAll([obj1, obj2]) .then(() => { const query = new Parse.Query(TestObject); - query.equalTo('field', false); + query.equalTo("field", false); return query.find(); }) .then(results => { @@ -1281,7 +1329,7 @@ describe('Parse.Query testing', () => { }); }); - it('where $eq false queries (rest)', done => { + it("where $eq false queries (rest)", done => { const options = Object.assign({}, masterKeyOptions, { qs: { where: JSON.stringify({ field: { $eq: false } }), @@ -1290,16 +1338,16 @@ describe('Parse.Query testing', () => { const obj1 = new TestObject({ field: false }); const obj2 = new TestObject({ field: true }); Parse.Object.saveAll([obj1, obj2]).then(() => { - request(Object.assign({ url: Parse.serverURL + '/classes/TestObject' }, options)).then( - resp => { - equal(resp.data.results.length, 1); - done(); - } - ); + request( + Object.assign({ url: Parse.serverURL + "/classes/TestObject" }, options) + ).then(resp => { + equal(resp.data.results.length, 1); + done(); + }); }); }); - it('where $eq null queries (rest)', done => { + it("where $eq null queries (rest)", done => { const options = Object.assign({}, masterKeyOptions, { qs: { where: JSON.stringify({ field: { $eq: null } }), @@ -1308,22 +1356,24 @@ describe('Parse.Query testing', () => { const obj1 = new TestObject({ field: false }); const obj2 = new TestObject({ field: null }); Parse.Object.saveAll([obj1, obj2]).then(() => { - return request(Object.assign({ url: Parse.serverURL + '/classes/TestObject' }, options)).then( - resp => { - equal(resp.data.results.length, 1); - done(); - } - ); + return request( + Object.assign({ url: Parse.serverURL + "/classes/TestObject" }, options) + ).then(resp => { + equal(resp.data.results.length, 1); + done(); + }); }); }); - it('containedIn queries', function (done) { + it("containedIn queries", function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; - Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber)).then(function () { + Parse.Object.saveAll( + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) + ).then(function () { const query = new Parse.Query(BoxedNumber); - query.containedIn('number', [3, 5, 7, 9, 11]); + query.containedIn("number", [3, 5, 7, 9, 11]); query.find().then(function (results) { equal(results.length, 4); done(); @@ -1331,7 +1381,7 @@ describe('Parse.Query testing', () => { }); }); - it('containedIn false queries', done => { + it("containedIn false queries", done => { const makeBoxedNumber = i => { return new BoxedNumber({ number: i }); }; @@ -1340,18 +1390,18 @@ describe('Parse.Query testing', () => { Parse.Object.saveAll(boxedNumbers) .then(() => { const query = new Parse.Query(BoxedNumber); - query.containedIn('number', false); + query.containedIn("number", false); return query.find(); }) .then(done.fail) .catch(error => { equal(error.code, Parse.Error.INVALID_JSON); - equal(error.message, 'bad $in value'); + equal(error.message, "bad $in value"); done(); }); }); - it('notContainedIn false queries', done => { + it("notContainedIn false queries", done => { const makeBoxedNumber = i => { return new BoxedNumber({ number: i }); }; @@ -1360,24 +1410,26 @@ describe('Parse.Query testing', () => { Parse.Object.saveAll(boxedNumbers) .then(() => { const query = new Parse.Query(BoxedNumber); - query.notContainedIn('number', false); + query.notContainedIn("number", false); return query.find(); }) .then(done.fail) .catch(error => { equal(error.code, Parse.Error.INVALID_JSON); - equal(error.message, 'bad $nin value'); + equal(error.message, "bad $nin value"); done(); }); }); - it('notContainedIn queries', function (done) { + it("notContainedIn queries", function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; - Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber)).then(function () { + Parse.Object.saveAll( + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) + ).then(function () { const query = new Parse.Query(BoxedNumber); - query.notContainedIn('number', [3, 5, 7, 9, 11]); + query.notContainedIn("number", [3, 5, 7, 9, 11]); query.find().then(function (results) { equal(results.length, 6); done(); @@ -1385,53 +1437,64 @@ describe('Parse.Query testing', () => { }); }); - it('objectId containedIn queries', function (done) { + it("objectId containedIn queries", function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; - Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber)).then(function (list) { + Parse.Object.saveAll( + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) + ).then(function (list) { const query = new Parse.Query(BoxedNumber); - query.containedIn('objectId', [list[2].id, list[3].id, list[0].id, 'NONSENSE']); - query.ascending('number'); + query.containedIn("objectId", [ + list[2].id, + list[3].id, + list[0].id, + "NONSENSE", + ]); + query.ascending("number"); query.find().then(function (results) { if (results.length != 3) { - fail('expected 3 results'); + fail("expected 3 results"); } else { - equal(results[0].get('number'), 0); - equal(results[1].get('number'), 2); - equal(results[2].get('number'), 3); + equal(results[0].get("number"), 0); + equal(results[1].get("number"), 2); + equal(results[2].get("number"), 3); } done(); }); }); }); - it('objectId equalTo queries', function (done) { + it("objectId equalTo queries", function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; - Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber)).then(function (list) { + Parse.Object.saveAll( + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) + ).then(function (list) { const query = new Parse.Query(BoxedNumber); - query.equalTo('objectId', list[4].id); + query.equalTo("objectId", list[4].id); query.find().then(function (results) { if (results.length != 1) { - fail('expected 1 result'); + fail("expected 1 result"); done(); } else { - equal(results[0].get('number'), 4); + equal(results[0].get("number"), 4); } done(); }); }); }); - it('find no elements', function (done) { + it("find no elements", function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; - Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber)).then(function () { + Parse.Object.saveAll( + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) + ).then(function () { const query = new Parse.Query(BoxedNumber); - query.equalTo('number', 17); + query.equalTo("number", 17); query.find().then(function (results) { equal(results.length, 0); done(); @@ -1439,9 +1502,9 @@ describe('Parse.Query testing', () => { }); }); - it('find with error', function (done) { + it("find with error", function (done) { const query = new Parse.Query(BoxedNumber); - query.equalTo('$foo', 'bar'); + query.equalTo("$foo", "bar"); query .find() .then(done.fail) @@ -1449,62 +1512,68 @@ describe('Parse.Query testing', () => { .then(done); }); - it('get', function (done) { - Parse.Object.saveAll([new TestObject({ foo: 'bar' })]).then(function (items) { - ok(items[0]); - const objectId = items[0].id; - const query = new Parse.Query(TestObject); - query.get(objectId).then(function (result) { - ok(result); - equal(result.id, objectId); - equal(result.get('foo'), 'bar'); - ok(result.createdAt instanceof Date); - ok(result.updatedAt instanceof Date); - done(); - }); - }); + it("get", function (done) { + Parse.Object.saveAll([new TestObject({ foo: "bar" })]).then( + function (items) { + ok(items[0]); + const objectId = items[0].id; + const query = new Parse.Query(TestObject); + query.get(objectId).then(function (result) { + ok(result); + equal(result.id, objectId); + equal(result.get("foo"), "bar"); + ok(result.createdAt instanceof Date); + ok(result.updatedAt instanceof Date); + done(); + }); + } + ); }); - it('get undefined', function (done) { - Parse.Object.saveAll([new TestObject({ foo: 'bar' })]).then(function (items) { - ok(items[0]); - const query = new Parse.Query(TestObject); - query.get(undefined).then(fail, () => done()); - }); + it("get undefined", function (done) { + Parse.Object.saveAll([new TestObject({ foo: "bar" })]).then( + function (items) { + ok(items[0]); + const query = new Parse.Query(TestObject); + query.get(undefined).then(fail, () => done()); + } + ); }); - it('get error', function (done) { - Parse.Object.saveAll([new TestObject({ foo: 'bar' })]).then(function (items) { - ok(items[0]); - const query = new Parse.Query(TestObject); - query.get('InvalidObjectID').then( - function () { - ok(false, 'The get should have failed.'); - done(); - }, - function (error) { - equal(error.code, Parse.Error.OBJECT_NOT_FOUND); - done(); - } - ); - }); + it("get error", function (done) { + Parse.Object.saveAll([new TestObject({ foo: "bar" })]).then( + function (items) { + ok(items[0]); + const query = new Parse.Query(TestObject); + query.get("InvalidObjectID").then( + function () { + ok(false, "The get should have failed."); + done(); + }, + function (error) { + equal(error.code, Parse.Error.OBJECT_NOT_FOUND); + done(); + } + ); + } + ); }); - it('first', function (done) { - Parse.Object.saveAll([new TestObject({ foo: 'bar' })]).then(function () { + it("first", function (done) { + Parse.Object.saveAll([new TestObject({ foo: "bar" })]).then(function () { const query = new Parse.Query(TestObject); - query.equalTo('foo', 'bar'); + query.equalTo("foo", "bar"); query.first().then(function (result) { - equal(result.get('foo'), 'bar'); + equal(result.get("foo"), "bar"); done(); }); }); }); - it('first no result', function (done) { - Parse.Object.saveAll([new TestObject({ foo: 'bar' })]).then(function () { + it("first no result", function (done) { + Parse.Object.saveAll([new TestObject({ foo: "bar" })]).then(function () { const query = new Parse.Query(TestObject); - query.equalTo('foo', 'baz'); + query.equalTo("foo", "baz"); query.first().then(function (result) { equal(result, undefined); done(); @@ -1512,22 +1581,23 @@ describe('Parse.Query testing', () => { }); }); - it('first with two results', function (done) { - Parse.Object.saveAll([new TestObject({ foo: 'bar' }), new TestObject({ foo: 'bar' })]).then( - function () { - const query = new Parse.Query(TestObject); - query.equalTo('foo', 'bar'); - query.first().then(function (result) { - equal(result.get('foo'), 'bar'); - done(); - }); - } - ); + it("first with two results", function (done) { + Parse.Object.saveAll([ + new TestObject({ foo: "bar" }), + new TestObject({ foo: "bar" }), + ]).then(function () { + const query = new Parse.Query(TestObject); + query.equalTo("foo", "bar"); + query.first().then(function (result) { + equal(result.get("foo"), "bar"); + done(); + }); + }); }); - it('first with error', function (done) { + it("first with error", function (done) { const query = new Parse.Query(BoxedNumber); - query.equalTo('$foo', 'bar'); + query.equalTo("$foo", "bar"); query .first() .then(done.fail) @@ -1536,64 +1606,72 @@ describe('Parse.Query testing', () => { }); const Container = Parse.Object.extend({ - className: 'Container', + className: "Container", }); - it('notEqualTo object', function (done) { + it("notEqualTo object", function (done) { const item1 = new TestObject(); const item2 = new TestObject(); const container1 = new Container({ item: item1 }); const container2 = new Container({ item: item2 }); - Parse.Object.saveAll([item1, item2, container1, container2]).then(function () { - const query = new Parse.Query(Container); - query.notEqualTo('item', item1); - query.find().then(function (results) { - equal(results.length, 1); - done(); - }); - }); + Parse.Object.saveAll([item1, item2, container1, container2]).then( + function () { + const query = new Parse.Query(Container); + query.notEqualTo("item", item1); + query.find().then(function (results) { + equal(results.length, 1); + done(); + }); + } + ); }); - it('skip', function (done) { - Parse.Object.saveAll([new TestObject(), new TestObject()]).then(function () { - const query = new Parse.Query(TestObject); - query.skip(1); - query.find().then(function (results) { - equal(results.length, 1); - query.skip(3); + it("skip", function (done) { + Parse.Object.saveAll([new TestObject(), new TestObject()]).then( + function () { + const query = new Parse.Query(TestObject); + query.skip(1); query.find().then(function (results) { - equal(results.length, 0); - done(); + equal(results.length, 1); + query.skip(3); + query.find().then(function (results) { + equal(results.length, 0); + done(); + }); }); - }); - }); + } + ); }); it("skip doesn't affect count", function (done) { - Parse.Object.saveAll([new TestObject(), new TestObject()]).then(function () { - const query = new Parse.Query(TestObject); - query.count().then(function (count) { - equal(count, 2); - query.skip(1); + Parse.Object.saveAll([new TestObject(), new TestObject()]).then( + function () { + const query = new Parse.Query(TestObject); query.count().then(function (count) { equal(count, 2); - query.skip(3); + query.skip(1); query.count().then(function (count) { equal(count, 2); - done(); + query.skip(3); + query.count().then(function (count) { + equal(count, 2); + done(); + }); }); }); - }); - }); + } + ); }); - it('count', function (done) { + it("count", function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; - Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber)).then(function () { + Parse.Object.saveAll( + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) + ).then(function () { const query = new Parse.Query(BoxedNumber); - query.greaterThan('number', 1); + query.greaterThan("number", 1); query.count().then(function (count) { equal(count, 8); done(); @@ -1601,84 +1679,90 @@ describe('Parse.Query testing', () => { }); }); - it('order by ascending number', function (done) { + it("order by ascending number", function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; Parse.Object.saveAll([3, 1, 2].map(makeBoxedNumber)).then(function () { const query = new Parse.Query(BoxedNumber); - query.ascending('number'); + query.ascending("number"); query.find().then(function (results) { equal(results.length, 3); - equal(results[0].get('number'), 1); - equal(results[1].get('number'), 2); - equal(results[2].get('number'), 3); + equal(results[0].get("number"), 1); + equal(results[1].get("number"), 2); + equal(results[2].get("number"), 3); done(); }); }); }); - it('order by descending number', function (done) { + it("order by descending number", function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; Parse.Object.saveAll([3, 1, 2].map(makeBoxedNumber)).then(function () { const query = new Parse.Query(BoxedNumber); - query.descending('number'); + query.descending("number"); query.find().then(function (results) { equal(results.length, 3); - equal(results[0].get('number'), 3); - equal(results[1].get('number'), 2); - equal(results[2].get('number'), 1); + equal(results[0].get("number"), 3); + equal(results[1].get("number"), 2); + equal(results[2].get("number"), 1); done(); }); }); }); - it('can order on an object string field', function (done) { + it("can order on an object string field", function (done) { const testSet = [ - { sortField: { value: 'Z' } }, - { sortField: { value: 'A' } }, - { sortField: { value: 'M' } }, + { sortField: { value: "Z" } }, + { sortField: { value: "A" } }, + { sortField: { value: "M" } }, ]; - const objects = testSet.map(e => new Parse.Object('Test', e)); + const objects = testSet.map(e => new Parse.Object("Test", e)); Parse.Object.saveAll(objects) - .then(() => new Parse.Query('Test').addDescending('sortField.value').first()) + .then(() => + new Parse.Query("Test").addDescending("sortField.value").first() + ) .then(result => { - expect(result.get('sortField').value).toBe('Z'); - return new Parse.Query('Test').addAscending('sortField.value').first(); + expect(result.get("sortField").value).toBe("Z"); + return new Parse.Query("Test").addAscending("sortField.value").first(); }) .then(result => { - expect(result.get('sortField').value).toBe('A'); + expect(result.get("sortField").value).toBe("A"); done(); }) .catch(done.fail); }); - it('can order on an object string field (level 2)', function (done) { + it("can order on an object string field (level 2)", function (done) { const testSet = [ - { sortField: { value: { field: 'Z' } } }, - { sortField: { value: { field: 'A' } } }, - { sortField: { value: { field: 'M' } } }, + { sortField: { value: { field: "Z" } } }, + { sortField: { value: { field: "A" } } }, + { sortField: { value: { field: "M" } } }, ]; - const objects = testSet.map(e => new Parse.Object('Test', e)); + const objects = testSet.map(e => new Parse.Object("Test", e)); Parse.Object.saveAll(objects) - .then(() => new Parse.Query('Test').addDescending('sortField.value.field').first()) + .then(() => + new Parse.Query("Test").addDescending("sortField.value.field").first() + ) .then(result => { - expect(result.get('sortField').value.field).toBe('Z'); - return new Parse.Query('Test').addAscending('sortField.value.field').first(); + expect(result.get("sortField").value.field).toBe("Z"); + return new Parse.Query("Test") + .addAscending("sortField.value.field") + .first(); }) .then(result => { - expect(result.get('sortField').value.field).toBe('A'); + expect(result.get("sortField").value.field).toBe("A"); done(); }) .catch(done.fail); }); - it_id('65c8238d-cf02-49d0-a919-8a17f5a58280')(it)( - 'can order on an object number field', + it_id("65c8238d-cf02-49d0-a919-8a17f5a58280")(it)( + "can order on an object number field", function (done) { const testSet = [ { sortField: { value: 10 } }, @@ -1686,23 +1770,27 @@ describe('Parse.Query testing', () => { { sortField: { value: 5 } }, ]; - const objects = testSet.map(e => new Parse.Object('Test', e)); + const objects = testSet.map(e => new Parse.Object("Test", e)); Parse.Object.saveAll(objects) - .then(() => new Parse.Query('Test').addDescending('sortField.value').first()) + .then(() => + new Parse.Query("Test").addDescending("sortField.value").first() + ) .then(result => { - expect(result.get('sortField').value).toBe(10); - return new Parse.Query('Test').addAscending('sortField.value').first(); + expect(result.get("sortField").value).toBe(10); + return new Parse.Query("Test") + .addAscending("sortField.value") + .first(); }) .then(result => { - expect(result.get('sortField').value).toBe(1); + expect(result.get("sortField").value).toBe(1); done(); }) .catch(done.fail); } ); - it_id('d8f0bead-b931-4d66-8b0c-28c5705e463c')(it)( - 'can order on an object number field (level 2)', + it_id("d8f0bead-b931-4d66-8b0c-28c5705e463c")(it)( + "can order on an object number field (level 2)", function (done) { const testSet = [ { sortField: { value: { field: 10 } } }, @@ -1710,56 +1798,60 @@ describe('Parse.Query testing', () => { { sortField: { value: { field: 5 } } }, ]; - const objects = testSet.map(e => new Parse.Object('Test', e)); + const objects = testSet.map(e => new Parse.Object("Test", e)); Parse.Object.saveAll(objects) - .then(() => new Parse.Query('Test').addDescending('sortField.value.field').first()) + .then(() => + new Parse.Query("Test").addDescending("sortField.value.field").first() + ) .then(result => { - expect(result.get('sortField').value.field).toBe(10); - return new Parse.Query('Test').addAscending('sortField.value.field').first(); + expect(result.get("sortField").value.field).toBe(10); + return new Parse.Query("Test") + .addAscending("sortField.value.field") + .first(); }) .then(result => { - expect(result.get('sortField').value.field).toBe(1); + expect(result.get("sortField").value.field).toBe(1); done(); }) .catch(done.fail); } ); - it('order by ascending number then descending string', function (done) { - const strings = ['a', 'b', 'c', 'd']; + it("order by ascending number then descending string", function (done) { + const strings = ["a", "b", "c", "d"]; const makeBoxedNumber = function (num, i) { return new BoxedNumber({ number: num, string: strings[i] }); }; Parse.Object.saveAll([3, 1, 3, 2].map(makeBoxedNumber)).then(function () { const query = new Parse.Query(BoxedNumber); - query.ascending('number').addDescending('string'); + query.ascending("number").addDescending("string"); query.find().then(function (results) { equal(results.length, 4); - equal(results[0].get('number'), 1); - equal(results[0].get('string'), 'b'); - equal(results[1].get('number'), 2); - equal(results[1].get('string'), 'd'); - equal(results[2].get('number'), 3); - equal(results[2].get('string'), 'c'); - equal(results[3].get('number'), 3); - equal(results[3].get('string'), 'a'); + equal(results[0].get("number"), 1); + equal(results[0].get("string"), "b"); + equal(results[1].get("number"), 2); + equal(results[1].get("string"), "d"); + equal(results[2].get("number"), 3); + equal(results[2].get("string"), "c"); + equal(results[3].get("number"), 3); + equal(results[3].get("string"), "a"); done(); }); }); }); - it('order by non-existing string', async () => { - const strings = ['a', 'b', 'c', 'd']; + it("order by non-existing string", async () => { + const strings = ["a", "b", "c", "d"]; const makeBoxedNumber = function (num, i) { return new BoxedNumber({ number: num, string: strings[i] }); }; await Parse.Object.saveAll([3, 1, 3, 2].map(makeBoxedNumber)); - const results = await new Parse.Query(BoxedNumber).ascending('foo').find(); + const results = await new Parse.Query(BoxedNumber).ascending("foo").find(); expect(results.length).toBe(4); }); - it('order by descending number then ascending string', function (done) { - const strings = ['a', 'b', 'c', 'd']; + it("order by descending number then ascending string", function (done) { + const strings = ["a", "b", "c", "d"]; const makeBoxedNumber = function (num, i) { return new BoxedNumber({ number: num, string: strings[i] }); }; @@ -1768,20 +1860,20 @@ describe('Parse.Query testing', () => { Parse.Object.saveAll(objects) .then(() => { const query = new Parse.Query(BoxedNumber); - query.descending('number').addAscending('string'); + query.descending("number").addAscending("string"); return query.find(); }) .then( results => { equal(results.length, 4); - equal(results[0].get('number'), 3); - equal(results[0].get('string'), 'a'); - equal(results[1].get('number'), 3); - equal(results[1].get('string'), 'c'); - equal(results[2].get('number'), 2); - equal(results[2].get('string'), 'd'); - equal(results[3].get('number'), 1); - equal(results[3].get('string'), 'b'); + equal(results[0].get("number"), 3); + equal(results[0].get("string"), "a"); + equal(results[1].get("number"), 3); + equal(results[1].get("string"), "c"); + equal(results[2].get("number"), 2); + equal(results[2].get("string"), "d"); + equal(results[3].get("number"), 1); + equal(results[3].get("string"), "b"); done(); }, err => { @@ -1791,48 +1883,48 @@ describe('Parse.Query testing', () => { ); }); - it('order by descending number and string', function (done) { - const strings = ['a', 'b', 'c', 'd']; + it("order by descending number and string", function (done) { + const strings = ["a", "b", "c", "d"]; const makeBoxedNumber = function (num, i) { return new BoxedNumber({ number: num, string: strings[i] }); }; Parse.Object.saveAll([3, 1, 3, 2].map(makeBoxedNumber)).then(function () { const query = new Parse.Query(BoxedNumber); - query.descending('number,string'); + query.descending("number,string"); query.find().then(function (results) { equal(results.length, 4); - equal(results[0].get('number'), 3); - equal(results[0].get('string'), 'c'); - equal(results[1].get('number'), 3); - equal(results[1].get('string'), 'a'); - equal(results[2].get('number'), 2); - equal(results[2].get('string'), 'd'); - equal(results[3].get('number'), 1); - equal(results[3].get('string'), 'b'); + equal(results[0].get("number"), 3); + equal(results[0].get("string"), "c"); + equal(results[1].get("number"), 3); + equal(results[1].get("string"), "a"); + equal(results[2].get("number"), 2); + equal(results[2].get("string"), "d"); + equal(results[3].get("number"), 1); + equal(results[3].get("string"), "b"); done(); }); }); }); - it('order by descending number and string, with space', function (done) { - const strings = ['a', 'b', 'c', 'd']; + it("order by descending number and string, with space", function (done) { + const strings = ["a", "b", "c", "d"]; const makeBoxedNumber = function (num, i) { return new BoxedNumber({ number: num, string: strings[i] }); }; Parse.Object.saveAll([3, 1, 3, 2].map(makeBoxedNumber)).then( function () { const query = new Parse.Query(BoxedNumber); - query.descending('number, string'); + query.descending("number, string"); query.find().then(function (results) { equal(results.length, 4); - equal(results[0].get('number'), 3); - equal(results[0].get('string'), 'c'); - equal(results[1].get('number'), 3); - equal(results[1].get('string'), 'a'); - equal(results[2].get('number'), 2); - equal(results[2].get('string'), 'd'); - equal(results[3].get('number'), 1); - equal(results[3].get('string'), 'b'); + equal(results[0].get("number"), 3); + equal(results[0].get("string"), "c"); + equal(results[1].get("number"), 3); + equal(results[1].get("string"), "a"); + equal(results[2].get("number"), 2); + equal(results[2].get("string"), "d"); + equal(results[3].get("number"), 1); + equal(results[3].get("string"), "b"); done(); }); }, @@ -1843,47 +1935,47 @@ describe('Parse.Query testing', () => { ); }); - it('order by descending number and string, with array arg', function (done) { - const strings = ['a', 'b', 'c', 'd']; + it("order by descending number and string, with array arg", function (done) { + const strings = ["a", "b", "c", "d"]; const makeBoxedNumber = function (num, i) { return new BoxedNumber({ number: num, string: strings[i] }); }; Parse.Object.saveAll([3, 1, 3, 2].map(makeBoxedNumber)).then(function () { const query = new Parse.Query(BoxedNumber); - query.descending(['number', 'string']); + query.descending(["number", "string"]); query.find().then(function (results) { equal(results.length, 4); - equal(results[0].get('number'), 3); - equal(results[0].get('string'), 'c'); - equal(results[1].get('number'), 3); - equal(results[1].get('string'), 'a'); - equal(results[2].get('number'), 2); - equal(results[2].get('string'), 'd'); - equal(results[3].get('number'), 1); - equal(results[3].get('string'), 'b'); + equal(results[0].get("number"), 3); + equal(results[0].get("string"), "c"); + equal(results[1].get("number"), 3); + equal(results[1].get("string"), "a"); + equal(results[2].get("number"), 2); + equal(results[2].get("string"), "d"); + equal(results[3].get("number"), 1); + equal(results[3].get("string"), "b"); done(); }); }); }); - it('order by descending number and string, with multiple args', function (done) { - const strings = ['a', 'b', 'c', 'd']; + it("order by descending number and string, with multiple args", function (done) { + const strings = ["a", "b", "c", "d"]; const makeBoxedNumber = function (num, i) { return new BoxedNumber({ number: num, string: strings[i] }); }; Parse.Object.saveAll([3, 1, 3, 2].map(makeBoxedNumber)).then(function () { const query = new Parse.Query(BoxedNumber); - query.descending('number', 'string'); + query.descending("number", "string"); query.find().then(function (results) { equal(results.length, 4); - equal(results[0].get('number'), 3); - equal(results[0].get('string'), 'c'); - equal(results[1].get('number'), 3); - equal(results[1].get('string'), 'a'); - equal(results[2].get('number'), 2); - equal(results[2].get('string'), 'd'); - equal(results[3].get('number'), 1); - equal(results[3].get('string'), 'b'); + equal(results[0].get("number"), 3); + equal(results[0].get("string"), "c"); + equal(results[1].get("number"), 3); + equal(results[1].get("string"), "a"); + equal(results[2].get("number"), 2); + equal(results[2].get("string"), "d"); + equal(results[3].get("number"), 1); + equal(results[3].get("string"), "b"); done(); }); }); @@ -1895,7 +1987,7 @@ describe('Parse.Query testing', () => { }; Parse.Object.saveAll([3, 1, 2].map(makeBoxedNumber)).then(function () { const query = new Parse.Query(BoxedNumber); - query.ascending('_password'); + query.ascending("_password"); query .find() .then(done.fail) @@ -1904,7 +1996,7 @@ describe('Parse.Query testing', () => { }); }); - it('order by _created_at', function (done) { + it("order by _created_at", function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; @@ -1919,18 +2011,18 @@ describe('Parse.Query testing', () => { }) .then(function () { const query = new Parse.Query(BoxedNumber); - query.ascending('_created_at'); + query.ascending("_created_at"); query.find().then(function (results) { equal(results.length, 3); - equal(results[0].get('number'), 3); - equal(results[1].get('number'), 1); - equal(results[2].get('number'), 2); + equal(results[0].get("number"), 3); + equal(results[1].get("number"), 1); + equal(results[2].get("number"), 2); done(); }, done.fail); }); }); - it('order by createdAt', function (done) { + it("order by createdAt", function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; @@ -1945,18 +2037,18 @@ describe('Parse.Query testing', () => { }) .then(function () { const query = new Parse.Query(BoxedNumber); - query.descending('createdAt'); + query.descending("createdAt"); query.find().then(function (results) { equal(results.length, 3); - equal(results[0].get('number'), 2); - equal(results[1].get('number'), 1); - equal(results[2].get('number'), 3); + equal(results[0].get("number"), 2); + equal(results[1].get("number"), 1); + equal(results[2].get("number"), 3); done(); }); }); }); - it('order by _updated_at', function (done) { + it("order by _updated_at", function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; @@ -1970,22 +2062,22 @@ describe('Parse.Query testing', () => { return numbers[2].save(); }) .then(function () { - numbers[1].set('number', 4); + numbers[1].set("number", 4); numbers[1].save().then(function () { const query = new Parse.Query(BoxedNumber); - query.ascending('_updated_at'); + query.ascending("_updated_at"); query.find().then(function (results) { equal(results.length, 3); - equal(results[0].get('number'), 3); - equal(results[1].get('number'), 2); - equal(results[2].get('number'), 4); + equal(results[0].get("number"), 3); + equal(results[1].get("number"), 2); + equal(results[2].get("number"), 4); done(); }); }); }); }); - it('order by updatedAt', function (done) { + it("order by updatedAt", function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; @@ -1999,15 +2091,15 @@ describe('Parse.Query testing', () => { return numbers[2].save(); }) .then(function () { - numbers[1].set('number', 4); + numbers[1].set("number", 4); numbers[1].save().then(function () { const query = new Parse.Query(BoxedNumber); - query.descending('_updated_at'); + query.descending("_updated_at"); query.find().then(function (results) { equal(results.length, 3); - equal(results[0].get('number'), 4); - equal(results[1].get('number'), 2); - equal(results[2].get('number'), 3); + equal(results[0].get("number"), 4); + equal(results[1].get("number"), 2); + equal(results[2].get("number"), 3); done(); }); }); @@ -2018,7 +2110,7 @@ describe('Parse.Query testing', () => { function makeTimeObject(start, i) { const time = new Date(); time.setSeconds(start.getSeconds() + i); - const item = new TestObject({ name: 'item' + i, time: time }); + const item = new TestObject({ name: "item" + i, time: time }); return item.save(); } @@ -2041,22 +2133,22 @@ describe('Parse.Query testing', () => { }); } - it('time equality', function (done) { + it("time equality", function (done) { makeThreeTimeObjects().then(function (list) { const query = new Parse.Query(TestObject); - query.equalTo('time', list[1].get('time')); + query.equalTo("time", list[1].get("time")); query.find().then(function (results) { equal(results.length, 1); - equal(results[0].get('name'), 'item2'); + equal(results[0].get("name"), "item2"); done(); }); }); }); - it('time lessThan', function (done) { + it("time lessThan", function (done) { makeThreeTimeObjects().then(function (list) { const query = new Parse.Query(TestObject); - query.lessThan('time', list[2].get('time')); + query.lessThan("time", list[2].get("time")); query.find().then(function (results) { equal(results.length, 2); done(); @@ -2065,10 +2157,10 @@ describe('Parse.Query testing', () => { }); // This test requires Date objects to be consistently stored as a Date. - it('time createdAt', function (done) { + it("time createdAt", function (done) { makeThreeTimeObjects().then(function (list) { const query = new Parse.Query(TestObject); - query.greaterThanOrEqualTo('createdAt', list[0].createdAt); + query.greaterThanOrEqualTo("createdAt", list[0].createdAt); query.find().then(function (results) { equal(results.length, 3); done(); @@ -2076,14 +2168,14 @@ describe('Parse.Query testing', () => { }); }); - it('matches string', function (done) { + it("matches string", function (done) { const thing1 = new TestObject(); - thing1.set('myString', 'football'); + thing1.set("myString", "football"); const thing2 = new TestObject(); - thing2.set('myString', 'soccer'); + thing2.set("myString", "soccer"); Parse.Object.saveAll([thing1, thing2]).then(function () { const query = new Parse.Query(TestObject); - query.matches('myString', '^fo*\\wb[^o]l+$'); + query.matches("myString", "^fo*\\wb[^o]l+$"); query.find().then(function (results) { equal(results.length, 1); done(); @@ -2091,14 +2183,14 @@ describe('Parse.Query testing', () => { }); }); - it('matches regex', function (done) { + it("matches regex", function (done) { const thing1 = new TestObject(); - thing1.set('myString', 'football'); + thing1.set("myString", "football"); const thing2 = new TestObject(); - thing2.set('myString', 'soccer'); + thing2.set("myString", "soccer"); Parse.Object.saveAll([thing1, thing2]).then(function () { const query = new Parse.Query(TestObject); - query.matches('myString', /^fo*\wb[^o]l+$/); + query.matches("myString", /^fo*\wb[^o]l+$/); query.find().then(function (results) { equal(results.length, 1); done(); @@ -2106,19 +2198,19 @@ describe('Parse.Query testing', () => { }); }); - it('case insensitive regex success', function (done) { + it("case insensitive regex success", function (done) { const thing = new TestObject(); - thing.set('myString', 'football'); + thing.set("myString", "football"); Parse.Object.saveAll([thing]).then(function () { const query = new Parse.Query(TestObject); - query.matches('myString', 'FootBall', 'i'); + query.matches("myString", "FootBall", "i"); query.find().then(done); }); }); - it('regexes with invalid options fail', function (done) { + it("regexes with invalid options fail", function (done) { const query = new Parse.Query(TestObject); - query.matches('myString', 'FootBall', 'some invalid option'); + query.matches("myString", "FootBall", "some invalid option"); query .find() .then(done.fail) @@ -2126,19 +2218,19 @@ describe('Parse.Query testing', () => { .then(done); }); - it_id('823852f6-1de5-45ba-a2b9-ed952fcc6012')(it)( - 'Use a regex that requires all modifiers', + it_id("823852f6-1de5-45ba-a2b9-ed952fcc6012")(it)( + "Use a regex that requires all modifiers", function (done) { const thing = new TestObject(); - thing.set('myString', 'PArSe\nCom'); + thing.set("myString", "PArSe\nCom"); Parse.Object.saveAll([thing]).then(function () { const query = new Parse.Query(TestObject); query.matches( - 'myString', + "myString", "parse # First fragment. We'll write this in one case but match insensitively\n" + - '.com # Second fragment. This can be separated by any character, including newline;' + - 'however, this comment must end with a newline to recognize it as a comment\n', - 'mixs' + ".com # Second fragment. This can be separated by any character, including newline;" + + "however, this comment must end with a newline to recognize it as a comment\n", + "mixs" ); query.find().then( function (results) { @@ -2154,12 +2246,12 @@ describe('Parse.Query testing', () => { } ); - it('Regular expression constructor includes modifiers inline', function (done) { + it("Regular expression constructor includes modifiers inline", function (done) { const thing = new TestObject(); - thing.set('myString', '\n\nbuffer\n\nparse.COM'); + thing.set("myString", "\n\nbuffer\n\nparse.COM"); Parse.Object.saveAll([thing]).then(function () { const query = new Parse.Query(TestObject); - query.matches('myString', /parse\.com/im); + query.matches("myString", /parse\.com/im); query.find().then(function (results) { equal(results.length, 1); done(); @@ -2171,15 +2263,15 @@ describe('Parse.Query testing', () => { "\\E' !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTU" + "VWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'"; - it('contains', function (done) { + it("contains", function (done) { Parse.Object.saveAll([ - new TestObject({ myString: 'zax' + someAscii + 'qub' }), - new TestObject({ myString: 'start' + someAscii }), - new TestObject({ myString: someAscii + 'end' }), + new TestObject({ myString: "zax" + someAscii + "qub" }), + new TestObject({ myString: "start" + someAscii }), + new TestObject({ myString: someAscii + "end" }), new TestObject({ myString: someAscii }), ]).then(function () { const query = new Parse.Query(TestObject); - query.contains('myString', someAscii); + query.contains("myString", someAscii); query.find().then(function (results) { equal(results.length, 4); done(); @@ -2187,17 +2279,17 @@ describe('Parse.Query testing', () => { }); }); - it('nested contains', done => { - const sender1 = { group: ['A', 'B'] }; - const sender2 = { group: ['A', 'C'] }; - const sender3 = { group: ['B', 'C'] }; + it("nested contains", done => { + const sender1 = { group: ["A", "B"] }; + const sender2 = { group: ["A", "C"] }; + const sender3 = { group: ["B", "C"] }; const obj1 = new TestObject({ sender: sender1 }); const obj2 = new TestObject({ sender: sender2 }); const obj3 = new TestObject({ sender: sender3 }); Parse.Object.saveAll([obj1, obj2, obj3]) .then(() => { const query = new Parse.Query(TestObject); - query.contains('sender.group', 'A'); + query.contains("sender.group", "A"); return query.find(); }) .then(results => { @@ -2206,15 +2298,15 @@ describe('Parse.Query testing', () => { }, done.fail); }); - it('startsWith', function (done) { + it("startsWith", function (done) { Parse.Object.saveAll([ - new TestObject({ myString: 'zax' + someAscii + 'qub' }), - new TestObject({ myString: 'start' + someAscii }), - new TestObject({ myString: someAscii + 'end' }), + new TestObject({ myString: "zax" + someAscii + "qub" }), + new TestObject({ myString: "start" + someAscii }), + new TestObject({ myString: someAscii + "end" }), new TestObject({ myString: someAscii }), ]).then(function () { const query = new Parse.Query(TestObject); - query.startsWith('myString', someAscii); + query.startsWith("myString", someAscii); query.find().then(function (results) { equal(results.length, 2); done(); @@ -2222,15 +2314,15 @@ describe('Parse.Query testing', () => { }); }); - it('endsWith', function (done) { + it("endsWith", function (done) { Parse.Object.saveAll([ - new TestObject({ myString: 'zax' + someAscii + 'qub' }), - new TestObject({ myString: 'start' + someAscii }), - new TestObject({ myString: someAscii + 'end' }), + new TestObject({ myString: "zax" + someAscii + "qub" }), + new TestObject({ myString: "start" + someAscii }), + new TestObject({ myString: someAscii + "end" }), new TestObject({ myString: someAscii }), ]).then(function () { const query = new Parse.Query(TestObject); - query.endsWith('myString', someAscii); + query.endsWith("myString", someAscii); query.find().then(function (results) { equal(results.length, 2); done(); @@ -2238,102 +2330,102 @@ describe('Parse.Query testing', () => { }); }); - it('exists', function (done) { + it("exists", function (done) { const objects = []; for (const i of [0, 1, 2, 3, 4, 5, 6, 7, 8]) { const item = new TestObject(); if (i % 2 === 0) { - item.set('x', i + 1); + item.set("x", i + 1); } else { - item.set('y', i + 1); + item.set("y", i + 1); } objects.push(item); } Parse.Object.saveAll(objects).then(function () { const query = new Parse.Query(TestObject); - query.exists('x'); + query.exists("x"); query.find().then(function (results) { equal(results.length, 5); for (const result of results) { - ok(result.get('x')); + ok(result.get("x")); } done(); }); }); }); - it('doesNotExist', function (done) { + it("doesNotExist", function (done) { const objects = []; for (const i of [0, 1, 2, 3, 4, 5, 6, 7, 8]) { const item = new TestObject(); if (i % 2 === 0) { - item.set('x', i + 1); + item.set("x", i + 1); } else { - item.set('y', i + 1); + item.set("y", i + 1); } objects.push(item); } Parse.Object.saveAll(objects).then(function () { const query = new Parse.Query(TestObject); - query.doesNotExist('x'); + query.doesNotExist("x"); query.find().then(function (results) { equal(results.length, 4); for (const result of results) { - ok(result.get('y')); + ok(result.get("y")); } done(); }); }); }); - it('exists relation', function (done) { + it("exists relation", function (done) { const objects = []; for (const i of [0, 1, 2, 3, 4, 5, 6, 7, 8]) { const container = new Container(); if (i % 2 === 0) { const item = new TestObject(); - item.set('x', i); - container.set('x', item); + item.set("x", i); + container.set("x", item); objects.push(item); } else { - container.set('y', i); + container.set("y", i); } objects.push(container); } Parse.Object.saveAll(objects).then(function () { const query = new Parse.Query(Container); - query.exists('x'); + query.exists("x"); query.find().then(function (results) { equal(results.length, 5); for (const result of results) { - ok(result.get('x')); + ok(result.get("x")); } done(); }); }); }); - it('doesNotExist relation', function (done) { + it("doesNotExist relation", function (done) { const objects = []; for (const i of [0, 1, 2, 3, 4, 5, 6, 7]) { const container = new Container(); if (i % 2 === 0) { const item = new TestObject(); - item.set('x', i); - container.set('x', item); + item.set("x", i); + container.set("x", item); objects.push(item); } else { - container.set('y', i); + container.set("y", i); } objects.push(container); } Parse.Object.saveAll(objects).then(function () { const query = new Parse.Query(Container); - query.doesNotExist('x'); + query.doesNotExist("x"); query.find().then(function (results) { equal(results.length, 4); for (const result of results) { - ok(result.get('y')); + ok(result.get("y")); } done(); }); @@ -2343,8 +2435,8 @@ describe('Parse.Query testing', () => { it("don't include by default", function (done) { const child = new TestObject(); const parent = new Container(); - child.set('foo', 'bar'); - parent.set('child', child); + child.set("foo", "bar"); + parent.set("child", child); Parse.Object.saveAll([child, parent]).then(function () { child._clearServerData(); const query = new Parse.Query(Container); @@ -2352,64 +2444,64 @@ describe('Parse.Query testing', () => { equal(results.length, 1); const parentAgain = results[0]; const goodURL = Parse.serverURL; - Parse.serverURL = 'YAAAAAAAAARRRRRGGGGGGGGG'; - const childAgain = parentAgain.get('child'); + Parse.serverURL = "YAAAAAAAAARRRRRGGGGGGGGG"; + const childAgain = parentAgain.get("child"); ok(childAgain); - equal(childAgain.get('foo'), undefined); + equal(childAgain.get("foo"), undefined); Parse.serverURL = goodURL; done(); }); }); }); - it('include relation', function (done) { + it("include relation", function (done) { const child = new TestObject(); const parent = new Container(); - child.set('foo', 'bar'); - parent.set('child', child); + child.set("foo", "bar"); + parent.set("child", child); Parse.Object.saveAll([child, parent]).then(function () { const query = new Parse.Query(Container); - query.include('child'); + query.include("child"); query.find().then(function (results) { equal(results.length, 1); const parentAgain = results[0]; const goodURL = Parse.serverURL; - Parse.serverURL = 'YAAAAAAAAARRRRRGGGGGGGGG'; - const childAgain = parentAgain.get('child'); + Parse.serverURL = "YAAAAAAAAARRRRRGGGGGGGGG"; + const childAgain = parentAgain.get("child"); ok(childAgain); - equal(childAgain.get('foo'), 'bar'); + equal(childAgain.get("foo"), "bar"); Parse.serverURL = goodURL; done(); }); }); }); - it('include relation array', function (done) { + it("include relation array", function (done) { const child = new TestObject(); const parent = new Container(); - child.set('foo', 'bar'); - parent.set('child', child); + child.set("foo", "bar"); + parent.set("child", child); Parse.Object.saveAll([child, parent]).then(function () { const query = new Parse.Query(Container); - query.include(['child']); + query.include(["child"]); query.find().then(function (results) { equal(results.length, 1); const parentAgain = results[0]; const goodURL = Parse.serverURL; - Parse.serverURL = 'YAAAAAAAAARRRRRGGGGGGGGG'; - const childAgain = parentAgain.get('child'); + Parse.serverURL = "YAAAAAAAAARRRRRGGGGGGGGG"; + const childAgain = parentAgain.get("child"); ok(childAgain); - equal(childAgain.get('foo'), 'bar'); + equal(childAgain.get("foo"), "bar"); Parse.serverURL = goodURL; done(); }); }); }); - it('nested include', function (done) { - const Child = Parse.Object.extend('Child'); - const Parent = Parse.Object.extend('Parent'); - const Grandparent = Parse.Object.extend('Grandparent'); + it("nested include", function (done) { + const Child = Parse.Object.extend("Child"); + const Parent = Parse.Object.extend("Parent"); + const Grandparent = Parse.Object.extend("Grandparent"); const objects = []; for (let i = 0; i < 5; ++i) { const grandparent = new Grandparent({ @@ -2426,12 +2518,12 @@ describe('Parse.Query testing', () => { Parse.Object.saveAll(objects).then(function () { const query = new Parse.Query(Grandparent); - query.include(['parent.child']); + query.include(["parent.child"]); query.find().then(function (results) { equal(results.length, 5); for (const object of results) { - equal(object.get('z'), object.get('parent').get('y')); - equal(object.get('z'), object.get('parent').get('child').get('x')); + equal(object.get("z"), object.get("parent").get("y")); + equal(object.get("z"), object.get("parent").get("child").get("x")); } done(); }); @@ -2439,23 +2531,23 @@ describe('Parse.Query testing', () => { }); it("include doesn't make dirty wrong", function (done) { - const Parent = Parse.Object.extend('ParentObject'); - const Child = Parse.Object.extend('ChildObject'); + const Parent = Parse.Object.extend("ParentObject"); + const Child = Parse.Object.extend("ChildObject"); const parent = new Parent(); const child = new Child(); - child.set('foo', 'bar'); - parent.set('child', child); + child.set("foo", "bar"); + parent.set("child", child); Parse.Object.saveAll([child, parent]).then(function () { const query = new Parse.Query(Parent); - query.include('child'); + query.include("child"); query.find().then(function (results) { equal(results.length, 1); const parentAgain = results[0]; - const childAgain = parentAgain.get('child'); + const childAgain = parentAgain.get("child"); equal(childAgain.id, child.id); equal(parentAgain.id, parent.id); - equal(childAgain.get('foo'), 'bar'); + equal(childAgain.get("foo"), "bar"); equal(false, parentAgain.dirty()); equal(false, childAgain.dirty()); done(); @@ -2463,213 +2555,215 @@ describe('Parse.Query testing', () => { }); }); - it('properly includes array', done => { + it("properly includes array", done => { const objects = []; let total = 0; while (objects.length != 5) { - const object = new Parse.Object('AnObject'); - object.set('key', objects.length); + const object = new Parse.Object("AnObject"); + object.set("key", objects.length); total += objects.length; objects.push(object); } Parse.Object.saveAll(objects) .then(() => { - const object = new Parse.Object('AContainer'); - object.set('objects', objects); + const object = new Parse.Object("AContainer"); + object.set("objects", objects); return object.save(); }) .then(() => { - const query = new Parse.Query('AContainer'); - query.include('objects'); + const query = new Parse.Query("AContainer"); + query.include("objects"); return query.find(); }) .then( results => { expect(results.length).toBe(1); const res = results[0]; - const objects = res.get('objects'); + const objects = res.get("objects"); expect(objects.length).toBe(5); objects.forEach(object => { - total -= object.get('key'); + total -= object.get("key"); }); expect(total).toBe(0); done(); }, () => { - fail('should not fail'); + fail("should not fail"); done(); } ); }); - it('properly includes array of mixed objects', done => { + it("properly includes array of mixed objects", done => { const objects = []; let total = 0; while (objects.length != 5) { - const object = new Parse.Object('AnObject'); - object.set('key', objects.length); + const object = new Parse.Object("AnObject"); + object.set("key", objects.length); total += objects.length; objects.push(object); } while (objects.length != 10) { - const object = new Parse.Object('AnotherObject'); - object.set('key', objects.length); + const object = new Parse.Object("AnotherObject"); + object.set("key", objects.length); total += objects.length; objects.push(object); } Parse.Object.saveAll(objects) .then(() => { - const object = new Parse.Object('AContainer'); - object.set('objects', objects); + const object = new Parse.Object("AContainer"); + object.set("objects", objects); return object.save(); }) .then(() => { - const query = new Parse.Query('AContainer'); - query.include('objects'); + const query = new Parse.Query("AContainer"); + query.include("objects"); return query.find(); }) .then( results => { expect(results.length).toBe(1); const res = results[0]; - const objects = res.get('objects'); + const objects = res.get("objects"); expect(objects.length).toBe(10); objects.forEach(object => { - total -= object.get('key'); + total -= object.get("key"); }); expect(total).toBe(0); done(); }, e => { - fail('should not fail'); + fail("should not fail"); fail(JSON.stringify(e)); done(); } ); }); - it('properly nested array of mixed objects with bad ids', done => { + it("properly nested array of mixed objects with bad ids", done => { const objects = []; let total = 0; while (objects.length != 5) { - const object = new Parse.Object('AnObject'); - object.set('key', objects.length); + const object = new Parse.Object("AnObject"); + object.set("key", objects.length); objects.push(object); } while (objects.length != 10) { - const object = new Parse.Object('AnotherObject'); - object.set('key', objects.length); + const object = new Parse.Object("AnotherObject"); + object.set("key", objects.length); objects.push(object); } Parse.Object.saveAll(objects) .then(() => { - const object = new Parse.Object('AContainer'); + const object = new Parse.Object("AContainer"); for (let i = 0; i < objects.length; i++) { if (i % 2 == 0) { - objects[i].id = 'randomThing'; + objects[i].id = "randomThing"; } else { - total += objects[i].get('key'); + total += objects[i].get("key"); } } - object.set('objects', objects); + object.set("objects", objects); return object.save(); }) .then(() => { - const query = new Parse.Query('AContainer'); - query.include('objects'); + const query = new Parse.Query("AContainer"); + query.include("objects"); return query.find(); }) .then( results => { expect(results.length).toBe(1); const res = results[0]; - const objects = res.get('objects'); + const objects = res.get("objects"); expect(objects.length).toBe(5); objects.forEach(object => { - total -= object.get('key'); + total -= object.get("key"); }); expect(total).toBe(0); done(); }, err => { jfail(err); - fail('should not fail'); + fail("should not fail"); done(); } ); }); - it('properly fetches nested pointers', done => { - const color = new Parse.Object('Color'); - color.set('hex', '#133733'); - const circle = new Parse.Object('Circle'); - circle.set('radius', 1337); + it("properly fetches nested pointers", done => { + const color = new Parse.Object("Color"); + color.set("hex", "#133733"); + const circle = new Parse.Object("Circle"); + circle.set("radius", 1337); Parse.Object.saveAll([color, circle]) .then(() => { - circle.set('color', color); - const badCircle = new Parse.Object('Circle'); - badCircle.id = 'badId'; - const complexFigure = new Parse.Object('ComplexFigure'); - complexFigure.set('consistsOf', [circle, badCircle]); + circle.set("color", color); + const badCircle = new Parse.Object("Circle"); + badCircle.id = "badId"; + const complexFigure = new Parse.Object("ComplexFigure"); + complexFigure.set("consistsOf", [circle, badCircle]); return complexFigure.save(); }) .then(() => { - const q = new Parse.Query('ComplexFigure'); - q.include('consistsOf.color'); + const q = new Parse.Query("ComplexFigure"); + q.include("consistsOf.color"); return q.find(); }) .then( results => { expect(results.length).toBe(1); const figure = results[0]; - expect(figure.get('consistsOf').length).toBe(1); - expect(figure.get('consistsOf')[0].get('color').get('hex')).toBe('#133733'); + expect(figure.get("consistsOf").length).toBe(1); + expect(figure.get("consistsOf")[0].get("color").get("hex")).toBe( + "#133733" + ); done(); }, () => { - fail('should not fail'); + fail("should not fail"); done(); } ); }); - it('result object creation uses current extension', function (done) { - const ParentObject = Parse.Object.extend({ className: 'ParentObject' }); + it("result object creation uses current extension", function (done) { + const ParentObject = Parse.Object.extend({ className: "ParentObject" }); // Add a foo() method to ChildObject. - let ChildObject = Parse.Object.extend('ChildObject', { + let ChildObject = Parse.Object.extend("ChildObject", { foo: function () { - return 'foo'; + return "foo"; }, }); const parent = new ParentObject(); const child = new ChildObject(); - parent.set('child', child); + parent.set("child", child); Parse.Object.saveAll([child, parent]).then(function () { // Add a bar() method to ChildObject. - ChildObject = Parse.Object.extend('ChildObject', { + ChildObject = Parse.Object.extend("ChildObject", { bar: function () { - return 'bar'; + return "bar"; }, }); const query = new Parse.Query(ParentObject); - query.include('child'); + query.include("child"); query.find().then(function (results) { equal(results.length, 1); const parentAgain = results[0]; - const childAgain = parentAgain.get('child'); - equal(childAgain.foo(), 'foo'); - equal(childAgain.bar(), 'bar'); + const childAgain = parentAgain.get("child"); + equal(childAgain.foo(), "foo"); + equal(childAgain.bar(), "bar"); done(); }); }); }); - it('matches query', function (done) { - const ParentObject = Parse.Object.extend('ParentObject'); - const ChildObject = Parse.Object.extend('ChildObject'); + it("matches query", function (done) { + const ParentObject = Parse.Object.extend("ParentObject"); + const ChildObject = Parse.Object.extend("ChildObject"); const objects = []; for (let i = 0; i < 10; ++i) { objects.push( @@ -2681,21 +2775,21 @@ describe('Parse.Query testing', () => { } Parse.Object.saveAll(objects).then(function () { const subQuery = new Parse.Query(ChildObject); - subQuery.greaterThan('x', 5); + subQuery.greaterThan("x", 5); const query = new Parse.Query(ParentObject); - query.matchesQuery('child', subQuery); + query.matchesQuery("child", subQuery); query.find().then(function (results) { equal(results.length, 4); for (const object of results) { - ok(object.get('x') > 15); + ok(object.get("x") > 15); } const query = new Parse.Query(ParentObject); - query.doesNotMatchQuery('child', subQuery); + query.doesNotMatchQuery("child", subQuery); query.find().then(function (results) { equal(results.length, 6); for (const object of results) { - ok(object.get('x') >= 10); - ok(object.get('x') <= 15); + ok(object.get("x") >= 10); + ok(object.get("x") <= 15); done(); } }); @@ -2703,49 +2797,49 @@ describe('Parse.Query testing', () => { }); }); - it('select query', function (done) { - const RestaurantObject = Parse.Object.extend('Restaurant'); - const PersonObject = Parse.Object.extend('Person'); + it("select query", function (done) { + const RestaurantObject = Parse.Object.extend("Restaurant"); + const PersonObject = Parse.Object.extend("Person"); const objects = [ - new RestaurantObject({ ratings: 5, location: 'Djibouti' }), - new RestaurantObject({ ratings: 3, location: 'Ouagadougou' }), - new PersonObject({ name: 'Bob', hometown: 'Djibouti' }), - new PersonObject({ name: 'Tom', hometown: 'Ouagadougou' }), - new PersonObject({ name: 'Billy', hometown: 'Detroit' }), + new RestaurantObject({ ratings: 5, location: "Djibouti" }), + new RestaurantObject({ ratings: 3, location: "Ouagadougou" }), + new PersonObject({ name: "Bob", hometown: "Djibouti" }), + new PersonObject({ name: "Tom", hometown: "Ouagadougou" }), + new PersonObject({ name: "Billy", hometown: "Detroit" }), ]; Parse.Object.saveAll(objects).then(function () { const query = new Parse.Query(RestaurantObject); - query.greaterThan('ratings', 4); + query.greaterThan("ratings", 4); const mainQuery = new Parse.Query(PersonObject); - mainQuery.matchesKeyInQuery('hometown', 'location', query); + mainQuery.matchesKeyInQuery("hometown", "location", query); mainQuery.find().then(function (results) { equal(results.length, 1); - equal(results[0].get('name'), 'Bob'); + equal(results[0].get("name"), "Bob"); done(); }); }); }); - it('$select inside $or', done => { - const Restaurant = Parse.Object.extend('Restaurant'); - const Person = Parse.Object.extend('Person'); + it("$select inside $or", done => { + const Restaurant = Parse.Object.extend("Restaurant"); + const Person = Parse.Object.extend("Person"); const objects = [ - new Restaurant({ ratings: 5, location: 'Djibouti' }), - new Restaurant({ ratings: 3, location: 'Ouagadougou' }), - new Person({ name: 'Bob', hometown: 'Djibouti' }), - new Person({ name: 'Tom', hometown: 'Ouagadougou' }), - new Person({ name: 'Billy', hometown: 'Detroit' }), + new Restaurant({ ratings: 5, location: "Djibouti" }), + new Restaurant({ ratings: 3, location: "Ouagadougou" }), + new Person({ name: "Bob", hometown: "Djibouti" }), + new Person({ name: "Tom", hometown: "Ouagadougou" }), + new Person({ name: "Billy", hometown: "Detroit" }), ]; Parse.Object.saveAll(objects) .then(() => { const subquery = new Parse.Query(Restaurant); - subquery.greaterThan('ratings', 4); + subquery.greaterThan("ratings", 4); const query1 = new Parse.Query(Person); - query1.matchesKeyInQuery('hometown', 'location', subquery); + query1.matchesKeyInQuery("hometown", "location", subquery); const query2 = new Parse.Query(Person); - query2.equalTo('name', 'Tom'); + query2.equalTo("name", "Tom"); const query = Parse.Query.or(query1, query2); return query.find(); }) @@ -2761,7 +2855,7 @@ describe('Parse.Query testing', () => { ); }); - it('$nor valid query', done => { + it("$nor valid query", done => { const objects = Array.from(Array(10).keys()).map(rating => { return new TestObject({ rating: rating }); }); @@ -2771,26 +2865,36 @@ describe('Parse.Query testing', () => { const options = Object.assign({}, masterKeyOptions, { qs: { where: JSON.stringify({ - $nor: [{ rating: { $gt: highValue } }, { rating: { $lte: lowValue } }], + $nor: [ + { rating: { $gt: highValue } }, + { rating: { $lte: lowValue } }, + ], }), }, }); Parse.Object.saveAll(objects) .then(() => { - return request(Object.assign({ url: Parse.serverURL + '/classes/TestObject' }, options)); + return request( + Object.assign( + { url: Parse.serverURL + "/classes/TestObject" }, + options + ) + ); }) .then(response => { const results = response.data; expect(results.results.length).toBe(highValue - lowValue); - expect(results.results.every(res => res.rating > lowValue && res.rating <= highValue)).toBe( - true - ); + expect( + results.results.every( + res => res.rating > lowValue && res.rating <= highValue + ) + ).toBe(true); done(); }); }); - it('$nor invalid query - empty array', done => { + it("$nor invalid query - empty array", done => { const options = Object.assign({}, masterKeyOptions, { qs: { where: JSON.stringify({ $nor: [] }), @@ -2800,7 +2904,12 @@ describe('Parse.Query testing', () => { obj .save() .then(() => { - return request(Object.assign({ url: Parse.serverURL + '/classes/TestObject' }, options)); + return request( + Object.assign( + { url: Parse.serverURL + "/classes/TestObject" }, + options + ) + ); }) .then(done.fail) .catch(response => { @@ -2809,7 +2918,7 @@ describe('Parse.Query testing', () => { }); }); - it('$nor invalid query - wrong type', done => { + it("$nor invalid query - wrong type", done => { const options = Object.assign({}, masterKeyOptions, { qs: { where: JSON.stringify({ $nor: 1337 }), @@ -2819,7 +2928,12 @@ describe('Parse.Query testing', () => { obj .save() .then(() => { - return request(Object.assign({ url: Parse.serverURL + '/classes/TestObject' }, options)); + return request( + Object.assign( + { url: Parse.serverURL + "/classes/TestObject" }, + options + ) + ); }) .then(done.fail) .catch(response => { @@ -2828,73 +2942,73 @@ describe('Parse.Query testing', () => { }); }); - it('dontSelect query', function (done) { - const RestaurantObject = Parse.Object.extend('Restaurant'); - const PersonObject = Parse.Object.extend('Person'); + it("dontSelect query", function (done) { + const RestaurantObject = Parse.Object.extend("Restaurant"); + const PersonObject = Parse.Object.extend("Person"); const objects = [ - new RestaurantObject({ ratings: 5, location: 'Djibouti' }), - new RestaurantObject({ ratings: 3, location: 'Ouagadougou' }), - new PersonObject({ name: 'Bob', hometown: 'Djibouti' }), - new PersonObject({ name: 'Tom', hometown: 'Ouagadougou' }), - new PersonObject({ name: 'Billy', hometown: 'Djibouti' }), + new RestaurantObject({ ratings: 5, location: "Djibouti" }), + new RestaurantObject({ ratings: 3, location: "Ouagadougou" }), + new PersonObject({ name: "Bob", hometown: "Djibouti" }), + new PersonObject({ name: "Tom", hometown: "Ouagadougou" }), + new PersonObject({ name: "Billy", hometown: "Djibouti" }), ]; Parse.Object.saveAll(objects).then(function () { const query = new Parse.Query(RestaurantObject); - query.greaterThan('ratings', 4); + query.greaterThan("ratings", 4); const mainQuery = new Parse.Query(PersonObject); - mainQuery.doesNotMatchKeyInQuery('hometown', 'location', query); + mainQuery.doesNotMatchKeyInQuery("hometown", "location", query); mainQuery.find().then(function (results) { equal(results.length, 1); - equal(results[0].get('name'), 'Tom'); + equal(results[0].get("name"), "Tom"); done(); }); }); }); - it('dontSelect query without conditions', function (done) { - const RestaurantObject = Parse.Object.extend('Restaurant'); - const PersonObject = Parse.Object.extend('Person'); + it("dontSelect query without conditions", function (done) { + const RestaurantObject = Parse.Object.extend("Restaurant"); + const PersonObject = Parse.Object.extend("Person"); const objects = [ - new RestaurantObject({ location: 'Djibouti' }), - new RestaurantObject({ location: 'Ouagadougou' }), - new PersonObject({ name: 'Bob', hometown: 'Djibouti' }), - new PersonObject({ name: 'Tom', hometown: 'Yoloblahblahblah' }), - new PersonObject({ name: 'Billy', hometown: 'Ouagadougou' }), + new RestaurantObject({ location: "Djibouti" }), + new RestaurantObject({ location: "Ouagadougou" }), + new PersonObject({ name: "Bob", hometown: "Djibouti" }), + new PersonObject({ name: "Tom", hometown: "Yoloblahblahblah" }), + new PersonObject({ name: "Billy", hometown: "Ouagadougou" }), ]; Parse.Object.saveAll(objects).then(function () { const query = new Parse.Query(RestaurantObject); const mainQuery = new Parse.Query(PersonObject); - mainQuery.doesNotMatchKeyInQuery('hometown', 'location', query); + mainQuery.doesNotMatchKeyInQuery("hometown", "location", query); mainQuery.find().then(results => { equal(results.length, 1); - equal(results[0].get('name'), 'Tom'); + equal(results[0].get("name"), "Tom"); done(); }); }); }); - it('equalTo on same column as $dontSelect should not break $dontSelect functionality (#3678)', function (done) { - const AuthorObject = Parse.Object.extend('Author'); - const BlockedObject = Parse.Object.extend('Blocked'); - const PostObject = Parse.Object.extend('Post'); + it("equalTo on same column as $dontSelect should not break $dontSelect functionality (#3678)", function (done) { + const AuthorObject = Parse.Object.extend("Author"); + const BlockedObject = Parse.Object.extend("Blocked"); + const PostObject = Parse.Object.extend("Post"); let postAuthor = null; let requestUser = null; - return new AuthorObject({ name: 'Julius' }) + return new AuthorObject({ name: "Julius" }) .save() .then(user => { postAuthor = user; - return new AuthorObject({ name: 'Bob' }).save(); + return new AuthorObject({ name: "Bob" }).save(); }) .then(user => { requestUser = user; const objects = [ - new PostObject({ author: postAuthor, title: 'Lorem ipsum' }), - new PostObject({ author: requestUser, title: 'Kafka' }), - new PostObject({ author: requestUser, title: 'Brown fox' }), + new PostObject({ author: postAuthor, title: "Lorem ipsum" }), + new PostObject({ author: requestUser, title: "Kafka" }), + new PostObject({ author: requestUser, title: "Brown fox" }), new BlockedObject({ blockedBy: postAuthor, blockedUser: requestUser, @@ -2904,11 +3018,11 @@ describe('Parse.Query testing', () => { }) .then(() => { const banListQuery = new Parse.Query(BlockedObject); - banListQuery.equalTo('blockedUser', requestUser); + banListQuery.equalTo("blockedUser", requestUser); return new Parse.Query(PostObject) - .equalTo('author', postAuthor) - .doesNotMatchKeyInQuery('author', 'blockedBy', banListQuery) + .equalTo("author", postAuthor) + .doesNotMatchKeyInQuery("author", "blockedBy", banListQuery) .find() .then(r => { expect(r.length).toEqual(0); @@ -2917,40 +3031,44 @@ describe('Parse.Query testing', () => { }); }); - it('multiple dontSelect query', function (done) { - const RestaurantObject = Parse.Object.extend('Restaurant'); - const PersonObject = Parse.Object.extend('Person'); + it("multiple dontSelect query", function (done) { + const RestaurantObject = Parse.Object.extend("Restaurant"); + const PersonObject = Parse.Object.extend("Person"); const objects = [ - new RestaurantObject({ ratings: 7, location: 'Djibouti2' }), - new RestaurantObject({ ratings: 5, location: 'Djibouti' }), - new RestaurantObject({ ratings: 3, location: 'Ouagadougou' }), - new PersonObject({ name: 'Bob2', hometown: 'Djibouti2' }), - new PersonObject({ name: 'Bob', hometown: 'Djibouti' }), - new PersonObject({ name: 'Tom', hometown: 'Ouagadougou' }), + new RestaurantObject({ ratings: 7, location: "Djibouti2" }), + new RestaurantObject({ ratings: 5, location: "Djibouti" }), + new RestaurantObject({ ratings: 3, location: "Ouagadougou" }), + new PersonObject({ name: "Bob2", hometown: "Djibouti2" }), + new PersonObject({ name: "Bob", hometown: "Djibouti" }), + new PersonObject({ name: "Tom", hometown: "Ouagadougou" }), ]; Parse.Object.saveAll(objects).then(function () { const query = new Parse.Query(RestaurantObject); - query.greaterThan('ratings', 6); + query.greaterThan("ratings", 6); const query2 = new Parse.Query(RestaurantObject); - query2.lessThan('ratings', 4); + query2.lessThan("ratings", 4); const subQuery = new Parse.Query(PersonObject); - subQuery.matchesKeyInQuery('hometown', 'location', query); + subQuery.matchesKeyInQuery("hometown", "location", query); const subQuery2 = new Parse.Query(PersonObject); - subQuery2.matchesKeyInQuery('hometown', 'location', query2); + subQuery2.matchesKeyInQuery("hometown", "location", query2); const mainQuery = new Parse.Query(PersonObject); - mainQuery.doesNotMatchKeyInQuery('objectId', 'objectId', Parse.Query.or(subQuery, subQuery2)); + mainQuery.doesNotMatchKeyInQuery( + "objectId", + "objectId", + Parse.Query.or(subQuery, subQuery2) + ); mainQuery.find().then(function (results) { equal(results.length, 1); - equal(results[0].get('name'), 'Bob'); + equal(results[0].get("name"), "Bob"); done(); }); }); }); - it('include user', function (done) { - Parse.User.signUp('bob', 'password', { age: 21 }).then(function (user) { - const TestObject = Parse.Object.extend('TestObject'); + it("include user", function (done) { + Parse.User.signUp("bob", "password", { age: 21 }).then(function (user) { + const TestObject = Parse.Object.extend("TestObject"); const obj = new TestObject(); obj .save({ @@ -2958,33 +3076,33 @@ describe('Parse.Query testing', () => { }) .then(function (obj) { const query = new Parse.Query(TestObject); - query.include('owner'); + query.include("owner"); query.get(obj.id).then(function (objAgain) { equal(objAgain.id, obj.id); - ok(objAgain.get('owner') instanceof Parse.User); - equal(objAgain.get('owner').get('age'), 21); + ok(objAgain.get("owner") instanceof Parse.User); + equal(objAgain.get("owner").get("age"), 21); done(); }, done.fail); }, done.fail); }, done.fail); }); - it('or queries', function (done) { + it("or queries", function (done) { const objects = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(function (x) { - const object = new Parse.Object('BoxedNumber'); - object.set('x', x); + const object = new Parse.Object("BoxedNumber"); + object.set("x", x); return object; }); Parse.Object.saveAll(objects).then(function () { - const query1 = new Parse.Query('BoxedNumber'); - query1.lessThan('x', 2); - const query2 = new Parse.Query('BoxedNumber'); - query2.greaterThan('x', 5); + const query1 = new Parse.Query("BoxedNumber"); + query1.lessThan("x", 2); + const query2 = new Parse.Query("BoxedNumber"); + query2.greaterThan("x", 5); const orQuery = Parse.Query.or(query1, query2); orQuery.find().then(function (results) { equal(results.length, 6); for (const number of results) { - ok(number.get('x') < 2 || number.get('x') > 5); + ok(number.get("x") < 2 || number.get("x") > 5); } done(); }); @@ -2992,23 +3110,23 @@ describe('Parse.Query testing', () => { }); // This relies on matchesQuery aka the $inQuery operator - it('or complex queries', function (done) { + it("or complex queries", function (done) { const objects = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(function (x) { - const child = new Parse.Object('Child'); - child.set('x', x); - const parent = new Parse.Object('Parent'); - parent.set('child', child); - parent.set('y', x); + const child = new Parse.Object("Child"); + child.set("x", x); + const parent = new Parse.Object("Parent"); + parent.set("child", child); + parent.set("y", x); return parent; }); Parse.Object.saveAll(objects).then(function () { - const subQuery = new Parse.Query('Child'); - subQuery.equalTo('x', 4); - const query1 = new Parse.Query('Parent'); - query1.matchesQuery('child', subQuery); - const query2 = new Parse.Query('Parent'); - query2.lessThan('y', 2); + const subQuery = new Parse.Query("Child"); + subQuery.equalTo("x", 4); + const query1 = new Parse.Query("Parent"); + query1.matchesQuery("child", subQuery); + const query2 = new Parse.Query("Parent"); + query2.lessThan("y", 2); const orQuery = Parse.Query.or(query1, query2); orQuery.find().then(function (results) { equal(results.length, 3); @@ -3017,33 +3135,33 @@ describe('Parse.Query testing', () => { }); }); - it('async methods', function (done) { + it("async methods", function (done) { const saves = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(function (x) { - const obj = new Parse.Object('TestObject'); - obj.set('x', x + 1); + const obj = new Parse.Object("TestObject"); + obj.set("x", x + 1); return obj; }); Parse.Object.saveAll(saves) .then(function () { - const query = new Parse.Query('TestObject'); - query.ascending('x'); + const query = new Parse.Query("TestObject"); + query.ascending("x"); return query.first(); }) .then(function (obj) { - equal(obj.get('x'), 1); - const query = new Parse.Query('TestObject'); - query.descending('x'); + equal(obj.get("x"), 1); + const query = new Parse.Query("TestObject"); + query.descending("x"); return query.find(); }) .then(function (results) { equal(results.length, 10); - const query = new Parse.Query('TestObject'); + const query = new Parse.Query("TestObject"); return query.get(results[0].id); }) .then(function (obj1) { - equal(obj1.get('x'), 10); - const query = new Parse.Query('TestObject'); + equal(obj1.get("x"), 10); + const query = new Parse.Query("TestObject"); return query.count(); }) .then(function (count) { @@ -3054,25 +3172,25 @@ describe('Parse.Query testing', () => { }); }); - it('query.each', function (done) { + it("query.each", function (done) { const TOTAL = 50; const COUNT = 25; const items = range(TOTAL).map(function (x) { const obj = new TestObject(); - obj.set('x', x); + obj.set("x", x); return obj; }); Parse.Object.saveAll(items).then(function () { const query = new Parse.Query(TestObject); - query.lessThan('x', COUNT); + query.lessThan("x", COUNT); const seen = []; query .each( function (obj) { - seen[obj.get('x')] = (seen[obj.get('x')] || 0) + 1; + seen[obj.get("x")] = (seen[obj.get("x")] || 0) + 1; }, { batchSize: 10, @@ -3081,14 +3199,14 @@ describe('Parse.Query testing', () => { .then(function () { equal(seen.length, COUNT); for (let i = 0; i < COUNT; i++) { - equal(seen[i], 1, 'Should have seen object number ' + i); + equal(seen[i], 1, "Should have seen object number " + i); } done(); }, done.fail); }); }); - it('query.each async', function (done) { + it("query.each async", function (done) { const TOTAL = 50; const COUNT = 25; @@ -3096,7 +3214,7 @@ describe('Parse.Query testing', () => { const items = range(TOTAL).map(function (x) { const obj = new TestObject(); - obj.set('x', x); + obj.set("x", x); return obj; }); @@ -3105,12 +3223,12 @@ describe('Parse.Query testing', () => { Parse.Object.saveAll(items) .then(function () { const query = new Parse.Query(TestObject); - query.lessThan('x', COUNT); + query.lessThan("x", COUNT); return query.each( function (obj) { return new Promise(resolve => { process.nextTick(function () { - seen[obj.get('x')] = (seen[obj.get('x')] || 0) + 1; + seen[obj.get("x")] = (seen[obj.get("x")] || 0) + 1; resolve(); }); }); @@ -3123,19 +3241,19 @@ describe('Parse.Query testing', () => { .then(function () { equal(seen.length, COUNT); for (let i = 0; i < COUNT; i++) { - equal(seen[i], 1, 'Should have seen object number ' + i); + equal(seen[i], 1, "Should have seen object number " + i); } done(); }); }); - it('query.each fails with order', function (done) { + it("query.each fails with order", function (done) { const TOTAL = 50; const COUNT = 25; const items = range(TOTAL).map(function (x) { const obj = new TestObject(); - obj.set('x', x); + obj.set("x", x); return obj; }); @@ -3144,15 +3262,15 @@ describe('Parse.Query testing', () => { Parse.Object.saveAll(items) .then(function () { const query = new Parse.Query(TestObject); - query.lessThan('x', COUNT); - query.ascending('x'); + query.lessThan("x", COUNT); + query.ascending("x"); return query.each(function (obj) { - seen[obj.get('x')] = (seen[obj.get('x')] || 0) + 1; + seen[obj.get("x")] = (seen[obj.get("x")] || 0) + 1; }); }) .then( function () { - ok(false, 'This should have failed.'); + ok(false, "This should have failed."); done(); }, function () { @@ -3161,13 +3279,13 @@ describe('Parse.Query testing', () => { ); }); - it('query.each fails with skip', function (done) { + it("query.each fails with skip", function (done) { const TOTAL = 50; const COUNT = 25; const items = range(TOTAL).map(function (x) { const obj = new TestObject(); - obj.set('x', x); + obj.set("x", x); return obj; }); @@ -3176,15 +3294,15 @@ describe('Parse.Query testing', () => { Parse.Object.saveAll(items) .then(function () { const query = new Parse.Query(TestObject); - query.lessThan('x', COUNT); + query.lessThan("x", COUNT); query.skip(5); return query.each(function (obj) { - seen[obj.get('x')] = (seen[obj.get('x')] || 0) + 1; + seen[obj.get("x")] = (seen[obj.get("x")] || 0) + 1; }); }) .then( function () { - ok(false, 'This should have failed.'); + ok(false, "This should have failed."); done(); }, function () { @@ -3193,7 +3311,7 @@ describe('Parse.Query testing', () => { ); }); - it('query.each fails with limit', function (done) { + it("query.each fails with limit", function (done) { const TOTAL = 50; const COUNT = 25; @@ -3201,7 +3319,7 @@ describe('Parse.Query testing', () => { const items = range(TOTAL).map(function (x) { const obj = new TestObject(); - obj.set('x', x); + obj.set("x", x); return obj; }); @@ -3210,15 +3328,15 @@ describe('Parse.Query testing', () => { Parse.Object.saveAll(items) .then(function () { const query = new Parse.Query(TestObject); - query.lessThan('x', COUNT); + query.lessThan("x", COUNT); query.limit(5); return query.each(function (obj) { - seen[obj.get('x')] = (seen[obj.get('x')] || 0) + 1; + seen[obj.get("x")] = (seen[obj.get("x")] || 0) + 1; }); }) .then( function () { - ok(false, 'This should have failed.'); + ok(false, "This should have failed."); done(); }, function () { @@ -3227,463 +3345,641 @@ describe('Parse.Query testing', () => { ); }); - it('select keys query JS SDK', async () => { - const obj = new TestObject({ foo: 'baz', bar: 1, qux: 2 }); + it("select keys query JS SDK", async () => { + const obj = new TestObject({ foo: "baz", bar: 1, qux: 2 }); await obj.save(); obj._clearServerData(); const query1 = new Parse.Query(TestObject); - query1.select('foo'); + query1.select("foo"); const result1 = await query1.first(); - ok(result1.id, 'expected object id to be set'); - ok(result1.createdAt, 'expected object createdAt to be set'); - ok(result1.updatedAt, 'expected object updatedAt to be set'); - ok(!result1.dirty(), 'expected result not to be dirty'); - strictEqual(result1.get('foo'), 'baz'); - strictEqual(result1.get('bar'), undefined, "expected 'bar' field to be unset"); - strictEqual(result1.get('qux'), undefined, "expected 'qux' field to be unset"); + ok(result1.id, "expected object id to be set"); + ok(result1.createdAt, "expected object createdAt to be set"); + ok(result1.updatedAt, "expected object updatedAt to be set"); + ok(!result1.dirty(), "expected result not to be dirty"); + strictEqual(result1.get("foo"), "baz"); + strictEqual( + result1.get("bar"), + undefined, + "expected 'bar' field to be unset" + ); + strictEqual( + result1.get("qux"), + undefined, + "expected 'qux' field to be unset" + ); const result2 = await result1.fetch(); - strictEqual(result2.get('foo'), 'baz'); - strictEqual(result2.get('bar'), 1); - strictEqual(result2.get('qux'), 2); + strictEqual(result2.get("foo"), "baz"); + strictEqual(result2.get("bar"), 1); + strictEqual(result2.get("qux"), 2); obj._clearServerData(); const query2 = new Parse.Query(TestObject); query2.select(); const result3 = await query2.first(); - ok(result3.id, 'expected object id to be set'); - ok(result3.createdAt, 'expected object createdAt to be set'); - ok(result3.updatedAt, 'expected object updatedAt to be set'); - ok(!result3.dirty(), 'expected result not to be dirty'); - strictEqual(result3.get('foo'), undefined, "expected 'foo' field to be unset"); - strictEqual(result3.get('bar'), undefined, "expected 'bar' field to be unset"); - strictEqual(result3.get('qux'), undefined, "expected 'qux' field to be unset"); + ok(result3.id, "expected object id to be set"); + ok(result3.createdAt, "expected object createdAt to be set"); + ok(result3.updatedAt, "expected object updatedAt to be set"); + ok(!result3.dirty(), "expected result not to be dirty"); + strictEqual( + result3.get("foo"), + undefined, + "expected 'foo' field to be unset" + ); + strictEqual( + result3.get("bar"), + undefined, + "expected 'bar' field to be unset" + ); + strictEqual( + result3.get("qux"), + undefined, + "expected 'qux' field to be unset" + ); obj._clearServerData(); const query3 = new Parse.Query(TestObject); query3.select([]); const result4 = await query3.first(); - ok(result4.id, 'expected object id to be set'); - ok(result4.createdAt, 'expected object createdAt to be set'); - ok(result4.updatedAt, 'expected object updatedAt to be set'); - ok(!result4.dirty(), 'expected result not to be dirty'); - strictEqual(result4.get('foo'), undefined, "expected 'foo' field to be unset"); - strictEqual(result4.get('bar'), undefined, "expected 'bar' field to be unset"); - strictEqual(result4.get('qux'), undefined, "expected 'qux' field to be unset"); + ok(result4.id, "expected object id to be set"); + ok(result4.createdAt, "expected object createdAt to be set"); + ok(result4.updatedAt, "expected object updatedAt to be set"); + ok(!result4.dirty(), "expected result not to be dirty"); + strictEqual( + result4.get("foo"), + undefined, + "expected 'foo' field to be unset" + ); + strictEqual( + result4.get("bar"), + undefined, + "expected 'bar' field to be unset" + ); + strictEqual( + result4.get("qux"), + undefined, + "expected 'qux' field to be unset" + ); obj._clearServerData(); const query4 = new Parse.Query(TestObject); - query4.select(['foo']); + query4.select(["foo"]); const result5 = await query4.first(); - ok(result5.id, 'expected object id to be set'); - ok(result5.createdAt, 'expected object createdAt to be set'); - ok(result5.updatedAt, 'expected object updatedAt to be set'); - ok(!result5.dirty(), 'expected result not to be dirty'); - strictEqual(result5.get('foo'), 'baz'); - strictEqual(result5.get('bar'), undefined, "expected 'bar' field to be unset"); - strictEqual(result5.get('qux'), undefined, "expected 'qux' field to be unset"); + ok(result5.id, "expected object id to be set"); + ok(result5.createdAt, "expected object createdAt to be set"); + ok(result5.updatedAt, "expected object updatedAt to be set"); + ok(!result5.dirty(), "expected result not to be dirty"); + strictEqual(result5.get("foo"), "baz"); + strictEqual( + result5.get("bar"), + undefined, + "expected 'bar' field to be unset" + ); + strictEqual( + result5.get("qux"), + undefined, + "expected 'qux' field to be unset" + ); obj._clearServerData(); const query5 = new Parse.Query(TestObject); - query5.select(['foo', 'bar']); + query5.select(["foo", "bar"]); const result6 = await query5.first(); - ok(result6.id, 'expected object id to be set'); - ok(!result6.dirty(), 'expected result not to be dirty'); - strictEqual(result6.get('foo'), 'baz'); - strictEqual(result6.get('bar'), 1); - strictEqual(result6.get('qux'), undefined, "expected 'qux' field to be unset"); + ok(result6.id, "expected object id to be set"); + ok(!result6.dirty(), "expected result not to be dirty"); + strictEqual(result6.get("foo"), "baz"); + strictEqual(result6.get("bar"), 1); + strictEqual( + result6.get("qux"), + undefined, + "expected 'qux' field to be unset" + ); obj._clearServerData(); const query6 = new Parse.Query(TestObject); - query6.select(['foo', 'bar', 'qux']); + query6.select(["foo", "bar", "qux"]); const result7 = await query6.first(); - ok(result7.id, 'expected object id to be set'); - ok(!result7.dirty(), 'expected result not to be dirty'); - strictEqual(result7.get('foo'), 'baz'); - strictEqual(result7.get('bar'), 1); - strictEqual(result7.get('qux'), 2); + ok(result7.id, "expected object id to be set"); + ok(!result7.dirty(), "expected result not to be dirty"); + strictEqual(result7.get("foo"), "baz"); + strictEqual(result7.get("bar"), 1); + strictEqual(result7.get("qux"), 2); obj._clearServerData(); const query7 = new Parse.Query(TestObject); - query7.select('foo', 'bar'); + query7.select("foo", "bar"); const result8 = await query7.first(); - ok(result8.id, 'expected object id to be set'); - ok(!result8.dirty(), 'expected result not to be dirty'); - strictEqual(result8.get('foo'), 'baz'); - strictEqual(result8.get('bar'), 1); - strictEqual(result8.get('qux'), undefined, "expected 'qux' field to be unset"); + ok(result8.id, "expected object id to be set"); + ok(!result8.dirty(), "expected result not to be dirty"); + strictEqual(result8.get("foo"), "baz"); + strictEqual(result8.get("bar"), 1); + strictEqual( + result8.get("qux"), + undefined, + "expected 'qux' field to be unset" + ); obj._clearServerData(); const query8 = new Parse.Query(TestObject); - query8.select('foo', 'bar', 'qux'); + query8.select("foo", "bar", "qux"); const result9 = await query8.first(); - ok(result9.id, 'expected object id to be set'); - ok(!result9.dirty(), 'expected result not to be dirty'); - strictEqual(result9.get('foo'), 'baz'); - strictEqual(result9.get('bar'), 1); - strictEqual(result9.get('qux'), 2); + ok(result9.id, "expected object id to be set"); + ok(!result9.dirty(), "expected result not to be dirty"); + strictEqual(result9.get("foo"), "baz"); + strictEqual(result9.get("bar"), 1); + strictEqual(result9.get("qux"), 2); }); - it('select keys (arrays)', async () => { - const obj = new TestObject({ foo: 'baz', bar: 1, hello: 'world' }); + it("select keys (arrays)", async () => { + const obj = new TestObject({ foo: "baz", bar: 1, hello: "world" }); await obj.save(); const response = await request({ - url: Parse.serverURL + '/classes/TestObject', + url: Parse.serverURL + "/classes/TestObject", qs: { - keys: 'hello', + keys: "hello", where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); expect(response.data.results[0].foo).toBeUndefined(); expect(response.data.results[0].bar).toBeUndefined(); - expect(response.data.results[0].hello).toBe('world'); + expect(response.data.results[0].hello).toBe("world"); const response2 = await request({ - url: Parse.serverURL + '/classes/TestObject', + url: Parse.serverURL + "/classes/TestObject", qs: { - keys: ['foo', 'hello'], + keys: ["foo", "hello"], where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); - expect(response2.data.results[0].foo).toBe('baz'); + expect(response2.data.results[0].foo).toBe("baz"); expect(response2.data.results[0].bar).toBeUndefined(); - expect(response2.data.results[0].hello).toBe('world'); + expect(response2.data.results[0].hello).toBe("world"); const response3 = await request({ - url: Parse.serverURL + '/classes/TestObject', + url: Parse.serverURL + "/classes/TestObject", qs: { - keys: ['foo', 'bar', 'hello'], + keys: ["foo", "bar", "hello"], where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); - expect(response3.data.results[0].foo).toBe('baz'); + expect(response3.data.results[0].foo).toBe("baz"); expect(response3.data.results[0].bar).toBe(1); - expect(response3.data.results[0].hello).toBe('world'); + expect(response3.data.results[0].hello).toBe("world"); const response4 = await request({ - url: Parse.serverURL + '/classes/TestObject', + url: Parse.serverURL + "/classes/TestObject", qs: { - keys: [''], + keys: [""], where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); - ok(response4.data.results[0].objectId, 'expected objectId to be set'); - ok(response4.data.results[0].createdAt, 'expected object createdAt to be set'); - ok(response4.data.results[0].updatedAt, 'expected object updatedAt to be set'); + ok(response4.data.results[0].objectId, "expected objectId to be set"); + ok( + response4.data.results[0].createdAt, + "expected object createdAt to be set" + ); + ok( + response4.data.results[0].updatedAt, + "expected object updatedAt to be set" + ); expect(response4.data.results[0].foo).toBeUndefined(); expect(response4.data.results[0].bar).toBeUndefined(); expect(response4.data.results[0].hello).toBeUndefined(); const response5 = await request({ - url: Parse.serverURL + '/classes/TestObject', + url: Parse.serverURL + "/classes/TestObject", qs: { keys: [], where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); - ok(response5.data.results[0].objectId, 'expected objectId to be set'); - ok(response5.data.results[0].createdAt, 'expected object createdAt to be set'); - ok(response5.data.results[0].updatedAt, 'expected object updatedAt to be set'); - expect(response5.data.results[0].foo).toBe('baz'); + ok(response5.data.results[0].objectId, "expected objectId to be set"); + ok( + response5.data.results[0].createdAt, + "expected object createdAt to be set" + ); + ok( + response5.data.results[0].updatedAt, + "expected object updatedAt to be set" + ); + expect(response5.data.results[0].foo).toBe("baz"); expect(response5.data.results[0].bar).toBe(1); - expect(response5.data.results[0].hello).toBe('world'); + expect(response5.data.results[0].hello).toBe("world"); }); - it('select keys (strings)', async () => { - const obj = new TestObject({ foo: 'baz', bar: 1, hello: 'world' }); + it("select keys (strings)", async () => { + const obj = new TestObject({ foo: "baz", bar: 1, hello: "world" }); await obj.save(); const response = await request({ - url: Parse.serverURL + '/classes/TestObject', + url: Parse.serverURL + "/classes/TestObject", qs: { - keys: '', + keys: "", where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); - ok(response.data.results[0].objectId, 'expected objectId to be set'); - ok(response.data.results[0].createdAt, 'expected object createdAt to be set'); - ok(response.data.results[0].updatedAt, 'expected object updatedAt to be set'); + ok(response.data.results[0].objectId, "expected objectId to be set"); + ok( + response.data.results[0].createdAt, + "expected object createdAt to be set" + ); + ok( + response.data.results[0].updatedAt, + "expected object updatedAt to be set" + ); expect(response.data.results[0].foo).toBeUndefined(); expect(response.data.results[0].bar).toBeUndefined(); expect(response.data.results[0].hello).toBeUndefined(); const response2 = await request({ - url: Parse.serverURL + '/classes/TestObject', + url: Parse.serverURL + "/classes/TestObject", qs: { keys: '["foo", "hello"]', where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); - ok(response2.data.results[0].objectId, 'expected objectId to be set'); - ok(response2.data.results[0].createdAt, 'expected object createdAt to be set'); - ok(response2.data.results[0].updatedAt, 'expected object updatedAt to be set'); - expect(response2.data.results[0].foo).toBe('baz'); + ok(response2.data.results[0].objectId, "expected objectId to be set"); + ok( + response2.data.results[0].createdAt, + "expected object createdAt to be set" + ); + ok( + response2.data.results[0].updatedAt, + "expected object updatedAt to be set" + ); + expect(response2.data.results[0].foo).toBe("baz"); expect(response2.data.results[0].bar).toBeUndefined(); - expect(response2.data.results[0].hello).toBe('world'); + expect(response2.data.results[0].hello).toBe("world"); const response3 = await request({ - url: Parse.serverURL + '/classes/TestObject', + url: Parse.serverURL + "/classes/TestObject", qs: { keys: '["foo", "bar", "hello"]', where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); - ok(response3.data.results[0].objectId, 'expected objectId to be set'); - ok(response3.data.results[0].createdAt, 'expected object createdAt to be set'); - ok(response3.data.results[0].updatedAt, 'expected object updatedAt to be set'); - expect(response3.data.results[0].foo).toBe('baz'); + ok(response3.data.results[0].objectId, "expected objectId to be set"); + ok( + response3.data.results[0].createdAt, + "expected object createdAt to be set" + ); + ok( + response3.data.results[0].updatedAt, + "expected object updatedAt to be set" + ); + expect(response3.data.results[0].foo).toBe("baz"); expect(response3.data.results[0].bar).toBe(1); - expect(response3.data.results[0].hello).toBe('world'); + expect(response3.data.results[0].hello).toBe("world"); }); - it('exclude keys query JS SDK', async () => { - const obj = new TestObject({ foo: 'baz', bar: 1, qux: 2 }); + it("exclude keys query JS SDK", async () => { + const obj = new TestObject({ foo: "baz", bar: 1, qux: 2 }); await obj.save(); obj._clearServerData(); const query1 = new Parse.Query(TestObject); - query1.exclude('foo'); + query1.exclude("foo"); const result1 = await query1.first(); - ok(result1.id, 'expected object id to be set'); - ok(result1.createdAt, 'expected object createdAt to be set'); - ok(result1.updatedAt, 'expected object updatedAt to be set'); - ok(!result1.dirty(), 'expected result not to be dirty'); - strictEqual(result1.get('foo'), undefined, "expected 'bar' field to be unset"); - strictEqual(result1.get('bar'), 1); - strictEqual(result1.get('qux'), 2); + ok(result1.id, "expected object id to be set"); + ok(result1.createdAt, "expected object createdAt to be set"); + ok(result1.updatedAt, "expected object updatedAt to be set"); + ok(!result1.dirty(), "expected result not to be dirty"); + strictEqual( + result1.get("foo"), + undefined, + "expected 'bar' field to be unset" + ); + strictEqual(result1.get("bar"), 1); + strictEqual(result1.get("qux"), 2); const result2 = await result1.fetch(); - strictEqual(result2.get('foo'), 'baz'); - strictEqual(result2.get('bar'), 1); - strictEqual(result2.get('qux'), 2); + strictEqual(result2.get("foo"), "baz"); + strictEqual(result2.get("bar"), 1); + strictEqual(result2.get("qux"), 2); obj._clearServerData(); const query2 = new Parse.Query(TestObject); query2.exclude(); const result3 = await query2.first(); - ok(result3.id, 'expected object id to be set'); - ok(result3.createdAt, 'expected object createdAt to be set'); - ok(result3.updatedAt, 'expected object updatedAt to be set'); - ok(!result3.dirty(), 'expected result not to be dirty'); - strictEqual(result3.get('foo'), 'baz'); - strictEqual(result3.get('bar'), 1); - strictEqual(result3.get('qux'), 2); + ok(result3.id, "expected object id to be set"); + ok(result3.createdAt, "expected object createdAt to be set"); + ok(result3.updatedAt, "expected object updatedAt to be set"); + ok(!result3.dirty(), "expected result not to be dirty"); + strictEqual(result3.get("foo"), "baz"); + strictEqual(result3.get("bar"), 1); + strictEqual(result3.get("qux"), 2); obj._clearServerData(); const query3 = new Parse.Query(TestObject); query3.exclude([]); const result4 = await query3.first(); - ok(result4.id, 'expected object id to be set'); - ok(result4.createdAt, 'expected object createdAt to be set'); - ok(result4.updatedAt, 'expected object updatedAt to be set'); - ok(!result4.dirty(), 'expected result not to be dirty'); - strictEqual(result4.get('foo'), 'baz'); - strictEqual(result4.get('bar'), 1); - strictEqual(result4.get('qux'), 2); + ok(result4.id, "expected object id to be set"); + ok(result4.createdAt, "expected object createdAt to be set"); + ok(result4.updatedAt, "expected object updatedAt to be set"); + ok(!result4.dirty(), "expected result not to be dirty"); + strictEqual(result4.get("foo"), "baz"); + strictEqual(result4.get("bar"), 1); + strictEqual(result4.get("qux"), 2); obj._clearServerData(); const query4 = new Parse.Query(TestObject); - query4.exclude(['foo']); + query4.exclude(["foo"]); const result5 = await query4.first(); - ok(result5.id, 'expected object id to be set'); - ok(result5.createdAt, 'expected object createdAt to be set'); - ok(result5.updatedAt, 'expected object updatedAt to be set'); - ok(!result5.dirty(), 'expected result not to be dirty'); - strictEqual(result5.get('foo'), undefined, "expected 'bar' field to be unset"); - strictEqual(result5.get('bar'), 1); - strictEqual(result5.get('qux'), 2); + ok(result5.id, "expected object id to be set"); + ok(result5.createdAt, "expected object createdAt to be set"); + ok(result5.updatedAt, "expected object updatedAt to be set"); + ok(!result5.dirty(), "expected result not to be dirty"); + strictEqual( + result5.get("foo"), + undefined, + "expected 'bar' field to be unset" + ); + strictEqual(result5.get("bar"), 1); + strictEqual(result5.get("qux"), 2); obj._clearServerData(); const query5 = new Parse.Query(TestObject); - query5.exclude(['foo', 'bar']); + query5.exclude(["foo", "bar"]); const result6 = await query5.first(); - ok(result6.id, 'expected object id to be set'); - ok(!result6.dirty(), 'expected result not to be dirty'); - strictEqual(result6.get('foo'), undefined, "expected 'bar' field to be unset"); - strictEqual(result6.get('bar'), undefined, "expected 'bar' field to be unset"); - strictEqual(result6.get('qux'), 2); + ok(result6.id, "expected object id to be set"); + ok(!result6.dirty(), "expected result not to be dirty"); + strictEqual( + result6.get("foo"), + undefined, + "expected 'bar' field to be unset" + ); + strictEqual( + result6.get("bar"), + undefined, + "expected 'bar' field to be unset" + ); + strictEqual(result6.get("qux"), 2); obj._clearServerData(); const query6 = new Parse.Query(TestObject); - query6.exclude(['foo', 'bar', 'qux']); + query6.exclude(["foo", "bar", "qux"]); const result7 = await query6.first(); - ok(result7.id, 'expected object id to be set'); - ok(!result7.dirty(), 'expected result not to be dirty'); - strictEqual(result7.get('foo'), undefined, "expected 'bar' field to be unset"); - strictEqual(result7.get('bar'), undefined, "expected 'bar' field to be unset"); - strictEqual(result7.get('qux'), undefined, "expected 'bar' field to be unset"); + ok(result7.id, "expected object id to be set"); + ok(!result7.dirty(), "expected result not to be dirty"); + strictEqual( + result7.get("foo"), + undefined, + "expected 'bar' field to be unset" + ); + strictEqual( + result7.get("bar"), + undefined, + "expected 'bar' field to be unset" + ); + strictEqual( + result7.get("qux"), + undefined, + "expected 'bar' field to be unset" + ); obj._clearServerData(); const query7 = new Parse.Query(TestObject); - query7.exclude('foo'); + query7.exclude("foo"); const result8 = await query7.first(); - ok(result8.id, 'expected object id to be set'); - ok(!result8.dirty(), 'expected result not to be dirty'); - strictEqual(result8.get('foo'), undefined, "expected 'bar' field to be unset"); - strictEqual(result8.get('bar'), 1); - strictEqual(result8.get('qux'), 2); + ok(result8.id, "expected object id to be set"); + ok(!result8.dirty(), "expected result not to be dirty"); + strictEqual( + result8.get("foo"), + undefined, + "expected 'bar' field to be unset" + ); + strictEqual(result8.get("bar"), 1); + strictEqual(result8.get("qux"), 2); obj._clearServerData(); const query8 = new Parse.Query(TestObject); - query8.exclude('foo', 'bar'); + query8.exclude("foo", "bar"); const result9 = await query8.first(); - ok(result9.id, 'expected object id to be set'); - ok(!result9.dirty(), 'expected result not to be dirty'); - strictEqual(result9.get('foo'), undefined, "expected 'bar' field to be unset"); - strictEqual(result9.get('bar'), undefined, "expected 'bar' field to be unset"); - strictEqual(result9.get('qux'), 2); + ok(result9.id, "expected object id to be set"); + ok(!result9.dirty(), "expected result not to be dirty"); + strictEqual( + result9.get("foo"), + undefined, + "expected 'bar' field to be unset" + ); + strictEqual( + result9.get("bar"), + undefined, + "expected 'bar' field to be unset" + ); + strictEqual(result9.get("qux"), 2); obj._clearServerData(); const query9 = new Parse.Query(TestObject); - query9.exclude('foo', 'bar', 'qux'); + query9.exclude("foo", "bar", "qux"); const result10 = await query9.first(); - ok(result10.id, 'expected object id to be set'); - ok(!result10.dirty(), 'expected result not to be dirty'); - strictEqual(result10.get('foo'), undefined, "expected 'bar' field to be unset"); - strictEqual(result10.get('bar'), undefined, "expected 'bar' field to be unset"); - strictEqual(result10.get('qux'), undefined, "expected 'bar' field to be unset"); + ok(result10.id, "expected object id to be set"); + ok(!result10.dirty(), "expected result not to be dirty"); + strictEqual( + result10.get("foo"), + undefined, + "expected 'bar' field to be unset" + ); + strictEqual( + result10.get("bar"), + undefined, + "expected 'bar' field to be unset" + ); + strictEqual( + result10.get("qux"), + undefined, + "expected 'bar' field to be unset" + ); }); - it('exclude keys (arrays)', async () => { - const obj = new TestObject({ foo: 'baz', hello: 'world' }); + it("exclude keys (arrays)", async () => { + const obj = new TestObject({ foo: "baz", hello: "world" }); await obj.save(); const response = await request({ - url: Parse.serverURL + '/classes/TestObject', + url: Parse.serverURL + "/classes/TestObject", qs: { - excludeKeys: ['foo'], + excludeKeys: ["foo"], where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); - ok(response.data.results[0].objectId, 'expected objectId to be set'); - ok(response.data.results[0].createdAt, 'expected object createdAt to be set'); - ok(response.data.results[0].updatedAt, 'expected object updatedAt to be set'); + ok(response.data.results[0].objectId, "expected objectId to be set"); + ok( + response.data.results[0].createdAt, + "expected object createdAt to be set" + ); + ok( + response.data.results[0].updatedAt, + "expected object updatedAt to be set" + ); expect(response.data.results[0].foo).toBeUndefined(); - expect(response.data.results[0].hello).toBe('world'); + expect(response.data.results[0].hello).toBe("world"); const response2 = await request({ - url: Parse.serverURL + '/classes/TestObject', + url: Parse.serverURL + "/classes/TestObject", qs: { - excludeKeys: ['foo', 'hello'], + excludeKeys: ["foo", "hello"], where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); - ok(response2.data.results[0].objectId, 'expected objectId to be set'); - ok(response2.data.results[0].createdAt, 'expected object createdAt to be set'); - ok(response2.data.results[0].updatedAt, 'expected object updatedAt to be set'); + ok(response2.data.results[0].objectId, "expected objectId to be set"); + ok( + response2.data.results[0].createdAt, + "expected object createdAt to be set" + ); + ok( + response2.data.results[0].updatedAt, + "expected object updatedAt to be set" + ); expect(response2.data.results[0].foo).toBeUndefined(); expect(response2.data.results[0].hello).toBeUndefined(); const response3 = await request({ - url: Parse.serverURL + '/classes/TestObject', + url: Parse.serverURL + "/classes/TestObject", qs: { excludeKeys: [], where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); - ok(response3.data.results[0].objectId, 'expected objectId to be set'); - ok(response3.data.results[0].createdAt, 'expected object createdAt to be set'); - ok(response3.data.results[0].updatedAt, 'expected object updatedAt to be set'); - expect(response3.data.results[0].foo).toBe('baz'); - expect(response3.data.results[0].hello).toBe('world'); + ok(response3.data.results[0].objectId, "expected objectId to be set"); + ok( + response3.data.results[0].createdAt, + "expected object createdAt to be set" + ); + ok( + response3.data.results[0].updatedAt, + "expected object updatedAt to be set" + ); + expect(response3.data.results[0].foo).toBe("baz"); + expect(response3.data.results[0].hello).toBe("world"); const response4 = await request({ - url: Parse.serverURL + '/classes/TestObject', + url: Parse.serverURL + "/classes/TestObject", qs: { - excludeKeys: [''], + excludeKeys: [""], where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); - ok(response4.data.results[0].objectId, 'expected objectId to be set'); - ok(response4.data.results[0].createdAt, 'expected object createdAt to be set'); - ok(response4.data.results[0].updatedAt, 'expected object updatedAt to be set'); - expect(response4.data.results[0].foo).toBe('baz'); - expect(response4.data.results[0].hello).toBe('world'); + ok(response4.data.results[0].objectId, "expected objectId to be set"); + ok( + response4.data.results[0].createdAt, + "expected object createdAt to be set" + ); + ok( + response4.data.results[0].updatedAt, + "expected object updatedAt to be set" + ); + expect(response4.data.results[0].foo).toBe("baz"); + expect(response4.data.results[0].hello).toBe("world"); }); - it('exclude keys (strings)', async () => { - const obj = new TestObject({ foo: 'baz', hello: 'world' }); + it("exclude keys (strings)", async () => { + const obj = new TestObject({ foo: "baz", hello: "world" }); await obj.save(); const response = await request({ - url: Parse.serverURL + '/classes/TestObject', + url: Parse.serverURL + "/classes/TestObject", qs: { - excludeKeys: 'foo', + excludeKeys: "foo", where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); - ok(response.data.results[0].objectId, 'expected objectId to be set'); - ok(response.data.results[0].createdAt, 'expected object createdAt to be set'); - ok(response.data.results[0].updatedAt, 'expected object updatedAt to be set'); + ok(response.data.results[0].objectId, "expected objectId to be set"); + ok( + response.data.results[0].createdAt, + "expected object createdAt to be set" + ); + ok( + response.data.results[0].updatedAt, + "expected object updatedAt to be set" + ); expect(response.data.results[0].foo).toBeUndefined(); - expect(response.data.results[0].hello).toBe('world'); + expect(response.data.results[0].hello).toBe("world"); const response2 = await request({ - url: Parse.serverURL + '/classes/TestObject', + url: Parse.serverURL + "/classes/TestObject", qs: { - excludeKeys: '', + excludeKeys: "", where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); - ok(response2.data.results[0].objectId, 'expected objectId to be set'); - ok(response2.data.results[0].createdAt, 'expected object createdAt to be set'); - ok(response2.data.results[0].updatedAt, 'expected object updatedAt to be set'); - expect(response2.data.results[0].foo).toBe('baz'); - expect(response2.data.results[0].hello).toBe('world'); + ok(response2.data.results[0].objectId, "expected objectId to be set"); + ok( + response2.data.results[0].createdAt, + "expected object createdAt to be set" + ); + ok( + response2.data.results[0].updatedAt, + "expected object updatedAt to be set" + ); + expect(response2.data.results[0].foo).toBe("baz"); + expect(response2.data.results[0].hello).toBe("world"); const response3 = await request({ - url: Parse.serverURL + '/classes/TestObject', + url: Parse.serverURL + "/classes/TestObject", qs: { excludeKeys: '["hello"]', where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); - ok(response3.data.results[0].objectId, 'expected objectId to be set'); - ok(response3.data.results[0].createdAt, 'expected object createdAt to be set'); - ok(response3.data.results[0].updatedAt, 'expected object updatedAt to be set'); - expect(response3.data.results[0].foo).toBe('baz'); + ok(response3.data.results[0].objectId, "expected objectId to be set"); + ok( + response3.data.results[0].createdAt, + "expected object createdAt to be set" + ); + ok( + response3.data.results[0].updatedAt, + "expected object updatedAt to be set" + ); + expect(response3.data.results[0].foo).toBe("baz"); expect(response3.data.results[0].hello).toBeUndefined(); const response4 = await request({ - url: Parse.serverURL + '/classes/TestObject', + url: Parse.serverURL + "/classes/TestObject", qs: { excludeKeys: '["foo", "hello"]', where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); - ok(response4.data.results[0].objectId, 'expected objectId to be set'); - ok(response4.data.results[0].createdAt, 'expected object createdAt to be set'); - ok(response4.data.results[0].updatedAt, 'expected object updatedAt to be set'); + ok(response4.data.results[0].objectId, "expected objectId to be set"); + ok( + response4.data.results[0].createdAt, + "expected object createdAt to be set" + ); + ok( + response4.data.results[0].updatedAt, + "expected object updatedAt to be set" + ); expect(response4.data.results[0].foo).toBeUndefined(); expect(response4.data.results[0].hello).toBeUndefined(); }); - it('exclude keys with select same key', async () => { - const obj = new TestObject({ foo: 'baz', hello: 'world' }); + it("exclude keys with select same key", async () => { + const obj = new TestObject({ foo: "baz", hello: "world" }); await obj.save(); const response = await request({ - url: Parse.serverURL + '/classes/TestObject', + url: Parse.serverURL + "/classes/TestObject", qs: { - keys: 'foo', - excludeKeys: 'foo', + keys: "foo", + excludeKeys: "foo", where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, @@ -3692,105 +3988,109 @@ describe('Parse.Query testing', () => { expect(response.data.results[0].hello).toBeUndefined(); }); - it('exclude keys with select different key', async () => { - const obj = new TestObject({ foo: 'baz', hello: 'world' }); + it("exclude keys with select different key", async () => { + const obj = new TestObject({ foo: "baz", hello: "world" }); await obj.save(); const response = await request({ - url: Parse.serverURL + '/classes/TestObject', + url: Parse.serverURL + "/classes/TestObject", qs: { - keys: 'foo,hello', - excludeKeys: 'foo', + keys: "foo,hello", + excludeKeys: "foo", where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); expect(response.data.results[0].foo).toBeUndefined(); - expect(response.data.results[0].hello).toBe('world'); + expect(response.data.results[0].hello).toBe("world"); }); - it('exclude keys with include same key', async () => { + it("exclude keys with include same key", async () => { const pointer = new TestObject(); await pointer.save(); - const obj = new TestObject({ child: pointer, hello: 'world' }); + const obj = new TestObject({ child: pointer, hello: "world" }); await obj.save(); const response = await request({ - url: Parse.serverURL + '/classes/TestObject', + url: Parse.serverURL + "/classes/TestObject", qs: { - include: 'child', - excludeKeys: 'child', + include: "child", + excludeKeys: "child", where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); expect(response.data.results[0].child).toBeUndefined(); - expect(response.data.results[0].hello).toBe('world'); + expect(response.data.results[0].hello).toBe("world"); }); - it('exclude keys with include different key', async () => { + it("exclude keys with include different key", async () => { const pointer = new TestObject(); await pointer.save(); const obj = new TestObject({ child1: pointer, child2: pointer, - hello: 'world', + hello: "world", }); await obj.save(); const response = await request({ - url: Parse.serverURL + '/classes/TestObject', + url: Parse.serverURL + "/classes/TestObject", qs: { - include: 'child1,child2', - excludeKeys: 'child1', + include: "child1,child2", + excludeKeys: "child1", where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); expect(response.data.results[0].child1).toBeUndefined(); expect(response.data.results[0].child2.objectId).toEqual(pointer.id); - expect(response.data.results[0].hello).toBe('world'); + expect(response.data.results[0].hello).toBe("world"); }); - it('exclude keys with includeAll', async () => { + it("exclude keys with includeAll", async () => { const pointer = new TestObject(); await pointer.save(); const obj = new TestObject({ child1: pointer, child2: pointer, - hello: 'world', + hello: "world", }); await obj.save(); const response = await request({ - url: Parse.serverURL + '/classes/TestObject', + url: Parse.serverURL + "/classes/TestObject", qs: { includeAll: true, - excludeKeys: 'child1', + excludeKeys: "child1", where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); expect(response.data.results[0].child).toBeUndefined(); expect(response.data.results[0].child2.objectId).toEqual(pointer.id); - expect(response.data.results[0].hello).toBe('world'); + expect(response.data.results[0].hello).toBe("world"); }); - it('select keys with each query', function (done) { - const obj = new TestObject({ foo: 'baz', bar: 1 }); + it("select keys with each query", function (done) { + const obj = new TestObject({ foo: "baz", bar: 1 }); obj.save().then(function () { obj._clearServerData(); const query = new Parse.Query(TestObject); - query.select('foo'); + query.select("foo"); query .each(function (result) { - ok(result.id, 'expected object id to be set'); - ok(result.createdAt, 'expected object createdAt to be set'); - ok(result.updatedAt, 'expected object updatedAt to be set'); - ok(!result.dirty(), 'expected result not to be dirty'); - strictEqual(result.get('foo'), 'baz'); - strictEqual(result.get('bar'), undefined, 'expected "bar" field to be unset'); + ok(result.id, "expected object id to be set"); + ok(result.createdAt, "expected object createdAt to be set"); + ok(result.updatedAt, "expected object updatedAt to be set"); + ok(!result.dirty(), "expected result not to be dirty"); + strictEqual(result.get("foo"), "baz"); + strictEqual( + result.get("bar"), + undefined, + 'expected "bar" field to be unset' + ); }) .then( function () { @@ -3804,73 +4104,76 @@ describe('Parse.Query testing', () => { }); }); - it_id('56b09b92-c756-4bae-8c32-1c32b5b4c397')(it)('notEqual with array of pointers', done => { - const children = []; - const parents = []; - const promises = []; - for (let i = 0; i < 2; i++) { - const proc = iter => { - const child = new Parse.Object('Child'); - children.push(child); - const parent = new Parse.Object('Parent'); - parents.push(parent); - promises.push( - child.save().then(() => { - parents[iter].set('child', [children[iter]]); - return parents[iter].save(); - }) - ); - }; - proc(i); + it_id("56b09b92-c756-4bae-8c32-1c32b5b4c397")(it)( + "notEqual with array of pointers", + done => { + const children = []; + const parents = []; + const promises = []; + for (let i = 0; i < 2; i++) { + const proc = iter => { + const child = new Parse.Object("Child"); + children.push(child); + const parent = new Parse.Object("Parent"); + parents.push(parent); + promises.push( + child.save().then(() => { + parents[iter].set("child", [children[iter]]); + return parents[iter].save(); + }) + ); + }; + proc(i); + } + Promise.all(promises) + .then(() => { + const query = new Parse.Query("Parent"); + query.notEqualTo("child", children[0]); + return query.find(); + }) + .then(results => { + expect(results.length).toEqual(1); + expect(results[0].id).toEqual(parents[1].id); + done(); + }) + .catch(error => { + console.log(error); + }); } - Promise.all(promises) - .then(() => { - const query = new Parse.Query('Parent'); - query.notEqualTo('child', children[0]); - return query.find(); - }) - .then(results => { - expect(results.length).toEqual(1); - expect(results[0].id).toEqual(parents[1].id); - done(); - }) - .catch(error => { - console.log(error); - }); - }); + ); // PG don't support creating a null column - it_exclude_dbs(['postgres'])('querying for null value', done => { - const obj = new Parse.Object('TestObject'); - obj.set('aNull', null); + it_exclude_dbs(["postgres"])("querying for null value", done => { + const obj = new Parse.Object("TestObject"); + obj.set("aNull", null); obj .save() .then(() => { - const query = new Parse.Query('TestObject'); - query.equalTo('aNull', null); + const query = new Parse.Query("TestObject"); + query.equalTo("aNull", null); return query.find(); }) .then(results => { expect(results.length).toEqual(1); - expect(results[0].get('aNull')).toEqual(null); + expect(results[0].get("aNull")).toEqual(null); done(); }); }); - it('query within dictionary', done => { + it("query within dictionary", done => { const promises = []; for (let i = 0; i < 2; i++) { const proc = iter => { - const obj = new Parse.Object('TestObject'); - obj.set('aDict', { x: iter + 1, y: iter + 2 }); + const obj = new Parse.Object("TestObject"); + obj.set("aDict", { x: iter + 1, y: iter + 2 }); promises.push(obj.save()); }; proc(i); } Promise.all(promises) .then(() => { - const query = new Parse.Query('TestObject'); - query.equalTo('aDict.x', 1); + const query = new Parse.Query("TestObject"); + query.equalTo("aDict.x", 1); return query.find(); }) .then( @@ -3884,52 +4187,52 @@ describe('Parse.Query testing', () => { ); }); - it('supports include on the wrong key type (#2262)', function (done) { - const childObject = new Parse.Object('TestChildObject'); - childObject.set('hello', 'world'); + it("supports include on the wrong key type (#2262)", function (done) { + const childObject = new Parse.Object("TestChildObject"); + childObject.set("hello", "world"); childObject .save() .then(() => { - const obj = new Parse.Object('TestObject'); - obj.set('foo', 'bar'); - obj.set('child', childObject); + const obj = new Parse.Object("TestObject"); + obj.set("foo", "bar"); + obj.set("child", childObject); return obj.save(); }) .then(() => { - const q = new Parse.Query('TestObject'); - q.include('child'); - q.include('child.parent'); - q.include('createdAt'); - q.include('createdAt.createdAt'); + const q = new Parse.Query("TestObject"); + q.include("child"); + q.include("child.parent"); + q.include("createdAt"); + q.include("createdAt.createdAt"); return q.find(); }) .then( objs => { expect(objs.length).toBe(1); - expect(objs[0].get('child').get('hello')).toEqual('world'); + expect(objs[0].get("child").get("hello")).toEqual("world"); expect(objs[0].createdAt instanceof Date).toBe(true); done(); }, () => { - fail('should not fail'); + fail("should not fail"); done(); } ); }); - it('query match on array with single object', done => { + it("query match on array with single object", done => { const target = { - __type: 'Pointer', - className: 'TestObject', - objectId: 'abc123', + __type: "Pointer", + className: "TestObject", + objectId: "abc123", }; - const obj = new Parse.Object('TestObject'); - obj.set('someObjs', [target]); + const obj = new Parse.Object("TestObject"); + obj.set("someObjs", [target]); obj .save() .then(() => { - const query = new Parse.Query('TestObject'); - query.equalTo('someObjs', target); + const query = new Parse.Query("TestObject"); + query.equalTo("someObjs", target); return query.find(); }) .then( @@ -3943,24 +4246,24 @@ describe('Parse.Query testing', () => { ); }); - it('query match on array with multiple objects', done => { + it("query match on array with multiple objects", done => { const target1 = { - __type: 'Pointer', - className: 'TestObject', - objectId: 'abc', + __type: "Pointer", + className: "TestObject", + objectId: "abc", }; const target2 = { - __type: 'Pointer', - className: 'TestObject', - objectId: '123', + __type: "Pointer", + className: "TestObject", + objectId: "123", }; - const obj = new Parse.Object('TestObject'); - obj.set('someObjs', [target1, target2]); + const obj = new Parse.Object("TestObject"); + obj.set("someObjs", [target1, target2]); obj .save() .then(() => { - const query = new Parse.Query('TestObject'); - query.equalTo('someObjs', target1); + const query = new Parse.Query("TestObject"); + query.equalTo("someObjs", target1); return query.find(); }) .then( @@ -3974,21 +4277,21 @@ describe('Parse.Query testing', () => { ); }); - it('query should not match on array when searching for null', done => { + it("query should not match on array when searching for null", done => { const target = { - __type: 'Pointer', - className: 'TestObject', - objectId: '123', + __type: "Pointer", + className: "TestObject", + objectId: "123", }; - const obj = new Parse.Object('TestObject'); - obj.set('someKey', 'someValue'); - obj.set('someObjs', [target]); + const obj = new Parse.Object("TestObject"); + obj.set("someKey", "someValue"); + obj.set("someObjs", [target]); obj .save() .then(() => { - const query = new Parse.Query('TestObject'); - query.equalTo('someKey', 'someValue'); - query.equalTo('someObjs', null); + const query = new Parse.Query("TestObject"); + query.equalTo("someKey", "someValue"); + query.equalTo("someObjs", null); return query.find(); }) .then( @@ -4003,49 +4306,64 @@ describe('Parse.Query testing', () => { }); // #371 - it('should properly interpret a query v1', done => { - const query = new Parse.Query('C1'); - const auxQuery = new Parse.Query('C1'); - query.matchesKeyInQuery('A1', 'A2', auxQuery); - query.include('A3'); - query.include('A2'); + it("should properly interpret a query v1", done => { + const query = new Parse.Query("C1"); + const auxQuery = new Parse.Query("C1"); + query.matchesKeyInQuery("A1", "A2", auxQuery); + query.include("A3"); + query.include("A2"); query.find().then( () => { done(); }, err => { jfail(err); - fail('should not failt'); + fail("should not failt"); done(); } ); }); - it_id('7079f0ef-47b3-4a1e-aac0-32654dadaa27')(it)( - 'should properly interpret a query v2', + it_id("7079f0ef-47b3-4a1e-aac0-32654dadaa27")(it)( + "should properly interpret a query v2", done => { const user = new Parse.User(); - user.set('username', 'foo'); - user.set('password', 'bar'); + user.set("username", "foo"); + user.set("password", "bar"); return user .save() .then(user => { - const objIdQuery = new Parse.Query('_User').equalTo('objectId', user.id); - const blockedUserQuery = user.relation('blockedUsers').query(); + const objIdQuery = new Parse.Query("_User").equalTo( + "objectId", + user.id + ); + const blockedUserQuery = user.relation("blockedUsers").query(); - const aResponseQuery = new Parse.Query('MatchRelationshipActivityResponse'); - aResponseQuery.equalTo('userA', user); - aResponseQuery.equalTo('userAResponse', 1); + const aResponseQuery = new Parse.Query( + "MatchRelationshipActivityResponse" + ); + aResponseQuery.equalTo("userA", user); + aResponseQuery.equalTo("userAResponse", 1); - const bResponseQuery = new Parse.Query('MatchRelationshipActivityResponse'); - bResponseQuery.equalTo('userB', user); - bResponseQuery.equalTo('userBResponse', 1); + const bResponseQuery = new Parse.Query( + "MatchRelationshipActivityResponse" + ); + bResponseQuery.equalTo("userB", user); + bResponseQuery.equalTo("userBResponse", 1); const matchOr = Parse.Query.or(aResponseQuery, bResponseQuery); - const matchRelationshipA = new Parse.Query('_User'); - matchRelationshipA.matchesKeyInQuery('objectId', 'userAObjectId', matchOr); - const matchRelationshipB = new Parse.Query('_User'); - matchRelationshipB.matchesKeyInQuery('objectId', 'userBObjectId', matchOr); + const matchRelationshipA = new Parse.Query("_User"); + matchRelationshipA.matchesKeyInQuery( + "objectId", + "userAObjectId", + matchOr + ); + const matchRelationshipB = new Parse.Query("_User"); + matchRelationshipB.matchesKeyInQuery( + "objectId", + "userBObjectId", + matchOr + ); const orQuery = Parse.Query.or( objIdQuery, @@ -4053,8 +4371,8 @@ describe('Parse.Query testing', () => { matchRelationshipA, matchRelationshipB ); - const query = new Parse.Query('_User'); - query.doesNotMatchQuery('objectId', orQuery); + const query = new Parse.Query("_User"); + query.doesNotMatchQuery("objectId", orQuery); return query.find(); }) .then( @@ -4063,17 +4381,17 @@ describe('Parse.Query testing', () => { }, err => { jfail(err); - fail('should not fail'); + fail("should not fail"); done(); } ); } ); - it('should match a key in an array (#3195)', function (done) { - const AuthorObject = Parse.Object.extend('Author'); - const GroupObject = Parse.Object.extend('Group'); - const PostObject = Parse.Object.extend('Post'); + it("should match a key in an array (#3195)", function (done) { + const AuthorObject = Parse.Object.extend("Author"); + const GroupObject = Parse.Object.extend("Group"); + const PostObject = Parse.Object.extend("Post"); return new AuthorObject() .save() @@ -4091,7 +4409,7 @@ describe('Parse.Query testing', () => { .then(results => { const p = results[0]; return new Parse.Query(PostObject) - .matchesKeyInQuery('author', 'members', new Parse.Query(GroupObject)) + .matchesKeyInQuery("author", "members", new Parse.Query(GroupObject)) .find() .then(r => { expect(r.length).toEqual(1); @@ -4103,36 +4421,36 @@ describe('Parse.Query testing', () => { }); }); - it_id('d95818c0-9e3c-41e6-be20-e7bafb59eefb')(it)( - 'should find objects with array of pointers', + it_id("d95818c0-9e3c-41e6-be20-e7bafb59eefb")(it)( + "should find objects with array of pointers", done => { const objects = []; while (objects.length != 5) { - const object = new Parse.Object('ContainedObject'); - object.set('index', objects.length); + const object = new Parse.Object("ContainedObject"); + object.set("index", objects.length); objects.push(object); } Parse.Object.saveAll(objects) .then(objects => { - const container = new Parse.Object('Container'); + const container = new Parse.Object("Container"); const pointers = objects.map(obj => { return { - __type: 'Pointer', - className: 'ContainedObject', + __type: "Pointer", + className: "ContainedObject", objectId: obj.id, }; }); - container.set('objects', pointers); - const container2 = new Parse.Object('Container'); - container2.set('objects', pointers.slice(2, 3)); + container.set("objects", pointers); + const container2 = new Parse.Object("Container"); + container2.set("objects", pointers.slice(2, 3)); return Parse.Object.saveAll([container, container2]); }) .then(() => { - const inQuery = new Parse.Query('ContainedObject'); - inQuery.greaterThanOrEqualTo('index', 1); - const query = new Parse.Query('Container'); - query.matchesQuery('objects', inQuery); + const inQuery = new Parse.Query("ContainedObject"); + inQuery.greaterThanOrEqualTo("index", 1); + const query = new Parse.Query("Container"); + query.matchesQuery("objects", inQuery); return query.find(); }) .then(results => { @@ -4143,27 +4461,27 @@ describe('Parse.Query testing', () => { }) .catch(err => { jfail(err); - fail('should not fail'); + fail("should not fail"); done(); }); } ); - it('query with two OR subqueries (regression test #1259)', done => { - const relatedObject = new Parse.Object('Class2'); + it("query with two OR subqueries (regression test #1259)", done => { + const relatedObject = new Parse.Object("Class2"); relatedObject .save() .then(relatedObject => { - const anObject = new Parse.Object('Class1'); - const relation = anObject.relation('relation'); + const anObject = new Parse.Object("Class1"); + const relation = anObject.relation("relation"); relation.add(relatedObject); return anObject.save(); }) .then(anObject => { - const q1 = anObject.relation('relation').query(); - q1.doesNotExist('nonExistantKey1'); - const q2 = anObject.relation('relation').query(); - q2.doesNotExist('nonExistantKey2'); + const q1 = anObject.relation("relation").query(); + q1.doesNotExist("nonExistantKey1"); + const q2 = anObject.relation("relation").query(); + q2.doesNotExist("nonExistantKey2"); Parse.Query.or(q1, q2) .find() .then(results => { @@ -4176,8 +4494,8 @@ describe('Parse.Query testing', () => { }); }); - it('objectId containedIn with multiple large array', done => { - const obj = new Parse.Object('MyClass'); + it("objectId containedIn with multiple large array", done => { + const obj = new Parse.Object("MyClass"); obj .save() .then(obj => { @@ -4186,9 +4504,9 @@ describe('Parse.Query testing', () => { longListOfStrings.push(i.toString()); } longListOfStrings.push(obj.id); - const q = new Parse.Query('MyClass'); - q.containedIn('objectId', longListOfStrings); - q.containedIn('objectId', longListOfStrings); + const q = new Parse.Query("MyClass"); + q.containedIn("objectId", longListOfStrings); + q.containedIn("objectId", longListOfStrings); return q.find(); }) .then(results => { @@ -4197,25 +4515,25 @@ describe('Parse.Query testing', () => { }); }); - it('containedIn with pointers should work with string array', done => { - const obj = new Parse.Object('MyClass'); - const child = new Parse.Object('Child'); + it("containedIn with pointers should work with string array", done => { + const obj = new Parse.Object("MyClass"); + const child = new Parse.Object("Child"); child .save() .then(() => { - obj.set('child', child); + obj.set("child", child); return obj.save(); }) .then(() => { const objs = []; for (let i = 0; i < 10; i++) { - objs.push(new Parse.Object('MyClass')); + objs.push(new Parse.Object("MyClass")); } return Parse.Object.saveAll(objs); }) .then(() => { - const query = new Parse.Query('MyClass'); - query.containedIn('child', [child.id]); + const query = new Parse.Query("MyClass"); + query.containedIn("child", [child.id]); return query.find(); }) .then(results => { @@ -4225,12 +4543,12 @@ describe('Parse.Query testing', () => { .catch(done.fail); }); - it('containedIn with pointers should work with string array, with many objects', done => { + it("containedIn with pointers should work with string array, with many objects", done => { const objs = []; const children = []; for (let i = 0; i < 10; i++) { - const obj = new Parse.Object('MyClass'); - const child = new Parse.Object('Child'); + const obj = new Parse.Object("MyClass"); + const child = new Parse.Object("Child"); objs.push(obj); children.push(child); } @@ -4238,17 +4556,17 @@ describe('Parse.Query testing', () => { .then(() => { return Parse.Object.saveAll( objs.map((obj, i) => { - obj.set('child', children[i]); + obj.set("child", children[i]); return obj; }) ); }) .then(() => { - const query = new Parse.Query('MyClass'); + const query = new Parse.Query("MyClass"); const subset = children.slice(0, 5).map(child => { return child.id; }); - query.containedIn('child', subset); + query.containedIn("child", subset); return query.find(); }) .then(results => { @@ -4258,194 +4576,197 @@ describe('Parse.Query testing', () => { .catch(done.fail); }); - it('include for specific object', function (done) { - const child = new Parse.Object('Child'); - const parent = new Parse.Object('Parent'); - child.set('foo', 'bar'); - parent.set('child', child); + it("include for specific object", function (done) { + const child = new Parse.Object("Child"); + const parent = new Parse.Object("Parent"); + child.set("foo", "bar"); + parent.set("child", child); Parse.Object.saveAll([child, parent]).then(function (response) { const savedParent = response[1]; - const parentQuery = new Parse.Query('Parent'); - parentQuery.include('child'); + const parentQuery = new Parse.Query("Parent"); + parentQuery.include("child"); parentQuery.get(savedParent.id).then(function (parentObj) { - const childPointer = parentObj.get('child'); + const childPointer = parentObj.get("child"); ok(childPointer); - equal(childPointer.get('foo'), 'bar'); + equal(childPointer.get("foo"), "bar"); done(); }); }); }); - it('select keys for specific object', function (done) { - const Foobar = new Parse.Object('Foobar'); - Foobar.set('foo', 'bar'); - Foobar.set('fizz', 'buzz'); + it("select keys for specific object", function (done) { + const Foobar = new Parse.Object("Foobar"); + Foobar.set("foo", "bar"); + Foobar.set("fizz", "buzz"); Foobar.save().then(function (savedFoobar) { - const foobarQuery = new Parse.Query('Foobar'); - foobarQuery.select('fizz'); + const foobarQuery = new Parse.Query("Foobar"); + foobarQuery.select("fizz"); foobarQuery.get(savedFoobar.id).then(function (foobarObj) { - equal(foobarObj.get('fizz'), 'buzz'); - equal(foobarObj.get('foo'), undefined); + equal(foobarObj.get("fizz"), "buzz"); + equal(foobarObj.get("foo"), undefined); done(); }); }); }); - it('select nested keys (issue #1567)', function (done) { - const Foobar = new Parse.Object('Foobar'); - const BarBaz = new Parse.Object('Barbaz'); - BarBaz.set('key', 'value'); - BarBaz.set('otherKey', 'value'); + it("select nested keys (issue #1567)", function (done) { + const Foobar = new Parse.Object("Foobar"); + const BarBaz = new Parse.Object("Barbaz"); + BarBaz.set("key", "value"); + BarBaz.set("otherKey", "value"); BarBaz.save() .then(() => { - Foobar.set('foo', 'bar'); - Foobar.set('fizz', 'buzz'); - Foobar.set('barBaz', BarBaz); + Foobar.set("foo", "bar"); + Foobar.set("fizz", "buzz"); + Foobar.set("barBaz", BarBaz); return Foobar.save(); }) .then(function (savedFoobar) { - const foobarQuery = new Parse.Query('Foobar'); - foobarQuery.select(['fizz', 'barBaz.key']); + const foobarQuery = new Parse.Query("Foobar"); + foobarQuery.select(["fizz", "barBaz.key"]); foobarQuery.get(savedFoobar.id).then(function (foobarObj) { - equal(foobarObj.get('fizz'), 'buzz'); - equal(foobarObj.get('foo'), undefined); - if (foobarObj.has('barBaz')) { - equal(foobarObj.get('barBaz').get('key'), 'value'); - equal(foobarObj.get('barBaz').get('otherKey'), undefined); + equal(foobarObj.get("fizz"), "buzz"); + equal(foobarObj.get("foo"), undefined); + if (foobarObj.has("barBaz")) { + equal(foobarObj.get("barBaz").get("key"), "value"); + equal(foobarObj.get("barBaz").get("otherKey"), undefined); } else { - fail('barBaz should be set'); + fail("barBaz should be set"); } done(); }); }); }); - it('select nested keys 2 level (issue #1567)', function (done) { - const Foobar = new Parse.Object('Foobar'); - const BarBaz = new Parse.Object('Barbaz'); - const Bazoo = new Parse.Object('Bazoo'); + it("select nested keys 2 level (issue #1567)", function (done) { + const Foobar = new Parse.Object("Foobar"); + const BarBaz = new Parse.Object("Barbaz"); + const Bazoo = new Parse.Object("Bazoo"); - Bazoo.set('some', 'thing'); - Bazoo.set('otherSome', 'value'); + Bazoo.set("some", "thing"); + Bazoo.set("otherSome", "value"); Bazoo.save() .then(() => { - BarBaz.set('key', 'value'); - BarBaz.set('otherKey', 'value'); - BarBaz.set('bazoo', Bazoo); + BarBaz.set("key", "value"); + BarBaz.set("otherKey", "value"); + BarBaz.set("bazoo", Bazoo); return BarBaz.save(); }) .then(() => { - Foobar.set('foo', 'bar'); - Foobar.set('fizz', 'buzz'); - Foobar.set('barBaz', BarBaz); + Foobar.set("foo", "bar"); + Foobar.set("fizz", "buzz"); + Foobar.set("barBaz", BarBaz); return Foobar.save(); }) .then(function (savedFoobar) { - const foobarQuery = new Parse.Query('Foobar'); - foobarQuery.select(['fizz', 'barBaz.key', 'barBaz.bazoo.some']); + const foobarQuery = new Parse.Query("Foobar"); + foobarQuery.select(["fizz", "barBaz.key", "barBaz.bazoo.some"]); foobarQuery.get(savedFoobar.id).then(function (foobarObj) { - equal(foobarObj.get('fizz'), 'buzz'); - equal(foobarObj.get('foo'), undefined); - if (foobarObj.has('barBaz')) { - equal(foobarObj.get('barBaz').get('key'), 'value'); - equal(foobarObj.get('barBaz').get('otherKey'), undefined); - equal(foobarObj.get('barBaz').get('bazoo').get('some'), 'thing'); - equal(foobarObj.get('barBaz').get('bazoo').get('otherSome'), undefined); + equal(foobarObj.get("fizz"), "buzz"); + equal(foobarObj.get("foo"), undefined); + if (foobarObj.has("barBaz")) { + equal(foobarObj.get("barBaz").get("key"), "value"); + equal(foobarObj.get("barBaz").get("otherKey"), undefined); + equal(foobarObj.get("barBaz").get("bazoo").get("some"), "thing"); + equal( + foobarObj.get("barBaz").get("bazoo").get("otherSome"), + undefined + ); } else { - fail('barBaz should be set'); + fail("barBaz should be set"); } done(); }); }); }); - it('exclude nested keys', async () => { - const Foobar = new Parse.Object('Foobar'); - const BarBaz = new Parse.Object('Barbaz'); - BarBaz.set('key', 'value'); - BarBaz.set('otherKey', 'value'); + it("exclude nested keys", async () => { + const Foobar = new Parse.Object("Foobar"); + const BarBaz = new Parse.Object("Barbaz"); + BarBaz.set("key", "value"); + BarBaz.set("otherKey", "value"); await BarBaz.save(); - Foobar.set('foo', 'bar'); - Foobar.set('fizz', 'buzz'); - Foobar.set('barBaz', BarBaz); + Foobar.set("foo", "bar"); + Foobar.set("fizz", "buzz"); + Foobar.set("barBaz", BarBaz); const savedFoobar = await Foobar.save(); - const foobarQuery = new Parse.Query('Foobar'); - foobarQuery.exclude(['foo', 'barBaz.otherKey']); + const foobarQuery = new Parse.Query("Foobar"); + foobarQuery.exclude(["foo", "barBaz.otherKey"]); const foobarObj = await foobarQuery.get(savedFoobar.id); - equal(foobarObj.get('fizz'), 'buzz'); - equal(foobarObj.get('foo'), undefined); - if (foobarObj.has('barBaz')) { - equal(foobarObj.get('barBaz').get('key'), 'value'); - equal(foobarObj.get('barBaz').get('otherKey'), undefined); + equal(foobarObj.get("fizz"), "buzz"); + equal(foobarObj.get("foo"), undefined); + if (foobarObj.has("barBaz")) { + equal(foobarObj.get("barBaz").get("key"), "value"); + equal(foobarObj.get("barBaz").get("otherKey"), undefined); } else { - fail('barBaz should be set'); + fail("barBaz should be set"); } }); - it('exclude nested keys 2 level', async () => { - const Foobar = new Parse.Object('Foobar'); - const BarBaz = new Parse.Object('Barbaz'); - const Bazoo = new Parse.Object('Bazoo'); + it("exclude nested keys 2 level", async () => { + const Foobar = new Parse.Object("Foobar"); + const BarBaz = new Parse.Object("Barbaz"); + const Bazoo = new Parse.Object("Bazoo"); - Bazoo.set('some', 'thing'); - Bazoo.set('otherSome', 'value'); + Bazoo.set("some", "thing"); + Bazoo.set("otherSome", "value"); await Bazoo.save(); - BarBaz.set('key', 'value'); - BarBaz.set('otherKey', 'value'); - BarBaz.set('bazoo', Bazoo); + BarBaz.set("key", "value"); + BarBaz.set("otherKey", "value"); + BarBaz.set("bazoo", Bazoo); await BarBaz.save(); - Foobar.set('foo', 'bar'); - Foobar.set('fizz', 'buzz'); - Foobar.set('barBaz', BarBaz); + Foobar.set("foo", "bar"); + Foobar.set("fizz", "buzz"); + Foobar.set("barBaz", BarBaz); const savedFoobar = await Foobar.save(); - const foobarQuery = new Parse.Query('Foobar'); - foobarQuery.exclude(['foo', 'barBaz.otherKey', 'barBaz.bazoo.otherSome']); + const foobarQuery = new Parse.Query("Foobar"); + foobarQuery.exclude(["foo", "barBaz.otherKey", "barBaz.bazoo.otherSome"]); const foobarObj = await foobarQuery.get(savedFoobar.id); - equal(foobarObj.get('fizz'), 'buzz'); - equal(foobarObj.get('foo'), undefined); - if (foobarObj.has('barBaz')) { - equal(foobarObj.get('barBaz').get('key'), 'value'); - equal(foobarObj.get('barBaz').get('otherKey'), undefined); - equal(foobarObj.get('barBaz').get('bazoo').get('some'), 'thing'); - equal(foobarObj.get('barBaz').get('bazoo').get('otherSome'), undefined); + equal(foobarObj.get("fizz"), "buzz"); + equal(foobarObj.get("foo"), undefined); + if (foobarObj.has("barBaz")) { + equal(foobarObj.get("barBaz").get("key"), "value"); + equal(foobarObj.get("barBaz").get("otherKey"), undefined); + equal(foobarObj.get("barBaz").get("bazoo").get("some"), "thing"); + equal(foobarObj.get("barBaz").get("bazoo").get("otherSome"), undefined); } else { - fail('barBaz should be set'); + fail("barBaz should be set"); } }); - it('include with *', async () => { - const child1 = new TestObject({ foo: 'bar', name: 'ac' }); - const child2 = new TestObject({ foo: 'baz', name: 'flo' }); - const child3 = new TestObject({ foo: 'bad', name: 'mo' }); + it("include with *", async () => { + const child1 = new TestObject({ foo: "bar", name: "ac" }); + const child2 = new TestObject({ foo: "baz", name: "flo" }); + const child3 = new TestObject({ foo: "bad", name: "mo" }); const parent = new Container({ child1, child2, child3 }); await Parse.Object.saveAll([parent, child1, child2, child3]); const options = Object.assign({}, masterKeyOptions, { qs: { where: JSON.stringify({ objectId: parent.id }), - include: '*', + include: "*", }, }); const resp = await request( - Object.assign({ url: Parse.serverURL + '/classes/Container' }, options) + Object.assign({ url: Parse.serverURL + "/classes/Container" }, options) ); const result = resp.data.results[0]; - equal(result.child1.foo, 'bar'); - equal(result.child2.foo, 'baz'); - equal(result.child3.foo, 'bad'); - equal(result.child1.name, 'ac'); - equal(result.child2.name, 'flo'); - equal(result.child3.name, 'mo'); + equal(result.child1.foo, "bar"); + equal(result.child2.foo, "baz"); + equal(result.child3.foo, "bad"); + equal(result.child1.name, "ac"); + equal(result.child2.name, "flo"); + equal(result.child3.name, "mo"); }); it('include with ["*"]', async () => { - const child1 = new TestObject({ foo: 'bar', name: 'ac' }); - const child2 = new TestObject({ foo: 'baz', name: 'flo' }); - const child3 = new TestObject({ foo: 'bad', name: 'mo' }); + const child1 = new TestObject({ foo: "bar", name: "ac" }); + const child2 = new TestObject({ foo: "baz", name: "flo" }); + const child3 = new TestObject({ foo: "bad", name: "mo" }); const parent = new Container({ child1, child2, child3 }); await Parse.Object.saveAll([parent, child1, child2, child3]); const options = Object.assign({}, masterKeyOptions, { @@ -4455,45 +4776,45 @@ describe('Parse.Query testing', () => { }, }); const resp = await request( - Object.assign({ url: Parse.serverURL + '/classes/Container' }, options) + Object.assign({ url: Parse.serverURL + "/classes/Container" }, options) ); const result = resp.data.results[0]; - equal(result.child1.foo, 'bar'); - equal(result.child2.foo, 'baz'); - equal(result.child3.foo, 'bad'); - equal(result.child1.name, 'ac'); - equal(result.child2.name, 'flo'); - equal(result.child3.name, 'mo'); - }); - - it('include with * overrides', async () => { - const child1 = new TestObject({ foo: 'bar', name: 'ac' }); - const child2 = new TestObject({ foo: 'baz', name: 'flo' }); - const child3 = new TestObject({ foo: 'bad', name: 'mo' }); + equal(result.child1.foo, "bar"); + equal(result.child2.foo, "baz"); + equal(result.child3.foo, "bad"); + equal(result.child1.name, "ac"); + equal(result.child2.name, "flo"); + equal(result.child3.name, "mo"); + }); + + it("include with * overrides", async () => { + const child1 = new TestObject({ foo: "bar", name: "ac" }); + const child2 = new TestObject({ foo: "baz", name: "flo" }); + const child3 = new TestObject({ foo: "bad", name: "mo" }); const parent = new Container({ child1, child2, child3 }); await Parse.Object.saveAll([parent, child1, child2, child3]); const options = Object.assign({}, masterKeyOptions, { qs: { where: JSON.stringify({ objectId: parent.id }), - include: 'child2,*', + include: "child2,*", }, }); const resp = await request( - Object.assign({ url: Parse.serverURL + '/classes/Container' }, options) + Object.assign({ url: Parse.serverURL + "/classes/Container" }, options) ); const result = resp.data.results[0]; - equal(result.child1.foo, 'bar'); - equal(result.child2.foo, 'baz'); - equal(result.child3.foo, 'bad'); - equal(result.child1.name, 'ac'); - equal(result.child2.name, 'flo'); - equal(result.child3.name, 'mo'); + equal(result.child1.foo, "bar"); + equal(result.child2.foo, "baz"); + equal(result.child3.foo, "bad"); + equal(result.child1.name, "ac"); + equal(result.child2.name, "flo"); + equal(result.child3.name, "mo"); }); it('include with ["*"] overrides', async () => { - const child1 = new TestObject({ foo: 'bar', name: 'ac' }); - const child2 = new TestObject({ foo: 'baz', name: 'flo' }); - const child3 = new TestObject({ foo: 'bad', name: 'mo' }); + const child1 = new TestObject({ foo: "bar", name: "ac" }); + const child2 = new TestObject({ foo: "baz", name: "flo" }); + const child3 = new TestObject({ foo: "bad", name: "mo" }); const parent = new Container({ child1, child2, child3 }); await Parse.Object.saveAll([parent, child1, child2, child3]); const options = Object.assign({}, masterKeyOptions, { @@ -4503,21 +4824,21 @@ describe('Parse.Query testing', () => { }, }); const resp = await request( - Object.assign({ url: Parse.serverURL + '/classes/Container' }, options) + Object.assign({ url: Parse.serverURL + "/classes/Container" }, options) ); const result = resp.data.results[0]; - equal(result.child1.foo, 'bar'); - equal(result.child2.foo, 'baz'); - equal(result.child3.foo, 'bad'); - equal(result.child1.name, 'ac'); - equal(result.child2.name, 'flo'); - equal(result.child3.name, 'mo'); - }); - - it('includeAll', done => { - const child1 = new TestObject({ foo: 'bar', name: 'ac' }); - const child2 = new TestObject({ foo: 'baz', name: 'flo' }); - const child3 = new TestObject({ foo: 'bad', name: 'mo' }); + equal(result.child1.foo, "bar"); + equal(result.child2.foo, "baz"); + equal(result.child3.foo, "bad"); + equal(result.child1.name, "ac"); + equal(result.child2.name, "flo"); + equal(result.child3.name, "mo"); + }); + + it("includeAll", done => { + const child1 = new TestObject({ foo: "bar", name: "ac" }); + const child2 = new TestObject({ foo: "baz", name: "flo" }); + const child3 = new TestObject({ foo: "bad", name: "mo" }); const parent = new Container({ child1, child2, child3 }); Parse.Object.saveAll([parent, child1, child2, child3]) .then(() => { @@ -4527,141 +4848,146 @@ describe('Parse.Query testing', () => { includeAll: true, }, }); - return request(Object.assign({ url: Parse.serverURL + '/classes/Container' }, options)); + return request( + Object.assign( + { url: Parse.serverURL + "/classes/Container" }, + options + ) + ); }) .then(resp => { const result = resp.data.results[0]; - equal(result.child1.foo, 'bar'); - equal(result.child2.foo, 'baz'); - equal(result.child3.foo, 'bad'); - equal(result.child1.name, 'ac'); - equal(result.child2.name, 'flo'); - equal(result.child3.name, 'mo'); + equal(result.child1.foo, "bar"); + equal(result.child2.foo, "baz"); + equal(result.child3.foo, "bad"); + equal(result.child1.name, "ac"); + equal(result.child2.name, "flo"); + equal(result.child3.name, "mo"); done(); }); }); - it('include pointer and pointer array', function (done) { + it("include pointer and pointer array", function (done) { const child = new TestObject(); const child2 = new TestObject(); - child.set('foo', 'bar'); - child2.set('hello', 'world'); + child.set("foo", "bar"); + child2.set("hello", "world"); Parse.Object.saveAll([child, child2]).then(function () { const parent = new Container(); - parent.set('child', child.toPointer()); - parent.set('child2', [child2.toPointer()]); + parent.set("child", child.toPointer()); + parent.set("child2", [child2.toPointer()]); parent.save().then(function () { const query = new Parse.Query(Container); - query.include(['child', 'child2']); + query.include(["child", "child2"]); query.find().then(function (results) { equal(results.length, 1); const parentAgain = results[0]; - const childAgain = parentAgain.get('child'); + const childAgain = parentAgain.get("child"); ok(childAgain); - equal(childAgain.get('foo'), 'bar'); - const child2Again = parentAgain.get('child2'); + equal(childAgain.get("foo"), "bar"); + const child2Again = parentAgain.get("child2"); equal(child2Again.length, 1); ok(child2Again); - equal(child2Again[0].get('hello'), 'world'); + equal(child2Again[0].get("hello"), "world"); done(); }); }); }); }); - it('include pointer and pointer array (keys switched)', function (done) { + it("include pointer and pointer array (keys switched)", function (done) { const child = new TestObject(); const child2 = new TestObject(); - child.set('foo', 'bar'); - child2.set('hello', 'world'); + child.set("foo", "bar"); + child2.set("hello", "world"); Parse.Object.saveAll([child, child2]).then(function () { const parent = new Container(); - parent.set('child', child.toPointer()); - parent.set('child2', [child2.toPointer()]); + parent.set("child", child.toPointer()); + parent.set("child2", [child2.toPointer()]); parent.save().then(function () { const query = new Parse.Query(Container); - query.include(['child2', 'child']); + query.include(["child2", "child"]); query.find().then(function (results) { equal(results.length, 1); const parentAgain = results[0]; - const childAgain = parentAgain.get('child'); + const childAgain = parentAgain.get("child"); ok(childAgain); - equal(childAgain.get('foo'), 'bar'); - const child2Again = parentAgain.get('child2'); + equal(childAgain.get("foo"), "bar"); + const child2Again = parentAgain.get("child2"); equal(child2Again.length, 1); ok(child2Again); - equal(child2Again[0].get('hello'), 'world'); + equal(child2Again[0].get("hello"), "world"); done(); }); }); }); }); - it('includeAll pointer and pointer array', function (done) { + it("includeAll pointer and pointer array", function (done) { const child = new TestObject(); const child2 = new TestObject(); - child.set('foo', 'bar'); - child2.set('hello', 'world'); + child.set("foo", "bar"); + child2.set("hello", "world"); Parse.Object.saveAll([child, child2]).then(function () { const parent = new Container(); - parent.set('child', child.toPointer()); - parent.set('child2', [child2.toPointer()]); + parent.set("child", child.toPointer()); + parent.set("child2", [child2.toPointer()]); parent.save().then(function () { const query = new Parse.Query(Container); query.includeAll(); query.find().then(function (results) { equal(results.length, 1); const parentAgain = results[0]; - const childAgain = parentAgain.get('child'); + const childAgain = parentAgain.get("child"); ok(childAgain); - equal(childAgain.get('foo'), 'bar'); - const child2Again = parentAgain.get('child2'); + equal(childAgain.get("foo"), "bar"); + const child2Again = parentAgain.get("child2"); equal(child2Again.length, 1); ok(child2Again); - equal(child2Again[0].get('hello'), 'world'); + equal(child2Again[0].get("hello"), "world"); done(); }); }); }); }); - it('select nested keys 2 level includeAll', done => { - const Foobar = new Parse.Object('Foobar'); - const BarBaz = new Parse.Object('Barbaz'); - const Bazoo = new Parse.Object('Bazoo'); - const Tang = new Parse.Object('Tang'); + it("select nested keys 2 level includeAll", done => { + const Foobar = new Parse.Object("Foobar"); + const BarBaz = new Parse.Object("Barbaz"); + const Bazoo = new Parse.Object("Bazoo"); + const Tang = new Parse.Object("Tang"); - Bazoo.set('some', 'thing'); - Bazoo.set('otherSome', 'value'); + Bazoo.set("some", "thing"); + Bazoo.set("otherSome", "value"); Bazoo.save() .then(() => { - BarBaz.set('key', 'value'); - BarBaz.set('otherKey', 'value'); - BarBaz.set('bazoo', Bazoo); + BarBaz.set("key", "value"); + BarBaz.set("otherKey", "value"); + BarBaz.set("bazoo", Bazoo); return BarBaz.save(); }) .then(() => { - Tang.set('clan', 'wu'); + Tang.set("clan", "wu"); return Tang.save(); }) .then(() => { - Foobar.set('foo', 'bar'); - Foobar.set('fizz', 'buzz'); - Foobar.set('barBaz', BarBaz); - Foobar.set('group', Tang); + Foobar.set("foo", "bar"); + Foobar.set("fizz", "buzz"); + Foobar.set("barBaz", BarBaz); + Foobar.set("group", Tang); return Foobar.save(); }) .then(savedFoobar => { const options = Object.assign( { - url: Parse.serverURL + '/classes/Foobar', + url: Parse.serverURL + "/classes/Foobar", }, masterKeyOptions, { qs: { where: JSON.stringify({ objectId: savedFoobar.id }), includeAll: true, - keys: 'fizz,barBaz.key,barBaz.bazoo.some', + keys: "fizz,barBaz.key,barBaz.bazoo.some", }, } ); @@ -4669,77 +4995,80 @@ describe('Parse.Query testing', () => { }) .then(resp => { const result = resp.data.results[0]; - equal(result.group.clan, 'wu'); + equal(result.group.clan, "wu"); equal(result.foo, undefined); - equal(result.fizz, 'buzz'); - equal(result.barBaz.key, 'value'); + equal(result.fizz, "buzz"); + equal(result.barBaz.key, "value"); equal(result.barBaz.otherKey, undefined); - equal(result.barBaz.bazoo.some, 'thing'); + equal(result.barBaz.bazoo.some, "thing"); equal(result.barBaz.bazoo.otherSome, undefined); done(); }) .catch(done.fail); }); - it('select nested keys 2 level without include (issue #3185)', function (done) { - const Foobar = new Parse.Object('Foobar'); - const BarBaz = new Parse.Object('Barbaz'); - const Bazoo = new Parse.Object('Bazoo'); + it("select nested keys 2 level without include (issue #3185)", function (done) { + const Foobar = new Parse.Object("Foobar"); + const BarBaz = new Parse.Object("Barbaz"); + const Bazoo = new Parse.Object("Bazoo"); - Bazoo.set('some', 'thing'); - Bazoo.set('otherSome', 'value'); + Bazoo.set("some", "thing"); + Bazoo.set("otherSome", "value"); Bazoo.save() .then(() => { - BarBaz.set('key', 'value'); - BarBaz.set('otherKey', 'value'); - BarBaz.set('bazoo', Bazoo); + BarBaz.set("key", "value"); + BarBaz.set("otherKey", "value"); + BarBaz.set("bazoo", Bazoo); return BarBaz.save(); }) .then(() => { - Foobar.set('foo', 'bar'); - Foobar.set('fizz', 'buzz'); - Foobar.set('barBaz', BarBaz); + Foobar.set("foo", "bar"); + Foobar.set("fizz", "buzz"); + Foobar.set("barBaz", BarBaz); return Foobar.save(); }) .then(function (savedFoobar) { - const foobarQuery = new Parse.Query('Foobar'); - foobarQuery.select(['fizz', 'barBaz.key', 'barBaz.bazoo.some']); + const foobarQuery = new Parse.Query("Foobar"); + foobarQuery.select(["fizz", "barBaz.key", "barBaz.bazoo.some"]); return foobarQuery.get(savedFoobar.id); }) .then(foobarObj => { - equal(foobarObj.get('fizz'), 'buzz'); - equal(foobarObj.get('foo'), undefined); - if (foobarObj.has('barBaz')) { - equal(foobarObj.get('barBaz').get('key'), 'value'); - equal(foobarObj.get('barBaz').get('otherKey'), undefined); - if (foobarObj.get('barBaz').has('bazoo')) { - equal(foobarObj.get('barBaz').get('bazoo').get('some'), 'thing'); - equal(foobarObj.get('barBaz').get('bazoo').get('otherSome'), undefined); + equal(foobarObj.get("fizz"), "buzz"); + equal(foobarObj.get("foo"), undefined); + if (foobarObj.has("barBaz")) { + equal(foobarObj.get("barBaz").get("key"), "value"); + equal(foobarObj.get("barBaz").get("otherKey"), undefined); + if (foobarObj.get("barBaz").has("bazoo")) { + equal(foobarObj.get("barBaz").get("bazoo").get("some"), "thing"); + equal( + foobarObj.get("barBaz").get("bazoo").get("otherSome"), + undefined + ); } else { - fail('bazoo should be set'); + fail("bazoo should be set"); } } else { - fail('barBaz should be set'); + fail("barBaz should be set"); } done(); }); }); - it('properly handles nested ors', function (done) { + it("properly handles nested ors", function (done) { const objects = []; while (objects.length != 4) { - const obj = new Parse.Object('Object'); - obj.set('x', objects.length); + const obj = new Parse.Object("Object"); + obj.set("x", objects.length); objects.push(obj); } Parse.Object.saveAll(objects) .then(() => { - const q0 = new Parse.Query('Object'); - q0.equalTo('x', 0); - const q1 = new Parse.Query('Object'); - q1.equalTo('x', 1); - const q2 = new Parse.Query('Object'); - q2.equalTo('x', 2); + const q0 = new Parse.Query("Object"); + q0.equalTo("x", 0); + const q1 = new Parse.Query("Object"); + q1.equalTo("x", 1); + const q2 = new Parse.Query("Object"); + q2.equalTo("x", 2); const or01 = Parse.Query.or(q0, q1); return Parse.Query.or(or01, q2).find(); }) @@ -4748,39 +5077,39 @@ describe('Parse.Query testing', () => { done(); }) .catch(error => { - fail('should not fail'); + fail("should not fail"); jfail(error); done(); }); }); - it('should not depend on parameter order #3169', function (done) { - const score1 = new Parse.Object('Score', { scoreId: '1' }); - const score2 = new Parse.Object('Score', { scoreId: '2' }); - const game1 = new Parse.Object('Game', { gameId: '1' }); - const game2 = new Parse.Object('Game', { gameId: '2' }); + it("should not depend on parameter order #3169", function (done) { + const score1 = new Parse.Object("Score", { scoreId: "1" }); + const score2 = new Parse.Object("Score", { scoreId: "2" }); + const game1 = new Parse.Object("Game", { gameId: "1" }); + const game2 = new Parse.Object("Game", { gameId: "2" }); Parse.Object.saveAll([score1, score2, game1, game2]) .then(() => { - game1.set('score', [score1]); - game2.set('score', [score2]); + game1.set("score", [score1]); + game2.set("score", [score2]); return Parse.Object.saveAll([game1, game2]); }) .then(() => { const where = { score: { objectId: score1.id, - className: 'Score', - __type: 'Pointer', + className: "Score", + __type: "Pointer", }, }; return request({ - method: 'POST', - url: Parse.serverURL + '/classes/Game', - body: { where, _method: 'GET' }, + method: "POST", + url: Parse.serverURL + "/classes/Game", + body: { where, _method: "GET" }, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Javascript-Key": Parse.javaScriptKey, + "Content-Type": "application/json", }, }); }) @@ -4794,258 +5123,278 @@ describe('Parse.Query testing', () => { ); }); - it('should not interfere with has when using select on field with undefined value #3999', done => { - const obj1 = new Parse.Object('TestObject'); - const obj2 = new Parse.Object('OtherObject'); - obj2.set('otherField', 1); - obj1.set('testPointerField', obj2); - obj1.set('shouldBe', true); - const obj3 = new Parse.Object('TestObject'); - obj3.set('shouldBe', false); + it("should not interfere with has when using select on field with undefined value #3999", done => { + const obj1 = new Parse.Object("TestObject"); + const obj2 = new Parse.Object("OtherObject"); + obj2.set("otherField", 1); + obj1.set("testPointerField", obj2); + obj1.set("shouldBe", true); + const obj3 = new Parse.Object("TestObject"); + obj3.set("shouldBe", false); Parse.Object.saveAll([obj1, obj3]) .then(() => { - const query = new Parse.Query('TestObject'); - query.include('testPointerField'); - query.select(['testPointerField', 'testPointerField.otherField', 'shouldBe']); + const query = new Parse.Query("TestObject"); + query.include("testPointerField"); + query.select([ + "testPointerField", + "testPointerField.otherField", + "shouldBe", + ]); return query.find(); }) .then(results => { results.forEach(result => { - equal(result.has('testPointerField'), result.get('shouldBe')); + equal(result.has("testPointerField"), result.get("shouldBe")); }); done(); }) .catch(done.fail); }); - it('should handle relative times correctly', async () => { + it("should handle relative times correctly", async () => { const now = Date.now(); - const obj1 = new Parse.Object('MyCustomObject', { - name: 'obj1', + const obj1 = new Parse.Object("MyCustomObject", { + name: "obj1", ttl: new Date(now + 2 * 24 * 60 * 60 * 1000), // 2 days from now }); - const obj2 = new Parse.Object('MyCustomObject', { - name: 'obj2', + const obj2 = new Parse.Object("MyCustomObject", { + name: "obj2", ttl: new Date(now - 2 * 24 * 60 * 60 * 1000), // 2 days ago }); await Parse.Object.saveAll([obj1, obj2]); - const q1 = new Parse.Query('MyCustomObject'); - q1.greaterThan('ttl', { $relativeTime: 'in 1 day' }); + const q1 = new Parse.Query("MyCustomObject"); + q1.greaterThan("ttl", { $relativeTime: "in 1 day" }); const results1 = await q1.find({ useMasterKey: true }); expect(results1.length).toBe(1); - const q2 = new Parse.Query('MyCustomObject'); - q2.greaterThan('ttl', { $relativeTime: '1 day ago' }); + const q2 = new Parse.Query("MyCustomObject"); + q2.greaterThan("ttl", { $relativeTime: "1 day ago" }); const results2 = await q2.find({ useMasterKey: true }); expect(results2.length).toBe(1); - const q3 = new Parse.Query('MyCustomObject'); - q3.lessThan('ttl', { $relativeTime: '5 days ago' }); + const q3 = new Parse.Query("MyCustomObject"); + q3.lessThan("ttl", { $relativeTime: "5 days ago" }); const results3 = await q3.find({ useMasterKey: true }); expect(results3.length).toBe(0); - const q4 = new Parse.Query('MyCustomObject'); - q4.greaterThan('ttl', { $relativeTime: '3 days ago' }); + const q4 = new Parse.Query("MyCustomObject"); + q4.greaterThan("ttl", { $relativeTime: "3 days ago" }); const results4 = await q4.find({ useMasterKey: true }); expect(results4.length).toBe(2); - const q5 = new Parse.Query('MyCustomObject'); - q5.greaterThan('ttl', { $relativeTime: 'now' }); + const q5 = new Parse.Query("MyCustomObject"); + q5.greaterThan("ttl", { $relativeTime: "now" }); const results5 = await q5.find({ useMasterKey: true }); expect(results5.length).toBe(1); - const q6 = new Parse.Query('MyCustomObject'); - q6.greaterThan('ttl', { $relativeTime: 'now' }); - q6.lessThan('ttl', { $relativeTime: 'in 1 day' }); + const q6 = new Parse.Query("MyCustomObject"); + q6.greaterThan("ttl", { $relativeTime: "now" }); + q6.lessThan("ttl", { $relativeTime: "in 1 day" }); const results6 = await q6.find({ useMasterKey: true }); expect(results6.length).toBe(0); - const q7 = new Parse.Query('MyCustomObject'); - q7.greaterThan('ttl', { $relativeTime: '1 year 3 weeks ago' }); + const q7 = new Parse.Query("MyCustomObject"); + q7.greaterThan("ttl", { $relativeTime: "1 year 3 weeks ago" }); const results7 = await q7.find({ useMasterKey: true }); expect(results7.length).toBe(2); }); - it('should error on invalid relative time', async () => { - const obj1 = new Parse.Object('MyCustomObject', { - name: 'obj1', + it("should error on invalid relative time", async () => { + const obj1 = new Parse.Object("MyCustomObject", { + name: "obj1", ttl: new Date(Date.now() + 2 * 24 * 60 * 60 * 1000), // 2 days from now }); await obj1.save({ useMasterKey: true }); - const q = new Parse.Query('MyCustomObject'); - q.greaterThan('ttl', { $relativeTime: '-12 bananas ago' }); + const q = new Parse.Query("MyCustomObject"); + q.greaterThan("ttl", { $relativeTime: "-12 bananas ago" }); try { await q.find({ useMasterKey: true }); - fail('Should have thrown error'); + fail("Should have thrown error"); } catch (error) { expect(error.code).toBe(Parse.Error.INVALID_JSON); } }); - it('should error when using $relativeTime on non-Date field', async () => { - const obj1 = new Parse.Object('MyCustomObject', { - name: 'obj1', - nonDateField: 'abcd', + it("should error when using $relativeTime on non-Date field", async () => { + const obj1 = new Parse.Object("MyCustomObject", { + name: "obj1", + nonDateField: "abcd", ttl: new Date(Date.now() + 2 * 24 * 60 * 60 * 1000), // 2 days from now }); await obj1.save({ useMasterKey: true }); - const q = new Parse.Query('MyCustomObject'); - q.greaterThan('nonDateField', { $relativeTime: '1 day ago' }); + const q = new Parse.Query("MyCustomObject"); + q.greaterThan("nonDateField", { $relativeTime: "1 day ago" }); try { await q.find({ useMasterKey: true }); - fail('Should have thrown error'); + fail("Should have thrown error"); } catch (error) { expect(error.code).toBe(Parse.Error.INVALID_JSON); } }); - it('should match complex structure with dot notation when using matchesKeyInQuery', function (done) { - const group1 = new Parse.Object('Group', { - name: 'Group #1', + it("should match complex structure with dot notation when using matchesKeyInQuery", function (done) { + const group1 = new Parse.Object("Group", { + name: "Group #1", }); - const group2 = new Parse.Object('Group', { - name: 'Group #2', + const group2 = new Parse.Object("Group", { + name: "Group #2", }); Parse.Object.saveAll([group1, group2]) .then(() => { - const role1 = new Parse.Object('Role', { - name: 'Role #1', - type: 'x', + const role1 = new Parse.Object("Role", { + name: "Role #1", + type: "x", belongsTo: group1, }); - const role2 = new Parse.Object('Role', { - name: 'Role #2', - type: 'y', + const role2 = new Parse.Object("Role", { + name: "Role #2", + type: "y", belongsTo: group1, }); return Parse.Object.saveAll([role1, role2]); }) .then(() => { - const rolesOfTypeX = new Parse.Query('Role'); - rolesOfTypeX.equalTo('type', 'x'); - - const groupsWithRoleX = new Parse.Query('Group'); - groupsWithRoleX.matchesKeyInQuery('objectId', 'belongsTo.objectId', rolesOfTypeX); + const rolesOfTypeX = new Parse.Query("Role"); + rolesOfTypeX.equalTo("type", "x"); + + const groupsWithRoleX = new Parse.Query("Group"); + groupsWithRoleX.matchesKeyInQuery( + "objectId", + "belongsTo.objectId", + rolesOfTypeX + ); groupsWithRoleX.find().then(function (results) { equal(results.length, 1); - equal(results[0].get('name'), group1.get('name')); + equal(results[0].get("name"), group1.get("name")); done(); }); }); }); - it('should match complex structure with dot notation when using doesNotMatchKeyInQuery', function (done) { - const group1 = new Parse.Object('Group', { - name: 'Group #1', + it("should match complex structure with dot notation when using doesNotMatchKeyInQuery", function (done) { + const group1 = new Parse.Object("Group", { + name: "Group #1", }); - const group2 = new Parse.Object('Group', { - name: 'Group #2', + const group2 = new Parse.Object("Group", { + name: "Group #2", }); Parse.Object.saveAll([group1, group2]) .then(() => { - const role1 = new Parse.Object('Role', { - name: 'Role #1', - type: 'x', + const role1 = new Parse.Object("Role", { + name: "Role #1", + type: "x", belongsTo: group1, }); - const role2 = new Parse.Object('Role', { - name: 'Role #2', - type: 'y', + const role2 = new Parse.Object("Role", { + name: "Role #2", + type: "y", belongsTo: group1, }); return Parse.Object.saveAll([role1, role2]); }) .then(() => { - const rolesOfTypeX = new Parse.Query('Role'); - rolesOfTypeX.equalTo('type', 'x'); - - const groupsWithRoleX = new Parse.Query('Group'); - groupsWithRoleX.doesNotMatchKeyInQuery('objectId', 'belongsTo.objectId', rolesOfTypeX); + const rolesOfTypeX = new Parse.Query("Role"); + rolesOfTypeX.equalTo("type", "x"); + + const groupsWithRoleX = new Parse.Query("Group"); + groupsWithRoleX.doesNotMatchKeyInQuery( + "objectId", + "belongsTo.objectId", + rolesOfTypeX + ); groupsWithRoleX.find().then(function (results) { equal(results.length, 1); - equal(results[0].get('name'), group2.get('name')); + equal(results[0].get("name"), group2.get("name")); done(); }); }); }); - it('should not throw error with undefined dot notation when using matchesKeyInQuery', async () => { - const group = new Parse.Object('Group', { name: 'Group #1' }); + it("should not throw error with undefined dot notation when using matchesKeyInQuery", async () => { + const group = new Parse.Object("Group", { name: "Group #1" }); await group.save(); - const role1 = new Parse.Object('Role', { - name: 'Role #1', - type: 'x', + const role1 = new Parse.Object("Role", { + name: "Role #1", + type: "x", belongsTo: group, }); - const role2 = new Parse.Object('Role', { - name: 'Role #2', - type: 'y', + const role2 = new Parse.Object("Role", { + name: "Role #2", + type: "y", belongsTo: undefined, }); await Parse.Object.saveAll([role1, role2]); - const rolesOfTypeX = new Parse.Query('Role'); - rolesOfTypeX.equalTo('type', 'x'); + const rolesOfTypeX = new Parse.Query("Role"); + rolesOfTypeX.equalTo("type", "x"); - const groupsWithRoleX = new Parse.Query('Group'); - groupsWithRoleX.matchesKeyInQuery('objectId', 'belongsTo.objectId', rolesOfTypeX); + const groupsWithRoleX = new Parse.Query("Group"); + groupsWithRoleX.matchesKeyInQuery( + "objectId", + "belongsTo.objectId", + rolesOfTypeX + ); const results = await groupsWithRoleX.find(); equal(results.length, 1); - equal(results[0].get('name'), group.get('name')); + equal(results[0].get("name"), group.get("name")); }); - it('should not throw error with undefined dot notation when using doesNotMatchKeyInQuery', async () => { - const group1 = new Parse.Object('Group', { name: 'Group #1' }); - const group2 = new Parse.Object('Group', { name: 'Group #2' }); + it("should not throw error with undefined dot notation when using doesNotMatchKeyInQuery", async () => { + const group1 = new Parse.Object("Group", { name: "Group #1" }); + const group2 = new Parse.Object("Group", { name: "Group #2" }); await Parse.Object.saveAll([group1, group2]); - const role1 = new Parse.Object('Role', { - name: 'Role #1', - type: 'x', + const role1 = new Parse.Object("Role", { + name: "Role #1", + type: "x", belongsTo: group1, }); - const role2 = new Parse.Object('Role', { - name: 'Role #2', - type: 'y', + const role2 = new Parse.Object("Role", { + name: "Role #2", + type: "y", belongsTo: undefined, }); await Parse.Object.saveAll([role1, role2]); - const rolesOfTypeX = new Parse.Query('Role'); - rolesOfTypeX.equalTo('type', 'x'); + const rolesOfTypeX = new Parse.Query("Role"); + rolesOfTypeX.equalTo("type", "x"); - const groupsWithRoleX = new Parse.Query('Group'); - groupsWithRoleX.doesNotMatchKeyInQuery('objectId', 'belongsTo.objectId', rolesOfTypeX); + const groupsWithRoleX = new Parse.Query("Group"); + groupsWithRoleX.doesNotMatchKeyInQuery( + "objectId", + "belongsTo.objectId", + rolesOfTypeX + ); const results = await groupsWithRoleX.find(); equal(results.length, 1); - equal(results[0].get('name'), group2.get('name')); + equal(results[0].get("name"), group2.get("name")); }); - it_id('8886b994-fbb8-487d-a863-43bbd2b24b73')(it)( - 'withJSON supports geoWithin.centerSphere', + it_id("8886b994-fbb8-487d-a863-43bbd2b24b73")(it)( + "withJSON supports geoWithin.centerSphere", done => { const inbound = new Parse.GeoPoint(1.5, 1.5); const onbound = new Parse.GeoPoint(10, 10); const outbound = new Parse.GeoPoint(20, 20); - const obj1 = new Parse.Object('TestObject', { location: inbound }); - const obj2 = new Parse.Object('TestObject', { location: onbound }); - const obj3 = new Parse.Object('TestObject', { location: outbound }); + const obj1 = new Parse.Object("TestObject", { location: inbound }); + const obj2 = new Parse.Object("TestObject", { location: onbound }); + const obj3 = new Parse.Object("TestObject", { location: outbound }); const center = new Parse.GeoPoint(0, 0); const distanceInKilometers = 1569 + 1; // 1569km is the approximate distance between {0, 0} and {10, 10}. Parse.Object.saveAll([obj1, obj2, obj3]) @@ -5083,7 +5432,7 @@ describe('Parse.Query testing', () => { } ); - it('withJSON with geoWithin.centerSphere fails without parameters', done => { + it("withJSON with geoWithin.centerSphere fails without parameters", done => { const q = new Parse.Query(TestObject); const jsonQ = q.toJSON(); jsonQ.where.location = { @@ -5098,12 +5447,12 @@ describe('Parse.Query testing', () => { .then(done); }); - it('withJSON with geoWithin.centerSphere fails with invalid distance', done => { + it("withJSON with geoWithin.centerSphere fails with invalid distance", done => { const q = new Parse.Query(TestObject); const jsonQ = q.toJSON(); jsonQ.where.location = { $geoWithin: { - $centerSphere: [[0, 0], 'invalid_distance'], + $centerSphere: [[0, 0], "invalid_distance"], }, }; q.withJSON(jsonQ); @@ -5113,7 +5462,7 @@ describe('Parse.Query testing', () => { .then(done); }); - it('withJSON with geoWithin.centerSphere fails with invalid coordinate', done => { + it("withJSON with geoWithin.centerSphere fails with invalid coordinate", done => { const q = new Parse.Query(TestObject); const jsonQ = q.toJSON(); jsonQ.where.location = { @@ -5127,7 +5476,7 @@ describe('Parse.Query testing', () => { .catch(() => done()); }); - it('withJSON with geoWithin.centerSphere fails with invalid geo point', done => { + it("withJSON with geoWithin.centerSphere fails with invalid geo point", done => { const q = new Parse.Query(TestObject); const jsonQ = q.toJSON(); jsonQ.where.location = { @@ -5141,172 +5490,172 @@ describe('Parse.Query testing', () => { .catch(() => done()); }); - it_id('02d4e7e6-859a-4ab6-878d-135ccc77040e')(it)( - 'can add new config to existing config', + it_id("02d4e7e6-859a-4ab6-878d-135ccc77040e")(it)( + "can add new config to existing config", async () => { await request({ - method: 'PUT', - url: 'http://localhost:8378/1/config', + method: "PUT", + url: "http://localhost:8378/1/config", json: true, body: { params: { - files: [{ __type: 'File', name: 'name', url: 'http://url' }], + files: [{ __type: "File", name: "name", url: "http://url" }], }, }, headers: masterKeyHeaders, }); await request({ - method: 'PUT', - url: 'http://localhost:8378/1/config', + method: "PUT", + url: "http://localhost:8378/1/config", json: true, body: { - params: { newConfig: 'good' }, + params: { newConfig: "good" }, }, headers: masterKeyHeaders, }); const result = await Parse.Config.get(); - equal(result.get('files')[0].toJSON(), { - __type: 'File', - name: 'name', - url: 'http://url', + equal(result.get("files")[0].toJSON(), { + __type: "File", + name: "name", + url: "http://url", }); - equal(result.get('newConfig'), 'good'); + equal(result.get("newConfig"), "good"); } ); - it('can set object type key', async () => { + it("can set object type key", async () => { const data = { bar: true, baz: 100 }; const object = new TestObject(); - object.set('objectField', data); + object.set("objectField", data); await object.save(); const query = new Parse.Query(TestObject); let result = await query.get(object.id); - equal(result.get('objectField'), data); + equal(result.get("objectField"), data); - object.set('objectField.baz', 50, { ignoreValidation: true }); + object.set("objectField.baz", 50, { ignoreValidation: true }); await object.save(); result = await query.get(object.id); - equal(result.get('objectField'), { bar: true, baz: 50 }); + equal(result.get("objectField"), { bar: true, baz: 50 }); }); - it('can update numeric array', async () => { + it("can update numeric array", async () => { const data1 = [0, 1.1, 1, -2, 3]; const data2 = [0, 1.1, 1, -2, 3, 4]; const obj1 = new TestObject(); - obj1.set('array', data1); + obj1.set("array", data1); await obj1.save(); - equal(obj1.get('array'), data1); + equal(obj1.get("array"), data1); const query = new Parse.Query(TestObject); - query.equalTo('objectId', obj1.id); + query.equalTo("objectId", obj1.id); const result = await query.first(); - equal(result.get('array'), data1); + equal(result.get("array"), data1); - result.set('array', data2); - equal(result.get('array'), data2); + result.set("array", data2); + equal(result.get("array"), data2); await result.save(); - equal(result.get('array'), data2); + equal(result.get("array"), data2); const results = await query.find(); - equal(results[0].get('array'), data2); + equal(results[0].get("array"), data2); }); - it('can update mixed array', async () => { - const data1 = [0, 1.1, 'hello world', { foo: 'bar' }]; - const data2 = [0, 1, { foo: 'bar' }, [], [1, 2, 'bar']]; + it("can update mixed array", async () => { + const data1 = [0, 1.1, "hello world", { foo: "bar" }]; + const data2 = [0, 1, { foo: "bar" }, [], [1, 2, "bar"]]; const obj1 = new TestObject(); - obj1.set('array', data1); + obj1.set("array", data1); await obj1.save(); - equal(obj1.get('array'), data1); + equal(obj1.get("array"), data1); const query = new Parse.Query(TestObject); - query.equalTo('objectId', obj1.id); + query.equalTo("objectId", obj1.id); const result = await query.first(); - equal(result.get('array'), data1); + equal(result.get("array"), data1); - result.set('array', data2); - equal(result.get('array'), data2); + result.set("array", data2); + equal(result.get("array"), data2); await result.save(); - equal(result.get('array'), data2); + equal(result.get("array"), data2); const results = await query.find(); - equal(results[0].get('array'), data2); + equal(results[0].get("array"), data2); }); - it('can query regex with unicode', async () => { + it("can query regex with unicode", async () => { const object = new TestObject(); - object.set('field', 'autoöo'); + object.set("field", "autoöo"); await object.save(); const query = new Parse.Query(TestObject); - query.contains('field', 'autoöo'); + query.contains("field", "autoöo"); const results = await query.find(); expect(results.length).toBe(1); - expect(results[0].get('field')).toBe('autoöo'); + expect(results[0].get("field")).toBe("autoöo"); }); - it('can update mixed array more than 100 elements', async () => { - const array = [0, 1.1, 'hello world', { foo: 'bar' }, null]; + it("can update mixed array more than 100 elements", async () => { + const array = [0, 1.1, "hello world", { foo: "bar" }, null]; const obj = new TestObject({ array }); await obj.save(); const query = new Parse.Query(TestObject); const result = await query.get(obj.id); - equal(result.get('array').length, 5); + equal(result.get("array").length, 5); for (let i = 0; i < 100; i += 1) { array.push(i); } - obj.set('array', array); + obj.set("array", array); await obj.save(); const results = await query.find(); - equal(results[0].get('array').length, 105); + equal(results[0].get("array").length, 105); }); - xit('todo: exclude keys with select key (sdk query get)', async done => { + xit("todo: exclude keys with select key (sdk query get)", async done => { // there is some problem with js sdk caching - const obj = new TestObject({ foo: 'baz', hello: 'world' }); + const obj = new TestObject({ foo: "baz", hello: "world" }); await obj.save(); - const query = new Parse.Query('TestObject'); + const query = new Parse.Query("TestObject"); query.withJSON({ - keys: 'hello', - excludeKeys: 'hello', + keys: "hello", + excludeKeys: "hello", }); const object = await query.get(obj.id); - expect(object.get('foo')).toBeUndefined(); - expect(object.get('hello')).toBeUndefined(); + expect(object.get("foo")).toBeUndefined(); + expect(object.get("hello")).toBeUndefined(); done(); }); - it_only_db('mongo')('can use explain on User class', async () => { + it_only_db("mongo")("can use explain on User class", async () => { // Create user const user = new Parse.User(); - user.set('username', 'foo'); - user.set('password', 'bar'); + user.set("username", "foo"); + user.set("password", "bar"); await user.save(); // Query for user with explain - const query = new Parse.Query('_User'); - query.equalTo('objectId', user.id); + const query = new Parse.Query("_User"); + query.equalTo("objectId", user.id); query.explain(); const result = await query.find(); // Validate expect(result.executionStats).not.toBeUndefined(); }); - it('should query with distinct within eachBatch and direct access enabled', async () => { + it("should query with distinct within eachBatch and direct access enabled", async () => { await reconfigureServer({ directAccess: true, }); @@ -5319,34 +5668,34 @@ describe('Parse.Query testing', () => { ); const user = new Parse.User(); - user.set('username', 'foo'); - user.set('password', 'bar'); + user.set("username", "foo"); + user.set("password", "bar"); await user.save(); - const score = new Parse.Object('Score'); - score.set('player', user); - score.set('score', 1); + const score = new Parse.Object("Score"); + score.set("player", user); + score.set("score", 1); await score.save(); - await new Parse.Query('_User').equalTo('objectId', user.id).eachBatch( + await new Parse.Query("_User").equalTo("objectId", user.id).eachBatch( async ([user]) => { - const score = await new Parse.Query('Score') - .equalTo('player', user) - .distinct('score', { useMasterKey: true }); + const score = await new Parse.Query("Score") + .equalTo("player", user) + .distinct("score", { useMasterKey: true }); expect(score).toEqual([1]); }, { useMasterKey: true } ); }); - describe_only_db('mongo')('query nested keys', () => { - it('queries nested key using equalTo', async () => { - const child = new Parse.Object('Child'); - child.set('key', 'value'); + describe_only_db("mongo")("query nested keys", () => { + it("queries nested key using equalTo", async () => { + const child = new Parse.Object("Child"); + child.set("key", "value"); await child.save(); - - const parent = new Parse.Object('Parent'); - parent.set('some', { + + const parent = new Parse.Object("Parent"); + parent.set("some", { nested: { key: { child, @@ -5354,21 +5703,21 @@ describe('Parse.Query testing', () => { }, }); await parent.save(); - - const query1 = await new Parse.Query('Parent') - .equalTo('some.nested.key.child', child) + + const query1 = await new Parse.Query("Parent") + .equalTo("some.nested.key.child", child) .find(); - + expect(query1.length).toEqual(1); }); - - it('queries nested key using containedIn', async () => { - const child = new Parse.Object('Child'); - child.set('key', 'value'); + + it("queries nested key using containedIn", async () => { + const child = new Parse.Object("Child"); + child.set("key", "value"); await child.save(); - - const parent = new Parse.Object('Parent'); - parent.set('some', { + + const parent = new Parse.Object("Parent"); + parent.set("some", { nested: { key: { child, @@ -5376,21 +5725,21 @@ describe('Parse.Query testing', () => { }, }); await parent.save(); - - const query1 = await new Parse.Query('Parent') - .containedIn('some.nested.key.child', [child]) + + const query1 = await new Parse.Query("Parent") + .containedIn("some.nested.key.child", [child]) .find(); - + expect(query1.length).toEqual(1); }); - - it('queries nested key using matchesQuery', async () => { - const child = new Parse.Object('Child'); - child.set('key', 'value'); + + it("queries nested key using matchesQuery", async () => { + const child = new Parse.Object("Child"); + child.set("key", "value"); await child.save(); - - const parent = new Parse.Object('Parent'); - parent.set('some', { + + const parent = new Parse.Object("Parent"); + parent.set("some", { nested: { key: { child, @@ -5398,11 +5747,14 @@ describe('Parse.Query testing', () => { }, }); await parent.save(); - - const query1 = await new Parse.Query('Parent') - .matchesQuery('some.nested.key.child', new Parse.Query('Child').equalTo('key', 'value')) + + const query1 = await new Parse.Query("Parent") + .matchesQuery( + "some.nested.key.child", + new Parse.Query("Child").equalTo("key", "value") + ) .find(); - + expect(query1.length).toEqual(1); }); }); diff --git a/spec/ParseRelation.spec.js b/spec/ParseRelation.spec.js index f0c746065d..b7a62f6e20 100644 --- a/spec/ParseRelation.spec.js +++ b/spec/ParseRelation.spec.js @@ -1,17 +1,17 @@ -'use strict'; +"use strict"; // This is a port of the test suite: // hungry/js/test/parse_relation_test.js -const ChildObject = Parse.Object.extend({ className: 'ChildObject' }); -const ParentObject = Parse.Object.extend({ className: 'ParentObject' }); +const ChildObject = Parse.Object.extend({ className: "ChildObject" }); +const ParentObject = Parse.Object.extend({ className: "ParentObject" }); -describe('Parse.Relation testing', () => { - it('simple add and remove relation', done => { +describe("Parse.Relation testing", () => { + it("simple add and remove relation", done => { const child = new ChildObject(); - child.set('x', 2); + child.set("x", 2); const parent = new ParentObject(); - parent.set('x', 4); - const relation = parent.relation('child'); + parent.set("x", 4); + const relation = parent.relation("child"); child .save() @@ -28,9 +28,9 @@ describe('Parse.Relation testing', () => { return relation.query().find(); }) .then(list => { - equal(list.length, 1, 'Should have gotten one element back'); - equal(list[0].id, child.id, 'Should have gotten the right value'); - ok(!parent.dirty('child'), 'The relation should not be dirty'); + equal(list.length, 1, "Should have gotten one element back"); + equal(list[0].id, child.id, "Should have gotten the right value"); + ok(!parent.dirty("child"), "The relation should not be dirty"); relation.remove(child); return parent.save(); @@ -39,59 +39,59 @@ describe('Parse.Relation testing', () => { return relation.query().find(); }) .then(list => { - equal(list.length, 0, 'Delete should have worked'); - ok(!parent.dirty('child'), 'The relation should not be dirty'); + equal(list.length, 0, "Delete should have worked"); + ok(!parent.dirty("child"), "The relation should not be dirty"); done(); }); }); - it('query relation without schema', async () => { - const ChildObject = Parse.Object.extend('ChildObject'); + it("query relation without schema", async () => { + const ChildObject = Parse.Object.extend("ChildObject"); const childObjects = []; for (let i = 0; i < 10; i++) { childObjects.push(new ChildObject({ x: i })); } await Parse.Object.saveAll(childObjects); - const ParentObject = Parse.Object.extend('ParentObject'); + const ParentObject = Parse.Object.extend("ParentObject"); const parent = new ParentObject(); - parent.set('x', 4); - let relation = parent.relation('child'); + parent.set("x", 4); + let relation = parent.relation("child"); relation.add(childObjects[0]); await parent.save(); const parentAgain = new ParentObject(); parentAgain.id = parent.id; - relation = parentAgain.relation('child'); + relation = parentAgain.relation("child"); const list = await relation.query().find(); - equal(list.length, 1, 'Should have gotten one element back'); - equal(list[0].id, childObjects[0].id, 'Should have gotten the right value'); + equal(list.length, 1, "Should have gotten one element back"); + equal(list[0].id, childObjects[0].id, "Should have gotten the right value"); }); - it('relations are constructed right from query', async () => { - const ChildObject = Parse.Object.extend('ChildObject'); + it("relations are constructed right from query", async () => { + const ChildObject = Parse.Object.extend("ChildObject"); const childObjects = []; for (let i = 0; i < 10; i++) { childObjects.push(new ChildObject({ x: i })); } await Parse.Object.saveAll(childObjects); - const ParentObject = Parse.Object.extend('ParentObject'); + const ParentObject = Parse.Object.extend("ParentObject"); const parent = new ParentObject(); - parent.set('x', 4); - const relation = parent.relation('child'); + parent.set("x", 4); + const relation = parent.relation("child"); relation.add(childObjects[0]); await parent.save(); const query = new Parse.Query(ParentObject); const object = await query.get(parent.id); - const relationAgain = object.relation('child'); + const relationAgain = object.relation("child"); const list = await relationAgain.query().find(); - equal(list.length, 1, 'Should have gotten one element back'); - equal(list[0].id, childObjects[0].id, 'Should have gotten the right value'); - ok(!parent.dirty('child'), 'The relation should not be dirty'); + equal(list.length, 1, "Should have gotten one element back"); + equal(list[0].id, childObjects[0].id, "Should have gotten the right value"); + ok(!parent.dirty("child"), "The relation should not be dirty"); }); - it('compound add and remove relation', done => { - const ChildObject = Parse.Object.extend('ChildObject'); + it("compound add and remove relation", done => { + const ChildObject = Parse.Object.extend("ChildObject"); const childObjects = []; for (let i = 0; i < 10; i++) { childObjects.push(new ChildObject({ x: i })); @@ -102,10 +102,10 @@ describe('Parse.Relation testing', () => { Parse.Object.saveAll(childObjects) .then(function () { - const ParentObject = Parse.Object.extend('ParentObject'); + const ParentObject = Parse.Object.extend("ParentObject"); parent = new ParentObject(); - parent.set('x', 4); - relation = parent.relation('child'); + parent.set("x", 4); + relation = parent.relation("child"); relation.add(childObjects[0]); relation.add(childObjects[1]); relation.remove(childObjects[0]); @@ -116,8 +116,8 @@ describe('Parse.Relation testing', () => { return relation.query().find(); }) .then(function (list) { - equal(list.length, 2, 'Should have gotten two elements back'); - ok(!parent.dirty('child'), 'The relation should not be dirty'); + equal(list.length, 2, "Should have gotten two elements back"); + ok(!parent.dirty("child"), "The relation should not be dirty"); relation.remove(childObjects[1]); relation.remove(childObjects[2]); relation.add(childObjects[1]); @@ -129,8 +129,8 @@ describe('Parse.Relation testing', () => { }) .then( function (list) { - equal(list.length, 2, 'Deletes and then adds should have worked'); - ok(!parent.dirty('child'), 'The relation should not be dirty'); + equal(list.length, 2, "Deletes and then adds should have worked"); + ok(!parent.dirty("child"), "The relation should not be dirty"); done(); }, function (err) { @@ -140,8 +140,8 @@ describe('Parse.Relation testing', () => { ); }); - it('related at ordering optimizations', done => { - const ChildObject = Parse.Object.extend('ChildObject'); + it("related at ordering optimizations", done => { + const ChildObject = Parse.Object.extend("ChildObject"); const childObjects = []; for (let i = 0; i < 10; i++) { childObjects.push(new ChildObject({ x: i })); @@ -152,16 +152,16 @@ describe('Parse.Relation testing', () => { Parse.Object.saveAll(childObjects) .then(function () { - const ParentObject = Parse.Object.extend('ParentObject'); + const ParentObject = Parse.Object.extend("ParentObject"); parent = new ParentObject(); - parent.set('x', 4); - relation = parent.relation('child'); + parent.set("x", 4); + relation = parent.relation("child"); relation.add(childObjects); return parent.save(); }) .then(function () { const query = relation.query(); - query.descending('createdAt'); + query.descending("createdAt"); query.skip(1); query.limit(3); return query.find(); @@ -172,48 +172,52 @@ describe('Parse.Relation testing', () => { .then(done, done.fail); }); - it('queries with relations', async () => { - const ChildObject = Parse.Object.extend('ChildObject'); + it("queries with relations", async () => { + const ChildObject = Parse.Object.extend("ChildObject"); const childObjects = []; for (let i = 0; i < 10; i++) { childObjects.push(new ChildObject({ x: i })); } await Parse.Object.saveAll(childObjects); - const ParentObject = Parse.Object.extend('ParentObject'); + const ParentObject = Parse.Object.extend("ParentObject"); const parent = new ParentObject(); - parent.set('x', 4); - const relation = parent.relation('child'); + parent.set("x", 4); + const relation = parent.relation("child"); relation.add(childObjects[0]); relation.add(childObjects[1]); relation.add(childObjects[2]); await parent.save(); const query = relation.query(); - query.equalTo('x', 2); + query.equalTo("x", 2); const list = await query.find(); - equal(list.length, 1, 'There should only be one element'); - ok(list[0] instanceof ChildObject, 'Should be of type ChildObject'); - equal(list[0].id, childObjects[2].id, 'We should have gotten back the right result'); + equal(list.length, 1, "There should only be one element"); + ok(list[0] instanceof ChildObject, "Should be of type ChildObject"); + equal( + list[0].id, + childObjects[2].id, + "We should have gotten back the right result" + ); }); - it('queries on relation fields', async () => { - const ChildObject = Parse.Object.extend('ChildObject'); + it("queries on relation fields", async () => { + const ChildObject = Parse.Object.extend("ChildObject"); const childObjects = []; for (let i = 0; i < 10; i++) { childObjects.push(new ChildObject({ x: i })); } await Parse.Object.saveAll(childObjects); - const ParentObject = Parse.Object.extend('ParentObject'); + const ParentObject = Parse.Object.extend("ParentObject"); const parent = new ParentObject(); - parent.set('x', 4); - const relation = parent.relation('child'); + parent.set("x", 4); + const relation = parent.relation("child"); relation.add(childObjects[0]); relation.add(childObjects[1]); relation.add(childObjects[2]); const parent2 = new ParentObject(); - parent2.set('x', 3); - const relation2 = parent2.relation('child'); + parent2.set("x", 3); + const relation2 = parent2.relation("child"); relation2.add(childObjects[4]); relation2.add(childObjects[5]); relation2.add(childObjects[6]); @@ -225,13 +229,13 @@ describe('Parse.Relation testing', () => { const objects = []; objects.push(childObjects[4]); objects.push(childObjects[9]); - const list = await query.containedIn('child', objects).find(); - equal(list.length, 1, 'There should be only one result'); - equal(list[0].id, parent2.id, 'Should have gotten back the right result'); + const list = await query.containedIn("child", objects).find(); + equal(list.length, 1, "There should be only one result"); + equal(list[0].id, parent2.id, "Should have gotten back the right result"); }); - it('queries on relation fields with multiple containedIn (regression test for #1271)', done => { - const ChildObject = Parse.Object.extend('ChildObject'); + it("queries on relation fields with multiple containedIn (regression test for #1271)", done => { + const ChildObject = Parse.Object.extend("ChildObject"); const childObjects = []; for (let i = 0; i < 10; i++) { childObjects.push(new ChildObject({ x: i })); @@ -239,21 +243,21 @@ describe('Parse.Relation testing', () => { Parse.Object.saveAll(childObjects) .then(() => { - const ParentObject = Parse.Object.extend('ParentObject'); + const ParentObject = Parse.Object.extend("ParentObject"); const parent = new ParentObject(); - parent.set('x', 4); - const parent1Children = parent.relation('child'); + parent.set("x", 4); + const parent1Children = parent.relation("child"); parent1Children.add(childObjects[0]); parent1Children.add(childObjects[1]); parent1Children.add(childObjects[2]); const parent2 = new ParentObject(); - parent2.set('x', 3); - const parent2Children = parent2.relation('child'); + parent2.set("x", 3); + const parent2Children = parent2.relation("child"); parent2Children.add(childObjects[4]); parent2Children.add(childObjects[5]); parent2Children.add(childObjects[6]); - const parent2OtherChildren = parent2.relation('otherChild'); + const parent2OtherChildren = parent2.relation("otherChild"); parent2OtherChildren.add(childObjects[0]); parent2OtherChildren.add(childObjects[1]); parent2OtherChildren.add(childObjects[2]); @@ -262,8 +266,10 @@ describe('Parse.Relation testing', () => { }) .then(() => { const objectsWithChild0InBothChildren = new Parse.Query(ParentObject); - objectsWithChild0InBothChildren.containedIn('child', [childObjects[0]]); - objectsWithChild0InBothChildren.containedIn('otherChild', [childObjects[0]]); + objectsWithChild0InBothChildren.containedIn("child", [childObjects[0]]); + objectsWithChild0InBothChildren.containedIn("otherChild", [ + childObjects[0], + ]); return objectsWithChild0InBothChildren.find(); }) .then(objectsWithChild0InBothChildren => { @@ -272,8 +278,10 @@ describe('Parse.Relation testing', () => { }) .then(() => { const objectsWithChild4andOtherChild1 = new Parse.Query(ParentObject); - objectsWithChild4andOtherChild1.containedIn('child', [childObjects[4]]); - objectsWithChild4andOtherChild1.containedIn('otherChild', [childObjects[1]]); + objectsWithChild4andOtherChild1.containedIn("child", [childObjects[4]]); + objectsWithChild4andOtherChild1.containedIn("otherChild", [ + childObjects[1], + ]); return objectsWithChild4andOtherChild1.find(); }) .then(objects => { @@ -283,8 +291,8 @@ describe('Parse.Relation testing', () => { }); }); - it('query on pointer and relation fields with equal', done => { - const ChildObject = Parse.Object.extend('ChildObject'); + it("query on pointer and relation fields with equal", done => { + const ChildObject = Parse.Object.extend("ChildObject"); const childObjects = []; for (let i = 0; i < 10; i++) { childObjects.push(new ChildObject({ x: i })); @@ -292,17 +300,17 @@ describe('Parse.Relation testing', () => { Parse.Object.saveAll(childObjects) .then(() => { - const ParentObject = Parse.Object.extend('ParentObject'); + const ParentObject = Parse.Object.extend("ParentObject"); const parent = new ParentObject(); - parent.set('x', 4); - const relation = parent.relation('toChilds'); + parent.set("x", 4); + const relation = parent.relation("toChilds"); relation.add(childObjects[0]); relation.add(childObjects[1]); relation.add(childObjects[2]); const parent2 = new ParentObject(); - parent2.set('x', 3); - parent2.set('toChild', childObjects[2]); + parent2.set("x", 3); + parent2.set("toChild", childObjects[2]); const parents = []; parents.push(parent); @@ -311,11 +319,11 @@ describe('Parse.Relation testing', () => { return Parse.Object.saveAll(parents).then(() => { const query = new Parse.Query(ParentObject); - query.equalTo('objectId', parent.id); - query.equalTo('toChilds', childObjects[2]); + query.equalTo("objectId", parent.id); + query.equalTo("toChilds", childObjects[2]); return query.find().then(list => { - equal(list.length, 1, 'There should be 1 result'); + equal(list.length, 1, "There should be 1 result"); done(); }); }); @@ -326,25 +334,25 @@ describe('Parse.Relation testing', () => { }); }); - it('query on pointer and relation fields with equal bis', done => { - const ChildObject = Parse.Object.extend('ChildObject'); + it("query on pointer and relation fields with equal bis", done => { + const ChildObject = Parse.Object.extend("ChildObject"); const childObjects = []; for (let i = 0; i < 10; i++) { childObjects.push(new ChildObject({ x: i })); } Parse.Object.saveAll(childObjects).then(() => { - const ParentObject = Parse.Object.extend('ParentObject'); + const ParentObject = Parse.Object.extend("ParentObject"); const parent = new ParentObject(); - parent.set('x', 4); - const relation = parent.relation('toChilds'); + parent.set("x", 4); + const relation = parent.relation("toChilds"); relation.add(childObjects[0]); relation.add(childObjects[1]); relation.add(childObjects[2]); const parent2 = new ParentObject(); - parent2.set('x', 3); - parent2.relation('toChilds').add(childObjects[2]); + parent2.set("x", 3); + parent2.relation("toChilds").add(childObjects[2]); const parents = []; parents.push(parent); @@ -353,38 +361,38 @@ describe('Parse.Relation testing', () => { return Parse.Object.saveAll(parents).then(() => { const query = new Parse.Query(ParentObject); - query.equalTo('objectId', parent2.id); + query.equalTo("objectId", parent2.id); // childObjects[2] is in 2 relations // before the fix, that woul yield 2 results - query.equalTo('toChilds', childObjects[2]); + query.equalTo("toChilds", childObjects[2]); return query.find().then(list => { - equal(list.length, 1, 'There should be 1 result'); + equal(list.length, 1, "There should be 1 result"); done(); }); }); }); }); - it('or queries on pointer and relation fields', done => { - const ChildObject = Parse.Object.extend('ChildObject'); + it("or queries on pointer and relation fields", done => { + const ChildObject = Parse.Object.extend("ChildObject"); const childObjects = []; for (let i = 0; i < 10; i++) { childObjects.push(new ChildObject({ x: i })); } Parse.Object.saveAll(childObjects).then(() => { - const ParentObject = Parse.Object.extend('ParentObject'); + const ParentObject = Parse.Object.extend("ParentObject"); const parent = new ParentObject(); - parent.set('x', 4); - const relation = parent.relation('toChilds'); + parent.set("x", 4); + const relation = parent.relation("toChilds"); relation.add(childObjects[0]); relation.add(childObjects[1]); relation.add(childObjects[2]); const parent2 = new ParentObject(); - parent2.set('x', 3); - parent2.set('toChild', childObjects[2]); + parent2.set("x", 3); + parent2.set("toChild", childObjects[2]); const parents = []; parents.push(parent); @@ -393,9 +401,9 @@ describe('Parse.Relation testing', () => { return Parse.Object.saveAll(parents).then(() => { const query1 = new Parse.Query(ParentObject); - query1.containedIn('toChilds', [childObjects[2]]); + query1.containedIn("toChilds", [childObjects[2]]); const query2 = new Parse.Query(ParentObject); - query2.equalTo('toChild', childObjects[2]); + query2.equalTo("toChild", childObjects[2]); const query = Parse.Query.or(query1, query2); return query.find().then(list => { const objectIds = list.map(function (item) { @@ -403,31 +411,31 @@ describe('Parse.Relation testing', () => { }); expect(objectIds.indexOf(parent.id)).not.toBe(-1); expect(objectIds.indexOf(parent2.id)).not.toBe(-1); - equal(list.length, 2, 'There should be 2 results'); + equal(list.length, 2, "There should be 2 results"); done(); }); }); }); }); - it('or queries with base constraint on relation field', async () => { - const ChildObject = Parse.Object.extend('ChildObject'); + it("or queries with base constraint on relation field", async () => { + const ChildObject = Parse.Object.extend("ChildObject"); const childObjects = []; for (let i = 0; i < 10; i++) { childObjects.push(new ChildObject({ x: i })); } await Parse.Object.saveAll(childObjects); - const ParentObject = Parse.Object.extend('ParentObject'); + const ParentObject = Parse.Object.extend("ParentObject"); const parent = new ParentObject(); - parent.set('x', 4); - const relation = parent.relation('toChilds'); + parent.set("x", 4); + const relation = parent.relation("toChilds"); relation.add(childObjects[0]); relation.add(childObjects[1]); relation.add(childObjects[2]); const parent2 = new ParentObject(); - parent2.set('x', 3); - const relation2 = parent2.relation('toChilds'); + parent2.set("x", 3); + const relation2 = parent2.relation("toChilds"); relation2.add(childObjects[0]); relation2.add(childObjects[1]); relation2.add(childObjects[2]); @@ -439,30 +447,30 @@ describe('Parse.Relation testing', () => { await Parse.Object.saveAll(parents); const query1 = new Parse.Query(ParentObject); - query1.equalTo('x', 4); + query1.equalTo("x", 4); const query2 = new Parse.Query(ParentObject); - query2.equalTo('x', 3); + query2.equalTo("x", 3); const query = Parse.Query.or(query1, query2); - query.equalTo('toChilds', childObjects[2]); + query.equalTo("toChilds", childObjects[2]); const list = await query.find(); const objectIds = list.map(item => item.id); expect(objectIds.indexOf(parent.id)).not.toBe(-1); expect(objectIds.indexOf(parent2.id)).not.toBe(-1); - equal(list.length, 2, 'There should be 2 results'); + equal(list.length, 2, "There should be 2 results"); }); - it('Get query on relation using un-fetched parent object', done => { + it("Get query on relation using un-fetched parent object", done => { // Setup data model - const Wheel = Parse.Object.extend('Wheel'); - const Car = Parse.Object.extend('Car'); + const Wheel = Parse.Object.extend("Wheel"); + const Car = Parse.Object.extend("Car"); const origWheel = new Wheel(); origWheel .save() .then(function () { const car = new Car(); - const relation = car.relation('wheels'); + const relation = car.relation("wheels"); relation.add(origWheel); return car.save(); }) @@ -471,7 +479,7 @@ describe('Parse.Relation testing', () => { // Create an un-fetched shell car object const unfetchedCar = new Car(); unfetchedCar.id = car.id; - const relation = unfetchedCar.relation('wheels'); + const relation = unfetchedCar.relation("wheels"); const query = relation.query(); // Parent object is un-fetched, so this will call /1/classes/Car instead @@ -480,7 +488,7 @@ describe('Parse.Relation testing', () => { }) .then(function (wheel) { // Make sure this is Wheel and not Car. - strictEqual(wheel.className, 'Wheel'); + strictEqual(wheel.className, "Wheel"); strictEqual(wheel.id, origWheel.id); }) .then( @@ -488,22 +496,22 @@ describe('Parse.Relation testing', () => { done(); }, function (err) { - ok(false, 'unexpected error: ' + JSON.stringify(err)); + ok(false, "unexpected error: " + JSON.stringify(err)); done(); } ); }); - it('Find query on relation using un-fetched parent object', done => { + it("Find query on relation using un-fetched parent object", done => { // Setup data model - const Wheel = Parse.Object.extend('Wheel'); - const Car = Parse.Object.extend('Car'); + const Wheel = Parse.Object.extend("Wheel"); + const Car = Parse.Object.extend("Car"); const origWheel = new Wheel(); origWheel .save() .then(function () { const car = new Car(); - const relation = car.relation('wheels'); + const relation = car.relation("wheels"); relation.add(origWheel); return car.save(); }) @@ -512,7 +520,7 @@ describe('Parse.Relation testing', () => { // Create an un-fetched shell car object const unfetchedCar = new Car(); unfetchedCar.id = car.id; - const relation = unfetchedCar.relation('wheels'); + const relation = unfetchedCar.relation("wheels"); const query = relation.query(); // Parent object is un-fetched, so this will call /1/classes/Car instead @@ -522,7 +530,7 @@ describe('Parse.Relation testing', () => { .then(function (results) { // Make sure this is Wheel and not Car. const wheel = results[0]; - strictEqual(wheel.className, 'Wheel'); + strictEqual(wheel.className, "Wheel"); strictEqual(wheel.id, origWheel.id); }) .then( @@ -530,28 +538,28 @@ describe('Parse.Relation testing', () => { done(); }, function (err) { - ok(false, 'unexpected error: ' + JSON.stringify(err)); + ok(false, "unexpected error: " + JSON.stringify(err)); done(); } ); }); - it('Find objects with a related object using equalTo', done => { + it("Find objects with a related object using equalTo", done => { // Setup the objects - const Card = Parse.Object.extend('Card'); - const House = Parse.Object.extend('House'); + const Card = Parse.Object.extend("Card"); + const House = Parse.Object.extend("House"); const card = new Card(); card .save() .then(() => { const house = new House(); - const relation = house.relation('cards'); + const relation = house.relation("cards"); relation.add(card); return house.save(); }) .then(() => { - const query = new Parse.Query('House'); - query.equalTo('cards', card); + const query = new Parse.Query("House"); + query.equalTo("cards", card); return query.find(); }) .then(results => { @@ -560,76 +568,76 @@ describe('Parse.Relation testing', () => { }); }); - it('should properly get related objects with unfetched queries', done => { + it("should properly get related objects with unfetched queries", done => { const objects = []; const owners = []; const allObjects = []; // Build 10 Objects and 10 owners while (objects.length != 10) { - const object = new Parse.Object('AnObject'); + const object = new Parse.Object("AnObject"); object.set({ index: objects.length, even: objects.length % 2 == 0, }); objects.push(object); - const owner = new Parse.Object('AnOwner'); + const owner = new Parse.Object("AnOwner"); owners.push(owner); allObjects.push(object); allObjects.push(owner); } - const anotherOwner = new Parse.Object('AnotherOwner'); + const anotherOwner = new Parse.Object("AnotherOwner"); return Parse.Object.saveAll(allObjects.concat([anotherOwner])) .then(() => { // put all the AnObject into the anotherOwner relationKey - anotherOwner.relation('relationKey').add(objects); + anotherOwner.relation("relationKey").add(objects); // Set each object[i] into owner[i]; owners.forEach((owner, i) => { - owner.set('key', objects[i]); + owner.set("key", objects[i]); }); return Parse.Object.saveAll(owners.concat([anotherOwner])); }) .then(() => { // Query on the relation of another owner - const object = new Parse.Object('AnotherOwner'); + const object = new Parse.Object("AnotherOwner"); object.id = anotherOwner.id; - const relationQuery = object.relation('relationKey').query(); + const relationQuery = object.relation("relationKey").query(); // Just get the even ones - relationQuery.equalTo('even', true); + relationQuery.equalTo("even", true); // Make the query on anOwner - const query = new Parse.Query('AnOwner'); + const query = new Parse.Query("AnOwner"); // where key match the relation query. - query.matchesQuery('key', relationQuery); - query.include('key'); + query.matchesQuery("key", relationQuery); + query.include("key"); return query.find(); }) .then(results => { expect(results.length).toBe(5); results.forEach(result => { - expect(result.get('key').get('even')).toBe(true); + expect(result.get("key").get("even")).toBe(true); }); return Promise.resolve(); }) .then(() => { // Query on the relation of another owner - const object = new Parse.Object('AnotherOwner'); + const object = new Parse.Object("AnotherOwner"); object.id = anotherOwner.id; - const relationQuery = object.relation('relationKey').query(); + const relationQuery = object.relation("relationKey").query(); // Just get the even ones - relationQuery.equalTo('even', true); + relationQuery.equalTo("even", true); // Make the query on anOwner - const query = new Parse.Query('AnOwner'); + const query = new Parse.Query("AnOwner"); // where key match the relation query. - query.doesNotMatchQuery('key', relationQuery); - query.include('key'); + query.doesNotMatchQuery("key", relationQuery); + query.include("key"); return query.find(); }) .then( results => { expect(results.length).toBe(5); results.forEach(result => { - expect(result.get('key').get('even')).toBe(false); + expect(result.get("key").get("even")).toBe(false); }); done(); }, @@ -640,39 +648,39 @@ describe('Parse.Relation testing', () => { ); }); - it('select query', function (done) { - const RestaurantObject = Parse.Object.extend('Restaurant'); - const PersonObject = Parse.Object.extend('Person'); - const OwnerObject = Parse.Object.extend('Owner'); + it("select query", function (done) { + const RestaurantObject = Parse.Object.extend("Restaurant"); + const PersonObject = Parse.Object.extend("Person"); + const OwnerObject = Parse.Object.extend("Owner"); const restaurants = [ - new RestaurantObject({ ratings: 5, location: 'Djibouti' }), - new RestaurantObject({ ratings: 3, location: 'Ouagadougou' }), + new RestaurantObject({ ratings: 5, location: "Djibouti" }), + new RestaurantObject({ ratings: 3, location: "Ouagadougou" }), ]; const persons = [ - new PersonObject({ name: 'Bob', hometown: 'Djibouti' }), - new PersonObject({ name: 'Tom', hometown: 'Ouagadougou' }), - new PersonObject({ name: 'Billy', hometown: 'Detroit' }), + new PersonObject({ name: "Bob", hometown: "Djibouti" }), + new PersonObject({ name: "Tom", hometown: "Ouagadougou" }), + new PersonObject({ name: "Billy", hometown: "Detroit" }), ]; - const owner = new OwnerObject({ name: 'Joe' }); + const owner = new OwnerObject({ name: "Joe" }); const allObjects = [owner].concat(restaurants).concat(persons); expect(allObjects.length).toEqual(6); Parse.Object.saveAll([owner].concat(restaurants).concat(persons)) .then(function () { - owner.relation('restaurants').add(restaurants); + owner.relation("restaurants").add(restaurants); return owner.save(); }) .then( async () => { const unfetchedOwner = new OwnerObject(); unfetchedOwner.id = owner.id; - const query = unfetchedOwner.relation('restaurants').query(); - query.greaterThan('ratings', 4); + const query = unfetchedOwner.relation("restaurants").query(); + query.greaterThan("ratings", 4); const mainQuery = new Parse.Query(PersonObject); - mainQuery.matchesKeyInQuery('hometown', 'location', query); + mainQuery.matchesKeyInQuery("hometown", "location", query); const results = await mainQuery.find(); equal(results.length, 1); if (results.length > 0) { - equal(results[0].get('name'), 'Bob'); + equal(results[0].get("name"), "Bob"); } done(); }, @@ -683,41 +691,41 @@ describe('Parse.Relation testing', () => { ); }); - it('dontSelect query', function (done) { - const RestaurantObject = Parse.Object.extend('Restaurant'); - const PersonObject = Parse.Object.extend('Person'); - const OwnerObject = Parse.Object.extend('Owner'); + it("dontSelect query", function (done) { + const RestaurantObject = Parse.Object.extend("Restaurant"); + const PersonObject = Parse.Object.extend("Person"); + const OwnerObject = Parse.Object.extend("Owner"); const restaurants = [ - new RestaurantObject({ ratings: 5, location: 'Djibouti' }), - new RestaurantObject({ ratings: 3, location: 'Ouagadougou' }), + new RestaurantObject({ ratings: 5, location: "Djibouti" }), + new RestaurantObject({ ratings: 3, location: "Ouagadougou" }), ]; const persons = [ - new PersonObject({ name: 'Bob', hometown: 'Djibouti' }), - new PersonObject({ name: 'Tom', hometown: 'Ouagadougou' }), - new PersonObject({ name: 'Billy', hometown: 'Detroit' }), + new PersonObject({ name: "Bob", hometown: "Djibouti" }), + new PersonObject({ name: "Tom", hometown: "Ouagadougou" }), + new PersonObject({ name: "Billy", hometown: "Detroit" }), ]; - const owner = new OwnerObject({ name: 'Joe' }); + const owner = new OwnerObject({ name: "Joe" }); const allObjects = [owner].concat(restaurants).concat(persons); expect(allObjects.length).toEqual(6); Parse.Object.saveAll([owner].concat(restaurants).concat(persons)) .then(function () { - owner.relation('restaurants').add(restaurants); + owner.relation("restaurants").add(restaurants); return owner.save(); }) .then( async () => { const unfetchedOwner = new OwnerObject(); unfetchedOwner.id = owner.id; - const query = unfetchedOwner.relation('restaurants').query(); - query.greaterThan('ratings', 4); + const query = unfetchedOwner.relation("restaurants").query(); + query.greaterThan("ratings", 4); const mainQuery = new Parse.Query(PersonObject); - mainQuery.doesNotMatchKeyInQuery('hometown', 'location', query); - mainQuery.ascending('name'); + mainQuery.doesNotMatchKeyInQuery("hometown", "location", query); + mainQuery.ascending("name"); const results = await mainQuery.find(); equal(results.length, 2); if (results.length > 0) { - equal(results[0].get('name'), 'Billy'); - equal(results[1].get('name'), 'Tom'); + equal(results[0].get("name"), "Billy"); + equal(results[1].get("name"), "Tom"); } done(); }, @@ -728,23 +736,23 @@ describe('Parse.Relation testing', () => { ); }); - it('relations are not bidirectional (regression test for #871)', done => { - const PersonObject = Parse.Object.extend('Person'); + it("relations are not bidirectional (regression test for #871)", done => { + const PersonObject = Parse.Object.extend("Person"); const p1 = new PersonObject(); const p2 = new PersonObject(); Parse.Object.saveAll([p1, p2]).then(results => { const p1 = results[0]; const p2 = results[1]; - const relation = p1.relation('relation'); + const relation = p1.relation("relation"); relation.add(p2); p1.save().then(() => { const query = new Parse.Query(PersonObject); - query.equalTo('relation', p1); + query.equalTo("relation", p1); query.find().then(results => { expect(results.length).toEqual(0); const query = new Parse.Query(PersonObject); - query.equalTo('relation', p2); + query.equalTo("relation", p2); query.find().then(results => { expect(results.length).toEqual(1); expect(results[0].objectId).toEqual(p1.objectId); @@ -755,32 +763,32 @@ describe('Parse.Relation testing', () => { }); }); - it('can query roles in Cloud Code (regession test #1489)', done => { - Parse.Cloud.define('isAdmin', request => { + it("can query roles in Cloud Code (regession test #1489)", done => { + Parse.Cloud.define("isAdmin", request => { const query = new Parse.Query(Parse.Role); - query.equalTo('name', 'admin'); + query.equalTo("name", "admin"); return query.first({ useMasterKey: true }).then( role => { - const relation = new Parse.Relation(role, 'users'); + const relation = new Parse.Relation(role, "users"); const admins = relation.query(); - admins.equalTo('username', request.user.get('username')); + admins.equalTo("username", request.user.get("username")); admins.first({ useMasterKey: true }).then( user => { if (user) { done(); } else { - fail('Should have found admin user, found nothing instead'); + fail("Should have found admin user, found nothing instead"); done(); } }, () => { - fail('User not admin'); + fail("User not admin"); done(); } ); }, error => { - fail('Should have found admin user, errored instead'); + fail("Should have found admin user, errored instead"); fail(error); done(); } @@ -788,107 +796,107 @@ describe('Parse.Relation testing', () => { }); const adminUser = new Parse.User(); - adminUser.set('username', 'name'); - adminUser.set('password', 'pass'); + adminUser.set("username", "name"); + adminUser.set("password", "pass"); adminUser.signUp().then( adminUser => { const adminACL = new Parse.ACL(); adminACL.setPublicReadAccess(true); // Create admin role - const adminRole = new Parse.Role('admin', adminACL); + const adminRole = new Parse.Role("admin", adminACL); adminRole.getUsers().add(adminUser); adminRole.save().then( () => { - Parse.Cloud.run('isAdmin'); + Parse.Cloud.run("isAdmin"); }, error => { - fail('failed to save role'); + fail("failed to save role"); fail(error); done(); } ); }, error => { - fail('failed to sign up'); + fail("failed to sign up"); fail(error); done(); } ); }); - it('can be saved without error', done => { - const obj1 = new Parse.Object('PPAP'); + it("can be saved without error", done => { + const obj1 = new Parse.Object("PPAP"); obj1.save().then( () => { - const newRelation = obj1.relation('aRelation'); + const newRelation = obj1.relation("aRelation"); newRelation.add(obj1); obj1.save().then( () => { - const relation = obj1.get('aRelation'); - obj1.set('aRelation', relation); + const relation = obj1.get("aRelation"); + obj1.set("aRelation", relation); obj1.save().then( () => { done(); }, error => { - fail('failed to save ParseRelation object'); + fail("failed to save ParseRelation object"); fail(error); done(); } ); }, error => { - fail('failed to create relation field'); + fail("failed to create relation field"); fail(error); done(); } ); }, error => { - fail('failed to save obj'); + fail("failed to save obj"); fail(error); done(); } ); }); - it('ensures beforeFind on relation doesnt side effect', done => { - const parent = new Parse.Object('Parent'); - const child = new Parse.Object('Child'); + it("ensures beforeFind on relation doesnt side effect", done => { + const parent = new Parse.Object("Parent"); + const child = new Parse.Object("Child"); child .save() .then(() => { - parent.relation('children').add(child); + parent.relation("children").add(child); return parent.save(); }) .then(() => { // We need to use a new reference otherwise the JS SDK remembers the className for a relation // After saves or finds - const otherParent = new Parse.Object('Parent'); + const otherParent = new Parse.Object("Parent"); otherParent.id = parent.id; - return otherParent.relation('children').query().find(); + return otherParent.relation("children").query().find(); }) .then(children => { // Without an after find all is good, all results have been redirected with proper className - children.forEach(child => expect(child.className).toBe('Child')); + children.forEach(child => expect(child.className).toBe("Child")); // Setup the afterFind - Parse.Cloud.afterFind('Child', req => { + Parse.Cloud.afterFind("Child", req => { return Promise.resolve( req.objects.map(child => { - child.set('afterFound', true); + child.set("afterFound", true); return child; }) ); }); - const otherParent = new Parse.Object('Parent'); + const otherParent = new Parse.Object("Parent"); otherParent.id = parent.id; - return otherParent.relation('children').query().find(); + return otherParent.relation("children").query().find(); }) .then(children => { children.forEach(child => { - expect(child.className).toBe('Child'); - expect(child.get('afterFound')).toBe(true); + expect(child.className).toBe("Child"); + expect(child.get("afterFound")).toBe(true); }); }) .then(done) diff --git a/spec/ParseRole.spec.js b/spec/ParseRole.spec.js index 0a9eb6df92..cd616a767d 100644 --- a/spec/ParseRole.spec.js +++ b/spec/ParseRole.spec.js @@ -1,32 +1,32 @@ -'use strict'; +"use strict"; // Roles are not accessible without the master key, so they are not intended // for use by clients. We can manually test them using the master key. -const RestQuery = require('../lib/RestQuery'); -const Auth = require('../lib/Auth').Auth; -const Config = require('../lib/Config'); +const RestQuery = require("../lib/RestQuery"); +const Auth = require("../lib/Auth").Auth; +const Config = require("../lib/Config"); function testLoadRoles(config, done) { - const rolesNames = ['FooRole', 'BarRole', 'BazRole']; + const rolesNames = ["FooRole", "BarRole", "BazRole"]; const roleIds = {}; createTestUser() .then(user => { // Put the user on the 1st role return createRole(rolesNames[0], null, user) .then(aRole => { - roleIds[aRole.get('name')] = aRole.id; + roleIds[aRole.get("name")] = aRole.id; // set the 1st role as a sibling of the second // user will should have 2 role now return createRole(rolesNames[1], aRole, null); }) .then(anotherRole => { - roleIds[anotherRole.get('name')] = anotherRole.id; + roleIds[anotherRole.get("name")] = anotherRole.id; // set this role as a sibling of the last // the user should now have 3 roles return createRole(rolesNames[2], anotherRole, null); }) .then(lastRole => { - roleIds[lastRole.get('name')] = lastRole.id; + roleIds[lastRole.get("name")] = lastRole.id; const auth = new Auth({ config, isMaster: true, user: user }); return auth._loadRoles(); }); @@ -35,12 +35,12 @@ function testLoadRoles(config, done) { roles => { expect(roles.length).toEqual(3); rolesNames.forEach(name => { - expect(roles.indexOf('role:' + name)).not.toBe(-1); + expect(roles.indexOf("role:" + name)).not.toBe(-1); }); done(); }, function () { - fail('should succeed'); + fail("should succeed"); done(); } ); @@ -49,17 +49,17 @@ function testLoadRoles(config, done) { const createRole = function (name, sibling, user) { const role = new Parse.Role(name, new Parse.ACL()); if (user) { - const users = role.relation('users'); + const users = role.relation("users"); users.add(user); } if (sibling) { - role.relation('roles').add(sibling); + role.relation("roles").add(sibling); } return role.save({}, { useMasterKey: true }); }; -describe('Parse Role testing', () => { - it('Do a bunch of basic role testing', done => { +describe("Parse Role testing", () => { + it("Do a bunch of basic role testing", done => { let user; let role; @@ -69,20 +69,20 @@ describe('Parse Role testing', () => { const acl = new Parse.ACL(); acl.setPublicReadAccess(true); acl.setPublicWriteAccess(false); - role = new Parse.Object('_Role'); - role.set('name', 'Foos'); + role = new Parse.Object("_Role"); + role.set("name", "Foos"); role.setACL(acl); - const users = role.relation('users'); + const users = role.relation("users"); users.add(user); return role.save({}, { useMasterKey: true }); }) .then(() => { - const query = new Parse.Query('_Role'); + const query = new Parse.Query("_Role"); return query.find({ useMasterKey: true }); }) .then(x => { expect(x.length).toEqual(1); - const relation = x[0].relation('users').query(); + const relation = x[0].relation("users").query(); return relation.first({ useMasterKey: true }); }) .then(x => { @@ -90,34 +90,34 @@ describe('Parse Role testing', () => { // Here we've got a valid role and a user assigned. // Lets create an object only the role can read/write and test // the different scenarios. - const obj = new Parse.Object('TestObject'); + const obj = new Parse.Object("TestObject"); const acl = new Parse.ACL(); acl.setPublicReadAccess(false); acl.setPublicWriteAccess(false); - acl.setRoleReadAccess('Foos', true); - acl.setRoleWriteAccess('Foos', true); + acl.setRoleReadAccess("Foos", true); + acl.setRoleWriteAccess("Foos", true); obj.setACL(acl); return obj.save(); }) .then(() => { - const query = new Parse.Query('TestObject'); + const query = new Parse.Query("TestObject"); return query.find({ sessionToken: user.getSessionToken() }); }) .then(x => { expect(x.length).toEqual(1); const objAgain = x[0]; - objAgain.set('foo', 'bar'); + objAgain.set("foo", "bar"); // This should succeed: return objAgain.save({}, { sessionToken: user.getSessionToken() }); }) .then(x => { - x.set('foo', 'baz'); + x.set("foo", "baz"); // This should fail: - return x.save({}, { sessionToken: '' }); + return x.save({}, { sessionToken: "" }); }) .then( () => { - fail('Should not have been able to save.'); + fail("Should not have been able to save."); }, e => { expect(e.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); @@ -126,11 +126,11 @@ describe('Parse Role testing', () => { ); }); - it_id('b03abe32-e8e4-4666-9b81-9c804aa53400')(it)( - 'should not recursively load the same role multiple times', + it_id("b03abe32-e8e4-4666-9b81-9c804aa53400")(it)( + "should not recursively load the same role multiple times", done => { - const rootRole = 'RootRole'; - const roleNames = ['FooRole', 'BarRole', 'BazRole']; + const rootRole = "RootRole"; + const roleNames = ["FooRole", "BarRole", "BazRole"]; const allRoles = [rootRole].concat(roleNames); const roleObjs = {}; @@ -144,7 +144,10 @@ describe('Parse Role testing', () => { return Promise.all(promises); }; - const restExecute = spyOn(RestQuery._UnsafeRestQuery.prototype, 'execute').and.callThrough(); + const restExecute = spyOn( + RestQuery._UnsafeRestQuery.prototype, + "execute" + ).and.callThrough(); let user, auth, getAllRolesSpy; createTestUser() @@ -157,11 +160,11 @@ describe('Parse Role testing', () => { roles.forEach(function (role, i) { // Add all roles to the RootRole if (role.id !== rootRoleObj.id) { - role.relation('roles').add(rootRoleObj); + role.relation("roles").add(rootRoleObj); } // Add all "roleNames" roles to the previous role if (i > 0) { - role.relation('roles').add(roles[i - 1]); + role.relation("roles").add(roles[i - 1]); } }); @@ -169,11 +172,14 @@ describe('Parse Role testing', () => { }) .then(() => { auth = new Auth({ - config: Config.get('test'), + config: Config.get("test"), isMaster: true, user: user, }); - getAllRolesSpy = spyOn(auth, '_getAllRolesNamesForRoleIds').and.callThrough(); + getAllRolesSpy = spyOn( + auth, + "_getAllRolesNamesForRoleIds" + ).and.callThrough(); return auth._loadRoles(); }) @@ -181,7 +187,7 @@ describe('Parse Role testing', () => { expect(roles.length).toEqual(4); allRoles.forEach(function (name) { - expect(roles.indexOf('role:' + name)).not.toBe(-1); + expect(roles.indexOf("role:" + name)).not.toBe(-1); }); // 1 Query for the initial setup @@ -194,32 +200,32 @@ describe('Parse Role testing', () => { done(); }) .catch(() => { - fail('should succeed'); + fail("should succeed"); done(); }); } ); - it('should recursively load roles', done => { - testLoadRoles(Config.get('test'), done); + it("should recursively load roles", done => { + testLoadRoles(Config.get("test"), done); }); - it('should recursively load roles without config', done => { + it("should recursively load roles without config", done => { testLoadRoles(undefined, done); }); - it('_Role object should not save without name.', done => { + it("_Role object should not save without name.", done => { const role = new Parse.Role(); role.save(null, { useMasterKey: true }).then( () => { - fail('_Role object should not save without name.'); + fail("_Role object should not save without name."); }, error => { expect(error.code).toEqual(111); - role.set('name', 'testRole'); + role.set("name", "testRole"); role.save(null, { useMasterKey: true }).then( () => { - fail('_Role object should not save without ACL.'); + fail("_Role object should not save without ACL."); }, error2 => { expect(error2.code).toEqual(111); @@ -230,9 +236,9 @@ describe('Parse Role testing', () => { ); }); - it('Different _Role objects cannot have the same name.', async done => { + it("Different _Role objects cannot have the same name.", async done => { await reconfigureServer(); - const roleName = 'MyRole'; + const roleName = "MyRole"; let aUser; createTestUser() .then(user => { @@ -245,7 +251,7 @@ describe('Parse Role testing', () => { }) .then( () => { - fail('_Role cannot have the same name as another role'); + fail("_Role cannot have the same name as another role"); done(); }, error => { @@ -255,37 +261,51 @@ describe('Parse Role testing', () => { ); }); - it('Should properly resolve roles', done => { - const admin = new Parse.Role('Admin', new Parse.ACL()); - const moderator = new Parse.Role('Moderator', new Parse.ACL()); - const superModerator = new Parse.Role('SuperModerator', new Parse.ACL()); - const contentManager = new Parse.Role('ContentManager', new Parse.ACL()); - const superContentManager = new Parse.Role('SuperContentManager', new Parse.ACL()); - Parse.Object.saveAll([admin, moderator, contentManager, superModerator, superContentManager], { - useMasterKey: true, - }) + it("Should properly resolve roles", done => { + const admin = new Parse.Role("Admin", new Parse.ACL()); + const moderator = new Parse.Role("Moderator", new Parse.ACL()); + const superModerator = new Parse.Role("SuperModerator", new Parse.ACL()); + const contentManager = new Parse.Role("ContentManager", new Parse.ACL()); + const superContentManager = new Parse.Role( + "SuperContentManager", + new Parse.ACL() + ); + Parse.Object.saveAll( + [admin, moderator, contentManager, superModerator, superContentManager], + { + useMasterKey: true, + } + ) .then(() => { contentManager.getRoles().add([moderator, superContentManager]); moderator.getRoles().add([admin, superModerator]); superContentManager.getRoles().add(superModerator); return Parse.Object.saveAll( - [admin, moderator, contentManager, superModerator, superContentManager], + [ + admin, + moderator, + contentManager, + superModerator, + superContentManager, + ], { useMasterKey: true } ); }) .then(() => { - const auth = new Auth({ config: Config.get('test'), isMaster: true }); + const auth = new Auth({ config: Config.get("test"), isMaster: true }); // For each role, fetch their sibling, what they inherit // return with result and roleId for later comparison - const promises = [admin, moderator, contentManager, superModerator].map(role => { - return auth._getAllRolesNamesForRoleIds([role.id]).then(result => { - return Promise.resolve({ - id: role.id, - name: role.get('name'), - roleNames: result, + const promises = [admin, moderator, contentManager, superModerator].map( + role => { + return auth._getAllRolesNamesForRoleIds([role.id]).then(result => { + return Promise.resolve({ + id: role.id, + name: role.get("name"), + roleNames: result, + }); }); - }); - }); + } + ); return Promise.all(promises); }) @@ -295,18 +315,18 @@ describe('Parse Role testing', () => { const roleNames = result.roleNames; if (id == admin.id) { expect(roleNames.length).toBe(2); - expect(roleNames.indexOf('Moderator')).not.toBe(-1); - expect(roleNames.indexOf('ContentManager')).not.toBe(-1); + expect(roleNames.indexOf("Moderator")).not.toBe(-1); + expect(roleNames.indexOf("ContentManager")).not.toBe(-1); } else if (id == moderator.id) { expect(roleNames.length).toBe(1); - expect(roleNames.indexOf('ContentManager')).toBe(0); + expect(roleNames.indexOf("ContentManager")).toBe(0); } else if (id == contentManager.id) { expect(roleNames.length).toBe(0); } else if (id == superModerator.id) { expect(roleNames.length).toBe(3); - expect(roleNames.indexOf('Moderator')).not.toBe(-1); - expect(roleNames.indexOf('ContentManager')).not.toBe(-1); - expect(roleNames.indexOf('SuperContentManager')).not.toBe(-1); + expect(roleNames.indexOf("Moderator")).not.toBe(-1); + expect(roleNames.indexOf("ContentManager")).not.toBe(-1); + expect(roleNames.indexOf("SuperContentManager")).not.toBe(-1); } }); done(); @@ -316,31 +336,31 @@ describe('Parse Role testing', () => { }); }); - it('can create role and query empty users', done => { + it("can create role and query empty users", done => { const roleACL = new Parse.ACL(); roleACL.setPublicReadAccess(true); - const role = new Parse.Role('subscribers', roleACL); + const role = new Parse.Role("subscribers", roleACL); role.save({}, { useMasterKey: true }).then( () => { - const query = role.relation('users').query(); + const query = role.relation("users").query(); query.find({ useMasterKey: true }).then( () => { done(); }, () => { - fail('should not have errors'); + fail("should not have errors"); done(); } ); }, () => { - fail('should not have errored'); + fail("should not have errored"); } ); }); // Based on various scenarios described in issues #827 and #683, - it('should properly handle role permissions on objects', done => { + it("should properly handle role permissions on objects", done => { let user, user2, user3; let role, role2, role3; let obj, obj2; @@ -353,94 +373,94 @@ describe('Parse Role testing', () => { .then(x => { user = x; user2 = new Parse.User(); - return user2.save({ username: 'user2', password: 'omgbbq' }); + return user2.save({ username: "user2", password: "omgbbq" }); }) .then(() => { user3 = new Parse.User(); - return user3.save({ username: 'user3', password: 'omgbbq' }); + return user3.save({ username: "user3", password: "omgbbq" }); }) .then(() => { - role = new Parse.Role('Admin', prACL); + role = new Parse.Role("Admin", prACL); role.getUsers().add(user); return role.save({}, { useMasterKey: true }); }) .then(() => { adminACL = new Parse.ACL(); - adminACL.setRoleReadAccess('Admin', true); - adminACL.setRoleWriteAccess('Admin', true); + adminACL.setRoleReadAccess("Admin", true); + adminACL.setRoleWriteAccess("Admin", true); - role2 = new Parse.Role('Super', prACL); + role2 = new Parse.Role("Super", prACL); role2.getUsers().add(user2); return role2.save({}, { useMasterKey: true }); }) .then(() => { superACL = new Parse.ACL(); - superACL.setRoleReadAccess('Super', true); - superACL.setRoleWriteAccess('Super', true); + superACL.setRoleReadAccess("Super", true); + superACL.setRoleWriteAccess("Super", true); role.getRoles().add(role2); return role.save({}, { useMasterKey: true }); }) .then(() => { - role3 = new Parse.Role('Customer', prACL); + role3 = new Parse.Role("Customer", prACL); role3.getUsers().add(user3); role3.getRoles().add(role); return role3.save({}, { useMasterKey: true }); }) .then(() => { customerACL = new Parse.ACL(); - customerACL.setRoleReadAccess('Customer', true); - customerACL.setRoleWriteAccess('Customer', true); + customerACL.setRoleReadAccess("Customer", true); + customerACL.setRoleWriteAccess("Customer", true); - const query = new Parse.Query('_Role'); + const query = new Parse.Query("_Role"); return query.find({ useMasterKey: true }); }) .then(x => { expect(x.length).toEqual(3); - obj = new Parse.Object('TestObjectRoles'); - obj.set('ACL', customerACL); + obj = new Parse.Object("TestObjectRoles"); + obj.set("ACL", customerACL); return obj.save(null, { useMasterKey: true }); }) .then(() => { // Above, the Admin role was added to the Customer role. // An object secured by the Customer ACL should be able to be edited by the Admin user. - obj.set('changedByAdmin', true); + obj.set("changedByAdmin", true); return obj.save(null, { sessionToken: user.getSessionToken() }); }) .then( () => { - obj2 = new Parse.Object('TestObjectRoles'); - obj2.set('ACL', adminACL); + obj2 = new Parse.Object("TestObjectRoles"); + obj2.set("ACL", adminACL); return obj2.save(null, { useMasterKey: true }); }, () => { - fail('Admin user should have been able to save.'); + fail("Admin user should have been able to save."); done(); } ) .then(() => { // An object secured by the Admin ACL should not be able to be edited by a Customer role user. - obj2.set('changedByCustomer', true); + obj2.set("changedByCustomer", true); return obj2.save(null, { sessionToken: user3.getSessionToken() }); }) .then( () => { - fail('Customer user should not have been able to save.'); + fail("Customer user should not have been able to save."); done(); }, e => { if (e) { expect(e.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); } else { - fail('should return an error'); + fail("should return an error"); } done(); } ); }); - it('should add multiple users to a role and remove users', done => { + it("should add multiple users to a role and remove users", done => { let user, user2, user3; let role; let obj; @@ -453,15 +473,15 @@ describe('Parse Role testing', () => { .then(x => { user = x; user2 = new Parse.User(); - return user2.save({ username: 'user2', password: 'omgbbq' }); + return user2.save({ username: "user2", password: "omgbbq" }); }) .then(() => { user3 = new Parse.User(); - return user3.save({ username: 'user3', password: 'omgbbq' }); + return user3.save({ username: "user3", password: "omgbbq" }); }) .then(() => { - role = new Parse.Role('sharedRole', prACL); - const users = role.relation('users'); + role = new Parse.Role("sharedRole", prACL); + const users = role.relation("users"); users.add(user); users.add(user2); users.add(user3); @@ -469,60 +489,60 @@ describe('Parse Role testing', () => { }) .then(() => { // query for saved role and get 3 users - const query = new Parse.Query('_Role'); - query.equalTo('name', 'sharedRole'); + const query = new Parse.Query("_Role"); + query.equalTo("name", "sharedRole"); return query.find({ useMasterKey: true }); }) .then(role => { expect(role.length).toEqual(1); - const users = role[0].relation('users').query(); + const users = role[0].relation("users").query(); return users.find({ useMasterKey: true }); }) .then(users => { expect(users.length).toEqual(3); - obj = new Parse.Object('TestObjectRoles'); - obj.set('ACL', prACL); + obj = new Parse.Object("TestObjectRoles"); + obj.set("ACL", prACL); return obj.save(null, { useMasterKey: true }); }) .then(() => { // Above, the Admin role was added to the Customer role. // An object secured by the Customer ACL should be able to be edited by the Admin user. - obj.set('changedByUsers', true); + obj.set("changedByUsers", true); return obj.save(null, { sessionToken: user.getSessionToken() }); }) .then(() => { // query for saved role and get 3 users - const query = new Parse.Query('_Role'); - query.equalTo('name', 'sharedRole'); + const query = new Parse.Query("_Role"); + query.equalTo("name", "sharedRole"); return query.find({ useMasterKey: true }); }) .then(role => { expect(role.length).toEqual(1); - const users = role[0].relation('users'); + const users = role[0].relation("users"); users.remove(user); users.remove(user3); return role[0].save({}, { useMasterKey: true }); }) .then(role => { - const users = role.relation('users').query(); + const users = role.relation("users").query(); return users.find({ useMasterKey: true }); }) .then(users => { expect(users.length).toEqual(1); - expect(users[0].get('username')).toEqual('user2'); + expect(users[0].get("username")).toEqual("user2"); done(); }); }); - it('should be secure (#3835)', done => { + it("should be secure (#3835)", done => { const acl = new Parse.ACL(); acl.getPublicReadAccess(true); - const role = new Parse.Role('admin', acl); + const role = new Parse.Role("admin", acl); role .save() .then(() => { const user = new Parse.User(); - return user.signUp({ username: 'hello', password: 'world' }); + return user.signUp({ username: "hello", password: "world" }); }) .then(user => { role.getUsers().add(user); @@ -539,19 +559,19 @@ describe('Parse Role testing', () => { .catch(done.fail); }); - it('should match when matching in users relation', done => { + it("should match when matching in users relation", done => { const user = new Parse.User(); - user.save({ username: 'admin', password: 'admin' }).then(user => { + user.save({ username: "admin", password: "admin" }).then(user => { const aCL = new Parse.ACL(); aCL.setPublicReadAccess(true); aCL.setPublicWriteAccess(true); - const role = new Parse.Role('admin', aCL); - const users = role.relation('users'); + const role = new Parse.Role("admin", aCL); + const users = role.relation("users"); users.add(user); role.save({}, { useMasterKey: true }).then(() => { const query = new Parse.Query(Parse.Role); - query.equalTo('name', 'admin'); - query.equalTo('users', user); + query.equalTo("name", "admin"); + query.equalTo("users", user); query.find().then(function (roles) { expect(roles.length).toEqual(1); done(); @@ -560,43 +580,45 @@ describe('Parse Role testing', () => { }); }); - it('should not match any entry when not matching in users relation', done => { + it("should not match any entry when not matching in users relation", done => { const user = new Parse.User(); - user.save({ username: 'admin', password: 'admin' }).then(user => { + user.save({ username: "admin", password: "admin" }).then(user => { const aCL = new Parse.ACL(); aCL.setPublicReadAccess(true); aCL.setPublicWriteAccess(true); - const role = new Parse.Role('admin', aCL); - const users = role.relation('users'); + const role = new Parse.Role("admin", aCL); + const users = role.relation("users"); users.add(user); role.save({}, { useMasterKey: true }).then(() => { const otherUser = new Parse.User(); - otherUser.save({ username: 'otherUser', password: 'otherUser' }).then(otherUser => { - const query = new Parse.Query(Parse.Role); - query.equalTo('name', 'admin'); - query.equalTo('users', otherUser); - query.find().then(function (roles) { - expect(roles.length).toEqual(0); - done(); + otherUser + .save({ username: "otherUser", password: "otherUser" }) + .then(otherUser => { + const query = new Parse.Query(Parse.Role); + query.equalTo("name", "admin"); + query.equalTo("users", otherUser); + query.find().then(function (roles) { + expect(roles.length).toEqual(0); + done(); + }); }); - }); }); }); }); - it('should not match any entry when searching for null in users relation', done => { + it("should not match any entry when searching for null in users relation", done => { const user = new Parse.User(); - user.save({ username: 'admin', password: 'admin' }).then(user => { + user.save({ username: "admin", password: "admin" }).then(user => { const aCL = new Parse.ACL(); aCL.setPublicReadAccess(true); aCL.setPublicWriteAccess(true); - const role = new Parse.Role('admin', aCL); - const users = role.relation('users'); + const role = new Parse.Role("admin", aCL); + const users = role.relation("users"); users.add(user); role.save({}, { useMasterKey: true }).then(() => { const query = new Parse.Query(Parse.Role); - query.equalTo('name', 'admin'); - query.equalTo('users', null); + query.equalTo("name", "admin"); + query.equalTo("users", null); query.find().then(function (roles) { expect(roles.length).toEqual(0); done(); diff --git a/spec/ParseServer.spec.js b/spec/ParseServer.spec.js index ec12d6f7fd..87a0e16c2a 100644 --- a/spec/ParseServer.spec.js +++ b/spec/ParseServer.spec.js @@ -1,18 +1,18 @@ -'use strict'; +"use strict"; /* Tests for ParseServer.js */ -const express = require('express'); -const ParseServer = require('../lib/ParseServer').default; -const path = require('path'); -const { spawn } = require('child_process'); +const express = require("express"); +const ParseServer = require("../lib/ParseServer").default; +const path = require("path"); +const { spawn } = require("child_process"); -describe('Server Url Checks', () => { +describe("Server Url Checks", () => { let server; beforeEach(done => { if (!server) { const app = express(); - app.get('/health', function (req, res) { + app.get("/health", function (req, res) { res.json({ - status: 'ok', + status: "ok", }); }); server = app.listen(13376, undefined, done); @@ -22,19 +22,19 @@ describe('Server Url Checks', () => { }); afterAll(done => { - Parse.serverURL = 'http://localhost:8378/1'; + Parse.serverURL = "http://localhost:8378/1"; server.close(done); }); - it('validate good server url', async () => { - Parse.serverURL = 'http://localhost:13376'; + it("validate good server url", async () => { + Parse.serverURL = "http://localhost:13376"; const response = await ParseServer.verifyServerUrl(); expect(response).toBeTrue(); }); - it('mark bad server url', async () => { - spyOn(console, 'warn').and.callFake(() => {}); - Parse.serverURL = 'notavalidurl'; + it("mark bad server url", async () => { + spyOn(console, "warn").and.callFake(() => {}); + Parse.serverURL = "notavalidurl"; const response = await ParseServer.verifyServerUrl(); expect(response).not.toBeTrue(); expect(console.warn).toHaveBeenCalledWith( @@ -42,20 +42,22 @@ describe('Server Url Checks', () => { ); }); - it('does not have unhandled promise rejection in the case of load error', done => { - const parseServerProcess = spawn(path.resolve(__dirname, './support/FailingServer.js')); + it("does not have unhandled promise rejection in the case of load error", done => { + const parseServerProcess = spawn( + path.resolve(__dirname, "./support/FailingServer.js") + ); let stdout; let stderr; - parseServerProcess.stdout.on('data', data => { + parseServerProcess.stdout.on("data", data => { stdout = data.toString(); }); - parseServerProcess.stderr.on('data', data => { + parseServerProcess.stderr.on("data", data => { stderr = data.toString(); }); - parseServerProcess.on('close', async code => { + parseServerProcess.on("close", async code => { expect(code).toEqual(1); - expect(stdout).not.toContain('UnhandledPromiseRejectionWarning'); - expect(stderr).toContain('MongoServerSelectionError'); + expect(stdout).not.toContain("UnhandledPromiseRejectionWarning"); + expect(stderr).toContain("MongoServerSelectionError"); await reconfigureServer(); done(); }); diff --git a/spec/ParseServerRESTController.spec.js b/spec/ParseServerRESTController.spec.js index f35b5c7a3c..b4baa51ea5 100644 --- a/spec/ParseServerRESTController.spec.js +++ b/spec/ParseServerRESTController.spec.js @@ -1,66 +1,66 @@ const ParseServerRESTController = - require('../lib/ParseServerRESTController').ParseServerRESTController; -const ParseServer = require('../lib/ParseServer').default; -const Parse = require('parse/node').Parse; + require("../lib/ParseServerRESTController").ParseServerRESTController; +const ParseServer = require("../lib/ParseServer").default; +const Parse = require("parse/node").Parse; let RESTController; -describe('ParseServerRESTController', () => { +describe("ParseServerRESTController", () => { let createSpy; beforeEach(() => { RESTController = ParseServerRESTController( Parse.applicationId, ParseServer.promiseRouter({ appId: Parse.applicationId }) ); - createSpy = spyOn(databaseAdapter, 'createObject').and.callThrough(); + createSpy = spyOn(databaseAdapter, "createObject").and.callThrough(); }); - it('should handle a get request', async () => { - const res = await RESTController.request('GET', '/classes/MyObject'); + it("should handle a get request", async () => { + const res = await RESTController.request("GET", "/classes/MyObject"); expect(res.results.length).toBe(0); }); - it('should handle a get request with full serverURL mount path', async () => { - const res = await RESTController.request('GET', '/1/classes/MyObject'); + it("should handle a get request with full serverURL mount path", async () => { + const res = await RESTController.request("GET", "/1/classes/MyObject"); expect(res.results.length).toBe(0); }); - it('should handle a POST batch without transaction', async () => { - const res = await RESTController.request('POST', 'batch', { + it("should handle a POST batch without transaction", async () => { + const res = await RESTController.request("POST", "batch", { requests: [ { - method: 'GET', - path: '/classes/MyObject', + method: "GET", + path: "/classes/MyObject", }, { - method: 'POST', - path: '/classes/MyObject', - body: { key: 'value' }, + method: "POST", + path: "/classes/MyObject", + body: { key: "value" }, }, { - method: 'GET', - path: '/classes/MyObject', + method: "GET", + path: "/classes/MyObject", }, ], }); expect(res.length).toBe(3); }); - it('should handle a POST batch with transaction=false', async () => { - const res = await RESTController.request('POST', 'batch', { + it("should handle a POST batch with transaction=false", async () => { + const res = await RESTController.request("POST", "batch", { requests: [ { - method: 'GET', - path: '/classes/MyObject', + method: "GET", + path: "/classes/MyObject", }, { - method: 'POST', - path: '/classes/MyObject', - body: { key: 'value' }, + method: "POST", + path: "/classes/MyObject", + body: { key: "value" }, }, { - method: 'GET', - path: '/classes/MyObject', + method: "GET", + path: "/classes/MyObject", }, ], transaction: false, @@ -68,34 +68,37 @@ describe('ParseServerRESTController', () => { expect(res.length).toBe(3); }); - it('should handle response status', async () => { + it("should handle response status", async () => { const router = ParseServer.promiseRouter({ appId: Parse.applicationId }); - spyOn(router, 'tryRouteRequest').and.callThrough(); + spyOn(router, "tryRouteRequest").and.callThrough(); RESTController = ParseServerRESTController(Parse.applicationId, router); - const resp = await RESTController.request('POST', '/classes/MyObject'); - const { status, response, location } = await router.tryRouteRequest.calls.all()[0].returnValue; + const resp = await RESTController.request("POST", "/classes/MyObject"); + const { status, response, location } = + await router.tryRouteRequest.calls.all()[0].returnValue; expect(status).toBe(201); expect(response).toEqual(resp); - expect(location).toBe(`http://localhost:8378/1/classes/MyObject/${resp.objectId}`); + expect(location).toBe( + `http://localhost:8378/1/classes/MyObject/${resp.objectId}` + ); }); - it('should handle response status in batch', async () => { + it("should handle response status in batch", async () => { const router = ParseServer.promiseRouter({ appId: Parse.applicationId }); - spyOn(router, 'tryRouteRequest').and.callThrough(); + spyOn(router, "tryRouteRequest").and.callThrough(); RESTController = ParseServerRESTController(Parse.applicationId, router); const resp = await RESTController.request( - 'POST', - 'batch', + "POST", + "batch", { requests: [ { - method: 'POST', - path: '/classes/MyObject', + method: "POST", + path: "/classes/MyObject", }, { - method: 'POST', - path: '/classes/MyObject', + method: "POST", + path: "/classes/MyObject", }, ], }, @@ -111,45 +114,45 @@ describe('ParseServerRESTController', () => { expect(router.tryRouteRequest.calls.all().length).toBe(2); }); - it('properly handle existed', async done => { + it("properly handle existed", async done => { const restController = Parse.CoreManager.getRESTController(); Parse.CoreManager.setRESTController(RESTController); - Parse.Cloud.define('handleStatus', async () => { - const obj = new Parse.Object('TestObject'); + Parse.Cloud.define("handleStatus", async () => { + const obj = new Parse.Object("TestObject"); expect(obj.existed()).toBe(false); await obj.save(); expect(obj.existed()).toBe(false); - const query = new Parse.Query('TestObject'); + const query = new Parse.Query("TestObject"); const result = await query.get(obj.id); expect(result.existed()).toBe(true); Parse.CoreManager.setRESTController(restController); done(); }); - await Parse.Cloud.run('handleStatus'); + await Parse.Cloud.run("handleStatus"); }); if ( - process.env.MONGODB_TOPOLOGY === 'replicaset' || - process.env.PARSE_SERVER_TEST_DB === 'postgres' + process.env.MONGODB_TOPOLOGY === "replicaset" || + process.env.PARSE_SERVER_TEST_DB === "postgres" ) { - describe('transactions', () => { - it('should handle a batch request with transaction = true', async () => { - const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections + describe("transactions", () => { + it("should handle a batch request with transaction = true", async () => { + const myObject = new Parse.Object("MyObject"); // This is important because transaction only works on pre-existing collections await myObject.save(); await myObject.destroy(); createSpy.calls.reset(); - const response = await RESTController.request('POST', 'batch', { + const response = await RESTController.request("POST", "batch", { requests: [ { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value2' }, + method: "POST", + path: "/1/classes/MyObject", + body: { key: "value2" }, }, ], transaction: true, @@ -159,112 +162,117 @@ describe('ParseServerRESTController', () => { expect(response[0].success.createdAt).toBeDefined(); expect(response[1].success.objectId).toBeDefined(); expect(response[1].success.createdAt).toBeDefined(); - const query = new Parse.Query('MyObject'); + const query = new Parse.Query("MyObject"); const results = await query.find(); expect(createSpy.calls.count()).toBe(2); for (let i = 0; i + 1 < createSpy.calls.length; i = i + 2) { - expect(createSpy.calls.argsFor(i)[3]).toBe(createSpy.calls.argsFor(i + 1)[3]); + expect(createSpy.calls.argsFor(i)[3]).toBe( + createSpy.calls.argsFor(i + 1)[3] + ); } - expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']); + expect(results.map(result => result.get("key")).sort()).toEqual([ + "value1", + "value2", + ]); }); - it('should not save anything when one operation fails in a transaction', async () => { - const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections - await myObject.save({ key: 'stringField' }); + it("should not save anything when one operation fails in a transaction", async () => { + const myObject = new Parse.Object("MyObject"); // This is important because transaction only works on pre-existing collections + await myObject.save({ key: "stringField" }); await myObject.destroy(); createSpy.calls.reset(); try { // Saving a number to a string field should fail - await RESTController.request('POST', 'batch', { + await RESTController.request("POST", "batch", { requests: [ { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject', + method: "POST", + path: "/1/classes/MyObject", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject', + method: "POST", + path: "/1/classes/MyObject", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject', + method: "POST", + path: "/1/classes/MyObject", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject', + method: "POST", + path: "/1/classes/MyObject", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject', + method: "POST", + path: "/1/classes/MyObject", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject', + method: "POST", + path: "/1/classes/MyObject", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject', + method: "POST", + path: "/1/classes/MyObject", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject', + method: "POST", + path: "/1/classes/MyObject", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject', + method: "POST", + path: "/1/classes/MyObject", body: { key: 10 }, }, ], @@ -273,142 +281,142 @@ describe('ParseServerRESTController', () => { fail(); } catch (error) { expect(error).toBeDefined(); - const query = new Parse.Query('MyObject'); + const query = new Parse.Query("MyObject"); const results = await query.find(); expect(results.length).toBe(0); } }); - it('should generate separate session for each call', async () => { - const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections - await myObject.save({ key: 'stringField' }); + it("should generate separate session for each call", async () => { + const myObject = new Parse.Object("MyObject"); // This is important because transaction only works on pre-existing collections + await myObject.save({ key: "stringField" }); await myObject.destroy(); - const myObject2 = new Parse.Object('MyObject2'); // This is important because transaction only works on pre-existing collections - await myObject2.save({ key: 'stringField' }); + const myObject2 = new Parse.Object("MyObject2"); // This is important because transaction only works on pre-existing collections + await myObject2.save({ key: "stringField" }); await myObject2.destroy(); createSpy.calls.reset(); let myObjectCalls = 0; - Parse.Cloud.beforeSave('MyObject', async () => { + Parse.Cloud.beforeSave("MyObject", async () => { myObjectCalls++; if (myObjectCalls === 2) { try { // Saving a number to a string field should fail - await RESTController.request('POST', 'batch', { + await RESTController.request("POST", "batch", { requests: [ { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject2", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject2', + method: "POST", + path: "/1/classes/MyObject2", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject2", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject2', + method: "POST", + path: "/1/classes/MyObject2", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject2", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject2', + method: "POST", + path: "/1/classes/MyObject2", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject2", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject2', + method: "POST", + path: "/1/classes/MyObject2", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject2", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject2', + method: "POST", + path: "/1/classes/MyObject2", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject2", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject2', + method: "POST", + path: "/1/classes/MyObject2", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject2", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject2', + method: "POST", + path: "/1/classes/MyObject2", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject2", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject2', + method: "POST", + path: "/1/classes/MyObject2", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject2", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject2', + method: "POST", + path: "/1/classes/MyObject2", body: { key: 10 }, }, ], transaction: true, }); - fail('should fail'); + fail("should fail"); } catch (e) { expect(e).toBeDefined(); } } }); - const response = await RESTController.request('POST', 'batch', { + const response = await RESTController.request("POST", "batch", { requests: [ { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value2' }, + method: "POST", + path: "/1/classes/MyObject", + body: { key: "value2" }, }, ], transaction: true, @@ -420,32 +428,38 @@ describe('ParseServerRESTController', () => { expect(response[1].success.objectId).toBeDefined(); expect(response[1].success.createdAt).toBeDefined(); - await RESTController.request('POST', 'batch', { + await RESTController.request("POST", "batch", { requests: [ { - method: 'POST', - path: '/1/classes/MyObject3', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject3", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject3', - body: { key: 'value2' }, + method: "POST", + path: "/1/classes/MyObject3", + body: { key: "value2" }, }, ], }); - const query = new Parse.Query('MyObject'); + const query = new Parse.Query("MyObject"); const results = await query.find(); - expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']); + expect(results.map(result => result.get("key")).sort()).toEqual([ + "value1", + "value2", + ]); - const query2 = new Parse.Query('MyObject2'); + const query2 = new Parse.Query("MyObject2"); const results2 = await query2.find(); expect(results2.length).toEqual(0); - const query3 = new Parse.Query('MyObject3'); + const query3 = new Parse.Query("MyObject3"); const results3 = await query3.find(); - expect(results3.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']); + expect(results3.map(result => result.get("key")).sort()).toEqual([ + "value1", + "value2", + ]); expect(createSpy.calls.count() >= 13).toEqual(true); let transactionalSession; @@ -456,7 +470,7 @@ describe('ParseServerRESTController', () => { for (let i = 0; i < createSpy.calls.count(); i++) { const args = createSpy.calls.argsFor(i); switch (args[0]) { - case 'MyObject': + case "MyObject": myObjectDBCalls++; if (!transactionalSession || (myObjectDBCalls - 1) % 2 === 0) { transactionalSession = args[3]; @@ -467,7 +481,7 @@ describe('ParseServerRESTController', () => { expect(transactionalSession2).not.toBe(args[3]); } break; - case 'MyObject2': + case "MyObject2": myObject2DBCalls++; if (!transactionalSession2 || (myObject2DBCalls - 1) % 9 === 0) { transactionalSession2 = args[3]; @@ -478,7 +492,7 @@ describe('ParseServerRESTController', () => { expect(transactionalSession).not.toBe(args[3]); } break; - case 'MyObject3': + case "MyObject3": myObject3DBCalls++; expect(args[3]).toEqual(null); break; @@ -494,121 +508,130 @@ describe('ParseServerRESTController', () => { }); } - it('should handle a POST request', async () => { - await RESTController.request('POST', '/classes/MyObject', { key: 'value' }); - const res = await RESTController.request('GET', '/classes/MyObject'); + it("should handle a POST request", async () => { + await RESTController.request("POST", "/classes/MyObject", { key: "value" }); + const res = await RESTController.request("GET", "/classes/MyObject"); expect(res.results.length).toBe(1); - expect(res.results[0].key).toEqual('value'); + expect(res.results[0].key).toEqual("value"); }); - it('should handle a POST request with context', async () => { - Parse.Cloud.beforeSave('MyObject', req => { - expect(req.context.a).toEqual('a'); + it("should handle a POST request with context", async () => { + Parse.Cloud.beforeSave("MyObject", req => { + expect(req.context.a).toEqual("a"); }); - Parse.Cloud.afterSave('MyObject', req => { - expect(req.context.a).toEqual('a'); + Parse.Cloud.afterSave("MyObject", req => { + expect(req.context.a).toEqual("a"); }); await RESTController.request( - 'POST', - '/classes/MyObject', - { key: 'value' }, - { context: { a: 'a' } } + "POST", + "/classes/MyObject", + { key: "value" }, + { context: { a: "a" } } ); }); - it('ensures sessionTokens are properly handled', async () => { - const user = await Parse.User.signUp('user', 'pass'); + it("ensures sessionTokens are properly handled", async () => { + const user = await Parse.User.signUp("user", "pass"); const sessionToken = user.getSessionToken(); - const res = await RESTController.request('GET', '/users/me', undefined, { + const res = await RESTController.request("GET", "/users/me", undefined, { sessionToken, }); // Result is in JSON format expect(res.objectId).toEqual(user.id); }); - it('ensures masterKey is properly handled', async () => { - const user = await Parse.User.signUp('user', 'pass'); + it("ensures masterKey is properly handled", async () => { + const user = await Parse.User.signUp("user", "pass"); const userId = user.id; await Parse.User.logOut(); - const res = await RESTController.request('GET', '/classes/_User', undefined, { - useMasterKey: true, - }); + const res = await RESTController.request( + "GET", + "/classes/_User", + undefined, + { + useMasterKey: true, + } + ); expect(res.results.length).toBe(1); expect(res.results[0].objectId).toEqual(userId); }); - it('ensures no user is created when passing an empty username', async () => { + it("ensures no user is created when passing an empty username", async () => { try { - await RESTController.request('POST', '/classes/_User', { - username: '', - password: 'world', + await RESTController.request("POST", "/classes/_User", { + username: "", + password: "world", }); - fail('Success callback should not be called when passing an empty username.'); + fail( + "Success callback should not be called when passing an empty username." + ); } catch (err) { expect(err.code).toBe(Parse.Error.USERNAME_MISSING); - expect(err.message).toBe('bad or missing username'); + expect(err.message).toBe("bad or missing username"); } }); - it('ensures no user is created when passing an empty password', async () => { + it("ensures no user is created when passing an empty password", async () => { try { - await RESTController.request('POST', '/classes/_User', { - username: 'hello', - password: '', + await RESTController.request("POST", "/classes/_User", { + username: "hello", + password: "", }); - fail('Success callback should not be called when passing an empty password.'); + fail( + "Success callback should not be called when passing an empty password." + ); } catch (err) { expect(err.code).toBe(Parse.Error.PASSWORD_MISSING); - expect(err.message).toBe('password is required'); + expect(err.message).toBe("password is required"); } }); - it('ensures no session token is created on creating users', async () => { - const user = await RESTController.request('POST', '/classes/_User', { - username: 'hello', - password: 'world', + it("ensures no session token is created on creating users", async () => { + const user = await RESTController.request("POST", "/classes/_User", { + username: "hello", + password: "world", }); expect(user.sessionToken).toBeUndefined(); - const query = new Parse.Query('_Session'); + const query = new Parse.Query("_Session"); const sessions = await query.find({ useMasterKey: true }); expect(sessions.length).toBe(0); }); - it('ensures a session token is created when passing installationId != cloud', async () => { + it("ensures a session token is created when passing installationId != cloud", async () => { const user = await RESTController.request( - 'POST', - '/classes/_User', - { username: 'hello', password: 'world' }, - { installationId: 'my-installation' } + "POST", + "/classes/_User", + { username: "hello", password: "world" }, + { installationId: "my-installation" } ); expect(user.sessionToken).not.toBeUndefined(); - const query = new Parse.Query('_Session'); + const query = new Parse.Query("_Session"); const sessions = await query.find({ useMasterKey: true }); expect(sessions.length).toBe(1); - expect(sessions[0].get('installationId')).toBe('my-installation'); + expect(sessions[0].get("installationId")).toBe("my-installation"); }); - it('ensures logIn is saved with installationId', async () => { - const installationId = 'installation123'; + it("ensures logIn is saved with installationId", async () => { + const installationId = "installation123"; const user = await RESTController.request( - 'POST', - '/classes/_User', - { username: 'hello', password: 'world' }, + "POST", + "/classes/_User", + { username: "hello", password: "world" }, { installationId } ); expect(user.sessionToken).not.toBeUndefined(); - const query = new Parse.Query('_Session'); + const query = new Parse.Query("_Session"); let sessions = await query.find({ useMasterKey: true }); expect(sessions.length).toBe(1); - expect(sessions[0].get('installationId')).toBe(installationId); - expect(sessions[0].get('sessionToken')).toBe(user.sessionToken); + expect(sessions[0].get("installationId")).toBe(installationId); + expect(sessions[0].get("sessionToken")).toBe(user.sessionToken); const loggedUser = await RESTController.request( - 'POST', - '/login', - { username: 'hello', password: 'world' }, + "POST", + "/login", + { username: "hello", password: "world" }, { installationId } ); expect(loggedUser.sessionToken).not.toBeUndefined(); @@ -616,55 +639,55 @@ describe('ParseServerRESTController', () => { // Should clean up old sessions with this installationId expect(sessions.length).toBe(1); - expect(sessions[0].get('installationId')).toBe(installationId); - expect(sessions[0].get('sessionToken')).toBe(loggedUser.sessionToken); + expect(sessions[0].get("installationId")).toBe(installationId); + expect(sessions[0].get("sessionToken")).toBe(loggedUser.sessionToken); }); - it('returns a statusId when running jobs', async () => { - Parse.Cloud.job('CloudJob', () => { - return 'Cloud job completed'; + it("returns a statusId when running jobs", async () => { + Parse.Cloud.job("CloudJob", () => { + return "Cloud job completed"; }); const res = await RESTController.request( - 'POST', - '/jobs/CloudJob', + "POST", + "/jobs/CloudJob", {}, { useMasterKey: true, returnStatus: true } ); - const jobStatusId = res._headers['X-Parse-Job-Status-Id']; + const jobStatusId = res._headers["X-Parse-Job-Status-Id"]; expect(jobStatusId).toBeDefined(); const result = await Parse.Cloud.getJobStatus(jobStatusId); expect(result.id).toBe(jobStatusId); }); - it('returns a statusId when running push notifications', async () => { + it("returns a statusId when running push notifications", async () => { const payload = { - data: { alert: 'We return status!' }, - where: { deviceType: 'ios' }, + data: { alert: "We return status!" }, + where: { deviceType: "ios" }, }; - const res = await RESTController.request('POST', '/push', payload, { + const res = await RESTController.request("POST", "/push", payload, { useMasterKey: true, returnStatus: true, }); - const pushStatusId = res._headers['X-Parse-Push-Status-Id']; + const pushStatusId = res._headers["X-Parse-Push-Status-Id"]; expect(pushStatusId).toBeDefined(); const result = await Parse.Push.getPushStatus(pushStatusId); expect(result.id).toBe(pushStatusId); }); - it('returns a statusId when running batch push notifications', async () => { + it("returns a statusId when running batch push notifications", async () => { const payload = { - data: { alert: 'We return status!' }, - where: { deviceType: 'ios' }, + data: { alert: "We return status!" }, + where: { deviceType: "ios" }, }; const res = await RESTController.request( - 'POST', - 'batch', + "POST", + "batch", { requests: [ { - method: 'POST', - path: '/push', + method: "POST", + path: "/push", body: payload, }, ], @@ -674,7 +697,7 @@ describe('ParseServerRESTController', () => { returnStatus: true, } ); - const pushStatusId = res[0]._headers['X-Parse-Push-Status-Id']; + const pushStatusId = res[0]._headers["X-Parse-Push-Status-Id"]; expect(pushStatusId).toBeDefined(); const result = await Parse.Push.getPushStatus(pushStatusId); diff --git a/spec/ParseSession.spec.js b/spec/ParseSession.spec.js index aca4c07263..43bb0bb79a 100644 --- a/spec/ParseSession.spec.js +++ b/spec/ParseSession.spec.js @@ -2,8 +2,8 @@ // Tests behavior of Parse Sessions // -'use strict'; -const request = require('../lib/request'); +"use strict"; +const request = require("../lib/request"); function setupTestUsers() { const acl = new Parse.ACL(); @@ -12,13 +12,13 @@ function setupTestUsers() { const user2 = new Parse.User(); const user3 = new Parse.User(); - user1.set('username', 'testuser_1'); - user2.set('username', 'testuser_2'); - user3.set('username', 'testuser_3'); + user1.set("username", "testuser_1"); + user2.set("username", "testuser_2"); + user3.set("username", "testuser_3"); - user1.set('password', 'password'); - user2.set('password', 'password'); - user3.set('password', 'password'); + user1.set("password", "password"); + user2.set("password", "password"); + user3.set("password", "password"); user1.setACL(acl); user2.setACL(acl); @@ -34,24 +34,24 @@ function setupTestUsers() { }); } -describe('Parse.Session', () => { +describe("Parse.Session", () => { // multiple sessions with masterKey + sessionToken - it('should retain original sessionTokens with masterKey & sessionToken set', done => { + it("should retain original sessionTokens with masterKey & sessionToken set", done => { setupTestUsers() .then(user => { const query = new Parse.Query(Parse.Session); return query.find({ useMasterKey: true, - sessionToken: user.get('sessionToken'), + sessionToken: user.get("sessionToken"), }); }) .then(results => { const foundKeys = []; expect(results.length).toBe(3); for (const key in results) { - const sessionToken = results[key].get('sessionToken'); + const sessionToken = results[key].get("sessionToken"); if (foundKeys[sessionToken]) { - fail('Duplicate session token present in response'); + fail("Duplicate session token present in response"); break; } foundKeys[sessionToken] = 1; @@ -64,11 +64,11 @@ describe('Parse.Session', () => { }); // single session returned, with just one sessionToken - it('should retain original sessionTokens with just sessionToken set', done => { + it("should retain original sessionTokens with just sessionToken set", done => { let knownSessionToken; setupTestUsers() .then(user => { - knownSessionToken = user.get('sessionToken'); + knownSessionToken = user.get("sessionToken"); const query = new Parse.Query(Parse.Session); return query.find({ sessionToken: knownSessionToken, @@ -76,7 +76,7 @@ describe('Parse.Session', () => { }) .then(results => { expect(results.length).toBe(1); - const sessionToken = results[0].get('sessionToken'); + const sessionToken = results[0].get("sessionToken"); expect(sessionToken).toBe(knownSessionToken); done(); }) @@ -86,22 +86,22 @@ describe('Parse.Session', () => { }); // multiple users with masterKey + sessionToken - it('token on users should retain original sessionTokens with masterKey & sessionToken set', done => { + it("token on users should retain original sessionTokens with masterKey & sessionToken set", done => { setupTestUsers() .then(user => { const query = new Parse.Query(Parse.User); return query.find({ useMasterKey: true, - sessionToken: user.get('sessionToken'), + sessionToken: user.get("sessionToken"), }); }) .then(results => { const foundKeys = []; expect(results.length).toBe(3); for (const key in results) { - const sessionToken = results[key].get('sessionToken'); + const sessionToken = results[key].get("sessionToken"); if (foundKeys[sessionToken] && sessionToken !== undefined) { - fail('Duplicate session token present in response'); + fail("Duplicate session token present in response"); break; } foundKeys[sessionToken] = 1; @@ -114,11 +114,11 @@ describe('Parse.Session', () => { }); // multiple users with just sessionToken - it('token on users should retain original sessionTokens with just sessionToken set', done => { + it("token on users should retain original sessionTokens with just sessionToken set", done => { let knownSessionToken; setupTestUsers() .then(user => { - knownSessionToken = user.get('sessionToken'); + knownSessionToken = user.get("sessionToken"); const query = new Parse.Query(Parse.User); return query.find({ sessionToken: knownSessionToken, @@ -128,9 +128,9 @@ describe('Parse.Session', () => { const foundKeys = []; expect(results.length).toBe(3); for (const key in results) { - const sessionToken = results[key].get('sessionToken'); + const sessionToken = results[key].get("sessionToken"); if (foundKeys[sessionToken] && sessionToken !== undefined) { - fail('Duplicate session token present in response'); + fail("Duplicate session token present in response"); break; } foundKeys[sessionToken] = 1; @@ -143,30 +143,32 @@ describe('Parse.Session', () => { }); }); - it('cannot edit session with known ID', async () => { + it("cannot edit session with known ID", async () => { await setupTestUsers(); - const [first, second] = await new Parse.Query(Parse.Session).find({ useMasterKey: true }); + const [first, second] = await new Parse.Query(Parse.Session).find({ + useMasterKey: true, + }); const headers = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Rest-API-Key': 'rest', - 'X-Parse-Session-Token': second.get('sessionToken'), - 'Content-Type': 'application/json', + "X-Parse-Application-Id": "test", + "X-Parse-Rest-API-Key": "rest", + "X-Parse-Session-Token": second.get("sessionToken"), + "Content-Type": "application/json", }; - const firstUser = first.get('user').id; - const secondUser = second.get('user').id; + const firstUser = first.get("user").id; + const secondUser = second.get("user").id; const e = await request({ - method: 'PUT', + method: "PUT", headers, url: `http://localhost:8378/1/sessions/${first.id}`, body: JSON.stringify({ - foo: 'bar', - user: { __type: 'Pointer', className: '_User', objectId: secondUser }, + foo: "bar", + user: { __type: "Pointer", className: "_User", objectId: secondUser }, }), }).catch(e => e.data); expect(e.code).toBe(Parse.Error.OBJECT_NOT_FOUND); - expect(e.error).toBe('Object not found.'); + expect(e.error).toBe("Object not found."); await Parse.Object.fetchAll([first, second], { useMasterKey: true }); - expect(first.get('user').id).toBe(firstUser); - expect(second.get('user').id).toBe(secondUser); + expect(first.get("user").id).toBe(firstUser); + expect(second.get("user").id).toBe(secondUser); }); }); diff --git a/spec/ParseUser.spec.js b/spec/ParseUser.spec.js index e43e018e05..d25af0eb82 100644 --- a/spec/ParseUser.spec.js +++ b/spec/ParseUser.spec.js @@ -5,57 +5,66 @@ // Tests that involve revocable sessions. // Tests that involve sending password reset emails. -'use strict'; +"use strict"; -const MongoStorageAdapter = require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter').default; -const request = require('../lib/request'); -const passwordCrypto = require('../lib/password'); -const Config = require('../lib/Config'); -const cryptoUtils = require('../lib/cryptoUtils'); +const MongoStorageAdapter = + require("../lib/Adapters/Storage/Mongo/MongoStorageAdapter").default; +const request = require("../lib/request"); +const passwordCrypto = require("../lib/password"); +const Config = require("../lib/Config"); +const cryptoUtils = require("../lib/cryptoUtils"); -describe('allowExpiredAuthDataToken option', () => { - it('should accept true value', async () => { +describe("allowExpiredAuthDataToken option", () => { + it("should accept true value", async () => { await reconfigureServer({ allowExpiredAuthDataToken: true }); - expect(Config.get(Parse.applicationId).allowExpiredAuthDataToken).toBe(true); + expect(Config.get(Parse.applicationId).allowExpiredAuthDataToken).toBe( + true + ); }); - it('should accept false value', async () => { + it("should accept false value", async () => { await reconfigureServer({ allowExpiredAuthDataToken: false }); - expect(Config.get(Parse.applicationId).allowExpiredAuthDataToken).toBe(false); + expect(Config.get(Parse.applicationId).allowExpiredAuthDataToken).toBe( + false + ); }); - it('should default false', async () => { + it("should default false", async () => { await reconfigureServer({}); - expect(Config.get(Parse.applicationId).allowExpiredAuthDataToken).toBe(false); + expect(Config.get(Parse.applicationId).allowExpiredAuthDataToken).toBe( + false + ); }); - it('should enforce boolean values', async () => { - const options = [[], 'a', '', 0, 1, {}, 'true', 'false']; + it("should enforce boolean values", async () => { + const options = [[], "a", "", 0, 1, {}, "true", "false"]; for (const option of options) { - await expectAsync(reconfigureServer({ allowExpiredAuthDataToken: option })).toBeRejected(); + await expectAsync( + reconfigureServer({ allowExpiredAuthDataToken: option }) + ).toBeRejected(); } }); }); -describe('Parse.User testing', () => { - it('user sign up class method', async done => { - const user = await Parse.User.signUp('asdf', 'zxcv'); +describe("Parse.User testing", () => { + it("user sign up class method", async done => { + const user = await Parse.User.signUp("asdf", "zxcv"); ok(user.getSessionToken()); done(); }); - it('user sign up instance method', async () => { + it("user sign up instance method", async () => { const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('zxcv'); + user.setPassword("asdf"); + user.setUsername("zxcv"); await user.signUp(); ok(user.getSessionToken()); }); - it('user login wrong username', async done => { - await Parse.User.signUp('asdf', 'zxcv'); + it("user login wrong username", async done => { + await Parse.User.signUp("asdf", "zxcv"); try { - await Parse.User.logIn('non_existent_user', 'asdf3'); + await Parse.User.logIn("non_existent_user", "asdf3"); done.fail(); } catch (e) { expect(e.code).toBe(Parse.Error.OBJECT_NOT_FOUND); @@ -63,10 +72,10 @@ describe('Parse.User testing', () => { } }); - it('user login wrong password', async done => { - await Parse.User.signUp('asdf', 'zxcv'); + it("user login wrong password", async done => { + await Parse.User.signUp("asdf", "zxcv"); try { - await Parse.User.logIn('asdf', 'asdfWrong'); + await Parse.User.logIn("asdf", "asdfWrong"); done.fail(); } catch (e) { expect(e.code).toBe(Parse.Error.OBJECT_NOT_FOUND); @@ -74,9 +83,9 @@ describe('Parse.User testing', () => { } }); - it('user login with context', async () => { + it("user login with context", async () => { let hit = 0; - const context = { foo: 'bar' }; + const context = { foo: "bar" }; Parse.Cloud.beforeLogin(req => { expect(req.context).toEqual(context); hit++; @@ -85,39 +94,39 @@ describe('Parse.User testing', () => { expect(req.context).toEqual(context); hit++; }); - await Parse.User.signUp('asdf', 'zxcv'); + await Parse.User.signUp("asdf", "zxcv"); await request({ - method: 'POST', - url: 'http://localhost:8378/1/login', + method: "POST", + url: "http://localhost:8378/1/login", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Cloud-Context': JSON.stringify(context), - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", + "X-Parse-Cloud-Context": JSON.stringify(context), + "Content-Type": "application/json", }, body: { - _method: 'GET', - username: 'asdf', - password: 'zxcv', + _method: "GET", + username: "asdf", + password: "zxcv", }, }); expect(hit).toBe(2); }); - it('user login with non-string username with REST API', async done => { - await Parse.User.signUp('asdf', 'zxcv'); + it("user login with non-string username with REST API", async done => { + await Parse.User.signUp("asdf", "zxcv"); request({ - method: 'POST', - url: 'http://localhost:8378/1/login', + method: "POST", + url: "http://localhost:8378/1/login", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }, body: { - _method: 'GET', - username: { $regex: '^asd' }, - password: 'zxcv', + _method: "GET", + username: { $regex: "^asd" }, + password: "zxcv", }, }) .then(res => { @@ -133,20 +142,20 @@ describe('Parse.User testing', () => { }); }); - it('user login with non-string username with REST API (again)', async done => { - await Parse.User.signUp('asdf', 'zxcv'); + it("user login with non-string username with REST API (again)", async done => { + await Parse.User.signUp("asdf", "zxcv"); request({ - method: 'POST', - url: 'http://localhost:8378/1/login', + method: "POST", + url: "http://localhost:8378/1/login", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }, body: { - _method: 'GET', - username: 'asdf', - password: { $regex: '^zx' }, + _method: "GET", + username: "asdf", + password: { $regex: "^zx" }, }, }) .then(res => { @@ -162,22 +171,22 @@ describe('Parse.User testing', () => { }); }); - it('user login using POST with REST API', async done => { - await Parse.User.signUp('some_user', 'some_password'); + it("user login using POST with REST API", async done => { + await Parse.User.signUp("some_user", "some_password"); request({ - method: 'POST', - url: 'http://localhost:8378/1/login', + method: "POST", + url: "http://localhost:8378/1/login", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", }, body: { - username: 'some_user', - password: 'some_password', + username: "some_user", + password: "some_password", }, }) .then(res => { - expect(res.data.username).toBe('some_user'); + expect(res.data.username).toBe("some_user"); done(); }) .catch(err => { @@ -186,10 +195,10 @@ describe('Parse.User testing', () => { }); }); - it('user login', async done => { - await Parse.User.signUp('asdf', 'zxcv'); - const user = await Parse.User.logIn('asdf', 'zxcv'); - equal(user.get('username'), 'asdf'); + it("user login", async done => { + await Parse.User.signUp("asdf", "zxcv"); + const user = await Parse.User.logIn("asdf", "zxcv"); + equal(user.get("username"), "asdf"); const ACL = user.getACL(); expect(ACL.getReadAccess(user)).toBe(true); expect(ACL.getWriteAccess(user)).toBe(true); @@ -199,25 +208,25 @@ describe('Parse.User testing', () => { expect(Object.keys(perms).length).toBe(1); expect(perms[user.id].read).toBe(true); expect(perms[user.id].write).toBe(true); - expect(perms['*']).toBeUndefined(); + expect(perms["*"]).toBeUndefined(); done(); }); - it('should respect ACL without locking user out', done => { + it("should respect ACL without locking user out", done => { const user = new Parse.User(); const ACL = new Parse.ACL(); ACL.setPublicReadAccess(false); ACL.setPublicWriteAccess(false); - user.setUsername('asdf'); - user.setPassword('zxcv'); + user.setUsername("asdf"); + user.setPassword("zxcv"); user.setACL(ACL); user .signUp() .then(() => { - return Parse.User.logIn('asdf', 'zxcv'); + return Parse.User.logIn("asdf", "zxcv"); }) .then(user => { - equal(user.get('username'), 'asdf'); + equal(user.get("username"), "asdf"); const ACL = user.getACL(); expect(ACL.getReadAccess(user)).toBe(true); expect(ACL.getWriteAccess(user)).toBe(true); @@ -227,7 +236,7 @@ describe('Parse.User testing', () => { expect(Object.keys(perms).length).toBe(1); expect(perms[user.id].read).toBe(true); expect(perms[user.id].write).toBe(true); - expect(perms['*']).toBeUndefined(); + expect(perms["*"]).toBeUndefined(); // Try to lock out user const newACL = new Parse.ACL(); newACL.setReadAccess(user.id, false); @@ -236,10 +245,10 @@ describe('Parse.User testing', () => { return user.save(); }) .then(() => { - return Parse.User.logIn('asdf', 'zxcv'); + return Parse.User.logIn("asdf", "zxcv"); }) .then(user => { - equal(user.get('username'), 'asdf'); + equal(user.get("username"), "asdf"); const ACL = user.getACL(); expect(ACL.getReadAccess(user)).toBe(true); expect(ACL.getWriteAccess(user)).toBe(true); @@ -249,30 +258,30 @@ describe('Parse.User testing', () => { expect(Object.keys(perms).length).toBe(1); expect(perms[user.id].read).toBe(true); expect(perms[user.id].write).toBe(true); - expect(perms['*']).toBeUndefined(); + expect(perms["*"]).toBeUndefined(); done(); }) .catch(() => { - fail('Should not fail'); + fail("Should not fail"); done(); }); }); - it('should let masterKey lockout user', done => { + it("should let masterKey lockout user", done => { const user = new Parse.User(); const ACL = new Parse.ACL(); ACL.setPublicReadAccess(false); ACL.setPublicWriteAccess(false); - user.setUsername('asdf'); - user.setPassword('zxcv'); + user.setUsername("asdf"); + user.setPassword("zxcv"); user.setACL(ACL); user .signUp() .then(() => { - return Parse.User.logIn('asdf', 'zxcv'); + return Parse.User.logIn("asdf", "zxcv"); }) .then(user => { - equal(user.get('username'), 'asdf'); + equal(user.get("username"), "asdf"); // Lock the user down const ACL = new Parse.ACL(); user.setACL(ACL); @@ -280,21 +289,22 @@ describe('Parse.User testing', () => { }) .then(() => { expect(user.getACL().getPublicReadAccess()).toBe(false); - return Parse.User.logIn('asdf', 'zxcv'); + return Parse.User.logIn("asdf", "zxcv"); }) .then(done.fail) .catch(err => { - expect(err.message).toBe('Invalid username/password.'); + expect(err.message).toBe("Invalid username/password."); expect(err.code).toBe(Parse.Error.OBJECT_NOT_FOUND); done(); }); }); - it_only_db('mongo')('should let legacy users without ACL login', async () => { + it_only_db("mongo")("should let legacy users without ACL login", async () => { await reconfigureServer(); - const databaseURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase'; + const databaseURI = + "mongodb://localhost:27017/parseServerMongoAdapterTestDatabase"; const adapter = new MongoStorageAdapter({ - collectionPrefix: 'test_', + collectionPrefix: "test_", uri: databaseURI, }); await adapter.connect(); @@ -304,44 +314,45 @@ describe('Parse.User testing', () => { const user = new Parse.User(); await user.signUp({ - username: 'newUser', - password: 'password', + username: "newUser", + password: "password", }); - const collection = await adapter._adaptiveCollection('_User'); + const collection = await adapter._adaptiveCollection("_User"); await collection.insertOne({ // the hashed password is 'password' hashed - _hashed_password: '$2b$10$mJ2ca2UbCM9hlojYHZxkQe8pyEXe5YMg0nMdvP4AJBeqlTEZJ6/Uu', - _session_token: 'xxx', - email: 'xxx@a.b', - username: 'oldUser', + _hashed_password: + "$2b$10$mJ2ca2UbCM9hlojYHZxkQe8pyEXe5YMg0nMdvP4AJBeqlTEZJ6/Uu", + _session_token: "xxx", + email: "xxx@a.b", + username: "oldUser", emailVerified: true, - _email_verify_token: 'yyy', + _email_verify_token: "yyy", }); // get the 2 users const users = await collection.find(); expect(users.length).toBe(2); - const aUser = await Parse.User.logIn('oldUser', 'password'); + const aUser = await Parse.User.logIn("oldUser", "password"); expect(aUser).not.toBeUndefined(); - const newUser = await Parse.User.logIn('newUser', 'password'); + const newUser = await Parse.User.logIn("newUser", "password"); expect(newUser).not.toBeUndefined(); }); - it('should be let masterKey lock user out with authData', async () => { + it("should be let masterKey lock user out with authData", async () => { const response = await request({ - method: 'POST', - url: 'http://localhost:8378/1/classes/_User', + method: "POST", + url: "http://localhost:8378/1/classes/_User", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }, body: { - key: 'value', - authData: { anonymous: { id: '00000000-0000-0000-0000-000000000001' } }, + key: "value", + authData: { anonymous: { id: "00000000-0000-0000-0000-000000000001" } }, }, }); const body = response.data; @@ -356,17 +367,17 @@ describe('Parse.User testing', () => { await user.save(null, { useMasterKey: true }); // update the user const options = { - method: 'POST', + method: "POST", url: `http://localhost:8378/1/classes/_User/`, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }, body: { - key: 'otherValue', + key: "otherValue", authData: { - anonymous: { id: '00000000-0000-0000-0000-000000000001' }, + anonymous: { id: "00000000-0000-0000-0000-000000000001" }, }, }, }; @@ -374,18 +385,18 @@ describe('Parse.User testing', () => { expect(res.data.objectId).not.toEqual(objectId); }); - it('user login with files', done => { - const file = new Parse.File('yolo.txt', [1, 2, 3], 'text/plain'); + it("user login with files", done => { + const file = new Parse.File("yolo.txt", [1, 2, 3], "text/plain"); file .save() .then(file => { - return Parse.User.signUp('asdf', 'zxcv', { file: file }); + return Parse.User.signUp("asdf", "zxcv", { file: file }); }) .then(() => { - return Parse.User.logIn('asdf', 'zxcv'); + return Parse.User.logIn("asdf", "zxcv"); }) .then(user => { - const fileAgain = user.get('file'); + const fileAgain = user.get("file"); ok(fileAgain.name()); ok(fileAgain.url()); done(); @@ -396,14 +407,14 @@ describe('Parse.User testing', () => { }); }); - it('become sends token back', done => { + it("become sends token back", done => { let user = null; let sessionToken = null; - Parse.User.signUp('Jason', 'Parse', { code: 'red' }) + Parse.User.signUp("Jason", "Parse", { code: "red" }) .then(newUser => { user = newUser; - expect(user.get('code'), 'red'); + expect(user.get("code"), "red"); sessionToken = newUser.getSessionToken(); expect(sessionToken).toBeDefined(); @@ -412,8 +423,8 @@ describe('Parse.User testing', () => { }) .then(newUser => { expect(newUser.id).toEqual(user.id); - expect(newUser.get('username'), 'Jason'); - expect(newUser.get('code'), 'red'); + expect(newUser.get("username"), "Jason"); + expect(newUser.get("code"), "red"); expect(newUser.getSessionToken()).toEqual(sessionToken); }) .then( @@ -427,13 +438,13 @@ describe('Parse.User testing', () => { ); }); - it('become', done => { + it("become", done => { let user = null; let sessionToken = null; Promise.resolve() .then(function () { - return Parse.User.signUp('Jason', 'Parse', { code: 'red' }); + return Parse.User.signUp("Jason", "Parse", { code: "red" }); }) .then(function (newUser) { equal(Parse.User.current(), newUser); @@ -454,20 +465,23 @@ describe('Parse.User testing', () => { ok(newUser); equal(newUser.id, user.id); - equal(newUser.get('username'), 'Jason'); - equal(newUser.get('code'), 'red'); + equal(newUser.get("username"), "Jason"); + equal(newUser.get("code"), "red"); return Parse.User.logOut(); }) .then(() => { ok(!Parse.User.current()); - return Parse.User.become('somegarbage'); + return Parse.User.become("somegarbage"); }) .then( function () { // This should have failed actually. - ok(false, "Shouldn't have been able to log in with garbage session token."); + ok( + false, + "Shouldn't have been able to log in with garbage session token." + ); }, function (error) { ok(error); @@ -486,7 +500,7 @@ describe('Parse.User testing', () => { ); }); - it('should not call beforeLogin with become', async done => { + it("should not call beforeLogin with become", async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); @@ -495,19 +509,19 @@ describe('Parse.User testing', () => { hit++; }); - await Parse.User._logInWith('facebook'); + await Parse.User._logInWith("facebook"); const sessionToken = Parse.User.current().getSessionToken(); await Parse.User.become(sessionToken); expect(hit).toBe(0); done(); }); - it('cannot save non-authed user', async done => { + it("cannot save non-authed user", async done => { let user = new Parse.User(); user.set({ - password: 'asdf', - email: 'asdf@example.com', - username: 'zxcv', + password: "asdf", + email: "asdf@example.com", + username: "zxcv", }); let userAgain = await user.signUp(); equal(userAgain, user); @@ -515,34 +529,34 @@ describe('Parse.User testing', () => { const userNotAuthed = await query.get(user.id); user = new Parse.User(); user.set({ - username: 'hacker', - password: 'password', + username: "hacker", + password: "password", }); userAgain = await user.signUp(); equal(userAgain, user); - userNotAuthed.set('username', 'changed'); + userNotAuthed.set("username", "changed"); userNotAuthed.save().then(fail, err => { expect(err.code).toEqual(Parse.Error.SESSION_MISSING); done(); }); }); - it('cannot delete non-authed user', async done => { + it("cannot delete non-authed user", async done => { let user = new Parse.User(); await user.signUp({ - password: 'asdf', - email: 'asdf@example.com', - username: 'zxcv', + password: "asdf", + email: "asdf@example.com", + username: "zxcv", }); const query = new Parse.Query(Parse.User); const userNotAuthed = await query.get(user.id); user = new Parse.User(); const userAgain = await user.signUp({ - username: 'hacker', - password: 'password', + username: "hacker", + password: "password", }); equal(userAgain, user); - userNotAuthed.set('username', 'changed'); + userNotAuthed.set("username", "changed"); try { await userNotAuthed.destroy(); done.fail(); @@ -552,22 +566,22 @@ describe('Parse.User testing', () => { } }); - it('cannot saveAll with non-authed user', async done => { + it("cannot saveAll with non-authed user", async done => { let user = new Parse.User(); await user.signUp({ - password: 'asdf', - email: 'asdf@example.com', - username: 'zxcv', + password: "asdf", + email: "asdf@example.com", + username: "zxcv", }); const query = new Parse.Query(Parse.User); const userNotAuthed = await query.get(user.id); user = new Parse.User(); await user.signUp({ - username: 'hacker', - password: 'password', + username: "hacker", + password: "password", }); const userNotAuthedNotChanged = await query.get(user.id); - userNotAuthed.set('username', 'changed'); + userNotAuthed.set("username", "changed"); const object = new TestObject(); await object.save({ user: userNotAuthedNotChanged, @@ -576,9 +590,9 @@ describe('Parse.User testing', () => { await item1.save({ number: 0, }); - item1.set('number', 1); + item1.set("number", 1); const item2 = new TestObject(); - item2.set('number', 2); + item2.set("number", 2); try { await Parse.Object.saveAll([item1, item2, userNotAuthed]); done.fail(); @@ -588,11 +602,11 @@ describe('Parse.User testing', () => { } }); - it('never locks himself up', async () => { + it("never locks himself up", async () => { const user = new Parse.User(); await user.signUp({ - username: 'username', - password: 'password', + username: "username", + password: "password", }); user.setACL(new Parse.ACL()); await user.save(); @@ -603,11 +617,11 @@ describe('Parse.User testing', () => { publicReadACL.setPublicReadAccess(true); // Create an administrator role with a single admin user - const role = new Parse.Role('admin', publicReadACL); + const role = new Parse.Role("admin", publicReadACL); const admin = new Parse.User(); await admin.signUp({ - username: 'admin', - password: 'admin', + username: "admin", + password: "admin", }); role.getUsers().add(admin); await role.save(null, { useMasterKey: true }); @@ -621,16 +635,19 @@ describe('Parse.User testing', () => { await user.save({ ACL: acl }, { useMasterKey: true }); // Try to update from admin... should all work fine - await user.save({ key: 'fromAdmin' }, { sessionToken: admin.getSessionToken() }); + await user.save( + { key: "fromAdmin" }, + { sessionToken: admin.getSessionToken() } + ); await user.fetch(); - expect(user.toJSON().key).toEqual('fromAdmin'); + expect(user.toJSON().key).toEqual("fromAdmin"); // Try to save when logged out (public) let failed = false; try { // Ensure no session token is sent await Parse.User.logOut(); - await user.save({ key: 'fromPublic' }); + await user.save({ key: "fromPublic" }); } catch (e) { failed = true; expect(e.code).toBe(Parse.Error.SESSION_MISSING); @@ -641,11 +658,11 @@ describe('Parse.User testing', () => { failed = false; const anyUser = new Parse.User(); await anyUser.signUp({ - username: 'randomUser', - password: 'password', + username: "randomUser", + password: "password", }); try { - await user.save({ key: 'fromAnyUser' }); + await user.save({ key: "fromAnyUser" }); } catch (e) { failed = true; expect(e.code).toBe(Parse.Error.SESSION_MISSING); @@ -653,11 +670,11 @@ describe('Parse.User testing', () => { expect({ failed }).toEqual({ failed: true }); }); - it('current user', done => { + it("current user", done => { const user = new Parse.User(); - user.set('password', 'asdf'); - user.set('email', 'asdf@example.com'); - user.set('username', 'zxcv'); + user.set("password", "asdf"); + user.set("email", "asdf@example.com"); + user.set("username", "zxcv"); user .signUp() .then(() => { @@ -678,18 +695,18 @@ describe('Parse.User testing', () => { }); }); - it('user.isCurrent', done => { + it("user.isCurrent", done => { const user1 = new Parse.User(); const user2 = new Parse.User(); const user3 = new Parse.User(); - user1.set('username', 'a'); - user2.set('username', 'b'); - user3.set('username', 'c'); + user1.set("username", "a"); + user2.set("username", "b"); + user3.set("username", "c"); - user1.set('password', 'password'); - user2.set('password', 'password'); - user3.set('password', 'password'); + user1.set("password", "password"); + user2.set("password", "password"); + user3.set("password", "password"); user1 .signUp() @@ -709,19 +726,19 @@ describe('Parse.User testing', () => { equal(user1.isCurrent(), false); equal(user2.isCurrent(), false); equal(user3.isCurrent(), true); - return Parse.User.logIn('a', 'password'); + return Parse.User.logIn("a", "password"); }) .then(() => { equal(user1.isCurrent(), true); equal(user2.isCurrent(), false); equal(user3.isCurrent(), false); - return Parse.User.logIn('b', 'password'); + return Parse.User.logIn("b", "password"); }) .then(() => { equal(user1.isCurrent(), false); equal(user2.isCurrent(), true); equal(user3.isCurrent(), false); - return Parse.User.logIn('b', 'password'); + return Parse.User.logIn("b", "password"); }) .then(() => { equal(user1.isCurrent(), false); @@ -735,32 +752,32 @@ describe('Parse.User testing', () => { }); }); - it('user associations', async done => { + it("user associations", async done => { const child = new TestObject(); await child.save(); const user = new Parse.User(); - user.set('password', 'asdf'); - user.set('email', 'asdf@example.com'); - user.set('username', 'zxcv'); - user.set('child', child); + user.set("password", "asdf"); + user.set("email", "asdf@example.com"); + user.set("username", "zxcv"); + user.set("child", child); await user.signUp(); const object = new TestObject(); - object.set('user', user); + object.set("user", user); await object.save(); const query = new Parse.Query(TestObject); const objectAgain = await query.get(object.id); - const userAgain = objectAgain.get('user'); + const userAgain = objectAgain.get("user"); await userAgain.fetch(); equal(user.id, userAgain.id); - equal(userAgain.get('child').id, child.id); + equal(userAgain.get("child").id, child.id); done(); }); - it('user queries', async done => { + it("user queries", async done => { const user = new Parse.User(); - user.set('password', 'asdf'); - user.set('email', 'asdf@example.com'); - user.set('username', 'zxcv'); + user.set("password", "asdf"); + user.set("email", "asdf@example.com"); + user.set("username", "zxcv"); await user.signUp(); const query = new Parse.Query(Parse.User); const userAgain = await query.get(user.id); @@ -768,7 +785,7 @@ describe('Parse.User testing', () => { const users = await query.find(); equal(users.length, 1); equal(users[0].id, user.id); - ok(userAgain.get('email'), 'asdf@example.com'); + ok(userAgain.get("email"), "asdf@example.com"); done(); }); @@ -785,30 +802,30 @@ describe('Parse.User testing', () => { return promise.then(optionsOrCallback); } - it('contained in user array queries', async done => { + it("contained in user array queries", async done => { const USERS = 4; const MESSAGES = 5; // Make a list of users. const userList = range(USERS).map(function (i) { const user = new Parse.User(); - user.set('password', 'user_num_' + i); - user.set('email', 'user_num_' + i + '@example.com'); - user.set('username', 'xinglblog_num_' + i); + user.set("password", "user_num_" + i); + user.set("email", "user_num_" + i + "@example.com"); + user.set("username", "xinglblog_num_" + i); return user; }); signUpAll(userList, async function (users) { // Make a list of messages. if (!users || users.length != USERS) { - fail('signupAll failed'); + fail("signupAll failed"); done(); return; } const messageList = range(MESSAGES).map(function (i) { const message = new TestObject(); - message.set('to', users[(i + 1) % USERS]); - message.set('from', users[i % USERS]); + message.set("to", users[(i + 1) % USERS]); + message.set("from", users[i % USERS]); return message; }); @@ -818,7 +835,7 @@ describe('Parse.User testing', () => { // Assemble an "in" list. const inList = [users[0], users[3], users[3]]; // Intentional dupe const query = new Parse.Query(TestObject); - query.containedIn('from', inList); + query.containedIn("from", inList); const results = await query.find(); equal(results.length, 3); done(); @@ -828,27 +845,27 @@ describe('Parse.User testing', () => { it("saving a user signs them up but doesn't log them in", async done => { const user = new Parse.User(); await user.save({ - password: 'asdf', - email: 'asdf@example.com', - username: 'zxcv', + password: "asdf", + email: "asdf@example.com", + username: "zxcv", }); equal(Parse.User.current(), null); done(); }); - it('user updates', async done => { + it("user updates", async done => { const user = new Parse.User(); await user.signUp({ - password: 'asdf', - email: 'asdf@example.com', - username: 'zxcv', + password: "asdf", + email: "asdf@example.com", + username: "zxcv", }); - user.set('username', 'test'); + user.set("username", "test"); await user.save(); equal(Object.keys(user.attributes).length, 5); - ok(user.attributes['username']); - ok(user.attributes['email']); + ok(user.attributes["username"]); + ok(user.attributes["email"]); await user.destroy(); const query = new Parse.Query(Parse.User); try { @@ -861,14 +878,14 @@ describe('Parse.User testing', () => { } }); - it('count users', async done => { + it("count users", async done => { const james = new Parse.User(); - james.set('username', 'james'); - james.set('password', 'mypass'); + james.set("username", "james"); + james.set("password", "mypass"); await james.signUp(); const kevin = new Parse.User(); - kevin.set('username', 'kevin'); - kevin.set('password', 'mypass'); + kevin.set("username", "kevin"); + kevin.set("password", "mypass"); await kevin.signUp(); const query = new Parse.Query(Parse.User); const count = await query.find({ useMasterKey: true }); @@ -876,82 +893,86 @@ describe('Parse.User testing', () => { done(); }); - it('user sign up with container class', async done => { - await Parse.User.signUp('ilya', 'mypass', { array: ['hello'] }); + it("user sign up with container class", async done => { + await Parse.User.signUp("ilya", "mypass", { array: ["hello"] }); done(); }); - it('user modified while saving', async done => { + it("user modified while saving", async done => { Parse.Object.disableSingleInstance(); await reconfigureServer(); const user = new Parse.User(); - user.set('username', 'alice'); - user.set('password', 'password'); + user.set("username", "alice"); + user.set("password", "password"); user.signUp().then(function (userAgain) { - equal(userAgain.get('username'), 'bob'); - ok(userAgain.dirty('username')); + equal(userAgain.get("username"), "bob"); + ok(userAgain.dirty("username")); const query = new Parse.Query(Parse.User); query.get(user.id).then(freshUser => { equal(freshUser.id, user.id); - equal(freshUser.get('username'), 'alice'); + equal(freshUser.get("username"), "alice"); done(); }); }); // Jump a frame so the signup call is properly sent // This is due to the fact that now, we use real promises process.nextTick(() => { - ok(user.set('username', 'bob')); + ok(user.set("username", "bob")); }); }); - it('user modified while saving with unsaved child', done => { + it("user modified while saving with unsaved child", done => { Parse.Object.disableSingleInstance(); const user = new Parse.User(); - user.set('username', 'alice'); - user.set('password', 'password'); - user.set('child', new TestObject()); + user.set("username", "alice"); + user.set("password", "password"); + user.set("child", new TestObject()); user.signUp().then(userAgain => { - equal(userAgain.get('username'), 'bob'); + equal(userAgain.get("username"), "bob"); // Should be dirty, but it depends on batch support. // ok(userAgain.dirty("username")); const query = new Parse.Query(Parse.User); query.get(user.id).then(freshUser => { equal(freshUser.id, user.id); // Should be alice, but it depends on batch support. - equal(freshUser.get('username'), 'bob'); + equal(freshUser.get("username"), "bob"); done(); }); }); - ok(user.set('username', 'bob')); + ok(user.set("username", "bob")); }); - it('user loaded from localStorage from signup', async done => { - const alice = await Parse.User.signUp('alice', 'password'); - ok(alice.id, 'Alice should have an objectId'); - ok(alice.getSessionToken(), 'Alice should have a session token'); - equal(alice.get('password'), undefined, 'Alice should not have a password'); + it("user loaded from localStorage from signup", async done => { + const alice = await Parse.User.signUp("alice", "password"); + ok(alice.id, "Alice should have an objectId"); + ok(alice.getSessionToken(), "Alice should have a session token"); + equal(alice.get("password"), undefined, "Alice should not have a password"); // Simulate the environment getting reset. Parse.User._currentUser = null; Parse.User._currentUserMatchesDisk = false; const aliceAgain = Parse.User.current(); - equal(aliceAgain.get('username'), 'alice'); - equal(aliceAgain.id, alice.id, 'currentUser should have objectId'); - ok(aliceAgain.getSessionToken(), 'currentUser should have a sessionToken'); - equal(alice.get('password'), undefined, 'currentUser should not have password'); + equal(aliceAgain.get("username"), "alice"); + equal(aliceAgain.id, alice.id, "currentUser should have objectId"); + ok(aliceAgain.getSessionToken(), "currentUser should have a sessionToken"); + equal( + alice.get("password"), + undefined, + "currentUser should not have password" + ); done(); }); - it('user loaded from localStorage from login', done => { + it("user loaded from localStorage from login", done => { let id; - Parse.User.signUp('alice', 'password') + Parse.User.signUp("alice", "password") .then(alice => { id = alice.id; return Parse.User.logOut(); }) .then(() => { - return Parse.User.logIn('alice', 'password'); + return Parse.User.logIn("alice", "password"); }) .then(() => { // Force the current user to read from disk @@ -959,23 +980,30 @@ describe('Parse.User testing', () => { delete Parse.User._currentUserMatchesDisk; const userFromDisk = Parse.User.current(); - equal(userFromDisk.get('password'), undefined, 'password should not be in attributes'); - equal(userFromDisk.id, id, 'id should be set'); - ok(userFromDisk.getSessionToken(), 'currentUser should have a sessionToken'); + equal( + userFromDisk.get("password"), + undefined, + "password should not be in attributes" + ); + equal(userFromDisk.id, id, "id should be set"); + ok( + userFromDisk.getSessionToken(), + "currentUser should have a sessionToken" + ); done(); }); }); - it('saving user after browser refresh', done => { + it("saving user after browser refresh", done => { let id; - Parse.User.signUp('alice', 'password', null) + Parse.User.signUp("alice", "password", null) .then(function (alice) { id = alice.id; return Parse.User.logOut(); }) .then(() => { - return Parse.User.logIn('alice', 'password'); + return Parse.User.logIn("alice", "password"); }) .then(function () { // Simulate browser refresh by force-reloading user from localStorage @@ -991,37 +1019,44 @@ describe('Parse.User testing', () => { equal( userInMemory.getUsername(), - 'alice', - 'saving user should not remove existing fields' + "alice", + "saving user should not remove existing fields" ); - equal(userInMemory.get('some_field'), 1, 'saving user should save specified field'); + equal( + userInMemory.get("some_field"), + 1, + "saving user should save specified field" + ); equal( - userInMemory.get('password'), + userInMemory.get("password"), undefined, - 'password should not be in attributes after saving user' + "password should not be in attributes after saving user" ); equal( - userInMemory.get('objectId'), + userInMemory.get("objectId"), undefined, - 'objectId should not be in attributes after saving user' + "objectId should not be in attributes after saving user" ); equal( - userInMemory.get('_id'), + userInMemory.get("_id"), undefined, - '_id should not be in attributes after saving user' + "_id should not be in attributes after saving user" ); - equal(userInMemory.id, id, 'id should be set'); + equal(userInMemory.id, id, "id should be set"); expect(userInMemory.updatedAt instanceof Date).toBe(true); ok(userInMemory.createdAt instanceof Date); - ok(userInMemory.getSessionToken(), 'user should have a sessionToken after saving'); + ok( + userInMemory.getSessionToken(), + "user should have a sessionToken after saving" + ); // Force the current user to read from localStorage, and check again delete Parse.User._currentUser; @@ -1030,37 +1065,44 @@ describe('Parse.User testing', () => { equal( userFromDisk.getUsername(), - 'alice', - 'userFromDisk should have previously existing fields' + "alice", + "userFromDisk should have previously existing fields" ); - equal(userFromDisk.get('some_field'), 1, 'userFromDisk should have saved field'); + equal( + userFromDisk.get("some_field"), + 1, + "userFromDisk should have saved field" + ); equal( - userFromDisk.get('password'), + userFromDisk.get("password"), undefined, - 'password should not be in attributes of userFromDisk' + "password should not be in attributes of userFromDisk" ); equal( - userFromDisk.get('objectId'), + userFromDisk.get("objectId"), undefined, - 'objectId should not be in attributes of userFromDisk' + "objectId should not be in attributes of userFromDisk" ); equal( - userFromDisk.get('_id'), + userFromDisk.get("_id"), undefined, - '_id should not be in attributes of userFromDisk' + "_id should not be in attributes of userFromDisk" ); - equal(userFromDisk.id, id, 'id should be set on userFromDisk'); + equal(userFromDisk.id, id, "id should be set on userFromDisk"); ok(userFromDisk.updatedAt instanceof Date); ok(userFromDisk.createdAt instanceof Date); - ok(userFromDisk.getSessionToken(), 'userFromDisk should have a sessionToken'); + ok( + userFromDisk.getSessionToken(), + "userFromDisk should have a sessionToken" + ); done(); }, @@ -1071,9 +1113,9 @@ describe('Parse.User testing', () => { ); }); - it('user with missing username', async done => { + it("user with missing username", async done => { const user = new Parse.User(); - user.set('password', 'foo'); + user.set("password", "foo"); try { await user.signUp(); done.fail(); @@ -1083,9 +1125,9 @@ describe('Parse.User testing', () => { } }); - it('user with missing password', async done => { + it("user with missing password", async done => { const user = new Parse.User(); - user.set('username', 'foo'); + user.set("username", "foo"); try { await user.signUp(); done.fail(); @@ -1095,56 +1137,56 @@ describe('Parse.User testing', () => { } }); - it('user stupid subclassing', async done => { - const SuperUser = Parse.Object.extend('User'); + it("user stupid subclassing", async done => { + const SuperUser = Parse.Object.extend("User"); const user = new SuperUser(); - user.set('username', 'bob'); - user.set('password', 'welcome'); - ok(user instanceof Parse.User, 'Subclassing User should have worked'); + user.set("username", "bob"); + user.set("password", "welcome"); + ok(user instanceof Parse.User, "Subclassing User should have worked"); await user.signUp(); done(); }); - it('user signup class method uses subclassing', async done => { + it("user signup class method uses subclassing", async done => { const SuperUser = Parse.User.extend({ secret: function () { return 1337; }, }); - const user = await Parse.User.signUp('bob', 'welcome'); - ok(user instanceof SuperUser, 'Subclassing User should have worked'); + const user = await Parse.User.signUp("bob", "welcome"); + ok(user instanceof SuperUser, "Subclassing User should have worked"); equal(user.secret(), 1337); done(); }); - it('user on disk gets updated after save', async done => { + it("user on disk gets updated after save", async done => { Parse.User.extend({ isSuper: function () { return true; }, }); - const user = await Parse.User.signUp('bob', 'welcome'); - await user.save('secret', 1337); + const user = await Parse.User.signUp("bob", "welcome"); + await user.save("secret", 1337); delete Parse.User._currentUser; delete Parse.User._currentUserMatchesDisk; const userFromDisk = Parse.User.current(); - equal(userFromDisk.get('secret'), 1337); - ok(userFromDisk.isSuper(), 'The subclass should have been used'); + equal(userFromDisk.get("secret"), 1337); + ok(userFromDisk.isSuper(), "The subclass should have been used"); done(); }); it("current user isn't dirty", async done => { - const user = await Parse.User.signUp('andrew', 'oppa', { - style: 'gangnam', + const user = await Parse.User.signUp("andrew", "oppa", { + style: "gangnam", }); - ok(!user.dirty('style'), 'The user just signed up.'); + ok(!user.dirty("style"), "The user just signed up."); Parse.User._currentUser = null; Parse.User._currentUserMatchesDisk = false; const userAgain = Parse.User.current(); - ok(!userAgain.dirty('style'), 'The user was just read from disk.'); + ok(!userAgain.dirty("style"), "The user was just read from disk."); done(); }); @@ -1163,7 +1205,7 @@ describe('Parse.User testing', () => { authenticate: function (options) { if (this.shouldError) { - options.error(this, 'An error occurred'); + options.error(this, "An error occurred"); } else if (this.shouldCancel) { options.error(this, null); } else { @@ -1183,7 +1225,7 @@ describe('Parse.User testing', () => { return true; }, getAuthType() { - return 'facebook'; + return "facebook"; }, deauthenticate: function () { this.loggedOut = true; @@ -1195,14 +1237,14 @@ describe('Parse.User testing', () => { // Note that this mocks out client-side Facebook action rather than // server-side. const getMockFacebookProvider = function () { - return getMockFacebookProviderWithIdToken('8675309', 'jenny'); + return getMockFacebookProviderWithIdToken("8675309", "jenny"); }; const getMockMyOauthProvider = function () { return { authData: { - id: '12345', - access_token: '12345', + id: "12345", + access_token: "12345", expiration_date: new Date().toJSON(), }, shouldError: false, @@ -1213,7 +1255,7 @@ describe('Parse.User testing', () => { authenticate(options) { if (this.shouldError) { - options.error(this, 'An error occurred'); + options.error(this, "An error occurred"); } else if (this.shouldCancel) { options.error(this, null); } else { @@ -1233,7 +1275,7 @@ describe('Parse.User testing', () => { return true; }, getAuthType() { - return 'myoauth'; + return "myoauth"; }, deauthenticate() { this.loggedOut = true; @@ -1248,133 +1290,142 @@ describe('Parse.User testing', () => { }, }); - it('log in with provider', async done => { + it("log in with provider", async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - const model = await Parse.User._logInWith('facebook'); - ok(model instanceof Parse.User, 'Model should be a Parse.User'); + const model = await Parse.User._logInWith("facebook"); + ok(model instanceof Parse.User, "Model should be a Parse.User"); strictEqual(Parse.User.current(), model); - ok(model.extended(), 'Should have used subclass.'); + ok(model.extended(), "Should have used subclass."); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); - strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); - ok(model._isLinked('facebook'), 'User should be linked to facebook'); + strictEqual( + provider.authData.expiration_date, + provider.synchronizedExpiration + ); + ok(model._isLinked("facebook"), "User should be linked to facebook"); done(); }); - it('can not set authdata to null', async () => { + it("can not set authdata to null", async () => { try { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - const user = await Parse.User._logInWith('facebook'); - user.set('authData', null); + const user = await Parse.User._logInWith("facebook"); + user.set("authData", null); await user.save(); fail(); } catch (e) { - expect(e.message).toBe('This authentication method is unsupported.'); + expect(e.message).toBe("This authentication method is unsupported."); } }); - it('ignore setting authdata to undefined', async () => { + it("ignore setting authdata to undefined", async () => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - const user = await Parse.User._logInWith('facebook'); - user.set('authData', undefined); + const user = await Parse.User._logInWith("facebook"); + user.set("authData", undefined); await user.save(); - let authData = user.get('authData'); + let authData = user.get("authData"); expect(authData).toBe(undefined); await user.fetch(); - authData = user.get('authData'); + authData = user.get("authData"); expect(authData.facebook.id).toBeDefined(); }); - it('user authData should be available in cloudcode (#2342)', async done => { - Parse.Cloud.define('checkLogin', req => { + it("user authData should be available in cloudcode (#2342)", async done => { + Parse.Cloud.define("checkLogin", req => { expect(req.user).not.toBeUndefined(); expect(Parse.FacebookUtils.isLinked(req.user)).toBe(true); - return 'ok'; + return "ok"; }); const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - const model = await Parse.User._logInWith('facebook'); - ok(model instanceof Parse.User, 'Model should be a Parse.User'); + const model = await Parse.User._logInWith("facebook"); + ok(model instanceof Parse.User, "Model should be a Parse.User"); strictEqual(Parse.User.current(), model); - ok(model.extended(), 'Should have used subclass.'); + ok(model.extended(), "Should have used subclass."); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); - strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); - ok(model._isLinked('facebook'), 'User should be linked to facebook'); + strictEqual( + provider.authData.expiration_date, + provider.synchronizedExpiration + ); + ok(model._isLinked("facebook"), "User should be linked to facebook"); - Parse.Cloud.run('checkLogin').then(done, done); + Parse.Cloud.run("checkLogin").then(done, done); }); - it('log in with provider and update token', async done => { + it("log in with provider and update token", async done => { const provider = getMockFacebookProvider(); - const secondProvider = getMockFacebookProviderWithIdToken('8675309', 'jenny_valid_token'); + const secondProvider = getMockFacebookProviderWithIdToken( + "8675309", + "jenny_valid_token" + ); Parse.User._registerAuthenticationProvider(provider); - await Parse.User._logInWith('facebook'); + await Parse.User._logInWith("facebook"); Parse.User._registerAuthenticationProvider(secondProvider); await Parse.User.logOut(); - await Parse.User._logInWith('facebook'); - expect(secondProvider.synchronizedAuthToken).toEqual('jenny_valid_token'); + await Parse.User._logInWith("facebook"); + expect(secondProvider.synchronizedAuthToken).toEqual("jenny_valid_token"); // Make sure we can login with the new token again await Parse.User.logOut(); - await Parse.User._logInWith('facebook'); + await Parse.User._logInWith("facebook"); done(); }); - it('returns authData when authed and logged in with provider (regression test for #1498)', async done => { + it("returns authData when authed and logged in with provider (regression test for #1498)", async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - const user = await Parse.User._logInWith('facebook'); + const user = await Parse.User._logInWith("facebook"); const userQuery = new Parse.Query(Parse.User); userQuery.get(user.id).then(user => { - expect(user.get('authData')).not.toBeUndefined(); + expect(user.get("authData")).not.toBeUndefined(); done(); }); }); - it('only creates a single session for an installation / user pair (#2885)', async done => { + it("only creates a single session for an installation / user pair (#2885)", async done => { Parse.Object.disableSingleInstance(); const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - await Parse.User.logInWith('facebook'); - await Parse.User.logInWith('facebook'); - const user = await Parse.User.logInWith('facebook'); + await Parse.User.logInWith("facebook"); + await Parse.User.logInWith("facebook"); + const user = await Parse.User.logInWith("facebook"); const sessionToken = user.getSessionToken(); - const query = new Parse.Query('_Session'); + const query = new Parse.Query("_Session"); return query .find({ useMasterKey: true }) .then(results => { expect(results.length).toBe(1); - expect(results[0].get('sessionToken')).toBe(sessionToken); - expect(results[0].get('createdWith')).toEqual({ - action: 'login', - authProvider: 'facebook', + expect(results[0].get("sessionToken")).toBe(sessionToken); + expect(results[0].get("createdWith")).toEqual({ + action: "login", + authProvider: "facebook", }); done(); }) .catch(done.fail); }); - it('log in with provider with files', done => { + it("log in with provider with files", done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - const file = new Parse.File('yolo.txt', [1, 2, 3], 'text/plain'); + const file = new Parse.File("yolo.txt", [1, 2, 3], "text/plain"); file .save() .then(file => { const user = new Parse.User(); - user.set('file', file); - return user._linkWith('facebook', {}); + user.set("file", file); + return user._linkWith("facebook", {}); }) .then(user => { - expect(user._isLinked('facebook')).toBeTruthy(); - return Parse.User._logInWith('facebook', {}); + expect(user._isLinked("facebook")).toBeTruthy(); + return Parse.User._logInWith("facebook", {}); }) .then(user => { - const fileAgain = user.get('file'); + const fileAgain = user.get("file"); expect(fileAgain.name()).toMatch(/yolo.txt$/); expect(fileAgain.url()).toMatch(/yolo.txt$/); }) @@ -1384,72 +1435,78 @@ describe('Parse.User testing', () => { .catch(done.fail); }); - it('log in with provider twice', async done => { + it("log in with provider twice", async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - const model = await Parse.User._logInWith('facebook'); - ok(model instanceof Parse.User, 'Model should be a Parse.User'); + const model = await Parse.User._logInWith("facebook"); + ok(model instanceof Parse.User, "Model should be a Parse.User"); strictEqual(Parse.User.current(), model); - ok(model.extended(), 'Should have used the subclass.'); + ok(model.extended(), "Should have used the subclass."); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); - strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); - ok(model._isLinked('facebook'), 'User should be linked to facebook'); + strictEqual( + provider.authData.expiration_date, + provider.synchronizedExpiration + ); + ok(model._isLinked("facebook"), "User should be linked to facebook"); Parse.User.logOut().then(async () => { ok(provider.loggedOut); provider.loggedOut = false; - const innerModel = await Parse.User._logInWith('facebook'); - ok(innerModel instanceof Parse.User, 'Model should be a Parse.User'); - ok(innerModel === Parse.User.current(), 'Returned model should be the current user'); + const innerModel = await Parse.User._logInWith("facebook"); + ok(innerModel instanceof Parse.User, "Model should be a Parse.User"); + ok( + innerModel === Parse.User.current(), + "Returned model should be the current user" + ); ok(provider.authData.id === provider.synchronizedUserId); ok(provider.authData.access_token === provider.synchronizedAuthToken); - ok(innerModel._isLinked('facebook'), 'User should be linked to facebook'); - ok(innerModel.existed(), 'User should not be newly-created'); + ok(innerModel._isLinked("facebook"), "User should be linked to facebook"); + ok(innerModel.existed(), "User should not be newly-created"); done(); }, done.fail); }); - it('log in with provider failed', async done => { + it("log in with provider failed", async done => { const provider = getMockFacebookProvider(); provider.shouldError = true; Parse.User._registerAuthenticationProvider(provider); try { - await Parse.User._logInWith('facebook'); + await Parse.User._logInWith("facebook"); done.fail(); } catch (error) { - ok(error, 'Error should be non-null'); + ok(error, "Error should be non-null"); done(); } }); - it('log in with provider cancelled', async done => { + it("log in with provider cancelled", async done => { const provider = getMockFacebookProvider(); provider.shouldCancel = true; Parse.User._registerAuthenticationProvider(provider); try { - await Parse.User._logInWith('facebook'); + await Parse.User._logInWith("facebook"); done.fail(); } catch (error) { - ok(error === null, 'Error should be null'); + ok(error === null, "Error should be null"); done(); } }); - it('login with provider should not call beforeSave trigger', async done => { + it("login with provider should not call beforeSave trigger", async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - await Parse.User._logInWith('facebook'); + await Parse.User._logInWith("facebook"); Parse.User.logOut().then(async () => { Parse.Cloud.beforeSave(Parse.User, function (req, res) { res.error("Before save shouldn't be called on login"); }); - await Parse.User._logInWith('facebook'); + await Parse.User._logInWith("facebook"); done(); }); }); - it('signup with provider should not call beforeLogin trigger', async done => { + it("signup with provider should not call beforeLogin trigger", async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); @@ -1458,30 +1515,30 @@ describe('Parse.User testing', () => { hit++; }); - await Parse.User._logInWith('facebook'); + await Parse.User._logInWith("facebook"); expect(hit).toBe(0); done(); }); - it('login with provider should call beforeLogin trigger', async done => { + it("login with provider should call beforeLogin trigger", async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); let hit = 0; Parse.Cloud.beforeLogin(req => { hit++; - expect(req.object.get('authData')).toBeDefined(); - expect(req.object.get('name')).toBe('tupac shakur'); + expect(req.object.get("authData")).toBeDefined(); + expect(req.object.get("name")).toBe("tupac shakur"); }); - await Parse.User._logInWith('facebook'); - await Parse.User.current().save({ name: 'tupac shakur' }); + await Parse.User._logInWith("facebook"); + await Parse.User.current().save({ name: "tupac shakur" }); await Parse.User.logOut(); - await Parse.User._logInWith('facebook'); + await Parse.User._logInWith("facebook"); expect(hit).toBe(1); done(); }); - it('incorrect login with provider should not call beforeLogin trigger', async done => { + it("incorrect login with provider should not call beforeLogin trigger", async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); @@ -1489,11 +1546,11 @@ describe('Parse.User testing', () => { Parse.Cloud.beforeLogin(() => { hit++; }); - await Parse.User._logInWith('facebook'); + await Parse.User._logInWith("facebook"); await Parse.User.logOut(); provider.shouldError = true; try { - await Parse.User._logInWith('facebook'); + await Parse.User._logInWith("facebook"); } catch (e) { expect(e).toBeDefined(); } @@ -1501,119 +1558,125 @@ describe('Parse.User testing', () => { done(); }); - it('login with provider should be blockable by beforeLogin', async done => { + it("login with provider should be blockable by beforeLogin", async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); let hit = 0; Parse.Cloud.beforeLogin(req => { hit++; - if (req.object.get('isBanned')) { - throw new Error('banned account'); + if (req.object.get("isBanned")) { + throw new Error("banned account"); } }); - await Parse.User._logInWith('facebook'); + await Parse.User._logInWith("facebook"); await Parse.User.current().save({ isBanned: true }); await Parse.User.logOut(); try { - await Parse.User._logInWith('facebook'); - throw new Error('should not have continued login.'); + await Parse.User._logInWith("facebook"); + throw new Error("should not have continued login."); } catch (e) { - expect(e.message).toBe('banned account'); + expect(e.message).toBe("banned account"); } expect(hit).toBe(1); done(); }); - it('login with provider should be blockable by beforeLogin even when the user has a attached file', async done => { + it("login with provider should be blockable by beforeLogin even when the user has a attached file", async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); let hit = 0; Parse.Cloud.beforeLogin(req => { hit++; - if (req.object.get('isBanned')) { - throw new Error('banned account'); + if (req.object.get("isBanned")) { + throw new Error("banned account"); } }); - const user = await Parse.User._logInWith('facebook'); - const base64 = 'aHR0cHM6Ly9naXRodWIuY29tL2t2bmt1YW5n'; - const file = new Parse.File('myfile.txt', { base64 }); + const user = await Parse.User._logInWith("facebook"); + const base64 = "aHR0cHM6Ly9naXRodWIuY29tL2t2bmt1YW5n"; + const file = new Parse.File("myfile.txt", { base64 }); await file.save(); await user.save({ isBanned: true, file }); await Parse.User.logOut(); try { - await Parse.User._logInWith('facebook'); - throw new Error('should not have continued login.'); + await Parse.User._logInWith("facebook"); + throw new Error("should not have continued login."); } catch (e) { - expect(e.message).toBe('banned account'); + expect(e.message).toBe("banned account"); } expect(hit).toBe(1); done(); }); - it('logout with provider should call afterLogout trigger', async done => { + it("logout with provider should call afterLogout trigger", async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); let userId; Parse.Cloud.afterLogout(req => { - expect(req.object.className).toEqual('_Session'); + expect(req.object.className).toEqual("_Session"); expect(req.object.id).toBeDefined(); - const user = req.object.get('user'); + const user = req.object.get("user"); expect(user).toBeDefined(); userId = user.id; }); - const user = await Parse.User._logInWith('facebook'); + const user = await Parse.User._logInWith("facebook"); await Parse.User.logOut(); expect(user.id).toBe(userId); done(); }); - it('link with provider', async done => { + it("link with provider", async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); const user = new Parse.User(); - user.set('username', 'testLinkWithProvider'); - user.set('password', 'mypass'); + user.set("username", "testLinkWithProvider"); + user.set("password", "mypass"); await user.signUp(); - const model = await user._linkWith('facebook'); - ok(model instanceof Parse.User, 'Model should be a Parse.User'); + const model = await user._linkWith("facebook"); + ok(model instanceof Parse.User, "Model should be a Parse.User"); strictEqual(Parse.User.current(), model); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); - strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); - ok(model._isLinked('facebook'), 'User should be linked'); + strictEqual( + provider.authData.expiration_date, + provider.synchronizedExpiration + ); + ok(model._isLinked("facebook"), "User should be linked"); done(); }); // What this means is, only one Parse User can be linked to a // particular Facebook account. - it('link with provider for already linked user', async done => { + it("link with provider for already linked user", async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); const user = new Parse.User(); - user.set('username', 'testLinkWithProviderToAlreadyLinkedUser'); - user.set('password', 'mypass'); + user.set("username", "testLinkWithProviderToAlreadyLinkedUser"); + user.set("password", "mypass"); await user.signUp(); - const model = await user._linkWith('facebook'); - ok(model instanceof Parse.User, 'Model should be a Parse.User'); + const model = await user._linkWith("facebook"); + ok(model instanceof Parse.User, "Model should be a Parse.User"); strictEqual(Parse.User.current(), model); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); - strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); - ok(model._isLinked('facebook'), 'User should be linked.'); + strictEqual( + provider.authData.expiration_date, + provider.synchronizedExpiration + ); + ok(model._isLinked("facebook"), "User should be linked."); const user2 = new Parse.User(); - user2.set('username', 'testLinkWithProviderToAlreadyLinkedUser2'); - user2.set('password', 'mypass'); + user2.set("username", "testLinkWithProviderToAlreadyLinkedUser2"); + user2.set("password", "mypass"); await user2.signUp(); try { - await user2._linkWith('facebook'); + await user2._linkWith("facebook"); done.fail(); } catch (error) { expect(error.code).toEqual(Parse.Error.ACCOUNT_ALREADY_LINKED); @@ -1621,31 +1684,31 @@ describe('Parse.User testing', () => { } }); - it('link with provider should return sessionToken', async () => { + it("link with provider should return sessionToken", async () => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); const user = new Parse.User(); - user.set('username', 'testLinkWithProvider'); - user.set('password', 'mypass'); + user.set("username", "testLinkWithProvider"); + user.set("password", "mypass"); await user.signUp(); const query = new Parse.Query(Parse.User); const u2 = await query.get(user.id); - const model = await u2._linkWith('facebook', {}, { useMasterKey: true }); + const model = await u2._linkWith("facebook", {}, { useMasterKey: true }); expect(u2.getSessionToken()).toBeDefined(); expect(model.getSessionToken()).toBeDefined(); expect(u2.getSessionToken()).toBe(model.getSessionToken()); }); - it('link with provider via sessionToken should not create new sessionToken (Regression #5799)', async () => { + it("link with provider via sessionToken should not create new sessionToken (Regression #5799)", async () => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); const user = new Parse.User(); - user.set('username', 'testLinkWithProviderNoOverride'); - user.set('password', 'mypass'); + user.set("username", "testLinkWithProviderNoOverride"); + user.set("password", "mypass"); await user.signUp(); const sessionToken = user.getSessionToken(); - await user._linkWith('facebook', {}, { sessionToken }); + await user._linkWith("facebook", {}, { sessionToken }); expect(sessionToken).toBe(user.getSessionToken()); expect(user._isLinked(provider)).toBe(true); @@ -1656,182 +1719,197 @@ describe('Parse.User testing', () => { expect(sessionToken).toBe(become.getSessionToken()); }); - it('link with provider failed', async done => { + it("link with provider failed", async done => { const provider = getMockFacebookProvider(); provider.shouldError = true; Parse.User._registerAuthenticationProvider(provider); const user = new Parse.User(); - user.set('username', 'testLinkWithProvider'); - user.set('password', 'mypass'); + user.set("username", "testLinkWithProvider"); + user.set("password", "mypass"); await user.signUp(); try { - await user._linkWith('facebook'); + await user._linkWith("facebook"); done.fail(); } catch (error) { - ok(error, 'Linking should fail'); - ok(!user._isLinked('facebook'), 'User should not be linked to facebook'); + ok(error, "Linking should fail"); + ok(!user._isLinked("facebook"), "User should not be linked to facebook"); done(); } }); - it('link with provider cancelled', async done => { + it("link with provider cancelled", async done => { const provider = getMockFacebookProvider(); provider.shouldCancel = true; Parse.User._registerAuthenticationProvider(provider); const user = new Parse.User(); - user.set('username', 'testLinkWithProvider'); - user.set('password', 'mypass'); + user.set("username", "testLinkWithProvider"); + user.set("password", "mypass"); await user.signUp(); try { - await user._linkWith('facebook'); + await user._linkWith("facebook"); done.fail(); } catch (error) { - ok(!error, 'Linking should be cancelled'); - ok(!user._isLinked('facebook'), 'User should not be linked to facebook'); + ok(!error, "Linking should be cancelled"); + ok(!user._isLinked("facebook"), "User should not be linked to facebook"); done(); } }); - it('unlink with provider', async done => { + it("unlink with provider", async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - const model = await Parse.User._logInWith('facebook'); - ok(model instanceof Parse.User, 'Model should be a Parse.User.'); + const model = await Parse.User._logInWith("facebook"); + ok(model instanceof Parse.User, "Model should be a Parse.User."); strictEqual(Parse.User.current(), model); - ok(model.extended(), 'Should have used the subclass.'); + ok(model.extended(), "Should have used the subclass."); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); - strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); - ok(model._isLinked('facebook'), 'User should be linked to facebook.'); - await model._unlinkFrom('facebook'); - ok(!model._isLinked('facebook'), 'User should not be linked.'); - ok(!provider.synchronizedUserId, 'User id should be cleared.'); - ok(!provider.synchronizedAuthToken, 'Auth token should be cleared.'); - ok(!provider.synchronizedExpiration, 'Expiration should be cleared.'); + strictEqual( + provider.authData.expiration_date, + provider.synchronizedExpiration + ); + ok(model._isLinked("facebook"), "User should be linked to facebook."); + await model._unlinkFrom("facebook"); + ok(!model._isLinked("facebook"), "User should not be linked."); + ok(!provider.synchronizedUserId, "User id should be cleared."); + ok(!provider.synchronizedAuthToken, "Auth token should be cleared."); + ok(!provider.synchronizedExpiration, "Expiration should be cleared."); done(); }); - it('unlink and link', async done => { + it("unlink and link", async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - const model = await Parse.User._logInWith('facebook'); - ok(model instanceof Parse.User, 'Model should be a Parse.User'); + const model = await Parse.User._logInWith("facebook"); + ok(model instanceof Parse.User, "Model should be a Parse.User"); strictEqual(Parse.User.current(), model); - ok(model.extended(), 'Should have used the subclass.'); + ok(model.extended(), "Should have used the subclass."); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); - strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); - ok(model._isLinked('facebook'), 'User should be linked to facebook'); - - await model._unlinkFrom('facebook'); - ok(!model._isLinked('facebook'), 'User should not be linked to facebook'); - ok(!provider.synchronizedUserId, 'User id should be cleared'); - ok(!provider.synchronizedAuthToken, 'Auth token should be cleared'); - ok(!provider.synchronizedExpiration, 'Expiration should be cleared'); - - await model._linkWith('facebook'); - ok(provider.synchronizedUserId, 'User id should have a value'); - ok(provider.synchronizedAuthToken, 'Auth token should have a value'); - ok(provider.synchronizedExpiration, 'Expiration should have a value'); - ok(model._isLinked('facebook'), 'User should be linked to facebook'); + strictEqual( + provider.authData.expiration_date, + provider.synchronizedExpiration + ); + ok(model._isLinked("facebook"), "User should be linked to facebook"); + + await model._unlinkFrom("facebook"); + ok(!model._isLinked("facebook"), "User should not be linked to facebook"); + ok(!provider.synchronizedUserId, "User id should be cleared"); + ok(!provider.synchronizedAuthToken, "Auth token should be cleared"); + ok(!provider.synchronizedExpiration, "Expiration should be cleared"); + + await model._linkWith("facebook"); + ok(provider.synchronizedUserId, "User id should have a value"); + ok(provider.synchronizedAuthToken, "Auth token should have a value"); + ok(provider.synchronizedExpiration, "Expiration should have a value"); + ok(model._isLinked("facebook"), "User should be linked to facebook"); done(); }); - it('link multiple providers', async done => { + it("link multiple providers", async done => { const provider = getMockFacebookProvider(); const mockProvider = getMockMyOauthProvider(); Parse.User._registerAuthenticationProvider(provider); - const model = await Parse.User._logInWith('facebook'); - ok(model instanceof Parse.User, 'Model should be a Parse.User'); + const model = await Parse.User._logInWith("facebook"); + ok(model instanceof Parse.User, "Model should be a Parse.User"); strictEqual(Parse.User.current(), model); - ok(model.extended(), 'Should have used the subclass.'); + ok(model.extended(), "Should have used the subclass."); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); - strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); - ok(model._isLinked('facebook'), 'User should be linked to facebook'); + strictEqual( + provider.authData.expiration_date, + provider.synchronizedExpiration + ); + ok(model._isLinked("facebook"), "User should be linked to facebook"); Parse.User._registerAuthenticationProvider(mockProvider); const objectId = model.id; - await model._linkWith('myoauth'); + await model._linkWith("myoauth"); expect(model.id).toEqual(objectId); - ok(model._isLinked('facebook'), 'User should be linked to facebook'); - ok(model._isLinked('myoauth'), 'User should be linked to myoauth'); + ok(model._isLinked("facebook"), "User should be linked to facebook"); + ok(model._isLinked("myoauth"), "User should be linked to myoauth"); done(); }); - it('link multiple providers and updates token', async done => { + it("link multiple providers and updates token", async done => { const provider = getMockFacebookProvider(); - const secondProvider = getMockFacebookProviderWithIdToken('8675309', 'jenny_valid_token'); + const secondProvider = getMockFacebookProviderWithIdToken( + "8675309", + "jenny_valid_token" + ); const mockProvider = getMockMyOauthProvider(); Parse.User._registerAuthenticationProvider(provider); - const model = await Parse.User._logInWith('facebook'); + const model = await Parse.User._logInWith("facebook"); Parse.User._registerAuthenticationProvider(mockProvider); const objectId = model.id; - await model._linkWith('myoauth'); + await model._linkWith("myoauth"); Parse.User._registerAuthenticationProvider(secondProvider); await Parse.User.logOut(); - await Parse.User._logInWith('facebook'); + await Parse.User._logInWith("facebook"); await Parse.User.logOut(); - const user = await Parse.User._logInWith('myoauth'); + const user = await Parse.User._logInWith("myoauth"); expect(user.id).toBe(objectId); done(); }); - it('link multiple providers and update token', async done => { + it("link multiple providers and update token", async done => { const provider = getMockFacebookProvider(); const mockProvider = getMockMyOauthProvider(); Parse.User._registerAuthenticationProvider(provider); - const model = await Parse.User._logInWith('facebook'); - ok(model instanceof Parse.User, 'Model should be a Parse.User'); + const model = await Parse.User._logInWith("facebook"); + ok(model instanceof Parse.User, "Model should be a Parse.User"); strictEqual(Parse.User.current(), model); - ok(model.extended(), 'Should have used the subclass.'); + ok(model.extended(), "Should have used the subclass."); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); - strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); - ok(model._isLinked('facebook'), 'User should be linked to facebook'); + strictEqual( + provider.authData.expiration_date, + provider.synchronizedExpiration + ); + ok(model._isLinked("facebook"), "User should be linked to facebook"); Parse.User._registerAuthenticationProvider(mockProvider); const objectId = model.id; - await model._linkWith('myoauth'); + await model._linkWith("myoauth"); expect(model.id).toEqual(objectId); - ok(model._isLinked('facebook'), 'User should be linked to facebook'); - ok(model._isLinked('myoauth'), 'User should be linked to myoauth'); - await model._linkWith('facebook'); - ok(model._isLinked('facebook'), 'User should be linked to facebook'); - ok(model._isLinked('myoauth'), 'User should be linked to myoauth'); + ok(model._isLinked("facebook"), "User should be linked to facebook"); + ok(model._isLinked("myoauth"), "User should be linked to myoauth"); + await model._linkWith("facebook"); + ok(model._isLinked("facebook"), "User should be linked to facebook"); + ok(model._isLinked("myoauth"), "User should be linked to myoauth"); done(); }); - it('should fail linking with existing', async done => { + it("should fail linking with existing", async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - await Parse.User._logInWith('facebook'); + await Parse.User._logInWith("facebook"); await Parse.User.logOut(); const user = new Parse.User(); - user.setUsername('user'); - user.setPassword('password'); + user.setUsername("user"); + user.setPassword("password"); await user.signUp(); // try to link here try { - await user._linkWith('facebook'); + await user._linkWith("facebook"); done.fail(); } catch (e) { done(); } }); - it('should fail linking with existing through REST', async done => { + it("should fail linking with existing through REST", async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - const model = await Parse.User._logInWith('facebook'); + const model = await Parse.User._logInWith("facebook"); const userId = model.id; Parse.User.logOut().then(() => { request({ - method: 'POST', - url: Parse.serverURL + '/classes/_User', + method: "POST", + url: Parse.serverURL + "/classes/_User", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }, body: { authData: { facebook: provider.authData } }, }).then(response => { @@ -1839,17 +1917,19 @@ describe('Parse.User testing', () => { // make sure the location header is properly set expect(userId).not.toBeUndefined(); expect(body.objectId).toEqual(userId); - expect(response.headers.location).toEqual(Parse.serverURL + '/users/' + userId); + expect(response.headers.location).toEqual( + Parse.serverURL + "/users/" + userId + ); done(); }); }); }); - it('should not allow login with expired authData token since allowExpiredAuthDataToken is set to false by default', async () => { + it("should not allow login with expired authData token since allowExpiredAuthDataToken is set to false by default", async () => { const provider = { authData: { - id: '12345', - access_token: 'token', + id: "12345", + access_token: "token", }, restoreAuthentication: function () { return true; @@ -1861,24 +1941,26 @@ describe('Parse.User testing', () => { options.success(this, provider.authData); }, getAuthType: function () { - return 'shortLivedAuth'; + return "shortLivedAuth"; }, }; - defaultConfiguration.auth.shortLivedAuth.setValidAccessToken('token'); + defaultConfiguration.auth.shortLivedAuth.setValidAccessToken("token"); Parse.User._registerAuthenticationProvider(provider); - await Parse.User._logInWith('shortLivedAuth', {}); + await Parse.User._logInWith("shortLivedAuth", {}); // Simulate a remotely expired token (like a short lived one) // In this case, we want success as it was valid once. // If the client needs an updated token, do lock the user out - defaultConfiguration.auth.shortLivedAuth.setValidAccessToken('otherToken'); - await expectAsync(Parse.User._logInWith('shortLivedAuth', {})).toBeRejected(); + defaultConfiguration.auth.shortLivedAuth.setValidAccessToken("otherToken"); + await expectAsync( + Parse.User._logInWith("shortLivedAuth", {}) + ).toBeRejected(); }); - it('should allow PUT request with stale auth Data', done => { + it("should allow PUT request with stale auth Data", done => { const provider = { authData: { - id: '12345', - access_token: 'token', + id: "12345", + access_token: "token", }, restoreAuthentication: function () { return true; @@ -1890,33 +1972,35 @@ describe('Parse.User testing', () => { options.success(this, provider.authData); }, getAuthType: function () { - return 'shortLivedAuth'; + return "shortLivedAuth"; }, }; - defaultConfiguration.auth.shortLivedAuth.setValidAccessToken('token'); + defaultConfiguration.auth.shortLivedAuth.setValidAccessToken("token"); Parse.User._registerAuthenticationProvider(provider); - Parse.User._logInWith('shortLivedAuth', {}) + Parse.User._logInWith("shortLivedAuth", {}) .then(() => { // Simulate a remotely expired token (like a short lived one) // In this case, we want success as it was valid once. // If the client needs an updated one, do lock the user out - defaultConfiguration.auth.shortLivedAuth.setValidAccessToken('otherToken'); + defaultConfiguration.auth.shortLivedAuth.setValidAccessToken( + "otherToken" + ); return request({ - method: 'PUT', - url: Parse.serverURL + '/users/' + Parse.User.current().id, + method: "PUT", + url: Parse.serverURL + "/users/" + Parse.User.current().id, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, - 'X-Parse-Session-Token': Parse.User.current().getSessionToken(), - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Javascript-Key": Parse.javaScriptKey, + "X-Parse-Session-Token": Parse.User.current().getSessionToken(), + "Content-Type": "application/json", }, body: { - key: 'value', // update a key + key: "value", // update a key authData: { // pass the original auth data shortLivedAuth: { - id: '12345', - access_token: 'token', + id: "12345", + access_token: "token", }, }, }, @@ -1932,74 +2016,74 @@ describe('Parse.User testing', () => { ); }); - it('should properly error when password is missing', async done => { + it("should properly error when password is missing", async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - const user = await Parse.User._logInWith('facebook'); - user.set('username', 'myUser'); - user.set('email', 'foo@example.com'); + const user = await Parse.User._logInWith("facebook"); + user.set("username", "myUser"); + user.set("email", "foo@example.com"); user .save() .then(() => { return Parse.User.logOut(); }) .then(() => { - return Parse.User.logIn('myUser', 'password'); + return Parse.User.logIn("myUser", "password"); }) .then( () => { - fail('should not succeed'); + fail("should not succeed"); done(); }, err => { expect(err.code).toBe(Parse.Error.OBJECT_NOT_FOUND); - expect(err.message).toEqual('Invalid username/password.'); + expect(err.message).toEqual("Invalid username/password."); done(); } ); }); - it('should have authData in beforeSave and afterSave', async done => { - Parse.Cloud.beforeSave('_User', request => { - const authData = request.object.get('authData'); + it("should have authData in beforeSave and afterSave", async done => { + Parse.Cloud.beforeSave("_User", request => { + const authData = request.object.get("authData"); expect(authData).not.toBeUndefined(); if (authData) { - expect(authData.facebook.id).toEqual('8675309'); - expect(authData.facebook.access_token).toEqual('jenny'); + expect(authData.facebook.id).toEqual("8675309"); + expect(authData.facebook.access_token).toEqual("jenny"); } else { - fail('authData should be set'); + fail("authData should be set"); } }); - Parse.Cloud.afterSave('_User', request => { - const authData = request.object.get('authData'); + Parse.Cloud.afterSave("_User", request => { + const authData = request.object.get("authData"); expect(authData).not.toBeUndefined(); if (authData) { - expect(authData.facebook.id).toEqual('8675309'); - expect(authData.facebook.access_token).toEqual('jenny'); + expect(authData.facebook.id).toEqual("8675309"); + expect(authData.facebook.access_token).toEqual("jenny"); } else { - fail('authData should be set'); + fail("authData should be set"); } }); const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - await Parse.User._logInWith('facebook'); + await Parse.User._logInWith("facebook"); done(); }); - it('set password then change password', done => { - Parse.User.signUp('bob', 'barker') + it("set password then change password", done => { + Parse.User.signUp("bob", "barker") .then(bob => { - bob.setPassword('meower'); + bob.setPassword("meower"); return bob.save(); }) .then(() => { - return Parse.User.logIn('bob', 'meower'); + return Parse.User.logIn("bob", "meower"); }) .then( bob => { - expect(bob.getUsername()).toEqual('bob'); + expect(bob.getUsername()).toEqual("bob"); done(); }, e => { @@ -2009,29 +2093,29 @@ describe('Parse.User testing', () => { ); }); - it('authenticated check', async done => { + it("authenticated check", async done => { const user = new Parse.User(); - user.set('username', 'darkhelmet'); - user.set('password', 'onetwothreefour'); + user.set("username", "darkhelmet"); + user.set("password", "onetwothreefour"); ok(!user.authenticated()); await user.signUp(null); ok(user.authenticated()); done(); }); - it('log in with explicit facebook auth data', async done => { + it("log in with explicit facebook auth data", async done => { await Parse.FacebookUtils.logIn({ - id: '8675309', - access_token: 'jenny', + id: "8675309", + access_token: "jenny", expiration_date: new Date().toJSON(), }); done(); }); - it('log in async with explicit facebook auth data', done => { + it("log in async with explicit facebook auth data", done => { Parse.FacebookUtils.logIn({ - id: '8675309', - access_token: 'jenny', + id: "8675309", + access_token: "jenny", expiration_date: new Date().toJSON(), }).then( function () { @@ -2044,11 +2128,11 @@ describe('Parse.User testing', () => { ); }); - it('link with explicit facebook auth data', async done => { - const user = await Parse.User.signUp('mask', 'open sesame'); + it("link with explicit facebook auth data", async done => { + const user = await Parse.User.signUp("mask", "open sesame"); Parse.FacebookUtils.link(user, { - id: '8675309', - access_token: 'jenny', + id: "8675309", + access_token: "jenny", expiration_date: new Date().toJSON(), }).then(done, error => { jfail(error); @@ -2056,11 +2140,11 @@ describe('Parse.User testing', () => { }); }); - it('link async with explicit facebook auth data', async done => { - const user = await Parse.User.signUp('mask', 'open sesame'); + it("link async with explicit facebook auth data", async done => { + const user = await Parse.User.signUp("mask", "open sesame"); Parse.FacebookUtils.link(user, { - id: '8675309', - access_token: 'jenny', + id: "8675309", + access_token: "jenny", expiration_date: new Date().toJSON(), }).then( function () { @@ -2073,56 +2157,56 @@ describe('Parse.User testing', () => { ); }); - it('async methods', done => { - const data = { foo: 'bar' }; + it("async methods", done => { + const data = { foo: "bar" }; - Parse.User.signUp('finn', 'human', data) + Parse.User.signUp("finn", "human", data) .then(function (user) { equal(Parse.User.current(), user); - equal(user.get('foo'), 'bar'); + equal(user.get("foo"), "bar"); return Parse.User.logOut(); }) .then(function () { - return Parse.User.logIn('finn', 'human'); + return Parse.User.logIn("finn", "human"); }) .then(function (user) { equal(user, Parse.User.current()); - equal(user.get('foo'), 'bar'); + equal(user.get("foo"), "bar"); return Parse.User.logOut(); }) .then(function () { const user = new Parse.User(); - user.set('username', 'jake'); - user.set('password', 'dog'); - user.set('foo', 'baz'); + user.set("username", "jake"); + user.set("password", "dog"); + user.set("foo", "baz"); return user.signUp(); }) .then(function (user) { equal(user, Parse.User.current()); - equal(user.get('foo'), 'baz'); + equal(user.get("foo"), "baz"); user = new Parse.User(); - user.set('username', 'jake'); - user.set('password', 'dog'); + user.set("username", "jake"); + user.set("password", "dog"); return user.logIn(); }) .then(function (user) { equal(user, Parse.User.current()); - equal(user.get('foo'), 'baz'); + equal(user.get("foo"), "baz"); const userAgain = new Parse.User(); userAgain.id = user.id; return userAgain.fetch(); }) .then(function (userAgain) { - equal(userAgain.get('foo'), 'baz'); + equal(userAgain.get("foo"), "baz"); done(); }); }); it("querying for users doesn't get session tokens", done => { const user = new Parse.User(); - user.set('username', 'finn'); - user.set('password', 'human'); - user.set('foo', 'bar'); + user.set("username", "finn"); + user.set("password", "human"); + user.set("foo", "bar"); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); user.setACL(acl); @@ -2133,9 +2217,9 @@ describe('Parse.User testing', () => { }) .then(() => { const user = new Parse.User(); - user.set('username', 'jake'); - user.set('password', 'dog'); - user.set('foo', 'baz'); + user.set("username", "jake"); + user.set("password", "dog"); + user.set("foo", "baz"); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); user.setACL(acl); @@ -2153,7 +2237,10 @@ describe('Parse.User testing', () => { equal(users.length, 2); users.forEach(user => { expect(user.getSessionToken()).toBeUndefined(); - ok(!user.getSessionToken(), 'user should not have a session token.'); + ok( + !user.getSessionToken(), + "user should not have a session token." + ); }); done(); }, @@ -2164,21 +2251,21 @@ describe('Parse.User testing', () => { ); }); - it('querying for users only gets the expected fields', done => { + it("querying for users only gets the expected fields", done => { const user = new Parse.User(); - user.setUsername('finn'); - user.setPassword('human'); - user.set('foo', 'bar'); + user.setUsername("finn"); + user.setPassword("human"); + user.set("foo", "bar"); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); user.setACL(acl); user.signUp().then(() => { request({ headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }, - url: 'http://localhost:8378/1/users', + url: "http://localhost:8378/1/users", }).then(response => { const b = response.data; expect(b.results.length).toEqual(1); @@ -2191,9 +2278,9 @@ describe('Parse.User testing', () => { it("retrieve user data from fetch, make sure the session token hasn't changed", done => { const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('zxcv'); - let currentSessionToken = ''; + user.setPassword("asdf"); + user.setUsername("zxcv"); + let currentSessionToken = ""; Promise.resolve() .then(function () { return user.signUp(); @@ -2214,14 +2301,14 @@ describe('Parse.User testing', () => { ); }); - it('user save should fail with invalid email', done => { + it("user save should fail with invalid email", done => { const user = new Parse.User(); - user.set('username', 'teste'); - user.set('password', 'test'); - user.set('email', 'invalid'); + user.set("username", "teste"); + user.set("password", "test"); + user.set("email", "invalid"); user.signUp().then( () => { - fail('Should not have been able to save.'); + fail("Should not have been able to save."); done(); }, error => { @@ -2231,23 +2318,23 @@ describe('Parse.User testing', () => { ); }); - it('user signup should error if email taken', done => { + it("user signup should error if email taken", done => { const user = new Parse.User(); - user.set('username', 'test1'); - user.set('password', 'test'); - user.set('email', 'test@test.com'); + user.set("username", "test1"); + user.set("password", "test"); + user.set("email", "test@test.com"); user .signUp() .then(() => { const user2 = new Parse.User(); - user2.set('username', 'test2'); - user2.set('password', 'test'); - user2.set('email', 'test@test.com'); + user2.set("username", "test2"); + user2.set("password", "test"); + user2.set("email", "test@test.com"); return user2.signUp(); }) .then( () => { - fail('Should not have been able to sign up.'); + fail("Should not have been able to sign up."); done(); }, () => { @@ -2256,123 +2343,136 @@ describe('Parse.User testing', () => { ); }); - describe('case insensitive signup not allowed', () => { - it_id('464eddc2-7a46-413d-888e-b43b040f1511')(it)( - 'signup should fail with duplicate case insensitive username with basic setter', + describe("case insensitive signup not allowed", () => { + it_id("464eddc2-7a46-413d-888e-b43b040f1511")(it)( + "signup should fail with duplicate case insensitive username with basic setter", async () => { const user = new Parse.User(); - user.set('username', 'test1'); - user.set('password', 'test'); + user.set("username", "test1"); + user.set("password", "test"); await user.signUp(); const user2 = new Parse.User(); - user2.set('username', 'Test1'); - user2.set('password', 'test'); + user2.set("username", "Test1"); + user2.set("password", "test"); await expectAsync(user2.signUp()).toBeRejectedWith( - new Parse.Error(Parse.Error.USERNAME_TAKEN, 'Account already exists for this username.') + new Parse.Error( + Parse.Error.USERNAME_TAKEN, + "Account already exists for this username." + ) ); } ); - it_id('1cef005b-d5f0-4699-af0c-bb0af27d2437')(it)( - 'signup should fail with duplicate case insensitive username with field specific setter', + it_id("1cef005b-d5f0-4699-af0c-bb0af27d2437")(it)( + "signup should fail with duplicate case insensitive username with field specific setter", async () => { const user = new Parse.User(); - user.setUsername('test1'); - user.setPassword('test'); + user.setUsername("test1"); + user.setPassword("test"); await user.signUp(); const user2 = new Parse.User(); - user2.setUsername('Test1'); - user2.setPassword('test'); + user2.setUsername("Test1"); + user2.setPassword("test"); await expectAsync(user2.signUp()).toBeRejectedWith( - new Parse.Error(Parse.Error.USERNAME_TAKEN, 'Account already exists for this username.') + new Parse.Error( + Parse.Error.USERNAME_TAKEN, + "Account already exists for this username." + ) ); } ); - it_id('12735529-98d1-42c0-b437-3b47fe78ddde')(it)( - 'signup should fail with duplicate case insensitive email', + it_id("12735529-98d1-42c0-b437-3b47fe78ddde")(it)( + "signup should fail with duplicate case insensitive email", async () => { const user = new Parse.User(); - user.setUsername('test1'); - user.setPassword('test'); - user.setEmail('test@example.com'); + user.setUsername("test1"); + user.setPassword("test"); + user.setEmail("test@example.com"); await user.signUp(); const user2 = new Parse.User(); - user2.setUsername('test2'); - user2.setPassword('test'); - user2.setEmail('Test@Example.Com'); + user2.setUsername("test2"); + user2.setPassword("test"); + user2.setEmail("Test@Example.Com"); await expectAsync(user2.signUp()).toBeRejectedWith( - new Parse.Error(Parse.Error.EMAIL_TAKEN, 'Account already exists for this email address.') + new Parse.Error( + Parse.Error.EMAIL_TAKEN, + "Account already exists for this email address." + ) ); } ); - it_id('66e51d52-2420-4b62-8a0d-c7e1b384763e')(it)( - 'edit should fail with duplicate case insensitive email', + it_id("66e51d52-2420-4b62-8a0d-c7e1b384763e")(it)( + "edit should fail with duplicate case insensitive email", async () => { const user = new Parse.User(); - user.setUsername('test1'); - user.setPassword('test'); - user.setEmail('test@example.com'); + user.setUsername("test1"); + user.setPassword("test"); + user.setEmail("test@example.com"); await user.signUp(); const user2 = new Parse.User(); - user2.setUsername('test2'); - user2.setPassword('test'); - user2.setEmail('Foo@Example.Com'); + user2.setUsername("test2"); + user2.setPassword("test"); + user2.setEmail("Foo@Example.Com"); await user2.signUp(); - user2.setEmail('Test@Example.Com'); + user2.setEmail("Test@Example.Com"); await expectAsync(user2.save()).toBeRejectedWith( - new Parse.Error(Parse.Error.EMAIL_TAKEN, 'Account already exists for this email address.') + new Parse.Error( + Parse.Error.EMAIL_TAKEN, + "Account already exists for this email address." + ) ); } ); - describe('anonymous users', () => { - it('should not fail on case insensitive matches', async () => { - spyOn(cryptoUtils, 'randomString').and.returnValue('abcdefghijklmnop'); - const logIn = id => Parse.User.logInWith('anonymous', { authData: { id } }); - const user1 = await logIn('test1'); - const username1 = user1.get('username'); + describe("anonymous users", () => { + it("should not fail on case insensitive matches", async () => { + spyOn(cryptoUtils, "randomString").and.returnValue("abcdefghijklmnop"); + const logIn = id => + Parse.User.logInWith("anonymous", { authData: { id } }); + const user1 = await logIn("test1"); + const username1 = user1.get("username"); - cryptoUtils.randomString.and.returnValue('ABCDEFGHIJKLMNOp'); - const user2 = await logIn('test2'); - const username2 = user2.get('username'); + cryptoUtils.randomString.and.returnValue("ABCDEFGHIJKLMNOp"); + const user2 = await logIn("test2"); + const username2 = user2.get("username"); expect(username1).not.toBeUndefined(); expect(username2).not.toBeUndefined(); - expect(username1.toLowerCase()).toBe('abcdefghijklmnop'); - expect(username2.toLowerCase()).toBe('abcdefghijklmnop'); + expect(username1.toLowerCase()).toBe("abcdefghijklmnop"); + expect(username2.toLowerCase()).toBe("abcdefghijklmnop"); expect(username2).not.toBe(username1); expect(username2.toLowerCase()).toBe(username1.toLowerCase()); // this is redundant :). }); }); }); - it('user cannot update email to existing user', done => { + it("user cannot update email to existing user", done => { const user = new Parse.User(); - user.set('username', 'test1'); - user.set('password', 'test'); - user.set('email', 'test@test.com'); + user.set("username", "test1"); + user.set("password", "test"); + user.set("email", "test@test.com"); user .signUp() .then(() => { const user2 = new Parse.User(); - user2.set('username', 'test2'); - user2.set('password', 'test'); + user2.set("username", "test2"); + user2.set("password", "test"); return user2.signUp(); }) .then(user2 => { - user2.set('email', 'test@test.com'); + user2.set("email", "test@test.com"); return user2.save(); }) .then( () => { - fail('Should not have been able to sign up.'); + fail("Should not have been able to sign up."); done(); }, () => { @@ -2381,19 +2481,19 @@ describe('Parse.User testing', () => { ); }); - it('unset user email', done => { + it("unset user email", done => { const user = new Parse.User(); - user.set('username', 'test'); - user.set('password', 'test'); - user.set('email', 'test@test.com'); + user.set("username", "test"); + user.set("password", "test"); + user.set("email", "test@test.com"); user .signUp() .then(() => { - user.unset('email'); + user.unset("email"); return user.save(); }) .then(() => { - return Parse.User.logIn('test', 'test'); + return Parse.User.logIn("test", "test"); }) .then(user => { expect(user.getEmail()).toBeUndefined(); @@ -2401,115 +2501,115 @@ describe('Parse.User testing', () => { }); }); - it('create session from user', done => { + it("create session from user", done => { Promise.resolve() .then(() => { - return Parse.User.signUp('finn', 'human', { foo: 'bar' }); + return Parse.User.signUp("finn", "human", { foo: "bar" }); }) .then(user => { request({ - method: 'POST', + method: "POST", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Session-Token': user.getSessionToken(), - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-Session-Token": user.getSessionToken(), + "X-Parse-REST-API-Key": "rest", }, - url: 'http://localhost:8378/1/sessions', + url: "http://localhost:8378/1/sessions", }).then(response => { const b = response.data; - expect(typeof b.sessionToken).toEqual('string'); - expect(typeof b.createdWith).toEqual('object'); - expect(b.createdWith.action).toEqual('create'); - expect(typeof b.user).toEqual('object'); + expect(typeof b.sessionToken).toEqual("string"); + expect(typeof b.createdWith).toEqual("object"); + expect(b.createdWith.action).toEqual("create"); + expect(typeof b.user).toEqual("object"); expect(b.user.objectId).toEqual(user.id); done(); }); }); }); - it('user get session from token on signup', async () => { - const user = await Parse.User.signUp('finn', 'human', { foo: 'bar' }); + it("user get session from token on signup", async () => { + const user = await Parse.User.signUp("finn", "human", { foo: "bar" }); const response = await request({ headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Session-Token': user.getSessionToken(), - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-Session-Token": user.getSessionToken(), + "X-Parse-REST-API-Key": "rest", }, - url: 'http://localhost:8378/1/sessions/me', + url: "http://localhost:8378/1/sessions/me", }); const data = response.data; - expect(typeof data.sessionToken).toEqual('string'); - expect(typeof data.createdWith).toEqual('object'); - expect(data.createdWith.action).toEqual('signup'); - expect(data.createdWith.authProvider).toEqual('password'); - expect(typeof data.user).toEqual('object'); + expect(typeof data.sessionToken).toEqual("string"); + expect(typeof data.createdWith).toEqual("object"); + expect(data.createdWith.action).toEqual("signup"); + expect(data.createdWith.authProvider).toEqual("password"); + expect(typeof data.user).toEqual("object"); expect(data.user.objectId).toEqual(user.id); }); - it('user get session from token on username/password login', async () => { - await Parse.User.signUp('finn', 'human', { foo: 'bar' }); + it("user get session from token on username/password login", async () => { + await Parse.User.signUp("finn", "human", { foo: "bar" }); await Parse.User.logOut(); - const user = await Parse.User.logIn('finn', 'human'); + const user = await Parse.User.logIn("finn", "human"); const response = await request({ headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Session-Token': user.getSessionToken(), - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-Session-Token": user.getSessionToken(), + "X-Parse-REST-API-Key": "rest", }, - url: 'http://localhost:8378/1/sessions/me', + url: "http://localhost:8378/1/sessions/me", }); const data = response.data; - expect(typeof data.sessionToken).toEqual('string'); - expect(typeof data.createdWith).toEqual('object'); - expect(data.createdWith.action).toEqual('login'); - expect(data.createdWith.authProvider).toEqual('password'); - expect(typeof data.user).toEqual('object'); + expect(typeof data.sessionToken).toEqual("string"); + expect(typeof data.createdWith).toEqual("object"); + expect(data.createdWith.action).toEqual("login"); + expect(data.createdWith.authProvider).toEqual("password"); + expect(typeof data.user).toEqual("object"); expect(data.user.objectId).toEqual(user.id); }); - it('user get session from token on anonymous login', async () => { + it("user get session from token on anonymous login", async () => { const user = await Parse.AnonymousUtils.logIn(); const response = await request({ headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Session-Token': user.getSessionToken(), - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-Session-Token": user.getSessionToken(), + "X-Parse-REST-API-Key": "rest", }, - url: 'http://localhost:8378/1/sessions/me', + url: "http://localhost:8378/1/sessions/me", }); const data = response.data; - expect(typeof data.sessionToken).toEqual('string'); - expect(typeof data.createdWith).toEqual('object'); - expect(data.createdWith.action).toEqual('login'); - expect(data.createdWith.authProvider).toEqual('anonymous'); - expect(typeof data.user).toEqual('object'); + expect(typeof data.sessionToken).toEqual("string"); + expect(typeof data.createdWith).toEqual("object"); + expect(data.createdWith.action).toEqual("login"); + expect(data.createdWith.authProvider).toEqual("anonymous"); + expect(typeof data.user).toEqual("object"); expect(data.user.objectId).toEqual(user.id); }); - it('user update session with other field', done => { + it("user update session with other field", done => { Promise.resolve() .then(() => { - return Parse.User.signUp('finn', 'human', { foo: 'bar' }); + return Parse.User.signUp("finn", "human", { foo: "bar" }); }) .then(user => { request({ headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Session-Token': user.getSessionToken(), - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-Session-Token": user.getSessionToken(), + "X-Parse-REST-API-Key": "rest", }, - url: 'http://localhost:8378/1/sessions/me', + url: "http://localhost:8378/1/sessions/me", }).then(response => { const b = response.data; request({ - method: 'PUT', + method: "PUT", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Session-Token': user.getSessionToken(), - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-Session-Token": user.getSessionToken(), + "X-Parse-REST-API-Key": "rest", }, - url: 'http://localhost:8378/1/sessions/' + b.objectId, - body: JSON.stringify({ foo: 'bar' }), + url: "http://localhost:8378/1/sessions/" + b.objectId, + body: JSON.stringify({ foo: "bar" }), }).then(() => { done(); }); @@ -2517,45 +2617,45 @@ describe('Parse.User testing', () => { }); }); - it('cannot update session if invalid or no session token', done => { + it("cannot update session if invalid or no session token", done => { Promise.resolve() .then(() => { - return Parse.User.signUp('finn', 'human', { foo: 'bar' }); + return Parse.User.signUp("finn", "human", { foo: "bar" }); }) .then(user => { request({ headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Session-Token': user.getSessionToken(), - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-Session-Token": user.getSessionToken(), + "X-Parse-REST-API-Key": "rest", }, - url: 'http://localhost:8378/1/sessions/me', + url: "http://localhost:8378/1/sessions/me", }).then(response => { const b = response.data; request({ - method: 'PUT', + method: "PUT", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Session-Token': 'foo', - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": "test", + "X-Parse-Session-Token": "foo", + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }, - url: 'http://localhost:8378/1/sessions/' + b.objectId, - body: JSON.stringify({ foo: 'bar' }), + url: "http://localhost:8378/1/sessions/" + b.objectId, + body: JSON.stringify({ foo: "bar" }), }).then(fail, response => { const b = response.data; - expect(b.error).toBe('Invalid session token'); + expect(b.error).toBe("Invalid session token"); request({ - method: 'PUT', + method: "PUT", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }, - url: 'http://localhost:8378/1/sessions/' + b.objectId, - body: JSON.stringify({ foo: 'bar' }), + url: "http://localhost:8378/1/sessions/" + b.objectId, + body: JSON.stringify({ foo: "bar" }), }).then(fail, response => { const b = response.data; - expect(b.error).toBe('Session token required.'); + expect(b.error).toBe("Session token required."); done(); }); }); @@ -2563,48 +2663,48 @@ describe('Parse.User testing', () => { }); }); - it('get session only for current user', done => { + it("get session only for current user", done => { Promise.resolve() .then(() => { - return Parse.User.signUp('test1', 'test', { foo: 'bar' }); + return Parse.User.signUp("test1", "test", { foo: "bar" }); }) .then(() => { - return Parse.User.signUp('test2', 'test', { foo: 'bar' }); + return Parse.User.signUp("test2", "test", { foo: "bar" }); }) .then(user => { request({ headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Session-Token': user.getSessionToken(), - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-Session-Token": user.getSessionToken(), + "X-Parse-REST-API-Key": "rest", }, - url: 'http://localhost:8378/1/sessions', + url: "http://localhost:8378/1/sessions", }).then(response => { const b = response.data; expect(b.results.length).toEqual(1); - expect(typeof b.results[0].user).toEqual('object'); + expect(typeof b.results[0].user).toEqual("object"); expect(b.results[0].user.objectId).toEqual(user.id); done(); }); }); }); - it('delete session by object', done => { + it("delete session by object", done => { Promise.resolve() .then(() => { - return Parse.User.signUp('test1', 'test', { foo: 'bar' }); + return Parse.User.signUp("test1", "test", { foo: "bar" }); }) .then(() => { - return Parse.User.signUp('test2', 'test', { foo: 'bar' }); + return Parse.User.signUp("test2", "test", { foo: "bar" }); }) .then(user => { request({ headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Session-Token': user.getSessionToken(), - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-Session-Token": user.getSessionToken(), + "X-Parse-REST-API-Key": "rest", }, - url: 'http://localhost:8378/1/sessions', + url: "http://localhost:8378/1/sessions", }).then(response => { const b = response.data; let objId; @@ -2617,25 +2717,25 @@ describe('Parse.User testing', () => { return; } request({ - method: 'DELETE', + method: "DELETE", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Session-Token': user.getSessionToken(), - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-Session-Token": user.getSessionToken(), + "X-Parse-REST-API-Key": "rest", }, - url: 'http://localhost:8378/1/sessions/' + objId, + url: "http://localhost:8378/1/sessions/" + objId, }).then(() => { request({ headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Session-Token': user.getSessionToken(), - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-Session-Token": user.getSessionToken(), + "X-Parse-REST-API-Key": "rest", }, - url: 'http://localhost:8378/1/sessions', + url: "http://localhost:8378/1/sessions", }).then(fail, response => { const b = response.data; expect(b.code).toEqual(209); - expect(b.error).toBe('Invalid session token'); + expect(b.error).toBe("Invalid session token"); done(); }); }); @@ -2643,69 +2743,70 @@ describe('Parse.User testing', () => { }); }); - it('cannot delete session if no sessionToken', done => { + it("cannot delete session if no sessionToken", done => { Promise.resolve() .then(() => { - return Parse.User.signUp('test1', 'test', { foo: 'bar' }); + return Parse.User.signUp("test1", "test", { foo: "bar" }); }) .then(() => { - return Parse.User.signUp('test2', 'test', { foo: 'bar' }); + return Parse.User.signUp("test2", "test", { foo: "bar" }); }) .then(user => { request({ headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Session-Token': user.getSessionToken(), - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-Session-Token": user.getSessionToken(), + "X-Parse-REST-API-Key": "rest", }, - url: 'http://localhost:8378/1/sessions', + url: "http://localhost:8378/1/sessions", }).then(response => { const b = response.data; expect(b.results.length).toEqual(1); const objId = b.results[0].objectId; request({ - method: 'DELETE', + method: "DELETE", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }, - url: 'http://localhost:8378/1/sessions/' + objId, + url: "http://localhost:8378/1/sessions/" + objId, }).then(fail, response => { const b = response.data; expect(b.code).toEqual(209); - expect(b.error).toBe('Invalid session token'); + expect(b.error).toBe("Invalid session token"); done(); }); }); }); }); - it('password format matches hosted parse', done => { - const hashed = '$2a$10$8/wZJyEuiEaobBBqzTG.jeY.XSFJd0rzaN//ososvEI4yLqI.4aie'; - passwordCrypto.compare('test', hashed).then( + it("password format matches hosted parse", done => { + const hashed = + "$2a$10$8/wZJyEuiEaobBBqzTG.jeY.XSFJd0rzaN//ososvEI4yLqI.4aie"; + passwordCrypto.compare("test", hashed).then( pass => { expect(pass).toBe(true); done(); }, () => { - fail('Password format did not match.'); + fail("Password format did not match."); done(); } ); }); - it('changing password clears sessions', done => { + it("changing password clears sessions", done => { let sessionToken = null; Promise.resolve() .then(function () { - return Parse.User.signUp('fosco', 'parse'); + return Parse.User.signUp("fosco", "parse"); }) .then(function (newUser) { equal(Parse.User.current(), newUser); sessionToken = newUser.getSessionToken(); ok(sessionToken); - newUser.set('password', 'facebook'); + newUser.set("password", "facebook"); return newUser.save(); }) .then(function () { @@ -2713,28 +2814,28 @@ describe('Parse.User testing', () => { }) .then( function () { - fail('Session should have been invalidated'); + fail("Session should have been invalidated"); done(); }, function (err) { expect(err.code).toBe(Parse.Error.INVALID_SESSION_TOKEN); - expect(err.message).toBe('Invalid session token'); + expect(err.message).toBe("Invalid session token"); done(); } ); }); - it('test parse user become', done => { + it("test parse user become", done => { let sessionToken = null; Promise.resolve() .then(function () { - return Parse.User.signUp('flessard', 'folo', { foo: 1 }); + return Parse.User.signUp("flessard", "folo", { foo: 1 }); }) .then(function (newUser) { equal(Parse.User.current(), newUser); sessionToken = newUser.getSessionToken(); ok(sessionToken); - newUser.set('foo', 2); + newUser.set("foo", 2); return newUser.save(); }) .then(function () { @@ -2742,23 +2843,23 @@ describe('Parse.User testing', () => { }) .then( function (newUser) { - equal(newUser.get('foo'), 2); + equal(newUser.get("foo"), 2); done(); }, function () { - fail('The session should still be valid'); + fail("The session should still be valid"); done(); } ); }); - it('ensure logout works', done => { + it("ensure logout works", done => { let user = null; let sessionToken = null; Promise.resolve() .then(function () { - return Parse.User.signUp('log', 'out'); + return Parse.User.signUp("log", "out"); }) .then(newUser => { user = newUser; @@ -2766,12 +2867,12 @@ describe('Parse.User testing', () => { return Parse.User.logOut(); }) .then(() => { - user.set('foo', 'bar'); + user.set("foo", "bar"); return user.save(null, { sessionToken: sessionToken }); }) .then( () => { - fail('Save should have failed.'); + fail("Save should have failed."); done(); }, e => { @@ -2781,135 +2882,137 @@ describe('Parse.User testing', () => { ); }); - it('support user/password signup with empty authData block', done => { + it("support user/password signup with empty authData block", done => { // The android SDK can send an empty authData object along with username and password. - Parse.User.signUp('artof', 'thedeal', { authData: {} }).then( + Parse.User.signUp("artof", "thedeal", { authData: {} }).then( () => { done(); }, () => { - fail('Signup should have succeeded.'); + fail("Signup should have succeeded."); done(); } ); }); - it('session expiresAt correct format', async done => { - await Parse.User.signUp('asdf', 'zxcv'); + it("session expiresAt correct format", async done => { + await Parse.User.signUp("asdf", "zxcv"); request({ - url: 'http://localhost:8378/1/classes/_Session', + url: "http://localhost:8378/1/classes/_Session", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-Master-Key": "test", }, }).then(response => { const body = response.data; - expect(body.results[0].expiresAt.__type).toEqual('Date'); + expect(body.results[0].expiresAt.__type).toEqual("Date"); done(); }); }); - it('Invalid session tokens are rejected', async done => { - await Parse.User.signUp('asdf', 'zxcv'); + it("Invalid session tokens are rejected", async done => { + await Parse.User.signUp("asdf", "zxcv"); request({ - url: 'http://localhost:8378/1/classes/AClass', + url: "http://localhost:8378/1/classes/AClass", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Rest-API-Key': 'rest', - 'X-Parse-Session-Token': 'text', + "X-Parse-Application-Id": "test", + "X-Parse-Rest-API-Key": "rest", + "X-Parse-Session-Token": "text", }, }).then(fail, response => { const body = response.data; expect(body.code).toBe(209); - expect(body.error).toBe('Invalid session token'); + expect(body.error).toBe("Invalid session token"); done(); }); }); - it_exclude_dbs(['postgres'])( - 'should cleanup null authData keys (regression test for #935)', + it_exclude_dbs(["postgres"])( + "should cleanup null authData keys (regression test for #935)", done => { const database = Config.get(Parse.applicationId).database; database .create( - '_User', + "_User", { - username: 'user', - _hashed_password: '$2a$10$8/wZJyEuiEaobBBqzTG.jeY.XSFJd0rzaN//ososvEI4yLqI.4aie', + username: "user", + _hashed_password: + "$2a$10$8/wZJyEuiEaobBBqzTG.jeY.XSFJd0rzaN//ososvEI4yLqI.4aie", _auth_data_facebook: null, }, {} ) .then(() => { return request({ - url: 'http://localhost:8378/1/login?username=user&password=test', + url: "http://localhost:8378/1/login?username=user&password=test", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-Master-Key": "test", }, }).then(res => res.data); }) .then(user => { const authData = user.authData; - expect(user.username).toEqual('user'); + expect(user.username).toEqual("user"); expect(authData).toBeUndefined(); done(); }) .catch(() => { - fail('this should not fail'); + fail("this should not fail"); done(); }); } ); - it_exclude_dbs(['postgres'])('should not serve null authData keys', done => { + it_exclude_dbs(["postgres"])("should not serve null authData keys", done => { const database = Config.get(Parse.applicationId).database; database .create( - '_User', + "_User", { - username: 'user', - _hashed_password: '$2a$10$8/wZJyEuiEaobBBqzTG.jeY.XSFJd0rzaN//ososvEI4yLqI.4aie', + username: "user", + _hashed_password: + "$2a$10$8/wZJyEuiEaobBBqzTG.jeY.XSFJd0rzaN//ososvEI4yLqI.4aie", _auth_data_facebook: null, }, {} ) .then(() => { return new Parse.Query(Parse.User) - .equalTo('username', 'user') + .equalTo("username", "user") .first({ useMasterKey: true }); }) .then(user => { - const authData = user.get('authData'); - expect(user.get('username')).toEqual('user'); + const authData = user.get("authData"); + expect(user.get("username")).toEqual("user"); expect(authData).toBeUndefined(); done(); }) .catch(() => { - fail('this should not fail'); + fail("this should not fail"); done(); }); }); - it('should cleanup null authData keys ParseUser update (regression test for #1198, #2252)', done => { - Parse.Cloud.beforeSave('_User', req => { - req.object.set('foo', 'bar'); + it("should cleanup null authData keys ParseUser update (regression test for #1198, #2252)", done => { + Parse.Cloud.beforeSave("_User", req => { + req.object.set("foo", "bar"); }); let originalSessionToken; let originalUserId; // Simulate anonymous user save request({ - method: 'POST', - url: 'http://localhost:8378/1/classes/_User', + method: "POST", + url: "http://localhost:8378/1/classes/_User", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }, body: { authData: { - anonymous: { id: '00000000-0000-0000-0000-000000000001' }, + anonymous: { id: "00000000-0000-0000-0000-000000000001" }, }, }, }) @@ -2919,53 +3022,53 @@ describe('Parse.User testing', () => { originalUserId = user.objectId; // Simulate registration return request({ - method: 'PUT', - url: 'http://localhost:8378/1/classes/_User/' + user.objectId, + method: "PUT", + url: "http://localhost:8378/1/classes/_User/" + user.objectId, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Session-Token': user.sessionToken, - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Session-Token": user.sessionToken, + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }, body: { authData: { anonymous: null }, - username: 'user', - password: 'password', + username: "user", + password: "password", }, }).then(response => { return response.data; }); }) .then(user => { - expect(typeof user).toEqual('object'); + expect(typeof user).toEqual("object"); expect(user.authData).toBeUndefined(); expect(user.sessionToken).not.toBeUndefined(); // Session token should have changed expect(user.sessionToken).not.toEqual(originalSessionToken); // test that the sessionToken is valid return request({ - url: 'http://localhost:8378/1/users/me', + url: "http://localhost:8378/1/users/me", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Session-Token': user.sessionToken, - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Session-Token": user.sessionToken, + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }, }).then(response => { const body = response.data; - expect(body.username).toEqual('user'); + expect(body.username).toEqual("user"); expect(body.objectId).toEqual(originalUserId); done(); }); }) .catch(err => { - fail('no request should fail: ' + JSON.stringify(err)); + fail("no request should fail: " + JSON.stringify(err)); done(); }); }); - it_id('1be98368-19ac-4c77-8531-762a114f43fb')(it)( - 'should send email when upgrading from anon', + it_id("1be98368-19ac-4c77-8531-762a114f43fb")(it)( + "should send email when upgrading from anon", async done => { await reconfigureServer(); let emailCalled = false; @@ -2979,42 +3082,42 @@ describe('Parse.User testing', () => { sendMail: () => Promise.resolve(), }; await reconfigureServer({ - appName: 'unused', + appName: "unused", verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }); // Simulate anonymous user save return request({ - method: 'POST', - url: 'http://localhost:8378/1/classes/_User', + method: "POST", + url: "http://localhost:8378/1/classes/_User", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }, body: { authData: { - anonymous: { id: '00000000-0000-0000-0000-000000000001' }, + anonymous: { id: "00000000-0000-0000-0000-000000000001" }, }, }, }) .then(response => { const user = response.data; return request({ - method: 'PUT', - url: 'http://localhost:8378/1/classes/_User/' + user.objectId, + method: "PUT", + url: "http://localhost:8378/1/classes/_User/" + user.objectId, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Session-Token': user.sessionToken, - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Session-Token": user.sessionToken, + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }, body: { authData: { anonymous: null }, - username: 'user', - email: 'user@email.com', - password: 'password', + username: "user", + email: "user@email.com", + password: "password", }, }); }) @@ -3022,19 +3125,19 @@ describe('Parse.User testing', () => { .then(() => { expect(emailCalled).toBe(true); expect(emailOptions).not.toBeUndefined(); - expect(emailOptions.user.get('email')).toEqual('user@email.com'); + expect(emailOptions.user.get("email")).toEqual("user@email.com"); done(); }) .catch(err => { jfail(err); - fail('no request should fail: ' + JSON.stringify(err)); + fail("no request should fail: " + JSON.stringify(err)); done(); }); } ); - it_id('bf668670-39fa-44d3-a9a9-cad52f36d272')(it)( - 'should not send email when email is not a string', + it_id("bf668670-39fa-44d3-a9a9-cad52f36d272")(it)( + "should not send email when email is not a string", async done => { let emailCalled = false; let emailOptions; @@ -3047,57 +3150,59 @@ describe('Parse.User testing', () => { sendMail: () => Promise.resolve(), }; await reconfigureServer({ - appName: 'unused', + appName: "unused", verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }); const user = new Parse.User(); - user.set('username', 'asdf@jkl.com'); - user.set('password', 'zxcv'); - user.set('email', 'asdf@jkl.com'); + user.set("username", "asdf@jkl.com"); + user.set("password", "zxcv"); + user.set("email", "asdf@jkl.com"); await user.signUp(); request({ - method: 'POST', - url: 'http://localhost:8378/1/requestPasswordReset', + method: "POST", + url: "http://localhost:8378/1/requestPasswordReset", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Session-Token': user.sessionToken, - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Session-Token": user.sessionToken, + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }, body: { - email: { $regex: '^asd' }, + email: { $regex: "^asd" }, }, }) .then(res => { - fail('no request should succeed: ' + JSON.stringify(res)); + fail("no request should succeed: " + JSON.stringify(res)); done(); }) .catch(err => { expect(emailCalled).toBeTruthy(); expect(emailOptions).toBeDefined(); expect(err.status).toBe(400); - expect(err.text).toMatch('{"code":125,"error":"you must provide a valid email string"}'); + expect(err.text).toMatch( + '{"code":125,"error":"you must provide a valid email string"}' + ); done(); }); } ); - it('should aftersave with full object', done => { + it("should aftersave with full object", done => { let hit = 0; - Parse.Cloud.afterSave('_User', (req, res) => { + Parse.Cloud.afterSave("_User", (req, res) => { hit++; - expect(req.object.get('username')).toEqual('User'); + expect(req.object.get("username")).toEqual("User"); res.success(); }); const user = new Parse.User(); - user.setUsername('User'); - user.setPassword('pass'); + user.setUsername("User"); + user.setPassword("pass"); user .signUp() .then(() => { - user.set('hello', 'world'); + user.set("hello", "world"); return user.save(); }) .then(() => { @@ -3106,44 +3211,44 @@ describe('Parse.User testing', () => { }); }); - it('changes to a user should update the cache', done => { - Parse.Cloud.define('testUpdatedUser', req => { - expect(req.user.get('han')).toEqual('solo'); + it("changes to a user should update the cache", done => { + Parse.Cloud.define("testUpdatedUser", req => { + expect(req.user.get("han")).toEqual("solo"); return {}; }); const user = new Parse.User(); - user.setUsername('harrison'); - user.setPassword('ford'); + user.setUsername("harrison"); + user.setPassword("ford"); user .signUp() .then(() => { - user.set('han', 'solo'); + user.set("han", "solo"); return user.save(); }) .then(() => { - return Parse.Cloud.run('testUpdatedUser'); + return Parse.Cloud.run("testUpdatedUser"); }) .then( () => { done(); }, () => { - fail('Should not have failed.'); + fail("Should not have failed."); done(); } ); }); - it('should fail to become user with expired token', done => { + it("should fail to become user with expired token", done => { let token; - Parse.User.signUp('auser', 'somepass', null) + Parse.User.signUp("auser", "somepass", null) .then(() => request({ - method: 'GET', - url: 'http://localhost:8378/1/classes/_Session', + method: "GET", + url: "http://localhost:8378/1/classes/_Session", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-Master-Key": "test", }, }) ) @@ -3153,84 +3258,84 @@ describe('Parse.User testing', () => { const expiresAt = new Date(new Date().setYear(2015)); token = body.results[0].sessionToken; return request({ - method: 'PUT', - url: 'http://localhost:8378/1/classes/_Session/' + id, + method: "PUT", + url: "http://localhost:8378/1/classes/_Session/" + id, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key': 'test', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": "test", + "X-Parse-Master-Key": "test", + "Content-Type": "application/json", }, body: { - expiresAt: { __type: 'Date', iso: expiresAt.toISOString() }, + expiresAt: { __type: "Date", iso: expiresAt.toISOString() }, }, }); }) .then(() => Parse.User.become(token)) .then( () => { - fail('Should not have succeded'); + fail("Should not have succeded"); done(); }, error => { expect(error.code).toEqual(209); - expect(error.message).toEqual('Session token is expired.'); + expect(error.message).toEqual("Session token is expired."); done(); } ) .catch(done.fail); }); - it('should return current session with expired expiration date', async () => { - await Parse.User.signUp('buser', 'somepass', null); + it("should return current session with expired expiration date", async () => { + await Parse.User.signUp("buser", "somepass", null); const response = await request({ - method: 'GET', - url: 'http://localhost:8378/1/classes/_Session', + method: "GET", + url: "http://localhost:8378/1/classes/_Session", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-Master-Key": "test", }, }); const body = response.data; const id = body.results[0].objectId; const expiresAt = new Date(new Date().setYear(2015)); await request({ - method: 'PUT', - url: 'http://localhost:8378/1/classes/_Session/' + id, + method: "PUT", + url: "http://localhost:8378/1/classes/_Session/" + id, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key': 'test', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": "test", + "X-Parse-Master-Key": "test", + "Content-Type": "application/json", }, body: { - expiresAt: { __type: 'Date', iso: expiresAt.toISOString() }, + expiresAt: { __type: "Date", iso: expiresAt.toISOString() }, }, }); const session = await Parse.Session.current(); - expect(session.get('expiresAt')).toEqual(expiresAt); + expect(session.get("expiresAt")).toEqual(expiresAt); }); - it('should not create extraneous session tokens', done => { + it("should not create extraneous session tokens", done => { const config = Config.get(Parse.applicationId); config.database .loadSchema() .then(s => { // Lock down the _User class for creation - return s.addClassIfNotExists('_User', {}, { create: {} }); + return s.addClassIfNotExists("_User", {}, { create: {} }); }) .then(() => { const user = new Parse.User(); - return user.save({ username: 'user', password: 'pass' }); + return user.save({ username: "user", password: "pass" }); }) .then( () => { - fail('should not be able to save the user'); + fail("should not be able to save the user"); }, () => { return Promise.resolve(); } ) .then(() => { - const q = new Parse.Query('_Session'); + const q = new Parse.Query("_Session"); return q.find({ useMasterKey: true }); }) .then( @@ -3240,55 +3345,55 @@ describe('Parse.User testing', () => { done(); }, () => { - fail('should not fail'); + fail("should not fail"); done(); } ); }); - it('should not overwrite username when unlinking facebook user (regression test for #1532)', async done => { + it("should not overwrite username when unlinking facebook user (regression test for #1532)", async done => { Parse.Object.disableSingleInstance(); const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); let user = new Parse.User(); - user.set('username', 'testLinkWithProvider'); - user.set('password', 'mypass'); + user.set("username", "testLinkWithProvider"); + user.set("password", "mypass"); await user.signUp(); - await user._linkWith('facebook'); - expect(user.get('username')).toEqual('testLinkWithProvider'); + await user._linkWith("facebook"); + expect(user.get("username")).toEqual("testLinkWithProvider"); expect(Parse.FacebookUtils.isLinked(user)).toBeTruthy(); - await user._unlinkFrom('facebook'); + await user._unlinkFrom("facebook"); user = await user.fetch(); - expect(user.get('username')).toEqual('testLinkWithProvider'); + expect(user.get("username")).toEqual("testLinkWithProvider"); expect(Parse.FacebookUtils.isLinked(user)).toBeFalsy(); done(); }); it('should revoke sessions when converting anonymous user to "normal" user', done => { request({ - method: 'POST', - url: 'http://localhost:8378/1/classes/_User', + method: "POST", + url: "http://localhost:8378/1/classes/_User", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }, body: { authData: { - anonymous: { id: '00000000-0000-0000-0000-000000000001' }, + anonymous: { id: "00000000-0000-0000-0000-000000000001" }, }, }, }).then(response => { const body = response.data; Parse.User.become(body.sessionToken).then(user => { - const obj = new Parse.Object('TestObject'); + const obj = new Parse.Object("TestObject"); obj.setACL(new Parse.ACL(user)); return obj .save() .then(() => { // Change password, revoking session - user.set('username', 'no longer anonymous'); - user.set('password', 'password'); + user.set("username", "no longer anonymous"); + user.set("password", "password"); return user.save(); }) .then(() => { @@ -3300,40 +3405,40 @@ describe('Parse.User testing', () => { done(); }) .catch(() => { - fail('should not fail'); + fail("should not fail"); done(); }); }); }); }); - it('should not revoke session tokens if the server is configures to not revoke session tokens', done => { + it("should not revoke session tokens if the server is configures to not revoke session tokens", done => { reconfigureServer({ revokeSessionOnPasswordReset: false }).then(() => { request({ - method: 'POST', - url: 'http://localhost:8378/1/classes/_User', + method: "POST", + url: "http://localhost:8378/1/classes/_User", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }, body: { authData: { - anonymous: { id: '00000000-0000-0000-0000-000000000001' }, + anonymous: { id: "00000000-0000-0000-0000-000000000001" }, }, }, }).then(response => { const body = response.data; Parse.User.become(body.sessionToken).then(user => { - const obj = new Parse.Object('TestObject'); + const obj = new Parse.Object("TestObject"); obj.setACL(new Parse.ACL(user)); return ( obj .save() .then(() => { // Change password, revoking session - user.set('username', 'no longer anonymous'); - user.set('password', 'password'); + user.set("username", "no longer anonymous"); + user.set("password", "password"); return user.save(); }) .then(() => obj.fetch()) @@ -3345,16 +3450,16 @@ describe('Parse.User testing', () => { }); }); - it('should not fail querying non existing relations', done => { + it("should not fail querying non existing relations", done => { const user = new Parse.User(); user.set({ - username: 'hello', - password: 'world', + username: "hello", + password: "world", }); user .signUp() .then(() => { - return Parse.User.current().relation('relation').query().find(); + return Parse.User.current().relation("relation").query().find(); }) .then(res => { expect(res.length).toBe(0); @@ -3366,7 +3471,7 @@ describe('Parse.User testing', () => { }); }); - it('should not allow updates to emailVerified', done => { + it("should not allow updates to emailVerified", done => { const emailAdapter = { sendVerificationEmail: () => {}, sendPasswordResetEmail: () => Promise.resolve(), @@ -3375,34 +3480,36 @@ describe('Parse.User testing', () => { const user = new Parse.User(); user.set({ - username: 'hello', - password: 'world', - email: 'test@email.com', + username: "hello", + password: "world", + email: "test@email.com", }); reconfigureServer({ - appName: 'unused', + appName: "unused", verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }) .then(() => { return user.signUp(); }) .then(() => { - return Parse.User.current().set('emailVerified', true).save(); + return Parse.User.current().set("emailVerified", true).save(); }) .then(() => { - fail('Should not be able to update emailVerified'); + fail("Should not be able to update emailVerified"); done(); }) .catch(err => { - expect(err.message).toBe("Clients aren't allowed to manually update email verification."); + expect(err.message).toBe( + "Clients aren't allowed to manually update email verification." + ); done(); }); }); - it('should not retrieve hidden fields on GET users/me (#3432)', done => { + it("should not retrieve hidden fields on GET users/me (#3432)", done => { const emailAdapter = { sendVerificationEmail: () => {}, sendPasswordResetEmail: () => Promise.resolve(), @@ -3411,28 +3518,28 @@ describe('Parse.User testing', () => { const user = new Parse.User(); user.set({ - username: 'hello', - password: 'world', - email: 'test@email.com', + username: "hello", + password: "world", + email: "test@email.com", }); reconfigureServer({ - appName: 'unused', + appName: "unused", verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }) .then(() => { return user.signUp(); }) .then(() => request({ - method: 'GET', - url: 'http://localhost:8378/1/users/me', + method: "GET", + url: "http://localhost:8378/1/users/me", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Session-Token': Parse.User.current().getSessionToken(), - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Session-Token": Parse.User.current().getSessionToken(), + "X-Parse-REST-API-Key": "rest", }, }) ) @@ -3445,7 +3552,7 @@ describe('Parse.User testing', () => { .catch(done.fail); }); - it('should not retrieve hidden fields on GET users/id (#3432)', done => { + it("should not retrieve hidden fields on GET users/id (#3432)", done => { const emailAdapter = { sendVerificationEmail: () => {}, sendPasswordResetEmail: () => Promise.resolve(), @@ -3454,30 +3561,30 @@ describe('Parse.User testing', () => { const user = new Parse.User(); user.set({ - username: 'hello', - password: 'world', - email: 'test@email.com', + username: "hello", + password: "world", + email: "test@email.com", }); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); user.setACL(acl); reconfigureServer({ - appName: 'unused', + appName: "unused", verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }) .then(() => { return user.signUp(); }) .then(() => request({ - method: 'GET', - url: 'http://localhost:8378/1/users/' + Parse.User.current().id, + method: "GET", + url: "http://localhost:8378/1/users/" + Parse.User.current().id, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", }, }) ) @@ -3493,7 +3600,7 @@ describe('Parse.User testing', () => { }); }); - it('should not retrieve hidden fields on login (#3432)', done => { + it("should not retrieve hidden fields on login (#3432)", done => { const emailAdapter = { sendVerificationEmail: () => {}, sendPasswordResetEmail: () => Promise.resolve(), @@ -3502,26 +3609,26 @@ describe('Parse.User testing', () => { const user = new Parse.User(); user.set({ - username: 'hello', - password: 'world', - email: 'test@email.com', + username: "hello", + password: "world", + email: "test@email.com", }); reconfigureServer({ - appName: 'unused', + appName: "unused", verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }) .then(() => { return user.signUp(); }) .then(() => request({ - url: 'http://localhost:8378/1/login?email=test@email.com&username=hello&password=world', + url: "http://localhost:8378/1/login?email=test@email.com&username=hello&password=world", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", }, }) ) @@ -3537,7 +3644,7 @@ describe('Parse.User testing', () => { }); }); - it('should not allow updates to hidden fields', async () => { + it("should not allow updates to hidden fields", async () => { const emailAdapter = { sendVerificationEmail: () => {}, sendPasswordResetEmail: () => Promise.resolve(), @@ -3545,24 +3652,27 @@ describe('Parse.User testing', () => { }; const user = new Parse.User(); user.set({ - username: 'hello', - password: 'world', - email: 'test@email.com', + username: "hello", + password: "world", + email: "test@email.com", }); await reconfigureServer({ - appName: 'unused', + appName: "unused", verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }); await user.signUp(); - user.set('_email_verify_token', 'bad', { ignoreValidation: true }); + user.set("_email_verify_token", "bad", { ignoreValidation: true }); await expectAsync(user.save()).toBeRejectedWith( - new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'Invalid field name: _email_verify_token.') + new Parse.Error( + Parse.Error.INVALID_KEY_NAME, + "Invalid field name: _email_verify_token." + ) ); }); - it('should allow updates to fields with maintenanceKey', async () => { + it("should allow updates to fields with maintenanceKey", async () => { const emailAdapter = { sendVerificationEmail: () => {}, sendPasswordResetEmail: () => Promise.resolve(), @@ -3570,13 +3680,13 @@ describe('Parse.User testing', () => { }; const user = new Parse.User(); user.set({ - username: 'hello', - password: 'world', - email: 'test@example.com', + username: "hello", + password: "world", + email: "test@example.com", }); await reconfigureServer({ - appName: 'unused', - maintenanceKey: 'test2', + appName: "unused", + maintenanceKey: "test2", verifyUserEmails: true, emailVerifyTokenValidityDuration: 5, accountLockout: { @@ -3584,67 +3694,67 @@ describe('Parse.User testing', () => { threshold: 1, }, emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }); await user.signUp(); for (let i = 0; i < 2; i++) { try { - await Parse.User.logIn(user.getEmail(), 'abc'); + await Parse.User.logIn(user.getEmail(), "abc"); } catch (e) { expect(e.code).toBe(Parse.Error.OBJECT_NOT_FOUND); expect( - e.message === 'Invalid username/password.' || + e.message === "Invalid username/password." || e.message === - 'Your account is locked due to multiple failed login attempts. Please try again after 1 minute(s)' + "Your account is locked due to multiple failed login attempts. Please try again after 1 minute(s)" ).toBeTrue(); } } await Parse.User.requestPasswordReset(user.getEmail()); const headers = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Rest-API-Key': 'rest', - 'X-Parse-Maintenance-Key': 'test2', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": "test", + "X-Parse-Rest-API-Key": "rest", + "X-Parse-Maintenance-Key": "test2", + "Content-Type": "application/json", }; const userMaster = await request({ - method: 'GET', + method: "GET", url: `http://localhost:8378/1/classes/_User`, json: true, headers, }).then(res => res.data.results[0]); expect(Object.keys(userMaster).sort()).toEqual( [ - 'ACL', - '_account_lockout_expires_at', - '_email_verify_token', - '_email_verify_token_expires_at', - '_failed_login_count', - '_perishable_token', - 'createdAt', - 'email', - 'emailVerified', - 'objectId', - 'updatedAt', - 'username', + "ACL", + "_account_lockout_expires_at", + "_email_verify_token", + "_email_verify_token_expires_at", + "_failed_login_count", + "_perishable_token", + "createdAt", + "email", + "emailVerified", + "objectId", + "updatedAt", + "username", ].sort() ); const toSet = { _account_lockout_expires_at: new Date(), - _email_verify_token: 'abc', + _email_verify_token: "abc", _email_verify_token_expires_at: new Date(), _failed_login_count: 0, _perishable_token_expires_at: new Date(), - _perishable_token: 'abc', + _perishable_token: "abc", }; await request({ - method: 'PUT', + method: "PUT", headers, - url: Parse.serverURL + '/users/' + userMaster.objectId, + url: Parse.serverURL + "/users/" + userMaster.objectId, json: true, body: toSet, }).then(res => res.data); const update = await request({ - method: 'GET', + method: "GET", url: `http://localhost:8378/1/classes/_User`, json: true, headers, @@ -3661,17 +3771,17 @@ describe('Parse.User testing', () => { } }); - it('should revoke sessions when setting paswword with masterKey (#3289)', done => { + it("should revoke sessions when setting paswword with masterKey (#3289)", done => { let user; - Parse.User.signUp('username', 'password') + Parse.User.signUp("username", "password") .then(newUser => { user = newUser; - user.set('password', 'newPassword'); + user.set("password", "newPassword"); return user.save(null, { useMasterKey: true }); }) .then(() => { - const query = new Parse.Query('_Session'); - query.equalTo('user', user); + const query = new Parse.Query("_Session"); + query.equalTo("user", user); return query.find({ useMasterKey: true }); }) .then(results => { @@ -3680,8 +3790,10 @@ describe('Parse.User testing', () => { }, done.fail); }); - xit('should not send a verification email if the user signed up using oauth', done => { - pending('this test fails. See: https://github.com/parse-community/parse-server/issues/5097'); + xit("should not send a verification email if the user signed up using oauth", done => { + pending( + "this test fails. See: https://github.com/parse-community/parse-server/issues/5097" + ); let emailCalledCount = 0; const emailAdapter = { sendVerificationEmail: () => { @@ -3692,19 +3804,19 @@ describe('Parse.User testing', () => { sendMail: () => Promise.resolve(), }; reconfigureServer({ - appName: 'unused', + appName: "unused", verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }); const user = new Parse.User(); - user.set('email', 'email1@host.com'); + user.set("email", "email1@host.com"); Parse.FacebookUtils.link(user, { - id: '8675309', - access_token: 'jenny', + id: "8675309", + access_token: "jenny", expiration_date: new Date().toJSON(), }).then(user => { - user.set('email', 'email2@host.com'); + user.set("email", "email2@host.com"); user.save().then(() => { expect(emailCalledCount).toBe(0); done(); @@ -3712,7 +3824,7 @@ describe('Parse.User testing', () => { }); }); - it('should be able to update user with authData passed', done => { + it("should be able to update user with authData passed", done => { let objectId; let sessionToken; @@ -3720,24 +3832,24 @@ describe('Parse.User testing', () => { return request({ url: `http://localhost:8378/1/classes/_User/${objectId}`, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Session-Token': sessionToken, + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", + "X-Parse-Session-Token": sessionToken, }, }).then(response => block(response.data)); } request({ - method: 'POST', - url: 'http://localhost:8378/1/classes/_User', + method: "POST", + url: "http://localhost:8378/1/classes/_User", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }, body: { - key: 'value', - authData: { anonymous: { id: '00000000-0000-0000-0000-000000000001' } }, + key: "value", + authData: { anonymous: { id: "00000000-0000-0000-0000-000000000001" } }, }, }) .then(response => { @@ -3748,24 +3860,24 @@ describe('Parse.User testing', () => { expect(objectId).toBeDefined(); return validate(user => { // validate that keys are set on creation - expect(user.key).toBe('value'); + expect(user.key).toBe("value"); }); }) .then(() => { // update the user const options = { - method: 'PUT', + method: "PUT", url: `http://localhost:8378/1/classes/_User/${objectId}`, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Session-Token': sessionToken, - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", + "X-Parse-Session-Token": sessionToken, + "Content-Type": "application/json", }, body: { - key: 'otherValue', + key: "otherValue", authData: { - anonymous: { id: '00000000-0000-0000-0000-000000000001' }, + anonymous: { id: "00000000-0000-0000-0000-000000000001" }, }, }, }; @@ -3774,7 +3886,7 @@ describe('Parse.User testing', () => { .then(() => { return validate(user => { // validate that keys are set on update - expect(user.key).toBe('otherValue'); + expect(user.key).toBe("otherValue"); }); }) .then(() => { @@ -3784,22 +3896,22 @@ describe('Parse.User testing', () => { .catch(done.fail); }); - it('can login with email', done => { + it("can login with email", done => { const user = new Parse.User(); user .save({ - username: 'yolo', - password: 'yolopass', - email: 'yo@lo.com', + username: "yolo", + password: "yolopass", + email: "yo@lo.com", }) .then(() => { const options = { url: `http://localhost:8378/1/login`, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", }, - qs: { email: 'yo@lo.com', password: 'yolopass' }, + qs: { email: "yo@lo.com", password: "yolopass" }, }; return request(options); }) @@ -3807,24 +3919,24 @@ describe('Parse.User testing', () => { .catch(done.fail); }); - it('cannot login with email and invalid password', done => { + it("cannot login with email and invalid password", done => { const user = new Parse.User(); user .save({ - username: 'yolo', - password: 'yolopass', - email: 'yo@lo.com', + username: "yolo", + password: "yolopass", + email: "yo@lo.com", }) .then(() => { const options = { - method: 'POST', + method: "POST", url: `http://localhost:8378/1/login`, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }, - body: { email: 'yo@lo.com', password: 'yolopass2' }, + body: { email: "yo@lo.com", password: "yolopass2" }, }; return request(options); }) @@ -3832,20 +3944,20 @@ describe('Parse.User testing', () => { .catch(() => done()); }); - it('can login with email through query string', done => { + it("can login with email through query string", done => { const user = new Parse.User(); user .save({ - username: 'yolo', - password: 'yolopass', - email: 'yo@lo.com', + username: "yolo", + password: "yolopass", + email: "yo@lo.com", }) .then(() => { const options = { url: `http://localhost:8378/1/login?email=yo@lo.com&password=yolopass`, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", }, }; return request(options); @@ -3854,20 +3966,20 @@ describe('Parse.User testing', () => { .catch(done.fail); }); - it('can login when both email and username are passed', done => { + it("can login when both email and username are passed", done => { const user = new Parse.User(); user .save({ - username: 'yolo', - password: 'yolopass', - email: 'yo@lo.com', + username: "yolo", + password: "yolopass", + email: "yo@lo.com", }) .then(() => { const options = { url: `http://localhost:8378/1/login?email=yo@lo.com&username=yolo&password=yolopass`, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", }, }; return request(options); @@ -3880,23 +3992,23 @@ describe('Parse.User testing', () => { const user = new Parse.User(); user .save({ - username: 'yolo', - password: 'yolopass', - email: 'yo@lo.com', + username: "yolo", + password: "yolopass", + email: "yo@lo.com", }) .then(() => { const options = { url: `http://localhost:8378/1/login?email=yo@lo.com&username=yolo2&password=yolopass`, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", }, }; return request(options); }) .then(done.fail) .catch(err => { - expect(err.data.error).toEqual('Invalid username/password.'); + expect(err.data.error).toEqual("Invalid username/password."); done(); }); }); @@ -3905,150 +4017,150 @@ describe('Parse.User testing', () => { const user = new Parse.User(); user .save({ - username: 'yolo', - password: 'yolopass', - email: 'yo@lo.com', + username: "yolo", + password: "yolopass", + email: "yo@lo.com", }) .then(() => { const options = { url: `http://localhost:8378/1/login?email=yo@lo2.com&username=yolo&password=yolopass`, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", }, }; return request(options); }) .then(done.fail) .catch(err => { - expect(err.data.error).toEqual('Invalid username/password.'); + expect(err.data.error).toEqual("Invalid username/password."); done(); }); }); - it('fails to login when email and username are not provided', done => { + it("fails to login when email and username are not provided", done => { const user = new Parse.User(); user .save({ - username: 'yolo', - password: 'yolopass', - email: 'yo@lo.com', + username: "yolo", + password: "yolopass", + email: "yo@lo.com", }) .then(() => { const options = { url: `http://localhost:8378/1/login?password=yolopass`, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", }, }; return request(options); }) .then(done.fail) .catch(err => { - expect(err.data.error).toEqual('username/email is required.'); + expect(err.data.error).toEqual("username/email is required."); done(); }); }); - it('allows login when providing email as username', done => { + it("allows login when providing email as username", done => { const user = new Parse.User(); user .save({ - username: 'yolo', - password: 'yolopass', - email: 'yo@lo.com', + username: "yolo", + password: "yolopass", + email: "yo@lo.com", }) .then(() => { - return Parse.User.logIn('yo@lo.com', 'yolopass'); + return Parse.User.logIn("yo@lo.com", "yolopass"); }) .then(user => { - expect(user.get('username')).toBe('yolo'); + expect(user.get("username")).toBe("yolo"); }) .then(done) .catch(done.fail); }); - it('handles properly when 2 users share username / email pairs', done => { + it("handles properly when 2 users share username / email pairs", done => { const user = new Parse.User({ - username: 'yo@loname.com', - password: 'yolopass', - email: 'yo@lo.com', + username: "yo@loname.com", + password: "yolopass", + email: "yo@lo.com", }); const user2 = new Parse.User({ - username: 'yo@lo.com', - email: 'yo@loname.com', - password: 'yolopass2', // different passwords + username: "yo@lo.com", + email: "yo@loname.com", + password: "yolopass2", // different passwords }); Parse.Object.saveAll([user, user2]) .then(() => { - return Parse.User.logIn('yo@loname.com', 'yolopass'); + return Parse.User.logIn("yo@loname.com", "yolopass"); }) .then(user => { // the username takes precedence over the email, // so we get the user with username as passed in - expect(user.get('username')).toBe('yo@loname.com'); + expect(user.get("username")).toBe("yo@loname.com"); }) .then(done) .catch(done.fail); }); - it('handles properly when 2 users share username / email pairs, counterpart', done => { + it("handles properly when 2 users share username / email pairs, counterpart", done => { const user = new Parse.User({ - username: 'yo@loname.com', - password: 'yolopass', - email: 'yo@lo.com', + username: "yo@loname.com", + password: "yolopass", + email: "yo@lo.com", }); const user2 = new Parse.User({ - username: 'yo@lo.com', - email: 'yo@loname.com', - password: 'yolopass2', // different passwords + username: "yo@lo.com", + email: "yo@loname.com", + password: "yolopass2", // different passwords }); Parse.Object.saveAll([user, user2]) .then(() => { - return Parse.User.logIn('yo@loname.com', 'yolopass2'); + return Parse.User.logIn("yo@loname.com", "yolopass2"); }) .then(done.fail) .catch(err => { - expect(err.message).toEqual('Invalid username/password.'); + expect(err.message).toEqual("Invalid username/password."); done(); }); }); - it('fails to login when password is not provided', done => { + it("fails to login when password is not provided", done => { const user = new Parse.User(); user .save({ - username: 'yolo', - password: 'yolopass', - email: 'yo@lo.com', + username: "yolo", + password: "yolopass", + email: "yo@lo.com", }) .then(() => { const options = { url: `http://localhost:8378/1/login?username=yolo`, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", }, }; return request(options); }) .then(done.fail) .catch(err => { - expect(err.data.error).toEqual('password is required.'); + expect(err.data.error).toEqual("password is required."); done(); }); }); - it('does not duplicate session when logging in multiple times #3451', done => { + it("does not duplicate session when logging in multiple times #3451", done => { const user = new Parse.User(); user .signUp({ - username: 'yolo', - password: 'yolo', - email: 'yo@lo.com', + username: "yolo", + password: "yolo", + email: "yo@lo.com", }) .then(() => { const token = user.getSessionToken(); @@ -4056,7 +4168,7 @@ describe('Parse.User testing', () => { let count = 0; while (count < 5) { promise = promise.then(() => { - return Parse.User.logIn('yolo', 'yolo').then(res => { + return Parse.User.logIn("yolo", "yolo").then(res => { // ensure a new session token is generated at each login expect(res.getSessionToken()).not.toBe(token); }); @@ -4072,7 +4184,7 @@ describe('Parse.User testing', () => { }); }) .then(() => { - const query = new Parse.Query('_Session'); + const query = new Parse.Query("_Session"); return query.find({ useMasterKey: true }); }) .then(results => { @@ -4082,48 +4194,50 @@ describe('Parse.User testing', () => { .then(done, done.fail); }); - it('should throw OBJECT_NOT_FOUND instead of SESSION_MISSING when using masterKey', async () => { + it("should throw OBJECT_NOT_FOUND instead of SESSION_MISSING when using masterKey", async () => { await reconfigureServer(); // create a fake user (just so we simulate an object not found) - const non_existent_user = Parse.User.createWithoutData('fake_id'); + const non_existent_user = Parse.User.createWithoutData("fake_id"); try { await non_existent_user.destroy({ useMasterKey: true }); - throw ''; + throw ""; } catch (e) { expect(e.code).toBe(Parse.Error.OBJECT_NOT_FOUND); } try { await non_existent_user.save({}, { useMasterKey: true }); - throw ''; + throw ""; } catch (e) { expect(e.code).toBe(Parse.Error.OBJECT_NOT_FOUND); } try { await non_existent_user.save(); - throw ''; + throw ""; } catch (e) { expect(e.code).toBe(Parse.Error.SESSION_MISSING); } try { await non_existent_user.destroy(); - throw ''; + throw ""; } catch (e) { expect(e.code).toBe(Parse.Error.SESSION_MISSING); } }); - it('should throw when enforcePrivateUsers is invalid', async () => { - const options = [[], 'a', 0, {}]; + it("should throw when enforcePrivateUsers is invalid", async () => { + const options = [[], "a", 0, {}]; for (const option of options) { - await expectAsync(reconfigureServer({ enforcePrivateUsers: option })).toBeRejected(); + await expectAsync( + reconfigureServer({ enforcePrivateUsers: option }) + ).toBeRejected(); } }); - it('user login with enforcePrivateUsers', async done => { + it("user login with enforcePrivateUsers", async done => { await reconfigureServer({ enforcePrivateUsers: true }); - await Parse.User.signUp('asdf', 'zxcv'); - const user = await Parse.User.logIn('asdf', 'zxcv'); - equal(user.get('username'), 'asdf'); + await Parse.User.signUp("asdf", "zxcv"); + const user = await Parse.User.logIn("asdf", "zxcv"); + equal(user.get("username"), "asdf"); const ACL = user.getACL(); expect(ACL.getReadAccess(user)).toBe(true); expect(ACL.getWriteAccess(user)).toBe(true); @@ -4133,49 +4247,58 @@ describe('Parse.User testing', () => { expect(Object.keys(perms).length).toBe(1); expect(perms[user.id].read).toBe(true); expect(perms[user.id].write).toBe(true); - expect(perms['*']).toBeUndefined(); + expect(perms["*"]).toBeUndefined(); done(); }); - describe('issue #4897', () => { - it_only_db('mongo')('should be able to login with a legacy user (no ACL)', async () => { - // This issue is a side effect of the locked users and legacy users which don't have ACL's - // In this scenario, a legacy user wasn't be able to login as there's no ACL on it - await reconfigureServer(); - const database = Config.get(Parse.applicationId).database; - const collection = await database.adapter._adaptiveCollection('_User'); - await collection.insertOne({ - _id: 'ABCDEF1234', - name: '', - email: '', - username: '', - _hashed_password: '', - _auth_data_facebook: { - id: '8675309', - access_token: 'jenny', - }, - sessionToken: '', - }); - const provider = getMockFacebookProvider(); - Parse.User._registerAuthenticationProvider(provider); - const model = await Parse.User._logInWith('facebook', {}); - expect(model.id).toBe('ABCDEF1234'); - ok(model instanceof Parse.User, 'Model should be a Parse.User'); - strictEqual(Parse.User.current(), model); - ok(model.extended(), 'Should have used subclass.'); - strictEqual(provider.authData.id, provider.synchronizedUserId); - strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); - strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); - ok(model._isLinked('facebook'), 'User should be linked to facebook'); - }); + describe("issue #4897", () => { + it_only_db("mongo")( + "should be able to login with a legacy user (no ACL)", + async () => { + // This issue is a side effect of the locked users and legacy users which don't have ACL's + // In this scenario, a legacy user wasn't be able to login as there's no ACL on it + await reconfigureServer(); + const database = Config.get(Parse.applicationId).database; + const collection = await database.adapter._adaptiveCollection("_User"); + await collection.insertOne({ + _id: "ABCDEF1234", + name: "", + email: "", + username: "", + _hashed_password: "", + _auth_data_facebook: { + id: "8675309", + access_token: "jenny", + }, + sessionToken: "", + }); + const provider = getMockFacebookProvider(); + Parse.User._registerAuthenticationProvider(provider); + const model = await Parse.User._logInWith("facebook", {}); + expect(model.id).toBe("ABCDEF1234"); + ok(model instanceof Parse.User, "Model should be a Parse.User"); + strictEqual(Parse.User.current(), model); + ok(model.extended(), "Should have used subclass."); + strictEqual(provider.authData.id, provider.synchronizedUserId); + strictEqual( + provider.authData.access_token, + provider.synchronizedAuthToken + ); + strictEqual( + provider.authData.expiration_date, + provider.synchronizedExpiration + ); + ok(model._isLinked("facebook"), "User should be linked to facebook"); + } + ); }); - it('should strip out authdata in LiveQuery', async () => { + it("should strip out authdata in LiveQuery", async () => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); await reconfigureServer({ - liveQuery: { classNames: ['_User'] }, + liveQuery: { classNames: ["_User"] }, startLiveQueryServer: true, verbose: false, silent: true, @@ -4188,16 +4311,16 @@ describe('Parse.User testing', () => { }); const query = new Parse.Query(Parse.User); - query.doesNotExist('foo'); + query.doesNotExist("foo"); const subscription = await query.subscribe(); - const events = ['create', 'update', 'enter', 'leave', 'delete']; + const events = ["create", "update", "enter", "leave", "delete"]; const response = (obj, prev) => { - expect(obj.get('authData')).toBeUndefined(); + expect(obj.get("authData")).toBeUndefined(); expect(obj.authData).toBeUndefined(); expect(prev && prev.authData).toBeUndefined(); if (prev && prev.get) { - expect(prev.get('authData')).toBeUndefined(); + expect(prev.get("authData")).toBeUndefined(); } }; const calls = {}; @@ -4206,12 +4329,12 @@ describe('Parse.User testing', () => { spyOn(calls, key).and.callThrough(); subscription.on(key, calls[key]); } - const user = await Parse.User._logInWith('facebook'); - user.set('foo', 'bar'); + const user = await Parse.User._logInWith("facebook"); + user.set("foo", "bar"); await user.save(); - user.unset('foo'); + user.unset("foo"); await user.save(); - user.set('yolo', 'bar'); + user.set("yolo", "bar"); await user.save(); await user.destroy(); await new Promise(resolve => setTimeout(resolve, 10)); @@ -4219,33 +4342,34 @@ describe('Parse.User testing', () => { expect(calls[key]).toHaveBeenCalled(); } subscription.unsubscribe(); - const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); + const client = + await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); client.close(); await new Promise(resolve => setTimeout(resolve, 10)); }); }); -describe('Security Advisory GHSA-8w3j-g983-8jh5', function () { - it_only_db('mongo')( - 'should validate credentials first and check if account already linked afterwards ()', +describe("Security Advisory GHSA-8w3j-g983-8jh5", function () { + it_only_db("mongo")( + "should validate credentials first and check if account already linked afterwards ()", async done => { await reconfigureServer(); // Add User to Database with authData const database = Config.get(Parse.applicationId).database; - const collection = await database.adapter._adaptiveCollection('_User'); + const collection = await database.adapter._adaptiveCollection("_User"); await collection.insertOne({ - _id: 'ABCDEF1234', - name: '', - email: '', - username: '', - _hashed_password: '', + _id: "ABCDEF1234", + name: "", + email: "", + username: "", + _hashed_password: "", _auth_data_custom: { - id: 'linkedID', // Already linked userid + id: "linkedID", // Already linked userid }, - sessionToken: '', + sessionToken: "", }); const provider = { - getAuthType: () => 'custom', + getAuthType: () => "custom", restoreAuthentication: () => true, }; // AuthProvider checks if password is 'password' Parse.User._registerAuthenticationProvider(provider); @@ -4254,7 +4378,7 @@ describe('Security Advisory GHSA-8w3j-g983-8jh5', function () { try { const user = await Parse.AnonymousUtils.logIn(); await user._linkWith(provider.getAuthType(), { - authData: { id: 'linkedID', password: 'wrong' }, + authData: { id: "linkedID", password: "wrong" }, }); } catch (error) { // This should throw Parse.Error.SESSION_MISSING and not Parse.Error.ACCOUNT_ALREADY_LINKED @@ -4266,48 +4390,48 @@ describe('Security Advisory GHSA-8w3j-g983-8jh5', function () { done(); } ); - it_only_db('mongo')('should ignore authData field', async () => { + it_only_db("mongo")("should ignore authData field", async () => { // Add User to Database with authData await reconfigureServer(); const database = Config.get(Parse.applicationId).database; - const collection = await database.adapter._adaptiveCollection('_User'); + const collection = await database.adapter._adaptiveCollection("_User"); await collection.insertOne({ - _id: '1234ABCDEF', - name: '', - email: '', - username: '', - _hashed_password: '', + _id: "1234ABCDEF", + name: "", + email: "", + username: "", + _hashed_password: "", _auth_data_custom: { - id: 'linkedID', + id: "linkedID", }, - sessionToken: '', + sessionToken: "", authData: null, // should ignore }); const provider = { - getAuthType: () => 'custom', + getAuthType: () => "custom", restoreAuthentication: () => true, }; Parse.User._registerAuthenticationProvider(provider); const query = new Parse.Query(Parse.User); - const user = await query.get('1234ABCDEF', { useMasterKey: true }); - expect(user.get('authData')).toEqual({ custom: { id: 'linkedID' } }); + const user = await query.get("1234ABCDEF", { useMasterKey: true }); + expect(user.get("authData")).toEqual({ custom: { id: "linkedID" } }); }); }); -describe('login as other user', () => { - it('allows creating a session for another user with the master key', async done => { - await Parse.User.signUp('some_user', 'some_password'); +describe("login as other user", () => { + it("allows creating a session for another user with the master key", async done => { + await Parse.User.signUp("some_user", "some_password"); const userId = Parse.User.current().id; await Parse.User.logOut(); try { const response = await request({ - method: 'POST', - url: 'http://localhost:8378/1/loginAs', + method: "POST", + url: "http://localhost:8378/1/loginAs", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Master-Key': 'test', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", + "X-Parse-Master-Key": "test", }, body: { userId, @@ -4321,130 +4445,146 @@ describe('login as other user', () => { } const sessionsQuery = new Parse.Query(Parse.Session); - const sessionsAfterRequest = await sessionsQuery.find({ useMasterKey: true }); + const sessionsAfterRequest = await sessionsQuery.find({ + useMasterKey: true, + }); expect(sessionsAfterRequest.length).toBe(1); done(); }); - it('rejects creating a session for another user if the user does not exist', async done => { + it("rejects creating a session for another user if the user does not exist", async done => { try { await request({ - method: 'POST', - url: 'http://localhost:8378/1/loginAs', + method: "POST", + url: "http://localhost:8378/1/loginAs", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Master-Key': 'test', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", + "X-Parse-Master-Key": "test", }, body: { - userId: 'bogus-user', + userId: "bogus-user", }, }); - fail('Request should fail without a valid user ID'); + fail("Request should fail without a valid user ID"); done(); } catch (err) { expect(err.data.code).toBe(Parse.Error.OBJECT_NOT_FOUND); - expect(err.data.error).toBe('user not found'); + expect(err.data.error).toBe("user not found"); } const sessionsQuery = new Parse.Query(Parse.Session); - const sessionsAfterRequest = await sessionsQuery.find({ useMasterKey: true }); + const sessionsAfterRequest = await sessionsQuery.find({ + useMasterKey: true, + }); expect(sessionsAfterRequest.length).toBe(0); done(); }); - it('rejects creating a session for another user with invalid parameters', async done => { - const invalidUserIds = [undefined, null, '']; + it("rejects creating a session for another user with invalid parameters", async done => { + const invalidUserIds = [undefined, null, ""]; for (const invalidUserId of invalidUserIds) { try { await request({ - method: 'POST', - url: 'http://localhost:8378/1/loginAs', + method: "POST", + url: "http://localhost:8378/1/loginAs", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Master-Key': 'test', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", + "X-Parse-Master-Key": "test", }, body: { userId: invalidUserId, }, }); - fail('Request should fail without a valid user ID'); + fail("Request should fail without a valid user ID"); done(); } catch (err) { expect(err.data.code).toBe(Parse.Error.INVALID_VALUE); - expect(err.data.error).toBe('userId must not be empty, null, or undefined'); + expect(err.data.error).toBe( + "userId must not be empty, null, or undefined" + ); } const sessionsQuery = new Parse.Query(Parse.Session); - const sessionsAfterRequest = await sessionsQuery.find({ useMasterKey: true }); + const sessionsAfterRequest = await sessionsQuery.find({ + useMasterKey: true, + }); expect(sessionsAfterRequest.length).toBe(0); } done(); }); - it('rejects creating a session for another user without the master key', async done => { - await Parse.User.signUp('some_user', 'some_password'); + it("rejects creating a session for another user without the master key", async done => { + await Parse.User.signUp("some_user", "some_password"); const userId = Parse.User.current().id; await Parse.User.logOut(); try { await request({ - method: 'POST', - url: 'http://localhost:8378/1/loginAs', + method: "POST", + url: "http://localhost:8378/1/loginAs", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", }, body: { userId, }, }); - fail('Request should fail without the master key'); + fail("Request should fail without the master key"); done(); } catch (err) { expect(err.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN); - expect(err.data.error).toBe('master key is required'); + expect(err.data.error).toBe("master key is required"); } const sessionsQuery = new Parse.Query(Parse.Session); - const sessionsAfterRequest = await sessionsQuery.find({ useMasterKey: true }); + const sessionsAfterRequest = await sessionsQuery.find({ + useMasterKey: true, + }); expect(sessionsAfterRequest.length).toBe(0); done(); }); }); -describe('allowClientClassCreation option', () => { - it('should enforce boolean values', async () => { - const options = [[], 'a', '', 0, 1, {}, 'true', 'false']; +describe("allowClientClassCreation option", () => { + it("should enforce boolean values", async () => { + const options = [[], "a", "", 0, 1, {}, "true", "false"]; for (const option of options) { - await expectAsync(reconfigureServer({ allowClientClassCreation: option })).toBeRejected(); + await expectAsync( + reconfigureServer({ allowClientClassCreation: option }) + ).toBeRejected(); } }); - it('should accept true value', async () => { + it("should accept true value", async () => { await reconfigureServer({ allowClientClassCreation: true }); expect(Config.get(Parse.applicationId).allowClientClassCreation).toBe(true); }); - it('should accept false value', async () => { + it("should accept false value", async () => { await reconfigureServer({ allowClientClassCreation: false }); - expect(Config.get(Parse.applicationId).allowClientClassCreation).toBe(false); + expect(Config.get(Parse.applicationId).allowClientClassCreation).toBe( + false + ); }); - it('should default false', async () => { + it("should default false", async () => { // remove predefined allowClientClassCreation:true on global defaultConfiguration delete defaultConfiguration.allowClientClassCreation; await reconfigureServer(defaultConfiguration); - expect(Config.get(Parse.applicationId).allowClientClassCreation).toBe(false); + expect(Config.get(Parse.applicationId).allowClientClassCreation).toBe( + false + ); // Need to set it back to true to avoid other test fails defaultConfiguration.allowClientClassCreation = true; }); diff --git a/spec/ParseWebSocket.spec.js b/spec/ParseWebSocket.spec.js index fe64bce1be..915591c183 100644 --- a/spec/ParseWebSocket.spec.js +++ b/spec/ParseWebSocket.spec.js @@ -1,42 +1,43 @@ -const ParseWebSocket = require('../lib/LiveQuery/ParseWebSocketServer').ParseWebSocket; +const ParseWebSocket = + require("../lib/LiveQuery/ParseWebSocketServer").ParseWebSocket; -describe('ParseWebSocket', function () { - it('can be initialized', function () { +describe("ParseWebSocket", function () { + it("can be initialized", function () { const ws = {}; const parseWebSocket = new ParseWebSocket(ws); expect(parseWebSocket.ws).toBe(ws); }); - it('can handle disconnect event', function (done) { + it("can handle disconnect event", function (done) { const ws = { onclose: () => {}, }; const parseWebSocket = new ParseWebSocket(ws); - parseWebSocket.on('disconnect', () => { + parseWebSocket.on("disconnect", () => { done(); }); ws.onclose(); }); - it('can handle message event', function (done) { + it("can handle message event", function (done) { const ws = { onmessage: () => {}, }; const parseWebSocket = new ParseWebSocket(ws); - parseWebSocket.on('message', () => { + parseWebSocket.on("message", () => { done(); }); ws.onmessage(); }); - it('can send a message', function () { + it("can send a message", function () { const ws = { - send: jasmine.createSpy('send'), + send: jasmine.createSpy("send"), }; const parseWebSocket = new ParseWebSocket(ws); - parseWebSocket.send('message'); + parseWebSocket.send("message"); - expect(parseWebSocket.ws.send).toHaveBeenCalledWith('message'); + expect(parseWebSocket.ws.send).toHaveBeenCalledWith("message"); }); }); diff --git a/spec/ParseWebSocketServer.spec.js b/spec/ParseWebSocketServer.spec.js index 5955ee3241..13ae4c5b62 100644 --- a/spec/ParseWebSocketServer.spec.js +++ b/spec/ParseWebSocketServer.spec.js @@ -1,28 +1,34 @@ -const { ParseWebSocketServer } = require('../lib/LiveQuery/ParseWebSocketServer'); -const EventEmitter = require('events'); +const { + ParseWebSocketServer, +} = require("../lib/LiveQuery/ParseWebSocketServer"); +const EventEmitter = require("events"); -describe('ParseWebSocketServer', function () { +describe("ParseWebSocketServer", function () { beforeEach(function (done) { // Mock ws server const mockServer = function () { return new EventEmitter(); }; - jasmine.mockLibrary('ws', 'Server', mockServer); + jasmine.mockLibrary("ws", "Server", mockServer); done(); }); - it('can handle connect event when ws is open', function (done) { - const onConnectCallback = jasmine.createSpy('onConnectCallback'); - const http = require('http'); + it("can handle connect event when ws is open", function (done) { + const onConnectCallback = jasmine.createSpy("onConnectCallback"); + const http = require("http"); const server = http.createServer(); - const parseWebSocketServer = new ParseWebSocketServer(server, onConnectCallback, { - websocketTimeout: 5, - }).server; + const parseWebSocketServer = new ParseWebSocketServer( + server, + onConnectCallback, + { + websocketTimeout: 5, + } + ).server; const ws = new EventEmitter(); ws.readyState = 0; ws.OPEN = 0; - ws.ping = jasmine.createSpy('ping'); + ws.ping = jasmine.createSpy("ping"); ws.terminate = () => {}; parseWebSocketServer.onConnection(ws); @@ -37,9 +43,9 @@ describe('ParseWebSocketServer', function () { }, 10); }); - it('can handle error event', async () => { - jasmine.restoreLibrary('ws', 'Server'); - const WebSocketServer = require('ws').Server; + it("can handle error event", async () => { + jasmine.restoreLibrary("ws", "Server"); + const WebSocketServer = require("ws").Server; let wssError; class WSSAdapter { constructor(options) { @@ -50,9 +56,9 @@ describe('ParseWebSocketServer', function () { onError() {} start() { const wss = new WebSocketServer({ server: this.options.server }); - wss.on('listening', this.onListen); - wss.on('connection', this.onConnection); - wss.on('error', error => { + wss.on("listening", this.onListen); + wss.on("connection", this.onConnection); + wss.on("error", error => { wssError = error; this.onError(error); }); @@ -62,7 +68,7 @@ describe('ParseWebSocketServer', function () { const server = await reconfigureServer({ liveQuery: { - classNames: ['TestObject'], + classNames: ["TestObject"], }, liveQueryServerOptions: { wssAdapter: WSSAdapter, @@ -72,23 +78,27 @@ describe('ParseWebSocketServer', function () { silent: true, }); const wssAdapter = server.liveQueryServer.parseWebSocketServer.server; - wssAdapter.wss.emit('error', 'Invalid Packet'); - expect(wssError).toBe('Invalid Packet'); + wssAdapter.wss.emit("error", "Invalid Packet"); + expect(wssError).toBe("Invalid Packet"); }); - it('can handle ping/pong', async () => { - const onConnectCallback = jasmine.createSpy('onConnectCallback'); - const http = require('http'); + it("can handle ping/pong", async () => { + const onConnectCallback = jasmine.createSpy("onConnectCallback"); + const http = require("http"); const server = http.createServer(); - const parseWebSocketServer = new ParseWebSocketServer(server, onConnectCallback, { - websocketTimeout: 10, - }).server; + const parseWebSocketServer = new ParseWebSocketServer( + server, + onConnectCallback, + { + websocketTimeout: 10, + } + ).server; const ws = new EventEmitter(); ws.readyState = 0; ws.OPEN = 0; - ws.ping = jasmine.createSpy('ping'); - ws.terminate = jasmine.createSpy('terminate'); + ws.ping = jasmine.createSpy("ping"); + ws.terminate = jasmine.createSpy("terminate"); parseWebSocketServer.onConnection(ws); @@ -97,7 +107,7 @@ describe('ParseWebSocketServer', function () { await new Promise(resolve => setTimeout(resolve, 10)); expect(ws.ping).toHaveBeenCalled(); expect(ws.waitingForPong).toBe(true); - ws.emit('pong'); + ws.emit("pong"); expect(ws.waitingForPong).toBe(false); await new Promise(resolve => setTimeout(resolve, 10)); expect(ws.waitingForPong).toBe(true); @@ -105,18 +115,22 @@ describe('ParseWebSocketServer', function () { server.close(); }); - it('closes interrupted connection', async () => { - const onConnectCallback = jasmine.createSpy('onConnectCallback'); - const http = require('http'); + it("closes interrupted connection", async () => { + const onConnectCallback = jasmine.createSpy("onConnectCallback"); + const http = require("http"); const server = http.createServer(); - const parseWebSocketServer = new ParseWebSocketServer(server, onConnectCallback, { - websocketTimeout: 5, - }).server; + const parseWebSocketServer = new ParseWebSocketServer( + server, + onConnectCallback, + { + websocketTimeout: 5, + } + ).server; const ws = new EventEmitter(); ws.readyState = 0; ws.OPEN = 0; - ws.ping = jasmine.createSpy('ping'); - ws.terminate = jasmine.createSpy('terminate'); + ws.ping = jasmine.createSpy("ping"); + ws.terminate = jasmine.createSpy("terminate"); parseWebSocketServer.onConnection(ws); @@ -132,6 +146,6 @@ describe('ParseWebSocketServer', function () { }); afterEach(function () { - jasmine.restoreLibrary('ws', 'Server'); + jasmine.restoreLibrary("ws", "Server"); }); }); diff --git a/spec/PasswordPolicy.spec.js b/spec/PasswordPolicy.spec.js index b8ad67c391..b420fc5911 100644 --- a/spec/PasswordPolicy.spec.js +++ b/spec/PasswordPolicy.spec.js @@ -1,10 +1,10 @@ -'use strict'; +"use strict"; -const request = require('../lib/request'); +const request = require("../lib/request"); -describe('Password Policy: ', () => { - it_id('b400a867-9f05-496f-af79-933aa588dde5')(it)( - 'should show the invalid link page if the user clicks on the password reset link after the token expires', +describe("Password Policy: ", () => { + it_id("b400a867-9f05-496f-af79-933aa588dde5")(it)( + "should show the invalid link page if the user clicks on the password reset link after the token expires", done => { const user = new Parse.User(); let sendEmailOptions; @@ -16,23 +16,23 @@ describe('Password Policy: ', () => { sendMail: () => {}, }; reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", emailAdapter: emailAdapter, passwordPolicy: { resetTokenValidityDuration: 0.5, // 0.5 second }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }) .then(() => { - user.setUsername('testResetTokenValidity'); - user.setPassword('original'); - user.set('email', 'user@parse.com'); + user.setUsername("testResetTokenValidity"); + user.setPassword("original"); + user.set("email", "user@parse.com"); return user.signUp(); }) .then(() => { - Parse.User.requestPasswordReset('user@parse.com').catch(err => { + Parse.User.requestPasswordReset("user@parse.com").catch(err => { jfail(err); - fail('Reset password request should not fail'); + fail("Reset password request should not fail"); done(); }); }) @@ -50,7 +50,7 @@ describe('Password Policy: ', () => { .then(response => { expect(response.status).toEqual(302); expect(response.text).toEqual( - 'Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html' + "Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html" ); done(); }) @@ -66,7 +66,7 @@ describe('Password Policy: ', () => { } ); - it('should show the reset password page if the user clicks on the password reset link before the token expires', done => { + it("should show the reset password page if the user clicks on the password reset link before the token expires", done => { const user = new Parse.User(); let sendEmailOptions; const emailAdapter = { @@ -77,23 +77,23 @@ describe('Password Policy: ', () => { sendMail: () => {}, }; reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", emailAdapter: emailAdapter, passwordPolicy: { resetTokenValidityDuration: 5, // 5 seconds }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }) .then(() => { - user.setUsername('testResetTokenValidity'); - user.setPassword('original'); - user.set('email', 'user@parse.com'); + user.setUsername("testResetTokenValidity"); + user.setPassword("original"); + user.set("email", "user@parse.com"); return user.signUp(); }) .then(() => { - Parse.User.requestPasswordReset('user@parse.com').catch(err => { + Parse.User.requestPasswordReset("user@parse.com").catch(err => { jfail(err); - fail('Reset password request should not fail'); + fail("Reset password request should not fail"); done(); }); }) @@ -126,7 +126,7 @@ describe('Password Policy: ', () => { }); }); - it('should not keep reset token by default', async done => { + it("should not keep reset token by default", async done => { const sendEmailOptions = []; const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), @@ -136,26 +136,26 @@ describe('Password Policy: ', () => { sendMail: () => {}, }; await reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", emailAdapter: emailAdapter, passwordPolicy: { resetTokenValidityDuration: 5 * 60, // 5 minutes }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }); const user = new Parse.User(); - user.setUsername('testResetTokenValidity'); - user.setPassword('original'); - user.set('email', 'user@example.com'); + user.setUsername("testResetTokenValidity"); + user.setPassword("original"); + user.set("email", "user@example.com"); await user.signUp(); - await Parse.User.requestPasswordReset('user@example.com'); - await Parse.User.requestPasswordReset('user@example.com'); + await Parse.User.requestPasswordReset("user@example.com"); + await Parse.User.requestPasswordReset("user@example.com"); expect(sendEmailOptions[0].link).not.toBe(sendEmailOptions[1].link); done(); }); - it_id('7d98e1f2-ae89-4038-9ea7-5254854ea42e')(it)( - 'should keep reset token with resetTokenReuseIfValid', + it_id("7d98e1f2-ae89-4038-9ea7-5254854ea42e")(it)( + "should keep reset token with resetTokenReuseIfValid", async done => { const sendEmailOptions = []; const emailAdapter = { @@ -166,27 +166,27 @@ describe('Password Policy: ', () => { sendMail: () => {}, }; await reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", emailAdapter: emailAdapter, passwordPolicy: { resetTokenValidityDuration: 5 * 60, // 5 minutes resetTokenReuseIfValid: true, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }); const user = new Parse.User(); - user.setUsername('testResetTokenValidity'); - user.setPassword('original'); - user.set('email', 'user@example.com'); + user.setUsername("testResetTokenValidity"); + user.setPassword("original"); + user.set("email", "user@example.com"); await user.signUp(); - await Parse.User.requestPasswordReset('user@example.com'); - await Parse.User.requestPasswordReset('user@example.com'); + await Parse.User.requestPasswordReset("user@example.com"); + await Parse.User.requestPasswordReset("user@example.com"); expect(sendEmailOptions[0].link).toBe(sendEmailOptions[1].link); done(); } ); - it('should throw with invalid resetTokenReuseIfValid', async done => { + it("should throw with invalid resetTokenReuseIfValid", async done => { const sendEmailOptions = []; const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), @@ -197,124 +197,136 @@ describe('Password Policy: ', () => { }; try { await reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", emailAdapter: emailAdapter, passwordPolicy: { resetTokenValidityDuration: 5 * 60, // 5 minutes resetTokenReuseIfValid: [], }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }); - fail('should have thrown.'); + fail("should have thrown."); } catch (e) { - expect(e).toBe('resetTokenReuseIfValid must be a boolean value'); + expect(e).toBe("resetTokenReuseIfValid must be a boolean value"); } try { await reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", emailAdapter: emailAdapter, passwordPolicy: { resetTokenReuseIfValid: true, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }); - fail('should have thrown.'); + fail("should have thrown."); } catch (e) { - expect(e).toBe('You cannot use resetTokenReuseIfValid without resetTokenValidityDuration'); + expect(e).toBe( + "You cannot use resetTokenReuseIfValid without resetTokenValidityDuration" + ); } done(); }); - it('should fail if passwordPolicy.resetTokenValidityDuration is not a number', done => { + it("should fail if passwordPolicy.resetTokenValidityDuration is not a number", done => { reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", passwordPolicy: { - resetTokenValidityDuration: 'not a number', + resetTokenValidityDuration: "not a number", }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }) .then(() => { - fail('passwordPolicy.resetTokenValidityDuration "not a number" test failed'); + fail( + 'passwordPolicy.resetTokenValidityDuration "not a number" test failed' + ); done(); }) .catch(err => { - expect(err).toEqual('passwordPolicy.resetTokenValidityDuration must be a positive number'); + expect(err).toEqual( + "passwordPolicy.resetTokenValidityDuration must be a positive number" + ); done(); }); }); - it('should fail if passwordPolicy.resetTokenValidityDuration is zero or a negative number', done => { + it("should fail if passwordPolicy.resetTokenValidityDuration is zero or a negative number", done => { reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", passwordPolicy: { resetTokenValidityDuration: 0, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }) .then(() => { - fail('resetTokenValidityDuration negative number test failed'); + fail("resetTokenValidityDuration negative number test failed"); done(); }) .catch(err => { - expect(err).toEqual('passwordPolicy.resetTokenValidityDuration must be a positive number'); + expect(err).toEqual( + "passwordPolicy.resetTokenValidityDuration must be a positive number" + ); done(); }); }); - it('should fail if passwordPolicy.validatorPattern setting is invalid type', done => { + it("should fail if passwordPolicy.validatorPattern setting is invalid type", done => { reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", passwordPolicy: { validatorPattern: 1234, // number is not a valid setting }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }) .then(() => { - fail('passwordPolicy.validatorPattern type test failed'); + fail("passwordPolicy.validatorPattern type test failed"); done(); }) .catch(err => { expect(err).toEqual( - 'passwordPolicy.validatorPattern must be a regex string or RegExp object.' + "passwordPolicy.validatorPattern must be a regex string or RegExp object." ); done(); }); }); - it('should fail if passwordPolicy.validatorCallback setting is invalid type', done => { + it("should fail if passwordPolicy.validatorCallback setting is invalid type", done => { reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", passwordPolicy: { - validatorCallback: 'abc', // string is not a valid setting + validatorCallback: "abc", // string is not a valid setting }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }) .then(() => { - fail('passwordPolicy.validatorCallback type test failed'); + fail("passwordPolicy.validatorCallback type test failed"); done(); }) .catch(err => { - expect(err).toEqual('passwordPolicy.validatorCallback must be a function.'); + expect(err).toEqual( + "passwordPolicy.validatorCallback must be a function." + ); done(); }); }); - it('signup should fail if password does not conform to the policy enforced using validatorPattern', done => { + it("signup should fail if password does not conform to the policy enforced using validatorPattern", done => { const user = new Parse.User(); reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", passwordPolicy: { validatorPattern: /[0-9]+/, // password should contain at least one digit }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { - user.setUsername('user1'); - user.setPassword('nodigit'); - user.set('email', 'user1@parse.com'); + user.setUsername("user1"); + user.setPassword("nodigit"); + user.set("email", "user1@parse.com"); user .signUp() .then(() => { - fail('Should have failed as password does not conform to the policy.'); + fail( + "Should have failed as password does not conform to the policy." + ); done(); }) .catch(error => { @@ -324,22 +336,24 @@ describe('Password Policy: ', () => { }); }); - it('signup should fail if password does not conform to the policy enforced using validatorPattern string', done => { + it("signup should fail if password does not conform to the policy enforced using validatorPattern string", done => { const user = new Parse.User(); reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", passwordPolicy: { - validatorPattern: '^.{8,}', // password should contain at least 8 char + validatorPattern: "^.{8,}", // password should contain at least 8 char }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { - user.setUsername('user1'); - user.setPassword('less'); - user.set('email', 'user1@parse.com'); + user.setUsername("user1"); + user.setPassword("less"); + user.set("email", "user1@parse.com"); user .signUp() .then(() => { - fail('Should have failed as password does not conform to the policy.'); + fail( + "Should have failed as password does not conform to the policy." + ); done(); }) .catch(error => { @@ -349,129 +363,139 @@ describe('Password Policy: ', () => { }); }); - it('signup should fail if password is empty', done => { + it("signup should fail if password is empty", done => { const user = new Parse.User(); reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", passwordPolicy: { - validatorPattern: '^.{8,}', // password should contain at least 8 char + validatorPattern: "^.{8,}", // password should contain at least 8 char }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { - user.setUsername('user1'); - user.setPassword(''); - user.set('email', 'user1@parse.com'); + user.setUsername("user1"); + user.setPassword(""); + user.set("email", "user1@parse.com"); user .signUp() .then(() => { - fail('Should have failed as password does not conform to the policy.'); + fail( + "Should have failed as password does not conform to the policy." + ); done(); }) .catch(error => { - expect(error.message).toEqual('Cannot sign up user with an empty password.'); + expect(error.message).toEqual( + "Cannot sign up user with an empty password." + ); done(); }); }); }); - it('signup should succeed if password conforms to the policy enforced using validatorPattern', done => { + it("signup should succeed if password conforms to the policy enforced using validatorPattern", done => { const user = new Parse.User(); reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", passwordPolicy: { validatorPattern: /[0-9]+/, // password should contain at least one digit }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { - user.setUsername('user1'); - user.setPassword('1digit'); - user.set('email', 'user1@parse.com'); + user.setUsername("user1"); + user.setPassword("1digit"); + user.set("email", "user1@parse.com"); user .signUp() .then(() => { Parse.User.logOut() .then(() => { - Parse.User.logIn('user1', '1digit') + Parse.User.logIn("user1", "1digit") .then(function () { done(); }) .catch(err => { jfail(err); - fail('Should be able to login'); + fail("Should be able to login"); done(); }); }) .catch(error => { jfail(error); - fail('logout should have succeeded'); + fail("logout should have succeeded"); done(); }); }) .catch(error => { jfail(error); - fail('Signup should have succeeded as password conforms to the policy.'); + fail( + "Signup should have succeeded as password conforms to the policy." + ); done(); }); }); }); - it('signup should succeed if password conforms to the policy enforced using validatorPattern string', done => { + it("signup should succeed if password conforms to the policy enforced using validatorPattern string", done => { const user = new Parse.User(); reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", passwordPolicy: { - validatorPattern: '[!@#$]+', // password should contain at least one special char + validatorPattern: "[!@#$]+", // password should contain at least one special char }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { - user.setUsername('user1'); - user.setPassword('p@sswrod'); - user.set('email', 'user1@parse.com'); + user.setUsername("user1"); + user.setPassword("p@sswrod"); + user.set("email", "user1@parse.com"); user .signUp() .then(() => { Parse.User.logOut() .then(() => { - Parse.User.logIn('user1', 'p@sswrod') + Parse.User.logIn("user1", "p@sswrod") .then(function () { done(); }) .catch(err => { jfail(err); - fail('Should be able to login'); + fail("Should be able to login"); done(); }); }) .catch(error => { jfail(error); - fail('logout should have succeeded'); + fail("logout should have succeeded"); done(); }); }) .catch(error => { jfail(error); - fail('Signup should have succeeded as password conforms to the policy.'); + fail( + "Signup should have succeeded as password conforms to the policy." + ); done(); }); }); }); - it('signup should fail if password does not conform to the policy enforced using validatorCallback', done => { + it("signup should fail if password does not conform to the policy enforced using validatorCallback", done => { const user = new Parse.User(); reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", passwordPolicy: { validatorCallback: () => false, // just fail }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { - user.setUsername('user1'); - user.setPassword('any'); - user.set('email', 'user1@parse.com'); + user.setUsername("user1"); + user.setPassword("any"); + user.set("email", "user1@parse.com"); user .signUp() .then(() => { - fail('Should have failed as password does not conform to the policy.'); + fail( + "Should have failed as password does not conform to the policy." + ); done(); }) .catch(error => { @@ -481,64 +505,66 @@ describe('Password Policy: ', () => { }); }); - it('signup should succeed if password conforms to the policy enforced using validatorCallback', done => { + it("signup should succeed if password conforms to the policy enforced using validatorCallback", done => { const user = new Parse.User(); reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", passwordPolicy: { validatorCallback: () => true, // never fail }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { - user.setUsername('user1'); - user.setPassword('oneUpper'); - user.set('email', 'user1@parse.com'); + user.setUsername("user1"); + user.setPassword("oneUpper"); + user.set("email", "user1@parse.com"); user .signUp() .then(() => { Parse.User.logOut() .then(() => { - Parse.User.logIn('user1', 'oneUpper') + Parse.User.logIn("user1", "oneUpper") .then(function () { done(); }) .catch(err => { jfail(err); - fail('Should be able to login'); + fail("Should be able to login"); done(); }); }) .catch(error => { jfail(error); - fail('Logout should have succeeded'); + fail("Logout should have succeeded"); done(); }); }) .catch(error => { jfail(error); - fail('Should have succeeded as password conforms to the policy.'); + fail("Should have succeeded as password conforms to the policy."); done(); }); }); }); - it('signup should fail if password does not match validatorPattern but succeeds validatorCallback', done => { + it("signup should fail if password does not match validatorPattern but succeeds validatorCallback", done => { const user = new Parse.User(); reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", passwordPolicy: { validatorPattern: /[A-Z]+/, // password should contain at least one UPPER case letter validatorCallback: () => true, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { - user.setUsername('user1'); - user.setPassword('all lower'); - user.set('email', 'user1@parse.com'); + user.setUsername("user1"); + user.setPassword("all lower"); + user.set("email", "user1@parse.com"); user .signUp() .then(() => { - fail('Should have failed as password does not conform to the policy.'); + fail( + "Should have failed as password does not conform to the policy." + ); done(); }) .catch(error => { @@ -548,23 +574,25 @@ describe('Password Policy: ', () => { }); }); - it('signup should fail if password matches validatorPattern but fails validatorCallback', done => { + it("signup should fail if password matches validatorPattern but fails validatorCallback", done => { const user = new Parse.User(); reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", passwordPolicy: { validatorPattern: /[A-Z]+/, // password should contain at least one UPPER case letter validatorCallback: () => false, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { - user.setUsername('user1'); - user.setPassword('oneUpper'); - user.set('email', 'user1@parse.com'); + user.setUsername("user1"); + user.setPassword("oneUpper"); + user.set("email", "user1@parse.com"); user .signUp() .then(() => { - fail('Should have failed as password does not conform to the policy.'); + fail( + "Should have failed as password does not conform to the policy." + ); done(); }) .catch(error => { @@ -574,49 +602,49 @@ describe('Password Policy: ', () => { }); }); - it('signup should succeed if password conforms to both validatorPattern and validatorCallback', done => { + it("signup should succeed if password conforms to both validatorPattern and validatorCallback", done => { const user = new Parse.User(); reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", passwordPolicy: { validatorPattern: /[A-Z]+/, // password should contain at least one digit validatorCallback: () => true, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { - user.setUsername('user1'); - user.setPassword('oneUpper'); - user.set('email', 'user1@parse.com'); + user.setUsername("user1"); + user.setPassword("oneUpper"); + user.set("email", "user1@parse.com"); user .signUp() .then(() => { Parse.User.logOut() .then(() => { - Parse.User.logIn('user1', 'oneUpper') + Parse.User.logIn("user1", "oneUpper") .then(function () { done(); }) .catch(err => { jfail(err); - fail('Should be able to login'); + fail("Should be able to login"); done(); }); }) .catch(error => { jfail(error); - fail('logout should have succeeded'); + fail("logout should have succeeded"); done(); }); }) .catch(error => { jfail(error); - fail('Should have succeeded as password conforms to the policy.'); + fail("Should have succeeded as password conforms to the policy."); done(); }); }); }); - it('should reset password if new password conforms to password policy', done => { + it("should reset password if new password conforms to password policy", done => { const user = new Parse.User(); const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), @@ -633,18 +661,18 @@ describe('Password Policy: ', () => { /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; const match = response.text.match(re); if (!match) { - fail('should have a token'); + fail("should have a token"); done(); return; } const token = match[1]; request({ - method: 'POST', - url: 'http://localhost:8378/1/apps/test/request_password_reset', + method: "POST", + url: "http://localhost:8378/1/apps/test/request_password_reset", body: `new_password=has2init&token=${token}`, headers: { - 'Content-Type': 'application/x-www-form-urlencoded', + "Content-Type": "application/x-www-form-urlencoded", }, followRedirects: false, simple: false, @@ -653,63 +681,63 @@ describe('Password Policy: ', () => { .then(response => { expect(response.status).toEqual(302); expect(response.text).toEqual( - 'Found. Redirecting to http://localhost:8378/1/apps/password_reset_success.html' + "Found. Redirecting to http://localhost:8378/1/apps/password_reset_success.html" ); - Parse.User.logIn('user1', 'has2init') + Parse.User.logIn("user1", "has2init") .then(function () { done(); }) .catch(err => { jfail(err); - fail('should login with new password'); + fail("should login with new password"); done(); }); }) .catch(error => { jfail(error); - fail('Failed to POST request password reset'); + fail("Failed to POST request password reset"); done(); }); }) .catch(error => { jfail(error); - fail('Failed to get the reset link'); + fail("Failed to get the reset link"); done(); }); }, sendMail: () => {}, }; reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", verifyUserEmails: false, emailAdapter: emailAdapter, passwordPolicy: { validatorPattern: /[0-9]+/, // password should contain at least one digit }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { - user.setUsername('user1'); - user.setPassword('has 1 digit'); - user.set('email', 'user1@parse.com'); + user.setUsername("user1"); + user.setPassword("has 1 digit"); + user.set("email", "user1@parse.com"); user .signUp() .then(() => { - Parse.User.requestPasswordReset('user1@parse.com').catch(err => { + Parse.User.requestPasswordReset("user1@parse.com").catch(err => { jfail(err); - fail('Reset password request should not fail'); + fail("Reset password request should not fail"); done(); }); }) .catch(error => { jfail(error); - fail('signUp should not fail'); + fail("signUp should not fail"); done(); }); }); }); - it('should fail to reset password if the new password does not conform to password policy', done => { + it("should fail to reset password if the new password does not conform to password policy", done => { const user = new Parse.User(); const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), @@ -726,18 +754,18 @@ describe('Password Policy: ', () => { /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; const match = response.text.match(re); if (!match) { - fail('should have a token'); + fail("should have a token"); done(); return; } const token = match[1]; request({ - method: 'POST', - url: 'http://localhost:8378/1/apps/test/request_password_reset', + method: "POST", + url: "http://localhost:8378/1/apps/test/request_password_reset", body: `new_password=hasnodigit&token=${token}`, headers: { - 'Content-Type': 'application/x-www-form-urlencoded', + "Content-Type": "application/x-www-form-urlencoded", }, followRedirects: false, simple: false, @@ -749,154 +777,158 @@ describe('Password Policy: ', () => { `Found. Redirecting to http://localhost:8378/1/apps/choose_password?token=${token}&id=test&error=Password%20should%20contain%20at%20least%20one%20digit.&app=passwordPolicy` ); - Parse.User.logIn('user1', 'has 1 digit') + Parse.User.logIn("user1", "has 1 digit") .then(function () { done(); }) .catch(err => { jfail(err); - fail('should login with old password'); + fail("should login with old password"); done(); }); }) .catch(error => { jfail(error); - fail('Failed to POST request password reset'); + fail("Failed to POST request password reset"); done(); }); }) .catch(error => { jfail(error); - fail('Failed to get the reset link'); + fail("Failed to get the reset link"); done(); }); }, sendMail: () => {}, }; reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", verifyUserEmails: false, emailAdapter: emailAdapter, passwordPolicy: { validatorPattern: /[0-9]+/, // password should contain at least one digit - validationError: 'Password should contain at least one digit.', + validationError: "Password should contain at least one digit.", }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { - user.setUsername('user1'); - user.setPassword('has 1 digit'); - user.set('email', 'user1@parse.com'); + user.setUsername("user1"); + user.setPassword("has 1 digit"); + user.set("email", "user1@parse.com"); user .signUp() .then(() => { - Parse.User.requestPasswordReset('user1@parse.com').catch(err => { + Parse.User.requestPasswordReset("user1@parse.com").catch(err => { jfail(err); - fail('Reset password request should not fail'); + fail("Reset password request should not fail"); done(); }); }) .catch(error => { jfail(error); - fail('signUp should not fail'); + fail("signUp should not fail"); done(); }); }); }); - it('should fail if passwordPolicy.doNotAllowUsername is not a boolean value', done => { + it("should fail if passwordPolicy.doNotAllowUsername is not a boolean value", done => { reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", passwordPolicy: { - doNotAllowUsername: 'no', + doNotAllowUsername: "no", }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }) .then(() => { - fail('passwordPolicy.doNotAllowUsername type test failed'); + fail("passwordPolicy.doNotAllowUsername type test failed"); done(); }) .catch(err => { - expect(err).toEqual('passwordPolicy.doNotAllowUsername must be a boolean value.'); + expect(err).toEqual( + "passwordPolicy.doNotAllowUsername must be a boolean value." + ); done(); }); }); - it('signup should fail if password contains the username and is not allowed by policy', done => { + it("signup should fail if password contains the username and is not allowed by policy", done => { const user = new Parse.User(); reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", passwordPolicy: { validatorPattern: /[0-9]+/, doNotAllowUsername: true, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { - user.setUsername('user1'); - user.setPassword('@user11'); - user.set('email', 'user1@parse.com'); + user.setUsername("user1"); + user.setPassword("@user11"); + user.set("email", "user1@parse.com"); user .signUp() .then(() => { - fail('Should have failed as password contains username.'); + fail("Should have failed as password contains username."); done(); }) .catch(error => { expect(error.code).toEqual(142); - expect(error.message).toEqual('Password cannot contain your username.'); + expect(error.message).toEqual( + "Password cannot contain your username." + ); done(); }); }); }); - it('signup should succeed if password does not contain the username and is not allowed by policy', done => { + it("signup should succeed if password does not contain the username and is not allowed by policy", done => { const user = new Parse.User(); reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", passwordPolicy: { doNotAllowUsername: true, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { - user.setUsername('user1'); - user.setPassword('r@nd0m'); - user.set('email', 'user1@parse.com'); + user.setUsername("user1"); + user.setPassword("r@nd0m"); + user.set("email", "user1@parse.com"); user .signUp() .then(() => { done(); }) .catch(() => { - fail('Should have succeeded as password does not contain username.'); + fail("Should have succeeded as password does not contain username."); done(); }); }); }); - it('signup should succeed if password contains the username and it is allowed by policy', done => { + it("signup should succeed if password contains the username and it is allowed by policy", done => { const user = new Parse.User(); reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", passwordPolicy: { validatorPattern: /[0-9]+/, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { - user.setUsername('user1'); - user.setPassword('user1'); - user.set('email', 'user1@parse.com'); + user.setUsername("user1"); + user.setPassword("user1"); + user.set("email", "user1@parse.com"); user .signUp() .then(() => { done(); }) .catch(() => { - fail('Should have succeeded as policy allows username in password.'); + fail("Should have succeeded as policy allows username in password."); done(); }); }); }); - it('should fail to reset password if the new password contains username and not allowed by password policy', done => { + it("should fail to reset password if the new password contains username and not allowed by password policy", done => { const user = new Parse.User(); const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), @@ -913,18 +945,18 @@ describe('Password Policy: ', () => { /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; const match = response.text.match(re); if (!match) { - fail('should have a token'); + fail("should have a token"); done(); return; } const token = match[1]; request({ - method: 'POST', - url: 'http://localhost:8378/1/apps/test/request_password_reset', + method: "POST", + url: "http://localhost:8378/1/apps/test/request_password_reset", body: `new_password=xuser12&token=${token}`, headers: { - 'Content-Type': 'application/x-www-form-urlencoded', + "Content-Type": "application/x-www-form-urlencoded", }, followRedirects: false, simple: false, @@ -936,60 +968,60 @@ describe('Password Policy: ', () => { `Found. Redirecting to http://localhost:8378/1/apps/choose_password?token=${token}&id=test&error=Password%20cannot%20contain%20your%20username.&app=passwordPolicy` ); - Parse.User.logIn('user1', 'r@nd0m') + Parse.User.logIn("user1", "r@nd0m") .then(function () { done(); }) .catch(err => { jfail(err); - fail('should login with old password'); + fail("should login with old password"); done(); }); }) .catch(error => { jfail(error); - fail('Failed to POST request password reset'); + fail("Failed to POST request password reset"); done(); }); }) .catch(error => { jfail(error); - fail('Failed to get the reset link'); + fail("Failed to get the reset link"); done(); }); }, sendMail: () => {}, }; reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", verifyUserEmails: false, emailAdapter: emailAdapter, passwordPolicy: { doNotAllowUsername: true, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { - user.setUsername('user1'); - user.setPassword('r@nd0m'); - user.set('email', 'user1@parse.com'); + user.setUsername("user1"); + user.setPassword("r@nd0m"); + user.set("email", "user1@parse.com"); user .signUp() .then(() => { - Parse.User.requestPasswordReset('user1@parse.com').catch(err => { + Parse.User.requestPasswordReset("user1@parse.com").catch(err => { jfail(err); - fail('Reset password request should not fail'); + fail("Reset password request should not fail"); done(); }); }) .catch(error => { jfail(error); - fail('signUp should not fail'); + fail("signUp should not fail"); done(); }); }); }); - it('Should return error when password violates Password Policy and reset through ajax', async done => { + it("Should return error when password violates Password Policy and reset through ajax", async done => { const user = new Parse.User(); const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), @@ -1005,19 +1037,19 @@ describe('Password Policy: ', () => { /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; const match = response.text.match(re); if (!match) { - fail('should have a token'); + fail("should have a token"); return; } const token = match[1]; try { await request({ - method: 'POST', - url: 'http://localhost:8378/1/apps/test/request_password_reset', + method: "POST", + url: "http://localhost:8378/1/apps/test/request_password_reset", body: `new_password=xuser12&token=${token}`, headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'X-Requested-With': 'XMLHttpRequest', + "Content-Type": "application/x-www-form-urlencoded", + "X-Requested-With": "XMLHttpRequest", }, followRedirects: false, }); @@ -1027,29 +1059,29 @@ describe('Password Policy: ', () => { '{"code":-1,"error":"Password cannot contain your username."}' ); } - await Parse.User.logIn('user1', 'r@nd0m'); + await Parse.User.logIn("user1", "r@nd0m"); done(); }, sendMail: () => {}, }; await reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", verifyUserEmails: false, emailAdapter: emailAdapter, passwordPolicy: { doNotAllowUsername: true, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }); - user.setUsername('user1'); - user.setPassword('r@nd0m'); - user.set('email', 'user1@parse.com'); + user.setUsername("user1"); + user.setPassword("r@nd0m"); + user.set("email", "user1@parse.com"); await user.signUp(); - await Parse.User.requestPasswordReset('user1@parse.com'); + await Parse.User.requestPasswordReset("user1@parse.com"); }); - it('should reset password even if the new password contains user name while the policy allows', done => { + it("should reset password even if the new password contains user name while the policy allows", done => { const user = new Parse.User(); const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), @@ -1066,18 +1098,18 @@ describe('Password Policy: ', () => { /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; const match = response.text.match(re); if (!match) { - fail('should have a token'); + fail("should have a token"); done(); return; } const token = match[1]; request({ - method: 'POST', - url: 'http://localhost:8378/1/apps/test/request_password_reset', + method: "POST", + url: "http://localhost:8378/1/apps/test/request_password_reset", body: `new_password=uuser11&token=${token}`, headers: { - 'Content-Type': 'application/x-www-form-urlencoded', + "Content-Type": "application/x-www-form-urlencoded", }, followRedirects: false, simple: false, @@ -1086,160 +1118,164 @@ describe('Password Policy: ', () => { .then(response => { expect(response.status).toEqual(302); expect(response.text).toEqual( - 'Found. Redirecting to http://localhost:8378/1/apps/password_reset_success.html' + "Found. Redirecting to http://localhost:8378/1/apps/password_reset_success.html" ); - Parse.User.logIn('user1', 'uuser11') + Parse.User.logIn("user1", "uuser11") .then(function () { done(); }) .catch(err => { jfail(err); - fail('should login with new password'); + fail("should login with new password"); done(); }); }) .catch(error => { jfail(error); - fail('Failed to POST request password reset'); + fail("Failed to POST request password reset"); }); }) .catch(error => { jfail(error); - fail('Failed to get the reset link'); + fail("Failed to get the reset link"); }); }, sendMail: () => {}, }; reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", verifyUserEmails: false, emailAdapter: emailAdapter, passwordPolicy: { validatorPattern: /[0-9]+/, doNotAllowUsername: false, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }) .then(() => { - user.setUsername('user1'); - user.setPassword('has 1 digit'); - user.set('email', 'user1@parse.com'); + user.setUsername("user1"); + user.setPassword("has 1 digit"); + user.set("email", "user1@parse.com"); user.signUp().then(() => { - Parse.User.requestPasswordReset('user1@parse.com').catch(err => { + Parse.User.requestPasswordReset("user1@parse.com").catch(err => { jfail(err); - fail('Reset password request should not fail'); + fail("Reset password request should not fail"); done(); }); }); }) .catch(error => { jfail(error); - fail('signUp should not fail'); + fail("signUp should not fail"); done(); }); }); - it('should fail if passwordPolicy.maxPasswordAge is not a number', done => { + it("should fail if passwordPolicy.maxPasswordAge is not a number", done => { reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", passwordPolicy: { - maxPasswordAge: 'not a number', + maxPasswordAge: "not a number", }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }) .then(() => { fail('passwordPolicy.maxPasswordAge "not a number" test failed'); done(); }) .catch(err => { - expect(err).toEqual('passwordPolicy.maxPasswordAge must be a positive number'); + expect(err).toEqual( + "passwordPolicy.maxPasswordAge must be a positive number" + ); done(); }); }); - it('should fail if passwordPolicy.maxPasswordAge is a negative number', done => { + it("should fail if passwordPolicy.maxPasswordAge is a negative number", done => { reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", passwordPolicy: { maxPasswordAge: -100, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }) .then(() => { - fail('passwordPolicy.maxPasswordAge negative number test failed'); + fail("passwordPolicy.maxPasswordAge negative number test failed"); done(); }) .catch(err => { - expect(err).toEqual('passwordPolicy.maxPasswordAge must be a positive number'); + expect(err).toEqual( + "passwordPolicy.maxPasswordAge must be a positive number" + ); done(); }); }); - it_id('d7d0a93e-efe6-48c0-b622-0f7fb570ccc1')(it)( - 'should succeed if logged in before password expires', + it_id("d7d0a93e-efe6-48c0-b622-0f7fb570ccc1")(it)( + "should succeed if logged in before password expires", done => { const user = new Parse.User(); reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", passwordPolicy: { maxPasswordAge: 1, // 1 day }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { - user.setUsername('user1'); - user.setPassword('user1'); - user.set('email', 'user1@parse.com'); + user.setUsername("user1"); + user.setPassword("user1"); + user.set("email", "user1@parse.com"); user .signUp() .then(() => { - Parse.User.logIn('user1', 'user1') + Parse.User.logIn("user1", "user1") .then(() => { done(); }) .catch(error => { jfail(error); - fail('Login should have succeeded before password expiry.'); + fail("Login should have succeeded before password expiry."); done(); }); }) .catch(error => { jfail(error); - fail('Signup failed.'); + fail("Signup failed."); done(); }); }); } ); - it_id('22428408-8763-445d-9833-2b2d92008f62')(it)( - 'should fail if logged in after password expires', + it_id("22428408-8763-445d-9833-2b2d92008f62")(it)( + "should fail if logged in after password expires", done => { const user = new Parse.User(); reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", passwordPolicy: { maxPasswordAge: 0.5 / (24 * 60 * 60), // 0.5 sec }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { - user.setUsername('user1'); - user.setPassword('user1'); - user.set('email', 'user1@parse.com'); + user.setUsername("user1"); + user.setPassword("user1"); + user.set("email", "user1@parse.com"); user .signUp() .then(() => { // wait for a bit more than the validity duration set setTimeout(() => { - Parse.User.logIn('user1', 'user1') + Parse.User.logIn("user1", "user1") .then(() => { - fail('logIn should have failed'); + fail("logIn should have failed"); done(); }) .catch(error => { expect(error.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); expect(error.message).toEqual( - 'Your password has expired. Please reset your password.' + "Your password has expired. Please reset your password." ); done(); }); @@ -1247,51 +1283,53 @@ describe('Password Policy: ', () => { }) .catch(error => { jfail(error); - fail('Signup failed.'); + fail("Signup failed."); done(); }); }); } ); - it_id('cc97a109-e35f-4f94-b942-3a6134921cdd')(it)( - 'should apply password expiry policy to existing user upon first login after policy is enabled', + it_id("cc97a109-e35f-4f94-b942-3a6134921cdd")(it)( + "should apply password expiry policy to existing user upon first login after policy is enabled", done => { const user = new Parse.User(); reconfigureServer({ - appName: 'passwordPolicy', - publicServerURL: 'http://localhost:8378/1', + appName: "passwordPolicy", + publicServerURL: "http://localhost:8378/1", }).then(() => { - user.setUsername('user1'); - user.setPassword('user1'); - user.set('email', 'user1@parse.com'); + user.setUsername("user1"); + user.setPassword("user1"); + user.set("email", "user1@parse.com"); user .signUp() .then(() => { Parse.User.logOut() .then(() => { reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", passwordPolicy: { maxPasswordAge: 0.5 / (24 * 60 * 60), // 0.5 sec }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { - Parse.User.logIn('user1', 'user1') + Parse.User.logIn("user1", "user1") .then(() => { Parse.User.logOut() .then(() => { // wait for a bit more than the validity duration set setTimeout(() => { - Parse.User.logIn('user1', 'user1') + Parse.User.logIn("user1", "user1") .then(() => { - fail('logIn should have failed'); + fail("logIn should have failed"); done(); }) .catch(error => { - expect(error.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); + expect(error.code).toEqual( + Parse.Error.OBJECT_NOT_FOUND + ); expect(error.message).toEqual( - 'Your password has expired. Please reset your password.' + "Your password has expired. Please reset your password." ); done(); }); @@ -1299,34 +1337,34 @@ describe('Password Policy: ', () => { }) .catch(error => { jfail(error); - fail('logout should have succeeded'); + fail("logout should have succeeded"); done(); }); }) .catch(error => { jfail(error); - fail('Login failed.'); + fail("Login failed."); done(); }); }); }) .catch(error => { jfail(error); - fail('logout should have succeeded'); + fail("logout should have succeeded"); done(); }); }) .catch(error => { jfail(error); - fail('Signup failed.'); + fail("Signup failed."); done(); }); }); } ); - it_id('d1e6ab9d-c091-4fea-b952-08b7484bfc89')(it)( - 'should reset password timestamp when password is reset', + it_id("d1e6ab9d-c091-4fea-b952-08b7484bfc89")(it)( + "should reset password timestamp when password is reset", done => { const user = new Parse.User(); const emailAdapter = { @@ -1344,18 +1382,18 @@ describe('Password Policy: ', () => { /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; const match = response.text.match(re); if (!match) { - fail('should have a token'); + fail("should have a token"); done(); return; } const token = match[1]; request({ - method: 'POST', - url: 'http://localhost:8378/1/apps/test/request_password_reset', + method: "POST", + url: "http://localhost:8378/1/apps/test/request_password_reset", body: `new_password=uuser11&token=${token}`, headers: { - 'Content-Type': 'application/x-www-form-urlencoded', + "Content-Type": "application/x-www-form-urlencoded", }, followRedirects: false, simple: false, @@ -1364,129 +1402,137 @@ describe('Password Policy: ', () => { .then(response => { expect(response.status).toEqual(302); expect(response.text).toEqual( - 'Found. Redirecting to http://localhost:8378/1/apps/password_reset_success.html' + "Found. Redirecting to http://localhost:8378/1/apps/password_reset_success.html" ); - Parse.User.logIn('user1', 'uuser11') + Parse.User.logIn("user1", "uuser11") .then(function () { done(); }) .catch(err => { jfail(err); - fail('should login with new password'); + fail("should login with new password"); done(); }); }) .catch(error => { jfail(error); - fail('Failed to POST request password reset'); + fail("Failed to POST request password reset"); }); }) .catch(error => { jfail(error); - fail('Failed to get the reset link'); + fail("Failed to get the reset link"); }); }, sendMail: () => {}, }; reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", emailAdapter: emailAdapter, passwordPolicy: { maxPasswordAge: 0.5 / (24 * 60 * 60), // 0.5 sec }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { - user.setUsername('user1'); - user.setPassword('user1'); - user.set('email', 'user1@parse.com'); + user.setUsername("user1"); + user.setPassword("user1"); + user.set("email", "user1@parse.com"); user .signUp() .then(() => { // wait for a bit more than the validity duration set setTimeout(() => { - Parse.User.logIn('user1', 'user1') + Parse.User.logIn("user1", "user1") .then(() => { - fail('logIn should have failed'); + fail("logIn should have failed"); done(); }) .catch(error => { expect(error.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); expect(error.message).toEqual( - 'Your password has expired. Please reset your password.' + "Your password has expired. Please reset your password." + ); + Parse.User.requestPasswordReset("user1@parse.com").catch( + err => { + jfail(err); + fail("Reset password request should not fail"); + done(); + } ); - Parse.User.requestPasswordReset('user1@parse.com').catch(err => { - jfail(err); - fail('Reset password request should not fail'); - done(); - }); }); }, 1000); }) .catch(error => { jfail(error); - fail('Signup failed.'); + fail("Signup failed."); done(); }); }); } ); - it('should fail if passwordPolicy.maxPasswordHistory is not a number', done => { + it("should fail if passwordPolicy.maxPasswordHistory is not a number", done => { reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", passwordPolicy: { - maxPasswordHistory: 'not a number', + maxPasswordHistory: "not a number", }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }) .then(() => { fail('passwordPolicy.maxPasswordHistory "not a number" test failed'); done(); }) .catch(err => { - expect(err).toEqual('passwordPolicy.maxPasswordHistory must be an integer ranging 0 - 20'); + expect(err).toEqual( + "passwordPolicy.maxPasswordHistory must be an integer ranging 0 - 20" + ); done(); }); }); - it('should fail if passwordPolicy.maxPasswordHistory is a negative number', done => { + it("should fail if passwordPolicy.maxPasswordHistory is a negative number", done => { reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", passwordPolicy: { maxPasswordHistory: -10, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }) .then(() => { - fail('passwordPolicy.maxPasswordHistory negative number test failed'); + fail("passwordPolicy.maxPasswordHistory negative number test failed"); done(); }) .catch(err => { - expect(err).toEqual('passwordPolicy.maxPasswordHistory must be an integer ranging 0 - 20'); + expect(err).toEqual( + "passwordPolicy.maxPasswordHistory must be an integer ranging 0 - 20" + ); done(); }); }); - it('should fail if passwordPolicy.maxPasswordHistory is greater than 20', done => { + it("should fail if passwordPolicy.maxPasswordHistory is greater than 20", done => { reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", passwordPolicy: { maxPasswordHistory: 21, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }) .then(() => { - fail('passwordPolicy.maxPasswordHistory negative number test failed'); + fail("passwordPolicy.maxPasswordHistory negative number test failed"); done(); }) .catch(err => { - expect(err).toEqual('passwordPolicy.maxPasswordHistory must be an integer ranging 0 - 20'); + expect(err).toEqual( + "passwordPolicy.maxPasswordHistory must be an integer ranging 0 - 20" + ); done(); }); }); - it('should fail to reset if the new password is same as the last password', done => { + it("should fail to reset if the new password is same as the last password", done => { const user = new Parse.User(); const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), @@ -1501,18 +1547,18 @@ describe('Password Policy: ', () => { /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; const match = response.text.match(re); if (!match) { - fail('should have a token'); - return Promise.reject('Invalid password link'); + fail("should have a token"); + return Promise.reject("Invalid password link"); } return Promise.resolve(match[1]); // token }) .then(token => { return request({ - method: 'POST', - url: 'http://localhost:8378/1/apps/test/request_password_reset', + method: "POST", + url: "http://localhost:8378/1/apps/test/request_password_reset", body: `new_password=user1&token=${token}`, headers: { - 'Content-Type': 'application/x-www-form-urlencoded', + "Content-Type": "application/x-www-form-urlencoded", }, followRedirects: false, simple: false, @@ -1533,218 +1579,228 @@ describe('Password Policy: ', () => { }) .catch(error => { fail(error); - fail('Repeat password test failed'); + fail("Repeat password test failed"); done(); }); }, sendMail: () => {}, }; reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", verifyUserEmails: false, emailAdapter: emailAdapter, passwordPolicy: { maxPasswordHistory: 1, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { - user.setUsername('user1'); - user.setPassword('user1'); - user.set('email', 'user1@parse.com'); + user.setUsername("user1"); + user.setPassword("user1"); + user.set("email", "user1@parse.com"); user .signUp() .then(() => { return Parse.User.logOut(); }) .then(() => { - return Parse.User.requestPasswordReset('user1@parse.com'); + return Parse.User.requestPasswordReset("user1@parse.com"); }) .catch(error => { jfail(error); - fail('SignUp or reset request failed'); + fail("SignUp or reset request failed"); done(); }); }); }); - it('should fail if the new password is same as the previous one', done => { + it("should fail if the new password is same as the previous one", done => { const user = new Parse.User(); reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", verifyUserEmails: false, passwordPolicy: { maxPasswordHistory: 5, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { - user.setUsername('user1'); - user.setPassword('user1'); - user.set('email', 'user1@parse.com'); + user.setUsername("user1"); + user.setPassword("user1"); + user.set("email", "user1@parse.com"); user .signUp() .then(() => { // try to set the same password as the previous one - user.setPassword('user1'); + user.setPassword("user1"); return user.save(); }) .then(() => { - fail('should have failed because the new password is same as the old'); + fail( + "should have failed because the new password is same as the old" + ); done(); }) .catch(error => { - expect(error.message).toEqual('New password should not be the same as last 5 passwords.'); + expect(error.message).toEqual( + "New password should not be the same as last 5 passwords." + ); expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); done(); }); }); }); - it('should fail if the new password is same as the 5th oldest one and policy does not allow the previous 5', done => { + it("should fail if the new password is same as the 5th oldest one and policy does not allow the previous 5", done => { const user = new Parse.User(); reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", verifyUserEmails: false, passwordPolicy: { maxPasswordHistory: 5, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { - user.setUsername('user1'); - user.setPassword('user1'); - user.set('email', 'user1@parse.com'); + user.setUsername("user1"); + user.setPassword("user1"); + user.set("email", "user1@parse.com"); user .signUp() .then(() => { // build history - user.setPassword('user2'); + user.setPassword("user2"); return user.save(); }) .then(() => { - user.setPassword('user3'); + user.setPassword("user3"); return user.save(); }) .then(() => { - user.setPassword('user4'); + user.setPassword("user4"); return user.save(); }) .then(() => { - user.setPassword('user5'); + user.setPassword("user5"); return user.save(); }) .then(() => { // set the same password as the initial one - user.setPassword('user1'); + user.setPassword("user1"); return user.save(); }) .then(() => { - fail('should have failed because the new password is same as the old'); + fail( + "should have failed because the new password is same as the old" + ); done(); }) .catch(error => { - expect(error.message).toEqual('New password should not be the same as last 5 passwords.'); + expect(error.message).toEqual( + "New password should not be the same as last 5 passwords." + ); expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); done(); }); }); }); - it('should succeed if the new password is same as the 6th oldest one and policy does not allow only previous 5', done => { + it("should succeed if the new password is same as the 6th oldest one and policy does not allow only previous 5", done => { const user = new Parse.User(); reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", verifyUserEmails: false, passwordPolicy: { maxPasswordHistory: 5, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { - user.setUsername('user1'); - user.setPassword('user1'); - user.set('email', 'user1@parse.com'); + user.setUsername("user1"); + user.setPassword("user1"); + user.set("email", "user1@parse.com"); user .signUp() .then(() => { // build history - user.setPassword('user2'); + user.setPassword("user2"); return user.save(); }) .then(() => { - user.setPassword('user3'); + user.setPassword("user3"); return user.save(); }) .then(() => { - user.setPassword('user4'); + user.setPassword("user4"); return user.save(); }) .then(() => { - user.setPassword('user5'); + user.setPassword("user5"); return user.save(); }) .then(() => { - user.setPassword('user6'); // this pushes initial password out of history + user.setPassword("user6"); // this pushes initial password out of history return user.save(); }) .then(() => { // set the same password as the initial one - user.setPassword('user1'); + user.setPassword("user1"); return user.save(); }) .then(() => { done(); }) .catch(() => { - fail('should have succeeded because the new password is not in history'); + fail( + "should have succeeded because the new password is not in history" + ); done(); }); }); }); - it('should not infinitely loop if maxPasswordHistory is 1 (#4918)', async () => { + it("should not infinitely loop if maxPasswordHistory is 1 (#4918)", async () => { const headers = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Rest-API-Key': 'test', - 'X-Parse-Maintenance-Key': 'test2', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": "test", + "X-Parse-Rest-API-Key": "test", + "X-Parse-Maintenance-Key": "test2", + "Content-Type": "application/json", }; const user = new Parse.User(); const query = new Parse.Query(Parse.User); await reconfigureServer({ - appName: 'passwordPolicy', + appName: "passwordPolicy", verifyUserEmails: false, - maintenanceKey: 'test2', + maintenanceKey: "test2", passwordPolicy: { maxPasswordHistory: 1, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }); - user.setUsername('user1'); - user.setPassword('user1'); - user.set('email', 'user1@parse.com'); + user.setUsername("user1"); + user.setPassword("user1"); + user.set("email", "user1@parse.com"); await user.signUp(); - user.setPassword('user2'); + user.setPassword("user2"); await user.save(); const user1 = await query.get(user.id, { useMasterKey: true }); - expect(user1.get('_password_history')).toBeUndefined(); + expect(user1.get("_password_history")).toBeUndefined(); const result1 = await request({ - method: 'GET', + method: "GET", url: `http://localhost:8378/1/classes/_User/${user.id}`, json: true, headers, }).then(res => res.data); expect(result1._password_history.length).toBe(1); - user.setPassword('user3'); + user.setPassword("user3"); await user.save(); const result2 = await request({ - method: 'GET', + method: "GET", url: `http://localhost:8378/1/classes/_User/${user.id}`, json: true, headers, diff --git a/spec/PointerPermissions.spec.js b/spec/PointerPermissions.spec.js index 94257681c5..473603d966 100644 --- a/spec/PointerPermissions.spec.js +++ b/spec/PointerPermissions.spec.js @@ -1,43 +1,47 @@ -'use strict'; -const Config = require('../lib/Config'); +"use strict"; +const Config = require("../lib/Config"); -describe('Pointer Permissions', () => { +describe("Pointer Permissions", () => { beforeEach(() => { Config.get(Parse.applicationId).schemaCache.clear(); }); - describe('using single user-pointers', () => { - it('should work with find', done => { + describe("using single user-pointers", () => { + it("should work with find", done => { const config = Config.get(Parse.applicationId); const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: 'user1', - password: 'password', + username: "user1", + password: "password", }); user2.set({ - username: 'user2', - password: 'password', + username: "user2", + password: "password", }); - const obj = new Parse.Object('AnObject'); - const obj2 = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); + const obj2 = new Parse.Object("AnObject"); Parse.Object.saveAll([user, user2]) .then(() => { - obj.set('owner', user); - obj2.set('owner', user2); + obj.set("owner", user); + obj2.set("owner", user2); return Parse.Object.saveAll([obj, obj2]); }) .then(() => { return config.database.loadSchema().then(schema => { - return schema.updateClass('AnObject', {}, { readUserFields: ['owner'] }); + return schema.updateClass( + "AnObject", + {}, + { readUserFields: ["owner"] } + ); }); }) .then(() => { - return Parse.User.logIn('user1', 'password'); + return Parse.User.logIn("user1", "password"); }) .then(() => { - const q = new Parse.Query('AnObject'); + const q = new Parse.Query("AnObject"); return q.find(); }) .then(res => { @@ -51,51 +55,51 @@ describe('Pointer Permissions', () => { }); }); - it('should work with write', done => { + it("should work with write", done => { const config = Config.get(Parse.applicationId); const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: 'user1', - password: 'password', + username: "user1", + password: "password", }); user2.set({ - username: 'user2', - password: 'password', + username: "user2", + password: "password", }); - const obj = new Parse.Object('AnObject'); - const obj2 = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); + const obj2 = new Parse.Object("AnObject"); Parse.Object.saveAll([user, user2]) .then(() => { - obj.set('owner', user); - obj.set('reader', user2); - obj2.set('owner', user2); - obj2.set('reader', user); + obj.set("owner", user); + obj.set("reader", user2); + obj2.set("owner", user2); + obj2.set("reader", user); return Parse.Object.saveAll([obj, obj2]); }) .then(() => { return config.database.loadSchema().then(schema => { return schema.updateClass( - 'AnObject', + "AnObject", {}, { - writeUserFields: ['owner'], - readUserFields: ['reader', 'owner'], + writeUserFields: ["owner"], + readUserFields: ["reader", "owner"], } ); }); }) .then(() => { - return Parse.User.logIn('user1', 'password'); + return Parse.User.logIn("user1", "password"); }) .then(() => { - obj2.set('hello', 'world'); + obj2.set("hello", "world"); return obj2.save(); }) .then( () => { - fail('User should not be able to update obj2'); + fail("User should not be able to update obj2"); }, err => { // User 1 should not be able to update obj2 @@ -104,25 +108,25 @@ describe('Pointer Permissions', () => { } ) .then(() => { - obj.set('hello', 'world'); + obj.set("hello", "world"); return obj.save(); }) .then( () => { - return Parse.User.logIn('user2', 'password'); + return Parse.User.logIn("user2", "password"); }, () => { - fail('User should be able to update'); + fail("User should be able to update"); return Promise.resolve(); } ) .then( () => { - const q = new Parse.Query('AnObject'); + const q = new Parse.Query("AnObject"); return q.find(); }, () => { - fail('should login with user 2'); + fail("should login with user 2"); } ) .then( @@ -130,7 +134,7 @@ describe('Pointer Permissions', () => { expect(res.length).toBe(2); res.forEach(result => { if (result.id == obj.id) { - expect(result.get('hello')).toBe('world'); + expect(result.get("hello")).toBe("world"); } else { expect(result.id).toBe(obj2.id); } @@ -138,26 +142,26 @@ describe('Pointer Permissions', () => { done(); }, () => { - fail('failed'); + fail("failed"); done(); } ); }); - it('should let a proper user find', done => { + it("should let a proper user find", done => { const config = Config.get(Parse.applicationId); const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: 'user1', - password: 'password', + username: "user1", + password: "password", }); user2.set({ - username: 'user2', - password: 'password', + username: "user2", + password: "password", }); - const obj = new Parse.Object('AnObject'); - const obj2 = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); + const obj2 = new Parse.Object("AnObject"); user .signUp() .then(() => { @@ -167,52 +171,52 @@ describe('Pointer Permissions', () => { Parse.User.logOut(); }) .then(() => { - obj.set('owner', user); + obj.set("owner", user); return Parse.Object.saveAll([obj, obj2]); }) .then(() => { return config.database.loadSchema().then(schema => { return schema.updateClass( - 'AnObject', + "AnObject", {}, - { find: {}, get: {}, readUserFields: ['owner'] } + { find: {}, get: {}, readUserFields: ["owner"] } ); }); }) .then(() => { - const q = new Parse.Query('AnObject'); + const q = new Parse.Query("AnObject"); return q.find(); }) .then(res => { expect(res.length).toBe(0); }) .then(() => { - return Parse.User.logIn('user2', 'password'); + return Parse.User.logIn("user2", "password"); }) .then(() => { - const q = new Parse.Query('AnObject'); + const q = new Parse.Query("AnObject"); return q.find(); }) .then(res => { expect(res.length).toBe(0); - const q = new Parse.Query('AnObject'); + const q = new Parse.Query("AnObject"); return q.get(obj.id); }) .then( () => { - fail('User 2 should not get the obj1 object'); + fail("User 2 should not get the obj1 object"); }, err => { expect(err.code).toBe(Parse.Error.OBJECT_NOT_FOUND); - expect(err.message).toBe('Object not found.'); + expect(err.message).toBe("Object not found."); return Promise.resolve(); } ) .then(() => { - return Parse.User.logIn('user1', 'password'); + return Parse.User.logIn("user1", "password"); }) .then(() => { - const q = new Parse.Query('AnObject'); + const q = new Parse.Query("AnObject"); return q.find(); }) .then(res => { @@ -221,27 +225,27 @@ describe('Pointer Permissions', () => { }) .catch(err => { jfail(err); - fail('should not fail'); + fail("should not fail"); done(); }); }); - it_id('f38c35e7-d804-4d32-986d-2579e25d2461')(it)( - 'should query on pointer permission enabled column', + it_id("f38c35e7-d804-4d32-986d-2579e25d2461")(it)( + "should query on pointer permission enabled column", done => { const config = Config.get(Parse.applicationId); const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: 'user1', - password: 'password', + username: "user1", + password: "password", }); user2.set({ - username: 'user2', - password: 'password', + username: "user2", + password: "password", }); - const obj = new Parse.Object('AnObject'); - const obj2 = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); + const obj2 = new Parse.Object("AnObject"); user .signUp() .then(() => { @@ -251,24 +255,24 @@ describe('Pointer Permissions', () => { Parse.User.logOut(); }) .then(() => { - obj.set('owner', user); + obj.set("owner", user); return Parse.Object.saveAll([obj, obj2]); }) .then(() => { return config.database.loadSchema().then(schema => { return schema.updateClass( - 'AnObject', + "AnObject", {}, - { find: {}, get: {}, readUserFields: ['owner'] } + { find: {}, get: {}, readUserFields: ["owner"] } ); }); }) .then(() => { - return Parse.User.logIn('user1', 'password'); + return Parse.User.logIn("user1", "password"); }) .then(() => { - const q = new Parse.Query('AnObject'); - q.equalTo('owner', user2); + const q = new Parse.Query("AnObject"); + q.equalTo("owner", user2); return q.find(); }) .then(res => { @@ -277,45 +281,45 @@ describe('Pointer Permissions', () => { }) .catch(err => { jfail(err); - fail('should not fail'); + fail("should not fail"); done(); }); } ); - it('should not allow creating objects', done => { + it("should not allow creating objects", done => { const config = Config.get(Parse.applicationId); const user = new Parse.User(); user.set({ - username: 'user1', - password: 'password', + username: "user1", + password: "password", }); - const obj = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); user .save() .then(() => { return config.database.loadSchema().then(schema => { return schema.addClassIfNotExists( - 'AnObject', - { owner: { type: 'Pointer', targetClass: '_User' } }, + "AnObject", + { owner: { type: "Pointer", targetClass: "_User" } }, { create: {}, - writeUserFields: ['owner'], - readUserFields: ['owner'], + writeUserFields: ["owner"], + readUserFields: ["owner"], } ); }); }) .then(() => { - return Parse.User.logIn('user1', 'password'); + return Parse.User.logIn("user1", "password"); }) .then(() => { - obj.set('owner', user); + obj.set("owner", user); return obj.save(); }) .then( () => { - fail('should not succeed'); + fail("should not succeed"); done(); }, err => { @@ -325,69 +329,69 @@ describe('Pointer Permissions', () => { ); }); - it('should handle multiple writeUserFields', done => { + it("should handle multiple writeUserFields", done => { const config = Config.get(Parse.applicationId); const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: 'user1', - password: 'password', + username: "user1", + password: "password", }); user2.set({ - username: 'user2', - password: 'password', + username: "user2", + password: "password", }); - const obj = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); Parse.Object.saveAll([user, user2]) .then(() => { - obj.set('owner', user); - obj.set('otherOwner', user2); + obj.set("owner", user); + obj.set("otherOwner", user2); return obj.save(); }) .then(() => config.database.loadSchema()) .then(schema => schema.updateClass( - 'AnObject', + "AnObject", {}, - { find: { '*': true }, writeUserFields: ['owner', 'otherOwner'] } + { find: { "*": true }, writeUserFields: ["owner", "otherOwner"] } ) ) - .then(() => Parse.User.logIn('user1', 'password')) - .then(() => obj.save({ hello: 'fromUser1' })) - .then(() => Parse.User.logIn('user2', 'password')) - .then(() => obj.save({ hello: 'fromUser2' })) + .then(() => Parse.User.logIn("user1", "password")) + .then(() => obj.save({ hello: "fromUser1" })) + .then(() => Parse.User.logIn("user2", "password")) + .then(() => obj.save({ hello: "fromUser2" })) .then(() => Parse.User.logOut()) .then(() => { - const q = new Parse.Query('AnObject'); + const q = new Parse.Query("AnObject"); return q.first(); }) .then(result => { - expect(result.get('hello')).toBe('fromUser2'); + expect(result.get("hello")).toBe("fromUser2"); done(); }) .catch(() => { - fail('should not fail'); + fail("should not fail"); done(); }); }); - it('should prevent creating pointer permission on missing field', done => { + it("should prevent creating pointer permission on missing field", done => { const config = Config.get(Parse.applicationId); config.database .loadSchema() .then(schema => { return schema.addClassIfNotExists( - 'AnObject', + "AnObject", {}, { create: {}, - writeUserFields: ['owner'], - readUserFields: ['owner'], + writeUserFields: ["owner"], + readUserFields: ["owner"], } ); }) .then(() => { - fail('should not succeed'); + fail("should not succeed"); }) .catch(err => { expect(err.code).toBe(107); @@ -398,23 +402,23 @@ describe('Pointer Permissions', () => { }); }); - it('should prevent creating pointer permission on bad field (of wrong type)', done => { + it("should prevent creating pointer permission on bad field (of wrong type)", done => { const config = Config.get(Parse.applicationId); config.database .loadSchema() .then(schema => { return schema.addClassIfNotExists( - 'AnObject', - { owner: { type: 'String' } }, + "AnObject", + { owner: { type: "String" } }, { create: {}, - writeUserFields: ['owner'], - readUserFields: ['owner'], + writeUserFields: ["owner"], + readUserFields: ["owner"], } ); }) .then(() => { - fail('should not succeed'); + fail("should not succeed"); }) .catch(err => { expect(err.code).toBe(107); @@ -425,23 +429,23 @@ describe('Pointer Permissions', () => { }); }); - it('should prevent creating pointer permission on bad field (non-user pointer)', done => { + it("should prevent creating pointer permission on bad field (non-user pointer)", done => { const config = Config.get(Parse.applicationId); config.database .loadSchema() .then(schema => { return schema.addClassIfNotExists( - 'AnObject', - { owner: { type: 'Pointer', targetClass: '_Session' } }, + "AnObject", + { owner: { type: "Pointer", targetClass: "_Session" } }, { create: {}, - writeUserFields: ['owner'], - readUserFields: ['owner'], + writeUserFields: ["owner"], + readUserFields: ["owner"], } ); }) .then(() => { - fail('should not succeed'); + fail("should not succeed"); }) .catch(err => { expect(err.code).toBe(107); @@ -452,10 +456,10 @@ describe('Pointer Permissions', () => { }); }); - it('should prevent creating pointer permission on bad field (non-existing)', done => { + it("should prevent creating pointer permission on bad field (non-existing)", done => { const config = Config.get(Parse.applicationId); - const object = new Parse.Object('AnObject'); - object.set('owner', 'value'); + const object = new Parse.Object("AnObject"); + object.set("owner", "value"); object .save() .then(() => { @@ -463,17 +467,17 @@ describe('Pointer Permissions', () => { }) .then(schema => { return schema.updateClass( - 'AnObject', + "AnObject", {}, { create: {}, - writeUserFields: ['owner'], - readUserFields: ['owner'], + writeUserFields: ["owner"], + readUserFields: ["owner"], } ); }) .then(() => { - fail('should not succeed'); + fail("should not succeed"); }) .catch(err => { expect(err.code).toBe(107); @@ -484,7 +488,7 @@ describe('Pointer Permissions', () => { }); }); - it('tests CLP / Pointer Perms / ACL write (PP Locked)', done => { + it("tests CLP / Pointer Perms / ACL write (PP Locked)", done => { /* tests: CLP: update closed ({}) @@ -497,39 +501,43 @@ describe('Pointer Permissions', () => { const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: 'user1', - password: 'password', + username: "user1", + password: "password", }); user2.set({ - username: 'user2', - password: 'password', + username: "user2", + password: "password", }); - const obj = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); Parse.Object.saveAll([user, user2]) .then(() => { const ACL = new Parse.ACL(); ACL.setReadAccess(user, true); ACL.setWriteAccess(user, true); obj.setACL(ACL); - obj.set('owner', user2); + obj.set("owner", user2); return obj.save(); }) .then(() => { return config.database.loadSchema().then(schema => { // Lock the update, and let only owner write - return schema.updateClass('AnObject', {}, { update: {}, writeUserFields: ['owner'] }); + return schema.updateClass( + "AnObject", + {}, + { update: {}, writeUserFields: ["owner"] } + ); }); }) .then(() => { - return Parse.User.logIn('user1', 'password'); + return Parse.User.logIn("user1", "password"); }) .then(() => { // user1 has ACL read/write but should be blocked by PP - return obj.save({ key: 'value' }); + return obj.save({ key: "value" }); }) .then( () => { - fail('Should not succeed saving'); + fail("Should not succeed saving"); done(); }, err => { @@ -539,7 +547,7 @@ describe('Pointer Permissions', () => { ); }); - it('tests CLP / Pointer Perms / ACL write (ACL Locked)', done => { + it("tests CLP / Pointer Perms / ACL write (ACL Locked)", done => { /* tests: CLP: update closed ({}) @@ -550,39 +558,43 @@ describe('Pointer Permissions', () => { const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: 'user1', - password: 'password', + username: "user1", + password: "password", }); user2.set({ - username: 'user2', - password: 'password', + username: "user2", + password: "password", }); - const obj = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); Parse.Object.saveAll([user, user2]) .then(() => { const ACL = new Parse.ACL(); ACL.setReadAccess(user, true); ACL.setWriteAccess(user, true); obj.setACL(ACL); - obj.set('owner', user2); + obj.set("owner", user2); return obj.save(); }) .then(() => { return config.database.loadSchema().then(schema => { // Lock the update, and let only owner write - return schema.updateClass('AnObject', {}, { update: {}, writeUserFields: ['owner'] }); + return schema.updateClass( + "AnObject", + {}, + { update: {}, writeUserFields: ["owner"] } + ); }); }) .then(() => { - return Parse.User.logIn('user2', 'password'); + return Parse.User.logIn("user2", "password"); }) .then(() => { // user1 has ACL read/write but should be blocked by ACL - return obj.save({ key: 'value' }); + return obj.save({ key: "value" }); }) .then( () => { - fail('Should not succeed saving'); + fail("Should not succeed saving"); done(); }, err => { @@ -592,7 +604,7 @@ describe('Pointer Permissions', () => { ); }); - it('tests CLP / Pointer Perms / ACL write (ACL/PP OK)', done => { + it("tests CLP / Pointer Perms / ACL write (ACL/PP OK)", done => { /* tests: CLP: update closed ({}) @@ -603,49 +615,53 @@ describe('Pointer Permissions', () => { const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: 'user1', - password: 'password', + username: "user1", + password: "password", }); user2.set({ - username: 'user2', - password: 'password', + username: "user2", + password: "password", }); - const obj = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); Parse.Object.saveAll([user, user2]) .then(() => { const ACL = new Parse.ACL(); ACL.setWriteAccess(user, true); ACL.setWriteAccess(user2, true); obj.setACL(ACL); - obj.set('owner', user2); + obj.set("owner", user2); return obj.save(); }) .then(() => { return config.database.loadSchema().then(schema => { // Lock the update, and let only owner write - return schema.updateClass('AnObject', {}, { update: {}, writeUserFields: ['owner'] }); + return schema.updateClass( + "AnObject", + {}, + { update: {}, writeUserFields: ["owner"] } + ); }); }) .then(() => { - return Parse.User.logIn('user2', 'password'); + return Parse.User.logIn("user2", "password"); }) .then(() => { // user1 has ACL read/write but should be blocked by ACL - return obj.save({ key: 'value' }); + return obj.save({ key: "value" }); }) .then( objAgain => { - expect(objAgain.get('key')).toBe('value'); + expect(objAgain.get("key")).toBe("value"); done(); }, () => { - fail('Should not fail saving'); + fail("Should not fail saving"); done(); } ); }); - it('tests CLP / Pointer Perms / ACL read (PP locked)', done => { + it("tests CLP / Pointer Perms / ACL read (PP locked)", done => { /* tests: CLP: find/get open ({}) @@ -658,35 +674,35 @@ describe('Pointer Permissions', () => { const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: 'user1', - password: 'password', + username: "user1", + password: "password", }); user2.set({ - username: 'user2', - password: 'password', + username: "user2", + password: "password", }); - const obj = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); Parse.Object.saveAll([user, user2]) .then(() => { const ACL = new Parse.ACL(); ACL.setReadAccess(user, true); ACL.setWriteAccess(user, true); obj.setACL(ACL); - obj.set('owner', user2); + obj.set("owner", user2); return obj.save(); }) .then(() => { return config.database.loadSchema().then(schema => { // Lock the update, and let only owner write return schema.updateClass( - 'AnObject', + "AnObject", {}, - { find: {}, get: {}, readUserFields: ['owner'] } + { find: {}, get: {}, readUserFields: ["owner"] } ); }); }) .then(() => { - return Parse.User.logIn('user1', 'password'); + return Parse.User.logIn("user1", "password"); }) .then(() => { // user1 has ACL read/write but should be block @@ -694,7 +710,7 @@ describe('Pointer Permissions', () => { }) .then( () => { - fail('Should not succeed saving'); + fail("Should not succeed saving"); done(); }, err => { @@ -704,7 +720,7 @@ describe('Pointer Permissions', () => { ); }); - it('tests CLP / Pointer Perms / ACL read (PP/ACL OK)', done => { + it("tests CLP / Pointer Perms / ACL read (PP/ACL OK)", done => { /* tests: CLP: find/get open ({"*": true}) @@ -715,14 +731,14 @@ describe('Pointer Permissions', () => { const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: 'user1', - password: 'password', + username: "user1", + password: "password", }); user2.set({ - username: 'user2', - password: 'password', + username: "user2", + password: "password", }); - const obj = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); Parse.Object.saveAll([user, user2]) .then(() => { const ACL = new Parse.ACL(); @@ -731,25 +747,25 @@ describe('Pointer Permissions', () => { ACL.setReadAccess(user2, true); ACL.setWriteAccess(user2, true); obj.setACL(ACL); - obj.set('owner', user2); + obj.set("owner", user2); return obj.save(); }) .then(() => { return config.database.loadSchema().then(schema => { // Lock the update, and let only owner write return schema.updateClass( - 'AnObject', + "AnObject", {}, { - find: { '*': true }, - get: { '*': true }, - readUserFields: ['owner'], + find: { "*": true }, + get: { "*": true }, + readUserFields: ["owner"], } ); }); }) .then(() => { - return Parse.User.logIn('user2', 'password'); + return Parse.User.logIn("user2", "password"); }) .then(() => { // user1 has ACL read/write but should be block @@ -761,13 +777,13 @@ describe('Pointer Permissions', () => { done(); }, () => { - fail('Should not fail fetching'); + fail("Should not fail fetching"); done(); } ); }); - it('tests CLP / Pointer Perms / ACL read (ACL locked)', done => { + it("tests CLP / Pointer Perms / ACL read (ACL locked)", done => { /* tests: CLP: find/get open ({"*": true}) @@ -778,39 +794,39 @@ describe('Pointer Permissions', () => { const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: 'user1', - password: 'password', + username: "user1", + password: "password", }); user2.set({ - username: 'user2', - password: 'password', + username: "user2", + password: "password", }); - const obj = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); Parse.Object.saveAll([user, user2]) .then(() => { const ACL = new Parse.ACL(); ACL.setReadAccess(user, true); ACL.setWriteAccess(user, true); obj.setACL(ACL); - obj.set('owner', user2); + obj.set("owner", user2); return obj.save(); }) .then(() => { return config.database.loadSchema().then(schema => { // Lock the update, and let only owner write return schema.updateClass( - 'AnObject', + "AnObject", {}, { - find: { '*': true }, - get: { '*': true }, - readUserFields: ['owner'], + find: { "*": true }, + get: { "*": true }, + readUserFields: ["owner"], } ); }); }) .then(() => { - return Parse.User.logIn('user2', 'password'); + return Parse.User.logIn("user2", "password"); }) .then(() => { // user2 has ACL read/write but should be block by ACL @@ -818,7 +834,7 @@ describe('Pointer Permissions', () => { }) .then( () => { - fail('Should not succeed saving'); + fail("Should not succeed saving"); done(); }, err => { @@ -828,24 +844,24 @@ describe('Pointer Permissions', () => { ); }); - it('should let master key find objects', done => { + it("should let master key find objects", done => { const config = Config.get(Parse.applicationId); - const object = new Parse.Object('AnObject'); - object.set('hello', 'world'); + const object = new Parse.Object("AnObject"); + object.set("hello", "world"); return object .save() .then(() => { return config.database.loadSchema().then(schema => { // Lock the update, and let only owner write return schema.updateClass( - 'AnObject', - { owner: { type: 'Pointer', targetClass: '_User' } }, - { find: {}, get: {}, readUserFields: ['owner'] } + "AnObject", + { owner: { type: "Pointer", targetClass: "_User" } }, + { find: {}, get: {}, readUserFields: ["owner"] } ); }); }) .then(() => { - const q = new Parse.Query('AnObject'); + const q = new Parse.Query("AnObject"); return q.find(); }) .then( @@ -856,7 +872,7 @@ describe('Pointer Permissions', () => { } ) .then(() => { - const q = new Parse.Query('AnObject'); + const q = new Parse.Query("AnObject"); return q.find({ useMasterKey: true }); }) .then( @@ -865,30 +881,30 @@ describe('Pointer Permissions', () => { done(); }, () => { - fail('master key should find the object'); + fail("master key should find the object"); done(); } ); }); - it('should let master key get objects', done => { + it("should let master key get objects", done => { const config = Config.get(Parse.applicationId); - const object = new Parse.Object('AnObject'); - object.set('hello', 'world'); + const object = new Parse.Object("AnObject"); + object.set("hello", "world"); return object .save() .then(() => { return config.database.loadSchema().then(schema => { // Lock the update, and let only owner write return schema.updateClass( - 'AnObject', - { owner: { type: 'Pointer', targetClass: '_User' } }, - { find: {}, get: {}, readUserFields: ['owner'] } + "AnObject", + { owner: { type: "Pointer", targetClass: "_User" } }, + { find: {}, get: {}, readUserFields: ["owner"] } ); }); }) .then(() => { - const q = new Parse.Query('AnObject'); + const q = new Parse.Query("AnObject"); return q.get(object.id); }) .then( @@ -899,7 +915,7 @@ describe('Pointer Permissions', () => { } ) .then(() => { - const q = new Parse.Query('AnObject'); + const q = new Parse.Query("AnObject"); return q.get(object.id, { useMasterKey: true }); }) .then( @@ -909,30 +925,30 @@ describe('Pointer Permissions', () => { done(); }, () => { - fail('master key should find the object'); + fail("master key should find the object"); done(); } ); }); - it('should let master key update objects', done => { + it("should let master key update objects", done => { const config = Config.get(Parse.applicationId); - const object = new Parse.Object('AnObject'); - object.set('hello', 'world'); + const object = new Parse.Object("AnObject"); + object.set("hello", "world"); return object .save() .then(() => { return config.database.loadSchema().then(schema => { // Lock the update, and let only owner write return schema.updateClass( - 'AnObject', - { owner: { type: 'Pointer', targetClass: '_User' } }, - { update: {}, writeUserFields: ['owner'] } + "AnObject", + { owner: { type: "Pointer", targetClass: "_User" } }, + { update: {}, writeUserFields: ["owner"] } ); }); }) .then(() => { - return object.save({ hello: 'bar' }); + return object.save({ hello: "bar" }); }) .then( () => {}, @@ -942,33 +958,33 @@ describe('Pointer Permissions', () => { } ) .then(() => { - return object.save({ hello: 'baz' }, { useMasterKey: true }); + return object.save({ hello: "baz" }, { useMasterKey: true }); }) .then( objectAgain => { - expect(objectAgain.get('hello')).toBe('baz'); + expect(objectAgain.get("hello")).toBe("baz"); done(); }, () => { - fail('master key should save the object'); + fail("master key should save the object"); done(); } ); }); - it('should let master key delete objects', done => { + it("should let master key delete objects", done => { const config = Config.get(Parse.applicationId); - const object = new Parse.Object('AnObject'); - object.set('hello', 'world'); + const object = new Parse.Object("AnObject"); + object.set("hello", "world"); return object .save() .then(() => { return config.database.loadSchema().then(schema => { // Lock the update, and let only owner write return schema.updateClass( - 'AnObject', - { owner: { type: 'Pointer', targetClass: '_User' } }, - { delete: {}, writeUserFields: ['owner'] } + "AnObject", + { owner: { type: "Pointer", targetClass: "_User" } }, + { delete: {}, writeUserFields: ["owner"] } ); }); }) @@ -992,22 +1008,22 @@ describe('Pointer Permissions', () => { done(); }, () => { - fail('master key should destroy the object'); + fail("master key should destroy the object"); done(); } ); }); - it('should fail with invalid pointer perms (not array)', done => { + it("should fail with invalid pointer perms (not array)", done => { const config = Config.get(Parse.applicationId); config.database .loadSchema() .then(schema => { // Lock the update, and let only owner write return schema.addClassIfNotExists( - 'AnObject', - { owner: { type: 'Pointer', targetClass: '_User' } }, - { delete: {}, writeUserFields: 'owner' } + "AnObject", + { owner: { type: "Pointer", targetClass: "_User" } }, + { delete: {}, writeUserFields: "owner" } ); }) .catch(err => { @@ -1016,16 +1032,16 @@ describe('Pointer Permissions', () => { }); }); - it('should fail with invalid pointer perms (non-existing field)', done => { + it("should fail with invalid pointer perms (non-existing field)", done => { const config = Config.get(Parse.applicationId); config.database .loadSchema() .then(schema => { // Lock the update, and let only owner write return schema.addClassIfNotExists( - 'AnObject', - { owner: { type: 'Pointer', targetClass: '_User' } }, - { delete: {}, writeUserFields: ['owner', 'invalid'] } + "AnObject", + { owner: { type: "Pointer", targetClass: "_User" } }, + { delete: {}, writeUserFields: ["owner", "invalid"] } ); }) .catch(err => { @@ -1035,35 +1051,35 @@ describe('Pointer Permissions', () => { }); }); - describe('using arrays of user-pointers', () => { - it('should work with find', async done => { + describe("using arrays of user-pointers", () => { + it("should work with find", async done => { const config = Config.get(Parse.applicationId); const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: 'user1', - password: 'password', + username: "user1", + password: "password", }); user2.set({ - username: 'user2', - password: 'password', + username: "user2", + password: "password", }); - const obj = new Parse.Object('AnObject'); - const obj2 = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); + const obj2 = new Parse.Object("AnObject"); await Parse.Object.saveAll([user, user2]); - obj.set('owners', [user]); - obj2.set('owners', [user2]); + obj.set("owners", [user]); + obj2.set("owners", [user2]); await Parse.Object.saveAll([obj, obj2]); const schema = await config.database.loadSchema(); - await schema.updateClass('AnObject', {}, { readUserFields: ['owners'] }); + await schema.updateClass("AnObject", {}, { readUserFields: ["owners"] }); - await Parse.User.logIn('user1', 'password'); + await Parse.User.logIn("user1", "password"); try { - const q = new Parse.Query('AnObject'); + const q = new Parse.Query("AnObject"); const res = await q.find(); expect(res.length).toBe(1); expect(res[0].id).toBe(obj.id); @@ -1073,264 +1089,279 @@ describe('Pointer Permissions', () => { } }); - it_id('1bbb9ed6-5558-4ce5-a238-b1a2015d273f')(it)('should work with write', async done => { - const config = Config.get(Parse.applicationId); - const user = new Parse.User(); - const user2 = new Parse.User(); - user.set({ - username: 'user1', - password: 'password', - }); - user2.set({ - username: 'user2', - password: 'password', - }); - const obj = new Parse.Object('AnObject'); - const obj2 = new Parse.Object('AnObject'); + it_id("1bbb9ed6-5558-4ce5-a238-b1a2015d273f")(it)( + "should work with write", + async done => { + const config = Config.get(Parse.applicationId); + const user = new Parse.User(); + const user2 = new Parse.User(); + user.set({ + username: "user1", + password: "password", + }); + user2.set({ + username: "user2", + password: "password", + }); + const obj = new Parse.Object("AnObject"); + const obj2 = new Parse.Object("AnObject"); - await Parse.Object.saveAll([user, user2]); + await Parse.Object.saveAll([user, user2]); - obj.set('owner', user); - obj.set('readers', [user2]); - obj2.set('owner', user2); - obj2.set('readers', [user]); - await Parse.Object.saveAll([obj, obj2]); + obj.set("owner", user); + obj.set("readers", [user2]); + obj2.set("owner", user2); + obj2.set("readers", [user]); + await Parse.Object.saveAll([obj, obj2]); - const schema = await config.database.loadSchema(); - await schema.updateClass( - 'AnObject', - {}, - { - writeUserFields: ['owner'], - readUserFields: ['readers', 'owner'], - } - ); + const schema = await config.database.loadSchema(); + await schema.updateClass( + "AnObject", + {}, + { + writeUserFields: ["owner"], + readUserFields: ["readers", "owner"], + } + ); - await Parse.User.logIn('user1', 'password'); + await Parse.User.logIn("user1", "password"); - obj2.set('hello', 'world'); - try { - await obj2.save(); - done.fail('User should not be able to update obj2'); - } catch (err) { - // User 1 should not be able to update obj2 - expect(err.code).toBe(Parse.Error.OBJECT_NOT_FOUND); - } + obj2.set("hello", "world"); + try { + await obj2.save(); + done.fail("User should not be able to update obj2"); + } catch (err) { + // User 1 should not be able to update obj2 + expect(err.code).toBe(Parse.Error.OBJECT_NOT_FOUND); + } - obj.set('hello', 'world'); - try { - await obj.save(); - } catch (err) { - done.fail('User should be able to update'); - } + obj.set("hello", "world"); + try { + await obj.save(); + } catch (err) { + done.fail("User should be able to update"); + } - await Parse.User.logIn('user2', 'password'); + await Parse.User.logIn("user2", "password"); - try { - const q = new Parse.Query('AnObject'); - const res = await q.find(); - expect(res.length).toBe(2); - res.forEach(result => { - if (result.id == obj.id) { - expect(result.get('hello')).toBe('world'); - } else { - expect(result.id).toBe(obj2.id); - } - }); - done(); - } catch (err) { - done.fail('failed'); + try { + const q = new Parse.Query("AnObject"); + const res = await q.find(); + expect(res.length).toBe(2); + res.forEach(result => { + if (result.id == obj.id) { + expect(result.get("hello")).toBe("world"); + } else { + expect(result.id).toBe(obj2.id); + } + }); + done(); + } catch (err) { + done.fail("failed"); + } } - }); + ); - it('should let a proper user find', async done => { + it("should let a proper user find", async done => { const config = Config.get(Parse.applicationId); const user = new Parse.User(); const user2 = new Parse.User(); const user3 = new Parse.User(); user.set({ - username: 'user1', - password: 'password', + username: "user1", + password: "password", }); user2.set({ - username: 'user2', - password: 'password', + username: "user2", + password: "password", }); user3.set({ - username: 'user3', - password: 'password', + username: "user3", + password: "password", }); - const obj = new Parse.Object('AnObject'); - const obj2 = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); + const obj2 = new Parse.Object("AnObject"); await user.signUp(); await user2.signUp(); await user3.signUp(); await Parse.User.logOut(); - obj.set('owners', [user, user2]); + obj.set("owners", [user, user2]); await Parse.Object.saveAll([obj, obj2]); const schema = await config.database.loadSchema(); - await schema.updateClass('AnObject', {}, { find: {}, get: {}, readUserFields: ['owners'] }); + await schema.updateClass( + "AnObject", + {}, + { find: {}, get: {}, readUserFields: ["owners"] } + ); - let q = new Parse.Query('AnObject'); + let q = new Parse.Query("AnObject"); let result = await q.find(); expect(result.length).toBe(0); - Parse.User.logIn('user3', 'password'); - q = new Parse.Query('AnObject'); + Parse.User.logIn("user3", "password"); + q = new Parse.Query("AnObject"); result = await q.find(); expect(result.length).toBe(0); - q = new Parse.Query('AnObject'); + q = new Parse.Query("AnObject"); try { await q.get(obj.id); - done.fail('User 3 should not get the obj1 object'); + done.fail("User 3 should not get the obj1 object"); } catch (err) { expect(err.code).toBe(Parse.Error.OBJECT_NOT_FOUND); - expect(err.message).toBe('Object not found.'); + expect(err.message).toBe("Object not found."); } - for (const owner of ['user1', 'user2']) { - await Parse.User.logIn(owner, 'password'); + for (const owner of ["user1", "user2"]) { + await Parse.User.logIn(owner, "password"); try { - const q = new Parse.Query('AnObject'); + const q = new Parse.Query("AnObject"); result = await q.find(); expect(result.length).toBe(1); } catch (err) { - done.fail('should not fail'); + done.fail("should not fail"); } } done(); }); - it_id('8a7d188c-b75c-4eac-90b6-9b0b11f873ae')(it)( - 'should query on pointer permission enabled column', + it_id("8a7d188c-b75c-4eac-90b6-9b0b11f873ae")(it)( + "should query on pointer permission enabled column", async done => { const config = Config.get(Parse.applicationId); const user = new Parse.User(); const user2 = new Parse.User(); const user3 = new Parse.User(); user.set({ - username: 'user1', - password: 'password', + username: "user1", + password: "password", }); user2.set({ - username: 'user2', - password: 'password', + username: "user2", + password: "password", }); user3.set({ - username: 'user3', - password: 'password', + username: "user3", + password: "password", }); - const obj = new Parse.Object('AnObject'); - const obj2 = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); + const obj2 = new Parse.Object("AnObject"); await user.signUp(); await user2.signUp(); await user3.signUp(); await Parse.User.logOut(); - obj.set('owners', [user, user2]); + obj.set("owners", [user, user2]); await Parse.Object.saveAll([obj, obj2]); const schema = await config.database.loadSchema(); - await schema.updateClass('AnObject', {}, { find: {}, get: {}, readUserFields: ['owners'] }); + await schema.updateClass( + "AnObject", + {}, + { find: {}, get: {}, readUserFields: ["owners"] } + ); - for (const owner of ['user1', 'user2']) { - await Parse.User.logIn(owner, 'password'); + for (const owner of ["user1", "user2"]) { + await Parse.User.logIn(owner, "password"); try { - const q = new Parse.Query('AnObject'); - q.equalTo('owners', user3); + const q = new Parse.Query("AnObject"); + q.equalTo("owners", user3); const result = await q.find(); expect(result.length).toBe(0); } catch (err) { - done.fail('should not fail'); + done.fail("should not fail"); } } done(); } ); - it('should not query using arrays on pointer permission enabled column', async done => { + it("should not query using arrays on pointer permission enabled column", async done => { const config = Config.get(Parse.applicationId); const user = new Parse.User(); const user2 = new Parse.User(); const user3 = new Parse.User(); user.set({ - username: 'user1', - password: 'password', + username: "user1", + password: "password", }); user2.set({ - username: 'user2', - password: 'password', + username: "user2", + password: "password", }); user3.set({ - username: 'user3', - password: 'password', + username: "user3", + password: "password", }); - const obj = new Parse.Object('AnObject'); - const obj2 = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); + const obj2 = new Parse.Object("AnObject"); await user.signUp(); await user2.signUp(); await user3.signUp(); await Parse.User.logOut(); - obj.set('owners', [user, user2]); + obj.set("owners", [user, user2]); await Parse.Object.saveAll([obj, obj2]); const schema = await config.database.loadSchema(); - await schema.updateClass('AnObject', {}, { find: {}, get: {}, readUserFields: ['owners'] }); + await schema.updateClass( + "AnObject", + {}, + { find: {}, get: {}, readUserFields: ["owners"] } + ); - for (const owner of ['user1', 'user2']) { + for (const owner of ["user1", "user2"]) { try { - await Parse.User.logIn(owner, 'password'); + await Parse.User.logIn(owner, "password"); // Since querying for arrays is not supported this should throw an error - const q = new Parse.Query('AnObject'); - q.equalTo('owners', [user3]); + const q = new Parse.Query("AnObject"); + q.equalTo("owners", [user3]); await q.find(); - done.fail('should fail'); + done.fail("should fail"); // eslint-disable-next-line no-empty } catch (error) {} } done(); }); - it('should not allow creating objects', async done => { + it("should not allow creating objects", async done => { const config = Config.get(Parse.applicationId); const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: 'user1', - password: 'password', + username: "user1", + password: "password", }); user2.set({ - username: 'user2', - password: 'password', + username: "user2", + password: "password", }); - const obj = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); await Parse.Object.saveAll([user, user2]); const schema = await config.database.loadSchema(); await schema.addClassIfNotExists( - 'AnObject', - { owners: { type: 'Array' } }, + "AnObject", + { owners: { type: "Array" } }, { create: {}, - writeUserFields: ['owners'], - readUserFields: ['owners'], + writeUserFields: ["owners"], + readUserFields: ["owners"], } ); - for (const owner of ['user1', 'user2']) { - await Parse.User.logIn(owner, 'password'); + for (const owner of ["user1", "user2"]) { + await Parse.User.logIn(owner, "password"); try { - obj.set('owners', [user, user2]); + obj.set("owners", [user, user2]); await obj.save(); - done.fail('should not succeed'); + done.fail("should not succeed"); } catch (err) { expect(err.code).toBe(119); } @@ -1338,62 +1369,62 @@ describe('Pointer Permissions', () => { done(); }); - it('should handle multiple writeUserFields', async done => { + it("should handle multiple writeUserFields", async done => { const config = Config.get(Parse.applicationId); const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: 'user1', - password: 'password', + username: "user1", + password: "password", }); user2.set({ - username: 'user2', - password: 'password', + username: "user2", + password: "password", }); - const obj = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); await Parse.Object.saveAll([user, user2]); - obj.set('owners', [user]); - obj.set('otherOwners', [user2]); + obj.set("owners", [user]); + obj.set("otherOwners", [user2]); await obj.save(); const schema = await config.database.loadSchema(); await schema.updateClass( - 'AnObject', + "AnObject", {}, - { find: { '*': true }, writeUserFields: ['owners', 'otherOwners'] } + { find: { "*": true }, writeUserFields: ["owners", "otherOwners"] } ); - await Parse.User.logIn('user1', 'password'); - await obj.save({ hello: 'fromUser1' }); - await Parse.User.logIn('user2', 'password'); - await obj.save({ hello: 'fromUser2' }); + await Parse.User.logIn("user1", "password"); + await obj.save({ hello: "fromUser1" }); + await Parse.User.logIn("user2", "password"); + await obj.save({ hello: "fromUser2" }); await Parse.User.logOut(); try { - const q = new Parse.Query('AnObject'); + const q = new Parse.Query("AnObject"); const result = await q.first(); - expect(result.get('hello')).toBe('fromUser2'); + expect(result.get("hello")).toBe("fromUser2"); done(); } catch (err) { - done.fail('should not fail'); + done.fail("should not fail"); } }); - it('should prevent creating pointer permission on missing field', async done => { + it("should prevent creating pointer permission on missing field", async done => { const config = Config.get(Parse.applicationId); const schema = await config.database.loadSchema(); try { await schema.addClassIfNotExists( - 'AnObject', + "AnObject", {}, { create: {}, - writeUserFields: ['owners'], - readUserFields: ['owners'], + writeUserFields: ["owners"], + readUserFields: ["owners"], } ); - done.fail('should not succeed'); + done.fail("should not succeed"); } catch (err) { expect(err.code).toBe(107); expect(err.message).toBe( @@ -1403,20 +1434,20 @@ describe('Pointer Permissions', () => { } }); - it('should prevent creating pointer permission on bad field (of wrong type)', async done => { + it("should prevent creating pointer permission on bad field (of wrong type)", async done => { const config = Config.get(Parse.applicationId); const schema = await config.database.loadSchema(); try { await schema.addClassIfNotExists( - 'AnObject', - { owners: { type: 'String' } }, + "AnObject", + { owners: { type: "String" } }, { create: {}, - writeUserFields: ['owners'], - readUserFields: ['owners'], + writeUserFields: ["owners"], + readUserFields: ["owners"], } ); - done.fail('should not succeed'); + done.fail("should not succeed"); } catch (err) { expect(err.code).toBe(107); expect(err.message).toBe( @@ -1426,24 +1457,24 @@ describe('Pointer Permissions', () => { } }); - it('should prevent creating pointer permission on bad field (non-existing)', async done => { + it("should prevent creating pointer permission on bad field (non-existing)", async done => { const config = Config.get(Parse.applicationId); - const object = new Parse.Object('AnObject'); - object.set('owners', 'value'); + const object = new Parse.Object("AnObject"); + object.set("owners", "value"); await object.save(); const schema = await config.database.loadSchema(); try { await schema.updateClass( - 'AnObject', + "AnObject", {}, { create: {}, - writeUserFields: ['owners'], - readUserFields: ['owners'], + writeUserFields: ["owners"], + readUserFields: ["owners"], } ); - done.fail('should not succeed'); + done.fail("should not succeed"); } catch (err) { expect(err.code).toBe(107); expect(err.message).toBe( @@ -1453,34 +1484,34 @@ describe('Pointer Permissions', () => { } }); - it('should work with arrays containing valid & invalid elements', async done => { + it("should work with arrays containing valid & invalid elements", async done => { /* Since there is no way to check the validity of objects in arrays before querying invalid elements in arrays should be ignored. */ const config = Config.get(Parse.applicationId); const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: 'user1', - password: 'password', + username: "user1", + password: "password", }); user2.set({ - username: 'user2', - password: 'password', + username: "user2", + password: "password", }); - const obj = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); await Parse.Object.saveAll([user, user2]); - obj.set('owners', [user, '', -1, true, [], { invalid: -1 }]); + obj.set("owners", [user, "", -1, true, [], { invalid: -1 }]); await Parse.Object.saveAll([obj]); const schema = await config.database.loadSchema(); - await schema.updateClass('AnObject', {}, { readUserFields: ['owners'] }); + await schema.updateClass("AnObject", {}, { readUserFields: ["owners"] }); - await Parse.User.logIn('user1', 'password'); + await Parse.User.logIn("user1", "password"); try { - const q = new Parse.Query('AnObject'); + const q = new Parse.Query("AnObject"); const res = await q.find(); expect(res.length).toBe(1); expect(res[0].id).toBe(obj.id); @@ -1489,10 +1520,10 @@ describe('Pointer Permissions', () => { } await Parse.User.logOut(); - await Parse.User.logIn('user2', 'password'); + await Parse.User.logIn("user2", "password"); try { - const q = new Parse.Query('AnObject'); + const q = new Parse.Query("AnObject"); const res = await q.find(); expect(res.length).toBe(0); done(); @@ -1501,7 +1532,7 @@ describe('Pointer Permissions', () => { } }); - it('tests CLP / Pointer Perms / ACL write (PP Locked)', async done => { + it("tests CLP / Pointer Perms / ACL write (PP Locked)", async done => { /* tests: CLP: update closed ({}) @@ -1515,18 +1546,18 @@ describe('Pointer Permissions', () => { const user2 = new Parse.User(); const user3 = new Parse.User(); user.set({ - username: 'user1', - password: 'password', + username: "user1", + password: "password", }); user2.set({ - username: 'user2', - password: 'password', + username: "user2", + password: "password", }); user3.set({ - username: 'user3', - password: 'password', + username: "user3", + password: "password", }); - const obj = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); await Parse.Object.saveAll([user, user2, user3]); @@ -1534,25 +1565,29 @@ describe('Pointer Permissions', () => { ACL.setReadAccess(user, true); ACL.setWriteAccess(user, true); obj.setACL(ACL); - obj.set('owners', [user2, user3]); + obj.set("owners", [user2, user3]); await obj.save(); const schema = await config.database.loadSchema(); // Lock the update, and let only owners write - await schema.updateClass('AnObject', {}, { update: {}, writeUserFields: ['owners'] }); + await schema.updateClass( + "AnObject", + {}, + { update: {}, writeUserFields: ["owners"] } + ); - await Parse.User.logIn('user1', 'password'); + await Parse.User.logIn("user1", "password"); try { // user1 has ACL read/write but should be blocked by PP - await obj.save({ key: 'value' }); - done.fail('Should not succeed saving'); + await obj.save({ key: "value" }); + done.fail("Should not succeed saving"); } catch (err) { expect(err.code).toBe(Parse.Error.OBJECT_NOT_FOUND); done(); } }); - it('tests CLP / Pointer Perms / ACL write (ACL Locked)', async done => { + it("tests CLP / Pointer Perms / ACL write (ACL Locked)", async done => { /* tests: CLP: update closed ({}) @@ -1564,18 +1599,18 @@ describe('Pointer Permissions', () => { const user2 = new Parse.User(); const user3 = new Parse.User(); user.set({ - username: 'user1', - password: 'password', + username: "user1", + password: "password", }); user2.set({ - username: 'user2', - password: 'password', + username: "user2", + password: "password", }); user3.set({ - username: 'user3', - password: 'password', + username: "user3", + password: "password", }); - const obj = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); await Parse.Object.saveAll([user, user2, user3]); @@ -1583,18 +1618,22 @@ describe('Pointer Permissions', () => { ACL.setReadAccess(user, true); ACL.setWriteAccess(user, true); obj.setACL(ACL); - obj.set('owners', [user2, user3]); + obj.set("owners", [user2, user3]); await obj.save(); const schema = await config.database.loadSchema(); // Lock the update, and let only owners write - await schema.updateClass('AnObject', {}, { update: {}, writeUserFields: ['owners'] }); + await schema.updateClass( + "AnObject", + {}, + { update: {}, writeUserFields: ["owners"] } + ); - for (const owner of ['user2', 'user3']) { - await Parse.User.logIn(owner, 'password'); + for (const owner of ["user2", "user3"]) { + await Parse.User.logIn(owner, "password"); try { - await obj.save({ key: 'value' }); - done.fail('Should not succeed saving'); + await obj.save({ key: "value" }); + done.fail("Should not succeed saving"); } catch (err) { expect(err.code).toBe(Parse.Error.OBJECT_NOT_FOUND); } @@ -1602,7 +1641,7 @@ describe('Pointer Permissions', () => { done(); }); - it('tests CLP / Pointer Perms / ACL write (ACL/PP OK)', async done => { + it("tests CLP / Pointer Perms / ACL write (ACL/PP OK)", async done => { /* tests: CLP: update closed ({}) @@ -1614,18 +1653,18 @@ describe('Pointer Permissions', () => { const user2 = new Parse.User(); const user3 = new Parse.User(); user.set({ - username: 'user1', - password: 'password', + username: "user1", + password: "password", }); user2.set({ - username: 'user2', - password: 'password', + username: "user2", + password: "password", }); user3.set({ - username: 'user3', - password: 'password', + username: "user3", + password: "password", }); - const obj = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); await Parse.Object.saveAll([user, user2, user3]); const ACL = new Parse.ACL(); @@ -1633,26 +1672,30 @@ describe('Pointer Permissions', () => { ACL.setWriteAccess(user2, true); ACL.setWriteAccess(user3, true); obj.setACL(ACL); - obj.set('owners', [user2, user3]); + obj.set("owners", [user2, user3]); await obj.save(); const schema = await config.database.loadSchema(); // Lock the update, and let only owners write - await schema.updateClass('AnObject', {}, { update: {}, writeUserFields: ['owners'] }); + await schema.updateClass( + "AnObject", + {}, + { update: {}, writeUserFields: ["owners"] } + ); - for (const owner of ['user2', 'user3']) { - await Parse.User.logIn(owner, 'password'); + for (const owner of ["user2", "user3"]) { + await Parse.User.logIn(owner, "password"); try { - const objectAgain = await obj.save({ key: 'value' }); - expect(objectAgain.get('key')).toBe('value'); + const objectAgain = await obj.save({ key: "value" }); + expect(objectAgain.get("key")).toBe("value"); } catch (err) { - done.fail('Should not fail saving'); + done.fail("Should not fail saving"); } } done(); }); - it('tests CLP / Pointer Perms / ACL read (PP locked)', async done => { + it("tests CLP / Pointer Perms / ACL read (PP locked)", async done => { /* tests: CLP: find/get open ({}) @@ -1666,18 +1709,18 @@ describe('Pointer Permissions', () => { const user2 = new Parse.User(); const user3 = new Parse.User(); user.set({ - username: 'user1', - password: 'password', + username: "user1", + password: "password", }); user2.set({ - username: 'user2', - password: 'password', + username: "user2", + password: "password", }); user3.set({ - username: 'user3', - password: 'password', + username: "user3", + password: "password", }); - const obj = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); await Parse.Object.saveAll([user, user2, user3]); @@ -1685,18 +1728,22 @@ describe('Pointer Permissions', () => { ACL.setReadAccess(user, true); ACL.setWriteAccess(user, true); obj.setACL(ACL); - obj.set('owners', [user2, user3]); + obj.set("owners", [user2, user3]); await obj.save(); const schema = await config.database.loadSchema(); // Lock reading, and let only owners read - await schema.updateClass('AnObject', {}, { find: {}, get: {}, readUserFields: ['owners'] }); + await schema.updateClass( + "AnObject", + {}, + { find: {}, get: {}, readUserFields: ["owners"] } + ); - await Parse.User.logIn('user1', 'password'); + await Parse.User.logIn("user1", "password"); try { // user1 has ACL read/write but should be blocked await obj.fetch(); - done.fail('Should not succeed fetching'); + done.fail("Should not succeed fetching"); } catch (err) { expect(err.code).toBe(Parse.Error.OBJECT_NOT_FOUND); done(); @@ -1704,7 +1751,7 @@ describe('Pointer Permissions', () => { done(); }); - it('tests CLP / Pointer Perms / ACL read (PP/ACL OK)', async done => { + it("tests CLP / Pointer Perms / ACL read (PP/ACL OK)", async done => { /* tests: CLP: find/get open ({"*": true}) @@ -1716,18 +1763,18 @@ describe('Pointer Permissions', () => { const user2 = new Parse.User(); const user3 = new Parse.User(); user.set({ - username: 'user1', - password: 'password', + username: "user1", + password: "password", }); user2.set({ - username: 'user2', - password: 'password', + username: "user2", + password: "password", }); user3.set({ - username: 'user3', - password: 'password', + username: "user3", + password: "password", }); - const obj = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); await Parse.Object.saveAll([user, user2, user3]); @@ -1739,34 +1786,34 @@ describe('Pointer Permissions', () => { ACL.setReadAccess(user3, true); ACL.setWriteAccess(user3, true); obj.setACL(ACL); - obj.set('owners', [user2, user3]); + obj.set("owners", [user2, user3]); await obj.save(); const schema = await config.database.loadSchema(); // Allow public and owners read await schema.updateClass( - 'AnObject', + "AnObject", {}, { - find: { '*': true }, - get: { '*': true }, - readUserFields: ['owners'], + find: { "*": true }, + get: { "*": true }, + readUserFields: ["owners"], } ); - for (const owner of ['user2', 'user3']) { - await Parse.User.logIn(owner, 'password'); + for (const owner of ["user2", "user3"]) { + await Parse.User.logIn(owner, "password"); try { const objectAgain = await obj.fetch(); expect(objectAgain.id).toBe(obj.id); } catch (err) { - done.fail('Should not fail fetching'); + done.fail("Should not fail fetching"); } } done(); }); - it('tests CLP / Pointer Perms / ACL read (ACL locked)', async done => { + it("tests CLP / Pointer Perms / ACL read (ACL locked)", async done => { /* tests: CLP: find/get open ({"*": true}) @@ -1778,44 +1825,44 @@ describe('Pointer Permissions', () => { const user2 = new Parse.User(); const user3 = new Parse.User(); user.set({ - username: 'user1', - password: 'password', + username: "user1", + password: "password", }); user2.set({ - username: 'user2', - password: 'password', + username: "user2", + password: "password", }); user3.set({ - username: 'user3', - password: 'password', + username: "user3", + password: "password", }); - const obj = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); await Parse.Object.saveAll([user, user2, user3]); const ACL = new Parse.ACL(); ACL.setReadAccess(user, true); ACL.setWriteAccess(user, true); obj.setACL(ACL); - obj.set('owners', [user2, user3]); + obj.set("owners", [user2, user3]); await obj.save(); const schema = await config.database.loadSchema(); // Allow public and owners read await schema.updateClass( - 'AnObject', + "AnObject", {}, { - find: { '*': true }, - get: { '*': true }, - readUserFields: ['owners'], + find: { "*": true }, + get: { "*": true }, + readUserFields: ["owners"], } ); - for (const owner of ['user2', 'user3']) { - await Parse.User.logIn(owner, 'password'); + for (const owner of ["user2", "user3"]) { + await Parse.User.logIn(owner, "password"); try { await obj.fetch(); - done.fail('Should not succeed fetching'); + done.fail("Should not succeed fetching"); } catch (err) { expect(err.code).toBe(Parse.Error.OBJECT_NOT_FOUND); } @@ -1823,21 +1870,21 @@ describe('Pointer Permissions', () => { done(); }); - it('should let master key find objects', async done => { + it("should let master key find objects", async done => { const config = Config.get(Parse.applicationId); - const object = new Parse.Object('AnObject'); - object.set('hello', 'world'); + const object = new Parse.Object("AnObject"); + object.set("hello", "world"); await object.save(); const schema = await config.database.loadSchema(); // Lock the find/get, and let only owners read await schema.updateClass( - 'AnObject', - { owners: { type: 'Array' } }, - { find: {}, get: {}, readUserFields: ['owners'] } + "AnObject", + { owners: { type: "Array" } }, + { find: {}, get: {}, readUserFields: ["owners"] } ); - const q = new Parse.Query('AnObject'); + const q = new Parse.Query("AnObject"); const objects = await q.find(); expect(objects.length).toBe(0); @@ -1846,25 +1893,25 @@ describe('Pointer Permissions', () => { expect(objects.length).toBe(1); done(); } catch (err) { - done.fail('master key should find the object'); + done.fail("master key should find the object"); } }); - it('should let master key get objects', async done => { + it("should let master key get objects", async done => { const config = Config.get(Parse.applicationId); - const object = new Parse.Object('AnObject'); - object.set('hello', 'world'); + const object = new Parse.Object("AnObject"); + object.set("hello", "world"); await object.save(); const schema = await config.database.loadSchema(); // Lock the find/get, and let only owners read await schema.updateClass( - 'AnObject', - { owners: { type: 'Array' } }, - { find: {}, get: {}, readUserFields: ['owners'] } + "AnObject", + { owners: { type: "Array" } }, + { find: {}, get: {}, readUserFields: ["owners"] } ); - const q = new Parse.Query('AnObject'); + const q = new Parse.Query("AnObject"); try { await q.get(object.id); done.fail(); @@ -1878,53 +1925,56 @@ describe('Pointer Permissions', () => { expect(objectAgain.id).toBe(object.id); done(); } catch (err) { - done.fail('master key should get the object'); + done.fail("master key should get the object"); } }); - it('should let master key update objects', async done => { + it("should let master key update objects", async done => { const config = Config.get(Parse.applicationId); - const object = new Parse.Object('AnObject'); - object.set('hello', 'world'); + const object = new Parse.Object("AnObject"); + object.set("hello", "world"); await object.save(); const schema = await config.database.loadSchema(); // Lock the update, and let only owners write await schema.updateClass( - 'AnObject', - { owners: { type: 'Array' } }, - { update: {}, writeUserFields: ['owners'] } + "AnObject", + { owners: { type: "Array" } }, + { update: {}, writeUserFields: ["owners"] } ); try { - await object.save({ hello: 'bar' }); + await object.save({ hello: "bar" }); done.fail(); } catch (err) { expect(err.code).toBe(Parse.Error.OBJECT_NOT_FOUND); } try { - const objectAgain = await object.save({ hello: 'baz' }, { useMasterKey: true }); - expect(objectAgain.get('hello')).toBe('baz'); + const objectAgain = await object.save( + { hello: "baz" }, + { useMasterKey: true } + ); + expect(objectAgain.get("hello")).toBe("baz"); done(); } catch (err) { - done.fail('master key should save the object'); + done.fail("master key should save the object"); } }); - it('should let master key delete objects', async done => { + it("should let master key delete objects", async done => { const config = Config.get(Parse.applicationId); - const object = new Parse.Object('AnObject'); - object.set('hello', 'world'); + const object = new Parse.Object("AnObject"); + object.set("hello", "world"); await object.save(); const schema = await config.database.loadSchema(); // Lock the delete, and let only owners write await schema.updateClass( - 'AnObject', - { owners: { type: 'Array' } }, - { delete: {}, writeUserFields: ['owners'] } + "AnObject", + { owners: { type: "Array" } }, + { delete: {}, writeUserFields: ["owners"] } ); try { @@ -1937,19 +1987,19 @@ describe('Pointer Permissions', () => { await object.destroy({ useMasterKey: true }); done(); } catch (err) { - done.fail('master key should destroy the object'); + done.fail("master key should destroy the object"); } }); - it('should fail with invalid pointer perms (not array)', async done => { + it("should fail with invalid pointer perms (not array)", async done => { const config = Config.get(Parse.applicationId); const schema = await config.database.loadSchema(); try { // Lock the delete, and let only owners write await schema.addClassIfNotExists( - 'AnObject', - { owners: { type: 'Array' } }, - { delete: {}, writeUserFields: 'owners' } + "AnObject", + { owners: { type: "Array" } }, + { delete: {}, writeUserFields: "owners" } ); } catch (err) { expect(err.code).toBe(Parse.Error.INVALID_JSON); @@ -1957,15 +2007,15 @@ describe('Pointer Permissions', () => { } }); - it('should fail with invalid pointer perms (non-existing field)', async done => { + it("should fail with invalid pointer perms (non-existing field)", async done => { const config = Config.get(Parse.applicationId); const schema = await config.database.loadSchema(); try { // Lock the delete, and let only owners write await schema.addClassIfNotExists( - 'AnObject', - { owners: { type: 'Array' } }, - { delete: {}, writeUserFields: ['owners', 'invalid'] } + "AnObject", + { owners: { type: "Array" } }, + { delete: {}, writeUserFields: ["owners", "invalid"] } ); } catch (err) { expect(err.code).toBe(Parse.Error.INVALID_JSON); @@ -1974,8 +2024,8 @@ describe('Pointer Permissions', () => { }); }); - describe('Granular ', () => { - const className = 'AnObject'; + describe("Granular ", () => { + const className = "AnObject"; const actionGet = id => new Parse.Query(className).get(id); const actionFind = () => new Parse.Query(className).find(); @@ -1984,13 +2034,17 @@ describe('Pointer Permissions', () => { const actionUpdate = obj => obj.save({ revision: 2 }); const actionDelete = obj => obj.destroy(); const actionAddFieldOnCreate = () => - new Parse.Object(className, { ['extra' + Date.now()]: 'field' }).save(); - const actionAddFieldOnUpdate = obj => obj.save({ ['another' + Date.now()]: 'field' }); + new Parse.Object(className, { ["extra" + Date.now()]: "field" }).save(); + const actionAddFieldOnUpdate = obj => + obj.save({ ["another" + Date.now()]: "field" }); - const OBJECT_NOT_FOUND = new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.'); - const PERMISSION_DENIED = jasmine.stringMatching('Permission denied'); + const OBJECT_NOT_FOUND = new Parse.Error( + Parse.Error.OBJECT_NOT_FOUND, + "Object not found." + ); + const PERMISSION_DENIED = jasmine.stringMatching("Permission denied"); - async function createUser(username, password = 'password') { + async function createUser(username, password = "password") { const user = new Parse.User({ username: username + Date.now(), password, @@ -2002,7 +2056,7 @@ describe('Pointer Permissions', () => { } async function logIn(userObject) { - return await Parse.User.logIn(userObject.getUsername(), 'password'); + return await Parse.User.logIn(userObject.getUsername(), "password"); } async function updateCLP(clp) { @@ -2012,7 +2066,7 @@ describe('Pointer Permissions', () => { await schemaController.updateClass(className, {}, clp); } - describe('on single-pointer fields', () => { + describe("on single-pointer fields", () => { /** owns: **obj1** */ let user1; @@ -2028,7 +2082,10 @@ describe('Pointer Permissions', () => { async function initialize() { await Config.get(Parse.applicationId).schemaCache.clear(); - [user1, user2] = await Promise.all([createUser('user1'), createUser('user2')]); + [user1, user2] = await Promise.all([ + createUser("user1"), + createUser("user2"), + ]); obj1 = new Parse.Object(className, { owner: user1, @@ -2049,11 +2106,11 @@ describe('Pointer Permissions', () => { await initialize(); }); - describe('get action', () => { - it('should be allowed', async done => { + describe("get action", () => { + it("should be allowed", async done => { await updateCLP({ get: { - pointerFields: ['owner'], + pointerFields: ["owner"], }, }); @@ -2064,26 +2121,28 @@ describe('Pointer Permissions', () => { done(); }); - it_id('9ba681d5-59f5-4996-b36d-6647d23e6a44')(it)( - 'should fail for user not listed', + it_id("9ba681d5-59f5-4996-b36d-6647d23e6a44")(it)( + "should fail for user not listed", async done => { await updateCLP({ get: { - pointerFields: ['owner'], + pointerFields: ["owner"], }, }); await logIn(user2); - await expectAsync(actionGet(obj1.id)).toBeRejectedWith(OBJECT_NOT_FOUND); + await expectAsync(actionGet(obj1.id)).toBeRejectedWith( + OBJECT_NOT_FOUND + ); done(); } ); - it('should not allow other actions', async done => { + it("should not allow other actions", async done => { await updateCLP({ get: { - pointerFields: ['owner'], + pointerFields: ["owner"], }, }); @@ -2105,11 +2164,11 @@ describe('Pointer Permissions', () => { }); }); - describe('find action', () => { - it('should be allowed', async done => { + describe("find action", () => { + it("should be allowed", async done => { await updateCLP({ find: { - pointerFields: ['owner'], + pointerFields: ["owner"], }, }); @@ -2119,10 +2178,10 @@ describe('Pointer Permissions', () => { done(); }); - it('should be limited to objects where user is listed in field', async done => { + it("should be limited to objects where user is listed in field", async done => { await updateCLP({ find: { - pointerFields: ['owner'], + pointerFields: ["owner"], }, }); @@ -2133,10 +2192,10 @@ describe('Pointer Permissions', () => { done(); }); - it('should not allow other actions', async done => { + it("should not allow other actions", async done => { await updateCLP({ find: { - pointerFields: ['owner'], + pointerFields: ["owner"], }, }); @@ -2158,11 +2217,11 @@ describe('Pointer Permissions', () => { }); }); - describe('count action', () => { - it('should be allowed', async done => { + describe("count action", () => { + it("should be allowed", async done => { await updateCLP({ count: { - pointerFields: ['owner'], + pointerFields: ["owner"], }, }); @@ -2173,14 +2232,14 @@ describe('Pointer Permissions', () => { done(); }); - it('should be limited to objects where user is listed in field', async done => { + it("should be limited to objects where user is listed in field", async done => { await updateCLP({ count: { - pointerFields: ['owner'], + pointerFields: ["owner"], }, }); - const user3 = await createUser('user3'); + const user3 = await createUser("user3"); await logIn(user3); const p = await actionCount(); @@ -2189,10 +2248,10 @@ describe('Pointer Permissions', () => { done(); }); - it('should not allow other actions', async done => { + it("should not allow other actions", async done => { await updateCLP({ count: { - pointerFields: ['owner'], + pointerFields: ["owner"], }, }); @@ -2214,11 +2273,11 @@ describe('Pointer Permissions', () => { }); }); - describe('update action', () => { - it('should be allowed', async done => { + describe("update action", () => { + it("should be allowed", async done => { await updateCLP({ update: { - pointerFields: ['owner'], + pointerFields: ["owner"], }, }); @@ -2227,26 +2286,28 @@ describe('Pointer Permissions', () => { done(); }); - it_id('bcdb158d-c0b6-45e3-84ab-a3636f7cb470')(it)( - 'should fail for user not listed', + it_id("bcdb158d-c0b6-45e3-84ab-a3636f7cb470")(it)( + "should fail for user not listed", async done => { await updateCLP({ update: { - pointerFields: ['owner'], + pointerFields: ["owner"], }, }); await logIn(user2); - await expectAsync(actionUpdate(obj1)).toBeRejectedWith(OBJECT_NOT_FOUND); + await expectAsync(actionUpdate(obj1)).toBeRejectedWith( + OBJECT_NOT_FOUND + ); done(); } ); - it('should not allow other actions', async done => { + it("should not allow other actions", async done => { await updateCLP({ update: { - pointerFields: ['owner'], + pointerFields: ["owner"], }, }); @@ -2268,11 +2329,11 @@ describe('Pointer Permissions', () => { }); }); - describe('delete action', () => { - it('should be allowed', async done => { + describe("delete action", () => { + it("should be allowed", async done => { await updateCLP({ delete: { - pointerFields: ['owner'], + pointerFields: ["owner"], }, }); @@ -2282,26 +2343,28 @@ describe('Pointer Permissions', () => { done(); }); - it_id('70aa3853-6e26-4c38-a927-2ddb24ced7d4')(it)( - 'should fail for user not listed', + it_id("70aa3853-6e26-4c38-a927-2ddb24ced7d4")(it)( + "should fail for user not listed", async done => { await updateCLP({ delete: { - pointerFields: ['owner'], + pointerFields: ["owner"], }, }); await logIn(user2); - await expectAsync(actionDelete(obj1)).toBeRejectedWith(OBJECT_NOT_FOUND); + await expectAsync(actionDelete(obj1)).toBeRejectedWith( + OBJECT_NOT_FOUND + ); done(); } ); - it('should not allow other actions', async done => { + it("should not allow other actions", async done => { await updateCLP({ delete: { - pointerFields: ['owner'], + pointerFields: ["owner"], }, }); @@ -2323,13 +2386,13 @@ describe('Pointer Permissions', () => { }); }); - describe('create action', () => { + describe("create action", () => { // For Pointer permissions create is different from other operations // since there's no object holding the pointer before created - it('should be denied (writelock) when no other permissions on class', async done => { + it("should be denied (writelock) when no other permissions on class", async done => { await updateCLP({ create: { - pointerFields: ['owner'], + pointerFields: ["owner"], }, }); @@ -2339,15 +2402,15 @@ describe('Pointer Permissions', () => { }); }); - describe('addField action', () => { - xit('should have no effect when creating object (and allowed by explicit userid permission)', async done => { + describe("addField action", () => { + xit("should have no effect when creating object (and allowed by explicit userid permission)", async done => { await updateCLP({ create: { - '*': true, + "*": true, }, addField: { [user1.id]: true, - pointerFields: ['owner'], + pointerFields: ["owner"], }, }); @@ -2357,13 +2420,13 @@ describe('Pointer Permissions', () => { done(); }); - xit('should be denied when creating object (and no explicit permission)', async done => { + xit("should be denied when creating object (and no explicit permission)", async done => { await updateCLP({ create: { - '*': true, + "*": true, }, addField: { - pointerFields: ['owner'], + pointerFields: ["owner"], }, }); @@ -2371,19 +2434,21 @@ describe('Pointer Permissions', () => { const newObject = new Parse.Object(className, { owner: user1, - extra: 'field', + extra: "field", }); - await expectAsync(newObject.save()).toBeRejectedWith(PERMISSION_DENIED); + await expectAsync(newObject.save()).toBeRejectedWith( + PERMISSION_DENIED + ); done(); }); - it('should be allowed when updating object', async done => { + it("should be allowed when updating object", async done => { await updateCLP({ update: { - '*': true, + "*": true, }, addField: { - pointerFields: ['owner'], + pointerFields: ["owner"], }, }); @@ -2394,26 +2459,28 @@ describe('Pointer Permissions', () => { done(); }); - it('should be denied when updating object for user without addField permission', async done => { + it("should be denied when updating object for user without addField permission", async done => { await updateCLP({ update: { - '*': true, + "*": true, }, addField: { - pointerFields: ['owner'], + pointerFields: ["owner"], }, }); await logIn(user2); - await expectAsync(actionAddFieldOnUpdate(obj1)).toBeRejectedWith(OBJECT_NOT_FOUND); + await expectAsync(actionAddFieldOnUpdate(obj1)).toBeRejectedWith( + OBJECT_NOT_FOUND + ); done(); }); }); }); - describe('on array of pointers', () => { + describe("on array of pointers", () => { /** * owns: **obj1** * @@ -2460,9 +2527,9 @@ describe('Pointer Permissions', () => { await Config.get(Parse.applicationId).schemaCache.clear(); [user1, user2, user3] = await Promise.all([ - createUser('user1'), - createUser('user2'), - createUser('user3'), + createUser("user1"), + createUser("user2"), + createUser("user3"), ]); obj1 = new Parse.Object(className); @@ -2503,11 +2570,11 @@ describe('Pointer Permissions', () => { await initialize(); }); - describe('get action', () => { - it('should be allowed (1 user in array)', async done => { + describe("get action", () => { + it("should be allowed (1 user in array)", async done => { await updateCLP({ get: { - pointerFields: ['owners'], + pointerFields: ["owners"], }, }); @@ -2518,10 +2585,10 @@ describe('Pointer Permissions', () => { done(); }); - it('should be allowed (multiple users in array)', async done => { + it("should be allowed (multiple users in array)", async done => { await updateCLP({ get: { - pointerFields: ['moderators'], + pointerFields: ["moderators"], }, }); @@ -2532,26 +2599,28 @@ describe('Pointer Permissions', () => { done(); }); - it_id('84a42339-c7b5-4735-a431-57b46535b073')(it)( - 'should fail for user not listed', + it_id("84a42339-c7b5-4735-a431-57b46535b073")(it)( + "should fail for user not listed", async done => { await updateCLP({ get: { - pointerFields: ['moderators'], + pointerFields: ["moderators"], }, }); await logIn(user1); - await expectAsync(actionGet(obj3.id)).toBeRejectedWith(OBJECT_NOT_FOUND); + await expectAsync(actionGet(obj3.id)).toBeRejectedWith( + OBJECT_NOT_FOUND + ); done(); } ); - it('should not allow other actions', async done => { + it("should not allow other actions", async done => { await updateCLP({ get: { - pointerFields: ['owners'], + pointerFields: ["owners"], }, }); @@ -2574,11 +2643,11 @@ describe('Pointer Permissions', () => { }); }); - describe('find action', () => { - it('should be allowed (1 user in array)', async done => { + describe("find action", () => { + it("should be allowed (1 user in array)", async done => { await updateCLP({ find: { - pointerFields: ['owners'], + pointerFields: ["owners"], }, }); @@ -2589,10 +2658,10 @@ describe('Pointer Permissions', () => { done(); }); - it('should be allowed (multiple users in array)', async done => { + it("should be allowed (multiple users in array)", async done => { await updateCLP({ find: { - pointerFields: ['moderators'], + pointerFields: ["moderators"], }, }); @@ -2603,10 +2672,10 @@ describe('Pointer Permissions', () => { done(); }); - it('should be limited to objects where user is listed in field', async done => { + it("should be limited to objects where user is listed in field", async done => { await updateCLP({ find: { - pointerFields: ['moderators'], + pointerFields: ["moderators"], }, }); @@ -2617,10 +2686,10 @@ describe('Pointer Permissions', () => { done(); }); - it('should not allow other actions', async done => { + it("should not allow other actions", async done => { await updateCLP({ find: { - pointerFields: ['moderators'], + pointerFields: ["moderators"], }, }); @@ -2643,16 +2712,16 @@ describe('Pointer Permissions', () => { }); }); - describe('count action', () => { + describe("count action", () => { beforeEach(async () => { await updateCLP({ count: { - pointerFields: ['moderators'], + pointerFields: ["moderators"], }, }); }); - it('should be allowed', async done => { + it("should be allowed", async done => { await logIn(user1); const count = await actionCount(); @@ -2660,7 +2729,7 @@ describe('Pointer Permissions', () => { done(); }); - it('should be limited to objects where user is listed in field', async done => { + it("should be limited to objects where user is listed in field", async done => { await logIn(user2); const count = await actionCount(); @@ -2669,7 +2738,7 @@ describe('Pointer Permissions', () => { done(); }); - it('should not allow other actions', async done => { + it("should not allow other actions", async done => { await logIn(user1); await Promise.all( @@ -2689,11 +2758,11 @@ describe('Pointer Permissions', () => { }); }); - describe('update action', () => { - it('should be allowed (1 user in array)', async done => { + describe("update action", () => { + it("should be allowed (1 user in array)", async done => { await updateCLP({ update: { - pointerFields: ['owners'], + pointerFields: ["owners"], }, }); @@ -2703,12 +2772,12 @@ describe('Pointer Permissions', () => { done(); }); - it_id('2b19234a-a471-48b4-bd1a-27bd286d066f')(it)( - 'should be allowed (multiple users in array)', + it_id("2b19234a-a471-48b4-bd1a-27bd286d066f")(it)( + "should be allowed (multiple users in array)", async done => { await updateCLP({ update: { - pointerFields: ['moderators'], + pointerFields: ["moderators"], }, }); @@ -2719,26 +2788,28 @@ describe('Pointer Permissions', () => { } ); - it_id('1abb9f4a-fb24-48c7-8025-3001d6cf8737')(it)( - 'should fail for user not listed', + it_id("1abb9f4a-fb24-48c7-8025-3001d6cf8737")(it)( + "should fail for user not listed", async done => { await updateCLP({ update: { - pointerFields: ['moderators'], + pointerFields: ["moderators"], }, }); await logIn(user2); - await expectAsync(actionUpdate(obj3)).toBeRejectedWith(OBJECT_NOT_FOUND); + await expectAsync(actionUpdate(obj3)).toBeRejectedWith( + OBJECT_NOT_FOUND + ); done(); } ); - it('should not allow other actions', async done => { + it("should not allow other actions", async done => { await updateCLP({ update: { - pointerFields: ['moderators'], + pointerFields: ["moderators"], }, }); @@ -2761,11 +2832,11 @@ describe('Pointer Permissions', () => { }); }); - describe('delete action', () => { - it('should be allowed (1 user in array)', async done => { + describe("delete action", () => { + it("should be allowed (1 user in array)", async done => { await updateCLP({ delete: { - pointerFields: ['owners'], + pointerFields: ["owners"], }, }); @@ -2775,10 +2846,10 @@ describe('Pointer Permissions', () => { done(); }); - it('should be allowed (multiple users in array)', async done => { + it("should be allowed (multiple users in array)", async done => { await updateCLP({ delete: { - pointerFields: ['moderators'], + pointerFields: ["moderators"], }, }); @@ -2788,26 +2859,28 @@ describe('Pointer Permissions', () => { done(); }); - it_id('3175a0e3-e51e-4b84-a2e6-50bbcc582123')(it)( - 'should fail for user not listed', + it_id("3175a0e3-e51e-4b84-a2e6-50bbcc582123")(it)( + "should fail for user not listed", async done => { await updateCLP({ delete: { - pointerFields: ['owners'], + pointerFields: ["owners"], }, }); await logIn(user1); - await expectAsync(actionDelete(obj3)).toBeRejectedWith(OBJECT_NOT_FOUND); + await expectAsync(actionDelete(obj3)).toBeRejectedWith( + OBJECT_NOT_FOUND + ); done(); } ); - it('should not allow other actions', async done => { + it("should not allow other actions", async done => { await updateCLP({ delete: { - pointerFields: ['moderators'], + pointerFields: ["moderators"], }, }); @@ -2830,13 +2903,13 @@ describe('Pointer Permissions', () => { }); }); - describe('create action', () => { + describe("create action", () => { /* For Pointer permissions 'create' is different from other operations since there's no object holding the pointer before created */ - it('should be denied (writelock) when no other permissions on class', async done => { + it("should be denied (writelock) when no other permissions on class", async done => { await updateCLP({ create: { - pointerFields: ['moderators'], + pointerFields: ["moderators"], }, }); @@ -2846,15 +2919,15 @@ describe('Pointer Permissions', () => { }); }); - describe('addField action', () => { - it('should have no effect on create (allowed by explicit userid)', async done => { + describe("addField action", () => { + it("should have no effect on create (allowed by explicit userid)", async done => { await updateCLP({ create: { - '*': true, + "*": true, }, addField: { [user1.id]: true, - pointerFields: ['moderators'], + pointerFields: ["moderators"], }, }); @@ -2864,13 +2937,13 @@ describe('Pointer Permissions', () => { done(); }); - it('should be denied when creating object (and no explicit permission)', async done => { + it("should be denied when creating object (and no explicit permission)", async done => { await updateCLP({ create: { - '*': true, + "*": true, }, addField: { - pointerFields: ['moderators'], + pointerFields: ["moderators"], }, }); @@ -2878,19 +2951,21 @@ describe('Pointer Permissions', () => { const newObject = new Parse.Object(className, { moderators: user1, - extra: 'field', + extra: "field", }); - await expectAsync(newObject.save()).toBeRejectedWith(PERMISSION_DENIED); + await expectAsync(newObject.save()).toBeRejectedWith( + PERMISSION_DENIED + ); done(); }); - it('should be allowed when updating object', async done => { + it("should be allowed when updating object", async done => { await updateCLP({ update: { - '*': true, + "*": true, }, addField: { - pointerFields: ['moderators'], + pointerFields: ["moderators"], }, }); @@ -2901,21 +2976,23 @@ describe('Pointer Permissions', () => { done(); }); - it_id('51e896e9-73b3-404f-b5ff-bdb99005a9f7')(it)( - 'should be restricted when updating object without addField permission', + it_id("51e896e9-73b3-404f-b5ff-bdb99005a9f7")(it)( + "should be restricted when updating object without addField permission", async done => { await updateCLP({ update: { - '*': true, + "*": true, }, addField: { - pointerFields: ['moderators'], + pointerFields: ["moderators"], }, }); await logIn(user1); - await expectAsync(actionAddFieldOnUpdate(obj2)).toBeRejectedWith(OBJECT_NOT_FOUND); + await expectAsync(actionAddFieldOnUpdate(obj2)).toBeRejectedWith( + OBJECT_NOT_FOUND + ); done(); } @@ -2923,7 +3000,7 @@ describe('Pointer Permissions', () => { }); }); - describe('combined with grouped', () => { + describe("combined with grouped", () => { /** * owns: **obj1** * @@ -2951,7 +3028,10 @@ describe('Pointer Permissions', () => { async function initialize() { await Config.get(Parse.applicationId).schemaCache.clear(); - [user1, user2] = await Promise.all([createUser('user1'), createUser('user2')]); + [user1, user2] = await Promise.all([ + createUser("user1"), + createUser("user2"), + ]); // User1 owns object1 // User2 owns object2 @@ -2976,14 +3056,14 @@ describe('Pointer Permissions', () => { await initialize(); }); - it_id('b43db366-8cce-4a11-9cf2-eeee9603d40b')(it)( - 'should not limit the scope of grouped read permissions', + it_id("b43db366-8cce-4a11-9cf2-eeee9603d40b")(it)( + "should not limit the scope of grouped read permissions", async done => { await updateCLP({ get: { - pointerFields: ['owner'], + pointerFields: ["owner"], }, - readUserFields: ['moderators'], + readUserFields: ["moderators"], }); await logIn(user2); @@ -3000,14 +3080,14 @@ describe('Pointer Permissions', () => { } ); - it_id('bbb1686d-0e2a-4365-8b64-b5faa3e7b9cf')(it)( - 'should not limit the scope of grouped write permissions', + it_id("bbb1686d-0e2a-4365-8b64-b5faa3e7b9cf")(it)( + "should not limit the scope of grouped write permissions", async done => { await updateCLP({ update: { - pointerFields: ['owner'], + pointerFields: ["owner"], }, - writeUserFields: ['moderators'], + writeUserFields: ["moderators"], }); await logIn(user2); @@ -3021,12 +3101,12 @@ describe('Pointer Permissions', () => { } ); - it('should not inherit scope of grouped read permissions from another field', async done => { + it("should not inherit scope of grouped read permissions from another field", async done => { await updateCLP({ get: { - pointerFields: ['owner'], + pointerFields: ["owner"], }, - readUserFields: ['moderators'], + readUserFields: ["moderators"], }); await logIn(user1); @@ -3040,23 +3120,25 @@ describe('Pointer Permissions', () => { done(); }); - it('should not inherit scope of grouped write permissions from another field', async done => { + it("should not inherit scope of grouped write permissions from another field", async done => { await updateCLP({ update: { - pointerFields: ['moderators'], + pointerFields: ["moderators"], }, - writeUserFields: ['owner'], + writeUserFields: ["owner"], }); await logIn(user1); - await expectAsync(actionDelete(obj2)).toBeRejectedWith(OBJECT_NOT_FOUND); + await expectAsync(actionDelete(obj2)).toBeRejectedWith( + OBJECT_NOT_FOUND + ); done(); }); }); - describe('using pointer-fields and queries with keys projection', () => { + describe("using pointer-fields and queries with keys projection", () => { let user1; /** * owner: user1 @@ -3071,14 +3153,14 @@ describe('Pointer Permissions', () => { async function initialize() { await Config.get(Parse.applicationId).schemaCache.clear(); - user1 = await createUser('user1'); + user1 = await createUser("user1"); user1 = await logIn(user1); obj = new Parse.Object(className); - obj.set('owner', user1); - obj.set('field', 'field'); - obj.set('test', 'test'); + obj.set("owner", user1); + obj.set("field", "field"); + obj.set("test", "test"); await Parse.Object.saveAll([obj], { useMasterKey: true }); @@ -3089,19 +3171,19 @@ describe('Pointer Permissions', () => { await initialize(); }); - it('should be enforced regardless of pointer-field being included in keys (select)', async done => { + it("should be enforced regardless of pointer-field being included in keys (select)", async done => { await updateCLP({ - get: { '*': true }, - find: { pointerFields: ['owner'] }, - update: { pointerFields: ['owner'] }, + get: { "*": true }, + find: { pointerFields: ["owner"] }, + update: { pointerFields: ["owner"] }, }); - const query = new Parse.Query('AnObject'); - query.select('field', 'test'); + const query = new Parse.Query("AnObject"); + query.select("field", "test"); const [object] = await query.find({ objectId: obj.id }); - expect(object.get('field')).toBe('field'); - expect(object.get('test')).toBe('test'); + expect(object.get("field")).toBe("field"); + expect(object.get("test")).toBe("test"); done(); }); }); diff --git a/spec/PostgresConfigParser.spec.js b/spec/PostgresConfigParser.spec.js index f4efc42114..d9fceb5a97 100644 --- a/spec/PostgresConfigParser.spec.js +++ b/spec/PostgresConfigParser.spec.js @@ -1,14 +1,14 @@ -const parser = require('../lib/Adapters/Storage/Postgres/PostgresConfigParser'); -const fs = require('fs'); +const parser = require("../lib/Adapters/Storage/Postgres/PostgresConfigParser"); +const fs = require("fs"); const queryParamTests = { - 'a=1&b=2': { a: '1', b: '2' }, - 'a=abcd%20efgh&b=abcd%3Defgh': { a: 'abcd efgh', b: 'abcd=efgh' }, - 'a=1&b&c=true': { a: '1', b: '', c: 'true' }, + "a=1&b=2": { a: "1", b: "2" }, + "a=abcd%20efgh&b=abcd%3Defgh": { a: "abcd efgh", b: "abcd=efgh" }, + "a=1&b&c=true": { a: "1", b: "", c: "true" }, }; -describe('PostgresConfigParser.parseQueryParams', () => { - it('creates a map from a query string', () => { +describe("PostgresConfigParser.parseQueryParams", () => { + it("creates a map from a query string", () => { for (const key in queryParamTests) { const result = parser.parseQueryParams(key); @@ -23,16 +23,16 @@ describe('PostgresConfigParser.parseQueryParams', () => { }); }); -const baseURI = 'postgres://username:password@localhost:5432/db-name'; -const testfile = fs.readFileSync('./Dockerfile').toString(); +const baseURI = "postgres://username:password@localhost:5432/db-name"; +const testfile = fs.readFileSync("./Dockerfile").toString(); const dbOptionsTest = {}; dbOptionsTest[ `${baseURI}?ssl=true&binary=true&application_name=app_name&fallback_application_name=f_app_name&poolSize=12` ] = { ssl: true, binary: true, - application_name: 'app_name', - fallback_application_name: 'f_app_name', + application_name: "app_name", + fallback_application_name: "f_app_name", max: 12, }; dbOptionsTest[`${baseURI}?ssl=&binary=aa`] = { @@ -46,7 +46,7 @@ dbOptionsTest[ pfx: testfile, cert: testfile, key: testfile, - passphrase: 'word', + passphrase: "word", secureOptions: 20, }, binary: false, @@ -60,15 +60,17 @@ dbOptionsTest[ dbOptionsTest[`${baseURI}?rejectUnauthorized=true`] = { ssl: { rejectUnauthorized: true }, }; -dbOptionsTest[`${baseURI}?max=5&query_timeout=100&idleTimeoutMillis=1000&keepAlive=true`] = { +dbOptionsTest[ + `${baseURI}?max=5&query_timeout=100&idleTimeoutMillis=1000&keepAlive=true` +] = { max: 5, query_timeout: 100, idleTimeoutMillis: 1000, keepAlive: true, }; -describe('PostgresConfigParser.getDatabaseOptionsFromURI', () => { - it('creates a db options map from a query string', () => { +describe("PostgresConfigParser.getDatabaseOptionsFromURI", () => { + it("creates a db options map from a query string", () => { for (const key in dbOptionsTest) { const result = parser.getDatabaseOptionsFromURI(key); @@ -80,21 +82,23 @@ describe('PostgresConfigParser.getDatabaseOptionsFromURI', () => { } }); - it('sets the poolSize to 10 if the it is not a number', () => { + it("sets the poolSize to 10 if the it is not a number", () => { const result = parser.getDatabaseOptionsFromURI(`${baseURI}?poolSize=sdf`); expect(result.max).toEqual(10); }); - it('sets the max to 10 if the it is not a number', () => { + it("sets the max to 10 if the it is not a number", () => { const result = parser.getDatabaseOptionsFromURI(`${baseURI}?&max=sdf`); expect(result.poolSize).toBeUndefined(); expect(result.max).toEqual(10); }); - it('max should take precedence over poolSize', () => { - const result = parser.getDatabaseOptionsFromURI(`${baseURI}?poolSize=20&max=12`); + it("max should take precedence over poolSize", () => { + const result = parser.getDatabaseOptionsFromURI( + `${baseURI}?poolSize=20&max=12` + ); expect(result.poolSize).toBeUndefined(); expect(result.max).toEqual(12); diff --git a/spec/PostgresInitOptions.spec.js b/spec/PostgresInitOptions.spec.js index 2af94e9fc3..f0bf554bcb 100644 --- a/spec/PostgresInitOptions.spec.js +++ b/spec/PostgresInitOptions.spec.js @@ -1,33 +1,33 @@ -const Parse = require('parse/node').Parse; +const Parse = require("parse/node").Parse; const PostgresStorageAdapter = - require('../lib/Adapters/Storage/Postgres/PostgresStorageAdapter').default; + require("../lib/Adapters/Storage/Postgres/PostgresStorageAdapter").default; const postgresURI = process.env.PARSE_SERVER_TEST_DATABASE_URI || - 'postgres://localhost:5432/parse_server_postgres_adapter_test_database'; + "postgres://localhost:5432/parse_server_postgres_adapter_test_database"; //public schema const databaseOptions1 = { initOptions: { - schema: 'public', + schema: "public", }, }; //not exists schema const databaseOptions2 = { initOptions: { - schema: 'not_exists_schema', + schema: "not_exists_schema", }, }; const GameScore = Parse.Object.extend({ - className: 'GameScore', + className: "GameScore", }); -describe_only_db('postgres')('Postgres database init options', () => { - it('should create server with public schema databaseOptions', async () => { +describe_only_db("postgres")("Postgres database init options", () => { + it("should create server with public schema databaseOptions", async () => { const adapter = new PostgresStorageAdapter({ uri: postgresURI, - collectionPrefix: 'test_', + collectionPrefix: "test_", databaseOptions: databaseOptions1, }); await reconfigureServer({ @@ -35,18 +35,18 @@ describe_only_db('postgres')('Postgres database init options', () => { }); const score = new GameScore({ score: 1337, - playerName: 'Sean Plott', + playerName: "Sean Plott", cheatMode: false, }); await score.save(); }); - it('should create server using postgresql uri with public schema databaseOptions', async () => { + it("should create server using postgresql uri with public schema databaseOptions", async () => { const postgresURI2 = new URL(postgresURI); - postgresURI2.protocol = 'postgresql:'; + postgresURI2.protocol = "postgresql:"; const adapter = new PostgresStorageAdapter({ uri: postgresURI2.toString(), - collectionPrefix: 'test_', + collectionPrefix: "test_", databaseOptions: databaseOptions1, }); await reconfigureServer({ @@ -54,23 +54,23 @@ describe_only_db('postgres')('Postgres database init options', () => { }); const score = new GameScore({ score: 1337, - playerName: 'Sean Plott', + playerName: "Sean Plott", cheatMode: false, }); await score.save(); }); - it('should fail to create server if schema databaseOptions does not exist', async () => { + it("should fail to create server if schema databaseOptions does not exist", async () => { const adapter = new PostgresStorageAdapter({ uri: postgresURI, - collectionPrefix: 'test_', + collectionPrefix: "test_", databaseOptions: databaseOptions2, }); try { await reconfigureServer({ databaseAdapter: adapter, }); - fail('Should have thrown error'); + fail("Should have thrown error"); } catch (error) { expect(error).toBeDefined(); } diff --git a/spec/PostgresStorageAdapter.spec.js b/spec/PostgresStorageAdapter.spec.js index 88a07c87d8..431865c824 100644 --- a/spec/PostgresStorageAdapter.spec.js +++ b/spec/PostgresStorageAdapter.spec.js @@ -1,41 +1,41 @@ const PostgresStorageAdapter = - require('../lib/Adapters/Storage/Postgres/PostgresStorageAdapter').default; + require("../lib/Adapters/Storage/Postgres/PostgresStorageAdapter").default; const databaseURI = process.env.PARSE_SERVER_TEST_DATABASE_URI || - 'postgres://localhost:5432/parse_server_postgres_adapter_test_database'; -const Config = require('../lib/Config'); + "postgres://localhost:5432/parse_server_postgres_adapter_test_database"; +const Config = require("../lib/Config"); const getColumns = (client, className) => { return client.map( - 'SELECT column_name FROM information_schema.columns WHERE table_name = $', + "SELECT column_name FROM information_schema.columns WHERE table_name = $", { className }, a => a.column_name ); }; const dropTable = (client, className) => { - return client.none('DROP TABLE IF EXISTS $', { className }); + return client.none("DROP TABLE IF EXISTS $", { className }); }; -describe_only_db('postgres')('PostgresStorageAdapter', () => { +describe_only_db("postgres")("PostgresStorageAdapter", () => { let adapter; beforeEach(async () => { - const config = Config.get('test'); + const config = Config.get("test"); adapter = config.database.adapter; }); - it('schemaUpgrade, upgrade the database schema when schema changes', async done => { + it("schemaUpgrade, upgrade the database schema when schema changes", async done => { await adapter.deleteAllClasses(); - const config = Config.get('test'); + const config = Config.get("test"); config.schemaCache.clear(); await adapter.performInitialization({ VolatileClassesSchemas: [] }); const client = adapter._client; - const className = '_PushStatus'; + const className = "_PushStatus"; const schema = { fields: { - pushTime: { type: 'String' }, - source: { type: 'String' }, - query: { type: 'String' }, + pushTime: { type: "String" }, + source: { type: "String" }, + query: { type: "String" }, }, }; @@ -43,34 +43,34 @@ describe_only_db('postgres')('PostgresStorageAdapter', () => { .createTable(className, schema) .then(() => getColumns(client, className)) .then(columns => { - expect(columns).toContain('pushTime'); - expect(columns).toContain('source'); - expect(columns).toContain('query'); - expect(columns).not.toContain('expiration_interval'); + expect(columns).toContain("pushTime"); + expect(columns).toContain("source"); + expect(columns).toContain("query"); + expect(columns).not.toContain("expiration_interval"); - schema.fields.expiration_interval = { type: 'Number' }; + schema.fields.expiration_interval = { type: "Number" }; return adapter.schemaUpgrade(className, schema); }) .then(() => getColumns(client, className)) .then(async columns => { - expect(columns).toContain('pushTime'); - expect(columns).toContain('source'); - expect(columns).toContain('query'); - expect(columns).toContain('expiration_interval'); + expect(columns).toContain("pushTime"); + expect(columns).toContain("source"); + expect(columns).toContain("query"); + expect(columns).toContain("expiration_interval"); await reconfigureServer(); done(); }) .catch(error => done.fail(error)); }); - it('schemaUpgrade, maintain correct schema', done => { + it("schemaUpgrade, maintain correct schema", done => { const client = adapter._client; - const className = 'Table'; + const className = "Table"; const schema = { fields: { - columnA: { type: 'String' }, - columnB: { type: 'String' }, - columnC: { type: 'String' }, + columnA: { type: "String" }, + columnB: { type: "String" }, + columnC: { type: "String" }, }, }; @@ -78,27 +78,27 @@ describe_only_db('postgres')('PostgresStorageAdapter', () => { .createTable(className, schema) .then(() => getColumns(client, className)) .then(columns => { - expect(columns).toContain('columnA'); - expect(columns).toContain('columnB'); - expect(columns).toContain('columnC'); + expect(columns).toContain("columnA"); + expect(columns).toContain("columnB"); + expect(columns).toContain("columnC"); return adapter.schemaUpgrade(className, schema); }) .then(() => getColumns(client, className)) .then(columns => { expect(columns.length).toEqual(3); - expect(columns).toContain('columnA'); - expect(columns).toContain('columnB'); - expect(columns).toContain('columnC'); + expect(columns).toContain("columnA"); + expect(columns).toContain("columnB"); + expect(columns).toContain("columnC"); done(); }) .catch(error => done.fail(error)); }); - it('Create a table without columns and upgrade with columns', done => { + it("Create a table without columns and upgrade with columns", done => { const client = adapter._client; - const className = 'EmptyTable'; + const className = "EmptyTable"; dropTable(client, className) .then(() => adapter.createTable(className, {})) .then(() => getColumns(client, className)) @@ -107,8 +107,8 @@ describe_only_db('postgres')('PostgresStorageAdapter', () => { const newSchema = { fields: { - columnA: { type: 'String' }, - columnB: { type: 'String' }, + columnA: { type: "String" }, + columnB: { type: "String" }, }, }; @@ -117,60 +117,59 @@ describe_only_db('postgres')('PostgresStorageAdapter', () => { .then(() => getColumns(client, className)) .then(columns => { expect(columns.length).toEqual(2); - expect(columns).toContain('columnA'); - expect(columns).toContain('columnB'); + expect(columns).toContain("columnA"); + expect(columns).toContain("columnB"); done(); }) .catch(done); }); - it('getClass if exists', async () => { + it("getClass if exists", async () => { const schema = { fields: { - array: { type: 'Array' }, - object: { type: 'Object' }, - date: { type: 'Date' }, + array: { type: "Array" }, + object: { type: "Object" }, + date: { type: "Date" }, }, }; - await adapter.createClass('MyClass', schema); - const myClassSchema = await adapter.getClass('MyClass'); + await adapter.createClass("MyClass", schema); + const myClassSchema = await adapter.getClass("MyClass"); expect(myClassSchema).toBeDefined(); }); - it('getClass if not exists', async () => { + it("getClass if not exists", async () => { const schema = { fields: { - array: { type: 'Array' }, - object: { type: 'Object' }, - date: { type: 'Date' }, + array: { type: "Array" }, + object: { type: "Object" }, + date: { type: "Date" }, }, }; - await adapter.createClass('MyClass', schema); - await expectAsync(adapter.getClass('UnknownClass')).toBeRejectedWith(undefined); + await adapter.createClass("MyClass", schema); + await expectAsync(adapter.getClass("UnknownClass")).toBeRejectedWith( + undefined + ); }); - it('$relativeTime should error on $eq', async () => { - const tableName = '_User'; + it("$relativeTime should error on $eq", async () => { + const tableName = "_User"; const schema = { fields: { - objectId: { type: 'String' }, - username: { type: 'String' }, - email: { type: 'String' }, - emailVerified: { type: 'Boolean' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - authData: { type: 'Object' }, + objectId: { type: "String" }, + username: { type: "String" }, + email: { type: "String" }, + emailVerified: { type: "Boolean" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + authData: { type: "Object" }, }, }; const client = adapter._client; await adapter.createTable(tableName, schema); - await client.none('INSERT INTO $1:name ($2:name, $3:name) VALUES ($4, $5)', [ - tableName, - 'objectId', - 'username', - 'Bugs', - 'Bunny', - ]); + await client.none( + "INSERT INTO $1:name ($2:name, $3:name) VALUES ($4, $5)", + [tableName, "objectId", "username", "Bugs", "Bunny"] + ); const database = Config.get(Parse.applicationId).database; await database.loadSchema({ clearCache: true }); try { @@ -179,41 +178,38 @@ describe_only_db('postgres')('PostgresStorageAdapter', () => { { createdAt: { $eq: { - $relativeTime: '12 days ago', + $relativeTime: "12 days ago", }, }, }, {} ); - fail('Should have thrown error'); + fail("Should have thrown error"); } catch (error) { expect(error.code).toBe(Parse.Error.INVALID_JSON); } await dropTable(client, tableName); }); - it('$relativeTime should error on $ne', async () => { - const tableName = '_User'; + it("$relativeTime should error on $ne", async () => { + const tableName = "_User"; const schema = { fields: { - objectId: { type: 'String' }, - username: { type: 'String' }, - email: { type: 'String' }, - emailVerified: { type: 'Boolean' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - authData: { type: 'Object' }, + objectId: { type: "String" }, + username: { type: "String" }, + email: { type: "String" }, + emailVerified: { type: "Boolean" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + authData: { type: "Object" }, }, }; const client = adapter._client; await adapter.createTable(tableName, schema); - await client.none('INSERT INTO $1:name ($2:name, $3:name) VALUES ($4, $5)', [ - tableName, - 'objectId', - 'username', - 'Bugs', - 'Bunny', - ]); + await client.none( + "INSERT INTO $1:name ($2:name, $3:name) VALUES ($4, $5)", + [tableName, "objectId", "username", "Bugs", "Bunny"] + ); const database = Config.get(Parse.applicationId).database; await database.loadSchema({ clearCache: true }); try { @@ -222,41 +218,38 @@ describe_only_db('postgres')('PostgresStorageAdapter', () => { { createdAt: { $ne: { - $relativeTime: '12 days ago', + $relativeTime: "12 days ago", }, }, }, {} ); - fail('Should have thrown error'); + fail("Should have thrown error"); } catch (error) { expect(error.code).toBe(Parse.Error.INVALID_JSON); } await dropTable(client, tableName); }); - it('$relativeTime should error on $exists', async () => { - const tableName = '_User'; + it("$relativeTime should error on $exists", async () => { + const tableName = "_User"; const schema = { fields: { - objectId: { type: 'String' }, - username: { type: 'String' }, - email: { type: 'String' }, - emailVerified: { type: 'Boolean' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - authData: { type: 'Object' }, + objectId: { type: "String" }, + username: { type: "String" }, + email: { type: "String" }, + emailVerified: { type: "Boolean" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + authData: { type: "Object" }, }, }; const client = adapter._client; await adapter.createTable(tableName, schema); - await client.none('INSERT INTO $1:name ($2:name, $3:name) VALUES ($4, $5)', [ - tableName, - 'objectId', - 'username', - 'Bugs', - 'Bunny', - ]); + await client.none( + "INSERT INTO $1:name ($2:name, $3:name) VALUES ($4, $5)", + [tableName, "objectId", "username", "Bugs", "Bunny"] + ); const database = Config.get(Parse.applicationId).database; await database.loadSchema({ clearCache: true }); try { @@ -265,126 +258,127 @@ describe_only_db('postgres')('PostgresStorageAdapter', () => { { createdAt: { $exists: { - $relativeTime: '12 days ago', + $relativeTime: "12 days ago", }, }, }, {} ); - fail('Should have thrown error'); + fail("Should have thrown error"); } catch (error) { expect(error.code).toBe(Parse.Error.INVALID_JSON); } await dropTable(client, tableName); }); - it('should use index for caseInsensitive query using Postgres', async () => { - const tableName = '_User'; + it("should use index for caseInsensitive query using Postgres", async () => { + const tableName = "_User"; const schema = { fields: { - objectId: { type: 'String' }, - username: { type: 'String' }, - email: { type: 'String' }, - emailVerified: { type: 'Boolean' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - authData: { type: 'Object' }, + objectId: { type: "String" }, + username: { type: "String" }, + email: { type: "String" }, + emailVerified: { type: "Boolean" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + authData: { type: "Object" }, }, }; const client = adapter._client; await adapter.createTable(tableName, schema); - await client.none('INSERT INTO $1:name ($2:name, $3:name) VALUES ($4, $5)', [ - tableName, - 'objectId', - 'username', - 'Bugs', - 'Bunny', - ]); + await client.none( + "INSERT INTO $1:name ($2:name, $3:name) VALUES ($4, $5)", + [tableName, "objectId", "username", "Bugs", "Bunny"] + ); //Postgres won't take advantage of the index until it has a lot of records because sequential is faster for small db's await client.none( - 'INSERT INTO $1:name ($2:name, $3:name) SELECT gen_random_uuid(), gen_random_uuid() FROM generate_series(1,5000)', - [tableName, 'objectId', 'username'] + "INSERT INTO $1:name ($2:name, $3:name) SELECT gen_random_uuid(), gen_random_uuid() FROM generate_series(1,5000)", + [tableName, "objectId", "username"] + ); + const caseInsensitiveData = "bugs"; + const originalQuery = + "SELECT * FROM $1:name WHERE lower($2:name)=lower($3)"; + const analyzedExplainQuery = adapter.createExplainableQuery( + originalQuery, + true ); - const caseInsensitiveData = 'bugs'; - const originalQuery = 'SELECT * FROM $1:name WHERE lower($2:name)=lower($3)'; - const analyzedExplainQuery = adapter.createExplainableQuery(originalQuery, true); const preIndexPlan = await client.one(analyzedExplainQuery, [ tableName, - 'objectId', + "objectId", caseInsensitiveData, ]); - preIndexPlan['QUERY PLAN'].forEach(element => { + preIndexPlan["QUERY PLAN"].forEach(element => { //Make sure search returned with only 1 result - expect(element.Plan['Actual Rows']).toBe(1); - expect(element.Plan['Node Type']).toBe('Seq Scan'); + expect(element.Plan["Actual Rows"]).toBe(1); + expect(element.Plan["Node Type"]).toBe("Seq Scan"); }); - const indexName = 'test_case_insensitive_column'; - await adapter.ensureIndex(tableName, schema, ['objectId'], indexName, true); + const indexName = "test_case_insensitive_column"; + await adapter.ensureIndex(tableName, schema, ["objectId"], indexName, true); const postIndexPlan = await client.one(analyzedExplainQuery, [ tableName, - 'objectId', + "objectId", caseInsensitiveData, ]); - postIndexPlan['QUERY PLAN'].forEach(element => { + postIndexPlan["QUERY PLAN"].forEach(element => { //Make sure search returned with only 1 result - expect(element.Plan['Actual Rows']).toBe(1); + expect(element.Plan["Actual Rows"]).toBe(1); //Should not be a sequential scan - expect(element.Plan['Node Type']).not.toContain('Seq Scan'); + expect(element.Plan["Node Type"]).not.toContain("Seq Scan"); //Should be using the index created for this element.Plan.Plans.forEach(innerElement => { - expect(innerElement['Index Name']).toBe(indexName); + expect(innerElement["Index Name"]).toBe(indexName); }); }); //These are the same query so should be the same size - for (let i = 0; i < preIndexPlan['QUERY PLAN'].length; i++) { + for (let i = 0; i < preIndexPlan["QUERY PLAN"].length; i++) { //Sequential should take more time to execute than indexed - expect(preIndexPlan['QUERY PLAN'][i]['Execution Time']).toBeGreaterThan( - postIndexPlan['QUERY PLAN'][i]['Execution Time'] + expect(preIndexPlan["QUERY PLAN"][i]["Execution Time"]).toBeGreaterThan( + postIndexPlan["QUERY PLAN"][i]["Execution Time"] ); } //Test explaining without analyzing const basicExplainQuery = adapter.createExplainableQuery(originalQuery); const explained = await client.one(basicExplainQuery, [ tableName, - 'objectId', + "objectId", caseInsensitiveData, ]); - explained['QUERY PLAN'].forEach(element => { + explained["QUERY PLAN"].forEach(element => { //Check that basic query plans isn't a sequential scan - expect(element.Plan['Node Type']).not.toContain('Seq Scan'); + expect(element.Plan["Node Type"]).not.toContain("Seq Scan"); //Basic query plans shouldn't have an execution time - expect(element['Execution Time']).toBeUndefined(); + expect(element["Execution Time"]).toBeUndefined(); }); await dropTable(client, tableName); }); - it('should use index for caseInsensitive query with user', async () => { + it("should use index for caseInsensitive query with user", async () => { await adapter.deleteAllClasses(); - const config = Config.get('test'); + const config = Config.get("test"); config.schemaCache.clear(); await adapter.performInitialization({ VolatileClassesSchemas: [] }); const database = Config.get(Parse.applicationId).database; await database.loadSchema({ clearCache: true }); - const tableName = '_User'; + const tableName = "_User"; const user = new Parse.User(); - user.set('username', 'Elmer'); - user.set('password', 'Fudd'); + user.set("username", "Elmer"); + user.set("password", "Fudd"); await user.signUp(); //Postgres won't take advantage of the index until it has a lot of records because sequential is faster for small db's const client = adapter._client; await client.none( - 'INSERT INTO $1:name ($2:name, $3:name) SELECT gen_random_uuid(), gen_random_uuid() FROM generate_series(1,5000)', - [tableName, 'objectId', 'username'] + "INSERT INTO $1:name ($2:name, $3:name) SELECT gen_random_uuid(), gen_random_uuid() FROM generate_series(1,5000)", + [tableName, "objectId", "username"] ); - const caseInsensitiveData = 'elmer'; - const fieldToSearch = 'username'; + const caseInsensitiveData = "elmer"; + const fieldToSearch = "username"; //Check using find method for Parse const preIndexPlan = await database.find( tableName, @@ -393,17 +387,23 @@ describe_only_db('postgres')('PostgresStorageAdapter', () => { ); preIndexPlan.forEach(element => { - element['QUERY PLAN'].forEach(innerElement => { + element["QUERY PLAN"].forEach(innerElement => { //Check that basic query plans isn't a sequential scan, be careful as find uses "any" to query - expect(innerElement.Plan['Node Type']).toBe('Seq Scan'); + expect(innerElement.Plan["Node Type"]).toBe("Seq Scan"); //Basic query plans shouldn't have an execution time - expect(innerElement['Execution Time']).toBeUndefined(); + expect(innerElement["Execution Time"]).toBeUndefined(); }); }); - const indexName = 'test_case_insensitive_column'; - const schema = await new Parse.Schema('_User').get(); - await adapter.ensureIndex(tableName, schema, [fieldToSearch], indexName, true); + const indexName = "test_case_insensitive_column"; + const schema = await new Parse.Schema("_User").get(); + await adapter.ensureIndex( + tableName, + schema, + [fieldToSearch], + indexName, + true + ); //Check using find method for Parse const postIndexPlan = await database.find( @@ -413,43 +413,43 @@ describe_only_db('postgres')('PostgresStorageAdapter', () => { ); postIndexPlan.forEach(element => { - element['QUERY PLAN'].forEach(innerElement => { + element["QUERY PLAN"].forEach(innerElement => { //Check that basic query plans isn't a sequential scan - expect(innerElement.Plan['Node Type']).not.toContain('Seq Scan'); + expect(innerElement.Plan["Node Type"]).not.toContain("Seq Scan"); //Basic query plans shouldn't have an execution time - expect(innerElement['Execution Time']).toBeUndefined(); + expect(innerElement["Execution Time"]).toBeUndefined(); }); }); }); - it('should use index for caseInsensitive query using default indexname', async () => { + it("should use index for caseInsensitive query using default indexname", async () => { await adapter.deleteAllClasses(); - const config = Config.get('test'); + const config = Config.get("test"); config.schemaCache.clear(); await adapter.performInitialization({ VolatileClassesSchemas: [] }); const database = Config.get(Parse.applicationId).database; await database.loadSchema({ clearCache: true }); - const tableName = '_User'; + const tableName = "_User"; const user = new Parse.User(); - user.set('username', 'Tweety'); - user.set('password', 'Bird'); + user.set("username", "Tweety"); + user.set("password", "Bird"); await user.signUp(); - const fieldToSearch = 'username'; + const fieldToSearch = "username"; //Create index before data is inserted - const schema = await new Parse.Schema('_User').get(); + const schema = await new Parse.Schema("_User").get(); await adapter.ensureIndex(tableName, schema, [fieldToSearch], null, true); //Postgres won't take advantage of the index until it has a lot of records because sequential is faster for small db's const client = adapter._client; await client.none( - 'INSERT INTO $1:name ($2:name, $3:name) SELECT gen_random_uuid(), gen_random_uuid() FROM generate_series(1,5000)', - [tableName, 'objectId', 'username'] + "INSERT INTO $1:name ($2:name, $3:name) SELECT gen_random_uuid(), gen_random_uuid() FROM generate_series(1,5000)", + [tableName, "objectId", "username"] ); - const caseInsensitiveData = 'tweeTy'; + const caseInsensitiveData = "tweeTy"; //Check using find method for Parse const indexPlan = await database.find( tableName, @@ -457,22 +457,22 @@ describe_only_db('postgres')('PostgresStorageAdapter', () => { { caseInsensitive: true, explain: true } ); indexPlan.forEach(element => { - element['QUERY PLAN'].forEach(innerElement => { - expect(innerElement.Plan['Node Type']).not.toContain('Seq Scan'); - expect(innerElement.Plan['Index Name']).toContain('parse_default'); + element["QUERY PLAN"].forEach(innerElement => { + expect(innerElement.Plan["Node Type"]).not.toContain("Seq Scan"); + expect(innerElement.Plan["Index Name"]).toContain("parse_default"); }); }); }); - it('should allow multiple unique indexes for same field name and different class', async () => { - const firstTableName = 'Test1'; + it("should allow multiple unique indexes for same field name and different class", async () => { + const firstTableName = "Test1"; const firstTableSchema = new Parse.Schema(firstTableName); - const uniqueField = 'uuid'; + const uniqueField = "uuid"; firstTableSchema.addString(uniqueField); await firstTableSchema.save(); await firstTableSchema.get(); - const secondTableName = 'Test2'; + const secondTableName = "Test2"; const secondTableSchema = new Parse.Schema(secondTableName); secondTableSchema.addString(uniqueField); await secondTableSchema.save(); @@ -481,51 +481,55 @@ describe_only_db('postgres')('PostgresStorageAdapter', () => { const database = Config.get(Parse.applicationId).database; //Create index before data is inserted - await adapter.ensureUniqueness(firstTableName, firstTableSchema, [uniqueField]); - await adapter.ensureUniqueness(secondTableName, secondTableSchema, [uniqueField]); + await adapter.ensureUniqueness(firstTableName, firstTableSchema, [ + uniqueField, + ]); + await adapter.ensureUniqueness(secondTableName, secondTableSchema, [ + uniqueField, + ]); //Postgres won't take advantage of the index until it has a lot of records because sequential is faster for small db's const client = adapter._client; await client.none( - 'INSERT INTO $1:name ($2:name, $3:name) SELECT gen_random_uuid(), gen_random_uuid() FROM generate_series(1,5000)', - [firstTableName, 'objectId', uniqueField] + "INSERT INTO $1:name ($2:name, $3:name) SELECT gen_random_uuid(), gen_random_uuid() FROM generate_series(1,5000)", + [firstTableName, "objectId", uniqueField] ); await client.none( - 'INSERT INTO $1:name ($2:name, $3:name) SELECT gen_random_uuid(), gen_random_uuid() FROM generate_series(1,5000)', - [secondTableName, 'objectId', uniqueField] + "INSERT INTO $1:name ($2:name, $3:name) SELECT gen_random_uuid(), gen_random_uuid() FROM generate_series(1,5000)", + [secondTableName, "objectId", uniqueField] ); //Check using find method for Parse const indexPlan = await database.find( firstTableName, - { uuid: '1234' }, + { uuid: "1234" }, { caseInsensitive: false, explain: true } ); indexPlan.forEach(element => { - element['QUERY PLAN'].forEach(innerElement => { - expect(innerElement.Plan['Node Type']).not.toContain('Seq Scan'); - expect(innerElement.Plan['Index Name']).toContain(uniqueField); + element["QUERY PLAN"].forEach(innerElement => { + expect(innerElement.Plan["Node Type"]).not.toContain("Seq Scan"); + expect(innerElement.Plan["Index Name"]).toContain(uniqueField); }); }); const indexPlan2 = await database.find( secondTableName, - { uuid: '1234' }, + { uuid: "1234" }, { caseInsensitive: false, explain: true } ); indexPlan2.forEach(element => { - element['QUERY PLAN'].forEach(innerElement => { - expect(innerElement.Plan['Node Type']).not.toContain('Seq Scan'); - expect(innerElement.Plan['Index Name']).toContain(uniqueField); + element["QUERY PLAN"].forEach(innerElement => { + expect(innerElement.Plan["Node Type"]).not.toContain("Seq Scan"); + expect(innerElement.Plan["Index Name"]).toContain(uniqueField); }); }); }); - it('should watch _SCHEMA changes', async () => { + it("should watch _SCHEMA changes", async () => { const enableSchemaHooks = true; await reconfigureServer({ databaseAdapter: undefined, databaseURI, - collectionPrefix: '', + collectionPrefix: "", databaseOptions: { enableSchemaHooks, }, @@ -533,25 +537,25 @@ describe_only_db('postgres')('PostgresStorageAdapter', () => { const { database } = Config.get(Parse.applicationId); const { adapter } = database; expect(adapter.enableSchemaHooks).toBe(enableSchemaHooks); - spyOn(adapter, '_onchange'); + spyOn(adapter, "_onchange"); enableSchemaHooks; const otherInstance = new PostgresStorageAdapter({ uri: databaseURI, - collectionPrefix: '', + collectionPrefix: "", databaseOptions: { enableSchemaHooks }, }); expect(otherInstance.enableSchemaHooks).toBe(enableSchemaHooks); otherInstance._listenToSchema(); - await otherInstance.createClass('Stuff', { - className: 'Stuff', + await otherInstance.createClass("Stuff", { + className: "Stuff", fields: { - objectId: { type: 'String' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - _rperm: { type: 'Array' }, - _wperm: { type: 'Array' }, + objectId: { type: "String" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + _rperm: { type: "Array" }, + _wperm: { type: "Array" }, }, classLevelPermissions: undefined, }); @@ -559,31 +563,35 @@ describe_only_db('postgres')('PostgresStorageAdapter', () => { expect(adapter._onchange).toHaveBeenCalled(); }); - it('Idempotency class should have function', async () => { + it("Idempotency class should have function", async () => { await reconfigureServer(); - const adapter = Config.get('test').database.adapter; + const adapter = Config.get("test").database.adapter; const client = adapter._client; const qs = "SELECT format('%I.%I(%s)', ns.nspname, p.proname, oidvectortypes(p.proargtypes)) FROM pg_proc p INNER JOIN pg_namespace ns ON (p.pronamespace = ns.oid) WHERE p.proname = 'idempotency_delete_expired_records'"; const foundFunction = await client.one(qs); - expect(foundFunction.format).toBe('public.idempotency_delete_expired_records()'); + expect(foundFunction.format).toBe( + "public.idempotency_delete_expired_records()" + ); await adapter.deleteIdempotencyFunction(); await client.none(qs); }); }); -describe_only_db('postgres')('PostgresStorageAdapter shutdown', () => { - it('handleShutdown, close connection', () => { +describe_only_db("postgres")("PostgresStorageAdapter shutdown", () => { + it("handleShutdown, close connection", () => { const adapter = new PostgresStorageAdapter({ uri: databaseURI }); expect(adapter._client.$pool.ending).toEqual(false); adapter.handleShutdown(); expect(adapter._client.$pool.ending).toEqual(true); }); - it('handleShutdown, close connection of postgresql uri', () => { + it("handleShutdown, close connection of postgresql uri", () => { const databaseURI2 = new URL(databaseURI); - databaseURI2.protocol = 'postgresql:'; - const adapter = new PostgresStorageAdapter({ uri: databaseURI2.toString() }); + databaseURI2.protocol = "postgresql:"; + const adapter = new PostgresStorageAdapter({ + uri: databaseURI2.toString(), + }); expect(adapter._client.$pool.ending).toEqual(false); adapter.handleShutdown(); expect(adapter._client.$pool.ending).toEqual(true); diff --git a/spec/PromiseRouter.spec.js b/spec/PromiseRouter.spec.js index 51a4ce21a1..b2d7e8c399 100644 --- a/spec/PromiseRouter.spec.js +++ b/spec/PromiseRouter.spec.js @@ -1,30 +1,30 @@ -const PromiseRouter = require('../lib/PromiseRouter').default; +const PromiseRouter = require("../lib/PromiseRouter").default; -describe('PromiseRouter', () => { - it('should properly handle rejects', done => { +describe("PromiseRouter", () => { + it("should properly handle rejects", done => { const router = new PromiseRouter(); router.route( - 'GET', - '/dummy', + "GET", + "/dummy", () => { return Promise.reject({ - error: 'an error', + error: "an error", code: -1, }); }, () => { - fail('this should not be called'); + fail("this should not be called"); } ); router.routes[0].handler({}).then( result => { jfail(result); - fail('this should not be called'); + fail("this should not be called"); done(); }, error => { - expect(error.error).toEqual('an error'); + expect(error.error).toEqual("an error"); expect(error.code).toEqual(-1); done(); } diff --git a/spec/ProtectedFields.spec.js b/spec/ProtectedFields.spec.js index 8195985dcb..db5e8218ef 100644 --- a/spec/ProtectedFields.spec.js +++ b/spec/ProtectedFields.spec.js @@ -1,766 +1,784 @@ -const Config = require('../lib/Config'); -const Parse = require('parse/node'); -const request = require('../lib/request'); -const { className, createRole, createUser, logIn, updateCLP } = require('./support/dev'); - -describe('ProtectedFields', function () { - it('should handle and empty protectedFields', async function () { +const Config = require("../lib/Config"); +const Parse = require("parse/node"); +const request = require("../lib/request"); +const { + className, + createRole, + createUser, + logIn, + updateCLP, +} = require("./support/dev"); + +describe("ProtectedFields", function () { + it("should handle and empty protectedFields", async function () { const protectedFields = {}; await reconfigureServer({ protectedFields }); const user = new Parse.User(); - user.setUsername('Alice'); - user.setPassword('sekrit'); - user.set('email', 'alice@aol.com'); - user.set('favoriteColor', 'yellow'); + user.setUsername("Alice"); + user.setPassword("sekrit"); + user.set("email", "alice@aol.com"); + user.set("favoriteColor", "yellow"); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); user.setACL(acl); await user.save(); const fetched = await new Parse.Query(Parse.User).get(user.id); - expect(fetched.has('email')).toBeFalsy(); - expect(fetched.has('favoriteColor')).toBeTruthy(); + expect(fetched.has("email")).toBeFalsy(); + expect(fetched.has("favoriteColor")).toBeTruthy(); }); - describe('interaction with legacy userSensitiveFields', function () { - it('should fall back on sensitive fields if protected fields are not configured', async function () { - const userSensitiveFields = ['phoneNumber', 'timeZone']; + describe("interaction with legacy userSensitiveFields", function () { + it("should fall back on sensitive fields if protected fields are not configured", async function () { + const userSensitiveFields = ["phoneNumber", "timeZone"]; - const protectedFields = { _User: { '*': ['email'] } }; + const protectedFields = { _User: { "*": ["email"] } }; await reconfigureServer({ userSensitiveFields, protectedFields }); const user = new Parse.User(); - user.setUsername('Alice'); - user.setPassword('sekrit'); - user.set('email', 'alice@aol.com'); - user.set('phoneNumber', 8675309); - user.set('timeZone', 'America/Los_Angeles'); - user.set('favoriteColor', 'yellow'); - user.set('favoriteFood', 'pizza'); + user.setUsername("Alice"); + user.setPassword("sekrit"); + user.set("email", "alice@aol.com"); + user.set("phoneNumber", 8675309); + user.set("timeZone", "America/Los_Angeles"); + user.set("favoriteColor", "yellow"); + user.set("favoriteFood", "pizza"); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); user.setACL(acl); await user.save(); const fetched = await new Parse.Query(Parse.User).get(user.id); - expect(fetched.has('email')).toBeFalsy(); - expect(fetched.has('phoneNumber')).toBeFalsy(); - expect(fetched.has('favoriteColor')).toBeTruthy(); + expect(fetched.has("email")).toBeFalsy(); + expect(fetched.has("phoneNumber")).toBeFalsy(); + expect(fetched.has("favoriteColor")).toBeTruthy(); }); - it('should merge protected and sensitive for extra safety', async function () { - const userSensitiveFields = ['phoneNumber', 'timeZone']; + it("should merge protected and sensitive for extra safety", async function () { + const userSensitiveFields = ["phoneNumber", "timeZone"]; - const protectedFields = { _User: { '*': ['email', 'favoriteFood'] } }; + const protectedFields = { _User: { "*": ["email", "favoriteFood"] } }; await reconfigureServer({ userSensitiveFields, protectedFields }); const user = new Parse.User(); - user.setUsername('Alice'); - user.setPassword('sekrit'); - user.set('email', 'alice@aol.com'); - user.set('phoneNumber', 8675309); - user.set('timeZone', 'America/Los_Angeles'); - user.set('favoriteColor', 'yellow'); - user.set('favoriteFood', 'pizza'); + user.setUsername("Alice"); + user.setPassword("sekrit"); + user.set("email", "alice@aol.com"); + user.set("phoneNumber", 8675309); + user.set("timeZone", "America/Los_Angeles"); + user.set("favoriteColor", "yellow"); + user.set("favoriteFood", "pizza"); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); user.setACL(acl); await user.save(); const fetched = await new Parse.Query(Parse.User).get(user.id); - expect(fetched.has('email')).toBeFalsy(); - expect(fetched.has('phoneNumber')).toBeFalsy(); - expect(fetched.has('favoriteFood')).toBeFalsy(); - expect(fetched.has('favoriteColor')).toBeTruthy(); + expect(fetched.has("email")).toBeFalsy(); + expect(fetched.has("phoneNumber")).toBeFalsy(); + expect(fetched.has("favoriteFood")).toBeFalsy(); + expect(fetched.has("favoriteColor")).toBeTruthy(); }); }); - describe('non user class', function () { - it('should hide fields in a non user class', async function () { + describe("non user class", function () { + it("should hide fields in a non user class", async function () { const protectedFields = { - ClassA: { '*': ['foo'] }, - ClassB: { '*': ['bar'] }, + ClassA: { "*": ["foo"] }, + ClassB: { "*": ["bar"] }, }; await reconfigureServer({ protectedFields }); - const objA = await new Parse.Object('ClassA').set('foo', 'zzz').set('bar', 'yyy').save(); + const objA = await new Parse.Object("ClassA") + .set("foo", "zzz") + .set("bar", "yyy") + .save(); - const objB = await new Parse.Object('ClassB').set('foo', 'zzz').set('bar', 'yyy').save(); + const objB = await new Parse.Object("ClassB") + .set("foo", "zzz") + .set("bar", "yyy") + .save(); const [fetchedA, fetchedB] = await Promise.all([ - new Parse.Query('ClassA').get(objA.id), - new Parse.Query('ClassB').get(objB.id), + new Parse.Query("ClassA").get(objA.id), + new Parse.Query("ClassB").get(objB.id), ]); - expect(fetchedA.has('foo')).toBeFalsy(); - expect(fetchedA.has('bar')).toBeTruthy(); + expect(fetchedA.has("foo")).toBeFalsy(); + expect(fetchedA.has("bar")).toBeTruthy(); - expect(fetchedB.has('foo')).toBeTruthy(); - expect(fetchedB.has('bar')).toBeFalsy(); + expect(fetchedB.has("foo")).toBeTruthy(); + expect(fetchedB.has("bar")).toBeFalsy(); }); - it('should hide fields in non user class and non standard user field at same time', async function () { + it("should hide fields in non user class and non standard user field at same time", async function () { const protectedFields = { - _User: { '*': ['phoneNumber'] }, - ClassA: { '*': ['foo'] }, - ClassB: { '*': ['bar'] }, + _User: { "*": ["phoneNumber"] }, + ClassA: { "*": ["foo"] }, + ClassB: { "*": ["bar"] }, }; await reconfigureServer({ protectedFields }); const user = new Parse.User(); - user.setUsername('Alice'); - user.setPassword('sekrit'); - user.set('email', 'alice@aol.com'); - user.set('phoneNumber', 8675309); - user.set('timeZone', 'America/Los_Angeles'); - user.set('favoriteColor', 'yellow'); - user.set('favoriteFood', 'pizza'); + user.setUsername("Alice"); + user.setPassword("sekrit"); + user.set("email", "alice@aol.com"); + user.set("phoneNumber", 8675309); + user.set("timeZone", "America/Los_Angeles"); + user.set("favoriteColor", "yellow"); + user.set("favoriteFood", "pizza"); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); user.setACL(acl); await user.save(); - const objA = await new Parse.Object('ClassA').set('foo', 'zzz').set('bar', 'yyy').save(); + const objA = await new Parse.Object("ClassA") + .set("foo", "zzz") + .set("bar", "yyy") + .save(); - const objB = await new Parse.Object('ClassB').set('foo', 'zzz').set('bar', 'yyy').save(); + const objB = await new Parse.Object("ClassB") + .set("foo", "zzz") + .set("bar", "yyy") + .save(); const [fetchedUser, fetchedA, fetchedB] = await Promise.all([ new Parse.Query(Parse.User).get(user.id), - new Parse.Query('ClassA').get(objA.id), - new Parse.Query('ClassB').get(objB.id), + new Parse.Query("ClassA").get(objA.id), + new Parse.Query("ClassB").get(objB.id), ]); - expect(fetchedA.has('foo')).toBeFalsy(); - expect(fetchedA.has('bar')).toBeTruthy(); + expect(fetchedA.has("foo")).toBeFalsy(); + expect(fetchedA.has("bar")).toBeTruthy(); - expect(fetchedB.has('foo')).toBeTruthy(); - expect(fetchedB.has('bar')).toBeFalsy(); + expect(fetchedB.has("foo")).toBeTruthy(); + expect(fetchedB.has("bar")).toBeFalsy(); - expect(fetchedUser.has('email')).toBeFalsy(); - expect(fetchedUser.has('phoneNumber')).toBeFalsy(); - expect(fetchedUser.has('favoriteColor')).toBeTruthy(); + expect(fetchedUser.has("email")).toBeFalsy(); + expect(fetchedUser.has("phoneNumber")).toBeFalsy(); + expect(fetchedUser.has("favoriteColor")).toBeTruthy(); }); }); - describe('using the pointer-permission variant', () => { + describe("using the pointer-permission variant", () => { let user1, user2; beforeEach(async () => { Config.get(Parse.applicationId).schemaCache.clear(); - user1 = await Parse.User.signUp('user1', 'password'); - user2 = await Parse.User.signUp('user2', 'password'); + user1 = await Parse.User.signUp("user1", "password"); + user2 = await Parse.User.signUp("user2", "password"); await Parse.User.logOut(); }); - describe('and get/fetch', () => { - it('should allow access using single user pointer-permissions', async done => { + describe("and get/fetch", () => { + it("should allow access using single user pointer-permissions", async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); - obj.set('owner', user1); - obj.set('test', 'test'); + obj.set("owner", user1); + obj.set("test", "test"); await obj.save(); const schema = await config.database.loadSchema(); await schema.updateClass( - 'AnObject', + "AnObject", {}, { - get: { '*': true }, - find: { '*': true }, - protectedFields: { '*': ['owner'], 'userField:owner': [] }, + get: { "*": true }, + find: { "*": true }, + protectedFields: { "*": ["owner"], "userField:owner": [] }, } ); - await Parse.User.logIn('user1', 'password'); + await Parse.User.logIn("user1", "password"); const objectAgain = await obj.fetch(); - expect(objectAgain.get('owner').id).toBe(user1.id); - expect(objectAgain.get('test')).toBe('test'); + expect(objectAgain.get("owner").id).toBe(user1.id); + expect(objectAgain.get("test")).toBe("test"); done(); }); - it('should deny access to other users using single user pointer-permissions', async done => { + it("should deny access to other users using single user pointer-permissions", async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); - obj.set('owner', user1); - obj.set('test', 'test'); + obj.set("owner", user1); + obj.set("test", "test"); await obj.save(); const schema = await config.database.loadSchema(); await schema.updateClass( - 'AnObject', + "AnObject", {}, { - get: { '*': true }, - find: { '*': true }, - protectedFields: { '*': ['owner'], 'userField:owner': [] }, + get: { "*": true }, + find: { "*": true }, + protectedFields: { "*": ["owner"], "userField:owner": [] }, } ); - await Parse.User.logIn('user2', 'password'); + await Parse.User.logIn("user2", "password"); const objectAgain = await obj.fetch(); - expect(objectAgain.get('owner')).toBe(undefined); - expect(objectAgain.get('test')).toBe('test'); + expect(objectAgain.get("owner")).toBe(undefined); + expect(objectAgain.get("test")).toBe("test"); done(); }); - it('should deny access to public using single user pointer-permissions', async done => { + it("should deny access to public using single user pointer-permissions", async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); - obj.set('owner', user1); - obj.set('test', 'test'); + obj.set("owner", user1); + obj.set("test", "test"); await obj.save(); const schema = await config.database.loadSchema(); await schema.updateClass( - 'AnObject', + "AnObject", {}, { - get: { '*': true }, - find: { '*': true }, - protectedFields: { '*': ['owner'], 'userField:owner': [] }, + get: { "*": true }, + find: { "*": true }, + protectedFields: { "*": ["owner"], "userField:owner": [] }, } ); const objectAgain = await obj.fetch(); - expect(objectAgain.get('owner')).toBe(undefined); - expect(objectAgain.get('test')).toBe('test'); + expect(objectAgain.get("owner")).toBe(undefined); + expect(objectAgain.get("test")).toBe("test"); done(); }); - it('should allow access using user array pointer-permissions', async done => { + it("should allow access using user array pointer-permissions", async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); - obj.set('owners', [user1, user2]); - obj.set('test', 'test'); + obj.set("owners", [user1, user2]); + obj.set("test", "test"); await obj.save(); const schema = await config.database.loadSchema(); await schema.updateClass( - 'AnObject', + "AnObject", {}, { - get: { '*': true }, - find: { '*': true }, - protectedFields: { '*': ['owners'], 'userField:owners': [] }, + get: { "*": true }, + find: { "*": true }, + protectedFields: { "*": ["owners"], "userField:owners": [] }, } ); - await Parse.User.logIn('user1', 'password'); + await Parse.User.logIn("user1", "password"); let objectAgain = await obj.fetch(); - expect(objectAgain.get('owners')[0].id).toBe(user1.id); - expect(objectAgain.get('test')).toBe('test'); - await Parse.User.logIn('user2', 'password'); + expect(objectAgain.get("owners")[0].id).toBe(user1.id); + expect(objectAgain.get("test")).toBe("test"); + await Parse.User.logIn("user2", "password"); objectAgain = await obj.fetch(); - expect(objectAgain.get('owners')[1].id).toBe(user2.id); - expect(objectAgain.get('test')).toBe('test'); + expect(objectAgain.get("owners")[1].id).toBe(user2.id); + expect(objectAgain.get("test")).toBe("test"); done(); }); - it('should deny access to other users using user array pointer-permissions', async done => { + it("should deny access to other users using user array pointer-permissions", async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); - obj.set('owners', [user1]); - obj.set('test', 'test'); + obj.set("owners", [user1]); + obj.set("test", "test"); await obj.save(); const schema = await config.database.loadSchema(); await schema.updateClass( - 'AnObject', + "AnObject", {}, { - get: { '*': true }, - find: { '*': true }, - protectedFields: { '*': ['owners'], 'userField:owners': [] }, + get: { "*": true }, + find: { "*": true }, + protectedFields: { "*": ["owners"], "userField:owners": [] }, } ); - await Parse.User.logIn('user2', 'password'); + await Parse.User.logIn("user2", "password"); const objectAgain = await obj.fetch(); - expect(objectAgain.get('owners')).toBe(undefined); - expect(objectAgain.get('test')).toBe('test'); + expect(objectAgain.get("owners")).toBe(undefined); + expect(objectAgain.get("test")).toBe("test"); done(); }); - it('should deny access to public using user array pointer-permissions', async done => { + it("should deny access to public using user array pointer-permissions", async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); - obj.set('owners', [user1, user2]); - obj.set('test', 'test'); + obj.set("owners", [user1, user2]); + obj.set("test", "test"); await obj.save(); const schema = await config.database.loadSchema(); await schema.updateClass( - 'AnObject', + "AnObject", {}, { - get: { '*': true }, - find: { '*': true }, - protectedFields: { '*': ['owners'], 'userField:owners': [] }, + get: { "*": true }, + find: { "*": true }, + protectedFields: { "*": ["owners"], "userField:owners": [] }, } ); const objectAgain = await obj.fetch(); - expect(objectAgain.get('owners')).toBe(undefined); - expect(objectAgain.get('test')).toBe('test'); + expect(objectAgain.get("owners")).toBe(undefined); + expect(objectAgain.get("test")).toBe("test"); done(); }); - it('should intersect protected fields when using multiple pointer-permission fields', async done => { + it("should intersect protected fields when using multiple pointer-permission fields", async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); - obj.set('owners', [user1]); - obj.set('owner', user1); - obj.set('test', 'test'); + obj.set("owners", [user1]); + obj.set("owner", user1); + obj.set("test", "test"); await obj.save(); const schema = await config.database.loadSchema(); await schema.updateClass( - 'AnObject', + "AnObject", {}, { - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, protectedFields: { - '*': ['owners', 'owner', 'test'], - 'userField:owners': ['owners', 'owner'], - 'userField:owner': ['owner'], + "*": ["owners", "owner", "test"], + "userField:owners": ["owners", "owner"], + "userField:owner": ["owner"], }, } ); // Check if protectFields from pointer-permissions got combined - await Parse.User.logIn('user1', 'password'); + await Parse.User.logIn("user1", "password"); const objectAgain = await obj.fetch(); - expect(objectAgain.get('owners').length).toBe(1); - expect(objectAgain.get('owner')).toBe(undefined); - expect(objectAgain.get('test')).toBe('test'); + expect(objectAgain.get("owners").length).toBe(1); + expect(objectAgain.get("owner")).toBe(undefined); + expect(objectAgain.get("test")).toBe("test"); done(); }); - it('should ignore pointer-permission fields not present in object', async done => { + it("should ignore pointer-permission fields not present in object", async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); - obj.set('owners', [user1]); - obj.set('owner', user1); - obj.set('test', 'test'); + obj.set("owners", [user1]); + obj.set("owner", user1); + obj.set("test", "test"); await obj.save(); const schema = await config.database.loadSchema(); await schema.updateClass( - 'AnObject', + "AnObject", {}, { - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, protectedFields: { - '*': [], - 'userField:idontexist': ['owner'], - 'userField:idontexist2': ['owners'], + "*": [], + "userField:idontexist": ["owner"], + "userField:idontexist2": ["owners"], }, } ); - await Parse.User.logIn('user1', 'password'); + await Parse.User.logIn("user1", "password"); const objectAgain = await obj.fetch(); - expect(objectAgain.get('owners')).not.toBe(undefined); - expect(objectAgain.get('owner')).not.toBe(undefined); - expect(objectAgain.get('test')).toBe('test'); + expect(objectAgain.get("owners")).not.toBe(undefined); + expect(objectAgain.get("owner")).not.toBe(undefined); + expect(objectAgain.get("test")).toBe("test"); done(); }); }); - describe('and find', () => { - it('should allow access using single user pointer-permissions', async done => { + describe("and find", () => { + it("should allow access using single user pointer-permissions", async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object('AnObject'); - const obj2 = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); + const obj2 = new Parse.Object("AnObject"); - obj.set('owner', user1); - obj.set('test', 'test'); - obj2.set('owner', user1); - obj2.set('test', 'test2'); + obj.set("owner", user1); + obj.set("test", "test"); + obj2.set("owner", user1); + obj2.set("test", "test2"); await Parse.Object.saveAll([obj, obj2]); const schema = await config.database.loadSchema(); await schema.updateClass( - 'AnObject', + "AnObject", {}, { - get: { '*': true }, - find: { '*': true }, - protectedFields: { '*': ['owner'], 'userField:owner': [] }, + get: { "*": true }, + find: { "*": true }, + protectedFields: { "*": ["owner"], "userField:owner": [] }, } ); - await Parse.User.logIn('user1', 'password'); + await Parse.User.logIn("user1", "password"); - const q = new Parse.Query('AnObject'); + const q = new Parse.Query("AnObject"); const results = await q.find(); // sort for checking in correct order - results.sort((a, b) => a.get('test').localeCompare(b.get('test'))); + results.sort((a, b) => a.get("test").localeCompare(b.get("test"))); expect(results.length).toBe(2); - expect(results[0].get('owner').id).toBe(user1.id); - expect(results[0].get('test')).toBe('test'); - expect(results[1].get('owner').id).toBe(user1.id); - expect(results[1].get('test')).toBe('test2'); + expect(results[0].get("owner").id).toBe(user1.id); + expect(results[0].get("test")).toBe("test"); + expect(results[1].get("owner").id).toBe(user1.id); + expect(results[1].get("test")).toBe("test2"); done(); }); - it('should deny access to other users using single user pointer-permissions', async done => { + it("should deny access to other users using single user pointer-permissions", async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object('AnObject'); - const obj2 = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); + const obj2 = new Parse.Object("AnObject"); - obj.set('owner', user1); - obj.set('test', 'test'); - obj2.set('owner', user1); - obj2.set('test', 'test2'); + obj.set("owner", user1); + obj.set("test", "test"); + obj2.set("owner", user1); + obj2.set("test", "test2"); await Parse.Object.saveAll([obj, obj2]); const schema = await config.database.loadSchema(); await schema.updateClass( - 'AnObject', + "AnObject", {}, { - get: { '*': true }, - find: { '*': true }, - protectedFields: { '*': ['owner'], 'userField:owner': [] }, + get: { "*": true }, + find: { "*": true }, + protectedFields: { "*": ["owner"], "userField:owner": [] }, } ); - await Parse.User.logIn('user2', 'password'); - const q = new Parse.Query('AnObject'); + await Parse.User.logIn("user2", "password"); + const q = new Parse.Query("AnObject"); const results = await q.find(); // sort for checking in correct order - results.sort((a, b) => a.get('test').localeCompare(b.get('test'))); + results.sort((a, b) => a.get("test").localeCompare(b.get("test"))); expect(results.length).toBe(2); - expect(results[0].get('owner')).toBe(undefined); - expect(results[0].get('test')).toBe('test'); - expect(results[1].get('owner')).toBe(undefined); - expect(results[1].get('test')).toBe('test2'); + expect(results[0].get("owner")).toBe(undefined); + expect(results[0].get("test")).toBe("test"); + expect(results[1].get("owner")).toBe(undefined); + expect(results[1].get("test")).toBe("test2"); done(); }); - it('should deny access to public using single user pointer-permissions', async done => { + it("should deny access to public using single user pointer-permissions", async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object('AnObject'); - const obj2 = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); + const obj2 = new Parse.Object("AnObject"); - obj.set('owner', user1); - obj.set('test', 'test'); - obj2.set('owner', user1); - obj2.set('test', 'test2'); + obj.set("owner", user1); + obj.set("test", "test"); + obj2.set("owner", user1); + obj2.set("test", "test2"); await Parse.Object.saveAll([obj, obj2]); const schema = await config.database.loadSchema(); await schema.updateClass( - 'AnObject', + "AnObject", {}, { - get: { '*': true }, - find: { '*': true }, - protectedFields: { '*': ['owner'], 'userField:owner': [] }, + get: { "*": true }, + find: { "*": true }, + protectedFields: { "*": ["owner"], "userField:owner": [] }, } ); - const q = new Parse.Query('AnObject'); + const q = new Parse.Query("AnObject"); const results = await q.find(); // sort for checking in correct order - results.sort((a, b) => a.get('test').localeCompare(b.get('test'))); + results.sort((a, b) => a.get("test").localeCompare(b.get("test"))); expect(results.length).toBe(2); - expect(results[0].get('owner')).toBe(undefined); - expect(results[0].get('test')).toBe('test'); - expect(results[1].get('owner')).toBe(undefined); - expect(results[1].get('test')).toBe('test2'); + expect(results[0].get("owner")).toBe(undefined); + expect(results[0].get("test")).toBe("test"); + expect(results[1].get("owner")).toBe(undefined); + expect(results[1].get("test")).toBe("test2"); done(); }); - it('should allow access using user array pointer-permissions', async done => { + it("should allow access using user array pointer-permissions", async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object('AnObject'); - const obj2 = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); + const obj2 = new Parse.Object("AnObject"); - obj.set('owners', [user1, user2]); - obj.set('test', 'test'); - obj2.set('owners', [user1, user2]); - obj2.set('test', 'test2'); + obj.set("owners", [user1, user2]); + obj.set("test", "test"); + obj2.set("owners", [user1, user2]); + obj2.set("test", "test2"); await Parse.Object.saveAll([obj, obj2]); const schema = await config.database.loadSchema(); await schema.updateClass( - 'AnObject', + "AnObject", {}, { - get: { '*': true }, - find: { '*': true }, - protectedFields: { '*': ['owners'], 'userField:owners': [] }, + get: { "*": true }, + find: { "*": true }, + protectedFields: { "*": ["owners"], "userField:owners": [] }, } ); - const q = new Parse.Query('AnObject'); + const q = new Parse.Query("AnObject"); let results; - await Parse.User.logIn('user1', 'password'); + await Parse.User.logIn("user1", "password"); results = await q.find(); // sort for checking in correct order - results.sort((a, b) => a.get('test').localeCompare(b.get('test'))); + results.sort((a, b) => a.get("test").localeCompare(b.get("test"))); expect(results.length).toBe(2); - expect(results[0].get('owners')[0].id).toBe(user1.id); - expect(results[0].get('test')).toBe('test'); - expect(results[1].get('owners')[0].id).toBe(user1.id); - expect(results[1].get('test')).toBe('test2'); + expect(results[0].get("owners")[0].id).toBe(user1.id); + expect(results[0].get("test")).toBe("test"); + expect(results[1].get("owners")[0].id).toBe(user1.id); + expect(results[1].get("test")).toBe("test2"); - await Parse.User.logIn('user2', 'password'); + await Parse.User.logIn("user2", "password"); results = await q.find(); // sort for checking in correct order - results.sort((a, b) => a.get('test').localeCompare(b.get('test'))); + results.sort((a, b) => a.get("test").localeCompare(b.get("test"))); expect(results.length).toBe(2); - expect(results[0].get('owners')[1].id).toBe(user2.id); - expect(results[0].get('test')).toBe('test'); - expect(results[1].get('owners')[1].id).toBe(user2.id); - expect(results[1].get('test')).toBe('test2'); + expect(results[0].get("owners")[1].id).toBe(user2.id); + expect(results[0].get("test")).toBe("test"); + expect(results[1].get("owners")[1].id).toBe(user2.id); + expect(results[1].get("test")).toBe("test2"); done(); }); - it('should deny access to other users using user array pointer-permissions', async done => { + it("should deny access to other users using user array pointer-permissions", async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object('AnObject'); - const obj2 = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); + const obj2 = new Parse.Object("AnObject"); - obj.set('owners', [user1]); - obj.set('test', 'test'); - obj2.set('owners', [user1]); - obj2.set('test', 'test2'); + obj.set("owners", [user1]); + obj.set("test", "test"); + obj2.set("owners", [user1]); + obj2.set("test", "test2"); await Parse.Object.saveAll([obj, obj2]); const schema = await config.database.loadSchema(); await schema.updateClass( - 'AnObject', + "AnObject", {}, { - get: { '*': true }, - find: { '*': true }, - protectedFields: { '*': ['owners'], 'userField:owners': [] }, + get: { "*": true }, + find: { "*": true }, + protectedFields: { "*": ["owners"], "userField:owners": [] }, } ); - await Parse.User.logIn('user2', 'password'); - const q = new Parse.Query('AnObject'); + await Parse.User.logIn("user2", "password"); + const q = new Parse.Query("AnObject"); const results = await q.find(); // sort for checking in correct order - results.sort((a, b) => a.get('test').localeCompare(b.get('test'))); + results.sort((a, b) => a.get("test").localeCompare(b.get("test"))); expect(results.length).toBe(2); - expect(results[0].get('owners')).toBe(undefined); - expect(results[0].get('test')).toBe('test'); - expect(results[1].get('owners')).toBe(undefined); - expect(results[1].get('test')).toBe('test2'); + expect(results[0].get("owners")).toBe(undefined); + expect(results[0].get("test")).toBe("test"); + expect(results[1].get("owners")).toBe(undefined); + expect(results[1].get("test")).toBe("test2"); done(); }); - it('should deny access to public using user array pointer-permissions', async done => { + it("should deny access to public using user array pointer-permissions", async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object('AnObject'); - const obj2 = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); + const obj2 = new Parse.Object("AnObject"); - obj.set('owners', [user1, user2]); - obj.set('test', 'test'); - obj2.set('owners', [user1, user2]); - obj2.set('test', 'test2'); + obj.set("owners", [user1, user2]); + obj.set("test", "test"); + obj2.set("owners", [user1, user2]); + obj2.set("test", "test2"); await Parse.Object.saveAll([obj, obj2]); const schema = await config.database.loadSchema(); await schema.updateClass( - 'AnObject', + "AnObject", {}, { - get: { '*': true }, - find: { '*': true }, - protectedFields: { '*': ['owners'], 'userField:owners': [] }, + get: { "*": true }, + find: { "*": true }, + protectedFields: { "*": ["owners"], "userField:owners": [] }, } ); - const q = new Parse.Query('AnObject'); + const q = new Parse.Query("AnObject"); const results = await q.find(); // sort for checking in correct order - results.sort((a, b) => a.get('test').localeCompare(b.get('test'))); + results.sort((a, b) => a.get("test").localeCompare(b.get("test"))); expect(results.length).toBe(2); - expect(results[0].get('owners')).toBe(undefined); - expect(results[0].get('test')).toBe('test'); - expect(results[1].get('owners')).toBe(undefined); - expect(results[1].get('test')).toBe('test2'); + expect(results[0].get("owners")).toBe(undefined); + expect(results[0].get("test")).toBe("test"); + expect(results[1].get("owners")).toBe(undefined); + expect(results[1].get("test")).toBe("test2"); done(); }); - it('should intersect protected fields when using multiple pointer-permission fields', async done => { + it("should intersect protected fields when using multiple pointer-permission fields", async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object('AnObject'); - const obj2 = new Parse.Object('AnObject'); - - obj.set('owners', [user1]); - obj.set('owner', user1); - obj.set('test', 'test'); - obj2.set('owners', [user1]); - obj2.set('test', 'test2'); + const obj = new Parse.Object("AnObject"); + const obj2 = new Parse.Object("AnObject"); + + obj.set("owners", [user1]); + obj.set("owner", user1); + obj.set("test", "test"); + obj2.set("owners", [user1]); + obj2.set("test", "test2"); await Parse.Object.saveAll([obj, obj2]); const schema = await config.database.loadSchema(); await schema.updateClass( - 'AnObject', + "AnObject", {}, { - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, protectedFields: { - '*': ['owners', 'owner', 'test'], - 'userField:owners': ['owners', 'owner'], - 'userField:owner': ['owner'], + "*": ["owners", "owner", "test"], + "userField:owners": ["owners", "owner"], + "userField:owner": ["owner"], }, } ); // Check if protectFields from pointer-permissions got combined - await Parse.User.logIn('user1', 'password'); + await Parse.User.logIn("user1", "password"); - const q = new Parse.Query('AnObject'); + const q = new Parse.Query("AnObject"); const results = await q.find(); // sort for checking in correct order - results.sort((a, b) => a.get('test').localeCompare(b.get('test'))); + results.sort((a, b) => a.get("test").localeCompare(b.get("test"))); expect(results.length).toBe(2); - expect(results[0].get('owners').length).toBe(1); - expect(results[0].get('owner')).toBe(undefined); - expect(results[0].get('test')).toBe('test'); - expect(results[1].get('owners')).toBe(undefined); - expect(results[1].get('owner')).toBe(undefined); - expect(results[1].get('test')).toBe('test2'); + expect(results[0].get("owners").length).toBe(1); + expect(results[0].get("owner")).toBe(undefined); + expect(results[0].get("test")).toBe("test"); + expect(results[1].get("owners")).toBe(undefined); + expect(results[1].get("owner")).toBe(undefined); + expect(results[1].get("test")).toBe("test2"); done(); }); - it('should ignore pointer-permission fields not present in object', async done => { + it("should ignore pointer-permission fields not present in object", async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object('AnObject'); - const obj2 = new Parse.Object('AnObject'); - - obj.set('owners', [user1]); - obj.set('owner', user1); - obj.set('test', 'test'); - obj2.set('owners', [user1]); - obj2.set('owner', user1); - obj2.set('test', 'test2'); + const obj = new Parse.Object("AnObject"); + const obj2 = new Parse.Object("AnObject"); + + obj.set("owners", [user1]); + obj.set("owner", user1); + obj.set("test", "test"); + obj2.set("owners", [user1]); + obj2.set("owner", user1); + obj2.set("test", "test2"); await Parse.Object.saveAll([obj, obj2]); const schema = await config.database.loadSchema(); await schema.updateClass( - 'AnObject', + "AnObject", {}, { - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, protectedFields: { - '*': [], - 'userField:idontexist': ['owner'], - 'userField:idontexist2': ['owners'], + "*": [], + "userField:idontexist": ["owner"], + "userField:idontexist2": ["owners"], }, } ); - await Parse.User.logIn('user1', 'password'); + await Parse.User.logIn("user1", "password"); - const q = new Parse.Query('AnObject'); + const q = new Parse.Query("AnObject"); const results = await q.find(); // sort for checking in correct order - results.sort((a, b) => a.get('test').localeCompare(b.get('test'))); + results.sort((a, b) => a.get("test").localeCompare(b.get("test"))); expect(results.length).toBe(2); - expect(results[0].get('owners')).not.toBe(undefined); - expect(results[0].get('owner')).not.toBe(undefined); - expect(results[0].get('test')).toBe('test'); - expect(results[1].get('owners')).not.toBe(undefined); - expect(results[1].get('owner')).not.toBe(undefined); - expect(results[1].get('test')).toBe('test2'); + expect(results[0].get("owners")).not.toBe(undefined); + expect(results[0].get("owner")).not.toBe(undefined); + expect(results[0].get("test")).toBe("test"); + expect(results[1].get("owners")).not.toBe(undefined); + expect(results[1].get("owner")).not.toBe(undefined); + expect(results[1].get("test")).toBe("test2"); done(); }); - it('should filter only fields from objects not owned by the user', async done => { + it("should filter only fields from objects not owned by the user", async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object('AnObject'); - const obj2 = new Parse.Object('AnObject'); - const obj3 = new Parse.Object('AnObject'); - - obj.set('owner', user1); - obj.set('test', 'test'); - obj2.set('owner', user2); - obj2.set('test', 'test2'); - obj3.set('owner', user2); - obj3.set('test', 'test3'); + const obj = new Parse.Object("AnObject"); + const obj2 = new Parse.Object("AnObject"); + const obj3 = new Parse.Object("AnObject"); + + obj.set("owner", user1); + obj.set("test", "test"); + obj2.set("owner", user2); + obj2.set("test", "test2"); + obj3.set("owner", user2); + obj3.set("test", "test3"); await Parse.Object.saveAll([obj, obj2, obj3]); const schema = await config.database.loadSchema(); await schema.updateClass( - 'AnObject', + "AnObject", {}, { - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, protectedFields: { - '*': ['owner'], - 'userField:owner': [], + "*": ["owner"], + "userField:owner": [], }, } ); - const q = new Parse.Query('AnObject'); + const q = new Parse.Query("AnObject"); let results; - await Parse.User.logIn('user1', 'password'); + await Parse.User.logIn("user1", "password"); results = await q.find(); // sort for checking in correct order - results.sort((a, b) => a.get('test').localeCompare(b.get('test'))); + results.sort((a, b) => a.get("test").localeCompare(b.get("test"))); expect(results.length).toBe(3); - expect(results[0].get('owner')).not.toBe(undefined); - expect(results[0].get('test')).toBe('test'); - expect(results[1].get('owner')).toBe(undefined); - expect(results[1].get('test')).toBe('test2'); - expect(results[2].get('owner')).toBe(undefined); - expect(results[2].get('test')).toBe('test3'); + expect(results[0].get("owner")).not.toBe(undefined); + expect(results[0].get("test")).toBe("test"); + expect(results[1].get("owner")).toBe(undefined); + expect(results[1].get("test")).toBe("test2"); + expect(results[2].get("owner")).toBe(undefined); + expect(results[2].get("test")).toBe("test3"); - await Parse.User.logIn('user2', 'password'); + await Parse.User.logIn("user2", "password"); results = await q.find(); // sort for checking in correct order - results.sort((a, b) => a.get('test').localeCompare(b.get('test'))); + results.sort((a, b) => a.get("test").localeCompare(b.get("test"))); expect(results.length).toBe(3); - expect(results[0].get('owner')).toBe(undefined); - expect(results[0].get('test')).toBe('test'); - expect(results[1].get('owner')).not.toBe(undefined); - expect(results[1].get('test')).toBe('test2'); - expect(results[2].get('owner')).not.toBe(undefined); - expect(results[2].get('test')).toBe('test3'); + expect(results[0].get("owner")).toBe(undefined); + expect(results[0].get("test")).toBe("test"); + expect(results[1].get("owner")).not.toBe(undefined); + expect(results[1].get("test")).toBe("test2"); + expect(results[2].get("owner")).not.toBe(undefined); + expect(results[2].get("test")).toBe("test3"); done(); }); }); }); - describe('schema setup', () => { + describe("schema setup", () => { let object; async function initialize() { @@ -768,8 +786,8 @@ describe('ProtectedFields', function () { object = new Parse.Object(className); - object.set('revision', 0); - object.set('test', 'test'); + object.set("revision", 0); + object.set("test", "test"); await object.save(null, { useMasterKey: true }); } @@ -778,9 +796,9 @@ describe('ProtectedFields', function () { await initialize(); }); - it('should fail setting non-existing protected field', async done => { - const field = 'non-existing'; - const entity = '*'; + it("should fail setting non-existing protected field", async done => { + const field = "non-existing"; + const entity = "*"; await expectAsync( updateCLP({ @@ -797,33 +815,36 @@ describe('ProtectedFields', function () { done(); }); - it('should allow setting authenticated', async () => { + it("should allow setting authenticated", async () => { await expectAsync( updateCLP({ protectedFields: { - authenticated: ['test'], + authenticated: ["test"], }, }) ).toBeResolved(); }); - it('should not allow protecting default fields', async () => { - const defaultFields = ['objectId', 'createdAt', 'updatedAt', 'ACL']; + it("should not allow protecting default fields", async () => { + const defaultFields = ["objectId", "createdAt", "updatedAt", "ACL"]; for (const field of defaultFields) { await expectAsync( updateCLP({ protectedFields: { - '*': [field], + "*": [field], }, }) ).toBeRejectedWith( - new Parse.Error(Parse.Error.INVALID_JSON, `Default field '${field}' can not be protected`) + new Parse.Error( + Parse.Error.INVALID_JSON, + `Default field '${field}' can not be protected` + ) ); } }); }); - describe('targeting public access', () => { + describe("targeting public access", () => { let obj1; async function initialize() { @@ -831,9 +852,9 @@ describe('ProtectedFields', function () { obj1 = new Parse.Object(className); - obj1.set('foo', 'foo'); - obj1.set('bar', 'bar'); - obj1.set('qux', 'qux'); + obj1.set("foo", "foo"); + obj1.set("bar", "bar"); + obj1.set("qux", "qux"); await obj1.save(null, { useMasterKey: true, @@ -844,59 +865,59 @@ describe('ProtectedFields', function () { await initialize(); }); - it('should hide field', async done => { + it("should hide field", async done => { await updateCLP({ - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, protectedFields: { - '*': ['foo'], + "*": ["foo"], }, }); // unauthenticated const object = await obj1.fetch(); - expect(object.get('foo')).toBe(undefined); - expect(object.get('bar')).toBeDefined(); - expect(object.get('qux')).toBeDefined(); + expect(object.get("foo")).toBe(undefined); + expect(object.get("bar")).toBeDefined(); + expect(object.get("qux")).toBeDefined(); done(); }); - it('should hide mutiple fields', async done => { + it("should hide mutiple fields", async done => { await updateCLP({ - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, protectedFields: { - '*': ['foo', 'bar'], + "*": ["foo", "bar"], }, }); // unauthenticated const object = await obj1.fetch(); - expect(object.get('foo')).toBe(undefined); - expect(object.get('bar')).toBe(undefined); - expect(object.get('qux')).toBeDefined(); + expect(object.get("foo")).toBe(undefined); + expect(object.get("bar")).toBe(undefined); + expect(object.get("qux")).toBeDefined(); done(); }); - it('should not hide any fields when set as empty array', async done => { + it("should not hide any fields when set as empty array", async done => { await updateCLP({ - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, protectedFields: { - '*': [], + "*": [], }, }); // unauthenticated const object = await obj1.fetch(); - expect(object.get('foo')).toBeDefined(); - expect(object.get('bar')).toBeDefined(); - expect(object.get('qux')).toBeDefined(); + expect(object.get("foo")).toBeDefined(); + expect(object.get("bar")).toBeDefined(); + expect(object.get("qux")).toBeDefined(); expect(object.id).toBeDefined(); expect(object.createdAt).toBeDefined(); expect(object.updatedAt).toBeDefined(); @@ -906,7 +927,7 @@ describe('ProtectedFields', function () { }); }); - describe('targeting authenticated', () => { + describe("targeting authenticated", () => { /** * is **owner** of: _obj1_ * @@ -940,18 +961,21 @@ describe('ProtectedFields', function () { await Parse.User.logOut(); - [user1, user2] = await Promise.all([createUser('user1'), createUser('user2')]); + [user1, user2] = await Promise.all([ + createUser("user1"), + createUser("user2"), + ]); obj1 = new Parse.Object(className); obj2 = new Parse.Object(className); - obj1.set('owner', user1); - obj1.set('testers', [user1, user2]); - obj1.set('test', 'test'); + obj1.set("owner", user1); + obj1.set("testers", [user1, user2]); + obj1.set("test", "test"); - obj2.set('owner', user2); - obj2.set('testers', [user1]); - obj2.set('test', 'test'); + obj2.set("owner", user2); + obj2.set("testers", [user1]); + obj2.set("test", "test"); await Parse.Object.saveAll([obj1, obj2], { useMasterKey: true, @@ -962,10 +986,10 @@ describe('ProtectedFields', function () { await initialize(); }); - it('should not hide any fields when set as empty array', async done => { + it("should not hide any fields when set as empty array", async done => { await updateCLP({ - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, protectedFields: { authenticated: [], }, @@ -976,9 +1000,9 @@ describe('ProtectedFields', function () { const object = await obj1.fetch(); - expect(object.get('owner')).toBeDefined(); - expect(object.get('testers')).toBeDefined(); - expect(object.get('test')).toBeDefined(); + expect(object.get("owner")).toBeDefined(); + expect(object.get("testers")).toBeDefined(); + expect(object.get("test")).toBeDefined(); expect(object.id).toBeDefined(); expect(object.createdAt).toBeDefined(); expect(object.updatedAt).toBeDefined(); @@ -987,36 +1011,36 @@ describe('ProtectedFields', function () { done(); }); - it('should hide fields for authenticated users only (* not set)', async done => { + it("should hide fields for authenticated users only (* not set)", async done => { await updateCLP({ - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, protectedFields: { - authenticated: ['test'], + authenticated: ["test"], }, }); // not authenticated const objectNonAuth = await obj1.fetch(); - expect(objectNonAuth.get('test')).toBeDefined(); + expect(objectNonAuth.get("test")).toBeDefined(); // authenticated await logIn(user1); const object = await obj1.fetch(); - expect(object.get('test')).toBe(undefined); + expect(object.get("test")).toBe(undefined); done(); }); - it('should intersect public and auth for authenticated user', async done => { + it("should intersect public and auth for authenticated user", async done => { await updateCLP({ - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, protectedFields: { - '*': ['owner', 'testers'], - authenticated: ['testers'], + "*": ["owner", "testers"], + authenticated: ["testers"], }, }); @@ -1026,25 +1050,25 @@ describe('ProtectedFields', function () { // ( {A,B} intersect {B} ) == {B} - expect(objectAuth.get('testers')).not.toBeDefined( - 'Should not be visible - protected for * and authenticated' + expect(objectAuth.get("testers")).not.toBeDefined( + "Should not be visible - protected for * and authenticated" ); - expect(objectAuth.get('test')).toBeDefined( - 'Should be visible - not protected for everyone (* and authenticated)' + expect(objectAuth.get("test")).toBeDefined( + "Should be visible - not protected for everyone (* and authenticated)" ); - expect(objectAuth.get('owner')).toBeDefined( - 'Should be visible - not protected for authenticated' + expect(objectAuth.get("owner")).toBeDefined( + "Should be visible - not protected for authenticated" ); done(); }); - it('should have higher prio than public for logged in users (intersect)', async done => { + it("should have higher prio than public for logged in users (intersect)", async done => { await updateCLP({ - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, protectedFields: { - '*': ['test'], + "*": ["test"], authenticated: [], }, }); @@ -1052,33 +1076,33 @@ describe('ProtectedFields', function () { await logIn(user1); const object = await obj1.fetch(); - expect(object.get('test')).toBe('test'); + expect(object.get("test")).toBe("test"); done(); }); - it('should have no effect on unauthenticated users (public not set)', async done => { + it("should have no effect on unauthenticated users (public not set)", async done => { await updateCLP({ - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, protectedFields: { - authenticated: ['test'], + authenticated: ["test"], }, }); // unauthenticated, protected const objectNonAuth = await obj1.fetch(); - expect(objectNonAuth.get('test')).toBe('test'); + expect(objectNonAuth.get("test")).toBe("test"); done(); }); - it('should protect multiple fields for authenticated users', async done => { + it("should protect multiple fields for authenticated users", async done => { await updateCLP({ - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, protectedFields: { - authenticated: ['test', 'owner'], + authenticated: ["test", "owner"], }, }); @@ -1086,23 +1110,23 @@ describe('ProtectedFields', function () { await logIn(user1); const object = await obj1.fetch(); - expect(object.get('test')).toBe(undefined); - expect(object.get('owner')).toBe(undefined); + expect(object.get("test")).toBe(undefined); + expect(object.get("owner")).toBe(undefined); done(); }); - it('should not be affected by rules not applicable to user (smoke)', async done => { + it("should not be affected by rules not applicable to user (smoke)", async done => { const role = await createRole({ users: user1 }); - const roleName = role.get('name'); + const roleName = role.get("name"); await updateCLP({ - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, protectedFields: { - authenticated: ['owner', 'testers'], - [`role:${roleName}`]: ['test'], - 'userField:owner': [], + authenticated: ["owner", "testers"], + [`role:${roleName}`]: ["test"], + "userField:owner": [], [user1.id]: [], }, }); @@ -1111,15 +1135,15 @@ describe('ProtectedFields', function () { await logIn(user2); const objectNotOwned = await obj1.fetch(); - expect(objectNotOwned.get('owner')).toBe(undefined); - expect(objectNotOwned.get('testers')).toBe(undefined); - expect(objectNotOwned.get('test')).toBeDefined(); + expect(objectNotOwned.get("owner")).toBe(undefined); + expect(objectNotOwned.get("testers")).toBe(undefined); + expect(objectNotOwned.get("test")).toBeDefined(); done(); }); }); - describe('targeting roles', () => { + describe("targeting roles", () => { let user1, user2; /** @@ -1139,18 +1163,21 @@ describe('ProtectedFields', function () { async function initialize() { await Config.get(Parse.applicationId).schemaCache.clear(); - [user1, user2] = await Promise.all([createUser('user1'), createUser('user2')]); + [user1, user2] = await Promise.all([ + createUser("user1"), + createUser("user2"), + ]); obj1 = new Parse.Object(className); obj2 = new Parse.Object(className); - obj1.set('owner', user1); - obj1.set('testers', [user1, user2]); - obj1.set('test', 'test'); + obj1.set("owner", user1); + obj1.set("testers", [user1, user2]); + obj1.set("test", "test"); - obj2.set('owner', user2); - obj2.set('testers', [user1]); - obj2.set('test', 'test'); + obj2.set("owner", user2); + obj2.set("testers", [user1]); + obj2.set("test", "test"); await Parse.Object.saveAll([obj1, obj2], { useMasterKey: true, @@ -1161,39 +1188,39 @@ describe('ProtectedFields', function () { await initialize(); }); - it('should hide field when user belongs to a role', async done => { + it("should hide field when user belongs to a role", async done => { const role = await createRole({ users: user1 }); - const roleName = role.get('name'); + const roleName = role.get("name"); await updateCLP({ protectedFields: { - [`role:${roleName}`]: ['test'], + [`role:${roleName}`]: ["test"], }, - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, }); // user has role await logIn(user1); const object = await obj1.fetch(); - expect(object.get('test')).toBe(undefined); // field protected - expect(object.get('owner')).toBeDefined(); - expect(object.get('testers')).toBeDefined(); + expect(object.get("test")).toBe(undefined); // field protected + expect(object.get("owner")).toBeDefined(); + expect(object.get("testers")).toBeDefined(); done(); }); - it('should not hide any fields when set as empty array', async done => { + it("should not hide any fields when set as empty array", async done => { const role = await createRole({ users: user1 }); - const roleName = role.get('name'); + const roleName = role.get("name"); await updateCLP({ protectedFields: { [`role:${roleName}`]: [], }, - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, }); // user has role @@ -1201,9 +1228,9 @@ describe('ProtectedFields', function () { const object = await obj1.fetch(); - expect(object.get('owner')).toBeDefined(); - expect(object.get('testers')).toBeDefined(); - expect(object.get('test')).toBeDefined(); + expect(object.get("owner")).toBeDefined(); + expect(object.get("testers")).toBeDefined(); + expect(object.get("test")).toBeDefined(); expect(object.id).toBeDefined(); expect(object.createdAt).toBeDefined(); expect(object.updatedAt).toBeDefined(); @@ -1212,15 +1239,15 @@ describe('ProtectedFields', function () { done(); }); - it('should hide multiple fields when user belongs to a role', async done => { + it("should hide multiple fields when user belongs to a role", async done => { const role = await createRole({ users: user1 }); - const roleName = role.get('name'); + const roleName = role.get("name"); await updateCLP({ - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, protectedFields: { - [`role:${roleName}`]: ['test', 'owner'], + [`role:${roleName}`]: ["test", "owner"], }, }); @@ -1229,25 +1256,28 @@ describe('ProtectedFields', function () { const object = await obj1.fetch(); - expect(object.get('test')).toBe(undefined, 'Field should not be visible - protected by role'); - expect(object.get('owner')).toBe( + expect(object.get("test")).toBe( undefined, - 'Field should not be visible - protected by role' + "Field should not be visible - protected by role" ); - expect(object.get('testers')).toBeDefined(); + expect(object.get("owner")).toBe( + undefined, + "Field should not be visible - protected by role" + ); + expect(object.get("testers")).toBeDefined(); done(); }); - it('should not protect when user does not belong to a role', async done => { + it("should not protect when user does not belong to a role", async done => { const role = await createRole({ users: user1 }); - const roleName = role.get('name'); + const roleName = role.get("name"); await updateCLP({ - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, protectedFields: { - [`role:${roleName}`]: ['test', 'owner'], + [`role:${roleName}`]: ["test", "owner"], }, }); @@ -1255,26 +1285,26 @@ describe('ProtectedFields', function () { await logIn(user2); const object = await obj1.fetch(); - expect(object.get('test')).toBeDefined(); - expect(object.get('owner')).toBeDefined(); - expect(object.get('testers')).toBeDefined(); + expect(object.get("test")).toBeDefined(); + expect(object.get("owner")).toBeDefined(); + expect(object.get("testers")).toBeDefined(); done(); }); - it('should intersect protected fields when user belongs to multiple roles', async done => { + it("should intersect protected fields when user belongs to multiple roles", async done => { const role1 = await createRole({ users: user1 }); const role2 = await createRole({ users: user1 }); - const role1name = role1.get('name'); - const role2name = role2.get('name'); + const role1name = role1.get("name"); + const role2name = role2.get("name"); await updateCLP({ - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, protectedFields: { - [`role:${role1name}`]: ['owner'], - [`role:${role2name}`]: ['test', 'owner'], + [`role:${role1name}`]: ["owner"], + [`role:${role2name}`]: ["test", "owner"], }, }); @@ -1283,49 +1313,49 @@ describe('ProtectedFields', function () { const object = await obj1.fetch(); // "owner" is a result of intersection - expect(object.get('owner')).toBe( + expect(object.get("owner")).toBe( undefined, - 'Must not be visible - protected for all roles the user belongs to' + "Must not be visible - protected for all roles the user belongs to" ); - expect(object.get('test')).toBeDefined( - 'Has to be visible - is not protected for users with role1' + expect(object.get("test")).toBeDefined( + "Has to be visible - is not protected for users with role1" ); done(); }); - it('should intersect protected fields when user belongs to multiple roles hierarchy', async done => { + it("should intersect protected fields when user belongs to multiple roles hierarchy", async done => { const admin = await createRole({ users: user1, - roleName: 'admin', + roleName: "admin", }); const moder = await createRole({ users: [user1, user2], - roleName: 'moder', + roleName: "moder", }); const tester = await createRole({ - roleName: 'tester', + roleName: "tester", }); // admin supersets moder role - moder.relation('roles').add(admin); + moder.relation("roles").add(admin); await moder.save(null, { useMasterKey: true }); - tester.relation('roles').add(moder); + tester.relation("roles").add(moder); await tester.save(null, { useMasterKey: true }); - const roleAdmin = `role:${admin.get('name')}`; - const roleModer = `role:${moder.get('name')}`; - const roleTester = `role:${tester.get('name')}`; + const roleAdmin = `role:${admin.get("name")}`; + const roleModer = `role:${moder.get("name")}`; + const roleTester = `role:${tester.get("name")}`; await updateCLP({ - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, protectedFields: { [roleAdmin]: [], - [roleModer]: ['owner'], - [roleTester]: ['test', 'owner'], + [roleModer]: ["owner"], + [roleTester]: ["test", "owner"], }, }); @@ -1334,11 +1364,11 @@ describe('ProtectedFields', function () { const object = await obj1.fetch(); // being admin makes all fields visible - expect(object.get('test')).toBeDefined( - 'Should be visible - admin role explicitly removes protection for all fields ( [] )' + expect(object.get("test")).toBeDefined( + "Should be visible - admin role explicitly removes protection for all fields ( [] )" ); - expect(object.get('owner')).toBeDefined( - 'Should be visible - admin role explicitly removes protection for all fields ( [] )' + expect(object.get("owner")).toBeDefined( + "Should be visible - admin role explicitly removes protection for all fields ( [] )" ); // user2 has moder & tester role, moder includes tester. @@ -1346,26 +1376,26 @@ describe('ProtectedFields', function () { const objectAgain = await obj1.fetch(); // being moder allows "test" field - expect(objectAgain.get('owner')).toBe( + expect(objectAgain.get("owner")).toBe( undefined, '"owner" should not be visible - protected for each role user belongs to' ); - expect(objectAgain.get('test')).toBeDefined( + expect(objectAgain.get("test")).toBeDefined( 'Should be visible - moder role does not protect "test" field' ); done(); }); - it('should be able to clear protected fields for role (protected for authenticated)', async done => { + it("should be able to clear protected fields for role (protected for authenticated)", async done => { const role = await createRole({ users: user1 }); - const roleName = role.get('name'); + const roleName = role.get("name"); await updateCLP({ - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, protectedFields: { - authenticated: ['test'], + authenticated: ["test"], [`role:${roleName}`]: [], }, }); @@ -1373,21 +1403,21 @@ describe('ProtectedFields', function () { // user has role, test field visible await logIn(user1); const object = await obj1.fetch(); - expect(object.get('test')).toBe('test'); + expect(object.get("test")).toBe("test"); done(); }); - it('should determine protectedFields as intersection of field sets for public and role', async done => { + it("should determine protectedFields as intersection of field sets for public and role", async done => { const role = await createRole({ users: user1 }); - const roleName = role.get('name'); + const roleName = role.get("name"); await updateCLP({ - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, protectedFields: { - '*': ['test', 'owner'], - [`role:${roleName}`]: ['owner', 'testers'], + "*": ["test", "owner"], + [`role:${roleName}`]: ["owner", "testers"], }, }); @@ -1395,22 +1425,22 @@ describe('ProtectedFields', function () { await logIn(user1); const object = await obj1.fetch(); - expect(object.get('test')).toBeDefined( + expect(object.get("test")).toBeDefined( 'Should be visible - "test" is not protected for role user belongs to' ); - expect(object.get('testers')).toBeDefined( + expect(object.get("testers")).toBeDefined( 'Should be visible - "testers" is allowed for everyone (*)' ); - expect(object.get('owner')).toBe( + expect(object.get("owner")).toBe( undefined, 'Should not be visible - "test" is not allowed for both public(*) and role' ); done(); }); - it('should be determined as an intersection of protecedFields for authenticated and role', async done => { + it("should be determined as an intersection of protecedFields for authenticated and role", async done => { const role = await createRole({ users: user1 }); - const roleName = role.get('name'); + const roleName = role.get("name"); // this is an example of misunderstood configuration. // If you allow (== do not restrict) some field for broader audience @@ -1418,11 +1448,11 @@ describe('ProtectedFields', function () { // it's not possible to narrow by protecting field for a role. // You'd have to protect it for 'authenticated' as well. await updateCLP({ - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, protectedFields: { - authenticated: ['test'], - [`role:${roleName}`]: ['owner'], + authenticated: ["test"], + [`role:${roleName}`]: ["owner"], }, }); @@ -1431,24 +1461,26 @@ describe('ProtectedFields', function () { const object = await obj1.fetch(); // - expect(object.get('test')).toBeDefined( + expect(object.get("test")).toBeDefined( "Being both auhenticated and having a role leads to clearing protection on 'test' (by role rules)" ); - expect(object.get('owner')).toBeDefined('All authenticated users allowed to see "owner"'); - expect(object.get('testers')).toBeDefined(); + expect(object.get("owner")).toBeDefined( + 'All authenticated users allowed to see "owner"' + ); + expect(object.get("testers")).toBeDefined(); done(); }); - it('should not hide fields when user does not belong to a role protectedFields set for', async done => { + it("should not hide fields when user does not belong to a role protectedFields set for", async done => { const role = await createRole({ users: user2 }); - const roleName = role.get('name'); + const roleName = role.get("name"); await updateCLP({ - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, protectedFields: { - [`role:${roleName}`]: ['test'], + [`role:${roleName}`]: ["test"], }, }); @@ -1458,15 +1490,15 @@ describe('ProtectedFields', function () { await logIn(user1); const object = await obj1.fetch(); - expect(object.get('test')).toBeDefined( - 'Field should be visible - user belongs to a role that has no protectedFields set' + expect(object.get("test")).toBeDefined( + "Field should be visible - user belongs to a role that has no protectedFields set" ); done(); }); }); - describe('using pointer-fields and queries with keys projection', () => { + describe("using pointer-fields and queries with keys projection", () => { /* * Pointer variant ("userField:column") relies on User ids * returned after query executed (hides fields before sending it to client) @@ -1491,23 +1523,23 @@ describe('ProtectedFields', function () { async function initialize() { await Config.get(Parse.applicationId).schemaCache.clear(); - user1 = await createUser('user1'); + user1 = await createUser("user1"); user1 = await logIn(user1); // await user1.fetch(); obj = new Parse.Object(className); - obj.set('owner', user1); - obj.set('field', 'field'); - obj.set('test', 'test'); + obj.set("owner", user1); + obj.set("field", "field"); + obj.set("test", "test"); await Parse.Object.saveAll([obj], { useMasterKey: true }); headers = { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Rest-API-Key': 'rest', - 'Content-Type': 'application/json', - 'X-Parse-Session-Token': user1.getSessionToken(), + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Rest-API-Key": "rest", + "Content-Type": "application/json", + "X-Parse-Session-Token": user1.getSessionToken(), }; } @@ -1515,87 +1547,90 @@ describe('ProtectedFields', function () { await initialize(); }); - it('should be enforced regardless of pointer-field being included in keys (select)', async done => { + it("should be enforced regardless of pointer-field being included in keys (select)", async done => { await updateCLP({ - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, protectedFields: { - '*': ['field', 'test'], - 'userField:owner': [], + "*": ["field", "test"], + "userField:owner": [], }, }); - const query = new Parse.Query('AnObject'); - query.select('field', 'test'); + const query = new Parse.Query("AnObject"); + query.select("field", "test"); const object = await query.get(obj.id); - expect(object.get('field')).toBe('field'); - expect(object.get('test')).toBe('test'); + expect(object.get("field")).toBe("field"); + expect(object.get("test")).toBe("test"); done(); }); - it('should protect fields for query where pointer field is not included via keys (REST GET)', async done => { + it("should protect fields for query where pointer field is not included via keys (REST GET)", async done => { const obj = new Parse.Object(className); - obj.set('owner', user1); - obj.set('field', 'field'); - obj.set('test', 'test'); + obj.set("owner", user1); + obj.set("field", "field"); + obj.set("test", "test"); await Parse.Object.saveAll([obj], { useMasterKey: true }); await updateCLP({ - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, protectedFields: { - '*': ['field', 'test'], - 'userField:owner': ['test'], + "*": ["field", "test"], + "userField:owner": ["test"], }, }); const { data: object } = await request({ url: `${Parse.serverURL}/classes/${className}/${obj.id}`, qs: { - keys: 'field,test', + keys: "field,test", }, headers: headers, }); expect(object.field).toBe( - 'field', + "field", 'Should BE in response - not protected by "userField:owner"' ); expect(object.test).toBe( undefined, 'Should NOT be in response - protected by "userField:owner"' ); - expect(object.owner).toBe(undefined, 'Should not be in response - not included in "keys"'); + expect(object.owner).toBe( + undefined, + 'Should not be in response - not included in "keys"' + ); done(); }); - it('should protect fields for query where pointer field is not included via keys (REST FIND)', async done => { + it("should protect fields for query where pointer field is not included via keys (REST FIND)", async done => { const obj = new Parse.Object(className); - obj.set('owner', user1); - obj.set('field', 'field'); - obj.set('test', 'test'); + obj.set("owner", user1); + obj.set("field", "field"); + obj.set("test", "test"); await Parse.Object.saveAll([obj], { useMasterKey: true }); await obj.fetch(); await updateCLP({ - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, protectedFields: { - '*': ['field', 'test'], - 'userField:owner': ['test'], + "*": ["field", "test"], + "userField:owner": ["test"], }, }); const { data } = await request({ url: `${Parse.serverURL}/classes/${className}`, qs: { - keys: 'field,test', + keys: "field,test", where: JSON.stringify({ objectId: obj.id }), }, headers, @@ -1604,60 +1639,66 @@ describe('ProtectedFields', function () { const object = data.results[0]; expect(object.field).toBe( - 'field', + "field", 'Should be in response - not protected by "userField:owner"' ); expect(object.test).toBe( undefined, 'Should not be in response - protected by "userField:owner"' ); - expect(object.owner).toBe(undefined, 'Should not be in response - not included in "keys"'); + expect(object.owner).toBe( + undefined, + 'Should not be in response - not included in "keys"' + ); done(); }); - it('should protect fields for query where pointer field is in excludeKeys (REST GET)', async done => { + it("should protect fields for query where pointer field is in excludeKeys (REST GET)", async done => { await updateCLP({ - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, protectedFields: { - '*': ['field', 'test'], - 'userField:owner': ['test'], + "*": ["field", "test"], + "userField:owner": ["test"], }, }); const { data: object } = await request({ qs: { - excludeKeys: 'owner', + excludeKeys: "owner", }, headers, url: `${Parse.serverURL}/classes/${className}/${obj.id}`, }); expect(object.field).toBe( - 'field', + "field", 'Should be in response - not protected by "userField:owner"' ); - expect(object['test']).toBe( + expect(object["test"]).toBe( undefined, 'Should not be in response - protected by "userField:owner"' ); - expect(object['owner']).toBe(undefined, 'Should not be in response - not included in "keys"'); + expect(object["owner"]).toBe( + undefined, + 'Should not be in response - not included in "keys"' + ); done(); }); - it('should protect fields for query where pointer field is in excludedKeys (REST FIND)', async done => { + it("should protect fields for query where pointer field is in excludedKeys (REST FIND)", async done => { await updateCLP({ protectedFields: { - '*': ['field', 'test'], - 'userField:owner': ['test'], + "*": ["field", "test"], + "userField:owner": ["test"], }, - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, }); const { data } = await request({ qs: { - excludeKeys: 'owner', + excludeKeys: "owner", where: JSON.stringify({ objectId: obj.id }), }, headers, @@ -1667,36 +1708,39 @@ describe('ProtectedFields', function () { const object = data.results[0]; expect(object.field).toBe( - 'field', + "field", 'Should be in response - not protected by "userField:owner"' ); expect(object.test).toBe( undefined, 'Should not be in response - protected by "userField:owner"' ); - expect(object.owner).toBe(undefined, 'Should not be in response - not included in "keys"'); + expect(object.owner).toBe( + undefined, + 'Should not be in response - not included in "keys"' + ); done(); }); - xit('todo: should be enforced regardless of pointer-field being excluded', async done => { + xit("todo: should be enforced regardless of pointer-field being excluded", async done => { await updateCLP({ - get: { '*': true }, - find: { '*': true }, + get: { "*": true }, + find: { "*": true }, protectedFields: { - '*': ['field', 'test'], - 'userField:owner': [], + "*": ["field", "test"], + "userField:owner": [], }, }); - const query = new Parse.Query('AnObject'); + const query = new Parse.Query("AnObject"); /* TODO: this has some caching problems on JS-SDK (2.11.) side */ // query.exclude('owner') const object = await query.get(obj.id); - expect(object.get('field')).toBe('field'); - expect(object.get('test')).toBe('test'); - expect(object.get('owner')).toBe(undefined); + expect(object.get("field")).toBe("field"); + expect(object.get("test")).toBe("test"); + expect(object.get("owner")).toBe(undefined); done(); }); }); diff --git a/spec/PublicAPI.spec.js b/spec/PublicAPI.spec.js index 940417ad24..4de3e3c10f 100644 --- a/spec/PublicAPI.spec.js +++ b/spec/PublicAPI.spec.js @@ -1,4 +1,4 @@ -const req = require('../lib/request'); +const req = require("../lib/request"); const request = function (url, callback) { return req({ @@ -9,20 +9,20 @@ const request = function (url, callback) { ); }; -describe('public API', () => { - it('should return missing token error on ajax request without token provided', async () => { +describe("public API", () => { + it("should return missing token error on ajax request without token provided", async () => { await reconfigureServer({ - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }); try { await req({ - method: 'POST', - url: 'http://localhost:8378/1/apps/test/request_password_reset', + method: "POST", + url: "http://localhost:8378/1/apps/test/request_password_reset", body: `new_password=user1&token=`, headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'X-Requested-With': 'XMLHttpRequest', + "Content-Type": "application/x-www-form-urlencoded", + "X-Requested-With": "XMLHttpRequest", }, followRedirects: false, }); @@ -32,19 +32,19 @@ describe('public API', () => { } }); - it('should return missing password error on ajax request without password provided', async () => { + it("should return missing password error on ajax request without password provided", async () => { await reconfigureServer({ - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }); try { await req({ - method: 'POST', - url: 'http://localhost:8378/1/apps/test/request_password_reset', + method: "POST", + url: "http://localhost:8378/1/apps/test/request_password_reset", body: `new_password=&token=132414`, headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'X-Requested-With': 'XMLHttpRequest', + "Content-Type": "application/x-www-form-urlencoded", + "X-Requested-With": "XMLHttpRequest", }, followRedirects: false, }); @@ -54,105 +54,135 @@ describe('public API', () => { } }); - it('should get invalid_link.html', done => { - request('http://localhost:8378/1/apps/invalid_link.html', (err, httpResponse) => { - expect(httpResponse.status).toBe(200); - done(); - }); + it("should get invalid_link.html", done => { + request( + "http://localhost:8378/1/apps/invalid_link.html", + (err, httpResponse) => { + expect(httpResponse.status).toBe(200); + done(); + } + ); }); - it('should get choose_password', done => { + it("should get choose_password", done => { reconfigureServer({ - appName: 'unused', - publicServerURL: 'http://localhost:8378/1', + appName: "unused", + publicServerURL: "http://localhost:8378/1", }).then(() => { - request('http://localhost:8378/1/apps/choose_password?id=test', (err, httpResponse) => { - expect(httpResponse.status).toBe(200); - done(); - }); + request( + "http://localhost:8378/1/apps/choose_password?id=test", + (err, httpResponse) => { + expect(httpResponse.status).toBe(200); + done(); + } + ); }); }); - it('should get verify_email_success.html', done => { - request('http://localhost:8378/1/apps/verify_email_success.html', (err, httpResponse) => { - expect(httpResponse.status).toBe(200); - done(); - }); + it("should get verify_email_success.html", done => { + request( + "http://localhost:8378/1/apps/verify_email_success.html", + (err, httpResponse) => { + expect(httpResponse.status).toBe(200); + done(); + } + ); }); - it('should get password_reset_success.html', done => { - request('http://localhost:8378/1/apps/password_reset_success.html', (err, httpResponse) => { - expect(httpResponse.status).toBe(200); - done(); - }); + it("should get password_reset_success.html", done => { + request( + "http://localhost:8378/1/apps/password_reset_success.html", + (err, httpResponse) => { + expect(httpResponse.status).toBe(200); + done(); + } + ); }); }); -describe('public API without publicServerURL', () => { +describe("public API without publicServerURL", () => { beforeEach(async () => { - await reconfigureServer({ appName: 'unused' }); + await reconfigureServer({ appName: "unused" }); }); - it('should get 404 on verify_email', done => { - request('http://localhost:8378/1/apps/test/verify_email', (err, httpResponse) => { - expect(httpResponse.status).toBe(404); - done(); - }); + it("should get 404 on verify_email", done => { + request( + "http://localhost:8378/1/apps/test/verify_email", + (err, httpResponse) => { + expect(httpResponse.status).toBe(404); + done(); + } + ); }); - it('should get 404 choose_password', done => { - request('http://localhost:8378/1/apps/choose_password?id=test', (err, httpResponse) => { - expect(httpResponse.status).toBe(404); - done(); - }); + it("should get 404 choose_password", done => { + request( + "http://localhost:8378/1/apps/choose_password?id=test", + (err, httpResponse) => { + expect(httpResponse.status).toBe(404); + done(); + } + ); }); - it('should get 404 on request_password_reset', done => { - request('http://localhost:8378/1/apps/test/request_password_reset', (err, httpResponse) => { - expect(httpResponse.status).toBe(404); - done(); - }); + it("should get 404 on request_password_reset", done => { + request( + "http://localhost:8378/1/apps/test/request_password_reset", + (err, httpResponse) => { + expect(httpResponse.status).toBe(404); + done(); + } + ); }); }); -describe('public API supplied with invalid application id', () => { +describe("public API supplied with invalid application id", () => { beforeEach(async () => { - await reconfigureServer({ appName: 'unused' }); + await reconfigureServer({ appName: "unused" }); }); - it('should get 403 on verify_email', done => { - request('http://localhost:8378/1/apps/invalid/verify_email', (err, httpResponse) => { - expect(httpResponse.status).toBe(403); - done(); - }); + it("should get 403 on verify_email", done => { + request( + "http://localhost:8378/1/apps/invalid/verify_email", + (err, httpResponse) => { + expect(httpResponse.status).toBe(403); + done(); + } + ); }); - it('should get 403 choose_password', done => { - request('http://localhost:8378/1/apps/choose_password?id=invalid', (err, httpResponse) => { - expect(httpResponse.status).toBe(403); - done(); - }); + it("should get 403 choose_password", done => { + request( + "http://localhost:8378/1/apps/choose_password?id=invalid", + (err, httpResponse) => { + expect(httpResponse.status).toBe(403); + done(); + } + ); }); - it('should get 403 on get of request_password_reset', done => { - request('http://localhost:8378/1/apps/invalid/request_password_reset', (err, httpResponse) => { - expect(httpResponse.status).toBe(403); - done(); - }); + it("should get 403 on get of request_password_reset", done => { + request( + "http://localhost:8378/1/apps/invalid/request_password_reset", + (err, httpResponse) => { + expect(httpResponse.status).toBe(403); + done(); + } + ); }); - it('should get 403 on post of request_password_reset', done => { + it("should get 403 on post of request_password_reset", done => { req({ - url: 'http://localhost:8378/1/apps/invalid/request_password_reset', - method: 'POST', + url: "http://localhost:8378/1/apps/invalid/request_password_reset", + method: "POST", }).then(done.fail, httpResponse => { expect(httpResponse.status).toBe(403); done(); }); }); - it('should get 403 on resendVerificationEmail', done => { + it("should get 403 on resendVerificationEmail", done => { request( - 'http://localhost:8378/1/apps/invalid/resend_verification_email', + "http://localhost:8378/1/apps/invalid/resend_verification_email", (err, httpResponse) => { expect(httpResponse.status).toBe(403); done(); diff --git a/spec/PurchaseValidation.spec.js b/spec/PurchaseValidation.spec.js index 478b81260e..2bc1d16881 100644 --- a/spec/PurchaseValidation.spec.js +++ b/spec/PurchaseValidation.spec.js @@ -1,74 +1,74 @@ -const request = require('../lib/request'); +const request = require("../lib/request"); function createProduct() { const file = new Parse.File( - 'name', + "name", { - base64: new Buffer('download_file', 'utf-8').toString('base64'), + base64: new Buffer("download_file", "utf-8").toString("base64"), }, - 'text' + "text" ); return file.save().then(function () { - const product = new Parse.Object('_Product'); + const product = new Parse.Object("_Product"); product.set({ download: file, icon: file, - title: 'a product', - subtitle: 'a product', + title: "a product", + subtitle: "a product", order: 1, - productIdentifier: 'a-product', + productIdentifier: "a-product", }); return product.save(); }); } -describe('test validate_receipt endpoint', () => { +describe("test validate_receipt endpoint", () => { beforeEach(async () => { await createProduct(); }); - it('should bypass appstore validation', async () => { + it("should bypass appstore validation", async () => { const httpResponse = await request({ headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }, - method: 'POST', - url: 'http://localhost:8378/1/validate_purchase', + method: "POST", + url: "http://localhost:8378/1/validate_purchase", body: { - productIdentifier: 'a-product', + productIdentifier: "a-product", receipt: { - __type: 'Bytes', - base64: new Buffer('receipt', 'utf-8').toString('base64'), + __type: "Bytes", + base64: new Buffer("receipt", "utf-8").toString("base64"), }, bypassAppStoreValidation: true, }, }); const body = httpResponse.data; - if (typeof body != 'object') { - fail('Body is not an object'); + if (typeof body != "object") { + fail("Body is not an object"); } else { - expect(body.__type).toEqual('File'); + expect(body.__type).toEqual("File"); const url = body.url; const otherResponse = await request({ url: url, }); - expect(otherResponse.text).toBe('download_file'); + expect(otherResponse.text).toBe("download_file"); } }); - it('should fail for missing receipt', async () => { + it("should fail for missing receipt", async () => { const response = await request({ headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }, - url: 'http://localhost:8378/1/validate_purchase', - method: 'POST', + url: "http://localhost:8378/1/validate_purchase", + method: "POST", body: { - productIdentifier: 'a-product', + productIdentifier: "a-product", bypassAppStoreValidation: true, }, }).then(fail, res => res); @@ -76,19 +76,19 @@ describe('test validate_receipt endpoint', () => { expect(body.code).toEqual(Parse.Error.INVALID_JSON); }); - it('should fail for missing product identifier', async () => { + it("should fail for missing product identifier", async () => { const response = await request({ headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }, - url: 'http://localhost:8378/1/validate_purchase', - method: 'POST', + url: "http://localhost:8378/1/validate_purchase", + method: "POST", body: { receipt: { - __type: 'Bytes', - base64: new Buffer('receipt', 'utf-8').toString('base64'), + __type: "Bytes", + base64: new Buffer("receipt", "utf-8").toString("base64"), }, bypassAppStoreValidation: true, }, @@ -97,64 +97,66 @@ describe('test validate_receipt endpoint', () => { expect(body.code).toEqual(Parse.Error.INVALID_JSON); }); - it('should bypass appstore validation and not find product', async () => { + it("should bypass appstore validation and not find product", async () => { const response = await request({ headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }, - url: 'http://localhost:8378/1/validate_purchase', - method: 'POST', + url: "http://localhost:8378/1/validate_purchase", + method: "POST", body: { - productIdentifier: 'another-product', + productIdentifier: "another-product", receipt: { - __type: 'Bytes', - base64: new Buffer('receipt', 'utf8').toString('base64'), + __type: "Bytes", + base64: new Buffer("receipt", "utf8").toString("base64"), }, bypassAppStoreValidation: true, }, }).catch(error => error); const body = response.data; - if (typeof body != 'object') { - fail('Body is not an object'); + if (typeof body != "object") { + fail("Body is not an object"); } else { expect(body.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); - expect(body.error).toEqual('Object not found.'); + expect(body.error).toEqual("Object not found."); } }); - it('should fail at appstore validation', async () => { + it("should fail at appstore validation", async () => { const response = await request({ headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }, - url: 'http://localhost:8378/1/validate_purchase', - method: 'POST', + url: "http://localhost:8378/1/validate_purchase", + method: "POST", body: { - productIdentifier: 'a-product', + productIdentifier: "a-product", receipt: { - __type: 'Bytes', - base64: new Buffer('receipt', 'utf-8').toString('base64'), + __type: "Bytes", + base64: new Buffer("receipt", "utf-8").toString("base64"), }, }, }); const body = response.data; - if (typeof body != 'object') { - fail('Body is not an object'); + if (typeof body != "object") { + fail("Body is not an object"); } else { expect(body.status).toBe(21002); - expect(body.error).toBe('The data in the receipt-data property was malformed or missing.'); + expect(body.error).toBe( + "The data in the receipt-data property was malformed or missing." + ); } }); - it('should not create a _Product', done => { - const product = new Parse.Object('_Product'); + it("should not create a _Product", done => { + const product = new Parse.Object("_Product"); product.save().then( function () { - fail('Should not be able to save'); + fail("Should not be able to save"); done(); }, function (err) { @@ -164,20 +166,22 @@ describe('test validate_receipt endpoint', () => { ); }); - it('should be able to update a _Product', done => { - const query = new Parse.Query('_Product'); + it("should be able to update a _Product", done => { + const query = new Parse.Query("_Product"); query .first() .then(function (product) { if (!product) { - return Promise.reject(new Error('Product should be found')); + return Promise.reject(new Error("Product should be found")); } - product.set('title', 'a new title'); + product.set("title", "a new title"); return product.save(); }) .then(function (productAgain) { - expect(productAgain.get('downloadName')).toEqual(productAgain.get('download').name()); - expect(productAgain.get('title')).toEqual('a new title'); + expect(productAgain.get("downloadName")).toEqual( + productAgain.get("download").name() + ); + expect(productAgain.get("title")).toEqual("a new title"); done(); }) .catch(function (err) { @@ -186,24 +190,24 @@ describe('test validate_receipt endpoint', () => { }); }); - it('should not be able to remove a require key in a _Product', done => { - const query = new Parse.Query('_Product'); + it("should not be able to remove a require key in a _Product", done => { + const query = new Parse.Query("_Product"); query .first() .then(function (product) { if (!product) { - return Promise.reject(new Error('Product should be found')); + return Promise.reject(new Error("Product should be found")); } - product.unset('title'); + product.unset("title"); return product.save(); }) .then(function () { - fail('Should not succeed'); + fail("Should not succeed"); done(); }) .catch(function (err) { expect(err.code).toEqual(Parse.Error.INCORRECT_TYPE); - expect(err.message).toEqual('title is required.'); + expect(err.message).toEqual("title is required."); done(); }); }); diff --git a/spec/PushController.spec.js b/spec/PushController.spec.js index b914ceac84..c6b858bcaf 100644 --- a/spec/PushController.spec.js +++ b/spec/PushController.spec.js @@ -1,8 +1,9 @@ -'use strict'; -const PushController = require('../lib/Controllers/PushController').PushController; -const StatusHandler = require('../lib/StatusHandler'); -const Config = require('../lib/Config'); -const validatePushType = require('../lib/Push/utils').validatePushType; +"use strict"; +const PushController = + require("../lib/Controllers/PushController").PushController; +const StatusHandler = require("../lib/StatusHandler"); +const Config = require("../lib/Config"); +const validatePushType = require("../lib/Push/utils").validatePushType; const successfulTransmissions = function (body, installations) { const promises = installations.map(device => { @@ -18,7 +19,7 @@ const successfulTransmissions = function (body, installations) { const successfulIOS = function (body, installations) { const promises = installations.map(device => { return Promise.resolve({ - transmitted: device.deviceType == 'ios', + transmitted: device.deviceType == "ios", device: device, }); }); @@ -27,10 +28,10 @@ const successfulIOS = function (body, installations) { }; const pushCompleted = async pushId => { - const query = new Parse.Query('_PushStatus'); - query.equalTo('objectId', pushId); + const query = new Parse.Query("_PushStatus"); + query.equalTo("objectId", pushId); let result = await query.first({ useMasterKey: true }); - while (!(result && result.get('status') === 'succeeded')) { + while (!(result && result.get("status") === "succeeded")) { await jasmine.timeout(); result = await query.first({ useMasterKey: true }); } @@ -39,15 +40,17 @@ const pushCompleted = async pushId => { const sendPush = (body, where, config, auth, now) => { const pushController = new PushController(); return new Promise((resolve, reject) => { - pushController.sendPush(body, where, config, auth, resolve, now).catch(reject); + pushController + .sendPush(body, where, config, auth, resolve, now) + .catch(reject); }); }; -describe('PushController', () => { - it('can validate device type when no device type is set', done => { +describe("PushController", () => { + it("can validate device type when no device type is set", done => { // Make query condition const where = {}; - const validPushTypes = ['ios', 'android']; + const validPushTypes = ["ios", "android"]; expect(function () { validatePushType(where, validPushTypes); @@ -55,12 +58,12 @@ describe('PushController', () => { done(); }); - it('can validate device type when single valid device type is set', done => { + it("can validate device type when single valid device type is set", done => { // Make query condition const where = { - deviceType: 'ios', + deviceType: "ios", }; - const validPushTypes = ['ios', 'android']; + const validPushTypes = ["ios", "android"]; expect(function () { validatePushType(where, validPushTypes); @@ -68,14 +71,14 @@ describe('PushController', () => { done(); }); - it('can validate device type when multiple valid device types are set', done => { + it("can validate device type when multiple valid device types are set", done => { // Make query condition const where = { deviceType: { - $in: ['android', 'ios'], + $in: ["android", "ios"], }, }; - const validPushTypes = ['ios', 'android']; + const validPushTypes = ["ios", "android"]; expect(function () { validatePushType(where, validPushTypes); @@ -83,12 +86,12 @@ describe('PushController', () => { done(); }); - it('can throw on validateDeviceType when single invalid device type is set', done => { + it("can throw on validateDeviceType when single invalid device type is set", done => { // Make query condition const where = { - deviceType: 'osx', + deviceType: "osx", }; - const validPushTypes = ['ios', 'android']; + const validPushTypes = ["ios", "android"]; expect(function () { validatePushType(where, validPushTypes); @@ -96,9 +99,9 @@ describe('PushController', () => { done(); }); - it('can get expiration time in string format', done => { + it("can get expiration time in string format", done => { // Make mock request - const timeStr = '2015-03-19T22:05:08Z'; + const timeStr = "2015-03-19T22:05:08Z"; const body = { expiration_time: timeStr, }; @@ -108,7 +111,7 @@ describe('PushController', () => { done(); }); - it('can get expiration time in number format', done => { + it("can get expiration time in number format", done => { // Make mock request const timeNumber = 1426802708; const body = { @@ -120,10 +123,10 @@ describe('PushController', () => { done(); }); - it('can throw on getExpirationTime in invalid format', done => { + it("can throw on getExpirationTime in invalid format", done => { // Make mock request const body = { - expiration_time: 'abcd', + expiration_time: "abcd", }; expect(function () { @@ -132,9 +135,9 @@ describe('PushController', () => { done(); }); - it('can get push time in string format', done => { + it("can get push time in string format", done => { // Make mock request - const timeStr = '2015-03-19T22:05:08Z'; + const timeStr = "2015-03-19T22:05:08Z"; const body = { push_time: timeStr, }; @@ -144,7 +147,7 @@ describe('PushController', () => { done(); }); - it('can get push time in number format', done => { + it("can get push time in number format", done => { // Make mock request const timeNumber = 1426802708; const body = { @@ -156,10 +159,10 @@ describe('PushController', () => { done(); }); - it('can throw on getPushTime in invalid format', done => { + it("can throw on getPushTime in invalid format", done => { // Make mock request const body = { - push_time: 'abcd', + push_time: "abcd", }; expect(function () { @@ -168,402 +171,451 @@ describe('PushController', () => { done(); }); - it_id('01e3e1b8-fad2-4249-b664-5a3efaab8cb1')(it)('properly increment badges', async () => { - const pushAdapter = { - send: function (body, installations) { - const badge = body.data.badge; - installations.forEach(installation => { - expect(installation.badge).toEqual(badge); - expect(installation.originalBadge + 1).toEqual(installation.badge); - }); - return successfulTransmissions(body, installations); - }, - getValidPushTypes: function () { - return ['ios', 'android']; - }, - }; - await reconfigureServer({ - push: { adapter: pushAdapter }, - }); - const payload = { - data: { - alert: 'Hello World!', - badge: 'Increment', - }, - }; - const installations = []; - while (installations.length != 10) { - const installation = new Parse.Object('_Installation'); - installation.set('installationId', 'installation_' + installations.length); - installation.set('deviceToken', 'device_token_' + installations.length); - installation.set('badge', installations.length); - installation.set('originalBadge', installations.length); - installation.set('deviceType', 'ios'); - installations.push(installation); - } - - while (installations.length != 15) { - const installation = new Parse.Object('_Installation'); - installation.set('installationId', 'installation_' + installations.length); - installation.set('deviceToken', 'device_token_' + installations.length); - installation.set('badge', installations.length); - installation.set('originalBadge', installations.length); - installation.set('deviceType', 'android'); - installations.push(installation); - } - const config = Config.get(Parse.applicationId); - const auth = { - isMaster: true, - }; - await Parse.Object.saveAll(installations); - const pushStatusId = await sendPush(payload, {}, config, auth); - await pushCompleted(pushStatusId); - - // Check we actually sent 15 pushes. - const pushStatus = await Parse.Push.getPushStatus(pushStatusId); - expect(pushStatus.get('numSent')).toBe(15); - - // Check that the installations were actually updated. - const query = new Parse.Query('_Installation'); - const results = await query.find({ useMasterKey: true }); - expect(results.length).toBe(15); - for (let i = 0; i < 15; i++) { - const installation = results[i]; - expect(installation.get('badge')).toBe(parseInt(installation.get('originalBadge')) + 1); - } - }); - - it_id('14afcedf-e65d-41cd-981e-07f32df84c14')(it)('properly increment badges by more than 1', async () => { - const pushAdapter = { - send: function (body, installations) { - const badge = body.data.badge; - installations.forEach(installation => { - expect(installation.badge).toEqual(badge); - expect(installation.originalBadge + 3).toEqual(installation.badge); - }); - return successfulTransmissions(body, installations); - }, - getValidPushTypes: function () { - return ['ios', 'android']; - }, - }; - await reconfigureServer({ - push: { adapter: pushAdapter }, - }); - const payload = { - data: { - alert: 'Hello World!', - badge: { __op: 'Increment', amount: 3 }, - }, - }; - const installations = []; - while (installations.length != 10) { - const installation = new Parse.Object('_Installation'); - installation.set('installationId', 'installation_' + installations.length); - installation.set('deviceToken', 'device_token_' + installations.length); - installation.set('badge', installations.length); - installation.set('originalBadge', installations.length); - installation.set('deviceType', 'ios'); - installations.push(installation); - } + it_id("01e3e1b8-fad2-4249-b664-5a3efaab8cb1")(it)( + "properly increment badges", + async () => { + const pushAdapter = { + send: function (body, installations) { + const badge = body.data.badge; + installations.forEach(installation => { + expect(installation.badge).toEqual(badge); + expect(installation.originalBadge + 1).toEqual(installation.badge); + }); + return successfulTransmissions(body, installations); + }, + getValidPushTypes: function () { + return ["ios", "android"]; + }, + }; + await reconfigureServer({ + push: { adapter: pushAdapter }, + }); + const payload = { + data: { + alert: "Hello World!", + badge: "Increment", + }, + }; + const installations = []; + while (installations.length != 10) { + const installation = new Parse.Object("_Installation"); + installation.set( + "installationId", + "installation_" + installations.length + ); + installation.set("deviceToken", "device_token_" + installations.length); + installation.set("badge", installations.length); + installation.set("originalBadge", installations.length); + installation.set("deviceType", "ios"); + installations.push(installation); + } - while (installations.length != 15) { - const installation = new Parse.Object('_Installation'); - installation.set('installationId', 'installation_' + installations.length); - installation.set('deviceToken', 'device_token_' + installations.length); - installation.set('badge', installations.length); - installation.set('originalBadge', installations.length); - installation.set('deviceType', 'android'); - installations.push(installation); - } - const config = Config.get(Parse.applicationId); - const auth = { - isMaster: true, - }; - await Parse.Object.saveAll(installations); - const pushStatusId = await sendPush(payload, {}, config, auth); - await pushCompleted(pushStatusId); - const pushStatus = await Parse.Push.getPushStatus(pushStatusId); - expect(pushStatus.get('numSent')).toBe(15); - // Check that the installations were actually updated. - const query = new Parse.Query('_Installation'); - const results = await query.find({ useMasterKey: true }); - expect(results.length).toBe(15); - for (let i = 0; i < 15; i++) { - const installation = results[i]; - expect(installation.get('badge')).toBe(parseInt(installation.get('originalBadge')) + 3); - } - }); + while (installations.length != 15) { + const installation = new Parse.Object("_Installation"); + installation.set( + "installationId", + "installation_" + installations.length + ); + installation.set("deviceToken", "device_token_" + installations.length); + installation.set("badge", installations.length); + installation.set("originalBadge", installations.length); + installation.set("deviceType", "android"); + installations.push(installation); + } + const config = Config.get(Parse.applicationId); + const auth = { + isMaster: true, + }; + await Parse.Object.saveAll(installations); + const pushStatusId = await sendPush(payload, {}, config, auth); + await pushCompleted(pushStatusId); - it_id('758dd579-aa91-4010-9033-8d48d3463644')(it)('properly set badges to 1', async () => { - const pushAdapter = { - send: function (body, installations) { - const badge = body.data.badge; - installations.forEach(installation => { - expect(installation.badge).toEqual(badge); - expect(1).toEqual(installation.badge); - }); - return successfulTransmissions(body, installations); - }, - getValidPushTypes: function () { - return ['ios']; - }, - }; - await reconfigureServer({ - push: { adapter: pushAdapter }, - }); - const payload = { - data: { - alert: 'Hello World!', - badge: 1, - }, - }; - const installations = []; - while (installations.length != 10) { - const installation = new Parse.Object('_Installation'); - installation.set('installationId', 'installation_' + installations.length); - installation.set('deviceToken', 'device_token_' + installations.length); - installation.set('badge', installations.length); - installation.set('originalBadge', installations.length); - installation.set('deviceType', 'ios'); - installations.push(installation); + // Check we actually sent 15 pushes. + const pushStatus = await Parse.Push.getPushStatus(pushStatusId); + expect(pushStatus.get("numSent")).toBe(15); + + // Check that the installations were actually updated. + const query = new Parse.Query("_Installation"); + const results = await query.find({ useMasterKey: true }); + expect(results.length).toBe(15); + for (let i = 0; i < 15; i++) { + const installation = results[i]; + expect(installation.get("badge")).toBe( + parseInt(installation.get("originalBadge")) + 1 + ); + } } + ); - const config = Config.get(Parse.applicationId); - const auth = { - isMaster: true, - }; - await Parse.Object.saveAll(installations); - const pushStatusId = await sendPush(payload, {}, config, auth); - await pushCompleted(pushStatusId); - const pushStatus = await Parse.Push.getPushStatus(pushStatusId); - expect(pushStatus.get('numSent')).toBe(10); + it_id("14afcedf-e65d-41cd-981e-07f32df84c14")(it)( + "properly increment badges by more than 1", + async () => { + const pushAdapter = { + send: function (body, installations) { + const badge = body.data.badge; + installations.forEach(installation => { + expect(installation.badge).toEqual(badge); + expect(installation.originalBadge + 3).toEqual(installation.badge); + }); + return successfulTransmissions(body, installations); + }, + getValidPushTypes: function () { + return ["ios", "android"]; + }, + }; + await reconfigureServer({ + push: { adapter: pushAdapter }, + }); + const payload = { + data: { + alert: "Hello World!", + badge: { __op: "Increment", amount: 3 }, + }, + }; + const installations = []; + while (installations.length != 10) { + const installation = new Parse.Object("_Installation"); + installation.set( + "installationId", + "installation_" + installations.length + ); + installation.set("deviceToken", "device_token_" + installations.length); + installation.set("badge", installations.length); + installation.set("originalBadge", installations.length); + installation.set("deviceType", "ios"); + installations.push(installation); + } - // Check that the installations were actually updated. - const query = new Parse.Query('_Installation'); - const results = await query.find({ useMasterKey: true }); - expect(results.length).toBe(10); - for (let i = 0; i < 10; i++) { - const installation = results[i]; - expect(installation.get('badge')).toBe(1); + while (installations.length != 15) { + const installation = new Parse.Object("_Installation"); + installation.set( + "installationId", + "installation_" + installations.length + ); + installation.set("deviceToken", "device_token_" + installations.length); + installation.set("badge", installations.length); + installation.set("originalBadge", installations.length); + installation.set("deviceType", "android"); + installations.push(installation); + } + const config = Config.get(Parse.applicationId); + const auth = { + isMaster: true, + }; + await Parse.Object.saveAll(installations); + const pushStatusId = await sendPush(payload, {}, config, auth); + await pushCompleted(pushStatusId); + const pushStatus = await Parse.Push.getPushStatus(pushStatusId); + expect(pushStatus.get("numSent")).toBe(15); + // Check that the installations were actually updated. + const query = new Parse.Query("_Installation"); + const results = await query.find({ useMasterKey: true }); + expect(results.length).toBe(15); + for (let i = 0; i < 15; i++) { + const installation = results[i]; + expect(installation.get("badge")).toBe( + parseInt(installation.get("originalBadge")) + 3 + ); + } } - }); + ); - it_id('75c39ae3-06ac-4354-b321-931e81c5a927')(it)('properly set badges to 1 with complex query #2903 #3022', async () => { - const payload = { - data: { - alert: 'Hello World!', - badge: 1, - }, - }; - const installations = []; - while (installations.length != 10) { - const installation = new Parse.Object('_Installation'); - installation.set('installationId', 'installation_' + installations.length); - installation.set('deviceToken', 'device_token_' + installations.length); - installation.set('badge', installations.length); - installation.set('originalBadge', installations.length); - installation.set('deviceType', 'ios'); - installations.push(installation); - } - let matchedInstallationsCount = 0; - const pushAdapter = { - send: function (body, installations) { - matchedInstallationsCount += installations.length; - const badge = body.data.badge; - installations.forEach(installation => { - expect(installation.badge).toEqual(badge); - expect(1).toEqual(installation.badge); - }); - return successfulTransmissions(body, installations); - }, - getValidPushTypes: function () { - return ['ios']; - }, - }; - await reconfigureServer({ - push: { adapter: pushAdapter }, - }); - const config = Config.get(Parse.applicationId); - const auth = { - isMaster: true, - }; - await Parse.Object.saveAll(installations); - const objectIds = installations.map(installation => { - return installation.id; - }); - const where = { - objectId: { $in: objectIds.slice(0, 5) }, - }; - const pushStatusId = await sendPush(payload, where, config, auth); - await pushCompleted(pushStatusId); - expect(matchedInstallationsCount).toBe(5); - const query = new Parse.Query(Parse.Installation); - query.equalTo('badge', 1); - const results = await query.find({ useMasterKey: true }); - expect(results.length).toBe(5); - }); - - it_id('667f31c0-b458-4f61-ab57-668c04e3cc0b')(it)('properly creates _PushStatus', async () => { - const pushStatusAfterSave = { - handler: function () {}, - }; - const spy = spyOn(pushStatusAfterSave, 'handler').and.callThrough(); - Parse.Cloud.afterSave('_PushStatus', pushStatusAfterSave.handler); - const installations = []; - while (installations.length != 10) { - const installation = new Parse.Object('_Installation'); - installation.set('installationId', 'installation_' + installations.length); - installation.set('deviceToken', 'device_token_' + installations.length); - installation.set('badge', installations.length); - installation.set('originalBadge', installations.length); - installation.set('deviceType', 'ios'); - installations.push(installation); - } + it_id("758dd579-aa91-4010-9033-8d48d3463644")(it)( + "properly set badges to 1", + async () => { + const pushAdapter = { + send: function (body, installations) { + const badge = body.data.badge; + installations.forEach(installation => { + expect(installation.badge).toEqual(badge); + expect(1).toEqual(installation.badge); + }); + return successfulTransmissions(body, installations); + }, + getValidPushTypes: function () { + return ["ios"]; + }, + }; + await reconfigureServer({ + push: { adapter: pushAdapter }, + }); + const payload = { + data: { + alert: "Hello World!", + badge: 1, + }, + }; + const installations = []; + while (installations.length != 10) { + const installation = new Parse.Object("_Installation"); + installation.set( + "installationId", + "installation_" + installations.length + ); + installation.set("deviceToken", "device_token_" + installations.length); + installation.set("badge", installations.length); + installation.set("originalBadge", installations.length); + installation.set("deviceType", "ios"); + installations.push(installation); + } - while (installations.length != 15) { - const installation = new Parse.Object('_Installation'); - installation.set('installationId', 'installation_' + installations.length); - installation.set('deviceToken', 'device_token_' + installations.length); - installation.set('deviceType', 'android'); - installations.push(installation); + const config = Config.get(Parse.applicationId); + const auth = { + isMaster: true, + }; + await Parse.Object.saveAll(installations); + const pushStatusId = await sendPush(payload, {}, config, auth); + await pushCompleted(pushStatusId); + const pushStatus = await Parse.Push.getPushStatus(pushStatusId); + expect(pushStatus.get("numSent")).toBe(10); + + // Check that the installations were actually updated. + const query = new Parse.Query("_Installation"); + const results = await query.find({ useMasterKey: true }); + expect(results.length).toBe(10); + for (let i = 0; i < 10; i++) { + const installation = results[i]; + expect(installation.get("badge")).toBe(1); + } } - const payload = { - data: { - alert: 'Hello World!', - badge: 1, - }, - }; + ); - const pushAdapter = { - send: function (body, installations) { - return successfulIOS(body, installations); - }, - getValidPushTypes: function () { - return ['ios']; - }, - }; - await reconfigureServer({ - push: { adapter: pushAdapter }, - }); - const config = Config.get(Parse.applicationId); - const auth = { - isMaster: true, - }; - await Parse.Object.saveAll(installations); - const pushStatusId = await sendPush(payload, {}, config, auth); - await pushCompleted(pushStatusId); - const result = await Parse.Push.getPushStatus(pushStatusId); - expect(result.createdAt instanceof Date).toBe(true); - expect(result.updatedAt instanceof Date).toBe(true); - expect(result.id.length).toBe(10); - expect(result.get('source')).toEqual('rest'); - expect(result.get('query')).toEqual(JSON.stringify({})); - expect(typeof result.get('payload')).toEqual('string'); - expect(JSON.parse(result.get('payload'))).toEqual(payload.data); - expect(result.get('status')).toEqual('succeeded'); - expect(result.get('numSent')).toEqual(10); - expect(result.get('sentPerType')).toEqual({ - ios: 10, // 10 ios - }); - expect(result.get('numFailed')).toEqual(5); - expect(result.get('failedPerType')).toEqual({ - android: 5, // android - }); - try { - // Try to get it without masterKey - const query = new Parse.Query('_PushStatus'); - await query.find(); - fail(); - } catch (error) { - expect(error.code).toBe(119); + it_id("75c39ae3-06ac-4354-b321-931e81c5a927")(it)( + "properly set badges to 1 with complex query #2903 #3022", + async () => { + const payload = { + data: { + alert: "Hello World!", + badge: 1, + }, + }; + const installations = []; + while (installations.length != 10) { + const installation = new Parse.Object("_Installation"); + installation.set( + "installationId", + "installation_" + installations.length + ); + installation.set("deviceToken", "device_token_" + installations.length); + installation.set("badge", installations.length); + installation.set("originalBadge", installations.length); + installation.set("deviceType", "ios"); + installations.push(installation); + } + let matchedInstallationsCount = 0; + const pushAdapter = { + send: function (body, installations) { + matchedInstallationsCount += installations.length; + const badge = body.data.badge; + installations.forEach(installation => { + expect(installation.badge).toEqual(badge); + expect(1).toEqual(installation.badge); + }); + return successfulTransmissions(body, installations); + }, + getValidPushTypes: function () { + return ["ios"]; + }, + }; + await reconfigureServer({ + push: { adapter: pushAdapter }, + }); + const config = Config.get(Parse.applicationId); + const auth = { + isMaster: true, + }; + await Parse.Object.saveAll(installations); + const objectIds = installations.map(installation => { + return installation.id; + }); + const where = { + objectId: { $in: objectIds.slice(0, 5) }, + }; + const pushStatusId = await sendPush(payload, where, config, auth); + await pushCompleted(pushStatusId); + expect(matchedInstallationsCount).toBe(5); + const query = new Parse.Query(Parse.Installation); + query.equalTo("badge", 1); + const results = await query.find({ useMasterKey: true }); + expect(results.length).toBe(5); } + ); - function getPushStatus(callIndex) { - return spy.calls.all()[callIndex].args[0].object; - } - expect(spy).toHaveBeenCalled(); - expect(spy.calls.count()).toBe(4); - const allCalls = spy.calls.all(); - let pendingCount = 0; - let runningCount = 0; - let succeedCount = 0; - allCalls.forEach((call, index) => { - expect(call.args.length).toBe(1); - const object = call.args[0].object; - expect(object instanceof Parse.Object).toBe(true); - const pushStatus = getPushStatus(index); - if (pushStatus.get('status') === 'pending') { - pendingCount += 1; + it_id("667f31c0-b458-4f61-ab57-668c04e3cc0b")(it)( + "properly creates _PushStatus", + async () => { + const pushStatusAfterSave = { + handler: function () {}, + }; + const spy = spyOn(pushStatusAfterSave, "handler").and.callThrough(); + Parse.Cloud.afterSave("_PushStatus", pushStatusAfterSave.handler); + const installations = []; + while (installations.length != 10) { + const installation = new Parse.Object("_Installation"); + installation.set( + "installationId", + "installation_" + installations.length + ); + installation.set("deviceToken", "device_token_" + installations.length); + installation.set("badge", installations.length); + installation.set("originalBadge", installations.length); + installation.set("deviceType", "ios"); + installations.push(installation); } - if (pushStatus.get('status') === 'running') { - runningCount += 1; + + while (installations.length != 15) { + const installation = new Parse.Object("_Installation"); + installation.set( + "installationId", + "installation_" + installations.length + ); + installation.set("deviceToken", "device_token_" + installations.length); + installation.set("deviceType", "android"); + installations.push(installation); } - if (pushStatus.get('status') === 'succeeded') { - succeedCount += 1; + const payload = { + data: { + alert: "Hello World!", + badge: 1, + }, + }; + + const pushAdapter = { + send: function (body, installations) { + return successfulIOS(body, installations); + }, + getValidPushTypes: function () { + return ["ios"]; + }, + }; + await reconfigureServer({ + push: { adapter: pushAdapter }, + }); + const config = Config.get(Parse.applicationId); + const auth = { + isMaster: true, + }; + await Parse.Object.saveAll(installations); + const pushStatusId = await sendPush(payload, {}, config, auth); + await pushCompleted(pushStatusId); + const result = await Parse.Push.getPushStatus(pushStatusId); + expect(result.createdAt instanceof Date).toBe(true); + expect(result.updatedAt instanceof Date).toBe(true); + expect(result.id.length).toBe(10); + expect(result.get("source")).toEqual("rest"); + expect(result.get("query")).toEqual(JSON.stringify({})); + expect(typeof result.get("payload")).toEqual("string"); + expect(JSON.parse(result.get("payload"))).toEqual(payload.data); + expect(result.get("status")).toEqual("succeeded"); + expect(result.get("numSent")).toEqual(10); + expect(result.get("sentPerType")).toEqual({ + ios: 10, // 10 ios + }); + expect(result.get("numFailed")).toEqual(5); + expect(result.get("failedPerType")).toEqual({ + android: 5, // android + }); + try { + // Try to get it without masterKey + const query = new Parse.Query("_PushStatus"); + await query.find(); + fail(); + } catch (error) { + expect(error.code).toBe(119); } - if (pushStatus.get('status') === 'running' && pushStatus.get('numSent') > 0) { - expect(pushStatus.get('numSent')).toBe(10); - expect(pushStatus.get('numFailed')).toBe(5); - expect(pushStatus.get('failedPerType')).toEqual({ - android: 5, - }); - expect(pushStatus.get('sentPerType')).toEqual({ - ios: 10, - }); + + function getPushStatus(callIndex) { + return spy.calls.all()[callIndex].args[0].object; } - }); - expect(pendingCount).toBe(1); - expect(runningCount).toBe(2); - expect(succeedCount).toBe(1); - }); + expect(spy).toHaveBeenCalled(); + expect(spy.calls.count()).toBe(4); + const allCalls = spy.calls.all(); + let pendingCount = 0; + let runningCount = 0; + let succeedCount = 0; + allCalls.forEach((call, index) => { + expect(call.args.length).toBe(1); + const object = call.args[0].object; + expect(object instanceof Parse.Object).toBe(true); + const pushStatus = getPushStatus(index); + if (pushStatus.get("status") === "pending") { + pendingCount += 1; + } + if (pushStatus.get("status") === "running") { + runningCount += 1; + } + if (pushStatus.get("status") === "succeeded") { + succeedCount += 1; + } + if ( + pushStatus.get("status") === "running" && + pushStatus.get("numSent") > 0 + ) { + expect(pushStatus.get("numSent")).toBe(10); + expect(pushStatus.get("numFailed")).toBe(5); + expect(pushStatus.get("failedPerType")).toEqual({ + android: 5, + }); + expect(pushStatus.get("sentPerType")).toEqual({ + ios: 10, + }); + } + }); + expect(pendingCount).toBe(1); + expect(runningCount).toBe(2); + expect(succeedCount).toBe(1); + } + ); - it_id('30e0591a-56de-4720-8c60-7d72291b532a')(it)('properly creates _PushStatus without serverURL', async () => { - const pushStatusAfterSave = { - handler: function () {}, - }; - Parse.Cloud.afterSave('_PushStatus', pushStatusAfterSave.handler); - const installation = new Parse.Object('_Installation'); - installation.set('installationId', 'installation'); - installation.set('deviceToken', 'device_token'); - installation.set('badge', 0); - installation.set('originalBadge', 0); - installation.set('deviceType', 'ios'); + it_id("30e0591a-56de-4720-8c60-7d72291b532a")(it)( + "properly creates _PushStatus without serverURL", + async () => { + const pushStatusAfterSave = { + handler: function () {}, + }; + Parse.Cloud.afterSave("_PushStatus", pushStatusAfterSave.handler); + const installation = new Parse.Object("_Installation"); + installation.set("installationId", "installation"); + installation.set("deviceToken", "device_token"); + installation.set("badge", 0); + installation.set("originalBadge", 0); + installation.set("deviceType", "ios"); - const payload = { - data: { - alert: 'Hello World!', - badge: 1, - }, - }; + const payload = { + data: { + alert: "Hello World!", + badge: 1, + }, + }; - const pushAdapter = { - send: function (body, installations) { - return successfulIOS(body, installations); - }, - getValidPushTypes: function () { - return ['ios']; - }, - }; - await installation.save(); - await reconfigureServer({ - serverURL: 'http://localhost:8378/', // server with borked URL - push: { adapter: pushAdapter }, - }); - const config = Config.get(Parse.applicationId); - const auth = { - isMaster: true, - }; - const pushStatusId = await sendPush(payload, {}, config, auth); - // it is enqueued so it can take time - await jasmine.timeout(1000); - Parse.serverURL = 'http://localhost:8378/1'; // GOOD url - const result = await Parse.Push.getPushStatus(pushStatusId); - expect(result).toBeDefined(); - await pushCompleted(pushStatusId); - }); + const pushAdapter = { + send: function (body, installations) { + return successfulIOS(body, installations); + }, + getValidPushTypes: function () { + return ["ios"]; + }, + }; + await installation.save(); + await reconfigureServer({ + serverURL: "http://localhost:8378/", // server with borked URL + push: { adapter: pushAdapter }, + }); + const config = Config.get(Parse.applicationId); + const auth = { + isMaster: true, + }; + const pushStatusId = await sendPush(payload, {}, config, auth); + // it is enqueued so it can take time + await jasmine.timeout(1000); + Parse.serverURL = "http://localhost:8378/1"; // GOOD url + const result = await Parse.Push.getPushStatus(pushStatusId); + expect(result).toBeDefined(); + await pushCompleted(pushStatusId); + } + ); - it('should properly report failures in _PushStatus', async () => { + it("should properly report failures in _PushStatus", async () => { const pushAdapter = { send: function (body, installations) { return installations.map(installation => { @@ -573,7 +625,7 @@ describe('PushController', () => { }); }, getValidPushTypes: function () { - return ['ios']; + return ["ios"]; }, }; await reconfigureServer({ @@ -582,12 +634,12 @@ describe('PushController', () => { // $ins is invalid query const where = { channels: { - $ins: ['Giants', 'Mets'], + $ins: ["Giants", "Mets"], }, }; const payload = { data: { - alert: 'Hello World!', + alert: "Hello World!", badge: 1, }, }; @@ -600,68 +652,74 @@ describe('PushController', () => { await pushController.sendPush(payload, where, config, auth); fail(); } catch (e) { - const query = new Parse.Query('_PushStatus'); + const query = new Parse.Query("_PushStatus"); let results = await query.find({ useMasterKey: true }); while (results.length === 0) { results = await query.find({ useMasterKey: true }); } expect(results.length).toBe(1); const pushStatus = results[0]; - expect(pushStatus.get('status')).toBe('failed'); + expect(pushStatus.get("status")).toBe("failed"); } }); - it_id('53551fc3-b975-4774-92e6-7e5f3c05e105')(it)('should support full RESTQuery for increment', async () => { - const payload = { - data: { - alert: 'Hello World!', - badge: 'Increment', - }, - }; + it_id("53551fc3-b975-4774-92e6-7e5f3c05e105")(it)( + "should support full RESTQuery for increment", + async () => { + const payload = { + data: { + alert: "Hello World!", + badge: "Increment", + }, + }; - const pushAdapter = { - send: function (body, installations) { - return successfulTransmissions(body, installations); - }, - getValidPushTypes: function () { - return ['ios']; - }, - }; - await reconfigureServer({ - push: { adapter: pushAdapter }, - }); - const config = Config.get(Parse.applicationId); - const auth = { - isMaster: true, - }; + const pushAdapter = { + send: function (body, installations) { + return successfulTransmissions(body, installations); + }, + getValidPushTypes: function () { + return ["ios"]; + }, + }; + await reconfigureServer({ + push: { adapter: pushAdapter }, + }); + const config = Config.get(Parse.applicationId); + const auth = { + isMaster: true, + }; - const where = { - deviceToken: { - $in: ['device_token_0', 'device_token_1', 'device_token_2'], - }, - }; - const installations = []; - while (installations.length != 5) { - const installation = new Parse.Object('_Installation'); - installation.set('installationId', 'installation_' + installations.length); - installation.set('deviceToken', 'device_token_' + installations.length); - installation.set('badge', installations.length); - installation.set('originalBadge', installations.length); - installation.set('deviceType', 'ios'); - installations.push(installation); + const where = { + deviceToken: { + $in: ["device_token_0", "device_token_1", "device_token_2"], + }, + }; + const installations = []; + while (installations.length != 5) { + const installation = new Parse.Object("_Installation"); + installation.set( + "installationId", + "installation_" + installations.length + ); + installation.set("deviceToken", "device_token_" + installations.length); + installation.set("badge", installations.length); + installation.set("originalBadge", installations.length); + installation.set("deviceType", "ios"); + installations.push(installation); + } + await Parse.Object.saveAll(installations); + const pushStatusId = await sendPush(payload, where, config, auth); + await pushCompleted(pushStatusId); + const pushStatus = await Parse.Push.getPushStatus(pushStatusId); + expect(pushStatus.get("numSent")).toBe(3); } - await Parse.Object.saveAll(installations); - const pushStatusId = await sendPush(payload, where, config, auth); - await pushCompleted(pushStatusId); - const pushStatus = await Parse.Push.getPushStatus(pushStatusId); - expect(pushStatus.get('numSent')).toBe(3); - }); + ); - it('should support object type for alert', async () => { + it("should support object type for alert", async () => { const payload = { data: { alert: { - 'loc-key': 'hello_world', + "loc-key": "hello_world", }, }, }; @@ -671,7 +729,7 @@ describe('PushController', () => { return successfulTransmissions(body, installations); }, getValidPushTypes: function () { - return ['ios']; + return ["ios"]; }, }; await reconfigureServer({ @@ -682,31 +740,34 @@ describe('PushController', () => { isMaster: true, }; const where = { - deviceType: 'ios', + deviceType: "ios", }; const installations = []; while (installations.length != 5) { - const installation = new Parse.Object('_Installation'); - installation.set('installationId', 'installation_' + installations.length); - installation.set('deviceToken', 'device_token_' + installations.length); - installation.set('badge', installations.length); - installation.set('originalBadge', installations.length); - installation.set('deviceType', 'ios'); + const installation = new Parse.Object("_Installation"); + installation.set( + "installationId", + "installation_" + installations.length + ); + installation.set("deviceToken", "device_token_" + installations.length); + installation.set("badge", installations.length); + installation.set("originalBadge", installations.length); + installation.set("deviceType", "ios"); installations.push(installation); } await Parse.Object.saveAll(installations); const pushStatusId = await sendPush(payload, where, config, auth); await pushCompleted(pushStatusId); const pushStatus = await Parse.Push.getPushStatus(pushStatusId); - expect(pushStatus.get('numSent')).toBe(5); + expect(pushStatus.get("numSent")).toBe(5); }); - it('should flatten', () => { + it("should flatten", () => { const res = StatusHandler.flatten([1, [2], [[3, 4], 5], [[[6]]]]); expect(res).toEqual([1, 2, 3, 4, 5, 6]); }); - it('properly transforms push time', () => { + it("properly transforms push time", () => { expect(PushController.getPushTime()).toBe(undefined); expect( PushController.getPushTime({ @@ -715,13 +776,13 @@ describe('PushController', () => { ).toEqual(new Date(1000 * 1000)); expect( PushController.getPushTime({ - push_time: '2017-01-01', + push_time: "2017-01-01", }).date - ).toEqual(new Date('2017-01-01')); + ).toEqual(new Date("2017-01-01")); expect(() => { PushController.getPushTime({ - push_time: 'gibberish-time', + push_time: "gibberish-time", }); }).toThrow(); expect(() => { @@ -732,37 +793,37 @@ describe('PushController', () => { expect( PushController.getPushTime({ - push_time: '2017-09-06T13:42:48.369Z', + push_time: "2017-09-06T13:42:48.369Z", }) ).toEqual({ - date: new Date('2017-09-06T13:42:48.369Z'), + date: new Date("2017-09-06T13:42:48.369Z"), isLocalTime: false, }); expect( PushController.getPushTime({ - push_time: '2007-04-05T12:30-02:00', + push_time: "2007-04-05T12:30-02:00", }) ).toEqual({ - date: new Date('2007-04-05T12:30-02:00'), + date: new Date("2007-04-05T12:30-02:00"), isLocalTime: false, }); expect( PushController.getPushTime({ - push_time: '2007-04-05T12:30', + push_time: "2007-04-05T12:30", }) ).toEqual({ - date: new Date('2007-04-05T12:30'), + date: new Date("2007-04-05T12:30"), isLocalTime: true, }); }); - it('should not schedule push when not configured', async () => { + it("should not schedule push when not configured", async () => { const pushAdapter = { send: function (body, installations) { return successfulTransmissions(body, installations); }, getValidPushTypes: function () { - return ['ios']; + return ["ios"]; }, }; await reconfigureServer({ @@ -775,32 +836,35 @@ describe('PushController', () => { const pushController = new PushController(); const payload = { data: { - alert: 'hello', + alert: "hello", }, push_time: new Date().getTime(), }; const installations = []; while (installations.length != 10) { - const installation = new Parse.Object('_Installation'); - installation.set('installationId', 'installation_' + installations.length); - installation.set('deviceToken', 'device_token_' + installations.length); - installation.set('badge', installations.length); - installation.set('originalBadge', installations.length); - installation.set('deviceType', 'ios'); + const installation = new Parse.Object("_Installation"); + installation.set( + "installationId", + "installation_" + installations.length + ); + installation.set("deviceToken", "device_token_" + installations.length); + installation.set("badge", installations.length); + installation.set("originalBadge", installations.length); + installation.set("deviceType", "ios"); installations.push(installation); } await Parse.Object.saveAll(installations); await pushController.sendPush(payload, {}, config, auth); await jasmine.timeout(1000); - const query = new Parse.Query('_PushStatus'); + const query = new Parse.Query("_PushStatus"); const results = await query.find({ useMasterKey: true }); expect(results.length).toBe(1); const pushStatus = results[0]; - expect(pushStatus.get('status')).not.toBe('scheduled'); + expect(pushStatus.get("status")).not.toBe("scheduled"); }); - it('should schedule push when configured', async () => { + it("should schedule push when configured", async () => { const auth = { isMaster: true, }; @@ -820,24 +884,27 @@ describe('PushController', () => { return Promise.all(promises); }, getValidPushTypes: function () { - return ['ios']; + return ["ios"]; }, }; const pushController = new PushController(); const payload = { data: { - alert: 'hello', + alert: "hello", }, push_time: new Date().getTime() / 1000, }; const installations = []; while (installations.length != 10) { - const installation = new Parse.Object('_Installation'); - installation.set('installationId', 'installation_' + installations.length); - installation.set('deviceToken', 'device_token_' + installations.length); - installation.set('badge', installations.length); - installation.set('originalBadge', installations.length); - installation.set('deviceType', 'ios'); + const installation = new Parse.Object("_Installation"); + installation.set( + "installationId", + "installation_" + installations.length + ); + installation.set("deviceToken", "device_token_" + installations.length); + installation.set("badge", installations.length); + installation.set("originalBadge", installations.length); + installation.set("deviceType", "ios"); installations.push(installation); } await reconfigureServer({ @@ -848,14 +915,14 @@ describe('PushController', () => { await Parse.Object.saveAll(installations); await pushController.sendPush(payload, {}, config, auth); await jasmine.timeout(1000); - const query = new Parse.Query('_PushStatus'); + const query = new Parse.Query("_PushStatus"); const results = await query.find({ useMasterKey: true }); expect(results.length).toBe(1); const pushStatus = results[0]; - expect(pushStatus.get('status')).toBe('scheduled'); + expect(pushStatus.get("status")).toBe("scheduled"); }); - it('should not enqueue push when device token is not set', async () => { + it("should not enqueue push when device token is not set", async () => { const auth = { isMaster: true, }; @@ -875,31 +942,37 @@ describe('PushController', () => { return Promise.all(promises); }, getValidPushTypes: function () { - return ['ios']; + return ["ios"]; }, }; const payload = { data: { - alert: 'hello', + alert: "hello", }, push_time: new Date().getTime() / 1000, }; const installations = []; while (installations.length != 5) { - const installation = new Parse.Object('_Installation'); - installation.set('installationId', 'installation_' + installations.length); - installation.set('deviceToken', 'device_token_' + installations.length); - installation.set('badge', installations.length); - installation.set('originalBadge', installations.length); - installation.set('deviceType', 'ios'); + const installation = new Parse.Object("_Installation"); + installation.set( + "installationId", + "installation_" + installations.length + ); + installation.set("deviceToken", "device_token_" + installations.length); + installation.set("badge", installations.length); + installation.set("originalBadge", installations.length); + installation.set("deviceType", "ios"); installations.push(installation); } while (installations.length != 15) { - const installation = new Parse.Object('_Installation'); - installation.set('installationId', 'installation_' + installations.length); - installation.set('badge', installations.length); - installation.set('originalBadge', installations.length); - installation.set('deviceType', 'ios'); + const installation = new Parse.Object("_Installation"); + installation.set( + "installationId", + "installation_" + installations.length + ); + installation.set("badge", installations.length); + installation.set("originalBadge", installations.length); + installation.set("deviceType", "ios"); installations.push(installation); } await reconfigureServer({ @@ -910,11 +983,11 @@ describe('PushController', () => { const pushStatusId = await sendPush(payload, {}, config, auth); await pushCompleted(pushStatusId); const pushStatus = await Parse.Push.getPushStatus(pushStatusId); - expect(pushStatus.get('numSent')).toBe(5); - expect(pushStatus.get('status')).toBe('succeeded'); + expect(pushStatus.get("numSent")).toBe(5); + expect(pushStatus.get("status")).toBe("succeeded"); }); - it('should not mark the _PushStatus as failed when audience has no deviceToken', async () => { + it("should not mark the _PushStatus as failed when audience has no deviceToken", async () => { const auth = { isMaster: true, }; @@ -934,22 +1007,25 @@ describe('PushController', () => { return Promise.all(promises); }, getValidPushTypes: function () { - return ['ios']; + return ["ios"]; }, }; const payload = { data: { - alert: 'hello', + alert: "hello", }, push_time: new Date().getTime() / 1000, }; const installations = []; while (installations.length != 5) { - const installation = new Parse.Object('_Installation'); - installation.set('installationId', 'installation_' + installations.length); - installation.set('badge', installations.length); - installation.set('originalBadge', installations.length); - installation.set('deviceType', 'ios'); + const installation = new Parse.Object("_Installation"); + installation.set( + "installationId", + "installation_" + installations.length + ); + installation.set("badge", installations.length); + installation.set("originalBadge", installations.length); + installation.set("deviceType", "ios"); installations.push(installation); } await reconfigureServer({ @@ -960,15 +1036,15 @@ describe('PushController', () => { const pushStatusId = await sendPush(payload, {}, config, auth); await pushCompleted(pushStatusId); const pushStatus = await Parse.Push.getPushStatus(pushStatusId); - expect(pushStatus.get('status')).toBe('succeeded'); + expect(pushStatus.get("status")).toBe("succeeded"); }); - it('should support localized payload data', async () => { + it("should support localized payload data", async () => { const payload = { data: { - alert: 'Hello!', - 'alert-fr': 'Bonjour', - 'alert-es': 'Ola', + alert: "Hello!", + "alert-fr": "Bonjour", + "alert-es": "Ola", }, }; const pushAdapter = { @@ -976,10 +1052,10 @@ describe('PushController', () => { return successfulTransmissions(body, installations); }, getValidPushTypes: function () { - return ['ios']; + return ["ios"]; }, }; - spyOn(pushAdapter, 'send').and.callThrough(); + spyOn(pushAdapter, "send").and.callThrough(); await reconfigureServer({ push: { adapter: pushAdapter }, }); @@ -988,21 +1064,24 @@ describe('PushController', () => { isMaster: true, }; const where = { - deviceType: 'ios', + deviceType: "ios", }; const installations = []; while (installations.length != 5) { - const installation = new Parse.Object('_Installation'); - installation.set('installationId', 'installation_' + installations.length); - installation.set('deviceToken', 'device_token_' + installations.length); - installation.set('badge', installations.length); - installation.set('originalBadge', installations.length); - installation.set('deviceType', 'ios'); + const installation = new Parse.Object("_Installation"); + installation.set( + "installationId", + "installation_" + installations.length + ); + installation.set("deviceToken", "device_token_" + installations.length); + installation.set("badge", installations.length); + installation.set("originalBadge", installations.length); + installation.set("deviceType", "ios"); installations.push(installation); } - installations[0].set('localeIdentifier', 'fr-CA'); - installations[1].set('localeIdentifier', 'fr-FR'); - installations[2].set('localeIdentifier', 'en-US'); + installations[0].set("localeIdentifier", "fr-CA"); + installations[1].set("localeIdentifier", "fr-FR"); + installations[2].set("localeIdentifier", "en-US"); await Parse.Object.saveAll(installations); const pushStatusId = await sendPush(payload, where, config, auth); @@ -1011,177 +1090,187 @@ describe('PushController', () => { expect(pushAdapter.send.calls.count()).toBe(2); const firstCall = pushAdapter.send.calls.first(); expect(firstCall.args[0].data).toEqual({ - alert: 'Hello!', + alert: "Hello!", }); expect(firstCall.args[1].length).toBe(3); // 3 installations const lastCall = pushAdapter.send.calls.mostRecent(); expect(lastCall.args[0].data).toEqual({ - alert: 'Bonjour', + alert: "Bonjour", }); expect(lastCall.args[1].length).toBe(2); // 2 installations // No installation is in es so only 1 call for fr, and another for default }); - it_id('ef2e5569-50c3-40c2-ab49-175cdbd5f024')(it)('should update audiences', async () => { - const pushAdapter = { - send: function (body, installations) { - return successfulTransmissions(body, installations); - }, - getValidPushTypes: function () { - return ['ios']; - }, - }; - spyOn(pushAdapter, 'send').and.callThrough(); - await reconfigureServer({ - push: { adapter: pushAdapter }, - }); - const config = Config.get(Parse.applicationId); - const auth = { - isMaster: true, - }; - let audienceId = null; - const now = new Date(); - let timesUsed = 0; - const where = { - deviceType: 'ios', - }; - const installations = []; - while (installations.length != 5) { - const installation = new Parse.Object('_Installation'); - installation.set('installationId', 'installation_' + installations.length); - installation.set('deviceToken', 'device_token_' + installations.length); - installation.set('badge', installations.length); - installation.set('originalBadge', installations.length); - installation.set('deviceType', 'ios'); - installations.push(installation); - } - await Parse.Object.saveAll(installations); - - // Create an audience - const query = new Parse.Query('_Audience'); - query.descending('createdAt'); - query.equalTo('query', JSON.stringify(where)); - const parseResults = results => { - if (results.length > 0) { - audienceId = results[0].id; - timesUsed = results[0].get('timesUsed'); - if (!isFinite(timesUsed)) { - timesUsed = 0; - } + it_id("ef2e5569-50c3-40c2-ab49-175cdbd5f024")(it)( + "should update audiences", + async () => { + const pushAdapter = { + send: function (body, installations) { + return successfulTransmissions(body, installations); + }, + getValidPushTypes: function () { + return ["ios"]; + }, + }; + spyOn(pushAdapter, "send").and.callThrough(); + await reconfigureServer({ + push: { adapter: pushAdapter }, + }); + const config = Config.get(Parse.applicationId); + const auth = { + isMaster: true, + }; + let audienceId = null; + const now = new Date(); + let timesUsed = 0; + const where = { + deviceType: "ios", + }; + const installations = []; + while (installations.length != 5) { + const installation = new Parse.Object("_Installation"); + installation.set( + "installationId", + "installation_" + installations.length + ); + installation.set("deviceToken", "device_token_" + installations.length); + installation.set("badge", installations.length); + installation.set("originalBadge", installations.length); + installation.set("deviceType", "ios"); + installations.push(installation); } - }; - const audience = new Parse.Object('_Audience'); - audience.set('name', 'testAudience'); - audience.set('query', JSON.stringify(where)); - await Parse.Object.saveAll(audience); - await query.find({ useMasterKey: true }).then(parseResults); - - const body = { - data: { alert: 'hello' }, - audience_id: audienceId, - }; - const pushStatusId = await sendPush(body, where, config, auth); - await pushCompleted(pushStatusId); - expect(pushAdapter.send.calls.count()).toBe(1); - const firstCall = pushAdapter.send.calls.first(); - expect(firstCall.args[0].data).toEqual({ - alert: 'hello', - }); - expect(firstCall.args[1].length).toBe(5); + await Parse.Object.saveAll(installations); + + // Create an audience + const query = new Parse.Query("_Audience"); + query.descending("createdAt"); + query.equalTo("query", JSON.stringify(where)); + const parseResults = results => { + if (results.length > 0) { + audienceId = results[0].id; + timesUsed = results[0].get("timesUsed"); + if (!isFinite(timesUsed)) { + timesUsed = 0; + } + } + }; + const audience = new Parse.Object("_Audience"); + audience.set("name", "testAudience"); + audience.set("query", JSON.stringify(where)); + await Parse.Object.saveAll(audience); + await query.find({ useMasterKey: true }).then(parseResults); + + const body = { + data: { alert: "hello" }, + audience_id: audienceId, + }; + const pushStatusId = await sendPush(body, where, config, auth); + await pushCompleted(pushStatusId); + expect(pushAdapter.send.calls.count()).toBe(1); + const firstCall = pushAdapter.send.calls.first(); + expect(firstCall.args[0].data).toEqual({ + alert: "hello", + }); + expect(firstCall.args[1].length).toBe(5); - // Get the audience we used above. - const audienceQuery = new Parse.Query('_Audience'); - audienceQuery.equalTo('objectId', audienceId); - const results = await audienceQuery.find({ useMasterKey: true }); + // Get the audience we used above. + const audienceQuery = new Parse.Query("_Audience"); + audienceQuery.equalTo("objectId", audienceId); + const results = await audienceQuery.find({ useMasterKey: true }); - expect(results[0].get('query')).toBe(JSON.stringify(where)); - expect(results[0].get('timesUsed')).toBe(timesUsed + 1); - expect(results[0].get('lastUsed')).not.toBeLessThan(now); - }); + expect(results[0].get("query")).toBe(JSON.stringify(where)); + expect(results[0].get("timesUsed")).toBe(timesUsed + 1); + expect(results[0].get("lastUsed")).not.toBeLessThan(now); + } + ); - describe('pushTimeHasTimezoneComponent', () => { - it('should be accurate', () => { - expect(PushController.pushTimeHasTimezoneComponent('2017-09-06T17:14:01.048Z')).toBe( - true, - 'UTC time' - ); - expect(PushController.pushTimeHasTimezoneComponent('2007-04-05T12:30-02:00')).toBe( - true, - 'Timezone offset' - ); - expect(PushController.pushTimeHasTimezoneComponent('2007-04-05T12:30:00.000Z-02:00')).toBe( - true, - 'Seconds + Milliseconds + Timezone offset' - ); + describe("pushTimeHasTimezoneComponent", () => { + it("should be accurate", () => { + expect( + PushController.pushTimeHasTimezoneComponent("2017-09-06T17:14:01.048Z") + ).toBe(true, "UTC time"); + expect( + PushController.pushTimeHasTimezoneComponent("2007-04-05T12:30-02:00") + ).toBe(true, "Timezone offset"); + expect( + PushController.pushTimeHasTimezoneComponent( + "2007-04-05T12:30:00.000Z-02:00" + ) + ).toBe(true, "Seconds + Milliseconds + Timezone offset"); - expect(PushController.pushTimeHasTimezoneComponent('2017-09-06T17:14:01.048')).toBe( + expect( + PushController.pushTimeHasTimezoneComponent("2017-09-06T17:14:01.048") + ).toBe(false, "No timezone"); + expect(PushController.pushTimeHasTimezoneComponent("2017-09-06")).toBe( false, - 'No timezone' + "YY-MM-DD" ); - expect(PushController.pushTimeHasTimezoneComponent('2017-09-06')).toBe(false, 'YY-MM-DD'); }); }); - describe('formatPushTime', () => { - it('should format as ISO string', () => { + describe("formatPushTime", () => { + it("should format as ISO string", () => { expect( PushController.formatPushTime({ - date: new Date('2017-09-06T17:14:01.048Z'), + date: new Date("2017-09-06T17:14:01.048Z"), isLocalTime: false, }) - ).toBe('2017-09-06T17:14:01.048Z', 'UTC time'); + ).toBe("2017-09-06T17:14:01.048Z", "UTC time"); expect( PushController.formatPushTime({ - date: new Date('2007-04-05T12:30-02:00'), + date: new Date("2007-04-05T12:30-02:00"), isLocalTime: false, }) - ).toBe('2007-04-05T14:30:00.000Z', 'Timezone offset'); + ).toBe("2007-04-05T14:30:00.000Z", "Timezone offset"); - const noTimezone = new Date('2017-09-06T17:14:01.048'); + const noTimezone = new Date("2017-09-06T17:14:01.048"); let expectedHour = 17 + noTimezone.getTimezoneOffset() / 60; - let day = '06'; + let day = "06"; if (expectedHour >= 24) { expectedHour = expectedHour - 24; - day = '07'; + day = "07"; } expect( PushController.formatPushTime({ date: noTimezone, isLocalTime: true, }) - ).toBe(`2017-09-${day}T${expectedHour.toString().padStart(2, '0')}:14:01.048`, 'No timezone'); + ).toBe( + `2017-09-${day}T${expectedHour.toString().padStart(2, "0")}:14:01.048`, + "No timezone" + ); expect( PushController.formatPushTime({ - date: new Date('2017-09-06'), + date: new Date("2017-09-06"), isLocalTime: true, }) - ).toBe('2017-09-06T00:00:00.000', 'YY-MM-DD'); + ).toBe("2017-09-06T00:00:00.000", "YY-MM-DD"); }); }); - describe('Scheduling pushes in local time', () => { - it('should preserve the push time', async () => { + describe("Scheduling pushes in local time", () => { + it("should preserve the push time", async () => { const auth = { isMaster: true }; const pushAdapter = { send(body, installations) { return successfulTransmissions(body, installations); }, getValidPushTypes() { - return ['ios']; + return ["ios"]; }, }; - const pushTime = '2017-09-06T17:14:01.048'; + const pushTime = "2017-09-06T17:14:01.048"; let expectedHour = 17 + new Date(pushTime).getTimezoneOffset() / 60; - let day = '06'; + let day = "06"; if (expectedHour >= 24) { expectedHour = expectedHour - 24; - day = '07'; + day = "07"; } const payload = { data: { - alert: 'Hello World!', - badge: 'Increment', + alert: "Hello World!", + badge: "Increment", }, push_time: pushTime, }; @@ -1192,14 +1281,14 @@ describe('PushController', () => { const config = Config.get(Parse.applicationId); const pushStatusId = await sendPush(payload, {}, config, auth); const pushStatus = await Parse.Push.getPushStatus(pushStatusId); - expect(pushStatus.get('status')).toBe('scheduled'); - expect(pushStatus.get('pushTime')).toBe( - `2017-09-${day}T${expectedHour.toString().padStart(2, '0')}:14:01.048` + expect(pushStatus.get("status")).toBe("scheduled"); + expect(pushStatus.get("pushTime")).toBe( + `2017-09-${day}T${expectedHour.toString().padStart(2, "0")}:14:01.048` ); }); }); - describe('With expiration defined', () => { + describe("With expiration defined", () => { const auth = { isMaster: true }; const pushController = new PushController(); @@ -1212,7 +1301,7 @@ describe('PushController', () => { return successfulTransmissions(body, installations); }, getValidPushTypes() { - return ['ios']; + return ["ios"]; }, }; @@ -1223,11 +1312,11 @@ describe('PushController', () => { config = Config.get(Parse.applicationId); }); - it('should throw if both expiration_time and expiration_interval are set', () => { + it("should throw if both expiration_time and expiration_interval are set", () => { expect(() => pushController.sendPush( { - expiration_time: '2017-09-25T13:21:20.841Z', + expiration_time: "2017-09-25T13:21:20.841Z", expiration_interval: 1000, }, {}, @@ -1237,7 +1326,7 @@ describe('PushController', () => { ).toThrow(); }); - it('should throw on invalid expiration_interval', () => { + it("should throw on invalid expiration_interval", () => { expect(() => pushController.sendPush( { @@ -1251,7 +1340,7 @@ describe('PushController', () => { expect(() => pushController.sendPush( { - expiration_interval: '', + expiration_interval: "", }, {}, config, @@ -1270,12 +1359,12 @@ describe('PushController', () => { ).toThrow(); }); - describe('For immediate pushes', () => { - it('should transform the expiration_interval into an absolute time', async () => { - const now = new Date('2017-09-25T13:30:10.452Z'); + describe("For immediate pushes", () => { + it("should transform the expiration_interval into an absolute time", async () => { + const now = new Date("2017-09-25T13:30:10.452Z"); const payload = { data: { - alert: 'immediate push', + alert: "immediate push", }, expiration_interval: 20 * 60, // twenty minutes }; @@ -1290,13 +1379,15 @@ describe('PushController', () => { now ); const pushStatus = await Parse.Push.getPushStatus(pushStatusId); - expect(pushStatus.get('expiry')).toBeDefined('expiry must be set'); - expect(pushStatus.get('expiry')).toEqual(new Date('2017-09-25T13:50:10.452Z').valueOf()); + expect(pushStatus.get("expiry")).toBeDefined("expiry must be set"); + expect(pushStatus.get("expiry")).toEqual( + new Date("2017-09-25T13:50:10.452Z").valueOf() + ); - expect(pushStatus.get('expiration_interval')).toBeDefined( - 'expiration_interval must be defined' + expect(pushStatus.get("expiration_interval")).toBeDefined( + "expiration_interval must be defined" ); - expect(pushStatus.get('expiration_interval')).toBe(20 * 60); + expect(pushStatus.get("expiration_interval")).toBe(20 * 60); }); }); }); diff --git a/spec/PushQueue.spec.js b/spec/PushQueue.spec.js index db851ba22e..b88f1637af 100644 --- a/spec/PushQueue.spec.js +++ b/spec/PushQueue.spec.js @@ -1,14 +1,14 @@ -const Config = require('../lib/Config'); -const { PushQueue } = require('../lib/Push/PushQueue'); +const Config = require("../lib/Config"); +const { PushQueue } = require("../lib/Push/PushQueue"); -describe('PushQueue', () => { - describe('With a defined channel', () => { - it('should be propagated to the PushWorker and PushQueue', done => { +describe("PushQueue", () => { + describe("With a defined channel", () => { + it("should be propagated to the PushWorker and PushQueue", done => { reconfigureServer({ push: { queueOptions: { disablePushWorker: false, - channel: 'my-specific-channel', + channel: "my-specific-channel", }, adapter: { send() { @@ -22,18 +22,21 @@ describe('PushQueue', () => { }) .then(() => { const config = Config.get(Parse.applicationId); - expect(config.pushWorker.channel).toEqual('my-specific-channel', 'pushWorker.channel'); + expect(config.pushWorker.channel).toEqual( + "my-specific-channel", + "pushWorker.channel" + ); expect(config.pushControllerQueue.channel).toEqual( - 'my-specific-channel', - 'pushWorker.channel' + "my-specific-channel", + "pushWorker.channel" ); }) .then(done, done.fail); }); }); - describe('Default channel', () => { - it('should be prefixed with the applicationId', done => { + describe("Default channel", () => { + it("should be prefixed with the applicationId", done => { reconfigureServer({ push: { queueOptions: { @@ -51,9 +54,13 @@ describe('PushQueue', () => { }) .then(() => { const config = Config.get(Parse.applicationId); - expect(PushQueue.defaultPushChannel()).toEqual('test-parse-server-push'); - expect(config.pushWorker.channel).toEqual('test-parse-server-push'); - expect(config.pushControllerQueue.channel).toEqual('test-parse-server-push'); + expect(PushQueue.defaultPushChannel()).toEqual( + "test-parse-server-push" + ); + expect(config.pushWorker.channel).toEqual("test-parse-server-push"); + expect(config.pushControllerQueue.channel).toEqual( + "test-parse-server-push" + ); }) .then(done, done.fail); }); diff --git a/spec/PushRouter.spec.js b/spec/PushRouter.spec.js index 99ff17d243..94e847b809 100644 --- a/spec/PushRouter.spec.js +++ b/spec/PushRouter.spec.js @@ -1,25 +1,25 @@ -const PushRouter = require('../lib/Routers/PushRouter').PushRouter; -const request = require('../lib/request'); +const PushRouter = require("../lib/Routers/PushRouter").PushRouter; +const request = require("../lib/request"); -describe('PushRouter', () => { - it('can get query condition when channels is set', done => { +describe("PushRouter", () => { + it("can get query condition when channels is set", done => { // Make mock request const request = { body: { - channels: ['Giants', 'Mets'], + channels: ["Giants", "Mets"], }, }; const where = PushRouter.getQueryCondition(request); expect(where).toEqual({ channels: { - $in: ['Giants', 'Mets'], + $in: ["Giants", "Mets"], }, }); done(); }); - it('can get query condition when where is set', done => { + it("can get query condition when where is set", done => { // Make mock request const request = { body: { @@ -36,7 +36,7 @@ describe('PushRouter', () => { done(); }); - it('can get query condition when nothing is set', done => { + it("can get query condition when nothing is set", done => { // Make mock request const request = { body: {}, @@ -48,12 +48,12 @@ describe('PushRouter', () => { done(); }); - it('can throw on getQueryCondition when channels and where are set', done => { + it("can throw on getQueryCondition when channels and where are set", done => { // Make mock request const request = { body: { channels: { - $in: ['Giants', 'Mets'], + $in: ["Giants", "Mets"], }, where: { injuryReports: true, @@ -67,23 +67,23 @@ describe('PushRouter', () => { done(); }); - it('sends a push through REST', done => { + it("sends a push through REST", done => { request({ - method: 'POST', - url: Parse.serverURL + '/push', + method: "POST", + url: Parse.serverURL + "/push", body: { channels: { - $in: ['Giants', 'Mets'], + $in: ["Giants", "Mets"], }, }, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': Parse.masterKey, - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Master-Key": Parse.masterKey, + "Content-Type": "application/json", }, }).then(res => { - expect(res.headers['x-parse-push-status-id']).not.toBe(undefined); - expect(res.headers['x-parse-push-status-id'].length).toBe(10); + expect(res.headers["x-parse-push-status-id"]).not.toBe(undefined); + expect(res.headers["x-parse-push-status-id"].length).toBe(10); expect(res.data.result).toBe(true); done(); }); diff --git a/spec/PushWorker.spec.js b/spec/PushWorker.spec.js index 23e8206fe3..358e88666b 100644 --- a/spec/PushWorker.spec.js +++ b/spec/PushWorker.spec.js @@ -1,11 +1,11 @@ -const PushWorker = require('../lib').PushWorker; -const PushUtils = require('../lib/Push/utils'); -const Config = require('../lib/Config'); -const { pushStatusHandler } = require('../lib/StatusHandler'); -const rest = require('../lib/rest'); +const PushWorker = require("../lib").PushWorker; +const PushUtils = require("../lib/Push/utils"); +const Config = require("../lib/Config"); +const { pushStatusHandler } = require("../lib/StatusHandler"); +const rest = require("../lib/rest"); -describe('PushWorker', () => { - it('should run with small batch', done => { +describe("PushWorker", () => { + it("should run with small batch", done => { const batchSize = 3; let sendCount = 0; reconfigureServer({ @@ -17,7 +17,7 @@ describe('PushWorker', () => { }, }) .then(() => { - expect(Config.get('test').pushWorker).toBeUndefined(); + expect(Config.get("test").pushWorker).toBeUndefined(); new PushWorker({ send: (body, installations) => { expect(installations.length <= batchSize).toBe(true); @@ -25,16 +25,22 @@ describe('PushWorker', () => { return Promise.resolve(); }, getValidPushTypes: function () { - return ['ios', 'android']; + return ["ios", "android"]; }, }); const installations = []; while (installations.length != 10) { - const installation = new Parse.Object('_Installation'); - installation.set('installationId', 'installation_' + installations.length); - installation.set('deviceToken', 'device_token_' + installations.length); - installation.set('badge', 1); - installation.set('deviceType', 'ios'); + const installation = new Parse.Object("_Installation"); + installation.set( + "installationId", + "installation_" + installations.length + ); + installation.set( + "deviceToken", + "device_token_" + installations.length + ); + installation.set("badge", 1); + installation.set("deviceType", "ios"); installations.push(installation); } return Parse.Object.saveAll(installations); @@ -43,10 +49,10 @@ describe('PushWorker', () => { return Parse.Push.send( { where: { - deviceType: 'ios', + deviceType: "ios", }, data: { - alert: 'Hello world!', + alert: "Hello world!", }, }, { useMasterKey: true } @@ -66,113 +72,113 @@ describe('PushWorker', () => { }); }); - describe('localized push', () => { - it('should return locales', () => { + describe("localized push", () => { + it("should return locales", () => { const locales = PushUtils.getLocalesFromPush({ data: { - 'alert-fr': 'french', - alert: 'Yo!', - 'alert-en-US': 'English', + "alert-fr": "french", + alert: "Yo!", + "alert-en-US": "English", }, }); - expect(locales).toEqual(['fr', 'en-US']); + expect(locales).toEqual(["fr", "en-US"]); }); - it('should return and empty array if no locale is set', () => { + it("should return and empty array if no locale is set", () => { const locales = PushUtils.getLocalesFromPush({ data: { - alert: 'Yo!', + alert: "Yo!", }, }); expect(locales).toEqual([]); }); - it('should deduplicate locales', () => { + it("should deduplicate locales", () => { const locales = PushUtils.getLocalesFromPush({ data: { - alert: 'Yo!', - 'alert-fr': 'french', - 'title-fr': 'french', + alert: "Yo!", + "alert-fr": "french", + "title-fr": "french", }, }); - expect(locales).toEqual(['fr']); + expect(locales).toEqual(["fr"]); }); - it('should handle empty body data', () => { + it("should handle empty body data", () => { expect(PushUtils.getLocalesFromPush({})).toEqual([]); }); - it('transforms body appropriately', () => { + it("transforms body appropriately", () => { const cleanBody = PushUtils.transformPushBodyForLocale( { data: { - alert: 'Yo!', - 'alert-fr': 'frenchy!', - 'alert-en': 'english', + alert: "Yo!", + "alert-fr": "frenchy!", + "alert-en": "english", }, }, - 'fr' + "fr" ); expect(cleanBody).toEqual({ data: { - alert: 'frenchy!', + alert: "frenchy!", }, }); }); - it('transforms body appropriately with title locale', () => { + it("transforms body appropriately with title locale", () => { const cleanBody = PushUtils.transformPushBodyForLocale( { data: { - alert: 'Yo!', - 'alert-fr': 'frenchy!', - 'alert-en': 'english', - 'title-fr': 'french title', + alert: "Yo!", + "alert-fr": "frenchy!", + "alert-en": "english", + "title-fr": "french title", }, }, - 'fr' + "fr" ); expect(cleanBody).toEqual({ data: { - alert: 'frenchy!', - title: 'french title', + alert: "frenchy!", + title: "french title", }, }); }); - it('maps body on all provided locales', () => { + it("maps body on all provided locales", () => { const bodies = PushUtils.bodiesPerLocales( { data: { - alert: 'Yo!', - 'alert-fr': 'frenchy!', - 'alert-en': 'english', - 'title-fr': 'french title', + alert: "Yo!", + "alert-fr": "frenchy!", + "alert-en": "english", + "title-fr": "french title", }, }, - ['fr', 'en'] + ["fr", "en"] ); expect(bodies).toEqual({ fr: { data: { - alert: 'frenchy!', - title: 'french title', + alert: "frenchy!", + title: "french title", }, }, en: { data: { - alert: 'english', + alert: "english", }, }, default: { data: { - alert: 'Yo!', + alert: "Yo!", }, }, }); }); - it('should properly handle default cases', () => { + it("should properly handle default cases", () => { expect(PushUtils.transformPushBodyForLocale({})).toEqual({}); expect(PushUtils.stripLocalesFromBody({})).toEqual({}); expect(PushUtils.bodiesPerLocales({ where: {} })).toEqual({ @@ -182,11 +188,11 @@ describe('PushWorker', () => { }); }); - describe('pushStatus', () => { - it('should remove invalid installations', done => { - const config = Config.get('test'); + describe("pushStatus", () => { + it("should remove invalid installations", done => { + const config = Config.get("test"); const handler = pushStatusHandler(config); - const spy = spyOn(config.database, 'update').and.callFake(() => { + const spy = spyOn(config.database, "update").and.callFake(() => { return Promise.resolve({}); }); const toAwait = handler.trackSent( @@ -195,64 +201,64 @@ describe('PushWorker', () => { transmitted: false, device: { deviceToken: 1, - deviceType: 'ios', + deviceType: "ios", }, - response: { error: 'Unregistered' }, + response: { error: "Unregistered" }, }, { transmitted: true, device: { deviceToken: 10, - deviceType: 'ios', + deviceType: "ios", }, }, { transmitted: false, device: { deviceToken: 2, - deviceType: 'ios', + deviceType: "ios", }, - response: { error: 'NotRegistered' }, + response: { error: "NotRegistered" }, }, { transmitted: false, device: { deviceToken: 3, - deviceType: 'ios', + deviceType: "ios", }, - response: { error: 'InvalidRegistration' }, + response: { error: "InvalidRegistration" }, }, { transmitted: true, device: { deviceToken: 11, - deviceType: 'ios', + deviceType: "ios", }, }, { transmitted: false, device: { deviceToken: 4, - deviceType: 'ios', + deviceType: "ios", }, - response: { error: 'InvalidRegistration' }, + response: { error: "InvalidRegistration" }, }, { transmitted: false, device: { deviceToken: 5, - deviceType: 'ios', + deviceType: "ios", }, - response: { error: 'InvalidRegistration' }, + response: { error: "InvalidRegistration" }, }, { // should not be deleted transmitted: false, device: { deviceToken: Parse.Error.OBJECT_NOT_FOUND, - deviceType: 'ios', + deviceType: "ios", }, - response: { error: 'invalid error...' }, + response: { error: "invalid error..." }, }, ], undefined, @@ -261,22 +267,22 @@ describe('PushWorker', () => { expect(spy).toHaveBeenCalled(); expect(spy.calls.count()).toBe(1); const lastCall = spy.calls.mostRecent(); - expect(lastCall.args[0]).toBe('_Installation'); + expect(lastCall.args[0]).toBe("_Installation"); expect(lastCall.args[1]).toEqual({ deviceToken: { $in: [1, 2, 3, 4, 5] }, }); expect(lastCall.args[2]).toEqual({ - deviceToken: { __op: 'Delete' }, + deviceToken: { __op: "Delete" }, }); toAwait.then(done).catch(done); }); - it_id('764d28ab-241b-4b96-8ce9-e03541850e3f')(it)( - 'tracks push status per UTC offsets', + it_id("764d28ab-241b-4b96-8ce9-e03541850e3f")(it)( + "tracks push status per UTC offsets", done => { - const config = Config.get('test'); + const config = Config.get("test"); const handler = pushStatusHandler(config); - const spy = spyOn(rest, 'update').and.callThrough(); + const spy = spyOn(rest, "update").and.callThrough(); const UTCOffset = 1; handler .setInitial() @@ -287,14 +293,14 @@ describe('PushWorker', () => { transmitted: false, device: { deviceToken: 1, - deviceType: 'ios', + deviceType: "ios", }, }, { transmitted: true, device: { deviceToken: 1, - deviceType: 'ios', + deviceType: "ios", }, }, ], @@ -306,47 +312,50 @@ describe('PushWorker', () => { const lastCall = spy.calls.mostRecent(); expect(lastCall.args[2]).toBe(`_PushStatus`); expect(lastCall.args[4]).toEqual({ - numSent: { __op: 'Increment', amount: 1 }, - numFailed: { __op: 'Increment', amount: 1 }, - 'sentPerType.ios': { __op: 'Increment', amount: 1 }, - 'failedPerType.ios': { __op: 'Increment', amount: 1 }, - [`sentPerUTCOffset.${UTCOffset}`]: { __op: 'Increment', amount: 1 }, + numSent: { __op: "Increment", amount: 1 }, + numFailed: { __op: "Increment", amount: 1 }, + "sentPerType.ios": { __op: "Increment", amount: 1 }, + "failedPerType.ios": { __op: "Increment", amount: 1 }, + [`sentPerUTCOffset.${UTCOffset}`]: { + __op: "Increment", + amount: 1, + }, [`failedPerUTCOffset.${UTCOffset}`]: { - __op: 'Increment', + __op: "Increment", amount: 1, }, - count: { __op: 'Increment', amount: -1 }, - status: 'running', + count: { __op: "Increment", amount: -1 }, + status: "running", }); - const query = new Parse.Query('_PushStatus'); + const query = new Parse.Query("_PushStatus"); return query.get(handler.objectId, { useMasterKey: true }); }) .then(pushStatus => { - const sentPerUTCOffset = pushStatus.get('sentPerUTCOffset'); - expect(sentPerUTCOffset['1']).toBe(1); - const failedPerUTCOffset = pushStatus.get('failedPerUTCOffset'); - expect(failedPerUTCOffset['1']).toBe(1); + const sentPerUTCOffset = pushStatus.get("sentPerUTCOffset"); + expect(sentPerUTCOffset["1"]).toBe(1); + const failedPerUTCOffset = pushStatus.get("failedPerUTCOffset"); + expect(failedPerUTCOffset["1"]).toBe(1); return handler.trackSent( [ { transmitted: false, device: { deviceToken: 1, - deviceType: 'ios', + deviceType: "ios", }, }, { transmitted: true, device: { deviceToken: 1, - deviceType: 'ios', + deviceType: "ios", }, }, { transmitted: true, device: { deviceToken: 1, - deviceType: 'ios', + deviceType: "ios", }, }, ], @@ -354,24 +363,24 @@ describe('PushWorker', () => { ); }) .then(() => { - const query = new Parse.Query('_PushStatus'); + const query = new Parse.Query("_PushStatus"); return query.get(handler.objectId, { useMasterKey: true }); }) .then(pushStatus => { - const sentPerUTCOffset = pushStatus.get('sentPerUTCOffset'); - expect(sentPerUTCOffset['1']).toBe(3); - const failedPerUTCOffset = pushStatus.get('failedPerUTCOffset'); - expect(failedPerUTCOffset['1']).toBe(2); + const sentPerUTCOffset = pushStatus.get("sentPerUTCOffset"); + expect(sentPerUTCOffset["1"]).toBe(3); + const failedPerUTCOffset = pushStatus.get("failedPerUTCOffset"); + expect(failedPerUTCOffset["1"]).toBe(2); }) .then(done) .catch(done.fail); } ); - it('tracks push status per UTC offsets with negative offsets', done => { - const config = Config.get('test'); + it("tracks push status per UTC offsets with negative offsets", done => { + const config = Config.get("test"); const handler = pushStatusHandler(config); - const spy = spyOn(rest, 'update').and.callThrough(); + const spy = spyOn(rest, "update").and.callThrough(); const UTCOffset = -6; handler .setInitial() @@ -382,17 +391,17 @@ describe('PushWorker', () => { transmitted: false, device: { deviceToken: 1, - deviceType: 'ios', + deviceType: "ios", }, - response: { error: 'Unregistered' }, + response: { error: "Unregistered" }, }, { transmitted: true, device: { deviceToken: 1, - deviceType: 'ios', + deviceType: "ios", }, - response: { error: 'Unregistered' }, + response: { error: "Unregistered" }, }, ], UTCOffset @@ -401,19 +410,19 @@ describe('PushWorker', () => { .then(() => { expect(spy).toHaveBeenCalled(); const lastCall = spy.calls.mostRecent(); - expect(lastCall.args[2]).toBe('_PushStatus'); + expect(lastCall.args[2]).toBe("_PushStatus"); expect(lastCall.args[4]).toEqual({ - numSent: { __op: 'Increment', amount: 1 }, - numFailed: { __op: 'Increment', amount: 1 }, - 'sentPerType.ios': { __op: 'Increment', amount: 1 }, - 'failedPerType.ios': { __op: 'Increment', amount: 1 }, - [`sentPerUTCOffset.${UTCOffset}`]: { __op: 'Increment', amount: 1 }, + numSent: { __op: "Increment", amount: 1 }, + numFailed: { __op: "Increment", amount: 1 }, + "sentPerType.ios": { __op: "Increment", amount: 1 }, + "failedPerType.ios": { __op: "Increment", amount: 1 }, + [`sentPerUTCOffset.${UTCOffset}`]: { __op: "Increment", amount: 1 }, [`failedPerUTCOffset.${UTCOffset}`]: { - __op: 'Increment', + __op: "Increment", amount: 1, }, - count: { __op: 'Increment', amount: -1 }, - status: 'running', + count: { __op: "Increment", amount: -1 }, + status: "running", }); done(); }); diff --git a/spec/QueryTools.spec.js b/spec/QueryTools.spec.js index 8dbda98b0a..51bb63e15e 100644 --- a/spec/QueryTools.spec.js +++ b/spec/QueryTools.spec.js @@ -1,140 +1,140 @@ -const Parse = require('parse/node'); +const Parse = require("parse/node"); -const Id = require('../lib/LiveQuery/Id'); -const QueryTools = require('../lib/LiveQuery/QueryTools'); +const Id = require("../lib/LiveQuery/Id"); +const QueryTools = require("../lib/LiveQuery/QueryTools"); const queryHash = QueryTools.queryHash; const matchesQuery = QueryTools.matchesQuery; -const Item = Parse.Object.extend('Item'); +const Item = Parse.Object.extend("Item"); -describe('queryHash', function () { - it('should always hash a query to the same string', function () { +describe("queryHash", function () { + it("should always hash a query to the same string", function () { const q = new Parse.Query(Item); - q.equalTo('field', 'value'); - q.exists('name'); - q.ascending('createdAt'); + q.equalTo("field", "value"); + q.exists("name"); + q.ascending("createdAt"); q.limit(10); const firstHash = queryHash(q); const secondHash = queryHash(q); expect(firstHash).toBe(secondHash); }); - it('should return equivalent hashes for equivalent queries', function () { + it("should return equivalent hashes for equivalent queries", function () { let q1 = new Parse.Query(Item); - q1.equalTo('field', 'value'); - q1.exists('name'); - q1.lessThan('age', 30); - q1.greaterThan('age', 3); - q1.ascending('createdAt'); - q1.include(['name', 'age']); + q1.equalTo("field", "value"); + q1.exists("name"); + q1.lessThan("age", 30); + q1.greaterThan("age", 3); + q1.ascending("createdAt"); + q1.include(["name", "age"]); q1.limit(10); let q2 = new Parse.Query(Item); q2.limit(10); - q2.greaterThan('age', 3); - q2.lessThan('age', 30); - q2.include(['name', 'age']); - q2.ascending('createdAt'); - q2.exists('name'); - q2.equalTo('field', 'value'); + q2.greaterThan("age", 3); + q2.lessThan("age", 30); + q2.include(["name", "age"]); + q2.ascending("createdAt"); + q2.exists("name"); + q2.equalTo("field", "value"); let firstHash = queryHash(q1); let secondHash = queryHash(q2); expect(firstHash).toBe(secondHash); - q1.containedIn('fruit', ['apple', 'banana', 'cherry']); + q1.containedIn("fruit", ["apple", "banana", "cherry"]); firstHash = queryHash(q1); expect(firstHash).not.toBe(secondHash); - q2.containedIn('fruit', ['banana', 'cherry', 'apple']); + q2.containedIn("fruit", ["banana", "cherry", "apple"]); secondHash = queryHash(q2); expect(secondHash).toBe(firstHash); - q1.containedIn('fruit', ['coconut']); + q1.containedIn("fruit", ["coconut"]); firstHash = queryHash(q1); expect(firstHash).not.toBe(secondHash); q1 = new Parse.Query(Item); - q1.equalTo('field', 'value'); - q1.lessThan('age', 30); - q1.exists('name'); + q1.equalTo("field", "value"); + q1.lessThan("age", 30); + q1.exists("name"); q2 = new Parse.Query(Item); - q2.equalTo('name', 'person'); - q2.equalTo('field', 'other'); + q2.equalTo("name", "person"); + q2.equalTo("field", "other"); firstHash = queryHash(Parse.Query.or(q1, q2)); secondHash = queryHash(Parse.Query.or(q2, q1)); expect(firstHash).toBe(secondHash); }); - it('should not let fields of different types appear similar', function () { + it("should not let fields of different types appear similar", function () { let q1 = new Parse.Query(Item); - q1.lessThan('age', 30); + q1.lessThan("age", 30); const q2 = new Parse.Query(Item); - q2.equalTo('age', '{$lt:30}'); + q2.equalTo("age", "{$lt:30}"); expect(queryHash(q1)).not.toBe(queryHash(q2)); q1 = new Parse.Query(Item); - q1.equalTo('age', 15); + q1.equalTo("age", 15); - q2.equalTo('age', '15'); + q2.equalTo("age", "15"); expect(queryHash(q1)).not.toBe(queryHash(q2)); }); }); -describe('matchesQuery', function () { - it('matches blanket queries', function () { +describe("matchesQuery", function () { + it("matches blanket queries", function () { const obj = { - id: new Id('Klass', 'O1'), + id: new Id("Klass", "O1"), value: 12, }; - const q = new Parse.Query('Klass'); + const q = new Parse.Query("Klass"); expect(matchesQuery(obj, q)).toBe(true); - obj.id = new Id('Other', 'O1'); + obj.id = new Id("Other", "O1"); expect(matchesQuery(obj, q)).toBe(false); }); - it('matches existence queries', function () { + it("matches existence queries", function () { const obj = { - id: new Id('Item', 'O1'), + id: new Id("Item", "O1"), count: 15, }; - const q = new Parse.Query('Item'); - q.exists('count'); + const q = new Parse.Query("Item"); + q.exists("count"); expect(matchesQuery(obj, q)).toBe(true); - q.exists('name'); + q.exists("name"); expect(matchesQuery(obj, q)).toBe(false); }); - it('matches queries with doesNotExist constraint', function () { + it("matches queries with doesNotExist constraint", function () { const obj = { - id: new Id('Item', 'O1'), + id: new Id("Item", "O1"), count: 15, }; - let q = new Parse.Query('Item'); - q.doesNotExist('name'); + let q = new Parse.Query("Item"); + q.doesNotExist("name"); expect(matchesQuery(obj, q)).toBe(true); - q = new Parse.Query('Item'); - q.doesNotExist('count'); + q = new Parse.Query("Item"); + q.doesNotExist("count"); expect(matchesQuery(obj, q)).toBe(false); }); - it('matches queries with eq constraint', function () { + it("matches queries with eq constraint", function () { const obj = { - objectId: 'Person2', + objectId: "Person2", score: 12, - name: 'Tom', + name: "Tom", }; const q1 = { objectId: { - $eq: 'Person2', + $eq: "Person2", }, }; @@ -146,7 +146,7 @@ describe('matchesQuery', function () { const q3 = { name: { - $eq: 'Tom', + $eq: "Tom", }, }; expect(matchesQuery(obj, q1)).toBe(true); @@ -154,51 +154,51 @@ describe('matchesQuery', function () { expect(matchesQuery(obj, q3)).toBe(true); }); - it('matches on equality queries', function () { + it("matches on equality queries", function () { const day = new Date(); const location = new Parse.GeoPoint({ latitude: 37.484815, longitude: -122.148377, }); const obj = { - id: new Id('Person', 'O1'), + id: new Id("Person", "O1"), score: 12, - name: 'Bill', + name: "Bill", birthday: day, lastLocation: location, }; - let q = new Parse.Query('Person'); - q.equalTo('score', 12); + let q = new Parse.Query("Person"); + q.equalTo("score", 12); expect(matchesQuery(obj, q)).toBe(true); - q = new Parse.Query('Person'); - q.equalTo('name', 'Bill'); + q = new Parse.Query("Person"); + q.equalTo("name", "Bill"); expect(matchesQuery(obj, q)).toBe(true); - q.equalTo('name', 'Jeff'); + q.equalTo("name", "Jeff"); expect(matchesQuery(obj, q)).toBe(false); - q = new Parse.Query('Person'); - q.containedIn('name', ['Adam', 'Ben', 'Charles']); + q = new Parse.Query("Person"); + q.containedIn("name", ["Adam", "Ben", "Charles"]); expect(matchesQuery(obj, q)).toBe(false); - q.containedIn('name', ['Adam', 'Bill', 'Charles']); + q.containedIn("name", ["Adam", "Bill", "Charles"]); expect(matchesQuery(obj, q)).toBe(true); - q = new Parse.Query('Person'); - q.notContainedIn('name', ['Adam', 'Bill', 'Charles']); + q = new Parse.Query("Person"); + q.notContainedIn("name", ["Adam", "Bill", "Charles"]); expect(matchesQuery(obj, q)).toBe(false); - q.notContainedIn('name', ['Adam', 'Ben', 'Charles']); + q.notContainedIn("name", ["Adam", "Ben", "Charles"]); expect(matchesQuery(obj, q)).toBe(true); - q = new Parse.Query('Person'); - q.equalTo('birthday', day); + q = new Parse.Query("Person"); + q.equalTo("birthday", day); expect(matchesQuery(obj, q)).toBe(true); - q.equalTo('birthday', new Date(1990, 1)); + q.equalTo("birthday", new Date(1990, 1)); expect(matchesQuery(obj, q)).toBe(false); - q = new Parse.Query('Person'); + q = new Parse.Query("Person"); q.equalTo( - 'lastLocation', + "lastLocation", new Parse.GeoPoint({ latitude: 37.484815, longitude: -122.148377, @@ -206,7 +206,7 @@ describe('matchesQuery', function () { ); expect(matchesQuery(obj, q)).toBe(true); q.equalTo( - 'lastLocation', + "lastLocation", new Parse.GeoPoint({ latitude: 37.4848, longitude: -122.1483, @@ -215,156 +215,156 @@ describe('matchesQuery', function () { expect(matchesQuery(obj, q)).toBe(false); q.equalTo( - 'lastLocation', + "lastLocation", new Parse.GeoPoint({ latitude: 37.484815, longitude: -122.148377, }) ); - q.equalTo('score', 12); - q.equalTo('name', 'Bill'); - q.equalTo('birthday', day); + q.equalTo("score", 12); + q.equalTo("name", "Bill"); + q.equalTo("birthday", day); expect(matchesQuery(obj, q)).toBe(true); - q.equalTo('name', 'bill'); + q.equalTo("name", "bill"); expect(matchesQuery(obj, q)).toBe(false); let img = { - id: new Id('Image', 'I1'), - tags: ['nofilter', 'latergram', 'tbt'], + id: new Id("Image", "I1"), + tags: ["nofilter", "latergram", "tbt"], }; - q = new Parse.Query('Image'); - q.equalTo('tags', 'selfie'); + q = new Parse.Query("Image"); + q.equalTo("tags", "selfie"); expect(matchesQuery(img, q)).toBe(false); - q.equalTo('tags', 'tbt'); + q.equalTo("tags", "tbt"); expect(matchesQuery(img, q)).toBe(true); - const q2 = new Parse.Query('Image'); - q2.containsAll('tags', ['latergram', 'nofilter']); + const q2 = new Parse.Query("Image"); + q2.containsAll("tags", ["latergram", "nofilter"]); expect(matchesQuery(img, q2)).toBe(true); - q2.containsAll('tags', ['latergram', 'selfie']); + q2.containsAll("tags", ["latergram", "selfie"]); expect(matchesQuery(img, q2)).toBe(false); const u = new Parse.User(); - u.id = 'U2'; - q = new Parse.Query('Image'); - q.equalTo('owner', u); + u.id = "U2"; + q = new Parse.Query("Image"); + q.equalTo("owner", u); img = { - className: 'Image', - objectId: 'I1', + className: "Image", + objectId: "I1", owner: { - className: '_User', - objectId: 'U2', + className: "_User", + objectId: "U2", }, }; expect(matchesQuery(img, q)).toBe(true); - img.owner.objectId = 'U3'; + img.owner.objectId = "U3"; expect(matchesQuery(img, q)).toBe(false); // pointers in arrays - q = new Parse.Query('Image'); - q.equalTo('owners', u); + q = new Parse.Query("Image"); + q.equalTo("owners", u); img = { - className: 'Image', - objectId: 'I1', + className: "Image", + objectId: "I1", owners: [ { - className: '_User', - objectId: 'U2', + className: "_User", + objectId: "U2", }, ], }; expect(matchesQuery(img, q)).toBe(true); - img.owners[0].objectId = 'U3'; + img.owners[0].objectId = "U3"; expect(matchesQuery(img, q)).toBe(false); }); - it('matches on inequalities', function () { + it("matches on inequalities", function () { const player = { - id: new Id('Person', 'O1'), + id: new Id("Person", "O1"), score: 12, - name: 'Bill', + name: "Bill", birthday: new Date(1980, 2, 4), }; - let q = new Parse.Query('Person'); - q.lessThan('score', 15); + let q = new Parse.Query("Person"); + q.lessThan("score", 15); expect(matchesQuery(player, q)).toBe(true); - q.lessThan('score', 10); + q.lessThan("score", 10); expect(matchesQuery(player, q)).toBe(false); - q = new Parse.Query('Person'); - q.lessThanOrEqualTo('score', 15); + q = new Parse.Query("Person"); + q.lessThanOrEqualTo("score", 15); expect(matchesQuery(player, q)).toBe(true); - q.lessThanOrEqualTo('score', 12); + q.lessThanOrEqualTo("score", 12); expect(matchesQuery(player, q)).toBe(true); - q.lessThanOrEqualTo('score', 10); + q.lessThanOrEqualTo("score", 10); expect(matchesQuery(player, q)).toBe(false); - q = new Parse.Query('Person'); - q.greaterThan('score', 15); + q = new Parse.Query("Person"); + q.greaterThan("score", 15); expect(matchesQuery(player, q)).toBe(false); - q.greaterThan('score', 10); + q.greaterThan("score", 10); expect(matchesQuery(player, q)).toBe(true); - q = new Parse.Query('Person'); - q.greaterThanOrEqualTo('score', 15); + q = new Parse.Query("Person"); + q.greaterThanOrEqualTo("score", 15); expect(matchesQuery(player, q)).toBe(false); - q.greaterThanOrEqualTo('score', 12); + q.greaterThanOrEqualTo("score", 12); expect(matchesQuery(player, q)).toBe(true); - q.greaterThanOrEqualTo('score', 10); + q.greaterThanOrEqualTo("score", 10); expect(matchesQuery(player, q)).toBe(true); - q = new Parse.Query('Person'); - q.notEqualTo('score', 12); + q = new Parse.Query("Person"); + q.notEqualTo("score", 12); expect(matchesQuery(player, q)).toBe(false); - q.notEqualTo('score', 40); + q.notEqualTo("score", 40); expect(matchesQuery(player, q)).toBe(true); }); - it('matches an $or query', function () { + it("matches an $or query", function () { const player = { - id: new Id('Player', 'P1'), - name: 'Player 1', + id: new Id("Player", "P1"), + name: "Player 1", score: 12, }; - const q = new Parse.Query('Player'); - q.equalTo('name', 'Player 1'); - const q2 = new Parse.Query('Player'); - q2.equalTo('name', 'Player 2'); + const q = new Parse.Query("Player"); + q.equalTo("name", "Player 1"); + const q2 = new Parse.Query("Player"); + q2.equalTo("name", "Player 2"); const orQuery = Parse.Query.or(q, q2); expect(matchesQuery(player, q)).toBe(true); expect(matchesQuery(player, q2)).toBe(false); expect(matchesQuery(player, orQuery)).toBe(true); }); - it('does not match $all query when value is missing', () => { + it("does not match $all query when value is missing", () => { const player = { - id: new Id('Player', 'P1'), - name: 'Player 1', + id: new Id("Player", "P1"), + name: "Player 1", score: 12, }; const q = { missing: { $all: [1, 2, 3] } }; expect(matchesQuery(player, q)).toBe(false); }); - it('matches an $and query', () => { + it("matches an $and query", () => { const player = { - id: new Id('Player', 'P1'), - name: 'Player 1', + id: new Id("Player", "P1"), + name: "Player 1", score: 12, }; - const q = new Parse.Query('Player'); - q.equalTo('name', 'Player 1'); - const q2 = new Parse.Query('Player'); - q2.equalTo('score', 12); - const q3 = new Parse.Query('Player'); - q3.equalTo('score', 100); + const q = new Parse.Query("Player"); + q.equalTo("name", "Player 1"); + const q2 = new Parse.Query("Player"); + q2.equalTo("score", 12); + const q3 = new Parse.Query("Player"); + q3.equalTo("score", 100); const andQuery1 = Parse.Query.and(q, q2); const andQuery2 = Parse.Query.and(q, q3); expect(matchesQuery(player, q)).toBe(true); @@ -373,19 +373,19 @@ describe('matchesQuery', function () { expect(matchesQuery(player, andQuery2)).toBe(false); }); - it('matches an $nor query', () => { + it("matches an $nor query", () => { const player = { - id: new Id('Player', 'P1'), - name: 'Player 1', + id: new Id("Player", "P1"), + name: "Player 1", score: 12, }; - const q = new Parse.Query('Player'); - q.equalTo('name', 'Player 1'); - const q2 = new Parse.Query('Player'); - q2.equalTo('name', 'Player 2'); - const q3 = new Parse.Query('Player'); - q3.equalTo('name', 'Player 3'); + const q = new Parse.Query("Player"); + q.equalTo("name", "Player 1"); + const q2 = new Parse.Query("Player"); + q2.equalTo("name", "Player 2"); + const q3 = new Parse.Query("Player"); + q3.equalTo("name", "Player 3"); const norQuery1 = Parse.Query.nor(q, q2); const norQuery2 = Parse.Query.nor(q2, q3); @@ -396,109 +396,109 @@ describe('matchesQuery', function () { expect(matchesQuery(player, norQuery2)).toBe(true); }); - it('matches $regex queries', function () { + it("matches $regex queries", function () { const player = { - id: new Id('Player', 'P1'), - name: 'Player 1', + id: new Id("Player", "P1"), + name: "Player 1", score: 12, }; - let q = new Parse.Query('Player'); - q.startsWith('name', 'Play'); + let q = new Parse.Query("Player"); + q.startsWith("name", "Play"); expect(matchesQuery(player, q)).toBe(true); - q.startsWith('name', 'Ploy'); + q.startsWith("name", "Ploy"); expect(matchesQuery(player, q)).toBe(false); - q = new Parse.Query('Player'); - q.endsWith('name', ' 1'); + q = new Parse.Query("Player"); + q.endsWith("name", " 1"); expect(matchesQuery(player, q)).toBe(true); - q.endsWith('name', ' 2'); + q.endsWith("name", " 2"); expect(matchesQuery(player, q)).toBe(false); // Check that special characters are escaped - player.name = 'Android-7'; - q = new Parse.Query('Player'); - q.contains('name', 'd-7'); + player.name = "Android-7"; + q = new Parse.Query("Player"); + q.contains("name", "d-7"); expect(matchesQuery(player, q)).toBe(true); - q = new Parse.Query('Player'); - q.matches('name', /A.d/); + q = new Parse.Query("Player"); + q.matches("name", /A.d/); expect(matchesQuery(player, q)).toBe(true); - q.matches('name', /A[^n]d/); + q.matches("name", /A[^n]d/); expect(matchesQuery(player, q)).toBe(false); // Check that the string \\E is returned to normal - player.name = 'Slash \\E'; - q = new Parse.Query('Player'); - q.endsWith('name', 'h \\E'); + player.name = "Slash \\E"; + q = new Parse.Query("Player"); + q.endsWith("name", "h \\E"); expect(matchesQuery(player, q)).toBe(true); - q.endsWith('name', 'h \\Ee'); + q.endsWith("name", "h \\Ee"); expect(matchesQuery(player, q)).toBe(false); - player.name = 'Slash \\Q and more'; - q = new Parse.Query('Player'); - q.contains('name', 'h \\Q and'); + player.name = "Slash \\Q and more"; + q = new Parse.Query("Player"); + q.contains("name", "h \\Q and"); expect(matchesQuery(player, q)).toBe(true); - q.contains('name', 'h \\Q or'); + q.contains("name", "h \\Q or"); expect(matchesQuery(player, q)).toBe(false); }); - it('matches $nearSphere queries', function () { - let q = new Parse.Query('Checkin'); - q.near('location', new Parse.GeoPoint(20, 20)); + it("matches $nearSphere queries", function () { + let q = new Parse.Query("Checkin"); + q.near("location", new Parse.GeoPoint(20, 20)); // With no max distance, any GeoPoint is 'near' const pt = { - id: new Id('Checkin', 'C1'), + id: new Id("Checkin", "C1"), location: new Parse.GeoPoint(40, 40), }; const ptUndefined = { - id: new Id('Checkin', 'C1'), + id: new Id("Checkin", "C1"), }; const ptNull = { - id: new Id('Checkin', 'C1'), + id: new Id("Checkin", "C1"), location: null, }; expect(matchesQuery(pt, q)).toBe(true); expect(matchesQuery(ptUndefined, q)).toBe(false); expect(matchesQuery(ptNull, q)).toBe(false); - q = new Parse.Query('Checkin'); + q = new Parse.Query("Checkin"); pt.location = new Parse.GeoPoint(40, 40); - q.withinRadians('location', new Parse.GeoPoint(30, 30), 0.3); + q.withinRadians("location", new Parse.GeoPoint(30, 30), 0.3); expect(matchesQuery(pt, q)).toBe(true); - q.withinRadians('location', new Parse.GeoPoint(30, 30), 0.2); + q.withinRadians("location", new Parse.GeoPoint(30, 30), 0.2); expect(matchesQuery(pt, q)).toBe(false); }); - it('matches $within queries', function () { + it("matches $within queries", function () { const caltrainStation = { - id: new Id('Checkin', 'C1'), + id: new Id("Checkin", "C1"), location: new Parse.GeoPoint(37.776346, -122.394218), - name: 'Caltrain', + name: "Caltrain", }; const santaClara = { - id: new Id('Checkin', 'C2'), + id: new Id("Checkin", "C2"), location: new Parse.GeoPoint(37.325635, -121.945753), - name: 'Santa Clara', + name: "Santa Clara", }; const noLocation = { - id: new Id('Checkin', 'C2'), - name: 'Santa Clara', + id: new Id("Checkin", "C2"), + name: "Santa Clara", }; const nullLocation = { - id: new Id('Checkin', 'C2'), + id: new Id("Checkin", "C2"), location: null, - name: 'Santa Clara', + name: "Santa Clara", }; - let q = new Parse.Query('Checkin').withinGeoBox( - 'location', + let q = new Parse.Query("Checkin").withinGeoBox( + "location", new Parse.GeoPoint(37.708813, -122.526398), new Parse.GeoPoint(37.822802, -122.373962) ); @@ -508,8 +508,8 @@ describe('matchesQuery', function () { expect(matchesQuery(noLocation, q)).toBe(false); expect(matchesQuery(nullLocation, q)).toBe(false); // Invalid rectangles - q = new Parse.Query('Checkin').withinGeoBox( - 'location', + q = new Parse.Query("Checkin").withinGeoBox( + "location", new Parse.GeoPoint(37.822802, -122.373962), new Parse.GeoPoint(37.708813, -122.526398) ); @@ -517,8 +517,8 @@ describe('matchesQuery', function () { expect(matchesQuery(caltrainStation, q)).toBe(false); expect(matchesQuery(santaClara, q)).toBe(false); - q = new Parse.Query('Checkin').withinGeoBox( - 'location', + q = new Parse.Query("Checkin").withinGeoBox( + "location", new Parse.GeoPoint(37.708813, -122.373962), new Parse.GeoPoint(37.822802, -122.526398) ); @@ -527,254 +527,254 @@ describe('matchesQuery', function () { expect(matchesQuery(santaClara, q)).toBe(false); }); - it('matches on subobjects with dot notation', function () { + it("matches on subobjects with dot notation", function () { const message = { - id: new Id('Message', 'O1'), - text: 'content', - status: { x: 'read', y: 'delivered' }, + id: new Id("Message", "O1"), + text: "content", + status: { x: "read", y: "delivered" }, }; - let q = new Parse.Query('Message'); - q.equalTo('status.x', 'read'); + let q = new Parse.Query("Message"); + q.equalTo("status.x", "read"); expect(matchesQuery(message, q)).toBe(true); - q = new Parse.Query('Message'); - q.equalTo('status.z', 'read'); + q = new Parse.Query("Message"); + q.equalTo("status.z", "read"); expect(matchesQuery(message, q)).toBe(false); - q = new Parse.Query('Message'); - q.equalTo('status.x', 'delivered'); + q = new Parse.Query("Message"); + q.equalTo("status.x", "delivered"); expect(matchesQuery(message, q)).toBe(false); - q = new Parse.Query('Message'); - q.notEqualTo('status.x', 'read'); + q = new Parse.Query("Message"); + q.notEqualTo("status.x", "read"); expect(matchesQuery(message, q)).toBe(false); - q = new Parse.Query('Message'); - q.notEqualTo('status.z', 'read'); + q = new Parse.Query("Message"); + q.notEqualTo("status.z", "read"); expect(matchesQuery(message, q)).toBe(true); - q = new Parse.Query('Message'); - q.notEqualTo('status.x', 'delivered'); + q = new Parse.Query("Message"); + q.notEqualTo("status.x", "delivered"); expect(matchesQuery(message, q)).toBe(true); - q = new Parse.Query('Message'); - q.exists('status.x'); + q = new Parse.Query("Message"); + q.exists("status.x"); expect(matchesQuery(message, q)).toBe(true); - q = new Parse.Query('Message'); - q.exists('status.z'); + q = new Parse.Query("Message"); + q.exists("status.z"); expect(matchesQuery(message, q)).toBe(false); - q = new Parse.Query('Message'); - q.exists('nonexistent.x'); + q = new Parse.Query("Message"); + q.exists("nonexistent.x"); expect(matchesQuery(message, q)).toBe(false); - q = new Parse.Query('Message'); - q.doesNotExist('status.x'); + q = new Parse.Query("Message"); + q.doesNotExist("status.x"); expect(matchesQuery(message, q)).toBe(false); - q = new Parse.Query('Message'); - q.doesNotExist('status.z'); + q = new Parse.Query("Message"); + q.doesNotExist("status.z"); expect(matchesQuery(message, q)).toBe(true); - q = new Parse.Query('Message'); - q.doesNotExist('nonexistent.z'); + q = new Parse.Query("Message"); + q.doesNotExist("nonexistent.z"); expect(matchesQuery(message, q)).toBe(true); - q = new Parse.Query('Message'); - q.equalTo('status.x', 'read'); - q.doesNotExist('status.y'); + q = new Parse.Query("Message"); + q.equalTo("status.x", "read"); + q.doesNotExist("status.y"); expect(matchesQuery(message, q)).toBe(false); }); function pointer(className, objectId) { - return { __type: 'Pointer', className, objectId }; + return { __type: "Pointer", className, objectId }; } - it('should support containedIn with pointers', () => { + it("should support containedIn with pointers", () => { const message = { - id: new Id('Message', 'O1'), - profile: pointer('Profile', 'abc'), + id: new Id("Message", "O1"), + profile: pointer("Profile", "abc"), }; - let q = new Parse.Query('Message'); - q.containedIn('profile', [ - Parse.Object.fromJSON({ className: 'Profile', objectId: 'abc' }), - Parse.Object.fromJSON({ className: 'Profile', objectId: 'def' }), + let q = new Parse.Query("Message"); + q.containedIn("profile", [ + Parse.Object.fromJSON({ className: "Profile", objectId: "abc" }), + Parse.Object.fromJSON({ className: "Profile", objectId: "def" }), ]); expect(matchesQuery(message, q)).toBe(true); - q = new Parse.Query('Message'); - q.containedIn('profile', [ - Parse.Object.fromJSON({ className: 'Profile', objectId: 'ghi' }), - Parse.Object.fromJSON({ className: 'Profile', objectId: 'def' }), + q = new Parse.Query("Message"); + q.containedIn("profile", [ + Parse.Object.fromJSON({ className: "Profile", objectId: "ghi" }), + Parse.Object.fromJSON({ className: "Profile", objectId: "def" }), ]); expect(matchesQuery(message, q)).toBe(false); }); - it('should support containedIn with array of pointers', () => { + it("should support containedIn with array of pointers", () => { const message = { - id: new Id('Message', 'O2'), - profiles: [pointer('Profile', 'yeahaw'), pointer('Profile', 'yes')], + id: new Id("Message", "O2"), + profiles: [pointer("Profile", "yeahaw"), pointer("Profile", "yes")], }; - let q = new Parse.Query('Message'); - q.containedIn('profiles', [ - Parse.Object.fromJSON({ className: 'Profile', objectId: 'no' }), - Parse.Object.fromJSON({ className: 'Profile', objectId: 'yes' }), + let q = new Parse.Query("Message"); + q.containedIn("profiles", [ + Parse.Object.fromJSON({ className: "Profile", objectId: "no" }), + Parse.Object.fromJSON({ className: "Profile", objectId: "yes" }), ]); expect(matchesQuery(message, q)).toBe(true); - q = new Parse.Query('Message'); - q.containedIn('profiles', [ - Parse.Object.fromJSON({ className: 'Profile', objectId: 'no' }), - Parse.Object.fromJSON({ className: 'Profile', objectId: 'nope' }), + q = new Parse.Query("Message"); + q.containedIn("profiles", [ + Parse.Object.fromJSON({ className: "Profile", objectId: "no" }), + Parse.Object.fromJSON({ className: "Profile", objectId: "nope" }), ]); expect(matchesQuery(message, q)).toBe(false); }); - it('should support notContainedIn with pointers', () => { + it("should support notContainedIn with pointers", () => { let message = { - id: new Id('Message', 'O1'), - profile: pointer('Profile', 'abc'), + id: new Id("Message", "O1"), + profile: pointer("Profile", "abc"), }; - let q = new Parse.Query('Message'); - q.notContainedIn('profile', [ - Parse.Object.fromJSON({ className: 'Profile', objectId: 'def' }), - Parse.Object.fromJSON({ className: 'Profile', objectId: 'ghi' }), + let q = new Parse.Query("Message"); + q.notContainedIn("profile", [ + Parse.Object.fromJSON({ className: "Profile", objectId: "def" }), + Parse.Object.fromJSON({ className: "Profile", objectId: "ghi" }), ]); expect(matchesQuery(message, q)).toBe(true); message = { - id: new Id('Message', 'O1'), - profile: pointer('Profile', 'def'), + id: new Id("Message", "O1"), + profile: pointer("Profile", "def"), }; - q = new Parse.Query('Message'); - q.notContainedIn('profile', [ - Parse.Object.fromJSON({ className: 'Profile', objectId: 'ghi' }), - Parse.Object.fromJSON({ className: 'Profile', objectId: 'def' }), + q = new Parse.Query("Message"); + q.notContainedIn("profile", [ + Parse.Object.fromJSON({ className: "Profile", objectId: "ghi" }), + Parse.Object.fromJSON({ className: "Profile", objectId: "def" }), ]); expect(matchesQuery(message, q)).toBe(false); }); - it('should support containedIn queries with [objectId]', () => { + it("should support containedIn queries with [objectId]", () => { let message = { - id: new Id('Message', 'O1'), - profile: pointer('Profile', 'abc'), + id: new Id("Message", "O1"), + profile: pointer("Profile", "abc"), }; - let q = new Parse.Query('Message'); - q.containedIn('profile', ['abc', 'def']); + let q = new Parse.Query("Message"); + q.containedIn("profile", ["abc", "def"]); expect(matchesQuery(message, q)).toBe(true); message = { - id: new Id('Message', 'O1'), - profile: pointer('Profile', 'ghi'), + id: new Id("Message", "O1"), + profile: pointer("Profile", "ghi"), }; - q = new Parse.Query('Message'); - q.containedIn('profile', ['abc', 'def']); + q = new Parse.Query("Message"); + q.containedIn("profile", ["abc", "def"]); expect(matchesQuery(message, q)).toBe(false); }); - it('should support notContainedIn queries with [objectId]', () => { + it("should support notContainedIn queries with [objectId]", () => { let message = { - id: new Id('Message', 'O1'), - profile: pointer('Profile', 'ghi'), + id: new Id("Message", "O1"), + profile: pointer("Profile", "ghi"), }; - let q = new Parse.Query('Message'); - q.notContainedIn('profile', ['abc', 'def']); + let q = new Parse.Query("Message"); + q.notContainedIn("profile", ["abc", "def"]); expect(matchesQuery(message, q)).toBe(true); message = { - id: new Id('Message', 'O1'), - profile: pointer('Profile', 'ghi'), + id: new Id("Message", "O1"), + profile: pointer("Profile", "ghi"), }; - q = new Parse.Query('Message'); - q.notContainedIn('profile', ['abc', 'def', 'ghi']); + q = new Parse.Query("Message"); + q.notContainedIn("profile", ["abc", "def", "ghi"]); expect(matchesQuery(message, q)).toBe(false); }); - it('matches on Date', () => { + it("matches on Date", () => { // given const now = new Date(); const obj = { - id: new Id('Person', '01'), + id: new Id("Person", "01"), dateObject: now, dateJSON: { - __type: 'Date', + __type: "Date", iso: now.toISOString(), }, }; // when, then: Equal - let q = new Parse.Query('Person'); - q.equalTo('dateObject', now); - q.equalTo('dateJSON', now); + let q = new Parse.Query("Person"); + q.equalTo("dateObject", now); + q.equalTo("dateJSON", now); expect(matchesQuery(Object.assign({}, obj), q)).toBe(true); // when, then: lessThan const future = Date(now.getTime() + 1000); - q = new Parse.Query('Person'); - q.lessThan('dateObject', future); - q.lessThan('dateJSON', future); + q = new Parse.Query("Person"); + q.lessThan("dateObject", future); + q.lessThan("dateJSON", future); expect(matchesQuery(Object.assign({}, obj), q)).toBe(true); // when, then: lessThanOrEqualTo - q = new Parse.Query('Person'); - q.lessThanOrEqualTo('dateObject', now); - q.lessThanOrEqualTo('dateJSON', now); + q = new Parse.Query("Person"); + q.lessThanOrEqualTo("dateObject", now); + q.lessThanOrEqualTo("dateJSON", now); expect(matchesQuery(Object.assign({}, obj), q)).toBe(true); // when, then: greaterThan const past = Date(now.getTime() - 1000); - q = new Parse.Query('Person'); - q.greaterThan('dateObject', past); - q.greaterThan('dateJSON', past); + q = new Parse.Query("Person"); + q.greaterThan("dateObject", past); + q.greaterThan("dateJSON", past); expect(matchesQuery(Object.assign({}, obj), q)).toBe(true); // when, then: greaterThanOrEqualTo - q = new Parse.Query('Person'); - q.greaterThanOrEqualTo('dateObject', now); - q.greaterThanOrEqualTo('dateJSON', now); + q = new Parse.Query("Person"); + q.greaterThanOrEqualTo("dateObject", now); + q.greaterThanOrEqualTo("dateJSON", now); expect(matchesQuery(Object.assign({}, obj), q)).toBe(true); }); - it('should support containedBy query', () => { + it("should support containedBy query", () => { const obj1 = { - id: new Id('Numbers', 'N1'), + id: new Id("Numbers", "N1"), numbers: [0, 1, 2], }; const obj2 = { - id: new Id('Numbers', 'N2'), + id: new Id("Numbers", "N2"), numbers: [2, 0], }; const obj3 = { - id: new Id('Numbers', 'N3'), + id: new Id("Numbers", "N3"), numbers: [1, 2, 3, 4], }; - const q = new Parse.Query('Numbers'); - q.containedBy('numbers', [1, 2, 3, 4, 5]); + const q = new Parse.Query("Numbers"); + q.containedBy("numbers", [1, 2, 3, 4, 5]); expect(matchesQuery(obj1, q)).toBe(false); expect(matchesQuery(obj2, q)).toBe(false); expect(matchesQuery(obj3, q)).toBe(true); }); - it('should support withinPolygon query', () => { + it("should support withinPolygon query", () => { const sacramento = { - id: new Id('Location', 'L1'), + id: new Id("Location", "L1"), location: new Parse.GeoPoint(38.52, -121.5), - name: 'Sacramento', + name: "Sacramento", }; const honolulu = { - id: new Id('Location', 'L2'), + id: new Id("Location", "L2"), location: new Parse.GeoPoint(21.35, -157.93), - name: 'Honolulu', + name: "Honolulu", }; const sf = { - id: new Id('Location', 'L3'), + id: new Id("Location", "L3"), location: new Parse.GeoPoint(37.75, -122.68), - name: 'San Francisco', + name: "San Francisco", }; const points = [ @@ -783,15 +783,15 @@ describe('matchesQuery', function () { new Parse.GeoPoint(37.68, -122.9), new Parse.GeoPoint(37.68, -122.33), ]; - const q = new Parse.Query('Location'); - q.withinPolygon('location', points); + const q = new Parse.Query("Location"); + q.withinPolygon("location", points); expect(matchesQuery(sacramento, q)).toBe(false); expect(matchesQuery(honolulu, q)).toBe(false); expect(matchesQuery(sf, q)).toBe(true); }); - it('should support polygonContains query', () => { + it("should support polygonContains query", () => { const p1 = [ [0, 0], [0, 1], @@ -813,21 +813,21 @@ describe('matchesQuery', function () { ]; const obj1 = { - id: new Id('Bounds', 'B1'), + id: new Id("Bounds", "B1"), polygon: new Parse.Polygon(p1), }; const obj2 = { - id: new Id('Bounds', 'B2'), + id: new Id("Bounds", "B2"), polygon: new Parse.Polygon(p2), }; const obj3 = { - id: new Id('Bounds', 'B3'), + id: new Id("Bounds", "B3"), polygon: new Parse.Polygon(p3), }; const point = new Parse.GeoPoint(0.5, 0.5); - const q = new Parse.Query('Bounds'); - q.polygonContains('polygon', point); + const q = new Parse.Query("Bounds"); + q.polygonContains("polygon", point); expect(matchesQuery(obj1, q)).toBe(true); expect(matchesQuery(obj2, q)).toBe(true); diff --git a/spec/RateLimit.spec.js b/spec/RateLimit.spec.js index 3c57810702..0ebf01e9d4 100644 --- a/spec/RateLimit.spec.js +++ b/spec/RateLimit.spec.js @@ -1,453 +1,483 @@ -const RedisCacheAdapter = require('../lib/Adapters/Cache/RedisCacheAdapter').default; -describe('rate limit', () => { - it('can limit cloud functions', async () => { - Parse.Cloud.define('test', () => 'Abc'); +const RedisCacheAdapter = + require("../lib/Adapters/Cache/RedisCacheAdapter").default; +describe("rate limit", () => { + it("can limit cloud functions", async () => { + Parse.Cloud.define("test", () => "Abc"); await reconfigureServer({ rateLimit: [ { - requestPath: '/functions/*', + requestPath: "/functions/*", requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: 'Too many requests', + errorResponseMessage: "Too many requests", includeInternalRequests: true, }, ], }); - const response1 = await Parse.Cloud.run('test'); - expect(response1).toBe('Abc'); - await expectAsync(Parse.Cloud.run('test')).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') + const response1 = await Parse.Cloud.run("test"); + expect(response1).toBe("Abc"); + await expectAsync(Parse.Cloud.run("test")).toBeRejectedWith( + new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") ); }); - it('can limit cloud functions with user session token', async () => { - await Parse.User.signUp('myUser', 'password'); - Parse.Cloud.define('test', () => 'Abc'); + it("can limit cloud functions with user session token", async () => { + await Parse.User.signUp("myUser", "password"); + Parse.Cloud.define("test", () => "Abc"); await reconfigureServer({ rateLimit: [ { - requestPath: '/functions/*', + requestPath: "/functions/*", requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: 'Too many requests', + errorResponseMessage: "Too many requests", includeInternalRequests: true, }, ], }); - const response1 = await Parse.Cloud.run('test'); - expect(response1).toBe('Abc'); - await expectAsync(Parse.Cloud.run('test')).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') + const response1 = await Parse.Cloud.run("test"); + expect(response1).toBe("Abc"); + await expectAsync(Parse.Cloud.run("test")).toBeRejectedWith( + new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") ); }); - it('can add global limit', async () => { - Parse.Cloud.define('test', () => 'Abc'); + it("can add global limit", async () => { + Parse.Cloud.define("test", () => "Abc"); await reconfigureServer({ rateLimit: { - requestPath: '*', + requestPath: "*", requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: 'Too many requests', + errorResponseMessage: "Too many requests", includeInternalRequests: true, }, }); - const response1 = await Parse.Cloud.run('test'); - expect(response1).toBe('Abc'); - await expectAsync(Parse.Cloud.run('test')).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') + const response1 = await Parse.Cloud.run("test"); + expect(response1).toBe("Abc"); + await expectAsync(Parse.Cloud.run("test")).toBeRejectedWith( + new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") ); - await expectAsync(new Parse.Object('Test').save()).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') + await expectAsync(new Parse.Object("Test").save()).toBeRejectedWith( + new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") ); }); - it('can limit cloud with validator', async () => { - Parse.Cloud.define('test', () => 'Abc', { + it("can limit cloud with validator", async () => { + Parse.Cloud.define("test", () => "Abc", { rateLimit: { requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: 'Too many requests', + errorResponseMessage: "Too many requests", includeInternalRequests: true, }, }); - const response1 = await Parse.Cloud.run('test'); - expect(response1).toBe('Abc'); - await expectAsync(Parse.Cloud.run('test')).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') + const response1 = await Parse.Cloud.run("test"); + expect(response1).toBe("Abc"); + await expectAsync(Parse.Cloud.run("test")).toBeRejectedWith( + new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") ); }); - it('can skip with masterKey', async () => { - Parse.Cloud.define('test', () => 'Abc'); + it("can skip with masterKey", async () => { + Parse.Cloud.define("test", () => "Abc"); await reconfigureServer({ rateLimit: [ { - requestPath: '/functions/*', + requestPath: "/functions/*", requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: 'Too many requests', + errorResponseMessage: "Too many requests", includeInternalRequests: true, }, ], }); - const response1 = await Parse.Cloud.run('test', null, { useMasterKey: true }); - expect(response1).toBe('Abc'); - const response2 = await Parse.Cloud.run('test', null, { useMasterKey: true }); - expect(response2).toBe('Abc'); + const response1 = await Parse.Cloud.run("test", null, { + useMasterKey: true, + }); + expect(response1).toBe("Abc"); + const response2 = await Parse.Cloud.run("test", null, { + useMasterKey: true, + }); + expect(response2).toBe("Abc"); }); - it('should run with masterKey', async () => { - Parse.Cloud.define('test', () => 'Abc'); + it("should run with masterKey", async () => { + Parse.Cloud.define("test", () => "Abc"); await reconfigureServer({ rateLimit: [ { - requestPath: '/functions/*', + requestPath: "/functions/*", requestTimeWindow: 10000, requestCount: 1, includeMasterKey: true, - errorResponseMessage: 'Too many requests', + errorResponseMessage: "Too many requests", includeInternalRequests: true, }, ], }); - const response1 = await Parse.Cloud.run('test', null, { useMasterKey: true }); - expect(response1).toBe('Abc'); - await expectAsync(Parse.Cloud.run('test')).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') + const response1 = await Parse.Cloud.run("test", null, { + useMasterKey: true, + }); + expect(response1).toBe("Abc"); + await expectAsync(Parse.Cloud.run("test")).toBeRejectedWith( + new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") ); }); - it('can limit saving objects', async () => { + it("can limit saving objects", async () => { await reconfigureServer({ rateLimit: [ { - requestPath: '/classes/*', + requestPath: "/classes/*", requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: 'Too many requests', + errorResponseMessage: "Too many requests", includeInternalRequests: true, }, ], }); - const obj = new Parse.Object('Test'); + const obj = new Parse.Object("Test"); await obj.save(); await expectAsync(obj.save()).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') + new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") ); }); - it('can set method to post', async () => { + it("can set method to post", async () => { await reconfigureServer({ rateLimit: [ { - requestPath: '/classes/*', + requestPath: "/classes/*", requestTimeWindow: 10000, requestCount: 1, - requestMethods: 'POST', - errorResponseMessage: 'Too many requests', + requestMethods: "POST", + errorResponseMessage: "Too many requests", includeInternalRequests: true, }, ], }); - const obj = new Parse.Object('Test'); + const obj = new Parse.Object("Test"); await obj.save(); await obj.save(); - const obj2 = new Parse.Object('Test'); + const obj2 = new Parse.Object("Test"); await expectAsync(obj2.save()).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') + new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") ); }); - it('can use a validator for post', async () => { - Parse.Cloud.beforeSave('Test', () => {}, { + it("can use a validator for post", async () => { + Parse.Cloud.beforeSave("Test", () => {}, { rateLimit: { requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: 'Too many requests', + errorResponseMessage: "Too many requests", includeInternalRequests: true, }, }); - const obj = new Parse.Object('Test'); + const obj = new Parse.Object("Test"); await obj.save(); await expectAsync(obj.save()).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') + new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") ); }); - it('can use a validator for file', async () => { + it("can use a validator for file", async () => { Parse.Cloud.beforeSave(Parse.File, () => {}, { rateLimit: { requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: 'Too many requests', + errorResponseMessage: "Too many requests", includeInternalRequests: true, }, }); - const file = new Parse.File('yolo.txt', [1, 2, 3], 'text/plain'); + const file = new Parse.File("yolo.txt", [1, 2, 3], "text/plain"); await file.save(); - const file2 = new Parse.File('yolo.txt', [1, 2, 3], 'text/plain'); + const file2 = new Parse.File("yolo.txt", [1, 2, 3], "text/plain"); await expectAsync(file2.save()).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') + new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") ); }); - it('can set method to get', async () => { + it("can set method to get", async () => { await reconfigureServer({ rateLimit: [ { - requestPath: '/classes/Test', + requestPath: "/classes/Test", requestTimeWindow: 10000, requestCount: 1, - requestMethods: 'GET', - errorResponseMessage: 'Too many requests', + requestMethods: "GET", + errorResponseMessage: "Too many requests", includeInternalRequests: true, }, ], }); - const obj = new Parse.Object('Test'); + const obj = new Parse.Object("Test"); await obj.save(); await obj.save(); - await new Parse.Query('Test').first(); - await expectAsync(new Parse.Query('Test').first()).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') + await new Parse.Query("Test").first(); + await expectAsync(new Parse.Query("Test").first()).toBeRejectedWith( + new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") ); }); - it('can use a validator', async () => { + it("can use a validator", async () => { await reconfigureServer({ silent: false }); - Parse.Cloud.beforeFind('TestObject', () => {}, { + Parse.Cloud.beforeFind("TestObject", () => {}, { rateLimit: { requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: 'Too many requests', + errorResponseMessage: "Too many requests", includeInternalRequests: true, }, }); - const obj = new Parse.Object('TestObject'); + const obj = new Parse.Object("TestObject"); await obj.save(); await obj.save(); - await new Parse.Query('TestObject').first(); - await expectAsync(new Parse.Query('TestObject').first()).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') + await new Parse.Query("TestObject").first(); + await expectAsync(new Parse.Query("TestObject").first()).toBeRejectedWith( + new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") ); - await expectAsync(new Parse.Query('TestObject').get('abc')).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') + await expectAsync( + new Parse.Query("TestObject").get("abc") + ).toBeRejectedWith( + new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") ); }); - it('can set method to delete', async () => { + it("can set method to delete", async () => { await reconfigureServer({ rateLimit: [ { - requestPath: '/classes/Test/*', + requestPath: "/classes/Test/*", requestTimeWindow: 10000, requestCount: 1, - requestMethods: 'DELETE', - errorResponseMessage: 'Too many requests', + requestMethods: "DELETE", + errorResponseMessage: "Too many requests", includeInternalRequests: true, }, ], }); - const obj = new Parse.Object('Test'); + const obj = new Parse.Object("Test"); await obj.save(); await obj.destroy(); await expectAsync(obj.destroy()).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') + new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") ); }); - it('can set beforeDelete', async () => { - const obj = new Parse.Object('TestDelete'); + it("can set beforeDelete", async () => { + const obj = new Parse.Object("TestDelete"); await obj.save(); - Parse.Cloud.beforeDelete('TestDelete', () => {}, { + Parse.Cloud.beforeDelete("TestDelete", () => {}, { rateLimit: { requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: 'Too many requests', + errorResponseMessage: "Too many requests", includeInternalRequests: true, }, }); await obj.destroy(); await expectAsync(obj.destroy()).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') + new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") ); }); - it('can set beforeLogin', async () => { + it("can set beforeLogin", async () => { Parse.Cloud.beforeLogin(() => {}, { rateLimit: { requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: 'Too many requests', + errorResponseMessage: "Too many requests", includeInternalRequests: true, }, }); - await Parse.User.signUp('myUser', 'password'); - await Parse.User.logIn('myUser', 'password'); - await expectAsync(Parse.User.logIn('myUser', 'password')).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') + await Parse.User.signUp("myUser", "password"); + await Parse.User.logIn("myUser", "password"); + await expectAsync(Parse.User.logIn("myUser", "password")).toBeRejectedWith( + new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") ); }); - it('can define limits via rateLimit and define', async () => { + it("can define limits via rateLimit and define", async () => { await reconfigureServer({ rateLimit: [ { - requestPath: '/functions/*', + requestPath: "/functions/*", requestTimeWindow: 10000, requestCount: 100, - errorResponseMessage: 'Too many requests', + errorResponseMessage: "Too many requests", includeInternalRequests: true, }, ], }); - Parse.Cloud.define('test', () => 'Abc', { + Parse.Cloud.define("test", () => "Abc", { rateLimit: { requestTimeWindow: 10000, requestCount: 1, includeInternalRequests: true, }, }); - const response1 = await Parse.Cloud.run('test'); - expect(response1).toBe('Abc'); - await expectAsync(Parse.Cloud.run('test')).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests.') + const response1 = await Parse.Cloud.run("test"); + expect(response1).toBe("Abc"); + await expectAsync(Parse.Cloud.run("test")).toBeRejectedWith( + new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests.") ); }); - it('does not limit internal calls', async () => { + it("does not limit internal calls", async () => { await reconfigureServer({ rateLimit: [ { - requestPath: '/functions/*', + requestPath: "/functions/*", requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: 'Too many requests', + errorResponseMessage: "Too many requests", }, ], }); - Parse.Cloud.define('test1', () => 'Abc'); - Parse.Cloud.define('test2', async () => { - await Parse.Cloud.run('test1'); - await Parse.Cloud.run('test1'); + Parse.Cloud.define("test1", () => "Abc"); + Parse.Cloud.define("test2", async () => { + await Parse.Cloud.run("test1"); + await Parse.Cloud.run("test1"); }); - await Parse.Cloud.run('test2'); + await Parse.Cloud.run("test2"); }); - describe('zone', () => { - const middlewares = require('../lib/middlewares'); - it('can use global zone', async () => { + describe("zone", () => { + const middlewares = require("../lib/middlewares"); + it("can use global zone", async () => { await reconfigureServer({ rateLimit: { - requestPath: '*', + requestPath: "*", requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: 'Too many requests', + errorResponseMessage: "Too many requests", includeInternalRequests: true, zone: Parse.Server.RateLimitZone.global, }, }); const fakeReq = { - originalUrl: 'http://example.com/parse/', - url: 'http://example.com/', + originalUrl: "http://example.com/parse/", + url: "http://example.com/", body: { - _ApplicationId: 'test', + _ApplicationId: "test", }, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }, get: key => { return fakeReq.headers[key]; }, }; - fakeReq.ip = '127.0.0.1'; - let fakeRes = jasmine.createSpyObj('fakeRes', ['end', 'status', 'setHeader', 'json']); - await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve)); - fakeReq.ip = '127.0.0.2'; - fakeRes = jasmine.createSpyObj('fakeRes', ['end', 'status', 'setHeader']); + fakeReq.ip = "127.0.0.1"; + let fakeRes = jasmine.createSpyObj("fakeRes", [ + "end", + "status", + "setHeader", + "json", + ]); + await new Promise(resolve => + middlewares.handleParseHeaders(fakeReq, fakeRes, resolve) + ); + fakeReq.ip = "127.0.0.2"; + fakeRes = jasmine.createSpyObj("fakeRes", ["end", "status", "setHeader"]); let resolvingPromise; const promise = new Promise(resolve => { resolvingPromise = resolve; }); - fakeRes.json = jasmine.createSpy('json').and.callFake(resolvingPromise); + fakeRes.json = jasmine.createSpy("json").and.callFake(resolvingPromise); middlewares.handleParseHeaders(fakeReq, fakeRes, () => { - throw 'Should not call next'; + throw "Should not call next"; }); await promise; expect(fakeRes.status).toHaveBeenCalledWith(429); expect(fakeRes.json).toHaveBeenCalledWith({ code: Parse.Error.CONNECTION_FAILED, - error: 'Too many requests', + error: "Too many requests", }); }); - it('can use session zone', async () => { + it("can use session zone", async () => { await reconfigureServer({ rateLimit: { - requestPath: '/functions/*', + requestPath: "/functions/*", requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: 'Too many requests', + errorResponseMessage: "Too many requests", includeInternalRequests: true, zone: Parse.Server.RateLimitZone.session, }, }); - Parse.Cloud.define('test', () => 'Abc'); - await Parse.User.signUp('username', 'password'); - await Parse.Cloud.run('test'); - await expectAsync(Parse.Cloud.run('test')).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') + Parse.Cloud.define("test", () => "Abc"); + await Parse.User.signUp("username", "password"); + await Parse.Cloud.run("test"); + await expectAsync(Parse.Cloud.run("test")).toBeRejectedWith( + new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") ); - await Parse.User.logIn('username', 'password'); - await Parse.Cloud.run('test'); + await Parse.User.logIn("username", "password"); + await Parse.Cloud.run("test"); }); - it('can use user zone', async () => { + it("can use user zone", async () => { await reconfigureServer({ rateLimit: { - requestPath: '/functions/*', + requestPath: "/functions/*", requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: 'Too many requests', + errorResponseMessage: "Too many requests", includeInternalRequests: true, zone: Parse.Server.RateLimitZone.user, }, }); - Parse.Cloud.define('test', () => 'Abc'); - await Parse.User.signUp('username', 'password'); - await Parse.Cloud.run('test'); - await expectAsync(Parse.Cloud.run('test')).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') + Parse.Cloud.define("test", () => "Abc"); + await Parse.User.signUp("username", "password"); + await Parse.Cloud.run("test"); + await expectAsync(Parse.Cloud.run("test")).toBeRejectedWith( + new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") ); - await Parse.User.logIn('username', 'password'); - await expectAsync(Parse.Cloud.run('test')).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') + await Parse.User.logIn("username", "password"); + await expectAsync(Parse.Cloud.run("test")).toBeRejectedWith( + new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") ); }); }); - it('can validate rateLimit', async () => { - const Config = require('../lib/Config'); - const validateRateLimit = ({ rateLimit }) => Config.validateRateLimit(rateLimit); + it("can validate rateLimit", async () => { + const Config = require("../lib/Config"); + const validateRateLimit = ({ rateLimit }) => + Config.validateRateLimit(rateLimit); expect(() => - validateRateLimit({ rateLimit: 'a', requestTimeWindow: 1000, requestCount: 3 }) - ).toThrow('rateLimit must be an array or object'); - expect(() => validateRateLimit({ rateLimit: ['a'] })).toThrow( - 'rateLimit must be an array of objects' - ); - expect(() => validateRateLimit({ rateLimit: [{ requestPath: [] }] })).toThrow( - 'rateLimit.requestPath must be a string' + validateRateLimit({ + rateLimit: "a", + requestTimeWindow: 1000, + requestCount: 3, + }) + ).toThrow("rateLimit must be an array or object"); + expect(() => validateRateLimit({ rateLimit: ["a"] })).toThrow( + "rateLimit must be an array of objects" ); expect(() => - validateRateLimit({ rateLimit: [{ requestTimeWindow: [], requestPath: 'a' }] }) - ).toThrow('rateLimit.requestTimeWindow must be a number'); + validateRateLimit({ rateLimit: [{ requestPath: [] }] }) + ).toThrow("rateLimit.requestPath must be a string"); expect(() => validateRateLimit({ - rateLimit: [{ requestPath: 'a', requestTimeWindow: 1000, requestCount: 3, zone: 'abc' }], + rateLimit: [{ requestTimeWindow: [], requestPath: "a" }], + }) + ).toThrow("rateLimit.requestTimeWindow must be a number"); + expect(() => + validateRateLimit({ + rateLimit: [ + { + requestPath: "a", + requestTimeWindow: 1000, + requestCount: 3, + zone: "abc", + }, + ], }) - ).toThrow('rateLimit.zone must be one of global, session, user, or ip'); + ).toThrow("rateLimit.zone must be one of global, session, user, or ip"); expect(() => validateRateLimit({ rateLimit: [ @@ -455,64 +485,84 @@ describe('rate limit', () => { includeInternalRequests: [], requestTimeWindow: 1000, requestCount: 3, - requestPath: 'a', + requestPath: "a", }, ], }) - ).toThrow('rateLimit.includeInternalRequests must be a boolean'); + ).toThrow("rateLimit.includeInternalRequests must be a boolean"); expect(() => validateRateLimit({ - rateLimit: [{ requestCount: [], requestTimeWindow: 1000, requestPath: 'a' }], + rateLimit: [ + { requestCount: [], requestTimeWindow: 1000, requestPath: "a" }, + ], }) - ).toThrow('rateLimit.requestCount must be a number'); + ).toThrow("rateLimit.requestCount must be a number"); expect(() => validateRateLimit({ rateLimit: [ - { errorResponseMessage: [], requestTimeWindow: 1000, requestCount: 3, requestPath: 'a' }, + { + errorResponseMessage: [], + requestTimeWindow: 1000, + requestCount: 3, + requestPath: "a", + }, ], }) - ).toThrow('rateLimit.errorResponseMessage must be a string'); + ).toThrow("rateLimit.errorResponseMessage must be a string"); expect(() => - validateRateLimit({ rateLimit: [{ requestCount: 3, requestPath: 'abc' }] }) - ).toThrow('rateLimit.requestTimeWindow must be defined'); + validateRateLimit({ + rateLimit: [{ requestCount: 3, requestPath: "abc" }], + }) + ).toThrow("rateLimit.requestTimeWindow must be defined"); expect(() => - validateRateLimit({ rateLimit: [{ requestTimeWindow: 3, requestPath: 'abc' }] }) - ).toThrow('rateLimit.requestCount must be defined'); + validateRateLimit({ + rateLimit: [{ requestTimeWindow: 3, requestPath: "abc" }], + }) + ).toThrow("rateLimit.requestCount must be defined"); expect(() => - validateRateLimit({ rateLimit: [{ requestTimeWindow: 3, requestCount: 'abc' }] }) - ).toThrow('rateLimit.requestPath must be defined'); + validateRateLimit({ + rateLimit: [{ requestTimeWindow: 3, requestCount: "abc" }], + }) + ).toThrow("rateLimit.requestPath must be defined"); await expectAsync( reconfigureServer({ - rateLimit: [{ requestTimeWindow: 3, requestCount: 1, path: 'abc', requestPath: 'a' }], + rateLimit: [ + { + requestTimeWindow: 3, + requestCount: 1, + path: "abc", + requestPath: "a", + }, + ], }) ).toBeRejectedWith(`Invalid rate limit option "path"`); }); describe_only(() => { - return process.env.PARSE_SERVER_TEST_CACHE === 'redis'; - })('with RedisCache', function () { - it('does work with cache', async () => { + return process.env.PARSE_SERVER_TEST_CACHE === "redis"; + })("with RedisCache", function () { + it("does work with cache", async () => { await reconfigureServer({ rateLimit: [ { - requestPath: '/classes/*', + requestPath: "/classes/*", requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: 'Too many requests', + errorResponseMessage: "Too many requests", includeInternalRequests: true, - redisUrl: 'redis://localhost:6379', + redisUrl: "redis://localhost:6379", }, ], }); - const obj = new Parse.Object('Test'); + const obj = new Parse.Object("Test"); await obj.save(); await expectAsync(obj.save()).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') + new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") ); const cache = new RedisCacheAdapter(); await cache.connect(); - const value = await cache.get('rl:127.0.0.1'); + const value = await cache.get("rl:127.0.0.1"); expect(value).toEqual(2); - const ttl = await cache.client.ttl('rl:127.0.0.1'); + const ttl = await cache.client.ttl("rl:127.0.0.1"); expect(ttl).toEqual(10); }); }); diff --git a/spec/ReadPreferenceOption.spec.js b/spec/ReadPreferenceOption.spec.js index 67b976674b..f4eae62596 100644 --- a/spec/ReadPreferenceOption.spec.js +++ b/spec/ReadPreferenceOption.spec.js @@ -1,8 +1,8 @@ -'use strict'; +"use strict"; -const Parse = require('parse/node'); -const { ReadPreference, Collection } = require('mongodb'); -const request = require('../lib/request'); +const Parse = require("parse/node"); +const { ReadPreference, Collection } = require("mongodb"); +const request = require("../lib/request"); function waitForReplication() { return new Promise(function (resolve) { @@ -10,28 +10,30 @@ function waitForReplication() { }); } -describe_only_db('mongo')('Read preference option', () => { - it('should find in primary by default', done => { - const obj0 = new Parse.Object('MyObject'); - obj0.set('boolKey', false); - const obj1 = new Parse.Object('MyObject'); - obj1.set('boolKey', true); +describe_only_db("mongo")("Read preference option", () => { + it("should find in primary by default", done => { + const obj0 = new Parse.Object("MyObject"); + obj0.set("boolKey", false); + const obj1 = new Parse.Object("MyObject"); + obj1.set("boolKey", true); Parse.Object.saveAll([obj0, obj1]) .then(() => { - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); - const query = new Parse.Query('MyObject'); - query.equalTo('boolKey', false); + const query = new Parse.Query("MyObject"); + query.equalTo("boolKey", false); return query.find().then(results => { expect(results.length).toBe(1); - expect(results[0].get('boolKey')).toBe(false); + expect(results[0].get("boolKey")).toBe(false); let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { myObjectReadPreference = true; - expect(call.object.s.readPreference.mode).toBe(ReadPreference.PRIMARY); + expect(call.object.s.readPreference.mode).toBe( + ReadPreference.PRIMARY + ); } }); @@ -43,10 +45,12 @@ describe_only_db('mongo')('Read preference option', () => { .catch(done.fail); }); - xit('should preserve the read preference set (#4831)', async () => { - const { MongoStorageAdapter } = require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter'); + xit("should preserve the read preference set (#4831)", async () => { + const { + MongoStorageAdapter, + } = require("../lib/Adapters/Storage/Mongo/MongoStorageAdapter"); const adapterOptions = { - uri: 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase', + uri: "mongodb://localhost:27017/parseServerMongoAdapterTestDatabase", mongoOptions: { readPreference: ReadPreference.NEAREST, }, @@ -55,24 +59,24 @@ describe_only_db('mongo')('Read preference option', () => { databaseAdapter: new MongoStorageAdapter(adapterOptions), }); - const obj0 = new Parse.Object('MyObject'); - obj0.set('boolKey', false); - const obj1 = new Parse.Object('MyObject'); - obj1.set('boolKey', true); + const obj0 = new Parse.Object("MyObject"); + obj0.set("boolKey", false); + const obj1 = new Parse.Object("MyObject"); + obj1.set("boolKey", true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); - const query = new Parse.Query('MyObject'); - query.equalTo('boolKey', false); + const query = new Parse.Query("MyObject"); + query.equalTo("boolKey", false); const results = await query.find(); expect(results.length).toBe(1); - expect(results[0].get('boolKey')).toBe(false); + expect(results[0].get("boolKey")).toBe(false); let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { myObjectReadPreference = true; expect(call.args[1].readPreference).toBe(ReadPreference.NEAREST); } @@ -81,30 +85,30 @@ describe_only_db('mongo')('Read preference option', () => { expect(myObjectReadPreference).toBe(true); }); - it('should change read preference in the beforeFind trigger', async () => { - const obj0 = new Parse.Object('MyObject'); - obj0.set('boolKey', false); - const obj1 = new Parse.Object('MyObject'); - obj1.set('boolKey', true); + it("should change read preference in the beforeFind trigger", async () => { + const obj0 = new Parse.Object("MyObject"); + obj0.set("boolKey", false); + const obj1 = new Parse.Object("MyObject"); + obj1.set("boolKey", true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); - Parse.Cloud.beforeFind('MyObject', req => { - req.readPreference = 'SECONDARY'; + Parse.Cloud.beforeFind("MyObject", req => { + req.readPreference = "SECONDARY"; }); await waitForReplication(); - const query = new Parse.Query('MyObject'); - query.equalTo('boolKey', false); + const query = new Parse.Query("MyObject"); + query.equalTo("boolKey", false); const results = await query.find(); expect(results.length).toBe(1); - expect(results[0].get('boolKey')).toBe(false); + expect(results[0].get("boolKey")).toBe(false); let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -112,31 +116,31 @@ describe_only_db('mongo')('Read preference option', () => { expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY); }); - it('should check read preference as case insensitive', async () => { - const obj0 = new Parse.Object('MyObject'); - obj0.set('boolKey', false); - const obj1 = new Parse.Object('MyObject'); - obj1.set('boolKey', true); + it("should check read preference as case insensitive", async () => { + const obj0 = new Parse.Object("MyObject"); + obj0.set("boolKey", false); + const obj1 = new Parse.Object("MyObject"); + obj1.set("boolKey", true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); - Parse.Cloud.beforeFind('MyObject', req => { - req.readPreference = 'sEcOnDarY'; + Parse.Cloud.beforeFind("MyObject", req => { + req.readPreference = "sEcOnDarY"; }); await waitForReplication(); - const query = new Parse.Query('MyObject'); - query.equalTo('boolKey', false); + const query = new Parse.Query("MyObject"); + query.equalTo("boolKey", false); const results = await query.find(); expect(results.length).toBe(1); - expect(results[0].get('boolKey')).toBe(false); + expect(results[0].get("boolKey")).toBe(false); let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -144,31 +148,31 @@ describe_only_db('mongo')('Read preference option', () => { expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY); }); - it('should change read preference in the beforeFind trigger even changing query', async () => { - const obj0 = new Parse.Object('MyObject'); - obj0.set('boolKey', false); - const obj1 = new Parse.Object('MyObject'); - obj1.set('boolKey', true); + it("should change read preference in the beforeFind trigger even changing query", async () => { + const obj0 = new Parse.Object("MyObject"); + obj0.set("boolKey", false); + const obj1 = new Parse.Object("MyObject"); + obj1.set("boolKey", true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); - Parse.Cloud.beforeFind('MyObject', req => { - req.query.equalTo('boolKey', true); - req.readPreference = 'SECONDARY'; + Parse.Cloud.beforeFind("MyObject", req => { + req.query.equalTo("boolKey", true); + req.readPreference = "SECONDARY"; }); await waitForReplication(); - const query = new Parse.Query('MyObject'); - query.equalTo('boolKey', false); + const query = new Parse.Query("MyObject"); + query.equalTo("boolKey", false); const results = await query.find(); expect(results.length).toBe(1); - expect(results[0].get('boolKey')).toBe(true); + expect(results[0].get("boolKey")).toBe(true); let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -176,35 +180,35 @@ describe_only_db('mongo')('Read preference option', () => { expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY); }); - it('should change read preference in the beforeFind trigger even returning query', async () => { - const obj0 = new Parse.Object('MyObject'); - obj0.set('boolKey', false); - const obj1 = new Parse.Object('MyObject'); - obj1.set('boolKey', true); + it("should change read preference in the beforeFind trigger even returning query", async () => { + const obj0 = new Parse.Object("MyObject"); + obj0.set("boolKey", false); + const obj1 = new Parse.Object("MyObject"); + obj1.set("boolKey", true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); - Parse.Cloud.beforeFind('MyObject', req => { - req.readPreference = 'SECONDARY'; + Parse.Cloud.beforeFind("MyObject", req => { + req.readPreference = "SECONDARY"; - const otherQuery = new Parse.Query('MyObject'); - otherQuery.equalTo('boolKey', true); + const otherQuery = new Parse.Query("MyObject"); + otherQuery.equalTo("boolKey", true); return otherQuery; }); await waitForReplication(); - const query = new Parse.Query('MyObject'); - query.equalTo('boolKey', false); + const query = new Parse.Query("MyObject"); + query.equalTo("boolKey", false); const results = await query.find(); expect(results.length).toBe(1); - expect(results[0].get('boolKey')).toBe(true); + expect(results[0].get("boolKey")).toBe(true); let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -212,34 +216,34 @@ describe_only_db('mongo')('Read preference option', () => { expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY); }); - it('should change read preference in the beforeFind trigger even returning promise', async () => { - const obj0 = new Parse.Object('MyObject'); - obj0.set('boolKey', false); - const obj1 = new Parse.Object('MyObject'); - obj1.set('boolKey', true); + it("should change read preference in the beforeFind trigger even returning promise", async () => { + const obj0 = new Parse.Object("MyObject"); + obj0.set("boolKey", false); + const obj1 = new Parse.Object("MyObject"); + obj1.set("boolKey", true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); - Parse.Cloud.beforeFind('MyObject', req => { - req.readPreference = 'SECONDARY'; + Parse.Cloud.beforeFind("MyObject", req => { + req.readPreference = "SECONDARY"; - const otherQuery = new Parse.Query('MyObject'); - otherQuery.equalTo('boolKey', true); + const otherQuery = new Parse.Query("MyObject"); + otherQuery.equalTo("boolKey", true); return Promise.resolve(otherQuery); }); await waitForReplication(); - const query = new Parse.Query('MyObject'); - query.equalTo('boolKey', false); + const query = new Parse.Query("MyObject"); + query.equalTo("boolKey", false); const results = await query.find(); expect(results.length).toBe(1); - expect(results[0].get('boolKey')).toBe(true); + expect(results[0].get("boolKey")).toBe(true); let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -247,30 +251,30 @@ describe_only_db('mongo')('Read preference option', () => { expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY); }); - it('should change read preference to PRIMARY_PREFERRED', async () => { - const obj0 = new Parse.Object('MyObject'); - obj0.set('boolKey', false); - const obj1 = new Parse.Object('MyObject'); - obj1.set('boolKey', true); + it("should change read preference to PRIMARY_PREFERRED", async () => { + const obj0 = new Parse.Object("MyObject"); + obj0.set("boolKey", false); + const obj1 = new Parse.Object("MyObject"); + obj1.set("boolKey", true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); - Parse.Cloud.beforeFind('MyObject', req => { - req.readPreference = 'PRIMARY_PREFERRED'; + Parse.Cloud.beforeFind("MyObject", req => { + req.readPreference = "PRIMARY_PREFERRED"; }); await waitForReplication(); - const query = new Parse.Query('MyObject'); - query.equalTo('boolKey', false); + const query = new Parse.Query("MyObject"); + query.equalTo("boolKey", false); const results = await query.find(); expect(results.length).toBe(1); - expect(results[0].get('boolKey')).toBe(false); + expect(results[0].get("boolKey")).toBe(false); let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -278,30 +282,30 @@ describe_only_db('mongo')('Read preference option', () => { expect(myObjectReadPreference).toEqual(ReadPreference.PRIMARY_PREFERRED); }); - it('should change read preference to SECONDARY_PREFERRED', async () => { - const obj0 = new Parse.Object('MyObject'); - obj0.set('boolKey', false); - const obj1 = new Parse.Object('MyObject'); - obj1.set('boolKey', true); + it("should change read preference to SECONDARY_PREFERRED", async () => { + const obj0 = new Parse.Object("MyObject"); + obj0.set("boolKey", false); + const obj1 = new Parse.Object("MyObject"); + obj1.set("boolKey", true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); - Parse.Cloud.beforeFind('MyObject', req => { - req.readPreference = 'SECONDARY_PREFERRED'; + Parse.Cloud.beforeFind("MyObject", req => { + req.readPreference = "SECONDARY_PREFERRED"; }); await waitForReplication(); - const query = new Parse.Query('MyObject'); - query.equalTo('boolKey', false); + const query = new Parse.Query("MyObject"); + query.equalTo("boolKey", false); const results = await query.find(); expect(results.length).toBe(1); - expect(results[0].get('boolKey')).toBe(false); + expect(results[0].get("boolKey")).toBe(false); let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -309,30 +313,30 @@ describe_only_db('mongo')('Read preference option', () => { expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY_PREFERRED); }); - it('should change read preference to NEAREST', async () => { - const obj0 = new Parse.Object('MyObject'); - obj0.set('boolKey', false); - const obj1 = new Parse.Object('MyObject'); - obj1.set('boolKey', true); + it("should change read preference to NEAREST", async () => { + const obj0 = new Parse.Object("MyObject"); + obj0.set("boolKey", false); + const obj1 = new Parse.Object("MyObject"); + obj1.set("boolKey", true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); - Parse.Cloud.beforeFind('MyObject', req => { - req.readPreference = 'NEAREST'; + Parse.Cloud.beforeFind("MyObject", req => { + req.readPreference = "NEAREST"; }); await waitForReplication(); - const query = new Parse.Query('MyObject'); - query.equalTo('boolKey', false); + const query = new Parse.Query("MyObject"); + query.equalTo("boolKey", false); const results = await query.find(); expect(results.length).toBe(1); - expect(results[0].get('boolKey')).toBe(false); + expect(results[0].get("boolKey")).toBe(false); let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -340,28 +344,28 @@ describe_only_db('mongo')('Read preference option', () => { expect(myObjectReadPreference).toEqual(ReadPreference.NEAREST); }); - it('should change read preference for GET', async () => { - const obj0 = new Parse.Object('MyObject'); - obj0.set('boolKey', false); - const obj1 = new Parse.Object('MyObject'); - obj1.set('boolKey', true); + it("should change read preference for GET", async () => { + const obj0 = new Parse.Object("MyObject"); + obj0.set("boolKey", false); + const obj1 = new Parse.Object("MyObject"); + obj1.set("boolKey", true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); - Parse.Cloud.beforeFind('MyObject', req => { - req.readPreference = 'SECONDARY'; + Parse.Cloud.beforeFind("MyObject", req => { + req.readPreference = "SECONDARY"; }); await waitForReplication(); - const query = new Parse.Query('MyObject'); + const query = new Parse.Query("MyObject"); const result = await query.get(obj0.id); - expect(result.get('boolKey')).toBe(false); + expect(result.get("boolKey")).toBe(false); let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -369,26 +373,26 @@ describe_only_db('mongo')('Read preference option', () => { expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY); }); - it('should change read preference for GET using API', async () => { - const obj0 = new Parse.Object('MyObject'); - obj0.set('boolKey', false); - const obj1 = new Parse.Object('MyObject'); - obj1.set('boolKey', true); + it("should change read preference for GET using API", async () => { + const obj0 = new Parse.Object("MyObject"); + obj0.set("boolKey", false); + const obj1 = new Parse.Object("MyObject"); + obj1.set("boolKey", true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); - Parse.Cloud.beforeFind('MyObject', req => { - req.readPreference = 'SECONDARY'; + Parse.Cloud.beforeFind("MyObject", req => { + req.readPreference = "SECONDARY"; }); await waitForReplication(); const response = await request({ - method: 'GET', - url: 'http://localhost:8378/1/classes/MyObject/' + obj0.id, + method: "GET", + url: "http://localhost:8378/1/classes/MyObject/" + obj0.id, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }, json: true, }); @@ -397,7 +401,7 @@ describe_only_db('mongo')('Read preference option', () => { let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -405,22 +409,25 @@ describe_only_db('mongo')('Read preference option', () => { expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY); }); - it('should change read preference for GET directly from API', async () => { - const obj0 = new Parse.Object('MyObject'); - obj0.set('boolKey', false); - const obj1 = new Parse.Object('MyObject'); - obj1.set('boolKey', true); + it("should change read preference for GET directly from API", async () => { + const obj0 = new Parse.Object("MyObject"); + obj0.set("boolKey", false); + const obj1 = new Parse.Object("MyObject"); + obj1.set("boolKey", true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); await waitForReplication(); const response = await request({ - method: 'GET', - url: 'http://localhost:8378/1/classes/MyObject/' + obj0.id + '?readPreference=SECONDARY', + method: "GET", + url: + "http://localhost:8378/1/classes/MyObject/" + + obj0.id + + "?readPreference=SECONDARY", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }, json: true, }); @@ -428,7 +435,7 @@ describe_only_db('mongo')('Read preference option', () => { let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -436,26 +443,29 @@ describe_only_db('mongo')('Read preference option', () => { expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY); }); - it('should change read preference for GET using API through the beforeFind overriding API option', async () => { - const obj0 = new Parse.Object('MyObject'); - obj0.set('boolKey', false); - const obj1 = new Parse.Object('MyObject'); - obj1.set('boolKey', true); + it("should change read preference for GET using API through the beforeFind overriding API option", async () => { + const obj0 = new Parse.Object("MyObject"); + obj0.set("boolKey", false); + const obj1 = new Parse.Object("MyObject"); + obj1.set("boolKey", true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); - Parse.Cloud.beforeFind('MyObject', req => { - req.readPreference = 'SECONDARY_PREFERRED'; + Parse.Cloud.beforeFind("MyObject", req => { + req.readPreference = "SECONDARY_PREFERRED"; }); await waitForReplication(); const response = await request({ - method: 'GET', - url: 'http://localhost:8378/1/classes/MyObject/' + obj0.id + '?readPreference=SECONDARY', + method: "GET", + url: + "http://localhost:8378/1/classes/MyObject/" + + obj0.id + + "?readPreference=SECONDARY", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }, json: true, }); @@ -463,7 +473,7 @@ describe_only_db('mongo')('Read preference option', () => { let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -471,26 +481,26 @@ describe_only_db('mongo')('Read preference option', () => { expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY_PREFERRED); }); - it('should change read preference for FIND using API through beforeFind trigger', async () => { - const obj0 = new Parse.Object('MyObject'); - obj0.set('boolKey', false); - const obj1 = new Parse.Object('MyObject'); - obj1.set('boolKey', true); + it("should change read preference for FIND using API through beforeFind trigger", async () => { + const obj0 = new Parse.Object("MyObject"); + obj0.set("boolKey", false); + const obj1 = new Parse.Object("MyObject"); + obj1.set("boolKey", true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); - Parse.Cloud.beforeFind('MyObject', req => { - req.readPreference = 'SECONDARY'; + Parse.Cloud.beforeFind("MyObject", req => { + req.readPreference = "SECONDARY"; }); await waitForReplication(); const response = await request({ - method: 'GET', - url: 'http://localhost:8378/1/classes/MyObject/', + method: "GET", + url: "http://localhost:8378/1/classes/MyObject/", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }, json: true, }); @@ -498,7 +508,7 @@ describe_only_db('mongo')('Read preference option', () => { let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -506,22 +516,22 @@ describe_only_db('mongo')('Read preference option', () => { expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY); }); - it('should change read preference for FIND directly from API', async () => { - const obj0 = new Parse.Object('MyObject'); - obj0.set('boolKey', false); - const obj1 = new Parse.Object('MyObject'); - obj1.set('boolKey', true); + it("should change read preference for FIND directly from API", async () => { + const obj0 = new Parse.Object("MyObject"); + obj0.set("boolKey", false); + const obj1 = new Parse.Object("MyObject"); + obj1.set("boolKey", true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); await waitForReplication(); const response = await request({ - method: 'GET', - url: 'http://localhost:8378/1/classes/MyObject?readPreference=SECONDARY', + method: "GET", + url: "http://localhost:8378/1/classes/MyObject?readPreference=SECONDARY", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }, json: true, }); @@ -529,7 +539,7 @@ describe_only_db('mongo')('Read preference option', () => { let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -537,26 +547,26 @@ describe_only_db('mongo')('Read preference option', () => { expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY); }); - it('should change read preference for FIND using API through the beforeFind overriding API option', async () => { - const obj0 = new Parse.Object('MyObject'); - obj0.set('boolKey', false); - const obj1 = new Parse.Object('MyObject'); - obj1.set('boolKey', true); + it("should change read preference for FIND using API through the beforeFind overriding API option", async () => { + const obj0 = new Parse.Object("MyObject"); + obj0.set("boolKey", false); + const obj1 = new Parse.Object("MyObject"); + obj1.set("boolKey", true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); - Parse.Cloud.beforeFind('MyObject', req => { - req.readPreference = 'SECONDARY_PREFERRED'; + Parse.Cloud.beforeFind("MyObject", req => { + req.readPreference = "SECONDARY_PREFERRED"; }); await waitForReplication(); const response = await request({ - method: 'GET', - url: 'http://localhost:8378/1/classes/MyObject/?readPreference=SECONDARY', + method: "GET", + url: "http://localhost:8378/1/classes/MyObject/?readPreference=SECONDARY", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }, json: true, }); @@ -564,7 +574,7 @@ describe_only_db('mongo')('Read preference option', () => { let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -572,21 +582,21 @@ describe_only_db('mongo')('Read preference option', () => { expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY_PREFERRED); }); - xit('should change read preference for count', done => { - const obj0 = new Parse.Object('MyObject'); - obj0.set('boolKey', false); - const obj1 = new Parse.Object('MyObject'); - obj1.set('boolKey', true); + xit("should change read preference for count", done => { + const obj0 = new Parse.Object("MyObject"); + obj0.set("boolKey", false); + const obj1 = new Parse.Object("MyObject"); + obj1.set("boolKey", true); Parse.Object.saveAll([obj0, obj1]).then(() => { - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); - Parse.Cloud.beforeFind('MyObject', req => { - req.readPreference = 'SECONDARY'; + Parse.Cloud.beforeFind("MyObject", req => { + req.readPreference = "SECONDARY"; }); - const query = new Parse.Query('MyObject'); - query.equalTo('boolKey', false); + const query = new Parse.Query("MyObject"); + query.equalTo("boolKey", false); query .count() @@ -595,7 +605,7 @@ describe_only_db('mongo')('Read preference option', () => { let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -608,129 +618,131 @@ describe_only_db('mongo')('Read preference option', () => { }); }); - it('should change read preference for `aggregate` using `beforeFind`', async () => { + it("should change read preference for `aggregate` using `beforeFind`", async () => { // Save objects - const obj0 = new Parse.Object('MyObject'); - obj0.set('boolKey', false); - const obj1 = new Parse.Object('MyObject'); - obj1.set('boolKey', true); + const obj0 = new Parse.Object("MyObject"); + obj0.set("boolKey", false); + const obj1 = new Parse.Object("MyObject"); + obj1.set("boolKey", true); await Parse.Object.saveAll([obj0, obj1]); // Add trigger - Parse.Cloud.beforeFind('MyObject', req => { - req.readPreference = 'SECONDARY'; + Parse.Cloud.beforeFind("MyObject", req => { + req.readPreference = "SECONDARY"; }); await waitForReplication(); // Spy on DB adapter - spyOn(Collection.prototype, 'aggregate').and.callThrough(); + spyOn(Collection.prototype, "aggregate").and.callThrough(); // Query - const query = new Parse.Query('MyObject'); + const query = new Parse.Query("MyObject"); const results = await query.aggregate([{ $match: { boolKey: false } }]); // Validate expect(results.length).toBe(1); let readPreference = null; Collection.prototype.aggregate.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('MyObject') > -1) { + if (call.object.s.namespace.collection.indexOf("MyObject") > -1) { readPreference = call.args[1].readPreference; } }); expect(readPreference).toEqual(ReadPreference.SECONDARY); }); - it('should change read preference for `find` using query option', async () => { + it("should change read preference for `find` using query option", async () => { // Save objects - const obj0 = new Parse.Object('MyObject'); - obj0.set('boolKey', false); - const obj1 = new Parse.Object('MyObject'); - obj1.set('boolKey', true); + const obj0 = new Parse.Object("MyObject"); + obj0.set("boolKey", false); + const obj1 = new Parse.Object("MyObject"); + obj1.set("boolKey", true); await Parse.Object.saveAll([obj0, obj1]); await waitForReplication(); // Spy on DB adapter - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); // Query - const query = new Parse.Query('MyObject'); - query.equalTo('boolKey', false); - query.readPreference('SECONDARY'); + const query = new Parse.Query("MyObject"); + query.equalTo("boolKey", false); + query.readPreference("SECONDARY"); const results = await query.find(); // Validate expect(results.length).toBe(1); let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY); }); - it('should change read preference for `aggregate` using query option', async () => { + it("should change read preference for `aggregate` using query option", async () => { // Save objects - const obj0 = new Parse.Object('MyObject'); - obj0.set('boolKey', false); - const obj1 = new Parse.Object('MyObject'); - obj1.set('boolKey', true); + const obj0 = new Parse.Object("MyObject"); + obj0.set("boolKey", false); + const obj1 = new Parse.Object("MyObject"); + obj1.set("boolKey", true); await Parse.Object.saveAll([obj0, obj1]); await waitForReplication(); // Spy on DB adapter - spyOn(Collection.prototype, 'aggregate').and.callThrough(); + spyOn(Collection.prototype, "aggregate").and.callThrough(); // Query - const query = new Parse.Query('MyObject'); - query.readPreference('SECONDARY'); + const query = new Parse.Query("MyObject"); + query.readPreference("SECONDARY"); const results = await query.aggregate([{ $match: { boolKey: false } }]); // Validate expect(results.length).toBe(1); let readPreference = null; Collection.prototype.aggregate.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('MyObject') > -1) { + if (call.object.s.namespace.collection.indexOf("MyObject") > -1) { readPreference = call.args[1].readPreference; } }); expect(readPreference).toEqual(ReadPreference.SECONDARY); }); - it('should find includes in same replica of readPreference by default', async () => { - const obj0 = new Parse.Object('MyObject0'); - obj0.set('boolKey', false); - const obj1 = new Parse.Object('MyObject1'); - obj1.set('boolKey', true); - obj1.set('myObject0', obj0); - const obj2 = new Parse.Object('MyObject2'); - obj2.set('boolKey', false); - obj2.set('myObject1', obj1); + it("should find includes in same replica of readPreference by default", async () => { + const obj0 = new Parse.Object("MyObject0"); + obj0.set("boolKey", false); + const obj1 = new Parse.Object("MyObject1"); + obj1.set("boolKey", true); + obj1.set("myObject0", obj0); + const obj2 = new Parse.Object("MyObject2"); + obj2.set("boolKey", false); + obj2.set("myObject1", obj1); await Parse.Object.saveAll([obj0, obj1, obj2]); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); - Parse.Cloud.beforeFind('MyObject2', req => { - req.readPreference = 'SECONDARY'; + Parse.Cloud.beforeFind("MyObject2", req => { + req.readPreference = "SECONDARY"; }); await waitForReplication(); - const query = new Parse.Query('MyObject2'); - query.equalTo('boolKey', false); - query.include('myObject1'); - query.include('myObject1.myObject0'); + const query = new Parse.Query("MyObject2"); + query.equalTo("boolKey", false); + query.include("myObject1"); + query.include("myObject1.myObject0"); const results = await query.find(); expect(results.length).toBe(1); const firstResult = results[0]; - expect(firstResult.get('boolKey')).toBe(false); - expect(firstResult.get('myObject1').get('boolKey')).toBe(true); - expect(firstResult.get('myObject1').get('myObject0').get('boolKey')).toBe(false); + expect(firstResult.get("boolKey")).toBe(false); + expect(firstResult.get("myObject1").get("boolKey")).toBe(true); + expect(firstResult.get("myObject1").get("myObject0").get("boolKey")).toBe( + false + ); let myObjectReadPreference0 = null; let myObjectReadPreference1 = null; let myObjectReadPreference2 = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject0") >= 0) { myObjectReadPreference0 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject1") >= 0) { myObjectReadPreference1 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject2") >= 0) { myObjectReadPreference2 = call.args[1].readPreference; } }); @@ -740,48 +752,50 @@ describe_only_db('mongo')('Read preference option', () => { expect(myObjectReadPreference2).toEqual(ReadPreference.SECONDARY); }); - it('should change includes read preference', async () => { - const obj0 = new Parse.Object('MyObject0'); - obj0.set('boolKey', false); - const obj1 = new Parse.Object('MyObject1'); - obj1.set('boolKey', true); - obj1.set('myObject0', obj0); - const obj2 = new Parse.Object('MyObject2'); - obj2.set('boolKey', false); - obj2.set('myObject1', obj1); + it("should change includes read preference", async () => { + const obj0 = new Parse.Object("MyObject0"); + obj0.set("boolKey", false); + const obj1 = new Parse.Object("MyObject1"); + obj1.set("boolKey", true); + obj1.set("myObject0", obj0); + const obj2 = new Parse.Object("MyObject2"); + obj2.set("boolKey", false); + obj2.set("myObject1", obj1); await Parse.Object.saveAll([obj0, obj1, obj2]); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); - Parse.Cloud.beforeFind('MyObject2', req => { - req.readPreference = 'SECONDARY_PREFERRED'; - req.includeReadPreference = 'SECONDARY'; + Parse.Cloud.beforeFind("MyObject2", req => { + req.readPreference = "SECONDARY_PREFERRED"; + req.includeReadPreference = "SECONDARY"; }); await waitForReplication(); - const query = new Parse.Query('MyObject2'); - query.equalTo('boolKey', false); - query.include('myObject1'); - query.include('myObject1.myObject0'); + const query = new Parse.Query("MyObject2"); + query.equalTo("boolKey", false); + query.include("myObject1"); + query.include("myObject1.myObject0"); const results = await query.find(); expect(results.length).toBe(1); const firstResult = results[0]; - expect(firstResult.get('boolKey')).toBe(false); - expect(firstResult.get('myObject1').get('boolKey')).toBe(true); - expect(firstResult.get('myObject1').get('myObject0').get('boolKey')).toBe(false); + expect(firstResult.get("boolKey")).toBe(false); + expect(firstResult.get("myObject1").get("boolKey")).toBe(true); + expect(firstResult.get("myObject1").get("myObject0").get("boolKey")).toBe( + false + ); let myObjectReadPreference0 = null; let myObjectReadPreference1 = null; let myObjectReadPreference2 = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject0") >= 0) { myObjectReadPreference0 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject1") >= 0) { myObjectReadPreference1 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject2") >= 0) { myObjectReadPreference2 = call.args[1].readPreference; } }); @@ -791,31 +805,31 @@ describe_only_db('mongo')('Read preference option', () => { expect(myObjectReadPreference2).toEqual(ReadPreference.SECONDARY_PREFERRED); }); - it('should change includes read preference when finding through API', async () => { - const obj0 = new Parse.Object('MyObject0'); - obj0.set('boolKey', false); - const obj1 = new Parse.Object('MyObject1'); - obj1.set('boolKey', true); - obj1.set('myObject0', obj0); - const obj2 = new Parse.Object('MyObject2'); - obj2.set('boolKey', false); - obj2.set('myObject1', obj1); + it("should change includes read preference when finding through API", async () => { + const obj0 = new Parse.Object("MyObject0"); + obj0.set("boolKey", false); + const obj1 = new Parse.Object("MyObject1"); + obj1.set("boolKey", true); + obj1.set("myObject0", obj0); + const obj2 = new Parse.Object("MyObject2"); + obj2.set("boolKey", false); + obj2.set("myObject1", obj1); await Parse.Object.saveAll([obj0, obj1, obj2]); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); await waitForReplication(); const response = await request({ - method: 'GET', + method: "GET", url: - 'http://localhost:8378/1/classes/MyObject2/' + + "http://localhost:8378/1/classes/MyObject2/" + obj2.id + - '?include=' + - JSON.stringify(['myObject1', 'myObject1.myObject0']) + - '&readPreference=SECONDARY_PREFERRED&includeReadPreference=SECONDARY', + "?include=" + + JSON.stringify(["myObject1", "myObject1.myObject0"]) + + "&readPreference=SECONDARY_PREFERRED&includeReadPreference=SECONDARY", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }, json: true, }); @@ -828,13 +842,13 @@ describe_only_db('mongo')('Read preference option', () => { let myObjectReadPreference1 = null; let myObjectReadPreference2 = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject0") >= 0) { myObjectReadPreference0 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject1") >= 0) { myObjectReadPreference1 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject2") >= 0) { myObjectReadPreference2 = call.args[1].readPreference; } }); @@ -844,31 +858,31 @@ describe_only_db('mongo')('Read preference option', () => { expect(myObjectReadPreference2).toEqual(ReadPreference.SECONDARY_PREFERRED); }); - it('should change includes read preference when getting through API', async () => { - const obj0 = new Parse.Object('MyObject0'); - obj0.set('boolKey', false); - const obj1 = new Parse.Object('MyObject1'); - obj1.set('boolKey', true); - obj1.set('myObject0', obj0); - const obj2 = new Parse.Object('MyObject2'); - obj2.set('boolKey', false); - obj2.set('myObject1', obj1); + it("should change includes read preference when getting through API", async () => { + const obj0 = new Parse.Object("MyObject0"); + obj0.set("boolKey", false); + const obj1 = new Parse.Object("MyObject1"); + obj1.set("boolKey", true); + obj1.set("myObject0", obj0); + const obj2 = new Parse.Object("MyObject2"); + obj2.set("boolKey", false); + obj2.set("myObject1", obj1); await Parse.Object.saveAll([obj0, obj1, obj2]); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); await waitForReplication(); const response = await request({ - method: 'GET', + method: "GET", url: - 'http://localhost:8378/1/classes/MyObject2?where=' + + "http://localhost:8378/1/classes/MyObject2?where=" + JSON.stringify({ boolKey: false }) + - '&include=' + - JSON.stringify(['myObject1', 'myObject1.myObject0']) + - '&readPreference=SECONDARY_PREFERRED&includeReadPreference=SECONDARY', + "&include=" + + JSON.stringify(["myObject1", "myObject1.myObject0"]) + + "&readPreference=SECONDARY_PREFERRED&includeReadPreference=SECONDARY", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }, json: true, }); @@ -882,13 +896,13 @@ describe_only_db('mongo')('Read preference option', () => { let myObjectReadPreference1 = null; let myObjectReadPreference2 = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject0") >= 0) { myObjectReadPreference0 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject1") >= 0) { myObjectReadPreference1 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject2") >= 0) { myObjectReadPreference2 = call.args[1].readPreference; } }); @@ -898,48 +912,48 @@ describe_only_db('mongo')('Read preference option', () => { expect(myObjectReadPreference2).toEqual(ReadPreference.SECONDARY_PREFERRED); }); - it('should find subqueries in same replica of readPreference by default', async () => { - const obj0 = new Parse.Object('MyObject0'); - obj0.set('boolKey', false); - const obj1 = new Parse.Object('MyObject1'); - obj1.set('boolKey', true); - obj1.set('myObject0', obj0); - const obj2 = new Parse.Object('MyObject2'); - obj2.set('boolKey', false); - obj2.set('myObject1', obj1); + it("should find subqueries in same replica of readPreference by default", async () => { + const obj0 = new Parse.Object("MyObject0"); + obj0.set("boolKey", false); + const obj1 = new Parse.Object("MyObject1"); + obj1.set("boolKey", true); + obj1.set("myObject0", obj0); + const obj2 = new Parse.Object("MyObject2"); + obj2.set("boolKey", false); + obj2.set("myObject1", obj1); await Parse.Object.saveAll([obj0, obj1, obj2]); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); - Parse.Cloud.beforeFind('MyObject2', req => { - req.readPreference = 'SECONDARY'; + Parse.Cloud.beforeFind("MyObject2", req => { + req.readPreference = "SECONDARY"; }); await waitForReplication(); - const query0 = new Parse.Query('MyObject0'); - query0.equalTo('boolKey', false); + const query0 = new Parse.Query("MyObject0"); + query0.equalTo("boolKey", false); - const query1 = new Parse.Query('MyObject1'); - query1.matchesQuery('myObject0', query0); + const query1 = new Parse.Query("MyObject1"); + query1.matchesQuery("myObject0", query0); - const query2 = new Parse.Query('MyObject2'); - query2.matchesQuery('myObject1', query1); + const query2 = new Parse.Query("MyObject2"); + query2.matchesQuery("myObject1", query1); const results = await query2.find(); expect(results.length).toBe(1); - expect(results[0].get('boolKey')).toBe(false); + expect(results[0].get("boolKey")).toBe(false); let myObjectReadPreference0 = null; let myObjectReadPreference1 = null; let myObjectReadPreference2 = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject0") >= 0) { myObjectReadPreference0 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject1") >= 0) { myObjectReadPreference1 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject2") >= 0) { myObjectReadPreference2 = call.args[1].readPreference; } }); @@ -949,49 +963,49 @@ describe_only_db('mongo')('Read preference option', () => { expect(myObjectReadPreference2).toEqual(ReadPreference.SECONDARY); }); - it('should change subqueries read preference when using matchesQuery', async () => { - const obj0 = new Parse.Object('MyObject0'); - obj0.set('boolKey', false); - const obj1 = new Parse.Object('MyObject1'); - obj1.set('boolKey', true); - obj1.set('myObject0', obj0); - const obj2 = new Parse.Object('MyObject2'); - obj2.set('boolKey', false); - obj2.set('myObject1', obj1); + it("should change subqueries read preference when using matchesQuery", async () => { + const obj0 = new Parse.Object("MyObject0"); + obj0.set("boolKey", false); + const obj1 = new Parse.Object("MyObject1"); + obj1.set("boolKey", true); + obj1.set("myObject0", obj0); + const obj2 = new Parse.Object("MyObject2"); + obj2.set("boolKey", false); + obj2.set("myObject1", obj1); await Parse.Object.saveAll([obj0, obj1, obj2]); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); - Parse.Cloud.beforeFind('MyObject2', req => { - req.readPreference = 'SECONDARY_PREFERRED'; - req.subqueryReadPreference = 'SECONDARY'; + Parse.Cloud.beforeFind("MyObject2", req => { + req.readPreference = "SECONDARY_PREFERRED"; + req.subqueryReadPreference = "SECONDARY"; }); await waitForReplication(); - const query0 = new Parse.Query('MyObject0'); - query0.equalTo('boolKey', false); + const query0 = new Parse.Query("MyObject0"); + query0.equalTo("boolKey", false); - const query1 = new Parse.Query('MyObject1'); - query1.matchesQuery('myObject0', query0); + const query1 = new Parse.Query("MyObject1"); + query1.matchesQuery("myObject0", query0); - const query2 = new Parse.Query('MyObject2'); - query2.matchesQuery('myObject1', query1); + const query2 = new Parse.Query("MyObject2"); + query2.matchesQuery("myObject1", query1); const results = await query2.find(); expect(results.length).toBe(1); - expect(results[0].get('boolKey')).toBe(false); + expect(results[0].get("boolKey")).toBe(false); let myObjectReadPreference0 = null; let myObjectReadPreference1 = null; let myObjectReadPreference2 = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject0") >= 0) { myObjectReadPreference0 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject1") >= 0) { myObjectReadPreference1 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject2") >= 0) { myObjectReadPreference2 = call.args[1].readPreference; } }); @@ -1001,49 +1015,49 @@ describe_only_db('mongo')('Read preference option', () => { expect(myObjectReadPreference2).toEqual(ReadPreference.SECONDARY_PREFERRED); }); - it('should change subqueries read preference when using doesNotMatchQuery', async () => { - const obj0 = new Parse.Object('MyObject0'); - obj0.set('boolKey', false); - const obj1 = new Parse.Object('MyObject1'); - obj1.set('boolKey', true); - obj1.set('myObject0', obj0); - const obj2 = new Parse.Object('MyObject2'); - obj2.set('boolKey', false); - obj2.set('myObject1', obj1); + it("should change subqueries read preference when using doesNotMatchQuery", async () => { + const obj0 = new Parse.Object("MyObject0"); + obj0.set("boolKey", false); + const obj1 = new Parse.Object("MyObject1"); + obj1.set("boolKey", true); + obj1.set("myObject0", obj0); + const obj2 = new Parse.Object("MyObject2"); + obj2.set("boolKey", false); + obj2.set("myObject1", obj1); await Parse.Object.saveAll([obj0, obj1, obj2]); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); - Parse.Cloud.beforeFind('MyObject2', req => { - req.readPreference = 'SECONDARY_PREFERRED'; - req.subqueryReadPreference = 'SECONDARY'; + Parse.Cloud.beforeFind("MyObject2", req => { + req.readPreference = "SECONDARY_PREFERRED"; + req.subqueryReadPreference = "SECONDARY"; }); await waitForReplication(); - const query0 = new Parse.Query('MyObject0'); - query0.equalTo('boolKey', false); + const query0 = new Parse.Query("MyObject0"); + query0.equalTo("boolKey", false); - const query1 = new Parse.Query('MyObject1'); - query1.doesNotMatchQuery('myObject0', query0); + const query1 = new Parse.Query("MyObject1"); + query1.doesNotMatchQuery("myObject0", query0); - const query2 = new Parse.Query('MyObject2'); - query2.doesNotMatchQuery('myObject1', query1); + const query2 = new Parse.Query("MyObject2"); + query2.doesNotMatchQuery("myObject1", query1); const results = await query2.find(); expect(results.length).toBe(1); - expect(results[0].get('boolKey')).toBe(false); + expect(results[0].get("boolKey")).toBe(false); let myObjectReadPreference0 = null; let myObjectReadPreference1 = null; let myObjectReadPreference2 = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject0") >= 0) { myObjectReadPreference0 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject1") >= 0) { myObjectReadPreference1 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject2") >= 0) { myObjectReadPreference2 = call.args[1].readPreference; } }); @@ -1053,50 +1067,50 @@ describe_only_db('mongo')('Read preference option', () => { expect(myObjectReadPreference2).toEqual(ReadPreference.SECONDARY_PREFERRED); }); - it('should change subqueries read preference when using matchesKeyInQuery and doesNotMatchKeyInQuery', async () => { - const obj0 = new Parse.Object('MyObject0'); - obj0.set('boolKey', false); - const obj1 = new Parse.Object('MyObject1'); - obj1.set('boolKey', true); - obj1.set('myObject0', obj0); - const obj2 = new Parse.Object('MyObject2'); - obj2.set('boolKey', false); - obj2.set('myObject1', obj1); + it("should change subqueries read preference when using matchesKeyInQuery and doesNotMatchKeyInQuery", async () => { + const obj0 = new Parse.Object("MyObject0"); + obj0.set("boolKey", false); + const obj1 = new Parse.Object("MyObject1"); + obj1.set("boolKey", true); + obj1.set("myObject0", obj0); + const obj2 = new Parse.Object("MyObject2"); + obj2.set("boolKey", false); + obj2.set("myObject1", obj1); await Parse.Object.saveAll([obj0, obj1, obj2]); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); - Parse.Cloud.beforeFind('MyObject2', req => { - req.readPreference = 'SECONDARY_PREFERRED'; - req.subqueryReadPreference = 'SECONDARY'; + Parse.Cloud.beforeFind("MyObject2", req => { + req.readPreference = "SECONDARY_PREFERRED"; + req.subqueryReadPreference = "SECONDARY"; }); await waitForReplication(); - const query0 = new Parse.Query('MyObject0'); - query0.equalTo('boolKey', false); + const query0 = new Parse.Query("MyObject0"); + query0.equalTo("boolKey", false); - const query1 = new Parse.Query('MyObject1'); - query1.equalTo('boolKey', true); + const query1 = new Parse.Query("MyObject1"); + query1.equalTo("boolKey", true); - const query2 = new Parse.Query('MyObject2'); - query2.matchesKeyInQuery('boolKey', 'boolKey', query0); - query2.doesNotMatchKeyInQuery('boolKey', 'boolKey', query1); + const query2 = new Parse.Query("MyObject2"); + query2.matchesKeyInQuery("boolKey", "boolKey", query0); + query2.doesNotMatchKeyInQuery("boolKey", "boolKey", query1); const results = await query2.find(); expect(results.length).toBe(1); - expect(results[0].get('boolKey')).toBe(false); + expect(results[0].get("boolKey")).toBe(false); let myObjectReadPreference0 = null; let myObjectReadPreference1 = null; let myObjectReadPreference2 = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject0") >= 0) { myObjectReadPreference0 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject1") >= 0) { myObjectReadPreference1 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject2") >= 0) { myObjectReadPreference2 = call.args[1].readPreference; } }); @@ -1106,48 +1120,48 @@ describe_only_db('mongo')('Read preference option', () => { expect(myObjectReadPreference2).toEqual(ReadPreference.SECONDARY_PREFERRED); }); - it('should change subqueries read preference when using matchesKeyInQuery and doesNotMatchKeyInQuery to find through API', async () => { - const obj0 = new Parse.Object('MyObject0'); - obj0.set('boolKey', false); - const obj1 = new Parse.Object('MyObject1'); - obj1.set('boolKey', true); - obj1.set('myObject0', obj0); - const obj2 = new Parse.Object('MyObject2'); - obj2.set('boolKey', false); - obj2.set('myObject1', obj1); + it("should change subqueries read preference when using matchesKeyInQuery and doesNotMatchKeyInQuery to find through API", async () => { + const obj0 = new Parse.Object("MyObject0"); + obj0.set("boolKey", false); + const obj1 = new Parse.Object("MyObject1"); + obj1.set("boolKey", true); + obj1.set("myObject0", obj0); + const obj2 = new Parse.Object("MyObject2"); + obj2.set("boolKey", false); + obj2.set("myObject1", obj1); await Parse.Object.saveAll([obj0, obj1, obj2]); - spyOn(Collection.prototype, 'find').and.callThrough(); + spyOn(Collection.prototype, "find").and.callThrough(); await waitForReplication(); const whereString = JSON.stringify({ boolKey: { $select: { query: { - className: 'MyObject0', + className: "MyObject0", where: { boolKey: false }, }, - key: 'boolKey', + key: "boolKey", }, $dontSelect: { query: { - className: 'MyObject1', + className: "MyObject1", where: { boolKey: true }, }, - key: 'boolKey', + key: "boolKey", }, }, }); const response = await request({ - method: 'GET', + method: "GET", url: - 'http://localhost:8378/1/classes/MyObject2/?where=' + + "http://localhost:8378/1/classes/MyObject2/?where=" + whereString + - '&readPreference=SECONDARY_PREFERRED&subqueryReadPreference=SECONDARY', + "&readPreference=SECONDARY_PREFERRED&subqueryReadPreference=SECONDARY", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }, json: true, }); @@ -1158,13 +1172,13 @@ describe_only_db('mongo')('Read preference option', () => { let myObjectReadPreference1 = null; let myObjectReadPreference2 = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject0") >= 0) { myObjectReadPreference0 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject1") >= 0) { myObjectReadPreference1 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) { + if (call.object.s.namespace.collection.indexOf("MyObject2") >= 0) { myObjectReadPreference2 = call.args[1].readPreference; } }); diff --git a/spec/RedisCacheAdapter.spec.js b/spec/RedisCacheAdapter.spec.js index 9b88e857c4..691e733e72 100644 --- a/spec/RedisCacheAdapter.spec.js +++ b/spec/RedisCacheAdapter.spec.js @@ -1,4 +1,5 @@ -const RedisCacheAdapter = require('../lib/Adapters/Cache/RedisCacheAdapter').default; +const RedisCacheAdapter = + require("../lib/Adapters/Cache/RedisCacheAdapter").default; function wait(sleep) { return new Promise(function (resolve) { @@ -11,10 +12,10 @@ set PARSE_SERVER_TEST_CACHE='redis' and make sure a redis server is available on the default port */ describe_only(() => { - return process.env.PARSE_SERVER_TEST_CACHE === 'redis'; -})('RedisCacheAdapter', function () { - const KEY = 'hello'; - const VALUE = 'world'; + return process.env.PARSE_SERVER_TEST_CACHE === "redis"; +})("RedisCacheAdapter", function () { + const KEY = "hello"; + const VALUE = "world"; let cache; beforeEach(async () => { @@ -23,7 +24,7 @@ describe_only(() => { await cache.clear(); }); - it('should get/set/clear', async () => { + it("should get/set/clear", async () => { const cacheNaN = new RedisCacheAdapter({ ttl: NaN, }); @@ -37,7 +38,7 @@ describe_only(() => { await cacheNaN.clear(); }); - it('should expire after ttl', done => { + it("should expire after ttl", done => { cache .put(KEY, VALUE) .then(() => cache.get(KEY)) @@ -48,7 +49,7 @@ describe_only(() => { .then(done); }); - it('should not store value for ttl=0', done => { + it("should not store value for ttl=0", done => { cache .put(KEY, VALUE, 0) .then(() => cache.get(KEY)) @@ -56,7 +57,7 @@ describe_only(() => { .then(done); }); - it('should not expire when ttl=Infinity', done => { + it("should not expire when ttl=Infinity", done => { cache .put(KEY, VALUE, Infinity) .then(() => cache.get(KEY)) @@ -67,10 +68,10 @@ describe_only(() => { .then(done); }); - it('should fallback to default ttl', done => { + it("should fallback to default ttl", done => { let promise = Promise.resolve(); - [-100, null, undefined, 'not number', true].forEach(ttl => { + [-100, null, undefined, "not number", true].forEach(ttl => { promise = promise.then(() => cache .put(KEY, VALUE, ttl) @@ -85,7 +86,7 @@ describe_only(() => { promise.then(done); }); - it('should find un-expired records', done => { + it("should find un-expired records", done => { cache .put(KEY, VALUE) .then(() => cache.get(KEY)) @@ -96,7 +97,7 @@ describe_only(() => { .then(done); }); - it('handleShutdown, close connection', async () => { + it("handleShutdown, close connection", async () => { await cache.handleShutdown(); setTimeout(() => { expect(cache.client.isOpen).toBe(false); @@ -105,11 +106,11 @@ describe_only(() => { }); describe_only(() => { - return process.env.PARSE_SERVER_TEST_CACHE === 'redis'; -})('RedisCacheAdapter/KeyPromiseQueue', function () { - const KEY1 = 'key1'; - const KEY2 = 'key2'; - const VALUE = 'hello'; + return process.env.PARSE_SERVER_TEST_CACHE === "redis"; +})("RedisCacheAdapter/KeyPromiseQueue", function () { + const KEY1 = "key1"; + const KEY2 = "key2"; + const VALUE = "hello"; // number of chained ops on a single key function getQueueCountForKey(cache, key) { @@ -121,7 +122,7 @@ describe_only(() => { return Object.keys(cache.queue.queue).length; } - it('it should clear completed operations from queue', async done => { + it("it should clear completed operations from queue", async done => { const cache = new RedisCacheAdapter({ ttl: NaN }); await cache.connect(); @@ -144,7 +145,7 @@ describe_only(() => { promise.then(() => expect(getQueueCount(cache)).toEqual(0)).then(done); }); - it('it should count per key chained operations correctly', async done => { + it("it should count per key chained operations correctly", async done => { const cache = new RedisCacheAdapter({ ttl: NaN }); await cache.connect(); @@ -168,12 +169,12 @@ describe_only(() => { .then(done); }); - it('should start and connect cache adapter', async () => { + it("should start and connect cache adapter", async () => { const server = await reconfigureServer({ cacheAdapter: { - module: `${__dirname.replace('/spec', '')}/lib/Adapters/Cache/RedisCacheAdapter`, + module: `${__dirname.replace("/spec", "")}/lib/Adapters/Cache/RedisCacheAdapter`, options: { - url: 'redis://127.0.0.1:6379/1', + url: "redis://127.0.0.1:6379/1", }, }, }); diff --git a/spec/RedisPubSub.spec.js b/spec/RedisPubSub.spec.js index 868e590740..cece4b6b63 100644 --- a/spec/RedisPubSub.spec.js +++ b/spec/RedisPubSub.spec.js @@ -1,45 +1,45 @@ -const RedisPubSub = require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; +const RedisPubSub = require("../lib/Adapters/PubSub/RedisPubSub").RedisPubSub; -describe('RedisPubSub', function () { +describe("RedisPubSub", function () { beforeEach(function (done) { // Mock redis - const createClient = jasmine.createSpy('createClient').and.returnValue({ - connect: jasmine.createSpy('connect').and.resolveTo(), - on: jasmine.createSpy('on'), + const createClient = jasmine.createSpy("createClient").and.returnValue({ + connect: jasmine.createSpy("connect").and.resolveTo(), + on: jasmine.createSpy("on"), }); - jasmine.mockLibrary('redis', 'createClient', createClient); + jasmine.mockLibrary("redis", "createClient", createClient); done(); }); - it('can create publisher', function () { + it("can create publisher", function () { RedisPubSub.createPublisher({ - redisURL: 'redisAddress', + redisURL: "redisAddress", redisOptions: { socket_keepalive: true }, }); - const redis = require('redis'); + const redis = require("redis"); expect(redis.createClient).toHaveBeenCalledWith({ - url: 'redisAddress', + url: "redisAddress", socket_keepalive: true, no_ready_check: true, }); }); - it('can create subscriber', function () { + it("can create subscriber", function () { RedisPubSub.createSubscriber({ - redisURL: 'redisAddress', + redisURL: "redisAddress", redisOptions: { socket_keepalive: true }, }); - const redis = require('redis'); + const redis = require("redis"); expect(redis.createClient).toHaveBeenCalledWith({ - url: 'redisAddress', + url: "redisAddress", socket_keepalive: true, no_ready_check: true, }); }); afterEach(function () { - jasmine.restoreLibrary('redis', 'createClient'); + jasmine.restoreLibrary("redis", "createClient"); }); }); diff --git a/spec/RegexVulnerabilities.spec.js b/spec/RegexVulnerabilities.spec.js index 8418494bac..7173fcd2b5 100644 --- a/spec/RegexVulnerabilities.spec.js +++ b/spec/RegexVulnerabilities.spec.js @@ -1,22 +1,22 @@ -const request = require('../lib/request'); +const request = require("../lib/request"); -const serverURL = 'http://localhost:8378/1'; +const serverURL = "http://localhost:8378/1"; const headers = { - 'Content-Type': 'application/json', + "Content-Type": "application/json", }; const keys = { - _ApplicationId: 'test', - _JavaScriptKey: 'test', + _ApplicationId: "test", + _JavaScriptKey: "test", }; const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), sendPasswordResetEmail: () => Promise.resolve(), sendMail: () => {}, }; -const appName = 'test'; -const publicServerURL = 'http://localhost:8378/1'; +const appName = "test"; +const publicServerURL = "http://localhost:8378/1"; -describe('Regex Vulnerabilities', () => { +describe("Regex Vulnerabilities", () => { let objectId; let sessionToken; let partialSessionToken; @@ -24,7 +24,7 @@ describe('Regex Vulnerabilities', () => { beforeEach(async () => { await reconfigureServer({ - maintenanceKey: 'test2', + maintenanceKey: "test2", verifyUserEmails: true, emailAdapter, appName, @@ -33,14 +33,14 @@ describe('Regex Vulnerabilities', () => { const signUpResponse = await request({ url: `${serverURL}/users`, - method: 'POST', + method: "POST", headers, body: JSON.stringify({ ...keys, - _method: 'POST', - username: 'someemail@somedomain.com', - password: 'somepassword', - email: 'someemail@somedomain.com', + _method: "POST", + username: "someemail@somedomain.com", + password: "somepassword", + email: "someemail@somedomain.com", }), }); objectId = signUpResponse.data.objectId; @@ -48,37 +48,37 @@ describe('Regex Vulnerabilities', () => { partialSessionToken = sessionToken.slice(0, 3); }); - describe('on session token', () => { - it('should not work with regex', async () => { + describe("on session token", () => { + it("should not work with regex", async () => { try { await request({ url: `${serverURL}/users/me`, - method: 'POST', + method: "POST", headers, body: JSON.stringify({ ...keys, _SessionToken: { $regex: partialSessionToken, }, - _method: 'GET', + _method: "GET", }), }); - fail('should not work'); + fail("should not work"); } catch (e) { expect(e.data.code).toEqual(209); - expect(e.data.error).toEqual('Invalid session token'); + expect(e.data.error).toEqual("Invalid session token"); } }); - it('should work with plain token', async () => { + it("should work with plain token", async () => { const meResponse = await request({ url: `${serverURL}/users/me`, - method: 'POST', + method: "POST", headers, body: JSON.stringify({ ...keys, _SessionToken: sessionToken, - _method: 'GET', + _method: "GET", }), }); expect(meResponse.data.objectId).toEqual(objectId); @@ -86,114 +86,119 @@ describe('Regex Vulnerabilities', () => { }); }); - describe('on verify e-mail', () => { + describe("on verify e-mail", () => { beforeEach(async function () { const userQuery = new Parse.Query(Parse.User); user = await userQuery.get(objectId, { useMasterKey: true }); }); - it('should not work with regex', async () => { - expect(user.get('emailVerified')).toEqual(false); + it("should not work with regex", async () => { + expect(user.get("emailVerified")).toEqual(false); await request({ url: `${serverURL}/apps/test/verify_email?token[$regex]=`, - method: 'GET', + method: "GET", }); await user.fetch({ useMasterKey: true }); - expect(user.get('emailVerified')).toEqual(false); + expect(user.get("emailVerified")).toEqual(false); }); - it_id('92bbb86d-bcda-49fa-8d79-aa0501078044')(it)('should work with plain token', async () => { - expect(user.get('emailVerified')).toEqual(false); - const current = await request({ - method: 'GET', - url: `http://localhost:8378/1/classes/_User/${user.id}`, - json: true, - headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Rest-API-Key': 'test', - 'X-Parse-Maintenance-Key': 'test2', - 'Content-Type': 'application/json', - }, - }).then(res => res.data); - // It should work - await request({ - url: `${serverURL}/apps/test/verify_email?token=${current._email_verify_token}`, - method: 'GET', - }); - await user.fetch({ useMasterKey: true }); - expect(user.get('emailVerified')).toEqual(true); - }); + it_id("92bbb86d-bcda-49fa-8d79-aa0501078044")(it)( + "should work with plain token", + async () => { + expect(user.get("emailVerified")).toEqual(false); + const current = await request({ + method: "GET", + url: `http://localhost:8378/1/classes/_User/${user.id}`, + json: true, + headers: { + "X-Parse-Application-Id": "test", + "X-Parse-Rest-API-Key": "test", + "X-Parse-Maintenance-Key": "test2", + "Content-Type": "application/json", + }, + }).then(res => res.data); + // It should work + await request({ + url: `${serverURL}/apps/test/verify_email?token=${current._email_verify_token}`, + method: "GET", + }); + await user.fetch({ useMasterKey: true }); + expect(user.get("emailVerified")).toEqual(true); + } + ); }); - describe('on password reset', () => { + describe("on password reset", () => { beforeEach(async () => { - user = await Parse.User.logIn('someemail@somedomain.com', 'somepassword'); + user = await Parse.User.logIn("someemail@somedomain.com", "somepassword"); }); - it('should not work with regex', async () => { + it("should not work with regex", async () => { expect(user.id).toEqual(objectId); await request({ url: `${serverURL}/requestPasswordReset`, - method: 'POST', + method: "POST", headers, body: JSON.stringify({ ...keys, - _method: 'POST', - email: 'someemail@somedomain.com', + _method: "POST", + email: "someemail@somedomain.com", }), }); await user.fetch({ useMasterKey: true }); const passwordResetResponse = await request({ url: `${serverURL}/apps/test/request_password_reset?token[$regex]=`, - method: 'GET', + method: "GET", }); expect(passwordResetResponse.status).toEqual(302); - expect(passwordResetResponse.headers.location).toMatch(`\\/invalid\\_link\\.html`); + expect(passwordResetResponse.headers.location).toMatch( + `\\/invalid\\_link\\.html` + ); await request({ url: `${serverURL}/apps/test/request_password_reset`, - method: 'POST', + method: "POST", body: { - token: { $regex: '' }, - username: 'someemail@somedomain.com', - new_password: 'newpassword', + token: { $regex: "" }, + username: "someemail@somedomain.com", + new_password: "newpassword", }, }); try { - await Parse.User.logIn('someemail@somedomain.com', 'newpassword'); - fail('should not work'); + await Parse.User.logIn("someemail@somedomain.com", "newpassword"); + fail("should not work"); } catch (e) { expect(e.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); - expect(e.message).toEqual('Invalid username/password.'); + expect(e.message).toEqual("Invalid username/password."); } }); - it('should work with plain token', async () => { + it("should work with plain token", async () => { expect(user.id).toEqual(objectId); await request({ url: `${serverURL}/requestPasswordReset`, - method: 'POST', + method: "POST", headers, body: JSON.stringify({ ...keys, - _method: 'POST', - email: 'someemail@somedomain.com', + _method: "POST", + email: "someemail@somedomain.com", }), }); const current = await request({ - method: 'GET', + method: "GET", url: `http://localhost:8378/1/classes/_User/${user.id}`, json: true, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Rest-API-Key': 'test', - 'X-Parse-Maintenance-Key': 'test2', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": "test", + "X-Parse-Rest-API-Key": "test", + "X-Parse-Maintenance-Key": "test2", + "Content-Type": "application/json", }, }).then(res => res.data); const token = current._perishable_token; const passwordResetResponse = await request({ url: `${serverURL}/apps/test/request_password_reset?token=${token}`, - method: 'GET', + method: "GET", }); expect(passwordResetResponse.status).toEqual(302); expect(passwordResetResponse.headers.location).toMatch( @@ -201,14 +206,17 @@ describe('Regex Vulnerabilities', () => { ); await request({ url: `${serverURL}/apps/test/request_password_reset`, - method: 'POST', + method: "POST", body: { token, - username: 'someemail@somedomain.com', - new_password: 'newpassword', + username: "someemail@somedomain.com", + new_password: "newpassword", }, }); - const userAgain = await Parse.User.logIn('someemail@somedomain.com', 'newpassword'); + const userAgain = await Parse.User.logIn( + "someemail@somedomain.com", + "newpassword" + ); expect(userAgain.id).toEqual(objectId); }); }); diff --git a/spec/RestQuery.spec.js b/spec/RestQuery.spec.js index 131433e177..6e6156fce3 100644 --- a/spec/RestQuery.spec.js +++ b/spec/RestQuery.spec.js @@ -1,28 +1,28 @@ -'use strict'; +"use strict"; // These tests check the "find" functionality of the REST API. -const auth = require('../lib/Auth'); -const Config = require('../lib/Config'); -const rest = require('../lib/rest'); -const RestQuery = require('../lib/RestQuery'); -const request = require('../lib/request'); +const auth = require("../lib/Auth"); +const Config = require("../lib/Config"); +const rest = require("../lib/rest"); +const RestQuery = require("../lib/RestQuery"); +const request = require("../lib/request"); -const querystring = require('querystring'); +const querystring = require("querystring"); let config; let database; const nobody = auth.nobody(config); -describe('rest query', () => { +describe("rest query", () => { beforeEach(() => { - config = Config.get('test'); + config = Config.get("test"); database = config.database; }); - it('basic query', done => { + it("basic query", done => { rest - .create(config, nobody, 'TestObject', {}) + .create(config, nobody, "TestObject", {}) .then(() => { - return rest.find(config, nobody, 'TestObject', {}); + return rest.find(config, nobody, "TestObject", {}); }) .then(response => { expect(response.results.length).toEqual(1); @@ -30,14 +30,14 @@ describe('rest query', () => { }); }); - it('query with limit', done => { + it("query with limit", done => { rest - .create(config, nobody, 'TestObject', { foo: 'baz' }) + .create(config, nobody, "TestObject", { foo: "baz" }) .then(() => { - return rest.create(config, nobody, 'TestObject', { foo: 'qux' }); + return rest.create(config, nobody, "TestObject", { foo: "qux" }); }) .then(() => { - return rest.find(config, nobody, 'TestObject', {}, { limit: 1 }); + return rest.find(config, nobody, "TestObject", {}, { limit: 1 }); }) .then(response => { expect(response.results.length).toEqual(1); @@ -47,22 +47,22 @@ describe('rest query', () => { }); const data = { - username: 'blah', - password: 'pass', - sessionToken: 'abc123', + username: "blah", + password: "pass", + sessionToken: "abc123", }; - it_exclude_dbs(['postgres'])( - 'query for user w/ legacy credentials without masterKey has them stripped from results', + it_exclude_dbs(["postgres"])( + "query for user w/ legacy credentials without masterKey has them stripped from results", done => { database - .create('_User', data) + .create("_User", data) .then(() => { - return rest.find(config, nobody, '_User'); + return rest.find(config, nobody, "_User"); }) .then(result => { const user = result.results[0]; - expect(user.username).toEqual('blah'); + expect(user.username).toEqual("blah"); expect(user.sessionToken).toBeUndefined(); expect(user.password).toBeUndefined(); done(); @@ -70,17 +70,17 @@ describe('rest query', () => { } ); - it_exclude_dbs(['postgres'])( - 'query for user w/ legacy credentials with masterKey has them stripped from results', + it_exclude_dbs(["postgres"])( + "query for user w/ legacy credentials with masterKey has them stripped from results", done => { database - .create('_User', data) + .create("_User", data) .then(() => { - return rest.find(config, { isMaster: true }, '_User'); + return rest.find(config, { isMaster: true }, "_User"); }) .then(result => { const user = result.results[0]; - expect(user.username).toEqual('blah'); + expect(user.username).toEqual("blah"); expect(user.sessionToken).toBeUndefined(); expect(user.password).toBeUndefined(); done(); @@ -89,64 +89,70 @@ describe('rest query', () => { ); // Created to test a scenario in AnyPic - it_exclude_dbs(['postgres'])('query with include', done => { + it_exclude_dbs(["postgres"])("query with include", done => { let photo = { - foo: 'bar', + foo: "bar", }; let user = { - username: 'aUsername', - password: 'aPassword', - ACL: { '*': { read: true } }, + username: "aUsername", + password: "aPassword", + ACL: { "*": { read: true } }, }; const activity = { - type: 'comment', + type: "comment", photo: { - __type: 'Pointer', - className: 'TestPhoto', - objectId: '', + __type: "Pointer", + className: "TestPhoto", + objectId: "", }, fromUser: { - __type: 'Pointer', - className: '_User', - objectId: '', + __type: "Pointer", + className: "_User", + objectId: "", }, }; const queryWhere = { photo: { - __type: 'Pointer', - className: 'TestPhoto', - objectId: '', + __type: "Pointer", + className: "TestPhoto", + objectId: "", }, - type: 'comment', + type: "comment", }; const queryOptions = { - include: 'fromUser', - order: 'createdAt', + include: "fromUser", + order: "createdAt", limit: 30, }; rest - .create(config, nobody, 'TestPhoto', photo) + .create(config, nobody, "TestPhoto", photo) .then(p => { photo = p; - return rest.create(config, nobody, '_User', user); + return rest.create(config, nobody, "_User", user); }) .then(u => { user = u.response; activity.photo.objectId = photo.objectId; activity.fromUser.objectId = user.objectId; - return rest.create(config, nobody, 'TestActivity', activity); + return rest.create(config, nobody, "TestActivity", activity); }) .then(() => { queryWhere.photo.objectId = photo.objectId; - return rest.find(config, nobody, 'TestActivity', queryWhere, queryOptions); + return rest.find( + config, + nobody, + "TestActivity", + queryWhere, + queryOptions + ); }) .then(response => { const results = response.results; expect(results.length).toEqual(1); - expect(typeof results[0].objectId).toEqual('string'); - expect(typeof results[0].photo).toEqual('object'); - expect(typeof results[0].fromUser).toEqual('object'); - expect(typeof results[0].fromUser.username).toEqual('string'); + expect(typeof results[0].objectId).toEqual("string"); + expect(typeof results[0].photo).toEqual("object"); + expect(typeof results[0].fromUser).toEqual("object"); + expect(typeof results[0].fromUser.username).toEqual("string"); done(); }) .catch(error => { @@ -154,59 +160,70 @@ describe('rest query', () => { }); }); - it('query non-existent class when disabled client class creation', done => { + it("query non-existent class when disabled client class creation", done => { const customConfig = Object.assign({}, config, { allowClientClassCreation: false, }); - rest.find(customConfig, auth.nobody(customConfig), 'ClientClassCreation', {}).then( - () => { - fail('Should throw an error'); - done(); - }, - err => { - expect(err.code).toEqual(Parse.Error.OPERATION_FORBIDDEN); - expect(err.message).toEqual( - 'This user is not allowed to access ' + 'non-existent class: ClientClassCreation' - ); - done(); - } - ); + rest + .find(customConfig, auth.nobody(customConfig), "ClientClassCreation", {}) + .then( + () => { + fail("Should throw an error"); + done(); + }, + err => { + expect(err.code).toEqual(Parse.Error.OPERATION_FORBIDDEN); + expect(err.message).toEqual( + "This user is not allowed to access " + + "non-existent class: ClientClassCreation" + ); + done(); + } + ); }); - it('query existent class when disabled client class creation', async () => { + it("query existent class when disabled client class creation", async () => { const customConfig = Object.assign({}, config, { allowClientClassCreation: false, }); const schema = await config.database.loadSchema(); - const actualSchema = await schema.addClassIfNotExists('ClientClassCreation', {}); - expect(actualSchema.className).toEqual('ClientClassCreation'); + const actualSchema = await schema.addClassIfNotExists( + "ClientClassCreation", + {} + ); + expect(actualSchema.className).toEqual("ClientClassCreation"); await schema.reloadData({ clearCache: true }); // Should not throw const result = await rest.find( customConfig, auth.nobody(customConfig), - 'ClientClassCreation', + "ClientClassCreation", {} ); expect(result.results.length).toEqual(0); }); - it('query internal field', async () => { + it("query internal field", async () => { const internalFields = [ - '_email_verify_token', - '_perishable_token', - '_tombstone', - '_email_verify_token_expires_at', - '_failed_login_count', - '_account_lockout_expires_at', - '_password_changed_at', - '_password_history', + "_email_verify_token", + "_perishable_token", + "_tombstone", + "_email_verify_token_expires_at", + "_failed_login_count", + "_account_lockout_expires_at", + "_password_changed_at", + "_password_history", ]; await Promise.all([ ...internalFields.map(field => - expectAsync(new Parse.Query(Parse.User).exists(field).find()).toBeRejectedWith( - new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid key name: ${field}`) + expectAsync( + new Parse.Query(Parse.User).exists(field).find() + ).toBeRejectedWith( + new Parse.Error( + Parse.Error.INVALID_KEY_NAME, + `Invalid key name: ${field}` + ) ) ), ...internalFields.map(field => @@ -215,80 +232,85 @@ describe('rest query', () => { ]); }); - it('query protected field', async () => { + it("query protected field", async () => { const user = new Parse.User(); - user.setUsername('username1'); - user.setPassword('password'); + user.setUsername("username1"); + user.setPassword("password"); await user.signUp(); const config = Config.get(Parse.applicationId); - const obj = new Parse.Object('Test'); + const obj = new Parse.Object("Test"); - obj.set('owner', user); - obj.set('test', 'test'); - obj.set('zip', 1234); + obj.set("owner", user); + obj.set("test", "test"); + obj.set("zip", 1234); await obj.save(); const schema = await config.database.loadSchema(); await schema.updateClass( - 'Test', + "Test", {}, { - get: { '*': true }, - find: { '*': true }, - protectedFields: { [user.id]: ['zip'] }, + get: { "*": true }, + find: { "*": true }, + protectedFields: { [user.id]: ["zip"] }, } ); await Promise.all([ - new Parse.Query('Test').exists('test').find(), - expectAsync(new Parse.Query('Test').exists('zip').find()).toBeRejectedWith( + new Parse.Query("Test").exists("test").find(), + expectAsync( + new Parse.Query("Test").exists("zip").find() + ).toBeRejectedWith( new Parse.Error( Parse.Error.OPERATION_FORBIDDEN, - 'This user is not allowed to query zip on class Test' + "This user is not allowed to query zip on class Test" ) ), ]); }); - it('query protected field with matchesQuery', async () => { + it("query protected field with matchesQuery", async () => { const user = new Parse.User(); - user.setUsername('username1'); - user.setPassword('password'); + user.setUsername("username1"); + user.setPassword("password"); await user.signUp(); - const test = new Parse.Object('TestObject', { user }); + const test = new Parse.Object("TestObject", { user }); await test.save(); const subQuery = new Parse.Query(Parse.User); - subQuery.exists('_perishable_token'); + subQuery.exists("_perishable_token"); await expectAsync( - new Parse.Query('TestObject').matchesQuery('user', subQuery).find() + new Parse.Query("TestObject").matchesQuery("user", subQuery).find() ).toBeRejectedWith( - new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'Invalid key name: _perishable_token') + new Parse.Error( + Parse.Error.INVALID_KEY_NAME, + "Invalid key name: _perishable_token" + ) ); }); - it('query with wrongly encoded parameter', done => { + it("query with wrongly encoded parameter", done => { rest - .create(config, nobody, 'TestParameterEncode', { foo: 'bar' }) + .create(config, nobody, "TestParameterEncode", { foo: "bar" }) .then(() => { - return rest.create(config, nobody, 'TestParameterEncode', { - foo: 'baz', + return rest.create(config, nobody, "TestParameterEncode", { + foo: "baz", }); }) .then(() => { const headers = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; const p0 = request({ headers: headers, url: - 'http://localhost:8378/1/classes/TestParameterEncode?' + + "http://localhost:8378/1/classes/TestParameterEncode?" + querystring .stringify({ where: '{"foo":{"$ne": "baz"}}', limit: 1, }) - .replace('=', '%3D'), + .replace("=", "%3D"), }).then(fail, response => { const error = response.data; expect(error.code).toEqual(Parse.Error.INVALID_QUERY); @@ -297,12 +319,12 @@ describe('rest query', () => { const p1 = request({ headers: headers, url: - 'http://localhost:8378/1/classes/TestParameterEncode?' + + "http://localhost:8378/1/classes/TestParameterEncode?" + querystring .stringify({ limit: 1, }) - .replace('=', '%3D'), + .replace("=", "%3D"), }).then(fail, response => { const error = response.data; expect(error.code).toEqual(Parse.Error.INVALID_QUERY); @@ -312,19 +334,19 @@ describe('rest query', () => { .then(done) .catch(err => { jfail(err); - fail('should not fail'); + fail("should not fail"); done(); }); }); - it('query with limit = 0', done => { + it("query with limit = 0", done => { rest - .create(config, nobody, 'TestObject', { foo: 'baz' }) + .create(config, nobody, "TestObject", { foo: "baz" }) .then(() => { - return rest.create(config, nobody, 'TestObject', { foo: 'qux' }); + return rest.create(config, nobody, "TestObject", { foo: "qux" }); }) .then(() => { - return rest.find(config, nobody, 'TestObject', {}, { limit: 0 }); + return rest.find(config, nobody, "TestObject", {}, { limit: 0 }); }) .then(response => { expect(response.results.length).toEqual(0); @@ -332,14 +354,20 @@ describe('rest query', () => { }); }); - it('query with limit = 0 and count = 1', done => { + it("query with limit = 0 and count = 1", done => { rest - .create(config, nobody, 'TestObject', { foo: 'baz' }) + .create(config, nobody, "TestObject", { foo: "baz" }) .then(() => { - return rest.create(config, nobody, 'TestObject', { foo: 'qux' }); + return rest.create(config, nobody, "TestObject", { foo: "qux" }); }) .then(() => { - return rest.find(config, nobody, 'TestObject', {}, { limit: 0, count: 1 }); + return rest.find( + config, + nobody, + "TestObject", + {}, + { limit: 0, count: 1 } + ); }) .then(response => { expect(response.results.length).toEqual(0); @@ -348,23 +376,23 @@ describe('rest query', () => { }); }); - it('makes sure null pointers are handed correctly #2189', done => { - const object = new Parse.Object('AnObject'); - const anotherObject = new Parse.Object('AnotherObject'); + it("makes sure null pointers are handed correctly #2189", done => { + const object = new Parse.Object("AnObject"); + const anotherObject = new Parse.Object("AnotherObject"); anotherObject .save() .then(() => { - object.set('values', [null, null, anotherObject]); + object.set("values", [null, null, anotherObject]); return object.save(); }) .then(() => { - const query = new Parse.Query('AnObject'); - query.include('values'); + const query = new Parse.Query("AnObject"); + query.include("values"); return query.first(); }) .then( result => { - const values = result.get('values'); + const values = result.get("values"); expect(values.length).toBe(3); let anotherObjectFound = false; let nullCounts = 0; @@ -388,54 +416,60 @@ describe('rest query', () => { }); }); -describe('RestQuery.each', () => { +describe("RestQuery.each", () => { beforeEach(() => { - config = Config.get('test'); + config = Config.get("test"); }); - it_id('3416c90b-ee2e-4bb5-9231-46cd181cd0a2')(it)('should run each', async () => { - const objects = []; - while (objects.length != 10) { - objects.push(new Parse.Object('Object', { value: objects.length })); + it_id("3416c90b-ee2e-4bb5-9231-46cd181cd0a2")(it)( + "should run each", + async () => { + const objects = []; + while (objects.length != 10) { + objects.push(new Parse.Object("Object", { value: objects.length })); + } + const config = Config.get("test"); + await Parse.Object.saveAll(objects); + const query = await RestQuery({ + method: RestQuery.Method.find, + config, + auth: auth.master(config), + className: "Object", + restWhere: { value: { $gt: 2 } }, + restOptions: { limit: 2 }, + }); + const spy = spyOn(query, "execute").and.callThrough(); + const classSpy = spyOn( + RestQuery._UnsafeRestQuery.prototype, + "execute" + ).and.callThrough(); + const results = []; + await query.each(result => { + expect(result.value).toBeGreaterThan(2); + results.push(result); + }); + expect(spy.calls.count()).toBe(0); + expect(classSpy.calls.count()).toBe(4); + expect(results.length).toBe(7); } - const config = Config.get('test'); - await Parse.Object.saveAll(objects); - const query = await RestQuery({ - method: RestQuery.Method.find, - config, - auth: auth.master(config), - className: 'Object', - restWhere: { value: { $gt: 2 } }, - restOptions: { limit: 2 }, - }); - const spy = spyOn(query, 'execute').and.callThrough(); - const classSpy = spyOn(RestQuery._UnsafeRestQuery.prototype, 'execute').and.callThrough(); - const results = []; - await query.each(result => { - expect(result.value).toBeGreaterThan(2); - results.push(result); - }); - expect(spy.calls.count()).toBe(0); - expect(classSpy.calls.count()).toBe(4); - expect(results.length).toBe(7); - }); + ); - it_id('0fe22501-4b18-461e-b87d-82ceac4a496e')(it)( - 'should work with query on relations', + it_id("0fe22501-4b18-461e-b87d-82ceac4a496e")(it)( + "should work with query on relations", async () => { - const objectA = new Parse.Object('Letter', { value: 'A' }); - const objectB = new Parse.Object('Letter', { value: 'B' }); + const objectA = new Parse.Object("Letter", { value: "A" }); + const objectB = new Parse.Object("Letter", { value: "B" }); - const object1 = new Parse.Object('Number', { value: '1' }); - const object2 = new Parse.Object('Number', { value: '2' }); - const object3 = new Parse.Object('Number', { value: '3' }); - const object4 = new Parse.Object('Number', { value: '4' }); + const object1 = new Parse.Object("Number", { value: "1" }); + const object2 = new Parse.Object("Number", { value: "2" }); + const object3 = new Parse.Object("Number", { value: "3" }); + const object4 = new Parse.Object("Number", { value: "4" }); await Parse.Object.saveAll([object1, object2, object3, object4]); - objectA.relation('numbers').add(object1); - objectB.relation('numbers').add(object2); + objectA.relation("numbers").add(object1); + objectB.relation("numbers").add(object2); await Parse.Object.saveAll([objectA, objectB]); - const config = Config.get('test'); + const config = Config.get("test"); /** * Two queries needed since objectId are sorted and we can't know which one @@ -445,11 +479,11 @@ describe('RestQuery.each', () => { method: RestQuery.Method.get, config, auth: auth.master(config), - className: 'Letter', + className: "Letter", restWhere: { numbers: { - __type: 'Pointer', - className: 'Number', + __type: "Pointer", + className: "Number", objectId: object1.id, }, }, @@ -460,18 +494,21 @@ describe('RestQuery.each', () => { method: RestQuery.Method.get, config, auth: auth.master(config), - className: 'Letter', + className: "Letter", restWhere: { numbers: { - __type: 'Pointer', - className: 'Number', + __type: "Pointer", + className: "Number", objectId: object2.id, }, }, restOptions: { limit: 1 }, }); - const classSpy = spyOn(RestQuery._UnsafeRestQuery.prototype, 'execute').and.callThrough(); + const classSpy = spyOn( + RestQuery._UnsafeRestQuery.prototype, + "execute" + ).and.callThrough(); const resultsOne = []; const resultsTwo = []; await queryOne.each(result => { @@ -486,13 +523,13 @@ describe('RestQuery.each', () => { } ); - it('test afterSave response object is return', done => { - Parse.Cloud.beforeSave('TestObject2', function (req) { - req.object.set('tobeaddbefore', true); - req.object.set('tobeaddbeforeandremoveafter', true); + it("test afterSave response object is return", done => { + Parse.Cloud.beforeSave("TestObject2", function (req) { + req.object.set("tobeaddbefore", true); + req.object.set("tobeaddbeforeandremoveafter", true); }); - Parse.Cloud.afterSave('TestObject2', function (req) { + Parse.Cloud.afterSave("TestObject2", function (req) { const jsonObject = req.object.toJSON(); delete jsonObject.todelete; delete jsonObject.tobeaddbeforeandremoveafter; @@ -501,34 +538,36 @@ describe('RestQuery.each', () => { return jsonObject; }); - rest.create(config, nobody, 'TestObject2', { todelete: true, tokeep: true }).then(response => { - expect(response.response.toadd).toBeTruthy(); - expect(response.response.tokeep).toBeTruthy(); - expect(response.response.tobeaddbefore).toBeTruthy(); - expect(response.response.tobeaddbeforeandremoveafter).toBeUndefined(); - expect(response.response.todelete).toBeUndefined(); - done(); - }); + rest + .create(config, nobody, "TestObject2", { todelete: true, tokeep: true }) + .then(response => { + expect(response.response.toadd).toBeTruthy(); + expect(response.response.tokeep).toBeTruthy(); + expect(response.response.tobeaddbefore).toBeTruthy(); + expect(response.response.tobeaddbeforeandremoveafter).toBeUndefined(); + expect(response.response.todelete).toBeUndefined(); + done(); + }); }); - it('test afterSave should not affect save response', async () => { - Parse.Cloud.beforeSave('TestObject2', ({ object }) => { - object.set('addedBeforeSave', true); + it("test afterSave should not affect save response", async () => { + Parse.Cloud.beforeSave("TestObject2", ({ object }) => { + object.set("addedBeforeSave", true); }); - Parse.Cloud.afterSave('TestObject2', ({ object }) => { - object.set('addedAfterSave', true); - object.unset('initialToRemove'); + Parse.Cloud.afterSave("TestObject2", ({ object }) => { + object.set("addedAfterSave", true); + object.unset("initialToRemove"); }); - const { response } = await rest.create(config, nobody, 'TestObject2', { + const { response } = await rest.create(config, nobody, "TestObject2", { initialSave: true, initialToRemove: true, }); expect(Object.keys(response).sort()).toEqual([ - 'addedAfterSave', - 'addedBeforeSave', - 'createdAt', - 'initialToRemove', - 'objectId', + "addedAfterSave", + "addedBeforeSave", + "createdAt", + "initialToRemove", + "objectId", ]); }); }); diff --git a/spec/RevocableSessionsUpgrade.spec.js b/spec/RevocableSessionsUpgrade.spec.js index ca7b5a98d6..bafd28f4e6 100644 --- a/spec/RevocableSessionsUpgrade.spec.js +++ b/spec/RevocableSessionsUpgrade.spec.js @@ -1,45 +1,50 @@ -const Config = require('../lib/Config'); -const sessionToken = 'legacySessionToken'; -const request = require('../lib/request'); -const Parse = require('parse/node'); +const Config = require("../lib/Config"); +const sessionToken = "legacySessionToken"; +const request = require("../lib/request"); +const Parse = require("parse/node"); function createUser() { const config = Config.get(Parse.applicationId); const user = { - objectId: '1234567890', - username: 'hello', - password: 'pass', + objectId: "1234567890", + username: "hello", + password: "pass", _session_token: sessionToken, }; - return config.database.create('_User', user); + return config.database.create("_User", user); } -describe_only_db('mongo')('revocable sessions', () => { +describe_only_db("mongo")("revocable sessions", () => { beforeEach(async () => { // Create 1 user with the legacy await createUser(); }); - it('should upgrade legacy session token', done => { + it("should upgrade legacy session token", done => { const user = Parse.Object.fromJSON({ - className: '_User', - objectId: '1234567890', + className: "_User", + objectId: "1234567890", sessionToken: sessionToken, }); user ._upgradeToRevocableSession() .then(res => { - expect(res.getSessionToken().indexOf('r:')).toBe(0); + expect(res.getSessionToken().indexOf("r:")).toBe(0); const config = Config.get(Parse.applicationId); // use direct access to the DB to make sure we're not // getting the session token stripped return config.database .loadSchema() .then(schemaController => { - return schemaController.getOneSchema('_User', true); + return schemaController.getOneSchema("_User", true); }) .then(schema => { - return config.database.adapter.find('_User', schema, { objectId: '1234567890' }, {}); + return config.database.adapter.find( + "_User", + schema, + { objectId: "1234567890" }, + {} + ); }) .then(results => { expect(results.length).toBe(1); @@ -57,22 +62,22 @@ describe_only_db('mongo')('revocable sessions', () => { ); }); - it('should be able to become with revocable session token', done => { + it("should be able to become with revocable session token", done => { const user = Parse.Object.fromJSON({ - className: '_User', - objectId: '1234567890', + className: "_User", + objectId: "1234567890", sessionToken: sessionToken, }); user ._upgradeToRevocableSession() .then(res => { - expect(res.getSessionToken().indexOf('r:')).toBe(0); + expect(res.getSessionToken().indexOf("r:")).toBe(0); return Parse.User.logOut() .then(() => { return Parse.User.become(res.getSessionToken()); }) .then(user => { - expect(user.id).toEqual('1234567890'); + expect(user.id).toEqual("1234567890"); }); }) .then( @@ -86,25 +91,25 @@ describe_only_db('mongo')('revocable sessions', () => { ); }); - it('should not upgrade bad legacy session token', done => { + it("should not upgrade bad legacy session token", done => { request({ - method: 'POST', - url: Parse.serverURL + '/upgradeToRevocableSession', + method: "POST", + url: Parse.serverURL + "/upgradeToRevocableSession", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Rest-API-Key': 'rest', - 'X-Parse-Session-Token': 'badSessionToken', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Rest-API-Key": "rest", + "X-Parse-Session-Token": "badSessionToken", }, }) .then( () => { - fail('should not be able to upgrade a bad token'); + fail("should not be able to upgrade a bad token"); }, response => { expect(response.status).toBe(400); expect(response.data).not.toBeUndefined(); expect(response.data.code).toBe(Parse.Error.INVALID_SESSION_TOKEN); - expect(response.data.error).toEqual('invalid legacy session token'); + expect(response.data.error).toEqual("invalid legacy session token"); } ) .then(() => { @@ -112,24 +117,24 @@ describe_only_db('mongo')('revocable sessions', () => { }); }); - it('should not crash without session token #2720', done => { + it("should not crash without session token #2720", done => { request({ - method: 'POST', - url: Parse.serverURL + '/upgradeToRevocableSession', + method: "POST", + url: Parse.serverURL + "/upgradeToRevocableSession", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Rest-API-Key': 'rest', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Rest-API-Key": "rest", }, }) .then( () => { - fail('should not be able to upgrade a bad token'); + fail("should not be able to upgrade a bad token"); }, response => { expect(response.status).toBe(404); expect(response.data).not.toBeUndefined(); expect(response.data.code).toBe(Parse.Error.OBJECT_NOT_FOUND); - expect(response.data.error).toEqual('invalid session'); + expect(response.data.error).toEqual("invalid session"); } ) .then(() => { diff --git a/spec/Schema.spec.js b/spec/Schema.spec.js index 2192678797..adc6ba6353 100644 --- a/spec/Schema.spec.js +++ b/spec/Schema.spec.js @@ -1,34 +1,37 @@ -'use strict'; +"use strict"; -const Config = require('../lib/Config'); -const SchemaController = require('../lib/Controllers/SchemaController'); -const dd = require('deep-diff'); +const Config = require("../lib/Config"); +const SchemaController = require("../lib/Controllers/SchemaController"); +const dd = require("deep-diff"); let config; const hasAllPODobject = () => { - const obj = new Parse.Object('HasAllPOD'); - obj.set('aNumber', 5); - obj.set('aString', 'string'); - obj.set('aBool', true); - obj.set('aDate', new Date()); - obj.set('aObject', { k1: 'value', k2: true, k3: 5 }); - obj.set('aArray', ['contents', true, 5]); - obj.set('aGeoPoint', new Parse.GeoPoint({ latitude: 0, longitude: 0 })); - obj.set('aFile', new Parse.File('f.txt', { base64: 'V29ya2luZyBhdCBQYXJzZSBpcyBncmVhdCE=' })); + const obj = new Parse.Object("HasAllPOD"); + obj.set("aNumber", 5); + obj.set("aString", "string"); + obj.set("aBool", true); + obj.set("aDate", new Date()); + obj.set("aObject", { k1: "value", k2: true, k3: 5 }); + obj.set("aArray", ["contents", true, 5]); + obj.set("aGeoPoint", new Parse.GeoPoint({ latitude: 0, longitude: 0 })); + obj.set( + "aFile", + new Parse.File("f.txt", { base64: "V29ya2luZyBhdCBQYXJzZSBpcyBncmVhdCE=" }) + ); return obj; }; -describe('SchemaController', () => { +describe("SchemaController", () => { beforeEach(() => { - config = Config.get('test'); + config = Config.get("test"); }); - it('can validate one object', done => { + it("can validate one object", done => { config.database .loadSchema() .then(schema => { - return schema.validateObject('TestObject', { a: 1, b: 'yo', c: false }); + return schema.validateObject("TestObject", { a: 1, b: "yo", c: false }); }) .then( () => { @@ -41,15 +44,15 @@ describe('SchemaController', () => { ); }); - it('can validate one object with dot notation', done => { + it("can validate one object with dot notation", done => { config.database .loadSchema() .then(schema => { - return schema.validateObject('TestObjectWithSubDoc', { + return schema.validateObject("TestObjectWithSubDoc", { x: false, - y: 'YY', + y: "YY", z: 1, - 'aObject.k1': 'newValue', + "aObject.k1": "newValue", }); }) .then( @@ -63,100 +66,100 @@ describe('SchemaController', () => { ); }); - it('can validate two objects in a row', done => { + it("can validate two objects in a row", done => { config.database .loadSchema() .then(schema => { - return schema.validateObject('Foo', { x: true, y: 'yyy', z: 0 }); + return schema.validateObject("Foo", { x: true, y: "yyy", z: 0 }); }) .then(schema => { - return schema.validateObject('Foo', { x: false, y: 'YY', z: 1 }); + return schema.validateObject("Foo", { x: false, y: "YY", z: 1 }); }) .then(() => { done(); }); }); - it('can validate Relation object', done => { + it("can validate Relation object", done => { config.database .loadSchema() .then(schema => { - return schema.validateObject('Stuff', { - aRelation: { __type: 'Relation', className: 'Stuff' }, + return schema.validateObject("Stuff", { + aRelation: { __type: "Relation", className: "Stuff" }, }); }) .then(schema => { return schema - .validateObject('Stuff', { - aRelation: { __type: 'Pointer', className: 'Stuff' }, + .validateObject("Stuff", { + aRelation: { __type: "Pointer", className: "Stuff" }, }) .then( () => { - done.fail('expected invalidity'); + done.fail("expected invalidity"); }, () => done() ); }, done.fail); }); - it('rejects inconsistent types', done => { + it("rejects inconsistent types", done => { config.database .loadSchema() .then(schema => { - return schema.validateObject('Stuff', { bacon: 7 }); + return schema.validateObject("Stuff", { bacon: 7 }); }) .then(schema => { - return schema.validateObject('Stuff', { bacon: 'z' }); + return schema.validateObject("Stuff", { bacon: "z" }); }) .then( () => { - fail('expected invalidity'); + fail("expected invalidity"); done(); }, () => done() ); }); - it('updates when new fields are added', done => { + it("updates when new fields are added", done => { config.database .loadSchema() .then(schema => { - return schema.validateObject('Stuff', { bacon: 7 }); + return schema.validateObject("Stuff", { bacon: 7 }); }) .then(schema => { - return schema.validateObject('Stuff', { sausage: 8 }); + return schema.validateObject("Stuff", { sausage: 8 }); }) .then(schema => { - return schema.validateObject('Stuff', { sausage: 'ate' }); + return schema.validateObject("Stuff", { sausage: "ate" }); }) .then( () => { - fail('expected invalidity'); + fail("expected invalidity"); done(); }, () => done() ); }); - it('class-level permissions test find', done => { + it("class-level permissions test find", done => { config.database .loadSchema() .then(schema => { // Just to create a valid class - return schema.validateObject('Stuff', { foo: 'bar' }); + return schema.validateObject("Stuff", { foo: "bar" }); }) .then(schema => { - return schema.setPermissions('Stuff', { + return schema.setPermissions("Stuff", { find: {}, }); }) .then(() => { - const query = new Parse.Query('Stuff'); + const query = new Parse.Query("Stuff"); return query.find(); }) .then( () => { - fail('Class permissions should have rejected this query.'); + fail("Class permissions should have rejected this query."); done(); }, () => { @@ -165,7 +168,7 @@ describe('SchemaController', () => { ); }); - it('class-level permissions test user', done => { + it("class-level permissions test user", done => { let user; createTestUser() .then(u => { @@ -174,17 +177,17 @@ describe('SchemaController', () => { }) .then(schema => { // Just to create a valid class - return schema.validateObject('Stuff', { foo: 'bar' }); + return schema.validateObject("Stuff", { foo: "bar" }); }) .then(schema => { const find = {}; find[user.id] = true; - return schema.setPermissions('Stuff', { + return schema.setPermissions("Stuff", { find: find, }); }) .then(() => { - const query = new Parse.Query('Stuff'); + const query = new Parse.Query("Stuff"); return query.find(); }) .then( @@ -192,53 +195,53 @@ describe('SchemaController', () => { done(); }, () => { - fail('Class permissions should have allowed this query.'); + fail("Class permissions should have allowed this query."); done(); } ); }); - it('class-level permissions test get', done => { + it("class-level permissions test get", done => { let obj; createTestUser().then(user => { return ( config.database .loadSchema() // Create a valid class - .then(schema => schema.validateObject('Stuff', { foo: 'bar' })) + .then(schema => schema.validateObject("Stuff", { foo: "bar" })) .then(schema => { const find = {}; const get = {}; get[user.id] = true; - return schema.setPermissions('Stuff', { - create: { '*': true }, + return schema.setPermissions("Stuff", { + create: { "*": true }, find: find, get: get, }); }) .then(() => { - obj = new Parse.Object('Stuff'); - obj.set('foo', 'bar'); + obj = new Parse.Object("Stuff"); + obj.set("foo", "bar"); return obj.save(); }) .then(o => { obj = o; - const query = new Parse.Query('Stuff'); + const query = new Parse.Query("Stuff"); return query.find(); }) .then( () => { - fail('Class permissions should have rejected this query.'); + fail("Class permissions should have rejected this query."); done(); }, () => { - const query = new Parse.Query('Stuff'); + const query = new Parse.Query("Stuff"); return query.get(obj.id).then( () => { done(); }, () => { - fail('Class permissions should have allowed this get query'); + fail("Class permissions should have allowed this get query"); done(); } ); @@ -248,134 +251,136 @@ describe('SchemaController', () => { }); }); - it('class-level permissions test count', done => { + it("class-level permissions test count", done => { let obj; return ( config.database .loadSchema() // Create a valid class - .then(schema => schema.validateObject('Stuff', { foo: 'bar' })) + .then(schema => schema.validateObject("Stuff", { foo: "bar" })) .then(schema => { const count = {}; - return schema.setPermissions('Stuff', { - create: { '*': true }, - find: { '*': true }, + return schema.setPermissions("Stuff", { + create: { "*": true }, + find: { "*": true }, count: count, }); }) .then(() => { - obj = new Parse.Object('Stuff'); - obj.set('foo', 'bar'); + obj = new Parse.Object("Stuff"); + obj.set("foo", "bar"); return obj.save(); }) .then(o => { obj = o; - const query = new Parse.Query('Stuff'); + const query = new Parse.Query("Stuff"); return query.find(); }) .then(results => { expect(results.length).toBe(1); - const query = new Parse.Query('Stuff'); + const query = new Parse.Query("Stuff"); return query.count(); }) .then( () => { - fail('Class permissions should have rejected this query.'); + fail("Class permissions should have rejected this query."); }, err => { - expect(err.message).toEqual('Permission denied for action count on class Stuff.'); + expect(err.message).toEqual( + "Permission denied for action count on class Stuff." + ); done(); } ) ); }); - it('can add classes without needing an object', done => { + it("can add classes without needing an object", done => { config.database .loadSchema() .then(schema => - schema.addClassIfNotExists('NewClass', { - foo: { type: 'String' }, + schema.addClassIfNotExists("NewClass", { + foo: { type: "String" }, }) ) .then(actualSchema => { const expectedSchema = { - className: 'NewClass', + className: "NewClass", fields: { - objectId: { type: 'String' }, - updatedAt: { type: 'Date' }, - createdAt: { type: 'Date' }, - ACL: { type: 'ACL' }, - foo: { type: 'String' }, + objectId: { type: "String" }, + updatedAt: { type: "Date" }, + createdAt: { type: "Date" }, + ACL: { type: "ACL" }, + foo: { type: "String" }, }, classLevelPermissions: { ACL: { - '*': { + "*": { read: true, write: true, }, }, - find: { '*': true }, - get: { '*': true }, - count: { '*': true }, - create: { '*': true }, - update: { '*': true }, - delete: { '*': true }, - addField: { '*': true }, - protectedFields: { '*': [] }, + find: { "*": true }, + get: { "*": true }, + count: { "*": true }, + create: { "*": true }, + update: { "*": true }, + delete: { "*": true }, + addField: { "*": true }, + protectedFields: { "*": [] }, }, }; expect(dd(actualSchema, expectedSchema)).toEqual(undefined); done(); }) .catch(error => { - fail('Error creating class: ' + JSON.stringify(error)); + fail("Error creating class: " + JSON.stringify(error)); }); }); - it('can update classes without needing an object', done => { + it("can update classes without needing an object", done => { const levelPermissions = { ACL: { - '*': { + "*": { read: true, write: true, }, }, - find: { '*': true }, - get: { '*': true }, - count: { '*': true }, - create: { '*': true }, - update: { '*': true }, - delete: { '*': true }, - addField: { '*': true }, - protectedFields: { '*': [] }, + find: { "*": true }, + get: { "*": true }, + count: { "*": true }, + create: { "*": true }, + update: { "*": true }, + delete: { "*": true }, + addField: { "*": true }, + protectedFields: { "*": [] }, }; config.database.loadSchema().then(schema => { schema - .validateObject('NewClass', { foo: 2 }) + .validateObject("NewClass", { foo: 2 }) .then(() => schema.reloadData()) .then(() => schema.updateClass( - 'NewClass', + "NewClass", { - fooOne: { type: 'Number' }, - fooTwo: { type: 'Array' }, - fooThree: { type: 'Date' }, - fooFour: { type: 'Object' }, - fooFive: { type: 'Relation', targetClass: '_User' }, - fooSix: { type: 'String' }, - fooSeven: { type: 'Object' }, - fooEight: { type: 'String' }, - fooNine: { type: 'String' }, - fooTeen: { type: 'Number' }, - fooEleven: { type: 'String' }, - fooTwelve: { type: 'String' }, - fooThirteen: { type: 'String' }, - fooFourteen: { type: 'String' }, - fooFifteen: { type: 'String' }, - fooSixteen: { type: 'String' }, - fooEighteen: { type: 'String' }, - fooNineteen: { type: 'String' }, + fooOne: { type: "Number" }, + fooTwo: { type: "Array" }, + fooThree: { type: "Date" }, + fooFour: { type: "Object" }, + fooFive: { type: "Relation", targetClass: "_User" }, + fooSix: { type: "String" }, + fooSeven: { type: "Object" }, + fooEight: { type: "String" }, + fooNine: { type: "String" }, + fooTeen: { type: "Number" }, + fooEleven: { type: "String" }, + fooTwelve: { type: "String" }, + fooThirteen: { type: "String" }, + fooFourteen: { type: "String" }, + fooFifteen: { type: "String" }, + fooSixteen: { type: "String" }, + fooEighteen: { type: "String" }, + fooNineteen: { type: "String" }, }, levelPermissions, {}, @@ -384,31 +389,31 @@ describe('SchemaController', () => { ) .then(actualSchema => { const expectedSchema = { - className: 'NewClass', + className: "NewClass", fields: { - objectId: { type: 'String' }, - updatedAt: { type: 'Date' }, - createdAt: { type: 'Date' }, - ACL: { type: 'ACL' }, - foo: { type: 'Number' }, - fooOne: { type: 'Number' }, - fooTwo: { type: 'Array' }, - fooThree: { type: 'Date' }, - fooFour: { type: 'Object' }, - fooFive: { type: 'Relation', targetClass: '_User' }, - fooSix: { type: 'String' }, - fooSeven: { type: 'Object' }, - fooEight: { type: 'String' }, - fooNine: { type: 'String' }, - fooTeen: { type: 'Number' }, - fooEleven: { type: 'String' }, - fooTwelve: { type: 'String' }, - fooThirteen: { type: 'String' }, - fooFourteen: { type: 'String' }, - fooFifteen: { type: 'String' }, - fooSixteen: { type: 'String' }, - fooEighteen: { type: 'String' }, - fooNineteen: { type: 'String' }, + objectId: { type: "String" }, + updatedAt: { type: "Date" }, + createdAt: { type: "Date" }, + ACL: { type: "ACL" }, + foo: { type: "Number" }, + fooOne: { type: "Number" }, + fooTwo: { type: "Array" }, + fooThree: { type: "Date" }, + fooFour: { type: "Object" }, + fooFive: { type: "Relation", targetClass: "_User" }, + fooSix: { type: "String" }, + fooSeven: { type: "Object" }, + fooEight: { type: "String" }, + fooNine: { type: "String" }, + fooTeen: { type: "Number" }, + fooEleven: { type: "String" }, + fooTwelve: { type: "String" }, + fooThirteen: { type: "String" }, + fooFourteen: { type: "String" }, + fooFifteen: { type: "String" }, + fooSixteen: { type: "String" }, + fooEighteen: { type: "String" }, + fooNineteen: { type: "String" }, }, classLevelPermissions: { ...levelPermissions }, indexes: { @@ -422,99 +427,109 @@ describe('SchemaController', () => { .catch(error => { console.trace(error); done(); - fail('Error creating class: ' + JSON.stringify(error)); + fail("Error creating class: " + JSON.stringify(error)); }); }); }); - it('can update class level permission', done => { + it("can update class level permission", done => { const newLevelPermissions = { find: {}, - get: { '*': true }, + get: { "*": true }, count: {}, - create: { '*': true }, + create: { "*": true }, update: {}, - delete: { '*': true }, + delete: { "*": true }, addField: {}, - protectedFields: { '*': [] }, + protectedFields: { "*": [] }, }; config.database.loadSchema().then(schema => { schema - .validateObject('NewClass', { foo: 2 }) + .validateObject("NewClass", { foo: 2 }) .then(() => schema.reloadData()) - .then(() => schema.updateClass('NewClass', {}, newLevelPermissions, {}, config.database)) + .then(() => + schema.updateClass( + "NewClass", + {}, + newLevelPermissions, + {}, + config.database + ) + ) .then(actualSchema => { - expect(dd(actualSchema.classLevelPermissions, newLevelPermissions)).toEqual(undefined); + expect( + dd(actualSchema.classLevelPermissions, newLevelPermissions) + ).toEqual(undefined); done(); }) .catch(error => { console.trace(error); done(); - fail('Error creating class: ' + JSON.stringify(error)); + fail("Error creating class: " + JSON.stringify(error)); }); }); }); - it('will fail to create a class if that class was already created by an object', done => { + it("will fail to create a class if that class was already created by an object", done => { config.database.loadSchema().then(schema => { schema - .validateObject('NewClass', { foo: 7 }) + .validateObject("NewClass", { foo: 7 }) .then(() => schema.reloadData()) .then(() => - schema.addClassIfNotExists('NewClass', { - foo: { type: 'String' }, + schema.addClassIfNotExists("NewClass", { + foo: { type: "String" }, }) ) .catch(error => { expect(error.code).toEqual(Parse.Error.INVALID_CLASS_NAME); - expect(error.message).toEqual('Class NewClass already exists.'); + expect(error.message).toEqual("Class NewClass already exists."); done(); }); }); }); - it('will resolve class creation races appropriately', done => { + it("will resolve class creation races appropriately", done => { // If two callers race to create the same schema, the response to the // race loser should be the same as if they hadn't been racing. config.database.loadSchema().then(schema => { const p1 = schema - .addClassIfNotExists('NewClass', { - foo: { type: 'String' }, + .addClassIfNotExists("NewClass", { + foo: { type: "String" }, }) .then(validateSchema) .catch(validateError); const p2 = schema - .addClassIfNotExists('NewClass', { - foo: { type: 'String' }, + .addClassIfNotExists("NewClass", { + foo: { type: "String" }, }) .then(validateSchema) .catch(validateError); let schemaValidated = false; function validateSchema(actualSchema) { const expectedSchema = { - className: 'NewClass', + className: "NewClass", fields: { - objectId: { type: 'String' }, - updatedAt: { type: 'Date' }, - createdAt: { type: 'Date' }, - ACL: { type: 'ACL' }, - foo: { type: 'String' }, + objectId: { type: "String" }, + updatedAt: { type: "Date" }, + createdAt: { type: "Date" }, + ACL: { type: "ACL" }, + foo: { type: "String" }, }, classLevelPermissions: { ACL: { - '*': { + "*": { read: true, write: true, }, }, - find: { '*': true }, - get: { '*': true }, - count: { '*': true }, - create: { '*': true }, - update: { '*': true }, - delete: { '*': true }, - addField: { '*': true }, - protectedFields: { '*': [] }, + find: { "*": true }, + get: { "*": true }, + count: { "*": true }, + create: { "*": true }, + update: { "*": true }, + delete: { "*": true }, + addField: { "*": true }, + protectedFields: { "*": [] }, }, }; expect(dd(actualSchema, expectedSchema)).toEqual(undefined); @@ -523,7 +538,7 @@ describe('SchemaController', () => { let errorValidated = false; function validateError(error) { expect(error.code).toEqual(Parse.Error.INVALID_CLASS_NAME); - expect(error.message).toEqual('Class NewClass already exists.'); + expect(error.message).toEqual("Class NewClass already exists."); errorValidated = true; } Promise.all([p1, p2]).then(() => { @@ -534,203 +549,215 @@ describe('SchemaController', () => { }); }); - it('refuses to create classes with invalid names', done => { + it("refuses to create classes with invalid names", done => { config.database.loadSchema().then(schema => { - schema.addClassIfNotExists('_InvalidName', { foo: { type: 'String' } }).catch(error => { - expect(error.message).toEqual( - 'Invalid classname: _InvalidName, classnames can only have alphanumeric characters and _, and must start with an alpha character ' - ); - done(); - }); + schema + .addClassIfNotExists("_InvalidName", { foo: { type: "String" } }) + .catch(error => { + expect(error.message).toEqual( + "Invalid classname: _InvalidName, classnames can only have alphanumeric characters and _, and must start with an alpha character " + ); + done(); + }); }); }); - it('refuses to add fields with invalid names', done => { + it("refuses to add fields with invalid names", done => { config.database .loadSchema() .then(schema => - schema.addClassIfNotExists('NewClass', { - '0InvalidName': { type: 'String' }, + schema.addClassIfNotExists("NewClass", { + "0InvalidName": { type: "String" }, }) ) .catch(error => { expect(error.code).toEqual(Parse.Error.INVALID_KEY_NAME); - expect(error.message).toEqual('invalid field name: 0InvalidName'); + expect(error.message).toEqual("invalid field name: 0InvalidName"); done(); }); }); - it('refuses to explicitly create the default fields for custom classes', done => { + it("refuses to explicitly create the default fields for custom classes", done => { config.database .loadSchema() - .then(schema => schema.addClassIfNotExists('NewClass', { objectId: { type: 'String' } })) + .then(schema => + schema.addClassIfNotExists("NewClass", { objectId: { type: "String" } }) + ) .catch(error => { expect(error.code).toEqual(136); - expect(error.message).toEqual('field objectId cannot be added'); + expect(error.message).toEqual("field objectId cannot be added"); done(); }); }); - it('refuses to explicitly create the default fields for non-custom classes', done => { + it("refuses to explicitly create the default fields for non-custom classes", done => { config.database .loadSchema() .then(schema => - schema.addClassIfNotExists('_Installation', { - localeIdentifier: { type: 'String' }, + schema.addClassIfNotExists("_Installation", { + localeIdentifier: { type: "String" }, }) ) .catch(error => { expect(error.code).toEqual(136); - expect(error.message).toEqual('field localeIdentifier cannot be added'); + expect(error.message).toEqual("field localeIdentifier cannot be added"); done(); }); }); - it('refuses to add fields with invalid types', done => { + it("refuses to add fields with invalid types", done => { config.database .loadSchema() .then(schema => - schema.addClassIfNotExists('NewClass', { + schema.addClassIfNotExists("NewClass", { foo: { type: 7 }, }) ) .catch(error => { expect(error.code).toEqual(Parse.Error.INVALID_JSON); - expect(error.message).toEqual('invalid JSON'); + expect(error.message).toEqual("invalid JSON"); done(); }); }); - it('refuses to add fields with invalid pointer types', done => { + it("refuses to add fields with invalid pointer types", done => { config.database .loadSchema() .then(schema => - schema.addClassIfNotExists('NewClass', { - foo: { type: 'Pointer' }, + schema.addClassIfNotExists("NewClass", { + foo: { type: "Pointer" }, }) ) .catch(error => { expect(error.code).toEqual(135); - expect(error.message).toEqual('type Pointer needs a class name'); + expect(error.message).toEqual("type Pointer needs a class name"); done(); }); }); - it('refuses to add fields with invalid pointer target', done => { + it("refuses to add fields with invalid pointer target", done => { config.database .loadSchema() .then(schema => - schema.addClassIfNotExists('NewClass', { - foo: { type: 'Pointer', targetClass: 7 }, + schema.addClassIfNotExists("NewClass", { + foo: { type: "Pointer", targetClass: 7 }, }) ) .catch(error => { expect(error.code).toEqual(Parse.Error.INVALID_JSON); - expect(error.message).toEqual('invalid JSON'); + expect(error.message).toEqual("invalid JSON"); done(); }); }); - it('refuses to add fields with invalid Relation type', done => { + it("refuses to add fields with invalid Relation type", done => { config.database .loadSchema() .then(schema => - schema.addClassIfNotExists('NewClass', { - foo: { type: 'Relation', uselessKey: 7 }, + schema.addClassIfNotExists("NewClass", { + foo: { type: "Relation", uselessKey: 7 }, }) ) .catch(error => { expect(error.code).toEqual(135); - expect(error.message).toEqual('type Relation needs a class name'); + expect(error.message).toEqual("type Relation needs a class name"); done(); }); }); - it('refuses to add fields with invalid relation target', done => { + it("refuses to add fields with invalid relation target", done => { config.database .loadSchema() .then(schema => - schema.addClassIfNotExists('NewClass', { - foo: { type: 'Relation', targetClass: 7 }, + schema.addClassIfNotExists("NewClass", { + foo: { type: "Relation", targetClass: 7 }, }) ) .catch(error => { expect(error.code).toEqual(Parse.Error.INVALID_JSON); - expect(error.message).toEqual('invalid JSON'); + expect(error.message).toEqual("invalid JSON"); done(); }); }); - it('refuses to add fields with uncreatable pointer target class', done => { + it("refuses to add fields with uncreatable pointer target class", done => { config.database .loadSchema() .then(schema => - schema.addClassIfNotExists('NewClass', { - foo: { type: 'Pointer', targetClass: 'not a valid class name' }, + schema.addClassIfNotExists("NewClass", { + foo: { type: "Pointer", targetClass: "not a valid class name" }, }) ) .catch(error => { expect(error.code).toEqual(Parse.Error.INVALID_CLASS_NAME); expect(error.message).toEqual( - 'Invalid classname: not a valid class name, classnames can only have alphanumeric characters and _, and must start with an alpha character ' + "Invalid classname: not a valid class name, classnames can only have alphanumeric characters and _, and must start with an alpha character " ); done(); }); }); - it('refuses to add fields with uncreatable relation target class', done => { + it("refuses to add fields with uncreatable relation target class", done => { config.database .loadSchema() .then(schema => - schema.addClassIfNotExists('NewClass', { - foo: { type: 'Relation', targetClass: 'not a valid class name' }, + schema.addClassIfNotExists("NewClass", { + foo: { type: "Relation", targetClass: "not a valid class name" }, }) ) .catch(error => { expect(error.code).toEqual(Parse.Error.INVALID_CLASS_NAME); expect(error.message).toEqual( - 'Invalid classname: not a valid class name, classnames can only have alphanumeric characters and _, and must start with an alpha character ' + "Invalid classname: not a valid class name, classnames can only have alphanumeric characters and _, and must start with an alpha character " ); done(); }); }); - it('refuses to add fields with unknown types', done => { + it("refuses to add fields with unknown types", done => { config.database .loadSchema() .then(schema => - schema.addClassIfNotExists('NewClass', { - foo: { type: 'Unknown' }, + schema.addClassIfNotExists("NewClass", { + foo: { type: "Unknown" }, }) ) .catch(error => { expect(error.code).toEqual(Parse.Error.INCORRECT_TYPE); - expect(error.message).toEqual('invalid field type: Unknown'); + expect(error.message).toEqual("invalid field type: Unknown"); done(); }); }); - it('refuses to add CLP with incorrect find', done => { + it("refuses to add CLP with incorrect find", done => { const levelPermissions = { ACL: { - '*': { + "*": { read: true, write: true, }, }, - find: { '*': false }, - get: { '*': true }, - create: { '*': true }, - update: { '*': true }, - delete: { '*': true }, - addField: { '*': true }, - protectedFields: { '*': ['email'] }, + find: { "*": false }, + get: { "*": true }, + create: { "*": true }, + update: { "*": true }, + delete: { "*": true }, + addField: { "*": true }, + protectedFields: { "*": ["email"] }, }; config.database.loadSchema().then(schema => { schema - .validateObject('NewClass', {}) + .validateObject("NewClass", {}) .then(() => schema.reloadData()) - .then(() => schema.updateClass('NewClass', {}, levelPermissions, {}, config.database)) + .then(() => + schema.updateClass( + "NewClass", + {}, + levelPermissions, + {}, + config.database + ) + ) .then(done.fail) .catch(error => { expect(error.code).toEqual(Parse.Error.INVALID_JSON); @@ -739,27 +766,35 @@ describe('SchemaController', () => { }); }); - it('refuses to add CLP when incorrectly sending a string to protectedFields object value instead of an array', done => { + it("refuses to add CLP when incorrectly sending a string to protectedFields object value instead of an array", done => { const levelPermissions = { ACL: { - '*': { + "*": { read: true, write: true, }, }, - find: { '*': true }, - get: { '*': true }, - create: { '*': true }, - update: { '*': true }, - delete: { '*': true }, - addField: { '*': true }, - protectedFields: { '*': 'email' }, + find: { "*": true }, + get: { "*": true }, + create: { "*": true }, + update: { "*": true }, + delete: { "*": true }, + addField: { "*": true }, + protectedFields: { "*": "email" }, }; config.database.loadSchema().then(schema => { schema - .validateObject('NewClass', {}) + .validateObject("NewClass", {}) .then(() => schema.reloadData()) - .then(() => schema.updateClass('NewClass', {}, levelPermissions, {}, config.database)) + .then(() => + schema.updateClass( + "NewClass", + {}, + levelPermissions, + {}, + config.database + ) + ) .then(done.fail) .catch(error => { expect(error.code).toEqual(Parse.Error.INVALID_JSON); @@ -768,67 +803,67 @@ describe('SchemaController', () => { }); }); - it('will create classes', done => { + it("will create classes", done => { config.database .loadSchema() .then(schema => - schema.addClassIfNotExists('NewClass', { - aNumber: { type: 'Number' }, - aString: { type: 'String' }, - aBool: { type: 'Boolean' }, - aDate: { type: 'Date' }, - aObject: { type: 'Object' }, - aArray: { type: 'Array' }, - aGeoPoint: { type: 'GeoPoint' }, - aFile: { type: 'File' }, + schema.addClassIfNotExists("NewClass", { + aNumber: { type: "Number" }, + aString: { type: "String" }, + aBool: { type: "Boolean" }, + aDate: { type: "Date" }, + aObject: { type: "Object" }, + aArray: { type: "Array" }, + aGeoPoint: { type: "GeoPoint" }, + aFile: { type: "File" }, aPointer: { - type: 'Pointer', - targetClass: 'ThisClassDoesNotExistYet', + type: "Pointer", + targetClass: "ThisClassDoesNotExistYet", }, - aRelation: { type: 'Relation', targetClass: 'NewClass' }, - aBytes: { type: 'Bytes' }, - aPolygon: { type: 'Polygon' }, + aRelation: { type: "Relation", targetClass: "NewClass" }, + aBytes: { type: "Bytes" }, + aPolygon: { type: "Polygon" }, }) ) .then(actualSchema => { const expectedSchema = { - className: 'NewClass', + className: "NewClass", fields: { - objectId: { type: 'String' }, - updatedAt: { type: 'Date' }, - createdAt: { type: 'Date' }, - ACL: { type: 'ACL' }, - aString: { type: 'String' }, - aNumber: { type: 'Number' }, - aBool: { type: 'Boolean' }, - aDate: { type: 'Date' }, - aObject: { type: 'Object' }, - aArray: { type: 'Array' }, - aGeoPoint: { type: 'GeoPoint' }, - aFile: { type: 'File' }, + objectId: { type: "String" }, + updatedAt: { type: "Date" }, + createdAt: { type: "Date" }, + ACL: { type: "ACL" }, + aString: { type: "String" }, + aNumber: { type: "Number" }, + aBool: { type: "Boolean" }, + aDate: { type: "Date" }, + aObject: { type: "Object" }, + aArray: { type: "Array" }, + aGeoPoint: { type: "GeoPoint" }, + aFile: { type: "File" }, aPointer: { - type: 'Pointer', - targetClass: 'ThisClassDoesNotExistYet', + type: "Pointer", + targetClass: "ThisClassDoesNotExistYet", }, - aRelation: { type: 'Relation', targetClass: 'NewClass' }, - aBytes: { type: 'Bytes' }, - aPolygon: { type: 'Polygon' }, + aRelation: { type: "Relation", targetClass: "NewClass" }, + aBytes: { type: "Bytes" }, + aPolygon: { type: "Polygon" }, }, classLevelPermissions: { ACL: { - '*': { + "*": { read: true, write: true, }, }, - find: { '*': true }, - get: { '*': true }, - count: { '*': true }, - create: { '*': true }, - update: { '*': true }, - delete: { '*': true }, - addField: { '*': true }, - protectedFields: { '*': [] }, + find: { "*": true }, + get: { "*": true }, + count: { "*": true }, + create: { "*": true }, + update: { "*": true }, + delete: { "*": true }, + addField: { "*": true }, + protectedFields: { "*": [] }, }, }; expect(dd(actualSchema, expectedSchema)).toEqual(undefined); @@ -836,52 +871,52 @@ describe('SchemaController', () => { }); }); - it('creates the default fields for non-custom classes', done => { + it("creates the default fields for non-custom classes", done => { config.database .loadSchema() .then(schema => - schema.addClassIfNotExists('_Installation', { - foo: { type: 'Number' }, + schema.addClassIfNotExists("_Installation", { + foo: { type: "Number" }, }) ) .then(actualSchema => { const expectedSchema = { - className: '_Installation', + className: "_Installation", fields: { - objectId: { type: 'String' }, - updatedAt: { type: 'Date' }, - createdAt: { type: 'Date' }, - ACL: { type: 'ACL' }, - foo: { type: 'Number' }, - installationId: { type: 'String' }, - deviceToken: { type: 'String' }, - channels: { type: 'Array' }, - deviceType: { type: 'String' }, - pushType: { type: 'String' }, - GCMSenderId: { type: 'String' }, - timeZone: { type: 'String' }, - localeIdentifier: { type: 'String' }, - badge: { type: 'Number' }, - appVersion: { type: 'String' }, - appName: { type: 'String' }, - appIdentifier: { type: 'String' }, - parseVersion: { type: 'String' }, + objectId: { type: "String" }, + updatedAt: { type: "Date" }, + createdAt: { type: "Date" }, + ACL: { type: "ACL" }, + foo: { type: "Number" }, + installationId: { type: "String" }, + deviceToken: { type: "String" }, + channels: { type: "Array" }, + deviceType: { type: "String" }, + pushType: { type: "String" }, + GCMSenderId: { type: "String" }, + timeZone: { type: "String" }, + localeIdentifier: { type: "String" }, + badge: { type: "Number" }, + appVersion: { type: "String" }, + appName: { type: "String" }, + appIdentifier: { type: "String" }, + parseVersion: { type: "String" }, }, classLevelPermissions: { ACL: { - '*': { + "*": { read: true, write: true, }, }, - find: { '*': true }, - get: { '*': true }, - count: { '*': true }, - create: { '*': true }, - update: { '*': true }, - delete: { '*': true }, - addField: { '*': true }, - protectedFields: { '*': [] }, + find: { "*": true }, + get: { "*": true }, + count: { "*": true }, + create: { "*": true }, + update: { "*": true }, + delete: { "*": true }, + addField: { "*": true }, + protectedFields: { "*": [] }, }, }; expect(dd(actualSchema, expectedSchema)).toEqual(undefined); @@ -889,39 +924,39 @@ describe('SchemaController', () => { }); }); - it('creates non-custom classes which include relation field', async done => { + it("creates non-custom classes which include relation field", async done => { await reconfigureServer(); config.database .loadSchema() //as `_Role` is always created by default, we only get it here - .then(schema => schema.getOneSchema('_Role')) + .then(schema => schema.getOneSchema("_Role")) .then(actualSchema => { const expectedSchema = { - className: '_Role', + className: "_Role", fields: { - objectId: { type: 'String' }, - updatedAt: { type: 'Date' }, - createdAt: { type: 'Date' }, - ACL: { type: 'ACL' }, - name: { type: 'String' }, - users: { type: 'Relation', targetClass: '_User' }, - roles: { type: 'Relation', targetClass: '_Role' }, + objectId: { type: "String" }, + updatedAt: { type: "Date" }, + createdAt: { type: "Date" }, + ACL: { type: "ACL" }, + name: { type: "String" }, + users: { type: "Relation", targetClass: "_User" }, + roles: { type: "Relation", targetClass: "_Role" }, }, classLevelPermissions: { ACL: { - '*': { + "*": { read: true, write: true, }, }, - find: { '*': true }, - get: { '*': true }, - count: { '*': true }, - create: { '*': true }, - update: { '*': true }, - delete: { '*': true }, - addField: { '*': true }, - protectedFields: { '*': [] }, + find: { "*": true }, + get: { "*": true }, + count: { "*": true }, + create: { "*": true }, + update: { "*": true }, + delete: { "*": true }, + addField: { "*": true }, + protectedFields: { "*": [] }, }, }; expect(dd(actualSchema, expectedSchema)).toEqual(undefined); @@ -929,39 +964,39 @@ describe('SchemaController', () => { }); }); - it('creates non-custom classes which include pointer field', done => { + it("creates non-custom classes which include pointer field", done => { config.database .loadSchema() - .then(schema => schema.addClassIfNotExists('_Session', {})) + .then(schema => schema.addClassIfNotExists("_Session", {})) .then(actualSchema => { const expectedSchema = { - className: '_Session', + className: "_Session", fields: { - objectId: { type: 'String' }, - updatedAt: { type: 'Date' }, - createdAt: { type: 'Date' }, - user: { type: 'Pointer', targetClass: '_User' }, - installationId: { type: 'String' }, - sessionToken: { type: 'String' }, - expiresAt: { type: 'Date' }, - createdWith: { type: 'Object' }, - ACL: { type: 'ACL' }, + objectId: { type: "String" }, + updatedAt: { type: "Date" }, + createdAt: { type: "Date" }, + user: { type: "Pointer", targetClass: "_User" }, + installationId: { type: "String" }, + sessionToken: { type: "String" }, + expiresAt: { type: "Date" }, + createdWith: { type: "Object" }, + ACL: { type: "ACL" }, }, classLevelPermissions: { ACL: { - '*': { + "*": { read: true, write: true, }, }, - find: { '*': true }, - get: { '*': true }, - count: { '*': true }, - create: { '*': true }, - update: { '*': true }, - delete: { '*': true }, - addField: { '*': true }, - protectedFields: { '*': [] }, + find: { "*": true }, + get: { "*": true }, + count: { "*": true }, + create: { "*": true }, + update: { "*": true }, + delete: { "*": true }, + addField: { "*": true }, + protectedFields: { "*": [] }, }, }; expect(dd(actualSchema, expectedSchema)).toEqual(undefined); @@ -969,34 +1004,34 @@ describe('SchemaController', () => { }); }); - it('refuses to create two geopoints', done => { + it("refuses to create two geopoints", done => { config.database .loadSchema() .then(schema => - schema.addClassIfNotExists('NewClass', { - geo1: { type: 'GeoPoint' }, - geo2: { type: 'GeoPoint' }, + schema.addClassIfNotExists("NewClass", { + geo1: { type: "GeoPoint" }, + geo2: { type: "GeoPoint" }, }) ) .catch(error => { expect(error.code).toEqual(Parse.Error.INCORRECT_TYPE); expect(error.message).toEqual( - 'currently, only one GeoPoint field may exist in an object. Adding geo2 when geo1 already exists.' + "currently, only one GeoPoint field may exist in an object. Adding geo2 when geo1 already exists." ); done(); }); }); - it('can check if a class exists', done => { + it("can check if a class exists", done => { config.database .loadSchema() .then(schema => { return schema - .addClassIfNotExists('NewClass', {}) + .addClassIfNotExists("NewClass", {}) .then(() => schema.reloadData({ clearCache: true })) .then(() => { schema - .hasClass('NewClass') + .hasClass("NewClass") .then(hasClass => { expect(hasClass).toEqual(true); done(); @@ -1004,7 +1039,7 @@ describe('SchemaController', () => { .catch(fail); schema - .hasClass('NonexistantClass') + .hasClass("NonexistantClass") .then(hasClass => { expect(hasClass).toEqual(false); done(); @@ -1019,84 +1054,104 @@ describe('SchemaController', () => { .catch(() => fail("Couldn't load schema")); }); - it('refuses to delete fields from invalid class names', done => { + it("refuses to delete fields from invalid class names", done => { config.database .loadSchema() - .then(schema => schema.deleteField('fieldName', 'invalid class name')) + .then(schema => schema.deleteField("fieldName", "invalid class name")) .catch(error => { expect(error.code).toEqual(Parse.Error.INVALID_CLASS_NAME); done(); }); }); - it('refuses to delete invalid fields', done => { + it("refuses to delete invalid fields", done => { config.database .loadSchema() - .then(schema => schema.deleteField('invalid field name', 'ValidClassName')) + .then(schema => + schema.deleteField("invalid field name", "ValidClassName") + ) .catch(error => { expect(error.code).toEqual(Parse.Error.INVALID_KEY_NAME); done(); }); }); - it('refuses to delete the default fields', done => { + it("refuses to delete the default fields", done => { config.database .loadSchema() - .then(schema => schema.deleteField('installationId', '_Installation')) + .then(schema => schema.deleteField("installationId", "_Installation")) .catch(error => { expect(error.code).toEqual(136); - expect(error.message).toEqual('field installationId cannot be changed'); + expect(error.message).toEqual("field installationId cannot be changed"); done(); }); }); - it('refuses to delete fields from nonexistant classes', done => { + it("refuses to delete fields from nonexistant classes", done => { config.database .loadSchema() - .then(schema => schema.deleteField('field', 'NoClass')) + .then(schema => schema.deleteField("field", "NoClass")) .catch(error => { expect(error.code).toEqual(Parse.Error.INVALID_CLASS_NAME); - expect(error.message).toEqual('Class NoClass does not exist.'); + expect(error.message).toEqual("Class NoClass does not exist."); done(); }); }); - it('refuses to delete fields that dont exist', done => { + it("refuses to delete fields that dont exist", done => { hasAllPODobject() .save() .then(() => config.database.loadSchema()) - .then(schema => schema.deleteField('missingField', 'HasAllPOD')) + .then(schema => schema.deleteField("missingField", "HasAllPOD")) .catch(error => { expect(error.code).toEqual(255); - expect(error.message).toEqual('Field missingField does not exist, cannot delete.'); + expect(error.message).toEqual( + "Field missingField does not exist, cannot delete." + ); done(); }); }); - it('drops related collection when deleting relation field', done => { + it("drops related collection when deleting relation field", done => { const obj1 = hasAllPODobject(); obj1 .save() .then(savedObj1 => { - const obj2 = new Parse.Object('HasPointersAndRelations'); - obj2.set('aPointer', savedObj1); - const relation = obj2.relation('aRelation'); + const obj2 = new Parse.Object("HasPointersAndRelations"); + obj2.set("aPointer", savedObj1); + const relation = obj2.relation("aRelation"); relation.add(obj1); return obj2.save(); }) - .then(() => config.database.collectionExists('_Join:aRelation:HasPointersAndRelations')) + .then(() => + config.database.collectionExists( + "_Join:aRelation:HasPointersAndRelations" + ) + ) .then(exists => { if (!exists) { - fail('Relation collection ' + 'should exist after save.'); + fail("Relation collection " + "should exist after save."); } }) .then(() => config.database.loadSchema()) - .then(schema => schema.deleteField('aRelation', 'HasPointersAndRelations', config.database)) - .then(() => config.database.collectionExists('_Join:aRelation:HasPointersAndRelations')) + .then(schema => + schema.deleteField( + "aRelation", + "HasPointersAndRelations", + config.database + ) + ) + .then(() => + config.database.collectionExists( + "_Join:aRelation:HasPointersAndRelations" + ) + ) .then( exists => { if (exists) { - fail('Relation collection should not exist after deleting relation field.'); + fail( + "Relation collection should not exist after deleting relation field." + ); } done(); }, @@ -1107,45 +1162,47 @@ describe('SchemaController', () => { ); }); - it('can delete relation field when related _Join collection not exist', done => { + it("can delete relation field when related _Join collection not exist", done => { config.database.loadSchema().then(schema => { schema - .addClassIfNotExists('NewClass', { - relationField: { type: 'Relation', targetClass: '_User' }, + .addClassIfNotExists("NewClass", { + relationField: { type: "Relation", targetClass: "_User" }, }) .then(actualSchema => { const expectedSchema = { - className: 'NewClass', + className: "NewClass", fields: { - objectId: { type: 'String' }, - updatedAt: { type: 'Date' }, - createdAt: { type: 'Date' }, - ACL: { type: 'ACL' }, - relationField: { type: 'Relation', targetClass: '_User' }, + objectId: { type: "String" }, + updatedAt: { type: "Date" }, + createdAt: { type: "Date" }, + ACL: { type: "ACL" }, + relationField: { type: "Relation", targetClass: "_User" }, }, classLevelPermissions: { ACL: { - '*': { + "*": { read: true, write: true, }, }, - find: { '*': true }, - get: { '*': true }, - count: { '*': true }, - create: { '*': true }, - update: { '*': true }, - delete: { '*': true }, - addField: { '*': true }, - protectedFields: { '*': [] }, + find: { "*": true }, + get: { "*": true }, + count: { "*": true }, + create: { "*": true }, + update: { "*": true }, + delete: { "*": true }, + addField: { "*": true }, + protectedFields: { "*": [] }, }, }; expect(dd(actualSchema, expectedSchema)).toEqual(undefined); }) - .then(() => config.database.collectionExists('_Join:relationField:NewClass')) + .then(() => + config.database.collectionExists("_Join:relationField:NewClass") + ) .then(exist => { on_db( - 'postgres', + "postgres", () => { // We create the table when creating the column expect(exist).toEqual(true); @@ -1155,41 +1212,50 @@ describe('SchemaController', () => { } ); }) - .then(() => schema.deleteField('relationField', 'NewClass', config.database)) + .then(() => + schema.deleteField("relationField", "NewClass", config.database) + ) .then(() => schema.reloadData()) .then(() => { const expectedSchema = { - objectId: { type: 'String' }, - updatedAt: { type: 'Date' }, - createdAt: { type: 'Date' }, - ACL: { type: 'ACL' }, + objectId: { type: "String" }, + updatedAt: { type: "Date" }, + createdAt: { type: "Date" }, + ACL: { type: "ACL" }, }; - expect(dd(schema.schemaData.NewClass.fields, expectedSchema)).toEqual(undefined); + expect(dd(schema.schemaData.NewClass.fields, expectedSchema)).toEqual( + undefined + ); }) .then(done) .catch(done.fail); }); }); - it('can delete string fields and resave as number field', done => { + it("can delete string fields and resave as number field", done => { Parse.Object.disableSingleInstance(); const obj1 = hasAllPODobject(); const obj2 = hasAllPODobject(); Parse.Object.saveAll([obj1, obj2]) .then(() => config.database.loadSchema()) - .then(schema => schema.deleteField('aString', 'HasAllPOD', config.database)) - .then(() => new Parse.Query('HasAllPOD').get(obj1.id)) + .then(schema => + schema.deleteField("aString", "HasAllPOD", config.database) + ) + .then(() => new Parse.Query("HasAllPOD").get(obj1.id)) .then(obj1Reloaded => { - expect(obj1Reloaded.get('aString')).toEqual(undefined); - obj1Reloaded.set('aString', ['not a string', 'this time']); + expect(obj1Reloaded.get("aString")).toEqual(undefined); + obj1Reloaded.set("aString", ["not a string", "this time"]); obj1Reloaded .save() .then(obj1reloadedAgain => { - expect(obj1reloadedAgain.get('aString')).toEqual(['not a string', 'this time']); - return new Parse.Query('HasAllPOD').get(obj2.id); + expect(obj1reloadedAgain.get("aString")).toEqual([ + "not a string", + "this time", + ]); + return new Parse.Query("HasAllPOD").get(obj2.id); }) .then(obj2reloaded => { - expect(obj2reloaded.get('aString')).toEqual(undefined); + expect(obj2reloaded.get("aString")).toEqual(undefined); done(); }); }) @@ -1199,189 +1265,201 @@ describe('SchemaController', () => { }); }); - it('can delete pointer fields and resave as string', done => { + it("can delete pointer fields and resave as string", done => { Parse.Object.disableSingleInstance(); - const obj1 = new Parse.Object('NewClass'); + const obj1 = new Parse.Object("NewClass"); obj1 .save() .then(() => { - obj1.set('aPointer', obj1); + obj1.set("aPointer", obj1); return obj1.save(); }) .then(obj1 => { - expect(obj1.get('aPointer').id).toEqual(obj1.id); + expect(obj1.get("aPointer").id).toEqual(obj1.id); }) .then(() => config.database.loadSchema()) - .then(schema => schema.deleteField('aPointer', 'NewClass', config.database)) - .then(() => new Parse.Query('NewClass').get(obj1.id)) + .then(schema => + schema.deleteField("aPointer", "NewClass", config.database) + ) + .then(() => new Parse.Query("NewClass").get(obj1.id)) .then(obj1 => { - expect(obj1.get('aPointer')).toEqual(undefined); - obj1.set('aPointer', 'Now a string'); + expect(obj1.get("aPointer")).toEqual(undefined); + obj1.set("aPointer", "Now a string"); return obj1.save(); }) .then(obj1 => { - expect(obj1.get('aPointer')).toEqual('Now a string'); + expect(obj1.get("aPointer")).toEqual("Now a string"); done(); }); }); - it('can merge schemas', done => { + it("can merge schemas", done => { expect( SchemaController.buildMergedSchemaObject( { - _id: 'SomeClass', - someType: { type: 'Number' }, + _id: "SomeClass", + someType: { type: "Number" }, }, { - newType: { type: 'Number' }, + newType: { type: "Number" }, } ) ).toEqual({ - someType: { type: 'Number' }, - newType: { type: 'Number' }, + someType: { type: "Number" }, + newType: { type: "Number" }, }); done(); }); - it('can merge deletions', done => { + it("can merge deletions", done => { expect( SchemaController.buildMergedSchemaObject( { - _id: 'SomeClass', - someType: { type: 'Number' }, - outDatedType: { type: 'String' }, + _id: "SomeClass", + someType: { type: "Number" }, + outDatedType: { type: "String" }, }, { - newType: { type: 'GeoPoint' }, - outDatedType: { __op: 'Delete' }, + newType: { type: "GeoPoint" }, + outDatedType: { __op: "Delete" }, } ) ).toEqual({ - someType: { type: 'Number' }, - newType: { type: 'GeoPoint' }, + someType: { type: "Number" }, + newType: { type: "GeoPoint" }, }); done(); }); - it('ignore default field when merge with system class', done => { + it("ignore default field when merge with system class", done => { expect( SchemaController.buildMergedSchemaObject( { - _id: '_User', - username: { type: 'String' }, - password: { type: 'String' }, - email: { type: 'String' }, - emailVerified: { type: 'Boolean' }, + _id: "_User", + username: { type: "String" }, + password: { type: "String" }, + email: { type: "String" }, + emailVerified: { type: "Boolean" }, }, { - emailVerified: { type: 'String' }, - customField: { type: 'String' }, + emailVerified: { type: "String" }, + customField: { type: "String" }, } ) ).toEqual({ - customField: { type: 'String' }, + customField: { type: "String" }, }); done(); }); - it('yields a proper schema mismatch error (#2661)', done => { - const anObject = new Parse.Object('AnObject'); - const anotherObject = new Parse.Object('AnotherObject'); - const someObject = new Parse.Object('SomeObject'); + it("yields a proper schema mismatch error (#2661)", done => { + const anObject = new Parse.Object("AnObject"); + const anotherObject = new Parse.Object("AnotherObject"); + const someObject = new Parse.Object("SomeObject"); Parse.Object.saveAll([anObject, anotherObject, someObject]) .then(() => { - anObject.set('pointer', anotherObject); + anObject.set("pointer", anotherObject); return anObject.save(); }) .then(() => { - anObject.set('pointer', someObject); + anObject.set("pointer", someObject); return anObject.save(); }) .then( () => { - fail('shoud not save correctly'); + fail("shoud not save correctly"); done(); }, err => { expect(err instanceof Parse.Error).toBeTruthy(); expect(err.message).toEqual( - 'schema mismatch for AnObject.pointer; expected Pointer but got Pointer' + "schema mismatch for AnObject.pointer; expected Pointer but got Pointer" ); done(); } ); }); - it('yields a proper schema mismatch error bis (#2661)', done => { - const anObject = new Parse.Object('AnObject'); - const someObject = new Parse.Object('SomeObject'); + it("yields a proper schema mismatch error bis (#2661)", done => { + const anObject = new Parse.Object("AnObject"); + const someObject = new Parse.Object("SomeObject"); Parse.Object.saveAll([anObject, someObject]) .then(() => { - anObject.set('number', 1); + anObject.set("number", 1); return anObject.save(); }) .then(() => { - anObject.set('number', someObject); + anObject.set("number", someObject); return anObject.save(); }) .then( () => { - fail('shoud not save correctly'); + fail("shoud not save correctly"); done(); }, err => { expect(err instanceof Parse.Error).toBeTruthy(); expect(err.message).toEqual( - 'schema mismatch for AnObject.number; expected Number but got Pointer' + "schema mismatch for AnObject.number; expected Number but got Pointer" ); done(); } ); }); - it('yields a proper schema mismatch error ter (#2661)', done => { - const anObject = new Parse.Object('AnObject'); - const someObject = new Parse.Object('SomeObject'); + it("yields a proper schema mismatch error ter (#2661)", done => { + const anObject = new Parse.Object("AnObject"); + const someObject = new Parse.Object("SomeObject"); Parse.Object.saveAll([anObject, someObject]) .then(() => { - anObject.set('pointer', someObject); + anObject.set("pointer", someObject); return anObject.save(); }) .then(() => { - anObject.set('pointer', 1); + anObject.set("pointer", 1); return anObject.save(); }) .then( () => { - fail('shoud not save correctly'); + fail("shoud not save correctly"); done(); }, err => { expect(err instanceof Parse.Error).toBeTruthy(); expect(err.message).toEqual( - 'schema mismatch for AnObject.pointer; expected Pointer but got Number' + "schema mismatch for AnObject.pointer; expected Pointer but got Number" ); done(); } ); }); - it('properly handles volatile _Schemas', async done => { + it("properly handles volatile _Schemas", async done => { await reconfigureServer(); function validateSchemaStructure(schema) { - expect(Object.prototype.hasOwnProperty.call(schema, 'className')).toBe(true); - expect(Object.prototype.hasOwnProperty.call(schema, 'fields')).toBe(true); - expect(Object.prototype.hasOwnProperty.call(schema, 'classLevelPermissions')).toBe(true); + expect(Object.prototype.hasOwnProperty.call(schema, "className")).toBe( + true + ); + expect(Object.prototype.hasOwnProperty.call(schema, "fields")).toBe(true); + expect( + Object.prototype.hasOwnProperty.call(schema, "classLevelPermissions") + ).toBe(true); } function validateSchemaDataStructure(schemaData) { Object.keys(schemaData).forEach(className => { const schema = schemaData[className]; // Hooks has className... - if (className != '_Hooks') { - expect(Object.prototype.hasOwnProperty.call(schema, 'className')).toBe(false); + if (className != "_Hooks") { + expect( + Object.prototype.hasOwnProperty.call(schema, "className") + ).toBe(false); } - expect(Object.prototype.hasOwnProperty.call(schema, 'fields')).toBe(false); - expect(Object.prototype.hasOwnProperty.call(schema, 'classLevelPermissions')).toBe(false); + expect(Object.prototype.hasOwnProperty.call(schema, "fields")).toBe( + false + ); + expect( + Object.prototype.hasOwnProperty.call(schema, "classLevelPermissions") + ).toBe(false); }); } let schema; @@ -1389,12 +1467,12 @@ describe('SchemaController', () => { .loadSchema() .then(s => { schema = s; - return schema.getOneSchema('_User', false); + return schema.getOneSchema("_User", false); }) .then(userSchema => { validateSchemaStructure(userSchema); validateSchemaDataStructure(schema.schemaData); - return schema.getOneSchema('_PushStatus', true); + return schema.getOneSchema("_PushStatus", true); }) .then(pushStatusSchema => { validateSchemaStructure(pushStatusSchema); @@ -1404,79 +1482,85 @@ describe('SchemaController', () => { .catch(done.fail); }); - it('should not throw on null field types', async () => { + it("should not throw on null field types", async () => { const schema = await config.database.loadSchema(); - const result = await schema.enforceFieldExists('NewClass', 'fieldName', null); + const result = await schema.enforceFieldExists( + "NewClass", + "fieldName", + null + ); expect(result).toBeUndefined(); }); - it('ensureFields should throw when schema is not set', async () => { + it("ensureFields should throw when schema is not set", async () => { const schema = await config.database.loadSchema(); try { schema.ensureFields([ { - className: 'NewClass', - fieldName: 'fieldName', - type: 'String', + className: "NewClass", + fieldName: "fieldName", + type: "String", }, ]); } catch (e) { - expect(e.message).toBe('Could not add field fieldName'); + expect(e.message).toBe("Could not add field fieldName"); } }); }); -describe('Class Level Permissions for requiredAuth', () => { +describe("Class Level Permissions for requiredAuth", () => { beforeEach(() => { - config = Config.get('test'); + config = Config.get("test"); }); function createUser() { const user = new Parse.User(); - user.set('username', 'hello'); - user.set('password', 'world'); + user.set("username", "hello"); + user.set("password", "world"); return user.signUp(null); } - it('required auth test find', done => { + it("required auth test find", done => { config.database .loadSchema() .then(schema => { // Just to create a valid class - return schema.validateObject('Stuff', { foo: 'bar' }); + return schema.validateObject("Stuff", { foo: "bar" }); }) .then(schema => { - return schema.setPermissions('Stuff', { + return schema.setPermissions("Stuff", { find: { requiresAuthentication: true, }, }); }) .then(() => { - const query = new Parse.Query('Stuff'); + const query = new Parse.Query("Stuff"); return query.find(); }) .then( () => { - fail('Class permissions should have rejected this query.'); + fail("Class permissions should have rejected this query."); done(); }, e => { - expect(e.message).toEqual('Permission denied, user needs to be authenticated.'); + expect(e.message).toEqual( + "Permission denied, user needs to be authenticated." + ); done(); } ); }); - it('required auth test find authenticated', done => { + it("required auth test find authenticated", done => { config.database .loadSchema() .then(schema => { // Just to create a valid class - return schema.validateObject('Stuff', { foo: 'bar' }); + return schema.validateObject("Stuff", { foo: "bar" }); }) .then(schema => { - return schema.setPermissions('Stuff', { + return schema.setPermissions("Stuff", { find: { requiresAuthentication: true, }, @@ -1486,7 +1570,7 @@ describe('Class Level Permissions for requiredAuth', () => { return createUser(); }) .then(() => { - const query = new Parse.Query('Stuff'); + const query = new Parse.Query("Stuff"); return query.find(); }) .then( @@ -1496,21 +1580,21 @@ describe('Class Level Permissions for requiredAuth', () => { }, e => { console.error(e); - fail('Should not have failed'); + fail("Should not have failed"); done(); } ); }); - it('required auth should allow create authenticated', done => { + it("required auth should allow create authenticated", done => { config.database .loadSchema() .then(schema => { // Just to create a valid class - return schema.validateObject('Stuff', { foo: 'bar' }); + return schema.validateObject("Stuff", { foo: "bar" }); }) .then(schema => { - return schema.setPermissions('Stuff', { + return schema.setPermissions("Stuff", { create: { requiresAuthentication: true, }, @@ -1520,8 +1604,8 @@ describe('Class Level Permissions for requiredAuth', () => { return createUser(); }) .then(() => { - const stuff = new Parse.Object('Stuff'); - stuff.set('foo', 'bar'); + const stuff = new Parse.Object("Stuff"); + stuff.set("foo", "bar"); return stuff.save(); }) .then( @@ -1530,52 +1614,54 @@ describe('Class Level Permissions for requiredAuth', () => { }, e => { console.error(e); - fail('Should not have failed'); + fail("Should not have failed"); done(); } ); }); - it('required auth should reject create when not authenticated', done => { + it("required auth should reject create when not authenticated", done => { config.database .loadSchema() .then(schema => { // Just to create a valid class - return schema.validateObject('Stuff', { foo: 'bar' }); + return schema.validateObject("Stuff", { foo: "bar" }); }) .then(schema => { - return schema.setPermissions('Stuff', { + return schema.setPermissions("Stuff", { create: { requiresAuthentication: true, }, }); }) .then(() => { - const stuff = new Parse.Object('Stuff'); - stuff.set('foo', 'bar'); + const stuff = new Parse.Object("Stuff"); + stuff.set("foo", "bar"); return stuff.save(); }) .then( () => { - fail('Class permissions should have rejected this query.'); + fail("Class permissions should have rejected this query."); done(); }, e => { - expect(e.message).toEqual('Permission denied, user needs to be authenticated.'); + expect(e.message).toEqual( + "Permission denied, user needs to be authenticated." + ); done(); } ); }); - it('required auth test create/get/update/delete authenticated', done => { + it("required auth test create/get/update/delete authenticated", done => { config.database .loadSchema() .then(schema => { // Just to create a valid class - return schema.validateObject('Stuff', { foo: 'bar' }); + return schema.validateObject("Stuff", { foo: "bar" }); }) .then(schema => { - return schema.setPermissions('Stuff', { + return schema.setPermissions("Stuff", { create: { requiresAuthentication: true, }, @@ -1594,15 +1680,15 @@ describe('Class Level Permissions for requiredAuth', () => { return createUser(); }) .then(() => { - const stuff = new Parse.Object('Stuff'); - stuff.set('foo', 'bar'); + const stuff = new Parse.Object("Stuff"); + stuff.set("foo", "bar"); return stuff.save().then(() => { - const query = new Parse.Query('Stuff'); + const query = new Parse.Query("Stuff"); return query.get(stuff.id); }); }) .then(gotStuff => { - return gotStuff.save({ foo: 'baz' }).then(() => { + return gotStuff.save({ foo: "baz" }).then(() => { return gotStuff.destroy(); }); }) @@ -1612,137 +1698,141 @@ describe('Class Level Permissions for requiredAuth', () => { }, e => { console.error(e); - fail('Should not have failed'); + fail("Should not have failed"); done(); } ); }); - it('required auth test get not authenticated', done => { + it("required auth test get not authenticated", done => { config.database .loadSchema() .then(schema => { // Just to create a valid class - return schema.validateObject('Stuff', { foo: 'bar' }); + return schema.validateObject("Stuff", { foo: "bar" }); }) .then(schema => { - return schema.setPermissions('Stuff', { + return schema.setPermissions("Stuff", { get: { requiresAuthentication: true, }, create: { - '*': true, + "*": true, }, }); }) .then(() => { - const stuff = new Parse.Object('Stuff'); - stuff.set('foo', 'bar'); + const stuff = new Parse.Object("Stuff"); + stuff.set("foo", "bar"); return stuff.save().then(() => { - const query = new Parse.Query('Stuff'); + const query = new Parse.Query("Stuff"); return query.get(stuff.id); }); }) .then( () => { - fail('Should not succeed!'); + fail("Should not succeed!"); done(); }, e => { - expect(e.message).toEqual('Permission denied, user needs to be authenticated.'); + expect(e.message).toEqual( + "Permission denied, user needs to be authenticated." + ); done(); } ); }); - it('required auth test find not authenticated', done => { + it("required auth test find not authenticated", done => { config.database .loadSchema() .then(schema => { // Just to create a valid class - return schema.validateObject('Stuff', { foo: 'bar' }); + return schema.validateObject("Stuff", { foo: "bar" }); }) .then(schema => { - return schema.setPermissions('Stuff', { + return schema.setPermissions("Stuff", { find: { requiresAuthentication: true, }, create: { - '*': true, + "*": true, }, get: { - '*': true, + "*": true, }, }); }) .then(() => { - const stuff = new Parse.Object('Stuff'); - stuff.set('foo', 'bar'); + const stuff = new Parse.Object("Stuff"); + stuff.set("foo", "bar"); return stuff.save().then(() => { - const query = new Parse.Query('Stuff'); + const query = new Parse.Query("Stuff"); return query.get(stuff.id); }); }) .then(result => { - expect(result.get('foo')).toEqual('bar'); - const query = new Parse.Query('Stuff'); + expect(result.get("foo")).toEqual("bar"); + const query = new Parse.Query("Stuff"); return query.find(); }) .then( () => { - fail('Should not succeed!'); + fail("Should not succeed!"); done(); }, e => { - expect(e.message).toEqual('Permission denied, user needs to be authenticated.'); + expect(e.message).toEqual( + "Permission denied, user needs to be authenticated." + ); done(); } ); }); - it('required auth test create/get/update/delete with roles (#3753)', done => { + it("required auth test create/get/update/delete with roles (#3753)", done => { let user; config.database .loadSchema() .then(schema => { // Just to create a valid class - return schema.validateObject('Stuff', { foo: 'bar' }); + return schema.validateObject("Stuff", { foo: "bar" }); }) .then(schema => { - return schema.setPermissions('Stuff', { + return schema.setPermissions("Stuff", { find: { requiresAuthentication: true, - 'role:admin': true, + "role:admin": true, }, - create: { 'role:admin': true }, - update: { 'role:admin': true }, - delete: { 'role:admin': true }, + create: { "role:admin": true }, + update: { "role:admin": true }, + delete: { "role:admin": true }, get: { requiresAuthentication: true, - 'role:admin': true, + "role:admin": true, }, }); }) .then(() => { - const stuff = new Parse.Object('Stuff'); - stuff.set('foo', 'bar'); + const stuff = new Parse.Object("Stuff"); + stuff.set("foo", "bar"); return stuff .save(null, { useMasterKey: true }) .then(() => { - const query = new Parse.Query('Stuff'); + const query = new Parse.Query("Stuff"); return query .get(stuff.id) .then( () => { - done.fail('should not succeed'); + done.fail("should not succeed"); }, () => { - return new Parse.Query('Stuff').find(); + return new Parse.Query("Stuff").find(); } ) .then( () => { - done.fail('should not succeed'); + done.fail("should not succeed"); }, () => { return Promise.resolve(); @@ -1750,9 +1840,9 @@ describe('Class Level Permissions for requiredAuth', () => { ); }) .then(() => { - return Parse.User.signUp('user', 'password').then(signedUpUser => { + return Parse.User.signUp("user", "password").then(signedUpUser => { user = signedUpUser; - const query = new Parse.Query('Stuff'); + const query = new Parse.Query("Stuff"); return query.get(stuff.id, { sessionToken: user.getSessionToken(), }); @@ -1760,8 +1850,8 @@ describe('Class Level Permissions for requiredAuth', () => { }); }) .then(result => { - expect(result.get('foo')).toEqual('bar'); - const query = new Parse.Query('Stuff'); + expect(result.get("foo")).toEqual("bar"); + const query = new Parse.Query("Stuff"); return query.find({ sessionToken: user.getSessionToken() }); }) .then( diff --git a/spec/SchemaPerformance.spec.js b/spec/SchemaPerformance.spec.js index 53d0190453..3fa06b60fc 100644 --- a/spec/SchemaPerformance.spec.js +++ b/spec/SchemaPerformance.spec.js @@ -1,47 +1,47 @@ -const Config = require('../lib/Config'); +const Config = require("../lib/Config"); -describe('Schema Performance', function () { +describe("Schema Performance", function () { let getAllSpy; let config; beforeEach(async () => { await reconfigureServer(); - config = Config.get('test'); - getAllSpy = spyOn(databaseAdapter, 'getAllClasses').and.callThrough(); + config = Config.get("test"); + getAllSpy = spyOn(databaseAdapter, "getAllClasses").and.callThrough(); }); - it('test new object', async () => { + it("test new object", async () => { const object = new TestObject(); - object.set('foo', 'bar'); + object.set("foo", "bar"); await object.save(); expect(getAllSpy.calls.count()).toBe(2); }); - it('test new object multiple fields', async () => { + it("test new object multiple fields", async () => { const container = new Container({ dateField: new Date(), arrayField: [], numberField: 1, - stringField: 'hello', + stringField: "hello", booleanField: true, }); await container.save(); expect(getAllSpy.calls.count()).toBe(2); }); - it('test update existing fields', async () => { + it("test update existing fields", async () => { const object = new TestObject(); - object.set('foo', 'bar'); + object.set("foo", "bar"); await object.save(); getAllSpy.calls.reset(); - object.set('foo', 'barz'); + object.set("foo", "barz"); await object.save(); expect(getAllSpy.calls.count()).toBe(0); }); - xit('test saveAll / destroyAll', async () => { + xit("test saveAll / destroyAll", async () => { // This test can be flaky due to the nature of /batch requests // Used for performance const object = new TestObject(); @@ -52,7 +52,7 @@ describe('Schema Performance', function () { const objects = []; for (let i = 0; i < 10; i++) { const object = new TestObject(); - object.set('number', i); + object.set("number", i); objects.push(object); } await Parse.Object.saveAll(objects); @@ -70,21 +70,21 @@ describe('Schema Performance', function () { expect(getAllSpy.calls.count()).toBe(0); }); - it('test add new field to existing object', async () => { + it("test add new field to existing object", async () => { const object = new TestObject(); - object.set('foo', 'bar'); + object.set("foo", "bar"); await object.save(); getAllSpy.calls.reset(); - object.set('new', 'barz'); + object.set("new", "barz"); await object.save(); expect(getAllSpy.calls.count()).toBe(1); }); - it('test add multiple fields to existing object', async () => { + it("test add multiple fields to existing object", async () => { const object = new TestObject(); - object.set('foo', 'bar'); + object.set("foo", "bar"); await object.save(); getAllSpy.calls.reset(); @@ -93,45 +93,45 @@ describe('Schema Performance', function () { dateField: new Date(), arrayField: [], numberField: 1, - stringField: 'hello', + stringField: "hello", booleanField: true, }); await object.save(); expect(getAllSpy.calls.count()).toBe(1); }); - it('test user', async () => { + it("test user", async () => { const user = new Parse.User(); - user.setUsername('testing'); - user.setPassword('testing'); + user.setUsername("testing"); + user.setPassword("testing"); await user.signUp(); expect(getAllSpy.calls.count()).toBe(1); }); - it('test query include', async () => { + it("test query include", async () => { const child = new TestObject(); await child.save(); const object = new TestObject(); - object.set('child', child); + object.set("child", child); await object.save(); getAllSpy.calls.reset(); const query = new Parse.Query(TestObject); - query.include('child'); + query.include("child"); await query.get(object.id); expect(getAllSpy.calls.count()).toBe(0); }); - it('query relation without schema', async () => { - const child = new Parse.Object('ChildObject'); + it("query relation without schema", async () => { + const child = new Parse.Object("ChildObject"); await child.save(); - const parent = new Parse.Object('ParentObject'); - const relation = parent.relation('child'); + const parent = new Parse.Object("ParentObject"); + const relation = parent.relation("child"); relation.add(child); await parent.save(); @@ -144,9 +144,9 @@ describe('Schema Performance', function () { expect(getAllSpy.calls.count()).toBe(0); }); - it('test delete object', async () => { + it("test delete object", async () => { const object = new TestObject(); - object.set('foo', 'bar'); + object.set("foo", "bar"); await object.save(); getAllSpy.calls.reset(); @@ -155,7 +155,7 @@ describe('Schema Performance', function () { expect(getAllSpy.calls.count()).toBe(0); }); - it('test schema update class', async () => { + it("test schema update class", async () => { const container = new Container(); await container.save(); @@ -166,41 +166,41 @@ describe('Schema Performance', function () { const levelPermissions = { ACL: { - '*': { + "*": { read: true, write: true, }, }, - find: { '*': true }, - get: { '*': true }, - create: { '*': true }, - update: { '*': true }, - delete: { '*': true }, - addField: { '*': true }, - protectedFields: { '*': [] }, + find: { "*": true }, + get: { "*": true }, + create: { "*": true }, + update: { "*": true }, + delete: { "*": true }, + addField: { "*": true }, + protectedFields: { "*": [] }, }; await schema.updateClass( - 'Container', + "Container", { - fooOne: { type: 'Number' }, - fooTwo: { type: 'Array' }, - fooThree: { type: 'Date' }, - fooFour: { type: 'Object' }, - fooFive: { type: 'Relation', targetClass: '_User' }, - fooSix: { type: 'String' }, - fooSeven: { type: 'Object' }, - fooEight: { type: 'String' }, - fooNine: { type: 'String' }, - fooTeen: { type: 'Number' }, - fooEleven: { type: 'String' }, - fooTwelve: { type: 'String' }, - fooThirteen: { type: 'String' }, - fooFourteen: { type: 'String' }, - fooFifteen: { type: 'String' }, - fooSixteen: { type: 'String' }, - fooEighteen: { type: 'String' }, - fooNineteen: { type: 'String' }, + fooOne: { type: "Number" }, + fooTwo: { type: "Array" }, + fooThree: { type: "Date" }, + fooFour: { type: "Object" }, + fooFive: { type: "Relation", targetClass: "_User" }, + fooSix: { type: "String" }, + fooSeven: { type: "Object" }, + fooEight: { type: "String" }, + fooNine: { type: "String" }, + fooTeen: { type: "Number" }, + fooEleven: { type: "String" }, + fooTwelve: { type: "String" }, + fooThirteen: { type: "String" }, + fooFourteen: { type: "String" }, + fooFifteen: { type: "String" }, + fooSixteen: { type: "String" }, + fooEighteen: { type: "String" }, + fooNineteen: { type: "String" }, }, levelPermissions, {}, @@ -209,59 +209,70 @@ describe('Schema Performance', function () { expect(getAllSpy.calls.count()).toBe(2); }); - it_id('9dd70965-b683-4cb8-b43a-44c1f4def9f4')(it)('does reload with schemaCacheTtl', async () => { - const databaseURI = - process.env.PARSE_SERVER_TEST_DB === 'postgres' - ? process.env.PARSE_SERVER_TEST_DATABASE_URI - : 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase'; - await reconfigureServer({ - databaseAdapter: undefined, - databaseURI, - silent: false, - databaseOptions: { schemaCacheTtl: 1000 }, - }); - const SchemaController = require('../lib/Controllers/SchemaController').SchemaController; - const spy = spyOn(SchemaController.prototype, 'reloadData').and.callThrough(); - Object.defineProperty(spy, 'reloadCalls', { - get: () => spy.calls.all().filter(call => call.args[0].clearCache).length, - }); + it_id("9dd70965-b683-4cb8-b43a-44c1f4def9f4")(it)( + "does reload with schemaCacheTtl", + async () => { + const databaseURI = + process.env.PARSE_SERVER_TEST_DB === "postgres" + ? process.env.PARSE_SERVER_TEST_DATABASE_URI + : "mongodb://localhost:27017/parseServerMongoAdapterTestDatabase"; + await reconfigureServer({ + databaseAdapter: undefined, + databaseURI, + silent: false, + databaseOptions: { schemaCacheTtl: 1000 }, + }); + const SchemaController = + require("../lib/Controllers/SchemaController").SchemaController; + const spy = spyOn( + SchemaController.prototype, + "reloadData" + ).and.callThrough(); + Object.defineProperty(spy, "reloadCalls", { + get: () => + spy.calls.all().filter(call => call.args[0].clearCache).length, + }); - const object = new TestObject(); - object.set('foo', 'bar'); - await object.save(); + const object = new TestObject(); + object.set("foo", "bar"); + await object.save(); - spy.calls.reset(); + spy.calls.reset(); - object.set('foo', 'bar'); - await object.save(); + object.set("foo", "bar"); + await object.save(); - expect(spy.reloadCalls).toBe(0); + expect(spy.reloadCalls).toBe(0); - await new Promise(resolve => setTimeout(resolve, 1100)); + await new Promise(resolve => setTimeout(resolve, 1100)); - object.set('foo', 'bar'); - await object.save(); + object.set("foo", "bar"); + await object.save(); - expect(spy.reloadCalls).toBe(1); - }); + expect(spy.reloadCalls).toBe(1); + } + ); - it_id('b0ae21f2-c947-48ed-a0db-e8900d45a4c8')(it)( - 'cannot set invalid databaseOptions', + it_id("b0ae21f2-c947-48ed-a0db-e8900d45a4c8")(it)( + "cannot set invalid databaseOptions", async () => { const expectError = async (key, value, expected) => expectAsync( - reconfigureServer({ databaseAdapter: undefined, databaseOptions: { [key]: value } }) + reconfigureServer({ + databaseAdapter: undefined, + databaseOptions: { [key]: value }, + }) ).toBeRejectedWith(`databaseOptions.${key} must be a ${expected}`); - for (const databaseOptions of [[], 0, 'string']) { + for (const databaseOptions of [[], 0, "string"]) { await expectAsync( reconfigureServer({ databaseAdapter: undefined, databaseOptions }) ).toBeRejectedWith(`databaseOptions must be an object`); } - for (const value of [null, 0, 'string', {}, []]) { - await expectError('enableSchemaHooks', value, 'boolean'); + for (const value of [null, 0, "string", {}, []]) { + await expectError("enableSchemaHooks", value, "boolean"); } - for (const value of [null, false, 'string', {}, []]) { - await expectError('schemaCacheTtl', value, 'number'); + for (const value of [null, false, "string", {}, []]) { + await expectError("schemaCacheTtl", value, "number"); } } ); diff --git a/spec/SecurityCheck.spec.js b/spec/SecurityCheck.spec.js index 6c61bbf90b..f57f900724 100644 --- a/spec/SecurityCheck.spec.js +++ b/spec/SecurityCheck.spec.js @@ -1,22 +1,22 @@ -'use strict'; +"use strict"; -const Utils = require('../lib/Utils'); -const Config = require('../lib/Config'); -const request = require('../lib/request'); -const Definitions = require('../lib/Options/Definitions'); -const { Check, CheckState } = require('../lib/Security/Check'); -const CheckGroup = require('../lib/Security/CheckGroup'); -const CheckRunner = require('../lib/Security/CheckRunner'); -const CheckGroups = require('../lib/Security/CheckGroups/CheckGroups'); +const Utils = require("../lib/Utils"); +const Config = require("../lib/Config"); +const request = require("../lib/request"); +const Definitions = require("../lib/Options/Definitions"); +const { Check, CheckState } = require("../lib/Security/Check"); +const CheckGroup = require("../lib/Security/CheckGroup"); +const CheckRunner = require("../lib/Security/CheckRunner"); +const CheckGroups = require("../lib/Security/CheckGroups/CheckGroups"); -describe('Security Check', () => { +describe("Security Check", () => { let Group; let groupName; let checkSuccess; let checkFail; let config; - const publicServerURL = 'http://localhost:8378/1'; - const securityUrl = publicServerURL + '/security'; + const publicServerURL = "http://localhost:8378/1"; + const securityUrl = publicServerURL + "/security"; async function reconfigureServerWithSecurityConfig(security) { config.security = security; @@ -29,8 +29,8 @@ describe('Security Check', () => { { url: securityUrl, headers: { - 'X-Parse-Master-Key': Parse.masterKey, - 'X-Parse-Application-Id': Parse.applicationId, + "X-Parse-Master-Key": Parse.masterKey, + "X-Parse-Application-Id": Parse.applicationId, }, followRedirects: false, }, @@ -39,23 +39,23 @@ describe('Security Check', () => { ).catch(e => e); beforeEach(async () => { - groupName = 'Example Group Name'; + groupName = "Example Group Name"; checkSuccess = new Check({ - group: 'TestGroup', - title: 'TestTitleSuccess', - warning: 'TestWarning', - solution: 'TestSolution', + group: "TestGroup", + title: "TestTitleSuccess", + warning: "TestWarning", + solution: "TestSolution", check: () => { return true; }, }); checkFail = new Check({ - group: 'TestGroup', - title: 'TestTitleFail', - warning: 'TestWarning', - solution: 'TestSolution', + group: "TestGroup", + title: "TestTitleFail", + warning: "TestWarning", + solution: "TestSolution", check: () => { - throw 'Fail'; + throw "Fail"; }, }); Group = class Group extends CheckGroup { @@ -67,8 +67,8 @@ describe('Security Check', () => { } }; config = { - appId: 'test', - appName: 'ExampleAppName', + appId: "test", + appName: "ExampleAppName", publicServerURL, security: { enableCheck: true, @@ -78,8 +78,8 @@ describe('Security Check', () => { await reconfigureServer(config); }); - describe('server options', () => { - it('uses default configuration when none is set', async () => { + describe("server options", () => { + it("uses default configuration when none is set", async () => { await reconfigureServerWithSecurityConfig({}); expect(Config.get(Parse.applicationId).security.enableCheck).toBe( Definitions.SecurityOptions.enableCheck.default @@ -89,36 +89,41 @@ describe('Security Check', () => { ); }); - it('throws on invalid configuration', async () => { + it("throws on invalid configuration", async () => { const options = [ [], - 'a', + "a", 0, true, - { enableCheck: 'a' }, + { enableCheck: "a" }, { enableCheck: 0 }, { enableCheck: {} }, { enableCheck: [] }, - { enableCheckLog: 'a' }, + { enableCheckLog: "a" }, { enableCheckLog: 0 }, { enableCheckLog: {} }, { enableCheckLog: [] }, ]; for (const option of options) { - await expectAsync(reconfigureServerWithSecurityConfig(option)).toBeRejected(); + await expectAsync( + reconfigureServerWithSecurityConfig(option) + ).toBeRejected(); } }); }); - describe('auto-run', () => { - it('runs security checks on server start if enabled', async () => { - const runnerSpy = spyOn(CheckRunner.prototype, 'run').and.callThrough(); - await reconfigureServerWithSecurityConfig({ enableCheck: true, enableCheckLog: true }); + describe("auto-run", () => { + it("runs security checks on server start if enabled", async () => { + const runnerSpy = spyOn(CheckRunner.prototype, "run").and.callThrough(); + await reconfigureServerWithSecurityConfig({ + enableCheck: true, + enableCheckLog: true, + }); expect(runnerSpy).toHaveBeenCalledTimes(1); }); - it('does not run security checks on server start if disabled', async () => { - const runnerSpy = spyOn(CheckRunner.prototype, 'run').and.callThrough(); + it("does not run security checks on server start if disabled", async () => { + const runnerSpy = spyOn(CheckRunner.prototype, "run").and.callThrough(); const configs = [ { enableCheck: true, enableCheckLog: false }, { enableCheck: false, enableCheckLog: false }, @@ -132,41 +137,41 @@ describe('Security Check', () => { }); }); - describe('security endpoint accessibility', () => { - it('responds with 403 without masterkey', async () => { + describe("security endpoint accessibility", () => { + it("responds with 403 without masterkey", async () => { const response = await securityRequest({ headers: {} }); expect(response.status).toBe(403); }); - it('responds with 409 with masterkey and security check disabled', async () => { + it("responds with 409 with masterkey and security check disabled", async () => { await reconfigureServerWithSecurityConfig({}); const response = await securityRequest(); expect(response.status).toBe(409); }); - it('responds with 200 with masterkey and security check enabled', async () => { + it("responds with 200 with masterkey and security check enabled", async () => { const response = await securityRequest(); expect(response.status).toBe(200); }); }); - describe('check', () => { + describe("check", () => { const initCheck = config => (() => new Check(config)).bind(null); - it('instantiates check with valid parameters', async () => { + it("instantiates check with valid parameters", async () => { const configs = [ { - group: 'string', - title: 'string', - warning: 'string', - solution: 'string', + group: "string", + title: "string", + warning: "string", + solution: "string", check: () => {}, }, { - group: 'string', - title: 'string', - warning: 'string', - solution: 'string', + group: "string", + title: "string", + warning: "string", + solution: "string", check: async () => {}, }, ]; @@ -175,13 +180,13 @@ describe('Security Check', () => { } }); - it('throws instantiating check with invalid parameters', async () => { + it("throws instantiating check with invalid parameters", async () => { const configDefinition = { group: [false, true, 0, 1, [], {}, () => {}], title: [false, true, 0, 1, [], {}, () => {}], warning: [false, true, 0, 1, [], {}, () => {}], solution: [false, true, 0, 1, [], {}, () => {}], - check: [false, true, 0, 1, [], {}, 'string'], + check: [false, true, 0, 1, [], {}, "string"], }; const configs = Utils.getObjectKeyPermutations(configDefinition); @@ -190,12 +195,12 @@ describe('Security Check', () => { } }); - it('sets correct states for check success', async () => { + it("sets correct states for check success", async () => { const check = new Check({ - group: 'string', - title: 'string', - warning: 'string', - solution: 'string', + group: "string", + title: "string", + warning: "string", + solution: "string", check: () => {}, }); expect(check._checkState == CheckState.none); @@ -203,14 +208,14 @@ describe('Security Check', () => { expect(check._checkState == CheckState.success); }); - it('sets correct states for check fail', async () => { + it("sets correct states for check fail", async () => { const check = new Check({ - group: 'string', - title: 'string', - warning: 'string', - solution: 'string', + group: "string", + title: "string", + warning: "string", + solution: "string", check: () => { - throw 'error'; + throw "error"; }, }); expect(check._checkState == CheckState.none); @@ -219,8 +224,8 @@ describe('Security Check', () => { }); }); - describe('check group', () => { - it('returns properties if subclassed correctly', async () => { + describe("check group", () => { + it("returns properties if subclassed correctly", async () => { const group = new Group(); expect(group.name()).toBe(groupName); expect(group.checks().length).toBe(2); @@ -228,18 +233,22 @@ describe('Security Check', () => { expect(group.checks()[1]).toEqual(checkFail); }); - it('throws if subclassed incorrectly', async () => { + it("throws if subclassed incorrectly", async () => { class InvalidGroup1 extends CheckGroup {} - expect((() => new InvalidGroup1()).bind()).toThrow('Check group has no name.'); + expect((() => new InvalidGroup1()).bind()).toThrow( + "Check group has no name." + ); class InvalidGroup2 extends CheckGroup { setName() { return groupName; } } - expect((() => new InvalidGroup2()).bind()).toThrow('Check group has no checks.'); + expect((() => new InvalidGroup2()).bind()).toThrow( + "Check group has no checks." + ); }); - it('runs checks', async () => { + it("runs checks", async () => { const group = new Group(); expect(group.checks()[0].checkState()).toBe(CheckState.none); expect(group.checks()[1].checkState()).toBe(CheckState.none); @@ -249,10 +258,10 @@ describe('Security Check', () => { }); }); - describe('check runner', () => { + describe("check runner", () => { const initRunner = config => (() => new CheckRunner(config)).bind(null); - it('instantiates runner with valid parameters', async () => { + it("instantiates runner with valid parameters", async () => { const configDefinition = { enableCheck: [false, true, undefined], enableCheckLog: [false, true, undefined], @@ -264,7 +273,7 @@ describe('Security Check', () => { } }); - it('throws instantiating runner with invalid parameters', async () => { + it("throws instantiating runner with invalid parameters", async () => { const configDefinition = { enableCheck: [0, 1, [], {}, () => {}], enableCheckLog: [0, 1, [], {}, () => {}], @@ -277,14 +286,14 @@ describe('Security Check', () => { } }); - it('instantiates runner with default parameters', async () => { + it("instantiates runner with default parameters", async () => { const runner = new CheckRunner(); expect(runner.enableCheck).toBeFalse(); expect(runner.enableCheckLog).toBeFalse(); expect(runner.checkGroups).toBe(CheckGroups); }); - it('runs all checks of all groups', async () => { + it("runs all checks of all groups", async () => { const checkGroups = [Group, Group]; const runner = new CheckRunner({ checkGroups }); const report = await runner.run(); @@ -294,28 +303,28 @@ describe('Security Check', () => { expect(report.report.groups[1].checks[1].state).toBe(CheckState.fail); }); - it('reports correct default syntax version 1.0.0', async () => { + it("reports correct default syntax version 1.0.0", async () => { const checkGroups = [Group]; const runner = new CheckRunner({ checkGroups, enableCheckLog: true }); const report = await runner.run(); expect(report).toEqual({ report: { - version: '1.0.0', - state: 'fail', + version: "1.0.0", + state: "fail", groups: [ { - name: 'Example Group Name', - state: 'fail', + name: "Example Group Name", + state: "fail", checks: [ { - title: 'TestTitleSuccess', - state: 'success', + title: "TestTitleSuccess", + state: "success", }, { - title: 'TestTitleFail', - state: 'fail', - warning: 'TestWarning', - solution: 'TestSolution', + title: "TestTitleFail", + state: "fail", + warning: "TestWarning", + solution: "TestSolution", }, ], }, @@ -324,13 +333,15 @@ describe('Security Check', () => { }); }); - it('logs report', async () => { - const logger = require('../lib/logger').logger; - const logSpy = spyOn(logger, 'warn').and.callThrough(); + it("logs report", async () => { + const logger = require("../lib/logger").logger; + const logSpy = spyOn(logger, "warn").and.callThrough(); const checkGroups = [Group]; const runner = new CheckRunner({ checkGroups, enableCheckLog: true }); const report = await runner.run(); - const titles = report.report.groups.flatMap(group => group.checks.map(check => check.title)); + const titles = report.report.groups.flatMap(group => + group.checks.map(check => check.title) + ); expect(titles.length).toBe(2); for (const title of titles) { @@ -338,14 +349,14 @@ describe('Security Check', () => { } }); - it('does update featuresRouter', async () => { + it("does update featuresRouter", async () => { let response = await request({ - url: 'http://localhost:8378/1/serverInfo', + url: "http://localhost:8378/1/serverInfo", json: true, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Master-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "X-Parse-Master-Key": "test", }, }); expect(response.data.features.settings.securityCheck).toBeTrue(); @@ -355,12 +366,12 @@ describe('Security Check', () => { }, }); response = await request({ - url: 'http://localhost:8378/1/serverInfo', + url: "http://localhost:8378/1/serverInfo", json: true, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Master-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "X-Parse-Master-Key": "test", }, }); expect(response.data.features.settings.securityCheck).toBeFalse(); diff --git a/spec/SecurityCheckGroups.spec.js b/spec/SecurityCheckGroups.spec.js index 21409a78c1..e2846fa0d0 100644 --- a/spec/SecurityCheckGroups.spec.js +++ b/spec/SecurityCheckGroups.spec.js @@ -1,18 +1,18 @@ -'use strict'; +"use strict"; -const Config = require('../lib/Config'); -const { CheckState } = require('../lib/Security/Check'); -const CheckGroupServerConfig = require('../lib/Security/CheckGroups/CheckGroupServerConfig'); -const CheckGroupDatabase = require('../lib/Security/CheckGroups/CheckGroupDatabase'); +const Config = require("../lib/Config"); +const { CheckState } = require("../lib/Security/Check"); +const CheckGroupServerConfig = require("../lib/Security/CheckGroups/CheckGroupServerConfig"); +const CheckGroupDatabase = require("../lib/Security/CheckGroups/CheckGroupDatabase"); -describe('Security Check Groups', () => { +describe("Security Check Groups", () => { let config; beforeEach(async () => { config = { - appId: 'test', - appName: 'ExampleAppName', - publicServerURL: 'http://localhost:8378/1', + appId: "test", + appName: "ExampleAppName", + publicServerURL: "http://localhost:8378/1", security: { enableCheck: true, enableCheckLog: false, @@ -21,15 +21,15 @@ describe('Security Check Groups', () => { await reconfigureServer(config); }); - describe('CheckGroupServerConfig', () => { - it('is subclassed correctly', async () => { + describe("CheckGroupServerConfig", () => { + it("is subclassed correctly", async () => { const group = new CheckGroupServerConfig(); expect(group.name()).toBeDefined(); expect(group.checks().length).toBeGreaterThan(0); }); - it('checks succeed correctly', async () => { - config.masterKey = 'aMoreSecur3Passwor7!'; + it("checks succeed correctly", async () => { + config.masterKey = "aMoreSecur3Passwor7!"; config.security.enableCheckLog = false; config.allowClientClassCreation = false; config.enableInsecureAuthAdapters = false; @@ -43,8 +43,8 @@ describe('Security Check Groups', () => { expect(group.checks()[4].checkState()).toBe(CheckState.success); }); - it('checks fail correctly', async () => { - config.masterKey = 'insecure'; + it("checks fail correctly", async () => { + config.masterKey = "insecure"; config.security.enableCheckLog = true; config.allowClientClassCreation = true; await reconfigureServer(config); @@ -58,27 +58,28 @@ describe('Security Check Groups', () => { }); }); - describe('CheckGroupDatabase', () => { - it('is subclassed correctly', async () => { + describe("CheckGroupDatabase", () => { + it("is subclassed correctly", async () => { const group = new CheckGroupDatabase(); expect(group.name()).toBeDefined(); expect(group.checks().length).toBeGreaterThan(0); }); - it('checks succeed correctly', async () => { + it("checks succeed correctly", async () => { const config = Config.get(Parse.applicationId); const uri = config.database.adapter._uri; - config.database.adapter._uri = 'protocol://user:aMoreSecur3Passwor7!@example.com'; + config.database.adapter._uri = + "protocol://user:aMoreSecur3Passwor7!@example.com"; const group = new CheckGroupDatabase(); await group.run(); expect(group.checks()[0].checkState()).toBe(CheckState.success); config.database.adapter._uri = uri; }); - it('checks fail correctly', async () => { + it("checks fail correctly", async () => { const config = Config.get(Parse.applicationId); const uri = config.database.adapter._uri; - config.database.adapter._uri = 'protocol://user:insecure@example.com'; + config.database.adapter._uri = "protocol://user:insecure@example.com"; const group = new CheckGroupDatabase(); await group.run(); expect(group.checks()[0].checkState()).toBe(CheckState.fail); diff --git a/spec/SessionTokenCache.spec.js b/spec/SessionTokenCache.spec.js index 6b3c83df62..fc86c56ee5 100644 --- a/spec/SessionTokenCache.spec.js +++ b/spec/SessionTokenCache.spec.js @@ -1,14 +1,15 @@ -const SessionTokenCache = require('../lib/LiveQuery/SessionTokenCache').SessionTokenCache; +const SessionTokenCache = + require("../lib/LiveQuery/SessionTokenCache").SessionTokenCache; -describe('SessionTokenCache', function () { +describe("SessionTokenCache", function () { beforeEach(function (done) { - const Parse = require('parse/node'); + const Parse = require("parse/node"); - spyOn(Parse, 'Query').and.returnValue({ - first: jasmine.createSpy('first').and.returnValue( + spyOn(Parse, "Query").and.returnValue({ + first: jasmine.createSpy("first").and.returnValue( Promise.resolve( - new Parse.Object('_Session', { - user: new Parse.User({ id: 'userId' }), + new Parse.Object("_Session", { + user: new Parse.User({ id: "userId" }), }) ) ), @@ -18,7 +19,7 @@ describe('SessionTokenCache', function () { done(); }); - it('can get undefined userId', function (done) { + it("can get undefined userId", function (done) { const sessionTokenCache = new SessionTokenCache(); sessionTokenCache.getUserId(undefined).then( @@ -30,10 +31,10 @@ describe('SessionTokenCache', function () { ); }); - it('can get existing userId', function (done) { + it("can get existing userId", function (done) { const sessionTokenCache = new SessionTokenCache(); - const sessionToken = 'sessionToken'; - const userId = 'userId'; + const sessionToken = "sessionToken"; + const userId = "userId"; sessionTokenCache.cache.set(sessionToken, userId); sessionTokenCache.getUserId(sessionToken).then(userIdFromCache => { @@ -42,11 +43,11 @@ describe('SessionTokenCache', function () { }); }); - it('can get new userId', function (done) { + it("can get new userId", function (done) { const sessionTokenCache = new SessionTokenCache(); - sessionTokenCache.getUserId('sessionToken').then(userIdFromCache => { - expect(userIdFromCache).toBe('userId'); + sessionTokenCache.getUserId("sessionToken").then(userIdFromCache => { + expect(userIdFromCache).toBe("userId"); expect(sessionTokenCache.cache.size).toBe(1); done(); }); diff --git a/spec/Subscription.spec.js b/spec/Subscription.spec.js index 9c7aa4c550..fef262afd4 100644 --- a/spec/Subscription.spec.js +++ b/spec/Subscription.spec.js @@ -1,43 +1,63 @@ -const Subscription = require('../lib/LiveQuery/Subscription').Subscription; +const Subscription = require("../lib/LiveQuery/Subscription").Subscription; let logger; -describe('Subscription', function () { +describe("Subscription", function () { beforeEach(function () { - logger = require('../lib/logger').logger; - spyOn(logger, 'error').and.callThrough(); + logger = require("../lib/logger").logger; + spyOn(logger, "error").and.callThrough(); }); - it('can be initialized', function () { - const subscription = new Subscription('className', { key: 'value' }, 'hash'); + it("can be initialized", function () { + const subscription = new Subscription( + "className", + { key: "value" }, + "hash" + ); - expect(subscription.className).toBe('className'); - expect(subscription.query).toEqual({ key: 'value' }); - expect(subscription.hash).toBe('hash'); + expect(subscription.className).toBe("className"); + expect(subscription.query).toEqual({ key: "value" }); + expect(subscription.hash).toBe("hash"); expect(subscription.clientRequestIds.size).toBe(0); }); - it('can check it has subscribing clients', function () { - const subscription = new Subscription('className', { key: 'value' }, 'hash'); + it("can check it has subscribing clients", function () { + const subscription = new Subscription( + "className", + { key: "value" }, + "hash" + ); expect(subscription.hasSubscribingClient()).toBe(false); }); - it('can check it does not have subscribing clients', function () { - const subscription = new Subscription('className', { key: 'value' }, 'hash'); + it("can check it does not have subscribing clients", function () { + const subscription = new Subscription( + "className", + { key: "value" }, + "hash" + ); subscription.addClientSubscription(1, 1); expect(subscription.hasSubscribingClient()).toBe(true); }); - it('can add one request for one client', function () { - const subscription = new Subscription('className', { key: 'value' }, 'hash'); + it("can add one request for one client", function () { + const subscription = new Subscription( + "className", + { key: "value" }, + "hash" + ); subscription.addClientSubscription(1, 1); expect(subscription.clientRequestIds.size).toBe(1); expect(subscription.clientRequestIds.get(1)).toEqual([1]); }); - it('can add requests for one client', function () { - const subscription = new Subscription('className', { key: 'value' }, 'hash'); + it("can add requests for one client", function () { + const subscription = new Subscription( + "className", + { key: "value" }, + "hash" + ); subscription.addClientSubscription(1, 1); subscription.addClientSubscription(1, 2); @@ -45,8 +65,12 @@ describe('Subscription', function () { expect(subscription.clientRequestIds.get(1)).toEqual([1, 2]); }); - it('can add requests for clients', function () { - const subscription = new Subscription('className', { key: 'value' }, 'hash'); + it("can add requests for clients", function () { + const subscription = new Subscription( + "className", + { key: "value" }, + "hash" + ); subscription.addClientSubscription(1, 1); subscription.addClientSubscription(1, 2); subscription.addClientSubscription(2, 2); @@ -57,15 +81,23 @@ describe('Subscription', function () { expect(subscription.clientRequestIds.get(2)).toEqual([2, 3]); }); - it('can delete requests for nonexistent client', function () { - const subscription = new Subscription('className', { key: 'value' }, 'hash'); + it("can delete requests for nonexistent client", function () { + const subscription = new Subscription( + "className", + { key: "value" }, + "hash" + ); subscription.deleteClientSubscription(1, 1); expect(logger.error).toHaveBeenCalled(); }); - it('can delete nonexistent request for one client', function () { - const subscription = new Subscription('className', { key: 'value' }, 'hash'); + it("can delete nonexistent request for one client", function () { + const subscription = new Subscription( + "className", + { key: "value" }, + "hash" + ); subscription.addClientSubscription(1, 1); subscription.deleteClientSubscription(1, 2); @@ -74,8 +106,12 @@ describe('Subscription', function () { expect(subscription.clientRequestIds.get(1)).toEqual([1]); }); - it('can delete some requests for one client', function () { - const subscription = new Subscription('className', { key: 'value' }, 'hash'); + it("can delete some requests for one client", function () { + const subscription = new Subscription( + "className", + { key: "value" }, + "hash" + ); subscription.addClientSubscription(1, 1); subscription.addClientSubscription(1, 2); subscription.deleteClientSubscription(1, 2); @@ -85,8 +121,12 @@ describe('Subscription', function () { expect(subscription.clientRequestIds.get(1)).toEqual([1]); }); - it('can delete all requests for one client', function () { - const subscription = new Subscription('className', { key: 'value' }, 'hash'); + it("can delete all requests for one client", function () { + const subscription = new Subscription( + "className", + { key: "value" }, + "hash" + ); subscription.addClientSubscription(1, 1); subscription.addClientSubscription(1, 2); subscription.deleteClientSubscription(1, 1); @@ -96,8 +136,12 @@ describe('Subscription', function () { expect(subscription.clientRequestIds.size).toBe(0); }); - it('can delete requests for multiple clients', function () { - const subscription = new Subscription('className', { key: 'value' }, 'hash'); + it("can delete requests for multiple clients", function () { + const subscription = new Subscription( + "className", + { key: "value" }, + "hash" + ); subscription.addClientSubscription(1, 1); subscription.addClientSubscription(1, 2); subscription.addClientSubscription(2, 1); diff --git a/spec/Uniqueness.spec.js b/spec/Uniqueness.spec.js index 7f53ac54bb..928bbe3bae 100644 --- a/spec/Uniqueness.spec.js +++ b/spec/Uniqueness.spec.js @@ -1,31 +1,31 @@ -'use strict'; +"use strict"; -const Parse = require('parse/node'); -const Config = require('../lib/Config'); +const Parse = require("parse/node"); +const Config = require("../lib/Config"); -describe('Uniqueness', function () { - it('fail when create duplicate value in unique field', done => { - const obj = new Parse.Object('UniqueField'); - obj.set('unique', 'value'); +describe("Uniqueness", function () { + it("fail when create duplicate value in unique field", done => { + const obj = new Parse.Object("UniqueField"); + obj.set("unique", "value"); obj .save() .then(() => { expect(obj.id).not.toBeUndefined(); - const config = Config.get('test'); + const config = Config.get("test"); return config.database.adapter.ensureUniqueness( - 'UniqueField', - { fields: { unique: { __type: 'String' } } }, - ['unique'] + "UniqueField", + { fields: { unique: { __type: "String" } } }, + ["unique"] ); }) .then(() => { - const obj = new Parse.Object('UniqueField'); - obj.set('unique', 'value'); + const obj = new Parse.Object("UniqueField"); + obj.set("unique", "value"); return obj.save(); }) .then( () => { - fail('Saving duplicate field should have failed'); + fail("Saving duplicate field should have failed"); done(); }, error => { @@ -35,31 +35,31 @@ describe('Uniqueness', function () { ); }); - it('unique indexing works on pointer fields', done => { - const obj = new Parse.Object('UniquePointer'); + it("unique indexing works on pointer fields", done => { + const obj = new Parse.Object("UniquePointer"); obj - .save({ string: 'who cares' }) + .save({ string: "who cares" }) .then(() => obj.save({ ptr: obj })) .then(() => { - const config = Config.get('test'); + const config = Config.get("test"); return config.database.adapter.ensureUniqueness( - 'UniquePointer', + "UniquePointer", { fields: { - string: { __type: 'String' }, - ptr: { __type: 'Pointer', targetClass: 'UniquePointer' }, + string: { __type: "String" }, + ptr: { __type: "Pointer", targetClass: "UniquePointer" }, }, }, - ['ptr'] + ["ptr"] ); }) .then(() => { - const newObj = new Parse.Object('UniquePointer'); - newObj.set('ptr', obj); + const newObj = new Parse.Object("UniquePointer"); + newObj.set("ptr", obj); return newObj.save(); }) .then(() => { - fail('save should have failed due to duplicate value'); + fail("save should have failed due to duplicate value"); done(); }) .catch(error => { @@ -68,20 +68,20 @@ describe('Uniqueness', function () { }); }); - it_id('802650a9-a6db-447e-88d0-8aae99100088')(it)( - 'fails when attempting to ensure uniqueness of fields that are not currently unique', + it_id("802650a9-a6db-447e-88d0-8aae99100088")(it)( + "fails when attempting to ensure uniqueness of fields that are not currently unique", done => { - const o1 = new Parse.Object('UniqueFail'); - o1.set('key', 'val'); - const o2 = new Parse.Object('UniqueFail'); - o2.set('key', 'val'); + const o1 = new Parse.Object("UniqueFail"); + o1.set("key", "val"); + const o2 = new Parse.Object("UniqueFail"); + o2.set("key", "val"); Parse.Object.saveAll([o1, o2]) .then(() => { - const config = Config.get('test'); + const config = Config.get("test"); return config.database.adapter.ensureUniqueness( - 'UniqueFail', - { fields: { key: { __type: 'String' } } }, - ['key'] + "UniqueFail", + { fields: { key: { __type: "String" } } }, + ["key"] ); }) .catch(error => { @@ -91,36 +91,36 @@ describe('Uniqueness', function () { } ); - it_exclude_dbs(['postgres'])('can do compound uniqueness', done => { - const config = Config.get('test'); + it_exclude_dbs(["postgres"])("can do compound uniqueness", done => { + const config = Config.get("test"); config.database.adapter .ensureUniqueness( - 'CompoundUnique', - { fields: { k1: { __type: 'String' }, k2: { __type: 'String' } } }, - ['k1', 'k2'] + "CompoundUnique", + { fields: { k1: { __type: "String" }, k2: { __type: "String" } } }, + ["k1", "k2"] ) .then(() => { - const o1 = new Parse.Object('CompoundUnique'); - o1.set('k1', 'v1'); - o1.set('k2', 'v2'); + const o1 = new Parse.Object("CompoundUnique"); + o1.set("k1", "v1"); + o1.set("k2", "v2"); return o1.save(); }) .then(() => { - const o2 = new Parse.Object('CompoundUnique'); - o2.set('k1', 'v1'); - o2.set('k2', 'not a dupe'); + const o2 = new Parse.Object("CompoundUnique"); + o2.set("k1", "v1"); + o2.set("k2", "not a dupe"); return o2.save(); }) .then(() => { - const o3 = new Parse.Object('CompoundUnique'); - o3.set('k1', 'not a dupe'); - o3.set('k2', 'v2'); + const o3 = new Parse.Object("CompoundUnique"); + o3.set("k1", "not a dupe"); + o3.set("k2", "v2"); return o3.save(); }) .then(() => { - const o4 = new Parse.Object('CompoundUnique'); - o4.set('k1', 'v1'); - o4.set('k2', 'v2'); + const o4 = new Parse.Object("CompoundUnique"); + o4.set("k1", "v1"); + o4.set("k2", "v2"); return o4.save(); }) .catch(error => { diff --git a/spec/UserController.spec.js b/spec/UserController.spec.js index 1993dde079..e61de3aaae 100644 --- a/spec/UserController.spec.js +++ b/spec/UserController.spec.js @@ -1,84 +1,104 @@ -const emailAdapter = require('./support/MockEmailAdapter'); -const Config = require('../lib/Config'); -const Auth = require('../lib/Auth'); -const { resolvingPromise } = require('../lib/TestUtils'); +const emailAdapter = require("./support/MockEmailAdapter"); +const Config = require("../lib/Config"); +const Auth = require("../lib/Auth"); +const { resolvingPromise } = require("../lib/TestUtils"); -describe('UserController', () => { - describe('sendVerificationEmail', () => { - describe('parseFrameURL not provided', () => { - it_id('61338330-eca7-4c33-8816-7ff05966f43b')(it)('uses publicServerURL', async () => { - await reconfigureServer({ - publicServerURL: 'http://www.example.com', - customPages: { - parseFrameURL: undefined, - }, - verifyUserEmails: true, - emailAdapter, - appName: 'test', - }); +describe("UserController", () => { + describe("sendVerificationEmail", () => { + describe("parseFrameURL not provided", () => { + it_id("61338330-eca7-4c33-8816-7ff05966f43b")(it)( + "uses publicServerURL", + async () => { + await reconfigureServer({ + publicServerURL: "http://www.example.com", + customPages: { + parseFrameURL: undefined, + }, + verifyUserEmails: true, + emailAdapter, + appName: "test", + }); - let emailOptions; - const sendPromise = resolvingPromise(); - emailAdapter.sendVerificationEmail = options => { - emailOptions = options; - sendPromise.resolve(); - }; + let emailOptions; + const sendPromise = resolvingPromise(); + emailAdapter.sendVerificationEmail = options => { + emailOptions = options; + sendPromise.resolve(); + }; - const username = 'verificationUser'; - const user = new Parse.User(); - user.setUsername(username); - user.setPassword('pass'); - user.setEmail('verification@example.com'); - await user.signUp(); - await sendPromise; + const username = "verificationUser"; + const user = new Parse.User(); + user.setUsername(username); + user.setPassword("pass"); + user.setEmail("verification@example.com"); + await user.signUp(); + await sendPromise; - const config = Config.get('test'); - const rawUser = await config.database.find('_User', { username }, {}, Auth.maintenance(config)); - const rawUsername = rawUser[0].username; - const rawToken = rawUser[0]._email_verify_token; - expect(rawToken).toBeDefined(); - expect(rawUsername).toBe(username); + const config = Config.get("test"); + const rawUser = await config.database.find( + "_User", + { username }, + {}, + Auth.maintenance(config) + ); + const rawUsername = rawUser[0].username; + const rawToken = rawUser[0]._email_verify_token; + expect(rawToken).toBeDefined(); + expect(rawUsername).toBe(username); - expect(emailOptions.link).toEqual(`http://www.example.com/apps/test/verify_email?token=${rawToken}`); - }); + expect(emailOptions.link).toEqual( + `http://www.example.com/apps/test/verify_email?token=${rawToken}` + ); + } + ); }); - describe('parseFrameURL provided', () => { - it_id('673c2bb1-049e-4dda-b6be-88c866260036')(it)('uses parseFrameURL and includes the destination in the link parameter', async () => { - await reconfigureServer({ - publicServerURL: 'http://www.example.com', - customPages: { - parseFrameURL: 'http://someother.example.com/handle-parse-iframe', - }, - verifyUserEmails: true, - emailAdapter, - appName: 'test', - }); + describe("parseFrameURL provided", () => { + it_id("673c2bb1-049e-4dda-b6be-88c866260036")(it)( + "uses parseFrameURL and includes the destination in the link parameter", + async () => { + await reconfigureServer({ + publicServerURL: "http://www.example.com", + customPages: { + parseFrameURL: "http://someother.example.com/handle-parse-iframe", + }, + verifyUserEmails: true, + emailAdapter, + appName: "test", + }); - let emailOptions; - const sendPromise = resolvingPromise(); - emailAdapter.sendVerificationEmail = options => { - emailOptions = options; - sendPromise.resolve(); - }; + let emailOptions; + const sendPromise = resolvingPromise(); + emailAdapter.sendVerificationEmail = options => { + emailOptions = options; + sendPromise.resolve(); + }; - const username = 'verificationUser'; - const user = new Parse.User(); - user.setUsername(username); - user.setPassword('pass'); - user.setEmail('verification@example.com'); - await user.signUp(); - await sendPromise; + const username = "verificationUser"; + const user = new Parse.User(); + user.setUsername(username); + user.setPassword("pass"); + user.setEmail("verification@example.com"); + await user.signUp(); + await sendPromise; - const config = Config.get('test'); - const rawUser = await config.database.find('_User', { username }, {}, Auth.maintenance(config)); - const rawUsername = rawUser[0].username; - const rawToken = rawUser[0]._email_verify_token; - expect(rawToken).toBeDefined(); - expect(rawUsername).toBe(username); + const config = Config.get("test"); + const rawUser = await config.database.find( + "_User", + { username }, + {}, + Auth.maintenance(config) + ); + const rawUsername = rawUser[0].username; + const rawToken = rawUser[0]._email_verify_token; + expect(rawToken).toBeDefined(); + expect(rawUsername).toBe(username); - expect(emailOptions.link).toEqual(`http://someother.example.com/handle-parse-iframe?link=%2Fapps%2Ftest%2Fverify_email&token=${rawToken}`); - }); + expect(emailOptions.link).toEqual( + `http://someother.example.com/handle-parse-iframe?link=%2Fapps%2Ftest%2Fverify_email&token=${rawToken}` + ); + } + ); }); }); }); diff --git a/spec/UserPII.spec.js b/spec/UserPII.spec.js index a94e3ca469..9533e58f3f 100644 --- a/spec/UserPII.spec.js +++ b/spec/UserPII.spec.js @@ -1,47 +1,52 @@ -'use strict'; +"use strict"; -const Parse = require('parse/node'); -const request = require('../lib/request'); +const Parse = require("parse/node"); +const request = require("../lib/request"); // const Config = require('../lib/Config'); -const EMAIL = 'foo@bar.com'; -const ZIP = '10001'; -const SSN = '999-99-9999'; +const EMAIL = "foo@bar.com"; +const ZIP = "10001"; +const SSN = "999-99-9999"; -describe('Personally Identifiable Information', () => { +describe("Personally Identifiable Information", () => { let user; beforeEach(async done => { await reconfigureServer(); - user = await Parse.User.signUp('tester', 'abc'); - user = await Parse.User.logIn(user.get('username'), 'abc'); + user = await Parse.User.signUp("tester", "abc"); + user = await Parse.User.logIn(user.get("username"), "abc"); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); - await user.set('email', EMAIL).set('zip', ZIP).set('ssn', SSN).setACL(acl).save(); + await user + .set("email", EMAIL) + .set("zip", ZIP) + .set("ssn", SSN) + .setACL(acl) + .save(); done(); }); - it('should be able to get own PII via API with object', done => { + it("should be able to get own PII via API with object", done => { const userObj = new (Parse.Object.extend(Parse.User))(); userObj.id = user.id; return userObj .fetch() .then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(EMAIL); + expect(fetchedUser.get("email")).toBe(EMAIL); }) .then(done) .catch(done.fail); }); - it('should not be able to get PII via API with object', done => { + it("should not be able to get PII via API with object", done => { return Parse.User.logOut().then(() => { const userObj = new (Parse.Object.extend(Parse.User))(); userObj.id = user.id; userObj .fetch() .then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(undefined); + expect(fetchedUser.get("email")).toBe(undefined); done(); }) .catch(e => { @@ -52,86 +57,90 @@ describe('Personally Identifiable Information', () => { }); }); - it('should be able to get PII via API with object using master key', done => { + it("should be able to get PII via API with object using master key", done => { return Parse.User.logOut().then(() => { const userObj = new (Parse.Object.extend(Parse.User))(); userObj.id = user.id; userObj .fetch({ useMasterKey: true }) - .then(fetchedUser => expect(fetchedUser.get('email')).toBe(EMAIL)) + .then(fetchedUser => expect(fetchedUser.get("email")).toBe(EMAIL)) .then(done) .catch(done.fail); }); }); - it('should be able to get own PII via API with Find', done => { + it("should be able to get own PII via API with Find", done => { return new Parse.Query(Parse.User).first().then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(EMAIL); - expect(fetchedUser.get('zip')).toBe(ZIP); - expect(fetchedUser.get('ssn')).toBe(SSN); + expect(fetchedUser.get("email")).toBe(EMAIL); + expect(fetchedUser.get("zip")).toBe(ZIP); + expect(fetchedUser.get("ssn")).toBe(SSN); done(); }); }); - it('should not get PII via API with Find', done => { + it("should not get PII via API with Find", done => { return Parse.User.logOut().then(() => new Parse.Query(Parse.User).first().then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(undefined); - expect(fetchedUser.get('zip')).toBe(ZIP); - expect(fetchedUser.get('ssn')).toBe(SSN); + expect(fetchedUser.get("email")).toBe(undefined); + expect(fetchedUser.get("zip")).toBe(ZIP); + expect(fetchedUser.get("ssn")).toBe(SSN); done(); }) ); }); - it('should get PII via API with Find using master key', done => { + it("should get PII via API with Find using master key", done => { return Parse.User.logOut().then(() => - new Parse.Query(Parse.User).first({ useMasterKey: true }).then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(EMAIL); - expect(fetchedUser.get('zip')).toBe(ZIP); - expect(fetchedUser.get('ssn')).toBe(SSN); - done(); - }) + new Parse.Query(Parse.User) + .first({ useMasterKey: true }) + .then(fetchedUser => { + expect(fetchedUser.get("email")).toBe(EMAIL); + expect(fetchedUser.get("zip")).toBe(ZIP); + expect(fetchedUser.get("ssn")).toBe(SSN); + done(); + }) ); }); - it('should be able to get own PII via API with Get', done => { + it("should be able to get own PII via API with Get", done => { return new Parse.Query(Parse.User).get(user.id).then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(EMAIL); - expect(fetchedUser.get('zip')).toBe(ZIP); - expect(fetchedUser.get('ssn')).toBe(SSN); + expect(fetchedUser.get("email")).toBe(EMAIL); + expect(fetchedUser.get("zip")).toBe(ZIP); + expect(fetchedUser.get("ssn")).toBe(SSN); done(); }); }); - it('should not get PII via API with Get', done => { + it("should not get PII via API with Get", done => { return Parse.User.logOut().then(() => new Parse.Query(Parse.User).get(user.id).then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(undefined); - expect(fetchedUser.get('zip')).toBe(ZIP); - expect(fetchedUser.get('ssn')).toBe(SSN); + expect(fetchedUser.get("email")).toBe(undefined); + expect(fetchedUser.get("zip")).toBe(ZIP); + expect(fetchedUser.get("ssn")).toBe(SSN); done(); }) ); }); - it('should get PII via API with Get using master key', done => { + it("should get PII via API with Get using master key", done => { return Parse.User.logOut().then(() => - new Parse.Query(Parse.User).get(user.id, { useMasterKey: true }).then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(EMAIL); - expect(fetchedUser.get('zip')).toBe(ZIP); - expect(fetchedUser.get('ssn')).toBe(SSN); - done(); - }) + new Parse.Query(Parse.User) + .get(user.id, { useMasterKey: true }) + .then(fetchedUser => { + expect(fetchedUser.get("email")).toBe(EMAIL); + expect(fetchedUser.get("zip")).toBe(ZIP); + expect(fetchedUser.get("ssn")).toBe(SSN); + done(); + }) ); }); - it('should not get PII via REST', done => { + it("should not get PII via REST", done => { return request({ - url: 'http://localhost:8378/1/classes/_User', + url: "http://localhost:8378/1/classes/_User", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Javascript-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-Javascript-Key": "test", }, }) .then(response => { @@ -144,14 +153,14 @@ describe('Personally Identifiable Information', () => { .catch(done.fail); }); - it('should get PII via REST with self credentials', done => { + it("should get PII via REST with self credentials", done => { return request({ - url: 'http://localhost:8378/1/classes/_User', + url: "http://localhost:8378/1/classes/_User", json: true, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Javascript-Key': 'test', - 'X-Parse-Session-Token': user.getSessionToken(), + "X-Parse-Application-Id": "test", + "X-Parse-Javascript-Key": "test", + "X-Parse-Session-Token": user.getSessionToken(), }, }) .then(response => { @@ -164,13 +173,13 @@ describe('Personally Identifiable Information', () => { .catch(done.fail); }); - it('should get PII via REST using master key', done => { + it("should get PII via REST using master key", done => { request({ - url: 'http://localhost:8378/1/classes/_User', + url: "http://localhost:8378/1/classes/_User", json: true, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-Master-Key": "test", }, }) .then(response => { @@ -183,12 +192,12 @@ describe('Personally Identifiable Information', () => { .catch(done.fail); }); - it('should not get PII via REST by ID', done => { + it("should not get PII via REST by ID", done => { request({ url: `http://localhost:8378/1/classes/_User/${user.id}`, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Javascript-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-Javascript-Key": "test", }, }) .then( @@ -202,14 +211,14 @@ describe('Personally Identifiable Information', () => { .then(() => done()); }); - it('should get PII via REST by ID with self credentials', done => { + it("should get PII via REST by ID with self credentials", done => { request({ url: `http://localhost:8378/1/classes/_User/${user.id}`, json: true, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Javascript-Key': 'test', - 'X-Parse-Session-Token': user.getSessionToken(), + "X-Parse-Application-Id": "test", + "X-Parse-Javascript-Key": "test", + "X-Parse-Session-Token": user.getSessionToken(), }, }) .then(response => { @@ -222,14 +231,14 @@ describe('Personally Identifiable Information', () => { .catch(done.fail); }); - it('should get PII via REST by ID with master key', done => { + it("should get PII via REST by ID with master key", done => { request({ url: `http://localhost:8378/1/classes/_User/${user.id}`, json: true, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Javascript-Key': 'test', - 'X-Parse-Master-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-Javascript-Key": "test", + "X-Parse-Master-Key": "test", }, }) .then(response => { @@ -242,125 +251,129 @@ describe('Personally Identifiable Information', () => { .catch(done.fail); }); - describe('with deprecated configured sensitive fields', () => { + describe("with deprecated configured sensitive fields", () => { beforeEach(async () => { - await reconfigureServer({ userSensitiveFields: ['ssn', 'zip'] }); + await reconfigureServer({ userSensitiveFields: ["ssn", "zip"] }); }); - it('should be able to get own PII via API with object', done => { + it("should be able to get own PII via API with object", done => { const userObj = new (Parse.Object.extend(Parse.User))(); userObj.id = user.id; return userObj .fetch() .then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(EMAIL); - expect(fetchedUser.get('zip')).toBe(ZIP); - expect(fetchedUser.get('ssn')).toBe(SSN); + expect(fetchedUser.get("email")).toBe(EMAIL); + expect(fetchedUser.get("zip")).toBe(ZIP); + expect(fetchedUser.get("ssn")).toBe(SSN); done(); }) .catch(done.fail); }); - it('should not be able to get PII via API with object', done => { + it("should not be able to get PII via API with object", done => { Parse.User.logOut().then(() => { const userObj = new (Parse.Object.extend(Parse.User))(); userObj.id = user.id; userObj .fetch() .then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(undefined); - expect(fetchedUser.get('zip')).toBe(undefined); - expect(fetchedUser.get('ssn')).toBe(undefined); + expect(fetchedUser.get("email")).toBe(undefined); + expect(fetchedUser.get("zip")).toBe(undefined); + expect(fetchedUser.get("ssn")).toBe(undefined); }) .then(done) .catch(done.fail); }); }); - it('should be able to get PII via API with object using master key', done => { + it("should be able to get PII via API with object using master key", done => { Parse.User.logOut().then(() => { const userObj = new (Parse.Object.extend(Parse.User))(); userObj.id = user.id; userObj .fetch({ useMasterKey: true }) .then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(EMAIL); - expect(fetchedUser.get('zip')).toBe(ZIP); - expect(fetchedUser.get('ssn')).toBe(SSN); + expect(fetchedUser.get("email")).toBe(EMAIL); + expect(fetchedUser.get("zip")).toBe(ZIP); + expect(fetchedUser.get("ssn")).toBe(SSN); }, done.fail) .then(done) .catch(done.fail); }); }); - it('should be able to get own PII via API with Find', done => { + it("should be able to get own PII via API with Find", done => { new Parse.Query(Parse.User).first().then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(EMAIL); - expect(fetchedUser.get('zip')).toBe(ZIP); - expect(fetchedUser.get('ssn')).toBe(SSN); + expect(fetchedUser.get("email")).toBe(EMAIL); + expect(fetchedUser.get("zip")).toBe(ZIP); + expect(fetchedUser.get("ssn")).toBe(SSN); done(); }); }); - it('should not get PII via API with Find', done => { + it("should not get PII via API with Find", done => { Parse.User.logOut().then(() => new Parse.Query(Parse.User).first().then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(undefined); - expect(fetchedUser.get('zip')).toBe(undefined); - expect(fetchedUser.get('ssn')).toBe(undefined); + expect(fetchedUser.get("email")).toBe(undefined); + expect(fetchedUser.get("zip")).toBe(undefined); + expect(fetchedUser.get("ssn")).toBe(undefined); done(); }) ); }); - it('should get PII via API with Find using master key', done => { + it("should get PII via API with Find using master key", done => { Parse.User.logOut().then(() => - new Parse.Query(Parse.User).first({ useMasterKey: true }).then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(EMAIL); - expect(fetchedUser.get('zip')).toBe(ZIP); - expect(fetchedUser.get('ssn')).toBe(SSN); - done(); - }) + new Parse.Query(Parse.User) + .first({ useMasterKey: true }) + .then(fetchedUser => { + expect(fetchedUser.get("email")).toBe(EMAIL); + expect(fetchedUser.get("zip")).toBe(ZIP); + expect(fetchedUser.get("ssn")).toBe(SSN); + done(); + }) ); }); - it('should be able to get own PII via API with Get', done => { + it("should be able to get own PII via API with Get", done => { new Parse.Query(Parse.User).get(user.id).then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(EMAIL); - expect(fetchedUser.get('zip')).toBe(ZIP); - expect(fetchedUser.get('ssn')).toBe(SSN); + expect(fetchedUser.get("email")).toBe(EMAIL); + expect(fetchedUser.get("zip")).toBe(ZIP); + expect(fetchedUser.get("ssn")).toBe(SSN); done(); }); }); - it('should not get PII via API with Get', done => { + it("should not get PII via API with Get", done => { Parse.User.logOut().then(() => new Parse.Query(Parse.User).get(user.id).then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(undefined); - expect(fetchedUser.get('zip')).toBe(undefined); - expect(fetchedUser.get('ssn')).toBe(undefined); + expect(fetchedUser.get("email")).toBe(undefined); + expect(fetchedUser.get("zip")).toBe(undefined); + expect(fetchedUser.get("ssn")).toBe(undefined); done(); }) ); }); - it('should get PII via API with Get using master key', done => { + it("should get PII via API with Get using master key", done => { Parse.User.logOut().then(() => - new Parse.Query(Parse.User).get(user.id, { useMasterKey: true }).then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(EMAIL); - expect(fetchedUser.get('zip')).toBe(ZIP); - expect(fetchedUser.get('ssn')).toBe(SSN); - done(); - }) + new Parse.Query(Parse.User) + .get(user.id, { useMasterKey: true }) + .then(fetchedUser => { + expect(fetchedUser.get("email")).toBe(EMAIL); + expect(fetchedUser.get("zip")).toBe(ZIP); + expect(fetchedUser.get("ssn")).toBe(SSN); + done(); + }) ); }); - it('should not get PII via REST', done => { + it("should not get PII via REST", done => { request({ - url: 'http://localhost:8378/1/classes/_User', + url: "http://localhost:8378/1/classes/_User", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Javascript-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-Javascript-Key": "test", }, }) .then(response => { @@ -374,14 +387,14 @@ describe('Personally Identifiable Information', () => { .catch(done.fail); }); - it('should get PII via REST with self credentials', done => { + it("should get PII via REST with self credentials", done => { request({ - url: 'http://localhost:8378/1/classes/_User', + url: "http://localhost:8378/1/classes/_User", json: true, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Javascript-Key': 'test', - 'X-Parse-Session-Token': user.getSessionToken(), + "X-Parse-Application-Id": "test", + "X-Parse-Javascript-Key": "test", + "X-Parse-Session-Token": user.getSessionToken(), }, }) .then(response => { @@ -395,13 +408,13 @@ describe('Personally Identifiable Information', () => { .catch(done.fail); }); - it('should get PII via REST using master key', done => { + it("should get PII via REST using master key", done => { request({ - url: 'http://localhost:8378/1/classes/_User', + url: "http://localhost:8378/1/classes/_User", json: true, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-Master-Key": "test", }, }) .then( @@ -418,13 +431,13 @@ describe('Personally Identifiable Information', () => { .catch(done.fail); }); - it('should not get PII via REST by ID', done => { + it("should not get PII via REST by ID", done => { request({ url: `http://localhost:8378/1/classes/_User/${user.id}`, json: true, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Javascript-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-Javascript-Key": "test", }, }) .then( @@ -439,14 +452,14 @@ describe('Personally Identifiable Information', () => { .catch(done.fail); }); - it('should get PII via REST by ID with self credentials', done => { + it("should get PII via REST by ID with self credentials", done => { request({ url: `http://localhost:8378/1/classes/_User/${user.id}`, json: true, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Javascript-Key': 'test', - 'X-Parse-Session-Token': user.getSessionToken(), + "X-Parse-Application-Id": "test", + "X-Parse-Javascript-Key": "test", + "X-Parse-Session-Token": user.getSessionToken(), }, }) .then( @@ -461,13 +474,13 @@ describe('Personally Identifiable Information', () => { .catch(done.fail); }); - it('should get PII via REST by ID with master key', done => { + it("should get PII via REST by ID with master key", done => { request({ url: `http://localhost:8378/1/classes/_User/${user.id}`, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Javascript-Key': 'test', - 'X-Parse-Master-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-Javascript-Key": "test", + "X-Parse-Master-Key": "test", }, }) .then( @@ -484,15 +497,21 @@ describe('Personally Identifiable Information', () => { }); // Explicit ACL should be able to read sensitive information - describe('with privileged user no CLP', () => { + describe("with privileged user no CLP", () => { let adminUser; beforeEach(async done => { - const adminRole = await new Parse.Role('Administrator', new Parse.ACL()).save(null, { + const adminRole = await new Parse.Role( + "Administrator", + new Parse.ACL() + ).save(null, { useMasterKey: true, }); - const managementRole = new Parse.Role('managementOf_user' + user.id, new Parse.ACL(user)); + const managementRole = new Parse.Role( + "managementOf_user" + user.id, + new Parse.ACL(user) + ); managementRole.getRoles().add(adminRole); await managementRole.save(null, { useMasterKey: true }); @@ -500,59 +519,62 @@ describe('Personally Identifiable Information', () => { userACL.setReadAccess(managementRole, true); await user.setACL(userACL).save(null, { useMasterKey: true }); - adminUser = await Parse.User.signUp('administrator', 'secure'); - adminUser = await Parse.User.logIn(adminUser.get('username'), 'secure'); - await adminRole.getUsers().add(adminUser).save(null, { useMasterKey: true }); + adminUser = await Parse.User.signUp("administrator", "secure"); + adminUser = await Parse.User.logIn(adminUser.get("username"), "secure"); + await adminRole + .getUsers() + .add(adminUser) + .save(null, { useMasterKey: true }); done(); }); - it('privileged user should not be able to get user PII via API with object', done => { + it("privileged user should not be able to get user PII via API with object", done => { const userObj = new (Parse.Object.extend(Parse.User))(); userObj.id = user.id; userObj .fetch() .then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(undefined); + expect(fetchedUser.get("email")).toBe(undefined); }) .then(done) .catch(done.fail); }); - it('privileged user should not be able to get user PII via API with Find', done => { + it("privileged user should not be able to get user PII via API with Find", done => { new Parse.Query(Parse.User) - .equalTo('objectId', user.id) + .equalTo("objectId", user.id) .find() .then(fetchedUser => { fetchedUser = fetchedUser[0]; - expect(fetchedUser.get('email')).toBe(undefined); - expect(fetchedUser.get('zip')).toBe(undefined); - expect(fetchedUser.get('ssn')).toBe(undefined); + expect(fetchedUser.get("email")).toBe(undefined); + expect(fetchedUser.get("zip")).toBe(undefined); + expect(fetchedUser.get("ssn")).toBe(undefined); done(); }) .catch(done.fail); }); - it('privileged user should not be able to get user PII via API with Get', done => { + it("privileged user should not be able to get user PII via API with Get", done => { new Parse.Query(Parse.User) .get(user.id) .then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(undefined); - expect(fetchedUser.get('zip')).toBe(undefined); - expect(fetchedUser.get('ssn')).toBe(undefined); + expect(fetchedUser.get("email")).toBe(undefined); + expect(fetchedUser.get("zip")).toBe(undefined); + expect(fetchedUser.get("ssn")).toBe(undefined); done(); }) .catch(done.fail); }); - it('privileged user should not get user PII via REST by ID', done => { + it("privileged user should not get user PII via REST by ID", done => { request({ url: `http://localhost:8378/1/classes/_User/${user.id}`, json: true, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Javascript-Key': 'test', - 'X-Parse-Session-Token': adminUser.getSessionToken(), + "X-Parse-Application-Id": "test", + "X-Parse-Javascript-Key": "test", + "X-Parse-Session-Token": adminUser.getSessionToken(), }, }) .then(response => { @@ -567,7 +589,7 @@ describe('Personally Identifiable Information', () => { }); // Public access ACL should always hide sensitive information - describe('with public read ACL', () => { + describe("with public read ACL", () => { beforeEach(async done => { const userACL = new Parse.ACL(); userACL.setPublicReadAccess(true); @@ -575,57 +597,57 @@ describe('Personally Identifiable Information', () => { done(); }); - it('should not be able to get user PII via API with object', done => { + it("should not be able to get user PII via API with object", done => { Parse.User.logOut().then(() => { const userObj = new (Parse.Object.extend(Parse.User))(); userObj.id = user.id; userObj .fetch() .then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(undefined); + expect(fetchedUser.get("email")).toBe(undefined); }) .then(done) .catch(done.fail); }); }); - it('should not be able to get user PII via API with Find', done => { + it("should not be able to get user PII via API with Find", done => { Parse.User.logOut().then(() => new Parse.Query(Parse.User) - .equalTo('objectId', user.id) + .equalTo("objectId", user.id) .find() .then(fetchedUser => { fetchedUser = fetchedUser[0]; - expect(fetchedUser.get('email')).toBe(undefined); - expect(fetchedUser.get('zip')).toBe(undefined); - expect(fetchedUser.get('ssn')).toBe(undefined); + expect(fetchedUser.get("email")).toBe(undefined); + expect(fetchedUser.get("zip")).toBe(undefined); + expect(fetchedUser.get("ssn")).toBe(undefined); done(); }) .catch(done.fail) ); }); - it('should not be able to get user PII via API with Get', done => { + it("should not be able to get user PII via API with Get", done => { Parse.User.logOut().then(() => new Parse.Query(Parse.User) .get(user.id) .then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(undefined); - expect(fetchedUser.get('zip')).toBe(undefined); - expect(fetchedUser.get('ssn')).toBe(undefined); + expect(fetchedUser.get("email")).toBe(undefined); + expect(fetchedUser.get("zip")).toBe(undefined); + expect(fetchedUser.get("ssn")).toBe(undefined); done(); }) .catch(done.fail) ); }); - it('should not get user PII via REST by ID', done => { + it("should not get user PII via REST by ID", done => { request({ url: `http://localhost:8378/1/classes/_User/${user.id}`, json: true, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Javascript-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-Javascript-Key": "test", }, }) .then(response => { @@ -639,49 +661,49 @@ describe('Personally Identifiable Information', () => { }); // Even with an authenticated user, Public read ACL should never expose sensitive data. - describe('with another authenticated user', () => { + describe("with another authenticated user", () => { let anotherUser; beforeEach(async done => { - return Parse.User.signUp('another', 'abc') + return Parse.User.signUp("another", "abc") .then(loggedInUser => (anotherUser = loggedInUser)) - .then(() => Parse.User.logIn(anotherUser.get('username'), 'abc')) + .then(() => Parse.User.logIn(anotherUser.get("username"), "abc")) .then(() => done()); }); - it('should not be able to get user PII via API with object', done => { + it("should not be able to get user PII via API with object", done => { const userObj = new (Parse.Object.extend(Parse.User))(); userObj.id = user.id; userObj .fetch() .then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(undefined); + expect(fetchedUser.get("email")).toBe(undefined); }) .then(done) .catch(done.fail); }); - it('should not be able to get user PII via API with Find', done => { + it("should not be able to get user PII via API with Find", done => { new Parse.Query(Parse.User) - .equalTo('objectId', user.id) + .equalTo("objectId", user.id) .find() .then(fetchedUser => { fetchedUser = fetchedUser[0]; - expect(fetchedUser.get('email')).toBe(undefined); - expect(fetchedUser.get('zip')).toBe(undefined); - expect(fetchedUser.get('ssn')).toBe(undefined); + expect(fetchedUser.get("email")).toBe(undefined); + expect(fetchedUser.get("zip")).toBe(undefined); + expect(fetchedUser.get("ssn")).toBe(undefined); done(); }) .catch(done.fail); }); - it('should not be able to get user PII via API with Get', done => { + it("should not be able to get user PII via API with Get", done => { new Parse.Query(Parse.User) .get(user.id) .then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(undefined); - expect(fetchedUser.get('zip')).toBe(undefined); - expect(fetchedUser.get('ssn')).toBe(undefined); + expect(fetchedUser.get("email")).toBe(undefined); + expect(fetchedUser.get("zip")).toBe(undefined); + expect(fetchedUser.get("ssn")).toBe(undefined); done(); }) .catch(done.fail); @@ -690,126 +712,130 @@ describe('Personally Identifiable Information', () => { }); }); - describe('with configured sensitive fields via CLP', () => { + describe("with configured sensitive fields via CLP", () => { beforeEach(async () => { await reconfigureServer({ protectedFields: { - _User: { '*': ['ssn', 'zip'], 'role:Administrator': [] }, + _User: { "*": ["ssn", "zip"], "role:Administrator": [] }, }, }); }); - it('should be able to get own PII via API with object', done => { + it("should be able to get own PII via API with object", done => { const userObj = new (Parse.Object.extend(Parse.User))(); userObj.id = user.id; userObj.fetch().then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(EMAIL); - expect(fetchedUser.get('zip')).toBe(ZIP); - expect(fetchedUser.get('ssn')).toBe(SSN); + expect(fetchedUser.get("email")).toBe(EMAIL); + expect(fetchedUser.get("zip")).toBe(ZIP); + expect(fetchedUser.get("ssn")).toBe(SSN); done(); }, done.fail); }); - it('should not be able to get PII via API with object', done => { + it("should not be able to get PII via API with object", done => { Parse.User.logOut().then(() => { const userObj = new (Parse.Object.extend(Parse.User))(); userObj.id = user.id; userObj .fetch() .then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(undefined); - expect(fetchedUser.get('zip')).toBe(undefined); - expect(fetchedUser.get('ssn')).toBe(undefined); + expect(fetchedUser.get("email")).toBe(undefined); + expect(fetchedUser.get("zip")).toBe(undefined); + expect(fetchedUser.get("ssn")).toBe(undefined); }) .then(done) .catch(done.fail); }); }); - it('should be able to get PII via API with object using master key', done => { + it("should be able to get PII via API with object using master key", done => { Parse.User.logOut().then(() => { const userObj = new (Parse.Object.extend(Parse.User))(); userObj.id = user.id; userObj .fetch({ useMasterKey: true }) .then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(EMAIL); - expect(fetchedUser.get('zip')).toBe(ZIP); - expect(fetchedUser.get('ssn')).toBe(SSN); + expect(fetchedUser.get("email")).toBe(EMAIL); + expect(fetchedUser.get("zip")).toBe(ZIP); + expect(fetchedUser.get("ssn")).toBe(SSN); }, done.fail) .then(done) .catch(done.fail); }); }); - it('should be able to get own PII via API with Find', done => { + it("should be able to get own PII via API with Find", done => { new Parse.Query(Parse.User).first().then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(EMAIL); - expect(fetchedUser.get('zip')).toBe(ZIP); - expect(fetchedUser.get('ssn')).toBe(SSN); + expect(fetchedUser.get("email")).toBe(EMAIL); + expect(fetchedUser.get("zip")).toBe(ZIP); + expect(fetchedUser.get("ssn")).toBe(SSN); done(); }); }); - it('should not get PII via API with Find', done => { + it("should not get PII via API with Find", done => { Parse.User.logOut().then(() => new Parse.Query(Parse.User).first().then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(undefined); - expect(fetchedUser.get('zip')).toBe(undefined); - expect(fetchedUser.get('ssn')).toBe(undefined); + expect(fetchedUser.get("email")).toBe(undefined); + expect(fetchedUser.get("zip")).toBe(undefined); + expect(fetchedUser.get("ssn")).toBe(undefined); done(); }) ); }); - it('should get PII via API with Find using master key', done => { + it("should get PII via API with Find using master key", done => { Parse.User.logOut().then(() => - new Parse.Query(Parse.User).first({ useMasterKey: true }).then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(EMAIL); - expect(fetchedUser.get('zip')).toBe(ZIP); - expect(fetchedUser.get('ssn')).toBe(SSN); - done(); - }) + new Parse.Query(Parse.User) + .first({ useMasterKey: true }) + .then(fetchedUser => { + expect(fetchedUser.get("email")).toBe(EMAIL); + expect(fetchedUser.get("zip")).toBe(ZIP); + expect(fetchedUser.get("ssn")).toBe(SSN); + done(); + }) ); }); - it('should be able to get own PII via API with Get', done => { + it("should be able to get own PII via API with Get", done => { new Parse.Query(Parse.User).get(user.id).then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(EMAIL); - expect(fetchedUser.get('zip')).toBe(ZIP); - expect(fetchedUser.get('ssn')).toBe(SSN); + expect(fetchedUser.get("email")).toBe(EMAIL); + expect(fetchedUser.get("zip")).toBe(ZIP); + expect(fetchedUser.get("ssn")).toBe(SSN); done(); }); }); - it('should not get PII via API with Get', done => { + it("should not get PII via API with Get", done => { Parse.User.logOut().then(() => new Parse.Query(Parse.User).get(user.id).then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(undefined); - expect(fetchedUser.get('zip')).toBe(undefined); - expect(fetchedUser.get('ssn')).toBe(undefined); + expect(fetchedUser.get("email")).toBe(undefined); + expect(fetchedUser.get("zip")).toBe(undefined); + expect(fetchedUser.get("ssn")).toBe(undefined); done(); }) ); }); - it('should get PII via API with Get using master key', done => { + it("should get PII via API with Get using master key", done => { Parse.User.logOut().then(() => - new Parse.Query(Parse.User).get(user.id, { useMasterKey: true }).then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(EMAIL); - expect(fetchedUser.get('zip')).toBe(ZIP); - expect(fetchedUser.get('ssn')).toBe(SSN); - done(); - }) + new Parse.Query(Parse.User) + .get(user.id, { useMasterKey: true }) + .then(fetchedUser => { + expect(fetchedUser.get("email")).toBe(EMAIL); + expect(fetchedUser.get("zip")).toBe(ZIP); + expect(fetchedUser.get("ssn")).toBe(SSN); + done(); + }) ); }); - it('should not get PII via REST', done => { + it("should not get PII via REST", done => { request({ - url: 'http://localhost:8378/1/classes/_User', + url: "http://localhost:8378/1/classes/_User", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Javascript-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-Javascript-Key": "test", }, }) .then(response => { @@ -823,14 +849,14 @@ describe('Personally Identifiable Information', () => { .catch(done.fail); }); - it('should get PII via REST with self credentials', done => { + it("should get PII via REST with self credentials", done => { request({ - url: 'http://localhost:8378/1/classes/_User', + url: "http://localhost:8378/1/classes/_User", json: true, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Javascript-Key': 'test', - 'X-Parse-Session-Token': user.getSessionToken(), + "X-Parse-Application-Id": "test", + "X-Parse-Javascript-Key": "test", + "X-Parse-Session-Token": user.getSessionToken(), }, }) .then( @@ -847,13 +873,13 @@ describe('Personally Identifiable Information', () => { .catch(done.fail); }); - it('should get PII via REST using master key', done => { + it("should get PII via REST using master key", done => { request({ - url: 'http://localhost:8378/1/classes/_User', + url: "http://localhost:8378/1/classes/_User", json: true, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-Master-Key": "test", }, }) .then( @@ -870,13 +896,13 @@ describe('Personally Identifiable Information', () => { .catch(done.fail); }); - it('should not get PII via REST by ID', done => { + it("should not get PII via REST by ID", done => { request({ url: `http://localhost:8378/1/classes/_User/${user.id}`, json: true, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Javascript-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-Javascript-Key": "test", }, }) .then( @@ -891,14 +917,14 @@ describe('Personally Identifiable Information', () => { .catch(done.fail); }); - it('should get PII via REST by ID with self credentials', done => { + it("should get PII via REST by ID with self credentials", done => { request({ url: `http://localhost:8378/1/classes/_User/${user.id}`, json: true, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Javascript-Key': 'test', - 'X-Parse-Session-Token': user.getSessionToken(), + "X-Parse-Application-Id": "test", + "X-Parse-Javascript-Key": "test", + "X-Parse-Session-Token": user.getSessionToken(), }, }) .then( @@ -913,13 +939,13 @@ describe('Personally Identifiable Information', () => { .catch(done.fail); }); - it('should get PII via REST by ID with master key', done => { + it("should get PII via REST by ID with master key", done => { request({ url: `http://localhost:8378/1/classes/_User/${user.id}`, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Javascript-Key': 'test', - 'X-Parse-Master-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-Javascript-Key": "test", + "X-Parse-Master-Key": "test", }, }) .then( @@ -936,15 +962,21 @@ describe('Personally Identifiable Information', () => { }); // Explicit ACL should be able to read sensitive information - describe('with privileged user CLP', () => { + describe("with privileged user CLP", () => { let adminUser; beforeEach(async done => { - const adminRole = await new Parse.Role('Administrator', new Parse.ACL()).save(null, { + const adminRole = await new Parse.Role( + "Administrator", + new Parse.ACL() + ).save(null, { useMasterKey: true, }); - const managementRole = new Parse.Role('managementOf_user' + user.id, new Parse.ACL(user)); + const managementRole = new Parse.Role( + "managementOf_user" + user.id, + new Parse.ACL(user) + ); managementRole.getRoles().add(adminRole); await managementRole.save(null, { useMasterKey: true }); @@ -952,59 +984,62 @@ describe('Personally Identifiable Information', () => { userACL.setReadAccess(managementRole, true); await user.setACL(userACL).save(null, { useMasterKey: true }); - adminUser = await Parse.User.signUp('administrator', 'secure'); - adminUser = await Parse.User.logIn(adminUser.get('username'), 'secure'); - await adminRole.getUsers().add(adminUser).save(null, { useMasterKey: true }); + adminUser = await Parse.User.signUp("administrator", "secure"); + adminUser = await Parse.User.logIn(adminUser.get("username"), "secure"); + await adminRole + .getUsers() + .add(adminUser) + .save(null, { useMasterKey: true }); done(); }); - it('privileged user should be able to get user PII via API with object', done => { + it("privileged user should be able to get user PII via API with object", done => { const userObj = new (Parse.Object.extend(Parse.User))(); userObj.id = user.id; userObj .fetch() .then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(EMAIL); + expect(fetchedUser.get("email")).toBe(EMAIL); }) .then(done) .catch(done.fail); }); - it('privileged user should be able to get user PII via API with Find', done => { + it("privileged user should be able to get user PII via API with Find", done => { new Parse.Query(Parse.User) - .equalTo('objectId', user.id) + .equalTo("objectId", user.id) .find() .then(fetchedUser => { fetchedUser = fetchedUser[0]; - expect(fetchedUser.get('email')).toBe(EMAIL); - expect(fetchedUser.get('zip')).toBe(ZIP); - expect(fetchedUser.get('ssn')).toBe(SSN); + expect(fetchedUser.get("email")).toBe(EMAIL); + expect(fetchedUser.get("zip")).toBe(ZIP); + expect(fetchedUser.get("ssn")).toBe(SSN); done(); }) .catch(done.fail); }); - it('privileged user should be able to get user PII via API with Get', done => { + it("privileged user should be able to get user PII via API with Get", done => { new Parse.Query(Parse.User) .get(user.id) .then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(EMAIL); - expect(fetchedUser.get('zip')).toBe(ZIP); - expect(fetchedUser.get('ssn')).toBe(SSN); + expect(fetchedUser.get("email")).toBe(EMAIL); + expect(fetchedUser.get("zip")).toBe(ZIP); + expect(fetchedUser.get("ssn")).toBe(SSN); done(); }) .catch(done.fail); }); - it('privileged user should get user PII via REST by ID', done => { + it("privileged user should get user PII via REST by ID", done => { request({ url: `http://localhost:8378/1/classes/_User/${user.id}`, json: true, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Javascript-Key': 'test', - 'X-Parse-Session-Token': adminUser.getSessionToken(), + "X-Parse-Application-Id": "test", + "X-Parse-Javascript-Key": "test", + "X-Parse-Session-Token": adminUser.getSessionToken(), }, }) .then(response => { @@ -1019,7 +1054,7 @@ describe('Personally Identifiable Information', () => { }); // Public access ACL should always hide sensitive information - describe('with public read ACL', () => { + describe("with public read ACL", () => { beforeEach(async done => { const userACL = new Parse.ACL(); userACL.setPublicReadAccess(true); @@ -1027,57 +1062,57 @@ describe('Personally Identifiable Information', () => { done(); }); - it('should not be able to get user PII via API with object', done => { + it("should not be able to get user PII via API with object", done => { Parse.User.logOut().then(() => { const userObj = new (Parse.Object.extend(Parse.User))(); userObj.id = user.id; userObj .fetch() .then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(undefined); + expect(fetchedUser.get("email")).toBe(undefined); }) .then(done) .catch(done.fail); }); }); - it('should not be able to get user PII via API with Find', done => { + it("should not be able to get user PII via API with Find", done => { Parse.User.logOut().then(() => new Parse.Query(Parse.User) - .equalTo('objectId', user.id) + .equalTo("objectId", user.id) .find() .then(fetchedUser => { fetchedUser = fetchedUser[0]; - expect(fetchedUser.get('email')).toBe(undefined); - expect(fetchedUser.get('zip')).toBe(undefined); - expect(fetchedUser.get('ssn')).toBe(undefined); + expect(fetchedUser.get("email")).toBe(undefined); + expect(fetchedUser.get("zip")).toBe(undefined); + expect(fetchedUser.get("ssn")).toBe(undefined); done(); }) .catch(done.fail) ); }); - it('should not be able to get user PII via API with Get', done => { + it("should not be able to get user PII via API with Get", done => { Parse.User.logOut().then(() => new Parse.Query(Parse.User) .get(user.id) .then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(undefined); - expect(fetchedUser.get('zip')).toBe(undefined); - expect(fetchedUser.get('ssn')).toBe(undefined); + expect(fetchedUser.get("email")).toBe(undefined); + expect(fetchedUser.get("zip")).toBe(undefined); + expect(fetchedUser.get("ssn")).toBe(undefined); done(); }) .catch(done.fail) ); }); - it('should not get user PII via REST by ID', done => { + it("should not get user PII via REST by ID", done => { request({ url: `http://localhost:8378/1/classes/_User/${user.id}`, json: true, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Javascript-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-Javascript-Key": "test", }, }) .then(response => { @@ -1091,79 +1126,87 @@ describe('Personally Identifiable Information', () => { }); // Even with an authenticated user, Public read ACL should never expose sensitive data. - describe('with another authenticated user', () => { + describe("with another authenticated user", () => { let anotherUser; - const ANOTHER_EMAIL = 'another@bar.com'; + const ANOTHER_EMAIL = "another@bar.com"; beforeEach(async done => { - return Parse.User.signUp('another', 'abc') + return Parse.User.signUp("another", "abc") .then(loggedInUser => (anotherUser = loggedInUser)) - .then(() => Parse.User.logIn(anotherUser.get('username'), 'abc')) + .then(() => Parse.User.logIn(anotherUser.get("username"), "abc")) .then(() => - anotherUser.set('email', ANOTHER_EMAIL).set('zip', ZIP).set('ssn', SSN).save() + anotherUser + .set("email", ANOTHER_EMAIL) + .set("zip", ZIP) + .set("ssn", SSN) + .save() ) .then(() => done()); }); - it('should not be able to get user PII via API with object', done => { + it("should not be able to get user PII via API with object", done => { const userObj = new (Parse.Object.extend(Parse.User))(); userObj.id = user.id; userObj .fetch() .then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(undefined); + expect(fetchedUser.get("email")).toBe(undefined); }) .then(done) .catch(done.fail); }); - it('should not be able to get user PII via API with Find', done => { + it("should not be able to get user PII via API with Find", done => { new Parse.Query(Parse.User) - .equalTo('objectId', user.id) + .equalTo("objectId", user.id) .find() .then(fetchedUser => { fetchedUser = fetchedUser[0]; - expect(fetchedUser.get('email')).toBe(undefined); - expect(fetchedUser.get('zip')).toBe(undefined); - expect(fetchedUser.get('ssn')).toBe(undefined); + expect(fetchedUser.get("email")).toBe(undefined); + expect(fetchedUser.get("zip")).toBe(undefined); + expect(fetchedUser.get("ssn")).toBe(undefined); done(); }) .catch(done.fail); }); - it('should not be able to get user PII via API with Find without constraints', done => { + it("should not be able to get user PII via API with Find without constraints", done => { new Parse.Query(Parse.User) .find() .then(fetchedUsers => { - const notCurrentUser = fetchedUsers.find(u => u.id !== anotherUser.id); - expect(notCurrentUser.get('email')).toBe(undefined); - expect(notCurrentUser.get('zip')).toBe(undefined); - expect(notCurrentUser.get('ssn')).toBe(undefined); + const notCurrentUser = fetchedUsers.find( + u => u.id !== anotherUser.id + ); + expect(notCurrentUser.get("email")).toBe(undefined); + expect(notCurrentUser.get("zip")).toBe(undefined); + expect(notCurrentUser.get("ssn")).toBe(undefined); done(); }) .catch(done.fail); }); - it('should be able to get own PII via API with Find without constraints', done => { + it("should be able to get own PII via API with Find without constraints", done => { new Parse.Query(Parse.User) .find() .then(fetchedUsers => { - const currentUser = fetchedUsers.find(u => u.id === anotherUser.id); - expect(currentUser.get('email')).toBe(ANOTHER_EMAIL); - expect(currentUser.get('zip')).toBe(ZIP); - expect(currentUser.get('ssn')).toBe(SSN); + const currentUser = fetchedUsers.find( + u => u.id === anotherUser.id + ); + expect(currentUser.get("email")).toBe(ANOTHER_EMAIL); + expect(currentUser.get("zip")).toBe(ZIP); + expect(currentUser.get("ssn")).toBe(SSN); done(); }) .catch(done.fail); }); - it('should not be able to get user PII via API with Get', done => { + it("should not be able to get user PII via API with Get", done => { new Parse.Query(Parse.User) .get(user.id) .then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(undefined); - expect(fetchedUser.get('zip')).toBe(undefined); - expect(fetchedUser.get('ssn')).toBe(undefined); + expect(fetchedUser.get("email")).toBe(undefined); + expect(fetchedUser.get("zip")).toBe(undefined); + expect(fetchedUser.get("ssn")).toBe(undefined); done(); }) .catch(done.fail); diff --git a/spec/Utils.spec.js b/spec/Utils.spec.js index c4f04096fa..83a1832423 100644 --- a/spec/Utils.spec.js +++ b/spec/Utils.spec.js @@ -1,10 +1,13 @@ -const Utils = require('../src/Utils'); +const Utils = require("../src/Utils"); -describe('Utils', () => { - describe('encodeForUrl', () => { - it('should properly escape email with all special ASCII characters for use in URLs', async () => { +describe("Utils", () => { + describe("encodeForUrl", () => { + it("should properly escape email with all special ASCII characters for use in URLs", async () => { const values = [ - { input: `!\"'),.:;<>?]^}`, output: '%21%22%27%29%2C%2E%3A%3B%3C%3E%3F%5D%5E%7D' }, + { + input: `!\"'),.:;<>?]^}`, + output: "%21%22%27%29%2C%2E%3A%3B%3C%3E%3F%5D%5E%7D", + }, ]; for (const value of values) { expect(Utils.encodeForUrl(value.input)).toBe(value.output); @@ -12,8 +15,8 @@ describe('Utils', () => { }); }); - describe('addNestedKeysToRoot', () => { - it('should move the nested keys to root of object', async () => { + describe("addNestedKeysToRoot", () => { + it("should move the nested keys to root of object", async () => { const obj = { a: 1, b: { @@ -22,7 +25,7 @@ describe('Utils', () => { }, e: 4, }; - Utils.addNestedKeysToRoot(obj, 'b'); + Utils.addNestedKeysToRoot(obj, "b"); expect(obj).toEqual({ a: 1, c: 2, @@ -31,25 +34,25 @@ describe('Utils', () => { }); }); - it('should not modify the object if the key does not exist', async () => { + it("should not modify the object if the key does not exist", async () => { const obj = { a: 1, e: 4, }; - Utils.addNestedKeysToRoot(obj, 'b'); + Utils.addNestedKeysToRoot(obj, "b"); expect(obj).toEqual({ a: 1, e: 4, }); }); - it('should not modify the object if the key is not an object', () => { + it("should not modify the object if the key is not an object", () => { const obj = { a: 1, b: 2, e: 4, }; - Utils.addNestedKeysToRoot(obj, 'b'); + Utils.addNestedKeysToRoot(obj, "b"); expect(obj).toEqual({ a: 1, b: 2, diff --git a/spec/ValidationAndPasswordsReset.spec.js b/spec/ValidationAndPasswordsReset.spec.js index 5816217a07..0bb52eb504 100644 --- a/spec/ValidationAndPasswordsReset.spec.js +++ b/spec/ValidationAndPasswordsReset.spec.js @@ -1,41 +1,43 @@ -'use strict'; +"use strict"; -const MockEmailAdapterWithOptions = require('./support/MockEmailAdapterWithOptions'); -const request = require('../lib/request'); -const Config = require('../lib/Config'); -const Auth = require('../lib/Auth'); +const MockEmailAdapterWithOptions = require("./support/MockEmailAdapterWithOptions"); +const request = require("../lib/request"); +const Config = require("../lib/Config"); +const Auth = require("../lib/Auth"); -describe('Custom Pages, Email Verification, Password Reset', () => { - it('should set the custom pages', done => { +describe("Custom Pages, Email Verification, Password Reset", () => { + it("should set the custom pages", done => { reconfigureServer({ - appName: 'unused', + appName: "unused", customPages: { - invalidLink: 'myInvalidLink', - verifyEmailSuccess: 'myVerifyEmailSuccess', - choosePassword: 'myChoosePassword', - passwordResetSuccess: 'myPasswordResetSuccess', - parseFrameURL: 'http://example.com/handle-parse-iframe', + invalidLink: "myInvalidLink", + verifyEmailSuccess: "myVerifyEmailSuccess", + choosePassword: "myChoosePassword", + passwordResetSuccess: "myPasswordResetSuccess", + parseFrameURL: "http://example.com/handle-parse-iframe", }, - publicServerURL: 'https://my.public.server.com/1', + publicServerURL: "https://my.public.server.com/1", }).then(() => { - const config = Config.get('test'); - expect(config.invalidLinkURL).toEqual('myInvalidLink'); - expect(config.verifyEmailSuccessURL).toEqual('myVerifyEmailSuccess'); - expect(config.choosePasswordURL).toEqual('myChoosePassword'); - expect(config.passwordResetSuccessURL).toEqual('myPasswordResetSuccess'); - expect(config.parseFrameURL).toEqual('http://example.com/handle-parse-iframe'); + const config = Config.get("test"); + expect(config.invalidLinkURL).toEqual("myInvalidLink"); + expect(config.verifyEmailSuccessURL).toEqual("myVerifyEmailSuccess"); + expect(config.choosePasswordURL).toEqual("myChoosePassword"); + expect(config.passwordResetSuccessURL).toEqual("myPasswordResetSuccess"); + expect(config.parseFrameURL).toEqual( + "http://example.com/handle-parse-iframe" + ); expect(config.verifyEmailURL).toEqual( - 'https://my.public.server.com/1/apps/test/verify_email' + "https://my.public.server.com/1/apps/test/verify_email" ); expect(config.requestResetPasswordURL).toEqual( - 'https://my.public.server.com/1/apps/test/request_password_reset' + "https://my.public.server.com/1/apps/test/request_password_reset" ); done(); }); }); - it_id('5e558687-40f3-496c-9e4f-af6100bd1b2f')(it)( - 'sends verification email if email verification is enabled', + it_id("5e558687-40f3-496c-9e4f-af6100bd1b2f")(it)( + "sends verification email if email verification is enabled", done => { const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), @@ -43,81 +45,81 @@ describe('Custom Pages, Email Verification, Password Reset', () => { sendMail: () => Promise.resolve(), }; reconfigureServer({ - appName: 'unused', + appName: "unused", verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(async () => { - spyOn(emailAdapter, 'sendVerificationEmail'); + spyOn(emailAdapter, "sendVerificationEmail"); const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('zxcv'); - user.setEmail('testIfEnabled@parse.com'); + user.setPassword("asdf"); + user.setUsername("zxcv"); + user.setEmail("testIfEnabled@parse.com"); await user.signUp(); await jasmine.timeout(); expect(emailAdapter.sendVerificationEmail).toHaveBeenCalled(); user.fetch().then(() => { - expect(user.get('emailVerified')).toEqual(false); + expect(user.get("emailVerified")).toEqual(false); done(); }); }); } ); - it('does not send verification email when verification is enabled and email is not set', done => { + it("does not send verification email when verification is enabled and email is not set", done => { const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), sendPasswordResetEmail: () => Promise.resolve(), sendMail: () => Promise.resolve(), }; reconfigureServer({ - appName: 'unused', + appName: "unused", verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(async () => { - spyOn(emailAdapter, 'sendVerificationEmail'); + spyOn(emailAdapter, "sendVerificationEmail"); const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('zxcv'); + user.setPassword("asdf"); + user.setUsername("zxcv"); await user.signUp(); expect(emailAdapter.sendVerificationEmail).not.toHaveBeenCalled(); user.fetch().then(() => { - expect(user.get('emailVerified')).toEqual(undefined); + expect(user.get("emailVerified")).toEqual(undefined); done(); }); }); }); - it('does send a validation email when updating the email', done => { + it("does send a validation email when updating the email", done => { const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), sendPasswordResetEmail: () => Promise.resolve(), sendMail: () => Promise.resolve(), }; reconfigureServer({ - appName: 'unused', + appName: "unused", verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(async () => { - spyOn(emailAdapter, 'sendVerificationEmail'); + spyOn(emailAdapter, "sendVerificationEmail"); const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('zxcv'); + user.setPassword("asdf"); + user.setUsername("zxcv"); await user.signUp(); expect(emailAdapter.sendVerificationEmail).not.toHaveBeenCalled(); user .fetch() .then(user => { - user.set('email', 'testWhenUpdating@parse.com'); + user.set("email", "testWhenUpdating@parse.com"); return user.save(); }) .then(user => { return user.fetch(); }) .then(() => { - expect(user.get('emailVerified')).toEqual(false); + expect(user.get("emailVerified")).toEqual(false); // Wait as on update email, we need to fetch the username setTimeout(function () { expect(emailAdapter.sendVerificationEmail).toHaveBeenCalled(); @@ -127,34 +129,34 @@ describe('Custom Pages, Email Verification, Password Reset', () => { }); }); - it('does send a validation email with valid verification link when updating the email', async done => { + it("does send a validation email with valid verification link when updating the email", async done => { const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), sendPasswordResetEmail: () => Promise.resolve(), sendMail: () => Promise.resolve(), }; await reconfigureServer({ - appName: 'unused', + appName: "unused", verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }); - spyOn(emailAdapter, 'sendVerificationEmail').and.callFake(options => { + spyOn(emailAdapter, "sendVerificationEmail").and.callFake(options => { expect(options.link).not.toBeNull(); expect(options.link).not.toMatch(/token=undefined/); expect(options.link).not.toMatch(/username=undefined/); Promise.resolve(); }); const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('zxcv'); + user.setPassword("asdf"); + user.setUsername("zxcv"); await user.signUp(); expect(emailAdapter.sendVerificationEmail).not.toHaveBeenCalled(); await user.fetch(); - user.set('email', 'testValidLinkWhenUpdating@parse.com'); + user.set("email", "testValidLinkWhenUpdating@parse.com"); await user.save(); await user.fetch(); - expect(user.get('emailVerified')).toEqual(false); + expect(user.get("emailVerified")).toEqual(false); // Wait as on update email, we need to fetch the username setTimeout(function () { expect(emailAdapter.sendVerificationEmail).toHaveBeenCalled(); @@ -162,83 +164,90 @@ describe('Custom Pages, Email Verification, Password Reset', () => { }, 200); }); - it_id('33d31119-c724-4f5d-83ec-f56815d23df3')(it)('does send with a simple adapter', done => { - let calls = 0; - const emailAdapter = { - sendMail: function (options) { - expect(options.to).toBe('testSendSimpleAdapter@parse.com'); - if (calls == 0) { - expect(options.subject).toEqual('Please verify your e-mail for My Cool App'); - expect(options.text.match(/verify_email/)).not.toBe(null); - } else if (calls == 1) { - expect(options.subject).toEqual('Password Reset for My Cool App'); - expect(options.text.match(/request_password_reset/)).not.toBe(null); - } - calls++; - return Promise.resolve(); - }, - }; - reconfigureServer({ - appName: 'My Cool App', - verifyUserEmails: true, - emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', - }).then(async () => { - const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('zxcv'); - user.set('email', 'testSendSimpleAdapter@parse.com'); - await user.signUp(); - await jasmine.timeout(); - expect(calls).toBe(1); - user - .fetch() - .then(user => { - return user.save(); - }) - .then(() => { - return Parse.User.requestPasswordReset('testSendSimpleAdapter@parse.com').catch(() => { - fail('Should not fail requesting a password'); + it_id("33d31119-c724-4f5d-83ec-f56815d23df3")(it)( + "does send with a simple adapter", + done => { + let calls = 0; + const emailAdapter = { + sendMail: function (options) { + expect(options.to).toBe("testSendSimpleAdapter@parse.com"); + if (calls == 0) { + expect(options.subject).toEqual( + "Please verify your e-mail for My Cool App" + ); + expect(options.text.match(/verify_email/)).not.toBe(null); + } else if (calls == 1) { + expect(options.subject).toEqual("Password Reset for My Cool App"); + expect(options.text.match(/request_password_reset/)).not.toBe(null); + } + calls++; + return Promise.resolve(); + }, + }; + reconfigureServer({ + appName: "My Cool App", + verifyUserEmails: true, + emailAdapter: emailAdapter, + publicServerURL: "http://localhost:8378/1", + }).then(async () => { + const user = new Parse.User(); + user.setPassword("asdf"); + user.setUsername("zxcv"); + user.set("email", "testSendSimpleAdapter@parse.com"); + await user.signUp(); + await jasmine.timeout(); + expect(calls).toBe(1); + user + .fetch() + .then(user => { + return user.save(); + }) + .then(() => { + return Parse.User.requestPasswordReset( + "testSendSimpleAdapter@parse.com" + ).catch(() => { + fail("Should not fail requesting a password"); + done(); + }); + }) + .then(() => { + expect(calls).toBe(2); done(); }); - }) - .then(() => { - expect(calls).toBe(2); - done(); - }); - }); - }); + }); + } + ); - it('prevents user from login if email is not verified but preventLoginWithUnverifiedEmail is set to true', done => { + it("prevents user from login if email is not verified but preventLoginWithUnverifiedEmail is set to true", done => { reconfigureServer({ - appName: 'test', - publicServerURL: 'http://localhost:1337/1', + appName: "test", + publicServerURL: "http://localhost:1337/1", verifyUserEmails: true, preventLoginWithUnverifiedEmail: true, emailAdapter: MockEmailAdapterWithOptions({ - fromAddress: 'parse@example.com', - apiKey: 'k', - domain: 'd', + fromAddress: "parse@example.com", + apiKey: "k", + domain: "d", }), }) .then(() => { const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('zxcv'); - user.set('email', 'testInvalidConfig@parse.com'); + user.setPassword("asdf"); + user.setUsername("zxcv"); + user.set("email", "testInvalidConfig@parse.com"); user .signUp(null) .then(user => { expect(user.getSessionToken()).toBe(undefined); - return Parse.User.logIn('zxcv', 'asdf'); + return Parse.User.logIn("zxcv", "asdf"); }) .then( () => { - fail('login should have failed'); + fail("login should have failed"); done(); }, error => { - expect(error.message).toEqual('User email is not verified.'); + expect(error.message).toEqual("User email is not verified."); done(); } ); @@ -249,36 +258,36 @@ describe('Custom Pages, Email Verification, Password Reset', () => { }); }); - it('prevents user from signup and login if email is not verified and preventLoginWithUnverifiedEmail is set to function returning true', async () => { + it("prevents user from signup and login if email is not verified and preventLoginWithUnverifiedEmail is set to function returning true", async () => { await reconfigureServer({ - appName: 'test', - publicServerURL: 'http://localhost:1337/1', + appName: "test", + publicServerURL: "http://localhost:1337/1", verifyUserEmails: async () => true, preventLoginWithUnverifiedEmail: async () => true, preventSignupWithUnverifiedEmail: true, emailAdapter: MockEmailAdapterWithOptions({ - fromAddress: 'parse@example.com', - apiKey: 'k', - domain: 'd', + fromAddress: "parse@example.com", + apiKey: "k", + domain: "d", }), }); const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('zxcv'); - user.set('email', 'testInvalidConfig@parse.com'); + user.setPassword("asdf"); + user.setUsername("zxcv"); + user.set("email", "testInvalidConfig@parse.com"); const signupRes = await user.signUp(null).catch(e => e); - expect(signupRes.message).toEqual('User email is not verified.'); + expect(signupRes.message).toEqual("User email is not verified."); - const loginRes = await Parse.User.logIn('zxcv', 'asdf').catch(e => e); - expect(loginRes.message).toEqual('User email is not verified.'); + const loginRes = await Parse.User.logIn("zxcv", "asdf").catch(e => e); + expect(loginRes.message).toEqual("User email is not verified."); }); - it('provides function arguments in verifyUserEmails on login', async () => { + it("provides function arguments in verifyUserEmails on login", async () => { const user = new Parse.User(); - user.setUsername('user'); - user.setPassword('pass'); - user.set('email', 'test@example.com'); + user.setUsername("user"); + user.setPassword("pass"); + user.set("email", "test@example.com"); await user.signUp(); const verifyUserEmails = { @@ -290,27 +299,30 @@ describe('Custom Pages, Email Verification, Password Reset', () => { return true; }, }; - const verifyUserEmailsSpy = spyOn(verifyUserEmails, 'method').and.callThrough(); + const verifyUserEmailsSpy = spyOn( + verifyUserEmails, + "method" + ).and.callThrough(); await reconfigureServer({ - appName: 'test', - publicServerURL: 'http://localhost:1337/1', + appName: "test", + publicServerURL: "http://localhost:1337/1", verifyUserEmails: verifyUserEmails.method, preventLoginWithUnverifiedEmail: verifyUserEmails.method, preventSignupWithUnverifiedEmail: true, emailAdapter: MockEmailAdapterWithOptions({ - fromAddress: 'parse@example.com', - apiKey: 'k', - domain: 'd', + fromAddress: "parse@example.com", + apiKey: "k", + domain: "d", }), }); - const res = await Parse.User.logIn('user', 'pass').catch(e => e); + const res = await Parse.User.logIn("user", "pass").catch(e => e); expect(res.code).toBe(205); expect(verifyUserEmailsSpy).toHaveBeenCalledTimes(2); }); - it_id('2a5d24be-2ca5-4385-b580-1423bd392e43')(it)( - 'allows user to login only after user clicks on the link to confirm email address if preventLoginWithUnverifiedEmail is set to true', + it_id("2a5d24be-2ca5-4385-b580-1423bd392e43")(it)( + "allows user to login only after user clicks on the link to confirm email address if preventLoginWithUnverifiedEmail is set to true", async () => { let sendEmailOptions; const emailAdapter = { @@ -321,16 +333,16 @@ describe('Custom Pages, Email Verification, Password Reset', () => { sendMail: () => {}, }; await reconfigureServer({ - appName: 'emailing app', + appName: "emailing app", verifyUserEmails: true, preventLoginWithUnverifiedEmail: true, emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }); let user = new Parse.User(); - user.setPassword('other-password'); - user.setUsername('user'); - user.set('email', 'user@example.com'); + user.setPassword("other-password"); + user.setUsername("user"); + user.set("email", "user@example.com"); await user.signUp(); await jasmine.timeout(); expect(sendEmailOptions).not.toBeUndefined(); @@ -340,44 +352,44 @@ describe('Custom Pages, Email Verification, Password Reset', () => { }); expect(response.status).toEqual(302); expect(response.text).toEqual( - 'Found. Redirecting to http://localhost:8378/1/apps/verify_email_success.html' + "Found. Redirecting to http://localhost:8378/1/apps/verify_email_success.html" ); user = await new Parse.Query(Parse.User).first({ useMasterKey: true }); - expect(user.get('emailVerified')).toEqual(true); - user = await Parse.User.logIn('user', 'other-password'); - expect(typeof user).toBe('object'); - expect(user.get('emailVerified')).toBe(true); + expect(user.get("emailVerified")).toEqual(true); + user = await Parse.User.logIn("user", "other-password"); + expect(typeof user).toBe("object"); + expect(user.get("emailVerified")).toBe(true); } ); - it('allows user to login if email is not verified but preventLoginWithUnverifiedEmail is set to false', done => { + it("allows user to login if email is not verified but preventLoginWithUnverifiedEmail is set to false", done => { reconfigureServer({ - appName: 'test', - publicServerURL: 'http://localhost:1337/1', + appName: "test", + publicServerURL: "http://localhost:1337/1", verifyUserEmails: true, preventLoginWithUnverifiedEmail: false, emailAdapter: MockEmailAdapterWithOptions({ - fromAddress: 'parse@example.com', - apiKey: 'k', - domain: 'd', + fromAddress: "parse@example.com", + apiKey: "k", + domain: "d", }), }) .then(() => { const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('zxcv'); - user.set('email', 'testInvalidConfig@parse.com'); + user.setPassword("asdf"); + user.setUsername("zxcv"); + user.set("email", "testInvalidConfig@parse.com"); user .signUp(null) - .then(() => Parse.User.logIn('zxcv', 'asdf')) + .then(() => Parse.User.logIn("zxcv", "asdf")) .then( user => { - expect(typeof user).toBe('object'); - expect(user.get('emailVerified')).toBe(false); + expect(typeof user).toBe("object"); + expect(user.get("emailVerified")).toBe(false); done(); }, () => { - fail('login should have succeeded'); + fail("login should have succeeded"); done(); } ); @@ -388,8 +400,8 @@ describe('Custom Pages, Email Verification, Password Reset', () => { }); }); - it_id('a18a07af-0319-4f15-8237-28070c5948fa')(it)( - 'does not allow signup with preventSignupWithUnverified', + it_id("a18a07af-0319-4f15-8237-28070c5948fa")(it)( + "does not allow signup with preventSignupWithUnverified", async () => { let sendEmailOptions; const emailAdapter = { @@ -400,52 +412,59 @@ describe('Custom Pages, Email Verification, Password Reset', () => { sendMail: () => {}, }; await reconfigureServer({ - appName: 'test', - publicServerURL: 'http://localhost:1337/1', + appName: "test", + publicServerURL: "http://localhost:1337/1", verifyUserEmails: true, preventLoginWithUnverifiedEmail: true, preventSignupWithUnverifiedEmail: true, emailAdapter, }); const newUser = new Parse.User(); - newUser.setPassword('asdf'); - newUser.setUsername('zxcv'); - newUser.set('email', 'test@example.com'); + newUser.setPassword("asdf"); + newUser.setUsername("zxcv"); + newUser.set("email", "test@example.com"); await expectAsync(newUser.signUp()).toBeRejectedWith( - new Parse.Error(Parse.Error.EMAIL_NOT_FOUND, 'User email is not verified.') + new Parse.Error( + Parse.Error.EMAIL_NOT_FOUND, + "User email is not verified." + ) ); - const user = await new Parse.Query(Parse.User).first({ useMasterKey: true }); + const user = await new Parse.Query(Parse.User).first({ + useMasterKey: true, + }); expect(user).toBeDefined(); expect(sendEmailOptions).toBeDefined(); } ); - it('fails if you include an emailAdapter, set a publicServerURL, but have no appName and send a password reset email', done => { + it("fails if you include an emailAdapter, set a publicServerURL, but have no appName and send a password reset email", done => { reconfigureServer({ appName: undefined, - publicServerURL: 'http://localhost:1337/1', + publicServerURL: "http://localhost:1337/1", emailAdapter: MockEmailAdapterWithOptions({ - fromAddress: 'parse@example.com', - apiKey: 'k', - domain: 'd', + fromAddress: "parse@example.com", + apiKey: "k", + domain: "d", }), }) .then(() => { const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('zxcv'); - user.set('email', 'testInvalidConfig@parse.com'); + user.setPassword("asdf"); + user.setUsername("zxcv"); + user.set("email", "testInvalidConfig@parse.com"); user .signUp(null) - .then(() => Parse.User.requestPasswordReset('testInvalidConfig@parse.com')) + .then(() => + Parse.User.requestPasswordReset("testInvalidConfig@parse.com") + ) .then( () => { - fail('sending password reset email should not have succeeded'); + fail("sending password reset email should not have succeeded"); done(); }, error => { expect(error.message).toEqual( - 'An appName, publicServerURL, and emailAdapter are required for password reset and email verification functionality.' + "An appName, publicServerURL, and emailAdapter are required for password reset and email verification functionality." ); done(); } @@ -457,31 +476,33 @@ describe('Custom Pages, Email Verification, Password Reset', () => { }); }); - it('fails if you include an emailAdapter, have an appName, but have no publicServerURL and send a password reset email', done => { + it("fails if you include an emailAdapter, have an appName, but have no publicServerURL and send a password reset email", done => { reconfigureServer({ appName: undefined, emailAdapter: MockEmailAdapterWithOptions({ - fromAddress: 'parse@example.com', - apiKey: 'k', - domain: 'd', + fromAddress: "parse@example.com", + apiKey: "k", + domain: "d", }), }) .then(() => { const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('zxcv'); - user.set('email', 'testInvalidConfig@parse.com'); + user.setPassword("asdf"); + user.setUsername("zxcv"); + user.set("email", "testInvalidConfig@parse.com"); user .signUp(null) - .then(() => Parse.User.requestPasswordReset('testInvalidConfig@parse.com')) + .then(() => + Parse.User.requestPasswordReset("testInvalidConfig@parse.com") + ) .then( () => { - fail('sending password reset email should not have succeeded'); + fail("sending password reset email should not have succeeded"); done(); }, error => { expect(error.message).toEqual( - 'An appName, publicServerURL, and emailAdapter are required for password reset and email verification functionality.' + "An appName, publicServerURL, and emailAdapter are required for password reset and email verification functionality." ); done(); } @@ -493,28 +514,30 @@ describe('Custom Pages, Email Verification, Password Reset', () => { }); }); - it('fails if you set a publicServerURL, have an appName, but no emailAdapter and send a password reset email', done => { + it("fails if you set a publicServerURL, have an appName, but no emailAdapter and send a password reset email", done => { reconfigureServer({ - appName: 'unused', - publicServerURL: 'http://localhost:1337/1', + appName: "unused", + publicServerURL: "http://localhost:1337/1", emailAdapter: undefined, }) .then(() => { const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('zxcv'); - user.set('email', 'testInvalidConfig@parse.com'); + user.setPassword("asdf"); + user.setUsername("zxcv"); + user.set("email", "testInvalidConfig@parse.com"); user .signUp(null) - .then(() => Parse.User.requestPasswordReset('testInvalidConfig@parse.com')) + .then(() => + Parse.User.requestPasswordReset("testInvalidConfig@parse.com") + ) .then( () => { - fail('sending password reset email should not have succeeded'); + fail("sending password reset email should not have succeeded"); done(); }, error => { expect(error.message).toEqual( - 'An appName, publicServerURL, and emailAdapter are required for password reset and email verification functionality.' + "An appName, publicServerURL, and emailAdapter are required for password reset and email verification functionality." ); done(); } @@ -526,24 +549,26 @@ describe('Custom Pages, Email Verification, Password Reset', () => { }); }); - it('succeeds sending a password reset email if appName, publicServerURL, and email adapter are provided', done => { + it("succeeds sending a password reset email if appName, publicServerURL, and email adapter are provided", done => { reconfigureServer({ - appName: 'coolapp', - publicServerURL: 'http://localhost:1337/1', + appName: "coolapp", + publicServerURL: "http://localhost:1337/1", emailAdapter: MockEmailAdapterWithOptions({ - fromAddress: 'parse@example.com', - apiKey: 'k', - domain: 'd', + fromAddress: "parse@example.com", + apiKey: "k", + domain: "d", }), }) .then(() => { const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('zxcv'); - user.set('email', 'testInvalidConfig@parse.com'); + user.setPassword("asdf"); + user.setUsername("zxcv"); + user.set("email", "testInvalidConfig@parse.com"); user .signUp(null) - .then(() => Parse.User.requestPasswordReset('testInvalidConfig@parse.com')) + .then(() => + Parse.User.requestPasswordReset("testInvalidConfig@parse.com") + ) .then( () => { done(); @@ -559,13 +584,13 @@ describe('Custom Pages, Email Verification, Password Reset', () => { }); }); - it('succeeds sending a password reset username if appName, publicServerURL, and email adapter are provided', done => { + it("succeeds sending a password reset username if appName, publicServerURL, and email adapter are provided", done => { const adapter = MockEmailAdapterWithOptions({ - fromAddress: 'parse@example.com', - apiKey: 'k', - domain: 'd', + fromAddress: "parse@example.com", + apiKey: "k", + domain: "d", sendMail: function (options) { - expect(options.to).toEqual('testValidConfig@parse.com'); + expect(options.to).toEqual("testValidConfig@parse.com"); return Promise.resolve(); }, }); @@ -573,19 +598,21 @@ describe('Custom Pages, Email Verification, Password Reset', () => { // delete that handler to force using the default delete adapter.sendPasswordResetEmail; - spyOn(adapter, 'sendMail').and.callThrough(); + spyOn(adapter, "sendMail").and.callThrough(); reconfigureServer({ - appName: 'coolapp', - publicServerURL: 'http://localhost:1337/1', + appName: "coolapp", + publicServerURL: "http://localhost:1337/1", emailAdapter: adapter, }) .then(() => { const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('testValidConfig@parse.com'); + user.setPassword("asdf"); + user.setUsername("testValidConfig@parse.com"); user .signUp(null) - .then(() => Parse.User.requestPasswordReset('testValidConfig@parse.com')) + .then(() => + Parse.User.requestPasswordReset("testValidConfig@parse.com") + ) .then( () => { expect(adapter.sendMail).toHaveBeenCalled(); @@ -602,53 +629,53 @@ describe('Custom Pages, Email Verification, Password Reset', () => { }); }); - it('does not send verification email if email verification is disabled', done => { + it("does not send verification email if email verification is disabled", done => { const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), sendPasswordResetEmail: () => Promise.resolve(), sendMail: () => Promise.resolve(), }; reconfigureServer({ - appName: 'unused', - publicServerURL: 'http://localhost:1337/1', + appName: "unused", + publicServerURL: "http://localhost:1337/1", verifyUserEmails: false, emailAdapter: emailAdapter, }).then(async () => { - spyOn(emailAdapter, 'sendVerificationEmail'); + spyOn(emailAdapter, "sendVerificationEmail"); const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('zxcv'); + user.setPassword("asdf"); + user.setUsername("zxcv"); await user.signUp(); await user.fetch(); expect(emailAdapter.sendVerificationEmail.calls.count()).toEqual(0); - expect(user.get('emailVerified')).toEqual(undefined); + expect(user.get("emailVerified")).toEqual(undefined); done(); }); }); - it_id('45f550a2-a2b2-4b2b-b533-ccbf96139cc9')(it)( - 'receives the app name and user in the adapter', + it_id("45f550a2-a2b2-4b2b-b533-ccbf96139cc9")(it)( + "receives the app name and user in the adapter", done => { let emailSent = false; const emailAdapter = { sendVerificationEmail: options => { - expect(options.appName).toEqual('emailing app'); - expect(options.user.get('email')).toEqual('user@parse.com'); + expect(options.appName).toEqual("emailing app"); + expect(options.user.get("email")).toEqual("user@parse.com"); emailSent = true; }, sendPasswordResetEmail: () => Promise.resolve(), sendMail: () => {}, }; reconfigureServer({ - appName: 'emailing app', + appName: "emailing app", verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(async () => { const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('zxcv'); - user.set('email', 'user@parse.com'); + user.setPassword("asdf"); + user.setUsername("zxcv"); + user.set("email", "user@parse.com"); await user.signUp(); await jasmine.timeout(); expect(emailSent).toBe(true); @@ -657,8 +684,8 @@ describe('Custom Pages, Email Verification, Password Reset', () => { } ); - it_id('ea37ef62-aad8-4a17-8dfe-35e5b2986f0f')(it)( - 'when you click the link in the email it sets emailVerified to true and redirects you', + it_id("ea37ef62-aad8-4a17-8dfe-35e5b2986f0f")(it)( + "when you click the link in the email it sets emailVerified to true and redirects you", done => { const user = new Parse.User(); let sendEmailOptions; @@ -670,15 +697,15 @@ describe('Custom Pages, Email Verification, Password Reset', () => { sendMail: () => {}, }; reconfigureServer({ - appName: 'emailing app', + appName: "emailing app", verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }) .then(() => { - user.setPassword('other-password'); - user.setUsername('user'); - user.set('email', 'user@parse.com'); + user.setPassword("other-password"); + user.setUsername("user"); + user.set("email", "user@parse.com"); return user.signUp(); }) .then(() => jasmine.timeout()) @@ -690,18 +717,18 @@ describe('Custom Pages, Email Verification, Password Reset', () => { }).then(response => { expect(response.status).toEqual(302); expect(response.text).toEqual( - 'Found. Redirecting to http://localhost:8378/1/apps/verify_email_success.html' + "Found. Redirecting to http://localhost:8378/1/apps/verify_email_success.html" ); user .fetch() .then( () => { - expect(user.get('emailVerified')).toEqual(true); + expect(user.get("emailVerified")).toEqual(true); done(); }, err => { jfail(err); - fail('this should not fail'); + fail("this should not fail"); done(); } ) @@ -714,96 +741,96 @@ describe('Custom Pages, Email Verification, Password Reset', () => { } ); - it('redirects you to invalid link if you try to verify email incorrectly', done => { + it("redirects you to invalid link if you try to verify email incorrectly", done => { reconfigureServer({ - appName: 'emailing app', + appName: "emailing app", verifyUserEmails: true, emailAdapter: { sendVerificationEmail: () => Promise.resolve(), sendPasswordResetEmail: () => Promise.resolve(), sendMail: () => {}, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { request({ - url: 'http://localhost:8378/1/apps/test/verify_email', + url: "http://localhost:8378/1/apps/test/verify_email", followRedirects: false, }).then(response => { expect(response.status).toEqual(302); expect(response.text).toEqual( - 'Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html' + "Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html" ); done(); }); }); }); - it('redirects you to invalid verification link page if you try to validate a nonexistant users email', done => { + it("redirects you to invalid verification link page if you try to validate a nonexistant users email", done => { reconfigureServer({ - appName: 'emailing app', + appName: "emailing app", verifyUserEmails: true, emailAdapter: { sendVerificationEmail: () => Promise.resolve(), sendPasswordResetEmail: () => Promise.resolve(), sendMail: () => {}, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { request({ - url: 'http://localhost:8378/1/apps/test/verify_email?token=asdfasdf', + url: "http://localhost:8378/1/apps/test/verify_email?token=asdfasdf", followRedirects: false, }).then(response => { expect(response.status).toEqual(302); expect(response.text).toEqual( - 'Found. Redirecting to http://localhost:8378/1/apps/invalid_verification_link.html?appId=test&token=asdfasdf' + "Found. Redirecting to http://localhost:8378/1/apps/invalid_verification_link.html?appId=test&token=asdfasdf" ); done(); }); }); }); - it('redirects you to link send fail page if you try to resend a link for a nonexistant user', done => { + it("redirects you to link send fail page if you try to resend a link for a nonexistant user", done => { reconfigureServer({ - appName: 'emailing app', + appName: "emailing app", verifyUserEmails: true, emailAdapter: { sendVerificationEmail: () => Promise.resolve(), sendPasswordResetEmail: () => Promise.resolve(), sendMail: () => {}, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { request({ - url: 'http://localhost:8378/1/apps/test/resend_verification_email', - method: 'POST', + url: "http://localhost:8378/1/apps/test/resend_verification_email", + method: "POST", followRedirects: false, body: { - username: 'sadfasga', + username: "sadfasga", }, }).then(response => { expect(response.status).toEqual(302); expect(response.text).toEqual( - 'Found. Redirecting to http://localhost:8378/1/apps/link_send_fail.html' + "Found. Redirecting to http://localhost:8378/1/apps/link_send_fail.html" ); done(); }); }); }); - it('does not update email verified if you use an invalid token', done => { + it("does not update email verified if you use an invalid token", done => { const user = new Parse.User(); const emailAdapter = { sendVerificationEmail: () => { request({ - url: 'http://localhost:8378/1/apps/test/verify_email?token=invalid', + url: "http://localhost:8378/1/apps/test/verify_email?token=invalid", followRedirects: false, }).then(response => { expect(response.status).toEqual(302); expect(response.text).toEqual( - 'Found. Redirecting to http://localhost:8378/1/apps/invalid_verification_link.html?appId=test&token=invalid' + "Found. Redirecting to http://localhost:8378/1/apps/invalid_verification_link.html?appId=test&token=invalid" ); user.fetch().then(() => { - expect(user.get('emailVerified')).toEqual(false); + expect(user.get("emailVerified")).toEqual(false); done(); }); }); @@ -812,25 +839,25 @@ describe('Custom Pages, Email Verification, Password Reset', () => { sendMail: () => {}, }; reconfigureServer({ - appName: 'emailing app', + appName: "emailing app", verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { - user.setPassword('asdf'); - user.setUsername('zxcv'); - user.set('email', 'user@parse.com'); + user.setPassword("asdf"); + user.setUsername("zxcv"); + user.set("email", "user@parse.com"); user.signUp(null, { success: () => {}, error: function () { - fail('Failed to save user'); + fail("Failed to save user"); done(); }, }); }); }); - it('should send a password reset link', done => { + it("should send a password reset link", done => { const user = new Parse.User(); const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), @@ -849,19 +876,19 @@ describe('Custom Pages, Email Verification, Password Reset', () => { sendMail: () => {}, }; reconfigureServer({ - appName: 'emailing app', + appName: "emailing app", verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { - user.setPassword('asdf'); - user.setUsername('zxcv+zxcv'); - user.set('email', 'user@parse.com'); + user.setPassword("asdf"); + user.setUsername("zxcv+zxcv"); + user.set("email", "user@parse.com"); user.signUp().then(() => { - Parse.User.requestPasswordReset('user@parse.com', { + Parse.User.requestPasswordReset("user@parse.com", { error: err => { jfail(err); - fail('Should not fail requesting a password'); + fail("Should not fail requesting a password"); done(); }, }); @@ -869,31 +896,31 @@ describe('Custom Pages, Email Verification, Password Reset', () => { }); }); - it('redirects you to invalid link if you try to request password for a nonexistant users email', done => { + it("redirects you to invalid link if you try to request password for a nonexistant users email", done => { reconfigureServer({ - appName: 'emailing app', + appName: "emailing app", verifyUserEmails: true, emailAdapter: { sendVerificationEmail: () => Promise.resolve(), sendPasswordResetEmail: () => Promise.resolve(), sendMail: () => {}, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { request({ - url: 'http://localhost:8378/1/apps/test/request_password_reset?token=asdfasdf', + url: "http://localhost:8378/1/apps/test/request_password_reset?token=asdfasdf", followRedirects: false, }).then(response => { expect(response.status).toEqual(302); expect(response.text).toEqual( - 'Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html' + "Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html" ); done(); }); }); }); - it('should programmatically reset password', done => { + it("should programmatically reset password", done => { const user = new Parse.User(); const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), @@ -907,41 +934,46 @@ describe('Custom Pages, Email Verification, Password Reset', () => { /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; const match = response.text.match(re); if (!match) { - fail('should have a token'); + fail("should have a token"); done(); return; } const token = match[1]; request({ - url: 'http://localhost:8378/1/apps/test/request_password_reset', - method: 'POST', - body: { new_password: 'hello', token, username: 'zxcv' }, + url: "http://localhost:8378/1/apps/test/request_password_reset", + method: "POST", + body: { new_password: "hello", token, username: "zxcv" }, headers: { - 'Content-Type': 'application/x-www-form-urlencoded', + "Content-Type": "application/x-www-form-urlencoded", }, followRedirects: false, }).then(response => { expect(response.status).toEqual(302); expect(response.text).toEqual( - 'Found. Redirecting to http://localhost:8378/1/apps/password_reset_success.html' + "Found. Redirecting to http://localhost:8378/1/apps/password_reset_success.html" ); - Parse.User.logIn('zxcv', 'hello').then( + Parse.User.logIn("zxcv", "hello").then( function () { - const config = Config.get('test'); + const config = Config.get("test"); config.database.adapter - .find('_User', { fields: {} }, { username: 'zxcv' }, { limit: 1 }) + .find( + "_User", + { fields: {} }, + { username: "zxcv" }, + { limit: 1 } + ) .then(results => { // _perishable_token should be unset after reset password expect(results.length).toEqual(1); - expect(results[0]['_perishable_token']).toEqual(undefined); + expect(results[0]["_perishable_token"]).toEqual(undefined); done(); }); }, err => { jfail(err); - fail('should login with new password'); + fail("should login with new password"); done(); } ); @@ -951,19 +983,19 @@ describe('Custom Pages, Email Verification, Password Reset', () => { sendMail: () => {}, }; reconfigureServer({ - appName: 'emailing app', + appName: "emailing app", verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { - user.setPassword('asdf'); - user.setUsername('zxcv'); - user.set('email', 'user@parse.com'); + user.setPassword("asdf"); + user.setUsername("zxcv"); + user.set("email", "user@parse.com"); user.signUp().then(() => { - Parse.User.requestPasswordReset('user@parse.com', { + Parse.User.requestPasswordReset("user@parse.com", { error: err => { jfail(err); - fail('Should not fail'); + fail("Should not fail"); done(); }, }); @@ -971,7 +1003,7 @@ describe('Custom Pages, Email Verification, Password Reset', () => { }); }); - it('should redirect with username encoded on success page', done => { + it("should redirect with username encoded on success page", done => { const user = new Parse.User(); const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), @@ -985,24 +1017,24 @@ describe('Custom Pages, Email Verification, Password Reset', () => { /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; const match = response.text.match(re); if (!match) { - fail('should have a token'); + fail("should have a token"); done(); return; } const token = match[1]; request({ - url: 'http://localhost:8378/1/apps/test/request_password_reset', - method: 'POST', - body: { new_password: 'hello', token, username: 'zxcv+1' }, + url: "http://localhost:8378/1/apps/test/request_password_reset", + method: "POST", + body: { new_password: "hello", token, username: "zxcv+1" }, headers: { - 'Content-Type': 'application/x-www-form-urlencoded', + "Content-Type": "application/x-www-form-urlencoded", }, followRedirects: false, }).then(response => { expect(response.status).toEqual(302); expect(response.text).toEqual( - 'Found. Redirecting to http://localhost:8378/1/apps/password_reset_success.html' + "Found. Redirecting to http://localhost:8378/1/apps/password_reset_success.html" ); done(); }); @@ -1011,19 +1043,19 @@ describe('Custom Pages, Email Verification, Password Reset', () => { sendMail: () => {}, }; reconfigureServer({ - appName: 'emailing app', + appName: "emailing app", verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(() => { - user.setPassword('asdf'); - user.setUsername('zxcv+1'); - user.set('email', 'user@parse.com'); + user.setPassword("asdf"); + user.setUsername("zxcv+1"); + user.set("email", "user@parse.com"); user.signUp().then(() => { - Parse.User.requestPasswordReset('user@parse.com', { + Parse.User.requestPasswordReset("user@parse.com", { error: err => { jfail(err); - fail('Should not fail'); + fail("Should not fail"); done(); }, }); @@ -1031,7 +1063,7 @@ describe('Custom Pages, Email Verification, Password Reset', () => { }); }); - it('should programmatically reset password on ajax request', async done => { + it("should programmatically reset password on ajax request", async done => { const user = new Parse.User(); const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), @@ -1045,65 +1077,65 @@ describe('Custom Pages, Email Verification, Password Reset', () => { /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; const match = response.text.match(re); if (!match) { - fail('should have a token'); + fail("should have a token"); return; } const token = match[1]; const resetResponse = await request({ - url: 'http://localhost:8378/1/apps/test/request_password_reset', - method: 'POST', - body: { new_password: 'hello', token, username: 'zxcv' }, + url: "http://localhost:8378/1/apps/test/request_password_reset", + method: "POST", + body: { new_password: "hello", token, username: "zxcv" }, headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'X-Requested-With': 'XMLHttpRequest', + "Content-Type": "application/x-www-form-urlencoded", + "X-Requested-With": "XMLHttpRequest", }, followRedirects: false, }); expect(resetResponse.status).toEqual(200); expect(resetResponse.text).toEqual('"Password successfully reset"'); - await Parse.User.logIn('zxcv', 'hello'); - const config = Config.get('test'); + await Parse.User.logIn("zxcv", "hello"); + const config = Config.get("test"); const results = await config.database.adapter.find( - '_User', + "_User", { fields: {} }, - { username: 'zxcv' }, + { username: "zxcv" }, { limit: 1 } ); // _perishable_token should be unset after reset password expect(results.length).toEqual(1); - expect(results[0]['_perishable_token']).toEqual(undefined); + expect(results[0]["_perishable_token"]).toEqual(undefined); done(); }, sendMail: () => {}, }; await reconfigureServer({ - appName: 'emailing app', + appName: "emailing app", verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }); - user.setPassword('asdf'); - user.setUsername('zxcv'); - user.set('email', 'user@parse.com'); + user.setPassword("asdf"); + user.setUsername("zxcv"); + user.set("email", "user@parse.com"); await user.signUp(); - await Parse.User.requestPasswordReset('user@parse.com'); + await Parse.User.requestPasswordReset("user@parse.com"); }); - it('should return ajax failure error on ajax request with wrong data provided', async () => { + it("should return ajax failure error on ajax request with wrong data provided", async () => { await reconfigureServer({ - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }); try { await request({ - method: 'POST', - url: 'http://localhost:8378/1/apps/test/request_password_reset', + method: "POST", + url: "http://localhost:8378/1/apps/test/request_password_reset", body: `new_password=user1&token=12345`, headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'X-Requested-With': 'XMLHttpRequest', + "Content-Type": "application/x-www-form-urlencoded", + "X-Requested-With": "XMLHttpRequest", }, followRedirects: false, }); @@ -1115,51 +1147,51 @@ describe('Custom Pages, Email Verification, Password Reset', () => { } }); - it('deletes password reset token on email address change', done => { + it("deletes password reset token on email address change", done => { reconfigureServer({ - appName: 'coolapp', - publicServerURL: 'http://localhost:1337/1', + appName: "coolapp", + publicServerURL: "http://localhost:1337/1", emailAdapter: MockEmailAdapterWithOptions({ - fromAddress: 'parse@example.com', - apiKey: 'k', - domain: 'd', + fromAddress: "parse@example.com", + apiKey: "k", + domain: "d", }), }) .then(() => { - const config = Config.get('test'); + const config = Config.get("test"); const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('zxcv'); - user.set('email', 'test@parse.com'); + user.setPassword("asdf"); + user.setUsername("zxcv"); + user.set("email", "test@parse.com"); return user .signUp(null) - .then(() => Parse.User.requestPasswordReset('test@parse.com')) + .then(() => Parse.User.requestPasswordReset("test@parse.com")) .then(() => config.database.adapter.find( - '_User', + "_User", { fields: {} }, - { username: 'zxcv' }, + { username: "zxcv" }, { limit: 1 } ) ) .then(results => { // validate that there is a token expect(results.length).toEqual(1); - expect(results[0]['_perishable_token']).not.toBeNull(); - user.set('email', 'test2@parse.com'); + expect(results[0]["_perishable_token"]).not.toBeNull(); + user.set("email", "test2@parse.com"); return user.save(); }) .then(() => config.database.adapter.find( - '_User', + "_User", { fields: {} }, - { username: 'zxcv' }, + { username: "zxcv" }, { limit: 1 } ) ) .then(results => { expect(results.length).toEqual(1); - expect(results[0]['_perishable_token']).toBeUndefined(); + expect(results[0]["_perishable_token"]).toBeUndefined(); done(); }); }) @@ -1169,7 +1201,7 @@ describe('Custom Pages, Email Verification, Password Reset', () => { }); }); - it('can resend email using an expired reset password token', async () => { + it("can resend email using an expired reset password token", async () => { const user = new Parse.User(); const emailAdapter = { sendVerificationEmail: () => {}, @@ -1177,32 +1209,32 @@ describe('Custom Pages, Email Verification, Password Reset', () => { sendMail: () => {}, }; await reconfigureServer({ - appName: 'emailVerifyToken', + appName: "emailVerifyToken", verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", passwordPolicy: { resetTokenValidityDuration: 5 * 60, // 5 minutes }, silent: false, }); - user.setUsername('test'); - user.setPassword('password'); - user.set('email', 'user@example.com'); + user.setUsername("test"); + user.setPassword("password"); + user.set("email", "user@example.com"); await user.signUp(); - await Parse.User.requestPasswordReset('user@example.com'); + await Parse.User.requestPasswordReset("user@example.com"); await Parse.Server.database.update( - '_User', + "_User", { objectId: user.id }, { - _perishable_token_expires_at: Parse._encode(new Date('2000')), + _perishable_token_expires_at: Parse._encode(new Date("2000")), } ); let obj = await Parse.Server.database.find( - '_User', + "_User", { objectId: user.id }, {}, Auth.maintenance(Parse.Server) @@ -1210,10 +1242,10 @@ describe('Custom Pages, Email Verification, Password Reset', () => { const token = obj[0]._perishable_token; const res = await request({ url: `http://localhost:8378/1/apps/test/request_password_reset`, - method: 'POST', + method: "POST", body: { token, - new_password: 'newpassword', + new_password: "newpassword", }, }); expect(res.text).toEqual( @@ -1222,19 +1254,19 @@ describe('Custom Pages, Email Verification, Password Reset', () => { await request({ url: `http://localhost:8378/1/requestPasswordReset`, - method: 'POST', + method: "POST", body: { token: token, }, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }, }); obj = await Parse.Server.database.find( - '_User', + "_User", { objectId: user.id }, {}, Auth.maintenance(Parse.Server) @@ -1243,42 +1275,49 @@ describe('Custom Pages, Email Verification, Password Reset', () => { expect(obj._perishable_token).not.toBe(token); }); - it('should throw on an invalid reset password', async () => { + it("should throw on an invalid reset password", async () => { await reconfigureServer({ - appName: 'coolapp', - publicServerURL: 'http://localhost:1337/1', + appName: "coolapp", + publicServerURL: "http://localhost:1337/1", emailAdapter: MockEmailAdapterWithOptions({ - fromAddress: 'parse@example.com', - apiKey: 'k', - domain: 'd', + fromAddress: "parse@example.com", + apiKey: "k", + domain: "d", }), passwordPolicy: { resetPasswordSuccessOnInvalidEmail: false, }, }); - await expectAsync(Parse.User.requestPasswordReset('test@example.com')).toBeRejectedWith( - new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'A user with that email does not exist.') + await expectAsync( + Parse.User.requestPasswordReset("test@example.com") + ).toBeRejectedWith( + new Parse.Error( + Parse.Error.OBJECT_NOT_FOUND, + "A user with that email does not exist." + ) ); }); - it('validate resetPasswordSuccessonInvalidEmail', async () => { - const invalidValues = [[], {}, 1, 'string']; + it("validate resetPasswordSuccessonInvalidEmail", async () => { + const invalidValues = [[], {}, 1, "string"]; for (const value of invalidValues) { await expectAsync( reconfigureServer({ - appName: 'coolapp', - publicServerURL: 'http://localhost:1337/1', + appName: "coolapp", + publicServerURL: "http://localhost:1337/1", emailAdapter: MockEmailAdapterWithOptions({ - fromAddress: 'parse@example.com', - apiKey: 'k', - domain: 'd', + fromAddress: "parse@example.com", + apiKey: "k", + domain: "d", }), passwordPolicy: { resetPasswordSuccessOnInvalidEmail: value, }, }) - ).toBeRejectedWith('resetPasswordSuccessOnInvalidEmail must be a boolean value'); + ).toBeRejectedWith( + "resetPasswordSuccessOnInvalidEmail must be a boolean value" + ); } }); }); diff --git a/spec/VerifyUserPassword.spec.js b/spec/VerifyUserPassword.spec.js index 3d15a25e15..5aae756e70 100644 --- a/spec/VerifyUserPassword.spec.js +++ b/spec/VerifyUserPassword.spec.js @@ -1,15 +1,17 @@ -'use strict'; +"use strict"; -const request = require('../lib/request'); -const MockEmailAdapterWithOptions = require('./support/MockEmailAdapterWithOptions'); +const request = require("../lib/request"); +const MockEmailAdapterWithOptions = require("./support/MockEmailAdapterWithOptions"); const verifyPassword = function (login, password, isEmail = false) { - const body = !isEmail ? { username: login, password } : { email: login, password }; + const body = !isEmail + ? { username: login, password } + : { email: login, password }; return request({ - url: Parse.serverURL + '/verifyPassword', + url: Parse.serverURL + "/verifyPassword", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", }, qs: body, }) @@ -17,17 +19,22 @@ const verifyPassword = function (login, password, isEmail = false) { .catch(err => err); }; -const isAccountLockoutError = function (username, password, duration, waitTime) { +const isAccountLockoutError = function ( + username, + password, + duration, + waitTime +) { return new Promise((resolve, reject) => { setTimeout(() => { Parse.User.logIn(username, password) - .then(() => reject('login should have failed')) + .then(() => reject("login should have failed")) .catch(err => { if ( err.message === - 'Your account is locked due to multiple failed login attempts. Please try again after ' + + "Your account is locked due to multiple failed login attempts. Please try again after " + duration + - ' minute(s)' + " minute(s)" ) { resolve(); } else { @@ -38,22 +45,22 @@ const isAccountLockoutError = function (username, password, duration, waitTime) }); }; -describe('Verify User Password', () => { - it('fails to verify password when masterKey has locked out user', done => { +describe("Verify User Password", () => { + it("fails to verify password when masterKey has locked out user", done => { const user = new Parse.User(); const ACL = new Parse.ACL(); ACL.setPublicReadAccess(false); ACL.setPublicWriteAccess(false); - user.setUsername('testuser'); - user.setPassword('mypass'); + user.setUsername("testuser"); + user.setPassword("mypass"); user.setACL(ACL); user .signUp() .then(() => { - return Parse.User.logIn('testuser', 'mypass'); + return Parse.User.logIn("testuser", "mypass"); }) .then(user => { - equal(user.get('username'), 'testuser'); + equal(user.get("username"), "testuser"); // Lock the user down const ACL = new Parse.ACL(); user.setACL(ACL); @@ -62,14 +69,14 @@ describe('Verify User Password', () => { .then(() => { expect(user.getACL().getPublicReadAccess()).toBe(false); return request({ - url: Parse.serverURL + '/verifyPassword', + url: Parse.serverURL + "/verifyPassword", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", }, qs: { - username: 'testuser', - password: 'mypass', + username: "testuser", + password: "mypass", }, }); }) @@ -85,24 +92,24 @@ describe('Verify User Password', () => { done(); }); }); - it('fails to verify password when username is not provided in query string REST API', done => { + it("fails to verify password when username is not provided in query string REST API", done => { const user = new Parse.User(); user .save({ - username: 'testuser', - password: 'mypass', - email: 'my@user.com', + username: "testuser", + password: "mypass", + email: "my@user.com", }) .then(() => { return request({ - url: Parse.serverURL + '/verifyPassword', + url: Parse.serverURL + "/verifyPassword", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", }, qs: { - username: '', - password: 'mypass', + username: "", + password: "mypass", }, }); }) @@ -112,28 +119,30 @@ describe('Verify User Password', () => { }) .catch(err => { expect(err.status).toBe(400); - expect(err.text).toMatch('{"code":200,"error":"username/email is required."}'); + expect(err.text).toMatch( + '{"code":200,"error":"username/email is required."}' + ); done(); }); }); - it('fails to verify password when email is not provided in query string REST API', done => { + it("fails to verify password when email is not provided in query string REST API", done => { const user = new Parse.User(); user .save({ - username: 'testuser', - password: 'mypass', - email: 'my@user.com', + username: "testuser", + password: "mypass", + email: "my@user.com", }) .then(() => { return request({ - url: Parse.serverURL + '/verifyPassword', + url: Parse.serverURL + "/verifyPassword", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", }, qs: { - email: '', - password: 'mypass', + email: "", + password: "mypass", }, }); }) @@ -143,24 +152,28 @@ describe('Verify User Password', () => { }) .catch(err => { expect(err.status).toBe(400); - expect(err.text).toMatch('{"code":200,"error":"username/email is required."}'); + expect(err.text).toMatch( + '{"code":200,"error":"username/email is required."}' + ); done(); }); }); - it('fails to verify password when username is not provided with json payload REST API', done => { + it("fails to verify password when username is not provided with json payload REST API", done => { const user = new Parse.User(); user .save({ - username: 'testuser', - password: 'mypass', - email: 'my@user.com', + username: "testuser", + password: "mypass", + email: "my@user.com", }) .then(() => { - return verifyPassword('', 'mypass'); + return verifyPassword("", "mypass"); }) .then(res => { expect(res.status).toBe(400); - expect(res.text).toMatch('{"code":200,"error":"username/email is required."}'); + expect(res.text).toMatch( + '{"code":200,"error":"username/email is required."}' + ); done(); }) .catch(err => { @@ -168,20 +181,22 @@ describe('Verify User Password', () => { done(); }); }); - it('fails to verify password when email is not provided with json payload REST API', done => { + it("fails to verify password when email is not provided with json payload REST API", done => { const user = new Parse.User(); user .save({ - username: 'testuser', - password: 'mypass', - email: 'my@user.com', + username: "testuser", + password: "mypass", + email: "my@user.com", }) .then(() => { - return verifyPassword('', 'mypass', true); + return verifyPassword("", "mypass", true); }) .then(res => { expect(res.status).toBe(400); - expect(res.text).toMatch('{"code":200,"error":"username/email is required."}'); + expect(res.text).toMatch( + '{"code":200,"error":"username/email is required."}' + ); done(); }) .catch(err => { @@ -189,20 +204,22 @@ describe('Verify User Password', () => { done(); }); }); - it('fails to verify password when password is not provided with json payload REST API', done => { + it("fails to verify password when password is not provided with json payload REST API", done => { const user = new Parse.User(); user .save({ - username: 'testuser', - password: 'mypass', - email: 'my@user.com', + username: "testuser", + password: "mypass", + email: "my@user.com", }) .then(() => { - return verifyPassword('testuser', ''); + return verifyPassword("testuser", ""); }) .then(res => { expect(res.status).toBe(400); - expect(res.text).toMatch('{"code":201,"error":"password is required."}'); + expect(res.text).toMatch( + '{"code":201,"error":"password is required."}' + ); done(); }) .catch(err => { @@ -210,16 +227,16 @@ describe('Verify User Password', () => { done(); }); }); - it('fails to verify password when username matches but password does not match hash with json payload REST API', done => { + it("fails to verify password when username matches but password does not match hash with json payload REST API", done => { const user = new Parse.User(); user .save({ - username: 'testuser', - password: 'mypass', - email: 'my@user.com', + username: "testuser", + password: "mypass", + email: "my@user.com", }) .then(() => { - return verifyPassword('testuser', 'wrong password'); + return verifyPassword("testuser", "wrong password"); }) .then(res => { expect(res.status).toBe(404); @@ -233,16 +250,16 @@ describe('Verify User Password', () => { done(); }); }); - it('fails to verify password when email matches but password does not match hash with json payload REST API', done => { + it("fails to verify password when email matches but password does not match hash with json payload REST API", done => { const user = new Parse.User(); user .save({ - username: 'testuser', - password: 'mypass', - email: 'my@user.com', + username: "testuser", + password: "mypass", + email: "my@user.com", }) .then(() => { - return verifyPassword('my@user.com', 'wrong password', true); + return verifyPassword("my@user.com", "wrong password", true); }) .then(res => { expect(res.status).toBe(404); @@ -256,16 +273,16 @@ describe('Verify User Password', () => { done(); }); }); - it('fails to verify password when typeof username does not equal string REST API', done => { + it("fails to verify password when typeof username does not equal string REST API", done => { const user = new Parse.User(); user .save({ - username: 'testuser', - password: 'mypass', - email: 'my@user.com', + username: "testuser", + password: "mypass", + email: "my@user.com", }) .then(() => { - return verifyPassword(123, 'mypass'); + return verifyPassword(123, "mypass"); }) .then(res => { expect(res.status).toBe(404); @@ -279,16 +296,16 @@ describe('Verify User Password', () => { done(); }); }); - it('fails to verify password when typeof email does not equal string REST API', done => { + it("fails to verify password when typeof email does not equal string REST API", done => { const user = new Parse.User(); user .save({ - username: 'testuser', - password: 'mypass', - email: 'my@user.com', + username: "testuser", + password: "mypass", + email: "my@user.com", }) .then(() => { - return verifyPassword(123, 'mypass', true); + return verifyPassword(123, "mypass", true); }) .then(res => { expect(res.status).toBe(404); @@ -302,16 +319,16 @@ describe('Verify User Password', () => { done(); }); }); - it('fails to verify password when typeof password does not equal string REST API', done => { + it("fails to verify password when typeof password does not equal string REST API", done => { const user = new Parse.User(); user .save({ - username: 'testuser', - password: 'mypass', - email: 'my@user.com', + username: "testuser", + password: "mypass", + email: "my@user.com", }) .then(() => { - return verifyPassword('my@user.com', 123, true); + return verifyPassword("my@user.com", 123, true); }) .then(res => { expect(res.status).toBe(404); @@ -325,8 +342,8 @@ describe('Verify User Password', () => { done(); }); }); - it('fails to verify password when username cannot be found REST API', done => { - verifyPassword('mytestuser', 'mypass') + it("fails to verify password when username cannot be found REST API", done => { + verifyPassword("mytestuser", "mypass") .then(res => { expect(res.status).toBe(404); expect(res.text).toMatch( @@ -339,8 +356,8 @@ describe('Verify User Password', () => { done(); }); }); - it('fails to verify password when email cannot be found REST API', done => { - verifyPassword('my@user.com', 'mypass', true) + it("fails to verify password when email cannot be found REST API", done => { + verifyPassword("my@user.com", "mypass", true) .then(res => { expect(res.status).toBe(404); expect(res.text).toMatch( @@ -354,88 +371,95 @@ describe('Verify User Password', () => { }); }); - it('fails to verify password when preventLoginWithUnverifiedEmail is set to true REST API', async () => { + it("fails to verify password when preventLoginWithUnverifiedEmail is set to true REST API", async () => { await reconfigureServer({ - publicServerURL: 'http://localhost:8378/', - appName: 'emailVerify', + publicServerURL: "http://localhost:8378/", + appName: "emailVerify", verifyUserEmails: true, preventLoginWithUnverifiedEmail: true, emailAdapter: MockEmailAdapterWithOptions({ - fromAddress: 'parse@example.com', - apiKey: 'k', - domain: 'd', + fromAddress: "parse@example.com", + apiKey: "k", + domain: "d", }), }); const user = new Parse.User(); await user.save({ - username: 'unverified-user', - password: 'mypass', - email: 'unverified-email@example.com', + username: "unverified-user", + password: "mypass", + email: "unverified-email@example.com", }); - const res = await verifyPassword('unverified-email@example.com', 'mypass', true); + const res = await verifyPassword( + "unverified-email@example.com", + "mypass", + true + ); expect(res.status).toBe(400); expect(res.data).toEqual({ code: Parse.Error.EMAIL_NOT_FOUND, - error: 'User email is not verified.', + error: "User email is not verified.", }); }); - it('verify password lock account if failed verify password attempts are above threshold', done => { + it("verify password lock account if failed verify password attempts are above threshold", done => { reconfigureServer({ - appName: 'lockout threshold', + appName: "lockout threshold", accountLockout: { duration: 1, threshold: 2, }, - publicServerURL: 'http://localhost:8378/', + publicServerURL: "http://localhost:8378/", }) .then(() => { const user = new Parse.User(); return user.save({ - username: 'testuser', - password: 'mypass', - email: 'my@user.com', + username: "testuser", + password: "mypass", + email: "my@user.com", }); }) .then(() => { - return verifyPassword('testuser', 'wrong password'); + return verifyPassword("testuser", "wrong password"); }) .then(() => { - return verifyPassword('testuser', 'wrong password'); + return verifyPassword("testuser", "wrong password"); }) .then(() => { - return verifyPassword('testuser', 'wrong password'); + return verifyPassword("testuser", "wrong password"); }) .then(() => { - return isAccountLockoutError('testuser', 'wrong password', 1, 1); + return isAccountLockoutError("testuser", "wrong password", 1, 1); }) .then(() => { done(); }) .catch(err => { - fail('lock account after failed login attempts test failed: ' + JSON.stringify(err)); + fail( + "lock account after failed login attempts test failed: " + + JSON.stringify(err) + ); done(); }); }); - it('succeed in verifying password when username and email are provided and password matches hash with json payload REST API', done => { + it("succeed in verifying password when username and email are provided and password matches hash with json payload REST API", done => { const user = new Parse.User(); user .save({ - username: 'testuser', - password: 'mypass', - email: 'my@user.com', + username: "testuser", + password: "mypass", + email: "my@user.com", }) .then(() => { return request({ - url: Parse.serverURL + '/verifyPassword', + url: Parse.serverURL + "/verifyPassword", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", }, qs: { - username: 'testuser', - email: 'my@user.com', - password: 'mypass', + username: "testuser", + email: "my@user.com", + password: "mypass", }, json: true, }) @@ -444,10 +468,14 @@ describe('Verify User Password', () => { }) .then(response => { const res = response.data; - expect(typeof res).toBe('object'); - expect(typeof res['objectId']).toEqual('string'); - expect(Object.prototype.hasOwnProperty.call(res, 'sessionToken')).toEqual(false); - expect(Object.prototype.hasOwnProperty.call(res, 'password')).toEqual(false); + expect(typeof res).toBe("object"); + expect(typeof res["objectId"]).toEqual("string"); + expect( + Object.prototype.hasOwnProperty.call(res, "sessionToken") + ).toEqual(false); + expect(Object.prototype.hasOwnProperty.call(res, "password")).toEqual( + false + ); done(); }) .catch(err => { @@ -455,208 +483,232 @@ describe('Verify User Password', () => { done(); }); }); - it('succeed in verifying password when username and password matches hash with json payload REST API', done => { + it("succeed in verifying password when username and password matches hash with json payload REST API", done => { const user = new Parse.User(); user .save({ - username: 'testuser', - password: 'mypass', - email: 'my@user.com', + username: "testuser", + password: "mypass", + email: "my@user.com", }) .then(() => { - return verifyPassword('testuser', 'mypass'); + return verifyPassword("testuser", "mypass"); }) .then(response => { const res = response.data; - expect(typeof res).toBe('object'); - expect(typeof res['objectId']).toEqual('string'); - expect(Object.prototype.hasOwnProperty.call(res, 'sessionToken')).toEqual(false); - expect(Object.prototype.hasOwnProperty.call(res, 'password')).toEqual(false); + expect(typeof res).toBe("object"); + expect(typeof res["objectId"]).toEqual("string"); + expect( + Object.prototype.hasOwnProperty.call(res, "sessionToken") + ).toEqual(false); + expect(Object.prototype.hasOwnProperty.call(res, "password")).toEqual( + false + ); done(); }); }); - it('succeed in verifying password when email and password matches hash with json payload REST API', done => { + it("succeed in verifying password when email and password matches hash with json payload REST API", done => { const user = new Parse.User(); user .save({ - username: 'testuser', - password: 'mypass', - email: 'my@user.com', + username: "testuser", + password: "mypass", + email: "my@user.com", }) .then(() => { - return verifyPassword('my@user.com', 'mypass', true); + return verifyPassword("my@user.com", "mypass", true); }) .then(response => { const res = response.data; - expect(typeof res).toBe('object'); - expect(typeof res['objectId']).toEqual('string'); - expect(Object.prototype.hasOwnProperty.call(res, 'sessionToken')).toEqual(false); - expect(Object.prototype.hasOwnProperty.call(res, 'password')).toEqual(false); + expect(typeof res).toBe("object"); + expect(typeof res["objectId"]).toEqual("string"); + expect( + Object.prototype.hasOwnProperty.call(res, "sessionToken") + ).toEqual(false); + expect(Object.prototype.hasOwnProperty.call(res, "password")).toEqual( + false + ); done(); }); }); - it('succeed to verify password when username and password provided in query string REST API', done => { + it("succeed to verify password when username and password provided in query string REST API", done => { const user = new Parse.User(); user .save({ - username: 'testuser', - password: 'mypass', - email: 'my@user.com', + username: "testuser", + password: "mypass", + email: "my@user.com", }) .then(() => { return request({ - url: Parse.serverURL + '/verifyPassword', + url: Parse.serverURL + "/verifyPassword", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", }, qs: { - username: 'testuser', - password: 'mypass', + username: "testuser", + password: "mypass", }, }); }) .then(response => { const res = response.text; - expect(typeof res).toBe('string'); + expect(typeof res).toBe("string"); const body = JSON.parse(res); - expect(typeof body['objectId']).toEqual('string'); - expect(Object.prototype.hasOwnProperty.call(body, 'sessionToken')).toEqual(false); - expect(Object.prototype.hasOwnProperty.call(body, 'password')).toEqual(false); + expect(typeof body["objectId"]).toEqual("string"); + expect( + Object.prototype.hasOwnProperty.call(body, "sessionToken") + ).toEqual(false); + expect(Object.prototype.hasOwnProperty.call(body, "password")).toEqual( + false + ); done(); }); }); - it('succeed to verify password when email and password provided in query string REST API', done => { + it("succeed to verify password when email and password provided in query string REST API", done => { const user = new Parse.User(); user .save({ - username: 'testuser', - password: 'mypass', - email: 'my@user.com', + username: "testuser", + password: "mypass", + email: "my@user.com", }) .then(() => { return request({ - url: Parse.serverURL + '/verifyPassword', + url: Parse.serverURL + "/verifyPassword", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", }, qs: { - email: 'my@user.com', - password: 'mypass', + email: "my@user.com", + password: "mypass", }, }); }) .then(response => { const res = response.text; - expect(typeof res).toBe('string'); + expect(typeof res).toBe("string"); const body = JSON.parse(res); - expect(typeof body['objectId']).toEqual('string'); - expect(Object.prototype.hasOwnProperty.call(body, 'sessionToken')).toEqual(false); - expect(Object.prototype.hasOwnProperty.call(body, 'password')).toEqual(false); + expect(typeof body["objectId"]).toEqual("string"); + expect( + Object.prototype.hasOwnProperty.call(body, "sessionToken") + ).toEqual(false); + expect(Object.prototype.hasOwnProperty.call(body, "password")).toEqual( + false + ); done(); }); }); - it('succeed to verify password with username when user1 has username === user2 email REST API', done => { + it("succeed to verify password with username when user1 has username === user2 email REST API", done => { const user1 = new Parse.User(); user1 .save({ - username: 'email@user.com', - password: 'mypass1', - email: '1@user.com', + username: "email@user.com", + password: "mypass1", + email: "1@user.com", }) .then(() => { const user2 = new Parse.User(); return user2.save({ - username: 'user2', - password: 'mypass2', - email: 'email@user.com', + username: "user2", + password: "mypass2", + email: "email@user.com", }); }) .then(() => { - return verifyPassword('email@user.com', 'mypass1'); + return verifyPassword("email@user.com", "mypass1"); }) .then(response => { const res = response.data; - expect(typeof res).toBe('object'); - expect(typeof res['objectId']).toEqual('string'); - expect(Object.prototype.hasOwnProperty.call(res, 'sessionToken')).toEqual(false); - expect(Object.prototype.hasOwnProperty.call(res, 'password')).toEqual(false); + expect(typeof res).toBe("object"); + expect(typeof res["objectId"]).toEqual("string"); + expect( + Object.prototype.hasOwnProperty.call(res, "sessionToken") + ).toEqual(false); + expect(Object.prototype.hasOwnProperty.call(res, "password")).toEqual( + false + ); done(); }); }); - it('verify password of user with unverified email with master key and ignoreEmailVerification=true', async () => { + it("verify password of user with unverified email with master key and ignoreEmailVerification=true", async () => { await reconfigureServer({ - publicServerURL: 'http://localhost:8378/', - appName: 'emailVerify', + publicServerURL: "http://localhost:8378/", + appName: "emailVerify", verifyUserEmails: true, preventLoginWithUnverifiedEmail: true, emailAdapter: MockEmailAdapterWithOptions({ - fromAddress: 'parse@example.com', - apiKey: 'k', - domain: 'd', + fromAddress: "parse@example.com", + apiKey: "k", + domain: "d", }), }); const user = new Parse.User(); - user.setUsername('user'); - user.setPassword('pass'); - user.setEmail('test@example.com'); + user.setUsername("user"); + user.setPassword("pass"); + user.setEmail("test@example.com"); await user.signUp(); const { data: res } = await request({ - method: 'POST', - url: Parse.serverURL + '/verifyPassword', + method: "POST", + url: Parse.serverURL + "/verifyPassword", headers: { - 'X-Parse-Master-Key': Parse.masterKey, - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Master-Key": Parse.masterKey, + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }, body: { - username: 'user', - password: 'pass', + username: "user", + password: "pass", ignoreEmailVerification: true, }, json: true, }); expect(res.objectId).toBe(user.id); - expect(Object.prototype.hasOwnProperty.call(res, 'sessionToken')).toEqual(false); - expect(Object.prototype.hasOwnProperty.call(res, 'password')).toEqual(false); + expect(Object.prototype.hasOwnProperty.call(res, "sessionToken")).toEqual( + false + ); + expect(Object.prototype.hasOwnProperty.call(res, "password")).toEqual( + false + ); }); - it('fails to verify password of user with unverified email with master key and ignoreEmailVerification=false', async () => { + it("fails to verify password of user with unverified email with master key and ignoreEmailVerification=false", async () => { await reconfigureServer({ - publicServerURL: 'http://localhost:8378/', - appName: 'emailVerify', + publicServerURL: "http://localhost:8378/", + appName: "emailVerify", verifyUserEmails: true, preventLoginWithUnverifiedEmail: true, emailAdapter: MockEmailAdapterWithOptions({ - fromAddress: 'parse@example.com', - apiKey: 'k', - domain: 'd', + fromAddress: "parse@example.com", + apiKey: "k", + domain: "d", }), }); const user = new Parse.User(); - user.setUsername('user'); - user.setPassword('pass'); - user.setEmail('test@example.com'); + user.setUsername("user"); + user.setPassword("pass"); + user.setEmail("test@example.com"); await user.signUp(); const res = await request({ - method: 'POST', - url: Parse.serverURL + '/verifyPassword', + method: "POST", + url: Parse.serverURL + "/verifyPassword", headers: { - 'X-Parse-Master-Key': Parse.masterKey, - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Master-Key": Parse.masterKey, + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }, body: { - username: 'user', - password: 'pass', + username: "user", + password: "pass", ignoreEmailVerification: false, }, json: true, diff --git a/spec/WinstonLoggerAdapter.spec.js b/spec/WinstonLoggerAdapter.spec.js index da9efd2610..0ce41a18e5 100644 --- a/spec/WinstonLoggerAdapter.spec.js +++ b/spec/WinstonLoggerAdapter.spec.js @@ -1,28 +1,30 @@ -'use strict'; +"use strict"; const WinstonLoggerAdapter = - require('../lib/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter; -const request = require('../lib/request'); + require("../lib/Adapters/Logger/WinstonLoggerAdapter").WinstonLoggerAdapter; +const request = require("../lib/request"); describe_only(() => { - return process.env.PARSE_SERVER_LOG_LEVEL !== 'debug'; -})('info logs', () => { - it('Verify INFO logs', done => { + return process.env.PARSE_SERVER_LOG_LEVEL !== "debug"; +})("info logs", () => { + it("Verify INFO logs", done => { const winstonLoggerAdapter = new WinstonLoggerAdapter(); - winstonLoggerAdapter.log('info', 'testing info logs with 1234'); + winstonLoggerAdapter.log("info", "testing info logs with 1234"); winstonLoggerAdapter.query( { from: new Date(Date.now() - 500), size: 100, - level: 'info', - order: 'desc', + level: "info", + order: "desc", }, results => { if (results.length == 0) { - fail('The adapter should return non-empty results'); + fail("The adapter should return non-empty results"); } else { - const log = results.find(x => x.message === 'testing info logs with 1234'); - expect(log.level).toEqual('info'); + const log = results.find( + x => x.message === "testing info logs with 1234" + ); + expect(log.level).toEqual("info"); } // Check the error log // Regression #2639 @@ -30,10 +32,12 @@ describe_only(() => { { from: new Date(Date.now() - 200), size: 100, - level: 'error', + level: "error", }, errors => { - const log = errors.find(x => x.message === 'testing info logs with 1234'); + const log = errors.find( + x => x.message === "testing info logs with 1234" + ); expect(log).toBeUndefined(); done(); } @@ -42,140 +46,148 @@ describe_only(() => { ); }); - it('info logs should interpolate string', async () => { + it("info logs should interpolate string", async () => { const winstonLoggerAdapter = new WinstonLoggerAdapter(); - winstonLoggerAdapter.log('info', 'testing info logs with %s', 'replace'); + winstonLoggerAdapter.log("info", "testing info logs with %s", "replace"); const results = await winstonLoggerAdapter.query({ from: new Date(Date.now() - 500), size: 100, - level: 'info', - order: 'desc', + level: "info", + order: "desc", }); expect(results.length > 0).toBeTruthy(); - const log = results.find(x => x.message === 'testing info logs with replace'); + const log = results.find( + x => x.message === "testing info logs with replace" + ); expect(log); }); - it('info logs should interpolate json', async () => { + it("info logs should interpolate json", async () => { const winstonLoggerAdapter = new WinstonLoggerAdapter(); - winstonLoggerAdapter.log('info', 'testing info logs with %j', { - hello: 'world', + winstonLoggerAdapter.log("info", "testing info logs with %j", { + hello: "world", }); const results = await winstonLoggerAdapter.query({ from: new Date(Date.now() - 500), size: 100, - level: 'info', - order: 'desc', + level: "info", + order: "desc", }); expect(results.length > 0).toBeTruthy(); - const log = results.find(x => x.message === 'testing info logs with {"hello":"world"}'); + const log = results.find( + x => x.message === 'testing info logs with {"hello":"world"}' + ); expect(log); }); - it('info logs should interpolate number', async () => { + it("info logs should interpolate number", async () => { const winstonLoggerAdapter = new WinstonLoggerAdapter(); - winstonLoggerAdapter.log('info', 'testing info logs with %d', 123); + winstonLoggerAdapter.log("info", "testing info logs with %d", 123); const results = await winstonLoggerAdapter.query({ from: new Date(Date.now() - 500), size: 100, - level: 'info', - order: 'desc', + level: "info", + order: "desc", }); expect(results.length > 0).toBeTruthy(); - const log = results.find(x => x.message === 'testing info logs with 123'); + const log = results.find(x => x.message === "testing info logs with 123"); expect(log); }); }); describe_only(() => { - return process.env.PARSE_SERVER_LOG_LEVEL !== 'debug'; -})('error logs', () => { - it('Verify ERROR logs', done => { + return process.env.PARSE_SERVER_LOG_LEVEL !== "debug"; +})("error logs", () => { + it("Verify ERROR logs", done => { const winstonLoggerAdapter = new WinstonLoggerAdapter(); - winstonLoggerAdapter.log('error', 'testing error logs'); + winstonLoggerAdapter.log("error", "testing error logs"); winstonLoggerAdapter.query( { from: new Date(Date.now() - 500), size: 100, - level: 'error', + level: "error", }, results => { if (results.length == 0) { - fail('The adapter should return non-empty results'); + fail("The adapter should return non-empty results"); done(); } else { - expect(results[0].message).toEqual('testing error logs'); + expect(results[0].message).toEqual("testing error logs"); done(); } } ); }); - it('Should filter on query', done => { + it("Should filter on query", done => { const winstonLoggerAdapter = new WinstonLoggerAdapter(); - winstonLoggerAdapter.log('error', 'testing error logs'); + winstonLoggerAdapter.log("error", "testing error logs"); winstonLoggerAdapter.query( { from: new Date(Date.now() - 500), size: 100, - level: 'error', + level: "error", }, results => { - expect(results.filter(e => e.level !== 'error').length).toBe(0); + expect(results.filter(e => e.level !== "error").length).toBe(0); done(); } ); }); - it('error logs should interpolate string', async () => { + it("error logs should interpolate string", async () => { const winstonLoggerAdapter = new WinstonLoggerAdapter(); - winstonLoggerAdapter.log('error', 'testing error logs with %s', 'replace'); + winstonLoggerAdapter.log("error", "testing error logs with %s", "replace"); const results = await winstonLoggerAdapter.query({ from: new Date(Date.now() - 500), size: 100, - level: 'error', + level: "error", }); expect(results.length > 0).toBeTruthy(); - const log = results.find(x => x.message === 'testing error logs with replace'); + const log = results.find( + x => x.message === "testing error logs with replace" + ); expect(log); }); - it('error logs should interpolate json', async () => { + it("error logs should interpolate json", async () => { const winstonLoggerAdapter = new WinstonLoggerAdapter(); - winstonLoggerAdapter.log('error', 'testing error logs with %j', { - hello: 'world', + winstonLoggerAdapter.log("error", "testing error logs with %j", { + hello: "world", }); const results = await winstonLoggerAdapter.query({ from: new Date(Date.now() - 500), size: 100, - level: 'error', - order: 'desc', + level: "error", + order: "desc", }); expect(results.length > 0).toBeTruthy(); - const log = results.find(x => x.message === 'testing error logs with {"hello":"world"}'); + const log = results.find( + x => x.message === 'testing error logs with {"hello":"world"}' + ); expect(log); }); - it('error logs should interpolate number', async () => { + it("error logs should interpolate number", async () => { const winstonLoggerAdapter = new WinstonLoggerAdapter(); - winstonLoggerAdapter.log('error', 'testing error logs with %d', 123); + winstonLoggerAdapter.log("error", "testing error logs with %d", 123); const results = await winstonLoggerAdapter.query({ from: new Date(Date.now() - 500), size: 100, - level: 'error', - order: 'desc', + level: "error", + order: "desc", }); expect(results.length > 0).toBeTruthy(); - const log = results.find(x => x.message === 'testing error logs with 123'); + const log = results.find(x => x.message === "testing error logs with 123"); expect(log); }); }); describe_only(() => { - return process.env.PARSE_SERVER_LOG_LEVEL !== 'debug'; -})('verbose logs', () => { - it_id('9ca72994-d255-4c11-a5a2-693c99ee2cdb')(it)( - 'mask sensitive information in _User class', + return process.env.PARSE_SERVER_LOG_LEVEL !== "debug"; +})("verbose logs", () => { + it_id("9ca72994-d255-4c11-a5a2-693c99ee2cdb")(it)( + "mask sensitive information in _User class", done => { reconfigureServer({ verbose: true }) .then(() => createTestUser()) @@ -184,7 +196,7 @@ describe_only(() => { return winstonLoggerAdapter.query({ from: new Date(Date.now() - 500), size: 100, - level: 'verbose', + level: "verbose", }); }) .then(results => { @@ -193,19 +205,19 @@ describe_only(() => { expect(logString.match(/moon-y/g)).toBe(null); const headers = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; request({ headers: headers, - url: 'http://localhost:8378/1/login?username=test&password=moon-y', + url: "http://localhost:8378/1/login?username=test&password=moon-y", }).then(() => { const winstonLoggerAdapter = new WinstonLoggerAdapter(); return winstonLoggerAdapter .query({ from: new Date(Date.now() - 500), size: 100, - level: 'verbose', + level: "verbose", }) .then(results => { const logString = JSON.stringify(results); @@ -222,60 +234,72 @@ describe_only(() => { } ); - it('verbose logs should interpolate string', async () => { + it("verbose logs should interpolate string", async () => { await reconfigureServer({ verbose: true }); const winstonLoggerAdapter = new WinstonLoggerAdapter(); - winstonLoggerAdapter.log('verbose', 'testing verbose logs with %s', 'replace'); + winstonLoggerAdapter.log( + "verbose", + "testing verbose logs with %s", + "replace" + ); const results = await winstonLoggerAdapter.query({ from: new Date(Date.now() - 500), size: 100, - level: 'verbose', + level: "verbose", }); expect(results.length > 0).toBeTruthy(); - const log = results.find(x => x.message === 'testing verbose logs with replace'); + const log = results.find( + x => x.message === "testing verbose logs with replace" + ); expect(log); }); - it('verbose logs should interpolate json', async () => { + it("verbose logs should interpolate json", async () => { await reconfigureServer({ verbose: true }); const winstonLoggerAdapter = new WinstonLoggerAdapter(); - winstonLoggerAdapter.log('verbose', 'testing verbose logs with %j', { - hello: 'world', + winstonLoggerAdapter.log("verbose", "testing verbose logs with %j", { + hello: "world", }); const results = await winstonLoggerAdapter.query({ from: new Date(Date.now() - 500), size: 100, - level: 'verbose', - order: 'desc', + level: "verbose", + order: "desc", }); expect(results.length > 0).toBeTruthy(); - const log = results.find(x => x.message === 'testing verbose logs with {"hello":"world"}'); + const log = results.find( + x => x.message === 'testing verbose logs with {"hello":"world"}' + ); expect(log); }); - it('verbose logs should interpolate number', async () => { + it("verbose logs should interpolate number", async () => { await reconfigureServer({ verbose: true }); const winstonLoggerAdapter = new WinstonLoggerAdapter(); - winstonLoggerAdapter.log('verbose', 'testing verbose logs with %d', 123); + winstonLoggerAdapter.log("verbose", "testing verbose logs with %d", 123); const results = await winstonLoggerAdapter.query({ from: new Date(Date.now() - 500), size: 100, - level: 'verbose', - order: 'desc', + level: "verbose", + order: "desc", }); expect(results.length > 0).toBeTruthy(); - const log = results.find(x => x.message === 'testing verbose logs with 123'); + const log = results.find( + x => x.message === "testing verbose logs with 123" + ); expect(log); }); - it('verbose logs should interpolate stdout', async () => { + it("verbose logs should interpolate stdout", async () => { await reconfigureServer({ verbose: true, silent: false, logsFolder: null }); - spyOn(process.stdout, 'write'); + spyOn(process.stdout, "write"); const winstonLoggerAdapter = new WinstonLoggerAdapter(); - winstonLoggerAdapter.log('verbose', 'testing verbose logs with %j', { - hello: 'world', + winstonLoggerAdapter.log("verbose", "testing verbose logs with %j", { + hello: "world", }); const firstLog = process.stdout.write.calls.first().args[0]; - expect(firstLog).toBe('verbose: testing verbose logs with {"hello":"world"}\n'); + expect(firstLog).toBe( + 'verbose: testing verbose logs with {"hello":"world"}\n' + ); }); }); diff --git a/spec/batch.spec.js b/spec/batch.spec.js index 1dbd5714b6..715c9e86a6 100644 --- a/spec/batch.spec.js +++ b/spec/batch.spec.js @@ -1,156 +1,158 @@ -const batch = require('../lib/batch'); -const request = require('../lib/request'); +const batch = require("../lib/batch"); +const request = require("../lib/request"); -const originalURL = '/parse/batch'; -const serverURL = 'http://localhost:1234/parse'; -const serverURL1 = 'http://localhost:1234/1'; -const serverURLNaked = 'http://localhost:1234/'; -const publicServerURL = 'http://domain.com/parse'; -const publicServerURLNaked = 'http://domain.com/'; -const publicServerURLLong = 'https://domain.com/something/really/long'; +const originalURL = "/parse/batch"; +const serverURL = "http://localhost:1234/parse"; +const serverURL1 = "http://localhost:1234/1"; +const serverURLNaked = "http://localhost:1234/"; +const publicServerURL = "http://domain.com/parse"; +const publicServerURLNaked = "http://domain.com/"; +const publicServerURLLong = "https://domain.com/something/really/long"; const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Installation-Id': 'yolo', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "X-Parse-Installation-Id": "yolo", }; -describe('batch', () => { +describe("batch", () => { let createSpy; beforeEach(async () => { - createSpy = spyOn(databaseAdapter, 'createObject').and.callThrough(); + createSpy = spyOn(databaseAdapter, "createObject").and.callThrough(); }); - it('should return the proper url', () => { - const internalURL = batch.makeBatchRoutingPathFunction(originalURL)('/parse/classes/Object'); - expect(internalURL).toEqual('/classes/Object'); + it("should return the proper url", () => { + const internalURL = batch.makeBatchRoutingPathFunction(originalURL)( + "/parse/classes/Object" + ); + expect(internalURL).toEqual("/classes/Object"); }); - it('should return the proper url given a public url-only path', () => { - const originalURL = '/something/really/long/batch'; + it("should return the proper url given a public url-only path", () => { + const originalURL = "/something/really/long/batch"; const internalURL = batch.makeBatchRoutingPathFunction( originalURL, serverURL, publicServerURLLong - )('/parse/classes/Object'); - expect(internalURL).toEqual('/classes/Object'); + )("/parse/classes/Object"); + expect(internalURL).toEqual("/classes/Object"); }); - it('should return the proper url given a server url-only path', () => { - const originalURL = '/parse/batch'; + it("should return the proper url given a server url-only path", () => { + const originalURL = "/parse/batch"; const internalURL = batch.makeBatchRoutingPathFunction( originalURL, serverURL, publicServerURLLong - )('/parse/classes/Object'); - expect(internalURL).toEqual('/classes/Object'); + )("/parse/classes/Object"); + expect(internalURL).toEqual("/classes/Object"); }); - it('should return the proper url same public/local endpoint', () => { - const originalURL = '/parse/batch'; + it("should return the proper url same public/local endpoint", () => { + const originalURL = "/parse/batch"; const internalURL = batch.makeBatchRoutingPathFunction( originalURL, serverURL, publicServerURL - )('/parse/classes/Object'); + )("/parse/classes/Object"); - expect(internalURL).toEqual('/classes/Object'); + expect(internalURL).toEqual("/classes/Object"); }); - it('should return the proper url with different public/local mount', () => { - const originalURL = '/parse/batch'; + it("should return the proper url with different public/local mount", () => { + const originalURL = "/parse/batch"; const internalURL = batch.makeBatchRoutingPathFunction( originalURL, serverURL1, publicServerURL - )('/parse/classes/Object'); + )("/parse/classes/Object"); - expect(internalURL).toEqual('/classes/Object'); + expect(internalURL).toEqual("/classes/Object"); }); - it('should return the proper url with naked public', () => { - const originalURL = '/batch'; + it("should return the proper url with naked public", () => { + const originalURL = "/batch"; const internalURL = batch.makeBatchRoutingPathFunction( originalURL, serverURL, publicServerURLNaked - )('/classes/Object'); + )("/classes/Object"); - expect(internalURL).toEqual('/classes/Object'); + expect(internalURL).toEqual("/classes/Object"); }); - it('should return the proper url with naked local', () => { - const originalURL = '/parse/batch'; + it("should return the proper url with naked local", () => { + const originalURL = "/parse/batch"; const internalURL = batch.makeBatchRoutingPathFunction( originalURL, serverURLNaked, publicServerURL - )('/parse/classes/Object'); + )("/parse/classes/Object"); - expect(internalURL).toEqual('/classes/Object'); + expect(internalURL).toEqual("/classes/Object"); }); - it('should return the proper url with no url provided', () => { - const originalURL = '/parse/batch'; + it("should return the proper url with no url provided", () => { + const originalURL = "/parse/batch"; const internalURL = batch.makeBatchRoutingPathFunction( originalURL, undefined, publicServerURL - )('/parse/classes/Object'); + )("/parse/classes/Object"); - expect(internalURL).toEqual('/classes/Object'); + expect(internalURL).toEqual("/classes/Object"); }); - it('should return the proper url with no public url provided', () => { - const originalURL = '/parse/batch'; + it("should return the proper url with no public url provided", () => { + const originalURL = "/parse/batch"; const internalURL = batch.makeBatchRoutingPathFunction( originalURL, serverURLNaked, undefined - )('/parse/classes/Object'); + )("/parse/classes/Object"); - expect(internalURL).toEqual('/classes/Object'); + expect(internalURL).toEqual("/classes/Object"); }); - it('should return the proper url with bad url provided', () => { - const originalURL = '/parse/batch'; + it("should return the proper url with bad url provided", () => { + const originalURL = "/parse/batch"; const internalURL = batch.makeBatchRoutingPathFunction( originalURL, - 'badurl.com', + "badurl.com", publicServerURL - )('/parse/classes/Object'); + )("/parse/classes/Object"); - expect(internalURL).toEqual('/classes/Object'); + expect(internalURL).toEqual("/classes/Object"); }); - it('should return the proper url with bad public url provided', () => { - const originalURL = '/parse/batch'; + it("should return the proper url with bad public url provided", () => { + const originalURL = "/parse/batch"; const internalURL = batch.makeBatchRoutingPathFunction( originalURL, serverURLNaked, - 'badurl.com' - )('/parse/classes/Object'); + "badurl.com" + )("/parse/classes/Object"); - expect(internalURL).toEqual('/classes/Object'); + expect(internalURL).toEqual("/classes/Object"); }); - it('should handle a batch request without transaction', async () => { + it("should handle a batch request without transaction", async () => { const response = await request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/batch', + url: "http://localhost:8378/1/batch", body: JSON.stringify({ requests: [ { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value2' }, + method: "POST", + path: "/1/classes/MyObject", + body: { key: "value2" }, }, ], }), @@ -160,30 +162,33 @@ describe('batch', () => { expect(response.data[0].success.createdAt).toBeDefined(); expect(response.data[1].success.objectId).toBeDefined(); expect(response.data[1].success.createdAt).toBeDefined(); - const query = new Parse.Query('MyObject'); + const query = new Parse.Query("MyObject"); const results = await query.find(); expect(createSpy.calls.count()).toBe(2); expect(createSpy.calls.argsFor(0)[3]).toEqual(null); expect(createSpy.calls.argsFor(1)[3]).toEqual(null); - expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']); + expect(results.map(result => result.get("key")).sort()).toEqual([ + "value1", + "value2", + ]); }); - it('should handle a batch request with transaction = false', async () => { + it("should handle a batch request with transaction = false", async () => { const response = await request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/batch', + url: "http://localhost:8378/1/batch", body: JSON.stringify({ requests: [ { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value2' }, + method: "POST", + path: "/1/classes/MyObject", + body: { key: "value2" }, }, ], transaction: false, @@ -195,39 +200,42 @@ describe('batch', () => { expect(response.data[1].success.objectId).toBeDefined(); expect(response.data[1].success.createdAt).toBeDefined(); - const query = new Parse.Query('MyObject'); + const query = new Parse.Query("MyObject"); const results = await query.find(); expect(createSpy.calls.count()).toBe(2); expect(createSpy.calls.argsFor(0)[3]).toEqual(null); expect(createSpy.calls.argsFor(1)[3]).toEqual(null); - expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']); + expect(results.map(result => result.get("key")).sort()).toEqual([ + "value1", + "value2", + ]); }); if ( - process.env.MONGODB_TOPOLOGY === 'replicaset' || - process.env.PARSE_SERVER_TEST_DB === 'postgres' + process.env.MONGODB_TOPOLOGY === "replicaset" || + process.env.PARSE_SERVER_TEST_DB === "postgres" ) { - describe('transactions', () => { - it('should handle a batch request with transaction = true', async () => { - const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections + describe("transactions", () => { + it("should handle a batch request with transaction = true", async () => { + const myObject = new Parse.Object("MyObject"); // This is important because transaction only works on pre-existing collections await myObject.save(); await myObject.destroy(); createSpy.calls.reset(); const response = await request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/batch', + url: "http://localhost:8378/1/batch", body: JSON.stringify({ requests: [ { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value2' }, + method: "POST", + path: "/1/classes/MyObject", + body: { key: "value2" }, }, ], transaction: true, @@ -238,116 +246,121 @@ describe('batch', () => { expect(response.data[0].success.createdAt).toBeDefined(); expect(response.data[1].success.objectId).toBeDefined(); expect(response.data[1].success.createdAt).toBeDefined(); - const query = new Parse.Query('MyObject'); + const query = new Parse.Query("MyObject"); const results = await query.find(); expect(createSpy.calls.count()).toBe(2); for (let i = 0; i + 1 < createSpy.calls.length; i = i + 2) { - expect(createSpy.calls.argsFor(i)[3]).toBe(createSpy.calls.argsFor(i + 1)[3]); + expect(createSpy.calls.argsFor(i)[3]).toBe( + createSpy.calls.argsFor(i + 1)[3] + ); } - expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']); + expect(results.map(result => result.get("key")).sort()).toEqual([ + "value1", + "value2", + ]); }); - it('should not save anything when one operation fails in a transaction', async () => { - const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections - await myObject.save({ key: 'stringField' }); + it("should not save anything when one operation fails in a transaction", async () => { + const myObject = new Parse.Object("MyObject"); // This is important because transaction only works on pre-existing collections + await myObject.save({ key: "stringField" }); await myObject.destroy(); createSpy.calls.reset(); try { // Saving a number to a string field should fail await request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/batch', + url: "http://localhost:8378/1/batch", body: JSON.stringify({ requests: [ { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject', + method: "POST", + path: "/1/classes/MyObject", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject', + method: "POST", + path: "/1/classes/MyObject", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject', + method: "POST", + path: "/1/classes/MyObject", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject', + method: "POST", + path: "/1/classes/MyObject", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject', + method: "POST", + path: "/1/classes/MyObject", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject', + method: "POST", + path: "/1/classes/MyObject", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject', + method: "POST", + path: "/1/classes/MyObject", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject', + method: "POST", + path: "/1/classes/MyObject", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject', + method: "POST", + path: "/1/classes/MyObject", body: { key: 10 }, }, ], @@ -357,129 +370,129 @@ describe('batch', () => { fail(); } catch (error) { expect(error).toBeDefined(); - const query = new Parse.Query('MyObject'); + const query = new Parse.Query("MyObject"); const results = await query.find(); expect(results.length).toBe(0); } }); - it('should generate separate session for each call', async () => { - const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections - await myObject.save({ key: 'stringField' }); + it("should generate separate session for each call", async () => { + const myObject = new Parse.Object("MyObject"); // This is important because transaction only works on pre-existing collections + await myObject.save({ key: "stringField" }); await myObject.destroy(); - const myObject2 = new Parse.Object('MyObject2'); // This is important because transaction only works on pre-existing collections - await myObject2.save({ key: 'stringField' }); + const myObject2 = new Parse.Object("MyObject2"); // This is important because transaction only works on pre-existing collections + await myObject2.save({ key: "stringField" }); await myObject2.destroy(); createSpy.calls.reset(); let myObjectCalls = 0; - Parse.Cloud.beforeSave('MyObject', async () => { + Parse.Cloud.beforeSave("MyObject", async () => { myObjectCalls++; if (myObjectCalls === 2) { try { // Saving a number to a string field should fail await request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/batch', + url: "http://localhost:8378/1/batch", body: JSON.stringify({ requests: [ { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject2", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject2', + method: "POST", + path: "/1/classes/MyObject2", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject2", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject2', + method: "POST", + path: "/1/classes/MyObject2", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject2", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject2', + method: "POST", + path: "/1/classes/MyObject2", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject2", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject2', + method: "POST", + path: "/1/classes/MyObject2", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject2", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject2', + method: "POST", + path: "/1/classes/MyObject2", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject2", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject2', + method: "POST", + path: "/1/classes/MyObject2", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject2", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject2', + method: "POST", + path: "/1/classes/MyObject2", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject2", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject2', + method: "POST", + path: "/1/classes/MyObject2", body: { key: 10 }, }, { - method: 'POST', - path: '/1/classes/MyObject2', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject2", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject2', + method: "POST", + path: "/1/classes/MyObject2", body: { key: 10 }, }, ], transaction: true, }), }); - fail('should fail'); + fail("should fail"); } catch (e) { expect(e).toBeDefined(); } @@ -487,20 +500,20 @@ describe('batch', () => { }); const response = await request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/batch', + url: "http://localhost:8378/1/batch", body: JSON.stringify({ requests: [ { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject', - body: { key: 'value2' }, + method: "POST", + path: "/1/classes/MyObject", + body: { key: "value2" }, }, ], transaction: true, @@ -514,36 +527,42 @@ describe('batch', () => { expect(response.data[1].success.createdAt).toBeDefined(); await request({ - method: 'POST', + method: "POST", headers: headers, - url: 'http://localhost:8378/1/batch', + url: "http://localhost:8378/1/batch", body: JSON.stringify({ requests: [ { - method: 'POST', - path: '/1/classes/MyObject3', - body: { key: 'value1' }, + method: "POST", + path: "/1/classes/MyObject3", + body: { key: "value1" }, }, { - method: 'POST', - path: '/1/classes/MyObject3', - body: { key: 'value2' }, + method: "POST", + path: "/1/classes/MyObject3", + body: { key: "value2" }, }, ], }), }); - const query = new Parse.Query('MyObject'); + const query = new Parse.Query("MyObject"); const results = await query.find(); - expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']); + expect(results.map(result => result.get("key")).sort()).toEqual([ + "value1", + "value2", + ]); - const query2 = new Parse.Query('MyObject2'); + const query2 = new Parse.Query("MyObject2"); const results2 = await query2.find(); expect(results2.length).toEqual(0); - const query3 = new Parse.Query('MyObject3'); + const query3 = new Parse.Query("MyObject3"); const results3 = await query3.find(); - expect(results3.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']); + expect(results3.map(result => result.get("key")).sort()).toEqual([ + "value1", + "value2", + ]); expect(createSpy.calls.count() >= 13).toEqual(true); let transactionalSession; @@ -554,7 +573,7 @@ describe('batch', () => { for (let i = 0; i < createSpy.calls.count(); i++) { const args = createSpy.calls.argsFor(i); switch (args[0]) { - case 'MyObject': + case "MyObject": myObjectDBCalls++; if (!transactionalSession || (myObjectDBCalls - 1) % 2 === 0) { transactionalSession = args[3]; @@ -565,7 +584,7 @@ describe('batch', () => { expect(transactionalSession2).not.toBe(args[3]); } break; - case 'MyObject2': + case "MyObject2": myObject2DBCalls++; if (!transactionalSession2 || (myObject2DBCalls - 1) % 9 === 0) { transactionalSession2 = args[3]; @@ -576,7 +595,7 @@ describe('batch', () => { expect(transactionalSession).not.toBe(args[3]); } break; - case 'MyObject3': + case "MyObject3": myObject3DBCalls++; expect(args[3]).toEqual(null); break; diff --git a/spec/cloud/cloudCodeAbsoluteFile.js b/spec/cloud/cloudCodeAbsoluteFile.js index a62b4fcc24..70e8b6a3ed 100644 --- a/spec/cloud/cloudCodeAbsoluteFile.js +++ b/spec/cloud/cloudCodeAbsoluteFile.js @@ -1,3 +1,3 @@ -Parse.Cloud.define('cloudCodeInFile', () => { - return 'It is possible to define cloud code in a file.'; +Parse.Cloud.define("cloudCodeInFile", () => { + return "It is possible to define cloud code in a file."; }); diff --git a/spec/cloud/cloudCodeModuleFile.js b/spec/cloud/cloudCodeModuleFile.js index a62b4fcc24..70e8b6a3ed 100644 --- a/spec/cloud/cloudCodeModuleFile.js +++ b/spec/cloud/cloudCodeModuleFile.js @@ -1,3 +1,3 @@ -Parse.Cloud.define('cloudCodeInFile', () => { - return 'It is possible to define cloud code in a file.'; +Parse.Cloud.define("cloudCodeInFile", () => { + return "It is possible to define cloud code in a file."; }); diff --git a/spec/cloud/cloudCodeRelativeFile.js b/spec/cloud/cloudCodeRelativeFile.js index a62b4fcc24..70e8b6a3ed 100644 --- a/spec/cloud/cloudCodeRelativeFile.js +++ b/spec/cloud/cloudCodeRelativeFile.js @@ -1,3 +1,3 @@ -Parse.Cloud.define('cloudCodeInFile', () => { - return 'It is possible to define cloud code in a file.'; +Parse.Cloud.define("cloudCodeInFile", () => { + return "It is possible to define cloud code in a file."; }); diff --git a/spec/cryptoUtils.spec.js b/spec/cryptoUtils.spec.js index 8270e052cf..7d222a9cda 100644 --- a/spec/cryptoUtils.spec.js +++ b/spec/cryptoUtils.spec.js @@ -1,4 +1,4 @@ -const cryptoUtils = require('../lib/cryptoUtils'); +const cryptoUtils = require("../lib/cryptoUtils"); function givesUniqueResults(fn, iterations) { const results = {}; @@ -12,76 +12,80 @@ function givesUniqueResults(fn, iterations) { return true; } -describe('randomString', () => { - it('returns a string', () => { - expect(typeof cryptoUtils.randomString(10)).toBe('string'); +describe("randomString", () => { + it("returns a string", () => { + expect(typeof cryptoUtils.randomString(10)).toBe("string"); }); - it('returns result of the given length', () => { + it("returns result of the given length", () => { expect(cryptoUtils.randomString(11).length).toBe(11); expect(cryptoUtils.randomString(25).length).toBe(25); }); - it('throws if requested length is zero', () => { + it("throws if requested length is zero", () => { expect(() => cryptoUtils.randomString(0)).toThrow(); }); - it('returns unique results', () => { - expect(givesUniqueResults(() => cryptoUtils.randomString(10), 100)).toBe(true); + it("returns unique results", () => { + expect(givesUniqueResults(() => cryptoUtils.randomString(10), 100)).toBe( + true + ); }); }); -describe('randomHexString', () => { - it('returns a string', () => { - expect(typeof cryptoUtils.randomHexString(10)).toBe('string'); +describe("randomHexString", () => { + it("returns a string", () => { + expect(typeof cryptoUtils.randomHexString(10)).toBe("string"); }); - it('returns result of the given length', () => { + it("returns result of the given length", () => { expect(cryptoUtils.randomHexString(10).length).toBe(10); expect(cryptoUtils.randomHexString(32).length).toBe(32); }); - it('throws if requested length is zero', () => { + it("throws if requested length is zero", () => { expect(() => cryptoUtils.randomHexString(0)).toThrow(); }); - it('throws if requested length is not even', () => { + it("throws if requested length is not even", () => { expect(() => cryptoUtils.randomHexString(11)).toThrow(); }); - it('returns unique results', () => { - expect(givesUniqueResults(() => cryptoUtils.randomHexString(20), 100)).toBe(true); + it("returns unique results", () => { + expect(givesUniqueResults(() => cryptoUtils.randomHexString(20), 100)).toBe( + true + ); }); }); -describe('newObjectId', () => { - it('returns a string', () => { - expect(typeof cryptoUtils.newObjectId()).toBe('string'); +describe("newObjectId", () => { + it("returns a string", () => { + expect(typeof cryptoUtils.newObjectId()).toBe("string"); }); - it('returns result with at least 10 characters', () => { + it("returns result with at least 10 characters", () => { expect(cryptoUtils.newObjectId().length).toBeGreaterThan(9); }); - it('returns result with required number of characters', () => { + it("returns result with required number of characters", () => { expect(cryptoUtils.newObjectId(42).length).toBe(42); }); - it('returns unique results', () => { + it("returns unique results", () => { expect(givesUniqueResults(() => cryptoUtils.newObjectId(), 100)).toBe(true); }); }); -describe('newToken', () => { - it('returns a string', () => { - expect(typeof cryptoUtils.newToken()).toBe('string'); +describe("newToken", () => { + it("returns a string", () => { + expect(typeof cryptoUtils.newToken()).toBe("string"); }); - it('returns result with at least 32 characters', () => { + it("returns result with at least 32 characters", () => { expect(cryptoUtils.newToken().length).toBeGreaterThan(31); }); - it('returns unique results', () => { + it("returns unique results", () => { expect(givesUniqueResults(() => cryptoUtils.newToken(), 100)).toBe(true); }); }); diff --git a/spec/defaultGraphQLTypes.spec.js b/spec/defaultGraphQLTypes.spec.js index 4e3e311467..a600fcfe21 100644 --- a/spec/defaultGraphQLTypes.spec.js +++ b/spec/defaultGraphQLTypes.spec.js @@ -1,4 +1,4 @@ -const { Kind } = require('graphql'); +const { Kind } = require("graphql"); const { TypeValidationError, parseStringValue, @@ -12,7 +12,7 @@ const { BYTES, DATE, FILE, -} = require('../lib/GraphQL/loaders/defaultGraphQLTypes'); +} = require("../lib/GraphQL/loaders/defaultGraphQLTypes"); function createValue(kind, value, values, fields) { return { @@ -32,131 +32,178 @@ function createObjectField(name, value) { }; } -describe('defaultGraphQLTypes', () => { - describe('TypeValidationError', () => { - it('should be an error with specific message', () => { - const typeValidationError = new TypeValidationError('somevalue', 'sometype'); +describe("defaultGraphQLTypes", () => { + describe("TypeValidationError", () => { + it("should be an error with specific message", () => { + const typeValidationError = new TypeValidationError( + "somevalue", + "sometype" + ); expect(typeValidationError).toEqual(jasmine.any(Error)); - expect(typeValidationError.message).toEqual('somevalue is not a valid sometype'); + expect(typeValidationError.message).toEqual( + "somevalue is not a valid sometype" + ); }); }); - describe('parseStringValue', () => { - it('should return itself if a string', () => { - const myString = 'myString'; + describe("parseStringValue", () => { + it("should return itself if a string", () => { + const myString = "myString"; expect(parseStringValue(myString)).toBe(myString); }); - it('should fail if not a string', () => { - expect(() => parseStringValue()).toThrow(jasmine.stringMatching('is not a valid String')); - expect(() => parseStringValue({})).toThrow(jasmine.stringMatching('is not a valid String')); - expect(() => parseStringValue([])).toThrow(jasmine.stringMatching('is not a valid String')); - expect(() => parseStringValue(123)).toThrow(jasmine.stringMatching('is not a valid String')); + it("should fail if not a string", () => { + expect(() => parseStringValue()).toThrow( + jasmine.stringMatching("is not a valid String") + ); + expect(() => parseStringValue({})).toThrow( + jasmine.stringMatching("is not a valid String") + ); + expect(() => parseStringValue([])).toThrow( + jasmine.stringMatching("is not a valid String") + ); + expect(() => parseStringValue(123)).toThrow( + jasmine.stringMatching("is not a valid String") + ); }); }); - describe('parseIntValue', () => { - it('should parse to number if a string', () => { - const myString = '123'; + describe("parseIntValue", () => { + it("should parse to number if a string", () => { + const myString = "123"; expect(parseIntValue(myString)).toBe(123); }); - it('should fail if not a string', () => { - expect(() => parseIntValue()).toThrow(jasmine.stringMatching('is not a valid Int')); - expect(() => parseIntValue({})).toThrow(jasmine.stringMatching('is not a valid Int')); - expect(() => parseIntValue([])).toThrow(jasmine.stringMatching('is not a valid Int')); - expect(() => parseIntValue(123)).toThrow(jasmine.stringMatching('is not a valid Int')); + it("should fail if not a string", () => { + expect(() => parseIntValue()).toThrow( + jasmine.stringMatching("is not a valid Int") + ); + expect(() => parseIntValue({})).toThrow( + jasmine.stringMatching("is not a valid Int") + ); + expect(() => parseIntValue([])).toThrow( + jasmine.stringMatching("is not a valid Int") + ); + expect(() => parseIntValue(123)).toThrow( + jasmine.stringMatching("is not a valid Int") + ); }); - it('should fail if not an integer string', () => { - expect(() => parseIntValue('a123')).toThrow(jasmine.stringMatching('is not a valid Int')); - expect(() => parseIntValue('123.4')).toThrow(jasmine.stringMatching('is not a valid Int')); + it("should fail if not an integer string", () => { + expect(() => parseIntValue("a123")).toThrow( + jasmine.stringMatching("is not a valid Int") + ); + expect(() => parseIntValue("123.4")).toThrow( + jasmine.stringMatching("is not a valid Int") + ); }); }); - describe('parseFloatValue', () => { - it('should parse to number if a string', () => { - expect(parseFloatValue('123')).toBe(123); - expect(parseFloatValue('123.4')).toBe(123.4); + describe("parseFloatValue", () => { + it("should parse to number if a string", () => { + expect(parseFloatValue("123")).toBe(123); + expect(parseFloatValue("123.4")).toBe(123.4); }); - it('should fail if not a string', () => { - expect(() => parseFloatValue()).toThrow(jasmine.stringMatching('is not a valid Float')); - expect(() => parseFloatValue({})).toThrow(jasmine.stringMatching('is not a valid Float')); - expect(() => parseFloatValue([])).toThrow(jasmine.stringMatching('is not a valid Float')); + it("should fail if not a string", () => { + expect(() => parseFloatValue()).toThrow( + jasmine.stringMatching("is not a valid Float") + ); + expect(() => parseFloatValue({})).toThrow( + jasmine.stringMatching("is not a valid Float") + ); + expect(() => parseFloatValue([])).toThrow( + jasmine.stringMatching("is not a valid Float") + ); }); - it('should fail if not a float string', () => { - expect(() => parseIntValue('a123')).toThrow(jasmine.stringMatching('is not a valid Int')); + it("should fail if not a float string", () => { + expect(() => parseIntValue("a123")).toThrow( + jasmine.stringMatching("is not a valid Int") + ); }); }); - describe('parseBooleanValue', () => { - it('should return itself if a boolean', () => { + describe("parseBooleanValue", () => { + it("should return itself if a boolean", () => { let myBoolean = true; expect(parseBooleanValue(myBoolean)).toBe(myBoolean); myBoolean = false; expect(parseBooleanValue(myBoolean)).toBe(myBoolean); }); - it('should fail if not a boolean', () => { - expect(() => parseBooleanValue()).toThrow(jasmine.stringMatching('is not a valid Boolean')); - expect(() => parseBooleanValue({})).toThrow(jasmine.stringMatching('is not a valid Boolean')); - expect(() => parseBooleanValue([])).toThrow(jasmine.stringMatching('is not a valid Boolean')); + it("should fail if not a boolean", () => { + expect(() => parseBooleanValue()).toThrow( + jasmine.stringMatching("is not a valid Boolean") + ); + expect(() => parseBooleanValue({})).toThrow( + jasmine.stringMatching("is not a valid Boolean") + ); + expect(() => parseBooleanValue([])).toThrow( + jasmine.stringMatching("is not a valid Boolean") + ); expect(() => parseBooleanValue(123)).toThrow( - jasmine.stringMatching('is not a valid Boolean') + jasmine.stringMatching("is not a valid Boolean") ); - expect(() => parseBooleanValue('true')).toThrow( - jasmine.stringMatching('is not a valid Boolean') + expect(() => parseBooleanValue("true")).toThrow( + jasmine.stringMatching("is not a valid Boolean") ); }); }); - describe('parseDateValue', () => { - it('should parse to date if a string', () => { - const myDateString = '2019-05-09T23:12:00.000Z'; + describe("parseDateValue", () => { + it("should parse to date if a string", () => { + const myDateString = "2019-05-09T23:12:00.000Z"; const myDate = new Date(Date.UTC(2019, 4, 9, 23, 12, 0, 0)); expect(parseDateIsoValue(myDateString)).toEqual(myDate); }); - it('should fail if not a string', () => { - expect(() => parseDateIsoValue()).toThrow(jasmine.stringMatching('is not a valid Date')); - expect(() => parseDateIsoValue({})).toThrow(jasmine.stringMatching('is not a valid Date')); - expect(() => parseDateIsoValue([])).toThrow(jasmine.stringMatching('is not a valid Date')); - expect(() => parseDateIsoValue(123)).toThrow(jasmine.stringMatching('is not a valid Date')); + it("should fail if not a string", () => { + expect(() => parseDateIsoValue()).toThrow( + jasmine.stringMatching("is not a valid Date") + ); + expect(() => parseDateIsoValue({})).toThrow( + jasmine.stringMatching("is not a valid Date") + ); + expect(() => parseDateIsoValue([])).toThrow( + jasmine.stringMatching("is not a valid Date") + ); + expect(() => parseDateIsoValue(123)).toThrow( + jasmine.stringMatching("is not a valid Date") + ); }); - it('should fail if not a date string', () => { - expect(() => parseDateIsoValue('not a date')).toThrow( - jasmine.stringMatching('is not a valid Date') + it("should fail if not a date string", () => { + expect(() => parseDateIsoValue("not a date")).toThrow( + jasmine.stringMatching("is not a valid Date") ); }); }); - describe('parseValue', () => { - const someString = createValue(Kind.STRING, 'somestring'); - const someInt = createValue(Kind.INT, '123'); - const someFloat = createValue(Kind.FLOAT, '123.4'); + describe("parseValue", () => { + const someString = createValue(Kind.STRING, "somestring"); + const someInt = createValue(Kind.INT, "123"); + const someFloat = createValue(Kind.FLOAT, "123.4"); const someBoolean = createValue(Kind.BOOLEAN, true); const someOther = createValue(undefined, new Object()); const someObject = createValue(Kind.OBJECT, undefined, undefined, [ - createObjectField('someString', someString), - createObjectField('someInt', someInt), - createObjectField('someFloat', someFloat), - createObjectField('someBoolean', someBoolean), - createObjectField('someOther', someOther), + createObjectField("someString", someString), + createObjectField("someInt", someInt), + createObjectField("someFloat", someFloat), + createObjectField("someBoolean", someBoolean), + createObjectField("someOther", someOther), createObjectField( - 'someList', + "someList", createValue(Kind.LIST, undefined, [ createValue(Kind.OBJECT, undefined, undefined, [ - createObjectField('someString', someString), + createObjectField("someString", someString), ]), ]) ), createObjectField( - 'someObject', + "someObject", createValue(Kind.OBJECT, undefined, undefined, [ - createObjectField('someString', someString), + createObjectField("someString", someString), ]) ), ]); @@ -177,62 +224,62 @@ describe('defaultGraphQLTypes', () => { ]), ]); - it('should parse string', () => { - expect(parseValue(someString)).toEqual('somestring'); + it("should parse string", () => { + expect(parseValue(someString)).toEqual("somestring"); }); - it('should parse int', () => { + it("should parse int", () => { expect(parseValue(someInt)).toEqual(123); }); - it('should parse float', () => { + it("should parse float", () => { expect(parseValue(someFloat)).toEqual(123.4); }); - it('should parse boolean', () => { + it("should parse boolean", () => { expect(parseValue(someBoolean)).toEqual(true); }); - it('should parse list', () => { + it("should parse list", () => { expect(parseValue(someList)).toEqual([ - 'somestring', + "somestring", 123, 123.4, true, { - someString: 'somestring', + someString: "somestring", someInt: 123, someFloat: 123.4, someBoolean: true, someOther: {}, someList: [ { - someString: 'somestring', + someString: "somestring", }, ], someObject: { - someString: 'somestring', + someString: "somestring", }, }, {}, [ - 'somestring', + "somestring", 123, 123.4, true, { - someString: 'somestring', + someString: "somestring", someInt: 123, someFloat: 123.4, someBoolean: true, someOther: {}, someList: [ { - someString: 'somestring', + someString: "somestring", }, ], someObject: { - someString: 'somestring', + someString: "somestring", }, }, {}, @@ -240,368 +287,428 @@ describe('defaultGraphQLTypes', () => { ]); }); - it('should parse object', () => { + it("should parse object", () => { expect(parseValue(someObject)).toEqual({ - someString: 'somestring', + someString: "somestring", someInt: 123, someFloat: 123.4, someBoolean: true, someOther: {}, someList: [ { - someString: 'somestring', + someString: "somestring", }, ], someObject: { - someString: 'somestring', + someString: "somestring", }, }); }); - it('should return value otherwise', () => { + it("should return value otherwise", () => { expect(parseValue(someOther)).toEqual(new Object()); }); }); - describe('parseListValues', () => { - it('should parse to list if an array', () => { + describe("parseListValues", () => { + it("should parse to list if an array", () => { expect( parseListValues([ - { kind: Kind.STRING, value: 'someString' }, - { kind: Kind.INT, value: '123' }, + { kind: Kind.STRING, value: "someString" }, + { kind: Kind.INT, value: "123" }, ]) - ).toEqual(['someString', 123]); + ).toEqual(["someString", 123]); }); - it('should fail if not an array', () => { - expect(() => parseListValues()).toThrow(jasmine.stringMatching('is not a valid List')); - expect(() => parseListValues({})).toThrow(jasmine.stringMatching('is not a valid List')); - expect(() => parseListValues('some string')).toThrow( - jasmine.stringMatching('is not a valid List') + it("should fail if not an array", () => { + expect(() => parseListValues()).toThrow( + jasmine.stringMatching("is not a valid List") + ); + expect(() => parseListValues({})).toThrow( + jasmine.stringMatching("is not a valid List") + ); + expect(() => parseListValues("some string")).toThrow( + jasmine.stringMatching("is not a valid List") + ); + expect(() => parseListValues(123)).toThrow( + jasmine.stringMatching("is not a valid List") ); - expect(() => parseListValues(123)).toThrow(jasmine.stringMatching('is not a valid List')); }); }); - describe('parseObjectFields', () => { - it('should parse to list if an array', () => { + describe("parseObjectFields", () => { + it("should parse to list if an array", () => { expect( parseObjectFields([ { - name: { value: 'someString' }, - value: { kind: Kind.STRING, value: 'someString' }, + name: { value: "someString" }, + value: { kind: Kind.STRING, value: "someString" }, }, { - name: { value: 'someInt' }, - value: { kind: Kind.INT, value: '123' }, + name: { value: "someInt" }, + value: { kind: Kind.INT, value: "123" }, }, ]) ).toEqual({ - someString: 'someString', + someString: "someString", someInt: 123, }); }); - it('should fail if not an array', () => { - expect(() => parseObjectFields()).toThrow(jasmine.stringMatching('is not a valid Object')); - expect(() => parseObjectFields({})).toThrow(jasmine.stringMatching('is not a valid Object')); - expect(() => parseObjectFields('some string')).toThrow( - jasmine.stringMatching('is not a valid Object') + it("should fail if not an array", () => { + expect(() => parseObjectFields()).toThrow( + jasmine.stringMatching("is not a valid Object") + ); + expect(() => parseObjectFields({})).toThrow( + jasmine.stringMatching("is not a valid Object") + ); + expect(() => parseObjectFields("some string")).toThrow( + jasmine.stringMatching("is not a valid Object") + ); + expect(() => parseObjectFields(123)).toThrow( + jasmine.stringMatching("is not a valid Object") ); - expect(() => parseObjectFields(123)).toThrow(jasmine.stringMatching('is not a valid Object')); }); }); - describe('Date', () => { - describe('parse literal', () => { + describe("Date", () => { + describe("parse literal", () => { const { parseLiteral } = DATE; - it('should parse to date if string', () => { - const date = '2019-05-09T23:12:00.000Z'; + it("should parse to date if string", () => { + const date = "2019-05-09T23:12:00.000Z"; expect(parseLiteral(createValue(Kind.STRING, date))).toEqual({ - __type: 'Date', + __type: "Date", iso: new Date(date), }); }); - it('should parse to date if object', () => { - const date = '2019-05-09T23:12:00.000Z'; + it("should parse to date if object", () => { + const date = "2019-05-09T23:12:00.000Z"; expect( parseLiteral( createValue(Kind.OBJECT, undefined, undefined, [ - createObjectField('__type', { value: 'Date' }), - createObjectField('iso', { value: date, kind: Kind.STRING }), + createObjectField("__type", { value: "Date" }), + createObjectField("iso", { value: date, kind: Kind.STRING }), ]) ) ).toEqual({ - __type: 'Date', + __type: "Date", iso: new Date(date), }); }); - it('should fail if not an valid object or string', () => { - expect(() => parseLiteral({})).toThrow(jasmine.stringMatching('is not a valid Date')); + it("should fail if not an valid object or string", () => { + expect(() => parseLiteral({})).toThrow( + jasmine.stringMatching("is not a valid Date") + ); expect(() => parseLiteral( createValue(Kind.OBJECT, undefined, undefined, [ - createObjectField('__type', { value: 'Foo' }), - createObjectField('iso', { value: '2019-05-09T23:12:00.000Z' }), + createObjectField("__type", { value: "Foo" }), + createObjectField("iso", { value: "2019-05-09T23:12:00.000Z" }), ]) ) - ).toThrow(jasmine.stringMatching('is not a valid Date')); - expect(() => parseLiteral([])).toThrow(jasmine.stringMatching('is not a valid Date')); - expect(() => parseLiteral(123)).toThrow(jasmine.stringMatching('is not a valid Date')); + ).toThrow(jasmine.stringMatching("is not a valid Date")); + expect(() => parseLiteral([])).toThrow( + jasmine.stringMatching("is not a valid Date") + ); + expect(() => parseLiteral(123)).toThrow( + jasmine.stringMatching("is not a valid Date") + ); }); }); - describe('parse value', () => { + describe("parse value", () => { const { parseValue } = DATE; - it('should parse string value', () => { - const date = '2019-05-09T23:12:00.000Z'; + it("should parse string value", () => { + const date = "2019-05-09T23:12:00.000Z"; expect(parseValue(date)).toEqual({ - __type: 'Date', + __type: "Date", iso: new Date(date), }); }); - it('should parse object value', () => { + it("should parse object value", () => { const input = { - __type: 'Date', - iso: new Date('2019-05-09T23:12:00.000Z'), + __type: "Date", + iso: new Date("2019-05-09T23:12:00.000Z"), }; expect(parseValue(input)).toEqual(input); }); - it('should fail if not an valid object or string', () => { - expect(() => parseValue({})).toThrow(jasmine.stringMatching('is not a valid Date')); + it("should fail if not an valid object or string", () => { + expect(() => parseValue({})).toThrow( + jasmine.stringMatching("is not a valid Date") + ); expect(() => parseValue({ - __type: 'Foo', - iso: '2019-05-09T23:12:00.000Z', + __type: "Foo", + iso: "2019-05-09T23:12:00.000Z", }) - ).toThrow(jasmine.stringMatching('is not a valid Date')); + ).toThrow(jasmine.stringMatching("is not a valid Date")); expect(() => parseValue({ - __type: 'Date', - iso: 'foo', + __type: "Date", + iso: "foo", }) - ).toThrow(jasmine.stringMatching('is not a valid Date')); - expect(() => parseValue([])).toThrow(jasmine.stringMatching('is not a valid Date')); - expect(() => parseValue(123)).toThrow(jasmine.stringMatching('is not a valid Date')); + ).toThrow(jasmine.stringMatching("is not a valid Date")); + expect(() => parseValue([])).toThrow( + jasmine.stringMatching("is not a valid Date") + ); + expect(() => parseValue(123)).toThrow( + jasmine.stringMatching("is not a valid Date") + ); }); }); - describe('serialize date type', () => { + describe("serialize date type", () => { const { serialize } = DATE; - it('should do nothing if string', () => { - const str = '2019-05-09T23:12:00.000Z'; + it("should do nothing if string", () => { + const str = "2019-05-09T23:12:00.000Z"; expect(serialize(str)).toBe(str); }); - it('should serialize date', () => { + it("should serialize date", () => { const date = new Date(); expect(serialize(date)).toBe(date.toISOString()); }); - it('should return iso value if object', () => { - const iso = '2019-05-09T23:12:00.000Z'; + it("should return iso value if object", () => { + const iso = "2019-05-09T23:12:00.000Z"; const date = { - __type: 'Date', + __type: "Date", iso, }; expect(serialize(date)).toEqual(iso); }); - it('should fail if not an valid object or string', () => { - expect(() => serialize({})).toThrow(jasmine.stringMatching('is not a valid Date')); + it("should fail if not an valid object or string", () => { + expect(() => serialize({})).toThrow( + jasmine.stringMatching("is not a valid Date") + ); expect(() => serialize({ - __type: 'Foo', - iso: '2019-05-09T23:12:00.000Z', + __type: "Foo", + iso: "2019-05-09T23:12:00.000Z", }) - ).toThrow(jasmine.stringMatching('is not a valid Date')); - expect(() => serialize([])).toThrow(jasmine.stringMatching('is not a valid Date')); - expect(() => serialize(123)).toThrow(jasmine.stringMatching('is not a valid Date')); + ).toThrow(jasmine.stringMatching("is not a valid Date")); + expect(() => serialize([])).toThrow( + jasmine.stringMatching("is not a valid Date") + ); + expect(() => serialize(123)).toThrow( + jasmine.stringMatching("is not a valid Date") + ); }); }); }); - describe('Bytes', () => { - describe('parse literal', () => { + describe("Bytes", () => { + describe("parse literal", () => { const { parseLiteral } = BYTES; - it('should parse to bytes if string', () => { - expect(parseLiteral(createValue(Kind.STRING, 'bytesContent'))).toEqual({ - __type: 'Bytes', - base64: 'bytesContent', + it("should parse to bytes if string", () => { + expect(parseLiteral(createValue(Kind.STRING, "bytesContent"))).toEqual({ + __type: "Bytes", + base64: "bytesContent", }); }); - it('should parse to bytes if object', () => { + it("should parse to bytes if object", () => { expect( parseLiteral( createValue(Kind.OBJECT, undefined, undefined, [ - createObjectField('__type', { value: 'Bytes' }), - createObjectField('base64', { value: 'bytesContent' }), + createObjectField("__type", { value: "Bytes" }), + createObjectField("base64", { value: "bytesContent" }), ]) ) ).toEqual({ - __type: 'Bytes', - base64: 'bytesContent', + __type: "Bytes", + base64: "bytesContent", }); }); - it('should fail if not an valid object or string', () => { - expect(() => parseLiteral({})).toThrow(jasmine.stringMatching('is not a valid Bytes')); + it("should fail if not an valid object or string", () => { + expect(() => parseLiteral({})).toThrow( + jasmine.stringMatching("is not a valid Bytes") + ); expect(() => parseLiteral( createValue(Kind.OBJECT, undefined, undefined, [ - createObjectField('__type', { value: 'Foo' }), - createObjectField('base64', { value: 'bytesContent' }), + createObjectField("__type", { value: "Foo" }), + createObjectField("base64", { value: "bytesContent" }), ]) ) - ).toThrow(jasmine.stringMatching('is not a valid Bytes')); - expect(() => parseLiteral([])).toThrow(jasmine.stringMatching('is not a valid Bytes')); - expect(() => parseLiteral(123)).toThrow(jasmine.stringMatching('is not a valid Bytes')); + ).toThrow(jasmine.stringMatching("is not a valid Bytes")); + expect(() => parseLiteral([])).toThrow( + jasmine.stringMatching("is not a valid Bytes") + ); + expect(() => parseLiteral(123)).toThrow( + jasmine.stringMatching("is not a valid Bytes") + ); }); }); - describe('parse value', () => { + describe("parse value", () => { const { parseValue } = BYTES; - it('should parse string value', () => { - expect(parseValue('bytesContent')).toEqual({ - __type: 'Bytes', - base64: 'bytesContent', + it("should parse string value", () => { + expect(parseValue("bytesContent")).toEqual({ + __type: "Bytes", + base64: "bytesContent", }); }); - it('should parse object value', () => { + it("should parse object value", () => { const input = { - __type: 'Bytes', - base64: 'bytesContent', + __type: "Bytes", + base64: "bytesContent", }; expect(parseValue(input)).toEqual(input); }); - it('should fail if not an valid object or string', () => { - expect(() => parseValue({})).toThrow(jasmine.stringMatching('is not a valid Bytes')); + it("should fail if not an valid object or string", () => { + expect(() => parseValue({})).toThrow( + jasmine.stringMatching("is not a valid Bytes") + ); expect(() => parseValue({ - __type: 'Foo', - base64: 'bytesContent', + __type: "Foo", + base64: "bytesContent", }) - ).toThrow(jasmine.stringMatching('is not a valid Bytes')); - expect(() => parseValue([])).toThrow(jasmine.stringMatching('is not a valid Bytes')); - expect(() => parseValue(123)).toThrow(jasmine.stringMatching('is not a valid Bytes')); + ).toThrow(jasmine.stringMatching("is not a valid Bytes")); + expect(() => parseValue([])).toThrow( + jasmine.stringMatching("is not a valid Bytes") + ); + expect(() => parseValue(123)).toThrow( + jasmine.stringMatching("is not a valid Bytes") + ); }); }); - describe('serialize bytes type', () => { + describe("serialize bytes type", () => { const { serialize } = BYTES; - it('should do nothing if string', () => { - const str = 'foo'; + it("should do nothing if string", () => { + const str = "foo"; expect(serialize(str)).toBe(str); }); - it('should return base64 value if object', () => { - const base64Content = 'bytesContent'; + it("should return base64 value if object", () => { + const base64Content = "bytesContent"; const bytes = { - __type: 'Bytes', + __type: "Bytes", base64: base64Content, }; expect(serialize(bytes)).toEqual(base64Content); }); - it('should fail if not an valid object or string', () => { - expect(() => serialize({})).toThrow(jasmine.stringMatching('is not a valid Bytes')); + it("should fail if not an valid object or string", () => { + expect(() => serialize({})).toThrow( + jasmine.stringMatching("is not a valid Bytes") + ); expect(() => serialize({ - __type: 'Foo', - base64: 'bytesContent', + __type: "Foo", + base64: "bytesContent", }) - ).toThrow(jasmine.stringMatching('is not a valid Bytes')); - expect(() => serialize([])).toThrow(jasmine.stringMatching('is not a valid Bytes')); - expect(() => serialize(123)).toThrow(jasmine.stringMatching('is not a valid Bytes')); + ).toThrow(jasmine.stringMatching("is not a valid Bytes")); + expect(() => serialize([])).toThrow( + jasmine.stringMatching("is not a valid Bytes") + ); + expect(() => serialize(123)).toThrow( + jasmine.stringMatching("is not a valid Bytes") + ); }); }); }); - describe('File', () => { - describe('parse literal', () => { + describe("File", () => { + describe("parse literal", () => { const { parseLiteral } = FILE; - it('should parse to file if string', () => { - expect(parseLiteral(createValue(Kind.STRING, 'parsefile'))).toEqual({ - __type: 'File', - name: 'parsefile', + it("should parse to file if string", () => { + expect(parseLiteral(createValue(Kind.STRING, "parsefile"))).toEqual({ + __type: "File", + name: "parsefile", }); }); - it('should parse to file if object', () => { + it("should parse to file if object", () => { expect( parseLiteral( createValue(Kind.OBJECT, undefined, undefined, [ - createObjectField('__type', { value: 'File' }), - createObjectField('name', { value: 'parsefile' }), - createObjectField('url', { value: 'myurl' }), + createObjectField("__type", { value: "File" }), + createObjectField("name", { value: "parsefile" }), + createObjectField("url", { value: "myurl" }), ]) ) ).toEqual({ - __type: 'File', - name: 'parsefile', - url: 'myurl', + __type: "File", + name: "parsefile", + url: "myurl", }); }); - it('should fail if not an valid object or string', () => { - expect(() => parseLiteral({})).toThrow(jasmine.stringMatching('is not a valid File')); + it("should fail if not an valid object or string", () => { + expect(() => parseLiteral({})).toThrow( + jasmine.stringMatching("is not a valid File") + ); expect(() => parseLiteral( createValue(Kind.OBJECT, undefined, undefined, [ - createObjectField('__type', { value: 'Foo' }), - createObjectField('name', { value: 'parsefile' }), - createObjectField('url', { value: 'myurl' }), + createObjectField("__type", { value: "Foo" }), + createObjectField("name", { value: "parsefile" }), + createObjectField("url", { value: "myurl" }), ]) ) - ).toThrow(jasmine.stringMatching('is not a valid File')); - expect(() => parseLiteral([])).toThrow(jasmine.stringMatching('is not a valid File')); - expect(() => parseLiteral(123)).toThrow(jasmine.stringMatching('is not a valid File')); + ).toThrow(jasmine.stringMatching("is not a valid File")); + expect(() => parseLiteral([])).toThrow( + jasmine.stringMatching("is not a valid File") + ); + expect(() => parseLiteral(123)).toThrow( + jasmine.stringMatching("is not a valid File") + ); }); }); - describe('serialize file type', () => { + describe("serialize file type", () => { const { serialize } = FILE; - it('should do nothing if string', () => { - const str = 'foo'; + it("should do nothing if string", () => { + const str = "foo"; expect(serialize(str)).toBe(str); }); - it('should return file name if object', () => { - const fileName = 'parsefile'; + it("should return file name if object", () => { + const fileName = "parsefile"; const file = { - __type: 'File', + __type: "File", name: fileName, - url: 'myurl', + url: "myurl", }; expect(serialize(file)).toEqual(fileName); }); - it('should fail if not an valid object or string', () => { - expect(() => serialize({})).toThrow(jasmine.stringMatching('is not a valid File')); + it("should fail if not an valid object or string", () => { + expect(() => serialize({})).toThrow( + jasmine.stringMatching("is not a valid File") + ); expect(() => serialize({ - __type: 'Foo', - name: 'parsefile', - url: 'myurl', + __type: "Foo", + name: "parsefile", + url: "myurl", }) - ).toThrow(jasmine.stringMatching('is not a valid File')); - expect(() => serialize([])).toThrow(jasmine.stringMatching('is not a valid File')); - expect(() => serialize(123)).toThrow(jasmine.stringMatching('is not a valid File')); + ).toThrow(jasmine.stringMatching("is not a valid File")); + expect(() => serialize([])).toThrow( + jasmine.stringMatching("is not a valid File") + ); + expect(() => serialize(123)).toThrow( + jasmine.stringMatching("is not a valid File") + ); }); }); }); diff --git a/spec/eslint.config.js b/spec/eslint.config.js index e870d91642..2f8faa2aec 100644 --- a/spec/eslint.config.js +++ b/spec/eslint.config.js @@ -44,7 +44,7 @@ module.exports = [ create: "readonly", arrayContains: "readonly", databaseAdapter: "readonly", - databaseURI: "readonly" + databaseURI: "readonly", }, }, rules: { @@ -52,6 +52,6 @@ module.exports = [ "no-var": "error", "no-unused-vars": "off", "no-useless-escape": "off", - } + }, }, ]; diff --git a/spec/features.spec.js b/spec/features.spec.js index f138fe4cf6..435e3890a9 100644 --- a/spec/features.spec.js +++ b/spec/features.spec.js @@ -1,16 +1,16 @@ -'use strict'; +"use strict"; -const request = require('../lib/request'); +const request = require("../lib/request"); -describe('features', () => { - it('should return the serverInfo', async () => { +describe("features", () => { + it("should return the serverInfo", async () => { const response = await request({ - url: 'http://localhost:8378/1/serverInfo', + url: "http://localhost:8378/1/serverInfo", json: true, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Master-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "X-Parse-Master-Key": "test", }, }); const data = response.data; @@ -19,20 +19,22 @@ describe('features', () => { expect(data.parseServerVersion).toBeDefined(); }); - it('requires the master key to get features', async done => { + it("requires the master key to get features", async done => { try { await request({ - url: 'http://localhost:8378/1/serverInfo', + url: "http://localhost:8378/1/serverInfo", json: true, headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }, }); - done.fail('The serverInfo request should be rejected without the master key'); + done.fail( + "The serverInfo request should be rejected without the master key" + ); } catch (error) { expect(error.status).toEqual(403); - expect(error.data.error).toEqual('unauthorized: master key is required'); + expect(error.data.error).toEqual("unauthorized: master key is required"); done(); } }); diff --git a/spec/graphQLObjectsQueries.js b/spec/graphQLObjectsQueries.js index f8783c67f8..ccf682b8ad 100644 --- a/spec/graphQLObjectsQueries.js +++ b/spec/graphQLObjectsQueries.js @@ -1,27 +1,29 @@ -const { offsetToCursor } = require('graphql-relay'); -const { calculateSkipAndLimit } = require('../lib/GraphQL/helpers/objectsQueries'); +const { offsetToCursor } = require("graphql-relay"); +const { + calculateSkipAndLimit, +} = require("../lib/GraphQL/helpers/objectsQueries"); -describe('GraphQL objectsQueries', () => { - describe('calculateSkipAndLimit', () => { - it('should fail with invalid params', () => { +describe("GraphQL objectsQueries", () => { + describe("calculateSkipAndLimit", () => { + it("should fail with invalid params", () => { expect(() => calculateSkipAndLimit(-1)).toThrow( - jasmine.stringMatching('Skip should be a positive number') + jasmine.stringMatching("Skip should be a positive number") ); expect(() => calculateSkipAndLimit(1, -1)).toThrow( - jasmine.stringMatching('First should be a positive number') + jasmine.stringMatching("First should be a positive number") ); expect(() => calculateSkipAndLimit(1, 1, offsetToCursor(-1))).toThrow( - jasmine.stringMatching('After is not a valid curso') + jasmine.stringMatching("After is not a valid curso") ); expect(() => calculateSkipAndLimit(1, 1, offsetToCursor(1), -1)).toThrow( - jasmine.stringMatching('Last should be a positive number') - ); - expect(() => calculateSkipAndLimit(1, 1, offsetToCursor(1), 1, offsetToCursor(-1))).toThrow( - jasmine.stringMatching('Before is not a valid curso') + jasmine.stringMatching("Last should be a positive number") ); + expect(() => + calculateSkipAndLimit(1, 1, offsetToCursor(1), 1, offsetToCursor(-1)) + ).toThrow(jasmine.stringMatching("Before is not a valid curso")); }); - it('should work only with skip', () => { + it("should work only with skip", () => { expect(calculateSkipAndLimit(10)).toEqual({ skip: 10, limit: undefined, @@ -29,15 +31,17 @@ describe('GraphQL objectsQueries', () => { }); }); - it('should work only with after', () => { - expect(calculateSkipAndLimit(undefined, undefined, offsetToCursor(9))).toEqual({ + it("should work only with after", () => { + expect( + calculateSkipAndLimit(undefined, undefined, offsetToCursor(9)) + ).toEqual({ skip: 10, limit: undefined, needToPreCount: false, }); }); - it('should work with limit and after', () => { + it("should work with limit and after", () => { expect(calculateSkipAndLimit(10, undefined, offsetToCursor(9))).toEqual({ skip: 20, limit: undefined, @@ -45,7 +49,7 @@ describe('GraphQL objectsQueries', () => { }); }); - it('first alone should set the limit', () => { + it("first alone should set the limit", () => { expect(calculateSkipAndLimit(10, 30, offsetToCursor(9))).toEqual({ skip: 20, limit: 30, @@ -53,9 +57,15 @@ describe('GraphQL objectsQueries', () => { }); }); - it('if before cursor is less than skipped items, no objects will be returned', () => { + it("if before cursor is less than skipped items, no objects will be returned", () => { expect( - calculateSkipAndLimit(10, 30, offsetToCursor(9), undefined, offsetToCursor(5)) + calculateSkipAndLimit( + 10, + 30, + offsetToCursor(9), + undefined, + offsetToCursor(5) + ) ).toEqual({ skip: 20, limit: 0, @@ -63,9 +73,15 @@ describe('GraphQL objectsQueries', () => { }); }); - it('if before cursor is greater than returned objects set by limit, nothing is changed', () => { + it("if before cursor is greater than returned objects set by limit, nothing is changed", () => { expect( - calculateSkipAndLimit(10, 30, offsetToCursor(9), undefined, offsetToCursor(100)) + calculateSkipAndLimit( + 10, + 30, + offsetToCursor(9), + undefined, + offsetToCursor(100) + ) ).toEqual({ skip: 20, limit: 30, @@ -73,9 +89,15 @@ describe('GraphQL objectsQueries', () => { }); }); - it('if before cursor is less than returned objects set by limit, limit is adjusted', () => { + it("if before cursor is less than returned objects set by limit, limit is adjusted", () => { expect( - calculateSkipAndLimit(10, 30, offsetToCursor(9), undefined, offsetToCursor(40)) + calculateSkipAndLimit( + 10, + 30, + offsetToCursor(9), + undefined, + offsetToCursor(40) + ) ).toEqual({ skip: 20, limit: 20, @@ -83,40 +105,50 @@ describe('GraphQL objectsQueries', () => { }); }); - it('last should work alone but requires pre count', () => { - expect(calculateSkipAndLimit(undefined, undefined, undefined, 10)).toEqual({ + it("last should work alone but requires pre count", () => { + expect( + calculateSkipAndLimit(undefined, undefined, undefined, 10) + ).toEqual({ skip: undefined, limit: 10, needToPreCount: true, }); }); - it('last should be adjusted to max limit', () => { - expect(calculateSkipAndLimit(undefined, undefined, undefined, 10, undefined, 5)).toEqual({ + it("last should be adjusted to max limit", () => { + expect( + calculateSkipAndLimit(undefined, undefined, undefined, 10, undefined, 5) + ).toEqual({ skip: undefined, limit: 5, needToPreCount: true, }); }); - it('no objects will be returned if last is equal to 0', () => { - expect(calculateSkipAndLimit(undefined, undefined, undefined, 0)).toEqual({ - skip: undefined, - limit: 0, - needToPreCount: false, - }); + it("no objects will be returned if last is equal to 0", () => { + expect(calculateSkipAndLimit(undefined, undefined, undefined, 0)).toEqual( + { + skip: undefined, + limit: 0, + needToPreCount: false, + } + ); }); - it('nothing changes if last is bigger than the calculared limit', () => { - expect(calculateSkipAndLimit(10, 30, offsetToCursor(9), 30, offsetToCursor(40))).toEqual({ + it("nothing changes if last is bigger than the calculared limit", () => { + expect( + calculateSkipAndLimit(10, 30, offsetToCursor(9), 30, offsetToCursor(40)) + ).toEqual({ skip: 20, limit: 20, needToPreCount: false, }); }); - it('If last is small than limit, new limit is calculated', () => { - expect(calculateSkipAndLimit(10, 30, offsetToCursor(9), 10, offsetToCursor(40))).toEqual({ + it("If last is small than limit, new limit is calculated", () => { + expect( + calculateSkipAndLimit(10, 30, offsetToCursor(9), 10, offsetToCursor(40)) + ).toEqual({ skip: 30, limit: 10, needToPreCount: false, diff --git a/spec/helper.js b/spec/helper.js index 07e39f4113..0aec0a777f 100644 --- a/spec/helper.js +++ b/spec/helper.js @@ -1,19 +1,20 @@ -'use strict'; -const dns = require('dns'); -const semver = require('semver'); -const Parse = require('parse/node'); -const CurrentSpecReporter = require('./support/CurrentSpecReporter.js'); -const { SpecReporter } = require('jasmine-spec-reporter'); -const SchemaCache = require('../lib/Adapters/Cache/SchemaCache').default; -const { sleep, Connections } = require('../lib/TestUtils'); +"use strict"; +const dns = require("dns"); +const semver = require("semver"); +const Parse = require("parse/node"); +const CurrentSpecReporter = require("./support/CurrentSpecReporter.js"); +const { SpecReporter } = require("jasmine-spec-reporter"); +const SchemaCache = require("../lib/Adapters/Cache/SchemaCache").default; +const { sleep, Connections } = require("../lib/TestUtils"); // Ensure localhost resolves to ipv4 address first on node v17+ if (dns.setDefaultResultOrder) { - dns.setDefaultResultOrder('ipv4first'); + dns.setDefaultResultOrder("ipv4first"); } // Sets up a Parse API server for testing. -jasmine.DEFAULT_TIMEOUT_INTERVAL = process.env.PARSE_SERVER_TEST_TIMEOUT || 10000; +jasmine.DEFAULT_TIMEOUT_INTERVAL = + process.env.PARSE_SERVER_TEST_TIMEOUT || 10000; jasmine.getEnv().addReporter(new CurrentSpecReporter()); jasmine.getEnv().addReporter(new SpecReporter()); global.retryFlakyTests(); @@ -21,7 +22,7 @@ global.retryFlakyTests(); global.on_db = (db, callback, elseCallback) => { if (process.env.PARSE_SERVER_TEST_DB == db) { return callback(); - } else if (!process.env.PARSE_SERVER_TEST_DB && db == 'mongo') { + } else if (!process.env.PARSE_SERVER_TEST_DB && db == "mongo") { return callback(); } if (elseCallback) { @@ -30,46 +31,52 @@ global.on_db = (db, callback, elseCallback) => { }; if (global._babelPolyfill) { - console.error('We should not use polyfilled tests'); + console.error("We should not use polyfilled tests"); process.exit(1); } process.noDeprecation = true; -const cache = require('../lib/cache').default; -const defaults = require('../lib/defaults').default; -const ParseServer = require('../lib/index').ParseServer; -const loadAdapter = require('../lib/Adapters/AdapterLoader').loadAdapter; -const path = require('path'); -const TestUtils = require('../lib/TestUtils'); +const cache = require("../lib/cache").default; +const defaults = require("../lib/defaults").default; +const ParseServer = require("../lib/index").ParseServer; +const loadAdapter = require("../lib/Adapters/AdapterLoader").loadAdapter; +const path = require("path"); +const TestUtils = require("../lib/TestUtils"); const GridFSBucketAdapter = - require('../lib/Adapters/Files/GridFSBucketAdapter').GridFSBucketAdapter; -const FSAdapter = require('@parse/fs-files-adapter'); + require("../lib/Adapters/Files/GridFSBucketAdapter").GridFSBucketAdapter; +const FSAdapter = require("@parse/fs-files-adapter"); const PostgresStorageAdapter = - require('../lib/Adapters/Storage/Postgres/PostgresStorageAdapter').default; -const MongoStorageAdapter = require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter').default; -const RedisCacheAdapter = require('../lib/Adapters/Cache/RedisCacheAdapter').default; -const RESTController = require('parse/lib/node/RESTController').default; -const { VolatileClassesSchemas } = require('../lib/Controllers/SchemaController'); - -const mongoURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase'; -const postgresURI = 'postgres://localhost:5432/parse_server_postgres_adapter_test_database'; + require("../lib/Adapters/Storage/Postgres/PostgresStorageAdapter").default; +const MongoStorageAdapter = + require("../lib/Adapters/Storage/Mongo/MongoStorageAdapter").default; +const RedisCacheAdapter = + require("../lib/Adapters/Cache/RedisCacheAdapter").default; +const RESTController = require("parse/lib/node/RESTController").default; +const { + VolatileClassesSchemas, +} = require("../lib/Controllers/SchemaController"); + +const mongoURI = + "mongodb://localhost:27017/parseServerMongoAdapterTestDatabase"; +const postgresURI = + "postgres://localhost:5432/parse_server_postgres_adapter_test_database"; let databaseAdapter; let databaseURI; if (process.env.PARSE_SERVER_DATABASE_ADAPTER) { databaseAdapter = JSON.parse(process.env.PARSE_SERVER_DATABASE_ADAPTER); databaseAdapter = loadAdapter(databaseAdapter); -} else if (process.env.PARSE_SERVER_TEST_DB === 'postgres') { +} else if (process.env.PARSE_SERVER_TEST_DB === "postgres") { databaseURI = process.env.PARSE_SERVER_TEST_DATABASE_URI || postgresURI; databaseAdapter = new PostgresStorageAdapter({ uri: databaseURI, - collectionPrefix: 'test_', + collectionPrefix: "test_", }); } else { databaseURI = mongoURI; databaseAdapter = new MongoStorageAdapter({ uri: databaseURI, - collectionPrefix: 'test_', + collectionPrefix: "test_", }); } @@ -78,7 +85,7 @@ const serverURL = `http://localhost:${port}/1`; let filesAdapter; on_db( - 'mongo', + "mongo", () => { filesAdapter = new GridFSBucketAdapter(mongoURI); }, @@ -91,7 +98,7 @@ let logLevel; let silent = true; if (process.env.VERBOSE) { silent = false; - logLevel = 'verbose'; + logLevel = "verbose"; } if (process.env.PARSE_SERVER_LOG_LEVEL) { silent = false; @@ -102,22 +109,22 @@ const defaultConfiguration = { filesAdapter, serverURL, databaseAdapter, - appId: 'test', - javascriptKey: 'test', - dotNetKey: 'windows', - clientKey: 'client', - restAPIKey: 'rest', - webhookKey: 'hook', - masterKey: 'test', - maintenanceKey: 'testing', - readOnlyMasterKey: 'read-only-test', - fileKey: 'test', + appId: "test", + javascriptKey: "test", + dotNetKey: "windows", + clientKey: "client", + restAPIKey: "rest", + webhookKey: "hook", + masterKey: "test", + maintenanceKey: "testing", + readOnlyMasterKey: "read-only-test", + fileKey: "test", directAccess: true, silent, verbose: !silent, logLevel, liveQuery: { - classNames: ['TestObject'], + classNames: ["TestObject"], }, startLiveQueryServer: true, fileUpload: { @@ -127,8 +134,8 @@ const defaultConfiguration = { }, push: { android: { - senderId: 'yolo', - apiKey: 'yolo', + senderId: "yolo", + apiKey: "yolo", }, }, auth: { @@ -136,7 +143,7 @@ const defaultConfiguration = { custom: mockCustom(), facebook: mockFacebook(), myoauth: { - module: path.resolve(__dirname, 'support/myoauth'), // relative path as it's run from src + module: path.resolve(__dirname, "support/myoauth"), // relative path as it's run from src }, shortLivedAuth: mockShortLivedAuth(), }, @@ -146,11 +153,11 @@ const defaultConfiguration = { if (silent) { defaultConfiguration.logLevels = { - cloudFunctionSuccess: 'silent', - cloudFunctionError: 'silent', - triggerAfter: 'silent', - triggerBeforeError: 'silent', - triggerBeforeSuccess: 'silent', + cloudFunctionSuccess: "silent", + cloudFunctionError: "silent", + triggerAfter: "silent", + triggerBeforeError: "silent", + triggerBeforeSuccess: "silent", }; } @@ -159,11 +166,13 @@ let parseServer; let didChangeConfiguration = false; const openConnections = new Connections(); -const shutdownServer = async (_parseServer) => { +const shutdownServer = async _parseServer => { await _parseServer.handleShutdown(); // Connection close events are not immediate on node 10+, so wait a bit await sleep(0); - expect(openConnections.count() > 0).toBeFalsy(`There were ${openConnections.count()} open connections to the server left after the test finished`); + expect(openConnections.count() > 0).toBeFalsy( + `There were ${openConnections.count()} open connections to the server left after the test finished` + ); parseServer = undefined; }; @@ -176,29 +185,37 @@ const reconfigureServer = async (changedConfiguration = {}) => { didChangeConfiguration = Object.keys(changedConfiguration).length !== 0; databaseAdapter = new databaseAdapter.constructor({ uri: databaseURI, - collectionPrefix: 'test_', + collectionPrefix: "test_", }); defaultConfiguration.databaseAdapter = databaseAdapter; global.databaseAdapter = databaseAdapter; if (filesAdapter instanceof GridFSBucketAdapter) { defaultConfiguration.filesAdapter = new GridFSBucketAdapter(mongoURI); } - if (process.env.PARSE_SERVER_TEST_CACHE === 'redis') { + if (process.env.PARSE_SERVER_TEST_CACHE === "redis") { defaultConfiguration.cacheAdapter = new RedisCacheAdapter(); } - const newConfiguration = Object.assign({}, defaultConfiguration, changedConfiguration, { - mountPath: '/1', - port, - }); + const newConfiguration = Object.assign( + {}, + defaultConfiguration, + changedConfiguration, + { + mountPath: "/1", + port, + } + ); cache.clear(); parseServer = await ParseServer.startApp(newConfiguration); Parse.CoreManager.setRESTController(RESTController); - parseServer.expressApp.use('/1', err => { + parseServer.expressApp.use("/1", err => { console.error(err); - fail('should not call next'); + fail("should not call next"); }); openConnections.track(parseServer.server); - if (parseServer.liveQueryServer?.server && parseServer.liveQueryServer.server !== parseServer.server) { + if ( + parseServer.liveQueryServer?.server && + parseServer.liveQueryServer.server !== parseServer.server + ) { openConnections.track(parseServer.liveQueryServer.server); } return parseServer; @@ -206,16 +223,16 @@ const reconfigureServer = async (changedConfiguration = {}) => { beforeAll(async () => { await reconfigureServer(); - Parse.initialize('test', 'test', 'test'); + Parse.initialize("test", "test", "test"); Parse.serverURL = serverURL; Parse.User.enableUnsafeCurrentUser(); - Parse.CoreManager.set('REQUEST_ATTEMPT_LIMIT', 1); + Parse.CoreManager.set("REQUEST_ATTEMPT_LIMIT", 1); }); global.afterEachFn = async () => { Parse.Cloud._removeAllHooks(); Parse.CoreManager.getLiveQueryController().setDefaultLiveQueryClient(); - defaults.protectedFields = { _User: { '*': ['email'] } }; + defaults.protectedFields = { _User: { "*": ["email"] } }; const allSchemas = await databaseAdapter.getAllClasses().catch(() => []); @@ -223,17 +240,17 @@ global.afterEachFn = async () => { const className = schema.className; expect(className).toEqual({ asymmetricMatch: className => { - if (!className.startsWith('_')) { + if (!className.startsWith("_")) { return true; } return [ - '_User', - '_Installation', - '_Role', - '_Session', - '_Product', - '_Audience', - '_Idempotency', + "_User", + "_Installation", + "_Role", + "_Session", + "_Product", + "_Audience", + "_Idempotency", ].includes(className); }, }); @@ -255,13 +272,13 @@ afterAll(() => { }); const TestObject = Parse.Object.extend({ - className: 'TestObject', + className: "TestObject", }); const Item = Parse.Object.extend({ - className: 'Item', + className: "Item", }); const Container = Parse.Object.extend({ - className: 'Container', + className: "Container", }); // Convenience method to create a new TestObject with a callback @@ -272,8 +289,8 @@ function create(options, callback) { function createTestUser() { const user = new Parse.User(); - user.set('username', 'test'); - user.set('password', 'moon-y'); + user.set("username", "test"); + user.set("password", "moon-y"); return user.signUp(); } @@ -298,19 +315,19 @@ function arrayContains(arr, item) { // Normalizes a JSON object. function normalize(obj) { - if (obj === null || typeof obj !== 'object') { + if (obj === null || typeof obj !== "object") { return JSON.stringify(obj); } if (obj instanceof Array) { - return '[' + obj.map(normalize).join(', ') + ']'; + return "[" + obj.map(normalize).join(", ") + "]"; } - let answer = '{'; + let answer = "{"; for (const key of Object.keys(obj).sort()) { - answer += key + ': '; + answer += key + ": "; answer += normalize(obj[key]); - answer += ', '; + answer += ", "; } - answer += '}'; + answer += "}"; return answer; } @@ -333,7 +350,7 @@ function mockCustomAuthenticator(id, password) { if (authData.id === id && authData.password.startsWith(password)) { return Promise.resolve(); } - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'not validated'); + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, "not validated"); }; custom.validateAppId = function () { return Promise.resolve(); @@ -342,7 +359,7 @@ function mockCustomAuthenticator(id, password) { } function mockCustom() { - return mockCustomAuthenticator('fastrde', 'password'); + return mockCustomAuthenticator("fastrde", "password"); } function mockFacebookAuthenticator(id, token) { @@ -365,7 +382,7 @@ function mockFacebookAuthenticator(id, token) { } function mockFacebook() { - return mockFacebookAuthenticator('8675309', 'jenny'); + return mockFacebookAuthenticator("8675309", "jenny"); } function mockShortLivedAuth() { @@ -378,7 +395,7 @@ function mockShortLivedAuth() { if (authData.access_token == accessToken) { return Promise.resolve(); } else { - return Promise.reject('Invalid access token'); + return Promise.reject("Invalid access token"); } }; auth.validateAppId = function () { @@ -388,21 +405,23 @@ function mockShortLivedAuth() { } function mockFetch(mockResponses) { - global.fetch = jasmine.createSpy('fetch').and.callFake((url, options = {}) => { - options.method ||= 'GET'; - const mockResponse = mockResponses.find( - mock => mock.url === url && mock.method === options.method - ); - - if (mockResponse) { - return Promise.resolve(mockResponse.response); - } - - return Promise.resolve({ - ok: false, - statusText: 'Unknown URL or method', + global.fetch = jasmine + .createSpy("fetch") + .and.callFake((url, options = {}) => { + options.method ||= "GET"; + const mockResponse = mockResponses.find( + mock => mock.url === url && mock.method === options.method + ); + + if (mockResponse) { + return Promise.resolve(mockResponse.response); + } + + return Promise.resolve({ + ok: false, + statusText: "Unknown URL or method", + }); }); - }); } // This is polluting, but, it makes it way easier to directly port old tests. @@ -442,10 +461,12 @@ global.it_exclude_dbs = excluded => { let testExclusionList = []; try { // Fetch test exclusion list - testExclusionList = require('./testExclusionList.json'); - console.log(`Using test exclusion list with ${testExclusionList.length} entries`); + testExclusionList = require("./testExclusionList.json"); + console.log( + `Using test exclusion list with ${testExclusionList.length} entries` + ); } catch (error) { - if (error.code !== 'MODULE_NOT_FOUND') { + if (error.code !== "MODULE_NOT_FOUND") { throw error; } } @@ -467,7 +488,7 @@ global.it_id = id => { global.it_only_db = db => { if ( process.env.PARSE_SERVER_TEST_DB === db || - (!process.env.PARSE_SERVER_TEST_DB && db == 'mongo') + (!process.env.PARSE_SERVER_TEST_DB && db == "mongo") ) { return it; } else { @@ -477,7 +498,7 @@ global.it_only_db = db => { global.it_only_mongodb_version = version => { if (!semver.validRange(version)) { - throw new Error('Invalid version range'); + throw new Error("Invalid version range"); } const envVersion = process.env.MONGODB_VERSION; if (!envVersion || semver.satisfies(envVersion, version)) { @@ -489,7 +510,7 @@ global.it_only_mongodb_version = version => { global.it_only_postgres_version = version => { if (!semver.validRange(version)) { - throw new Error('Invalid version range'); + throw new Error("Invalid version range"); } const envVersion = process.env.POSTGRES_VERSION; if (!envVersion || semver.satisfies(envVersion, version)) { @@ -501,7 +522,7 @@ global.it_only_postgres_version = version => { global.it_only_node_version = version => { if (!semver.validRange(version)) { - throw new Error('Invalid version range'); + throw new Error("Invalid version range"); } const envVersion = process.version; if (!envVersion || semver.satisfies(envVersion, version)) { @@ -513,7 +534,7 @@ global.it_only_node_version = version => { global.fit_only_mongodb_version = version => { if (!semver.validRange(version)) { - throw new Error('Invalid version range'); + throw new Error("Invalid version range"); } const envVersion = process.env.MONGODB_VERSION; if (!envVersion || semver.satisfies(envVersion, version)) { @@ -525,7 +546,7 @@ global.fit_only_mongodb_version = version => { global.fit_only_postgres_version = version => { if (!semver.validRange(version)) { - throw new Error('Invalid version range'); + throw new Error("Invalid version range"); } const envVersion = process.env.POSTGRES_VERSION; if (!envVersion || semver.satisfies(envVersion, version)) { @@ -537,7 +558,7 @@ global.fit_only_postgres_version = version => { global.fit_only_node_version = version => { if (!semver.validRange(version)) { - throw new Error('Invalid version range'); + throw new Error("Invalid version range"); } const envVersion = process.version; if (!envVersion || semver.satisfies(envVersion, version)) { @@ -558,7 +579,7 @@ global.fit_exclude_dbs = excluded => { global.describe_only_db = db => { if (process.env.PARSE_SERVER_TEST_DB == db) { return describe; - } else if (!process.env.PARSE_SERVER_TEST_DB && db == 'mongo') { + } else if (!process.env.PARSE_SERVER_TEST_DB && db == "mongo") { return describe; } else { return xdescribe; @@ -568,7 +589,7 @@ global.describe_only_db = db => { global.fdescribe_only_db = db => { if (process.env.PARSE_SERVER_TEST_DB == db) { return fdescribe; - } else if (!process.env.PARSE_SERVER_TEST_DB && db == 'mongo') { + } else if (!process.env.PARSE_SERVER_TEST_DB && db == "mongo") { return fdescribe; } else { return xdescribe; @@ -603,7 +624,7 @@ jasmine.mockLibrary = function (library, name, mock) { jasmine.restoreLibrary = function (library, name) { if (!libraryCache[library] || !libraryCache[library][name]) { - throw 'Can not find library ' + library + ' ' + name; + throw "Can not find library " + library + " " + name; } require(library)[name] = libraryCache[library][name]; }; diff --git a/spec/index.spec.js b/spec/index.spec.js index 0af24630b1..77f09d5cee 100644 --- a/spec/index.spec.js +++ b/spec/index.spec.js @@ -1,44 +1,46 @@ -'use strict'; -const request = require('../lib/request'); -const parseServerPackage = require('../package.json'); -const MockEmailAdapterWithOptions = require('./support/MockEmailAdapterWithOptions'); -const ParseServer = require('../lib/index'); -const Config = require('../lib/Config'); -const express = require('express'); - -const MongoStorageAdapter = require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter').default; - -describe('server', () => { - it('requires a master key and app id', done => { +"use strict"; +const request = require("../lib/request"); +const parseServerPackage = require("../package.json"); +const MockEmailAdapterWithOptions = require("./support/MockEmailAdapterWithOptions"); +const ParseServer = require("../lib/index"); +const Config = require("../lib/Config"); +const express = require("express"); + +const MongoStorageAdapter = + require("../lib/Adapters/Storage/Mongo/MongoStorageAdapter").default; + +describe("server", () => { + it("requires a master key and app id", done => { reconfigureServer({ appId: undefined }) .catch(error => { - expect(error).toEqual('You must provide an appId!'); + expect(error).toEqual("You must provide an appId!"); return reconfigureServer({ masterKey: undefined }); }) .catch(error => { - expect(error).toEqual('You must provide a masterKey!'); + expect(error).toEqual("You must provide a masterKey!"); return reconfigureServer({ serverURL: undefined }); }) .catch(error => { - expect(error).toEqual('You must provide a serverURL!'); + expect(error).toEqual("You must provide a serverURL!"); done(); }); }); - it('show warning if any reserved characters in appId', done => { - spyOn(console, 'warn').and.callFake(() => {}); - reconfigureServer({ appId: 'test!-^' }).then(() => { + it("show warning if any reserved characters in appId", done => { + spyOn(console, "warn").and.callFake(() => {}); + reconfigureServer({ appId: "test!-^" }).then(() => { expect(console.warn).toHaveBeenCalled(); return done(); }); }); - it('support http basic authentication with masterkey', done => { - reconfigureServer({ appId: 'test' }).then(() => { + it("support http basic authentication with masterkey", done => { + reconfigureServer({ appId: "test" }).then(() => { request({ - url: 'http://localhost:8378/1/classes/TestObject', + url: "http://localhost:8378/1/classes/TestObject", headers: { - Authorization: 'Basic ' + Buffer.from('test:' + 'test').toString('base64'), + Authorization: + "Basic " + Buffer.from("test:" + "test").toString("base64"), }, }).then(response => { expect(response.status).toEqual(200); @@ -47,12 +49,14 @@ describe('server', () => { }); }); - it('support http basic authentication with javascriptKey', done => { - reconfigureServer({ appId: 'test' }).then(() => { + it("support http basic authentication with javascriptKey", done => { + reconfigureServer({ appId: "test" }).then(() => { request({ - url: 'http://localhost:8378/1/classes/TestObject', + url: "http://localhost:8378/1/classes/TestObject", headers: { - Authorization: 'Basic ' + Buffer.from('test:javascript-key=' + 'test').toString('base64'), + Authorization: + "Basic " + + Buffer.from("test:javascript-key=" + "test").toString("base64"), }, }).then(response => { expect(response.status).toEqual(200); @@ -61,102 +65,104 @@ describe('server', () => { }); }); - it('fails if database is unreachable', async () => { - spyOn(console, 'error').and.callFake(() => {}); + it("fails if database is unreachable", async () => { + spyOn(console, "error").and.callFake(() => {}); const server = new ParseServer.default({ ...defaultConfiguration, databaseAdapter: new MongoStorageAdapter({ - uri: 'mongodb://fake:fake@localhost:43605/drew3', + uri: "mongodb://fake:fake@localhost:43605/drew3", mongoOptions: { serverSelectionTimeoutMS: 2000, }, }), }); const error = await server.start().catch(e => e); - expect(`${error}`.includes('MongoServerSelectionError')).toBeTrue(); + expect(`${error}`.includes("MongoServerSelectionError")).toBeTrue(); await reconfigureServer(); }); - describe('mail adapter', () => { - it('can load email adapter via object', done => { + describe("mail adapter", () => { + it("can load email adapter via object", done => { reconfigureServer({ - appName: 'unused', + appName: "unused", verifyUserEmails: true, emailAdapter: MockEmailAdapterWithOptions({ - fromAddress: 'parse@example.com', - apiKey: 'k', - domain: 'd', + fromAddress: "parse@example.com", + apiKey: "k", + domain: "d", }), - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(done, fail); }); - it('can load email adapter via class', done => { + it("can load email adapter via class", done => { reconfigureServer({ - appName: 'unused', + appName: "unused", verifyUserEmails: true, emailAdapter: { class: MockEmailAdapterWithOptions, options: { - fromAddress: 'parse@example.com', - apiKey: 'k', - domain: 'd', + fromAddress: "parse@example.com", + apiKey: "k", + domain: "d", }, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }).then(done, fail); }); - it('can load email adapter via module name', async () => { + it("can load email adapter via module name", async () => { const options = { - appName: 'unused', + appName: "unused", verifyUserEmails: true, emailAdapter: { - module: 'mock-mail-adapter', + module: "mock-mail-adapter", options: {}, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }; await reconfigureServer(options); - const config = Config.get('test'); + const config = Config.get("test"); const mailAdapter = config.userController.adapter; expect(mailAdapter.sendMail).toBeDefined(); }); - it('can load email adapter via only module name', async () => { + it("can load email adapter via only module name", async () => { const options = { - appName: 'unused', + appName: "unused", verifyUserEmails: true, - emailAdapter: 'mock-mail-adapter', - publicServerURL: 'http://localhost:8378/1', + emailAdapter: "mock-mail-adapter", + publicServerURL: "http://localhost:8378/1", }; await reconfigureServer(options); - const config = Config.get('test'); + const config = Config.get("test"); const mailAdapter = config.userController.adapter; expect(mailAdapter.sendMail).toBeDefined(); }); - it('throws if you initialize email adapter incorrectly', async () => { + it("throws if you initialize email adapter incorrectly", async () => { const options = { - appName: 'unused', + appName: "unused", verifyUserEmails: true, emailAdapter: { - module: 'mock-mail-adapter', + module: "mock-mail-adapter", options: { throw: true }, }, - publicServerURL: 'http://localhost:8378/1', + publicServerURL: "http://localhost:8378/1", }; - await expectAsync(reconfigureServer(options)).toBeRejected('MockMailAdapterConstructor'); + await expectAsync(reconfigureServer(options)).toBeRejected( + "MockMailAdapterConstructor" + ); }); }); - it('can report the server version', async done => { + it("can report the server version", async done => { await reconfigureServer(); request({ - url: 'http://localhost:8378/1/serverInfo', + url: "http://localhost:8378/1/serverInfo", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-Master-Key": "test", }, }).then(response => { const body = response.data; @@ -165,17 +171,17 @@ describe('server', () => { }); }); - it('can properly sets the push support', async done => { + it("can properly sets the push support", async done => { await reconfigureServer(); // default config passes push options - const config = Config.get('test'); + const config = Config.get("test"); expect(config.hasPushSupport).toEqual(true); expect(config.hasPushScheduledSupport).toEqual(false); request({ - url: 'http://localhost:8378/1/serverInfo', + url: "http://localhost:8378/1/serverInfo", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-Master-Key": "test", }, json: true, }).then(response => { @@ -186,19 +192,19 @@ describe('server', () => { }); }); - it('can properly sets the push support when not configured', done => { + it("can properly sets the push support when not configured", done => { reconfigureServer({ push: undefined, // force no config }) .then(() => { - const config = Config.get('test'); + const config = Config.get("test"); expect(config.hasPushSupport).toEqual(false); expect(config.hasPushScheduledSupport).toEqual(false); request({ - url: 'http://localhost:8378/1/serverInfo', + url: "http://localhost:8378/1/serverInfo", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-Master-Key": "test", }, json: true, }).then(response => { @@ -211,7 +217,7 @@ describe('server', () => { .catch(done.fail); }); - it('can properly sets the push support ', done => { + it("can properly sets the push support ", done => { reconfigureServer({ push: { adapter: { @@ -221,14 +227,14 @@ describe('server', () => { }, }) .then(() => { - const config = Config.get('test'); + const config = Config.get("test"); expect(config.hasPushSupport).toEqual(true); expect(config.hasPushScheduledSupport).toEqual(false); request({ - url: 'http://localhost:8378/1/serverInfo', + url: "http://localhost:8378/1/serverInfo", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-Master-Key": "test", }, json: true, }).then(response => { @@ -241,7 +247,7 @@ describe('server', () => { .catch(done.fail); }); - it('can properly sets the push schedule support', done => { + it("can properly sets the push schedule support", done => { reconfigureServer({ push: { adapter: { @@ -252,14 +258,14 @@ describe('server', () => { scheduledPush: true, }) .then(() => { - const config = Config.get('test'); + const config = Config.get("test"); expect(config.hasPushSupport).toEqual(true); expect(config.hasPushScheduledSupport).toEqual(true); request({ - url: 'http://localhost:8378/1/serverInfo', + url: "http://localhost:8378/1/serverInfo", headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key': 'test', + "X-Parse-Application-Id": "test", + "X-Parse-Master-Key": "test", }, json: true, }).then(response => { @@ -272,72 +278,76 @@ describe('server', () => { .catch(done.fail); }); - it('can respond 200 on path health', done => { + it("can respond 200 on path health", done => { request({ - url: 'http://localhost:8378/1/health', + url: "http://localhost:8378/1/health", }).then(response => { expect(response.status).toBe(200); done(); }); }); - it('can create a parse-server v1', async () => { - await reconfigureServer({ appId: 'aTestApp' }); + it("can create a parse-server v1", async () => { + await reconfigureServer({ appId: "aTestApp" }); const parseServer = new ParseServer.default( Object.assign({}, defaultConfiguration, { - appId: 'aTestApp', - masterKey: 'aTestMasterKey', - serverURL: 'http://localhost:12666/parse', + appId: "aTestApp", + masterKey: "aTestMasterKey", + serverURL: "http://localhost:12666/parse", }) ); await parseServer.start(); - expect(Parse.applicationId).toEqual('aTestApp'); + expect(Parse.applicationId).toEqual("aTestApp"); const app = express(); - app.use('/parse', parseServer.app); + app.use("/parse", parseServer.app); const server = app.listen(12666); - const obj = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); await obj.save(); - const query = await new Parse.Query('AnObject').first(); + const query = await new Parse.Query("AnObject").first(); expect(obj.id).toEqual(query.id); await new Promise(resolve => server.close(resolve)); }); - it('can create a parse-server v2', async () => { - await reconfigureServer({ appId: 'anOtherTestApp' }); + it("can create a parse-server v2", async () => { + await reconfigureServer({ appId: "anOtherTestApp" }); const parseServer = ParseServer.ParseServer( Object.assign({}, defaultConfiguration, { - appId: 'anOtherTestApp', - masterKey: 'anOtherTestMasterKey', - serverURL: 'http://localhost:12667/parse', + appId: "anOtherTestApp", + masterKey: "anOtherTestMasterKey", + serverURL: "http://localhost:12667/parse", }) ); - expect(Parse.applicationId).toEqual('anOtherTestApp'); + expect(Parse.applicationId).toEqual("anOtherTestApp"); await parseServer.start(); const app = express(); - app.use('/parse', parseServer.app); + app.use("/parse", parseServer.app); const server = app.listen(12667); - const obj = new Parse.Object('AnObject'); + const obj = new Parse.Object("AnObject"); await obj.save(); - const q = await new Parse.Query('AnObject').first(); + const q = await new Parse.Query("AnObject").first(); expect(obj.id).toEqual(q.id); await new Promise(resolve => server.close(resolve)); }); - it('has createLiveQueryServer', done => { + it("has createLiveQueryServer", done => { // original implementation through the factory - expect(typeof ParseServer.ParseServer.createLiveQueryServer).toEqual('function'); + expect(typeof ParseServer.ParseServer.createLiveQueryServer).toEqual( + "function" + ); // For import calls - expect(typeof ParseServer.default.createLiveQueryServer).toEqual('function'); + expect(typeof ParseServer.default.createLiveQueryServer).toEqual( + "function" + ); done(); }); - it('exposes correct adapters', done => { + it("exposes correct adapters", done => { expect(ParseServer.S3Adapter).toThrow( - 'S3Adapter is not provided by parse-server anymore; please install @parse/s3-files-adapter' + "S3Adapter is not provided by parse-server anymore; please install @parse/s3-files-adapter" ); expect(ParseServer.GCSAdapter).toThrow( - 'GCSAdapter is not provided by parse-server anymore; please install @parse/gcs-files-adapter' + "GCSAdapter is not provided by parse-server anymore; please install @parse/gcs-files-adapter" ); expect(ParseServer.FileSystemAdapter).toThrow(); expect(ParseServer.InMemoryCacheAdapter).toThrow(); @@ -345,144 +355,153 @@ describe('server', () => { done(); }); - it('properly gives publicServerURL when set', done => { - reconfigureServer({ publicServerURL: 'https://myserver.com/1' }).then(() => { - const config = Config.get('test', 'http://localhost:8378/1'); - expect(config.mount).toEqual('https://myserver.com/1'); - done(); - }); + it("properly gives publicServerURL when set", done => { + reconfigureServer({ publicServerURL: "https://myserver.com/1" }).then( + () => { + const config = Config.get("test", "http://localhost:8378/1"); + expect(config.mount).toEqual("https://myserver.com/1"); + done(); + } + ); }); - it('properly removes trailing slash in mount', done => { + it("properly removes trailing slash in mount", done => { reconfigureServer({}).then(() => { - const config = Config.get('test', 'http://localhost:8378/1/'); - expect(config.mount).toEqual('http://localhost:8378/1'); + const config = Config.get("test", "http://localhost:8378/1/"); + expect(config.mount).toEqual("http://localhost:8378/1"); done(); }); }); - it('should throw when getting invalid mount', done => { - reconfigureServer({ publicServerURL: 'blabla:/some' }).catch(error => { - expect(error).toEqual('publicServerURL should be a valid HTTPS URL starting with https://'); + it("should throw when getting invalid mount", done => { + reconfigureServer({ publicServerURL: "blabla:/some" }).catch(error => { + expect(error).toEqual( + "publicServerURL should be a valid HTTPS URL starting with https://" + ); done(); }); }); - it('should throw when extendSessionOnUse is invalid', async () => { + it("should throw when extendSessionOnUse is invalid", async () => { await expectAsync( reconfigureServer({ - extendSessionOnUse: 'yolo', + extendSessionOnUse: "yolo", }) - ).toBeRejectedWith('extendSessionOnUse must be a boolean value'); + ).toBeRejectedWith("extendSessionOnUse must be a boolean value"); }); - it('should throw when revokeSessionOnPasswordReset is invalid', async () => { + it("should throw when revokeSessionOnPasswordReset is invalid", async () => { await expectAsync( reconfigureServer({ - revokeSessionOnPasswordReset: 'yolo', + revokeSessionOnPasswordReset: "yolo", }) - ).toBeRejectedWith('revokeSessionOnPasswordReset must be a boolean value'); + ).toBeRejectedWith("revokeSessionOnPasswordReset must be a boolean value"); }); - it('fails if the session length is not a number', done => { - reconfigureServer({ sessionLength: 'test' }) + it("fails if the session length is not a number", done => { + reconfigureServer({ sessionLength: "test" }) .then(done.fail) .catch(error => { - expect(error).toEqual('Session length must be a valid number.'); + expect(error).toEqual("Session length must be a valid number."); done(); }); }); - it('fails if the session length is less than or equal to 0', done => { - reconfigureServer({ sessionLength: '-33' }) + it("fails if the session length is less than or equal to 0", done => { + reconfigureServer({ sessionLength: "-33" }) .then(done.fail) .catch(error => { - expect(error).toEqual('Session length must be a value greater than 0.'); - return reconfigureServer({ sessionLength: '0' }); + expect(error).toEqual("Session length must be a value greater than 0."); + return reconfigureServer({ sessionLength: "0" }); }) .catch(error => { - expect(error).toEqual('Session length must be a value greater than 0.'); + expect(error).toEqual("Session length must be a value greater than 0."); done(); }); }); - it('ignores the session length when expireInactiveSessions set to false', done => { + it("ignores the session length when expireInactiveSessions set to false", done => { reconfigureServer({ - sessionLength: '-33', + sessionLength: "-33", expireInactiveSessions: false, }) .then(() => reconfigureServer({ - sessionLength: '0', + sessionLength: "0", expireInactiveSessions: false, }) ) .then(done); }); - it('fails if default limit is negative', async () => { + it("fails if default limit is negative", async () => { await expectAsync(reconfigureServer({ defaultLimit: -1 })).toBeRejectedWith( - 'Default limit must be a value greater than 0.' + "Default limit must be a value greater than 0." ); }); - it('fails if default limit is wrong type', async () => { - for (const value of ['invalid', {}, [], true]) { - await expectAsync(reconfigureServer({ defaultLimit: value })).toBeRejectedWith( - 'Default limit must be a number.' - ); + it("fails if default limit is wrong type", async () => { + for (const value of ["invalid", {}, [], true]) { + await expectAsync( + reconfigureServer({ defaultLimit: value }) + ).toBeRejectedWith("Default limit must be a number."); } }); - it('fails if default limit is zero', async () => { + it("fails if default limit is zero", async () => { await expectAsync(reconfigureServer({ defaultLimit: 0 })).toBeRejectedWith( - 'Default limit must be a value greater than 0.' + "Default limit must be a value greater than 0." ); }); - it('fails if maxLimit is negative', done => { + it("fails if maxLimit is negative", done => { reconfigureServer({ maxLimit: -100 }).catch(error => { - expect(error).toEqual('Max limit must be a value greater than 0.'); + expect(error).toEqual("Max limit must be a value greater than 0."); done(); }); }); - it('fails if you try to set revokeSessionOnPasswordReset to non-boolean', done => { - reconfigureServer({ revokeSessionOnPasswordReset: 'non-bool' }).catch(done); + it("fails if you try to set revokeSessionOnPasswordReset to non-boolean", done => { + reconfigureServer({ revokeSessionOnPasswordReset: "non-bool" }).catch(done); }); - it('fails if you provides invalid ip in masterKeyIps', done => { - reconfigureServer({ masterKeyIps: ['invalidIp', '1.2.3.4'] }).catch(error => { - expect(error).toEqual( - 'The Parse Server option "masterKeyIps" contains an invalid IP address "invalidIp".' - ); - done(); - }); + it("fails if you provides invalid ip in masterKeyIps", done => { + reconfigureServer({ masterKeyIps: ["invalidIp", "1.2.3.4"] }).catch( + error => { + expect(error).toEqual( + 'The Parse Server option "masterKeyIps" contains an invalid IP address "invalidIp".' + ); + done(); + } + ); }); - it('should succeed if you provide valid ip in masterKeyIps', done => { + it("should succeed if you provide valid ip in masterKeyIps", done => { reconfigureServer({ - masterKeyIps: ['1.2.3.4', '2001:0db8:0000:0042:0000:8a2e:0370:7334'], + masterKeyIps: ["1.2.3.4", "2001:0db8:0000:0042:0000:8a2e:0370:7334"], }).then(done); }); - it('should set default masterKeyIps for IPv4 and IPv6 localhost', () => { - const definitions = require('../lib/Options/Definitions.js'); - expect(definitions.ParseServerOptions.masterKeyIps.default).toEqual(['127.0.0.1', '::1']); + it("should set default masterKeyIps for IPv4 and IPv6 localhost", () => { + const definitions = require("../lib/Options/Definitions.js"); + expect(definitions.ParseServerOptions.masterKeyIps.default).toEqual([ + "127.0.0.1", + "::1", + ]); }); - it('should load a middleware', done => { + it("should load a middleware", done => { const obj = { middleware: function (req, res, next) { next(); }, }; - const spy = spyOn(obj, 'middleware').and.callThrough(); + const spy = spyOn(obj, "middleware").and.callThrough(); reconfigureServer({ middleware: obj.middleware, }) .then(() => { - const query = new Parse.Query('AnObject'); + const query = new Parse.Query("AnObject"); return query.find(); }) .then(() => { @@ -492,9 +511,9 @@ describe('server', () => { .catch(done.fail); }); - it('should allow direct access', async () => { + it("should allow direct access", async () => { const RESTController = Parse.CoreManager.getRESTController(); - const spy = spyOn(Parse.CoreManager, 'setRESTController').and.callThrough(); + const spy = spyOn(Parse.CoreManager, "setRESTController").and.callThrough(); await reconfigureServer({ directAccess: true, }); @@ -502,128 +521,135 @@ describe('server', () => { Parse.CoreManager.setRESTController(RESTController); }); - it('should load a middleware from string', done => { + it("should load a middleware from string", done => { reconfigureServer({ - middleware: 'spec/support/CustomMiddleware', + middleware: "spec/support/CustomMiddleware", }) .then(() => { - return request({ url: 'http://localhost:8378/1' }).then(fail, res => { + return request({ url: "http://localhost:8378/1" }).then(fail, res => { // Just check that the middleware set the header - expect(res.headers['x-yolo']).toBe('1'); + expect(res.headers["x-yolo"]).toBe("1"); done(); }); }) .catch(done.fail); }); - it('can call start', async () => { - await reconfigureServer({ appId: 'aTestApp' }); + it("can call start", async () => { + await reconfigureServer({ appId: "aTestApp" }); const config = { ...defaultConfiguration, - appId: 'aTestApp', - masterKey: 'aTestMasterKey', - serverURL: 'http://localhost:12701/parse', + appId: "aTestApp", + masterKey: "aTestMasterKey", + serverURL: "http://localhost:12701/parse", }; const parseServer = new ParseServer.ParseServer(config); await parseServer.start(); - expect(Parse.applicationId).toEqual('aTestApp'); - expect(Parse.serverURL).toEqual('http://localhost:12701/parse'); + expect(Parse.applicationId).toEqual("aTestApp"); + expect(Parse.serverURL).toEqual("http://localhost:12701/parse"); const app = express(); - app.use('/parse', parseServer.app); + app.use("/parse", parseServer.app); const server = app.listen(12701); - const testObject = new Parse.Object('TestObject'); + const testObject = new Parse.Object("TestObject"); await expectAsync(testObject.save()).toBeResolved(); await new Promise(resolve => server.close(resolve)); }); - it('start is required to mount', async () => { - await reconfigureServer({ appId: 'aTestApp' }); + it("start is required to mount", async () => { + await reconfigureServer({ appId: "aTestApp" }); const config = { ...defaultConfiguration, - appId: 'aTestApp', - masterKey: 'aTestMasterKey', - serverURL: 'http://localhost:12701/parse', + appId: "aTestApp", + masterKey: "aTestMasterKey", + serverURL: "http://localhost:12701/parse", }; const parseServer = new ParseServer.ParseServer(config); - expect(Parse.applicationId).toEqual('aTestApp'); - expect(Parse.serverURL).toEqual('http://localhost:12701/parse'); + expect(Parse.applicationId).toEqual("aTestApp"); + expect(Parse.serverURL).toEqual("http://localhost:12701/parse"); const app = express(); - app.use('/parse', parseServer.app); + app.use("/parse", parseServer.app); const server = app.listen(12701); const response = await request({ headers: { - 'X-Parse-Application-Id': 'aTestApp', + "X-Parse-Application-Id": "aTestApp", }, - method: 'POST', - url: 'http://localhost:12701/parse/classes/TestObject', + method: "POST", + url: "http://localhost:12701/parse/classes/TestObject", }).catch(e => new Parse.Error(e.data.code, e.data.error)); expect(response).toEqual( - new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Invalid server state: initialized') + new Parse.Error( + Parse.Error.INTERNAL_SERVER_ERROR, + "Invalid server state: initialized" + ) ); const health = await request({ - url: 'http://localhost:12701/parse/health', + url: "http://localhost:12701/parse/health", }).catch(e => e); - spyOn(console, 'warn').and.callFake(() => {}); + spyOn(console, "warn").and.callFake(() => {}); const verify = await ParseServer.default.verifyServerUrl(); expect(verify).not.toBeTrue(); expect(console.warn).toHaveBeenCalledWith( `\nWARNING, Unable to connect to 'http://localhost:12701/parse'. Cloud code and push notifications may be unavailable!\n` ); - expect(health.data.status).toBe('initialized'); + expect(health.data.status).toBe("initialized"); expect(health.status).toBe(503); await new Promise(resolve => server.close(resolve)); }); - it('can get starting state', async () => { - await reconfigureServer({ appId: 'test2' }); + it("can get starting state", async () => { + await reconfigureServer({ appId: "test2" }); const parseServer = new ParseServer.ParseServer({ ...defaultConfiguration, - appId: 'test2', - masterKey: 'abc', - serverURL: 'http://localhost:12668/parse', + appId: "test2", + masterKey: "abc", + serverURL: "http://localhost:12668/parse", async cloud() { await new Promise(resolve => setTimeout(resolve, 2000)); }, }); - const express = require('express'); + const express = require("express"); const app = express(); - app.use('/parse', parseServer.app); + app.use("/parse", parseServer.app); const server = app.listen(12668); const startingPromise = parseServer.start(); const health = await request({ - url: 'http://localhost:12668/parse/health', + url: "http://localhost:12668/parse/health", }).catch(e => e); - expect(health.data.status).toBe('starting'); + expect(health.data.status).toBe("starting"); expect(health.status).toBe(503); - expect(health.headers['retry-after']).toBe('1'); + expect(health.headers["retry-after"]).toBe("1"); const response = await ParseServer.default.verifyServerUrl(); expect(response).toBeTrue(); await startingPromise; await new Promise(resolve => server.close(resolve)); }); - it('should load masterKey', async () => { + it("should load masterKey", async () => { await reconfigureServer({ - masterKey: () => 'testMasterKey', + masterKey: () => "testMasterKey", masterKeyTtl: 1000, // TTL is set }); - await new Parse.Object('TestObject').save(); + await new Parse.Object("TestObject").save(); const config = Config.get(Parse.applicationId); - expect(config.masterKeyCache.masterKey).toEqual('testMasterKey'); - expect(config.masterKeyCache.expiresAt.getTime()).toBeGreaterThan(Date.now()); + expect(config.masterKeyCache.masterKey).toEqual("testMasterKey"); + expect(config.masterKeyCache.expiresAt.getTime()).toBeGreaterThan( + Date.now() + ); }); - it('should not reload if ttl is not set', async () => { - const masterKeySpy = jasmine.createSpy().and.returnValue(Promise.resolve('initialMasterKey')); + it("should not reload if ttl is not set", async () => { + const masterKeySpy = jasmine + .createSpy() + .and.returnValue(Promise.resolve("initialMasterKey")); await reconfigureServer({ masterKey: masterKeySpy, masterKeyTtl: null, // No TTL set }); - await new Parse.Object('TestObject').save(); + await new Parse.Object("TestObject").save(); const config = Config.get(Parse.applicationId); const firstMasterKey = config.masterKeyCache.masterKey; @@ -632,54 +658,59 @@ describe('server', () => { await config.loadMasterKey(); const secondMasterKey = config.masterKeyCache.masterKey; - expect(firstMasterKey).toEqual('initialMasterKey'); - expect(secondMasterKey).toEqual('initialMasterKey'); + expect(firstMasterKey).toEqual("initialMasterKey"); + expect(secondMasterKey).toEqual("initialMasterKey"); expect(masterKeySpy).toHaveBeenCalledTimes(1); // Should only be called once expect(config.masterKeyCache.expiresAt).toBeNull(); // TTL is not set, so expiresAt should remain null }); - it('should reload masterKey if ttl is set and expired', async () => { + it("should reload masterKey if ttl is set and expired", async () => { const masterKeySpy = jasmine .createSpy() - .and.returnValues(Promise.resolve('firstMasterKey'), Promise.resolve('secondMasterKey')); + .and.returnValues( + Promise.resolve("firstMasterKey"), + Promise.resolve("secondMasterKey") + ); await reconfigureServer({ masterKey: masterKeySpy, masterKeyTtl: 1 / 1000, // TTL is set to 1ms }); - await new Parse.Object('TestObject').save(); + await new Parse.Object("TestObject").save(); await new Promise(resolve => setTimeout(resolve, 10)); - await new Parse.Object('TestObject').save(); + await new Parse.Object("TestObject").save(); const config = Config.get(Parse.applicationId); expect(masterKeySpy).toHaveBeenCalledTimes(2); - expect(config.masterKeyCache.masterKey).toEqual('secondMasterKey'); + expect(config.masterKeyCache.masterKey).toEqual("secondMasterKey"); }); - it('should not fail when Google signin is introduced without the optional clientId', done => { - const jwt = require('jsonwebtoken'); - const authUtils = require('../lib/Adapters/Auth/utils'); + it("should not fail when Google signin is introduced without the optional clientId", done => { + const jwt = require("jsonwebtoken"); + const authUtils = require("../lib/Adapters/Auth/utils"); reconfigureServer({ auth: { google: {} }, }) .then(() => { const fakeClaim = { - iss: 'https://accounts.google.com', - aud: 'secret', + iss: "https://accounts.google.com", + aud: "secret", exp: Date.now(), - sub: 'the_user_id', + sub: "the_user_id", }; - const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); - spyOn(jwt, 'verify').and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; + spyOn(authUtils, "getHeaderFromToken").and.callFake( + () => fakeDecodedToken + ); + spyOn(jwt, "verify").and.callFake(() => fakeClaim); const user = new Parse.User(); user - .linkWith('google', { - authData: { id: 'the_user_id', id_token: 'the_token' }, + .linkWith("google", { + authData: { id: "the_user_id", id_token: "the_token" }, }) .then(done); }) diff --git a/spec/parsers.spec.js b/spec/parsers.spec.js index 413bdb5156..0527d07b6b 100644 --- a/spec/parsers.spec.js +++ b/spec/parsers.spec.js @@ -7,77 +7,77 @@ const { arrayParser, moduleOrObjectParser, nullParser, -} = require('../lib/Options/parsers'); +} = require("../lib/Options/parsers"); -describe('parsers', () => { - it('parses correctly with numberParser', () => { - const parser = numberParser('key'); +describe("parsers", () => { + it("parses correctly with numberParser", () => { + const parser = numberParser("key"); expect(parser(2)).toEqual(2); - expect(parser('2')).toEqual(2); + expect(parser("2")).toEqual(2); expect(() => { - parser('string'); + parser("string"); }).toThrow(); }); - it('parses correctly with numberOrStringParser', () => { - const parser = numberOrStringParser('key'); - expect(parser('100d')).toEqual('100d'); + it("parses correctly with numberOrStringParser", () => { + const parser = numberOrStringParser("key"); + expect(parser("100d")).toEqual("100d"); expect(parser(100)).toEqual(100); expect(() => { parser(undefined); }).toThrow(); }); - it('parses correctly with numberOrBoolParser', () => { - const parser = numberOrBoolParser('key'); + it("parses correctly with numberOrBoolParser", () => { + const parser = numberOrBoolParser("key"); expect(parser(true)).toEqual(true); expect(parser(false)).toEqual(false); - expect(parser('true')).toEqual(true); - expect(parser('false')).toEqual(false); + expect(parser("true")).toEqual(true); + expect(parser("false")).toEqual(false); expect(parser(1)).toEqual(1); - expect(parser('1')).toEqual(1); + expect(parser("1")).toEqual(1); }); - it('parses correctly with booleanParser', () => { + it("parses correctly with booleanParser", () => { const parser = booleanParser; expect(parser(true)).toEqual(true); expect(parser(false)).toEqual(false); - expect(parser('true')).toEqual(true); - expect(parser('false')).toEqual(false); + expect(parser("true")).toEqual(true); + expect(parser("false")).toEqual(false); expect(parser(1)).toEqual(true); expect(parser(2)).toEqual(false); }); - it('parses correctly with objectParser', () => { + it("parses correctly with objectParser", () => { const parser = objectParser; - expect(parser({ hello: 'world' })).toEqual({ hello: 'world' }); - expect(parser('{"hello": "world"}')).toEqual({ hello: 'world' }); + expect(parser({ hello: "world" })).toEqual({ hello: "world" }); + expect(parser('{"hello": "world"}')).toEqual({ hello: "world" }); expect(() => { - parser('string'); + parser("string"); }).toThrow(); }); - it('parses correctly with moduleOrObjectParser', () => { + it("parses correctly with moduleOrObjectParser", () => { const parser = moduleOrObjectParser; - expect(parser({ hello: 'world' })).toEqual({ hello: 'world' }); - expect(parser('{"hello": "world"}')).toEqual({ hello: 'world' }); - expect(parser('string')).toEqual('string'); + expect(parser({ hello: "world" })).toEqual({ hello: "world" }); + expect(parser('{"hello": "world"}')).toEqual({ hello: "world" }); + expect(parser("string")).toEqual("string"); }); - it('parses correctly with arrayParser', () => { + it("parses correctly with arrayParser", () => { const parser = arrayParser; expect(parser([1, 2, 3])).toEqual([1, 2, 3]); expect(parser('{"hello": "world"}')).toEqual(['{"hello": "world"}']); - expect(parser('1,2,3')).toEqual(['1', '2', '3']); + expect(parser("1,2,3")).toEqual(["1", "2", "3"]); expect(() => { parser(1); }).toThrow(); }); - it('parses correctly with nullParser', () => { + it("parses correctly with nullParser", () => { const parser = nullParser; - expect(parser('null')).toEqual(null); + expect(parser("null")).toEqual(null); expect(parser(1)).toEqual(1); - expect(parser('blabla')).toEqual('blabla'); + expect(parser("blabla")).toEqual("blabla"); }); }); diff --git a/spec/rest.spec.js b/spec/rest.spec.js index 92e25c3941..7c641e886a 100644 --- a/spec/rest.spec.js +++ b/spec/rest.spec.js @@ -1,50 +1,50 @@ -'use strict'; +"use strict"; // These tests check the "create" / "update" functionality of the REST API. -const auth = require('../lib/Auth'); -const Config = require('../lib/Config'); -const Parse = require('parse/node').Parse; -const rest = require('../lib/rest'); -const RestWrite = require('../lib/RestWrite'); -const request = require('../lib/request'); +const auth = require("../lib/Auth"); +const Config = require("../lib/Config"); +const Parse = require("parse/node").Parse; +const rest = require("../lib/rest"); +const RestWrite = require("../lib/RestWrite"); +const request = require("../lib/request"); let config; let database; -describe('rest create', () => { +describe("rest create", () => { beforeEach(() => { - config = Config.get('test'); + config = Config.get("test"); database = config.database; }); - it('handles _id', done => { + it("handles _id", done => { rest - .create(config, auth.nobody(config), 'Foo', {}) - .then(() => database.adapter.find('Foo', { fields: {} }, {}, {})) + .create(config, auth.nobody(config), "Foo", {}) + .then(() => database.adapter.find("Foo", { fields: {} }, {}, {})) .then(results => { expect(results.length).toEqual(1); const obj = results[0]; - expect(typeof obj.objectId).toEqual('string'); + expect(typeof obj.objectId).toEqual("string"); expect(obj.objectId.length).toEqual(10); expect(obj._id).toBeUndefined(); done(); }); }); - it('can use custom _id size', done => { + it("can use custom _id size", done => { config.objectIdSize = 20; rest - .create(config, auth.nobody(config), 'Foo', {}) - .then(() => database.adapter.find('Foo', { fields: {} }, {}, {})) + .create(config, auth.nobody(config), "Foo", {}) + .then(() => database.adapter.find("Foo", { fields: {} }, {}, {})) .then(results => { expect(results.length).toEqual(1); const obj = results[0]; - expect(typeof obj.objectId).toEqual('string'); + expect(typeof obj.objectId).toEqual("string"); expect(obj.objectId.length).toEqual(20); done(); }); }); - it('should use objectId from client when allowCustomObjectId true', async () => { + it("should use objectId from client when allowCustomObjectId true", async () => { config.allowCustomObjectId = true; // use time as unique custom id for test reusability @@ -56,13 +56,13 @@ describe('rest create', () => { const { status, response: { objectId }, - } = await rest.create(config, auth.nobody(config), 'MyClass', obj); + } = await rest.create(config, auth.nobody(config), "MyClass", obj); expect(status).toEqual(201); expect(objectId).toEqual(customId); }); - it('should throw on invalid objectId when allowCustomObjectId true', () => { + it("should throw on invalid objectId when allowCustomObjectId true", () => { config.allowCustomObjectId = true; const objIdNull = { @@ -74,36 +74,42 @@ describe('rest create', () => { }; const objIdEmpty = { - objectId: '', + objectId: "", }; - const err = 'objectId must not be empty, null or undefined'; + const err = "objectId must not be empty, null or undefined"; - expect(() => rest.create(config, auth.nobody(config), 'MyClass', objIdEmpty)).toThrowError(err); + expect(() => + rest.create(config, auth.nobody(config), "MyClass", objIdEmpty) + ).toThrowError(err); - expect(() => rest.create(config, auth.nobody(config), 'MyClass', objIdNull)).toThrowError(err); + expect(() => + rest.create(config, auth.nobody(config), "MyClass", objIdNull) + ).toThrowError(err); - expect(() => rest.create(config, auth.nobody(config), 'MyClass', objIdUndef)).toThrowError(err); + expect(() => + rest.create(config, auth.nobody(config), "MyClass", objIdUndef) + ).toThrowError(err); }); - it('should generate objectId when not set by client with allowCustomObjectId true', async () => { + it("should generate objectId when not set by client with allowCustomObjectId true", async () => { config.allowCustomObjectId = true; const { status, response: { objectId }, - } = await rest.create(config, auth.nobody(config), 'MyClass', {}); + } = await rest.create(config, auth.nobody(config), "MyClass", {}); expect(status).toEqual(201); expect(objectId).toBeDefined(); }); - it('is backwards compatible when _id size changes', done => { + it("is backwards compatible when _id size changes", done => { rest - .create(config, auth.nobody(config), 'Foo', { size: 10 }) + .create(config, auth.nobody(config), "Foo", { size: 10 }) .then(() => { config.objectIdSize = 20; - return rest.find(config, auth.nobody(config), 'Foo', { size: 10 }); + return rest.find(config, auth.nobody(config), "Foo", { size: 10 }); }) .then(response => { expect(response.results.length).toEqual(1); @@ -111,23 +117,23 @@ describe('rest create', () => { return rest.update( config, auth.nobody(config), - 'Foo', + "Foo", { objectId: response.results[0].objectId }, { update: 20 } ); }) .then(() => { - return rest.find(config, auth.nobody(config), 'Foo', { size: 10 }); + return rest.find(config, auth.nobody(config), "Foo", { size: 10 }); }) .then(response => { expect(response.results.length).toEqual(1); expect(response.results[0].objectId.length).toEqual(10); expect(response.results[0].update).toEqual(20); - return rest.create(config, auth.nobody(config), 'Foo', { size: 20 }); + return rest.create(config, auth.nobody(config), "Foo", { size: 20 }); }) .then(() => { config.objectIdSize = 10; - return rest.find(config, auth.nobody(config), 'Foo', { size: 20 }); + return rest.find(config, auth.nobody(config), "Foo", { size: 20 }); }) .then(response => { expect(response.results.length).toEqual(1); @@ -136,16 +142,16 @@ describe('rest create', () => { }); }); - describe('with maintenance key', () => { + describe("with maintenance key", () => { let req; async function getObject(id) { const res = await request({ headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }, - method: 'GET', + method: "GET", url: `http://localhost:8378/1/classes/TestObject/${id}`, }); @@ -155,27 +161,27 @@ describe('rest create', () => { beforeEach(() => { req = { headers: { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Maintenance-Key': 'testing', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "X-Parse-Maintenance-Key": "testing", }, - method: 'POST', - url: 'http://localhost:8378/1/classes/TestObject', + method: "POST", + url: "http://localhost:8378/1/classes/TestObject", }; }); - it('allows createdAt', async () => { - const createdAt = { __type: 'Date', iso: '2019-01-01T00:00:00.000Z' }; + it("allows createdAt", async () => { + const createdAt = { __type: "Date", iso: "2019-01-01T00:00:00.000Z" }; req.body = { createdAt }; const res = await request(req); expect(res.data.createdAt).toEqual(createdAt.iso); }); - it('allows createdAt and updatedAt', async () => { - const createdAt = { __type: 'Date', iso: '2019-01-01T00:00:00.000Z' }; - const updatedAt = { __type: 'Date', iso: '2019-02-01T00:00:00.000Z' }; + it("allows createdAt and updatedAt", async () => { + const createdAt = { __type: "Date", iso: "2019-01-01T00:00:00.000Z" }; + const updatedAt = { __type: "Date", iso: "2019-02-01T00:00:00.000Z" }; req.body = { createdAt, updatedAt }; const res = await request(req); @@ -185,9 +191,9 @@ describe('rest create', () => { expect(obj.updatedAt).toEqual(updatedAt.iso); }); - it('allows createdAt, updatedAt, and additional field', async () => { - const createdAt = { __type: 'Date', iso: '2019-01-01T00:00:00.000Z' }; - const updatedAt = { __type: 'Date', iso: '2019-02-01T00:00:00.000Z' }; + it("allows createdAt, updatedAt, and additional field", async () => { + const createdAt = { __type: "Date", iso: "2019-01-01T00:00:00.000Z" }; + const updatedAt = { __type: "Date", iso: "2019-02-01T00:00:00.000Z" }; req.body = { createdAt, updatedAt, testing: 123 }; const res = await request(req); @@ -198,9 +204,9 @@ describe('rest create', () => { expect(obj.testing).toEqual(123); }); - it('cannot set updatedAt dated before createdAt', async () => { - const createdAt = { __type: 'Date', iso: '2019-01-01T00:00:00.000Z' }; - const updatedAt = { __type: 'Date', iso: '2018-12-01T00:00:00.000Z' }; + it("cannot set updatedAt dated before createdAt", async () => { + const createdAt = { __type: "Date", iso: "2019-01-01T00:00:00.000Z" }; + const updatedAt = { __type: "Date", iso: "2018-12-01T00:00:00.000Z" }; req.body = { createdAt, updatedAt }; try { @@ -211,8 +217,8 @@ describe('rest create', () => { } }); - it('cannot set updatedAt without createdAt', async () => { - const updatedAt = { __type: 'Date', iso: '2018-12-01T00:00:00.000Z' }; + it("cannot set updatedAt without createdAt", async () => { + const updatedAt = { __type: "Date", iso: "2018-12-01T00:00:00.000Z" }; req.body = { updatedAt }; const res = await request(req); @@ -221,7 +227,7 @@ describe('rest create', () => { expect(obj.updatedAt).not.toEqual(updatedAt.iso); }); - it('handles bad types for createdAt and updatedAt', async () => { + it("handles bad types for createdAt and updatedAt", async () => { const createdAt = 12345; const updatedAt = true; req.body = { createdAt, updatedAt }; @@ -234,11 +240,11 @@ describe('rest create', () => { } }); - it('cannot set createdAt or updatedAt without maintenance key', async () => { - const createdAt = { __type: 'Date', iso: '2019-01-01T00:00:00.000Z' }; - const updatedAt = { __type: 'Date', iso: '2019-02-01T00:00:00.000Z' }; + it("cannot set createdAt or updatedAt without maintenance key", async () => { + const createdAt = { __type: "Date", iso: "2019-01-01T00:00:00.000Z" }; + const updatedAt = { __type: "Date", iso: "2019-02-01T00:00:00.000Z" }; req.body = { createdAt, updatedAt }; - delete req.headers['X-Parse-Maintenance-Key']; + delete req.headers["X-Parse-Maintenance-Key"]; const res = await request(req); @@ -247,189 +253,214 @@ describe('rest create', () => { }); }); - it_id('6c30306f-328c-47f2-88a7-2deffaee997f')(it)('handles array, object, date', done => { - const now = new Date(); - const obj = { - array: [1, 2, 3], - object: { foo: 'bar' }, - date: Parse._encode(now), - }; - rest - .create(config, auth.nobody(config), 'MyClass', obj) - .then(() => - database.adapter.find( - 'MyClass', - { - fields: { - array: { type: 'Array' }, - object: { type: 'Object' }, - date: { type: 'Date' }, + it_id("6c30306f-328c-47f2-88a7-2deffaee997f")(it)( + "handles array, object, date", + done => { + const now = new Date(); + const obj = { + array: [1, 2, 3], + object: { foo: "bar" }, + date: Parse._encode(now), + }; + rest + .create(config, auth.nobody(config), "MyClass", obj) + .then(() => + database.adapter.find( + "MyClass", + { + fields: { + array: { type: "Array" }, + object: { type: "Object" }, + date: { type: "Date" }, + }, }, - }, - {}, - {} + {}, + {} + ) ) - ) - .then(results => { - expect(results.length).toEqual(1); - const mob = results[0]; - expect(mob.array instanceof Array).toBe(true); - expect(typeof mob.object).toBe('object'); - expect(mob.date.__type).toBe('Date'); - expect(new Date(mob.date.iso).getTime()).toBe(now.getTime()); - done(); - }); - }); + .then(results => { + expect(results.length).toEqual(1); + const mob = results[0]; + expect(mob.array instanceof Array).toBe(true); + expect(typeof mob.object).toBe("object"); + expect(mob.date.__type).toBe("Date"); + expect(new Date(mob.date.iso).getTime()).toBe(now.getTime()); + done(); + }); + } + ); - it('handles object and subdocument', done => { - const obj = { subdoc: { foo: 'bar', wu: 'tan' } }; + it("handles object and subdocument", done => { + const obj = { subdoc: { foo: "bar", wu: "tan" } }; - Parse.Cloud.beforeSave('MyClass', function () { + Parse.Cloud.beforeSave("MyClass", function () { // this beforeSave trigger should do nothing but can mess with the object }); rest - .create(config, auth.nobody(config), 'MyClass', obj) - .then(() => database.adapter.find('MyClass', { fields: {} }, {}, {})) + .create(config, auth.nobody(config), "MyClass", obj) + .then(() => database.adapter.find("MyClass", { fields: {} }, {}, {})) .then(results => { expect(results.length).toEqual(1); const mob = results[0]; - expect(typeof mob.subdoc).toBe('object'); - expect(mob.subdoc.foo).toBe('bar'); - expect(mob.subdoc.wu).toBe('tan'); - expect(typeof mob.objectId).toEqual('string'); - const obj = { 'subdoc.wu': 'clan' }; - return rest.update(config, auth.nobody(config), 'MyClass', { objectId: mob.objectId }, obj); + expect(typeof mob.subdoc).toBe("object"); + expect(mob.subdoc.foo).toBe("bar"); + expect(mob.subdoc.wu).toBe("tan"); + expect(typeof mob.objectId).toEqual("string"); + const obj = { "subdoc.wu": "clan" }; + return rest.update( + config, + auth.nobody(config), + "MyClass", + { objectId: mob.objectId }, + obj + ); }) - .then(() => database.adapter.find('MyClass', { fields: {} }, {}, {})) + .then(() => database.adapter.find("MyClass", { fields: {} }, {}, {})) .then(results => { expect(results.length).toEqual(1); const mob = results[0]; - expect(typeof mob.subdoc).toBe('object'); - expect(mob.subdoc.foo).toBe('bar'); - expect(mob.subdoc.wu).toBe('clan'); + expect(typeof mob.subdoc).toBe("object"); + expect(mob.subdoc.foo).toBe("bar"); + expect(mob.subdoc.wu).toBe("clan"); done(); }) .catch(done.fail); }); - it('handles create on non-existent class when disabled client class creation', done => { + it("handles create on non-existent class when disabled client class creation", done => { const customConfig = Object.assign({}, config, { allowClientClassCreation: false, }); - rest.create(customConfig, auth.nobody(customConfig), 'ClientClassCreation', {}).then( - () => { - fail('Should throw an error'); - done(); - }, - err => { - expect(err.code).toEqual(Parse.Error.OPERATION_FORBIDDEN); - expect(err.message).toEqual( - 'This user is not allowed to access ' + 'non-existent class: ClientClassCreation' - ); - done(); - } - ); + rest + .create( + customConfig, + auth.nobody(customConfig), + "ClientClassCreation", + {} + ) + .then( + () => { + fail("Should throw an error"); + done(); + }, + err => { + expect(err.code).toEqual(Parse.Error.OPERATION_FORBIDDEN); + expect(err.message).toEqual( + "This user is not allowed to access " + + "non-existent class: ClientClassCreation" + ); + done(); + } + ); }); - it('handles create on existent class when disabled client class creation', async () => { + it("handles create on existent class when disabled client class creation", async () => { const customConfig = Object.assign({}, config, { allowClientClassCreation: false, }); const schema = await config.database.loadSchema(); - const actualSchema = await schema.addClassIfNotExists('ClientClassCreation', {}); - expect(actualSchema.className).toEqual('ClientClassCreation'); + const actualSchema = await schema.addClassIfNotExists( + "ClientClassCreation", + {} + ); + expect(actualSchema.className).toEqual("ClientClassCreation"); await schema.reloadData({ clearCache: true }); // Should not throw - await rest.create(customConfig, auth.nobody(customConfig), 'ClientClassCreation', {}); + await rest.create( + customConfig, + auth.nobody(customConfig), + "ClientClassCreation", + {} + ); }); - it('handles user signup', done => { + it("handles user signup", done => { const user = { - username: 'asdf', - password: 'zxcv', - foo: 'bar', + username: "asdf", + password: "zxcv", + foo: "bar", }; - rest.create(config, auth.nobody(config), '_User', user).then(r => { + rest.create(config, auth.nobody(config), "_User", user).then(r => { expect(Object.keys(r.response).length).toEqual(3); - expect(typeof r.response.objectId).toEqual('string'); - expect(typeof r.response.createdAt).toEqual('string'); - expect(typeof r.response.sessionToken).toEqual('string'); + expect(typeof r.response.objectId).toEqual("string"); + expect(typeof r.response.createdAt).toEqual("string"); + expect(typeof r.response.sessionToken).toEqual("string"); done(); }); }); - it('handles anonymous user signup', done => { + it("handles anonymous user signup", done => { const data1 = { authData: { anonymous: { - id: '00000000-0000-0000-0000-000000000001', + id: "00000000-0000-0000-0000-000000000001", }, }, }; const data2 = { authData: { anonymous: { - id: '00000000-0000-0000-0000-000000000002', + id: "00000000-0000-0000-0000-000000000002", }, }, }; let username1; rest - .create(config, auth.nobody(config), '_User', data1) + .create(config, auth.nobody(config), "_User", data1) .then(r => { - expect(typeof r.response.objectId).toEqual('string'); - expect(typeof r.response.createdAt).toEqual('string'); - expect(typeof r.response.sessionToken).toEqual('string'); - expect(typeof r.response.username).toEqual('string'); - return rest.create(config, auth.nobody(config), '_User', data1); + expect(typeof r.response.objectId).toEqual("string"); + expect(typeof r.response.createdAt).toEqual("string"); + expect(typeof r.response.sessionToken).toEqual("string"); + expect(typeof r.response.username).toEqual("string"); + return rest.create(config, auth.nobody(config), "_User", data1); }) .then(r => { - expect(typeof r.response.objectId).toEqual('string'); - expect(typeof r.response.createdAt).toEqual('string'); - expect(typeof r.response.username).toEqual('string'); - expect(typeof r.response.updatedAt).toEqual('string'); + expect(typeof r.response.objectId).toEqual("string"); + expect(typeof r.response.createdAt).toEqual("string"); + expect(typeof r.response.username).toEqual("string"); + expect(typeof r.response.updatedAt).toEqual("string"); username1 = r.response.username; - return rest.create(config, auth.nobody(config), '_User', data2); + return rest.create(config, auth.nobody(config), "_User", data2); }) .then(r => { - expect(typeof r.response.objectId).toEqual('string'); - expect(typeof r.response.createdAt).toEqual('string'); - expect(typeof r.response.sessionToken).toEqual('string'); - return rest.create(config, auth.nobody(config), '_User', data2); + expect(typeof r.response.objectId).toEqual("string"); + expect(typeof r.response.createdAt).toEqual("string"); + expect(typeof r.response.sessionToken).toEqual("string"); + return rest.create(config, auth.nobody(config), "_User", data2); }) .then(r => { - expect(typeof r.response.objectId).toEqual('string'); - expect(typeof r.response.createdAt).toEqual('string'); - expect(typeof r.response.username).toEqual('string'); - expect(typeof r.response.updatedAt).toEqual('string'); + expect(typeof r.response.objectId).toEqual("string"); + expect(typeof r.response.createdAt).toEqual("string"); + expect(typeof r.response.username).toEqual("string"); + expect(typeof r.response.updatedAt).toEqual("string"); expect(r.response.username).not.toEqual(username1); done(); }); }); - it('handles anonymous user signup and upgrade to new user', done => { + it("handles anonymous user signup and upgrade to new user", done => { const data1 = { authData: { anonymous: { - id: '00000000-0000-0000-0000-000000000001', + id: "00000000-0000-0000-0000-000000000001", }, }, }; const updatedData = { authData: { anonymous: null }, - username: 'hello', - password: 'world', + username: "hello", + password: "world", }; let objectId; rest - .create(config, auth.nobody(config), '_User', data1) + .create(config, auth.nobody(config), "_User", data1) .then(r => { - expect(typeof r.response.objectId).toEqual('string'); - expect(typeof r.response.createdAt).toEqual('string'); - expect(typeof r.response.sessionToken).toEqual('string'); + expect(typeof r.response.objectId).toEqual("string"); + expect(typeof r.response.createdAt).toEqual("string"); + expect(typeof r.response.sessionToken).toEqual("string"); objectId = r.response.objectId; return auth.getAuthForSessionToken({ config, @@ -437,16 +468,22 @@ describe('rest create', () => { }); }) .then(sessionAuth => { - return rest.update(config, sessionAuth, '_User', { objectId }, updatedData); + return rest.update( + config, + sessionAuth, + "_User", + { objectId }, + updatedData + ); }) .then(() => { return Parse.User.logOut().then(() => { - return Parse.User.logIn('hello', 'world'); + return Parse.User.logIn("hello", "world"); }); }) .then(r => { expect(r.id).toEqual(objectId); - expect(r.get('username')).toEqual('hello'); + expect(r.get("username")).toEqual("hello"); done(); }) .catch(err => { @@ -455,56 +492,58 @@ describe('rest create', () => { }); }); - it('handles no anonymous users config', done => { + it("handles no anonymous users config", done => { const NoAnnonConfig = Object.assign({}, config); NoAnnonConfig.authDataManager.setEnableAnonymousUsers(false); const data1 = { authData: { anonymous: { - id: '00000000-0000-0000-0000-000000000001', + id: "00000000-0000-0000-0000-000000000001", }, }, }; - rest.create(NoAnnonConfig, auth.nobody(NoAnnonConfig), '_User', data1).then( + rest.create(NoAnnonConfig, auth.nobody(NoAnnonConfig), "_User", data1).then( () => { - fail('Should throw an error'); + fail("Should throw an error"); done(); }, err => { expect(err.code).toEqual(Parse.Error.UNSUPPORTED_SERVICE); - expect(err.message).toEqual('This authentication method is unsupported.'); + expect(err.message).toEqual( + "This authentication method is unsupported." + ); NoAnnonConfig.authDataManager.setEnableAnonymousUsers(true); done(); } ); }); - it('test facebook signup and login', done => { + it("test facebook signup and login", done => { const data = { authData: { facebook: { - id: '8675309', - access_token: 'jenny', + id: "8675309", + access_token: "jenny", }, }, }; let newUserSignedUpByFacebookObjectId; rest - .create(config, auth.nobody(config), '_User', data) + .create(config, auth.nobody(config), "_User", data) .then(r => { - expect(typeof r.response.objectId).toEqual('string'); - expect(typeof r.response.createdAt).toEqual('string'); - expect(typeof r.response.sessionToken).toEqual('string'); + expect(typeof r.response.objectId).toEqual("string"); + expect(typeof r.response.createdAt).toEqual("string"); + expect(typeof r.response.sessionToken).toEqual("string"); newUserSignedUpByFacebookObjectId = r.response.objectId; - return rest.create(config, auth.nobody(config), '_User', data); + return rest.create(config, auth.nobody(config), "_User", data); }) .then(r => { - expect(typeof r.response.objectId).toEqual('string'); - expect(typeof r.response.createdAt).toEqual('string'); - expect(typeof r.response.username).toEqual('string'); - expect(typeof r.response.updatedAt).toEqual('string'); + expect(typeof r.response.objectId).toEqual("string"); + expect(typeof r.response.createdAt).toEqual("string"); + expect(typeof r.response.username).toEqual("string"); + expect(typeof r.response.updatedAt).toEqual("string"); expect(r.response.objectId).toEqual(newUserSignedUpByFacebookObjectId); - return rest.find(config, auth.master(config), '_Session', { + return rest.find(config, auth.master(config), "_Session", { sessionToken: r.response.sessionToken, }); }) @@ -520,24 +559,24 @@ describe('rest create', () => { }); }); - it('stores pointers', done => { + it("stores pointers", done => { const obj = { - foo: 'bar', + foo: "bar", aPointer: { - __type: 'Pointer', - className: 'JustThePointer', - objectId: 'qwerty1234', // make it 10 chars to match PG storage + __type: "Pointer", + className: "JustThePointer", + objectId: "qwerty1234", // make it 10 chars to match PG storage }, }; rest - .create(config, auth.nobody(config), 'APointerDarkly', obj) + .create(config, auth.nobody(config), "APointerDarkly", obj) .then(() => database.adapter.find( - 'APointerDarkly', + "APointerDarkly", { fields: { - foo: { type: 'String' }, - aPointer: { type: 'Pointer', targetClass: 'JustThePointer' }, + foo: { type: "String" }, + aPointer: { type: "Pointer", targetClass: "JustThePointer" }, }, }, {}, @@ -547,36 +586,36 @@ describe('rest create', () => { .then(results => { expect(results.length).toEqual(1); const output = results[0]; - expect(typeof output.foo).toEqual('string'); - expect(typeof output._p_aPointer).toEqual('undefined'); + expect(typeof output.foo).toEqual("string"); + expect(typeof output._p_aPointer).toEqual("undefined"); expect(output._p_aPointer).toBeUndefined(); expect(output.aPointer).toEqual({ - __type: 'Pointer', - className: 'JustThePointer', - objectId: 'qwerty1234', + __type: "Pointer", + className: "JustThePointer", + objectId: "qwerty1234", }); done(); }); }); - it('stores pointers to objectIds larger than 10 characters', done => { + it("stores pointers to objectIds larger than 10 characters", done => { const obj = { - foo: 'bar', + foo: "bar", aPointer: { - __type: 'Pointer', - className: 'JustThePointer', - objectId: '49F62F92-9B56-46E7-A3D4-BBD14C52F666', + __type: "Pointer", + className: "JustThePointer", + objectId: "49F62F92-9B56-46E7-A3D4-BBD14C52F666", }, }; rest - .create(config, auth.nobody(config), 'APointerDarkly', obj) + .create(config, auth.nobody(config), "APointerDarkly", obj) .then(() => database.adapter.find( - 'APointerDarkly', + "APointerDarkly", { fields: { - foo: { type: 'String' }, - aPointer: { type: 'Pointer', targetClass: 'JustThePointer' }, + foo: { type: "String" }, + aPointer: { type: "Pointer", targetClass: "JustThePointer" }, }, }, {}, @@ -586,78 +625,78 @@ describe('rest create', () => { .then(results => { expect(results.length).toEqual(1); const output = results[0]; - expect(typeof output.foo).toEqual('string'); - expect(typeof output._p_aPointer).toEqual('undefined'); + expect(typeof output.foo).toEqual("string"); + expect(typeof output._p_aPointer).toEqual("undefined"); expect(output._p_aPointer).toBeUndefined(); expect(output.aPointer).toEqual({ - __type: 'Pointer', - className: 'JustThePointer', - objectId: '49F62F92-9B56-46E7-A3D4-BBD14C52F666', + __type: "Pointer", + className: "JustThePointer", + objectId: "49F62F92-9B56-46E7-A3D4-BBD14C52F666", }); done(); }); }); - it('cannot set objectId', done => { + it("cannot set objectId", done => { const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; request({ headers: headers, - method: 'POST', - url: 'http://localhost:8378/1/classes/TestObject', + method: "POST", + url: "http://localhost:8378/1/classes/TestObject", body: JSON.stringify({ - foo: 'bar', - objectId: 'hello', + foo: "bar", + objectId: "hello", }), }).then(fail, response => { const b = response.data; expect(b.code).toEqual(105); - expect(b.error).toEqual('objectId is an invalid field name.'); + expect(b.error).toEqual("objectId is an invalid field name."); done(); }); }); - it('cannot set id', done => { + it("cannot set id", done => { const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; request({ headers: headers, - method: 'POST', - url: 'http://localhost:8378/1/classes/TestObject', + method: "POST", + url: "http://localhost:8378/1/classes/TestObject", body: JSON.stringify({ - foo: 'bar', - id: 'hello', + foo: "bar", + id: "hello", }), }).then(fail, response => { const b = response.data; expect(b.code).toEqual(105); - expect(b.error).toEqual('id is an invalid field name.'); + expect(b.error).toEqual("id is an invalid field name."); done(); }); }); - it('test default session length', done => { + it("test default session length", done => { const user = { - username: 'asdf', - password: 'zxcv', - foo: 'bar', + username: "asdf", + password: "zxcv", + foo: "bar", }; const now = new Date(); rest - .create(config, auth.nobody(config), '_User', user) + .create(config, auth.nobody(config), "_User", user) .then(r => { expect(Object.keys(r.response).length).toEqual(3); - expect(typeof r.response.objectId).toEqual('string'); - expect(typeof r.response.createdAt).toEqual('string'); - expect(typeof r.response.sessionToken).toEqual('string'); - return rest.find(config, auth.master(config), '_Session', { + expect(typeof r.response.objectId).toEqual("string"); + expect(typeof r.response.createdAt).toEqual("string"); + expect(typeof r.response.sessionToken).toEqual("string"); + return rest.find(config, auth.master(config), "_Session", { sessionToken: r.response.sessionToken, }); }) @@ -668,30 +707,32 @@ describe('rest create', () => { const actual = new Date(session.expiresAt.iso); const expected = new Date(now.getTime() + 1000 * 3600 * 24 * 365); - expect(Math.abs(actual - expected) <= jasmine.DEFAULT_TIMEOUT_INTERVAL).toEqual(true); + expect( + Math.abs(actual - expected) <= jasmine.DEFAULT_TIMEOUT_INTERVAL + ).toEqual(true); done(); }); }); - it('test specified session length', done => { + it("test specified session length", done => { const user = { - username: 'asdf', - password: 'zxcv', - foo: 'bar', + username: "asdf", + password: "zxcv", + foo: "bar", }; const sessionLength = 3600, // 1 Hour ahead now = new Date(); // For reference later config.sessionLength = sessionLength; rest - .create(config, auth.nobody(config), '_User', user) + .create(config, auth.nobody(config), "_User", user) .then(r => { expect(Object.keys(r.response).length).toEqual(3); - expect(typeof r.response.objectId).toEqual('string'); - expect(typeof r.response.createdAt).toEqual('string'); - expect(typeof r.response.sessionToken).toEqual('string'); - return rest.find(config, auth.master(config), '_Session', { + expect(typeof r.response.objectId).toEqual("string"); + expect(typeof r.response.createdAt).toEqual("string"); + expect(typeof r.response.sessionToken).toEqual("string"); + return rest.find(config, auth.master(config), "_Session", { sessionToken: r.response.sessionToken, }); }) @@ -702,7 +743,9 @@ describe('rest create', () => { const actual = new Date(session.expiresAt.iso); const expected = new Date(now.getTime() + sessionLength * 1000); - expect(Math.abs(actual - expected) <= jasmine.DEFAULT_TIMEOUT_INTERVAL).toEqual(true); + expect( + Math.abs(actual - expected) <= jasmine.DEFAULT_TIMEOUT_INTERVAL + ).toEqual(true); done(); }) @@ -712,22 +755,22 @@ describe('rest create', () => { }); }); - it('can create a session with no expiration', done => { + it("can create a session with no expiration", done => { const user = { - username: 'asdf', - password: 'zxcv', - foo: 'bar', + username: "asdf", + password: "zxcv", + foo: "bar", }; config.expireInactiveSessions = false; rest - .create(config, auth.nobody(config), '_User', user) + .create(config, auth.nobody(config), "_User", user) .then(r => { expect(Object.keys(r.response).length).toEqual(3); - expect(typeof r.response.objectId).toEqual('string'); - expect(typeof r.response.createdAt).toEqual('string'); - expect(typeof r.response.sessionToken).toEqual('string'); - return rest.find(config, auth.master(config), '_Session', { + expect(typeof r.response.objectId).toEqual("string"); + expect(typeof r.response.createdAt).toEqual("string"); + expect(typeof r.response.sessionToken).toEqual("string"); + return rest.find(config, auth.master(config), "_Session", { sessionToken: r.response.sessionToken, }); }) @@ -746,24 +789,24 @@ describe('rest create', () => { }); }); - it('can create object in volatileClasses if masterKey', done => { + it("can create object in volatileClasses if masterKey", done => { rest - .create(config, auth.master(config), '_PushStatus', {}) + .create(config, auth.master(config), "_PushStatus", {}) .then(r => { expect(r.response.objectId.length).toBe(10); }) .then(() => { - rest.create(config, auth.master(config), '_JobStatus', {}).then(r => { + rest.create(config, auth.master(config), "_JobStatus", {}).then(r => { expect(r.response.objectId.length).toBe(10); done(); }); }); }); - it('cannot create object in volatileClasses if not masterKey', done => { + it("cannot create object in volatileClasses if not masterKey", done => { Promise.resolve() .then(() => { - return rest.create(config, auth.nobody(config), '_PushStatus', {}); + return rest.create(config, auth.nobody(config), "_PushStatus", {}); }) .catch(error => { expect(error.code).toEqual(119); @@ -771,66 +814,66 @@ describe('rest create', () => { }); }); - it('cannot get object in volatileClasses if not masterKey through pointer', async () => { - const masterKeyOnlyClassObject = new Parse.Object('_PushStatus'); + it("cannot get object in volatileClasses if not masterKey through pointer", async () => { + const masterKeyOnlyClassObject = new Parse.Object("_PushStatus"); await masterKeyOnlyClassObject.save(null, { useMasterKey: true }); - const obj2 = new Parse.Object('TestObject'); + const obj2 = new Parse.Object("TestObject"); // Anyone is can basically create a pointer to any object // or some developers can use master key in some hook to link // private objects to standard objects - obj2.set('pointer', masterKeyOnlyClassObject); + obj2.set("pointer", masterKeyOnlyClassObject); await obj2.save(); - const query = new Parse.Query('TestObject'); - query.include('pointer'); + const query = new Parse.Query("TestObject"); + query.include("pointer"); await expectAsync(query.get(obj2.id)).toBeRejectedWithError( "Clients aren't allowed to perform the get operation on the _PushStatus collection." ); }); - it_id('3ce563bf-93aa-4d0b-9af9-c5fb246ac9fc')(it)( - 'cannot get object in _GlobalConfig if not masterKey through pointer', + it_id("3ce563bf-93aa-4d0b-9af9-c5fb246ac9fc")(it)( + "cannot get object in _GlobalConfig if not masterKey through pointer", async () => { - await Parse.Config.save({ privateData: 'secret' }, { privateData: true }); - const obj2 = new Parse.Object('TestObject'); - obj2.set('globalConfigPointer', { - __type: 'Pointer', - className: '_GlobalConfig', + await Parse.Config.save({ privateData: "secret" }, { privateData: true }); + const obj2 = new Parse.Object("TestObject"); + obj2.set("globalConfigPointer", { + __type: "Pointer", + className: "_GlobalConfig", objectId: 1, }); await obj2.save(); - const query = new Parse.Query('TestObject'); - query.include('globalConfigPointer'); + const query = new Parse.Query("TestObject"); + query.include("globalConfigPointer"); await expectAsync(query.get(obj2.id)).toBeRejectedWithError( "Clients aren't allowed to perform the get operation on the _GlobalConfig collection." ); } ); - it('locks down session', done => { + it("locks down session", done => { let currentUser; - Parse.User.signUp('foo', 'bar') + Parse.User.signUp("foo", "bar") .then(user => { currentUser = user; const sessionToken = user.getSessionToken(); const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Session-Token': sessionToken, + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "X-Parse-Session-Token": sessionToken, }; let sessionId; return request({ headers: headers, - url: 'http://localhost:8378/1/sessions/me', + url: "http://localhost:8378/1/sessions/me", }) .then(response => { sessionId = response.data.objectId; return request({ headers, - method: 'PUT', - url: 'http://localhost:8378/1/sessions/' + sessionId, + method: "PUT", + url: "http://localhost:8378/1/sessions/" + sessionId, body: { - installationId: 'yolo', + installationId: "yolo", }, }); }) @@ -839,25 +882,25 @@ describe('rest create', () => { expect(res.data.code).toBe(105); return request({ headers, - method: 'PUT', - url: 'http://localhost:8378/1/sessions/' + sessionId, + method: "PUT", + url: "http://localhost:8378/1/sessions/" + sessionId, body: { - sessionToken: 'yolo', + sessionToken: "yolo", }, }); }) .then(done.fail, res => { expect(res.status).toBe(400); expect(res.data.code).toBe(105); - return Parse.User.signUp('other', 'user'); + return Parse.User.signUp("other", "user"); }) .then(otherUser => { const user = new Parse.User(); user.id = otherUser.id; return request({ headers, - method: 'PUT', - url: 'http://localhost:8378/1/sessions/' + sessionId, + method: "PUT", + url: "http://localhost:8378/1/sessions/" + sessionId, body: { user: Parse._encode(user), }, @@ -870,8 +913,8 @@ describe('rest create', () => { user.id = currentUser.id; return request({ headers, - method: 'PUT', - url: 'http://localhost:8378/1/sessions/' + sessionId, + method: "PUT", + url: "http://localhost:8378/1/sessions/" + sessionId, body: { user: Parse._encode(user), }, @@ -883,24 +926,24 @@ describe('rest create', () => { .catch(done.fail); }); - it('sets current user in new sessions', done => { + it("sets current user in new sessions", done => { let currentUser; - Parse.User.signUp('foo', 'bar') + Parse.User.signUp("foo", "bar") .then(user => { currentUser = user; const sessionToken = user.getSessionToken(); const headers = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Session-Token': sessionToken, - 'Content-Type': 'application/json', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "X-Parse-Session-Token": sessionToken, + "Content-Type": "application/json", }; return request({ headers, - method: 'POST', - url: 'http://localhost:8378/1/sessions', + method: "POST", + url: "http://localhost:8378/1/sessions", body: { - user: { __type: 'Pointer', className: '_User', objectId: 'fakeId' }, + user: { __type: "Pointer", className: "_User", objectId: "fakeId" }, }, }); }) @@ -915,27 +958,29 @@ describe('rest create', () => { }); }); -describe('rest update', () => { - it('ignores createdAt', done => { - const config = Config.get('test'); +describe("rest update", () => { + it("ignores createdAt", done => { + const config = Config.get("test"); const nobody = auth.nobody(config); - const className = 'Foo'; - const newCreatedAt = new Date('1970-01-01T00:00:00.000Z'); + const className = "Foo"; + const newCreatedAt = new Date("1970-01-01T00:00:00.000Z"); rest .create(config, nobody, className, {}) .then(res => { const objectId = res.response.objectId; const restObject = { - createdAt: { __type: 'Date', iso: newCreatedAt }, // should be ignored + createdAt: { __type: "Date", iso: newCreatedAt }, // should be ignored }; - return rest.update(config, nobody, className, { objectId }, restObject).then(() => { - const restWhere = { - objectId: objectId, - }; - return rest.find(config, nobody, className, restWhere, {}); - }); + return rest + .update(config, nobody, className, { objectId }, restObject) + .then(() => { + const restWhere = { + objectId: objectId, + }; + return rest.find(config, nobody, className, restWhere, {}); + }); }) .then(res2 => { const updatedObject = res2.results[0]; @@ -947,12 +992,12 @@ describe('rest update', () => { }); }); -describe('read-only masterKey', () => { - it('properly throws on rest.create, rest.update and rest.del', () => { - const config = Config.get('test'); +describe("read-only masterKey", () => { + it("properly throws on rest.create, rest.update and rest.del", () => { + const config = Config.get("test"); const readOnly = auth.readOnly(config); expect(() => { - rest.create(config, readOnly, 'AnObject', {}); + rest.create(config, readOnly, "AnObject", {}); }).toThrow( new Parse.Error( Parse.Error.OPERATION_FORBIDDEN, @@ -960,27 +1005,27 @@ describe('read-only masterKey', () => { ) ); expect(() => { - rest.update(config, readOnly, 'AnObject', {}); + rest.update(config, readOnly, "AnObject", {}); }).toThrow(); expect(() => { - rest.del(config, readOnly, 'AnObject', {}); + rest.del(config, readOnly, "AnObject", {}); }).toThrow(); }); - it('properly blocks writes', async () => { + it("properly blocks writes", async () => { await reconfigureServer({ - readOnlyMasterKey: 'yolo-read-only', + readOnlyMasterKey: "yolo-read-only", }); try { await request({ url: `${Parse.serverURL}/classes/MyYolo`, - method: 'POST', + method: "POST", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': 'yolo-read-only', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Master-Key": "yolo-read-only", + "Content-Type": "application/json", }, - body: { foo: 'bar' }, + body: { foo: "bar" }, }); fail(); } catch (res) { @@ -992,143 +1037,157 @@ describe('read-only masterKey', () => { await reconfigureServer(); }); - it('should throw when masterKey and readOnlyMasterKey are the same', async () => { + it("should throw when masterKey and readOnlyMasterKey are the same", async () => { try { await reconfigureServer({ - masterKey: 'yolo', - readOnlyMasterKey: 'yolo', + masterKey: "yolo", + readOnlyMasterKey: "yolo", }); fail(); } catch (err) { - expect(err).toEqual(new Error('masterKey and readOnlyMasterKey should be different')); + expect(err).toEqual( + new Error("masterKey and readOnlyMasterKey should be different") + ); } await reconfigureServer(); }); - it('should throw when masterKey and maintenanceKey are the same', async () => { + it("should throw when masterKey and maintenanceKey are the same", async () => { await expectAsync( reconfigureServer({ - masterKey: 'yolo', - maintenanceKey: 'yolo', + masterKey: "yolo", + maintenanceKey: "yolo", }) - ).toBeRejectedWith(new Error('masterKey and maintenanceKey should be different')); + ).toBeRejectedWith( + new Error("masterKey and maintenanceKey should be different") + ); }); - it('should throw when trying to create RestWrite', () => { - const config = Config.get('test'); + it("should throw when trying to create RestWrite", () => { + const config = Config.get("test"); expect(() => { new RestWrite(config, auth.readOnly(config)); }).toThrow( new Parse.Error( Parse.Error.OPERATION_FORBIDDEN, - 'Cannot perform a write operation when using readOnlyMasterKey' + "Cannot perform a write operation when using readOnlyMasterKey" ) ); }); - it('should throw when trying to create schema', done => { + it("should throw when trying to create schema", done => { request({ - method: 'POST', + method: "POST", url: `${Parse.serverURL}/schemas`, headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': 'read-only-test', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Master-Key": "read-only-test", + "Content-Type": "application/json", }, json: {}, }) .then(done.fail) .catch(res => { expect(res.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN); - expect(res.data.error).toBe("read-only masterKey isn't allowed to create a schema."); + expect(res.data.error).toBe( + "read-only masterKey isn't allowed to create a schema." + ); done(); }); }); - it('should throw when trying to create schema with a name', done => { + it("should throw when trying to create schema with a name", done => { request({ url: `${Parse.serverURL}/schemas/MyClass`, - method: 'POST', + method: "POST", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': 'read-only-test', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Master-Key": "read-only-test", + "Content-Type": "application/json", }, json: {}, }) .then(done.fail) .catch(res => { expect(res.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN); - expect(res.data.error).toBe("read-only masterKey isn't allowed to create a schema."); + expect(res.data.error).toBe( + "read-only masterKey isn't allowed to create a schema." + ); done(); }); }); - it('should throw when trying to update schema', done => { + it("should throw when trying to update schema", done => { request({ url: `${Parse.serverURL}/schemas/MyClass`, - method: 'PUT', + method: "PUT", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': 'read-only-test', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Master-Key": "read-only-test", + "Content-Type": "application/json", }, json: {}, }) .then(done.fail) .catch(res => { expect(res.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN); - expect(res.data.error).toBe("read-only masterKey isn't allowed to update a schema."); + expect(res.data.error).toBe( + "read-only masterKey isn't allowed to update a schema." + ); done(); }); }); - it('should throw when trying to delete schema', done => { + it("should throw when trying to delete schema", done => { request({ url: `${Parse.serverURL}/schemas/MyClass`, - method: 'DELETE', + method: "DELETE", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': 'read-only-test', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Master-Key": "read-only-test", + "Content-Type": "application/json", }, json: {}, }) .then(done.fail) .catch(res => { expect(res.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN); - expect(res.data.error).toBe("read-only masterKey isn't allowed to delete a schema."); + expect(res.data.error).toBe( + "read-only masterKey isn't allowed to delete a schema." + ); done(); }); }); - it('should throw when trying to update the global config', done => { + it("should throw when trying to update the global config", done => { request({ url: `${Parse.serverURL}/config`, - method: 'PUT', + method: "PUT", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': 'read-only-test', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Master-Key": "read-only-test", + "Content-Type": "application/json", }, json: {}, }) .then(done.fail) .catch(res => { expect(res.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN); - expect(res.data.error).toBe("read-only masterKey isn't allowed to update the config."); + expect(res.data.error).toBe( + "read-only masterKey isn't allowed to update the config." + ); done(); }); }); - it('should throw when trying to send push', done => { + it("should throw when trying to send push", done => { request({ url: `${Parse.serverURL}/push`, - method: 'POST', + method: "POST", headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Master-Key': 'read-only-test', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": Parse.applicationId, + "X-Parse-Master-Key": "read-only-test", + "Content-Type": "application/json", }, json: {}, }) diff --git a/spec/schemas.spec.js b/spec/schemas.spec.js index 167f3ff19a..0ee6ef4448 100644 --- a/spec/schemas.spec.js +++ b/spec/schemas.spec.js @@ -1,24 +1,28 @@ -'use strict'; +"use strict"; -const Parse = require('parse/node').Parse; -const dd = require('deep-diff'); -const Config = require('../lib/Config'); -const request = require('../lib/request'); -const TestUtils = require('../lib/TestUtils'); -const SchemaController = require('../lib/Controllers/SchemaController').SchemaController; +const Parse = require("parse/node").Parse; +const dd = require("deep-diff"); +const Config = require("../lib/Config"); +const request = require("../lib/request"); +const TestUtils = require("../lib/TestUtils"); +const SchemaController = + require("../lib/Controllers/SchemaController").SchemaController; let config; const hasAllPODobject = () => { - const obj = new Parse.Object('HasAllPOD'); - obj.set('aNumber', 5); - obj.set('aString', 'string'); - obj.set('aBool', true); - obj.set('aDate', new Date()); - obj.set('aObject', { k1: 'value', k2: true, k3: 5 }); - obj.set('aArray', ['contents', true, 5]); - obj.set('aGeoPoint', new Parse.GeoPoint({ latitude: 0, longitude: 0 })); - obj.set('aFile', new Parse.File('f.txt', { base64: 'V29ya2luZyBhdCBQYXJzZSBpcyBncmVhdCE=' })); + const obj = new Parse.Object("HasAllPOD"); + obj.set("aNumber", 5); + obj.set("aString", "string"); + obj.set("aBool", true); + obj.set("aDate", new Date()); + obj.set("aObject", { k1: "value", k2: true, k3: 5 }); + obj.set("aArray", ["contents", true, 5]); + obj.set("aGeoPoint", new Parse.GeoPoint({ latitude: 0, longitude: 0 })); + obj.set( + "aFile", + new Parse.File("f.txt", { base64: "V29ya2luZyBhdCBQYXJzZSBpcyBncmVhdCE=" }) + ); const objACL = new Parse.ACL(); objACL.setPublicWriteAccess(false); obj.setACL(objACL); @@ -27,172 +31,176 @@ const hasAllPODobject = () => { const defaultClassLevelPermissions = { ACL: { - '*': { + "*": { read: true, write: true, }, }, find: { - '*': true, + "*": true, }, count: { - '*': true, + "*": true, }, create: { - '*': true, + "*": true, }, get: { - '*': true, + "*": true, }, update: { - '*': true, + "*": true, }, addField: { - '*': true, + "*": true, }, delete: { - '*': true, + "*": true, }, protectedFields: { - '*': [], + "*": [], }, }; const plainOldDataSchema = { - className: 'HasAllPOD', + className: "HasAllPOD", fields: { //Default fields - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - objectId: { type: 'String' }, + ACL: { type: "ACL" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + objectId: { type: "String" }, //Custom fields - aNumber: { type: 'Number' }, - aString: { type: 'String' }, - aBool: { type: 'Boolean' }, - aDate: { type: 'Date' }, - aObject: { type: 'Object' }, - aArray: { type: 'Array' }, - aGeoPoint: { type: 'GeoPoint' }, - aFile: { type: 'File' }, + aNumber: { type: "Number" }, + aString: { type: "String" }, + aBool: { type: "Boolean" }, + aDate: { type: "Date" }, + aObject: { type: "Object" }, + aArray: { type: "Array" }, + aGeoPoint: { type: "GeoPoint" }, + aFile: { type: "File" }, }, classLevelPermissions: defaultClassLevelPermissions, }; const pointersAndRelationsSchema = { - className: 'HasPointersAndRelations', + className: "HasPointersAndRelations", fields: { //Default fields - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - objectId: { type: 'String' }, + ACL: { type: "ACL" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + objectId: { type: "String" }, //Custom fields aPointer: { - type: 'Pointer', - targetClass: 'HasAllPOD', + type: "Pointer", + targetClass: "HasAllPOD", }, aRelation: { - type: 'Relation', - targetClass: 'HasAllPOD', + type: "Relation", + targetClass: "HasAllPOD", }, }, classLevelPermissions: defaultClassLevelPermissions, }; const userSchema = { - className: '_User', + className: "_User", fields: { - objectId: { type: 'String' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - ACL: { type: 'ACL' }, - username: { type: 'String' }, - password: { type: 'String' }, - email: { type: 'String' }, - emailVerified: { type: 'Boolean' }, - authData: { type: 'Object' }, + objectId: { type: "String" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + ACL: { type: "ACL" }, + username: { type: "String" }, + password: { type: "String" }, + email: { type: "String" }, + emailVerified: { type: "Boolean" }, + authData: { type: "Object" }, }, classLevelPermissions: defaultClassLevelPermissions, }; const roleSchema = { - className: '_Role', + className: "_Role", fields: { - objectId: { type: 'String' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - ACL: { type: 'ACL' }, - name: { type: 'String' }, - users: { type: 'Relation', targetClass: '_User' }, - roles: { type: 'Relation', targetClass: '_Role' }, + objectId: { type: "String" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + ACL: { type: "ACL" }, + name: { type: "String" }, + users: { type: "Relation", targetClass: "_User" }, + roles: { type: "Relation", targetClass: "_Role" }, }, classLevelPermissions: defaultClassLevelPermissions, }; const noAuthHeaders = { - 'X-Parse-Application-Id': 'test', + "X-Parse-Application-Id": "test", }; const restKeyHeaders = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", + "Content-Type": "application/json", }; const masterKeyHeaders = { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key': 'test', - 'Content-Type': 'application/json', + "X-Parse-Application-Id": "test", + "X-Parse-Master-Key": "test", + "Content-Type": "application/json", }; -describe('schemas', () => { +describe("schemas", () => { beforeEach(async () => { await reconfigureServer(); - config = Config.get('test'); + config = Config.get("test"); }); - it('requires the master key to get all schemas', done => { + it("requires the master key to get all schemas", done => { request({ - url: 'http://localhost:8378/1/schemas', + url: "http://localhost:8378/1/schemas", json: true, headers: noAuthHeaders, }).then(fail, response => { //api.parse.com uses status code 401, but due to the lack of keys //being necessary in parse-server, 403 makes more sense expect(response.status).toEqual(403); - expect(response.data.error).toEqual('unauthorized'); + expect(response.data.error).toEqual("unauthorized"); done(); }); }); - it('requires the master key to get one schema', done => { + it("requires the master key to get one schema", done => { request({ - url: 'http://localhost:8378/1/schemas/SomeSchema', + url: "http://localhost:8378/1/schemas/SomeSchema", json: true, headers: restKeyHeaders, }).then(fail, response => { expect(response.status).toEqual(403); - expect(response.data.error).toEqual('unauthorized: master key is required'); + expect(response.data.error).toEqual( + "unauthorized: master key is required" + ); done(); }); }); - it('asks for the master key if you use the rest key', done => { + it("asks for the master key if you use the rest key", done => { request({ - url: 'http://localhost:8378/1/schemas', + url: "http://localhost:8378/1/schemas", json: true, headers: restKeyHeaders, }).then(fail, response => { expect(response.status).toEqual(403); - expect(response.data.error).toEqual('unauthorized: master key is required'); + expect(response.data.error).toEqual( + "unauthorized: master key is required" + ); done(); }); }); - it('creates _User schema when server starts', done => { + it("creates _User schema when server starts", done => { request({ - url: 'http://localhost:8378/1/schemas', + url: "http://localhost:8378/1/schemas", json: true, headers: masterKeyHeaders, }).then(response => { @@ -207,30 +215,39 @@ describe('schemas', () => { delete withoutIndexes.indexes; return withoutIndexes; }) - ).toEqual(expected.results.sort((s1, s2) => s1.className.localeCompare(s2.className))); + ).toEqual( + expected.results.sort((s1, s2) => + s1.className.localeCompare(s2.className) + ) + ); done(); }); }); - it('responds with a list of schemas after creating objects', done => { + it("responds with a list of schemas after creating objects", done => { const obj1 = hasAllPODobject(); obj1 .save() .then(savedObj1 => { - const obj2 = new Parse.Object('HasPointersAndRelations'); - obj2.set('aPointer', savedObj1); - const relation = obj2.relation('aRelation'); + const obj2 = new Parse.Object("HasPointersAndRelations"); + obj2.set("aPointer", savedObj1); + const relation = obj2.relation("aRelation"); relation.add(obj1); return obj2.save(); }) .then(() => { request({ - url: 'http://localhost:8378/1/schemas', + url: "http://localhost:8378/1/schemas", json: true, headers: masterKeyHeaders, }).then(response => { const expected = { - results: [userSchema, roleSchema, plainOldDataSchema, pointersAndRelationsSchema], + results: [ + userSchema, + roleSchema, + plainOldDataSchema, + pointersAndRelationsSchema, + ], }; expect( response.data.results @@ -240,26 +257,32 @@ describe('schemas', () => { delete withoutIndexes.indexes; return withoutIndexes; }) - ).toEqual(expected.results.sort((s1, s2) => s1.className.localeCompare(s2.className))); + ).toEqual( + expected.results.sort((s1, s2) => + s1.className.localeCompare(s2.className) + ) + ); done(); }); }); }); - it('ensure refresh cache after creating a class', async done => { - spyOn(SchemaController.prototype, 'reloadData').and.callFake(() => Promise.resolve()); + it("ensure refresh cache after creating a class", async done => { + spyOn(SchemaController.prototype, "reloadData").and.callFake(() => + Promise.resolve() + ); await request({ - url: 'http://localhost:8378/1/schemas', - method: 'POST', + url: "http://localhost:8378/1/schemas", + method: "POST", headers: masterKeyHeaders, json: true, body: { - className: 'A', + className: "A", }, }); const response = await request({ - url: 'http://localhost:8378/1/schemas', - method: 'GET', + url: "http://localhost:8378/1/schemas", + method: "GET", headers: masterKeyHeaders, json: true, }); @@ -268,13 +291,13 @@ describe('schemas', () => { userSchema, roleSchema, { - className: 'A', + className: "A", fields: { //Default fields - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - objectId: { type: 'String' }, + ACL: { type: "ACL" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + objectId: { type: "String" }, }, classLevelPermissions: defaultClassLevelPermissions, }, @@ -288,15 +311,19 @@ describe('schemas', () => { delete withoutIndexes.indexes; return withoutIndexes; }) - ).toEqual(expected.results.sort((s1, s2) => s1.className.localeCompare(s2.className))); + ).toEqual( + expected.results.sort((s1, s2) => + s1.className.localeCompare(s2.className) + ) + ); done(); }); - it('responds with a single schema', done => { + it("responds with a single schema", done => { const obj = hasAllPODobject(); obj.save().then(() => { request({ - url: 'http://localhost:8378/1/schemas/HasAllPOD', + url: "http://localhost:8378/1/schemas/HasAllPOD", json: true, headers: masterKeyHeaders, }).then(response => { @@ -306,63 +333,63 @@ describe('schemas', () => { }); }); - it('treats class names case sensitively', done => { + it("treats class names case sensitively", done => { const obj = hasAllPODobject(); obj.save().then(() => { request({ - url: 'http://localhost:8378/1/schemas/HASALLPOD', + url: "http://localhost:8378/1/schemas/HASALLPOD", json: true, headers: masterKeyHeaders, }).then(fail, response => { expect(response.status).toEqual(400); expect(response.data).toEqual({ code: 103, - error: 'Class HASALLPOD does not exist.', + error: "Class HASALLPOD does not exist.", }); done(); }); }); }); - it('requires the master key to create a schema', done => { + it("requires the master key to create a schema", done => { request({ - url: 'http://localhost:8378/1/schemas', - method: 'POST', + url: "http://localhost:8378/1/schemas", + method: "POST", json: true, headers: noAuthHeaders, body: { - className: 'MyClass', + className: "MyClass", }, }).then(fail, response => { expect(response.status).toEqual(403); - expect(response.data.error).toEqual('unauthorized'); + expect(response.data.error).toEqual("unauthorized"); done(); }); }); - it('sends an error if you use mismatching class names', done => { + it("sends an error if you use mismatching class names", done => { request({ - url: 'http://localhost:8378/1/schemas/A', - method: 'POST', + url: "http://localhost:8378/1/schemas/A", + method: "POST", headers: masterKeyHeaders, json: true, body: { - className: 'B', + className: "B", }, }).then(fail, response => { expect(response.status).toEqual(400); expect(response.data).toEqual({ code: Parse.Error.INVALID_CLASS_NAME, - error: 'Class name mismatch between B and A.', + error: "Class name mismatch between B and A.", }); done(); }); }); - it('sends an error if you use no class name', done => { + it("sends an error if you use no class name", done => { request({ - url: 'http://localhost:8378/1/schemas', - method: 'POST', + url: "http://localhost:8378/1/schemas", + method: "POST", headers: masterKeyHeaders, json: true, body: {}, @@ -370,64 +397,64 @@ describe('schemas', () => { expect(response.status).toEqual(400); expect(response.data).toEqual({ code: 135, - error: 'POST /schemas needs a class name.', + error: "POST /schemas needs a class name.", }); done(); }); }); - it('sends an error if you try to create the same class twice', done => { + it("sends an error if you try to create the same class twice", done => { request({ - url: 'http://localhost:8378/1/schemas', - method: 'POST', + url: "http://localhost:8378/1/schemas", + method: "POST", headers: masterKeyHeaders, json: true, body: { - className: 'A', + className: "A", }, }).then(() => { request({ - url: 'http://localhost:8378/1/schemas', - method: 'POST', + url: "http://localhost:8378/1/schemas", + method: "POST", headers: masterKeyHeaders, json: true, body: { - className: 'A', + className: "A", }, }).then(fail, response => { expect(response.status).toEqual(400); expect(response.data).toEqual({ code: Parse.Error.INVALID_CLASS_NAME, - error: 'Class A already exists.', + error: "Class A already exists.", }); done(); }); }); }); - it('responds with all fields when you create a class', done => { + it("responds with all fields when you create a class", done => { request({ - url: 'http://localhost:8378/1/schemas', - method: 'POST', + url: "http://localhost:8378/1/schemas", + method: "POST", headers: masterKeyHeaders, json: true, body: { - className: 'NewClass', + className: "NewClass", fields: { - foo: { type: 'Number' }, - ptr: { type: 'Pointer', targetClass: 'SomeClass' }, + foo: { type: "Number" }, + ptr: { type: "Pointer", targetClass: "SomeClass" }, }, }, }).then(response => { expect(response.data).toEqual({ - className: 'NewClass', + className: "NewClass", fields: { - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - objectId: { type: 'String' }, - foo: { type: 'Number' }, - ptr: { type: 'Pointer', targetClass: 'SomeClass' }, + ACL: { type: "ACL" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + objectId: { type: "String" }, + foo: { type: "Number" }, + ptr: { type: "Pointer", targetClass: "SomeClass" }, }, classLevelPermissions: defaultClassLevelPermissions, }); @@ -435,288 +462,292 @@ describe('schemas', () => { }); }); - it('responds with all fields and options when you create a class with field options', done => { + it("responds with all fields and options when you create a class with field options", done => { request({ - url: 'http://localhost:8378/1/schemas', - method: 'POST', + url: "http://localhost:8378/1/schemas", + method: "POST", headers: masterKeyHeaders, json: true, body: { - className: 'NewClassWithOptions', + className: "NewClassWithOptions", fields: { - foo1: { type: 'Number' }, - foo2: { type: 'Number', required: true, defaultValue: 10 }, + foo1: { type: "Number" }, + foo2: { type: "Number", required: true, defaultValue: 10 }, foo3: { - type: 'String', + type: "String", required: false, - defaultValue: 'some string', + defaultValue: "some string", }, - foo4: { type: 'Date', required: true }, - foo5: { type: 'Number', defaultValue: 5 }, - ptr: { type: 'Pointer', targetClass: 'SomeClass', required: false }, + foo4: { type: "Date", required: true }, + foo5: { type: "Number", defaultValue: 5 }, + ptr: { type: "Pointer", targetClass: "SomeClass", required: false }, defaultFalse: { - type: 'Boolean', + type: "Boolean", required: true, defaultValue: false, }, - defaultZero: { type: 'Number', defaultValue: 0 }, - relation: { type: 'Relation', targetClass: 'SomeClass' }, + defaultZero: { type: "Number", defaultValue: 0 }, + relation: { type: "Relation", targetClass: "SomeClass" }, }, }, }).then(async response => { expect(response.data).toEqual({ - className: 'NewClassWithOptions', + className: "NewClassWithOptions", fields: { - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - objectId: { type: 'String' }, - foo1: { type: 'Number' }, - foo2: { type: 'Number', required: true, defaultValue: 10 }, + ACL: { type: "ACL" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + objectId: { type: "String" }, + foo1: { type: "Number" }, + foo2: { type: "Number", required: true, defaultValue: 10 }, foo3: { - type: 'String', + type: "String", required: false, - defaultValue: 'some string', + defaultValue: "some string", }, - foo4: { type: 'Date', required: true }, - foo5: { type: 'Number', defaultValue: 5 }, - ptr: { type: 'Pointer', targetClass: 'SomeClass', required: false }, + foo4: { type: "Date", required: true }, + foo5: { type: "Number", defaultValue: 5 }, + ptr: { type: "Pointer", targetClass: "SomeClass", required: false }, defaultFalse: { - type: 'Boolean', + type: "Boolean", required: true, defaultValue: false, }, - defaultZero: { type: 'Number', defaultValue: 0 }, - relation: { type: 'Relation', targetClass: 'SomeClass' }, + defaultZero: { type: "Number", defaultValue: 0 }, + relation: { type: "Relation", targetClass: "SomeClass" }, }, classLevelPermissions: defaultClassLevelPermissions, }); - const obj = new Parse.Object('NewClassWithOptions'); + const obj = new Parse.Object("NewClassWithOptions"); try { await obj.save(); - fail('should fail'); + fail("should fail"); } catch (e) { expect(e.code).toEqual(142); } const date = new Date(); - obj.set('foo4', date); + obj.set("foo4", date); await obj.save(); - expect(obj.get('foo1')).toBeUndefined(); - expect(obj.get('foo2')).toEqual(10); - expect(obj.get('foo3')).toEqual('some string'); - expect(obj.get('foo4')).toEqual(date); - expect(obj.get('foo5')).toEqual(5); - expect(obj.get('ptr')).toBeUndefined(); - expect(obj.get('defaultFalse')).toEqual(false); - expect(obj.get('defaultZero')).toEqual(0); - expect(obj.get('ptr')).toBeUndefined(); - expect(obj.get('relation')).toBeUndefined(); + expect(obj.get("foo1")).toBeUndefined(); + expect(obj.get("foo2")).toEqual(10); + expect(obj.get("foo3")).toEqual("some string"); + expect(obj.get("foo4")).toEqual(date); + expect(obj.get("foo5")).toEqual(5); + expect(obj.get("ptr")).toBeUndefined(); + expect(obj.get("defaultFalse")).toEqual(false); + expect(obj.get("defaultZero")).toEqual(0); + expect(obj.get("ptr")).toBeUndefined(); + expect(obj.get("relation")).toBeUndefined(); done(); }); }); - it('try to set a relation field as a required field', async done => { + it("try to set a relation field as a required field", async done => { try { await request({ - url: 'http://localhost:8378/1/schemas', - method: 'POST', + url: "http://localhost:8378/1/schemas", + method: "POST", headers: masterKeyHeaders, json: true, body: { - className: 'NewClassWithRelationRequired', + className: "NewClassWithRelationRequired", fields: { - foo: { type: 'String' }, + foo: { type: "String" }, relation: { - type: 'Relation', - targetClass: 'SomeClass', + type: "Relation", + targetClass: "SomeClass", required: true, }, }, }, }); - fail('should fail'); + fail("should fail"); } catch (e) { expect(e.data.code).toEqual(111); } done(); }); - it('try to set a relation field with a default value', async done => { + it("try to set a relation field with a default value", async done => { try { await request({ - url: 'http://localhost:8378/1/schemas', - method: 'POST', + url: "http://localhost:8378/1/schemas", + method: "POST", headers: masterKeyHeaders, json: true, body: { - className: 'NewClassRelationWithOptions', + className: "NewClassRelationWithOptions", fields: { - foo: { type: 'String' }, + foo: { type: "String" }, relation: { - type: 'Relation', - targetClass: 'SomeClass', - defaultValue: { __type: 'Relation', className: '_User' }, + type: "Relation", + targetClass: "SomeClass", + defaultValue: { __type: "Relation", className: "_User" }, }, }, }, }); - fail('should fail'); + fail("should fail"); } catch (e) { expect(e.data.code).toEqual(111); } done(); }); - it('try to update schemas with a relation field with options', async done => { + it("try to update schemas with a relation field with options", async done => { await request({ - url: 'http://localhost:8378/1/schemas', - method: 'POST', + url: "http://localhost:8378/1/schemas", + method: "POST", headers: masterKeyHeaders, json: true, body: { - className: 'NewClassRelationWithOptions', + className: "NewClassRelationWithOptions", fields: { - foo: { type: 'String' }, + foo: { type: "String" }, }, }, }); try { await request({ - url: 'http://localhost:8378/1/schemas/NewClassRelationWithOptions', - method: 'POST', + url: "http://localhost:8378/1/schemas/NewClassRelationWithOptions", + method: "POST", headers: masterKeyHeaders, json: true, body: { - className: 'NewClassRelationWithOptions', + className: "NewClassRelationWithOptions", fields: { relation: { - type: 'Relation', - targetClass: 'SomeClass', + type: "Relation", + targetClass: "SomeClass", required: true, }, }, - _method: 'PUT', + _method: "PUT", }, }); - fail('should fail'); + fail("should fail"); } catch (e) { expect(e.data.code).toEqual(111); } try { await request({ - url: 'http://localhost:8378/1/schemas/NewClassRelationWithOptions', - method: 'POST', + url: "http://localhost:8378/1/schemas/NewClassRelationWithOptions", + method: "POST", headers: masterKeyHeaders, json: true, body: { - className: 'NewClassRelationWithOptions', + className: "NewClassRelationWithOptions", fields: { relation: { - type: 'Relation', - targetClass: 'SomeClass', - defaultValue: { __type: 'Relation', className: '_User' }, + type: "Relation", + targetClass: "SomeClass", + defaultValue: { __type: "Relation", className: "_User" }, }, }, - _method: 'PUT', + _method: "PUT", }, }); - fail('should fail'); + fail("should fail"); } catch (e) { expect(e.data.code).toEqual(111); } done(); }); - it('validated the data type of default values when creating a new class', async () => { + it("validated the data type of default values when creating a new class", async () => { try { await request({ - url: 'http://localhost:8378/1/schemas', - method: 'POST', + url: "http://localhost:8378/1/schemas", + method: "POST", headers: masterKeyHeaders, json: true, body: { - className: 'NewClassWithValidation', + className: "NewClassWithValidation", fields: { - foo: { type: 'String', defaultValue: 10 }, + foo: { type: "String", defaultValue: 10 }, }, }, }); - fail('should fail'); + fail("should fail"); } catch (e) { expect(e.data.error).toEqual( - 'schema mismatch for NewClassWithValidation.foo default value; expected String but got Number' + "schema mismatch for NewClassWithValidation.foo default value; expected String but got Number" ); } }); - it('validated the data type of default values when adding new fields', async () => { + it("validated the data type of default values when adding new fields", async () => { try { await request({ - url: 'http://localhost:8378/1/schemas', - method: 'POST', + url: "http://localhost:8378/1/schemas", + method: "POST", headers: masterKeyHeaders, json: true, body: { - className: 'NewClassWithValidation', + className: "NewClassWithValidation", fields: { - foo: { type: 'String', defaultValue: 'some value' }, + foo: { type: "String", defaultValue: "some value" }, }, }, }); await request({ - url: 'http://localhost:8378/1/schemas/NewClassWithValidation', - method: 'PUT', + url: "http://localhost:8378/1/schemas/NewClassWithValidation", + method: "PUT", headers: masterKeyHeaders, json: true, body: { - className: 'NewClassWithValidation', + className: "NewClassWithValidation", fields: { - foo2: { type: 'String', defaultValue: 10 }, + foo2: { type: "String", defaultValue: 10 }, }, }, }); - fail('should fail'); + fail("should fail"); } catch (e) { expect(e.data.error).toEqual( - 'schema mismatch for NewClassWithValidation.foo2 default value; expected String but got Number' + "schema mismatch for NewClassWithValidation.foo2 default value; expected String but got Number" ); } }); - it('responds with all fields when getting incomplete schema', done => { + it("responds with all fields when getting incomplete schema", done => { config.database .loadSchema() .then(schemaController => - schemaController.addClassIfNotExists('_Installation', {}, defaultClassLevelPermissions) + schemaController.addClassIfNotExists( + "_Installation", + {}, + defaultClassLevelPermissions + ) ) .then(() => { request({ - url: 'http://localhost:8378/1/schemas/_Installation', + url: "http://localhost:8378/1/schemas/_Installation", headers: masterKeyHeaders, json: true, }).then(response => { expect( dd(response.data, { - className: '_Installation', + className: "_Installation", fields: { - objectId: { type: 'String' }, - updatedAt: { type: 'Date' }, - createdAt: { type: 'Date' }, - installationId: { type: 'String' }, - deviceToken: { type: 'String' }, - channels: { type: 'Array' }, - deviceType: { type: 'String' }, - pushType: { type: 'String' }, - GCMSenderId: { type: 'String' }, - timeZone: { type: 'String' }, - badge: { type: 'Number' }, - appIdentifier: { type: 'String' }, - localeIdentifier: { type: 'String' }, - appVersion: { type: 'String' }, - appName: { type: 'String' }, - parseVersion: { type: 'String' }, - ACL: { type: 'ACL' }, + objectId: { type: "String" }, + updatedAt: { type: "Date" }, + createdAt: { type: "Date" }, + installationId: { type: "String" }, + deviceToken: { type: "String" }, + channels: { type: "Array" }, + deviceType: { type: "String" }, + pushType: { type: "String" }, + GCMSenderId: { type: "String" }, + timeZone: { type: "String" }, + badge: { type: "Number" }, + appIdentifier: { type: "String" }, + localeIdentifier: { type: "String" }, + appVersion: { type: "String" }, + appName: { type: "String" }, + parseVersion: { type: "String" }, + ACL: { type: "ACL" }, }, classLevelPermissions: defaultClassLevelPermissions, }) @@ -730,23 +761,23 @@ describe('schemas', () => { }); }); - it('lets you specify class name in both places', done => { + it("lets you specify class name in both places", done => { request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'POST', + url: "http://localhost:8378/1/schemas/NewClass", + method: "POST", headers: masterKeyHeaders, json: true, body: { - className: 'NewClass', + className: "NewClass", }, }).then(response => { expect(response.data).toEqual({ - className: 'NewClass', + className: "NewClass", fields: { - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - objectId: { type: 'String' }, + ACL: { type: "ACL" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + objectId: { type: "String" }, }, classLevelPermissions: defaultClassLevelPermissions, }); @@ -754,183 +785,187 @@ describe('schemas', () => { }); }); - it('requires the master key to modify schemas', done => { + it("requires the master key to modify schemas", done => { request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'POST', + url: "http://localhost:8378/1/schemas/NewClass", + method: "POST", headers: masterKeyHeaders, json: true, body: {}, }).then(() => { request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'PUT', + url: "http://localhost:8378/1/schemas/NewClass", + method: "PUT", headers: noAuthHeaders, json: true, body: {}, }).then(fail, response => { expect(response.status).toEqual(403); - expect(response.data.error).toEqual('unauthorized'); + expect(response.data.error).toEqual("unauthorized"); done(); }); }); }); - it('rejects class name mis-matches in put', done => { + it("rejects class name mis-matches in put", done => { request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'PUT', + url: "http://localhost:8378/1/schemas/NewClass", + method: "PUT", headers: masterKeyHeaders, json: true, - body: { className: 'WrongClassName' }, + body: { className: "WrongClassName" }, }).then(fail, response => { expect(response.status).toEqual(400); expect(response.data.code).toEqual(Parse.Error.INVALID_CLASS_NAME); expect(response.data.error).toEqual( - 'Class name mismatch between WrongClassName and NewClass.' + "Class name mismatch between WrongClassName and NewClass." ); done(); }); }); - it('refuses to add fields to non-existent classes', done => { + it("refuses to add fields to non-existent classes", done => { request({ - url: 'http://localhost:8378/1/schemas/NoClass', - method: 'PUT', + url: "http://localhost:8378/1/schemas/NoClass", + method: "PUT", headers: masterKeyHeaders, json: true, body: { fields: { - newField: { type: 'String' }, + newField: { type: "String" }, }, }, }).then(fail, response => { expect(response.status).toEqual(400); expect(response.data.code).toEqual(Parse.Error.INVALID_CLASS_NAME); - expect(response.data.error).toEqual('Class NoClass does not exist.'); + expect(response.data.error).toEqual("Class NoClass does not exist."); done(); }); }); - it('refuses to put to existing fields with different type, even if it would not be a change', done => { + it("refuses to put to existing fields with different type, even if it would not be a change", done => { const obj = hasAllPODobject(); obj.save().then(() => { request({ - url: 'http://localhost:8378/1/schemas/HasAllPOD', - method: 'PUT', + url: "http://localhost:8378/1/schemas/HasAllPOD", + method: "PUT", headers: masterKeyHeaders, json: true, body: { fields: { - aString: { type: 'Number' }, + aString: { type: "Number" }, }, }, }).then(fail, response => { expect(response.status).toEqual(400); expect(response.data.code).toEqual(255); - expect(response.data.error).toEqual('Field aString exists, cannot update.'); + expect(response.data.error).toEqual( + "Field aString exists, cannot update." + ); done(); }); }); }); - it('refuses to delete non-existent fields', done => { + it("refuses to delete non-existent fields", done => { const obj = hasAllPODobject(); obj.save().then(() => { request({ - url: 'http://localhost:8378/1/schemas/HasAllPOD', - method: 'PUT', + url: "http://localhost:8378/1/schemas/HasAllPOD", + method: "PUT", headers: masterKeyHeaders, json: true, body: { fields: { - nonExistentKey: { __op: 'Delete' }, + nonExistentKey: { __op: "Delete" }, }, }, }).then(fail, response => { expect(response.status).toEqual(400); expect(response.data.code).toEqual(255); - expect(response.data.error).toEqual('Field nonExistentKey does not exist, cannot delete.'); + expect(response.data.error).toEqual( + "Field nonExistentKey does not exist, cannot delete." + ); done(); }); }); }); - it('refuses to add a geopoint to a class that already has one', done => { + it("refuses to add a geopoint to a class that already has one", done => { const obj = hasAllPODobject(); obj.save().then(() => { request({ - url: 'http://localhost:8378/1/schemas/HasAllPOD', - method: 'PUT', + url: "http://localhost:8378/1/schemas/HasAllPOD", + method: "PUT", headers: masterKeyHeaders, json: true, body: { fields: { - newGeo: { type: 'GeoPoint' }, + newGeo: { type: "GeoPoint" }, }, }, }).then(fail, response => { expect(response.status).toEqual(400); expect(response.data.code).toEqual(Parse.Error.INCORRECT_TYPE); expect(response.data.error).toEqual( - 'currently, only one GeoPoint field may exist in an object. Adding newGeo when aGeoPoint already exists.' + "currently, only one GeoPoint field may exist in an object. Adding newGeo when aGeoPoint already exists." ); done(); }); }); }); - it('refuses to add two geopoints', done => { - const obj = new Parse.Object('NewClass'); - obj.set('aString', 'aString'); + it("refuses to add two geopoints", done => { + const obj = new Parse.Object("NewClass"); + obj.set("aString", "aString"); obj.save().then(() => { request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'PUT', + url: "http://localhost:8378/1/schemas/NewClass", + method: "PUT", headers: masterKeyHeaders, json: true, body: { fields: { - newGeo1: { type: 'GeoPoint' }, - newGeo2: { type: 'GeoPoint' }, + newGeo1: { type: "GeoPoint" }, + newGeo2: { type: "GeoPoint" }, }, }, }).then(fail, response => { expect(response.status).toEqual(400); expect(response.data.code).toEqual(Parse.Error.INCORRECT_TYPE); expect(response.data.error).toEqual( - 'currently, only one GeoPoint field may exist in an object. Adding newGeo2 when newGeo1 already exists.' + "currently, only one GeoPoint field may exist in an object. Adding newGeo2 when newGeo1 already exists." ); done(); }); }); }); - it('allows you to delete and add a geopoint in the same request', done => { - const obj = new Parse.Object('NewClass'); - obj.set('geo1', new Parse.GeoPoint({ latitude: 0, longitude: 0 })); + it("allows you to delete and add a geopoint in the same request", done => { + const obj = new Parse.Object("NewClass"); + obj.set("geo1", new Parse.GeoPoint({ latitude: 0, longitude: 0 })); obj.save().then(() => { request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'PUT', + url: "http://localhost:8378/1/schemas/NewClass", + method: "PUT", headers: masterKeyHeaders, json: true, body: { fields: { - geo2: { type: 'GeoPoint' }, - geo1: { __op: 'Delete' }, + geo2: { type: "GeoPoint" }, + geo1: { __op: "Delete" }, }, }, }).then(response => { expect( dd(response.data, { - className: 'NewClass', + className: "NewClass", fields: { - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - objectId: { type: 'String' }, - updatedAt: { type: 'Date' }, - geo2: { type: 'GeoPoint' }, + ACL: { type: "ACL" }, + createdAt: { type: "Date" }, + objectId: { type: "String" }, + updatedAt: { type: "Date" }, + geo2: { type: "GeoPoint" }, }, classLevelPermissions: defaultClassLevelPermissions, }) @@ -940,12 +975,12 @@ describe('schemas', () => { }); }); - it('put with no modifications returns all fields', done => { + it("put with no modifications returns all fields", done => { const obj = hasAllPODobject(); obj.save().then(() => { request({ - url: 'http://localhost:8378/1/schemas/HasAllPOD', - method: 'PUT', + url: "http://localhost:8378/1/schemas/HasAllPOD", + method: "PUT", headers: masterKeyHeaders, json: true, body: {}, @@ -956,51 +991,51 @@ describe('schemas', () => { }); }); - it('lets you add fields', done => { + it("lets you add fields", done => { request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'POST', + url: "http://localhost:8378/1/schemas/NewClass", + method: "POST", headers: masterKeyHeaders, json: true, body: {}, }).then(() => { request({ - method: 'PUT', - url: 'http://localhost:8378/1/schemas/NewClass', + method: "PUT", + url: "http://localhost:8378/1/schemas/NewClass", headers: masterKeyHeaders, json: true, body: { fields: { - newField: { type: 'String' }, + newField: { type: "String" }, }, }, }).then(response => { expect( dd(response.data, { - className: 'NewClass', + className: "NewClass", fields: { - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - objectId: { type: 'String' }, - updatedAt: { type: 'Date' }, - newField: { type: 'String' }, + ACL: { type: "ACL" }, + createdAt: { type: "Date" }, + objectId: { type: "String" }, + updatedAt: { type: "Date" }, + newField: { type: "String" }, }, classLevelPermissions: defaultClassLevelPermissions, }) ).toEqual(undefined); request({ - url: 'http://localhost:8378/1/schemas/NewClass', + url: "http://localhost:8378/1/schemas/NewClass", headers: masterKeyHeaders, json: true, }).then(response => { expect(response.data).toEqual({ - className: 'NewClass', + className: "NewClass", fields: { - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - objectId: { type: 'String' }, - newField: { type: 'String' }, + ACL: { type: "ACL" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + objectId: { type: "String" }, + newField: { type: "String" }, }, classLevelPermissions: defaultClassLevelPermissions, }); @@ -1010,62 +1045,62 @@ describe('schemas', () => { }); }); - it('lets you add fields with options', done => { + it("lets you add fields with options", done => { request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'POST', + url: "http://localhost:8378/1/schemas/NewClass", + method: "POST", headers: masterKeyHeaders, json: true, body: {}, }).then(() => { request({ - method: 'PUT', - url: 'http://localhost:8378/1/schemas/NewClass', + method: "PUT", + url: "http://localhost:8378/1/schemas/NewClass", headers: masterKeyHeaders, json: true, body: { fields: { newField: { - type: 'String', + type: "String", required: true, - defaultValue: 'some value', + defaultValue: "some value", }, }, }, }).then(response => { expect( dd(response.data, { - className: 'NewClass', + className: "NewClass", fields: { - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - objectId: { type: 'String' }, - updatedAt: { type: 'Date' }, + ACL: { type: "ACL" }, + createdAt: { type: "Date" }, + objectId: { type: "String" }, + updatedAt: { type: "Date" }, newField: { - type: 'String', + type: "String", required: true, - defaultValue: 'some value', + defaultValue: "some value", }, }, classLevelPermissions: defaultClassLevelPermissions, }) ).toEqual(undefined); request({ - url: 'http://localhost:8378/1/schemas/NewClass', + url: "http://localhost:8378/1/schemas/NewClass", headers: masterKeyHeaders, json: true, }).then(response => { expect(response.data).toEqual({ - className: 'NewClass', + className: "NewClass", fields: { - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - objectId: { type: 'String' }, + ACL: { type: "ACL" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + objectId: { type: "String" }, newField: { - type: 'String', + type: "String", required: true, - defaultValue: 'some value', + defaultValue: "some value", }, }, classLevelPermissions: defaultClassLevelPermissions, @@ -1076,297 +1111,315 @@ describe('schemas', () => { }); }); - it('should validate required fields', done => { + it("should validate required fields", done => { request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'POST', + url: "http://localhost:8378/1/schemas/NewClass", + method: "POST", headers: masterKeyHeaders, json: true, body: {}, }).then(() => { request({ - method: 'PUT', - url: 'http://localhost:8378/1/schemas/NewClass', + method: "PUT", + url: "http://localhost:8378/1/schemas/NewClass", headers: masterKeyHeaders, json: true, body: { fields: { newRequiredField: { - type: 'String', + type: "String", required: true, }, newRequiredFieldWithDefaultValue: { - type: 'String', + type: "String", required: true, - defaultValue: 'some value', + defaultValue: "some value", }, newNotRequiredField: { - type: 'String', + type: "String", required: false, }, newNotRequiredFieldWithDefaultValue: { - type: 'String', + type: "String", required: false, - defaultValue: 'some value', + defaultValue: "some value", }, newRegularFieldWithDefaultValue: { - type: 'String', - defaultValue: 'some value', + type: "String", + defaultValue: "some value", }, newRegularField: { - type: 'String', + type: "String", }, }, }, }).then(async () => { - let obj = new Parse.Object('NewClass'); + let obj = new Parse.Object("NewClass"); try { await obj.save(); - fail('Should fail'); + fail("Should fail"); } catch (e) { expect(e.code).toEqual(142); - expect(e.message).toEqual('newRequiredField is required'); + expect(e.message).toEqual("newRequiredField is required"); } - obj.set('newRequiredField', 'some value'); + obj.set("newRequiredField", "some value"); await obj.save(); - expect(obj.get('newRequiredField')).toEqual('some value'); - expect(obj.get('newRequiredFieldWithDefaultValue')).toEqual('some value'); - expect(obj.get('newNotRequiredField')).toEqual(undefined); - expect(obj.get('newNotRequiredFieldWithDefaultValue')).toEqual('some value'); - expect(obj.get('newRegularField')).toEqual(undefined); - obj.set('newRequiredField', null); + expect(obj.get("newRequiredField")).toEqual("some value"); + expect(obj.get("newRequiredFieldWithDefaultValue")).toEqual( + "some value" + ); + expect(obj.get("newNotRequiredField")).toEqual(undefined); + expect(obj.get("newNotRequiredFieldWithDefaultValue")).toEqual( + "some value" + ); + expect(obj.get("newRegularField")).toEqual(undefined); + obj.set("newRequiredField", null); try { await obj.save(); - fail('Should fail'); + fail("Should fail"); } catch (e) { expect(e.code).toEqual(142); - expect(e.message).toEqual('newRequiredField is required'); + expect(e.message).toEqual("newRequiredField is required"); } - obj.unset('newRequiredField'); + obj.unset("newRequiredField"); try { await obj.save(); - fail('Should fail'); + fail("Should fail"); } catch (e) { expect(e.code).toEqual(142); - expect(e.message).toEqual('newRequiredField is required'); + expect(e.message).toEqual("newRequiredField is required"); } - obj.set('newRequiredField', 'some value2'); + obj.set("newRequiredField", "some value2"); await obj.save(); - expect(obj.get('newRequiredField')).toEqual('some value2'); - expect(obj.get('newRequiredFieldWithDefaultValue')).toEqual('some value'); - expect(obj.get('newNotRequiredField')).toEqual(undefined); - expect(obj.get('newNotRequiredFieldWithDefaultValue')).toEqual('some value'); - expect(obj.get('newRegularField')).toEqual(undefined); - obj.unset('newRequiredFieldWithDefaultValue'); + expect(obj.get("newRequiredField")).toEqual("some value2"); + expect(obj.get("newRequiredFieldWithDefaultValue")).toEqual( + "some value" + ); + expect(obj.get("newNotRequiredField")).toEqual(undefined); + expect(obj.get("newNotRequiredFieldWithDefaultValue")).toEqual( + "some value" + ); + expect(obj.get("newRegularField")).toEqual(undefined); + obj.unset("newRequiredFieldWithDefaultValue"); try { await obj.save(); - fail('Should fail'); + fail("Should fail"); } catch (e) { expect(e.code).toEqual(142); - expect(e.message).toEqual('newRequiredFieldWithDefaultValue is required'); + expect(e.message).toEqual( + "newRequiredFieldWithDefaultValue is required" + ); } - obj.set('newRequiredFieldWithDefaultValue', ''); + obj.set("newRequiredFieldWithDefaultValue", ""); try { await obj.save(); - fail('Should fail'); + fail("Should fail"); } catch (e) { expect(e.code).toEqual(142); - expect(e.message).toEqual('newRequiredFieldWithDefaultValue is required'); + expect(e.message).toEqual( + "newRequiredFieldWithDefaultValue is required" + ); } - obj.set('newRequiredFieldWithDefaultValue', 'some value2'); - obj.set('newNotRequiredField', ''); - obj.set('newNotRequiredFieldWithDefaultValue', null); - obj.unset('newRegularField'); + obj.set("newRequiredFieldWithDefaultValue", "some value2"); + obj.set("newNotRequiredField", ""); + obj.set("newNotRequiredFieldWithDefaultValue", null); + obj.unset("newRegularField"); await obj.save(); - expect(obj.get('newRequiredField')).toEqual('some value2'); - expect(obj.get('newRequiredFieldWithDefaultValue')).toEqual('some value2'); - expect(obj.get('newNotRequiredField')).toEqual(''); - expect(obj.get('newNotRequiredFieldWithDefaultValue')).toEqual(null); - expect(obj.get('newRegularField')).toEqual(undefined); - obj = new Parse.Object('NewClass'); - obj.set('newRequiredField', 'some value3'); - obj.set('newRequiredFieldWithDefaultValue', 'some value3'); - obj.set('newNotRequiredField', 'some value3'); - obj.set('newNotRequiredFieldWithDefaultValue', 'some value3'); - obj.set('newRegularField', 'some value3'); + expect(obj.get("newRequiredField")).toEqual("some value2"); + expect(obj.get("newRequiredFieldWithDefaultValue")).toEqual( + "some value2" + ); + expect(obj.get("newNotRequiredField")).toEqual(""); + expect(obj.get("newNotRequiredFieldWithDefaultValue")).toEqual(null); + expect(obj.get("newRegularField")).toEqual(undefined); + obj = new Parse.Object("NewClass"); + obj.set("newRequiredField", "some value3"); + obj.set("newRequiredFieldWithDefaultValue", "some value3"); + obj.set("newNotRequiredField", "some value3"); + obj.set("newNotRequiredFieldWithDefaultValue", "some value3"); + obj.set("newRegularField", "some value3"); await obj.save(); - expect(obj.get('newRequiredField')).toEqual('some value3'); - expect(obj.get('newRequiredFieldWithDefaultValue')).toEqual('some value3'); - expect(obj.get('newNotRequiredField')).toEqual('some value3'); - expect(obj.get('newNotRequiredFieldWithDefaultValue')).toEqual('some value3'); - expect(obj.get('newRegularField')).toEqual('some value3'); + expect(obj.get("newRequiredField")).toEqual("some value3"); + expect(obj.get("newRequiredFieldWithDefaultValue")).toEqual( + "some value3" + ); + expect(obj.get("newNotRequiredField")).toEqual("some value3"); + expect(obj.get("newNotRequiredFieldWithDefaultValue")).toEqual( + "some value3" + ); + expect(obj.get("newRegularField")).toEqual("some value3"); done(); }); }); }); - it('should validate required fields and set default values after before save trigger', async () => { + it("should validate required fields and set default values after before save trigger", async () => { await request({ - url: 'http://localhost:8378/1/schemas', - method: 'POST', + url: "http://localhost:8378/1/schemas", + method: "POST", headers: masterKeyHeaders, json: true, body: { - className: 'NewClassForBeforeSaveTest', + className: "NewClassForBeforeSaveTest", fields: { - foo1: { type: 'String' }, - foo2: { type: 'String', required: true }, + foo1: { type: "String" }, + foo2: { type: "String", required: true }, foo3: { - type: 'String', + type: "String", required: true, - defaultValue: 'some default value 3', + defaultValue: "some default value 3", }, - foo4: { type: 'String', defaultValue: 'some default value 4' }, + foo4: { type: "String", defaultValue: "some default value 4" }, }, }, }); - Parse.Cloud.beforeSave('NewClassForBeforeSaveTest', req => { - req.object.set('foo1', 'some value 1'); - req.object.set('foo2', 'some value 2'); - req.object.set('foo3', 'some value 3'); - req.object.set('foo4', 'some value 4'); + Parse.Cloud.beforeSave("NewClassForBeforeSaveTest", req => { + req.object.set("foo1", "some value 1"); + req.object.set("foo2", "some value 2"); + req.object.set("foo3", "some value 3"); + req.object.set("foo4", "some value 4"); }); - let obj = new Parse.Object('NewClassForBeforeSaveTest'); + let obj = new Parse.Object("NewClassForBeforeSaveTest"); await obj.save(); - expect(obj.get('foo1')).toEqual('some value 1'); - expect(obj.get('foo2')).toEqual('some value 2'); - expect(obj.get('foo3')).toEqual('some value 3'); - expect(obj.get('foo4')).toEqual('some value 4'); + expect(obj.get("foo1")).toEqual("some value 1"); + expect(obj.get("foo2")).toEqual("some value 2"); + expect(obj.get("foo3")).toEqual("some value 3"); + expect(obj.get("foo4")).toEqual("some value 4"); - Parse.Cloud.beforeSave('NewClassForBeforeSaveTest', req => { - req.object.set('foo1', 'some value 1'); - req.object.set('foo2', 'some value 2'); + Parse.Cloud.beforeSave("NewClassForBeforeSaveTest", req => { + req.object.set("foo1", "some value 1"); + req.object.set("foo2", "some value 2"); }); - obj = new Parse.Object('NewClassForBeforeSaveTest'); + obj = new Parse.Object("NewClassForBeforeSaveTest"); await obj.save(); - expect(obj.get('foo1')).toEqual('some value 1'); - expect(obj.get('foo2')).toEqual('some value 2'); - expect(obj.get('foo3')).toEqual('some default value 3'); - expect(obj.get('foo4')).toEqual('some default value 4'); + expect(obj.get("foo1")).toEqual("some value 1"); + expect(obj.get("foo2")).toEqual("some value 2"); + expect(obj.get("foo3")).toEqual("some default value 3"); + expect(obj.get("foo4")).toEqual("some default value 4"); - Parse.Cloud.beforeSave('NewClassForBeforeSaveTest', req => { - req.object.set('foo1', 'some value 1'); - req.object.set('foo2', 'some value 2'); - req.object.set('foo3', undefined); - req.object.unset('foo4'); + Parse.Cloud.beforeSave("NewClassForBeforeSaveTest", req => { + req.object.set("foo1", "some value 1"); + req.object.set("foo2", "some value 2"); + req.object.set("foo3", undefined); + req.object.unset("foo4"); }); - obj = new Parse.Object('NewClassForBeforeSaveTest'); - obj.set('foo3', 'some value 3'); - obj.set('foo4', 'some value 4'); + obj = new Parse.Object("NewClassForBeforeSaveTest"); + obj.set("foo3", "some value 3"); + obj.set("foo4", "some value 4"); await obj.save(); - expect(obj.get('foo1')).toEqual('some value 1'); - expect(obj.get('foo2')).toEqual('some value 2'); - expect(obj.get('foo3')).toEqual('some default value 3'); - expect(obj.get('foo4')).toEqual('some default value 4'); + expect(obj.get("foo1")).toEqual("some value 1"); + expect(obj.get("foo2")).toEqual("some value 2"); + expect(obj.get("foo3")).toEqual("some default value 3"); + expect(obj.get("foo4")).toEqual("some default value 4"); - Parse.Cloud.beforeSave('NewClassForBeforeSaveTest', req => { - req.object.set('foo1', 'some value 1'); - req.object.set('foo2', undefined); - req.object.set('foo3', undefined); - req.object.unset('foo4'); + Parse.Cloud.beforeSave("NewClassForBeforeSaveTest", req => { + req.object.set("foo1", "some value 1"); + req.object.set("foo2", undefined); + req.object.set("foo3", undefined); + req.object.unset("foo4"); }); - obj = new Parse.Object('NewClassForBeforeSaveTest'); - obj.set('foo2', 'some value 2'); - obj.set('foo3', 'some value 3'); - obj.set('foo4', 'some value 4'); + obj = new Parse.Object("NewClassForBeforeSaveTest"); + obj.set("foo2", "some value 2"); + obj.set("foo3", "some value 3"); + obj.set("foo4", "some value 4"); try { await obj.save(); - fail('should fail'); + fail("should fail"); } catch (e) { - expect(e.message).toEqual('foo2 is required'); + expect(e.message).toEqual("foo2 is required"); } - Parse.Cloud.beforeSave('NewClassForBeforeSaveTest', req => { - req.object.set('foo1', 'some value 1'); - req.object.unset('foo2'); - req.object.set('foo3', undefined); - req.object.unset('foo4'); + Parse.Cloud.beforeSave("NewClassForBeforeSaveTest", req => { + req.object.set("foo1", "some value 1"); + req.object.unset("foo2"); + req.object.set("foo3", undefined); + req.object.unset("foo4"); }); - obj = new Parse.Object('NewClassForBeforeSaveTest'); - obj.set('foo2', 'some value 2'); - obj.set('foo3', 'some value 3'); - obj.set('foo4', 'some value 4'); + obj = new Parse.Object("NewClassForBeforeSaveTest"); + obj.set("foo2", "some value 2"); + obj.set("foo3", "some value 3"); + obj.set("foo4", "some value 4"); try { await obj.save(); - fail('should fail'); + fail("should fail"); } catch (e) { - expect(e.message).toEqual('foo2 is required'); + expect(e.message).toEqual("foo2 is required"); } }); - it('lets you add fields to system schema', done => { + it("lets you add fields to system schema", done => { request({ - method: 'POST', - url: 'http://localhost:8378/1/schemas/_User', + method: "POST", + url: "http://localhost:8378/1/schemas/_User", headers: masterKeyHeaders, json: true, }).then(fail, () => { request({ - url: 'http://localhost:8378/1/schemas/_User', - method: 'PUT', + url: "http://localhost:8378/1/schemas/_User", + method: "PUT", headers: masterKeyHeaders, json: true, body: { fields: { - newField: { type: 'String' }, + newField: { type: "String" }, }, }, }).then(response => { delete response.data.indexes; expect( dd(response.data, { - className: '_User', + className: "_User", fields: { - objectId: { type: 'String' }, - updatedAt: { type: 'Date' }, - createdAt: { type: 'Date' }, - username: { type: 'String' }, - password: { type: 'String' }, - email: { type: 'String' }, - emailVerified: { type: 'Boolean' }, - authData: { type: 'Object' }, - newField: { type: 'String' }, - ACL: { type: 'ACL' }, + objectId: { type: "String" }, + updatedAt: { type: "Date" }, + createdAt: { type: "Date" }, + username: { type: "String" }, + password: { type: "String" }, + email: { type: "String" }, + emailVerified: { type: "Boolean" }, + authData: { type: "Object" }, + newField: { type: "String" }, + ACL: { type: "ACL" }, }, classLevelPermissions: { ...defaultClassLevelPermissions, protectedFields: { - '*': ['email'], + "*": ["email"], }, }, }) ).toBeUndefined(); request({ - url: 'http://localhost:8378/1/schemas/_User', + url: "http://localhost:8378/1/schemas/_User", headers: masterKeyHeaders, json: true, }).then(response => { delete response.data.indexes; expect( dd(response.data, { - className: '_User', + className: "_User", fields: { - objectId: { type: 'String' }, - updatedAt: { type: 'Date' }, - createdAt: { type: 'Date' }, - username: { type: 'String' }, - password: { type: 'String' }, - email: { type: 'String' }, - emailVerified: { type: 'Boolean' }, - authData: { type: 'Object' }, - newField: { type: 'String' }, - ACL: { type: 'ACL' }, + objectId: { type: "String" }, + updatedAt: { type: "Date" }, + createdAt: { type: "Date" }, + username: { type: "String" }, + password: { type: "String" }, + email: { type: "String" }, + emailVerified: { type: "Boolean" }, + authData: { type: "Object" }, + newField: { type: "String" }, + ACL: { type: "ACL" }, }, classLevelPermissions: defaultClassLevelPermissions, }) @@ -1377,12 +1430,12 @@ describe('schemas', () => { }); }); - it('lets you delete multiple fields and check schema', done => { + it("lets you delete multiple fields and check schema", done => { const simpleOneObject = () => { - const obj = new Parse.Object('SimpleOne'); - obj.set('aNumber', 5); - obj.set('aString', 'string'); - obj.set('aBool', true); + const obj = new Parse.Object("SimpleOne"); + obj.set("aNumber", 5); + obj.set("aString", "string"); + obj.set("aBool", true); return obj; }; @@ -1390,27 +1443,27 @@ describe('schemas', () => { .save() .then(() => { request({ - url: 'http://localhost:8378/1/schemas/SimpleOne', - method: 'PUT', + url: "http://localhost:8378/1/schemas/SimpleOne", + method: "PUT", headers: masterKeyHeaders, json: true, body: { fields: { - aString: { __op: 'Delete' }, - aNumber: { __op: 'Delete' }, + aString: { __op: "Delete" }, + aNumber: { __op: "Delete" }, }, }, }).then(response => { expect(response.data).toEqual({ - className: 'SimpleOne', + className: "SimpleOne", fields: { //Default fields - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - objectId: { type: 'String' }, + ACL: { type: "ACL" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + objectId: { type: "String" }, //Custom fields - aBool: { type: 'Boolean' }, + aBool: { type: "Boolean" }, }, classLevelPermissions: defaultClassLevelPermissions, }); @@ -1420,76 +1473,76 @@ describe('schemas', () => { }); }); - it('lets you delete multiple fields and add fields', done => { + it("lets you delete multiple fields and add fields", done => { const obj1 = hasAllPODobject(); obj1.save().then(() => { request({ - url: 'http://localhost:8378/1/schemas/HasAllPOD', - method: 'PUT', + url: "http://localhost:8378/1/schemas/HasAllPOD", + method: "PUT", headers: masterKeyHeaders, json: true, body: { fields: { - aString: { __op: 'Delete' }, - aNumber: { __op: 'Delete' }, - aNewString: { type: 'String' }, - aNewNumber: { type: 'Number' }, - aNewRelation: { type: 'Relation', targetClass: 'HasAllPOD' }, - aNewPointer: { type: 'Pointer', targetClass: 'HasAllPOD' }, + aString: { __op: "Delete" }, + aNumber: { __op: "Delete" }, + aNewString: { type: "String" }, + aNewNumber: { type: "Number" }, + aNewRelation: { type: "Relation", targetClass: "HasAllPOD" }, + aNewPointer: { type: "Pointer", targetClass: "HasAllPOD" }, }, }, }).then(response => { expect(response.data).toEqual({ - className: 'HasAllPOD', + className: "HasAllPOD", fields: { //Default fields - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - objectId: { type: 'String' }, + ACL: { type: "ACL" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + objectId: { type: "String" }, //Custom fields - aBool: { type: 'Boolean' }, - aDate: { type: 'Date' }, - aObject: { type: 'Object' }, - aArray: { type: 'Array' }, - aGeoPoint: { type: 'GeoPoint' }, - aFile: { type: 'File' }, - aNewNumber: { type: 'Number' }, - aNewString: { type: 'String' }, - aNewPointer: { type: 'Pointer', targetClass: 'HasAllPOD' }, - aNewRelation: { type: 'Relation', targetClass: 'HasAllPOD' }, + aBool: { type: "Boolean" }, + aDate: { type: "Date" }, + aObject: { type: "Object" }, + aArray: { type: "Array" }, + aGeoPoint: { type: "GeoPoint" }, + aFile: { type: "File" }, + aNewNumber: { type: "Number" }, + aNewString: { type: "String" }, + aNewPointer: { type: "Pointer", targetClass: "HasAllPOD" }, + aNewRelation: { type: "Relation", targetClass: "HasAllPOD" }, }, classLevelPermissions: defaultClassLevelPermissions, }); - const obj2 = new Parse.Object('HasAllPOD'); - obj2.set('aNewPointer', obj1); - const relation = obj2.relation('aNewRelation'); + const obj2 = new Parse.Object("HasAllPOD"); + obj2.set("aNewPointer", obj1); + const relation = obj2.relation("aNewRelation"); relation.add(obj1); obj2.save().then(done); //Just need to make sure saving works on the new object. }); }); }); - it('will not delete any fields if the additions are invalid', done => { + it("will not delete any fields if the additions are invalid", done => { const obj = hasAllPODobject(); obj.save().then(() => { request({ - url: 'http://localhost:8378/1/schemas/HasAllPOD', - method: 'PUT', + url: "http://localhost:8378/1/schemas/HasAllPOD", + method: "PUT", headers: masterKeyHeaders, json: true, body: { fields: { - fakeNewField: { type: 'fake type' }, - aString: { __op: 'Delete' }, + fakeNewField: { type: "fake type" }, + aString: { __op: "Delete" }, }, }, }).then(fail, response => { expect(response.data.code).toEqual(Parse.Error.INCORRECT_TYPE); - expect(response.data.error).toEqual('invalid field type: fake type'); + expect(response.data.error).toEqual("invalid field type: fake type"); request({ - method: 'PUT', - url: 'http://localhost:8378/1/schemas/HasAllPOD', + method: "PUT", + url: "http://localhost:8378/1/schemas/HasAllPOD", headers: masterKeyHeaders, json: true, }).then(response => { @@ -1500,25 +1553,25 @@ describe('schemas', () => { }); }); - it('requires the master key to delete schemas', done => { + it("requires the master key to delete schemas", done => { request({ - url: 'http://localhost:8378/1/schemas/DoesntMatter', - method: 'DELETE', + url: "http://localhost:8378/1/schemas/DoesntMatter", + method: "DELETE", headers: noAuthHeaders, json: true, }).then(fail, response => { expect(response.status).toEqual(403); - expect(response.data.error).toEqual('unauthorized'); + expect(response.data.error).toEqual("unauthorized"); done(); }); }); - it('refuses to delete non-empty collection', done => { + it("refuses to delete non-empty collection", done => { const obj = hasAllPODobject(); obj.save().then(() => { request({ - url: 'http://localhost:8378/1/schemas/HasAllPOD', - method: 'DELETE', + url: "http://localhost:8378/1/schemas/HasAllPOD", + method: "DELETE", headers: masterKeyHeaders, json: true, }).then(fail, response => { @@ -1531,26 +1584,26 @@ describe('schemas', () => { }); }); - it('fails when deleting collections with invalid class names', done => { + it("fails when deleting collections with invalid class names", done => { request({ - url: 'http://localhost:8378/1/schemas/_GlobalConfig', - method: 'DELETE', + url: "http://localhost:8378/1/schemas/_GlobalConfig", + method: "DELETE", headers: masterKeyHeaders, json: true, }).then(fail, response => { expect(response.status).toEqual(400); expect(response.data.code).toEqual(Parse.Error.INVALID_CLASS_NAME); expect(response.data.error).toEqual( - 'Invalid classname: _GlobalConfig, classnames can only have alphanumeric characters and _, and must start with an alpha character ' + "Invalid classname: _GlobalConfig, classnames can only have alphanumeric characters and _, and must start with an alpha character " ); done(); }); }); - it('does not fail when deleting nonexistant collections', done => { + it("does not fail when deleting nonexistant collections", done => { request({ - url: 'http://localhost:8378/1/schemas/Missing', - method: 'DELETE', + url: "http://localhost:8378/1/schemas/Missing", + method: "DELETE", headers: masterKeyHeaders, json: true, }).then(response => { @@ -1560,28 +1613,30 @@ describe('schemas', () => { }); }); - it('ensure refresh cache after deleting a class', async done => { - config = Config.get('test'); - spyOn(config.schemaCache, 'del').and.callFake(() => {}); - spyOn(SchemaController.prototype, 'reloadData').and.callFake(() => Promise.resolve()); + it("ensure refresh cache after deleting a class", async done => { + config = Config.get("test"); + spyOn(config.schemaCache, "del").and.callFake(() => {}); + spyOn(SchemaController.prototype, "reloadData").and.callFake(() => + Promise.resolve() + ); await request({ - url: 'http://localhost:8378/1/schemas', - method: 'POST', + url: "http://localhost:8378/1/schemas", + method: "POST", headers: masterKeyHeaders, json: true, body: { - className: 'A', + className: "A", }, }); await request({ - method: 'DELETE', - url: 'http://localhost:8378/1/schemas/A', + method: "DELETE", + url: "http://localhost:8378/1/schemas/A", headers: masterKeyHeaders, json: true, }); const response = await request({ - url: 'http://localhost:8378/1/schemas', - method: 'GET', + url: "http://localhost:8378/1/schemas", + method: "GET", headers: masterKeyHeaders, json: true, }); @@ -1596,56 +1651,64 @@ describe('schemas', () => { delete withoutIndexes.indexes; return withoutIndexes; }) - ).toEqual(expected.results.sort((s1, s2) => s1.className.localeCompare(s2.className))); + ).toEqual( + expected.results.sort((s1, s2) => + s1.className.localeCompare(s2.className) + ) + ); done(); }); - it('deletes collections including join tables', done => { - const obj = new Parse.Object('MyClass'); - obj.set('data', 'data'); + it("deletes collections including join tables", done => { + const obj = new Parse.Object("MyClass"); + obj.set("data", "data"); obj .save() .then(() => { - const obj2 = new Parse.Object('MyOtherClass'); - const relation = obj2.relation('aRelation'); + const obj2 = new Parse.Object("MyOtherClass"); + const relation = obj2.relation("aRelation"); relation.add(obj); return obj2.save(); }) .then(obj2 => obj2.destroy()) .then(() => { request({ - url: 'http://localhost:8378/1/schemas/MyOtherClass', - method: 'DELETE', + url: "http://localhost:8378/1/schemas/MyOtherClass", + method: "DELETE", headers: masterKeyHeaders, json: true, }).then(response => { expect(response.status).toEqual(200); expect(response.data).toEqual({}); config.database - .collectionExists('_Join:aRelation:MyOtherClass') + .collectionExists("_Join:aRelation:MyOtherClass") .then(exists => { if (exists) { - fail('Relation collection should be deleted.'); + fail("Relation collection should be deleted."); done(); } - return config.database.collectionExists('MyOtherClass'); + return config.database.collectionExists("MyOtherClass"); }) .then(exists => { if (exists) { - fail('Class collection should be deleted.'); + fail("Class collection should be deleted."); done(); } }) .then(() => { request({ - url: 'http://localhost:8378/1/schemas/MyOtherClass', + url: "http://localhost:8378/1/schemas/MyOtherClass", headers: masterKeyHeaders, json: true, }).then(fail, response => { //Expect _SCHEMA entry to be gone. expect(response.status).toEqual(400); - expect(response.data.code).toEqual(Parse.Error.INVALID_CLASS_NAME); - expect(response.data.error).toEqual('Class MyOtherClass does not exist.'); + expect(response.data.code).toEqual( + Parse.Error.INVALID_CLASS_NAME + ); + expect(response.data.error).toEqual( + "Class MyOtherClass does not exist." + ); done(); }); }); @@ -1660,27 +1723,27 @@ describe('schemas', () => { ); }); - it('deletes schema when actual collection does not exist', done => { + it("deletes schema when actual collection does not exist", done => { request({ - method: 'POST', - url: 'http://localhost:8378/1/schemas/NewClassForDelete', + method: "POST", + url: "http://localhost:8378/1/schemas/NewClassForDelete", headers: masterKeyHeaders, json: true, body: { - className: 'NewClassForDelete', + className: "NewClassForDelete", }, }).then(response => { - expect(response.data.className).toEqual('NewClassForDelete'); + expect(response.data.className).toEqual("NewClassForDelete"); request({ - url: 'http://localhost:8378/1/schemas/NewClassForDelete', - method: 'DELETE', + url: "http://localhost:8378/1/schemas/NewClassForDelete", + method: "DELETE", headers: masterKeyHeaders, json: true, }).then(response => { expect(response.status).toEqual(200); expect(response.data).toEqual({}); config.database.loadSchema().then(schema => { - schema.hasClass('NewClassForDelete').then(exist => { + schema.hasClass("NewClassForDelete").then(exist => { expect(exist).toEqual(false); done(); }); @@ -1689,40 +1752,42 @@ describe('schemas', () => { }); }); - it('deletes schema when actual collection exists', done => { + it("deletes schema when actual collection exists", done => { request({ - method: 'POST', - url: 'http://localhost:8378/1/schemas/NewClassForDelete', + method: "POST", + url: "http://localhost:8378/1/schemas/NewClassForDelete", headers: masterKeyHeaders, json: true, body: { - className: 'NewClassForDelete', + className: "NewClassForDelete", }, }).then(response => { - expect(response.data.className).toEqual('NewClassForDelete'); + expect(response.data.className).toEqual("NewClassForDelete"); request({ - url: 'http://localhost:8378/1/classes/NewClassForDelete', - method: 'POST', + url: "http://localhost:8378/1/classes/NewClassForDelete", + method: "POST", headers: restKeyHeaders, json: true, }).then(response => { - expect(typeof response.data.objectId).toEqual('string'); + expect(typeof response.data.objectId).toEqual("string"); request({ - method: 'DELETE', - url: 'http://localhost:8378/1/classes/NewClassForDelete/' + response.data.objectId, + method: "DELETE", + url: + "http://localhost:8378/1/classes/NewClassForDelete/" + + response.data.objectId, headers: restKeyHeaders, json: true, }).then(() => { request({ - method: 'DELETE', - url: 'http://localhost:8378/1/schemas/NewClassForDelete', + method: "DELETE", + url: "http://localhost:8378/1/schemas/NewClassForDelete", headers: masterKeyHeaders, json: true, }).then(response => { expect(response.status).toEqual(200); expect(response.data).toEqual({}); config.database.loadSchema().then(schema => { - schema.hasClass('NewClassForDelete').then(exist => { + schema.hasClass("NewClassForDelete").then(exist => { expect(exist).toEqual(false); done(); }); @@ -1733,35 +1798,35 @@ describe('schemas', () => { }); }); - it('should set/get schema permissions', done => { + it("should set/get schema permissions", done => { request({ - method: 'POST', - url: 'http://localhost:8378/1/schemas/AClass', + method: "POST", + url: "http://localhost:8378/1/schemas/AClass", headers: masterKeyHeaders, json: true, body: { classLevelPermissions: { find: { - '*': true, + "*": true, }, create: { - 'role:admin': true, + "role:admin": true, }, }, }, }).then(() => { request({ - url: 'http://localhost:8378/1/schemas/AClass', + url: "http://localhost:8378/1/schemas/AClass", headers: masterKeyHeaders, json: true, }).then(response => { expect(response.status).toEqual(200); expect(response.data.classLevelPermissions).toEqual({ find: { - '*': true, + "*": true, }, create: { - 'role:admin': true, + "role:admin": true, }, get: {}, count: {}, @@ -1775,21 +1840,21 @@ describe('schemas', () => { }); }); - it('should fail setting schema permissions with invalid key', done => { - const object = new Parse.Object('AClass'); + it("should fail setting schema permissions with invalid key", done => { + const object = new Parse.Object("AClass"); object.save().then(() => { request({ - method: 'PUT', - url: 'http://localhost:8378/1/schemas/AClass', + method: "PUT", + url: "http://localhost:8378/1/schemas/AClass", headers: masterKeyHeaders, json: true, body: { classLevelPermissions: { find: { - '*': true, + "*": true, }, create: { - 'role:admin': true, + "role:admin": true, }, dummy: { some: true, @@ -1799,117 +1864,119 @@ describe('schemas', () => { }).then(fail, response => { expect(response.data.code).toEqual(107); expect(response.data.error).toEqual( - 'dummy is not a valid operation for class level permissions' + "dummy is not a valid operation for class level permissions" ); done(); }); }); }); - it('should not be able to add a field', done => { + it("should not be able to add a field", done => { request({ - method: 'POST', - url: 'http://localhost:8378/1/schemas/AClass', + method: "POST", + url: "http://localhost:8378/1/schemas/AClass", headers: masterKeyHeaders, json: true, body: { classLevelPermissions: { create: { - '*': true, + "*": true, }, find: { - '*': true, + "*": true, }, addField: { - 'role:admin': true, + "role:admin": true, }, }, }, }).then(() => { - const object = new Parse.Object('AClass'); - object.set('hello', 'world'); + const object = new Parse.Object("AClass"); + object.set("hello", "world"); return object.save().then( () => { - fail('should not be able to add a field'); + fail("should not be able to add a field"); done(); }, err => { - expect(err.message).toEqual('Permission denied for action addField on class AClass.'); + expect(err.message).toEqual( + "Permission denied for action addField on class AClass." + ); done(); } ); }); }); - it('should be able to add a field', done => { + it("should be able to add a field", done => { request({ - method: 'POST', - url: 'http://localhost:8378/1/schemas/AClass', + method: "POST", + url: "http://localhost:8378/1/schemas/AClass", headers: masterKeyHeaders, json: true, body: { classLevelPermissions: { create: { - '*': true, + "*": true, }, addField: { - '*': true, + "*": true, }, }, }, }).then(() => { - const object = new Parse.Object('AClass'); - object.set('hello', 'world'); + const object = new Parse.Object("AClass"); + object.set("hello", "world"); return object.save().then( () => { done(); }, () => { - fail('should be able to add a field'); + fail("should be able to add a field"); done(); } ); }); }); - describe('Nested documents', () => { + describe("Nested documents", () => { beforeAll(async () => { - const testSchema = new Parse.Schema('test_7371'); + const testSchema = new Parse.Schema("test_7371"); testSchema.setCLP({ - create: { ['*']: true }, - update: { ['*']: true }, + create: { ["*"]: true }, + update: { ["*"]: true }, addField: {}, }); - testSchema.addObject('a'); + testSchema.addObject("a"); await testSchema.save(); }); - it('addField permission not required for adding a nested property', async () => { - const obj = new Parse.Object('test_7371'); - obj.set('a', {}); + it("addField permission not required for adding a nested property", async () => { + const obj = new Parse.Object("test_7371"); + obj.set("a", {}); await obj.save(); - obj.set('a.b', 2); + obj.set("a.b", 2); await obj.save(); }); - it('addField permission not required for modifying a nested property', async () => { - const obj = new Parse.Object('test_7371'); - obj.set('a', { b: 1 }); + it("addField permission not required for modifying a nested property", async () => { + const obj = new Parse.Object("test_7371"); + obj.set("a", { b: 1 }); await obj.save(); - obj.set('a.b', 2); + obj.set("a.b", 2); await obj.save(); }); }); - it('should aceept class-level permission with userid of any length', async done => { + it("should aceept class-level permission with userid of any length", async done => { await global.reconfigureServer({ customIdSize: 11, }); - const id = 'e1evenChars'; + const id = "e1evenChars"; const { data } = await request({ - method: 'POST', - url: 'http://localhost:8378/1/schemas/AClass', + method: "POST", + url: "http://localhost:8378/1/schemas/AClass", headers: masterKeyHeaders, json: true, body: { @@ -1926,16 +1993,16 @@ describe('schemas', () => { done(); }); - it('should allow set class-level permission for custom userid of any length and chars', async done => { + it("should allow set class-level permission for custom userid of any length and chars", async done => { await global.reconfigureServer({ allowCustomObjectId: true, }); - const symbolsId = 'set:ID+symbol$=@llowed'; - const shortId = '1'; + const symbolsId = "set:ID+symbol$=@llowed"; + const shortId = "1"; const { data } = await request({ - method: 'POST', - url: 'http://localhost:8378/1/schemas/AClass', + method: "POST", + url: "http://localhost:8378/1/schemas/AClass", headers: masterKeyHeaders, json: true, body: { @@ -1954,18 +2021,18 @@ describe('schemas', () => { done(); }); - it('should allow set ACL for custom userid', async done => { + it("should allow set ACL for custom userid", async done => { await global.reconfigureServer({ allowCustomObjectId: true, }); - const symbolsId = 'symbols:id@allowed='; - const shortId = '1'; - const normalId = 'tensymbols'; + const symbolsId = "symbols:id@allowed="; + const shortId = "1"; + const normalId = "tensymbols"; const { data } = await request({ - method: 'POST', - url: 'http://localhost:8378/1/classes/AClass', + method: "POST", + url: "http://localhost:8378/1/classes/AClass", headers: masterKeyHeaders, json: true, body: { @@ -1978,7 +2045,7 @@ describe('schemas', () => { }); const { data: created } = await request({ - method: 'GET', + method: "GET", url: `http://localhost:8378/1/classes/AClass/${data.objectId}`, headers: masterKeyHeaders, json: true, @@ -1990,16 +2057,16 @@ describe('schemas', () => { done(); }); - it('should throw with invalid userId (invalid char)', done => { + it("should throw with invalid userId (invalid char)", done => { request({ - method: 'POST', - url: 'http://localhost:8378/1/schemas/AClass', + method: "POST", + url: "http://localhost:8378/1/schemas/AClass", headers: masterKeyHeaders, json: true, body: { classLevelPermissions: { find: { - '12345_6789': true, + "12345_6789": true, }, }, }, @@ -2011,54 +2078,58 @@ describe('schemas', () => { }); }); - it('should throw with invalid * (spaces before)', done => { + it("should throw with invalid * (spaces before)", done => { request({ - method: 'POST', - url: 'http://localhost:8378/1/schemas/AClass', + method: "POST", + url: "http://localhost:8378/1/schemas/AClass", headers: masterKeyHeaders, json: true, body: { classLevelPermissions: { find: { - ' *': true, + " *": true, }, }, }, }).then(fail, response => { - expect(response.data.error).toEqual("' *' is not a valid key for class level permissions"); + expect(response.data.error).toEqual( + "' *' is not a valid key for class level permissions" + ); done(); }); }); - it('should throw with invalid * (spaces after)', done => { + it("should throw with invalid * (spaces after)", done => { request({ - method: 'POST', - url: 'http://localhost:8378/1/schemas/AClass', + method: "POST", + url: "http://localhost:8378/1/schemas/AClass", headers: masterKeyHeaders, json: true, body: { classLevelPermissions: { find: { - '* ': true, + "* ": true, }, }, }, }).then(fail, response => { - expect(response.data.error).toEqual("'* ' is not a valid key for class level permissions"); + expect(response.data.error).toEqual( + "'* ' is not a valid key for class level permissions" + ); done(); }); }); - it('should throw if permission is number', done => { + it("should throw if permission is number", done => { request({ - method: 'POST', - url: 'http://localhost:8378/1/schemas/AClass', + method: "POST", + url: "http://localhost:8378/1/schemas/AClass", headers: masterKeyHeaders, json: true, body: { classLevelPermissions: { find: { - '*': 1, + "*": 1, }, }, }, @@ -2070,34 +2141,36 @@ describe('schemas', () => { }); }); - it('should validate defaultAcl with class level permissions when request is not an object', async () => { + it("should validate defaultAcl with class level permissions when request is not an object", async () => { const response = await request({ - method: 'POST', - url: 'http://localhost:8378/1/schemas/AClass', + method: "POST", + url: "http://localhost:8378/1/schemas/AClass", headers: masterKeyHeaders, json: true, body: { classLevelPermissions: { ACL: { - '*': true, + "*": true, }, }, }, }).catch(error => error.data); - expect(response.error).toEqual(`'true' is not a valid value for class level permissions acl`); + expect(response.error).toEqual( + `'true' is not a valid value for class level permissions acl` + ); }); - it('should validate defaultAcl with class level permissions when request is an object and invalid key', async () => { + it("should validate defaultAcl with class level permissions when request is an object and invalid key", async () => { const response = await request({ - method: 'POST', - url: 'http://localhost:8378/1/schemas/AClass', + method: "POST", + url: "http://localhost:8378/1/schemas/AClass", headers: masterKeyHeaders, json: true, body: { classLevelPermissions: { ACL: { - '*': { + "*": { foo: true, }, }, @@ -2105,19 +2178,21 @@ describe('schemas', () => { }, }).catch(error => error.data); - expect(response.error).toEqual(`'foo' is not a valid key for class level permissions acl`); + expect(response.error).toEqual( + `'foo' is not a valid key for class level permissions acl` + ); }); - it('should validate defaultAcl with class level permissions when request is an object and invalid value', async () => { + it("should validate defaultAcl with class level permissions when request is an object and invalid value", async () => { const response = await request({ - method: 'POST', - url: 'http://localhost:8378/1/schemas/AClass', + method: "POST", + url: "http://localhost:8378/1/schemas/AClass", headers: masterKeyHeaders, json: true, body: { classLevelPermissions: { ACL: { - '*': { + "*": { read: 1, }, }, @@ -2125,19 +2200,21 @@ describe('schemas', () => { }, }).catch(error => error.data); - expect(response.error).toEqual(`'1' is not a valid value for class level permissions acl`); + expect(response.error).toEqual( + `'1' is not a valid value for class level permissions acl` + ); }); - it('should throw if permission is empty string', done => { + it("should throw if permission is empty string", done => { request({ - method: 'POST', - url: 'http://localhost:8378/1/schemas/AClass', + method: "POST", + url: "http://localhost:8378/1/schemas/AClass", headers: masterKeyHeaders, json: true, body: { classLevelPermissions: { find: { - '*': '', + "*": "", }, }, }, @@ -2151,8 +2228,8 @@ describe('schemas', () => { function setPermissionsOnClass(className, permissions, doPut) { return request({ - url: 'http://localhost:8378/1/schemas/' + className, - method: doPut ? 'PUT' : 'POST', + url: "http://localhost:8378/1/schemas/" + className, + method: doPut ? "PUT" : "POST", headers: masterKeyHeaders, json: true, body: { @@ -2166,20 +2243,20 @@ describe('schemas', () => { }); } - it('validate CLP 1', done => { + it("validate CLP 1", done => { const user = new Parse.User(); - user.setUsername('user'); - user.setPassword('user'); + user.setUsername("user"); + user.setPassword("user"); const admin = new Parse.User(); - admin.setUsername('admin'); - admin.setPassword('admin'); + admin.setUsername("admin"); + admin.setPassword("admin"); - const role = new Parse.Role('admin', new Parse.ACL()); + const role = new Parse.Role("admin", new Parse.ACL()); - setPermissionsOnClass('AClass', { + setPermissionsOnClass("AClass", { find: { - 'role:admin': true, + "role:admin": true, }, }) .then(() => { @@ -2188,32 +2265,34 @@ describe('schemas', () => { }); }) .then(() => { - role.relation('users').add(admin); + role.relation("users").add(admin); return role.save(null, { useMasterKey: true }); }) .then(() => { - return Parse.User.logIn('user', 'user').then(() => { - const obj = new Parse.Object('AClass'); + return Parse.User.logIn("user", "user").then(() => { + const obj = new Parse.Object("AClass"); return obj.save(null, { useMasterKey: true }); }); }) .then(() => { - const query = new Parse.Query('AClass'); + const query = new Parse.Query("AClass"); return query.find().then( () => { - fail('Use should hot be able to find!'); + fail("Use should hot be able to find!"); }, err => { - expect(err.message).toEqual('Permission denied for action find on class AClass.'); + expect(err.message).toEqual( + "Permission denied for action find on class AClass." + ); return Promise.resolve(); } ); }) .then(() => { - return Parse.User.logIn('admin', 'admin'); + return Parse.User.logIn("admin", "admin"); }) .then(() => { - const query = new Parse.Query('AClass'); + const query = new Parse.Query("AClass"); return query.find(); }) .then(results => { @@ -2226,20 +2305,20 @@ describe('schemas', () => { }); }); - it('validate CLP 2', done => { + it("validate CLP 2", done => { const user = new Parse.User(); - user.setUsername('user'); - user.setPassword('user'); + user.setUsername("user"); + user.setPassword("user"); const admin = new Parse.User(); - admin.setUsername('admin'); - admin.setPassword('admin'); + admin.setUsername("admin"); + admin.setPassword("admin"); - const role = new Parse.Role('admin', new Parse.ACL()); + const role = new Parse.Role("admin", new Parse.ACL()); - setPermissionsOnClass('AClass', { + setPermissionsOnClass("AClass", { find: { - 'role:admin': true, + "role:admin": true, }, }) .then(() => { @@ -2248,23 +2327,25 @@ describe('schemas', () => { }); }) .then(() => { - role.relation('users').add(admin); + role.relation("users").add(admin); return role.save(null, { useMasterKey: true }); }) .then(() => { - return Parse.User.logIn('user', 'user').then(() => { - const obj = new Parse.Object('AClass'); + return Parse.User.logIn("user", "user").then(() => { + const obj = new Parse.Object("AClass"); return obj.save(null, { useMasterKey: true }); }); }) .then(() => { - const query = new Parse.Query('AClass'); + const query = new Parse.Query("AClass"); return query.find().then( () => { - fail('User should not be able to find!'); + fail("User should not be able to find!"); }, err => { - expect(err.message).toEqual('Permission denied for action find on class AClass.'); + expect(err.message).toEqual( + "Permission denied for action find on class AClass." + ); return Promise.resolve(); } ); @@ -2272,33 +2353,33 @@ describe('schemas', () => { .then(() => { // let everyone see it now return setPermissionsOnClass( - 'AClass', + "AClass", { find: { - 'role:admin': true, - '*': true, + "role:admin": true, + "*": true, }, }, true ); }) .then(() => { - const query = new Parse.Query('AClass'); + const query = new Parse.Query("AClass"); return query.find().then( result => { expect(result.length).toBe(1); }, () => { - fail('User should be able to find!'); + fail("User should be able to find!"); done(); } ); }) .then(() => { - return Parse.User.logIn('admin', 'admin'); + return Parse.User.logIn("admin", "admin"); }) .then(() => { - const query = new Parse.Query('AClass'); + const query = new Parse.Query("AClass"); return query.find(); }) .then(results => { @@ -2311,20 +2392,20 @@ describe('schemas', () => { }); }); - it('validate CLP 3', done => { + it("validate CLP 3", done => { const user = new Parse.User(); - user.setUsername('user'); - user.setPassword('user'); + user.setUsername("user"); + user.setPassword("user"); const admin = new Parse.User(); - admin.setUsername('admin'); - admin.setPassword('admin'); + admin.setUsername("admin"); + admin.setPassword("admin"); - const role = new Parse.Role('admin', new Parse.ACL()); + const role = new Parse.Role("admin", new Parse.ACL()); - setPermissionsOnClass('AClass', { + setPermissionsOnClass("AClass", { find: { - 'role:admin': true, + "role:admin": true, }, }) .then(() => { @@ -2333,48 +2414,50 @@ describe('schemas', () => { }); }) .then(() => { - role.relation('users').add(admin); + role.relation("users").add(admin); return role.save(null, { useMasterKey: true }); }) .then(() => { - return Parse.User.logIn('user', 'user').then(() => { - const obj = new Parse.Object('AClass'); + return Parse.User.logIn("user", "user").then(() => { + const obj = new Parse.Object("AClass"); return obj.save(null, { useMasterKey: true }); }); }) .then(() => { - const query = new Parse.Query('AClass'); + const query = new Parse.Query("AClass"); return query.find().then( () => { - fail('User should not be able to find!'); + fail("User should not be able to find!"); }, err => { - expect(err.message).toEqual('Permission denied for action find on class AClass.'); + expect(err.message).toEqual( + "Permission denied for action find on class AClass." + ); return Promise.resolve(); } ); }) .then(() => { // delete all CLP - return setPermissionsOnClass('AClass', null, true); + return setPermissionsOnClass("AClass", null, true); }) .then(() => { - const query = new Parse.Query('AClass'); + const query = new Parse.Query("AClass"); return query.find().then( result => { expect(result.length).toBe(1); }, () => { - fail('User should be able to find!'); + fail("User should be able to find!"); done(); } ); }) .then(() => { - return Parse.User.logIn('admin', 'admin'); + return Parse.User.logIn("admin", "admin"); }) .then(() => { - const query = new Parse.Query('AClass'); + const query = new Parse.Query("AClass"); return query.find(); }) .then(results => { @@ -2387,20 +2470,20 @@ describe('schemas', () => { }); }); - it('validate CLP 4', done => { + it("validate CLP 4", done => { const user = new Parse.User(); - user.setUsername('user'); - user.setPassword('user'); + user.setUsername("user"); + user.setPassword("user"); const admin = new Parse.User(); - admin.setUsername('admin'); - admin.setPassword('admin'); + admin.setUsername("admin"); + admin.setPassword("admin"); - const role = new Parse.Role('admin', new Parse.ACL()); + const role = new Parse.Role("admin", new Parse.ACL()); - setPermissionsOnClass('AClass', { + setPermissionsOnClass("AClass", { find: { - 'role:admin': true, + "role:admin": true, }, }) .then(() => { @@ -2409,23 +2492,25 @@ describe('schemas', () => { }); }) .then(() => { - role.relation('users').add(admin); + role.relation("users").add(admin); return role.save(null, { useMasterKey: true }); }) .then(() => { - return Parse.User.logIn('user', 'user').then(() => { - const obj = new Parse.Object('AClass'); + return Parse.User.logIn("user", "user").then(() => { + const obj = new Parse.Object("AClass"); return obj.save(null, { useMasterKey: true }); }); }) .then(() => { - const query = new Parse.Query('AClass'); + const query = new Parse.Query("AClass"); return query.find().then( () => { - fail('User should not be able to find!'); + fail("User should not be able to find!"); }, err => { - expect(err.message).toEqual('Permission denied for action find on class AClass.'); + expect(err.message).toEqual( + "Permission denied for action find on class AClass." + ); return Promise.resolve(); } ); @@ -2433,16 +2518,16 @@ describe('schemas', () => { .then(() => { // borked CLP should not affec security return setPermissionsOnClass( - 'AClass', + "AClass", { found: { - 'role:admin': true, + "role:admin": true, }, }, true ).then( () => { - fail('Should not be able to save a borked CLP'); + fail("Should not be able to save a borked CLP"); }, () => { return Promise.resolve(); @@ -2450,22 +2535,24 @@ describe('schemas', () => { ); }) .then(() => { - const query = new Parse.Query('AClass'); + const query = new Parse.Query("AClass"); return query.find().then( () => { - fail('User should not be able to find!'); + fail("User should not be able to find!"); }, err => { - expect(err.message).toEqual('Permission denied for action find on class AClass.'); + expect(err.message).toEqual( + "Permission denied for action find on class AClass." + ); return Promise.resolve(); } ); }) .then(() => { - return Parse.User.logIn('admin', 'admin'); + return Parse.User.logIn("admin", "admin"); }) .then(() => { - const query = new Parse.Query('AClass'); + const query = new Parse.Query("AClass"); return query.find(); }) .then(results => { @@ -2478,19 +2565,19 @@ describe('schemas', () => { }); }); - it('validate CLP 5', done => { + it("validate CLP 5", done => { const user = new Parse.User(); - user.setUsername('user'); - user.setPassword('user'); + user.setUsername("user"); + user.setPassword("user"); const user2 = new Parse.User(); - user2.setUsername('user2'); - user2.setPassword('user2'); + user2.setUsername("user2"); + user2.setPassword("user2"); const admin = new Parse.User(); - admin.setUsername('admin'); - admin.setPassword('admin'); + admin.setUsername("admin"); + admin.setPassword("admin"); - const role = new Parse.Role('admin', new Parse.ACL()); + const role = new Parse.Role("admin", new Parse.ACL()); Promise.resolve() .then(() => { @@ -2499,65 +2586,69 @@ describe('schemas', () => { }); }) .then(() => { - role.relation('users').add(admin); + role.relation("users").add(admin); return role.save(null, { useMasterKey: true }).then(() => { const perm = { find: {}, }; // let the user find - perm['find'][user.id] = true; - return setPermissionsOnClass('AClass', perm); + perm["find"][user.id] = true; + return setPermissionsOnClass("AClass", perm); }); }) .then(() => { - return Parse.User.logIn('user', 'user').then(() => { - const obj = new Parse.Object('AClass'); + return Parse.User.logIn("user", "user").then(() => { + const obj = new Parse.Object("AClass"); return obj.save(); }); }) .then(() => { - const query = new Parse.Query('AClass'); + const query = new Parse.Query("AClass"); return query.find().then( res => { expect(res.length).toEqual(1); }, () => { - fail('User should be able to find!'); + fail("User should be able to find!"); return Promise.resolve(); } ); }) .then(() => { - return Parse.User.logIn('admin', 'admin'); + return Parse.User.logIn("admin", "admin"); }) .then(() => { - const query = new Parse.Query('AClass'); + const query = new Parse.Query("AClass"); return query.find(); }) .then( () => { - fail('should not be able to read!'); + fail("should not be able to read!"); return Promise.resolve(); }, err => { - expect(err.message).toEqual('Permission denied for action create on class AClass.'); + expect(err.message).toEqual( + "Permission denied for action create on class AClass." + ); return Promise.resolve(); } ) .then(() => { - return Parse.User.logIn('user2', 'user2'); + return Parse.User.logIn("user2", "user2"); }) .then(() => { - const query = new Parse.Query('AClass'); + const query = new Parse.Query("AClass"); return query.find(); }) .then( () => { - fail('should not be able to read!'); + fail("should not be able to read!"); return Promise.resolve(); }, err => { - expect(err.message).toEqual('Permission denied for action find on class AClass.'); + expect(err.message).toEqual( + "Permission denied for action find on class AClass." + ); return Promise.resolve(); } ) @@ -2566,30 +2657,30 @@ describe('schemas', () => { }); }); - it('can query with include and CLP (issue #2005)', done => { - setPermissionsOnClass('AnotherObject', { - get: { '*': true }, + it("can query with include and CLP (issue #2005)", done => { + setPermissionsOnClass("AnotherObject", { + get: { "*": true }, find: {}, - create: { '*': true }, - update: { '*': true }, - delete: { '*': true }, - addField: { '*': true }, + create: { "*": true }, + update: { "*": true }, + delete: { "*": true }, + addField: { "*": true }, }) .then(() => { - const obj = new Parse.Object('AnObject'); - const anotherObject = new Parse.Object('AnotherObject'); + const obj = new Parse.Object("AnObject"); + const anotherObject = new Parse.Object("AnotherObject"); return obj.save({ anotherObject, }); }) .then(() => { - const query = new Parse.Query('AnObject'); - query.include('anotherObject'); + const query = new Parse.Query("AnObject"); + query.include("anotherObject"); return query.find(); }) .then(res => { expect(res.length).toBe(1); - expect(res[0].get('anotherObject')).not.toBeUndefined(); + expect(res[0].get("anotherObject")).not.toBeUndefined(); done(); }) .catch(err => { @@ -2598,42 +2689,42 @@ describe('schemas', () => { }); }); - it('can add field as master (issue #1257)', done => { - setPermissionsOnClass('AClass', { + it("can add field as master (issue #1257)", done => { + setPermissionsOnClass("AClass", { addField: {}, }) .then(() => { - const obj = new Parse.Object('AClass'); - obj.set('key', 'value'); + const obj = new Parse.Object("AClass"); + obj.set("key", "value"); return obj.save(null, { useMasterKey: true }); }) .then( obj => { - expect(obj.get('key')).toEqual('value'); + expect(obj.get("key")).toEqual("value"); done(); }, () => { - fail('should not fail'); + fail("should not fail"); done(); } ); }); - it('can login when addFields is false (issue #1355)', done => { + it("can login when addFields is false (issue #1355)", done => { setPermissionsOnClass( - '_User', + "_User", { - create: { '*': true }, + create: { "*": true }, addField: {}, }, true ) .then(() => { - return Parse.User.signUp('foo', 'bar'); + return Parse.User.signUp("foo", "bar"); }) .then( user => { - expect(user.getUsername()).toBe('foo'); + expect(user.getUsername()).toBe("foo"); done(); }, error => { @@ -2643,59 +2734,59 @@ describe('schemas', () => { ); }); - it('unset field in beforeSave should not stop object creation', done => { + it("unset field in beforeSave should not stop object creation", done => { const hook = { method: function (req) { - if (req.object.get('undesiredField')) { - req.object.unset('undesiredField'); + if (req.object.get("undesiredField")) { + req.object.unset("undesiredField"); } }, }; - spyOn(hook, 'method').and.callThrough(); - Parse.Cloud.beforeSave('AnObject', hook.method); - setPermissionsOnClass('AnObject', { - get: { '*': true }, - find: { '*': true }, - create: { '*': true }, - update: { '*': true }, - delete: { '*': true }, + spyOn(hook, "method").and.callThrough(); + Parse.Cloud.beforeSave("AnObject", hook.method); + setPermissionsOnClass("AnObject", { + get: { "*": true }, + find: { "*": true }, + create: { "*": true }, + update: { "*": true }, + delete: { "*": true }, addField: {}, }) .then(() => { - const obj = new Parse.Object('AnObject'); - obj.set('desiredField', 'createMe'); + const obj = new Parse.Object("AnObject"); + obj.set("desiredField", "createMe"); return obj.save(null, { useMasterKey: true }); }) .then(() => { - const obj = new Parse.Object('AnObject'); - obj.set('desiredField', 'This value should be kept'); - obj.set('undesiredField', 'This value should be IGNORED'); + const obj = new Parse.Object("AnObject"); + obj.set("desiredField", "This value should be kept"); + obj.set("undesiredField", "This value should be IGNORED"); return obj.save(); }) .then(() => { - const query = new Parse.Query('AnObject'); + const query = new Parse.Query("AnObject"); return query.find(); }) .then(results => { expect(results.length).toBe(2); - expect(results[0].has('desiredField')).toBe(true); - expect(results[1].has('desiredField')).toBe(true); - expect(results[0].has('undesiredField')).toBe(false); - expect(results[1].has('undesiredField')).toBe(false); + expect(results[0].has("desiredField")).toBe(true); + expect(results[1].has("desiredField")).toBe(true); + expect(results[0].has("undesiredField")).toBe(false); + expect(results[1].has("undesiredField")).toBe(false); expect(hook.method).toHaveBeenCalled(); done(); }); }); - it('gives correct response when deleting a schema with CLPs (regression test #1919)', done => { - new Parse.Object('MyClass') - .save({ data: 'foo' }) + it("gives correct response when deleting a schema with CLPs (regression test #1919)", done => { + new Parse.Object("MyClass") + .save({ data: "foo" }) .then(obj => obj.destroy()) - .then(() => setPermissionsOnClass('MyClass', { find: {}, get: {} }, true)) + .then(() => setPermissionsOnClass("MyClass", { find: {}, get: {} }, true)) .then(() => { request({ - method: 'DELETE', - url: 'http://localhost:8378/1/schemas/MyClass', + method: "DELETE", + url: "http://localhost:8378/1/schemas/MyClass", headers: masterKeyHeaders, json: true, }).then(response => { @@ -2706,37 +2797,37 @@ describe('schemas', () => { }); }); - it('regression test for #1991', done => { + it("regression test for #1991", done => { const user = new Parse.User(); - user.setUsername('user'); - user.setPassword('user'); - const role = new Parse.Role('admin', new Parse.ACL()); - const obj = new Parse.Object('AnObject'); + user.setUsername("user"); + user.setPassword("user"); + const role = new Parse.Role("admin", new Parse.ACL()); + const obj = new Parse.Object("AnObject"); Parse.Object.saveAll([user, role]) .then(() => { - role.relation('users').add(user); + role.relation("users").add(user); return role.save(null, { useMasterKey: true }); }) .then(() => { - return setPermissionsOnClass('AnObject', { - get: { '*': true }, - find: { '*': true }, - create: { '*': true }, - update: { 'role:admin': true }, - delete: { 'role:admin': true }, + return setPermissionsOnClass("AnObject", { + get: { "*": true }, + find: { "*": true }, + create: { "*": true }, + update: { "role:admin": true }, + delete: { "role:admin": true }, }); }) .then(() => { return obj.save(); }) .then(() => { - return Parse.User.logIn('user', 'user'); + return Parse.User.logIn("user", "user"); }) .then(() => { return obj.destroy(); }) .then(() => { - const query = new Parse.Query('AnObject'); + const query = new Parse.Query("AnObject"); return query.find(); }) .then(results => { @@ -2744,36 +2835,36 @@ describe('schemas', () => { done(); }) .catch(err => { - fail('should not fail'); + fail("should not fail"); jfail(err); done(); }); }); - it('regression test for #4409 (indexes override the clp)', done => { + it("regression test for #4409 (indexes override the clp)", done => { setPermissionsOnClass( - '_Role', + "_Role", { ACL: { - '*': { + "*": { read: true, write: true, }, }, - get: { '*': true }, - find: { '*': true }, - count: { '*': true }, - create: { '*': true }, + get: { "*": true }, + find: { "*": true }, + count: { "*": true }, + create: { "*": true }, }, true ) .then(() => { - const config = Config.get('test'); + const config = Config.get("test"); return config.database.adapter.updateSchemaWithIndexes(); }) .then(() => { return request({ - url: 'http://localhost:8378/1/schemas/_Role', + url: "http://localhost:8378/1/schemas/_Role", headers: masterKeyHeaders, json: true, }); @@ -2781,15 +2872,15 @@ describe('schemas', () => { .then(res => { expect(res.data.classLevelPermissions).toEqual({ ACL: { - '*': { + "*": { read: true, write: true, }, }, - get: { '*': true }, - find: { '*': true }, - count: { '*': true }, - create: { '*': true }, + get: { "*": true }, + find: { "*": true }, + count: { "*": true }, + create: { "*": true }, update: {}, delete: {}, addField: {}, @@ -2800,31 +2891,31 @@ describe('schemas', () => { .catch(done.fail); }); - it('regression test for #5177', async () => { + it("regression test for #5177", async () => { Parse.Object.disableSingleInstance(); - Parse.Cloud.beforeSave('AClass', () => {}); + Parse.Cloud.beforeSave("AClass", () => {}); await setPermissionsOnClass( - 'AClass', + "AClass", { - update: { '*': true }, + update: { "*": true }, }, false ); - const obj = new Parse.Object('AClass'); + const obj = new Parse.Object("AClass"); await obj.save({ key: 1 }, { useMasterKey: true }); - obj.increment('key', 10); + obj.increment("key", 10); const objectAgain = await obj.save(); - expect(objectAgain.get('key')).toBe(11); + expect(objectAgain.get("key")).toBe(11); }); - it('regression test for #2246', done => { - const profile = new Parse.Object('UserProfile'); + it("regression test for #2246", done => { + const profile = new Parse.Object("UserProfile"); const user = new Parse.User(); function initialize() { return user .save({ - username: 'user', - password: 'password', + username: "user", + password: "password", }) .then(() => { return profile.save({ user }).then(() => { @@ -2841,25 +2932,25 @@ describe('schemas', () => { initialize() .then(() => { return setPermissionsOnClass( - 'UserProfile', + "UserProfile", { - readUserFields: ['user'], - writeUserFields: ['user'], + readUserFields: ["user"], + writeUserFields: ["user"], }, true ); }) .then(() => { - return Parse.User.logIn('user', 'password'); + return Parse.User.logIn("user", "password"); }) .then(() => { - const query = new Parse.Query('_User'); - query.include('userProfile'); + const query = new Parse.Query("_User"); + query.include("userProfile"); return query.get(user.id); }) .then( user => { - expect(user.get('userProfile')).not.toBeUndefined(); + expect(user.get("userProfile")).not.toBeUndefined(); done(); }, err => { @@ -2869,44 +2960,52 @@ describe('schemas', () => { ); }); - it('should reject creating class schema with field with invalid key', async done => { + it("should reject creating class schema with field with invalid key", async done => { const config = Config.get(Parse.applicationId); const schemaController = await config.database.loadSchema(); - const fieldName = '1invalid'; + const fieldName = "1invalid"; const schemaCreation = () => - schemaController.addClassIfNotExists('AnObject', { - [fieldName]: { __type: 'String' }, + schemaController.addClassIfNotExists("AnObject", { + [fieldName]: { __type: "String" }, }); await expectAsync(schemaCreation()).toBeRejectedWith( - new Parse.Error(Parse.Error.INVALID_KEY_NAME, `invalid field name: ${fieldName}`) + new Parse.Error( + Parse.Error.INVALID_KEY_NAME, + `invalid field name: ${fieldName}` + ) ); done(); }); - it('should reject creating invalid field name', async done => { - const object = new Parse.Object('AnObject'); + it("should reject creating invalid field name", async done => { + const object = new Parse.Object("AnObject"); await expectAsync( object.save({ - '!12field': 'field', + "!12field": "field", }) - ).toBeRejectedWith(new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'Invalid key name: !12field')); + ).toBeRejectedWith( + new Parse.Error( + Parse.Error.INVALID_KEY_NAME, + "Invalid key name: !12field" + ) + ); done(); }); - it('should be rejected if CLP operation is not an object', async done => { + it("should be rejected if CLP operation is not an object", async done => { const config = Config.get(Parse.applicationId); const schemaController = await config.database.loadSchema(); - const operationKey = 'get'; + const operationKey = "get"; const operation = true; const schemaSetup = async () => await schemaController.addClassIfNotExists( - 'AnObject', + "AnObject", {}, { [operationKey]: operation, @@ -2923,16 +3022,16 @@ describe('schemas', () => { done(); }); - it('should be rejected if CLP protectedFields is not an object', async done => { + it("should be rejected if CLP protectedFields is not an object", async done => { const config = Config.get(Parse.applicationId); const schemaController = await config.database.loadSchema(); - const operationKey = 'get'; - const operation = 'wrongtype'; + const operationKey = "get"; + const operation = "wrongtype"; const schemaSetup = async () => await schemaController.addClassIfNotExists( - 'AnObject', + "AnObject", {}, { [operationKey]: operation, @@ -2949,16 +3048,16 @@ describe('schemas', () => { done(); }); - it('should be rejected if CLP read/writeUserFields is not an array', async done => { + it("should be rejected if CLP read/writeUserFields is not an array", async done => { const config = Config.get(Parse.applicationId); const schemaController = await config.database.loadSchema(); - const operationKey = 'readUserFields'; + const operationKey = "readUserFields"; const operation = true; const schemaSetup = async () => await schemaController.addClassIfNotExists( - 'AnObject', + "AnObject", {}, { [operationKey]: operation, @@ -2975,17 +3074,17 @@ describe('schemas', () => { done(); }); - it('should be rejected if CLP pointerFields is not an array', async done => { + it("should be rejected if CLP pointerFields is not an array", async done => { const config = Config.get(Parse.applicationId); const schemaController = await config.database.loadSchema(); - const operationKey = 'get'; - const entity = 'pointerFields'; + const operationKey = "get"; + const entity = "pointerFields"; const value = {}; const schemaSetup = async () => await schemaController.addClassIfNotExists( - 'AnObject', + "AnObject", {}, { [operationKey]: { @@ -3004,23 +3103,25 @@ describe('schemas', () => { done(); }); - describe('index management', () => { + describe("index management", () => { beforeEach(async () => { await TestUtils.destroyAllDataPermanently(false); - await config.database.adapter.performInitialization({ VolatileClassesSchemas: [] }); + await config.database.adapter.performInitialization({ + VolatileClassesSchemas: [], + }); }); - it('cannot create index if field does not exist', done => { + it("cannot create index if field does not exist", done => { request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'POST', + url: "http://localhost:8378/1/schemas/NewClass", + method: "POST", headers: masterKeyHeaders, json: true, body: {}, }).then(() => { request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'PUT', + url: "http://localhost:8378/1/schemas/NewClass", + method: "PUT", headers: masterKeyHeaders, json: true, body: { @@ -3030,23 +3131,25 @@ describe('schemas', () => { }, }).then(fail, response => { expect(response.data.code).toBe(Parse.Error.INVALID_QUERY); - expect(response.data.error).toBe('Field aString does not exist, cannot add index.'); + expect(response.data.error).toBe( + "Field aString does not exist, cannot add index." + ); done(); }); }); }); - it('can create index on default field', done => { + it("can create index on default field", done => { request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'POST', + url: "http://localhost:8378/1/schemas/NewClass", + method: "POST", headers: masterKeyHeaders, json: true, body: {}, }).then(() => { request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'PUT', + url: "http://localhost:8378/1/schemas/NewClass", + method: "PUT", headers: masterKeyHeaders, json: true, body: { @@ -3061,22 +3164,22 @@ describe('schemas', () => { }); }); - it('cannot create compound index if field does not exist', done => { + it("cannot create compound index if field does not exist", done => { request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'POST', + url: "http://localhost:8378/1/schemas/NewClass", + method: "POST", headers: masterKeyHeaders, json: true, body: {}, }).then(() => { request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'PUT', + url: "http://localhost:8378/1/schemas/NewClass", + method: "PUT", headers: masterKeyHeaders, json: true, body: { fields: { - aString: { type: 'String' }, + aString: { type: "String" }, }, indexes: { name1: { aString: 1, bString: 1 }, @@ -3084,22 +3187,24 @@ describe('schemas', () => { }, }).then(fail, response => { expect(response.data.code).toBe(Parse.Error.INVALID_QUERY); - expect(response.data.error).toBe('Field bString does not exist, cannot add index.'); + expect(response.data.error).toBe( + "Field bString does not exist, cannot add index." + ); done(); }); }); }); - it('allows add index when you create a class', done => { + it("allows add index when you create a class", done => { request({ - url: 'http://localhost:8378/1/schemas', - method: 'POST', + url: "http://localhost:8378/1/schemas", + method: "POST", headers: masterKeyHeaders, json: true, body: { - className: 'NewClass', + className: "NewClass", fields: { - aString: { type: 'String' }, + aString: { type: "String" }, }, indexes: { name1: { aString: 1 }, @@ -3107,48 +3212,48 @@ describe('schemas', () => { }, }).then(response => { expect(response.data).toEqual({ - className: 'NewClass', + className: "NewClass", fields: { - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - objectId: { type: 'String' }, - aString: { type: 'String' }, + ACL: { type: "ACL" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + objectId: { type: "String" }, + aString: { type: "String" }, }, classLevelPermissions: defaultClassLevelPermissions, indexes: { name1: { aString: 1 }, }, }); - config.database.adapter.getIndexes('NewClass').then(indexes => { + config.database.adapter.getIndexes("NewClass").then(indexes => { expect(indexes.length).toBe(2); done(); }); }); }); - it('empty index returns nothing', done => { + it("empty index returns nothing", done => { request({ - url: 'http://localhost:8378/1/schemas', - method: 'POST', + url: "http://localhost:8378/1/schemas", + method: "POST", headers: masterKeyHeaders, json: true, body: { - className: 'NewClass', + className: "NewClass", fields: { - aString: { type: 'String' }, + aString: { type: "String" }, }, indexes: {}, }, }).then(response => { expect(response.data).toEqual({ - className: 'NewClass', + className: "NewClass", fields: { - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - objectId: { type: 'String' }, - aString: { type: 'String' }, + ACL: { type: "ACL" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + objectId: { type: "String" }, + aString: { type: "String" }, }, classLevelPermissions: defaultClassLevelPermissions, }); @@ -3156,22 +3261,22 @@ describe('schemas', () => { }); }); - it('lets you add indexes', done => { + it("lets you add indexes", done => { request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'POST', + url: "http://localhost:8378/1/schemas/NewClass", + method: "POST", headers: masterKeyHeaders, json: true, body: {}, }).then(() => { request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'PUT', + url: "http://localhost:8378/1/schemas/NewClass", + method: "PUT", headers: masterKeyHeaders, json: true, body: { fields: { - aString: { type: 'String' }, + aString: { type: "String" }, }, indexes: { name1: { aString: 1 }, @@ -3180,13 +3285,13 @@ describe('schemas', () => { }).then(response => { expect( dd(response.data, { - className: 'NewClass', + className: "NewClass", fields: { - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - objectId: { type: 'String' }, - aString: { type: 'String' }, + ACL: { type: "ACL" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + objectId: { type: "String" }, + aString: { type: "String" }, }, classLevelPermissions: defaultClassLevelPermissions, indexes: { @@ -3196,18 +3301,18 @@ describe('schemas', () => { }) ).toEqual(undefined); request({ - url: 'http://localhost:8378/1/schemas/NewClass', + url: "http://localhost:8378/1/schemas/NewClass", headers: masterKeyHeaders, json: true, }).then(response => { expect(response.data).toEqual({ - className: 'NewClass', + className: "NewClass", fields: { - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - objectId: { type: 'String' }, - aString: { type: 'String' }, + ACL: { type: "ACL" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + objectId: { type: "String" }, + aString: { type: "String" }, }, classLevelPermissions: defaultClassLevelPermissions, indexes: { @@ -3215,7 +3320,7 @@ describe('schemas', () => { name1: { aString: 1 }, }, }); - config.database.adapter.getIndexes('NewClass').then(indexes => { + config.database.adapter.getIndexes("NewClass").then(indexes => { expect(indexes.length).toEqual(2); done(); }); @@ -3224,93 +3329,96 @@ describe('schemas', () => { }); }); - it_only_db('mongo')('lets you add index with with pointer like structure', done => { - request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'POST', - headers: masterKeyHeaders, - json: true, - body: {}, - }).then(() => { + it_only_db("mongo")( + "lets you add index with with pointer like structure", + done => { request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'PUT', + url: "http://localhost:8378/1/schemas/NewClass", + method: "POST", headers: masterKeyHeaders, json: true, - body: { - fields: { - aPointer: { type: 'Pointer', targetClass: 'NewClass' }, - }, - indexes: { - pointer: { _p_aPointer: 1 }, - }, - }, - }).then(response => { - expect( - dd(response.data, { - className: 'NewClass', - fields: { - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - objectId: { type: 'String' }, - aPointer: { type: 'Pointer', targetClass: 'NewClass' }, - }, - classLevelPermissions: defaultClassLevelPermissions, - indexes: { - _id_: { _id: 1 }, - pointer: { _p_aPointer: 1 }, - }, - }) - ).toEqual(undefined); + body: {}, + }).then(() => { request({ - url: 'http://localhost:8378/1/schemas/NewClass', + url: "http://localhost:8378/1/schemas/NewClass", + method: "PUT", headers: masterKeyHeaders, json: true, - }).then(response => { - expect(response.data).toEqual({ - className: 'NewClass', + body: { fields: { - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - objectId: { type: 'String' }, - aPointer: { type: 'Pointer', targetClass: 'NewClass' }, + aPointer: { type: "Pointer", targetClass: "NewClass" }, }, - classLevelPermissions: defaultClassLevelPermissions, indexes: { - _id_: { _id: 1 }, pointer: { _p_aPointer: 1 }, }, - }); - config.database.adapter.getIndexes('NewClass').then(indexes => { - expect(indexes.length).toEqual(2); - done(); + }, + }).then(response => { + expect( + dd(response.data, { + className: "NewClass", + fields: { + ACL: { type: "ACL" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + objectId: { type: "String" }, + aPointer: { type: "Pointer", targetClass: "NewClass" }, + }, + classLevelPermissions: defaultClassLevelPermissions, + indexes: { + _id_: { _id: 1 }, + pointer: { _p_aPointer: 1 }, + }, + }) + ).toEqual(undefined); + request({ + url: "http://localhost:8378/1/schemas/NewClass", + headers: masterKeyHeaders, + json: true, + }).then(response => { + expect(response.data).toEqual({ + className: "NewClass", + fields: { + ACL: { type: "ACL" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + objectId: { type: "String" }, + aPointer: { type: "Pointer", targetClass: "NewClass" }, + }, + classLevelPermissions: defaultClassLevelPermissions, + indexes: { + _id_: { _id: 1 }, + pointer: { _p_aPointer: 1 }, + }, + }); + config.database.adapter.getIndexes("NewClass").then(indexes => { + expect(indexes.length).toEqual(2); + done(); + }); }); }); }); - }); - }); + } + ); - it('lets you add multiple indexes', done => { + it("lets you add multiple indexes", done => { request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'POST', + url: "http://localhost:8378/1/schemas/NewClass", + method: "POST", headers: masterKeyHeaders, json: true, body: {}, }).then(() => { request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'PUT', + url: "http://localhost:8378/1/schemas/NewClass", + method: "PUT", headers: masterKeyHeaders, json: true, body: { fields: { - aString: { type: 'String' }, - bString: { type: 'String' }, - cString: { type: 'String' }, - dString: { type: 'String' }, + aString: { type: "String" }, + bString: { type: "String" }, + cString: { type: "String" }, + dString: { type: "String" }, }, indexes: { name1: { aString: 1 }, @@ -3321,16 +3429,16 @@ describe('schemas', () => { }).then(response => { expect( dd(response.data, { - className: 'NewClass', + className: "NewClass", fields: { - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - objectId: { type: 'String' }, - aString: { type: 'String' }, - bString: { type: 'String' }, - cString: { type: 'String' }, - dString: { type: 'String' }, + ACL: { type: "ACL" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + objectId: { type: "String" }, + aString: { type: "String" }, + bString: { type: "String" }, + cString: { type: "String" }, + dString: { type: "String" }, }, classLevelPermissions: defaultClassLevelPermissions, indexes: { @@ -3342,21 +3450,21 @@ describe('schemas', () => { }) ).toEqual(undefined); request({ - url: 'http://localhost:8378/1/schemas/NewClass', + url: "http://localhost:8378/1/schemas/NewClass", headers: masterKeyHeaders, json: true, }).then(response => { expect(response.data).toEqual({ - className: 'NewClass', + className: "NewClass", fields: { - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - objectId: { type: 'String' }, - aString: { type: 'String' }, - bString: { type: 'String' }, - cString: { type: 'String' }, - dString: { type: 'String' }, + ACL: { type: "ACL" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + objectId: { type: "String" }, + aString: { type: "String" }, + bString: { type: "String" }, + cString: { type: "String" }, + dString: { type: "String" }, }, classLevelPermissions: defaultClassLevelPermissions, indexes: { @@ -3366,7 +3474,7 @@ describe('schemas', () => { name3: { cString: 1, dString: 1 }, }, }); - config.database.adapter.getIndexes('NewClass').then(indexes => { + config.database.adapter.getIndexes("NewClass").then(indexes => { expect(indexes.length).toEqual(4); done(); }); @@ -3375,22 +3483,22 @@ describe('schemas', () => { }); }); - it('lets you delete indexes', done => { + it("lets you delete indexes", done => { request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'POST', + url: "http://localhost:8378/1/schemas/NewClass", + method: "POST", headers: masterKeyHeaders, json: true, body: {}, }).then(() => { request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'PUT', + url: "http://localhost:8378/1/schemas/NewClass", + method: "PUT", headers: masterKeyHeaders, json: true, body: { fields: { - aString: { type: 'String' }, + aString: { type: "String" }, }, indexes: { name1: { aString: 1 }, @@ -3399,13 +3507,13 @@ describe('schemas', () => { }).then(response => { expect( dd(response.data, { - className: 'NewClass', + className: "NewClass", fields: { - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - objectId: { type: 'String' }, - aString: { type: 'String' }, + ACL: { type: "ACL" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + objectId: { type: "String" }, + aString: { type: "String" }, }, classLevelPermissions: defaultClassLevelPermissions, indexes: { @@ -3415,31 +3523,31 @@ describe('schemas', () => { }) ).toEqual(undefined); request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'PUT', + url: "http://localhost:8378/1/schemas/NewClass", + method: "PUT", headers: masterKeyHeaders, json: true, body: { indexes: { - name1: { __op: 'Delete' }, + name1: { __op: "Delete" }, }, }, }).then(response => { expect(response.data).toEqual({ - className: 'NewClass', + className: "NewClass", fields: { - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - objectId: { type: 'String' }, - aString: { type: 'String' }, + ACL: { type: "ACL" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + objectId: { type: "String" }, + aString: { type: "String" }, }, classLevelPermissions: defaultClassLevelPermissions, indexes: { _id_: { _id: 1 }, }, }); - config.database.adapter.getIndexes('NewClass').then(indexes => { + config.database.adapter.getIndexes("NewClass").then(indexes => { expect(indexes.length).toEqual(1); done(); }); @@ -3448,24 +3556,24 @@ describe('schemas', () => { }); }); - it('lets you delete multiple indexes', done => { + it("lets you delete multiple indexes", done => { request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'POST', + url: "http://localhost:8378/1/schemas/NewClass", + method: "POST", headers: masterKeyHeaders, json: true, body: {}, }).then(() => { request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'PUT', + url: "http://localhost:8378/1/schemas/NewClass", + method: "PUT", headers: masterKeyHeaders, json: true, body: { fields: { - aString: { type: 'String' }, - bString: { type: 'String' }, - cString: { type: 'String' }, + aString: { type: "String" }, + bString: { type: "String" }, + cString: { type: "String" }, }, indexes: { name1: { aString: 1 }, @@ -3476,15 +3584,15 @@ describe('schemas', () => { }).then(response => { expect( dd(response.data, { - className: 'NewClass', + className: "NewClass", fields: { - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - objectId: { type: 'String' }, - aString: { type: 'String' }, - bString: { type: 'String' }, - cString: { type: 'String' }, + ACL: { type: "ACL" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + objectId: { type: "String" }, + aString: { type: "String" }, + bString: { type: "String" }, + cString: { type: "String" }, }, classLevelPermissions: defaultClassLevelPermissions, indexes: { @@ -3496,27 +3604,27 @@ describe('schemas', () => { }) ).toEqual(undefined); request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'PUT', + url: "http://localhost:8378/1/schemas/NewClass", + method: "PUT", headers: masterKeyHeaders, json: true, body: { indexes: { - name1: { __op: 'Delete' }, - name2: { __op: 'Delete' }, + name1: { __op: "Delete" }, + name2: { __op: "Delete" }, }, }, }).then(response => { expect(response.data).toEqual({ - className: 'NewClass', + className: "NewClass", fields: { - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - objectId: { type: 'String' }, - aString: { type: 'String' }, - bString: { type: 'String' }, - cString: { type: 'String' }, + ACL: { type: "ACL" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + objectId: { type: "String" }, + aString: { type: "String" }, + bString: { type: "String" }, + cString: { type: "String" }, }, classLevelPermissions: defaultClassLevelPermissions, indexes: { @@ -3524,7 +3632,7 @@ describe('schemas', () => { name3: { cString: 1 }, }, }); - config.database.adapter.getIndexes('NewClass').then(indexes => { + config.database.adapter.getIndexes("NewClass").then(indexes => { expect(indexes.length).toEqual(2); done(); }); @@ -3533,29 +3641,29 @@ describe('schemas', () => { }); }); - it('lets you add and delete indexes', async () => { + it("lets you add and delete indexes", async () => { // Wait due to index building in MongoDB on background process with collection lock const waitForIndexBuild = new Promise(r => setTimeout(r, 500)); await request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'POST', + url: "http://localhost:8378/1/schemas/NewClass", + method: "POST", headers: masterKeyHeaders, json: true, body: {}, }); let response = await request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'PUT', + url: "http://localhost:8378/1/schemas/NewClass", + method: "PUT", headers: masterKeyHeaders, json: true, body: { fields: { - aString: { type: 'String' }, - bString: { type: 'String' }, - cString: { type: 'String' }, - dString: { type: 'String' }, + aString: { type: "String" }, + bString: { type: "String" }, + cString: { type: "String" }, + dString: { type: "String" }, }, indexes: { name1: { aString: 1 }, @@ -3567,16 +3675,16 @@ describe('schemas', () => { expect( dd(response.data, { - className: 'NewClass', + className: "NewClass", fields: { - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - objectId: { type: 'String' }, - aString: { type: 'String' }, - bString: { type: 'String' }, - cString: { type: 'String' }, - dString: { type: 'String' }, + ACL: { type: "ACL" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + objectId: { type: "String" }, + aString: { type: "String" }, + bString: { type: "String" }, + cString: { type: "String" }, + dString: { type: "String" }, }, classLevelPermissions: defaultClassLevelPermissions, indexes: { @@ -3590,29 +3698,29 @@ describe('schemas', () => { await waitForIndexBuild; response = await request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'PUT', + url: "http://localhost:8378/1/schemas/NewClass", + method: "PUT", headers: masterKeyHeaders, json: true, body: { indexes: { - name1: { __op: 'Delete' }, - name2: { __op: 'Delete' }, + name1: { __op: "Delete" }, + name2: { __op: "Delete" }, }, }, }); expect(response.data).toEqual({ - className: 'NewClass', + className: "NewClass", fields: { - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - objectId: { type: 'String' }, - aString: { type: 'String' }, - bString: { type: 'String' }, - cString: { type: 'String' }, - dString: { type: 'String' }, + ACL: { type: "ACL" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + objectId: { type: "String" }, + aString: { type: "String" }, + bString: { type: "String" }, + cString: { type: "String" }, + dString: { type: "String" }, }, classLevelPermissions: defaultClassLevelPermissions, indexes: { @@ -3623,8 +3731,8 @@ describe('schemas', () => { await waitForIndexBuild; response = await request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'PUT', + url: "http://localhost:8378/1/schemas/NewClass", + method: "PUT", headers: masterKeyHeaders, json: true, body: { @@ -3635,16 +3743,16 @@ describe('schemas', () => { }); expect(response.data).toEqual({ - className: 'NewClass', + className: "NewClass", fields: { - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - objectId: { type: 'String' }, - aString: { type: 'String' }, - bString: { type: 'String' }, - cString: { type: 'String' }, - dString: { type: 'String' }, + ACL: { type: "ACL" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + objectId: { type: "String" }, + aString: { type: "String" }, + bString: { type: "String" }, + cString: { type: "String" }, + dString: { type: "String" }, }, classLevelPermissions: defaultClassLevelPermissions, indexes: { @@ -3655,52 +3763,54 @@ describe('schemas', () => { }); await waitForIndexBuild; - const indexes = await config.database.adapter.getIndexes('NewClass'); + const indexes = await config.database.adapter.getIndexes("NewClass"); expect(indexes.length).toEqual(3); }); - it('cannot delete index that does not exist', done => { + it("cannot delete index that does not exist", done => { request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'POST', + url: "http://localhost:8378/1/schemas/NewClass", + method: "POST", headers: masterKeyHeaders, json: true, body: {}, }).then(() => { request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'PUT', + url: "http://localhost:8378/1/schemas/NewClass", + method: "PUT", headers: masterKeyHeaders, json: true, body: { indexes: { - unknownIndex: { __op: 'Delete' }, + unknownIndex: { __op: "Delete" }, }, }, }).then(fail, response => { expect(response.data.code).toBe(Parse.Error.INVALID_QUERY); - expect(response.data.error).toBe('Index unknownIndex does not exist, cannot delete.'); + expect(response.data.error).toBe( + "Index unknownIndex does not exist, cannot delete." + ); done(); }); }); }); - it('cannot update index that exist', done => { + it("cannot update index that exist", done => { request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'POST', + url: "http://localhost:8378/1/schemas/NewClass", + method: "POST", headers: masterKeyHeaders, json: true, body: {}, }).then(() => { request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'PUT', + url: "http://localhost:8378/1/schemas/NewClass", + method: "PUT", headers: masterKeyHeaders, json: true, body: { fields: { - aString: { type: 'String' }, + aString: { type: "String" }, }, indexes: { name1: { aString: 1 }, @@ -3708,8 +3818,8 @@ describe('schemas', () => { }, }).then(() => { request({ - url: 'http://localhost:8378/1/schemas/NewClass', - method: 'PUT', + url: "http://localhost:8378/1/schemas/NewClass", + method: "PUT", headers: masterKeyHeaders, json: true, body: { @@ -3719,100 +3829,117 @@ describe('schemas', () => { }, }).then(fail, response => { expect(response.data.code).toBe(Parse.Error.INVALID_QUERY); - expect(response.data.error).toBe('Index name1 exists, cannot update.'); + expect(response.data.error).toBe( + "Index name1 exists, cannot update." + ); done(); }); }); }); }); - it_id('5d0926b2-2d31-459d-a2b1-23ecc32e72a3')(it_exclude_dbs(['postgres']))('get indexes on startup', done => { - const obj = new Parse.Object('TestObject'); - obj - .save() - .then(() => { - return reconfigureServer({ - appId: 'test', - restAPIKey: 'test', - publicServerURL: 'http://localhost:8378/1', - }); - }) - .then(() => { - request({ - url: 'http://localhost:8378/1/schemas/TestObject', - headers: masterKeyHeaders, - json: true, - }).then(response => { - expect(response.data.indexes._id_).toBeDefined(); - done(); + it_id("5d0926b2-2d31-459d-a2b1-23ecc32e72a3")(it_exclude_dbs(["postgres"]))( + "get indexes on startup", + done => { + const obj = new Parse.Object("TestObject"); + obj + .save() + .then(() => { + return reconfigureServer({ + appId: "test", + restAPIKey: "test", + publicServerURL: "http://localhost:8378/1", + }); + }) + .then(() => { + request({ + url: "http://localhost:8378/1/schemas/TestObject", + headers: masterKeyHeaders, + json: true, + }).then(response => { + expect(response.data.indexes._id_).toBeDefined(); + done(); + }); }); - }); - }); + } + ); - it_id('9f2ba51a-6a9c-4b25-9da0-51c82ac65f90')(it_exclude_dbs(['postgres']))('get compound indexes on startup', done => { - const obj = new Parse.Object('TestObject'); - obj.set('subject', 'subject'); - obj.set('comment', 'comment'); - obj - .save() - .then(() => { - return config.database.adapter.createIndex('TestObject', { - subject: 'text', - comment: 'text', - }); - }) - .then(() => { - return reconfigureServer({ - appId: 'test', - restAPIKey: 'test', - publicServerURL: 'http://localhost:8378/1', + it_id("9f2ba51a-6a9c-4b25-9da0-51c82ac65f90")(it_exclude_dbs(["postgres"]))( + "get compound indexes on startup", + done => { + const obj = new Parse.Object("TestObject"); + obj.set("subject", "subject"); + obj.set("comment", "comment"); + obj + .save() + .then(() => { + return config.database.adapter.createIndex("TestObject", { + subject: "text", + comment: "text", + }); + }) + .then(() => { + return reconfigureServer({ + appId: "test", + restAPIKey: "test", + publicServerURL: "http://localhost:8378/1", + }); + }) + .then(() => { + request({ + url: "http://localhost:8378/1/schemas/TestObject", + headers: masterKeyHeaders, + json: true, + }).then(response => { + expect(response.data.indexes._id_).toBeDefined(); + expect(response.data.indexes._id_._id).toEqual(1); + expect( + response.data.indexes.subject_text_comment_text + ).toBeDefined(); + expect( + response.data.indexes.subject_text_comment_text.subject + ).toEqual("text"); + expect( + response.data.indexes.subject_text_comment_text.comment + ).toEqual("text"); + done(); + }); }); - }) - .then(() => { - request({ - url: 'http://localhost:8378/1/schemas/TestObject', - headers: masterKeyHeaders, - json: true, - }).then(response => { - expect(response.data.indexes._id_).toBeDefined(); - expect(response.data.indexes._id_._id).toEqual(1); - expect(response.data.indexes.subject_text_comment_text).toBeDefined(); - expect(response.data.indexes.subject_text_comment_text.subject).toEqual('text'); - expect(response.data.indexes.subject_text_comment_text.comment).toEqual('text'); + } + ); + + it_id("cbd5d897-b938-43a4-8f5a-5d02dd2be9be")(it_exclude_dbs(["postgres"]))( + "cannot update to duplicate value on unique index", + done => { + const index = { + code: 1, + }; + const obj1 = new Parse.Object("UniqueIndexClass"); + obj1.set("code", 1); + const obj2 = new Parse.Object("UniqueIndexClass"); + obj2.set("code", 2); + const adapter = config.database.adapter; + adapter + ._adaptiveCollection("UniqueIndexClass") + .then(collection => { + return collection._ensureSparseUniqueIndexInBackground(index); + }) + .then(() => { + return obj1.save(); + }) + .then(() => { + return obj2.save(); + }) + .then(() => { + obj1.set("code", 2); + return obj1.save(); + }) + .then(done.fail) + .catch(error => { + expect(error.code).toEqual(Parse.Error.DUPLICATE_VALUE); done(); }); - }); - }); - - it_id('cbd5d897-b938-43a4-8f5a-5d02dd2be9be')(it_exclude_dbs(['postgres']))('cannot update to duplicate value on unique index', done => { - const index = { - code: 1, - }; - const obj1 = new Parse.Object('UniqueIndexClass'); - obj1.set('code', 1); - const obj2 = new Parse.Object('UniqueIndexClass'); - obj2.set('code', 2); - const adapter = config.database.adapter; - adapter - ._adaptiveCollection('UniqueIndexClass') - .then(collection => { - return collection._ensureSparseUniqueIndexInBackground(index); - }) - .then(() => { - return obj1.save(); - }) - .then(() => { - return obj2.save(); - }) - .then(() => { - obj1.set('code', 2); - return obj1.save(); - }) - .then(done.fail) - .catch(error => { - expect(error.code).toEqual(Parse.Error.DUPLICATE_VALUE); - done(); - }); - }); + } + ); }); }); diff --git a/spec/support/CurrentSpecReporter.js b/spec/support/CurrentSpecReporter.js index 4f4968fdcb..1ae756568a 100755 --- a/spec/support/CurrentSpecReporter.js +++ b/spec/support/CurrentSpecReporter.js @@ -1,6 +1,6 @@ // Sets a global variable to the current test spec // ex: global.currentSpec.description -const { performance } = require('perf_hooks'); +const { performance } = require("perf_hooks"); global.currentSpec = null; @@ -28,45 +28,51 @@ const duplicates = []; class CurrentSpecReporter { specStarted(spec) { if (timerMap[spec.fullName]) { - console.log('Duplicate spec: ' + spec.fullName); + console.log("Duplicate spec: " + spec.fullName); duplicates.push(spec.fullName); } timerMap[spec.fullName] = performance.now(); global.currentSpec = spec; } specDone(result) { - if (result.status === 'excluded') { + if (result.status === "excluded") { delete timerMap[result.fullName]; return; } - timerMap[result.fullName] = (performance.now() - timerMap[result.fullName]) / 1000; + timerMap[result.fullName] = + (performance.now() - timerMap[result.fullName]) / 1000; global.currentSpec = null; } } -global.displayTestStats = function() { - const times = Object.values(timerMap).sort((a,b) => b - a).filter(time => time >= slowTestLimit); +global.displayTestStats = function () { + const times = Object.values(timerMap) + .sort((a, b) => b - a) + .filter(time => time >= slowTestLimit); if (times.length > 0) { console.log(`Slow tests with execution time >=${slowTestLimit}s:`); } - times.forEach((time) => { - console.warn(`${time.toFixed(1)}s:`, Object.keys(timerMap).find(key => timerMap[key] === time)); + times.forEach(time => { + console.warn( + `${time.toFixed(1)}s:`, + Object.keys(timerMap).find(key => timerMap[key] === time) + ); }); - console.log('\n'); - duplicates.forEach((spec) => { - console.warn('Duplicate spec: ' + spec); + console.log("\n"); + duplicates.forEach(spec => { + console.warn("Duplicate spec: " + spec); }); - console.log('\n'); - Object.keys(retryMap).forEach((spec) => { + console.log("\n"); + Object.keys(retryMap).forEach(spec => { console.warn(`Flaky test: ${spec} failed ${retryMap[spec]} times`); }); - console.log('\n'); + console.log("\n"); }; -global.retryFlakyTests = function() { +global.retryFlakyTests = function () { const originalSpecConstructor = jasmine.Spec; - jasmine.Spec = function(attrs) { + jasmine.Spec = function (attrs) { const spec = new originalSpecConstructor(attrs); const originalTestFn = spec.queueableFn.fn; const runOriginalTest = () => { @@ -75,12 +81,12 @@ global.retryFlakyTests = function() { return originalTestFn(); } else { // handle done() callback - return new Promise((resolve) => { + return new Promise(resolve => { originalTestFn(resolve); }); } }; - spec.queueableFn.fn = async function() { + spec.queueableFn.fn = async function () { const isFlaky = flakyTests.includes(spec.result.fullName); const runs = isFlaky ? retries : 1; let exceptionCaught; @@ -95,13 +101,15 @@ global.retryFlakyTests = function() { } catch (exception) { exceptionCaught = exception; } - const failed = !spec.markedPending && - (exceptionCaught || spec.result.failedExpectations.length != 0); + const failed = + !spec.markedPending && + (exceptionCaught || spec.result.failedExpectations.length != 0); if (!failed) { break; } if (isFlaky) { - retryMap[spec.result.fullName] = (retryMap[spec.result.fullName] || 0) + 1; + retryMap[spec.result.fullName] = + (retryMap[spec.result.fullName] || 0) + 1; await global.afterEachFn(); } } @@ -112,6 +120,6 @@ global.retryFlakyTests = function() { }; return spec; }; -} +}; -module.exports = CurrentSpecReporter; \ No newline at end of file +module.exports = CurrentSpecReporter; diff --git a/spec/support/CustomAuth.js b/spec/support/CustomAuth.js index f6698e5b03..80a527803e 100644 --- a/spec/support/CustomAuth.js +++ b/spec/support/CustomAuth.js @@ -3,7 +3,7 @@ module.exports = { return Promise.resolve(); }, validateAuthData: function (authData) { - if (authData.token == 'my-token') { + if (authData.token == "my-token") { return Promise.resolve(); } return Promise.reject(); diff --git a/spec/support/CustomMiddleware.js b/spec/support/CustomMiddleware.js index 97e71bd67b..bfef31c586 100644 --- a/spec/support/CustomMiddleware.js +++ b/spec/support/CustomMiddleware.js @@ -1,4 +1,4 @@ module.exports = function (req, res, next) { - res.set('X-Yolo', '1'); + res.set("X-Yolo", "1"); next(); }; diff --git a/spec/support/FailingServer.js b/spec/support/FailingServer.js index 60112ae82c..579644468d 100755 --- a/spec/support/FailingServer.js +++ b/spec/support/FailingServer.js @@ -1,16 +1,20 @@ #!/usr/bin/env node -const MongoStorageAdapter = require('../../lib/Adapters/Storage/Mongo/MongoStorageAdapter').default; -const { GridFSBucketAdapter } = require('../../lib/Adapters/Files/GridFSBucketAdapter'); +const MongoStorageAdapter = + require("../../lib/Adapters/Storage/Mongo/MongoStorageAdapter").default; +const { + GridFSBucketAdapter, +} = require("../../lib/Adapters/Files/GridFSBucketAdapter"); -const ParseServer = require('../../lib/index').ParseServer; +const ParseServer = require("../../lib/index").ParseServer; -const databaseURI = 'mongodb://doesnotexist:27017/parseServerMongoAdapterTestDatabase'; +const databaseURI = + "mongodb://doesnotexist:27017/parseServerMongoAdapterTestDatabase"; (async () => { try { await ParseServer.startApp({ - appId: 'test', - masterKey: 'test', + appId: "test", + masterKey: "test", databaseAdapter: new MongoStorageAdapter({ uri: databaseURI, mongoOptions: { diff --git a/spec/support/MockEmailAdapterWithOptions.js b/spec/support/MockEmailAdapterWithOptions.js index 71d23892ef..0d24d91b28 100644 --- a/spec/support/MockEmailAdapterWithOptions.js +++ b/spec/support/MockEmailAdapterWithOptions.js @@ -1,6 +1,6 @@ module.exports = options => { if (!options) { - throw 'Options were not provided'; + throw "Options were not provided"; } const adapter = { sendVerificationEmail: () => Promise.resolve(), diff --git a/spec/support/MockLdapServer.js b/spec/support/MockLdapServer.js index 270a9603cc..05924ce8e9 100644 --- a/spec/support/MockLdapServer.js +++ b/spec/support/MockLdapServer.js @@ -1,23 +1,23 @@ -const ldapjs = require('ldapjs'); -const fs = require('fs'); +const ldapjs = require("ldapjs"); +const fs = require("fs"); const tlsOptions = { - key: fs.readFileSync(__dirname + '/cert/key.pem'), - certificate: fs.readFileSync(__dirname + '/cert/cert.pem'), + key: fs.readFileSync(__dirname + "/cert/key.pem"), + certificate: fs.readFileSync(__dirname + "/cert/cert.pem"), }; function newServer(port, dn, provokeSearchError = false, ssl = false) { const server = ssl ? ldapjs.createServer(tlsOptions) : ldapjs.createServer(); - server.bind('o=example', function (req, res, next) { - if (req.dn.toString() !== dn || req.credentials !== 'secret') { + server.bind("o=example", function (req, res, next) { + if (req.dn.toString() !== dn || req.credentials !== "secret") { return next(new ldapjs.InvalidCredentialsError()); } res.end(); return next(); }); - server.search('o=example', function (req, res, next) { + server.search("o=example", function (req, res, next) { if (provokeSearchError) { res.end(ldapjs.LDAP_SIZE_LIMIT_EXCEEDED); return next(); @@ -25,18 +25,18 @@ function newServer(port, dn, provokeSearchError = false, ssl = false) { const obj = { dn: req.dn.toString(), attributes: { - objectclass: ['organization', 'top'], - o: 'example', + objectclass: ["organization", "top"], + o: "example", }, }; const group = { dn: req.dn.toString(), attributes: { - objectClass: ['groupOfUniqueNames', 'top'], - uniqueMember: ['uid=testuser, o=example'], - cn: 'powerusers', - ou: 'powerusers', + objectClass: ["groupOfUniqueNames", "top"], + uniqueMember: ["uid=testuser, o=example"], + cn: "powerusers", + ou: "powerusers", }, }; diff --git a/spec/support/dev.js b/spec/support/dev.js index 3415387c14..a70c757cf9 100644 --- a/spec/support/dev.js +++ b/spec/support/dev.js @@ -1,8 +1,8 @@ -const Config = require('../../lib/Config'); -const Parse = require('parse/node'); +const Config = require("../../lib/Config"); +const Parse = require("parse/node"); -const className = 'AnObject'; -const defaultRoleName = 'tester'; +const className = "AnObject"; +const defaultRoleName = "tester"; module.exports = { /* AnObject */ @@ -15,7 +15,7 @@ module.exports = { * @param {string} username - username base, will be postfixed with current time in millis; * @param {string} [password='password'] - optional, defaults to "password" if not set; */ - createUser: async (username, password = 'password') => { + createUser: async (username, password = "password") => { const user = new Parse.User({ username: username + Date.now(), password, @@ -32,7 +32,10 @@ module.exports = { * @param {string} [password='password'] - optional, defaults to "password" if not set; */ logIn: async (userObject, password) => { - return await Parse.User.logIn(userObject.getUsername(), password || 'password'); + return await Parse.User.logIn( + userObject.getUsername(), + password || "password" + ); }, /** @@ -70,19 +73,19 @@ module.exports = { acl.setPublicReadAccess(read); acl.setPublicWriteAccess(write); - const role = new Parse.Object('_Role'); + const role = new Parse.Object("_Role"); role.setACL(acl); // generate name based on roleName or use exactName (if botth not provided name is generated) const name = roleName ? roleName + Date.now() : exactName; - role.set('name', name); + role.set("name", name); if (roles) { - role.relation('roles').add(roles); + role.relation("roles").add(roles); } if (users) { - role.relation('users').add(users); + role.relation("users").add(users); } await role.save({ useMasterKey: true }); diff --git a/spec/support/myoauth.js b/spec/support/myoauth.js index 2367ad62ce..b14e9db385 100644 --- a/spec/support/myoauth.js +++ b/spec/support/myoauth.js @@ -2,7 +2,7 @@ // Returns a promise that fulfills iff this user id is valid. function validateAuthData(authData) { - if (authData.id == '12345' && authData.access_token == '12345') { + if (authData.id == "12345" && authData.access_token == "12345") { return Promise.resolve(); } return Promise.reject(); diff --git a/spec/vulnerabilities.spec.js b/spec/vulnerabilities.spec.js index 858245ecc4..1b8c6ae7d7 100644 --- a/spec/vulnerabilities.spec.js +++ b/spec/vulnerabilities.spec.js @@ -1,7 +1,7 @@ -const request = require('../lib/request'); +const request = require("../lib/request"); -describe('Vulnerabilities', () => { - describe('(GHSA-8xq9-g7ch-35hg) Custom object ID allows to acquire role privilege', () => { +describe("Vulnerabilities", () => { + describe("(GHSA-8xq9-g7ch-35hg) Custom object ID allows to acquire role privilege", () => { beforeAll(async () => { await reconfigureServer({ allowCustomObjectId: true }); Parse.allowCustomObjectId = true; @@ -12,13 +12,15 @@ describe('Vulnerabilities', () => { Parse.allowCustomObjectId = false; }); - it('denies user creation with poisoned object ID', async () => { + it("denies user creation with poisoned object ID", async () => { await expectAsync( - new Parse.User({ id: 'role:a', username: 'a', password: '123' }).save() - ).toBeRejectedWith(new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Invalid object ID.')); + new Parse.User({ id: "role:a", username: "a", password: "123" }).save() + ).toBeRejectedWith( + new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, "Invalid object ID.") + ); }); - describe('existing sessions for users with poisoned object ID', () => { + describe("existing sessions for users with poisoned object ID", () => { /** @type {Parse.User} */ let poisonedUser; /** @type {Parse.User} */ @@ -28,37 +30,44 @@ describe('Vulnerabilities', () => { const parseServer = await global.reconfigureServer(); const databaseController = parseServer.config.databaseController; [poisonedUser, innocentUser] = await Promise.all( - ['role:abc', 'abc'].map(async id => { + ["role:abc", "abc"].map(async id => { // Create the users directly on the db to bypass the user creation check - await databaseController.create('_User', { objectId: id }); + await databaseController.create("_User", { objectId: id }); // Use the master key to create a session for them to bypass the session check return Parse.User.loginAs(id); }) ); }); - it('refuses session token of user with poisoned object ID', async () => { + it("refuses session token of user with poisoned object ID", async () => { await expectAsync( - new Parse.Query(Parse.User).find({ sessionToken: poisonedUser.getSessionToken() }) + new Parse.Query(Parse.User).find({ + sessionToken: poisonedUser.getSessionToken(), + }) ).toBeRejectedWith( - new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Invalid object ID.') + new Parse.Error( + Parse.Error.INTERNAL_SERVER_ERROR, + "Invalid object ID." + ) ); - await new Parse.Query(Parse.User).find({ sessionToken: innocentUser.getSessionToken() }); + await new Parse.Query(Parse.User).find({ + sessionToken: innocentUser.getSessionToken(), + }); }); }); }); - describe('Object prototype pollution', () => { + describe("Object prototype pollution", () => { it('denies object prototype to be polluted with keyword "constructor"', async () => { const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; const response = await request({ headers: headers, - method: 'POST', - url: 'http://localhost:8378/1/classes/PP', + method: "POST", + url: "http://localhost:8378/1/classes/PP", body: JSON.stringify({ obj: { constructor: { @@ -72,31 +81,33 @@ describe('Vulnerabilities', () => { expect(response.status).toBe(400); const text = JSON.parse(response.text); expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME); - expect(text.error).toBe('Prohibited keyword in request data: {"key":"constructor"}.'); + expect(text.error).toBe( + 'Prohibited keyword in request data: {"key":"constructor"}.' + ); expect(Object.prototype.dummy).toBeUndefined(); }); it('denies object prototype to be polluted with keypath string "constructor"', async () => { const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; const objResponse = await request({ headers: headers, - method: 'POST', - url: 'http://localhost:8378/1/classes/PP', + method: "POST", + url: "http://localhost:8378/1/classes/PP", body: JSON.stringify({ obj: {}, }), }).catch(e => e); const pollResponse = await request({ headers: headers, - method: 'PUT', + method: "PUT", url: `http://localhost:8378/1/classes/PP/${objResponse.data.objectId}`, body: JSON.stringify({ - 'obj.constructor.prototype.dummy': { - __op: 'Increment', + "obj.constructor.prototype.dummy": { + __op: "Increment", amount: 1, }, }), @@ -105,45 +116,49 @@ describe('Vulnerabilities', () => { expect(pollResponse.status).toBe(400); const text = JSON.parse(pollResponse.text); expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME); - expect(text.error).toBe('Prohibited keyword in request data: {"key":"constructor"}.'); + expect(text.error).toBe( + 'Prohibited keyword in request data: {"key":"constructor"}.' + ); expect(Object.prototype.dummy).toBeUndefined(); }); it('denies object prototype to be polluted with keyword "__proto__"', async () => { const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; const response = await request({ headers: headers, - method: 'POST', - url: 'http://localhost:8378/1/classes/PP', - body: JSON.stringify({ 'obj.__proto__.dummy': 0 }), + method: "POST", + url: "http://localhost:8378/1/classes/PP", + body: JSON.stringify({ "obj.__proto__.dummy": 0 }), }).catch(e => e); expect(response.status).toBe(400); const text = JSON.parse(response.text); expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME); - expect(text.error).toBe('Prohibited keyword in request data: {"key":"__proto__"}.'); + expect(text.error).toBe( + 'Prohibited keyword in request data: {"key":"__proto__"}.' + ); expect(Object.prototype.dummy).toBeUndefined(); }); }); - describe('Request denylist', () => { - it('denies BSON type code data in write request by default', async () => { + describe("Request denylist", () => { + it("denies BSON type code data in write request by default", async () => { const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; const params = { headers: headers, - method: 'POST', - url: 'http://localhost:8378/1/classes/RCE', + method: "POST", + url: "http://localhost:8378/1/classes/RCE", body: JSON.stringify({ obj: { - _bsontype: 'Code', - code: 'delete Object.prototype.evalFunctions', + _bsontype: "Code", + code: "delete Object.prototype.evalFunctions", }, }), }; @@ -156,20 +171,23 @@ describe('Vulnerabilities', () => { ); }); - it('denies expanding existing object with polluted keys', async () => { - const obj = await new Parse.Object('RCE', { a: { foo: [] } }).save(); + it("denies expanding existing object with polluted keys", async () => { + const obj = await new Parse.Object("RCE", { a: { foo: [] } }).save(); await reconfigureServer({ - requestKeywordDenylist: ['foo'], + requestKeywordDenylist: ["foo"], }); - obj.addUnique('a.foo', 'abc'); + obj.addUnique("a.foo", "abc"); await expectAsync(obj.save()).toBeRejectedWith( - new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Prohibited keyword in request data: "foo".`) + new Parse.Error( + Parse.Error.INVALID_KEY_NAME, + `Prohibited keyword in request data: "foo".` + ) ); }); - it('denies creating a cloud trigger with polluted data', async () => { - Parse.Cloud.beforeSave('TestObject', ({ object }) => { - object.set('obj', { + it("denies creating a cloud trigger with polluted data", async () => { + Parse.Cloud.beforeSave("TestObject", ({ object }) => { + object.set("obj", { constructor: { prototype: { dummy: 0, @@ -177,7 +195,7 @@ describe('Vulnerabilities', () => { }, }); }); - await expectAsync(new Parse.Object('TestObject').save()).toBeRejectedWith( + await expectAsync(new Parse.Object("TestObject").save()).toBeRejectedWith( new Parse.Error( Parse.Error.INVALID_KEY_NAME, 'Prohibited keyword in request data: {"key":"constructor"}.' @@ -185,20 +203,20 @@ describe('Vulnerabilities', () => { ); }); - it('denies creating global config with polluted data', async () => { + it("denies creating global config with polluted data", async () => { const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key': 'test', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-Master-Key": "test", }; const params = { - method: 'PUT', - url: 'http://localhost:8378/1/config', + method: "PUT", + url: "http://localhost:8378/1/config", json: true, body: { params: { - welcomeMesssage: 'Welcome to Parse', - foo: { _bsontype: 'Code', code: 'shell' }, + welcomeMesssage: "Welcome to Parse", + foo: { _bsontype: "Code", code: "shell" }, }, }, headers, @@ -212,17 +230,17 @@ describe('Vulnerabilities', () => { ); }); - it('denies direct database write wih prohibited keys', async () => { - const Config = require('../lib/Config'); + it("denies direct database write wih prohibited keys", async () => { + const Config = require("../lib/Config"); const config = Config.get(Parse.applicationId); const user = { - objectId: '1234567890', - username: 'hello', - password: 'pass', - _session_token: 'abc', - foo: { _bsontype: 'Code', code: 'shell' }, + objectId: "1234567890", + username: "hello", + password: "pass", + _session_token: "abc", + foo: { _bsontype: "Code", code: "shell" }, }; - await expectAsync(config.database.create('_User', user)).toBeRejectedWith( + await expectAsync(config.database.create("_User", user)).toBeRejectedWith( new Parse.Error( Parse.Error.INVALID_KEY_NAME, 'Prohibited keyword in request data: {"key":"_bsontype","value":"Code"}.' @@ -230,18 +248,18 @@ describe('Vulnerabilities', () => { ); }); - it('denies direct database update wih prohibited keys', async () => { - const Config = require('../lib/Config'); + it("denies direct database update wih prohibited keys", async () => { + const Config = require("../lib/Config"); const config = Config.get(Parse.applicationId); const user = { - objectId: '1234567890', - username: 'hello', - password: 'pass', - _session_token: 'abc', - foo: { _bsontype: 'Code', code: 'shell' }, + objectId: "1234567890", + username: "hello", + password: "pass", + _session_token: "abc", + foo: { _bsontype: "Code", code: "shell" }, }; await expectAsync( - config.database.update('_User', { _id: user.objectId }, user) + config.database.update("_User", { _id: user.objectId }, user) ).toBeRejectedWith( new Parse.Error( Parse.Error.INVALID_KEY_NAME, @@ -250,21 +268,21 @@ describe('Vulnerabilities', () => { ); }); - it_id('e8b5f1e1-8326-4c70-b5f4-1e8678dfff8d')(it)( - 'denies creating a hook with polluted data', + it_id("e8b5f1e1-8326-4c70-b5f4-1e8678dfff8d")(it)( + "denies creating a hook with polluted data", async () => { - const express = require('express'); + const express = require("express"); const port = 34567; - const hookServerURL = 'http://localhost:' + port; + const hookServerURL = "http://localhost:" + port; const app = express(); - app.use(express.json({ type: '*/*' })); + app.use(express.json({ type: "*/*" })); const server = await new Promise(resolve => { const res = app.listen(port, undefined, () => resolve(res)); }); - app.post('/BeforeSave', function (req, res) { + app.post("/BeforeSave", function (req, res) { const object = Parse.Object.fromJSON(req.body.object); - object.set('hello', 'world'); - object.set('obj', { + object.set("hello", "world"); + object.set("obj", { constructor: { prototype: { dummy: 0, @@ -273,8 +291,14 @@ describe('Vulnerabilities', () => { }); res.json({ success: object }); }); - await Parse.Hooks.createTrigger('TestObject', 'beforeSave', hookServerURL + '/BeforeSave'); - await expectAsync(new Parse.Object('TestObject').save()).toBeRejectedWith( + await Parse.Hooks.createTrigger( + "TestObject", + "beforeSave", + hookServerURL + "/BeforeSave" + ); + await expectAsync( + new Parse.Object("TestObject").save() + ).toBeRejectedWith( new Parse.Error( Parse.Error.INVALID_KEY_NAME, 'Prohibited keyword in request data: {"key":"constructor"}.' @@ -284,23 +308,23 @@ describe('Vulnerabilities', () => { } ); - it('denies write request with custom denylist of key/value', async () => { + it("denies write request with custom denylist of key/value", async () => { await reconfigureServer({ - requestKeywordDenylist: [{ key: 'a[K]ey', value: 'aValue[123]*' }], + requestKeywordDenylist: [{ key: "a[K]ey", value: "aValue[123]*" }], }); const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; const params = { headers: headers, - method: 'POST', - url: 'http://localhost:8378/1/classes/RCE', + method: "POST", + url: "http://localhost:8378/1/classes/RCE", body: JSON.stringify({ obj: { - aKey: 'aValue321', - code: 'delete Object.prototype.evalFunctions', + aKey: "aValue321", + code: "delete Object.prototype.evalFunctions", }, }), }; @@ -313,24 +337,24 @@ describe('Vulnerabilities', () => { ); }); - it('denies write request with custom denylist of nested key/value', async () => { + it("denies write request with custom denylist of nested key/value", async () => { await reconfigureServer({ - requestKeywordDenylist: [{ key: 'a[K]ey', value: 'aValue[123]*' }], + requestKeywordDenylist: [{ key: "a[K]ey", value: "aValue[123]*" }], }); const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; const params = { headers: headers, - method: 'POST', - url: 'http://localhost:8378/1/classes/RCE', + method: "POST", + url: "http://localhost:8378/1/classes/RCE", body: JSON.stringify({ obj: { nested: { - aKey: 'aValue321', - code: 'delete Object.prototype.evalFunctions', + aKey: "aValue321", + code: "delete Object.prototype.evalFunctions", }, }, }), @@ -344,24 +368,24 @@ describe('Vulnerabilities', () => { ); }); - it('denies write request with custom denylist of key/value in array', async () => { + it("denies write request with custom denylist of key/value in array", async () => { await reconfigureServer({ - requestKeywordDenylist: [{ key: 'a[K]ey', value: 'aValue[123]*' }], + requestKeywordDenylist: [{ key: "a[K]ey", value: "aValue[123]*" }], }); const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; const params = { headers: headers, - method: 'POST', - url: 'http://localhost:8378/1/classes/RCE', + method: "POST", + url: "http://localhost:8378/1/classes/RCE", body: JSON.stringify({ obj: [ { - aKey: 'aValue321', - code: 'delete Object.prototype.evalFunctions', + aKey: "aValue321", + code: "delete Object.prototype.evalFunctions", }, ], }), @@ -375,23 +399,23 @@ describe('Vulnerabilities', () => { ); }); - it('denies write request with custom denylist of key', async () => { + it("denies write request with custom denylist of key", async () => { await reconfigureServer({ - requestKeywordDenylist: [{ key: 'a[K]ey' }], + requestKeywordDenylist: [{ key: "a[K]ey" }], }); const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; const params = { headers: headers, - method: 'POST', - url: 'http://localhost:8378/1/classes/RCE', + method: "POST", + url: "http://localhost:8378/1/classes/RCE", body: JSON.stringify({ obj: { - aKey: 'aValue321', - code: 'delete Object.prototype.evalFunctions', + aKey: "aValue321", + code: "delete Object.prototype.evalFunctions", }, }), }; @@ -399,26 +423,28 @@ describe('Vulnerabilities', () => { expect(response.status).toBe(400); const text = JSON.parse(response.text); expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME); - expect(text.error).toBe('Prohibited keyword in request data: {"key":"a[K]ey"}.'); + expect(text.error).toBe( + 'Prohibited keyword in request data: {"key":"a[K]ey"}.' + ); }); - it('denies write request with custom denylist of value', async () => { + it("denies write request with custom denylist of value", async () => { await reconfigureServer({ - requestKeywordDenylist: [{ value: 'aValue[123]*' }], + requestKeywordDenylist: [{ value: "aValue[123]*" }], }); const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }; const params = { headers: headers, - method: 'POST', - url: 'http://localhost:8378/1/classes/RCE', + method: "POST", + url: "http://localhost:8378/1/classes/RCE", body: JSON.stringify({ obj: { - aKey: 'aValue321', - code: 'delete Object.prototype.evalFunctions', + aKey: "aValue321", + code: "delete Object.prototype.evalFunctions", }, }), }; @@ -426,19 +452,21 @@ describe('Vulnerabilities', () => { expect(response.status).toBe(400); const text = JSON.parse(response.text); expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME); - expect(text.error).toBe('Prohibited keyword in request data: {"value":"aValue[123]*"}.'); + expect(text.error).toBe( + 'Prohibited keyword in request data: {"value":"aValue[123]*"}.' + ); }); - it('denies BSON type code data in file metadata', async () => { - const str = 'Hello World!'; + it("denies BSON type code data in file metadata", async () => { + const str = "Hello World!"; const data = []; for (let i = 0; i < str.length; i++) { data.push(str.charCodeAt(i)); } - const file = new Parse.File('hello.txt', data, 'text/plain'); - file.addMetadata('obj', { - _bsontype: 'Code', - code: 'delete Object.prototype.evalFunctions', + const file = new Parse.File("hello.txt", data, "text/plain"); + file.addMetadata("obj", { + _bsontype: "Code", + code: "delete Object.prototype.evalFunctions", }); await expectAsync(file.save()).toBeRejectedWith( new Parse.Error( @@ -448,16 +476,16 @@ describe('Vulnerabilities', () => { ); }); - it('denies BSON type code data in file tags', async () => { - const str = 'Hello World!'; + it("denies BSON type code data in file tags", async () => { + const str = "Hello World!"; const data = []; for (let i = 0; i < str.length; i++) { data.push(str.charCodeAt(i)); } - const file = new Parse.File('hello.txt', data, 'text/plain'); - file.addTag('obj', { - _bsontype: 'Code', - code: 'delete Object.prototype.evalFunctions', + const file = new Parse.File("hello.txt", data, "text/plain"); + file.addTag("obj", { + _bsontype: "Code", + code: "delete Object.prototype.evalFunctions", }); await expectAsync(file.save()).toBeRejectedWith( new Parse.Error( @@ -468,36 +496,36 @@ describe('Vulnerabilities', () => { }); }); - describe('Ignore non-matches', () => { - it('ignores write request that contains only fraction of denied keyword', async () => { + describe("Ignore non-matches", () => { + it("ignores write request that contains only fraction of denied keyword", async () => { await reconfigureServer({ - requestKeywordDenylist: [{ key: 'abc' }], + requestKeywordDenylist: [{ key: "abc" }], }); // Initially saving an object executes the keyword detection in RestWrite.js const obj = new TestObject({ a: { b: { c: 0 } } }); await expectAsync(obj.save()).toBeResolved(); // Modifying a nested key executes the keyword detection in DatabaseController.js - obj.increment('a.b.c'); + obj.increment("a.b.c"); await expectAsync(obj.save()).toBeResolved(); }); }); }); -describe('Postgres regex sanitizater', () => { - it('sanitizes the regex correctly to prevent Injection', async () => { +describe("Postgres regex sanitizater", () => { + it("sanitizes the regex correctly to prevent Injection", async () => { const user = new Parse.User(); - user.set('username', 'username'); - user.set('password', 'password'); - user.set('email', 'email@example.com'); + user.set("username", "username"); + user.set("password", "password"); + user.set("email", "email@example.com"); await user.signUp(); const response = await request({ - method: 'GET', + method: "GET", url: "http://localhost:8378/1/classes/_User?where[username][$regex]=A'B'%3BSELECT+PG_SLEEP(3)%3B--", headers: { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', + "Content-Type": "application/json", + "X-Parse-Application-Id": "test", + "X-Parse-REST-API-Key": "rest", }, }); diff --git a/src/AccountLockout.js b/src/AccountLockout.js index 13d655e6b7..4181a2cc05 100644 --- a/src/AccountLockout.js +++ b/src/AccountLockout.js @@ -1,5 +1,5 @@ // This class handles the Account Lockout Policy settings. -import Parse from 'parse/node'; +import Parse from "parse/node"; export class AccountLockout { constructor(user, config) { @@ -19,7 +19,7 @@ export class AccountLockout { _failed_login_count: value, }; - return this._config.database.update('_User', query, updateFields); + return this._config.database.update("_User", query, updateFields); } /** @@ -31,7 +31,7 @@ export class AccountLockout { _failed_login_count: { $exists: true }, }; - return this._config.database.find('_User', query).then(users => { + return this._config.database.find("_User", query).then(users => { if (Array.isArray(users) && users.length > 0) { return true; } else { @@ -61,10 +61,10 @@ export class AccountLockout { }; const updateFields = { - _failed_login_count: { __op: 'Increment', amount: 1 }, + _failed_login_count: { __op: "Increment", amount: 1 }, }; - return this._config.database.update('_User', query, updateFields); + return this._config.database.update("_User", query, updateFields); } /** @@ -82,23 +82,27 @@ export class AccountLockout { const updateFields = { _account_lockout_expires_at: Parse._encode( - new Date(now.getTime() + this._config.accountLockout.duration * 60 * 1000) + new Date( + now.getTime() + this._config.accountLockout.duration * 60 * 1000 + ) ), }; - return this._config.database.update('_User', query, updateFields).catch(err => { - if ( - err && - err.code && - err.message && - err.code === Parse.Error.OBJECT_NOT_FOUND && - err.message === 'Object not found.' - ) { - return; // nothing to update so we are good - } else { - throw err; // unknown error - } - }); + return this._config.database + .update("_User", query, updateFields) + .catch(err => { + if ( + err && + err.code && + err.message && + err.code === Parse.Error.OBJECT_NOT_FOUND && + err.message === "Object not found." + ) { + return; // nothing to update so we are good + } else { + throw err; // unknown error + } + }); } /** @@ -114,13 +118,13 @@ export class AccountLockout { _failed_login_count: { $gte: this._config.accountLockout.threshold }, }; - return this._config.database.find('_User', query).then(users => { + return this._config.database.find("_User", query).then(users => { if (Array.isArray(users) && users.length > 0) { throw new Parse.Error( Parse.Error.OBJECT_NOT_FOUND, - 'Your account is locked due to multiple failed login attempts. Please try again after ' + + "Your account is locked due to multiple failed login attempts. Please try again after " + this._config.accountLockout.duration + - ' minute(s)' + " minute(s)" ); } }); @@ -163,15 +167,18 @@ export class AccountLockout { * Removes the account lockout. */ unlockAccount() { - if (!this._config.accountLockout || !this._config.accountLockout.unlockOnPasswordReset) { + if ( + !this._config.accountLockout || + !this._config.accountLockout.unlockOnPasswordReset + ) { return Promise.resolve(); } return this._config.database.update( - '_User', + "_User", { username: this._user.username }, { - _failed_login_count: { __op: 'Delete' }, - _account_lockout_expires_at: { __op: 'Delete' }, + _failed_login_count: { __op: "Delete" }, + _account_lockout_expires_at: { __op: "Delete" }, } ); } diff --git a/src/Auth.js b/src/Auth.js index a93f49051f..ca4f68a6a3 100644 --- a/src/Auth.js +++ b/src/Auth.js @@ -1,10 +1,10 @@ -const Parse = require('parse/node'); -import { isDeepStrictEqual } from 'util'; -import { getRequestObject, resolveError } from './triggers'; -import { logger } from './logger'; -import { LRUCache as LRU } from 'lru-cache'; -import RestQuery from './RestQuery'; -import RestWrite from './RestWrite'; +const Parse = require("parse/node"); +import { isDeepStrictEqual } from "util"; +import { getRequestObject, resolveError } from "./triggers"; +import { logger } from "./logger"; +import { LRUCache as LRU } from "lru-cache"; +import RestQuery from "./RestQuery"; +import RestWrite from "./RestWrite"; // An Auth object tells you who is requesting something and whether // the master key was used. @@ -98,7 +98,7 @@ const renewSessionIfNeeded = async ({ config, session, sessionToken }) => { config, auth: master(config), runBeforeFind: false, - className: '_Session', + className: "_Session", restWhere: { sessionToken }, restOptions: { limit: 1 }, }); @@ -113,13 +113,13 @@ const renewSessionIfNeeded = async ({ config, session, sessionToken }) => { await new RestWrite( config, master(config), - '_Session', + "_Session", { objectId: session.objectId }, { expiresAt: Parse._encode(expiresAt) } ).execute(); } catch (e) { if (e?.code !== Parse.Error.OBJECT_NOT_FOUND) { - logger.error('Could not update session expiry: ', e); + logger.error("Could not update session expiry: ", e); } } }; @@ -153,15 +153,15 @@ const getAuthForSessionToken = async function ({ if (config) { const restOptions = { limit: 1, - include: 'user', + include: "user", }; - const RestQuery = require('./RestQuery'); + const RestQuery = require("./RestQuery"); const query = await RestQuery({ method: RestQuery.Method.get, config, runBeforeFind: false, auth: master(config), - className: '_Session', + className: "_Session", restWhere: { sessionToken }, restOptions, }); @@ -170,30 +170,42 @@ const getAuthForSessionToken = async function ({ results = ( await new Parse.Query(Parse.Session) .limit(1) - .include('user') - .equalTo('sessionToken', sessionToken) + .include("user") + .equalTo("sessionToken", sessionToken) .find({ useMasterKey: true }) ).map(obj => obj.toJSON()); } - if (results.length !== 1 || !results[0]['user']) { - throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid session token'); + if (results.length !== 1 || !results[0]["user"]) { + throw new Parse.Error( + Parse.Error.INVALID_SESSION_TOKEN, + "Invalid session token" + ); } const session = results[0]; const now = new Date(), expiresAt = session.expiresAt ? new Date(session.expiresAt.iso) : undefined; if (expiresAt < now) { - throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Session token is expired.'); + throw new Parse.Error( + Parse.Error.INVALID_SESSION_TOKEN, + "Session token is expired." + ); } const obj = session.user; - if (typeof obj['objectId'] === 'string' && obj['objectId'].startsWith('role:')) { - throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Invalid object ID.'); + if ( + typeof obj["objectId"] === "string" && + obj["objectId"].startsWith("role:") + ) { + throw new Parse.Error( + Parse.Error.INTERNAL_SERVER_ERROR, + "Invalid object ID." + ); } delete obj.password; - obj['className'] = '_User'; - obj['sessionToken'] = sessionToken; + obj["className"] = "_User"; + obj["sessionToken"] = sessionToken; if (cacheController) { cacheController.user.put(sessionToken, obj); } @@ -208,27 +220,34 @@ const getAuthForSessionToken = async function ({ }); }; -var getAuthForLegacySessionToken = async function ({ config, sessionToken, installationId }) { +var getAuthForLegacySessionToken = async function ({ + config, + sessionToken, + installationId, +}) { var restOptions = { limit: 1, }; - const RestQuery = require('./RestQuery'); + const RestQuery = require("./RestQuery"); var query = await RestQuery({ method: RestQuery.Method.get, config, runBeforeFind: false, auth: master(config), - className: '_User', + className: "_User", restWhere: { _session_token: sessionToken }, restOptions, }); return query.execute().then(response => { var results = response.results; if (results.length !== 1) { - throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'invalid legacy session token'); + throw new Parse.Error( + Parse.Error.INVALID_SESSION_TOKEN, + "invalid legacy session token" + ); } const obj = results[0]; - obj.className = '_User'; + obj.className = "_User"; const userObject = Parse.Object.fromJSON(obj); return new Auth({ config, @@ -260,24 +279,24 @@ Auth.prototype.getRolesForUser = async function () { if (this.config) { const restWhere = { users: { - __type: 'Pointer', - className: '_User', + __type: "Pointer", + className: "_User", objectId: this.user.id, }, }; - const RestQuery = require('./RestQuery'); + const RestQuery = require("./RestQuery"); const query = await RestQuery({ method: RestQuery.Method.find, runBeforeFind: false, config: this.config, auth: master(this.config), - className: '_Role', + className: "_Role", restWhere, }); await query.each(result => results.push(result)); } else { await new Parse.Query(Parse.Role) - .equalTo('users', this.user) + .equalTo("users", this.user) .each(result => results.push(result.toJSON()), { useMasterKey: true }); } return results; @@ -315,9 +334,12 @@ Auth.prototype._loadRoles = async function () { ); // run the recursive finding - const roleNames = await this._getAllRolesNamesForRoleIds(rolesMap.ids, rolesMap.names); + const roleNames = await this._getAllRolesNamesForRoleIds( + rolesMap.ids, + rolesMap.names + ); this.userRoles = roleNames.map(r => { - return 'role:' + r; + return "role:" + r; }); this.fetchedRoles = true; this.rolePromise = null; @@ -348,7 +370,7 @@ Auth.prototype.getRolesByIds = async function (ins) { if (!this.config) { await new Parse.Query(Parse.Role) .containedIn( - 'roles', + "roles", ins.map(id => { const role = new Parse.Object(Parse.Role); role.id = id; @@ -359,19 +381,19 @@ Auth.prototype.getRolesByIds = async function (ins) { } else { const roles = ins.map(id => { return { - __type: 'Pointer', - className: '_Role', + __type: "Pointer", + className: "_Role", objectId: id, }; }); const restWhere = { roles: { $in: roles } }; - const RestQuery = require('./RestQuery'); + const RestQuery = require("./RestQuery"); const query = await RestQuery({ method: RestQuery.Method.find, config: this.config, runBeforeFind: false, auth: master(this.config), - className: '_Role', + className: "_Role", restWhere, }); await query.each(result => results.push(result)); @@ -380,7 +402,11 @@ Auth.prototype.getRolesByIds = async function (ins) { }; // Given a list of roleIds, find all the parent roles, returns a promise with all names -Auth.prototype._getAllRolesNamesForRoleIds = function (roleIDs, names = [], queriedRoles = {}) { +Auth.prototype._getAllRolesNamesForRoleIds = function ( + roleIDs, + names = [], + queriedRoles = {} +) { const ins = roleIDs.filter(roleID => { const wasQueried = queriedRoles[roleID] !== true; queriedRoles[roleID] = true; @@ -410,7 +436,11 @@ Auth.prototype._getAllRolesNamesForRoleIds = function (roleIDs, names = [], quer // store the new found names names = names.concat(resultMap.names); // find the next ones, circular roles will be cut - return this._getAllRolesNamesForRoleIds(resultMap.ids, names, queriedRoles); + return this._getAllRolesNamesForRoleIds( + resultMap.ids, + names, + queriedRoles + ); }) .then(names => { return Promise.resolve([...new Set(names)]); @@ -424,8 +454,9 @@ const findUsersWithAuthData = async (config, authData, beforeFind) => { providers.map(async provider => { const providerAuthData = authData[provider]; - const adapter = config.authDataManager.getValidatorForProvider(provider)?.adapter; - if (beforeFind && typeof adapter?.beforeFind === 'function') { + const adapter = + config.authDataManager.getValidatorForProvider(provider)?.adapter; + if (beforeFind && typeof adapter?.beforeFind === "function") { await adapter.beforeFind(providerAuthData); } @@ -445,7 +476,7 @@ const findUsersWithAuthData = async (config, authData, beforeFind) => { } // Perform database query - return config.database.find('_User', { $or: validQueries }, { limit: 2 }); + return config.database.find("_User", { $or: validQueries }, { limit: 2 }); }; const hasMutatedAuthData = (authData, userAuthData) => { @@ -455,7 +486,7 @@ const hasMutatedAuthData = (authData, userAuthData) => { const mutatedAuthData = {}; Object.keys(authData).forEach(provider => { // Anonymous provider is not handled this way - if (provider === 'anonymous') { + if (provider === "anonymous") { return; } const providerData = authData[provider]; @@ -481,7 +512,10 @@ const checkIfUserHasProvidedConfiguredProvidersForLogin = ( const hasProvidedASoloProvider = savedUserProviders.some( provider => - provider && provider.adapter && provider.adapter.policy === 'solo' && authData[provider.name] + provider && + provider.adapter && + provider.adapter.policy === "solo" && + authData[provider.name] ); // Solo providers can be considered as safe, so we do not have to check if the user needs @@ -492,32 +526,41 @@ const checkIfUserHasProvidedConfiguredProvidersForLogin = ( } const additionProvidersNotFound = []; - const hasProvidedAtLeastOneAdditionalProvider = savedUserProviders.some(provider => { - let policy = provider.adapter.policy; - if (typeof policy === 'function') { - const requestObject = { - ip: req.config.ip, - user: req.auth.user, - master: req.auth.isMaster, - }; - policy = policy.call(provider.adapter, requestObject, userAuthData[provider.name]); - } - if (policy === 'additional') { - if (authData[provider.name]) { - return true; - } else { - // Push missing provider for error message - additionProvidersNotFound.push(provider.name); + const hasProvidedAtLeastOneAdditionalProvider = savedUserProviders.some( + provider => { + let policy = provider.adapter.policy; + if (typeof policy === "function") { + const requestObject = { + ip: req.config.ip, + user: req.auth.user, + master: req.auth.isMaster, + }; + policy = policy.call( + provider.adapter, + requestObject, + userAuthData[provider.name] + ); + } + if (policy === "additional") { + if (authData[provider.name]) { + return true; + } else { + // Push missing provider for error message + additionProvidersNotFound.push(provider.name); + } } } - }); - if (hasProvidedAtLeastOneAdditionalProvider || !additionProvidersNotFound.length) { + ); + if ( + hasProvidedAtLeastOneAdditionalProvider || + !additionProvidersNotFound.length + ) { return; } throw new Parse.Error( Parse.Error.OTHER_CAUSE, - `Missing additional authData ${additionProvidersNotFound.join(',')}` + `Missing additional authData ${additionProvidersNotFound.join(",")}` ); }; @@ -525,14 +568,17 @@ const checkIfUserHasProvidedConfiguredProvidersForLogin = ( const handleAuthDataValidation = async (authData, req, foundUser) => { let user; if (foundUser) { - user = Parse.User.fromJSON({ className: '_User', ...foundUser }); + user = Parse.User.fromJSON({ className: "_User", ...foundUser }); // Find user by session and current objectId; only pass user if it's the current user or master key is provided } else if ( (req.auth && req.auth.user && - typeof req.getUserId === 'function' && + typeof req.getUserId === "function" && req.getUserId() === req.auth.user.id) || - (req.auth && req.auth.isMaster && typeof req.getUserId === 'function' && req.getUserId()) + (req.auth && + req.auth.isMaster && + typeof req.getUserId === "function" && + req.getUserId()) ) { user = new Parse.User(); user.id = req.auth.isMaster ? req.getUserId() : req.auth.user.id; @@ -540,27 +586,39 @@ const handleAuthDataValidation = async (authData, req, foundUser) => { } const { updatedObject } = req.buildParseObjects(); - const requestObject = getRequestObject(undefined, req.auth, updatedObject, user, req.config); + const requestObject = getRequestObject( + undefined, + req.auth, + updatedObject, + user, + req.config + ); // Perform validation as step-by-step pipeline for better error consistency // and also to avoid to trigger a provider (like OTP SMS) if another one fails const acc = { authData: {}, authDataResponse: {} }; const authKeys = Object.keys(authData).sort(); for (const provider of authKeys) { - let method = ''; + let method = ""; try { if (authData[provider] === null) { acc.authData[provider] = null; continue; } - const { validator } = req.config.authDataManager.getValidatorForProvider(provider) || {}; + const { validator } = + req.config.authDataManager.getValidatorForProvider(provider) || {}; const authProvider = (req.config.auth || {})[provider] || {}; if (!validator || authProvider.enabled === false) { throw new Parse.Error( Parse.Error.UNSUPPORTED_SERVICE, - 'This authentication method is unsupported.' + "This authentication method is unsupported." ); } - let validationResult = await validator(authData[provider], req, user, requestObject); + let validationResult = await validator( + authData[provider], + req, + user, + requestObject + ); method = validationResult && validationResult.method; requestObject.triggerName = method; if (validationResult && validationResult.validator) { @@ -585,10 +643,12 @@ const handleAuthDataValidation = async (authData, req, foundUser) => { } catch (err) { const e = resolveError(err, { code: Parse.Error.SCRIPT_FAILED, - message: 'Auth failed. Unknown error.', + message: "Auth failed. Unknown error.", }); const userString = - req.auth && req.auth.user ? req.auth.user.id : req.data.objectId || undefined; + req.auth && req.auth.user + ? req.auth.user.id + : req.data.objectId || undefined; logger.error( `Failed running auth step ${method} for ${provider} for user ${userString} with Error: ` + JSON.stringify(e), diff --git a/src/ClientSDK.js b/src/ClientSDK.js index 698729fc4f..f56f3df274 100644 --- a/src/ClientSDK.js +++ b/src/ClientSDK.js @@ -1,8 +1,8 @@ -var semver = require('semver'); +var semver = require("semver"); function compatible(compatibleSDK) { return function (clientSDK) { - if (typeof clientSDK === 'string') { + if (typeof clientSDK === "string") { clientSDK = fromString(clientSDK); } // REST API, or custom SDK @@ -17,7 +17,7 @@ function compatible(compatibleSDK) { function supportsForwardDelete(clientSDK) { return compatible({ - js: '>=1.9.0', + js: ">=1.9.0", })(clientSDK); } diff --git a/src/Controllers/AdaptableController.js b/src/Controllers/AdaptableController.js index 15551a6e38..1802e1592e 100644 --- a/src/Controllers/AdaptableController.js +++ b/src/Controllers/AdaptableController.js @@ -28,7 +28,7 @@ export class AdaptableController { } expectedAdapterType() { - throw new Error('Subclasses should implement expectedAdapterType()'); + throw new Error("Subclasses should implement expectedAdapterType()"); } validateAdapter(adapter) { @@ -37,7 +37,7 @@ export class AdaptableController { static validateAdapter(adapter, self, ExpectedType) { if (!adapter) { - throw new Error(this.constructor.name + ' requires an adapter'); + throw new Error(this.constructor.name + " requires an adapter"); } const Type = ExpectedType || self.expectedAdapterType(); @@ -47,20 +47,27 @@ export class AdaptableController { } // Makes sure the prototype matches - const mismatches = Object.getOwnPropertyNames(Type.prototype).reduce((obj, key) => { - const adapterType = typeof adapter[key]; - const expectedType = typeof Type.prototype[key]; - if (adapterType !== expectedType) { - obj[key] = { - expected: expectedType, - actual: adapterType, - }; - } - return obj; - }, {}); + const mismatches = Object.getOwnPropertyNames(Type.prototype).reduce( + (obj, key) => { + const adapterType = typeof adapter[key]; + const expectedType = typeof Type.prototype[key]; + if (adapterType !== expectedType) { + obj[key] = { + expected: expectedType, + actual: adapterType, + }; + } + return obj; + }, + {} + ); if (Object.keys(mismatches).length > 0) { - throw new Error("Adapter prototype don't match expected prototype", adapter, mismatches); + throw new Error( + "Adapter prototype don't match expected prototype", + adapter, + mismatches + ); } } } diff --git a/src/Controllers/AnalyticsController.js b/src/Controllers/AnalyticsController.js index af74681d86..dfb4a737e3 100644 --- a/src/Controllers/AnalyticsController.js +++ b/src/Controllers/AnalyticsController.js @@ -1,5 +1,5 @@ -import AdaptableController from './AdaptableController'; -import { AnalyticsAdapter } from '../Adapters/Analytics/AnalyticsAdapter'; +import AdaptableController from "./AdaptableController"; +import { AnalyticsAdapter } from "../Adapters/Analytics/AnalyticsAdapter"; export class AnalyticsController extends AdaptableController { appOpened(req) { @@ -18,7 +18,11 @@ export class AnalyticsController extends AdaptableController { trackEvent(req) { return Promise.resolve() .then(() => { - return this.adapter.trackEvent(req.params.eventName, req.body || {}, req); + return this.adapter.trackEvent( + req.params.eventName, + req.body || {}, + req + ); }) .then(response => { return { response: response || {} }; diff --git a/src/Controllers/CacheController.js b/src/Controllers/CacheController.js index 0c645c5236..c89fe97abc 100644 --- a/src/Controllers/CacheController.js +++ b/src/Controllers/CacheController.js @@ -1,7 +1,7 @@ -import AdaptableController from './AdaptableController'; -import CacheAdapter from '../Adapters/Cache/CacheAdapter'; +import AdaptableController from "./AdaptableController"; +import CacheAdapter from "../Adapters/Cache/CacheAdapter"; -const KEY_SEPARATOR_CHAR = ':'; +const KEY_SEPARATOR_CHAR = ":"; function joinKeys(...keys) { return keys.join(KEY_SEPARATOR_CHAR); @@ -43,9 +43,9 @@ export class CacheController extends AdaptableController { constructor(adapter, appId, options = {}) { super(adapter, appId, options); - this.role = new SubCache('role', this); - this.user = new SubCache('user', this); - this.graphQL = new SubCache('graphQL', this); + this.role = new SubCache("role", this); + this.user = new SubCache("user", this); + this.graphQL = new SubCache("graphQL", this); } get(key) { diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index 9c1fdc7033..fd859e7f9e 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -3,23 +3,26 @@ // Parse database. // @flow-disable-next -import { Parse } from 'parse/node'; +import { Parse } from "parse/node"; // @flow-disable-next -import _ from 'lodash'; +import _ from "lodash"; // @flow-disable-next -import intersect from 'intersect'; +import intersect from "intersect"; // @flow-disable-next -import deepcopy from 'deepcopy'; -import logger from '../logger'; -import Utils from '../Utils'; -import * as SchemaController from './SchemaController'; -import { StorageAdapter } from '../Adapters/Storage/StorageAdapter'; -import MongoStorageAdapter from '../Adapters/Storage/Mongo/MongoStorageAdapter'; -import PostgresStorageAdapter from '../Adapters/Storage/Postgres/PostgresStorageAdapter'; -import SchemaCache from '../Adapters/Cache/SchemaCache'; -import type { LoadSchemaOptions } from './types'; -import type { ParseServerOptions } from '../Options'; -import type { QueryOptions, FullQueryOptions } from '../Adapters/Storage/StorageAdapter'; +import deepcopy from "deepcopy"; +import logger from "../logger"; +import Utils from "../Utils"; +import * as SchemaController from "./SchemaController"; +import { StorageAdapter } from "../Adapters/Storage/StorageAdapter"; +import MongoStorageAdapter from "../Adapters/Storage/Mongo/MongoStorageAdapter"; +import PostgresStorageAdapter from "../Adapters/Storage/Postgres/PostgresStorageAdapter"; +import SchemaCache from "../Adapters/Cache/SchemaCache"; +import type { LoadSchemaOptions } from "./types"; +import type { ParseServerOptions } from "../Options"; +import type { + QueryOptions, + FullQueryOptions, +} from "../Adapters/Storage/StorageAdapter"; function addWriteACL(query, acl) { const newQuery = _.cloneDeep(query); @@ -31,7 +34,7 @@ function addWriteACL(query, acl) { function addReadACL(query, acl) { const newQuery = _.cloneDeep(query); //Can't be any existing '_rperm' query, we don't allow client queries on that, no need to $and - newQuery._rperm = { $in: [null, '*', ...acl] }; + newQuery._rperm = { $in: [null, "*", ...acl] }; return newQuery; } @@ -55,17 +58,17 @@ const transformObjectACL = ({ ACL, ...result }) => { return result; }; -const specialQueryKeys = ['$and', '$or', '$nor', '_rperm', '_wperm']; +const specialQueryKeys = ["$and", "$or", "$nor", "_rperm", "_wperm"]; const specialMasterQueryKeys = [ ...specialQueryKeys, - '_email_verify_token', - '_perishable_token', - '_tombstone', - '_email_verify_token_expires_at', - '_failed_login_count', - '_account_lockout_expires_at', - '_password_changed_at', - '_password_history', + "_email_verify_token", + "_perishable_token", + "_tombstone", + "_email_verify_token_expires_at", + "_failed_login_count", + "_account_lockout_expires_at", + "_password_changed_at", + "_password_history", ]; const validateQuery = ( @@ -78,39 +81,51 @@ const validateQuery = ( isMaster = true; } if (query.ACL) { - throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Cannot query on ACL.'); + throw new Parse.Error(Parse.Error.INVALID_QUERY, "Cannot query on ACL."); } if (query.$or) { if (query.$or instanceof Array) { - query.$or.forEach(value => validateQuery(value, isMaster, isMaintenance, update)); + query.$or.forEach(value => + validateQuery(value, isMaster, isMaintenance, update) + ); } else { - throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Bad $or format - use an array value.'); + throw new Parse.Error( + Parse.Error.INVALID_QUERY, + "Bad $or format - use an array value." + ); } } if (query.$and) { if (query.$and instanceof Array) { - query.$and.forEach(value => validateQuery(value, isMaster, isMaintenance, update)); + query.$and.forEach(value => + validateQuery(value, isMaster, isMaintenance, update) + ); } else { - throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Bad $and format - use an array value.'); + throw new Parse.Error( + Parse.Error.INVALID_QUERY, + "Bad $and format - use an array value." + ); } } if (query.$nor) { if (query.$nor instanceof Array && query.$nor.length > 0) { - query.$nor.forEach(value => validateQuery(value, isMaster, isMaintenance, update)); + query.$nor.forEach(value => + validateQuery(value, isMaster, isMaintenance, update) + ); } else { throw new Parse.Error( Parse.Error.INVALID_QUERY, - 'Bad $nor format - use an array of at least 1 value.' + "Bad $nor format - use an array of at least 1 value." ); } } Object.keys(query).forEach(key => { if (query && query[key] && query[key].$regex) { - if (typeof query[key].$options === 'string') { + if (typeof query[key].$options === "string") { if (!query[key].$options.match(/^[imxs]+$/)) { throw new Parse.Error( Parse.Error.INVALID_QUERY, @@ -124,7 +139,10 @@ const validateQuery = ( ((!specialQueryKeys.includes(key) && !isMaster && !update) || (update && isMaster && !specialMasterQueryKeys.includes(key))) ) { - throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid key name: ${key}`); + throw new Parse.Error( + Parse.Error.INVALID_KEY_NAME, + `Invalid key name: ${key}` + ); } }); }; @@ -148,14 +166,16 @@ const filterSensitiveData = ( // replace protectedFields when using pointer-permissions const perms = - schema && schema.getClassLevelPermissions ? schema.getClassLevelPermissions(className) : {}; + schema && schema.getClassLevelPermissions + ? schema.getClassLevelPermissions(className) + : {}; if (perms) { - const isReadOperation = ['get', 'find'].indexOf(operation) > -1; + const isReadOperation = ["get", "find"].indexOf(operation) > -1; if (isReadOperation && perms.protectedFields) { // extract protectedFields added with the pointer-permission prefix const protectedFieldsPointerPerm = Object.keys(perms.protectedFields) - .filter(key => key.startsWith('userField:')) + .filter(key => key.startsWith("userField:")) .map(key => { return { key: key.substring(10), value: perms.protectedFields[key] }; }); @@ -174,7 +194,8 @@ const filterSensitiveData = ( ); } else { pointerPermIncludesUser = - readUserFieldValue.objectId && readUserFieldValue.objectId === userId; + readUserFieldValue.objectId && + readUserFieldValue.objectId === userId; } } @@ -205,7 +226,7 @@ const filterSensitiveData = ( } } - const isUserClass = className === '_User'; + const isUserClass = className === "_User"; if (isUserClass) { object.password = object._hashed_password; delete object._hashed_password; @@ -227,7 +248,7 @@ const filterSensitiveData = ( } for (const key in object) { - if (key.charAt(0) === '_') { + if (key.charAt(0) === "_") { delete object[key]; } } @@ -252,15 +273,15 @@ const filterSensitiveData = ( // one of the provided strings must provide the caller with // write permissions. const specialKeysForUpdate = [ - '_hashed_password', - '_perishable_token', - '_email_verify_token', - '_email_verify_token_expires_at', - '_account_lockout_expires_at', - '_failed_login_count', - '_perishable_token_expires_at', - '_password_changed_at', - '_password_history', + "_hashed_password", + "_perishable_token", + "_email_verify_token", + "_email_verify_token_expires_at", + "_account_lockout_expires_at", + "_failed_login_count", + "_perishable_token_expires_at", + "_password_changed_at", + "_password_history", ]; const isSpecialUpdateKey = key => { @@ -275,34 +296,46 @@ const flattenUpdateOperatorsForCreate = object => { for (const key in object) { if (object[key] && object[key].__op) { switch (object[key].__op) { - case 'Increment': - if (typeof object[key].amount !== 'number') { - throw new Parse.Error(Parse.Error.INVALID_JSON, 'objects to add must be an array'); + case "Increment": + if (typeof object[key].amount !== "number") { + throw new Parse.Error( + Parse.Error.INVALID_JSON, + "objects to add must be an array" + ); } object[key] = object[key].amount; break; - case 'SetOnInsert': + case "SetOnInsert": object[key] = object[key].amount; break; - case 'Add': + case "Add": if (!(object[key].objects instanceof Array)) { - throw new Parse.Error(Parse.Error.INVALID_JSON, 'objects to add must be an array'); + throw new Parse.Error( + Parse.Error.INVALID_JSON, + "objects to add must be an array" + ); } object[key] = object[key].objects; break; - case 'AddUnique': + case "AddUnique": if (!(object[key].objects instanceof Array)) { - throw new Parse.Error(Parse.Error.INVALID_JSON, 'objects to add must be an array'); + throw new Parse.Error( + Parse.Error.INVALID_JSON, + "objects to add must be an array" + ); } object[key] = object[key].objects; break; - case 'Remove': + case "Remove": if (!(object[key].objects instanceof Array)) { - throw new Parse.Error(Parse.Error.INVALID_JSON, 'objects to add must be an array'); + throw new Parse.Error( + Parse.Error.INVALID_JSON, + "objects to add must be an array" + ); } object[key] = []; break; - case 'Delete': + case "Delete": delete object[key]; break; default: @@ -316,17 +349,17 @@ const flattenUpdateOperatorsForCreate = object => { }; const transformAuthData = (className, object, schema) => { - if (object.authData && className === '_User') { + if (object.authData && className === "_User") { Object.keys(object.authData).forEach(provider => { const providerData = object.authData[provider]; const fieldName = `_auth_data_${provider}`; if (providerData == null) { object[fieldName] = { - __op: 'Delete', + __op: "Delete", }; } else { object[fieldName] = providerData; - schema.fields[fieldName] = { type: 'Object' }; + schema.fields[fieldName] = { type: "Object" }; } }); delete object.authData; @@ -341,7 +374,7 @@ const untransformObjectACL = ({ _rperm, _wperm, ...output }) => { if (!output.ACL[entry]) { output.ACL[entry] = { read: true }; } else { - output.ACL[entry]['read'] = true; + output.ACL[entry]["read"] = true; } }); @@ -349,7 +382,7 @@ const untransformObjectACL = ({ _rperm, _wperm, ...output }) => { if (!output.ACL[entry]) { output.ACL[entry] = { write: true }; } else { - output.ACL[entry]['write'] = true; + output.ACL[entry]["write"] = true; } }); } @@ -363,25 +396,25 @@ const untransformObjectACL = ({ _rperm, _wperm, ...output }) => { * @returns {string} the root name of the field */ const getRootFieldName = (fieldName: string): string => { - return fieldName.split('.')[0]; + return fieldName.split(".")[0]; }; const relationSchema = { - fields: { relatedId: { type: 'String' }, owningId: { type: 'String' } }, + fields: { relatedId: { type: "String" }, owningId: { type: "String" } }, }; const convertEmailToLowercase = (object, className, options) => { - if (className === '_User' && options.convertEmailToLowercase) { - if (typeof object['email'] === 'string') { - object['email'] = object['email'].toLowerCase(); + if (className === "_User" && options.convertEmailToLowercase) { + if (typeof object["email"] === "string") { + object["email"] = object["email"].toLowerCase(); } } }; const convertUsernameToLowercase = (object, className, options) => { - if (className === '_User' && options.convertUsernameToLowercase) { - if (typeof object['username'] === 'string') { - object['username'] = object['username'].toLowerCase(); + if (className === "_User" && options.convertUsernameToLowercase) { + if (typeof object["username"] === "string") { + object["username"] = object["username"].toLowerCase(); } } }; @@ -418,7 +451,10 @@ class DatabaseController { validateClassName(className: string): Promise { if (!SchemaController.classNameIsValid(className)) { return Promise.reject( - new Parse.Error(Parse.Error.INVALID_CLASS_NAME, 'invalid className: ' + className) + new Parse.Error( + Parse.Error.INVALID_CLASS_NAME, + "invalid className: " + className + ) ); } return Promise.resolve(); @@ -443,7 +479,9 @@ class DatabaseController { schemaController: SchemaController.SchemaController, options: LoadSchemaOptions = { clearCache: false } ): Promise { - return schemaController ? Promise.resolve(schemaController) : this.loadSchema(options); + return schemaController + ? Promise.resolve(schemaController) + : this.loadSchema(options); } // Returns a promise for the classname that is related to the given @@ -452,7 +490,7 @@ class DatabaseController { redirectClassNameForKey(className: string, key: string): Promise { return this.loadSchema().then(schema => { var t = schema.getExpectedType(className, key); - if (t != null && typeof t !== 'string' && t.type === 'Relation') { + if (t != null && typeof t !== "string" && t.type === "Relation") { return t.targetClass; } return className; @@ -480,7 +518,13 @@ class DatabaseController { if (isMaster) { return Promise.resolve(); } - return this.canAddField(schema, className, object, aclGroup, runOptions); + return this.canAddField( + schema, + className, + object, + aclGroup, + runOptions + ); }) .then(() => { return schema.validateObject(className, object, query, maintenance); @@ -499,7 +543,9 @@ class DatabaseController { try { Utils.checkProhibitedKeywords(this.options, update); } catch (error) { - return Promise.reject(new Parse.Error(Parse.Error.INVALID_KEY_NAME, error)); + return Promise.reject( + new Parse.Error(Parse.Error.INVALID_KEY_NAME, error) + ); } const originalQuery = query; const originalUpdate = update; @@ -509,150 +555,168 @@ class DatabaseController { var isMaster = acl === undefined; var aclGroup = acl || []; - return this.loadSchemaIfNeeded(validSchemaController).then(schemaController => { - return ( - isMaster - ? Promise.resolve() - : schemaController.validatePermission(className, aclGroup, 'update') - ) - .then(() => { - relationUpdates = this.collectRelationUpdates(className, originalQuery.objectId, update); - if (!isMaster) { - query = this.addPointerPermissions( - schemaController, + return this.loadSchemaIfNeeded(validSchemaController).then( + schemaController => { + return ( + isMaster + ? Promise.resolve() + : schemaController.validatePermission(className, aclGroup, "update") + ) + .then(() => { + relationUpdates = this.collectRelationUpdates( className, - 'update', - query, - aclGroup + originalQuery.objectId, + update ); + if (!isMaster) { + query = this.addPointerPermissions( + schemaController, + className, + "update", + query, + aclGroup + ); - if (addsField) { - query = { - $and: [ - query, - this.addPointerPermissions( - schemaController, - className, - 'addField', + if (addsField) { + query = { + $and: [ query, - aclGroup - ), - ], - }; - } - } - if (!query) { - return Promise.resolve(); - } - if (acl) { - query = addWriteACL(query, acl); - } - validateQuery(query, isMaster, false, true); - return schemaController - .getOneSchema(className, true) - .catch(error => { - // If the schema doesn't exist, pretend it exists with no fields. This behavior - // will likely need revisiting. - if (error === undefined) { - return { fields: {} }; + this.addPointerPermissions( + schemaController, + className, + "addField", + query, + aclGroup + ), + ], + }; } - throw error; - }) - .then(schema => { - Object.keys(update).forEach(fieldName => { - if (fieldName.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) { - throw new Parse.Error( - Parse.Error.INVALID_KEY_NAME, - `Invalid field name for update: ${fieldName}` - ); + } + if (!query) { + return Promise.resolve(); + } + if (acl) { + query = addWriteACL(query, acl); + } + validateQuery(query, isMaster, false, true); + return schemaController + .getOneSchema(className, true) + .catch(error => { + // If the schema doesn't exist, pretend it exists with no fields. This behavior + // will likely need revisiting. + if (error === undefined) { + return { fields: {} }; } - const rootFieldName = getRootFieldName(fieldName); - if ( - !SchemaController.fieldNameIsValid(rootFieldName, className) && - !isSpecialUpdateKey(rootFieldName) - ) { - throw new Parse.Error( - Parse.Error.INVALID_KEY_NAME, - `Invalid field name for update: ${fieldName}` - ); + throw error; + }) + .then(schema => { + Object.keys(update).forEach(fieldName => { + if (fieldName.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) { + throw new Parse.Error( + Parse.Error.INVALID_KEY_NAME, + `Invalid field name for update: ${fieldName}` + ); + } + const rootFieldName = getRootFieldName(fieldName); + if ( + !SchemaController.fieldNameIsValid( + rootFieldName, + className + ) && + !isSpecialUpdateKey(rootFieldName) + ) { + throw new Parse.Error( + Parse.Error.INVALID_KEY_NAME, + `Invalid field name for update: ${fieldName}` + ); + } + }); + for (const updateOperation in update) { + if ( + update[updateOperation] && + typeof update[updateOperation] === "object" && + Object.keys(update[updateOperation]).some( + innerKey => + innerKey.includes("$") || innerKey.includes(".") + ) + ) { + throw new Parse.Error( + Parse.Error.INVALID_NESTED_KEY, + "Nested keys should not contain the '$' or '.' characters" + ); + } } - }); - for (const updateOperation in update) { - if ( - update[updateOperation] && - typeof update[updateOperation] === 'object' && - Object.keys(update[updateOperation]).some( - innerKey => innerKey.includes('$') || innerKey.includes('.') - ) - ) { - throw new Parse.Error( - Parse.Error.INVALID_NESTED_KEY, - "Nested keys should not contain the '$' or '.' characters" + update = transformObjectACL(update); + convertEmailToLowercase(update, className, this.options); + convertUsernameToLowercase(update, className, this.options); + transformAuthData(className, update, schema); + if (validateOnly) { + return this.adapter + .find(className, schema, query, {}) + .then(result => { + if (!result || !result.length) { + throw new Parse.Error( + Parse.Error.OBJECT_NOT_FOUND, + "Object not found." + ); + } + return {}; + }); + } + if (many) { + return this.adapter.updateObjectsByQuery( + className, + schema, + query, + update, + this._transactionalSession + ); + } else if (upsert) { + return this.adapter.upsertOneObject( + className, + schema, + query, + update, + this._transactionalSession + ); + } else { + return this.adapter.findOneAndUpdate( + className, + schema, + query, + update, + this._transactionalSession ); } - } - update = transformObjectACL(update); - convertEmailToLowercase(update, className, this.options); - convertUsernameToLowercase(update, className, this.options); - transformAuthData(className, update, schema); - if (validateOnly) { - return this.adapter.find(className, schema, query, {}).then(result => { - if (!result || !result.length) { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.'); - } - return {}; - }); - } - if (many) { - return this.adapter.updateObjectsByQuery( - className, - schema, - query, - update, - this._transactionalSession - ); - } else if (upsert) { - return this.adapter.upsertOneObject( - className, - schema, - query, - update, - this._transactionalSession - ); - } else { - return this.adapter.findOneAndUpdate( - className, - schema, - query, - update, - this._transactionalSession - ); - } + }); + }) + .then((result: any) => { + if (!result) { + throw new Parse.Error( + Parse.Error.OBJECT_NOT_FOUND, + "Object not found." + ); + } + if (validateOnly) { + return result; + } + return this.handleRelationUpdates( + className, + originalQuery.objectId, + update, + relationUpdates + ).then(() => { + return result; }); - }) - .then((result: any) => { - if (!result) { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.'); - } - if (validateOnly) { - return result; - } - return this.handleRelationUpdates( - className, - originalQuery.objectId, - update, - relationUpdates - ).then(() => { - return result; + }) + .then(result => { + if (skipSanitization) { + return Promise.resolve(result); + } + return this._sanitizeDatabaseResult(originalUpdate, result); }); - }) - .then(result => { - if (skipSanitization) { - return Promise.resolve(result); - } - return this._sanitizeDatabaseResult(originalUpdate, result); - }); - }); + } + ); } // Collect all relation-updating operations from a REST-format update. @@ -667,17 +731,17 @@ class DatabaseController { if (!op) { return; } - if (op.__op == 'AddRelation') { + if (op.__op == "AddRelation") { ops.push({ key, op }); deleteMe.push(key); } - if (op.__op == 'RemoveRelation') { + if (op.__op == "RemoveRelation") { ops.push({ key, op }); deleteMe.push(key); } - if (op.__op == 'Batch') { + if (op.__op == "Batch") { for (var x of op.ops) { process(x, key); } @@ -695,22 +759,31 @@ class DatabaseController { // Processes relation-updating operations from a REST-format update. // Returns a promise that resolves when all updates have been performed - handleRelationUpdates(className: string, objectId: string, update: any, ops: any) { + handleRelationUpdates( + className: string, + objectId: string, + update: any, + ops: any + ) { var pending = []; objectId = update.objectId || objectId; ops.forEach(({ key, op }) => { if (!op) { return; } - if (op.__op == 'AddRelation') { + if (op.__op == "AddRelation") { for (const object of op.objects) { - pending.push(this.addRelation(key, className, objectId, object.objectId)); + pending.push( + this.addRelation(key, className, objectId, object.objectId) + ); } } - if (op.__op == 'RemoveRelation') { + if (op.__op == "RemoveRelation") { for (const object of op.objects) { - pending.push(this.removeRelation(key, className, objectId, object.objectId)); + pending.push( + this.removeRelation(key, className, objectId, object.objectId) + ); } } }); @@ -720,7 +793,12 @@ class DatabaseController { // Adds a relation. // Returns a promise that resolves successfully iff the add was successful. - addRelation(key: string, fromClassName: string, fromId: string, toId: string) { + addRelation( + key: string, + fromClassName: string, + fromId: string, + toId: string + ) { const doc = { relatedId: toId, owningId: fromId, @@ -737,7 +815,12 @@ class DatabaseController { // Removes a relation. // Returns a promise that resolves successfully iff the remove was // successful. - removeRelation(key: string, fromClassName: string, fromId: string, toId: string) { + removeRelation( + key: string, + fromClassName: string, + fromId: string, + toId: string + ) { var doc = { relatedId: toId, owningId: fromId, @@ -774,56 +857,64 @@ class DatabaseController { const isMaster = acl === undefined; const aclGroup = acl || []; - return this.loadSchemaIfNeeded(validSchemaController).then(schemaController => { - return ( - isMaster - ? Promise.resolve() - : schemaController.validatePermission(className, aclGroup, 'delete') - ).then(() => { - if (!isMaster) { - query = this.addPointerPermissions( - schemaController, - className, - 'delete', - query, - aclGroup - ); - if (!query) { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.'); - } - } - // delete by query - if (acl) { - query = addWriteACL(query, acl); - } - validateQuery(query, isMaster, false, false); - return schemaController - .getOneSchema(className) - .catch(error => { - // If the schema doesn't exist, pretend it exists with no fields. This behavior - // will likely need revisiting. - if (error === undefined) { - return { fields: {} }; - } - throw error; - }) - .then(parseFormatSchema => - this.adapter.deleteObjectsByQuery( + return this.loadSchemaIfNeeded(validSchemaController).then( + schemaController => { + return ( + isMaster + ? Promise.resolve() + : schemaController.validatePermission(className, aclGroup, "delete") + ).then(() => { + if (!isMaster) { + query = this.addPointerPermissions( + schemaController, className, - parseFormatSchema, + "delete", query, - this._transactionalSession - ) - ) - .catch(error => { - // When deleting sessions while changing passwords, don't throw an error if they don't have any sessions. - if (className === '_Session' && error.code === Parse.Error.OBJECT_NOT_FOUND) { - return Promise.resolve({}); + aclGroup + ); + if (!query) { + throw new Parse.Error( + Parse.Error.OBJECT_NOT_FOUND, + "Object not found." + ); } - throw error; - }); - }); - }); + } + // delete by query + if (acl) { + query = addWriteACL(query, acl); + } + validateQuery(query, isMaster, false, false); + return schemaController + .getOneSchema(className) + .catch(error => { + // If the schema doesn't exist, pretend it exists with no fields. This behavior + // will likely need revisiting. + if (error === undefined) { + return { fields: {} }; + } + throw error; + }) + .then(parseFormatSchema => + this.adapter.deleteObjectsByQuery( + className, + parseFormatSchema, + query, + this._transactionalSession + ) + ) + .catch(error => { + // When deleting sessions while changing passwords, don't throw an error if they don't have any sessions. + if ( + className === "_Session" && + error.code === Parse.Error.OBJECT_NOT_FOUND + ) { + return Promise.resolve({}); + } + throw error; + }); + }); + } + ); } // Inserts an object into the database. @@ -838,7 +929,9 @@ class DatabaseController { try { Utils.checkProhibitedKeywords(this.options, object); } catch (error) { - return Promise.reject(new Parse.Error(Parse.Error.INVALID_KEY_NAME, error)); + return Promise.reject( + new Parse.Error(Parse.Error.INVALID_KEY_NAME, error) + ); } // Make a copy of the object, so we don't mutate the incoming data. const originalObject = object; @@ -846,12 +939,16 @@ class DatabaseController { convertEmailToLowercase(object, className, this.options); convertUsernameToLowercase(object, className, this.options); - object.createdAt = { iso: object.createdAt, __type: 'Date' }; - object.updatedAt = { iso: object.updatedAt, __type: 'Date' }; + object.createdAt = { iso: object.createdAt, __type: "Date" }; + object.updatedAt = { iso: object.updatedAt, __type: "Date" }; var isMaster = acl === undefined; var aclGroup = acl || []; - const relationUpdates = this.collectRelationUpdates(className, null, object); + const relationUpdates = this.collectRelationUpdates( + className, + null, + object + ); return this.validateClassName(className) .then(() => this.loadSchemaIfNeeded(validSchemaController)) @@ -859,7 +956,7 @@ class DatabaseController { return ( isMaster ? Promise.resolve() - : schemaController.validatePermission(className, aclGroup, 'create') + : schemaController.validatePermission(className, aclGroup, "create") ) .then(() => schemaController.enforceClassExists(className)) .then(() => schemaController.getOneSchema(className, true)) @@ -886,7 +983,10 @@ class DatabaseController { object, relationUpdates ).then(() => { - return this._sanitizeDatabaseResult(originalObject, result.ops[0]); + return this._sanitizeDatabaseResult( + originalObject, + result.ops[0] + ); }); }); }); @@ -907,7 +1007,11 @@ class DatabaseController { const schemaFields = Object.keys(classSchema.fields); const newKeys = fields.filter(field => { // Skip fields that are unset - if (object[field] && object[field].__op && object[field].__op === 'Delete') { + if ( + object[field] && + object[field].__op && + object[field].__op === "Delete" + ) { return false; } return schemaFields.indexOf(getRootFieldName(field)) < 0; @@ -917,7 +1021,7 @@ class DatabaseController { runOptions.addsField = true; const action = runOptions.action; - return schema.validatePermission(className, aclGroup, 'addField', action); + return schema.validatePermission(className, aclGroup, "addField", action); } return Promise.resolve(); } @@ -952,19 +1056,28 @@ class DatabaseController { queryOptions.skip = 0; } return this.adapter - .find(joinTableName(className, key), relationSchema, { owningId }, findOptions) + .find( + joinTableName(className, key), + relationSchema, + { owningId }, + findOptions + ) .then(results => results.map(result => result.relatedId)); } // Returns a promise for a list of owning ids given some related ids. // className here is the owning className. - owningIds(className: string, key: string, relatedIds: string[]): Promise { + owningIds( + className: string, + key: string, + relatedIds: string[] + ): Promise { return this.adapter .find( joinTableName(className, key), relationSchema, { relatedId: { $in: relatedIds } }, - { keys: ['owningId'] } + { keys: ["owningId"] } ) .then(results => results.map(result => result.owningId)); } @@ -976,57 +1089,61 @@ class DatabaseController { // Search for an in-relation or equal-to-relation // Make it sequential for now, not sure of paralleization side effects const promises = []; - if (query['$or']) { - const ors = query['$or']; + if (query["$or"]) { + const ors = query["$or"]; promises.push( ...ors.map((aQuery, index) => { - return this.reduceInRelation(className, aQuery, schema).then(aQuery => { - query['$or'][index] = aQuery; - }); + return this.reduceInRelation(className, aQuery, schema).then( + aQuery => { + query["$or"][index] = aQuery; + } + ); }) ); } - if (query['$and']) { - const ands = query['$and']; + if (query["$and"]) { + const ands = query["$and"]; promises.push( ...ands.map((aQuery, index) => { - return this.reduceInRelation(className, aQuery, schema).then(aQuery => { - query['$and'][index] = aQuery; - }); + return this.reduceInRelation(className, aQuery, schema).then( + aQuery => { + query["$and"][index] = aQuery; + } + ); }) ); } const otherKeys = Object.keys(query).map(key => { - if (key === '$and' || key === '$or') { + if (key === "$and" || key === "$or") { return; } const t = schema.getExpectedType(className, key); - if (!t || t.type !== 'Relation') { + if (!t || t.type !== "Relation") { return Promise.resolve(query); } let queries: ?(any[]) = null; if ( query[key] && - (query[key]['$in'] || - query[key]['$ne'] || - query[key]['$nin'] || - query[key].__type == 'Pointer') + (query[key]["$in"] || + query[key]["$ne"] || + query[key]["$nin"] || + query[key].__type == "Pointer") ) { // Build the list of queries queries = Object.keys(query[key]).map(constraintKey => { let relatedIds; let isNegation = false; - if (constraintKey === 'objectId') { + if (constraintKey === "objectId") { relatedIds = [query[key].objectId]; - } else if (constraintKey == '$in') { - relatedIds = query[key]['$in'].map(r => r.objectId); - } else if (constraintKey == '$nin') { + } else if (constraintKey == "$in") { + relatedIds = query[key]["$in"].map(r => r.objectId); + } else if (constraintKey == "$nin") { isNegation = true; - relatedIds = query[key]['$nin'].map(r => r.objectId); - } else if (constraintKey == '$ne') { + relatedIds = query[key]["$nin"].map(r => r.objectId); + } else if (constraintKey == "$ne") { isNegation = true; - relatedIds = [query[key]['$ne'].objectId]; + relatedIds = [query[key]["$ne"].objectId]; } else { return; } @@ -1069,22 +1186,26 @@ class DatabaseController { // Modifies query so that it no longer has $relatedTo // Returns a promise that resolves when query is mutated - reduceRelationKeys(className: string, query: any, queryOptions: any): ?Promise { - if (query['$or']) { + reduceRelationKeys( + className: string, + query: any, + queryOptions: any + ): ?Promise { + if (query["$or"]) { return Promise.all( - query['$or'].map(aQuery => { + query["$or"].map(aQuery => { return this.reduceRelationKeys(className, aQuery, queryOptions); }) ); } - if (query['$and']) { + if (query["$and"]) { return Promise.all( - query['$and'].map(aQuery => { + query["$and"].map(aQuery => { return this.reduceRelationKeys(className, aQuery, queryOptions); }) ); } - var relatedTo = query['$relatedTo']; + var relatedTo = query["$relatedTo"]; if (relatedTo) { return this.relatedIds( relatedTo.object.className, @@ -1093,7 +1214,7 @@ class DatabaseController { queryOptions ) .then(ids => { - delete query['$relatedTo']; + delete query["$relatedTo"]; this.addInObjectIdsIds(ids, query); return this.reduceRelationKeys(className, query, queryOptions); }) @@ -1103,16 +1224,19 @@ class DatabaseController { addInObjectIdsIds(ids: ?Array = null, query: any) { const idsFromString: ?Array = - typeof query.objectId === 'string' ? [query.objectId] : null; + typeof query.objectId === "string" ? [query.objectId] : null; const idsFromEq: ?Array = - query.objectId && query.objectId['$eq'] ? [query.objectId['$eq']] : null; + query.objectId && query.objectId["$eq"] ? [query.objectId["$eq"]] : null; const idsFromIn: ?Array = - query.objectId && query.objectId['$in'] ? query.objectId['$in'] : null; + query.objectId && query.objectId["$in"] ? query.objectId["$in"] : null; // @flow-disable-next - const allIds: Array> = [idsFromString, idsFromEq, idsFromIn, ids].filter( - list => list !== null - ); + const allIds: Array> = [ + idsFromString, + idsFromEq, + idsFromIn, + ids, + ].filter(list => list !== null); const totalLength = allIds.reduce((memo, list) => memo + list.length, 0); let idsIntersection = []; @@ -1123,41 +1247,42 @@ class DatabaseController { } // Need to make sure we don't clobber existing shorthand $eq constraints on objectId. - if (!('objectId' in query)) { + if (!("objectId" in query)) { query.objectId = { $in: undefined, }; - } else if (typeof query.objectId === 'string') { + } else if (typeof query.objectId === "string") { query.objectId = { $in: undefined, $eq: query.objectId, }; } - query.objectId['$in'] = idsIntersection; + query.objectId["$in"] = idsIntersection; return query; } addNotInObjectIdsIds(ids: string[] = [], query: any) { - const idsFromNin = query.objectId && query.objectId['$nin'] ? query.objectId['$nin'] : []; + const idsFromNin = + query.objectId && query.objectId["$nin"] ? query.objectId["$nin"] : []; let allIds = [...idsFromNin, ...ids].filter(list => list !== null); // make a set and spread to remove duplicates allIds = [...new Set(allIds)]; // Need to make sure we don't clobber existing shorthand $eq constraints on objectId. - if (!('objectId' in query)) { + if (!("objectId" in query)) { query.objectId = { $nin: undefined, }; - } else if (typeof query.objectId === 'string') { + } else if (typeof query.objectId === "string") { query.objectId = { $nin: undefined, $eq: query.objectId, }; } - query.objectId['$nin'] = allIds; + query.objectId["$nin"] = allIds; return query; } @@ -1202,170 +1327,205 @@ class DatabaseController { const isMaster = acl === undefined || isMaintenance; const aclGroup = acl || []; op = - op || (typeof query.objectId == 'string' && Object.keys(query).length === 1 ? 'get' : 'find'); + op || + (typeof query.objectId == "string" && Object.keys(query).length === 1 + ? "get" + : "find"); // Count operation if counting - op = count === true ? 'count' : op; + op = count === true ? "count" : op; let classExists = true; - return this.loadSchemaIfNeeded(validSchemaController).then(schemaController => { - //Allow volatile classes if querying with Master (for _PushStatus) - //TODO: Move volatile classes concept into mongo adapter, postgres adapter shouldn't care - //that api.parse.com breaks when _PushStatus exists in mongo. - return schemaController - .getOneSchema(className, isMaster) - .catch(error => { - // Behavior for non-existent classes is kinda weird on Parse.com. Probably doesn't matter too much. - // For now, pretend the class exists but has no objects, - if (error === undefined) { - classExists = false; - return { fields: {} }; - } - throw error; - }) - .then(schema => { - // Parse.com treats queries on _created_at and _updated_at as if they were queries on createdAt and updatedAt, - // so duplicate that behavior here. If both are specified, the correct behavior to match Parse.com is to - // use the one that appears first in the sort list. - if (sort._created_at) { - sort.createdAt = sort._created_at; - delete sort._created_at; - } - if (sort._updated_at) { - sort.updatedAt = sort._updated_at; - delete sort._updated_at; - } - const queryOptions = { - skip, - limit, - sort, - keys, - readPreference, - hint, - caseInsensitive: this.options.enableCollationCaseComparison ? false : caseInsensitive, - explain, - comment, - }; - Object.keys(sort).forEach(fieldName => { - if (fieldName.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) { - throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Cannot sort by ${fieldName}`); + return this.loadSchemaIfNeeded(validSchemaController).then( + schemaController => { + //Allow volatile classes if querying with Master (for _PushStatus) + //TODO: Move volatile classes concept into mongo adapter, postgres adapter shouldn't care + //that api.parse.com breaks when _PushStatus exists in mongo. + return schemaController + .getOneSchema(className, isMaster) + .catch(error => { + // Behavior for non-existent classes is kinda weird on Parse.com. Probably doesn't matter too much. + // For now, pretend the class exists but has no objects, + if (error === undefined) { + classExists = false; + return { fields: {} }; } - const rootFieldName = getRootFieldName(fieldName); - if (!SchemaController.fieldNameIsValid(rootFieldName, className)) { - throw new Parse.Error( - Parse.Error.INVALID_KEY_NAME, - `Invalid field name: ${fieldName}.` - ); + throw error; + }) + .then(schema => { + // Parse.com treats queries on _created_at and _updated_at as if they were queries on createdAt and updatedAt, + // so duplicate that behavior here. If both are specified, the correct behavior to match Parse.com is to + // use the one that appears first in the sort list. + if (sort._created_at) { + sort.createdAt = sort._created_at; + delete sort._created_at; } - if (!schema.fields[fieldName.split('.')[0]] && fieldName !== 'score') { - delete sort[fieldName]; + if (sort._updated_at) { + sort.updatedAt = sort._updated_at; + delete sort._updated_at; } - }); - return ( - isMaster - ? Promise.resolve() - : schemaController.validatePermission(className, aclGroup, op) - ) - .then(() => this.reduceRelationKeys(className, query, queryOptions)) - .then(() => this.reduceInRelation(className, query, schemaController)) - .then(() => { - let protectedFields; - if (!isMaster) { - query = this.addPointerPermissions( - schemaController, - className, - op, - query, - aclGroup - ); - /* Don't use projections to optimize the protectedFields since the protectedFields - based on pointer-permissions are determined after querying. The filtering can - overwrite the protected fields. */ - protectedFields = this.addProtectedFields( - schemaController, - className, - query, - aclGroup, - auth, - queryOptions + const queryOptions = { + skip, + limit, + sort, + keys, + readPreference, + hint, + caseInsensitive: this.options.enableCollationCaseComparison + ? false + : caseInsensitive, + explain, + comment, + }; + Object.keys(sort).forEach(fieldName => { + if (fieldName.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) { + throw new Parse.Error( + Parse.Error.INVALID_KEY_NAME, + `Cannot sort by ${fieldName}` ); } - if (!query) { - if (op === 'get') { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.'); - } else { - return []; - } + const rootFieldName = getRootFieldName(fieldName); + if ( + !SchemaController.fieldNameIsValid(rootFieldName, className) + ) { + throw new Parse.Error( + Parse.Error.INVALID_KEY_NAME, + `Invalid field name: ${fieldName}.` + ); } - if (!isMaster) { - if (op === 'update' || op === 'delete') { - query = addWriteACL(query, aclGroup); - } else { - query = addReadACL(query, aclGroup); - } + if ( + !schema.fields[fieldName.split(".")[0]] && + fieldName !== "score" + ) { + delete sort[fieldName]; } - validateQuery(query, isMaster, isMaintenance, false); - if (count) { - if (!classExists) { - return 0; - } else { - return this.adapter.count( + }); + return ( + isMaster + ? Promise.resolve() + : schemaController.validatePermission(className, aclGroup, op) + ) + .then(() => + this.reduceRelationKeys(className, query, queryOptions) + ) + .then(() => + this.reduceInRelation(className, query, schemaController) + ) + .then(() => { + let protectedFields; + if (!isMaster) { + query = this.addPointerPermissions( + schemaController, + className, + op, + query, + aclGroup + ); + /* Don't use projections to optimize the protectedFields since the protectedFields + based on pointer-permissions are determined after querying. The filtering can + overwrite the protected fields. */ + protectedFields = this.addProtectedFields( + schemaController, className, - schema, query, - readPreference, - undefined, - hint, - comment + aclGroup, + auth, + queryOptions ); } - } else if (distinct) { - if (!classExists) { - return []; - } else { - return this.adapter.distinct(className, schema, query, distinct); + if (!query) { + if (op === "get") { + throw new Parse.Error( + Parse.Error.OBJECT_NOT_FOUND, + "Object not found." + ); + } else { + return []; + } } - } else if (pipeline) { - if (!classExists) { - return []; - } else { - return this.adapter.aggregate( + if (!isMaster) { + if (op === "update" || op === "delete") { + query = addWriteACL(query, aclGroup); + } else { + query = addReadACL(query, aclGroup); + } + } + validateQuery(query, isMaster, isMaintenance, false); + if (count) { + if (!classExists) { + return 0; + } else { + return this.adapter.count( + className, + schema, + query, + readPreference, + undefined, + hint, + comment + ); + } + } else if (distinct) { + if (!classExists) { + return []; + } else { + return this.adapter.distinct( + className, + schema, + query, + distinct + ); + } + } else if (pipeline) { + if (!classExists) { + return []; + } else { + return this.adapter.aggregate( + className, + schema, + pipeline, + readPreference, + hint, + explain, + comment + ); + } + } else if (explain) { + return this.adapter.find( className, schema, - pipeline, - readPreference, - hint, - explain, - comment + query, + queryOptions ); - } - } else if (explain) { - return this.adapter.find(className, schema, query, queryOptions); - } else { - return this.adapter - .find(className, schema, query, queryOptions) - .then(objects => - objects.map(object => { - object = untransformObjectACL(object); - return filterSensitiveData( - isMaster, - isMaintenance, - aclGroup, - auth, - op, - schemaController, - className, - protectedFields, - object + } else { + return this.adapter + .find(className, schema, query, queryOptions) + .then(objects => + objects.map(object => { + object = untransformObjectACL(object); + return filterSensitiveData( + isMaster, + isMaintenance, + aclGroup, + auth, + op, + schemaController, + className, + protectedFields, + object + ); + }) + ) + .catch(error => { + throw new Parse.Error( + Parse.Error.INTERNAL_SERVER_ERROR, + error ); - }) - ) - .catch(error => { - throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, error); - }); - } - }); - }); - }); + }); + } + }); + }); + } + ); } deleteSchema(className: string): Promise { @@ -1384,7 +1544,9 @@ class DatabaseController { }) .then((schema: any) => { return this.collectionExists(className) - .then(() => this.adapter.count(className, { fields: {} }, null, '', false)) + .then(() => + this.adapter.count(className, { fields: {} }, null, "", false) + ) .then(count => { if (count > 0) { throw new Parse.Error( @@ -1397,7 +1559,7 @@ class DatabaseController { .then(wasParseCollection => { if (wasParseCollection) { const relationFieldNames = Object.keys(schema.fields).filter( - fieldName => schema.fields[fieldName].type === 'Relation' + fieldName => schema.fields[fieldName].type === "Relation" ); return Promise.all( relationFieldNames.map(name => @@ -1418,7 +1580,9 @@ class DatabaseController { // key value pairs used in query objects. Each key value pair will represented // in a similar way to json objectToEntriesStrings(query: any): Array { - return Object.entries(query).map(a => a.map(s => JSON.stringify(s)).join(':')); + return Object.entries(query).map(a => + a.map(s => JSON.stringify(s)).join(":") + ); } // Naive logic reducer for OR operations meant to be used only for pointer permissions. @@ -1432,7 +1596,8 @@ class DatabaseController { repeat = false; for (let i = 0; i < queries.length - 1; i++) { for (let j = i + 1; j < queries.length; j++) { - const [shorter, longer] = queries[i].length > queries[j].length ? [j, i] : [i, j]; + const [shorter, longer] = + queries[i].length > queries[j].length ? [j, i] : [i, j]; const foundEntries = queries[shorter].reduce( (acc, entry) => acc + (queries[longer].includes(entry) ? 1 : 0), 0 @@ -1467,7 +1632,8 @@ class DatabaseController { repeat = false; for (let i = 0; i < queries.length - 1; i++) { for (let j = i + 1; j < queries.length; j++) { - const [shorter, longer] = queries[i].length > queries[j].length ? [j, i] : [i, j]; + const [shorter, longer] = + queries[i].length > queries[j].length ? [j, i] : [i, j]; const foundEntries = queries[shorter].reduce( (acc, entry) => acc + (queries[longer].includes(entry) ? 1 : 0), 0 @@ -1511,11 +1677,13 @@ class DatabaseController { const perms = schema.getClassLevelPermissions(className); const userACL = aclGroup.filter(acl => { - return acl.indexOf('role:') != 0 && acl != '*'; + return acl.indexOf("role:") != 0 && acl != "*"; }); const groupKey = - ['get', 'find', 'count'].indexOf(operation) > -1 ? 'readUserFields' : 'writeUserFields'; + ["get", "find", "count"].indexOf(operation) > -1 + ? "readUserFields" + : "writeUserFields"; const permFields = []; @@ -1540,8 +1708,8 @@ class DatabaseController { } const userId = userACL[0]; const userPointer = { - __type: 'Pointer', - className: '_User', + __type: "Pointer", + className: "_User", objectId: userId, }; @@ -1549,20 +1717,20 @@ class DatabaseController { const fieldDescriptor = schema.getExpectedType(className, key); const fieldType = fieldDescriptor && - typeof fieldDescriptor === 'object' && - Object.prototype.hasOwnProperty.call(fieldDescriptor, 'type') + typeof fieldDescriptor === "object" && + Object.prototype.hasOwnProperty.call(fieldDescriptor, "type") ? fieldDescriptor.type : null; let queryClause; - if (fieldType === 'Pointer') { + if (fieldType === "Pointer") { // constraint for single pointer setup queryClause = { [key]: userPointer }; - } else if (fieldType === 'Array') { + } else if (fieldType === "Array") { // constraint for users-array setup queryClause = { [key]: { $all: [userPointer] } }; - } else if (fieldType === 'Object') { + } else if (fieldType === "Object") { // constraint for object setup queryClause = { [key]: userPointer }; } else { @@ -1580,7 +1748,9 @@ class DatabaseController { return Object.assign({}, query, queryClause); }); - return queries.length === 1 ? queries[0] : this.reduceOrOperation({ $or: queries }); + return queries.length === 1 + ? queries[0] + : this.reduceOrOperation({ $or: queries }); } else { return query; } @@ -1635,7 +1805,7 @@ class DatabaseController { for (const key in protectedFields) { // skip userFields - if (key.startsWith('userField:')) { + if (key.startsWith("userField:")) { if (preserveKeys) { const fieldName = key.substring(10); if (!preserveKeys.includes(fieldName)) { @@ -1649,19 +1819,19 @@ class DatabaseController { } // add public tier - if (key === '*') { + if (key === "*") { protectedKeysSets.push(protectedFields[key]); continue; } if (authenticated) { - if (key === 'authenticated') { + if (key === "authenticated") { // for logged in users protectedKeysSets.push(protectedFields[key]); continue; } - if (roles[key] && key.startsWith('role:')) { + if (roles[key] && key.startsWith("role:")) { // add applicable roles protectedKeysSets.push(roles[key]); } @@ -1699,27 +1869,33 @@ class DatabaseController { } createTransactionalSession() { - return this.adapter.createTransactionalSession().then(transactionalSession => { - this._transactionalSession = transactionalSession; - }); + return this.adapter + .createTransactionalSession() + .then(transactionalSession => { + this._transactionalSession = transactionalSession; + }); } commitTransactionalSession() { if (!this._transactionalSession) { - throw new Error('There is no transactional session to commit'); + throw new Error("There is no transactional session to commit"); } - return this.adapter.commitTransactionalSession(this._transactionalSession).then(() => { - this._transactionalSession = null; - }); + return this.adapter + .commitTransactionalSession(this._transactionalSession) + .then(() => { + this._transactionalSession = null; + }); } abortTransactionalSession() { if (!this._transactionalSession) { - throw new Error('There is no transactional session to abort'); + throw new Error("There is no transactional session to abort"); } - return this.adapter.abortTransactionalSession(this._transactionalSession).then(() => { - this._transactionalSession = null; - }); + return this.adapter + .abortTransactionalSession(this._transactionalSession) + .then(() => { + this._transactionalSession = null; + }); } // TODO: create indexes on first creation of a _User object. Otherwise it's impossible to @@ -1746,45 +1922,74 @@ class DatabaseController { ...SchemaController.defaultColumns._Idempotency, }, }; - await this.loadSchema().then(schema => schema.enforceClassExists('_User')); - await this.loadSchema().then(schema => schema.enforceClassExists('_Role')); - await this.loadSchema().then(schema => schema.enforceClassExists('_Idempotency')); + await this.loadSchema().then(schema => schema.enforceClassExists("_User")); + await this.loadSchema().then(schema => schema.enforceClassExists("_Role")); + await this.loadSchema().then(schema => + schema.enforceClassExists("_Idempotency") + ); - await this.adapter.ensureUniqueness('_User', requiredUserFields, ['username']).catch(error => { - logger.warn('Unable to ensure uniqueness for usernames: ', error); - throw error; - }); + await this.adapter + .ensureUniqueness("_User", requiredUserFields, ["username"]) + .catch(error => { + logger.warn("Unable to ensure uniqueness for usernames: ", error); + throw error; + }); if (!this.options.enableCollationCaseComparison) { await this.adapter - .ensureIndex('_User', requiredUserFields, ['username'], 'case_insensitive_username', true) + .ensureIndex( + "_User", + requiredUserFields, + ["username"], + "case_insensitive_username", + true + ) .catch(error => { - logger.warn('Unable to create case insensitive username index: ', error); + logger.warn( + "Unable to create case insensitive username index: ", + error + ); throw error; }); await this.adapter - .ensureIndex('_User', requiredUserFields, ['email'], 'case_insensitive_email', true) + .ensureIndex( + "_User", + requiredUserFields, + ["email"], + "case_insensitive_email", + true + ) .catch(error => { - logger.warn('Unable to create case insensitive email index: ', error); + logger.warn("Unable to create case insensitive email index: ", error); throw error; }); } - await this.adapter.ensureUniqueness('_User', requiredUserFields, ['email']).catch(error => { - logger.warn('Unable to ensure uniqueness for user email addresses: ', error); - throw error; - }); + await this.adapter + .ensureUniqueness("_User", requiredUserFields, ["email"]) + .catch(error => { + logger.warn( + "Unable to ensure uniqueness for user email addresses: ", + error + ); + throw error; + }); - await this.adapter.ensureUniqueness('_Role', requiredRoleFields, ['name']).catch(error => { - logger.warn('Unable to ensure uniqueness for role name: ', error); - throw error; - }); + await this.adapter + .ensureUniqueness("_Role", requiredRoleFields, ["name"]) + .catch(error => { + logger.warn("Unable to ensure uniqueness for role name: ", error); + throw error; + }); await this.adapter - .ensureUniqueness('_Idempotency', requiredIdempotencyFields, ['reqId']) + .ensureUniqueness("_Idempotency", requiredIdempotencyFields, ["reqId"]) .catch(error => { - logger.warn('Unable to ensure uniqueness for idempotency request ID: ', error); + logger.warn( + "Unable to ensure uniqueness for idempotency request ID: ", + error + ); throw error; }); @@ -1801,9 +2006,19 @@ class DatabaseController { options.setIdempotencyFunction = true; } await this.adapter - .ensureIndex('_Idempotency', requiredIdempotencyFields, ['expire'], 'ttl', false, options) + .ensureIndex( + "_Idempotency", + requiredIdempotencyFields, + ["expire"], + "ttl", + false, + options + ) .catch(error => { - logger.warn('Unable to create TTL index for idempotency expire date: ', error); + logger.warn( + "Unable to create TTL index for idempotency expire date: ", + error + ); throw error; }); } @@ -1811,13 +2026,13 @@ class DatabaseController { } _expandResultOnKeyPath(object: any, key: string, value: any): any { - if (key.indexOf('.') < 0) { + if (key.indexOf(".") < 0) { object[key] = value[key]; return object; } - const path = key.split('.'); + const path = key.split("."); const firstKey = path[0]; - const nextPath = path.slice(1).join('.'); + const nextPath = path.slice(1).join("."); // Scan request data for denied keywords if (this.options && this.options.requestKeywordDenylist) { @@ -1856,18 +2071,26 @@ class DatabaseController { // determine if that was an op if ( keyUpdate && - typeof keyUpdate === 'object' && + typeof keyUpdate === "object" && keyUpdate.__op && - ['Add', 'AddUnique', 'Remove', 'Increment', 'SetOnInsert'].indexOf(keyUpdate.__op) > -1 + ["Add", "AddUnique", "Remove", "Increment", "SetOnInsert"].indexOf( + keyUpdate.__op + ) > -1 ) { // only valid ops that produce an actionable result // the op may have happened on a keypath this._expandResultOnKeyPath(response, key, result); // Revert array to object conversion on dot notation for arrays (e.g. "field.0.key") - if (key.includes('.')) { - const [field, index] = key.split('.'); - const isArrayIndex = Array.from(index).every(c => c >= '0' && c <= '9'); - if (isArrayIndex && Array.isArray(result[field]) && !Array.isArray(response[field])) { + if (key.includes(".")) { + const [field, index] = key.split("."); + const isArrayIndex = Array.from(index).every( + c => c >= "0" && c <= "9" + ); + if ( + isArrayIndex && + Array.isArray(result[field]) && + !Array.isArray(response[field]) + ) { response[field] = result[field]; } } @@ -1877,7 +2100,17 @@ class DatabaseController { } static _validateQuery: (any, boolean, boolean, boolean) => void; - static filterSensitiveData: (boolean, boolean, any[], any, any, any, string, any[], any) => void; + static filterSensitiveData: ( + boolean, + boolean, + any[], + any, + any, + any, + string, + any[], + any + ) => void; } module.exports = DatabaseController; diff --git a/src/Controllers/FilesController.js b/src/Controllers/FilesController.js index 21ab5efe4c..d6cccc402e 100644 --- a/src/Controllers/FilesController.js +++ b/src/Controllers/FilesController.js @@ -1,12 +1,12 @@ // FilesController.js -import { randomHexString } from '../cryptoUtils'; -import AdaptableController from './AdaptableController'; -import { validateFilename, FilesAdapter } from '../Adapters/Files/FilesAdapter'; -import path from 'path'; -const Parse = require('parse').Parse; +import { randomHexString } from "../cryptoUtils"; +import AdaptableController from "./AdaptableController"; +import { validateFilename, FilesAdapter } from "../Adapters/Files/FilesAdapter"; +import path from "path"; +const Parse = require("parse").Parse; const legacyFilesRegex = new RegExp( - '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}-.*' + "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}-.*" ); export class FilesController extends AdaptableController { @@ -18,15 +18,15 @@ export class FilesController extends AdaptableController { const extname = path.extname(filename); const hasExtension = extname.length > 0; - const mime = (await import('mime')).default; + const mime = (await import("mime")).default; if (!hasExtension && contentType && mime.getExtension(contentType)) { - filename = filename + '.' + mime.getExtension(contentType); + filename = filename + "." + mime.getExtension(contentType); } else if (hasExtension && !contentType) { contentType = mime.getType(filename); } if (!this.options.preserveFileName) { - filename = randomHexString(32) + '_' + filename; + filename = randomHexString(32) + "_" + filename; } const location = await this.adapter.getFileLocation(config, filename); @@ -42,7 +42,7 @@ export class FilesController extends AdaptableController { } getMetadata(filename) { - if (typeof this.adapter.getMetadata === 'function') { + if (typeof this.adapter.getMetadata === "function") { return this.adapter.getMetadata(filename); } return Promise.resolve({}); @@ -59,30 +59,42 @@ export class FilesController extends AdaptableController { await Promise.all(promises); return; } - if (typeof object !== 'object') { + if (typeof object !== "object") { return; } for (const key in object) { const fileObject = object[key]; - if (fileObject && fileObject['__type'] === 'File') { - if (fileObject['url']) { + if (fileObject && fileObject["__type"] === "File") { + if (fileObject["url"]) { continue; } - const filename = fileObject['name']; + const filename = fileObject["name"]; // all filenames starting with "tfss-" should be from files.parsetfss.com // all filenames starting with a "-" seperated UUID should be from files.parse.com // all other filenames have been migrated or created from Parse Server if (config.fileKey === undefined) { - fileObject['url'] = await this.adapter.getFileLocation(config, filename); + fileObject["url"] = await this.adapter.getFileLocation( + config, + filename + ); } else { - if (filename.indexOf('tfss-') === 0) { - fileObject['url'] = - 'http://files.parsetfss.com/' + config.fileKey + '/' + encodeURIComponent(filename); + if (filename.indexOf("tfss-") === 0) { + fileObject["url"] = + "http://files.parsetfss.com/" + + config.fileKey + + "/" + + encodeURIComponent(filename); } else if (legacyFilesRegex.test(filename)) { - fileObject['url'] = - 'http://files.parse.com/' + config.fileKey + '/' + encodeURIComponent(filename); + fileObject["url"] = + "http://files.parse.com/" + + config.fileKey + + "/" + + encodeURIComponent(filename); } else { - fileObject['url'] = await this.adapter.getFileLocation(config, filename); + fileObject["url"] = await this.adapter.getFileLocation( + config, + filename + ); } } } @@ -98,9 +110,9 @@ export class FilesController extends AdaptableController { } validateFilename(filename) { - if (typeof this.adapter.validateFilename === 'function') { + if (typeof this.adapter.validateFilename === "function") { const error = this.adapter.validateFilename(filename); - if (typeof error !== 'string') { + if (typeof error !== "string") { return error; } return new Parse.Error(Parse.Error.INVALID_FILE_NAME, error); diff --git a/src/Controllers/HooksController.js b/src/Controllers/HooksController.js index 277104ef32..4999913f78 100644 --- a/src/Controllers/HooksController.js +++ b/src/Controllers/HooksController.js @@ -1,15 +1,15 @@ /** @flow weak */ -import * as triggers from '../triggers'; +import * as triggers from "../triggers"; // @flow-disable-next -import * as Parse from 'parse/node'; +import * as Parse from "parse/node"; // @flow-disable-next -import request from '../request'; -import { logger } from '../logger'; -import http from 'http'; -import https from 'https'; +import request from "../request"; +import { logger } from "../logger"; +import http from "http"; +import https from "https"; -const DefaultHooksCollectionName = '_Hooks'; +const DefaultHooksCollectionName = "_Hooks"; const HTTPAgents = { http: new http.Agent({ keepAlive: true }), https: new https.Agent({ keepAlive: true }), @@ -36,7 +36,9 @@ export class HooksController { } getFunction(functionName) { - return this._getHooks({ functionName: functionName }).then(results => results[0]); + return this._getHooks({ functionName: functionName }).then( + results => results[0] + ); } getFunctions() { @@ -71,12 +73,14 @@ export class HooksController { } _getHooks(query = {}) { - return this.database.find(DefaultHooksCollectionName, query).then(results => { - return results.map(result => { - delete result.objectId; - return result; + return this.database + .find(DefaultHooksCollectionName, query) + .then(results => { + return results.map(result => { + delete result.objectId; + return result; + }); }); - }); } _removeHooks(query) { @@ -92,7 +96,7 @@ export class HooksController { } else if (hook.triggerName && hook.className && hook.url) { query = { className: hook.className, triggerName: hook.triggerName }; } else { - throw new Parse.Error(143, 'invalid hook declaration'); + throw new Parse.Error(143, "invalid hook declaration"); } return this.database .update(DefaultHooksCollectionName, query, hook, { upsert: true }) @@ -105,9 +109,19 @@ export class HooksController { var wrappedFunction = wrapToHTTPRequest(hook, this._webhookKey); wrappedFunction.url = hook.url; if (hook.className) { - triggers.addTrigger(hook.triggerName, hook.className, wrappedFunction, this._applicationId); + triggers.addTrigger( + hook.triggerName, + hook.className, + wrappedFunction, + this._applicationId + ); } else { - triggers.addFunction(hook.functionName, wrappedFunction, null, this._applicationId); + triggers.addFunction( + hook.functionName, + wrappedFunction, + null, + this._applicationId + ); } } @@ -134,7 +148,7 @@ export class HooksController { hook.url = aHook.url; hook.triggerName = aHook.triggerName; } else { - throw new Parse.Error(143, 'invalid hook declaration'); + throw new Parse.Error(143, "invalid hook declaration"); } return this.addHook(hook); @@ -144,24 +158,29 @@ export class HooksController { if (aHook.functionName) { return this.getFunction(aHook.functionName).then(result => { if (result) { - throw new Parse.Error(143, `function name: ${aHook.functionName} already exists`); + throw new Parse.Error( + 143, + `function name: ${aHook.functionName} already exists` + ); } else { return this.createOrUpdateHook(aHook); } }); } else if (aHook.className && aHook.triggerName) { - return this.getTrigger(aHook.className, aHook.triggerName).then(result => { - if (result) { - throw new Parse.Error( - 143, - `class ${aHook.className} already has trigger ${aHook.triggerName}` - ); + return this.getTrigger(aHook.className, aHook.triggerName).then( + result => { + if (result) { + throw new Parse.Error( + 143, + `class ${aHook.className} already has trigger ${aHook.triggerName}` + ); + } + return this.createOrUpdateHook(aHook); } - return this.createOrUpdateHook(aHook); - }); + ); } - throw new Parse.Error(143, 'invalid hook declaration'); + throw new Parse.Error(143, "invalid hook declaration"); } updateHook(aHook) { @@ -170,17 +189,22 @@ export class HooksController { if (result) { return this.createOrUpdateHook(aHook); } - throw new Parse.Error(143, `no function named: ${aHook.functionName} is defined`); + throw new Parse.Error( + 143, + `no function named: ${aHook.functionName} is defined` + ); }); } else if (aHook.className && aHook.triggerName) { - return this.getTrigger(aHook.className, aHook.triggerName).then(result => { - if (result) { - return this.createOrUpdateHook(aHook); + return this.getTrigger(aHook.className, aHook.triggerName).then( + result => { + if (result) { + return this.createOrUpdateHook(aHook); + } + throw new Parse.Error(143, `class ${aHook.className} does not exist`); } - throw new Parse.Error(143, `class ${aHook.className} does not exist`); - }); + ); } - throw new Parse.Error(143, 'invalid hook declaration'); + throw new Parse.Error(143, "invalid hook declaration"); } } @@ -201,31 +225,35 @@ function wrapToHTTPRequest(hook, key) { const jsonRequest: any = { url: hook.url, headers: { - 'Content-Type': 'application/json', + "Content-Type": "application/json", }, body: jsonBody, - method: 'POST', + method: "POST", }; - const agent = hook.url.startsWith('https') ? HTTPAgents['https'] : HTTPAgents['http']; + const agent = hook.url.startsWith("https") + ? HTTPAgents["https"] + : HTTPAgents["http"]; jsonRequest.agent = agent; if (key) { - jsonRequest.headers['X-Parse-Webhook-Key'] = key; + jsonRequest.headers["X-Parse-Webhook-Key"] = key; } else { - logger.warn('Making outgoing webhook request without webhookKey being set!'); + logger.warn( + "Making outgoing webhook request without webhookKey being set!" + ); } return request(jsonRequest).then(response => { let err; let result; let body = response.data; if (body) { - if (typeof body === 'string') { + if (typeof body === "string") { try { body = JSON.parse(body); } catch (e) { err = { - error: 'Malformed response', + error: "Malformed response", code: -1, partialResponse: body.substring(0, 100), }; @@ -238,8 +266,8 @@ function wrapToHTTPRequest(hook, key) { } if (err) { throw err; - } else if (hook.triggerName === 'beforeSave') { - if (typeof result === 'object') { + } else if (hook.triggerName === "beforeSave") { + if (typeof result === "object") { delete result.createdAt; delete result.updatedAt; delete result.className; diff --git a/src/Controllers/LoggerController.js b/src/Controllers/LoggerController.js index 6dac43518f..3d641cad04 100644 --- a/src/Controllers/LoggerController.js +++ b/src/Controllers/LoggerController.js @@ -1,29 +1,37 @@ -import { Parse } from 'parse/node'; -import AdaptableController from './AdaptableController'; -import { LoggerAdapter } from '../Adapters/Logger/LoggerAdapter'; +import { Parse } from "parse/node"; +import AdaptableController from "./AdaptableController"; +import { LoggerAdapter } from "../Adapters/Logger/LoggerAdapter"; const MILLISECONDS_IN_A_DAY = 24 * 60 * 60 * 1000; const LOG_STRING_TRUNCATE_LENGTH = 1000; -const truncationMarker = '... (truncated)'; +const truncationMarker = "... (truncated)"; export const LogLevel = { - INFO: 'info', - ERROR: 'error', + INFO: "info", + ERROR: "error", }; export const LogOrder = { - DESCENDING: 'desc', - ASCENDING: 'asc', + DESCENDING: "desc", + ASCENDING: "asc", }; -export const logLevels = ['error', 'warn', 'info', 'debug', 'verbose', 'silly', 'silent']; +export const logLevels = [ + "error", + "warn", + "info", + "debug", + "verbose", + "silly", + "silent", +]; export class LoggerController extends AdaptableController { - constructor(adapter, appId, options = { logLevel: 'info' }) { + constructor(adapter, appId, options = { logLevel: "info" }) { super(adapter, appId, options); - let level = 'info'; + let level = "info"; if (options.verbose) { - level = 'verbose'; + level = "verbose"; } if (options.logLevel) { level = options.logLevel; @@ -38,18 +46,18 @@ export class LoggerController extends AdaptableController { } maskSensitiveUrl(path) { - const urlString = 'http://localhost' + path; // prepend dummy string to make a real URL + const urlString = "http://localhost" + path; // prepend dummy string to make a real URL const urlObj = new URL(urlString); const query = urlObj.searchParams; - let sanitizedQuery = '?'; + let sanitizedQuery = "?"; for (const [key, value] of query) { - if (key !== 'password') { + if (key !== "password") { // normal value - sanitizedQuery += key + '=' + value + '&'; + sanitizedQuery += key + "=" + value + "&"; } else { // password value, redact it - sanitizedQuery += key + '=' + '********' + '&'; + sanitizedQuery += key + "=" + "********" + "&"; } } @@ -66,7 +74,7 @@ export class LoggerController extends AdaptableController { return e; } - if (typeof e === 'string') { + if (typeof e === "string") { return e.replace(/(password".?:.?")[^"]*"/g, '$1********"'); } // else it is an object... @@ -74,12 +82,12 @@ export class LoggerController extends AdaptableController { // check the url if (e.url) { // for strings - if (typeof e.url === 'string') { + if (typeof e.url === "string") { e.url = this.maskSensitiveUrl(e.url); } else if (Array.isArray(e.url)) { // for strings in array e.url = e.url.map(item => { - if (typeof item === 'string') { + if (typeof item === "string") { return this.maskSensitiveUrl(item); } @@ -90,8 +98,8 @@ export class LoggerController extends AdaptableController { if (e.body) { for (const key of Object.keys(e.body)) { - if (key === 'password') { - e.body[key] = '********'; + if (key === "password") { + e.body[key] = "********"; break; } } @@ -99,8 +107,8 @@ export class LoggerController extends AdaptableController { if (e.params) { for (const key of Object.keys(e.params)) { - if (key === 'password') { - e.params[key] = '********'; + if (key === "password") { + e.params[key] = "********"; break; } } @@ -116,7 +124,7 @@ export class LoggerController extends AdaptableController { args = [].concat( level, args.map(arg => { - if (typeof arg === 'function') { + if (typeof arg === "function") { return arg(); } return arg; @@ -126,27 +134,27 @@ export class LoggerController extends AdaptableController { } info() { - return this.log('info', arguments); + return this.log("info", arguments); } error() { - return this.log('error', arguments); + return this.log("error", arguments); } warn() { - return this.log('warn', arguments); + return this.log("warn", arguments); } verbose() { - return this.log('verbose', arguments); + return this.log("verbose", arguments); } debug() { - return this.log('debug', arguments); + return this.log("debug", arguments); } silly() { - return this.log('silly', arguments); + return this.log("silly", arguments); } logRequest({ method, url, headers, body }) { @@ -189,7 +197,8 @@ export class LoggerController extends AdaptableController { truncateLogMessage(string) { if (string && string.length > LOG_STRING_TRUNCATE_LENGTH) { - const truncated = string.substring(0, LOG_STRING_TRUNCATE_LENGTH) + truncationMarker; + const truncated = + string.substring(0, LOG_STRING_TRUNCATE_LENGTH) + truncationMarker; return truncated; } @@ -223,12 +232,15 @@ export class LoggerController extends AdaptableController { // size (optional) Number of rows returned by search. Defaults to 10 getLogs(options = {}) { if (!this.adapter) { - throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED, 'Logger adapter is not available'); + throw new Parse.Error( + Parse.Error.PUSH_MISCONFIGURED, + "Logger adapter is not available" + ); } - if (typeof this.adapter.query !== 'function') { + if (typeof this.adapter.query !== "function") { throw new Parse.Error( Parse.Error.PUSH_MISCONFIGURED, - 'Querying logs is not supported with this adapter' + "Querying logs is not supported with this adapter" ); } options = LoggerController.parseOptions(options); diff --git a/src/Controllers/SchemaController.js b/src/Controllers/SchemaController.js index ef3b96e594..baeb9a1994 100644 --- a/src/Controllers/SchemaController.js +++ b/src/Controllers/SchemaController.js @@ -15,176 +15,176 @@ // different databases. // TODO: hide all schema logic inside the database adapter. // @flow-disable-next -const Parse = require('parse/node').Parse; -import { StorageAdapter } from '../Adapters/Storage/StorageAdapter'; -import SchemaCache from '../Adapters/Cache/SchemaCache'; -import DatabaseController from './DatabaseController'; -import Config from '../Config'; +const Parse = require("parse/node").Parse; +import { StorageAdapter } from "../Adapters/Storage/StorageAdapter"; +import SchemaCache from "../Adapters/Cache/SchemaCache"; +import DatabaseController from "./DatabaseController"; +import Config from "../Config"; // @flow-disable-next -import deepcopy from 'deepcopy'; +import deepcopy from "deepcopy"; import type { Schema, SchemaFields, ClassLevelPermissions, SchemaField, LoadSchemaOptions, -} from './types'; +} from "./types"; const defaultColumns: { [string]: SchemaFields } = Object.freeze({ // Contain the default columns for every parse object type (except _Join collection) _Default: { - objectId: { type: 'String' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - ACL: { type: 'ACL' }, + objectId: { type: "String" }, + createdAt: { type: "Date" }, + updatedAt: { type: "Date" }, + ACL: { type: "ACL" }, }, // The additional default columns for the _User collection (in addition to DefaultCols) _User: { - username: { type: 'String' }, - password: { type: 'String' }, - email: { type: 'String' }, - emailVerified: { type: 'Boolean' }, - authData: { type: 'Object' }, + username: { type: "String" }, + password: { type: "String" }, + email: { type: "String" }, + emailVerified: { type: "Boolean" }, + authData: { type: "Object" }, }, // The additional default columns for the _Installation collection (in addition to DefaultCols) _Installation: { - installationId: { type: 'String' }, - deviceToken: { type: 'String' }, - channels: { type: 'Array' }, - deviceType: { type: 'String' }, - pushType: { type: 'String' }, - GCMSenderId: { type: 'String' }, - timeZone: { type: 'String' }, - localeIdentifier: { type: 'String' }, - badge: { type: 'Number' }, - appVersion: { type: 'String' }, - appName: { type: 'String' }, - appIdentifier: { type: 'String' }, - parseVersion: { type: 'String' }, + installationId: { type: "String" }, + deviceToken: { type: "String" }, + channels: { type: "Array" }, + deviceType: { type: "String" }, + pushType: { type: "String" }, + GCMSenderId: { type: "String" }, + timeZone: { type: "String" }, + localeIdentifier: { type: "String" }, + badge: { type: "Number" }, + appVersion: { type: "String" }, + appName: { type: "String" }, + appIdentifier: { type: "String" }, + parseVersion: { type: "String" }, }, // The additional default columns for the _Role collection (in addition to DefaultCols) _Role: { - name: { type: 'String' }, - users: { type: 'Relation', targetClass: '_User' }, - roles: { type: 'Relation', targetClass: '_Role' }, + name: { type: "String" }, + users: { type: "Relation", targetClass: "_User" }, + roles: { type: "Relation", targetClass: "_Role" }, }, // The additional default columns for the _Session collection (in addition to DefaultCols) _Session: { - user: { type: 'Pointer', targetClass: '_User' }, - installationId: { type: 'String' }, - sessionToken: { type: 'String' }, - expiresAt: { type: 'Date' }, - createdWith: { type: 'Object' }, + user: { type: "Pointer", targetClass: "_User" }, + installationId: { type: "String" }, + sessionToken: { type: "String" }, + expiresAt: { type: "Date" }, + createdWith: { type: "Object" }, }, _Product: { - productIdentifier: { type: 'String' }, - download: { type: 'File' }, - downloadName: { type: 'String' }, - icon: { type: 'File' }, - order: { type: 'Number' }, - title: { type: 'String' }, - subtitle: { type: 'String' }, + productIdentifier: { type: "String" }, + download: { type: "File" }, + downloadName: { type: "String" }, + icon: { type: "File" }, + order: { type: "Number" }, + title: { type: "String" }, + subtitle: { type: "String" }, }, _PushStatus: { - pushTime: { type: 'String' }, - source: { type: 'String' }, // rest or webui - query: { type: 'String' }, // the stringified JSON query - payload: { type: 'String' }, // the stringified JSON payload, - title: { type: 'String' }, - expiry: { type: 'Number' }, - expiration_interval: { type: 'Number' }, - status: { type: 'String' }, - numSent: { type: 'Number' }, - numFailed: { type: 'Number' }, - pushHash: { type: 'String' }, - errorMessage: { type: 'Object' }, - sentPerType: { type: 'Object' }, - failedPerType: { type: 'Object' }, - sentPerUTCOffset: { type: 'Object' }, - failedPerUTCOffset: { type: 'Object' }, - count: { type: 'Number' }, // tracks # of batches queued and pending + pushTime: { type: "String" }, + source: { type: "String" }, // rest or webui + query: { type: "String" }, // the stringified JSON query + payload: { type: "String" }, // the stringified JSON payload, + title: { type: "String" }, + expiry: { type: "Number" }, + expiration_interval: { type: "Number" }, + status: { type: "String" }, + numSent: { type: "Number" }, + numFailed: { type: "Number" }, + pushHash: { type: "String" }, + errorMessage: { type: "Object" }, + sentPerType: { type: "Object" }, + failedPerType: { type: "Object" }, + sentPerUTCOffset: { type: "Object" }, + failedPerUTCOffset: { type: "Object" }, + count: { type: "Number" }, // tracks # of batches queued and pending }, _JobStatus: { - jobName: { type: 'String' }, - source: { type: 'String' }, - status: { type: 'String' }, - message: { type: 'String' }, - params: { type: 'Object' }, // params received when calling the job - finishedAt: { type: 'Date' }, + jobName: { type: "String" }, + source: { type: "String" }, + status: { type: "String" }, + message: { type: "String" }, + params: { type: "Object" }, // params received when calling the job + finishedAt: { type: "Date" }, }, _JobSchedule: { - jobName: { type: 'String' }, - description: { type: 'String' }, - params: { type: 'String' }, - startAfter: { type: 'String' }, - daysOfWeek: { type: 'Array' }, - timeOfDay: { type: 'String' }, - lastRun: { type: 'Number' }, - repeatMinutes: { type: 'Number' }, + jobName: { type: "String" }, + description: { type: "String" }, + params: { type: "String" }, + startAfter: { type: "String" }, + daysOfWeek: { type: "Array" }, + timeOfDay: { type: "String" }, + lastRun: { type: "Number" }, + repeatMinutes: { type: "Number" }, }, _Hooks: { - functionName: { type: 'String' }, - className: { type: 'String' }, - triggerName: { type: 'String' }, - url: { type: 'String' }, + functionName: { type: "String" }, + className: { type: "String" }, + triggerName: { type: "String" }, + url: { type: "String" }, }, _GlobalConfig: { - objectId: { type: 'String' }, - params: { type: 'Object' }, - masterKeyOnly: { type: 'Object' }, + objectId: { type: "String" }, + params: { type: "Object" }, + masterKeyOnly: { type: "Object" }, }, _GraphQLConfig: { - objectId: { type: 'String' }, - config: { type: 'Object' }, + objectId: { type: "String" }, + config: { type: "Object" }, }, _Audience: { - objectId: { type: 'String' }, - name: { type: 'String' }, - query: { type: 'String' }, //storing query as JSON string to prevent "Nested keys should not contain the '$' or '.' characters" error - lastUsed: { type: 'Date' }, - timesUsed: { type: 'Number' }, + objectId: { type: "String" }, + name: { type: "String" }, + query: { type: "String" }, //storing query as JSON string to prevent "Nested keys should not contain the '$' or '.' characters" error + lastUsed: { type: "Date" }, + timesUsed: { type: "Number" }, }, _Idempotency: { - reqId: { type: 'String' }, - expire: { type: 'Date' }, + reqId: { type: "String" }, + expire: { type: "Date" }, }, }); // fields required for read or write operations on their respective classes. const requiredColumns = Object.freeze({ read: { - _User: ['username'], + _User: ["username"], }, write: { - _Product: ['productIdentifier', 'icon', 'order', 'title', 'subtitle'], - _Role: ['name', 'ACL'], + _Product: ["productIdentifier", "icon", "order", "title", "subtitle"], + _Role: ["name", "ACL"], }, }); -const invalidColumns = ['length']; +const invalidColumns = ["length"]; const systemClasses = Object.freeze([ - '_User', - '_Installation', - '_Role', - '_Session', - '_Product', - '_PushStatus', - '_JobStatus', - '_JobSchedule', - '_Audience', - '_Idempotency', + "_User", + "_Installation", + "_Role", + "_Session", + "_Product", + "_PushStatus", + "_JobStatus", + "_JobSchedule", + "_Audience", + "_Idempotency", ]); const volatileClasses = Object.freeze([ - '_JobStatus', - '_PushStatus', - '_Hooks', - '_GlobalConfig', - '_GraphQLConfig', - '_JobSchedule', - '_Audience', - '_Idempotency', + "_JobStatus", + "_PushStatus", + "_Hooks", + "_GlobalConfig", + "_GraphQLConfig", + "_JobSchedule", + "_Audience", + "_Idempotency", ]); // Anything that start with role @@ -255,21 +255,25 @@ function validateProtectedFieldsKey(key, userIdRegExp) { } const CLPValidKeys = Object.freeze([ - 'ACL', - 'find', - 'count', - 'get', - 'create', - 'update', - 'delete', - 'addField', - 'readUserFields', - 'writeUserFields', - 'protectedFields', + "ACL", + "find", + "count", + "get", + "create", + "update", + "delete", + "addField", + "readUserFields", + "writeUserFields", + "protectedFields", ]); // validation before setting class-level permissions on collection -function validateCLP(perms: ClassLevelPermissions, fields: SchemaFields, userIdRegExp: RegExp) { +function validateCLP( + perms: ClassLevelPermissions, + fields: SchemaFields, + userIdRegExp: RegExp +) { if (!perms) { return; } @@ -287,7 +291,10 @@ function validateCLP(perms: ClassLevelPermissions, fields: SchemaFields, userIdR // throws when root fields are of wrong type validateCLPjson(operation, operationKey); - if (operationKey === 'readUserFields' || operationKey === 'writeUserFields') { + if ( + operationKey === "readUserFields" || + operationKey === "writeUserFields" + ) { // validate grouped pointer permissions // must be an array with field names for (const fieldName of operation) { @@ -299,7 +306,7 @@ function validateCLP(perms: ClassLevelPermissions, fields: SchemaFields, userIdR } // validate protected fields - if (operationKey === 'protectedFields') { + if (operationKey === "protectedFields") { for (const entity in operation) { // throws on unexpected key validateProtectedFieldsKey(entity, userIdRegExp); @@ -348,7 +355,7 @@ function validateCLP(perms: ClassLevelPermissions, fields: SchemaFields, userIdR // entity can be either: // "pointerFields": string[] - if (entity === 'pointerFields') { + if (entity === "pointerFields") { const pointerFields = operation[entity]; if (Array.isArray(pointerFields)) { @@ -367,26 +374,30 @@ function validateCLP(perms: ClassLevelPermissions, fields: SchemaFields, userIdR const permit = operation[entity]; - if (operationKey === 'ACL') { - if (Object.prototype.toString.call(permit) !== '[object Object]') { + if (operationKey === "ACL") { + if (Object.prototype.toString.call(permit) !== "[object Object]") { throw new Parse.Error( Parse.Error.INVALID_JSON, `'${permit}' is not a valid value for class level permissions acl` ); } - const invalidKeys = Object.keys(permit).filter(key => !['read', 'write'].includes(key)); - const invalidValues = Object.values(permit).filter(key => typeof key !== 'boolean'); + const invalidKeys = Object.keys(permit).filter( + key => !["read", "write"].includes(key) + ); + const invalidValues = Object.values(permit).filter( + key => typeof key !== "boolean" + ); if (invalidKeys.length) { throw new Parse.Error( Parse.Error.INVALID_JSON, - `'${invalidKeys.join(',')}' is not a valid key for class level permissions acl` + `'${invalidKeys.join(",")}' is not a valid key for class level permissions acl` ); } if (invalidValues.length) { throw new Parse.Error( Parse.Error.INVALID_JSON, - `'${invalidValues.join(',')}' is not a valid value for class level permissions acl` + `'${invalidValues.join(",")}' is not a valid value for class level permissions acl` ); } } else if (permit !== true) { @@ -400,7 +411,7 @@ function validateCLP(perms: ClassLevelPermissions, fields: SchemaFields, userIdR } function validateCLPjson(operation: any, operationKey: string) { - if (operationKey === 'readUserFields' || operationKey === 'writeUserFields') { + if (operationKey === "readUserFields" || operationKey === "writeUserFields") { if (!Array.isArray(operation)) { throw new Parse.Error( Parse.Error.INVALID_JSON, @@ -408,7 +419,7 @@ function validateCLPjson(operation: any, operationKey: string) { ); } } else { - if (typeof operation === 'object' && operation !== null) { + if (typeof operation === "object" && operation !== null) { // ok to proceed return; } else { @@ -420,7 +431,11 @@ function validateCLPjson(operation: any, operationKey: string) { } } -function validatePointerPermission(fieldName: string, fields: Object, operation: string) { +function validatePointerPermission( + fieldName: string, + fields: Object, + operation: string +) { // Uses collection schema to ensure the field is of type: // - Pointer<_User> (pointers) // - Array @@ -431,8 +446,9 @@ function validatePointerPermission(fieldName: string, fields: Object, operation: if ( !( fields[fieldName] && - ((fields[fieldName].type == 'Pointer' && fields[fieldName].targetClass == '_User') || - fields[fieldName].type == 'Array') + ((fields[fieldName].type == "Pointer" && + fields[fieldName].targetClass == "_User") || + fields[fieldName].type == "Array") ) ) { throw new Parse.Error( @@ -459,16 +475,21 @@ function classNameIsValid(className: string): boolean { // Valid fields must be alpha-numeric, and not start with an underscore or number // must not be a reserved key function fieldNameIsValid(fieldName: string, className: string): boolean { - if (className && className !== '_Hooks') { - if (fieldName === 'className') { + if (className && className !== "_Hooks") { + if (fieldName === "className") { return false; } } - return classAndFieldRegex.test(fieldName) && !invalidColumns.includes(fieldName); + return ( + classAndFieldRegex.test(fieldName) && !invalidColumns.includes(fieldName) + ); } // Checks that it's not trying to clobber one of the default fields of the class. -function fieldNameIsValidForClass(fieldName: string, className: string): boolean { +function fieldNameIsValidForClass( + fieldName: string, + className: string +): boolean { if (!fieldNameIsValid(fieldName, className)) { return false; } @@ -483,43 +504,52 @@ function fieldNameIsValidForClass(fieldName: string, className: string): boolean function invalidClassNameMessage(className: string): string { return ( - 'Invalid classname: ' + + "Invalid classname: " + className + - ', classnames can only have alphanumeric characters and _, and must start with an alpha character ' + ", classnames can only have alphanumeric characters and _, and must start with an alpha character " ); } -const invalidJsonError = new Parse.Error(Parse.Error.INVALID_JSON, 'invalid JSON'); +const invalidJsonError = new Parse.Error( + Parse.Error.INVALID_JSON, + "invalid JSON" +); const validNonRelationOrPointerTypes = [ - 'Number', - 'String', - 'Boolean', - 'Date', - 'Object', - 'Array', - 'GeoPoint', - 'File', - 'Bytes', - 'Polygon', + "Number", + "String", + "Boolean", + "Date", + "Object", + "Array", + "GeoPoint", + "File", + "Bytes", + "Polygon", ]; // Returns an error suitable for throwing if the type is invalid const fieldTypeIsInvalid = ({ type, targetClass }) => { - if (['Pointer', 'Relation'].indexOf(type) >= 0) { + if (["Pointer", "Relation"].indexOf(type) >= 0) { if (!targetClass) { return new Parse.Error(135, `type ${type} needs a class name`); - } else if (typeof targetClass !== 'string') { + } else if (typeof targetClass !== "string") { return invalidJsonError; } else if (!classNameIsValid(targetClass)) { - return new Parse.Error(Parse.Error.INVALID_CLASS_NAME, invalidClassNameMessage(targetClass)); + return new Parse.Error( + Parse.Error.INVALID_CLASS_NAME, + invalidClassNameMessage(targetClass) + ); } else { return undefined; } } - if (typeof type !== 'string') { + if (typeof type !== "string") { return invalidJsonError; } if (validNonRelationOrPointerTypes.indexOf(type) < 0) { - return new Parse.Error(Parse.Error.INCORRECT_TYPE, `invalid field type: ${type}`); + return new Parse.Error( + Parse.Error.INCORRECT_TYPE, + `invalid field type: ${type}` + ); } return undefined; }; @@ -527,12 +557,12 @@ const fieldTypeIsInvalid = ({ type, targetClass }) => { const convertSchemaToAdapterSchema = (schema: any) => { schema = injectDefaultSchema(schema); delete schema.fields.ACL; - schema.fields._rperm = { type: 'Array' }; - schema.fields._wperm = { type: 'Array' }; + schema.fields._rperm = { type: "Array" }; + schema.fields._wperm = { type: "Array" }; - if (schema.className === '_User') { + if (schema.className === "_User") { delete schema.fields.password; - schema.fields._hashed_password = { type: 'String' }; + schema.fields._hashed_password = { type: "String" }; } return schema; @@ -542,12 +572,12 @@ const convertAdapterSchemaToParseSchema = ({ ...schema }) => { delete schema.fields._rperm; delete schema.fields._wperm; - schema.fields.ACL = { type: 'ACL' }; + schema.fields.ACL = { type: "ACL" }; - if (schema.className === '_User') { + if (schema.className === "_User") { delete schema.fields.authData; //Auth data is implicit delete schema.fields._hashed_password; - schema.fields.password = { type: 'String' }; + schema.fields.password = { type: "String" }; } if (schema.indexes && Object.keys(schema.indexes).length === 0) { @@ -575,14 +605,16 @@ class SchemaData { data.classLevelPermissions = deepcopy(schema.classLevelPermissions); data.indexes = schema.indexes; - const classProtectedFields = this.__protectedFields[schema.className]; + const classProtectedFields = + this.__protectedFields[schema.className]; if (classProtectedFields) { for (const key in classProtectedFields) { const unq = new Set([ ...(data.classLevelPermissions.protectedFields[key] || []), ...classProtectedFields[key], ]); - data.classLevelPermissions.protectedFields[key] = Array.from(unq); + data.classLevelPermissions.protectedFields[key] = + Array.from(unq); } } @@ -616,7 +648,12 @@ class SchemaData { } } -const injectDefaultSchema = ({ className, fields, classLevelPermissions, indexes }: Schema) => { +const injectDefaultSchema = ({ + className, + fields, + classLevelPermissions, + indexes, +}: Schema) => { const defaultSchema: Schema = { className, fields: { @@ -632,46 +669,46 @@ const injectDefaultSchema = ({ className, fields, classLevelPermissions, indexes return defaultSchema; }; -const _HooksSchema = { className: '_Hooks', fields: defaultColumns._Hooks }; +const _HooksSchema = { className: "_Hooks", fields: defaultColumns._Hooks }; const _GlobalConfigSchema = { - className: '_GlobalConfig', + className: "_GlobalConfig", fields: defaultColumns._GlobalConfig, }; const _GraphQLConfigSchema = { - className: '_GraphQLConfig', + className: "_GraphQLConfig", fields: defaultColumns._GraphQLConfig, }; const _PushStatusSchema = convertSchemaToAdapterSchema( injectDefaultSchema({ - className: '_PushStatus', + className: "_PushStatus", fields: {}, classLevelPermissions: {}, }) ); const _JobStatusSchema = convertSchemaToAdapterSchema( injectDefaultSchema({ - className: '_JobStatus', + className: "_JobStatus", fields: {}, classLevelPermissions: {}, }) ); const _JobScheduleSchema = convertSchemaToAdapterSchema( injectDefaultSchema({ - className: '_JobSchedule', + className: "_JobSchedule", fields: {}, classLevelPermissions: {}, }) ); const _AudienceSchema = convertSchemaToAdapterSchema( injectDefaultSchema({ - className: '_Audience', + className: "_Audience", fields: defaultColumns._Audience, classLevelPermissions: {}, }) ); const _IdempotencySchema = convertSchemaToAdapterSchema( injectDefaultSchema({ - className: '_Idempotency', + className: "_Idempotency", fields: defaultColumns._Idempotency, classLevelPermissions: {}, }) @@ -687,7 +724,10 @@ const VolatileClassesSchemas = [ _IdempotencySchema, ]; -const dbTypeMatchesObjectType = (dbType: SchemaField | string, objectType: SchemaField) => { +const dbTypeMatchesObjectType = ( + dbType: SchemaField | string, + objectType: SchemaField +) => { if (dbType.type !== objectType.type) { return false; } @@ -704,7 +744,7 @@ const dbTypeMatchesObjectType = (dbType: SchemaField | string, objectType: Schem }; const typeToString = (type: SchemaField | string): string => { - if (typeof type === 'string') { + if (typeof type === "string") { return type; } if (type.targetClass) { @@ -779,7 +819,9 @@ export default class SchemaController { return this.reloadDataPromise; } - async getAllClasses(options: LoadSchemaOptions = { clearCache: false }): Promise> { + async getAllClasses( + options: LoadSchemaOptions = { clearCache: false } + ): Promise> { if (options.clearCache) { return this.setAllClasses(); } @@ -823,7 +865,9 @@ export default class SchemaController { return Promise.resolve(cached); } return this.setAllClasses().then(allSchemas => { - const oneSchema = allSchemas.find(schema => schema.className === className); + const oneSchema = allSchemas.find( + schema => schema.className === className + ); if (!oneSchema) { return Promise.reject(undefined); } @@ -844,12 +888,18 @@ export default class SchemaController { classLevelPermissions: any, indexes: any = {} ): Promise { - var validationError = this.validateNewClass(className, fields, classLevelPermissions); + var validationError = this.validateNewClass( + className, + fields, + classLevelPermissions + ); if (validationError) { if (validationError instanceof Parse.Error) { return Promise.reject(validationError); } else if (validationError.code && validationError.error) { - return Promise.reject(new Parse.Error(validationError.code, validationError.error)); + return Promise.reject( + new Parse.Error(validationError.code, validationError.error) + ); } return Promise.reject(validationError); } @@ -869,7 +919,10 @@ export default class SchemaController { return parseSchema; } catch (error) { if (error && error.code === Parse.Error.DUPLICATE_VALUE) { - throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, `Class ${className} already exists.`); + throw new Parse.Error( + Parse.Error.INVALID_CLASS_NAME, + `Class ${className} already exists.` + ); } else { throw error; } @@ -891,19 +944,26 @@ export default class SchemaController { if ( existingFields[name] && existingFields[name].type !== field.type && - field.__op !== 'Delete' + field.__op !== "Delete" ) { throw new Parse.Error(255, `Field ${name} exists, cannot update.`); } - if (!existingFields[name] && field.__op === 'Delete') { - throw new Parse.Error(255, `Field ${name} does not exist, cannot delete.`); + if (!existingFields[name] && field.__op === "Delete") { + throw new Parse.Error( + 255, + `Field ${name} does not exist, cannot delete.` + ); } }); delete existingFields._rperm; delete existingFields._wperm; - const newSchema = buildMergedSchemaObject(existingFields, submittedFields); - const defaultFields = defaultColumns[className] || defaultColumns._Default; + const newSchema = buildMergedSchemaObject( + existingFields, + submittedFields + ); + const defaultFields = + defaultColumns[className] || defaultColumns._Default; const fullNewSchema = Object.assign({}, newSchema, defaultFields); const validationError = this.validateSchemaData( className, @@ -920,7 +980,7 @@ export default class SchemaController { const deletedFields: string[] = []; const insertedFields = []; Object.keys(submittedFields).forEach(fieldName => { - if (submittedFields[fieldName].__op === 'Delete') { + if (submittedFields[fieldName].__op === "Delete") { deletedFields.push(fieldName); } else { insertedFields.push(fieldName); @@ -944,7 +1004,11 @@ export default class SchemaController { }) .then(results => { enforceFields = results.filter(result => !!result); - return this.setPermissions(className, classLevelPermissions, newSchema); + return this.setPermissions( + className, + classLevelPermissions, + newSchema + ); }) .then(() => this._dbAdapter.setIndexesWithSchemaFormat( @@ -1005,19 +1069,32 @@ export default class SchemaController { if (this.schemaData[className]) { return this; } else { - throw new Parse.Error(Parse.Error.INVALID_JSON, `Failed to add ${className}`); + throw new Parse.Error( + Parse.Error.INVALID_JSON, + `Failed to add ${className}` + ); } }) .catch(() => { // The schema still doesn't validate. Give up - throw new Parse.Error(Parse.Error.INVALID_JSON, 'schema class name does not revalidate'); + throw new Parse.Error( + Parse.Error.INVALID_JSON, + "schema class name does not revalidate" + ); }) ); } - validateNewClass(className: string, fields: SchemaFields = {}, classLevelPermissions: any): any { + validateNewClass( + className: string, + fields: SchemaFields = {}, + classLevelPermissions: any + ): any { if (this.schemaData[className]) { - throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, `Class ${className} already exists.`); + throw new Parse.Error( + Parse.Error.INVALID_CLASS_NAME, + `Class ${className} already exists.` + ); } if (!classNameIsValid(className)) { return { @@ -1025,7 +1102,12 @@ export default class SchemaController { error: invalidClassNameMessage(className), }; } - return this.validateSchemaData(className, fields, classLevelPermissions, []); + return this.validateSchemaData( + className, + fields, + classLevelPermissions, + [] + ); } validateSchemaData( @@ -1039,13 +1121,13 @@ export default class SchemaController { if (!fieldNameIsValid(fieldName, className)) { return { code: Parse.Error.INVALID_KEY_NAME, - error: 'invalid field name: ' + fieldName, + error: "invalid field name: " + fieldName, }; } if (!fieldNameIsValidForClass(fieldName, className)) { return { code: 136, - error: 'field ' + fieldName + ' cannot be added', + error: "field " + fieldName + " cannot be added", }; } const fieldType = fields[fieldName]; @@ -1055,9 +1137,12 @@ export default class SchemaController { } if (fieldType.defaultValue !== undefined) { let defaultValueType = getType(fieldType.defaultValue); - if (typeof defaultValueType === 'string') { + if (typeof defaultValueType === "string") { defaultValueType = { type: defaultValueType }; - } else if (typeof defaultValueType === 'object' && fieldType.type === 'Relation') { + } else if ( + typeof defaultValueType === "object" && + fieldType.type === "Relation" + ) { return { code: Parse.Error.INCORRECT_TYPE, error: `The 'default value' option is not applicable for ${typeToString(fieldType)}`, @@ -1072,7 +1157,7 @@ export default class SchemaController { }; } } else if (fieldType.required) { - if (typeof fieldType === 'object' && fieldType.type === 'Relation') { + if (typeof fieldType === "object" && fieldType.type === "Relation") { return { code: Parse.Error.INCORRECT_TYPE, error: `The 'required' option is not applicable for ${typeToString(fieldType)}`, @@ -1087,17 +1172,17 @@ export default class SchemaController { } const geoPoints = Object.keys(fields).filter( - key => fields[key] && fields[key].type === 'GeoPoint' + key => fields[key] && fields[key].type === "GeoPoint" ); if (geoPoints.length > 1) { return { code: Parse.Error.INCORRECT_TYPE, error: - 'currently, only one GeoPoint field may exist in an object. Adding ' + + "currently, only one GeoPoint field may exist in an object. Adding " + geoPoints[1] + - ' when ' + + " when " + geoPoints[0] + - ' already exists.', + " already exists.", }; } validateCLP(classLevelPermissions, fields, this.userIdRegEx); @@ -1105,7 +1190,7 @@ export default class SchemaController { // Sets the Class-level permissions for a given className, which must exist. async setPermissions(className: string, perms: any, newSchema: SchemaFields) { - if (typeof perms === 'undefined') { + if (typeof perms === "undefined") { return Promise.resolve(); } validateCLP(perms, newSchema, this.userIdRegEx); @@ -1127,25 +1212,31 @@ export default class SchemaController { isValidation?: boolean, maintenance?: boolean ) { - if (fieldName.indexOf('.') > 0) { + if (fieldName.indexOf(".") > 0) { // "." for Nested Arrays // "." for Nested Objects // JSON Arrays are treated as Nested Objects - const [x, y] = fieldName.split('.'); + const [x, y] = fieldName.split("."); fieldName = x; - const isArrayIndex = Array.from(y).every(c => c >= '0' && c <= '9'); - if (isArrayIndex && !['sentPerUTCOffset', 'failedPerUTCOffset'].includes(fieldName)) { - type = 'Array'; + const isArrayIndex = Array.from(y).every(c => c >= "0" && c <= "9"); + if ( + isArrayIndex && + !["sentPerUTCOffset", "failedPerUTCOffset"].includes(fieldName) + ) { + type = "Array"; } else { - type = 'Object'; + type = "Object"; } } let fieldNameToValidate = `${fieldName}`; - if (maintenance && fieldNameToValidate.charAt(0) === '_') { + if (maintenance && fieldNameToValidate.charAt(0) === "_") { fieldNameToValidate = fieldNameToValidate.substring(1); } if (!fieldNameIsValid(fieldNameToValidate, className)) { - throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid field name: ${fieldName}.`); + throw new Parse.Error( + Parse.Error.INVALID_KEY_NAME, + `Invalid field name: ${fieldName}.` + ); } // If someone tries to create a new field with null/undefined as the value, return; @@ -1154,13 +1245,13 @@ export default class SchemaController { } const expectedType = this.getExpectedType(className, fieldName); - if (typeof type === 'string') { + if (typeof type === "string") { type = ({ type }: SchemaField); } if (type.defaultValue !== undefined) { let defaultValueType = getType(type.defaultValue); - if (typeof defaultValueType === 'string') { + if (typeof defaultValueType === "string") { defaultValueType = { type: defaultValueType }; } if (!dbTypeMatchesObjectType(type, defaultValueType)) { @@ -1184,7 +1275,10 @@ export default class SchemaController { } // If type options do not change // we can safely return - if (isValidation || JSON.stringify(expectedType) === JSON.stringify(type)) { + if ( + isValidation || + JSON.stringify(expectedType) === JSON.stringify(type) + ) { return undefined; } // Field options are may be changed @@ -1218,17 +1312,24 @@ export default class SchemaController { const { className, fieldName } = fields[i]; let { type } = fields[i]; const expectedType = this.getExpectedType(className, fieldName); - if (typeof type === 'string') { + if (typeof type === "string") { type = { type: type }; } if (!expectedType || !dbTypeMatchesObjectType(expectedType, type)) { - throw new Parse.Error(Parse.Error.INVALID_JSON, `Could not add field ${fieldName}`); + throw new Parse.Error( + Parse.Error.INVALID_JSON, + `Could not add field ${fieldName}` + ); } } } // maintain compatibility - deleteField(fieldName: string, className: string, database: DatabaseController) { + deleteField( + fieldName: string, + className: string, + database: DatabaseController + ) { return this.deleteFields([fieldName], className, database); } @@ -1239,14 +1340,24 @@ export default class SchemaController { // Passing the database and prefix is necessary in order to drop relation collections // and remove fields from objects. Ideally the database would belong to // a database adapter and this function would close over it or access it via member. - deleteFields(fieldNames: Array, className: string, database: DatabaseController) { + deleteFields( + fieldNames: Array, + className: string, + database: DatabaseController + ) { if (!classNameIsValid(className)) { - throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, invalidClassNameMessage(className)); + throw new Parse.Error( + Parse.Error.INVALID_CLASS_NAME, + invalidClassNameMessage(className) + ); } fieldNames.forEach(fieldName => { if (!fieldNameIsValid(fieldName, className)) { - throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `invalid field name: ${fieldName}`); + throw new Parse.Error( + Parse.Error.INVALID_KEY_NAME, + `invalid field name: ${fieldName}` + ); } //Don't allow deleting the default fields. if (!fieldNameIsValidForClass(fieldName, className)) { @@ -1268,23 +1379,30 @@ export default class SchemaController { .then(schema => { fieldNames.forEach(fieldName => { if (!schema.fields[fieldName]) { - throw new Parse.Error(255, `Field ${fieldName} does not exist, cannot delete.`); + throw new Parse.Error( + 255, + `Field ${fieldName} does not exist, cannot delete.` + ); } }); const schemaFields = { ...schema.fields }; - return database.adapter.deleteFields(className, schema, fieldNames).then(() => { - return Promise.all( - fieldNames.map(fieldName => { - const field = schemaFields[fieldName]; - if (field && field.type === 'Relation') { - //For relations, drop the _Join table - return database.adapter.deleteClass(`_Join:${fieldName}:${className}`); - } - return Promise.resolve(); - }) - ); - }); + return database.adapter + .deleteFields(className, schema, fieldNames) + .then(() => { + return Promise.all( + fieldNames.map(fieldName => { + const field = schemaFields[fieldName]; + if (field && field.type === "Relation") { + //For relations, drop the _Join table + return database.adapter.deleteClass( + `_Join:${fieldName}:${className}` + ); + } + return Promise.resolve(); + }) + ); + }); }) .then(() => { SchemaCache.clear(); @@ -1294,20 +1412,25 @@ export default class SchemaController { // Validates an object provided in REST format. // Returns a promise that resolves to the new schema if this object is // valid. - async validateObject(className: string, object: any, query: any, maintenance: boolean) { + async validateObject( + className: string, + object: any, + query: any, + maintenance: boolean + ) { let geocount = 0; const schema = await this.enforceClassExists(className); const promises = []; for (const fieldName in object) { - if (object[fieldName] && getType(object[fieldName]) === 'GeoPoint') { + if (object[fieldName] && getType(object[fieldName]) === "GeoPoint") { geocount++; } if (geocount > 1) { return Promise.reject( new Parse.Error( Parse.Error.INCORRECT_TYPE, - 'there can only be one geopoint field in a class' + "there can only be one geopoint field in a class" ) ); } @@ -1320,11 +1443,19 @@ export default class SchemaController { if (!expected) { continue; } - if (fieldName === 'ACL') { + if (fieldName === "ACL") { // Every object has ACL implicitly. continue; } - promises.push(schema.enforceFieldExists(className, fieldName, expected, true, maintenance)); + promises.push( + schema.enforceFieldExists( + className, + fieldName, + expected, + true, + maintenance + ) + ); } const results = await Promise.all(promises); const enforceFields = results.filter(result => !!result); @@ -1348,9 +1479,9 @@ export default class SchemaController { const missingColumns = columns.filter(function (column) { if (query && query.objectId) { - if (object[column] && typeof object[column] === 'object') { + if (object[column] && typeof object[column] === "object") { // Trying to delete a required column - return object[column].__op == 'Delete'; + return object[column].__op == "Delete"; } // Not trying to do anything there return false; @@ -1359,12 +1490,19 @@ export default class SchemaController { }); if (missingColumns.length > 0) { - throw new Parse.Error(Parse.Error.INCORRECT_TYPE, missingColumns[0] + ' is required.'); + throw new Parse.Error( + Parse.Error.INCORRECT_TYPE, + missingColumns[0] + " is required." + ); } return Promise.resolve(this); } - testPermissionsForClassName(className: string, aclGroup: string[], operation: string) { + testPermissionsForClassName( + className: string, + aclGroup: string[], + operation: string + ) { return SchemaController.testPermissions( this.getClassLevelPermissions(className), aclGroup, @@ -1373,12 +1511,16 @@ export default class SchemaController { } // Tests that the class level permission let pass the operation for a given aclGroup - static testPermissions(classPermissions: ?any, aclGroup: string[], operation: string): boolean { + static testPermissions( + classPermissions: ?any, + aclGroup: string[], + operation: string + ): boolean { if (!classPermissions || !classPermissions[operation]) { return true; } const perms = classPermissions[operation]; - if (perms['*']) { + if (perms["*"]) { return true; } // Check permissions against the aclGroup provided (array of userId/roles) @@ -1400,7 +1542,9 @@ export default class SchemaController { operation: string, action?: string ) { - if (SchemaController.testPermissions(classPermissions, aclGroup, operation)) { + if ( + SchemaController.testPermissions(classPermissions, aclGroup, operation) + ) { return Promise.resolve(); } @@ -1410,17 +1554,17 @@ export default class SchemaController { const perms = classPermissions[operation]; // If only for authenticated users // make sure we have an aclGroup - if (perms['requiresAuthentication']) { + if (perms["requiresAuthentication"]) { // If aclGroup has * (public) if (!aclGroup || aclGroup.length == 0) { throw new Parse.Error( Parse.Error.OBJECT_NOT_FOUND, - 'Permission denied, user needs to be authenticated.' + "Permission denied, user needs to be authenticated." ); - } else if (aclGroup.indexOf('*') > -1 && aclGroup.length == 1) { + } else if (aclGroup.indexOf("*") > -1 && aclGroup.length == 1) { throw new Parse.Error( Parse.Error.OBJECT_NOT_FOUND, - 'Permission denied, user needs to be authenticated.' + "Permission denied, user needs to be authenticated." ); } // requiresAuthentication passed, just move forward @@ -1431,10 +1575,12 @@ export default class SchemaController { // No matching CLP, let's check the Pointer permissions // And handle those later const permissionField = - ['get', 'find', 'count'].indexOf(operation) > -1 ? 'readUserFields' : 'writeUserFields'; + ["get", "find", "count"].indexOf(operation) > -1 + ? "readUserFields" + : "writeUserFields"; // Reject create when write lockdown - if (permissionField == 'writeUserFields' && operation == 'create') { + if (permissionField == "writeUserFields" && operation == "create") { throw new Parse.Error( Parse.Error.OPERATION_FORBIDDEN, `Permission denied for action ${operation} on class ${className}.` @@ -1452,7 +1598,7 @@ export default class SchemaController { const pointerFields = classPermissions[operation].pointerFields; if (Array.isArray(pointerFields) && pointerFields.length > 0) { // any op except 'addField as part of create' is ok. - if (operation !== 'addField' || action === 'update') { + if (operation !== "addField" || action === "update") { // We can allow adding field on update flow only. return Promise.resolve(); } @@ -1465,7 +1611,12 @@ export default class SchemaController { } // Validates an operation passes class-level-permissions set in the schema - validatePermission(className: string, aclGroup: string[], operation: string, action?: string) { + validatePermission( + className: string, + aclGroup: string[], + operation: string, + action?: string + ) { return SchemaController.validatePermission( this.getClassLevelPermissions(className), className, @@ -1476,15 +1627,21 @@ export default class SchemaController { } getClassLevelPermissions(className: string): any { - return this.schemaData[className] && this.schemaData[className].classLevelPermissions; + return ( + this.schemaData[className] && + this.schemaData[className].classLevelPermissions + ); } // Returns the expected type for a className+key combination // or undefined if the schema is not set - getExpectedType(className: string, fieldName: string): ?(SchemaField | string) { + getExpectedType( + className: string, + fieldName: string + ): ?(SchemaField | string) { if (this.schemaData[className]) { const expectedType = this.schemaData[className].fields[fieldName]; - return expectedType === 'map' ? 'Object' : expectedType; + return expectedType === "map" ? "Object" : expectedType; } return undefined; } @@ -1499,7 +1656,10 @@ export default class SchemaController { } // Returns a promise for a new Schema. -const load = (dbAdapter: StorageAdapter, options: any): Promise => { +const load = ( + dbAdapter: StorageAdapter, + options: any +): Promise => { const schema = new SchemaController(dbAdapter); ttl.duration = dbAdapter.schemaCacheTtl; return schema.reloadData(options).then(() => schema); @@ -1510,7 +1670,10 @@ const load = (dbAdapter: StorageAdapter, options: any): Promise 0 && sysSchemaField.indexOf(oldField) !== -1) { + if ( + sysSchemaField.length > 0 && + sysSchemaField.indexOf(oldField) !== -1 + ) { continue; } - const fieldIsDeleted = putRequest[oldField] && putRequest[oldField].__op === 'Delete'; + const fieldIsDeleted = + putRequest[oldField] && putRequest[oldField].__op === "Delete"; if (!fieldIsDeleted) { newSchema[oldField] = existingFields[oldField]; } } } for (const newField in putRequest) { - if (newField !== 'objectId' && putRequest[newField].__op !== 'Delete') { - if (sysSchemaField.length > 0 && sysSchemaField.indexOf(newField) !== -1) { + if (newField !== "objectId" && putRequest[newField].__op !== "Delete") { + if ( + sysSchemaField.length > 0 && + sysSchemaField.indexOf(newField) !== -1 + ) { continue; } newSchema[newField] = putRequest[newField]; @@ -1561,23 +1731,23 @@ function thenValidateRequiredColumns(schemaPromise, className, object, query) { function getType(obj: any): ?(SchemaField | string) { const type = typeof obj; switch (type) { - case 'boolean': - return 'Boolean'; - case 'string': - return 'String'; - case 'number': - return 'Number'; - case 'map': - case 'object': + case "boolean": + return "Boolean"; + case "string": + return "String"; + case "number": + return "Number"; + case "map": + case "object": if (!obj) { return undefined; } return getObjectType(obj); - case 'function': - case 'symbol': - case 'undefined': + case "function": + case "symbol": + case "undefined": default: - throw 'bad obj: ' + obj; + throw "bad obj: " + obj; } } @@ -1586,80 +1756,83 @@ function getType(obj: any): ?(SchemaField | string) { // Returns null if the type is unknown. function getObjectType(obj): ?(SchemaField | string) { if (obj instanceof Array) { - return 'Array'; + return "Array"; } if (obj.__type) { switch (obj.__type) { - case 'Pointer': + case "Pointer": if (obj.className) { return { - type: 'Pointer', + type: "Pointer", targetClass: obj.className, }; } break; - case 'Relation': + case "Relation": if (obj.className) { return { - type: 'Relation', + type: "Relation", targetClass: obj.className, }; } break; - case 'File': + case "File": if (obj.name) { - return 'File'; + return "File"; } break; - case 'Date': + case "Date": if (obj.iso) { - return 'Date'; + return "Date"; } break; - case 'GeoPoint': + case "GeoPoint": if (obj.latitude != null && obj.longitude != null) { - return 'GeoPoint'; + return "GeoPoint"; } break; - case 'Bytes': + case "Bytes": if (obj.base64) { - return 'Bytes'; + return "Bytes"; } break; - case 'Polygon': + case "Polygon": if (obj.coordinates) { - return 'Polygon'; + return "Polygon"; } break; } - throw new Parse.Error(Parse.Error.INCORRECT_TYPE, 'This is not a valid ' + obj.__type); + throw new Parse.Error( + Parse.Error.INCORRECT_TYPE, + "This is not a valid " + obj.__type + ); } - if (obj['$ne']) { - return getObjectType(obj['$ne']); + if (obj["$ne"]) { + return getObjectType(obj["$ne"]); } if (obj.__op) { switch (obj.__op) { - case 'Increment': - return 'Number'; - case 'Delete': + case "Increment": + return "Number"; + case "Delete": return null; - case 'Add': - case 'AddUnique': - case 'Remove': - return 'Array'; - case 'AddRelation': - case 'RemoveRelation': + case "Add": + case "AddUnique": + case "Remove": + return "Array"; + case "AddRelation": + case "RemoveRelation": return { - type: 'Relation', + type: "Relation", targetClass: obj.objects[0].className, }; - case 'Batch': + case "Batch": return getObjectType(obj.ops[0]); default: - throw 'unexpected op: ' + obj.__op; + throw "unexpected op: " + obj.__op; } } - return 'Object'; + return "Object"; } export { diff --git a/src/Controllers/UserController.js b/src/Controllers/UserController.js index 2a6796052f..a2f51e186e 100644 --- a/src/Controllers/UserController.js +++ b/src/Controllers/UserController.js @@ -1,14 +1,14 @@ -import { randomString } from '../cryptoUtils'; -import { inflate } from '../triggers'; -import AdaptableController from './AdaptableController'; -import MailAdapter from '../Adapters/Email/MailAdapter'; -import rest from '../rest'; -import Parse from 'parse/node'; -import AccountLockout from '../AccountLockout'; -import Config from '../Config'; - -var RestQuery = require('../RestQuery'); -var Auth = require('../Auth'); +import { randomString } from "../cryptoUtils"; +import { inflate } from "../triggers"; +import AdaptableController from "./AdaptableController"; +import MailAdapter from "../Adapters/Email/MailAdapter"; +import rest from "../rest"; +import Parse from "parse/node"; +import AccountLockout from "../AccountLockout"; +import Config from "../Config"; + +var RestQuery = require("../RestQuery"); +var Auth = require("../Auth"); export class UserController extends AdaptableController { constructor(adapter, appId, options = {}) { @@ -38,7 +38,7 @@ export class UserController extends AdaptableController { async setEmailVerifyToken(user, req, storage = {}) { const shouldSendEmail = this.shouldVerifyEmails === true || - (typeof this.shouldVerifyEmails === 'function' && + (typeof this.shouldVerifyEmails === "function" && (await Promise.resolve(this.shouldVerifyEmails(req))) === true); if (!shouldSendEmail) { return false; @@ -47,7 +47,7 @@ export class UserController extends AdaptableController { user._email_verify_token = randomString(25); if ( !storage.fieldsChangedByTrigger || - !storage.fieldsChangedByTrigger.includes('emailVerified') + !storage.fieldsChangedByTrigger.includes("emailVerified") ) { user.emailVerified = false; } @@ -70,7 +70,7 @@ export class UserController extends AdaptableController { const query = { _email_verify_token: token }; const updateFields = { emailVerified: true, - _email_verify_token: { __op: 'Delete' }, + _email_verify_token: { __op: "Delete" }, }; // if the email verify token needs to be validated then @@ -79,14 +79,14 @@ export class UserController extends AdaptableController { query.emailVerified = false; query._email_verify_token_expires_at = { $gt: Parse._encode(new Date()) }; - updateFields._email_verify_token_expires_at = { __op: 'Delete' }; + updateFields._email_verify_token_expires_at = { __op: "Delete" }; } const maintenanceAuth = Auth.maintenance(this.config); const restQuery = await RestQuery({ method: RestQuery.Method.get, config: this.config, auth: maintenanceAuth, - className: '_User', + className: "_User", restWhere: query, }); @@ -94,12 +94,18 @@ export class UserController extends AdaptableController { if (result.results.length) { query.objectId = result.results[0].objectId; } - return await rest.update(this.config, maintenanceAuth, '_User', query, updateFields); + return await rest.update( + this.config, + maintenanceAuth, + "_User", + query, + updateFields + ); } async checkResetTokenValidity(token) { const results = await this.config.database.find( - '_User', + "_User", { _perishable_token: token, }, @@ -107,16 +113,19 @@ export class UserController extends AdaptableController { Auth.maintenance(this.config) ); if (results.length !== 1) { - throw 'Failed to reset password: username / email / token is invalid'; + throw "Failed to reset password: username / email / token is invalid"; } - if (this.config.passwordPolicy && this.config.passwordPolicy.resetTokenValidityDuration) { + if ( + this.config.passwordPolicy && + this.config.passwordPolicy.resetTokenValidityDuration + ) { let expiresDate = results[0]._perishable_token_expires_at; - if (expiresDate && expiresDate.__type == 'Date') { + if (expiresDate && expiresDate.__type == "Date") { expiresDate = new Date(expiresDate.iso); } if (expiresDate < new Date()) { - throw 'The password reset link has expired'; + throw "The password reset link has expired"; } } @@ -140,7 +149,7 @@ export class UserController extends AdaptableController { config: this.config, runBeforeFind: false, auth: Auth.master(this.config), - className: '_User', + className: "_User", restWhere: where, }); const result = await query.execute(); @@ -159,10 +168,10 @@ export class UserController extends AdaptableController { // from this point onwards; do not use the `user` as it may not contain all fields. const fetchedUser = await this.getUserIfNeeded(user); let shouldSendEmail = this.config.sendUserEmailVerification; - if (typeof shouldSendEmail === 'function') { + if (typeof shouldSendEmail === "function") { const response = await Promise.resolve( this.config.sendUserEmailVerification({ - user: Parse.Object.fromJSON({ className: '_User', ...fetchedUser }), + user: Parse.Object.fromJSON({ className: "_User", ...fetchedUser }), master: req.auth?.isMaster, }) ); @@ -175,7 +184,7 @@ export class UserController extends AdaptableController { const options = { appName: this.config.appName, link: link, - user: inflate('_User', fetchedUser), + user: inflate("_User", fetchedUser), }; if (this.adapter.sendVerificationEmail) { this.adapter.sendVerificationEmail(options); @@ -193,7 +202,10 @@ export class UserController extends AdaptableController { async regenerateEmailVerifyToken(user, master, installationId, ip) { const { _email_verify_token } = user; let { _email_verify_token_expires_at } = user; - if (_email_verify_token_expires_at && _email_verify_token_expires_at.__type === 'Date') { + if ( + _email_verify_token_expires_at && + _email_verify_token_expires_at.__type === "Date" + ) { _email_verify_token_expires_at = _email_verify_token_expires_at.iso; } if ( @@ -205,7 +217,7 @@ export class UserController extends AdaptableController { return Promise.resolve(true); } const shouldSend = await this.setEmailVerifyToken(user, { - object: Parse.User.fromJSON(Object.assign({ className: '_User' }, user)), + object: Parse.User.fromJSON(Object.assign({ className: "_User" }, user)), master, installationId, ip, @@ -214,11 +226,18 @@ export class UserController extends AdaptableController { if (!shouldSend) { return; } - return this.config.database.update('_User', { username: user.username }, user); + return this.config.database.update( + "_User", + { username: user.username }, + user + ); } async resendVerificationEmail(username, req, token) { - const aUser = await this.getUserIfNeeded({ username, _email_verify_token: token }); + const aUser = await this.getUserIfNeeded({ + username, + _email_verify_token: token, + }); if (!aUser || aUser.emailVerified) { throw undefined; } @@ -236,14 +255,17 @@ export class UserController extends AdaptableController { setPasswordResetToken(email) { const token = { _perishable_token: randomString(25) }; - if (this.config.passwordPolicy && this.config.passwordPolicy.resetTokenValidityDuration) { + if ( + this.config.passwordPolicy && + this.config.passwordPolicy.resetTokenValidityDuration + ) { token._perishable_token_expires_at = Parse._encode( this.config.generatePasswordResetTokenExpiresAt() ); } return this.config.database.update( - '_User', + "_User", { $or: [{ email }, { username: email, email: { $exists: false } }] }, token, {}, @@ -253,7 +275,7 @@ export class UserController extends AdaptableController { async sendPasswordResetEmail(email) { if (!this.adapter) { - throw 'Trying to send a reset password but no adapter is set'; + throw "Trying to send a reset password but no adapter is set"; // TODO: No adapter? } let user; @@ -263,11 +285,15 @@ export class UserController extends AdaptableController { this.config.passwordPolicy.resetTokenValidityDuration ) { const results = await this.config.database.find( - '_User', + "_User", { $or: [ { email, _perishable_token: { $exists: true } }, - { username: email, email: { $exists: false }, _perishable_token: { $exists: true } }, + { + username: email, + email: { $exists: false }, + _perishable_token: { $exists: true }, + }, ], }, { limit: 1 }, @@ -275,7 +301,7 @@ export class UserController extends AdaptableController { ); if (results.length == 1) { let expiresDate = results[0]._perishable_token_expires_at; - if (expiresDate && expiresDate.__type == 'Date') { + if (expiresDate && expiresDate.__type == "Date") { expiresDate = new Date(expiresDate.iso); } if (expiresDate > new Date()) { @@ -287,11 +313,15 @@ export class UserController extends AdaptableController { user = await this.setPasswordResetToken(email); } const token = encodeURIComponent(user._perishable_token); - const link = buildEmailLink(this.config.requestResetPasswordURL, token, this.config); + const link = buildEmailLink( + this.config.requestResetPasswordURL, + token, + this.config + ); const options = { appName: this.config.appName, link: link, - user: inflate('_User', user), + user: inflate("_User", user), }; if (this.adapter.sendPasswordResetEmail) { @@ -321,32 +351,34 @@ export class UserController extends AdaptableController { defaultVerificationEmail({ link, user, appName }) { const text = - 'Hi,\n\n' + - 'You are being asked to confirm the e-mail address ' + - user.get('email') + - ' with ' + + "Hi,\n\n" + + "You are being asked to confirm the e-mail address " + + user.get("email") + + " with " + appName + - '\n\n' + - '' + - 'Click here to confirm it:\n' + + "\n\n" + + "" + + "Click here to confirm it:\n" + link; - const to = user.get('email'); - const subject = 'Please verify your e-mail for ' + appName; + const to = user.get("email"); + const subject = "Please verify your e-mail for " + appName; return { text, to, subject }; } defaultResetPasswordEmail({ link, user, appName }) { const text = - 'Hi,\n\n' + - 'You requested to reset your password for ' + + "Hi,\n\n" + + "You requested to reset your password for " + appName + - (user.get('username') ? " (your username is '" + user.get('username') + "')" : '') + - '.\n\n' + - '' + - 'Click here to reset it:\n' + + (user.get("username") + ? " (your username is '" + user.get("username") + "')" + : "") + + ".\n\n" + + "" + + "Click here to reset it:\n" + link; - const to = user.get('email') || user.get('username'); - const subject = 'Password Reset for ' + appName; + const to = user.get("email") || user.get("username"); + const subject = "Password Reset for " + appName; return { text, to, subject }; } } @@ -357,7 +389,7 @@ function updateUserPassword(user, password, config) { .update( config, Auth.master(config), - '_User', + "_User", { objectId: user.objectId }, { password: password, @@ -369,7 +401,10 @@ function updateUserPassword(user, password, config) { function buildEmailLink(destination, token, config) { token = `token=${token}`; if (config.parseFrameURL) { - const destinationWithoutHost = destination.replace(config.publicServerURL, ''); + const destinationWithoutHost = destination.replace( + config.publicServerURL, + "" + ); return `${config.parseFrameURL}?link=${encodeURIComponent(destinationWithoutHost)}&${token}`; } else { diff --git a/src/Deprecator/Deprecations.js b/src/Deprecator/Deprecations.js index 970364432b..98f458b68e 100644 --- a/src/Deprecator/Deprecations.js +++ b/src/Deprecator/Deprecations.js @@ -16,6 +16,6 @@ * If there are no deprecations, this must return an empty array. */ module.exports = [ - { optionKey: 'encodeParseObjectInCloudFunction', changeNewDefault: 'true' }, - { optionKey: 'enableInsecureAuthAdapters', changeNewDefault: 'false' }, + { optionKey: "encodeParseObjectInCloudFunction", changeNewDefault: "true" }, + { optionKey: "enableInsecureAuthAdapters", changeNewDefault: "false" }, ]; diff --git a/src/Deprecator/Deprecator.js b/src/Deprecator/Deprecator.js index 27033c946d..856338feb3 100644 --- a/src/Deprecator/Deprecator.js +++ b/src/Deprecator/Deprecator.js @@ -1,5 +1,5 @@ -import logger from '../logger'; -import Deprecations from './Deprecations'; +import logger from "../logger"; +import Deprecations from "./Deprecations"; /** * The deprecator class. @@ -76,7 +76,7 @@ class Deprecator { static _logGeneric({ usage, solution }) { // Compose message let output = `DeprecationWarning: ${usage} is deprecated and will be removed in a future version.`; - output += solution ? ` ${solution}` : ''; + output += solution ? ` ${solution}` : ""; logger.warn(output); } @@ -94,8 +94,14 @@ class Deprecator { * automatically added to the message. It should only contain the instruction on how * to resolve this warning. */ - static _logOption({ optionKey, envKey, changeNewKey, changeNewDefault, solution }) { - const type = optionKey ? 'option' : 'environment key'; + static _logOption({ + optionKey, + envKey, + changeNewKey, + changeNewDefault, + solution, + }) { + const type = optionKey ? "option" : "environment key"; const key = optionKey ? optionKey : envKey; const keyAction = changeNewKey == null @@ -106,11 +112,13 @@ class Deprecator { // Compose message let output = `DeprecationWarning: The Parse Server ${type} '${key}' `; - output += changeNewKey ? `is deprecated and will be ${keyAction} in a future version.` : ''; + output += changeNewKey + ? `is deprecated and will be ${keyAction} in a future version.` + : ""; output += changeNewDefault ? `default will change to '${changeNewDefault}' in a future version.` - : ''; - output += solution ? ` ${solution}` : ''; + : ""; + output += solution ? ` ${solution}` : ""; logger.warn(output); } } diff --git a/src/GraphQL/parseGraphQLUtils.js b/src/GraphQL/parseGraphQLUtils.js index d7164525a7..bb0da68ea7 100644 --- a/src/GraphQL/parseGraphQLUtils.js +++ b/src/GraphQL/parseGraphQLUtils.js @@ -1,9 +1,12 @@ -import Parse from 'parse/node'; -import { GraphQLError } from 'graphql'; +import Parse from "parse/node"; +import { GraphQLError } from "graphql"; export function enforceMasterKeyAccess(auth) { if (!auth.isMaster) { - throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'unauthorized: master key is required'); + throw new Parse.Error( + Parse.Error.OPERATION_FORBIDDEN, + "unauthorized: master key is required" + ); } } @@ -14,27 +17,29 @@ export function toGraphQLError(error) { message = error.message; } else { code = Parse.Error.INTERNAL_SERVER_ERROR; - message = 'Internal server error'; + message = "Internal server error"; } return new GraphQLError(message, { extensions: { code } }); } export const extractKeysAndInclude = selectedFields => { - selectedFields = selectedFields.filter(field => !field.includes('__typename')); + selectedFields = selectedFields.filter( + field => !field.includes("__typename") + ); // Handles "id" field for both current and included objects selectedFields = selectedFields.map(field => { - if (field === 'id') { - return 'objectId'; + if (field === "id") { + return "objectId"; } - return field.endsWith('.id') - ? `${field.substring(0, field.lastIndexOf('.id'))}.objectId` + return field.endsWith(".id") + ? `${field.substring(0, field.lastIndexOf(".id"))}.objectId` : field; }); let keys = undefined; let include = undefined; if (selectedFields.length > 0) { - keys = [...new Set(selectedFields)].join(','); + keys = [...new Set(selectedFields)].join(","); // We can use this shortcut since optimization is handled // later on RestQuery, avoid overhead here. include = keys; @@ -44,7 +49,7 @@ export const extractKeysAndInclude = selectedFields => { // If authData is detected keys will not work properly // since authData has a special storage behavior // so we need to skip keys currently - keys: keys && keys.indexOf('authData') === -1 ? keys : undefined, + keys: keys && keys.indexOf("authData") === -1 ? keys : undefined, include, }; }; diff --git a/src/LiveQuery/RequestSchema.js b/src/LiveQuery/RequestSchema.js index 6e0a0566b2..5bd1c278bd 100644 --- a/src/LiveQuery/RequestSchema.js +++ b/src/LiveQuery/RequestSchema.js @@ -1,151 +1,151 @@ const general = { - title: 'General request schema', - type: 'object', + title: "General request schema", + type: "object", properties: { op: { - type: 'string', - enum: ['connect', 'subscribe', 'unsubscribe', 'update'], + type: "string", + enum: ["connect", "subscribe", "unsubscribe", "update"], }, }, - required: ['op'], + required: ["op"], }; const connect = { - title: 'Connect operation schema', - type: 'object', + title: "Connect operation schema", + type: "object", properties: { - op: 'connect', + op: "connect", applicationId: { - type: 'string', + type: "string", }, javascriptKey: { - type: 'string', + type: "string", }, masterKey: { - type: 'string', + type: "string", }, clientKey: { - type: 'string', + type: "string", }, windowsKey: { - type: 'string', + type: "string", }, restAPIKey: { - type: 'string', + type: "string", }, sessionToken: { - type: 'string', + type: "string", }, installationId: { - type: 'string', + type: "string", }, }, - required: ['op', 'applicationId'], + required: ["op", "applicationId"], additionalProperties: false, }; const subscribe = { - title: 'Subscribe operation schema', - type: 'object', + title: "Subscribe operation schema", + type: "object", properties: { - op: 'subscribe', + op: "subscribe", requestId: { - type: 'number', + type: "number", }, query: { - title: 'Query field schema', - type: 'object', + title: "Query field schema", + type: "object", properties: { className: { - type: 'string', + type: "string", }, where: { - type: 'object', + type: "object", }, keys: { - type: 'array', + type: "array", items: { - type: 'string', + type: "string", }, minItems: 1, uniqueItems: true, }, watch: { - type: 'array', + type: "array", items: { - type: 'string', + type: "string", }, minItems: 1, uniqueItems: true, }, }, - required: ['where', 'className'], + required: ["where", "className"], additionalProperties: false, }, sessionToken: { - type: 'string', + type: "string", }, }, - required: ['op', 'requestId', 'query'], + required: ["op", "requestId", "query"], additionalProperties: false, }; const update = { - title: 'Update operation schema', - type: 'object', + title: "Update operation schema", + type: "object", properties: { - op: 'update', + op: "update", requestId: { - type: 'number', + type: "number", }, query: { - title: 'Query field schema', - type: 'object', + title: "Query field schema", + type: "object", properties: { className: { - type: 'string', + type: "string", }, where: { - type: 'object', + type: "object", }, keys: { - type: 'array', + type: "array", items: { - type: 'string', + type: "string", }, minItems: 1, uniqueItems: true, }, watch: { - type: 'array', + type: "array", items: { - type: 'string', + type: "string", }, minItems: 1, uniqueItems: true, }, }, - required: ['where', 'className'], + required: ["where", "className"], additionalProperties: false, }, sessionToken: { - type: 'string', + type: "string", }, }, - required: ['op', 'requestId', 'query'], + required: ["op", "requestId", "query"], additionalProperties: false, }; const unsubscribe = { - title: 'Unsubscribe operation schema', - type: 'object', + title: "Unsubscribe operation schema", + type: "object", properties: { - op: 'unsubscribe', + op: "unsubscribe", requestId: { - type: 'number', + type: "number", }, }, - required: ['op', 'requestId'], + required: ["op", "requestId"], additionalProperties: false, }; diff --git a/src/LiveQuery/equalObjects.js b/src/LiveQuery/equalObjects.js index 5bc3f5e957..4c5028a861 100644 --- a/src/LiveQuery/equalObjects.js +++ b/src/LiveQuery/equalObjects.js @@ -8,14 +8,14 @@ function equalObjects(a, b) { if (typeof a !== typeof b) { return false; } - if (typeof a !== 'object') { + if (typeof a !== "object") { return a === b; } if (a === b) { return true; } - if (toString.call(a) === '[object Date]') { - if (toString.call(b) === '[object Date]') { + if (toString.call(a) === "[object Date]") { + if (toString.call(b) === "[object Date]") { return +a === +b; } return false; diff --git a/src/Options/Definitions.js b/src/Options/Definitions.js index faffb754ac..9e47017a48 100644 --- a/src/Options/Definitions.js +++ b/src/Options/Definitions.js @@ -3,1074 +3,1074 @@ This code has been generated by resources/buildConfigDefinitions.js Do not edit manually, but update Options/index.js */ -var parsers = require('./parsers'); +var parsers = require("./parsers"); module.exports.SchemaOptions = { afterMigration: { - env: 'PARSE_SERVER_SCHEMA_AFTER_MIGRATION', - help: 'Execute a callback after running schema migrations.', + env: "PARSE_SERVER_SCHEMA_AFTER_MIGRATION", + help: "Execute a callback after running schema migrations.", }, beforeMigration: { - env: 'PARSE_SERVER_SCHEMA_BEFORE_MIGRATION', - help: 'Execute a callback before running schema migrations.', + env: "PARSE_SERVER_SCHEMA_BEFORE_MIGRATION", + help: "Execute a callback before running schema migrations.", }, definitions: { - env: 'PARSE_SERVER_SCHEMA_DEFINITIONS', - help: 'Rest representation on Parse.Schema https://docs.parseplatform.org/rest/guide/#adding-a-schema', + env: "PARSE_SERVER_SCHEMA_DEFINITIONS", + help: "Rest representation on Parse.Schema https://docs.parseplatform.org/rest/guide/#adding-a-schema", required: true, action: parsers.objectParser, default: [], }, deleteExtraFields: { - env: 'PARSE_SERVER_SCHEMA_DELETE_EXTRA_FIELDS', - help: 'Is true if Parse Server should delete any fields not defined in a schema definition. This should only be used during development.', + env: "PARSE_SERVER_SCHEMA_DELETE_EXTRA_FIELDS", + help: "Is true if Parse Server should delete any fields not defined in a schema definition. This should only be used during development.", action: parsers.booleanParser, default: false, }, lockSchemas: { - env: 'PARSE_SERVER_SCHEMA_LOCK_SCHEMAS', - help: 'Is true if Parse Server will reject any attempts to modify the schema while the server is running.', + env: "PARSE_SERVER_SCHEMA_LOCK_SCHEMAS", + help: "Is true if Parse Server will reject any attempts to modify the schema while the server is running.", action: parsers.booleanParser, default: false, }, recreateModifiedFields: { - env: 'PARSE_SERVER_SCHEMA_RECREATE_MODIFIED_FIELDS', - help: 'Is true if Parse Server should recreate any fields that are different between the current database schema and theschema definition. This should only be used during development.', + env: "PARSE_SERVER_SCHEMA_RECREATE_MODIFIED_FIELDS", + help: "Is true if Parse Server should recreate any fields that are different between the current database schema and theschema definition. This should only be used during development.", action: parsers.booleanParser, default: false, }, strict: { - env: 'PARSE_SERVER_SCHEMA_STRICT', - help: 'Is true if Parse Server should exit if schema update fail.', + env: "PARSE_SERVER_SCHEMA_STRICT", + help: "Is true if Parse Server should exit if schema update fail.", action: parsers.booleanParser, default: false, }, }; module.exports.ParseServerOptions = { accountLockout: { - env: 'PARSE_SERVER_ACCOUNT_LOCKOUT', - help: 'The account lockout policy for failed login attempts.', + env: "PARSE_SERVER_ACCOUNT_LOCKOUT", + help: "The account lockout policy for failed login attempts.", action: parsers.objectParser, - type: 'AccountLockoutOptions', + type: "AccountLockoutOptions", }, allowClientClassCreation: { - env: 'PARSE_SERVER_ALLOW_CLIENT_CLASS_CREATION', - help: 'Enable (or disable) client class creation, defaults to false', + env: "PARSE_SERVER_ALLOW_CLIENT_CLASS_CREATION", + help: "Enable (or disable) client class creation, defaults to false", action: parsers.booleanParser, default: false, }, allowCustomObjectId: { - env: 'PARSE_SERVER_ALLOW_CUSTOM_OBJECT_ID', - help: 'Enable (or disable) custom objectId', + env: "PARSE_SERVER_ALLOW_CUSTOM_OBJECT_ID", + help: "Enable (or disable) custom objectId", action: parsers.booleanParser, default: false, }, allowExpiredAuthDataToken: { - env: 'PARSE_SERVER_ALLOW_EXPIRED_AUTH_DATA_TOKEN', - help: 'Allow a user to log in even if the 3rd party authentication token that was used to sign in to their account has expired. If this is set to `false`, then the token will be validated every time the user signs in to their account. This refers to the token that is stored in the `_User.authData` field. Defaults to `false`.', + env: "PARSE_SERVER_ALLOW_EXPIRED_AUTH_DATA_TOKEN", + help: "Allow a user to log in even if the 3rd party authentication token that was used to sign in to their account has expired. If this is set to `false`, then the token will be validated every time the user signs in to their account. This refers to the token that is stored in the `_User.authData` field. Defaults to `false`.", action: parsers.booleanParser, default: false, }, allowHeaders: { - env: 'PARSE_SERVER_ALLOW_HEADERS', - help: 'Add headers to Access-Control-Allow-Headers', + env: "PARSE_SERVER_ALLOW_HEADERS", + help: "Add headers to Access-Control-Allow-Headers", action: parsers.arrayParser, }, allowOrigin: { - env: 'PARSE_SERVER_ALLOW_ORIGIN', - help: 'Sets origins for Access-Control-Allow-Origin. This can be a string for a single origin or an array of strings for multiple origins.', + env: "PARSE_SERVER_ALLOW_ORIGIN", + help: "Sets origins for Access-Control-Allow-Origin. This can be a string for a single origin or an array of strings for multiple origins.", action: parsers.arrayParser, }, analyticsAdapter: { - env: 'PARSE_SERVER_ANALYTICS_ADAPTER', - help: 'Adapter module for the analytics', + env: "PARSE_SERVER_ANALYTICS_ADAPTER", + help: "Adapter module for the analytics", action: parsers.moduleOrObjectParser, }, appId: { - env: 'PARSE_SERVER_APPLICATION_ID', - help: 'Your Parse Application ID', + env: "PARSE_SERVER_APPLICATION_ID", + help: "Your Parse Application ID", required: true, }, appName: { - env: 'PARSE_SERVER_APP_NAME', - help: 'Sets the app name', + env: "PARSE_SERVER_APP_NAME", + help: "Sets the app name", }, auth: { - env: 'PARSE_SERVER_AUTH_PROVIDERS', - help: 'Configuration for your authentication providers, as stringified JSON. See http://docs.parseplatform.org/parse-server/guide/#oauth-and-3rd-party-authentication', + env: "PARSE_SERVER_AUTH_PROVIDERS", + help: "Configuration for your authentication providers, as stringified JSON. See http://docs.parseplatform.org/parse-server/guide/#oauth-and-3rd-party-authentication", }, cacheAdapter: { - env: 'PARSE_SERVER_CACHE_ADAPTER', - help: 'Adapter module for the cache', + env: "PARSE_SERVER_CACHE_ADAPTER", + help: "Adapter module for the cache", action: parsers.moduleOrObjectParser, }, cacheMaxSize: { - env: 'PARSE_SERVER_CACHE_MAX_SIZE', - help: 'Sets the maximum size for the in memory cache, defaults to 10000', - action: parsers.numberParser('cacheMaxSize'), + env: "PARSE_SERVER_CACHE_MAX_SIZE", + help: "Sets the maximum size for the in memory cache, defaults to 10000", + action: parsers.numberParser("cacheMaxSize"), default: 10000, }, cacheTTL: { - env: 'PARSE_SERVER_CACHE_TTL', - help: 'Sets the TTL for the in memory cache (in ms), defaults to 5000 (5 seconds)', - action: parsers.numberParser('cacheTTL'), + env: "PARSE_SERVER_CACHE_TTL", + help: "Sets the TTL for the in memory cache (in ms), defaults to 5000 (5 seconds)", + action: parsers.numberParser("cacheTTL"), default: 5000, }, clientKey: { - env: 'PARSE_SERVER_CLIENT_KEY', - help: 'Key for iOS, MacOS, tvOS clients', + env: "PARSE_SERVER_CLIENT_KEY", + help: "Key for iOS, MacOS, tvOS clients", }, cloud: { - env: 'PARSE_SERVER_CLOUD', - help: 'Full path to your cloud code main.js', + env: "PARSE_SERVER_CLOUD", + help: "Full path to your cloud code main.js", }, cluster: { - env: 'PARSE_SERVER_CLUSTER', - help: 'Run with cluster, optionally set the number of processes default to os.cpus().length', + env: "PARSE_SERVER_CLUSTER", + help: "Run with cluster, optionally set the number of processes default to os.cpus().length", action: parsers.numberOrBooleanParser, }, collectionPrefix: { - env: 'PARSE_SERVER_COLLECTION_PREFIX', - help: 'A collection prefix for the classes', - default: '', + env: "PARSE_SERVER_COLLECTION_PREFIX", + help: "A collection prefix for the classes", + default: "", }, convertEmailToLowercase: { - env: 'PARSE_SERVER_CONVERT_EMAIL_TO_LOWERCASE', - help: 'Optional. If set to `true`, the `email` property of a user is automatically converted to lowercase before being stored in the database. Consequently, queries must match the case as stored in the database, which would be lowercase in this scenario. If `false`, the `email` property is stored as set, without any case modifications. Default is `false`.', + env: "PARSE_SERVER_CONVERT_EMAIL_TO_LOWERCASE", + help: "Optional. If set to `true`, the `email` property of a user is automatically converted to lowercase before being stored in the database. Consequently, queries must match the case as stored in the database, which would be lowercase in this scenario. If `false`, the `email` property is stored as set, without any case modifications. Default is `false`.", action: parsers.booleanParser, default: false, }, convertUsernameToLowercase: { - env: 'PARSE_SERVER_CONVERT_USERNAME_TO_LOWERCASE', - help: 'Optional. If set to `true`, the `username` property of a user is automatically converted to lowercase before being stored in the database. Consequently, queries must match the case as stored in the database, which would be lowercase in this scenario. If `false`, the `username` property is stored as set, without any case modifications. Default is `false`.', + env: "PARSE_SERVER_CONVERT_USERNAME_TO_LOWERCASE", + help: "Optional. If set to `true`, the `username` property of a user is automatically converted to lowercase before being stored in the database. Consequently, queries must match the case as stored in the database, which would be lowercase in this scenario. If `false`, the `username` property is stored as set, without any case modifications. Default is `false`.", action: parsers.booleanParser, default: false, }, customPages: { - env: 'PARSE_SERVER_CUSTOM_PAGES', - help: 'custom pages for password validation and reset', + env: "PARSE_SERVER_CUSTOM_PAGES", + help: "custom pages for password validation and reset", action: parsers.objectParser, - type: 'CustomPagesOptions', + type: "CustomPagesOptions", default: {}, }, databaseAdapter: { - env: 'PARSE_SERVER_DATABASE_ADAPTER', - help: 'Adapter module for the database; any options that are not explicitly described here are passed directly to the database client.', + env: "PARSE_SERVER_DATABASE_ADAPTER", + help: "Adapter module for the database; any options that are not explicitly described here are passed directly to the database client.", action: parsers.moduleOrObjectParser, }, databaseOptions: { - env: 'PARSE_SERVER_DATABASE_OPTIONS', - help: 'Options to pass to the database client', + env: "PARSE_SERVER_DATABASE_OPTIONS", + help: "Options to pass to the database client", action: parsers.objectParser, - type: 'DatabaseOptions', + type: "DatabaseOptions", }, databaseURI: { - env: 'PARSE_SERVER_DATABASE_URI', - help: 'The full URI to your database. Supported databases are mongodb or postgres.', + env: "PARSE_SERVER_DATABASE_URI", + help: "The full URI to your database. Supported databases are mongodb or postgres.", required: true, - default: 'mongodb://localhost:27017/parse', + default: "mongodb://localhost:27017/parse", }, defaultLimit: { - env: 'PARSE_SERVER_DEFAULT_LIMIT', - help: 'Default value for limit option on queries, defaults to `100`.', - action: parsers.numberParser('defaultLimit'), + env: "PARSE_SERVER_DEFAULT_LIMIT", + help: "Default value for limit option on queries, defaults to `100`.", + action: parsers.numberParser("defaultLimit"), default: 100, }, directAccess: { - env: 'PARSE_SERVER_DIRECT_ACCESS', - help: 'Set to `true` if Parse requests within the same Node.js environment as Parse Server should be routed to Parse Server directly instead of via the HTTP interface. Default is `false`.

If set to `false` then Parse requests within the same Node.js environment as Parse Server are executed as HTTP requests sent to Parse Server via the `serverURL`. For example, a `Parse.Query` in Cloud Code is calling Parse Server via a HTTP request. The server is essentially making a HTTP request to itself, unnecessarily using network resources such as network ports.

\u26A0\uFE0F In environments where multiple Parse Server instances run behind a load balancer and Parse requests within the current Node.js environment should be routed via the load balancer and distributed as HTTP requests among all instances via the `serverURL`, this should be set to `false`.', + env: "PARSE_SERVER_DIRECT_ACCESS", + help: "Set to `true` if Parse requests within the same Node.js environment as Parse Server should be routed to Parse Server directly instead of via the HTTP interface. Default is `false`.

If set to `false` then Parse requests within the same Node.js environment as Parse Server are executed as HTTP requests sent to Parse Server via the `serverURL`. For example, a `Parse.Query` in Cloud Code is calling Parse Server via a HTTP request. The server is essentially making a HTTP request to itself, unnecessarily using network resources such as network ports.

\u26A0\uFE0F In environments where multiple Parse Server instances run behind a load balancer and Parse requests within the current Node.js environment should be routed via the load balancer and distributed as HTTP requests among all instances via the `serverURL`, this should be set to `false`.", action: parsers.booleanParser, default: true, }, dotNetKey: { - env: 'PARSE_SERVER_DOT_NET_KEY', - help: 'Key for Unity and .Net SDK', + env: "PARSE_SERVER_DOT_NET_KEY", + help: "Key for Unity and .Net SDK", }, emailAdapter: { - env: 'PARSE_SERVER_EMAIL_ADAPTER', - help: 'Adapter module for email sending', + env: "PARSE_SERVER_EMAIL_ADAPTER", + help: "Adapter module for email sending", action: parsers.moduleOrObjectParser, }, emailVerifyTokenReuseIfValid: { - env: 'PARSE_SERVER_EMAIL_VERIFY_TOKEN_REUSE_IF_VALID', - help: 'Set to `true` if a email verification token should be reused in case another token is requested but there is a token that is still valid, i.e. has not expired. This avoids the often observed issue that a user requests multiple emails and does not know which link contains a valid token because each newly generated token would invalidate the previous token.

Default is `false`.
Requires option `verifyUserEmails: true`.', + env: "PARSE_SERVER_EMAIL_VERIFY_TOKEN_REUSE_IF_VALID", + help: "Set to `true` if a email verification token should be reused in case another token is requested but there is a token that is still valid, i.e. has not expired. This avoids the often observed issue that a user requests multiple emails and does not know which link contains a valid token because each newly generated token would invalidate the previous token.

Default is `false`.
Requires option `verifyUserEmails: true`.", action: parsers.booleanParser, default: false, }, emailVerifyTokenValidityDuration: { - env: 'PARSE_SERVER_EMAIL_VERIFY_TOKEN_VALIDITY_DURATION', - help: 'Set the validity duration of the email verification token in seconds after which the token expires. The token is used in the link that is set in the email. After the token expires, the link becomes invalid and a new link has to be sent. If the option is not set or set to `undefined`, then the token never expires.

For example, to expire the token after 2 hours, set a value of 7200 seconds (= 60 seconds * 60 minutes * 2 hours).

Default is `undefined`.
Requires option `verifyUserEmails: true`.', - action: parsers.numberParser('emailVerifyTokenValidityDuration'), + env: "PARSE_SERVER_EMAIL_VERIFY_TOKEN_VALIDITY_DURATION", + help: "Set the validity duration of the email verification token in seconds after which the token expires. The token is used in the link that is set in the email. After the token expires, the link becomes invalid and a new link has to be sent. If the option is not set or set to `undefined`, then the token never expires.

For example, to expire the token after 2 hours, set a value of 7200 seconds (= 60 seconds * 60 minutes * 2 hours).

Default is `undefined`.
Requires option `verifyUserEmails: true`.", + action: parsers.numberParser("emailVerifyTokenValidityDuration"), }, enableAnonymousUsers: { - env: 'PARSE_SERVER_ENABLE_ANON_USERS', - help: 'Enable (or disable) anonymous users, defaults to true', + env: "PARSE_SERVER_ENABLE_ANON_USERS", + help: "Enable (or disable) anonymous users, defaults to true", action: parsers.booleanParser, default: true, }, enableCollationCaseComparison: { - env: 'PARSE_SERVER_ENABLE_COLLATION_CASE_COMPARISON', - help: 'Optional. If set to `true`, the collation rule of case comparison for queries and indexes is enabled. Enable this option to run Parse Server with MongoDB Atlas Serverless or AWS Amazon DocumentDB. If `false`, the collation rule of case comparison is disabled. Default is `false`.', + env: "PARSE_SERVER_ENABLE_COLLATION_CASE_COMPARISON", + help: "Optional. If set to `true`, the collation rule of case comparison for queries and indexes is enabled. Enable this option to run Parse Server with MongoDB Atlas Serverless or AWS Amazon DocumentDB. If `false`, the collation rule of case comparison is disabled. Default is `false`.", action: parsers.booleanParser, default: false, }, enableExpressErrorHandler: { - env: 'PARSE_SERVER_ENABLE_EXPRESS_ERROR_HANDLER', - help: 'Enables the default express error handler for all errors', + env: "PARSE_SERVER_ENABLE_EXPRESS_ERROR_HANDLER", + help: "Enables the default express error handler for all errors", action: parsers.booleanParser, default: false, }, enableInsecureAuthAdapters: { - env: 'PARSE_SERVER_ENABLE_INSECURE_AUTH_ADAPTERS', - help: 'Enable (or disable) insecure auth adapters, defaults to true. Insecure auth adapters are deprecated and it is recommended to disable them.', + env: "PARSE_SERVER_ENABLE_INSECURE_AUTH_ADAPTERS", + help: "Enable (or disable) insecure auth adapters, defaults to true. Insecure auth adapters are deprecated and it is recommended to disable them.", action: parsers.booleanParser, default: true, }, encodeParseObjectInCloudFunction: { - env: 'PARSE_SERVER_ENCODE_PARSE_OBJECT_IN_CLOUD_FUNCTION', - help: 'If set to `true`, a `Parse.Object` that is in the payload when calling a Cloud Function will be converted to an instance of `Parse.Object`. If `false`, the object will not be converted and instead be a plain JavaScript object, which contains the raw data of a `Parse.Object` but is not an actual instance of `Parse.Object`. Default is `false`.

\u2139\uFE0F The expected behavior would be that the object is converted to an instance of `Parse.Object`, so you would normally set this option to `true`. The default is `false` because this is a temporary option that has been introduced to avoid a breaking change when fixing a bug where JavaScript objects are not converted to actual instances of `Parse.Object`.', + env: "PARSE_SERVER_ENCODE_PARSE_OBJECT_IN_CLOUD_FUNCTION", + help: "If set to `true`, a `Parse.Object` that is in the payload when calling a Cloud Function will be converted to an instance of `Parse.Object`. If `false`, the object will not be converted and instead be a plain JavaScript object, which contains the raw data of a `Parse.Object` but is not an actual instance of `Parse.Object`. Default is `false`.

\u2139\uFE0F The expected behavior would be that the object is converted to an instance of `Parse.Object`, so you would normally set this option to `true`. The default is `false` because this is a temporary option that has been introduced to avoid a breaking change when fixing a bug where JavaScript objects are not converted to actual instances of `Parse.Object`.", action: parsers.booleanParser, default: true, }, encryptionKey: { - env: 'PARSE_SERVER_ENCRYPTION_KEY', - help: 'Key for encrypting your files', + env: "PARSE_SERVER_ENCRYPTION_KEY", + help: "Key for encrypting your files", }, enforcePrivateUsers: { - env: 'PARSE_SERVER_ENFORCE_PRIVATE_USERS', - help: 'Set to true if new users should be created without public read and write access.', + env: "PARSE_SERVER_ENFORCE_PRIVATE_USERS", + help: "Set to true if new users should be created without public read and write access.", action: parsers.booleanParser, default: true, }, expireInactiveSessions: { - env: 'PARSE_SERVER_EXPIRE_INACTIVE_SESSIONS', - help: 'Sets whether we should expire the inactive sessions, defaults to true. If false, all new sessions are created with no expiration date.', + env: "PARSE_SERVER_EXPIRE_INACTIVE_SESSIONS", + help: "Sets whether we should expire the inactive sessions, defaults to true. If false, all new sessions are created with no expiration date.", action: parsers.booleanParser, default: true, }, extendSessionOnUse: { - env: 'PARSE_SERVER_EXTEND_SESSION_ON_USE', + env: "PARSE_SERVER_EXTEND_SESSION_ON_USE", help: "Whether Parse Server should automatically extend a valid session by the sessionLength. In order to reduce the number of session updates in the database, a session will only be extended when a request is received after at least half of the current session's lifetime has passed.", action: parsers.booleanParser, default: false, }, fileKey: { - env: 'PARSE_SERVER_FILE_KEY', - help: 'Key for your files', + env: "PARSE_SERVER_FILE_KEY", + help: "Key for your files", }, filesAdapter: { - env: 'PARSE_SERVER_FILES_ADAPTER', - help: 'Adapter module for the files sub-system', + env: "PARSE_SERVER_FILES_ADAPTER", + help: "Adapter module for the files sub-system", action: parsers.moduleOrObjectParser, }, fileUpload: { - env: 'PARSE_SERVER_FILE_UPLOAD_OPTIONS', - help: 'Options for file uploads', + env: "PARSE_SERVER_FILE_UPLOAD_OPTIONS", + help: "Options for file uploads", action: parsers.objectParser, - type: 'FileUploadOptions', + type: "FileUploadOptions", default: {}, }, graphQLPath: { - env: 'PARSE_SERVER_GRAPHQL_PATH', - help: 'Mount path for the GraphQL endpoint, defaults to /graphql', - default: '/graphql', + env: "PARSE_SERVER_GRAPHQL_PATH", + help: "Mount path for the GraphQL endpoint, defaults to /graphql", + default: "/graphql", }, graphQLSchema: { - env: 'PARSE_SERVER_GRAPH_QLSCHEMA', - help: 'Full path to your GraphQL custom schema.graphql file', + env: "PARSE_SERVER_GRAPH_QLSCHEMA", + help: "Full path to your GraphQL custom schema.graphql file", }, host: { - env: 'PARSE_SERVER_HOST', - help: 'The host to serve ParseServer on, defaults to 0.0.0.0', - default: '0.0.0.0', + env: "PARSE_SERVER_HOST", + help: "The host to serve ParseServer on, defaults to 0.0.0.0", + default: "0.0.0.0", }, idempotencyOptions: { - env: 'PARSE_SERVER_EXPERIMENTAL_IDEMPOTENCY_OPTIONS', - help: 'Options for request idempotency to deduplicate identical requests that may be caused by network issues. Caution, this is an experimental feature that may not be appropriate for production.', + env: "PARSE_SERVER_EXPERIMENTAL_IDEMPOTENCY_OPTIONS", + help: "Options for request idempotency to deduplicate identical requests that may be caused by network issues. Caution, this is an experimental feature that may not be appropriate for production.", action: parsers.objectParser, - type: 'IdempotencyOptions', + type: "IdempotencyOptions", default: {}, }, javascriptKey: { - env: 'PARSE_SERVER_JAVASCRIPT_KEY', - help: 'Key for the Javascript SDK', + env: "PARSE_SERVER_JAVASCRIPT_KEY", + help: "Key for the Javascript SDK", }, jsonLogs: { - env: 'JSON_LOGS', - help: 'Log as structured JSON objects', + env: "JSON_LOGS", + help: "Log as structured JSON objects", action: parsers.booleanParser, }, liveQuery: { - env: 'PARSE_SERVER_LIVE_QUERY', + env: "PARSE_SERVER_LIVE_QUERY", help: "parse-server's LiveQuery configuration object", action: parsers.objectParser, - type: 'LiveQueryOptions', + type: "LiveQueryOptions", }, liveQueryServerOptions: { - env: 'PARSE_SERVER_LIVE_QUERY_SERVER_OPTIONS', - help: 'Live query server configuration options (will start the liveQuery server)', + env: "PARSE_SERVER_LIVE_QUERY_SERVER_OPTIONS", + help: "Live query server configuration options (will start the liveQuery server)", action: parsers.objectParser, - type: 'LiveQueryServerOptions', + type: "LiveQueryServerOptions", }, loggerAdapter: { - env: 'PARSE_SERVER_LOGGER_ADAPTER', - help: 'Adapter module for the logging sub-system', + env: "PARSE_SERVER_LOGGER_ADAPTER", + help: "Adapter module for the logging sub-system", action: parsers.moduleOrObjectParser, }, logLevel: { - env: 'PARSE_SERVER_LOG_LEVEL', - help: 'Sets the level for logs', + env: "PARSE_SERVER_LOG_LEVEL", + help: "Sets the level for logs", }, logLevels: { - env: 'PARSE_SERVER_LOG_LEVELS', - help: '(Optional) Overrides the log levels used internally by Parse Server to log events.', + env: "PARSE_SERVER_LOG_LEVELS", + help: "(Optional) Overrides the log levels used internally by Parse Server to log events.", action: parsers.objectParser, - type: 'LogLevels', + type: "LogLevels", default: {}, }, logsFolder: { - env: 'PARSE_SERVER_LOGS_FOLDER', + env: "PARSE_SERVER_LOGS_FOLDER", help: "Folder for the logs (defaults to './logs'); set to null to disable file based logging", - default: './logs', + default: "./logs", }, maintenanceKey: { - env: 'PARSE_SERVER_MAINTENANCE_KEY', - help: '(Optional) The maintenance key is used for modifying internal and read-only fields of Parse Server.

\u26A0\uFE0F This key is not intended to be used as part of a regular operation of Parse Server. This key is intended to conduct out-of-band changes such as one-time migrations or data correction tasks. Internal fields are not officially documented and may change at any time without publication in release changelogs. We strongly advice not to rely on internal fields as part of your regular operation and to investigate the implications of any planned changes *directly in the source code* of your current version of Parse Server.', + env: "PARSE_SERVER_MAINTENANCE_KEY", + help: "(Optional) The maintenance key is used for modifying internal and read-only fields of Parse Server.

\u26A0\uFE0F This key is not intended to be used as part of a regular operation of Parse Server. This key is intended to conduct out-of-band changes such as one-time migrations or data correction tasks. Internal fields are not officially documented and may change at any time without publication in release changelogs. We strongly advice not to rely on internal fields as part of your regular operation and to investigate the implications of any planned changes *directly in the source code* of your current version of Parse Server.", required: true, }, maintenanceKeyIps: { - env: 'PARSE_SERVER_MAINTENANCE_KEY_IPS', + env: "PARSE_SERVER_MAINTENANCE_KEY_IPS", help: "(Optional) Restricts the use of maintenance key permissions to a list of IP addresses or ranges.

This option accepts a list of single IP addresses, for example `['10.0.0.1', '10.0.0.2']`. You can also use CIDR notation to specify an IP address range, for example `['10.0.1.0/24']`.

Special scenarios:
- Setting an empty array `[]` means that the maintenance key cannot be used even in Parse Server Cloud Code. This value cannot be set via an environment variable as there is no way to pass an empty array to Parse Server via an environment variable.
- Setting `['0.0.0.0/0', '::0']` means to allow any IPv4 and IPv6 address to use the maintenance key and effectively disables the IP filter.

Considerations:
- IPv4 and IPv6 addresses are not compared against each other. Each IP version (IPv4 and IPv6) needs to be considered separately. For example, `['0.0.0.0/0']` allows any IPv4 address and blocks every IPv6 address. Conversely, `['::0']` allows any IPv6 address and blocks every IPv4 address.
- Keep in mind that the IP version in use depends on the network stack of the environment in which Parse Server runs. A local environment may use a different IP version than a remote environment. For example, it's possible that locally the value `['0.0.0.0/0']` allows the request IP because the environment is using IPv4, but when Parse Server is deployed remotely the request IP is blocked because the remote environment is using IPv6.
- When setting the option via an environment variable the notation is a comma-separated string, for example `\"0.0.0.0/0,::0\"`.
- IPv6 zone indices (`%` suffix) are not supported, for example `fe80::1%eth0`, `fe80::1%1` or `::1%lo`.

Defaults to `['127.0.0.1', '::1']` which means that only `localhost`, the server instance on which Parse Server runs, is allowed to use the maintenance key.", action: parsers.arrayParser, - default: ['127.0.0.1', '::1'], + default: ["127.0.0.1", "::1"], }, masterKey: { - env: 'PARSE_SERVER_MASTER_KEY', - help: 'Your Parse Master Key', + env: "PARSE_SERVER_MASTER_KEY", + help: "Your Parse Master Key", required: true, }, masterKeyIps: { - env: 'PARSE_SERVER_MASTER_KEY_IPS', + env: "PARSE_SERVER_MASTER_KEY_IPS", help: "(Optional) Restricts the use of master key permissions to a list of IP addresses or ranges.

This option accepts a list of single IP addresses, for example `['10.0.0.1', '10.0.0.2']`. You can also use CIDR notation to specify an IP address range, for example `['10.0.1.0/24']`.

Special scenarios:
- Setting an empty array `[]` means that the master key cannot be used even in Parse Server Cloud Code. This value cannot be set via an environment variable as there is no way to pass an empty array to Parse Server via an environment variable.
- Setting `['0.0.0.0/0', '::0']` means to allow any IPv4 and IPv6 address to use the master key and effectively disables the IP filter.

Considerations:
- IPv4 and IPv6 addresses are not compared against each other. Each IP version (IPv4 and IPv6) needs to be considered separately. For example, `['0.0.0.0/0']` allows any IPv4 address and blocks every IPv6 address. Conversely, `['::0']` allows any IPv6 address and blocks every IPv4 address.
- Keep in mind that the IP version in use depends on the network stack of the environment in which Parse Server runs. A local environment may use a different IP version than a remote environment. For example, it's possible that locally the value `['0.0.0.0/0']` allows the request IP because the environment is using IPv4, but when Parse Server is deployed remotely the request IP is blocked because the remote environment is using IPv6.
- When setting the option via an environment variable the notation is a comma-separated string, for example `\"0.0.0.0/0,::0\"`.
- IPv6 zone indices (`%` suffix) are not supported, for example `fe80::1%eth0`, `fe80::1%1` or `::1%lo`.

Defaults to `['127.0.0.1', '::1']` which means that only `localhost`, the server instance on which Parse Server runs, is allowed to use the master key.", action: parsers.arrayParser, - default: ['127.0.0.1', '::1'], + default: ["127.0.0.1", "::1"], }, masterKeyTtl: { - env: 'PARSE_SERVER_MASTER_KEY_TTL', - help: '(Optional) The duration in seconds for which the current `masterKey` is being used before it is requested again if `masterKey` is set to a function. If `masterKey` is not set to a function, this option has no effect. Default is `0`, which means the master key is requested by invoking the `masterKey` function every time the master key is used internally by Parse Server.', - action: parsers.numberParser('masterKeyTtl'), + env: "PARSE_SERVER_MASTER_KEY_TTL", + help: "(Optional) The duration in seconds for which the current `masterKey` is being used before it is requested again if `masterKey` is set to a function. If `masterKey` is not set to a function, this option has no effect. Default is `0`, which means the master key is requested by invoking the `masterKey` function every time the master key is used internally by Parse Server.", + action: parsers.numberParser("masterKeyTtl"), }, maxLimit: { - env: 'PARSE_SERVER_MAX_LIMIT', - help: 'Max value for limit option on queries, defaults to unlimited', - action: parsers.numberParser('maxLimit'), + env: "PARSE_SERVER_MAX_LIMIT", + help: "Max value for limit option on queries, defaults to unlimited", + action: parsers.numberParser("maxLimit"), }, maxLogFiles: { - env: 'PARSE_SERVER_MAX_LOG_FILES', + env: "PARSE_SERVER_MAX_LOG_FILES", help: "Maximum number of logs to keep. If not set, no logs will be removed. This can be a number of files or number of days. If using days, add 'd' as the suffix. (default: null)", - action: parsers.numberOrStringParser('maxLogFiles'), + action: parsers.numberOrStringParser("maxLogFiles"), }, maxUploadSize: { - env: 'PARSE_SERVER_MAX_UPLOAD_SIZE', - help: 'Max file size for uploads, defaults to 20mb', - default: '20mb', + env: "PARSE_SERVER_MAX_UPLOAD_SIZE", + help: "Max file size for uploads, defaults to 20mb", + default: "20mb", }, middleware: { - env: 'PARSE_SERVER_MIDDLEWARE', - help: 'middleware for express server, can be string or function', + env: "PARSE_SERVER_MIDDLEWARE", + help: "middleware for express server, can be string or function", }, mountGraphQL: { - env: 'PARSE_SERVER_MOUNT_GRAPHQL', - help: 'Mounts the GraphQL endpoint', + env: "PARSE_SERVER_MOUNT_GRAPHQL", + help: "Mounts the GraphQL endpoint", action: parsers.booleanParser, default: false, }, mountPath: { - env: 'PARSE_SERVER_MOUNT_PATH', - help: 'Mount path for the server, defaults to /parse', - default: '/parse', + env: "PARSE_SERVER_MOUNT_PATH", + help: "Mount path for the server, defaults to /parse", + default: "/parse", }, mountPlayground: { - env: 'PARSE_SERVER_MOUNT_PLAYGROUND', - help: 'Mounts the GraphQL Playground - never use this option in production', + env: "PARSE_SERVER_MOUNT_PLAYGROUND", + help: "Mounts the GraphQL Playground - never use this option in production", action: parsers.booleanParser, default: false, }, objectIdSize: { - env: 'PARSE_SERVER_OBJECT_ID_SIZE', + env: "PARSE_SERVER_OBJECT_ID_SIZE", help: "Sets the number of characters in generated object id's, default 10", - action: parsers.numberParser('objectIdSize'), + action: parsers.numberParser("objectIdSize"), default: 10, }, pages: { - env: 'PARSE_SERVER_PAGES', - help: 'The options for pages such as password reset and email verification.', + env: "PARSE_SERVER_PAGES", + help: "The options for pages such as password reset and email verification.", action: parsers.objectParser, - type: 'PagesOptions', + type: "PagesOptions", default: {}, }, passwordPolicy: { - env: 'PARSE_SERVER_PASSWORD_POLICY', - help: 'The password policy for enforcing password related rules.', + env: "PARSE_SERVER_PASSWORD_POLICY", + help: "The password policy for enforcing password related rules.", action: parsers.objectParser, - type: 'PasswordPolicyOptions', + type: "PasswordPolicyOptions", }, playgroundPath: { - env: 'PARSE_SERVER_PLAYGROUND_PATH', - help: 'Mount path for the GraphQL Playground, defaults to /playground', - default: '/playground', + env: "PARSE_SERVER_PLAYGROUND_PATH", + help: "Mount path for the GraphQL Playground, defaults to /playground", + default: "/playground", }, port: { - env: 'PORT', - help: 'The port to run the ParseServer, defaults to 1337.', - action: parsers.numberParser('port'), + env: "PORT", + help: "The port to run the ParseServer, defaults to 1337.", + action: parsers.numberParser("port"), default: 1337, }, preserveFileName: { - env: 'PARSE_SERVER_PRESERVE_FILE_NAME', - help: 'Enable (or disable) the addition of a unique hash to the file names', + env: "PARSE_SERVER_PRESERVE_FILE_NAME", + help: "Enable (or disable) the addition of a unique hash to the file names", action: parsers.booleanParser, default: false, }, preventLoginWithUnverifiedEmail: { - env: 'PARSE_SERVER_PREVENT_LOGIN_WITH_UNVERIFIED_EMAIL', - help: 'Set to `true` to prevent a user from logging in if the email has not yet been verified and email verification is required.

Default is `false`.
Requires option `verifyUserEmails: true`.', + env: "PARSE_SERVER_PREVENT_LOGIN_WITH_UNVERIFIED_EMAIL", + help: "Set to `true` to prevent a user from logging in if the email has not yet been verified and email verification is required.

Default is `false`.
Requires option `verifyUserEmails: true`.", action: parsers.booleanParser, default: false, }, preventSignupWithUnverifiedEmail: { - env: 'PARSE_SERVER_PREVENT_SIGNUP_WITH_UNVERIFIED_EMAIL', + env: "PARSE_SERVER_PREVENT_SIGNUP_WITH_UNVERIFIED_EMAIL", help: "If set to `true` it prevents a user from signing up if the email has not yet been verified and email verification is required. In that case the server responds to the sign-up with HTTP status 400 and a Parse Error 205 `EMAIL_NOT_FOUND`. If set to `false` the server responds with HTTP status 200, and client SDKs return an unauthenticated Parse User without session token. In that case subsequent requests fail until the user's email address is verified.

Default is `false`.
Requires option `verifyUserEmails: true`.", action: parsers.booleanParser, default: false, }, protectedFields: { - env: 'PARSE_SERVER_PROTECTED_FIELDS', - help: 'Protected fields that should be treated with extra security when fetching details.', + env: "PARSE_SERVER_PROTECTED_FIELDS", + help: "Protected fields that should be treated with extra security when fetching details.", action: parsers.objectParser, default: { _User: { - '*': ['email'], + "*": ["email"], }, }, }, publicServerURL: { - env: 'PARSE_PUBLIC_SERVER_URL', - help: 'Public URL to your parse server with http:// or https://.', + env: "PARSE_PUBLIC_SERVER_URL", + help: "Public URL to your parse server with http:// or https://.", }, push: { - env: 'PARSE_SERVER_PUSH', - help: 'Configuration for push, as stringified JSON. See http://docs.parseplatform.org/parse-server/guide/#push-notifications', + env: "PARSE_SERVER_PUSH", + help: "Configuration for push, as stringified JSON. See http://docs.parseplatform.org/parse-server/guide/#push-notifications", action: parsers.objectParser, }, rateLimit: { - env: 'PARSE_SERVER_RATE_LIMIT', + env: "PARSE_SERVER_RATE_LIMIT", help: "Options to limit repeated requests to Parse Server APIs. This can be used to protect sensitive endpoints such as `/requestPasswordReset` from brute-force attacks or Parse Server as a whole from denial-of-service (DoS) attacks.

\u2139\uFE0F Mind the following limitations:
- rate limits applied per IP address; this limits protection against distributed denial-of-service (DDoS) attacks where many requests are coming from various IP addresses
- if multiple Parse Server instances are behind a load balancer or ran in a cluster, each instance will calculate it's own request rates, independent from other instances; this limits the applicability of this feature when using a load balancer and another rate limiting solution that takes requests across all instances into account may be more suitable
- this feature provides basic protection against denial-of-service attacks, but a more sophisticated solution works earlier in the request flow and prevents a malicious requests to even reach a server instance; it's therefore recommended to implement a solution according to architecture and user case.", action: parsers.arrayParser, - type: 'RateLimitOptions[]', + type: "RateLimitOptions[]", default: [], }, readOnlyMasterKey: { - env: 'PARSE_SERVER_READ_ONLY_MASTER_KEY', - help: 'Read-only key, which has the same capabilities as MasterKey without writes', + env: "PARSE_SERVER_READ_ONLY_MASTER_KEY", + help: "Read-only key, which has the same capabilities as MasterKey without writes", }, requestKeywordDenylist: { - env: 'PARSE_SERVER_REQUEST_KEYWORD_DENYLIST', + env: "PARSE_SERVER_REQUEST_KEYWORD_DENYLIST", help: 'An array of keys and values that are prohibited in database read and write requests to prevent potential security vulnerabilities. It is possible to specify only a key (`{"key":"..."}`), only a value (`{"value":"..."}`) or a key-value pair (`{"key":"...","value":"..."}`). The specification can use the following types: `boolean`, `numeric` or `string`, where `string` will be interpreted as a regex notation. Request data is deep-scanned for matching definitions to detect also any nested occurrences. Defaults are patterns that are likely to be used in malicious requests. Setting this option will override the default patterns.', action: parsers.arrayParser, default: [ { - key: '_bsontype', - value: 'Code', + key: "_bsontype", + value: "Code", }, { - key: 'constructor', + key: "constructor", }, { - key: '__proto__', + key: "__proto__", }, ], }, restAPIKey: { - env: 'PARSE_SERVER_REST_API_KEY', - help: 'Key for REST calls', + env: "PARSE_SERVER_REST_API_KEY", + help: "Key for REST calls", }, revokeSessionOnPasswordReset: { - env: 'PARSE_SERVER_REVOKE_SESSION_ON_PASSWORD_RESET', + env: "PARSE_SERVER_REVOKE_SESSION_ON_PASSWORD_RESET", help: "When a user changes their password, either through the reset password email or while logged in, all sessions are revoked if this is true. Set to false if you don't want to revoke sessions.", action: parsers.booleanParser, default: true, }, scheduledPush: { - env: 'PARSE_SERVER_SCHEDULED_PUSH', - help: 'Configuration for push scheduling, defaults to false.', + env: "PARSE_SERVER_SCHEDULED_PUSH", + help: "Configuration for push scheduling, defaults to false.", action: parsers.booleanParser, default: false, }, schema: { - env: 'PARSE_SERVER_SCHEMA', - help: 'Defined schema', + env: "PARSE_SERVER_SCHEMA", + help: "Defined schema", action: parsers.objectParser, - type: 'SchemaOptions', + type: "SchemaOptions", }, security: { - env: 'PARSE_SERVER_SECURITY', - help: 'The security options to identify and report weak security settings.', + env: "PARSE_SERVER_SECURITY", + help: "The security options to identify and report weak security settings.", action: parsers.objectParser, - type: 'SecurityOptions', + type: "SecurityOptions", default: {}, }, sendUserEmailVerification: { - env: 'PARSE_SERVER_SEND_USER_EMAIL_VERIFICATION', - help: 'Set to `false` to prevent sending of verification email. Supports a function with a return value of `true` or `false` for conditional email sending.

Default is `true`.
', + env: "PARSE_SERVER_SEND_USER_EMAIL_VERIFICATION", + help: "Set to `false` to prevent sending of verification email. Supports a function with a return value of `true` or `false` for conditional email sending.

Default is `true`.
", default: true, }, serverCloseComplete: { - env: 'PARSE_SERVER_SERVER_CLOSE_COMPLETE', - help: 'Callback when server has closed', + env: "PARSE_SERVER_SERVER_CLOSE_COMPLETE", + help: "Callback when server has closed", }, serverURL: { - env: 'PARSE_SERVER_URL', - help: 'URL to your parse server with http:// or https://.', + env: "PARSE_SERVER_URL", + help: "URL to your parse server with http:// or https://.", required: true, }, sessionLength: { - env: 'PARSE_SERVER_SESSION_LENGTH', - help: 'Session duration, in seconds, defaults to 1 year', - action: parsers.numberParser('sessionLength'), + env: "PARSE_SERVER_SESSION_LENGTH", + help: "Session duration, in seconds, defaults to 1 year", + action: parsers.numberParser("sessionLength"), default: 31536000, }, silent: { - env: 'SILENT', - help: 'Disables console output', + env: "SILENT", + help: "Disables console output", action: parsers.booleanParser, }, startLiveQueryServer: { - env: 'PARSE_SERVER_START_LIVE_QUERY_SERVER', - help: 'Starts the liveQuery server', + env: "PARSE_SERVER_START_LIVE_QUERY_SERVER", + help: "Starts the liveQuery server", action: parsers.booleanParser, }, trustProxy: { - env: 'PARSE_SERVER_TRUST_PROXY', + env: "PARSE_SERVER_TRUST_PROXY", help: 'The trust proxy settings. It is important to understand the exact setup of the reverse proxy, since this setting will trust values provided in the Parse Server API request. See the express trust proxy settings documentation. Defaults to `false`.', action: parsers.objectParser, default: [], }, userSensitiveFields: { - env: 'PARSE_SERVER_USER_SENSITIVE_FIELDS', - help: 'Personally identifiable information fields in the user table the should be removed for non-authorized users. Deprecated @see protectedFields', + env: "PARSE_SERVER_USER_SENSITIVE_FIELDS", + help: "Personally identifiable information fields in the user table the should be removed for non-authorized users. Deprecated @see protectedFields", action: parsers.arrayParser, }, verbose: { - env: 'VERBOSE', - help: 'Set the logging to verbose', + env: "VERBOSE", + help: "Set the logging to verbose", action: parsers.booleanParser, }, verifyUserEmails: { - env: 'PARSE_SERVER_VERIFY_USER_EMAILS', - help: 'Set to `true` to require users to verify their email address to complete the sign-up process. Supports a function with a return value of `true` or `false` for conditional verification.

Default is `false`.', + env: "PARSE_SERVER_VERIFY_USER_EMAILS", + help: "Set to `true` to require users to verify their email address to complete the sign-up process. Supports a function with a return value of `true` or `false` for conditional verification.

Default is `false`.", default: false, }, webhookKey: { - env: 'PARSE_SERVER_WEBHOOK_KEY', - help: 'Key sent with outgoing webhook calls', + env: "PARSE_SERVER_WEBHOOK_KEY", + help: "Key sent with outgoing webhook calls", }, }; module.exports.RateLimitOptions = { errorResponseMessage: { - env: 'PARSE_SERVER_RATE_LIMIT_ERROR_RESPONSE_MESSAGE', - help: 'The error message that should be returned in the body of the HTTP 429 response when the rate limit is hit. Default is `Too many requests.`.', - default: 'Too many requests.', + env: "PARSE_SERVER_RATE_LIMIT_ERROR_RESPONSE_MESSAGE", + help: "The error message that should be returned in the body of the HTTP 429 response when the rate limit is hit. Default is `Too many requests.`.", + default: "Too many requests.", }, includeInternalRequests: { - env: 'PARSE_SERVER_RATE_LIMIT_INCLUDE_INTERNAL_REQUESTS', - help: 'Optional, if `true` the rate limit will also apply to requests that are made in by Cloud Code, default is `false`. Note that a public Cloud Code function that triggers internal requests may circumvent rate limiting and be vulnerable to attacks.', + env: "PARSE_SERVER_RATE_LIMIT_INCLUDE_INTERNAL_REQUESTS", + help: "Optional, if `true` the rate limit will also apply to requests that are made in by Cloud Code, default is `false`. Note that a public Cloud Code function that triggers internal requests may circumvent rate limiting and be vulnerable to attacks.", action: parsers.booleanParser, default: false, }, includeMasterKey: { - env: 'PARSE_SERVER_RATE_LIMIT_INCLUDE_MASTER_KEY', - help: 'Optional, if `true` the rate limit will also apply to requests using the `masterKey`, default is `false`. Note that a public Cloud Code function that triggers internal requests using the `masterKey` may circumvent rate limiting and be vulnerable to attacks.', + env: "PARSE_SERVER_RATE_LIMIT_INCLUDE_MASTER_KEY", + help: "Optional, if `true` the rate limit will also apply to requests using the `masterKey`, default is `false`. Note that a public Cloud Code function that triggers internal requests using the `masterKey` may circumvent rate limiting and be vulnerable to attacks.", action: parsers.booleanParser, default: false, }, redisUrl: { - env: 'PARSE_SERVER_RATE_LIMIT_REDIS_URL', - help: 'Optional, the URL of the Redis server to store rate limit data. This allows to rate limit requests for multiple servers by calculating the sum of all requests across all servers. This is useful if multiple servers are processing requests behind a load balancer. For example, the limit of 10 requests is reached if each of 2 servers processed 5 requests.', + env: "PARSE_SERVER_RATE_LIMIT_REDIS_URL", + help: "Optional, the URL of the Redis server to store rate limit data. This allows to rate limit requests for multiple servers by calculating the sum of all requests across all servers. This is useful if multiple servers are processing requests behind a load balancer. For example, the limit of 10 requests is reached if each of 2 servers processed 5 requests.", }, requestCount: { - env: 'PARSE_SERVER_RATE_LIMIT_REQUEST_COUNT', - help: 'The number of requests that can be made per IP address within the time window set in `requestTimeWindow` before the rate limit is applied.', - action: parsers.numberParser('requestCount'), + env: "PARSE_SERVER_RATE_LIMIT_REQUEST_COUNT", + help: "The number of requests that can be made per IP address within the time window set in `requestTimeWindow` before the rate limit is applied.", + action: parsers.numberParser("requestCount"), }, requestMethods: { - env: 'PARSE_SERVER_RATE_LIMIT_REQUEST_METHODS', - help: 'Optional, the HTTP request methods to which the rate limit should be applied, default is all methods.', + env: "PARSE_SERVER_RATE_LIMIT_REQUEST_METHODS", + help: "Optional, the HTTP request methods to which the rate limit should be applied, default is all methods.", action: parsers.arrayParser, }, requestPath: { - env: 'PARSE_SERVER_RATE_LIMIT_REQUEST_PATH', - help: 'The path of the API route to be rate limited. Route paths, in combination with a request method, define the endpoints at which requests can be made. Route paths can be strings, string patterns, or regular expression. See: https://expressjs.com/en/guide/routing.html', + env: "PARSE_SERVER_RATE_LIMIT_REQUEST_PATH", + help: "The path of the API route to be rate limited. Route paths, in combination with a request method, define the endpoints at which requests can be made. Route paths can be strings, string patterns, or regular expression. See: https://expressjs.com/en/guide/routing.html", required: true, }, requestTimeWindow: { - env: 'PARSE_SERVER_RATE_LIMIT_REQUEST_TIME_WINDOW', - help: 'The window of time in milliseconds within which the number of requests set in `requestCount` can be made before the rate limit is applied.', - action: parsers.numberParser('requestTimeWindow'), + env: "PARSE_SERVER_RATE_LIMIT_REQUEST_TIME_WINDOW", + help: "The window of time in milliseconds within which the number of requests set in `requestCount` can be made before the rate limit is applied.", + action: parsers.numberParser("requestTimeWindow"), }, zone: { - env: 'PARSE_SERVER_RATE_LIMIT_ZONE', + env: "PARSE_SERVER_RATE_LIMIT_ZONE", help: "The type of rate limit to apply. The following types are supported:

- `global`: rate limit based on the number of requests made by all users
- `ip`: rate limit based on the IP address of the request
- `user`: rate limit based on the user ID of the request
- `session`: rate limit based on the session token of the request


:default: 'ip'", }, }; module.exports.SecurityOptions = { checkGroups: { - env: 'PARSE_SERVER_SECURITY_CHECK_GROUPS', - help: 'The security check groups to run. This allows to add custom security checks or override existing ones. Default are the groups defined in `CheckGroups.js`.', + env: "PARSE_SERVER_SECURITY_CHECK_GROUPS", + help: "The security check groups to run. This allows to add custom security checks or override existing ones. Default are the groups defined in `CheckGroups.js`.", action: parsers.arrayParser, }, enableCheck: { - env: 'PARSE_SERVER_SECURITY_ENABLE_CHECK', - help: 'Is true if Parse Server should check for weak security settings.', + env: "PARSE_SERVER_SECURITY_ENABLE_CHECK", + help: "Is true if Parse Server should check for weak security settings.", action: parsers.booleanParser, default: false, }, enableCheckLog: { - env: 'PARSE_SERVER_SECURITY_ENABLE_CHECK_LOG', - help: 'Is true if the security check report should be written to logs. This should only be enabled temporarily to not expose weak security settings in logs.', + env: "PARSE_SERVER_SECURITY_ENABLE_CHECK_LOG", + help: "Is true if the security check report should be written to logs. This should only be enabled temporarily to not expose weak security settings in logs.", action: parsers.booleanParser, default: false, }, }; module.exports.PagesOptions = { customRoutes: { - env: 'PARSE_SERVER_PAGES_CUSTOM_ROUTES', - help: 'The custom routes.', + env: "PARSE_SERVER_PAGES_CUSTOM_ROUTES", + help: "The custom routes.", action: parsers.arrayParser, - type: 'PagesRoute[]', + type: "PagesRoute[]", default: [], }, customUrls: { - env: 'PARSE_SERVER_PAGES_CUSTOM_URLS', - help: 'The URLs to the custom pages.', + env: "PARSE_SERVER_PAGES_CUSTOM_URLS", + help: "The URLs to the custom pages.", action: parsers.objectParser, - type: 'PagesCustomUrlsOptions', + type: "PagesCustomUrlsOptions", default: {}, }, enableLocalization: { - env: 'PARSE_SERVER_PAGES_ENABLE_LOCALIZATION', - help: 'Is true if pages should be localized; this has no effect on custom page redirects.', + env: "PARSE_SERVER_PAGES_ENABLE_LOCALIZATION", + help: "Is true if pages should be localized; this has no effect on custom page redirects.", action: parsers.booleanParser, default: false, }, enableRouter: { - env: 'PARSE_SERVER_PAGES_ENABLE_ROUTER', - help: 'Is true if the pages router should be enabled; this is required for any of the pages options to take effect.', + env: "PARSE_SERVER_PAGES_ENABLE_ROUTER", + help: "Is true if the pages router should be enabled; this is required for any of the pages options to take effect.", action: parsers.booleanParser, default: false, }, forceRedirect: { - env: 'PARSE_SERVER_PAGES_FORCE_REDIRECT', - help: 'Is true if responses should always be redirects and never content, false if the response type should depend on the request type (GET request -> content response; POST request -> redirect response).', + env: "PARSE_SERVER_PAGES_FORCE_REDIRECT", + help: "Is true if responses should always be redirects and never content, false if the response type should depend on the request type (GET request -> content response; POST request -> redirect response).", action: parsers.booleanParser, default: false, }, localizationFallbackLocale: { - env: 'PARSE_SERVER_PAGES_LOCALIZATION_FALLBACK_LOCALE', - help: 'The fallback locale for localization if no matching translation is provided for the given locale. This is only relevant when providing translation resources via JSON file.', - default: 'en', + env: "PARSE_SERVER_PAGES_LOCALIZATION_FALLBACK_LOCALE", + help: "The fallback locale for localization if no matching translation is provided for the given locale. This is only relevant when providing translation resources via JSON file.", + default: "en", }, localizationJsonPath: { - env: 'PARSE_SERVER_PAGES_LOCALIZATION_JSON_PATH', - help: 'The path to the JSON file for localization; the translations will be used to fill template placeholders according to the locale.', + env: "PARSE_SERVER_PAGES_LOCALIZATION_JSON_PATH", + help: "The path to the JSON file for localization; the translations will be used to fill template placeholders according to the locale.", }, pagesEndpoint: { - env: 'PARSE_SERVER_PAGES_PAGES_ENDPOINT', + env: "PARSE_SERVER_PAGES_PAGES_ENDPOINT", help: "The API endpoint for the pages. Default is 'apps'.", - default: 'apps', + default: "apps", }, pagesPath: { - env: 'PARSE_SERVER_PAGES_PAGES_PATH', + env: "PARSE_SERVER_PAGES_PAGES_PATH", help: "The path to the pages directory; this also defines where the static endpoint '/apps' points to. Default is the './public/' directory.", - default: './public', + default: "./public", }, placeholders: { - env: 'PARSE_SERVER_PAGES_PLACEHOLDERS', - help: 'The placeholder keys and values which will be filled in pages; this can be a simple object or a callback function.', + env: "PARSE_SERVER_PAGES_PLACEHOLDERS", + help: "The placeholder keys and values which will be filled in pages; this can be a simple object or a callback function.", action: parsers.objectParser, default: {}, }, }; module.exports.PagesRoute = { handler: { - env: 'PARSE_SERVER_PAGES_ROUTE_HANDLER', - help: 'The route handler that is an async function.', + env: "PARSE_SERVER_PAGES_ROUTE_HANDLER", + help: "The route handler that is an async function.", required: true, }, method: { - env: 'PARSE_SERVER_PAGES_ROUTE_METHOD', + env: "PARSE_SERVER_PAGES_ROUTE_METHOD", help: "The route method, e.g. 'GET' or 'POST'.", required: true, }, path: { - env: 'PARSE_SERVER_PAGES_ROUTE_PATH', - help: 'The route path.', + env: "PARSE_SERVER_PAGES_ROUTE_PATH", + help: "The route path.", required: true, }, }; module.exports.PagesCustomUrlsOptions = { emailVerificationLinkExpired: { - env: 'PARSE_SERVER_PAGES_CUSTOM_URL_EMAIL_VERIFICATION_LINK_EXPIRED', - help: 'The URL to the custom page for email verification -> link expired.', + env: "PARSE_SERVER_PAGES_CUSTOM_URL_EMAIL_VERIFICATION_LINK_EXPIRED", + help: "The URL to the custom page for email verification -> link expired.", }, emailVerificationLinkInvalid: { - env: 'PARSE_SERVER_PAGES_CUSTOM_URL_EMAIL_VERIFICATION_LINK_INVALID', - help: 'The URL to the custom page for email verification -> link invalid.', + env: "PARSE_SERVER_PAGES_CUSTOM_URL_EMAIL_VERIFICATION_LINK_INVALID", + help: "The URL to the custom page for email verification -> link invalid.", }, emailVerificationSendFail: { - env: 'PARSE_SERVER_PAGES_CUSTOM_URL_EMAIL_VERIFICATION_SEND_FAIL', - help: 'The URL to the custom page for email verification -> link send fail.', + env: "PARSE_SERVER_PAGES_CUSTOM_URL_EMAIL_VERIFICATION_SEND_FAIL", + help: "The URL to the custom page for email verification -> link send fail.", }, emailVerificationSendSuccess: { - env: 'PARSE_SERVER_PAGES_CUSTOM_URL_EMAIL_VERIFICATION_SEND_SUCCESS', - help: 'The URL to the custom page for email verification -> resend link -> success.', + env: "PARSE_SERVER_PAGES_CUSTOM_URL_EMAIL_VERIFICATION_SEND_SUCCESS", + help: "The URL to the custom page for email verification -> resend link -> success.", }, emailVerificationSuccess: { - env: 'PARSE_SERVER_PAGES_CUSTOM_URL_EMAIL_VERIFICATION_SUCCESS', - help: 'The URL to the custom page for email verification -> success.', + env: "PARSE_SERVER_PAGES_CUSTOM_URL_EMAIL_VERIFICATION_SUCCESS", + help: "The URL to the custom page for email verification -> success.", }, passwordReset: { - env: 'PARSE_SERVER_PAGES_CUSTOM_URL_PASSWORD_RESET', - help: 'The URL to the custom page for password reset.', + env: "PARSE_SERVER_PAGES_CUSTOM_URL_PASSWORD_RESET", + help: "The URL to the custom page for password reset.", }, passwordResetLinkInvalid: { - env: 'PARSE_SERVER_PAGES_CUSTOM_URL_PASSWORD_RESET_LINK_INVALID', - help: 'The URL to the custom page for password reset -> link invalid.', + env: "PARSE_SERVER_PAGES_CUSTOM_URL_PASSWORD_RESET_LINK_INVALID", + help: "The URL to the custom page for password reset -> link invalid.", }, passwordResetSuccess: { - env: 'PARSE_SERVER_PAGES_CUSTOM_URL_PASSWORD_RESET_SUCCESS', - help: 'The URL to the custom page for password reset -> success.', + env: "PARSE_SERVER_PAGES_CUSTOM_URL_PASSWORD_RESET_SUCCESS", + help: "The URL to the custom page for password reset -> success.", }, }; module.exports.CustomPagesOptions = { choosePassword: { - env: 'PARSE_SERVER_CUSTOM_PAGES_CHOOSE_PASSWORD', - help: 'choose password page path', + env: "PARSE_SERVER_CUSTOM_PAGES_CHOOSE_PASSWORD", + help: "choose password page path", }, expiredVerificationLink: { - env: 'PARSE_SERVER_CUSTOM_PAGES_EXPIRED_VERIFICATION_LINK', - help: 'expired verification link page path', + env: "PARSE_SERVER_CUSTOM_PAGES_EXPIRED_VERIFICATION_LINK", + help: "expired verification link page path", }, invalidLink: { - env: 'PARSE_SERVER_CUSTOM_PAGES_INVALID_LINK', - help: 'invalid link page path', + env: "PARSE_SERVER_CUSTOM_PAGES_INVALID_LINK", + help: "invalid link page path", }, invalidPasswordResetLink: { - env: 'PARSE_SERVER_CUSTOM_PAGES_INVALID_PASSWORD_RESET_LINK', - help: 'invalid password reset link page path', + env: "PARSE_SERVER_CUSTOM_PAGES_INVALID_PASSWORD_RESET_LINK", + help: "invalid password reset link page path", }, invalidVerificationLink: { - env: 'PARSE_SERVER_CUSTOM_PAGES_INVALID_VERIFICATION_LINK', - help: 'invalid verification link page path', + env: "PARSE_SERVER_CUSTOM_PAGES_INVALID_VERIFICATION_LINK", + help: "invalid verification link page path", }, linkSendFail: { - env: 'PARSE_SERVER_CUSTOM_PAGES_LINK_SEND_FAIL', - help: 'verification link send fail page path', + env: "PARSE_SERVER_CUSTOM_PAGES_LINK_SEND_FAIL", + help: "verification link send fail page path", }, linkSendSuccess: { - env: 'PARSE_SERVER_CUSTOM_PAGES_LINK_SEND_SUCCESS', - help: 'verification link send success page path', + env: "PARSE_SERVER_CUSTOM_PAGES_LINK_SEND_SUCCESS", + help: "verification link send success page path", }, parseFrameURL: { - env: 'PARSE_SERVER_CUSTOM_PAGES_PARSE_FRAME_URL', - help: 'for masking user-facing pages', + env: "PARSE_SERVER_CUSTOM_PAGES_PARSE_FRAME_URL", + help: "for masking user-facing pages", }, passwordResetSuccess: { - env: 'PARSE_SERVER_CUSTOM_PAGES_PASSWORD_RESET_SUCCESS', - help: 'password reset success page path', + env: "PARSE_SERVER_CUSTOM_PAGES_PASSWORD_RESET_SUCCESS", + help: "password reset success page path", }, verifyEmailSuccess: { - env: 'PARSE_SERVER_CUSTOM_PAGES_VERIFY_EMAIL_SUCCESS', - help: 'verify email success page path', + env: "PARSE_SERVER_CUSTOM_PAGES_VERIFY_EMAIL_SUCCESS", + help: "verify email success page path", }, }; module.exports.LiveQueryOptions = { classNames: { - env: 'PARSE_SERVER_LIVEQUERY_CLASSNAMES', + env: "PARSE_SERVER_LIVEQUERY_CLASSNAMES", help: "parse-server's LiveQuery classNames", action: parsers.arrayParser, }, pubSubAdapter: { - env: 'PARSE_SERVER_LIVEQUERY_PUB_SUB_ADAPTER', - help: 'LiveQuery pubsub adapter', + env: "PARSE_SERVER_LIVEQUERY_PUB_SUB_ADAPTER", + help: "LiveQuery pubsub adapter", action: parsers.moduleOrObjectParser, }, redisOptions: { - env: 'PARSE_SERVER_LIVEQUERY_REDIS_OPTIONS', + env: "PARSE_SERVER_LIVEQUERY_REDIS_OPTIONS", help: "parse-server's LiveQuery redisOptions", action: parsers.objectParser, }, redisURL: { - env: 'PARSE_SERVER_LIVEQUERY_REDIS_URL', + env: "PARSE_SERVER_LIVEQUERY_REDIS_URL", help: "parse-server's LiveQuery redisURL", }, wssAdapter: { - env: 'PARSE_SERVER_LIVEQUERY_WSS_ADAPTER', - help: 'Adapter module for the WebSocketServer', + env: "PARSE_SERVER_LIVEQUERY_WSS_ADAPTER", + help: "Adapter module for the WebSocketServer", action: parsers.moduleOrObjectParser, }, }; module.exports.LiveQueryServerOptions = { appId: { - env: 'PARSE_LIVE_QUERY_SERVER_APP_ID', - help: 'This string should match the appId in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same appId.', + env: "PARSE_LIVE_QUERY_SERVER_APP_ID", + help: "This string should match the appId in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same appId.", }, cacheTimeout: { - env: 'PARSE_LIVE_QUERY_SERVER_CACHE_TIMEOUT', + env: "PARSE_LIVE_QUERY_SERVER_CACHE_TIMEOUT", help: "Number in milliseconds. When clients provide the sessionToken to the LiveQuery server, the LiveQuery server will try to fetch its ParseUser's objectId from parse server and store it in the cache. The value defines the duration of the cache. Check the following Security section and our protocol specification for details, defaults to 5 * 1000 ms (5 seconds).", - action: parsers.numberParser('cacheTimeout'), + action: parsers.numberParser("cacheTimeout"), }, keyPairs: { - env: 'PARSE_LIVE_QUERY_SERVER_KEY_PAIRS', - help: 'A JSON object that serves as a whitelist of keys. It is used for validating clients when they try to connect to the LiveQuery server. Check the following Security section and our protocol specification for details.', + env: "PARSE_LIVE_QUERY_SERVER_KEY_PAIRS", + help: "A JSON object that serves as a whitelist of keys. It is used for validating clients when they try to connect to the LiveQuery server. Check the following Security section and our protocol specification for details.", action: parsers.objectParser, }, logLevel: { - env: 'PARSE_LIVE_QUERY_SERVER_LOG_LEVEL', - help: 'This string defines the log level of the LiveQuery server. We support VERBOSE, INFO, ERROR, NONE, defaults to INFO.', + env: "PARSE_LIVE_QUERY_SERVER_LOG_LEVEL", + help: "This string defines the log level of the LiveQuery server. We support VERBOSE, INFO, ERROR, NONE, defaults to INFO.", }, masterKey: { - env: 'PARSE_LIVE_QUERY_SERVER_MASTER_KEY', - help: 'This string should match the masterKey in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same masterKey.', + env: "PARSE_LIVE_QUERY_SERVER_MASTER_KEY", + help: "This string should match the masterKey in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same masterKey.", }, port: { - env: 'PARSE_LIVE_QUERY_SERVER_PORT', - help: 'The port to run the LiveQuery server, defaults to 1337.', - action: parsers.numberParser('port'), + env: "PARSE_LIVE_QUERY_SERVER_PORT", + help: "The port to run the LiveQuery server, defaults to 1337.", + action: parsers.numberParser("port"), default: 1337, }, pubSubAdapter: { - env: 'PARSE_LIVE_QUERY_SERVER_PUB_SUB_ADAPTER', - help: 'LiveQuery pubsub adapter', + env: "PARSE_LIVE_QUERY_SERVER_PUB_SUB_ADAPTER", + help: "LiveQuery pubsub adapter", action: parsers.moduleOrObjectParser, }, redisOptions: { - env: 'PARSE_LIVE_QUERY_SERVER_REDIS_OPTIONS', + env: "PARSE_LIVE_QUERY_SERVER_REDIS_OPTIONS", help: "parse-server's LiveQuery redisOptions", action: parsers.objectParser, }, redisURL: { - env: 'PARSE_LIVE_QUERY_SERVER_REDIS_URL', + env: "PARSE_LIVE_QUERY_SERVER_REDIS_URL", help: "parse-server's LiveQuery redisURL", }, serverURL: { - env: 'PARSE_LIVE_QUERY_SERVER_SERVER_URL', - help: 'This string should match the serverURL in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same serverURL.', + env: "PARSE_LIVE_QUERY_SERVER_SERVER_URL", + help: "This string should match the serverURL in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same serverURL.", }, websocketTimeout: { - env: 'PARSE_LIVE_QUERY_SERVER_WEBSOCKET_TIMEOUT', - help: 'Number of milliseconds between ping/pong frames. The WebSocket server sends ping/pong frames to the clients to keep the WebSocket alive. This value defines the interval of the ping/pong frame from the server to clients, defaults to 10 * 1000 ms (10 s).', - action: parsers.numberParser('websocketTimeout'), + env: "PARSE_LIVE_QUERY_SERVER_WEBSOCKET_TIMEOUT", + help: "Number of milliseconds between ping/pong frames. The WebSocket server sends ping/pong frames to the clients to keep the WebSocket alive. This value defines the interval of the ping/pong frame from the server to clients, defaults to 10 * 1000 ms (10 s).", + action: parsers.numberParser("websocketTimeout"), }, wssAdapter: { - env: 'PARSE_LIVE_QUERY_SERVER_WSS_ADAPTER', - help: 'Adapter module for the WebSocketServer', + env: "PARSE_LIVE_QUERY_SERVER_WSS_ADAPTER", + help: "Adapter module for the WebSocketServer", action: parsers.moduleOrObjectParser, }, }; module.exports.IdempotencyOptions = { paths: { - env: 'PARSE_SERVER_EXPERIMENTAL_IDEMPOTENCY_PATHS', - help: 'An array of paths for which the feature should be enabled. The mount path must not be included, for example instead of `/parse/functions/myFunction` specifiy `functions/myFunction`. The entries are interpreted as regular expression, for example `functions/.*` matches all functions, `jobs/.*` matches all jobs, `classes/.*` matches all classes, `.*` matches all paths.', + env: "PARSE_SERVER_EXPERIMENTAL_IDEMPOTENCY_PATHS", + help: "An array of paths for which the feature should be enabled. The mount path must not be included, for example instead of `/parse/functions/myFunction` specifiy `functions/myFunction`. The entries are interpreted as regular expression, for example `functions/.*` matches all functions, `jobs/.*` matches all jobs, `classes/.*` matches all classes, `.*` matches all paths.", action: parsers.arrayParser, default: [], }, ttl: { - env: 'PARSE_SERVER_EXPERIMENTAL_IDEMPOTENCY_TTL', - help: 'The duration in seconds after which a request record is discarded from the database, defaults to 300s.', - action: parsers.numberParser('ttl'), + env: "PARSE_SERVER_EXPERIMENTAL_IDEMPOTENCY_TTL", + help: "The duration in seconds after which a request record is discarded from the database, defaults to 300s.", + action: parsers.numberParser("ttl"), default: 300, }, }; module.exports.AccountLockoutOptions = { duration: { - env: 'PARSE_SERVER_ACCOUNT_LOCKOUT_DURATION', - help: 'Set the duration in minutes that a locked-out account remains locked out before automatically becoming unlocked.

Valid values are greater than `0` and less than `100000`.', - action: parsers.numberParser('duration'), + env: "PARSE_SERVER_ACCOUNT_LOCKOUT_DURATION", + help: "Set the duration in minutes that a locked-out account remains locked out before automatically becoming unlocked.

Valid values are greater than `0` and less than `100000`.", + action: parsers.numberParser("duration"), }, threshold: { - env: 'PARSE_SERVER_ACCOUNT_LOCKOUT_THRESHOLD', - help: 'Set the number of failed sign-in attempts that will cause a user account to be locked. If the account is locked. The account will unlock after the duration set in the `duration` option has passed and no further login attempts have been made.

Valid values are greater than `0` and less than `1000`.', - action: parsers.numberParser('threshold'), + env: "PARSE_SERVER_ACCOUNT_LOCKOUT_THRESHOLD", + help: "Set the number of failed sign-in attempts that will cause a user account to be locked. If the account is locked. The account will unlock after the duration set in the `duration` option has passed and no further login attempts have been made.

Valid values are greater than `0` and less than `1000`.", + action: parsers.numberParser("threshold"), }, unlockOnPasswordReset: { - env: 'PARSE_SERVER_ACCOUNT_LOCKOUT_UNLOCK_ON_PASSWORD_RESET', - help: 'Set to `true` if the account should be unlocked after a successful password reset.

Default is `false`.
Requires options `duration` and `threshold` to be set.', + env: "PARSE_SERVER_ACCOUNT_LOCKOUT_UNLOCK_ON_PASSWORD_RESET", + help: "Set to `true` if the account should be unlocked after a successful password reset.

Default is `false`.
Requires options `duration` and `threshold` to be set.", action: parsers.booleanParser, default: false, }, }; module.exports.PasswordPolicyOptions = { doNotAllowUsername: { - env: 'PARSE_SERVER_PASSWORD_POLICY_DO_NOT_ALLOW_USERNAME', - help: 'Set to `true` to disallow the username as part of the password.

Default is `false`.', + env: "PARSE_SERVER_PASSWORD_POLICY_DO_NOT_ALLOW_USERNAME", + help: "Set to `true` to disallow the username as part of the password.

Default is `false`.", action: parsers.booleanParser, default: false, }, maxPasswordAge: { - env: 'PARSE_SERVER_PASSWORD_POLICY_MAX_PASSWORD_AGE', - help: 'Set the number of days after which a password expires. Login attempts fail if the user does not reset the password before expiration.', - action: parsers.numberParser('maxPasswordAge'), + env: "PARSE_SERVER_PASSWORD_POLICY_MAX_PASSWORD_AGE", + help: "Set the number of days after which a password expires. Login attempts fail if the user does not reset the password before expiration.", + action: parsers.numberParser("maxPasswordAge"), }, maxPasswordHistory: { - env: 'PARSE_SERVER_PASSWORD_POLICY_MAX_PASSWORD_HISTORY', - help: 'Set the number of previous password that will not be allowed to be set as new password. If the option is not set or set to `0`, no previous passwords will be considered.

Valid values are >= `0` and <= `20`.
Default is `0`.', - action: parsers.numberParser('maxPasswordHistory'), + env: "PARSE_SERVER_PASSWORD_POLICY_MAX_PASSWORD_HISTORY", + help: "Set the number of previous password that will not be allowed to be set as new password. If the option is not set or set to `0`, no previous passwords will be considered.

Valid values are >= `0` and <= `20`.
Default is `0`.", + action: parsers.numberParser("maxPasswordHistory"), }, resetPasswordSuccessOnInvalidEmail: { - env: 'PARSE_SERVER_PASSWORD_POLICY_RESET_PASSWORD_SUCCESS_ON_INVALID_EMAIL', - help: 'Set to `true` if a request to reset the password should return a success response even if the provided email address is invalid, or `false` if the request should return an error response if the email address is invalid.

Default is `true`.', + env: "PARSE_SERVER_PASSWORD_POLICY_RESET_PASSWORD_SUCCESS_ON_INVALID_EMAIL", + help: "Set to `true` if a request to reset the password should return a success response even if the provided email address is invalid, or `false` if the request should return an error response if the email address is invalid.

Default is `true`.", action: parsers.booleanParser, default: true, }, resetTokenReuseIfValid: { - env: 'PARSE_SERVER_PASSWORD_POLICY_RESET_TOKEN_REUSE_IF_VALID', - help: 'Set to `true` if a password reset token should be reused in case another token is requested but there is a token that is still valid, i.e. has not expired. This avoids the often observed issue that a user requests multiple emails and does not know which link contains a valid token because each newly generated token would invalidate the previous token.

Default is `false`.', + env: "PARSE_SERVER_PASSWORD_POLICY_RESET_TOKEN_REUSE_IF_VALID", + help: "Set to `true` if a password reset token should be reused in case another token is requested but there is a token that is still valid, i.e. has not expired. This avoids the often observed issue that a user requests multiple emails and does not know which link contains a valid token because each newly generated token would invalidate the previous token.

Default is `false`.", action: parsers.booleanParser, default: false, }, resetTokenValidityDuration: { - env: 'PARSE_SERVER_PASSWORD_POLICY_RESET_TOKEN_VALIDITY_DURATION', - help: 'Set the validity duration of the password reset token in seconds after which the token expires. The token is used in the link that is set in the email. After the token expires, the link becomes invalid and a new link has to be sent. If the option is not set or set to `undefined`, then the token never expires.

For example, to expire the token after 2 hours, set a value of 7200 seconds (= 60 seconds * 60 minutes * 2 hours).

Default is `undefined`.', - action: parsers.numberParser('resetTokenValidityDuration'), + env: "PARSE_SERVER_PASSWORD_POLICY_RESET_TOKEN_VALIDITY_DURATION", + help: "Set the validity duration of the password reset token in seconds after which the token expires. The token is used in the link that is set in the email. After the token expires, the link becomes invalid and a new link has to be sent. If the option is not set or set to `undefined`, then the token never expires.

For example, to expire the token after 2 hours, set a value of 7200 seconds (= 60 seconds * 60 minutes * 2 hours).

Default is `undefined`.", + action: parsers.numberParser("resetTokenValidityDuration"), }, validationError: { - env: 'PARSE_SERVER_PASSWORD_POLICY_VALIDATION_ERROR', - help: 'Set the error message to be sent.

Default is `Password does not meet the Password Policy requirements.`', + env: "PARSE_SERVER_PASSWORD_POLICY_VALIDATION_ERROR", + help: "Set the error message to be sent.

Default is `Password does not meet the Password Policy requirements.`", }, validatorCallback: { - env: 'PARSE_SERVER_PASSWORD_POLICY_VALIDATOR_CALLBACK', - help: 'Set a callback function to validate a password to be accepted.

If used in combination with `validatorPattern`, the password must pass both to be accepted.', + env: "PARSE_SERVER_PASSWORD_POLICY_VALIDATOR_CALLBACK", + help: "Set a callback function to validate a password to be accepted.

If used in combination with `validatorPattern`, the password must pass both to be accepted.", }, validatorPattern: { - env: 'PARSE_SERVER_PASSWORD_POLICY_VALIDATOR_PATTERN', - help: 'Set the regular expression validation pattern a password must match to be accepted.

If used in combination with `validatorCallback`, the password must pass both to be accepted.', + env: "PARSE_SERVER_PASSWORD_POLICY_VALIDATOR_PATTERN", + help: "Set the regular expression validation pattern a password must match to be accepted.

If used in combination with `validatorCallback`, the password must pass both to be accepted.", }, }; module.exports.FileUploadOptions = { enableForAnonymousUser: { - env: 'PARSE_SERVER_FILE_UPLOAD_ENABLE_FOR_ANONYMOUS_USER', - help: 'Is true if file upload should be allowed for anonymous users.', + env: "PARSE_SERVER_FILE_UPLOAD_ENABLE_FOR_ANONYMOUS_USER", + help: "Is true if file upload should be allowed for anonymous users.", action: parsers.booleanParser, default: false, }, enableForAuthenticatedUser: { - env: 'PARSE_SERVER_FILE_UPLOAD_ENABLE_FOR_AUTHENTICATED_USER', - help: 'Is true if file upload should be allowed for authenticated users.', + env: "PARSE_SERVER_FILE_UPLOAD_ENABLE_FOR_AUTHENTICATED_USER", + help: "Is true if file upload should be allowed for authenticated users.", action: parsers.booleanParser, default: true, }, enableForPublic: { - env: 'PARSE_SERVER_FILE_UPLOAD_ENABLE_FOR_PUBLIC', - help: 'Is true if file upload should be allowed for anyone, regardless of user authentication.', + env: "PARSE_SERVER_FILE_UPLOAD_ENABLE_FOR_PUBLIC", + help: "Is true if file upload should be allowed for anyone, regardless of user authentication.", action: parsers.booleanParser, default: false, }, fileExtensions: { - env: 'PARSE_SERVER_FILE_UPLOAD_FILE_EXTENSIONS', + env: "PARSE_SERVER_FILE_UPLOAD_FILE_EXTENSIONS", help: "Sets the allowed file extensions for uploading files. The extension is defined as an array of file extensions, or a regex pattern.

It is recommended to restrict the file upload extensions as much as possible. HTML files are especially problematic as they may be used by an attacker who uploads a HTML form to look legitimate under your app's domain name, or to compromise the session token of another user via accessing the browser's local storage.

Defaults to `^(?!(h|H)(t|T)(m|M)(l|L)?$)` which allows any file extension except HTML files.", action: parsers.arrayParser, - default: ['^(?!(h|H)(t|T)(m|M)(l|L)?$)'], + default: ["^(?!(h|H)(t|T)(m|M)(l|L)?$)"], }, }; module.exports.DatabaseOptions = { autoSelectFamily: { - env: 'PARSE_SERVER_DATABASE_AUTO_SELECT_FAMILY', - help: 'The MongoDB driver option to set whether the socket attempts to connect to IPv6 and IPv4 addresses until a connection is established. If available, the driver will select the first IPv6 address.', + env: "PARSE_SERVER_DATABASE_AUTO_SELECT_FAMILY", + help: "The MongoDB driver option to set whether the socket attempts to connect to IPv6 and IPv4 addresses until a connection is established. If available, the driver will select the first IPv6 address.", action: parsers.booleanParser, }, autoSelectFamilyAttemptTimeout: { - env: 'PARSE_SERVER_DATABASE_AUTO_SELECT_FAMILY_ATTEMPT_TIMEOUT', - help: 'The MongoDB driver option to specify the amount of time in milliseconds to wait for a connection attempt to finish before trying the next address when using the autoSelectFamily option. If set to a positive integer less than 10, the value 10 is used instead.', - action: parsers.numberParser('autoSelectFamilyAttemptTimeout'), + env: "PARSE_SERVER_DATABASE_AUTO_SELECT_FAMILY_ATTEMPT_TIMEOUT", + help: "The MongoDB driver option to specify the amount of time in milliseconds to wait for a connection attempt to finish before trying the next address when using the autoSelectFamily option. If set to a positive integer less than 10, the value 10 is used instead.", + action: parsers.numberParser("autoSelectFamilyAttemptTimeout"), }, connectTimeoutMS: { - env: 'PARSE_SERVER_DATABASE_CONNECT_TIMEOUT_MS', - help: 'The MongoDB driver option to specify the amount of time, in milliseconds, to wait to establish a single TCP socket connection to the server before raising an error. Specifying 0 disables the connection timeout.', - action: parsers.numberParser('connectTimeoutMS'), + env: "PARSE_SERVER_DATABASE_CONNECT_TIMEOUT_MS", + help: "The MongoDB driver option to specify the amount of time, in milliseconds, to wait to establish a single TCP socket connection to the server before raising an error. Specifying 0 disables the connection timeout.", + action: parsers.numberParser("connectTimeoutMS"), }, enableSchemaHooks: { - env: 'PARSE_SERVER_DATABASE_ENABLE_SCHEMA_HOOKS', - help: 'Enables database real-time hooks to update single schema cache. Set to `true` if using multiple Parse Servers instances connected to the same database. Failing to do so will cause a schema change to not propagate to all instances and re-syncing will only happen when the instances restart. To use this feature with MongoDB, a replica set cluster with [change stream](https://docs.mongodb.com/manual/changeStreams/#availability) support is required.', + env: "PARSE_SERVER_DATABASE_ENABLE_SCHEMA_HOOKS", + help: "Enables database real-time hooks to update single schema cache. Set to `true` if using multiple Parse Servers instances connected to the same database. Failing to do so will cause a schema change to not propagate to all instances and re-syncing will only happen when the instances restart. To use this feature with MongoDB, a replica set cluster with [change stream](https://docs.mongodb.com/manual/changeStreams/#availability) support is required.", action: parsers.booleanParser, default: false, }, maxPoolSize: { - env: 'PARSE_SERVER_DATABASE_MAX_POOL_SIZE', - help: 'The MongoDB driver option to set the maximum number of opened, cached, ready-to-use database connections maintained by the driver.', - action: parsers.numberParser('maxPoolSize'), + env: "PARSE_SERVER_DATABASE_MAX_POOL_SIZE", + help: "The MongoDB driver option to set the maximum number of opened, cached, ready-to-use database connections maintained by the driver.", + action: parsers.numberParser("maxPoolSize"), }, maxStalenessSeconds: { - env: 'PARSE_SERVER_DATABASE_MAX_STALENESS_SECONDS', - help: 'The MongoDB driver option to set the maximum replication lag for reads from secondary nodes.', - action: parsers.numberParser('maxStalenessSeconds'), + env: "PARSE_SERVER_DATABASE_MAX_STALENESS_SECONDS", + help: "The MongoDB driver option to set the maximum replication lag for reads from secondary nodes.", + action: parsers.numberParser("maxStalenessSeconds"), }, maxTimeMS: { - env: 'PARSE_SERVER_DATABASE_MAX_TIME_MS', - help: 'The MongoDB driver option to set a cumulative time limit in milliseconds for processing operations on a cursor.', - action: parsers.numberParser('maxTimeMS'), + env: "PARSE_SERVER_DATABASE_MAX_TIME_MS", + help: "The MongoDB driver option to set a cumulative time limit in milliseconds for processing operations on a cursor.", + action: parsers.numberParser("maxTimeMS"), }, minPoolSize: { - env: 'PARSE_SERVER_DATABASE_MIN_POOL_SIZE', - help: 'The MongoDB driver option to set the minimum number of opened, cached, ready-to-use database connections maintained by the driver.', - action: parsers.numberParser('minPoolSize'), + env: "PARSE_SERVER_DATABASE_MIN_POOL_SIZE", + help: "The MongoDB driver option to set the minimum number of opened, cached, ready-to-use database connections maintained by the driver.", + action: parsers.numberParser("minPoolSize"), }, retryWrites: { - env: 'PARSE_SERVER_DATABASE_RETRY_WRITES', - help: 'The MongoDB driver option to set whether to retry failed writes.', + env: "PARSE_SERVER_DATABASE_RETRY_WRITES", + help: "The MongoDB driver option to set whether to retry failed writes.", action: parsers.booleanParser, }, schemaCacheTtl: { - env: 'PARSE_SERVER_DATABASE_SCHEMA_CACHE_TTL', - help: 'The duration in seconds after which the schema cache expires and will be refetched from the database. Use this option if using multiple Parse Servers instances connected to the same database. A low duration will cause the schema cache to be updated too often, causing unnecessary database reads. A high duration will cause the schema to be updated too rarely, increasing the time required until schema changes propagate to all server instances. This feature can be used as an alternative or in conjunction with the option `enableSchemaHooks`. Default is infinite which means the schema cache never expires.', - action: parsers.numberParser('schemaCacheTtl'), + env: "PARSE_SERVER_DATABASE_SCHEMA_CACHE_TTL", + help: "The duration in seconds after which the schema cache expires and will be refetched from the database. Use this option if using multiple Parse Servers instances connected to the same database. A low duration will cause the schema cache to be updated too often, causing unnecessary database reads. A high duration will cause the schema to be updated too rarely, increasing the time required until schema changes propagate to all server instances. This feature can be used as an alternative or in conjunction with the option `enableSchemaHooks`. Default is infinite which means the schema cache never expires.", + action: parsers.numberParser("schemaCacheTtl"), }, socketTimeoutMS: { - env: 'PARSE_SERVER_DATABASE_SOCKET_TIMEOUT_MS', - help: 'The MongoDB driver option to specify the amount of time, in milliseconds, spent attempting to send or receive on a socket before timing out. Specifying 0 means no timeout.', - action: parsers.numberParser('socketTimeoutMS'), + env: "PARSE_SERVER_DATABASE_SOCKET_TIMEOUT_MS", + help: "The MongoDB driver option to specify the amount of time, in milliseconds, spent attempting to send or receive on a socket before timing out. Specifying 0 means no timeout.", + action: parsers.numberParser("socketTimeoutMS"), }, }; module.exports.AuthAdapter = { enabled: { - help: 'Is `true` if the auth adapter is enabled, `false` otherwise.', + help: "Is `true` if the auth adapter is enabled, `false` otherwise.", action: parsers.booleanParser, default: false, }, }; module.exports.LogLevels = { cloudFunctionError: { - env: 'PARSE_SERVER_LOG_LEVELS_CLOUD_FUNCTION_ERROR', - help: 'Log level used by the Cloud Code Functions on error. Default is `error`.', - default: 'error', + env: "PARSE_SERVER_LOG_LEVELS_CLOUD_FUNCTION_ERROR", + help: "Log level used by the Cloud Code Functions on error. Default is `error`.", + default: "error", }, cloudFunctionSuccess: { - env: 'PARSE_SERVER_LOG_LEVELS_CLOUD_FUNCTION_SUCCESS', - help: 'Log level used by the Cloud Code Functions on success. Default is `info`.', - default: 'info', + env: "PARSE_SERVER_LOG_LEVELS_CLOUD_FUNCTION_SUCCESS", + help: "Log level used by the Cloud Code Functions on success. Default is `info`.", + default: "info", }, triggerAfter: { - env: 'PARSE_SERVER_LOG_LEVELS_TRIGGER_AFTER', - help: 'Log level used by the Cloud Code Triggers `afterSave`, `afterDelete`, `afterFind`, `afterLogout`. Default is `info`.', - default: 'info', + env: "PARSE_SERVER_LOG_LEVELS_TRIGGER_AFTER", + help: "Log level used by the Cloud Code Triggers `afterSave`, `afterDelete`, `afterFind`, `afterLogout`. Default is `info`.", + default: "info", }, triggerBeforeError: { - env: 'PARSE_SERVER_LOG_LEVELS_TRIGGER_BEFORE_ERROR', - help: 'Log level used by the Cloud Code Triggers `beforeSave`, `beforeDelete`, `beforeFind`, `beforeLogin` on error. Default is `error`.', - default: 'error', + env: "PARSE_SERVER_LOG_LEVELS_TRIGGER_BEFORE_ERROR", + help: "Log level used by the Cloud Code Triggers `beforeSave`, `beforeDelete`, `beforeFind`, `beforeLogin` on error. Default is `error`.", + default: "error", }, triggerBeforeSuccess: { - env: 'PARSE_SERVER_LOG_LEVELS_TRIGGER_BEFORE_SUCCESS', - help: 'Log level used by the Cloud Code Triggers `beforeSave`, `beforeDelete`, `beforeFind`, `beforeLogin` on success. Default is `info`.', - default: 'info', + env: "PARSE_SERVER_LOG_LEVELS_TRIGGER_BEFORE_SUCCESS", + help: "Log level used by the Cloud Code Triggers `beforeSave`, `beforeDelete`, `beforeFind`, `beforeLogin` on success. Default is `info`.", + default: "info", }, }; diff --git a/src/Options/index.js b/src/Options/index.js index b3c04462ca..959bd7b45f 100644 --- a/src/Options/index.js +++ b/src/Options/index.js @@ -1,13 +1,13 @@ // @flow -import { AnalyticsAdapter } from '../Adapters/Analytics/AnalyticsAdapter'; -import { CacheAdapter } from '../Adapters/Cache/CacheAdapter'; -import { MailAdapter } from '../Adapters/Email/MailAdapter'; -import { FilesAdapter } from '../Adapters/Files/FilesAdapter'; -import { LoggerAdapter } from '../Adapters/Logger/LoggerAdapter'; -import { PubSubAdapter } from '../Adapters/PubSub/PubSubAdapter'; -import { StorageAdapter } from '../Adapters/Storage/StorageAdapter'; -import { WSSAdapter } from '../Adapters/WebSocketServer/WSSAdapter'; -import { CheckGroup } from '../Security/CheckGroup'; +import { AnalyticsAdapter } from "../Adapters/Analytics/AnalyticsAdapter"; +import { CacheAdapter } from "../Adapters/Cache/CacheAdapter"; +import { MailAdapter } from "../Adapters/Email/MailAdapter"; +import { FilesAdapter } from "../Adapters/Files/FilesAdapter"; +import { LoggerAdapter } from "../Adapters/Logger/LoggerAdapter"; +import { PubSubAdapter } from "../Adapters/PubSub/PubSubAdapter"; +import { StorageAdapter } from "../Adapters/Storage/StorageAdapter"; +import { WSSAdapter } from "../Adapters/WebSocketServer/WSSAdapter"; +import { CheckGroup } from "../Security/CheckGroup"; export interface SchemaOptions { /* Rest representation on Parse.Schema https://docs.parseplatform.org/rest/guide/#adding-a-schema diff --git a/src/Options/parsers.js b/src/Options/parsers.js index 384b5494ef..8e2a2ebf5c 100644 --- a/src/Options/parsers.js +++ b/src/Options/parsers.js @@ -10,13 +10,13 @@ function numberParser(key) { function numberOrBoolParser(key) { return function (opt) { - if (typeof opt === 'boolean') { + if (typeof opt === "boolean") { return opt; } - if (opt === 'true') { + if (opt === "true") { return true; } - if (opt === 'false') { + if (opt === "false") { return false; } return numberParser(key)(opt); @@ -25,7 +25,7 @@ function numberOrBoolParser(key) { function numberOrStringParser(key) { return function (opt) { - if (typeof opt === 'string') { + if (typeof opt === "string") { return opt; } return numberParser(key)(opt); @@ -33,7 +33,7 @@ function numberOrStringParser(key) { } function objectParser(opt) { - if (typeof opt == 'object') { + if (typeof opt == "object") { return opt; } return JSON.parse(opt); @@ -42,15 +42,15 @@ function objectParser(opt) { function arrayParser(opt) { if (Array.isArray(opt)) { return opt; - } else if (typeof opt === 'string') { - return opt.split(','); + } else if (typeof opt === "string") { + return opt.split(","); } else { throw new Error(`${opt} should be a comma separated string or an array`); } } function moduleOrObjectParser(opt) { - if (typeof opt == 'object') { + if (typeof opt == "object") { return opt; } try { @@ -62,14 +62,14 @@ function moduleOrObjectParser(opt) { } function booleanParser(opt) { - if (opt == true || opt == 'true' || opt == '1') { + if (opt == true || opt == "true" || opt == "1") { return true; } return false; } function nullParser(opt) { - if (opt == 'null') { + if (opt == "null") { return null; } return opt; diff --git a/src/ParseServerRESTController.js b/src/ParseServerRESTController.js index 9ec4b6f86e..9ccc9564fa 100644 --- a/src/ParseServerRESTController.js +++ b/src/ParseServerRESTController.js @@ -1,19 +1,21 @@ -const Config = require('./Config'); -const Auth = require('./Auth'); -import RESTController from 'parse/lib/node/RESTController'; -const Parse = require('parse/node'); +const Config = require("./Config"); +const Auth = require("./Auth"); +import RESTController from "parse/lib/node/RESTController"; +const Parse = require("parse/node"); function getSessionToken(options) { - if (options && typeof options.sessionToken === 'string') { + if (options && typeof options.sessionToken === "string") { return Promise.resolve(options.sessionToken); } return Promise.resolve(null); } function getAuth(options = {}, config) { - const installationId = options.installationId || 'cloud'; + const installationId = options.installationId || "cloud"; if (options.useMasterKey) { - return Promise.resolve(new Auth.Auth({ config, isMaster: true, installationId })); + return Promise.resolve( + new Auth.Auth({ config, isMaster: true, installationId }) + ); } return getSessionToken(options).then(sessionToken => { if (sessionToken) { @@ -42,11 +44,11 @@ function ParseServerRESTController(applicationId, router) { path = path.slice(serverURL.pathname.length, path.length); } - if (path[0] !== '/') { - path = '/' + path; + if (path[0] !== "/") { + path = "/" + path; } - if (path === '/batch') { + if (path === "/batch") { const batch = transactionRetries => { let initialPromise = Promise.resolve(); if (data.transaction === true) { @@ -54,14 +56,24 @@ function ParseServerRESTController(applicationId, router) { } return initialPromise.then(() => { const promises = data.requests.map(request => { - return handleRequest(request.method, request.path, request.body, options, config).then( + return handleRequest( + request.method, + request.path, + request.body, + options, + config + ).then( response => { if (options.returnStatus) { const status = response._status; const headers = response._headers; delete response._status; delete response._headers; - return { success: response, _status: status, _headers: headers }; + return { + success: response, + _status: status, + _headers: headers, + }; } return { success: response }; }, @@ -75,14 +87,22 @@ function ParseServerRESTController(applicationId, router) { return Promise.all(promises) .then(result => { if (data.transaction === true) { - if (result.find(resultItem => typeof resultItem.error === 'object')) { - return config.database.abortTransactionalSession().then(() => { - return Promise.reject(result); - }); + if ( + result.find( + resultItem => typeof resultItem.error === "object" + ) + ) { + return config.database + .abortTransactionalSession() + .then(() => { + return Promise.reject(result); + }); } else { - return config.database.commitTransactionalSession().then(() => { - return result; - }); + return config.database + .commitTransactionalSession() + .then(() => { + return result; + }); } } else { return result; @@ -92,7 +112,9 @@ function ParseServerRESTController(applicationId, router) { if ( error && error.find( - errorItem => typeof errorItem.error === 'object' && errorItem.error.code === 251 + errorItem => + typeof errorItem.error === "object" && + errorItem.error.code === 251 ) && transactionRetries > 0 ) { @@ -106,7 +128,7 @@ function ParseServerRESTController(applicationId, router) { } let query; - if (method === 'GET') { + if (method === "GET") { query = data; } diff --git a/src/PromiseRouter.js b/src/PromiseRouter.js index 3386daf223..39bdd6d49b 100644 --- a/src/PromiseRouter.js +++ b/src/PromiseRouter.js @@ -5,18 +5,18 @@ // themselves use our routing information, without disturbing express // components that external developers may be modifying. -import Parse from 'parse/node'; -import express from 'express'; -import log from './logger'; -import { inspect } from 'util'; -const Layer = require('router/lib/layer'); +import Parse from "parse/node"; +import express from "express"; +import log from "./logger"; +import { inspect } from "util"; +const Layer = require("router/lib/layer"); function validateParameter(key, value) { - if (key == 'className') { + if (key == "className") { if (value.match(/_?[A-Za-z][A-Za-z_0-9]*/)) { return value; } - } else if (key == 'objectId') { + } else if (key == "objectId") { if (value.match(/[A-Za-z0-9]+/)) { return value; } @@ -54,13 +54,13 @@ export default class PromiseRouter { route(method, path, ...handlers) { switch (method) { - case 'POST': - case 'GET': - case 'PUT': - case 'DELETE': + case "POST": + case "GET": + case "PUT": + case "DELETE": break; default: - throw 'cannot route method: ' + method; + throw "cannot route method: " + method; } let handler = handlers[0]; @@ -121,7 +121,10 @@ export default class PromiseRouter { tryRouteRequest(method, path, request) { var match = this.match(method, path); if (!match) { - throw new Parse.Error(Parse.Error.INVALID_JSON, 'cannot route ' + method + ' ' + path); + throw new Parse.Error( + Parse.Error.INVALID_JSON, + "cannot route " + method + " " + path + ); } request.params = match.params; return new Promise((resolve, reject) => { @@ -151,8 +154,10 @@ function makeExpressHandler(appId, promiseHandler) { .then( result => { if (!result.response && !result.location && !result.text) { - log.error('the handler did not include a "response" or a "location" field'); - throw 'control should not get here'; + log.error( + 'the handler did not include a "response" or a "location" field' + ); + throw "control should not get here"; } log.logResponse({ method, url, result }); @@ -172,11 +177,11 @@ function makeExpressHandler(appId, promiseHandler) { } if (result.location) { - res.set('Location', result.location); + res.set("Location", result.location); // Override the default expressjs response // as it double encodes %encoded chars in URL if (!result.response) { - res.send('Found. Redirecting to ' + result.location); + res.send("Found. Redirecting to " + result.location); return; } } @@ -200,9 +205,9 @@ function makeExpressHandler(appId, promiseHandler) { function maskSensitiveUrl(req) { let maskUrl = req.originalUrl.toString(); const shouldMaskUrl = - req.method === 'GET' && - req.originalUrl.includes('/login') && - !req.originalUrl.includes('classes'); + req.method === "GET" && + req.originalUrl.includes("/login") && + !req.originalUrl.includes("classes"); if (shouldMaskUrl) { maskUrl = log.maskSensitiveUrl(maskUrl); } diff --git a/src/Push/PushWorker.js b/src/Push/PushWorker.js index 2b3c4a2fb7..799670609a 100644 --- a/src/Push/PushWorker.js +++ b/src/Push/PushWorker.js @@ -1,20 +1,20 @@ // @flow // @flow-disable-next -import deepcopy from 'deepcopy'; -import AdaptableController from '../Controllers/AdaptableController'; -import { master } from '../Auth'; -import Config from '../Config'; -import { PushAdapter } from '../Adapters/Push/PushAdapter'; -import rest from '../rest'; -import { pushStatusHandler } from '../StatusHandler'; -import * as utils from './utils'; -import { ParseMessageQueue } from '../ParseMessageQueue'; -import { PushQueue } from './PushQueue'; -import logger from '../logger'; +import deepcopy from "deepcopy"; +import AdaptableController from "../Controllers/AdaptableController"; +import { master } from "../Auth"; +import Config from "../Config"; +import { PushAdapter } from "../Adapters/Push/PushAdapter"; +import rest from "../rest"; +import { pushStatusHandler } from "../StatusHandler"; +import * as utils from "./utils"; +import { ParseMessageQueue } from "../ParseMessageQueue"; +import { PushQueue } from "./PushQueue"; +import logger from "../logger"; function groupByBadge(installations) { return installations.reduce((map, installation) => { - const badge = installation.badge + ''; + const badge = installation.badge + ""; map[badge] = map[badge] || []; map[badge].push(installation); return map; @@ -35,7 +35,7 @@ export class PushWorker { if (this.subscriber) { const subscriber = this.subscriber; subscriber.subscribe(this.channel); - subscriber.on('message', (channel, messageStr) => { + subscriber.on("message", (channel, messageStr) => { const workItem = JSON.parse(messageStr); this.run(workItem); }); @@ -48,12 +48,14 @@ export class PushWorker { const where = utils.applyDeviceTokenExists(query.where); delete query.where; pushStatus = pushStatusHandler(config, pushStatus.objectId); - return rest.find(config, auth, '_Installation', where, query).then(({ results }) => { - if (results.length == 0) { - return; - } - return this.sendToAdapter(body, results, pushStatus, config, UTCOffset); - }); + return rest + .find(config, auth, "_Installation", where, query) + .then(({ results }) => { + if (results.length == 0) { + return; + } + return this.sendToAdapter(body, results, pushStatus, config, UTCOffset); + }); } sendToAdapter( @@ -70,20 +72,31 @@ export class PushWorker { const bodiesPerLocales = utils.bodiesPerLocales(body, locales); // Group installations on the specified locales (en, fr, default etc...) - const grouppedInstallations = utils.groupByLocaleIdentifier(installations, locales); + const grouppedInstallations = utils.groupByLocaleIdentifier( + installations, + locales + ); const promises = Object.keys(grouppedInstallations).map(locale => { const installations = grouppedInstallations[locale]; const body = bodiesPerLocales[locale]; - return this.sendToAdapter(body, installations, pushStatus, config, UTCOffset); + return this.sendToAdapter( + body, + installations, + pushStatus, + config, + UTCOffset + ); }); return Promise.all(promises); } if (!utils.isPushIncrementing(body)) { logger.verbose(`Sending push to ${installations.length}`); - return this.adapter.send(body, installations, pushStatus.objectId).then(results => { - return pushStatus.trackSent(results, UTCOffset).then(() => results); - }); + return this.adapter + .send(body, installations, pushStatus.objectId) + .then(results => { + return pushStatus.trackSent(results, UTCOffset).then(() => results); + }); } // Collect the badges to reduce the # of calls @@ -94,7 +107,13 @@ export class PushWorker { const payload = deepcopy(body); payload.data.badge = parseInt(badge); const installations = badgeInstallationsMap[badge]; - return this.sendToAdapter(payload, installations, pushStatus, config, UTCOffset); + return this.sendToAdapter( + payload, + installations, + pushStatus, + config, + UTCOffset + ); }); return Promise.all(promises); } diff --git a/src/Push/utils.js b/src/Push/utils.js index 5be13c272e..e8bfb00a5d 100644 --- a/src/Push/utils.js +++ b/src/Push/utils.js @@ -1,5 +1,5 @@ -import Parse from 'parse/node'; -import deepcopy from 'deepcopy'; +import Parse from "parse/node"; +import deepcopy from "deepcopy"; export function isPushIncrementing(body) { if (!body.data || !body.data.badge) { @@ -7,19 +7,19 @@ export function isPushIncrementing(body) { } const badge = body.data.badge; - if (typeof badge == 'string' && badge.toLowerCase() == 'increment') { + if (typeof badge == "string" && badge.toLowerCase() == "increment") { return true; } return ( - typeof badge == 'object' && - typeof badge.__op == 'string' && - badge.__op.toLowerCase() == 'increment' && + typeof badge == "object" && + typeof badge.__op == "string" && + badge.__op.toLowerCase() == "increment" && Number(badge.amount) ); } -const localizableKeys = ['alert', 'title']; +const localizableKeys = ["alert", "title"]; export function getLocalesFromPush(body) { const data = body.data; @@ -88,7 +88,10 @@ export function groupByLocaleIdentifier(installations, locales = []) { if (added) { return; } - if (installation.localeIdentifier && installation.localeIdentifier.indexOf(locale) === 0) { + if ( + installation.localeIdentifier && + installation.localeIdentifier.indexOf(locale) === 0 + ) { added = true; map[locale] = map[locale] || []; map[locale].push(installation); @@ -111,17 +114,17 @@ export function groupByLocaleIdentifier(installations, locales = []) { export function validatePushType(where = {}, validPushTypes = []) { var deviceTypeField = where.deviceType || {}; var deviceTypes = []; - if (typeof deviceTypeField === 'string') { + if (typeof deviceTypeField === "string") { deviceTypes.push(deviceTypeField); - } else if (Array.isArray(deviceTypeField['$in'])) { - deviceTypes.concat(deviceTypeField['$in']); + } else if (Array.isArray(deviceTypeField["$in"])) { + deviceTypes.concat(deviceTypeField["$in"]); } for (var i = 0; i < deviceTypes.length; i++) { var deviceType = deviceTypes[i]; if (validPushTypes.indexOf(deviceType) < 0) { throw new Parse.Error( Parse.Error.PUSH_MISCONFIGURED, - deviceType + ' is not supported push type.' + deviceType + " is not supported push type." ); } } @@ -129,8 +132,8 @@ export function validatePushType(where = {}, validPushTypes = []) { export function applyDeviceTokenExists(where) { where = deepcopy(where); - if (!Object.prototype.hasOwnProperty.call(where, 'deviceToken')) { - where['deviceToken'] = { $exists: true }; + if (!Object.prototype.hasOwnProperty.call(where, "deviceToken")) { + where["deviceToken"] = { $exists: true }; } return where; } diff --git a/src/RestQuery.js b/src/RestQuery.js index 323a83f2bf..5aec10df99 100644 --- a/src/RestQuery.js +++ b/src/RestQuery.js @@ -1,12 +1,12 @@ // An object that encapsulates everything we need to run a 'find' // operation, encoded in the REST API format. -var SchemaController = require('./Controllers/SchemaController'); -var Parse = require('parse/node').Parse; -const triggers = require('./triggers'); -const { continueWhile } = require('parse/lib/node/promiseUtils'); -const AlwaysSelectedKeys = ['objectId', 'createdAt', 'updatedAt', 'ACL']; -const { enforceRoleSecurity } = require('./SharedRest'); +var SchemaController = require("./Controllers/SchemaController"); +var Parse = require("parse/node").Parse; +const triggers = require("./triggers"); +const { continueWhile } = require("parse/lib/node/promiseUtils"); +const AlwaysSelectedKeys = ["objectId", "createdAt", "updatedAt", "ACL"]; +const { enforceRoleSecurity } = require("./SharedRest"); // restOptions can include: // skip @@ -48,20 +48,20 @@ async function RestQuery({ context, }) { if (![RestQuery.Method.find, RestQuery.Method.get].includes(method)) { - throw new Parse.Error(Parse.Error.INVALID_QUERY, 'bad query type'); + throw new Parse.Error(Parse.Error.INVALID_QUERY, "bad query type"); } enforceRoleSecurity(method, className, auth); const result = runBeforeFind ? await triggers.maybeRunQueryTrigger( - triggers.Types.beforeFind, - className, - restWhere, - restOptions, - config, - auth, - context, - method === RestQuery.Method.get - ) + triggers.Types.beforeFind, + className, + restWhere, + restOptions, + config, + auth, + context, + method === RestQuery.Method.get + ) : Promise.resolve({ restWhere, restOptions }); return new _UnsafeRestQuery( @@ -77,8 +77,8 @@ async function RestQuery({ } RestQuery.Method = Object.freeze({ - get: 'get', - find: 'find', + get: "get", + find: "find", }); /** @@ -114,17 +114,20 @@ function _UnsafeRestQuery( this.findOptions = {}; this.context = context || {}; if (!this.auth.isMaster) { - if (this.className == '_Session') { + if (this.className == "_Session") { if (!this.auth.user) { - throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid session token'); + throw new Parse.Error( + Parse.Error.INVALID_SESSION_TOKEN, + "Invalid session token" + ); } this.restWhere = { $and: [ this.restWhere, { user: { - __type: 'Pointer', - className: '_User', + __type: "Pointer", + className: "_User", objectId: this.auth.user.id, }, }, @@ -143,33 +146,33 @@ function _UnsafeRestQuery( // For example, passing an arg of include=foo.bar,foo.baz could lead to // this.include = [['foo'], ['foo', 'baz'], ['foo', 'bar']] this.include = []; - let keysForInclude = ''; + let keysForInclude = ""; // If we have keys, we probably want to force some includes (n-1 level) // See issue: https://github.com/parse-community/parse-server/issues/3185 - if (Object.prototype.hasOwnProperty.call(restOptions, 'keys')) { + if (Object.prototype.hasOwnProperty.call(restOptions, "keys")) { keysForInclude = restOptions.keys; } // If we have keys, we probably want to force some includes (n-1 level) // in order to exclude specific keys. - if (Object.prototype.hasOwnProperty.call(restOptions, 'excludeKeys')) { - keysForInclude += ',' + restOptions.excludeKeys; + if (Object.prototype.hasOwnProperty.call(restOptions, "excludeKeys")) { + keysForInclude += "," + restOptions.excludeKeys; } if (keysForInclude.length > 0) { keysForInclude = keysForInclude - .split(',') + .split(",") .filter(key => { // At least 2 components - return key.split('.').length > 1; + return key.split(".").length > 1; }) .map(key => { // Slice the last component (a.b.c -> a.b) // Otherwise we'll include one level too much. - return key.slice(0, key.lastIndexOf('.')); + return key.slice(0, key.lastIndexOf(".")); }) - .join(','); + .join(","); // Concat the possibly present include string with the one from the keys // Dedup / sorting is handle in 'include' case. @@ -177,51 +180,51 @@ function _UnsafeRestQuery( if (!restOptions.include || restOptions.include.length == 0) { restOptions.include = keysForInclude; } else { - restOptions.include += ',' + keysForInclude; + restOptions.include += "," + keysForInclude; } } } for (var option in restOptions) { switch (option) { - case 'keys': { + case "keys": { const keys = restOptions.keys - .split(',') + .split(",") .filter(key => key.length > 0) .concat(AlwaysSelectedKeys); this.keys = Array.from(new Set(keys)); break; } - case 'excludeKeys': { + case "excludeKeys": { const exclude = restOptions.excludeKeys - .split(',') + .split(",") .filter(k => AlwaysSelectedKeys.indexOf(k) < 0); this.excludeKeys = Array.from(new Set(exclude)); break; } - case 'count': + case "count": this.doCount = true; break; - case 'includeAll': + case "includeAll": this.includeAll = true; break; - case 'explain': - case 'hint': - case 'distinct': - case 'pipeline': - case 'skip': - case 'limit': - case 'readPreference': - case 'comment': + case "explain": + case "hint": + case "distinct": + case "pipeline": + case "skip": + case "limit": + case "readPreference": + case "comment": this.findOptions[option] = restOptions[option]; break; - case 'order': - var fields = restOptions.order.split(','); + case "order": + var fields = restOptions.order.split(","); this.findOptions.sort = fields.reduce((sortMap, field) => { field = field.trim(); - if (field === '$score' || field === '-$score') { - sortMap.score = { $meta: 'textScore' }; - } else if (field[0] == '-') { + if (field === "$score" || field === "-$score") { + sortMap.score = { $meta: "textScore" }; + } else if (field[0] == "-") { sortMap[field.slice(1)] = -1; } else { sortMap[field] = 1; @@ -229,9 +232,9 @@ function _UnsafeRestQuery( return sortMap; }, {}); break; - case 'include': { - const paths = restOptions.include.split(','); - if (paths.includes('*')) { + case "include": { + const paths = restOptions.include.split(","); + if (paths.includes("*")) { this.includeAll = true; break; } @@ -240,30 +243,33 @@ function _UnsafeRestQuery( // Split each paths on . (a.b.c -> [a,b,c]) // reduce to create all paths // ([a,b,c] -> {a: true, 'a.b': true, 'a.b.c': true}) - return path.split('.').reduce((memo, path, index, parts) => { - memo[parts.slice(0, index + 1).join('.')] = true; + return path.split(".").reduce((memo, path, index, parts) => { + memo[parts.slice(0, index + 1).join(".")] = true; return memo; }, memo); }, {}); this.include = Object.keys(pathSet) .map(s => { - return s.split('.'); + return s.split("."); }) .sort((a, b) => { return a.length - b.length; // Sort by number of components }); break; } - case 'redirectClassNameForKey': + case "redirectClassNameForKey": this.redirectKey = restOptions.redirectClassNameForKey; this.redirectClassName = null; break; - case 'includeReadPreference': - case 'subqueryReadPreference': + case "includeReadPreference": + case "subqueryReadPreference": break; default: - throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad option: ' + option); + throw new Parse.Error( + Parse.Error.INVALID_JSON, + "bad option: " + option + ); } } } @@ -311,7 +317,7 @@ _UnsafeRestQuery.prototype.each = function (callback) { const { config, auth, className, restWhere, restOptions, clientSDK } = this; // if the limit is set, use it restOptions.limit = restOptions.limit || 100; - restOptions.order = 'objectId'; + restOptions.order = "objectId"; let finished = false; return continueWhile( @@ -377,11 +383,13 @@ _UnsafeRestQuery.prototype.getUserAndRoleACL = function () { return Promise.resolve(); } - this.findOptions.acl = ['*']; + this.findOptions.acl = ["*"]; if (this.auth.user) { return this.auth.getUserRoles().then(roles => { - this.findOptions.acl = this.findOptions.acl.concat(roles, [this.auth.user.id]); + this.findOptions.acl = this.findOptions.acl.concat(roles, [ + this.auth.user.id, + ]); return; }); } else { @@ -419,7 +427,9 @@ _UnsafeRestQuery.prototype.validateClientClassCreation = function () { if (hasClass !== true) { throw new Parse.Error( Parse.Error.OPERATION_FORBIDDEN, - 'This user is not allowed to access ' + 'non-existent class: ' + this.className + "This user is not allowed to access " + + "non-existent class: " + + this.className ); } }); @@ -432,16 +442,16 @@ function transformInQuery(inQueryObject, className, results) { var values = []; for (var result of results) { values.push({ - __type: 'Pointer', + __type: "Pointer", className: className, objectId: result.objectId, }); } - delete inQueryObject['$inQuery']; - if (Array.isArray(inQueryObject['$in'])) { - inQueryObject['$in'] = inQueryObject['$in'].concat(values); + delete inQueryObject["$inQuery"]; + if (Array.isArray(inQueryObject["$in"])) { + inQueryObject["$in"] = inQueryObject["$in"].concat(values); } else { - inQueryObject['$in'] = values; + inQueryObject["$in"] = values; } } @@ -450,15 +460,18 @@ function transformInQuery(inQueryObject, className, results) { // The $inQuery clause turns into an $in with values that are just // pointers to the objects returned in the subquery. _UnsafeRestQuery.prototype.replaceInQuery = async function () { - var inQueryObject = findObjectWithKey(this.restWhere, '$inQuery'); + var inQueryObject = findObjectWithKey(this.restWhere, "$inQuery"); if (!inQueryObject) { return; } // The inQuery value must have precisely two keys - where and className - var inQueryValue = inQueryObject['$inQuery']; + var inQueryValue = inQueryObject["$inQuery"]; if (!inQueryValue.where || !inQueryValue.className) { - throw new Parse.Error(Parse.Error.INVALID_QUERY, 'improper usage of $inQuery'); + throw new Parse.Error( + Parse.Error.INVALID_QUERY, + "improper usage of $inQuery" + ); } const additionalOptions = { @@ -467,7 +480,8 @@ _UnsafeRestQuery.prototype.replaceInQuery = async function () { if (this.restOptions.subqueryReadPreference) { additionalOptions.readPreference = this.restOptions.subqueryReadPreference; - additionalOptions.subqueryReadPreference = this.restOptions.subqueryReadPreference; + additionalOptions.subqueryReadPreference = + this.restOptions.subqueryReadPreference; } else if (this.restOptions.readPreference) { additionalOptions.readPreference = this.restOptions.readPreference; } @@ -492,16 +506,16 @@ function transformNotInQuery(notInQueryObject, className, results) { var values = []; for (var result of results) { values.push({ - __type: 'Pointer', + __type: "Pointer", className: className, objectId: result.objectId, }); } - delete notInQueryObject['$notInQuery']; - if (Array.isArray(notInQueryObject['$nin'])) { - notInQueryObject['$nin'] = notInQueryObject['$nin'].concat(values); + delete notInQueryObject["$notInQuery"]; + if (Array.isArray(notInQueryObject["$nin"])) { + notInQueryObject["$nin"] = notInQueryObject["$nin"].concat(values); } else { - notInQueryObject['$nin'] = values; + notInQueryObject["$nin"] = values; } } @@ -510,15 +524,18 @@ function transformNotInQuery(notInQueryObject, className, results) { // The $notInQuery clause turns into a $nin with values that are just // pointers to the objects returned in the subquery. _UnsafeRestQuery.prototype.replaceNotInQuery = async function () { - var notInQueryObject = findObjectWithKey(this.restWhere, '$notInQuery'); + var notInQueryObject = findObjectWithKey(this.restWhere, "$notInQuery"); if (!notInQueryObject) { return; } // The notInQuery value must have precisely two keys - where and className - var notInQueryValue = notInQueryObject['$notInQuery']; + var notInQueryValue = notInQueryObject["$notInQuery"]; if (!notInQueryValue.where || !notInQueryValue.className) { - throw new Parse.Error(Parse.Error.INVALID_QUERY, 'improper usage of $notInQuery'); + throw new Parse.Error( + Parse.Error.INVALID_QUERY, + "improper usage of $notInQuery" + ); } const additionalOptions = { @@ -527,7 +544,8 @@ _UnsafeRestQuery.prototype.replaceNotInQuery = async function () { if (this.restOptions.subqueryReadPreference) { additionalOptions.readPreference = this.restOptions.subqueryReadPreference; - additionalOptions.subqueryReadPreference = this.restOptions.subqueryReadPreference; + additionalOptions.subqueryReadPreference = + this.restOptions.subqueryReadPreference; } else if (this.restOptions.readPreference) { additionalOptions.readPreference = this.restOptions.readPreference; } @@ -560,13 +578,13 @@ const getDeepestObjectFromKey = (json, key, idx, src) => { const transformSelect = (selectObject, key, objects) => { var values = []; for (var result of objects) { - values.push(key.split('.').reduce(getDeepestObjectFromKey, result)); + values.push(key.split(".").reduce(getDeepestObjectFromKey, result)); } - delete selectObject['$select']; - if (Array.isArray(selectObject['$in'])) { - selectObject['$in'] = selectObject['$in'].concat(values); + delete selectObject["$select"]; + if (Array.isArray(selectObject["$in"])) { + selectObject["$in"] = selectObject["$in"].concat(values); } else { - selectObject['$in'] = values; + selectObject["$in"] = values; } }; @@ -576,22 +594,25 @@ const transformSelect = (selectObject, key, objects) => { // the subquery. // Returns a possible-promise. _UnsafeRestQuery.prototype.replaceSelect = async function () { - var selectObject = findObjectWithKey(this.restWhere, '$select'); + var selectObject = findObjectWithKey(this.restWhere, "$select"); if (!selectObject) { return; } // The select value must have precisely two keys - query and key - var selectValue = selectObject['$select']; + var selectValue = selectObject["$select"]; // iOS SDK don't send where if not set, let it pass if ( !selectValue.query || !selectValue.key || - typeof selectValue.query !== 'object' || + typeof selectValue.query !== "object" || !selectValue.query.className || Object.keys(selectValue).length !== 2 ) { - throw new Parse.Error(Parse.Error.INVALID_QUERY, 'improper usage of $select'); + throw new Parse.Error( + Parse.Error.INVALID_QUERY, + "improper usage of $select" + ); } const additionalOptions = { @@ -600,7 +621,8 @@ _UnsafeRestQuery.prototype.replaceSelect = async function () { if (this.restOptions.subqueryReadPreference) { additionalOptions.readPreference = this.restOptions.subqueryReadPreference; - additionalOptions.subqueryReadPreference = this.restOptions.subqueryReadPreference; + additionalOptions.subqueryReadPreference = + this.restOptions.subqueryReadPreference; } else if (this.restOptions.readPreference) { additionalOptions.readPreference = this.restOptions.readPreference; } @@ -625,13 +647,13 @@ _UnsafeRestQuery.prototype.replaceSelect = async function () { const transformDontSelect = (dontSelectObject, key, objects) => { var values = []; for (var result of objects) { - values.push(key.split('.').reduce(getDeepestObjectFromKey, result)); + values.push(key.split(".").reduce(getDeepestObjectFromKey, result)); } - delete dontSelectObject['$dontSelect']; - if (Array.isArray(dontSelectObject['$nin'])) { - dontSelectObject['$nin'] = dontSelectObject['$nin'].concat(values); + delete dontSelectObject["$dontSelect"]; + if (Array.isArray(dontSelectObject["$nin"])) { + dontSelectObject["$nin"] = dontSelectObject["$nin"].concat(values); } else { - dontSelectObject['$nin'] = values; + dontSelectObject["$nin"] = values; } }; @@ -641,21 +663,24 @@ const transformDontSelect = (dontSelectObject, key, objects) => { // the subquery. // Returns a possible-promise. _UnsafeRestQuery.prototype.replaceDontSelect = async function () { - var dontSelectObject = findObjectWithKey(this.restWhere, '$dontSelect'); + var dontSelectObject = findObjectWithKey(this.restWhere, "$dontSelect"); if (!dontSelectObject) { return; } // The dontSelect value must have precisely two keys - query and key - var dontSelectValue = dontSelectObject['$dontSelect']; + var dontSelectValue = dontSelectObject["$dontSelect"]; if ( !dontSelectValue.query || !dontSelectValue.key || - typeof dontSelectValue.query !== 'object' || + typeof dontSelectValue.query !== "object" || !dontSelectValue.query.className || Object.keys(dontSelectValue).length !== 2 ) { - throw new Parse.Error(Parse.Error.INVALID_QUERY, 'improper usage of $dontSelect'); + throw new Parse.Error( + Parse.Error.INVALID_QUERY, + "improper usage of $dontSelect" + ); } const additionalOptions = { redirectClassNameForKey: dontSelectValue.query.redirectClassNameForKey, @@ -663,7 +688,8 @@ _UnsafeRestQuery.prototype.replaceDontSelect = async function () { if (this.restOptions.subqueryReadPreference) { additionalOptions.readPreference = this.restOptions.subqueryReadPreference; - additionalOptions.subqueryReadPreference = this.restOptions.subqueryReadPreference; + additionalOptions.subqueryReadPreference = + this.restOptions.subqueryReadPreference; } else if (this.restOptions.readPreference) { additionalOptions.readPreference = this.restOptions.readPreference; } @@ -679,7 +705,11 @@ _UnsafeRestQuery.prototype.replaceDontSelect = async function () { }); return subquery.execute().then(response => { - transformDontSelect(dontSelectObject, dontSelectValue.key, response.results); + transformDontSelect( + dontSelectObject, + dontSelectValue.key, + response.results + ); // Keep replacing $dontSelect clauses return this.replaceDontSelect(); }); @@ -701,14 +731,14 @@ _UnsafeRestQuery.prototype.cleanResultAuthData = function (result) { }; const replaceEqualityConstraint = constraint => { - if (typeof constraint !== 'object') { + if (typeof constraint !== "object") { return constraint; } const equalToObject = {}; let hasDirectConstraint = false; let hasOperatorConstraint = false; for (const key in constraint) { - if (key.indexOf('$') !== 0) { + if (key.indexOf("$") !== 0) { hasDirectConstraint = true; equalToObject[key] = constraint[key]; } else { @@ -716,7 +746,7 @@ const replaceEqualityConstraint = constraint => { } } if (hasDirectConstraint && hasOperatorConstraint) { - constraint['$eq'] = equalToObject; + constraint["$eq"] = equalToObject; Object.keys(equalToObject).forEach(key => { delete constraint[key]; }); @@ -725,7 +755,7 @@ const replaceEqualityConstraint = constraint => { }; _UnsafeRestQuery.prototype.replaceEquality = function () { - if (typeof this.restWhere !== 'object') { + if (typeof this.restWhere !== "object") { return; } for (const key in this.restWhere) { @@ -743,7 +773,7 @@ _UnsafeRestQuery.prototype.runFind = async function (options = {}) { const findOptions = Object.assign({}, this.findOptions); if (this.keys) { findOptions.keys = this.keys.map(key => { - return key.split('.')[0]; + return key.split(".")[0]; }); } if (options.op) { @@ -755,7 +785,7 @@ _UnsafeRestQuery.prototype.runFind = async function (options = {}) { findOptions, this.auth ); - if (this.className === '_User' && !findOptions.explain) { + if (this.className === "_User" && !findOptions.explain) { for (var result of results) { this.cleanResultAuthData(result); } @@ -780,9 +810,11 @@ _UnsafeRestQuery.prototype.runCount = function () { this.findOptions.count = true; delete this.findOptions.skip; delete this.findOptions.limit; - return this.config.database.find(this.className, this.restWhere, this.findOptions).then(c => { - this.response.count = c; - }); + return this.config.database + .find(this.className, this.restWhere, this.findOptions) + .then(c => { + this.response.count = c; + }); }; _UnsafeRestQuery.prototype.denyProtectedFields = async function () { @@ -822,8 +854,9 @@ _UnsafeRestQuery.prototype.handleIncludeAll = function () { const keyFields = []; for (const field in schema.fields) { if ( - (schema.fields[field].type && schema.fields[field].type === 'Pointer') || - (schema.fields[field].type && schema.fields[field].type === 'Array') + (schema.fields[field].type && + schema.fields[field].type === "Pointer") || + (schema.fields[field].type && schema.fields[field].type === "Array") ) { includeFields.push([field]); keyFields.push(field); @@ -938,7 +971,7 @@ _UnsafeRestQuery.prototype.runAfterFindTrigger = function () { }; _UnsafeRestQuery.prototype.handleAuthAdapters = async function () { - if (this.className !== '_User' || this.findOptions.explain) { + if (this.className !== "_User" || this.findOptions.explain) { return; } await Promise.all( @@ -973,9 +1006,9 @@ function includePath(config, auth, response, path, context, restOptions = {}) { } const includeRestOptions = {}; if (restOptions.keys) { - const keys = new Set(restOptions.keys.split(',')); + const keys = new Set(restOptions.keys.split(",")); const keySet = Array.from(keys).reduce((set, key) => { - const keyPath = key.split('.'); + const keyPath = key.split("."); let i = 0; for (i; i < path.length; i++) { if (path[i] != keyPath[i]) { @@ -988,14 +1021,14 @@ function includePath(config, auth, response, path, context, restOptions = {}) { return set; }, new Set()); if (keySet.size > 0) { - includeRestOptions.keys = Array.from(keySet).join(','); + includeRestOptions.keys = Array.from(keySet).join(","); } } if (restOptions.excludeKeys) { - const excludeKeys = new Set(restOptions.excludeKeys.split(',')); + const excludeKeys = new Set(restOptions.excludeKeys.split(",")); const excludeKeySet = Array.from(excludeKeys).reduce((set, key) => { - const keyPath = key.split('.'); + const keyPath = key.split("."); let i = 0; for (i; i < path.length; i++) { if (path[i] != keyPath[i]) { @@ -1008,13 +1041,14 @@ function includePath(config, auth, response, path, context, restOptions = {}) { return set; }, new Set()); if (excludeKeySet.size > 0) { - includeRestOptions.excludeKeys = Array.from(excludeKeySet).join(','); + includeRestOptions.excludeKeys = Array.from(excludeKeySet).join(","); } } if (restOptions.includeReadPreference) { includeRestOptions.readPreference = restOptions.includeReadPreference; - includeRestOptions.includeReadPreference = restOptions.includeReadPreference; + includeRestOptions.includeReadPreference = + restOptions.includeReadPreference; } else if (restOptions.readPreference) { includeRestOptions.readPreference = restOptions.readPreference; } @@ -1028,7 +1062,8 @@ function includePath(config, auth, response, path, context, restOptions = {}) { where = { objectId: { $in: objectIds } }; } const query = await RestQuery({ - method: objectIds.length === 1 ? RestQuery.Method.get : RestQuery.Method.find, + method: + objectIds.length === 1 ? RestQuery.Method.get : RestQuery.Method.find, config, auth, className, @@ -1036,7 +1071,7 @@ function includePath(config, auth, response, path, context, restOptions = {}) { restOptions: includeRestOptions, context: context, }); - return query.execute({ op: 'get' }).then(results => { + return query.execute({ op: "get" }).then(results => { results.className = className; return Promise.resolve(results); }); @@ -1046,10 +1081,10 @@ function includePath(config, auth, response, path, context, restOptions = {}) { return Promise.all(queryPromises).then(responses => { var replace = responses.reduce((replace, includeResponse) => { for (var obj of includeResponse.results) { - obj.__type = 'Object'; + obj.__type = "Object"; obj.className = includeResponse.className; - if (obj.className == '_User' && !auth.isMaster) { + if (obj.className == "_User" && !auth.isMaster) { delete obj.sessionToken; delete obj.authData; } @@ -1078,12 +1113,12 @@ function findPointers(object, path) { return object.map(x => findPointers(x, path)).flat(); } - if (typeof object !== 'object' || !object) { + if (typeof object !== "object" || !object) { return []; } if (path.length == 0) { - if (object === null || object.__type == 'Pointer') { + if (object === null || object.__type == "Pointer") { return [object]; } return []; @@ -1106,15 +1141,15 @@ function replacePointers(object, path, replace) { if (object instanceof Array) { return object .map(obj => replacePointers(obj, path, replace)) - .filter(obj => typeof obj !== 'undefined'); + .filter(obj => typeof obj !== "undefined"); } - if (typeof object !== 'object' || !object) { + if (typeof object !== "object" || !object) { return object; } if (path.length === 0) { - if (object && object.__type === 'Pointer') { + if (object && object.__type === "Pointer") { return replace[object.objectId]; } return object; @@ -1139,7 +1174,7 @@ function replacePointers(object, path, replace) { // Finds a subobject that has the given key, if there is one. // Returns undefined otherwise. function findObjectWithKey(root, key) { - if (typeof root !== 'object') { + if (typeof root !== "object") { return; } if (root instanceof Array) { diff --git a/src/RestWrite.js b/src/RestWrite.js index e149632b7d..ad76db33d0 100644 --- a/src/RestWrite.js +++ b/src/RestWrite.js @@ -2,21 +2,21 @@ // that writes to the database. // This could be either a "create" or an "update". -var SchemaController = require('./Controllers/SchemaController'); -var deepcopy = require('deepcopy'); - -const Auth = require('./Auth'); -const Utils = require('./Utils'); -var cryptoUtils = require('./cryptoUtils'); -var passwordCrypto = require('./password'); -var Parse = require('parse/node'); -var triggers = require('./triggers'); -var ClientSDK = require('./ClientSDK'); -const util = require('util'); -import RestQuery from './RestQuery'; -import _ from 'lodash'; -import logger from './logger'; -import { requiredColumns } from './Controllers/SchemaController'; +var SchemaController = require("./Controllers/SchemaController"); +var deepcopy = require("deepcopy"); + +const Auth = require("./Auth"); +const Utils = require("./Utils"); +var cryptoUtils = require("./cryptoUtils"); +var passwordCrypto = require("./password"); +var Parse = require("parse/node"); +var triggers = require("./triggers"); +var ClientSDK = require("./ClientSDK"); +const util = require("util"); +import RestQuery from "./RestQuery"; +import _ from "lodash"; +import logger from "./logger"; +import { requiredColumns } from "./Controllers/SchemaController"; // query and data are both provided in REST API format. So data // types are encoded by plain old objects. @@ -27,11 +27,21 @@ import { requiredColumns } from './Controllers/SchemaController'; // RestWrite will handle objectId, createdAt, and updatedAt for // everything. It also knows to use triggers and special modifications // for the _User class. -function RestWrite(config, auth, className, query, data, originalData, clientSDK, context, action) { +function RestWrite( + config, + auth, + className, + query, + data, + originalData, + clientSDK, + context, + action +) { if (auth.isReadOnly) { throw new Parse.Error( Parse.Error.OPERATION_FORBIDDEN, - 'Cannot perform a write operation when using readOnlyMasterKey' + "Cannot perform a write operation when using readOnlyMasterKey" ); } this.config = config; @@ -48,18 +58,27 @@ function RestWrite(config, auth, className, query, data, originalData, clientSDK if (!query) { if (this.config.allowCustomObjectId) { - if (Object.prototype.hasOwnProperty.call(data, 'objectId') && !data.objectId) { + if ( + Object.prototype.hasOwnProperty.call(data, "objectId") && + !data.objectId + ) { throw new Parse.Error( Parse.Error.MISSING_OBJECT_ID, - 'objectId must not be empty, null or undefined' + "objectId must not be empty, null or undefined" ); } } else { if (data.objectId) { - throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'objectId is an invalid field name.'); + throw new Parse.Error( + Parse.Error.INVALID_KEY_NAME, + "objectId is an invalid field name." + ); } if (data.id) { - throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'id is an invalid field name.'); + throw new Parse.Error( + Parse.Error.INVALID_KEY_NAME, + "id is an invalid field name." + ); } } } @@ -161,8 +180,14 @@ RestWrite.prototype.execute = function () { this.response.response.authDataResponse = this.authDataResponse; } } - if (this.storage.rejectSignup && this.config.preventSignupWithUnverifiedEmail) { - throw new Parse.Error(Parse.Error.EMAIL_NOT_FOUND, 'User email is not verified.'); + if ( + this.storage.rejectSignup && + this.config.preventSignupWithUnverifiedEmail + ) { + throw new Parse.Error( + Parse.Error.EMAIL_NOT_FOUND, + "User email is not verified." + ); } return this.response; }); @@ -174,11 +199,13 @@ RestWrite.prototype.getUserAndRoleACL = function () { return Promise.resolve(); } - this.runOptions.acl = ['*']; + this.runOptions.acl = ["*"]; if (this.auth.user) { return this.auth.getUserRoles().then(roles => { - this.runOptions.acl = this.runOptions.acl.concat(roles, [this.auth.user.id]); + this.runOptions.acl = this.runOptions.acl.concat(roles, [ + this.auth.user.id, + ]); return; }); } else { @@ -201,7 +228,9 @@ RestWrite.prototype.validateClientClassCreation = function () { if (hasClass !== true) { throw new Parse.Error( Parse.Error.OPERATION_FORBIDDEN, - 'This user is not allowed to access ' + 'non-existent class: ' + this.className + "This user is not allowed to access " + + "non-existent class: " + + this.className ); } }); @@ -230,7 +259,11 @@ RestWrite.prototype.runBeforeSaveTrigger = function () { // Avoid doing any setup for triggers if there is no 'beforeSave' trigger for this class. if ( - !triggers.triggerExists(this.className, triggers.Types.beforeSave, this.config.applicationId) + !triggers.triggerExists( + this.className, + triggers.Types.beforeSave, + this.config.applicationId + ) ) { return Promise.resolve(); } @@ -270,7 +303,10 @@ RestWrite.prototype.runBeforeSaveTrigger = function () { // In the case that there is no permission for the operation, it throws an error return databasePromise.then(result => { if (!result || result.length <= 0) { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.'); + throw new Parse.Error( + Parse.Error.OBJECT_NOT_FOUND, + "Object not found." + ); } }); }) @@ -313,7 +349,11 @@ RestWrite.prototype.runBeforeSaveTrigger = function () { RestWrite.prototype.runBeforeLoginTrigger = async function (userData) { // Avoid doing any setup for triggers if there is no 'beforeLogin' trigger if ( - !triggers.triggerExists(this.className, triggers.Types.beforeLogin, this.config.applicationId) + !triggers.triggerExists( + this.className, + triggers.Types.beforeLogin, + this.config.applicationId + ) ) { return; } @@ -340,13 +380,16 @@ RestWrite.prototype.runBeforeLoginTrigger = async function (userData) { RestWrite.prototype.setRequiredFieldsIfNeeded = function () { if (this.data) { return this.validSchemaController.getAllClasses().then(allClasses => { - const schema = allClasses.find(oneClass => oneClass.className === this.className); + const schema = allClasses.find( + oneClass => oneClass.className === this.className + ); const setRequiredFieldIfNeeded = (fieldName, setDefault) => { if ( this.data[fieldName] === undefined || this.data[fieldName] === null || - this.data[fieldName] === '' || - (typeof this.data[fieldName] === 'object' && this.data[fieldName].__op === 'Delete') + this.data[fieldName] === "" || + (typeof this.data[fieldName] === "object" && + this.data[fieldName].__op === "Delete") ) { if ( setDefault && @@ -354,15 +397,23 @@ RestWrite.prototype.setRequiredFieldsIfNeeded = function () { schema.fields[fieldName].defaultValue !== null && schema.fields[fieldName].defaultValue !== undefined && (this.data[fieldName] === undefined || - (typeof this.data[fieldName] === 'object' && this.data[fieldName].__op === 'Delete')) + (typeof this.data[fieldName] === "object" && + this.data[fieldName].__op === "Delete")) ) { this.data[fieldName] = schema.fields[fieldName].defaultValue; - this.storage.fieldsChangedByTrigger = this.storage.fieldsChangedByTrigger || []; + this.storage.fieldsChangedByTrigger = + this.storage.fieldsChangedByTrigger || []; if (this.storage.fieldsChangedByTrigger.indexOf(fieldName) < 0) { this.storage.fieldsChangedByTrigger.push(fieldName); } - } else if (schema.fields[fieldName] && schema.fields[fieldName].required === true) { - throw new Parse.Error(Parse.Error.VALIDATION_ERROR, `${fieldName} is required`); + } else if ( + schema.fields[fieldName] && + schema.fields[fieldName].required === true + ) { + throw new Parse.Error( + Parse.Error.VALIDATION_ERROR, + `${fieldName} is required` + ); } } }; @@ -372,7 +423,7 @@ RestWrite.prototype.setRequiredFieldsIfNeeded = function () { schema?.classLevelPermissions?.ACL && !this.data.ACL && JSON.stringify(schema.classLevelPermissions.ACL) !== - JSON.stringify({ '*': { read: true, write: true } }) + JSON.stringify({ "*": { read: true, write: true } }) ) { const acl = deepcopy(schema.classLevelPermissions.ACL); if (acl.currentUser) { @@ -382,8 +433,9 @@ RestWrite.prototype.setRequiredFieldsIfNeeded = function () { delete acl.currentUser; } this.data.ACL = acl; - this.storage.fieldsChangedByTrigger = this.storage.fieldsChangedByTrigger || []; - this.storage.fieldsChangedByTrigger.push('ACL'); + this.storage.fieldsChangedByTrigger = + this.storage.fieldsChangedByTrigger || []; + this.storage.fieldsChangedByTrigger.push("ACL"); } // Add default fields @@ -392,18 +444,18 @@ RestWrite.prototype.setRequiredFieldsIfNeeded = function () { if ( this.auth.isMaintenance && this.data.createdAt && - this.data.createdAt.__type === 'Date' + this.data.createdAt.__type === "Date" ) { this.data.createdAt = this.data.createdAt.iso; - if (this.data.updatedAt && this.data.updatedAt.__type === 'Date') { + if (this.data.updatedAt && this.data.updatedAt.__type === "Date") { const createdAt = new Date(this.data.createdAt); const updatedAt = new Date(this.data.updatedAt.iso); if (updatedAt < createdAt) { throw new Parse.Error( Parse.Error.VALIDATION_ERROR, - 'updatedAt cannot occur before createdAt' + "updatedAt cannot occur before createdAt" ); } @@ -420,7 +472,9 @@ RestWrite.prototype.setRequiredFieldsIfNeeded = function () { // Only assign new objectId if we are creating new object if (!this.data.objectId) { - this.data.objectId = cryptoUtils.newObjectId(this.config.objectIdSize); + this.data.objectId = cryptoUtils.newObjectId( + this.config.objectIdSize + ); } if (schema) { Object.keys(schema.fields).forEach(fieldName => { @@ -443,34 +497,50 @@ RestWrite.prototype.setRequiredFieldsIfNeeded = function () { // Does nothing if this isn't a user object. // Returns a promise for when we're done if it can't finish this tick. RestWrite.prototype.validateAuthData = function () { - if (this.className !== '_User') { + if (this.className !== "_User") { return; } const authData = this.data.authData; const hasUsernameAndPassword = - typeof this.data.username === 'string' && typeof this.data.password === 'string'; + typeof this.data.username === "string" && + typeof this.data.password === "string"; if (!this.query && !authData) { - if (typeof this.data.username !== 'string' || _.isEmpty(this.data.username)) { - throw new Parse.Error(Parse.Error.USERNAME_MISSING, 'bad or missing username'); + if ( + typeof this.data.username !== "string" || + _.isEmpty(this.data.username) + ) { + throw new Parse.Error( + Parse.Error.USERNAME_MISSING, + "bad or missing username" + ); } - if (typeof this.data.password !== 'string' || _.isEmpty(this.data.password)) { - throw new Parse.Error(Parse.Error.PASSWORD_MISSING, 'password is required'); + if ( + typeof this.data.password !== "string" || + _.isEmpty(this.data.password) + ) { + throw new Parse.Error( + Parse.Error.PASSWORD_MISSING, + "password is required" + ); } } if ( (authData && !Object.keys(authData).length) || - !Object.prototype.hasOwnProperty.call(this.data, 'authData') + !Object.prototype.hasOwnProperty.call(this.data, "authData") ) { // Nothing to validate here return; - } else if (Object.prototype.hasOwnProperty.call(this.data, 'authData') && !this.data.authData) { + } else if ( + Object.prototype.hasOwnProperty.call(this.data, "authData") && + !this.data.authData + ) { // Handle saving authData to null throw new Parse.Error( Parse.Error.UNSUPPORTED_SERVICE, - 'This authentication method is unsupported.' + "This authentication method is unsupported." ); } @@ -480,13 +550,18 @@ RestWrite.prototype.validateAuthData = function () { const providerAuthData = authData[provider] || {}; return !!Object.keys(providerAuthData).length; }); - if (canHandleAuthData || hasUsernameAndPassword || this.auth.isMaster || this.getUserId()) { + if ( + canHandleAuthData || + hasUsernameAndPassword || + this.auth.isMaster || + this.getUserId() + ) { return this.handleAuthData(authData); } } throw new Parse.Error( Parse.Error.UNSUPPORTED_SERVICE, - 'This authentication method is unsupported.' + "This authentication method is unsupported." ); }; @@ -504,7 +579,7 @@ RestWrite.prototype.filteredObjectsByACL = function (objects) { }; RestWrite.prototype.getUserId = function () { - if (this.query && this.query.objectId && this.className === '_User') { + if (this.query && this.query.objectId && this.className === "_User") { return this.query.objectId; } else if (this.auth && this.auth.user && this.auth.user.id) { return this.auth.user.id; @@ -515,7 +590,7 @@ RestWrite.prototype.getUserId = function () { // we need after before save to ensure that the developer // is not currently duplicating auth data ID RestWrite.prototype.ensureUniqueAuthDataId = async function () { - if (this.className !== '_User' || !this.data.authData) { + if (this.className !== "_User" || !this.data.authData) { return; } @@ -530,12 +605,18 @@ RestWrite.prototype.ensureUniqueAuthDataId = async function () { const r = await Auth.findUsersWithAuthData(this.config, this.data.authData); const results = this.filteredObjectsByACL(r); if (results.length > 1) { - throw new Parse.Error(Parse.Error.ACCOUNT_ALREADY_LINKED, 'this auth is already used'); + throw new Parse.Error( + Parse.Error.ACCOUNT_ALREADY_LINKED, + "this auth is already used" + ); } // use data.objectId in case of login time and found user during handle validateAuthData const userId = this.getUserId() || this.data.objectId; if (results.length === 1 && userId !== results[0].objectId) { - throw new Parse.Error(Parse.Error.ACCOUNT_ALREADY_LINKED, 'this auth is already used'); + throw new Parse.Error( + Parse.Error.ACCOUNT_ALREADY_LINKED, + "this auth is already used" + ); } }; @@ -545,21 +626,23 @@ RestWrite.prototype.handleAuthData = async function (authData) { const userId = this.getUserId(); const userResult = results[0]; - const foundUserIsNotCurrentUser = userId && userResult && userId !== userResult.objectId; + const foundUserIsNotCurrentUser = + userId && userResult && userId !== userResult.objectId; if (results.length > 1 || foundUserIsNotCurrentUser) { // To avoid https://github.com/parse-community/parse-server/security/advisories/GHSA-8w3j-g983-8jh5 // Let's run some validation before throwing await Auth.handleAuthDataValidation(authData, this, userResult); - throw new Parse.Error(Parse.Error.ACCOUNT_ALREADY_LINKED, 'this auth is already used'); + throw new Parse.Error( + Parse.Error.ACCOUNT_ALREADY_LINKED, + "this auth is already used" + ); } // No user found with provided authData we need to validate if (!results.length) { - const { authData: validatedAuthData, authDataResponse } = await Auth.handleAuthDataValidation( - authData, - this - ); + const { authData: validatedAuthData, authDataResponse } = + await Auth.handleAuthDataValidation(authData, this); this.authDataResponse = authDataResponse; // Replace current authData by the new validated one this.data.authData = validatedAuthData; @@ -568,7 +651,7 @@ RestWrite.prototype.handleAuthData = async function (authData) { // User found with provided authData if (results.length === 1) { - this.storage.authProvider = Object.keys(authData).join(','); + this.storage.authProvider = Object.keys(authData).join(","); const { hasMutatedAuthData, mutatedAuthData } = Auth.hasMutatedAuthData( authData, @@ -576,7 +659,9 @@ RestWrite.prototype.handleAuthData = async function (authData) { ); const isCurrentUserLoggedOrMaster = - (this.auth && this.auth.user && this.auth.user.id === userResult.objectId) || + (this.auth && + this.auth.user && + this.auth.user.id === userResult.objectId) || this.auth.isMaster; const isLogin = !userId; @@ -656,11 +741,15 @@ RestWrite.prototype.handleAuthData = async function (authData) { }; RestWrite.prototype.checkRestrictedFields = async function () { - if (this.className !== '_User') { + if (this.className !== "_User") { return; } - if (!this.auth.isMaintenance && !this.auth.isMaster && 'emailVerified' in this.data) { + if ( + !this.auth.isMaintenance && + !this.auth.isMaster && + "emailVerified" in this.data + ) { const error = `Clients aren't allowed to manually update email verification.`; throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, error); } @@ -669,7 +758,7 @@ RestWrite.prototype.checkRestrictedFields = async function () { // The non-third-party parts of User transformation RestWrite.prototype.transformUser = async function () { var promise = Promise.resolve(); - if (this.className !== '_User') { + if (this.className !== "_User") { return promise; } @@ -681,12 +770,12 @@ RestWrite.prototype.transformUser = async function () { method: RestQuery.Method.find, config: this.config, auth: Auth.master(this.config), - className: '_Session', + className: "_Session", runBeforeFind: false, restWhere: { user: { - __type: 'Pointer', - className: '_User', + __type: "Pointer", + className: "_User", objectId: this.objectId(), }, }, @@ -707,10 +796,10 @@ RestWrite.prototype.transformUser = async function () { } if (this.query) { - this.storage['clearSessions'] = true; + this.storage["clearSessions"] = true; // Generate a new session only if the user requested if (!this.auth.isMaster && !this.auth.isMaintenance) { - this.storage['generateNewSession'] = true; + this.storage["generateNewSession"] = true; } } @@ -760,7 +849,7 @@ RestWrite.prototype._validateUserName = function () { if (results.length > 0) { throw new Parse.Error( Parse.Error.USERNAME_TAKEN, - 'Account already exists for this username.' + "Account already exists for this username." ); } return; @@ -780,13 +869,16 @@ RestWrite.prototype._validateUserName = function () { unique index will be used by the db for the query, this is an adequate solution. */ RestWrite.prototype._validateEmail = function () { - if (!this.data.email || this.data.email.__op === 'Delete') { + if (!this.data.email || this.data.email.__op === "Delete") { return Promise.resolve(); } // Validate basic email address format if (!this.data.email.match(/^.+@.+$/)) { return Promise.reject( - new Parse.Error(Parse.Error.INVALID_EMAIL_ADDRESS, 'Email address format is invalid.') + new Parse.Error( + Parse.Error.INVALID_EMAIL_ADDRESS, + "Email address format is invalid." + ) ); } // Case insensitive match, see note above function. @@ -805,14 +897,14 @@ RestWrite.prototype._validateEmail = function () { if (results.length > 0) { throw new Parse.Error( Parse.Error.EMAIL_TAKEN, - 'Account already exists for this email address.' + "Account already exists for this email address." ); } if ( !this.data.authData || !Object.keys(this.data.authData).length || (Object.keys(this.data.authData).length === 1 && - Object.keys(this.data.authData)[0] === 'anonymous') + Object.keys(this.data.authData)[0] === "anonymous") ) { // We updated the email, send a new validation const { originalObject, updatedObject } = this.buildParseObjects(); @@ -823,7 +915,11 @@ RestWrite.prototype._validateEmail = function () { ip: this.config.ip, installationId: this.auth.installationId, }; - return this.config.userController.setEmailVerifyToken(this.data, request, this.storage); + return this.config.userController.setEmailVerifyToken( + this.data, + request, + this.storage + ); } }); }; @@ -848,8 +944,8 @@ RestWrite.prototype._validatePasswordRequirements = function () { // b. making a custom password reset page that shows the requirements const policyError = this.config.passwordPolicy.validationError ? this.config.passwordPolicy.validationError - : 'Password does not meet the Password Policy requirements.'; - const containsUsernameError = 'Password cannot contain your username.'; + : "Password does not meet the Password Policy requirements."; + const containsUsernameError = "Password cannot contain your username."; // check whether the password meets the password strength requirements if ( @@ -858,7 +954,9 @@ RestWrite.prototype._validatePasswordRequirements = function () { (this.config.passwordPolicy.validatorCallback && !this.config.passwordPolicy.validatorCallback(this.data.password)) ) { - return Promise.reject(new Parse.Error(Parse.Error.VALIDATION_ERROR, policyError)); + return Promise.reject( + new Parse.Error(Parse.Error.VALIDATION_ERROR, policyError) + ); } // check whether password contain username @@ -866,21 +964,28 @@ RestWrite.prototype._validatePasswordRequirements = function () { if (this.data.username) { // username is not passed during password reset if (this.data.password.indexOf(this.data.username) >= 0) { - return Promise.reject(new Parse.Error(Parse.Error.VALIDATION_ERROR, containsUsernameError)); + return Promise.reject( + new Parse.Error(Parse.Error.VALIDATION_ERROR, containsUsernameError) + ); } } else { // retrieve the User object using objectId during password reset - return this.config.database.find('_User', { objectId: this.objectId() }).then(results => { - if (results.length != 1) { - throw undefined; - } - if (this.data.password.indexOf(results[0].username) >= 0) { - return Promise.reject( - new Parse.Error(Parse.Error.VALIDATION_ERROR, containsUsernameError) - ); - } - return Promise.resolve(); - }); + return this.config.database + .find("_User", { objectId: this.objectId() }) + .then(results => { + if (results.length != 1) { + throw undefined; + } + if (this.data.password.indexOf(results[0].username) >= 0) { + return Promise.reject( + new Parse.Error( + Parse.Error.VALIDATION_ERROR, + containsUsernameError + ) + ); + } + return Promise.resolve(); + }); } } return Promise.resolve(); @@ -891,9 +996,9 @@ RestWrite.prototype._validatePasswordHistory = function () { if (this.query && this.config.passwordPolicy.maxPasswordHistory) { return this.config.database .find( - '_User', + "_User", { objectId: this.objectId() }, - { keys: ['_password_history', '_hashed_password'] }, + { keys: ["_password_history", "_hashed_password"] }, Auth.maintenance(this.config) ) .then(results => { @@ -915,7 +1020,7 @@ RestWrite.prototype._validatePasswordHistory = function () { return passwordCrypto.compare(newPassword, hash).then(result => { if (result) { // reject if there is a match - return Promise.reject('REPEAT_PASSWORD'); + return Promise.reject("REPEAT_PASSWORD"); } return Promise.resolve(); }); @@ -926,7 +1031,7 @@ RestWrite.prototype._validatePasswordHistory = function () { return Promise.resolve(); }) .catch(err => { - if (err === 'REPEAT_PASSWORD') { + if (err === "REPEAT_PASSWORD") { // a match was found return Promise.reject( new Parse.Error( @@ -943,7 +1048,7 @@ RestWrite.prototype._validatePasswordHistory = function () { }; RestWrite.prototype.createSessionTokenIfNeeded = async function () { - if (this.className !== '_User') { + if (this.className !== "_User") { return; } // Don't generate session for updating user (this.query is set) unless authData exists @@ -970,14 +1075,20 @@ RestWrite.prototype.createSessionTokenIfNeeded = async function () { // conditional statement below, as a developer may decide to execute expensive operations in them const verifyUserEmails = async () => this.config.verifyUserEmails === true || - (typeof this.config.verifyUserEmails === 'function' && - (await Promise.resolve(this.config.verifyUserEmails(request))) === true); + (typeof this.config.verifyUserEmails === "function" && + (await Promise.resolve(this.config.verifyUserEmails(request))) === + true); const preventLoginWithUnverifiedEmail = async () => this.config.preventLoginWithUnverifiedEmail === true || - (typeof this.config.preventLoginWithUnverifiedEmail === 'function' && - (await Promise.resolve(this.config.preventLoginWithUnverifiedEmail(request))) === true); + (typeof this.config.preventLoginWithUnverifiedEmail === "function" && + (await Promise.resolve( + this.config.preventLoginWithUnverifiedEmail(request) + )) === true); // If verification is required - if ((await verifyUserEmails()) && (await preventLoginWithUnverifiedEmail())) { + if ( + (await verifyUserEmails()) && + (await preventLoginWithUnverifiedEmail()) + ) { this.storage.rejectSignup = true; return; } @@ -988,19 +1099,19 @@ RestWrite.prototype.createSessionTokenIfNeeded = async function () { RestWrite.prototype.createSessionToken = async function () { // cloud installationId from Cloud Code, // never create session tokens from there. - if (this.auth.installationId && this.auth.installationId === 'cloud') { + if (this.auth.installationId && this.auth.installationId === "cloud") { return; } if (this.storage.authProvider == null && this.data.authData) { - this.storage.authProvider = Object.keys(this.data.authData).join(','); + this.storage.authProvider = Object.keys(this.data.authData).join(","); } const { sessionData, createSession } = RestWrite.createSession(this.config, { userId: this.objectId(), createdWith: { - action: this.storage.authProvider ? 'login' : 'signup', - authProvider: this.storage.authProvider || 'password', + action: this.storage.authProvider ? "login" : "signup", + authProvider: this.storage.authProvider || "password", }, installationId: this.auth.installationId, }); @@ -1016,13 +1127,13 @@ RestWrite.createSession = function ( config, { userId, createdWith, installationId, additionalSessionData } ) { - const token = 'r:' + cryptoUtils.newToken(); + const token = "r:" + cryptoUtils.newToken(); const expiresAt = config.generateSessionExpiresAt(); const sessionData = { sessionToken: token, user: { - __type: 'Pointer', - className: '_User', + __type: "Pointer", + className: "_User", objectId: userId, }, createdWith, @@ -1038,21 +1149,27 @@ RestWrite.createSession = function ( return { sessionData, createSession: () => - new RestWrite(config, Auth.master(config), '_Session', null, sessionData).execute(), + new RestWrite( + config, + Auth.master(config), + "_Session", + null, + sessionData + ).execute(), }; }; // Delete email reset tokens if user is changing password or email. RestWrite.prototype.deleteEmailResetTokenIfNeeded = function () { - if (this.className !== '_User' || this.query === null) { + if (this.className !== "_User" || this.query === null) { // null query means create return; } - if ('password' in this.data || 'email' in this.data) { + if ("password" in this.data || "email" in this.data) { const addOps = { - _perishable_token: { __op: 'Delete' }, - _perishable_token_expires_at: { __op: 'Delete' }, + _perishable_token: { __op: "Delete" }, + _perishable_token_expires_at: { __op: "Delete" }, }; this.data = Object.assign(this.data, addOps); } @@ -1060,7 +1177,7 @@ RestWrite.prototype.deleteEmailResetTokenIfNeeded = function () { RestWrite.prototype.destroyDuplicatedSessions = function () { // Only for _Session, and at creation time - if (this.className != '_Session' || this.query) { + if (this.className != "_Session" || this.query) { return; } // Destroy the sessions in 'Background' @@ -1072,7 +1189,7 @@ RestWrite.prototype.destroyDuplicatedSessions = function () { return; } this.config.database.destroy( - '_Session', + "_Session", { user, installationId, @@ -1085,29 +1202,35 @@ RestWrite.prototype.destroyDuplicatedSessions = function () { // Handles any followup logic RestWrite.prototype.handleFollowup = function () { - if (this.storage && this.storage['clearSessions'] && this.config.revokeSessionOnPasswordReset) { + if ( + this.storage && + this.storage["clearSessions"] && + this.config.revokeSessionOnPasswordReset + ) { var sessionQuery = { user: { - __type: 'Pointer', - className: '_User', + __type: "Pointer", + className: "_User", objectId: this.objectId(), }, }; - delete this.storage['clearSessions']; + delete this.storage["clearSessions"]; return this.config.database - .destroy('_Session', sessionQuery) + .destroy("_Session", sessionQuery) .then(this.handleFollowup.bind(this)); } - if (this.storage && this.storage['generateNewSession']) { - delete this.storage['generateNewSession']; + if (this.storage && this.storage["generateNewSession"]) { + delete this.storage["generateNewSession"]; return this.createSessionToken().then(this.handleFollowup.bind(this)); } - if (this.storage && this.storage['sendVerificationEmail']) { - delete this.storage['sendVerificationEmail']; + if (this.storage && this.storage["sendVerificationEmail"]) { + delete this.storage["sendVerificationEmail"]; // Fire and forget! - this.config.userController.sendVerificationEmail(this.data, { auth: this.auth }); + this.config.userController.sendVerificationEmail(this.data, { + auth: this.auth, + }); return this.handleFollowup.bind(this); } }; @@ -1115,21 +1238,31 @@ RestWrite.prototype.handleFollowup = function () { // Handles the _Session class specialness. // Does nothing if this isn't an _Session object. RestWrite.prototype.handleSession = function () { - if (this.response || this.className !== '_Session') { + if (this.response || this.className !== "_Session") { return; } if (!this.auth.user && !this.auth.isMaster && !this.auth.isMaintenance) { - throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Session token required.'); + throw new Parse.Error( + Parse.Error.INVALID_SESSION_TOKEN, + "Session token required." + ); } // TODO: Verify proper error to throw if (this.data.ACL) { - throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'Cannot set ' + 'ACL on a Session.'); + throw new Parse.Error( + Parse.Error.INVALID_KEY_NAME, + "Cannot set " + "ACL on a Session." + ); } if (this.query) { - if (this.data.user && !this.auth.isMaster && this.data.user.objectId != this.auth.user.id) { + if ( + this.data.user && + !this.auth.isMaster && + this.data.user.objectId != this.auth.user.id + ) { throw new Parse.Error(Parse.Error.INVALID_KEY_NAME); } else if (this.data.installationId) { throw new Parse.Error(Parse.Error.INVALID_KEY_NAME); @@ -1142,8 +1275,8 @@ RestWrite.prototype.handleSession = function () { this.query, { user: { - __type: 'Pointer', - className: '_User', + __type: "Pointer", + className: "_User", objectId: this.auth.user.id, }, }, @@ -1155,25 +1288,31 @@ RestWrite.prototype.handleSession = function () { if (!this.query && !this.auth.isMaster && !this.auth.isMaintenance) { const additionalSessionData = {}; for (var key in this.data) { - if (key === 'objectId' || key === 'user') { + if (key === "objectId" || key === "user") { continue; } additionalSessionData[key] = this.data[key]; } - const { sessionData, createSession } = RestWrite.createSession(this.config, { - userId: this.auth.user.id, - createdWith: { - action: 'create', - }, - additionalSessionData, - }); + const { sessionData, createSession } = RestWrite.createSession( + this.config, + { + userId: this.auth.user.id, + createdWith: { + action: "create", + }, + additionalSessionData, + } + ); return createSession().then(results => { if (!results.response) { - throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Error creating session.'); + throw new Parse.Error( + Parse.Error.INTERNAL_SERVER_ERROR, + "Error creating session." + ); } - sessionData['objectId'] = results.response['objectId']; + sessionData["objectId"] = results.response["objectId"]; this.response = { status: 201, location: results.location, @@ -1189,7 +1328,7 @@ RestWrite.prototype.handleSession = function () { // into an update. // Returns a promise for when we're done if it can't finish this tick. RestWrite.prototype.handleInstallation = function () { - if (this.response || this.className !== '_Installation') { + if (this.response || this.className !== "_Installation") { return; } @@ -1201,7 +1340,8 @@ RestWrite.prototype.handleInstallation = function () { ) { throw new Parse.Error( 135, - 'at least one ID field (deviceToken, installationId) ' + 'must be specified in this operation' + "at least one ID field (deviceToken, installationId) " + + "must be specified in this operation" ); } @@ -1228,7 +1368,12 @@ RestWrite.prototype.handleInstallation = function () { } // Updating _Installation but not updating anything critical - if (this.query && !this.data.deviceToken && !installationId && !this.data.deviceType) { + if ( + this.query && + !this.data.deviceToken && + !installationId && + !this.data.deviceType + ) { return; } @@ -1262,7 +1407,7 @@ RestWrite.prototype.handleInstallation = function () { promise = promise .then(() => { return this.config.database.find( - '_Installation', + "_Installation", { $or: orQueries, }, @@ -1271,7 +1416,11 @@ RestWrite.prototype.handleInstallation = function () { }) .then(results => { results.forEach(result => { - if (this.query && this.query.objectId && result.objectId == this.query.objectId) { + if ( + this.query && + this.query.objectId && + result.objectId == this.query.objectId + ) { objectIdMatch = result; } if (result.installationId == installationId) { @@ -1285,14 +1434,20 @@ RestWrite.prototype.handleInstallation = function () { // Sanity checks when running a query if (this.query && this.query.objectId) { if (!objectIdMatch) { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found for update.'); + throw new Parse.Error( + Parse.Error.OBJECT_NOT_FOUND, + "Object not found for update." + ); } if ( this.data.installationId && objectIdMatch.installationId && this.data.installationId !== objectIdMatch.installationId ) { - throw new Parse.Error(136, 'installationId may not be changed in this ' + 'operation'); + throw new Parse.Error( + 136, + "installationId may not be changed in this " + "operation" + ); } if ( this.data.deviceToken && @@ -1301,14 +1456,20 @@ RestWrite.prototype.handleInstallation = function () { !this.data.installationId && !objectIdMatch.installationId ) { - throw new Parse.Error(136, 'deviceToken may not be changed in this ' + 'operation'); + throw new Parse.Error( + 136, + "deviceToken may not be changed in this " + "operation" + ); } if ( this.data.deviceType && this.data.deviceType && this.data.deviceType !== objectIdMatch.deviceType ) { - throw new Parse.Error(136, 'deviceType may not be changed in this ' + 'operation'); + throw new Parse.Error( + 136, + "deviceType may not be changed in this " + "operation" + ); } } @@ -1321,7 +1482,10 @@ RestWrite.prototype.handleInstallation = function () { } // need to specify deviceType only if it's new if (!this.query && !this.data.deviceType && !idMatch) { - throw new Parse.Error(135, 'deviceType must be specified in this operation'); + throw new Parse.Error( + 135, + "deviceType must be specified in this operation" + ); } }) .then(() => { @@ -1330,17 +1494,17 @@ RestWrite.prototype.handleInstallation = function () { return; } else if ( deviceTokenMatches.length == 1 && - (!deviceTokenMatches[0]['installationId'] || !installationId) + (!deviceTokenMatches[0]["installationId"] || !installationId) ) { // Single match on device token but none on installationId, and either // the passed object or the match is missing an installationId, so we // can just return the match. - return deviceTokenMatches[0]['objectId']; + return deviceTokenMatches[0]["objectId"]; } else if (!this.data.installationId) { throw new Parse.Error( 132, - 'Must specify installationId when deviceToken ' + - 'matches multiple Installation objects' + "Must specify installationId when deviceToken " + + "matches multiple Installation objects" ); } else { // Multiple device token matches and we specified an installation ID, @@ -1355,9 +1519,9 @@ RestWrite.prototype.handleInstallation = function () { }, }; if (this.data.appIdentifier) { - delQuery['appIdentifier'] = this.data.appIdentifier; + delQuery["appIdentifier"] = this.data.appIdentifier; } - this.config.database.destroy('_Installation', delQuery).catch(err => { + this.config.database.destroy("_Installation", delQuery).catch(err => { if (err.code == Parse.Error.OBJECT_NOT_FOUND) { // no deletions were made. Can be ignored. return; @@ -1368,15 +1532,18 @@ RestWrite.prototype.handleInstallation = function () { return; } } else { - if (deviceTokenMatches.length == 1 && !deviceTokenMatches[0]['installationId']) { + if ( + deviceTokenMatches.length == 1 && + !deviceTokenMatches[0]["installationId"] + ) { // Exactly one device token match and it doesn't have an installation // ID. This is the one case where we want to merge with the existing // object. const delQuery = { objectId: idMatch.objectId }; return this.config.database - .destroy('_Installation', delQuery) + .destroy("_Installation", delQuery) .then(() => { - return deviceTokenMatches[0]['objectId']; + return deviceTokenMatches[0]["objectId"]; }) .catch(err => { if (err.code == Parse.Error.OBJECT_NOT_FOUND) { @@ -1387,7 +1554,10 @@ RestWrite.prototype.handleInstallation = function () { throw err; }); } else { - if (this.data.deviceToken && idMatch.deviceToken != this.data.deviceToken) { + if ( + this.data.deviceToken && + idMatch.deviceToken != this.data.deviceToken + ) { // We're setting the device token on an existing installation, so // we should try cleaning out old installations that match this // device token. @@ -1397,7 +1567,7 @@ RestWrite.prototype.handleInstallation = function () { // We have a unique install Id, use that to preserve // the interesting installation if (this.data.installationId) { - delQuery['installationId'] = { + delQuery["installationId"] = { $ne: this.data.installationId, }; } else if ( @@ -1406,7 +1576,7 @@ RestWrite.prototype.handleInstallation = function () { idMatch.objectId == this.data.objectId ) { // we passed an objectId, preserve that instalation - delQuery['objectId'] = { + delQuery["objectId"] = { $ne: idMatch.objectId, }; } else { @@ -1414,16 +1584,18 @@ RestWrite.prototype.handleInstallation = function () { return idMatch.objectId; } if (this.data.appIdentifier) { - delQuery['appIdentifier'] = this.data.appIdentifier; + delQuery["appIdentifier"] = this.data.appIdentifier; } - this.config.database.destroy('_Installation', delQuery).catch(err => { - if (err.code == Parse.Error.OBJECT_NOT_FOUND) { - // no deletions were made. Can be ignored. - return; - } - // rethrow the error - throw err; - }); + this.config.database + .destroy("_Installation", delQuery) + .catch(err => { + if (err.code == Parse.Error.OBJECT_NOT_FOUND) { + // no deletions were made. Can be ignored. + return; + } + // rethrow the error + throw err; + }); } // In non-merge scenarios, just return the installation match id return idMatch.objectId; @@ -1447,7 +1619,10 @@ RestWrite.prototype.handleInstallation = function () { RestWrite.prototype.expandFilesForExistingObjects = async function () { // Check whether we have a short-circuited response - only then run expansion. if (this.response && this.response.response) { - await this.config.filesController.expandFilesInObject(this.config, this.response.response); + await this.config.filesController.expandFilesInObject( + this.config, + this.response.response + ); } }; @@ -1456,35 +1631,39 @@ RestWrite.prototype.runDatabaseOperation = function () { return; } - if (this.className === '_Role') { + if (this.className === "_Role") { this.config.cacheController.role.clear(); if (this.config.liveQueryController) { this.config.liveQueryController.clearCachedRoles(this.auth.user); } } - if (this.className === '_User' && this.query && this.auth.isUnauthenticated()) { + if ( + this.className === "_User" && + this.query && + this.auth.isUnauthenticated() + ) { throw new Parse.Error( Parse.Error.SESSION_MISSING, `Cannot modify user ${this.query.objectId}.` ); } - if (this.className === '_Product' && this.data.download) { + if (this.className === "_Product" && this.data.download) { this.data.downloadName = this.data.download.name; } // TODO: Add better detection for ACL, ensuring a user can't be locked from // their own user record. - if (this.data.ACL && this.data.ACL['*unresolved']) { - throw new Parse.Error(Parse.Error.INVALID_ACL, 'Invalid ACL.'); + if (this.data.ACL && this.data.ACL["*unresolved"]) { + throw new Parse.Error(Parse.Error.INVALID_ACL, "Invalid ACL."); } if (this.query) { // Force the user to not lockout // Matched with parse.com if ( - this.className === '_User' && + this.className === "_User" && this.data.ACL && this.auth.isMaster !== true && this.auth.isMaintenance !== true @@ -1493,7 +1672,7 @@ RestWrite.prototype.runDatabaseOperation = function () { } // update password timestamp if user password is being changed if ( - this.className === '_User' && + this.className === "_User" && this.data._hashed_password && this.config.passwordPolicy && this.config.passwordPolicy.maxPasswordAge @@ -1506,16 +1685,16 @@ RestWrite.prototype.runDatabaseOperation = function () { let defer = Promise.resolve(); // if password history is enabled then save the current password to history if ( - this.className === '_User' && + this.className === "_User" && this.data._hashed_password && this.config.passwordPolicy && this.config.passwordPolicy.maxPasswordHistory ) { defer = this.config.database .find( - '_User', + "_User", { objectId: this.objectId() }, - { keys: ['_password_history', '_hashed_password'] }, + { keys: ["_password_history", "_hashed_password"] }, Auth.maintenance(this.config) ) .then(results => { @@ -1532,7 +1711,8 @@ RestWrite.prototype.runDatabaseOperation = function () { } //n-1 passwords go into history including last password while ( - oldPasswords.length > Math.max(0, this.config.passwordPolicy.maxPasswordHistory - 2) + oldPasswords.length > + Math.max(0, this.config.passwordPolicy.maxPasswordHistory - 2) ) { oldPasswords.shift(); } @@ -1561,44 +1741,64 @@ RestWrite.prototype.runDatabaseOperation = function () { }); } else { // Set the default ACL and password timestamp for the new _User - if (this.className === '_User') { + if (this.className === "_User") { var ACL = this.data.ACL; // default public r/w ACL if (!ACL) { ACL = {}; if (!this.config.enforcePrivateUsers) { - ACL['*'] = { read: true, write: false }; + ACL["*"] = { read: true, write: false }; } } // make sure the user is not locked down ACL[this.data.objectId] = { read: true, write: true }; this.data.ACL = ACL; // password timestamp to be used when password expiry policy is enforced - if (this.config.passwordPolicy && this.config.passwordPolicy.maxPasswordAge) { + if ( + this.config.passwordPolicy && + this.config.passwordPolicy.maxPasswordAge + ) { this.data._password_changed_at = Parse._encode(new Date()); } } // Run a create return this.config.database - .create(this.className, this.data, this.runOptions, false, this.validSchemaController) + .create( + this.className, + this.data, + this.runOptions, + false, + this.validSchemaController + ) .catch(error => { - if (this.className !== '_User' || error.code !== Parse.Error.DUPLICATE_VALUE) { + if ( + this.className !== "_User" || + error.code !== Parse.Error.DUPLICATE_VALUE + ) { throw error; } // Quick check, if we were able to infer the duplicated field name - if (error && error.userInfo && error.userInfo.duplicated_field === 'username') { + if ( + error && + error.userInfo && + error.userInfo.duplicated_field === "username" + ) { throw new Parse.Error( Parse.Error.USERNAME_TAKEN, - 'Account already exists for this username.' + "Account already exists for this username." ); } - if (error && error.userInfo && error.userInfo.duplicated_field === 'email') { + if ( + error && + error.userInfo && + error.userInfo.duplicated_field === "email" + ) { throw new Parse.Error( Parse.Error.EMAIL_TAKEN, - 'Account already exists for this email address.' + "Account already exists for this email address." ); } @@ -1619,7 +1819,7 @@ RestWrite.prototype.runDatabaseOperation = function () { if (results.length > 0) { throw new Parse.Error( Parse.Error.USERNAME_TAKEN, - 'Account already exists for this username.' + "Account already exists for this username." ); } return this.config.database.find( @@ -1632,12 +1832,12 @@ RestWrite.prototype.runDatabaseOperation = function () { if (results.length > 0) { throw new Parse.Error( Parse.Error.EMAIL_TAKEN, - 'Account already exists for this email address.' + "Account already exists for this email address." ); } throw new Parse.Error( Parse.Error.DUPLICATE_VALUE, - 'A duplicate value for a field with unique values was provided' + "A duplicate value for a field with unique values was provided" ); }); }) @@ -1670,18 +1870,25 @@ RestWrite.prototype.runAfterSaveTrigger = function () { triggers.Types.afterSave, this.config.applicationId ); - const hasLiveQuery = this.config.liveQueryController.hasLiveQuery(this.className); + const hasLiveQuery = this.config.liveQueryController.hasLiveQuery( + this.className + ); if (!hasAfterSaveHook && !hasLiveQuery) { return Promise.resolve(); } const { originalObject, updatedObject } = this.buildParseObjects(); - updatedObject._handleSaveResponse(this.response.response, this.response.status || 200); + updatedObject._handleSaveResponse( + this.response.response, + this.response.status || 200 + ); if (hasLiveQuery) { this.config.database.loadSchema().then(schemaController => { // Notify LiveQueryServer if possible - const perms = schemaController.getClassLevelPermissions(updatedObject.className); + const perms = schemaController.getClassLevelPermissions( + updatedObject.className + ); this.config.liveQueryController.onAfterSave( updatedObject.className, updatedObject, @@ -1716,13 +1923,14 @@ RestWrite.prototype.runAfterSaveTrigger = function () { } }) .catch(function (err) { - logger.warn('afterSave caught an error', err); + logger.warn("afterSave caught an error", err); }); }; // A helper to figure out what location this operation happens at. RestWrite.prototype.location = function () { - var middle = this.className === '_User' ? '/users/' : '/classes/' + this.className + '/'; + var middle = + this.className === "_User" ? "/users/" : "/classes/" + this.className + "/"; const mount = this.config.mount || this.config.serverURL; return mount + middle + this.data.objectId; }; @@ -1747,7 +1955,10 @@ RestWrite.prototype.sanitizedData = function () { // Returns an updated copy of the object RestWrite.prototype.buildParseObjects = function () { - const extraData = { className: this.className, objectId: this.query?.objectId }; + const extraData = { + className: this.className, + objectId: this.query?.objectId, + }; let originalObject; if (this.query && this.query.objectId) { originalObject = triggers.inflate(extraData, this.originalData); @@ -1764,17 +1975,17 @@ RestWrite.prototype.buildParseObjects = function () { } const updatedObject = triggers.inflate(extraData, this.originalData); Object.keys(this.data).reduce(function (data, key) { - if (key.indexOf('.') > 0) { - if (typeof data[key].__op === 'string') { + if (key.indexOf(".") > 0) { + if (typeof data[key].__op === "string") { if (!readOnlyAttributes.includes(key)) { updatedObject.set(key, data[key]); } } else { // subdocument key with dot notation { 'x.y': v } => { 'x': { 'y' : v } }) - const splittedKey = key.split('.'); + const splittedKey = key.split("."); const parentProp = splittedKey[0]; let parentVal = updatedObject.get(parentProp); - if (typeof parentVal !== 'object') { + if (typeof parentVal !== "object") { parentVal = {}; } parentVal[splittedKey[1]] = data[key]; @@ -1794,7 +2005,7 @@ RestWrite.prototype.buildParseObjects = function () { }; RestWrite.prototype.cleanUserAuthData = function () { - if (this.response && this.response.response && this.className === '_User') { + if (this.response && this.response.response && this.className === "_User") { const user = this.response.response; if (user.authData) { Object.keys(user.authData).forEach(provider => { @@ -1814,15 +2025,17 @@ RestWrite.prototype._updateResponseWithData = function (response, data) { const [pending] = stateController.getPendingOps(this.pendingOps.identifier); for (const key in this.pendingOps.operations) { if (!pending[key]) { - data[key] = this.originalData ? this.originalData[key] : { __op: 'Delete' }; + data[key] = this.originalData + ? this.originalData[key] + : { __op: "Delete" }; this.storage.fieldsChangedByTrigger.push(key); } } const skipKeys = [...(requiredColumns.read[this.className] || [])]; if (!this.query) { - skipKeys.push('objectId', 'createdAt'); + skipKeys.push("objectId", "createdAt"); } else { - skipKeys.push('updatedAt'); + skipKeys.push("updatedAt"); delete response.objectId; } for (const key in response) { @@ -1832,7 +2045,7 @@ RestWrite.prototype._updateResponseWithData = function (response, data) { const value = response[key]; if ( value == null || - (value.__type && value.__type === 'Pointer') || + (value.__type && value.__type === "Pointer") || util.isDeepStrictEqual(data[key], value) || util.isDeepStrictEqual((this.originalData || {})[key], value) ) { @@ -1853,7 +2066,7 @@ RestWrite.prototype._updateResponseWithData = function (response, data) { // Strips operations from responses if (response[fieldName] && response[fieldName].__op) { delete response[fieldName]; - if (clientSupportsDelete && dataValue.__op == 'Delete') { + if (clientSupportsDelete && dataValue.__op == "Delete") { response[fieldName] = dataValue; } } diff --git a/src/Routers/AggregateRouter.js b/src/Routers/AggregateRouter.js index cf9b5cd190..d422917b92 100644 --- a/src/Routers/AggregateRouter.js +++ b/src/Routers/AggregateRouter.js @@ -1,12 +1,15 @@ -import Parse from 'parse/node'; -import * as middleware from '../middlewares'; -import rest from '../rest'; -import ClassesRouter from './ClassesRouter'; -import UsersRouter from './UsersRouter'; +import Parse from "parse/node"; +import * as middleware from "../middlewares"; +import rest from "../rest"; +import ClassesRouter from "./ClassesRouter"; +import UsersRouter from "./UsersRouter"; export class AggregateRouter extends ClassesRouter { async handleFind(req) { - const body = Object.assign(req.body || {}, ClassesRouter.JSONFromQuery(req.query)); + const body = Object.assign( + req.body || {}, + ClassesRouter.JSONFromQuery(req.query) + ); const options = {}; if (body.distinct) { options.distinct = String(body.distinct); @@ -28,7 +31,7 @@ export class AggregateRouter extends ClassesRouter { delete body.readPreference; } options.pipeline = AggregateRouter.getPipeline(body); - if (typeof body.where === 'string') { + if (typeof body.where === "string") { body.where = JSON.parse(body.where); } try { @@ -42,7 +45,7 @@ export class AggregateRouter extends ClassesRouter { req.info.context ); for (const result of response.results) { - if (typeof result === 'object') { + if (typeof result === "object") { UsersRouter.removeHiddenProperties(result); } } @@ -92,7 +95,7 @@ export class AggregateRouter extends ClassesRouter { if (keys.length !== 1) { throw new Parse.Error( Parse.Error.INVALID_QUERY, - `Pipeline stages should only have one key but found ${keys.join(', ')}.` + `Pipeline stages should only have one key but found ${keys.join(", ")}.` ); } return AggregateRouter.transformStage(keys[0], stage); @@ -100,21 +103,24 @@ export class AggregateRouter extends ClassesRouter { } static transformStage(stageName, stage) { - const skipKeys = ['distinct', 'where']; + const skipKeys = ["distinct", "where"]; if (skipKeys.includes(stageName)) { return; } - if (stageName[0] !== '$') { - throw new Parse.Error(Parse.Error.INVALID_QUERY, `Invalid aggregate stage '${stageName}'.`); + if (stageName[0] !== "$") { + throw new Parse.Error( + Parse.Error.INVALID_QUERY, + `Invalid aggregate stage '${stageName}'.` + ); } - if (stageName === '$group') { - if (Object.prototype.hasOwnProperty.call(stage[stageName], 'objectId')) { + if (stageName === "$group") { + if (Object.prototype.hasOwnProperty.call(stage[stageName], "objectId")) { throw new Parse.Error( Parse.Error.INVALID_QUERY, `Cannot use 'objectId' in aggregation stage $group.` ); } - if (!Object.prototype.hasOwnProperty.call(stage[stageName], '_id')) { + if (!Object.prototype.hasOwnProperty.call(stage[stageName], "_id")) { throw new Parse.Error( Parse.Error.INVALID_QUERY, `Invalid parameter for query: group. Missing key _id` @@ -125,9 +131,14 @@ export class AggregateRouter extends ClassesRouter { } mountRoutes() { - this.route('GET', '/aggregate/:className', middleware.promiseEnforceMasterKeyAccess, req => { - return this.handleFind(req); - }); + this.route( + "GET", + "/aggregate/:className", + middleware.promiseEnforceMasterKeyAccess, + req => { + return this.handleFind(req); + } + ); } } diff --git a/src/Routers/AnalyticsRouter.js b/src/Routers/AnalyticsRouter.js index 90ffcdcc4a..2379211f51 100644 --- a/src/Routers/AnalyticsRouter.js +++ b/src/Routers/AnalyticsRouter.js @@ -1,5 +1,5 @@ // AnalyticsRouter.js -import PromiseRouter from '../PromiseRouter'; +import PromiseRouter from "../PromiseRouter"; function appOpened(req) { const analyticsController = req.config.analyticsController; @@ -13,7 +13,7 @@ function trackEvent(req) { export class AnalyticsRouter extends PromiseRouter { mountRoutes() { - this.route('POST', '/events/AppOpened', appOpened); - this.route('POST', '/events/:eventName', trackEvent); + this.route("POST", "/events/AppOpened", appOpened); + this.route("POST", "/events/:eventName", trackEvent); } } diff --git a/src/Routers/AudiencesRouter.js b/src/Routers/AudiencesRouter.js index d16a34fb30..97fbfb13fc 100644 --- a/src/Routers/AudiencesRouter.js +++ b/src/Routers/AudiencesRouter.js @@ -1,21 +1,27 @@ -import ClassesRouter from './ClassesRouter'; -import rest from '../rest'; -import * as middleware from '../middlewares'; +import ClassesRouter from "./ClassesRouter"; +import rest from "../rest"; +import * as middleware from "../middlewares"; export class AudiencesRouter extends ClassesRouter { className() { - return '_Audience'; + return "_Audience"; } handleFind(req) { - const body = Object.assign(req.body || {}, ClassesRouter.JSONFromQuery(req.query)); - const options = ClassesRouter.optionsFromBody(body, req.config.defaultLimit); + const body = Object.assign( + req.body || {}, + ClassesRouter.JSONFromQuery(req.query) + ); + const options = ClassesRouter.optionsFromBody( + body, + req.config.defaultLimit + ); return rest .find( req.config, req.auth, - '_Audience', + "_Audience", body.where, options, req.info.clientSDK, @@ -39,31 +45,41 @@ export class AudiencesRouter extends ClassesRouter { } mountRoutes() { - this.route('GET', '/push_audiences', middleware.promiseEnforceMasterKeyAccess, req => { - return this.handleFind(req); - }); this.route( - 'GET', - '/push_audiences/:objectId', + "GET", + "/push_audiences", + middleware.promiseEnforceMasterKeyAccess, + req => { + return this.handleFind(req); + } + ); + this.route( + "GET", + "/push_audiences/:objectId", middleware.promiseEnforceMasterKeyAccess, req => { return this.handleGet(req); } ); - this.route('POST', '/push_audiences', middleware.promiseEnforceMasterKeyAccess, req => { - return this.handleCreate(req); - }); this.route( - 'PUT', - '/push_audiences/:objectId', + "POST", + "/push_audiences", + middleware.promiseEnforceMasterKeyAccess, + req => { + return this.handleCreate(req); + } + ); + this.route( + "PUT", + "/push_audiences/:objectId", middleware.promiseEnforceMasterKeyAccess, req => { return this.handleUpdate(req); } ); this.route( - 'DELETE', - '/push_audiences/:objectId', + "DELETE", + "/push_audiences/:objectId", middleware.promiseEnforceMasterKeyAccess, req => { return this.handleDelete(req); diff --git a/src/Routers/ClassesRouter.js b/src/Routers/ClassesRouter.js index 8b6e447757..67bd042639 100644 --- a/src/Routers/ClassesRouter.js +++ b/src/Routers/ClassesRouter.js @@ -1,16 +1,16 @@ -import PromiseRouter from '../PromiseRouter'; -import rest from '../rest'; -import _ from 'lodash'; -import Parse from 'parse/node'; -import { promiseEnsureIdempotency } from '../middlewares'; +import PromiseRouter from "../PromiseRouter"; +import rest from "../rest"; +import _ from "lodash"; +import Parse from "parse/node"; +import { promiseEnsureIdempotency } from "../middlewares"; const ALLOWED_GET_QUERY_KEYS = [ - 'keys', - 'include', - 'excludeKeys', - 'readPreference', - 'includeReadPreference', - 'subqueryReadPreference', + "keys", + "include", + "excludeKeys", + "readPreference", + "includeReadPreference", + "subqueryReadPreference", ]; export class ClassesRouter extends PromiseRouter { @@ -19,8 +19,14 @@ export class ClassesRouter extends PromiseRouter { } handleFind(req) { - const body = Object.assign(req.body || {}, ClassesRouter.JSONFromQuery(req.query)); - const options = ClassesRouter.optionsFromBody(body, req.config.defaultLimit); + const body = Object.assign( + req.body || {}, + ClassesRouter.JSONFromQuery(req.query) + ); + const options = ClassesRouter.optionsFromBody( + body, + req.config.defaultLimit + ); if (req.config.maxLimit && body.limit > req.config.maxLimit) { // Silently replace the limit on the query with the max configured options.limit = Number(req.config.maxLimit); @@ -28,7 +34,7 @@ export class ClassesRouter extends PromiseRouter { if (body.redirectClassNameForKey) { options.redirectClassNameForKey = String(body.redirectClassNameForKey); } - if (typeof body.where === 'string') { + if (typeof body.where === "string") { body.where = JSON.parse(body.where); } return rest @@ -48,12 +54,18 @@ export class ClassesRouter extends PromiseRouter { // Returns a promise for a {response} object. handleGet(req) { - const body = Object.assign(req.body || {}, ClassesRouter.JSONFromQuery(req.query)); + const body = Object.assign( + req.body || {}, + ClassesRouter.JSONFromQuery(req.query) + ); const options = {}; for (const key of Object.keys(body)) { if (ALLOWED_GET_QUERY_KEYS.indexOf(key) === -1) { - throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Improper encode of parameter'); + throw new Parse.Error( + Parse.Error.INVALID_QUERY, + "Improper encode of parameter" + ); } } @@ -66,13 +78,13 @@ export class ClassesRouter extends PromiseRouter { if (body.excludeKeys != null) { options.excludeKeys = String(body.excludeKeys); } - if (typeof body.readPreference === 'string') { + if (typeof body.readPreference === "string") { options.readPreference = body.readPreference; } - if (typeof body.includeReadPreference === 'string') { + if (typeof body.includeReadPreference === "string") { options.includeReadPreference = body.includeReadPreference; } - if (typeof body.subqueryReadPreference === 'string') { + if (typeof body.subqueryReadPreference === "string") { options.subqueryReadPreference = body.subqueryReadPreference; } @@ -88,10 +100,13 @@ export class ClassesRouter extends PromiseRouter { ) .then(response => { if (!response.results || response.results.length == 0) { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.'); + throw new Parse.Error( + Parse.Error.OBJECT_NOT_FOUND, + "Object not found." + ); } - if (this.className(req) === '_User') { + if (this.className(req) === "_User") { delete response.results[0].sessionToken; const user = response.results[0]; @@ -107,11 +122,14 @@ export class ClassesRouter extends PromiseRouter { handleCreate(req) { if ( - this.className(req) === '_User' && - typeof req.body?.objectId === 'string' && - req.body.objectId.startsWith('role:') + this.className(req) === "_User" && + typeof req.body?.objectId === "string" && + req.body.objectId.startsWith("role:") ) { - throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Invalid object ID.'); + throw new Parse.Error( + Parse.Error.OPERATION_FORBIDDEN, + "Invalid object ID." + ); } return rest.create( req.config, @@ -138,7 +156,13 @@ export class ClassesRouter extends PromiseRouter { handleDelete(req) { return rest - .del(req.config, req.auth, this.className(req), req.params.objectId, req.info.context) + .del( + req.config, + req.auth, + this.className(req), + req.params.objectId, + req.info.context + ) .then(() => { return { response: {} }; }); @@ -158,27 +182,30 @@ export class ClassesRouter extends PromiseRouter { static optionsFromBody(body, defaultLimit) { const allowConstraints = [ - 'skip', - 'limit', - 'order', - 'count', - 'keys', - 'excludeKeys', - 'include', - 'includeAll', - 'redirectClassNameForKey', - 'where', - 'readPreference', - 'includeReadPreference', - 'subqueryReadPreference', - 'hint', - 'explain', - 'comment', + "skip", + "limit", + "order", + "count", + "keys", + "excludeKeys", + "include", + "includeAll", + "redirectClassNameForKey", + "where", + "readPreference", + "includeReadPreference", + "subqueryReadPreference", + "hint", + "explain", + "comment", ]; for (const key of Object.keys(body)) { if (allowConstraints.indexOf(key) === -1) { - throw new Parse.Error(Parse.Error.INVALID_QUERY, `Invalid parameter for query: ${key}`); + throw new Parse.Error( + Parse.Error.INVALID_QUERY, + `Invalid parameter for query: ${key}` + ); } } const options = {}; @@ -208,41 +235,49 @@ export class ClassesRouter extends PromiseRouter { if (body.includeAll) { options.includeAll = true; } - if (typeof body.readPreference === 'string') { + if (typeof body.readPreference === "string") { options.readPreference = body.readPreference; } - if (typeof body.includeReadPreference === 'string') { + if (typeof body.includeReadPreference === "string") { options.includeReadPreference = body.includeReadPreference; } - if (typeof body.subqueryReadPreference === 'string') { + if (typeof body.subqueryReadPreference === "string") { options.subqueryReadPreference = body.subqueryReadPreference; } - if (body.hint && (typeof body.hint === 'string' || typeof body.hint === 'object')) { + if ( + body.hint && + (typeof body.hint === "string" || typeof body.hint === "object") + ) { options.hint = body.hint; } if (body.explain) { options.explain = body.explain; } - if (body.comment && typeof body.comment === 'string') { + if (body.comment && typeof body.comment === "string") { options.comment = body.comment; } return options; } mountRoutes() { - this.route('GET', '/classes/:className', req => { + this.route("GET", "/classes/:className", req => { return this.handleFind(req); }); - this.route('GET', '/classes/:className/:objectId', req => { + this.route("GET", "/classes/:className/:objectId", req => { return this.handleGet(req); }); - this.route('POST', '/classes/:className', promiseEnsureIdempotency, req => { + this.route("POST", "/classes/:className", promiseEnsureIdempotency, req => { return this.handleCreate(req); }); - this.route('PUT', '/classes/:className/:objectId', promiseEnsureIdempotency, req => { - return this.handleUpdate(req); - }); - this.route('DELETE', '/classes/:className/:objectId', req => { + this.route( + "PUT", + "/classes/:className/:objectId", + promiseEnsureIdempotency, + req => { + return this.handleUpdate(req); + } + ); + this.route("DELETE", "/classes/:className/:objectId", req => { return this.handleDelete(req); }); } diff --git a/src/Routers/CloudCodeRouter.js b/src/Routers/CloudCodeRouter.js index 58408151e7..cc5a0dd4de 100644 --- a/src/Routers/CloudCodeRouter.js +++ b/src/Routers/CloudCodeRouter.js @@ -1,11 +1,11 @@ -import PromiseRouter from '../PromiseRouter'; -import Parse from 'parse/node'; -import rest from '../rest'; -const triggers = require('../triggers'); -const middleware = require('../middlewares'); +import PromiseRouter from "../PromiseRouter"; +import Parse from "parse/node"; +import rest from "../rest"; +const triggers = require("../triggers"); +const middleware = require("../middlewares"); function formatJobSchedule(job_schedule) { - if (typeof job_schedule.startAfter === 'undefined') { + if (typeof job_schedule.startAfter === "undefined") { job_schedule.startAfter = new Date().toISOString(); } return job_schedule; @@ -16,7 +16,7 @@ function validateJobSchedule(config, job_schedule) { if (job_schedule.jobName && !jobs[job_schedule.jobName]) { throw new Parse.Error( Parse.Error.INTERNAL_SERVER_ERROR, - 'Cannot Schedule a job that is not deployed' + "Cannot Schedule a job that is not deployed" ); } } @@ -24,56 +24,60 @@ function validateJobSchedule(config, job_schedule) { export class CloudCodeRouter extends PromiseRouter { mountRoutes() { this.route( - 'GET', - '/cloud_code/jobs', + "GET", + "/cloud_code/jobs", middleware.promiseEnforceMasterKeyAccess, CloudCodeRouter.getJobs ); this.route( - 'GET', - '/cloud_code/jobs/data', + "GET", + "/cloud_code/jobs/data", middleware.promiseEnforceMasterKeyAccess, CloudCodeRouter.getJobsData ); this.route( - 'POST', - '/cloud_code/jobs', + "POST", + "/cloud_code/jobs", middleware.promiseEnforceMasterKeyAccess, CloudCodeRouter.createJob ); this.route( - 'PUT', - '/cloud_code/jobs/:objectId', + "PUT", + "/cloud_code/jobs/:objectId", middleware.promiseEnforceMasterKeyAccess, CloudCodeRouter.editJob ); this.route( - 'DELETE', - '/cloud_code/jobs/:objectId', + "DELETE", + "/cloud_code/jobs/:objectId", middleware.promiseEnforceMasterKeyAccess, CloudCodeRouter.deleteJob ); } static getJobs(req) { - return rest.find(req.config, req.auth, '_JobSchedule', {}, {}).then(scheduledJobs => { - return { - response: scheduledJobs.results, - }; - }); + return rest + .find(req.config, req.auth, "_JobSchedule", {}, {}) + .then(scheduledJobs => { + return { + response: scheduledJobs.results, + }; + }); } static getJobsData(req) { const config = req.config; const jobs = triggers.getJobs(config.applicationId) || {}; - return rest.find(req.config, req.auth, '_JobSchedule', {}, {}).then(scheduledJobs => { - return { - response: { - in_use: scheduledJobs.results.map(job => job.jobName), - jobs: Object.keys(jobs), - }, - }; - }); + return rest + .find(req.config, req.auth, "_JobSchedule", {}, {}) + .then(scheduledJobs => { + return { + response: { + in_use: scheduledJobs.results.map(job => job.jobName), + jobs: Object.keys(jobs), + }, + }; + }); } static createJob(req) { @@ -82,7 +86,7 @@ export class CloudCodeRouter extends PromiseRouter { return rest.create( req.config, req.auth, - '_JobSchedule', + "_JobSchedule", formatJobSchedule(job_schedule), req.client, req.info.context @@ -97,7 +101,7 @@ export class CloudCodeRouter extends PromiseRouter { .update( req.config, req.auth, - '_JobSchedule', + "_JobSchedule", { objectId }, formatJobSchedule(job_schedule), undefined, @@ -113,7 +117,7 @@ export class CloudCodeRouter extends PromiseRouter { static deleteJob(req) { const { objectId } = req.params; return rest - .del(req.config, req.auth, '_JobSchedule', objectId, req.info.context) + .del(req.config, req.auth, "_JobSchedule", objectId, req.info.context) .then(response => { return { response, diff --git a/src/Routers/FeaturesRouter.js b/src/Routers/FeaturesRouter.js index df26338955..cdfcd07092 100644 --- a/src/Routers/FeaturesRouter.js +++ b/src/Routers/FeaturesRouter.js @@ -1,62 +1,67 @@ -import { version } from '../../package.json'; -import PromiseRouter from '../PromiseRouter'; -import * as middleware from '../middlewares'; +import { version } from "../../package.json"; +import PromiseRouter from "../PromiseRouter"; +import * as middleware from "../middlewares"; export class FeaturesRouter extends PromiseRouter { mountRoutes() { - this.route('GET', '/serverInfo', middleware.promiseEnforceMasterKeyAccess, req => { - const { config } = req; - const features = { - globalConfig: { - create: true, - read: true, - update: true, - delete: true, - }, - hooks: { - create: true, - read: true, - update: true, - delete: true, - }, - cloudCode: { - jobs: true, - }, - logs: { - level: true, - size: true, - order: true, - until: true, - from: true, - }, - push: { - immediatePush: config.hasPushSupport, - scheduledPush: config.hasPushScheduledSupport, - storedPushData: config.hasPushSupport, - pushAudiences: true, - localization: true, - }, - schemas: { - addField: true, - removeField: true, - addClass: true, - removeClass: true, - clearAllDataFromClass: true, - exportClass: false, - editClassLevelPermissions: true, - editPointerPermissions: true, - }, - settings: { - securityCheck: !!config.security?.enableCheck, - }, - }; + this.route( + "GET", + "/serverInfo", + middleware.promiseEnforceMasterKeyAccess, + req => { + const { config } = req; + const features = { + globalConfig: { + create: true, + read: true, + update: true, + delete: true, + }, + hooks: { + create: true, + read: true, + update: true, + delete: true, + }, + cloudCode: { + jobs: true, + }, + logs: { + level: true, + size: true, + order: true, + until: true, + from: true, + }, + push: { + immediatePush: config.hasPushSupport, + scheduledPush: config.hasPushScheduledSupport, + storedPushData: config.hasPushSupport, + pushAudiences: true, + localization: true, + }, + schemas: { + addField: true, + removeField: true, + addClass: true, + removeClass: true, + clearAllDataFromClass: true, + exportClass: false, + editClassLevelPermissions: true, + editPointerPermissions: true, + }, + settings: { + securityCheck: !!config.security?.enableCheck, + }, + }; - return { - response: { - features: features, - parseServerVersion: version, - }, - }; - }); + return { + response: { + features: features, + parseServerVersion: version, + }, + }; + } + ); } } diff --git a/src/Routers/FilesRouter.js b/src/Routers/FilesRouter.js index 0bec64c9aa..2b4a9e308a 100644 --- a/src/Routers/FilesRouter.js +++ b/src/Routers/FilesRouter.js @@ -1,29 +1,29 @@ -import express from 'express'; -import * as Middlewares from '../middlewares'; -import Parse from 'parse/node'; -import Config from '../Config'; -import logger from '../logger'; -const triggers = require('../triggers'); -const http = require('http'); -const Utils = require('../Utils'); +import express from "express"; +import * as Middlewares from "../middlewares"; +import Parse from "parse/node"; +import Config from "../Config"; +import logger from "../logger"; +const triggers = require("../triggers"); +const http = require("http"); +const Utils = require("../Utils"); const downloadFileFromURI = uri => { return new Promise((res, rej) => { http .get(uri, response => { - response.setDefaultEncoding('base64'); - let body = `data:${response.headers['content-type']};base64,`; - response.on('data', data => (body += data)); - response.on('end', () => res(body)); + response.setDefaultEncoding("base64"); + let body = `data:${response.headers["content-type"]};base64,`; + response.on("data", data => (body += data)); + response.on("end", () => res(body)); }) - .on('error', e => { + .on("error", e => { rej(`Error downloading file from ${uri}: ${e.message}`); }); }); }; const addFileDataIfNeeded = async file => { - if (file._source.format === 'uri') { + if (file._source.format === "uri") { const base64 = await downloadFileFromURI(file._source.uri); file._previousSave = file; file._data = base64; @@ -33,17 +33,19 @@ const addFileDataIfNeeded = async file => { }; export class FilesRouter { - expressRouter({ maxUploadSize = '20Mb' } = {}) { + expressRouter({ maxUploadSize = "20Mb" } = {}) { var router = express.Router(); - router.get('/files/:appId/:filename', this.getHandler); - router.get('/files/:appId/metadata/:filename', this.metadataHandler); + router.get("/files/:appId/:filename", this.getHandler); + router.get("/files/:appId/metadata/:filename", this.metadataHandler); - router.post('/files', function (req, res, next) { - next(new Parse.Error(Parse.Error.INVALID_FILE_NAME, 'Filename not provided.')); + router.post("/files", function (req, res, next) { + next( + new Parse.Error(Parse.Error.INVALID_FILE_NAME, "Filename not provided.") + ); }); router.post( - '/files/:filename', + "/files/:filename", express.raw({ type: () => { return true; @@ -56,7 +58,7 @@ export class FilesRouter { ); router.delete( - '/files/:filename', + "/files/:filename", Middlewares.handleParseHeaders, Middlewares.handleParseSession, Middlewares.enforceMasterKeyAccess, @@ -69,7 +71,10 @@ export class FilesRouter { const config = Config.get(req.params.appId); if (!config) { res.status(403); - const err = new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Invalid application ID.'); + const err = new Parse.Error( + Parse.Error.OPERATION_FORBIDDEN, + "Invalid application ID." + ); res.json({ code: err.code, error: err.message }); return; } @@ -77,9 +82,9 @@ export class FilesRouter { let filename = req.params.filename; try { const filesController = config.filesController; - const mime = (await import('mime')).default; + const mime = (await import("mime")).default; let contentType = mime.getType(filename); - let file = new Parse.File(filename, { base64: '' }, contentType); + let file = new Parse.File(filename, { base64: "" }, contentType); const triggerResult = await triggers.maybeRunFileTrigger( triggers.Types.beforeFind, { file }, @@ -92,23 +97,31 @@ export class FilesRouter { } if (isFileStreamable(req, filesController)) { - filesController.handleFileStream(config, filename, req, res, contentType).catch(() => { - res.status(404); - res.set('Content-Type', 'text/plain'); - res.end('File not found.'); - }); + filesController + .handleFileStream(config, filename, req, res, contentType) + .catch(() => { + res.status(404); + res.set("Content-Type", "text/plain"); + res.end("File not found."); + }); return; } - let data = await filesController.getFileData(config, filename).catch(() => { - res.status(404); - res.set('Content-Type', 'text/plain'); - res.end('File not found.'); - }); + let data = await filesController + .getFileData(config, filename) + .catch(() => { + res.status(404); + res.set("Content-Type", "text/plain"); + res.end("File not found."); + }); if (!data) { return; } - file = new Parse.File(filename, { base64: data.toString('base64') }, contentType); + file = new Parse.File( + filename, + { base64: data.toString("base64") }, + contentType + ); const afterFind = await triggers.maybeRunFileTrigger( triggers.Types.afterFind, { file, forceDownload: false }, @@ -118,14 +131,17 @@ export class FilesRouter { if (afterFind?.file) { contentType = mime.getType(afterFind.file._name); - data = Buffer.from(afterFind.file._data, 'base64'); + data = Buffer.from(afterFind.file._data, "base64"); } res.status(200); - res.set('Content-Type', contentType); - res.set('Content-Length', data.length); + res.set("Content-Type", contentType); + res.set("Content-Length", data.length); if (afterFind.forceDownload) { - res.set('Content-Disposition', `attachment;filename=${afterFind.file._name}`); + res.set( + "Content-Disposition", + `attachment;filename=${afterFind.file._name}` + ); } res.end(data); } catch (e) { @@ -145,29 +161,44 @@ export class FilesRouter { const isLinked = user && Parse.AnonymousUtils.isLinked(user); if (!isMaster && !config.fileUpload.enableForAnonymousUser && isLinked) { next( - new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'File upload by anonymous user is disabled.') + new Parse.Error( + Parse.Error.FILE_SAVE_ERROR, + "File upload by anonymous user is disabled." + ) ); return; } - if (!isMaster && !config.fileUpload.enableForAuthenticatedUser && !isLinked && user) { + if ( + !isMaster && + !config.fileUpload.enableForAuthenticatedUser && + !isLinked && + user + ) { next( new Parse.Error( Parse.Error.FILE_SAVE_ERROR, - 'File upload by authenticated user is disabled.' + "File upload by authenticated user is disabled." ) ); return; } if (!isMaster && !config.fileUpload.enableForPublic && !user) { - next(new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'File upload by public is disabled.')); + next( + new Parse.Error( + Parse.Error.FILE_SAVE_ERROR, + "File upload by public is disabled." + ) + ); return; } const filesController = config.filesController; const { filename } = req.params; - const contentType = req.get('Content-type'); + const contentType = req.get("Content-type"); if (!req.body || !req.body.length) { - next(new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'Invalid file upload.')); + next( + new Parse.Error(Parse.Error.FILE_SAVE_ERROR, "Invalid file upload.") + ); return; } @@ -181,7 +212,7 @@ export class FilesRouter { if (!isMaster && fileExtensions) { const isValidExtension = extension => { return fileExtensions.some(ext => { - if (ext === '*') { + if (ext === "*") { return true; } const regex = new RegExp(ext); @@ -191,12 +222,12 @@ export class FilesRouter { }); }; let extension = contentType; - if (filename && filename.includes('.')) { - extension = filename.substring(filename.lastIndexOf('.') + 1); - } else if (contentType && contentType.includes('/')) { - extension = contentType.split('/')[1]; + if (filename && filename.includes(".")) { + extension = filename.substring(filename.lastIndexOf(".") + 1); + } else if (contentType && contentType.includes("/")) { + extension = contentType.split("/")[1]; } - extension = extension?.split(' ')?.join(''); + extension = extension?.split(" ")?.join(""); if (extension && !isValidExtension(extension)) { next( @@ -209,7 +240,7 @@ export class FilesRouter { } } - const base64 = req.body.toString('base64'); + const base64 = req.body.toString("base64"); const file = new Parse.File(filename, { base64 }, contentType); const { metadata = {}, tags = {} } = req.fileData || {}; try { @@ -250,7 +281,7 @@ export class FilesRouter { // if the ParseFile returned is type uri, download the file before saving it await addFileDataIfNeeded(fileObject.file); // update fileSize - const bufferData = Buffer.from(fileObject.file._data, 'base64'); + const bufferData = Buffer.from(fileObject.file._data, "base64"); fileObject.fileSize = Buffer.byteLength(bufferData); // prepare file options const fileOptions = { @@ -259,7 +290,9 @@ export class FilesRouter { // some s3-compatible providers (DigitalOcean, Linode) do not accept tags // so we do not include the tags option if it is empty. const fileTags = - Object.keys(fileObject.file._tags).length > 0 ? { tags: fileObject.file._tags } : {}; + Object.keys(fileObject.file._tags).length > 0 + ? { tags: fileObject.file._tags } + : {}; Object.assign(fileOptions, fileTags); // save file const createFileResult = await filesController.createFile( @@ -280,12 +313,17 @@ export class FilesRouter { }; } // run afterSaveFile trigger - await triggers.maybeRunFileTrigger(triggers.Types.afterSave, fileObject, config, req.auth); + await triggers.maybeRunFileTrigger( + triggers.Types.afterSave, + fileObject, + config, + req.auth + ); res.status(201); - res.set('Location', saveResult.url); + res.set("Location", saveResult.url); res.json(saveResult); } catch (e) { - logger.error('Error creating a file: ', e); + logger.error("Error creating a file: ", e); const error = triggers.resolveError(e, { code: Parse.Error.FILE_SAVE_ERROR, message: `Could not store file: ${fileObject.file._name}.`, @@ -300,7 +338,10 @@ export class FilesRouter { const { filename } = req.params; // run beforeDeleteFile trigger const file = new Parse.File(filename); - file._url = await filesController.adapter.getFileLocation(req.config, filename); + file._url = await filesController.adapter.getFileLocation( + req.config, + filename + ); const fileObject = { file, fileSize: null }; await triggers.maybeRunFileTrigger( triggers.Types.beforeDelete, @@ -321,10 +362,10 @@ export class FilesRouter { // TODO: return useful JSON here? res.end(); } catch (e) { - logger.error('Error deleting a file: ', e); + logger.error("Error deleting a file: ", e); const error = triggers.resolveError(e, { code: Parse.Error.FILE_DELETE_ERROR, - message: 'Could not delete file.', + message: "Could not delete file.", }); next(error); } @@ -346,10 +387,11 @@ export class FilesRouter { } function isFileStreamable(req, filesController) { - const range = (req.get('Range') || '/-/').split('-'); + const range = (req.get("Range") || "/-/").split("-"); const start = Number(range[0]); const end = Number(range[1]); return ( - (!isNaN(start) || !isNaN(end)) && typeof filesController.adapter.handleFileStream === 'function' + (!isNaN(start) || !isNaN(end)) && + typeof filesController.adapter.handleFileStream === "function" ); } diff --git a/src/Routers/FunctionsRouter.js b/src/Routers/FunctionsRouter.js index 4c90ac2810..f0518524a0 100644 --- a/src/Routers/FunctionsRouter.js +++ b/src/Routers/FunctionsRouter.js @@ -1,30 +1,37 @@ // FunctionsRouter.js -var Parse = require('parse/node').Parse, - triggers = require('../triggers'); +var Parse = require("parse/node").Parse, + triggers = require("../triggers"); -import PromiseRouter from '../PromiseRouter'; -import { promiseEnforceMasterKeyAccess, promiseEnsureIdempotency } from '../middlewares'; -import { jobStatusHandler } from '../StatusHandler'; -import _ from 'lodash'; -import { logger } from '../logger'; +import PromiseRouter from "../PromiseRouter"; +import { + promiseEnforceMasterKeyAccess, + promiseEnsureIdempotency, +} from "../middlewares"; +import { jobStatusHandler } from "../StatusHandler"; +import _ from "lodash"; +import { logger } from "../logger"; function parseObject(obj, config) { if (Array.isArray(obj)) { return obj.map(item => { return parseObject(item, config); }); - } else if (obj && obj.__type == 'Date') { + } else if (obj && obj.__type == "Date") { return Object.assign(new Date(obj.iso), obj); - } else if (obj && obj.__type == 'File') { + } else if (obj && obj.__type == "File") { return Parse.File.fromJSON(obj); - } else if (obj && obj.__type == 'Pointer' && config.encodeParseObjectInCloudFunction) { + } else if ( + obj && + obj.__type == "Pointer" && + config.encodeParseObjectInCloudFunction + ) { return Parse.Object.fromJSON({ - __type: 'Pointer', + __type: "Pointer", className: obj.className, objectId: obj.objectId, }); - } else if (obj && typeof obj === 'object') { + } else if (obj && typeof obj === "object") { return parseParams(obj, config); } else { return obj; @@ -38,21 +45,21 @@ function parseParams(params, config) { export class FunctionsRouter extends PromiseRouter { mountRoutes() { this.route( - 'POST', - '/functions/:functionName', + "POST", + "/functions/:functionName", promiseEnsureIdempotency, FunctionsRouter.handleCloudFunction ); this.route( - 'POST', - '/jobs/:jobName', + "POST", + "/jobs/:jobName", promiseEnsureIdempotency, promiseEnforceMasterKeyAccess, function (req) { return FunctionsRouter.handleCloudJob(req); } ); - this.route('POST', '/jobs', promiseEnforceMasterKeyAccess, function (req) { + this.route("POST", "/jobs", promiseEnforceMasterKeyAccess, function (req) { return FunctionsRouter.handleCloudJob(req); }); } @@ -63,7 +70,7 @@ export class FunctionsRouter extends PromiseRouter { const jobHandler = jobStatusHandler(req.config); const jobFunction = triggers.getJob(jobName, applicationId); if (!jobFunction) { - throw new Parse.Error(Parse.Error.SCRIPT_FAILED, 'Invalid job.'); + throw new Parse.Error(Parse.Error.SCRIPT_FAILED, "Invalid job."); } let params = Object.assign({}, req.body, req.query); params = parseParams(params, req.config); @@ -95,7 +102,7 @@ export class FunctionsRouter extends PromiseRouter { }); return { headers: { - 'X-Parse-Job-Status-Id': jobStatus.objectId, + "X-Parse-Job-Status-Id": jobStatus.objectId, }, response: {}, }; @@ -123,7 +130,10 @@ export class FunctionsRouter extends PromiseRouter { const theFunction = triggers.getFunction(functionName, applicationId); if (!theFunction) { - throw new Parse.Error(Parse.Error.SCRIPT_FAILED, `Invalid function: "${functionName}"`); + throw new Parse.Error( + Parse.Error.SCRIPT_FAILED, + `Invalid function: "${functionName}"` + ); } let params = Object.assign({}, req.body, req.query); params = parseParams(params, req.config); @@ -140,13 +150,18 @@ export class FunctionsRouter extends PromiseRouter { }; return new Promise(function (resolve, reject) { - const userString = req.auth && req.auth.user ? req.auth.user.id : undefined; + const userString = + req.auth && req.auth.user ? req.auth.user.id : undefined; const { success, error } = FunctionsRouter.createResponseObject( result => { try { - if (req.config.logLevels.cloudFunctionSuccess !== 'silent') { - const cleanInput = logger.truncateLogMessage(JSON.stringify(params)); - const cleanResult = logger.truncateLogMessage(JSON.stringify(result.response.result)); + if (req.config.logLevels.cloudFunctionSuccess !== "silent") { + const cleanInput = logger.truncateLogMessage( + JSON.stringify(params) + ); + const cleanResult = logger.truncateLogMessage( + JSON.stringify(result.response.result) + ); logger[req.config.logLevels.cloudFunctionSuccess]( `Ran cloud function ${functionName} for user ${userString} with:\n Input: ${cleanInput}\n Result: ${cleanResult}`, { @@ -163,8 +178,10 @@ export class FunctionsRouter extends PromiseRouter { }, error => { try { - if (req.config.logLevels.cloudFunctionError !== 'silent') { - const cleanInput = logger.truncateLogMessage(JSON.stringify(params)); + if (req.config.logLevels.cloudFunctionError !== "silent") { + const cleanInput = logger.truncateLogMessage( + JSON.stringify(params) + ); logger[req.config.logLevels.cloudFunctionError]( `Failed running cloud function ${functionName} for user ${userString} with:\n Input: ${cleanInput}\n Error: ` + JSON.stringify(error), diff --git a/src/Routers/GlobalConfigRouter.js b/src/Routers/GlobalConfigRouter.js index 31d38c8569..06895720ad 100644 --- a/src/Routers/GlobalConfigRouter.js +++ b/src/Routers/GlobalConfigRouter.js @@ -1,8 +1,8 @@ // global_config.js -import Parse from 'parse/node'; -import PromiseRouter from '../PromiseRouter'; -import * as middleware from '../middlewares'; -import * as triggers from '../triggers'; +import Parse from "parse/node"; +import PromiseRouter from "../PromiseRouter"; +import * as middleware from "../middlewares"; +import * as triggers from "../triggers"; const getConfigFromParams = params => { const config = new Parse.Config(); @@ -15,7 +15,7 @@ const getConfigFromParams = params => { export class GlobalConfigRouter extends PromiseRouter { getGlobalConfig(req) { return req.config.database - .find('_GlobalConfig', { objectId: '1' }, { limit: 1 }) + .find("_GlobalConfig", { objectId: "1" }, { limit: 1 }) .then(results => { if (results.length != 1) { // If there is no config in the database - return empty config. @@ -71,8 +71,8 @@ export class GlobalConfigRouter extends PromiseRouter { configObject.attributes = params; const results = await req.config.database.find( - '_GlobalConfig', - { objectId: '1' }, + "_GlobalConfig", + { objectId: "1" }, { limit: 1 } ); const isNew = results.length !== 1; @@ -90,8 +90,8 @@ export class GlobalConfigRouter extends PromiseRouter { ); if (isNew) { await req.config.database.update( - '_GlobalConfig', - { objectId: '1' }, + "_GlobalConfig", + { objectId: "1" }, update, { upsert: true }, true @@ -99,8 +99,8 @@ export class GlobalConfigRouter extends PromiseRouter { updatedConfigObject = configObject; } else { const result = await req.config.database.update( - '_GlobalConfig', - { objectId: '1' }, + "_GlobalConfig", + { objectId: "1" }, update, {}, true @@ -119,19 +119,24 @@ export class GlobalConfigRouter extends PromiseRouter { } catch (err) { const error = triggers.resolveError(err, { code: Parse.Error.SCRIPT_FAILED, - message: 'Script failed. Unknown error.', + message: "Script failed. Unknown error.", }); throw error; } } mountRoutes() { - this.route('GET', '/config', req => { + this.route("GET", "/config", req => { return this.getGlobalConfig(req); }); - this.route('PUT', '/config', middleware.promiseEnforceMasterKeyAccess, req => { - return this.updateGlobalConfig(req); - }); + this.route( + "PUT", + "/config", + middleware.promiseEnforceMasterKeyAccess, + req => { + return this.updateGlobalConfig(req); + } + ); } } diff --git a/src/Routers/GraphQLRouter.js b/src/Routers/GraphQLRouter.js index 71ca8b3ce8..71624a8b83 100644 --- a/src/Routers/GraphQLRouter.js +++ b/src/Routers/GraphQLRouter.js @@ -1,8 +1,8 @@ -import Parse from 'parse/node'; -import PromiseRouter from '../PromiseRouter'; -import * as middleware from '../middlewares'; +import Parse from "parse/node"; +import PromiseRouter from "../PromiseRouter"; +import * as middleware from "../middlewares"; -const GraphQLConfigPath = '/graphql-config'; +const GraphQLConfigPath = "/graphql-config"; export class GraphQLRouter extends PromiseRouter { async getGraphQLConfig(req) { @@ -28,12 +28,22 @@ export class GraphQLRouter extends PromiseRouter { } mountRoutes() { - this.route('GET', GraphQLConfigPath, middleware.promiseEnforceMasterKeyAccess, req => { - return this.getGraphQLConfig(req); - }); - this.route('PUT', GraphQLConfigPath, middleware.promiseEnforceMasterKeyAccess, req => { - return this.updateGraphQLConfig(req); - }); + this.route( + "GET", + GraphQLConfigPath, + middleware.promiseEnforceMasterKeyAccess, + req => { + return this.getGraphQLConfig(req); + } + ); + this.route( + "PUT", + GraphQLConfigPath, + middleware.promiseEnforceMasterKeyAccess, + req => { + return this.updateGraphQLConfig(req); + } + ); } } diff --git a/src/Routers/HooksRouter.js b/src/Routers/HooksRouter.js index 104ef799c2..14157d11e4 100644 --- a/src/Routers/HooksRouter.js +++ b/src/Routers/HooksRouter.js @@ -1,14 +1,18 @@ -import { Parse } from 'parse/node'; -import PromiseRouter from '../PromiseRouter'; -import * as middleware from '../middlewares'; +import { Parse } from "parse/node"; +import PromiseRouter from "../PromiseRouter"; +import * as middleware from "../middlewares"; export class HooksRouter extends PromiseRouter { createHook(aHook, config) { - return config.hooksController.createHook(aHook).then(hook => ({ response: hook })); + return config.hooksController + .createHook(aHook) + .then(hook => ({ response: hook })); } updateHook(aHook, config) { - return config.hooksController.updateHook(aHook).then(hook => ({ response: hook })); + return config.hooksController + .updateHook(aHook) + .then(hook => ({ response: hook })); } handlePost(req) { @@ -18,12 +22,17 @@ export class HooksRouter extends PromiseRouter { handleGetFunctions(req) { var hooksController = req.config.hooksController; if (req.params.functionName) { - return hooksController.getFunction(req.params.functionName).then(foundFunction => { - if (!foundFunction) { - throw new Parse.Error(143, `no function named: ${req.params.functionName} is defined`); - } - return Promise.resolve({ response: foundFunction }); - }); + return hooksController + .getFunction(req.params.functionName) + .then(foundFunction => { + if (!foundFunction) { + throw new Parse.Error( + 143, + `no function named: ${req.params.functionName} is defined` + ); + } + return Promise.resolve({ response: foundFunction }); + }); } return hooksController.getFunctions().then( @@ -43,19 +52,26 @@ export class HooksRouter extends PromiseRouter { .getTrigger(req.params.className, req.params.triggerName) .then(foundTrigger => { if (!foundTrigger) { - throw new Parse.Error(143, `class ${req.params.className} does not exist`); + throw new Parse.Error( + 143, + `class ${req.params.className} does not exist` + ); } return Promise.resolve({ response: foundTrigger }); }); } - return hooksController.getTriggers().then(triggers => ({ response: triggers || [] })); + return hooksController + .getTriggers() + .then(triggers => ({ response: triggers || [] })); } handleDelete(req) { var hooksController = req.config.hooksController; if (req.params.functionName) { - return hooksController.deleteFunction(req.params.functionName).then(() => ({ response: {} })); + return hooksController + .deleteFunction(req.params.functionName) + .then(() => ({ response: {} })); } else if (req.params.className && req.params.triggerName) { return hooksController .deleteTrigger(req.params.className, req.params.triggerName) @@ -70,20 +86,24 @@ export class HooksRouter extends PromiseRouter { hook = {}; hook.functionName = req.params.functionName; hook.url = req.body.url; - } else if (req.params.className && req.params.triggerName && req.body?.url) { + } else if ( + req.params.className && + req.params.triggerName && + req.body?.url + ) { hook = {}; hook.className = req.params.className; hook.triggerName = req.params.triggerName; hook.url = req.body.url; } else { - throw new Parse.Error(143, 'invalid hook declaration'); + throw new Parse.Error(143, "invalid hook declaration"); } return this.updateHook(hook, req.config); } handlePut(req) { var body = req.body || {}; - if (body.__op == 'Delete') { + if (body.__op == "Delete") { return this.handleDelete(req); } else { return this.handleUpdate(req); @@ -92,50 +112,50 @@ export class HooksRouter extends PromiseRouter { mountRoutes() { this.route( - 'GET', - '/hooks/functions', + "GET", + "/hooks/functions", middleware.promiseEnforceMasterKeyAccess, this.handleGetFunctions.bind(this) ); this.route( - 'GET', - '/hooks/triggers', + "GET", + "/hooks/triggers", middleware.promiseEnforceMasterKeyAccess, this.handleGetTriggers.bind(this) ); this.route( - 'GET', - '/hooks/functions/:functionName', + "GET", + "/hooks/functions/:functionName", middleware.promiseEnforceMasterKeyAccess, this.handleGetFunctions.bind(this) ); this.route( - 'GET', - '/hooks/triggers/:className/:triggerName', + "GET", + "/hooks/triggers/:className/:triggerName", middleware.promiseEnforceMasterKeyAccess, this.handleGetTriggers.bind(this) ); this.route( - 'POST', - '/hooks/functions', + "POST", + "/hooks/functions", middleware.promiseEnforceMasterKeyAccess, this.handlePost.bind(this) ); this.route( - 'POST', - '/hooks/triggers', + "POST", + "/hooks/triggers", middleware.promiseEnforceMasterKeyAccess, this.handlePost.bind(this) ); this.route( - 'PUT', - '/hooks/functions/:functionName', + "PUT", + "/hooks/functions/:functionName", middleware.promiseEnforceMasterKeyAccess, this.handlePut.bind(this) ); this.route( - 'PUT', - '/hooks/triggers/:className/:triggerName', + "PUT", + "/hooks/triggers/:className/:triggerName", middleware.promiseEnforceMasterKeyAccess, this.handlePut.bind(this) ); diff --git a/src/Routers/IAPValidationRouter.js b/src/Routers/IAPValidationRouter.js index 17eae685d7..ae3aef827c 100644 --- a/src/Routers/IAPValidationRouter.js +++ b/src/Routers/IAPValidationRouter.js @@ -1,39 +1,39 @@ -import PromiseRouter from '../PromiseRouter'; -const request = require('../request'); -const rest = require('../rest'); -import Parse from 'parse/node'; +import PromiseRouter from "../PromiseRouter"; +const request = require("../request"); +const rest = require("../rest"); +import Parse from "parse/node"; // TODO move validation logic in IAPValidationController -const IAP_SANDBOX_URL = 'https://sandbox.itunes.apple.com/verifyReceipt'; -const IAP_PRODUCTION_URL = 'https://buy.itunes.apple.com/verifyReceipt'; +const IAP_SANDBOX_URL = "https://sandbox.itunes.apple.com/verifyReceipt"; +const IAP_PRODUCTION_URL = "https://buy.itunes.apple.com/verifyReceipt"; const APP_STORE_ERRORS = { - 21000: 'The App Store could not read the JSON object you provided.', - 21002: 'The data in the receipt-data property was malformed or missing.', - 21003: 'The receipt could not be authenticated.', + 21000: "The App Store could not read the JSON object you provided.", + 21002: "The data in the receipt-data property was malformed or missing.", + 21003: "The receipt could not be authenticated.", 21004: - 'The shared secret you provided does not match the shared secret on file for your account.', - 21005: 'The receipt server is not currently available.', - 21006: 'This receipt is valid but the subscription has expired.', + "The shared secret you provided does not match the shared secret on file for your account.", + 21005: "The receipt server is not currently available.", + 21006: "This receipt is valid but the subscription has expired.", 21007: - 'This receipt is from the test environment, but it was sent to the production environment for verification. Send it to the test environment instead.', + "This receipt is from the test environment, but it was sent to the production environment for verification. Send it to the test environment instead.", 21008: - 'This receipt is from the production environment, but it was sent to the test environment for verification. Send it to the production environment instead.', + "This receipt is from the production environment, but it was sent to the test environment for verification. Send it to the production environment instead.", }; function appStoreError(status) { status = parseInt(status); - var errorString = APP_STORE_ERRORS[status] || 'unknown error.'; + var errorString = APP_STORE_ERRORS[status] || "unknown error."; return { status: status, error: errorString }; } function validateWithAppStore(url, receipt) { return request({ url: url, - method: 'POST', - body: { 'receipt-data': receipt }, + method: "POST", + body: { "receipt-data": receipt }, headers: { - 'Content-Type': 'application/json', + "Content-Type": "application/json", }, }).then(httpResponse => { const body = httpResponse.data; @@ -51,7 +51,7 @@ function getFileForProductIdentifier(productIdentifier, req) { .find( req.config, req.auth, - '_Product', + "_Product", { productIdentifier: productIdentifier }, undefined, req.info.clientSDK, @@ -61,7 +61,10 @@ function getFileForProductIdentifier(productIdentifier, req) { const products = result.results; if (!products || products.length != 1) { // Error not found or too many - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.'); + throw new Parse.Error( + Parse.Error.OBJECT_NOT_FOUND, + "Object not found." + ); } var download = products[0].download; @@ -76,18 +79,21 @@ export class IAPValidationRouter extends PromiseRouter { if (!receipt || !productIdentifier) { // TODO: Error, malformed request - throw new Parse.Error(Parse.Error.INVALID_JSON, 'missing receipt or productIdentifier'); + throw new Parse.Error( + Parse.Error.INVALID_JSON, + "missing receipt or productIdentifier" + ); } // Transform the object if there // otherwise assume it's in Base64 already - if (typeof receipt == 'object') { - if (receipt['__type'] == 'Bytes') { + if (typeof receipt == "object") { + if (receipt["__type"] == "Bytes") { receipt = receipt.base64; } } - if (process.env.TESTING == '1' && req.body?.bypassAppStoreValidation) { + if (process.env.TESTING == "1" && req.body?.bypassAppStoreValidation) { return getFileForProductIdentifier(productIdentifier, req); } @@ -121,6 +127,6 @@ export class IAPValidationRouter extends PromiseRouter { } mountRoutes() { - this.route('POST', '/validate_purchase', this.handleRequest); + this.route("POST", "/validate_purchase", this.handleRequest); } } diff --git a/src/Routers/InstallationsRouter.js b/src/Routers/InstallationsRouter.js index 7142d0fe5c..493fee9a8b 100644 --- a/src/Routers/InstallationsRouter.js +++ b/src/Routers/InstallationsRouter.js @@ -1,22 +1,28 @@ // InstallationsRouter.js -import ClassesRouter from './ClassesRouter'; -import rest from '../rest'; -import { promiseEnsureIdempotency } from '../middlewares'; +import ClassesRouter from "./ClassesRouter"; +import rest from "../rest"; +import { promiseEnsureIdempotency } from "../middlewares"; export class InstallationsRouter extends ClassesRouter { className() { - return '_Installation'; + return "_Installation"; } handleFind(req) { - const body = Object.assign(req.body || {}, ClassesRouter.JSONFromQuery(req.query)); - const options = ClassesRouter.optionsFromBody(body, req.config.defaultLimit); + const body = Object.assign( + req.body || {}, + ClassesRouter.JSONFromQuery(req.query) + ); + const options = ClassesRouter.optionsFromBody( + body, + req.config.defaultLimit + ); return rest .find( req.config, req.auth, - '_Installation', + "_Installation", body.where, options, req.info.clientSDK, @@ -28,19 +34,24 @@ export class InstallationsRouter extends ClassesRouter { } mountRoutes() { - this.route('GET', '/installations', req => { + this.route("GET", "/installations", req => { return this.handleFind(req); }); - this.route('GET', '/installations/:objectId', req => { + this.route("GET", "/installations/:objectId", req => { return this.handleGet(req); }); - this.route('POST', '/installations', promiseEnsureIdempotency, req => { + this.route("POST", "/installations", promiseEnsureIdempotency, req => { return this.handleCreate(req); }); - this.route('PUT', '/installations/:objectId', promiseEnsureIdempotency, req => { - return this.handleUpdate(req); - }); - this.route('DELETE', '/installations/:objectId', req => { + this.route( + "PUT", + "/installations/:objectId", + promiseEnsureIdempotency, + req => { + return this.handleUpdate(req); + } + ); + this.route("DELETE", "/installations/:objectId", req => { return this.handleDelete(req); }); } diff --git a/src/Routers/LogsRouter.js b/src/Routers/LogsRouter.js index 182a4f1669..aebb9de0a7 100644 --- a/src/Routers/LogsRouter.js +++ b/src/Routers/LogsRouter.js @@ -1,12 +1,12 @@ -import { Parse } from 'parse/node'; -import PromiseRouter from '../PromiseRouter'; -import * as middleware from '../middlewares'; +import { Parse } from "parse/node"; +import PromiseRouter from "../PromiseRouter"; +import * as middleware from "../middlewares"; export class LogsRouter extends PromiseRouter { mountRoutes() { this.route( - 'GET', - '/scriptlog', + "GET", + "/scriptlog", middleware.promiseEnforceMasterKeyAccess, this.validateRequest, req => { @@ -17,7 +17,10 @@ export class LogsRouter extends PromiseRouter { validateRequest(req) { if (!req.config || !req.config.loggerController) { - throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED, 'Logger adapter is not available'); + throw new Parse.Error( + Parse.Error.PUSH_MISCONFIGURED, + "Logger adapter is not available" + ); } } diff --git a/src/Routers/PagesRouter.js b/src/Routers/PagesRouter.js index 1ea3211684..3ba78b0b4d 100644 --- a/src/Routers/PagesRouter.js +++ b/src/Routers/PagesRouter.js @@ -1,64 +1,68 @@ -import PromiseRouter from '../PromiseRouter'; -import Config from '../Config'; -import express from 'express'; -import path from 'path'; -import { promises as fs } from 'fs'; -import { Parse } from 'parse/node'; -import Utils from '../Utils'; -import mustache from 'mustache'; -import Page from '../Page'; +import PromiseRouter from "../PromiseRouter"; +import Config from "../Config"; +import express from "express"; +import path from "path"; +import { promises as fs } from "fs"; +import { Parse } from "parse/node"; +import Utils from "../Utils"; +import mustache from "mustache"; +import Page from "../Page"; // All pages with custom page key for reference and file name const pages = Object.freeze({ - passwordReset: new Page({ id: 'passwordReset', defaultFile: 'password_reset.html' }), + passwordReset: new Page({ + id: "passwordReset", + defaultFile: "password_reset.html", + }), passwordResetSuccess: new Page({ - id: 'passwordResetSuccess', - defaultFile: 'password_reset_success.html', + id: "passwordResetSuccess", + defaultFile: "password_reset_success.html", }), passwordResetLinkInvalid: new Page({ - id: 'passwordResetLinkInvalid', - defaultFile: 'password_reset_link_invalid.html', + id: "passwordResetLinkInvalid", + defaultFile: "password_reset_link_invalid.html", }), emailVerificationSuccess: new Page({ - id: 'emailVerificationSuccess', - defaultFile: 'email_verification_success.html', + id: "emailVerificationSuccess", + defaultFile: "email_verification_success.html", }), emailVerificationSendFail: new Page({ - id: 'emailVerificationSendFail', - defaultFile: 'email_verification_send_fail.html', + id: "emailVerificationSendFail", + defaultFile: "email_verification_send_fail.html", }), emailVerificationSendSuccess: new Page({ - id: 'emailVerificationSendSuccess', - defaultFile: 'email_verification_send_success.html', + id: "emailVerificationSendSuccess", + defaultFile: "email_verification_send_success.html", }), emailVerificationLinkInvalid: new Page({ - id: 'emailVerificationLinkInvalid', - defaultFile: 'email_verification_link_invalid.html', + id: "emailVerificationLinkInvalid", + defaultFile: "email_verification_link_invalid.html", }), emailVerificationLinkExpired: new Page({ - id: 'emailVerificationLinkExpired', - defaultFile: 'email_verification_link_expired.html', + id: "emailVerificationLinkExpired", + defaultFile: "email_verification_link_expired.html", }), }); // All page parameters for reference to be used as template placeholders or query params const pageParams = Object.freeze({ - appName: 'appName', - appId: 'appId', - token: 'token', - username: 'username', - error: 'error', - locale: 'locale', - publicServerUrl: 'publicServerUrl', + appName: "appName", + appId: "appId", + token: "token", + username: "username", + error: "error", + locale: "locale", + publicServerUrl: "publicServerUrl", }); // The header prefix to add page params as response headers -const pageParamHeaderPrefix = 'x-parse-page-param-'; +const pageParamHeaderPrefix = "x-parse-page-param-"; // The errors being thrown const errors = Object.freeze({ - jsonFailedFileLoading: 'failed to load JSON file', - fileOutsideAllowedScope: 'not allowed to read file outside of pages directory', + jsonFailedFileLoading: "failed to load JSON file", + fileOutsideAllowedScope: + "not allowed to read file outside of pages directory", }); export class PagesRouter extends PromiseRouter { @@ -71,10 +75,10 @@ export class PagesRouter extends PromiseRouter { // Set instance properties this.pagesConfig = pages; - this.pagesEndpoint = pages.pagesEndpoint ? pages.pagesEndpoint : 'apps'; + this.pagesEndpoint = pages.pagesEndpoint ? pages.pagesEndpoint : "apps"; this.pagesPath = pages.pagesPath - ? path.resolve('./', pages.pagesPath) - : path.resolve(__dirname, '../../public'); + ? path.resolve("./", pages.pagesPath) + : path.resolve(__dirname, "../../public"); this.loadJsonResource(); this.mountPagesRoutes(); this.mountCustomRoutes(); @@ -84,7 +88,8 @@ export class PagesRouter extends PromiseRouter { verifyEmail(req) { const config = req.config; const { token: rawToken } = req.query; - const token = rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken; + const token = + rawToken && typeof rawToken !== "string" ? rawToken.toString() : rawToken; if (!config) { this.invalidRequest(); @@ -150,7 +155,8 @@ export class PagesRouter extends PromiseRouter { } const { token: rawToken } = req.query; - const token = rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken; + const token = + rawToken && typeof rawToken !== "string" ? rawToken.toString() : rawToken; if (!token) { return this.goToPage(req, pages.passwordResetLinkInvalid); @@ -179,18 +185,19 @@ export class PagesRouter extends PromiseRouter { } const { new_password, token: rawToken } = req.body || {}; - const token = rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken; + const token = + rawToken && typeof rawToken !== "string" ? rawToken.toString() : rawToken; if ((!token || !new_password) && req.xhr === false) { return this.goToPage(req, pages.passwordResetLinkInvalid); } if (!token) { - throw new Parse.Error(Parse.Error.OTHER_CAUSE, 'Missing token'); + throw new Parse.Error(Parse.Error.OTHER_CAUSE, "Missing token"); } if (!new_password) { - throw new Parse.Error(Parse.Error.PASSWORD_MISSING, 'Missing password'); + throw new Parse.Error(Parse.Error.PASSWORD_MISSING, "Missing password"); } return config.userController @@ -213,7 +220,7 @@ export class PagesRouter extends PromiseRouter { if (result.success) { return Promise.resolve({ status: 200, - response: 'Password successfully reset', + response: "Password successfully reset", }); } if (result.err) { @@ -224,17 +231,19 @@ export class PagesRouter extends PromiseRouter { const query = result.success ? {} : { - [pageParams.token]: token, - [pageParams.appId]: config.applicationId, - [pageParams.error]: result.err, - [pageParams.appName]: config.appName, - }; + [pageParams.token]: token, + [pageParams.appId]: config.applicationId, + [pageParams.error]: result.err, + [pageParams.appName]: config.appName, + }; - if (result?.err === 'The password reset link has expired') { + if (result?.err === "The password reset link has expired") { delete query[pageParams.token]; query[pageParams.token] = token; } - const page = result.success ? pages.passwordResetSuccess : pages.passwordReset; + const page = result.success + ? pages.passwordResetSuccess + : pages.passwordReset; return this.goToPage(req, page, query, false); }); @@ -263,7 +272,7 @@ export class PagesRouter extends PromiseRouter { ? true : responseType !== undefined ? responseType - : req.method == 'POST'; + : req.method == "POST"; // Include default parameters const defaultParams = this.getDefaultParams(config); @@ -297,13 +306,18 @@ export class PagesRouter extends PromiseRouter { // Send response if (config.pages.enableLocalization && locale) { - return Utils.getLocalizedPath(defaultPath, locale).then(({ path, subdir }) => - redirect - ? this.redirectResponse( - this.composePageUrl(defaultFile, config.publicServerURL, subdir), - params - ) - : this.pageResponse(path, params, placeholders) + return Utils.getLocalizedPath(defaultPath, locale).then( + ({ path, subdir }) => + redirect + ? this.redirectResponse( + this.composePageUrl( + defaultFile, + config.publicServerURL, + subdir + ), + params + ) + : this.pageResponse(path, params, placeholders) ); } else { return redirect @@ -320,13 +334,13 @@ export class PagesRouter extends PromiseRouter { */ staticRoute(req) { // Get requested path - const relativePath = req.params['resource'][0]; + const relativePath = req.params["resource"][0]; // Resolve requested path to absolute path const absolutePath = path.resolve(this.pagesPath, relativePath); // If the requested file is not a HTML file send its raw content - if (!absolutePath || !absolutePath.endsWith('.html')) { + if (!absolutePath || !absolutePath.endsWith(".html")) { return this.fileResponse(absolutePath); } @@ -381,7 +395,7 @@ export class PagesRouter extends PromiseRouter { locale = locale || this.pagesConfig.localizationFallbackLocale; // Get matching translation by locale, language or fallback locale - const language = locale.split('-')[0]; + const language = locale.split("-")[0]; const resource = this.jsonParameters[locale] || this.jsonParameters[language] || @@ -402,7 +416,10 @@ export class PagesRouter extends PromiseRouter { */ getJsonPlaceholders(locale, params = {}) { // If localization is disabled or there is no JSON resource - if (!this.pagesConfig.enableLocalization || !this.pagesConfig.localizationJsonPath) { + if ( + !this.pagesConfig.enableLocalization || + !this.pagesConfig.localizationJsonPath + ) { return {}; } @@ -438,9 +455,10 @@ export class PagesRouter extends PromiseRouter { // Get config placeholders; can be an object, a function or an async function let configPlaceholders = - typeof this.pagesConfig.placeholders === 'function' + typeof this.pagesConfig.placeholders === "function" ? this.pagesConfig.placeholders(params) - : Object.prototype.toString.call(this.pagesConfig.placeholders) === '[object Object]' + : Object.prototype.toString.call(this.pagesConfig.placeholders) === + "[object Object]" ? this.pagesConfig.placeholders : {}; if (configPlaceholders instanceof Promise) { @@ -504,7 +522,7 @@ export class PagesRouter extends PromiseRouter { throw errors.fileOutsideAllowedScope; } - return await fs.readFile(normalizedPath, 'utf-8'); + return await fs.readFile(normalizedPath, "utf-8"); } /** @@ -515,7 +533,9 @@ export class PagesRouter extends PromiseRouter { return; } try { - const json = require(path.resolve('./', this.pagesConfig.localizationJsonPath)); + const json = require( + path.resolve("./", this.pagesConfig.localizationJsonPath) + ); this.jsonParameters = json; } catch (e) { throw errors.jsonFailedFileLoading; @@ -532,10 +552,10 @@ export class PagesRouter extends PromiseRouter { getDefaultParams(config) { return config ? { - [pageParams.appId]: config.appId, - [pageParams.appName]: config.appName, - [pageParams.publicServerUrl]: config.publicServerURL, - } + [pageParams.appId]: config.appId, + [pageParams.appName]: config.appName, + [pageParams.publicServerUrl]: config.publicServerURL, + } : {}; } @@ -596,16 +616,16 @@ export class PagesRouter extends PromiseRouter { composePageUrl(file, publicServerUrl, locale) { let url = publicServerUrl; - url += url.endsWith('/') ? '' : '/'; - url += this.pagesEndpoint + '/'; - url += locale === undefined ? '' : locale + '/'; + url += url.endsWith("/") ? "" : "/"; + url += this.pagesEndpoint + "/"; + url += locale === undefined ? "" : locale + "/"; url += file; return url; } notFound() { return { - text: 'Not found.', + text: "Not found.", status: 404, }; } @@ -613,7 +633,7 @@ export class PagesRouter extends PromiseRouter { invalidRequest() { const error = new Error(); error.status = 403; - error.message = 'unauthorized'; + error.message = "unauthorized"; throw error; } @@ -634,7 +654,7 @@ export class PagesRouter extends PromiseRouter { mountPagesRoutes() { this.route( - 'GET', + "GET", `/${this.pagesEndpoint}/:appId/verify_email`, req => { this.setConfig(req); @@ -645,7 +665,7 @@ export class PagesRouter extends PromiseRouter { ); this.route( - 'POST', + "POST", `/${this.pagesEndpoint}/:appId/resend_verification_email`, req => { this.setConfig(req); @@ -656,7 +676,7 @@ export class PagesRouter extends PromiseRouter { ); this.route( - 'GET', + "GET", `/${this.pagesEndpoint}/choose_password`, req => { this.setConfig(req); @@ -667,7 +687,7 @@ export class PagesRouter extends PromiseRouter { ); this.route( - 'POST', + "POST", `/${this.pagesEndpoint}/:appId/request_password_reset`, req => { this.setConfig(req); @@ -678,7 +698,7 @@ export class PagesRouter extends PromiseRouter { ); this.route( - 'GET', + "GET", `/${this.pagesEndpoint}/:appId/request_password_reset`, req => { this.setConfig(req); @@ -715,7 +735,7 @@ export class PagesRouter extends PromiseRouter { mountStaticRoute() { this.route( - 'GET', + "GET", `/${this.pagesEndpoint}/*resource`, req => { this.setConfig(req, true); @@ -728,7 +748,7 @@ export class PagesRouter extends PromiseRouter { expressRouter() { const router = express.Router(); - router.use('/', super.expressRouter()); + router.use("/", super.expressRouter()); return router; } } diff --git a/src/Routers/PublicAPIRouter.js b/src/Routers/PublicAPIRouter.js index 98da1c4542..f6b79314ae 100644 --- a/src/Routers/PublicAPIRouter.js +++ b/src/Routers/PublicAPIRouter.js @@ -1,26 +1,27 @@ -import PromiseRouter from '../PromiseRouter'; -import Config from '../Config'; -import express from 'express'; -import path from 'path'; -import fs from 'fs'; -import qs from 'querystring'; -import { Parse } from 'parse/node'; -import Deprecator from '../Deprecator/Deprecator'; - -const public_html = path.resolve(__dirname, '../../public_html'); -const views = path.resolve(__dirname, '../../views'); +import PromiseRouter from "../PromiseRouter"; +import Config from "../Config"; +import express from "express"; +import path from "path"; +import fs from "fs"; +import qs from "querystring"; +import { Parse } from "parse/node"; +import Deprecator from "../Deprecator/Deprecator"; + +const public_html = path.resolve(__dirname, "../../public_html"); +const views = path.resolve(__dirname, "../../views"); export class PublicAPIRouter extends PromiseRouter { constructor() { super(); Deprecator.logRuntimeDeprecation({ - usage: 'PublicAPIRouter', - solution: 'pages.enableRouter', + usage: "PublicAPIRouter", + solution: "pages.enableRouter", }); } verifyEmail(req) { const { token: rawToken } = req.query; - const token = rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken; + const token = + rawToken && typeof rawToken !== "string" ? rawToken.toString() : rawToken; const appId = req.params.appId; const config = Config.get(appId); @@ -99,19 +100,26 @@ export class PublicAPIRouter extends PromiseRouter { if (!config.publicServerURL) { return resolve({ status: 404, - text: 'Not found.', + text: "Not found.", }); } // Should we keep the file in memory or leave like that? - fs.readFile(path.resolve(views, 'choose_password'), 'utf-8', (err, data) => { - if (err) { - return reject(err); + fs.readFile( + path.resolve(views, "choose_password"), + "utf-8", + (err, data) => { + if (err) { + return reject(err); + } + data = data.replace( + "PARSE_SERVER_URL", + `'${config.publicServerURL}'` + ); + resolve({ + text: data, + }); } - data = data.replace('PARSE_SERVER_URL', `'${config.publicServerURL}'`); - resolve({ - text: data, - }); - }); + ); }); } @@ -127,7 +135,8 @@ export class PublicAPIRouter extends PromiseRouter { } const { token: rawToken } = req.query; - const token = rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken; + const token = + rawToken && typeof rawToken !== "string" ? rawToken.toString() : rawToken; if (!token) { return this.invalidLink(req); @@ -163,18 +172,19 @@ export class PublicAPIRouter extends PromiseRouter { } const { new_password, token: rawToken } = req.body || {}; - const token = rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken; + const token = + rawToken && typeof rawToken !== "string" ? rawToken.toString() : rawToken; if ((!token || !new_password) && req.xhr === false) { return this.invalidLink(req); } if (!token) { - throw new Parse.Error(Parse.Error.OTHER_CAUSE, 'Missing token'); + throw new Parse.Error(Parse.Error.OTHER_CAUSE, "Missing token"); } if (!new_password) { - throw new Parse.Error(Parse.Error.PASSWORD_MISSING, 'Missing password'); + throw new Parse.Error(Parse.Error.PASSWORD_MISSING, "Missing password"); } return config.userController @@ -200,7 +210,7 @@ export class PublicAPIRouter extends PromiseRouter { app: config.appName, }; - if (result?.err === 'The password reset link has expired') { + if (result?.err === "The password reset link has expired") { delete queryString.token; queryString.token = token; } @@ -210,7 +220,7 @@ export class PublicAPIRouter extends PromiseRouter { if (result.success) { return Promise.resolve({ status: 200, - response: 'Password successfully reset', + response: "Password successfully reset", }); } if (result.err) { @@ -254,7 +264,7 @@ export class PublicAPIRouter extends PromiseRouter { missingPublicServerURL() { return Promise.resolve({ - text: 'Not found.', + text: "Not found.", status: 404, }); } @@ -262,7 +272,7 @@ export class PublicAPIRouter extends PromiseRouter { invalidRequest() { const error = new Error(); error.status = 403; - error.message = 'unauthorized'; + error.message = "unauthorized"; throw error; } @@ -273,8 +283,8 @@ export class PublicAPIRouter extends PromiseRouter { mountRoutes() { this.route( - 'GET', - '/apps/:appId/verify_email', + "GET", + "/apps/:appId/verify_email", req => { this.setConfig(req); }, @@ -284,8 +294,8 @@ export class PublicAPIRouter extends PromiseRouter { ); this.route( - 'POST', - '/apps/:appId/resend_verification_email', + "POST", + "/apps/:appId/resend_verification_email", req => { this.setConfig(req); }, @@ -294,13 +304,13 @@ export class PublicAPIRouter extends PromiseRouter { } ); - this.route('GET', '/apps/choose_password', req => { + this.route("GET", "/apps/choose_password", req => { return this.changePassword(req); }); this.route( - 'POST', - '/apps/:appId/request_password_reset', + "POST", + "/apps/:appId/request_password_reset", req => { this.setConfig(req); }, @@ -310,8 +320,8 @@ export class PublicAPIRouter extends PromiseRouter { ); this.route( - 'GET', - '/apps/:appId/request_password_reset', + "GET", + "/apps/:appId/request_password_reset", req => { this.setConfig(req); }, @@ -323,8 +333,8 @@ export class PublicAPIRouter extends PromiseRouter { expressRouter() { const router = express.Router(); - router.use('/apps', express.static(public_html)); - router.use('/', super.expressRouter()); + router.use("/apps", express.static(public_html)); + router.use("/", super.expressRouter()); return router; } } diff --git a/src/Routers/PurgeRouter.js b/src/Routers/PurgeRouter.js index 3195d134af..d30aea6d93 100644 --- a/src/Routers/PurgeRouter.js +++ b/src/Routers/PurgeRouter.js @@ -1,6 +1,6 @@ -import PromiseRouter from '../PromiseRouter'; -import * as middleware from '../middlewares'; -import Parse from 'parse/node'; +import PromiseRouter from "../PromiseRouter"; +import * as middleware from "../middlewares"; +import Parse from "parse/node"; export class PurgeRouter extends PromiseRouter { handlePurge(req) { @@ -14,9 +14,9 @@ export class PurgeRouter extends PromiseRouter { .purgeCollection(req.params.className) .then(() => { var cacheAdapter = req.config.cacheController; - if (req.params.className == '_Session') { + if (req.params.className == "_Session") { cacheAdapter.user.clear(); - } else if (req.params.className == '_Role') { + } else if (req.params.className == "_Role") { cacheAdapter.role.clear(); } return { response: {} }; @@ -30,9 +30,14 @@ export class PurgeRouter extends PromiseRouter { } mountRoutes() { - this.route('DELETE', '/purge/:className', middleware.promiseEnforceMasterKeyAccess, req => { - return this.handlePurge(req); - }); + this.route( + "DELETE", + "/purge/:className", + middleware.promiseEnforceMasterKeyAccess, + req => { + return this.handlePurge(req); + } + ); } } diff --git a/src/Routers/PushRouter.js b/src/Routers/PushRouter.js index 1c1c8f3b5f..3c7bd55e8b 100644 --- a/src/Routers/PushRouter.js +++ b/src/Routers/PushRouter.js @@ -1,10 +1,15 @@ -import PromiseRouter from '../PromiseRouter'; -import * as middleware from '../middlewares'; -import { Parse } from 'parse/node'; +import PromiseRouter from "../PromiseRouter"; +import * as middleware from "../middlewares"; +import { Parse } from "parse/node"; export class PushRouter extends PromiseRouter { mountRoutes() { - this.route('POST', '/push', middleware.promiseEnforceMasterKeyAccess, PushRouter.handlePOST); + this.route( + "POST", + "/push", + middleware.promiseEnforceMasterKeyAccess, + PushRouter.handlePOST + ); } static handlePOST(req) { @@ -16,7 +21,10 @@ export class PushRouter extends PromiseRouter { } const pushController = req.config.pushController; if (!pushController) { - throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED, 'Push controller is not set'); + throw new Parse.Error( + Parse.Error.PUSH_MISCONFIGURED, + "Push controller is not set" + ); } const where = PushRouter.getQueryCondition(req); @@ -30,7 +38,7 @@ export class PushRouter extends PromiseRouter { pushStatusId = objectId; resolve({ headers: { - 'X-Parse-Push-Status-Id': pushStatusId, + "X-Parse-Push-Status-Id": pushStatusId, }, response: { result: true, @@ -53,14 +61,14 @@ export class PushRouter extends PromiseRouter { */ static getQueryCondition(req) { const body = req.body || {}; - const hasWhere = typeof body.where !== 'undefined'; - const hasChannels = typeof body.channels !== 'undefined'; + const hasWhere = typeof body.where !== "undefined"; + const hasChannels = typeof body.channels !== "undefined"; let where; if (hasWhere && hasChannels) { throw new Parse.Error( Parse.Error.PUSH_MISCONFIGURED, - 'Channels and query can not be set at the same time.' + "Channels and query can not be set at the same time." ); } else if (hasWhere) { where = body.where; diff --git a/src/Routers/RolesRouter.js b/src/Routers/RolesRouter.js index e6a10df77b..142aebbda8 100644 --- a/src/Routers/RolesRouter.js +++ b/src/Routers/RolesRouter.js @@ -1,24 +1,24 @@ -import ClassesRouter from './ClassesRouter'; +import ClassesRouter from "./ClassesRouter"; export class RolesRouter extends ClassesRouter { className() { - return '_Role'; + return "_Role"; } mountRoutes() { - this.route('GET', '/roles', req => { + this.route("GET", "/roles", req => { return this.handleFind(req); }); - this.route('GET', '/roles/:objectId', req => { + this.route("GET", "/roles/:objectId", req => { return this.handleGet(req); }); - this.route('POST', '/roles', req => { + this.route("POST", "/roles", req => { return this.handleCreate(req); }); - this.route('PUT', '/roles/:objectId', req => { + this.route("PUT", "/roles/:objectId", req => { return this.handleUpdate(req); }); - this.route('DELETE', '/roles/:objectId', req => { + this.route("DELETE", "/roles/:objectId", req => { return this.handleDelete(req); }); } diff --git a/src/Routers/SchemasRouter.js b/src/Routers/SchemasRouter.js index 0a42123af7..036c4a0ae6 100644 --- a/src/Routers/SchemasRouter.js +++ b/src/Routers/SchemasRouter.js @@ -1,10 +1,10 @@ // schemas.js -var Parse = require('parse/node').Parse, - SchemaController = require('../Controllers/SchemaController'); +var Parse = require("parse/node").Parse, + SchemaController = require("../Controllers/SchemaController"); -import PromiseRouter from '../PromiseRouter'; -import * as middleware from '../middlewares'; +import PromiseRouter from "../PromiseRouter"; +import * as middleware from "../middlewares"; function classNameMismatchResponse(bodyClass, pathClass) { throw new Parse.Error( @@ -16,7 +16,9 @@ function classNameMismatchResponse(bodyClass, pathClass) { function getAllSchemas(req) { return req.config.database .loadSchema({ clearCache: true }) - .then(schemaController => schemaController.getAllClasses({ clearCache: true })) + .then(schemaController => + schemaController.getAllClasses({ clearCache: true }) + ) .then(schemas => ({ response: { results: schemas } })); } @@ -28,9 +30,15 @@ function getOneSchema(req) { .then(schema => ({ response: schema })) .catch(error => { if (error === undefined) { - throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, `Class ${className} does not exist.`); + throw new Parse.Error( + Parse.Error.INVALID_CLASS_NAME, + `Class ${className} does not exist.` + ); } else { - throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Database adapter error.'); + throw new Parse.Error( + Parse.Error.INTERNAL_SERVER_ERROR, + "Database adapter error." + ); } }); } @@ -39,7 +47,7 @@ const checkIfDefinedSchemasIsUsed = req => { if (req.config?.schema?.lockSchemas === true) { throw new Parse.Error( Parse.Error.OPERATION_FORBIDDEN, - 'Cannot perform this operation when schemas options is used.' + "Cannot perform this operation when schemas options is used." ); } }; @@ -79,7 +87,10 @@ async function createSchema(req) { } if (req.params.className && req.body?.className) { if (req.params.className != req.body.className) { - return classNameMismatchResponse(req.body.className, req.params.className); + return classNameMismatchResponse( + req.body.className, + req.params.className + ); } } @@ -120,34 +131,46 @@ const deleteSchema = req => { SchemaController.invalidClassNameMessage(req.params.className) ); } - return req.config.database.deleteSchema(req.params.className).then(() => ({ response: {} })); + return req.config.database + .deleteSchema(req.params.className) + .then(() => ({ response: {} })); }; export class SchemasRouter extends PromiseRouter { mountRoutes() { - this.route('GET', '/schemas', middleware.promiseEnforceMasterKeyAccess, getAllSchemas); this.route( - 'GET', - '/schemas/:className', + "GET", + "/schemas", + middleware.promiseEnforceMasterKeyAccess, + getAllSchemas + ); + this.route( + "GET", + "/schemas/:className", middleware.promiseEnforceMasterKeyAccess, getOneSchema ); - this.route('POST', '/schemas', middleware.promiseEnforceMasterKeyAccess, createSchema); this.route( - 'POST', - '/schemas/:className', + "POST", + "/schemas", + middleware.promiseEnforceMasterKeyAccess, + createSchema + ); + this.route( + "POST", + "/schemas/:className", middleware.promiseEnforceMasterKeyAccess, createSchema ); this.route( - 'PUT', - '/schemas/:className', + "PUT", + "/schemas/:className", middleware.promiseEnforceMasterKeyAccess, modifySchema ); this.route( - 'DELETE', - '/schemas/:className', + "DELETE", + "/schemas/:className", middleware.promiseEnforceMasterKeyAccess, deleteSchema ); diff --git a/src/Routers/SecurityRouter.js b/src/Routers/SecurityRouter.js index c7c217a048..3f5724d985 100644 --- a/src/Routers/SecurityRouter.js +++ b/src/Routers/SecurityRouter.js @@ -1,12 +1,12 @@ -import PromiseRouter from '../PromiseRouter'; -import * as middleware from '../middlewares'; -import CheckRunner from '../Security/CheckRunner'; +import PromiseRouter from "../PromiseRouter"; +import * as middleware from "../middlewares"; +import CheckRunner from "../Security/CheckRunner"; export class SecurityRouter extends PromiseRouter { mountRoutes() { this.route( - 'GET', - '/security', + "GET", + "/security", middleware.promiseEnforceMasterKeyAccess, this._enforceSecurityCheckEnabled, async req => { @@ -24,7 +24,8 @@ export class SecurityRouter extends PromiseRouter { if (!config.security || !config.security.enableCheck) { const error = new Error(); error.status = 409; - error.message = 'Enable Parse Server option `security.enableCheck` to run security check.'; + error.message = + "Enable Parse Server option `security.enableCheck` to run security check."; throw error; } } diff --git a/src/Routers/SessionsRouter.js b/src/Routers/SessionsRouter.js index e5275fae06..5b04a9e302 100644 --- a/src/Routers/SessionsRouter.js +++ b/src/Routers/SessionsRouter.js @@ -1,24 +1,27 @@ -import ClassesRouter from './ClassesRouter'; -import Parse from 'parse/node'; -import rest from '../rest'; -import Auth from '../Auth'; -import RestWrite from '../RestWrite'; +import ClassesRouter from "./ClassesRouter"; +import Parse from "parse/node"; +import rest from "../rest"; +import Auth from "../Auth"; +import RestWrite from "../RestWrite"; export class SessionsRouter extends ClassesRouter { className() { - return '_Session'; + return "_Session"; } handleMe(req) { // TODO: Verify correct behavior if (!req.info || !req.info.sessionToken) { - throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Session token required.'); + throw new Parse.Error( + Parse.Error.INVALID_SESSION_TOKEN, + "Session token required." + ); } return rest .find( req.config, Auth.master(req.config), - '_Session', + "_Session", { sessionToken: req.info.sessionToken }, undefined, req.info.clientSDK, @@ -26,7 +29,10 @@ export class SessionsRouter extends ClassesRouter { ) .then(response => { if (!response.results || response.results.length == 0) { - throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Session token not found.'); + throw new Parse.Error( + Parse.Error.INVALID_SESSION_TOKEN, + "Session token not found." + ); } return { response: response.results[0], @@ -40,12 +46,12 @@ export class SessionsRouter extends ClassesRouter { // Issue #2720 // Calling without a session token would result in a not found user if (!user) { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'invalid session'); + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, "invalid session"); } const { sessionData, createSession } = RestWrite.createSession(config, { userId: user.id, createdWith: { - action: 'upgrade', + action: "upgrade", }, installationId: req.auth.installationId, }); @@ -54,12 +60,12 @@ export class SessionsRouter extends ClassesRouter { .then(() => { // delete the session token, use the db to skip beforeSave return config.database.update( - '_User', + "_User", { objectId: user.id, }, { - sessionToken: { __op: 'Delete' }, + sessionToken: { __op: "Delete" }, } ); }) @@ -69,25 +75,25 @@ export class SessionsRouter extends ClassesRouter { } mountRoutes() { - this.route('GET', '/sessions/me', req => { + this.route("GET", "/sessions/me", req => { return this.handleMe(req); }); - this.route('GET', '/sessions', req => { + this.route("GET", "/sessions", req => { return this.handleFind(req); }); - this.route('GET', '/sessions/:objectId', req => { + this.route("GET", "/sessions/:objectId", req => { return this.handleGet(req); }); - this.route('POST', '/sessions', req => { + this.route("POST", "/sessions", req => { return this.handleCreate(req); }); - this.route('PUT', '/sessions/:objectId', req => { + this.route("PUT", "/sessions/:objectId", req => { return this.handleUpdate(req); }); - this.route('DELETE', '/sessions/:objectId', req => { + this.route("DELETE", "/sessions/:objectId", req => { return this.handleDelete(req); }); - this.route('POST', '/upgradeToRevocableSession', req => { + this.route("POST", "/upgradeToRevocableSession", req => { return this.handleUpdateToRevocableSession(req); }); } diff --git a/src/Routers/UsersRouter.js b/src/Routers/UsersRouter.js index 4afeb7bbd2..6c5e6638fe 100644 --- a/src/Routers/UsersRouter.js +++ b/src/Routers/UsersRouter.js @@ -1,25 +1,25 @@ // These methods handle the User-related routes. -import Parse from 'parse/node'; -import Config from '../Config'; -import AccountLockout from '../AccountLockout'; -import ClassesRouter from './ClassesRouter'; -import rest from '../rest'; -import Auth from '../Auth'; -import passwordCrypto from '../password'; +import Parse from "parse/node"; +import Config from "../Config"; +import AccountLockout from "../AccountLockout"; +import ClassesRouter from "./ClassesRouter"; +import rest from "../rest"; +import Auth from "../Auth"; +import passwordCrypto from "../password"; import { maybeRunTrigger, Types as TriggerTypes, getRequestObject, resolveError, -} from '../triggers'; -import { promiseEnsureIdempotency } from '../middlewares'; -import RestWrite from '../RestWrite'; -import { logger } from '../logger'; +} from "../triggers"; +import { promiseEnsureIdempotency } from "../middlewares"; +import RestWrite from "../RestWrite"; +import { logger } from "../logger"; export class UsersRouter extends ClassesRouter { className() { - return '_User'; + return "_User"; } /** @@ -30,7 +30,7 @@ export class UsersRouter extends ClassesRouter { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { // Regexp comes from Parse.Object.prototype.validate - if (key !== '__type' && !/^[A-Za-z][0-9A-Za-z_]*$/.test(key)) { + if (key !== "__type" && !/^[A-Za-z][0-9A-Za-z_]*$/.test(key)) { delete obj[key]; } } @@ -79,17 +79,26 @@ export class UsersRouter extends ClassesRouter { // TODO: use the right error codes / descriptions. if (!username && !email) { - throw new Parse.Error(Parse.Error.USERNAME_MISSING, 'username/email is required.'); + throw new Parse.Error( + Parse.Error.USERNAME_MISSING, + "username/email is required." + ); } if (!password) { - throw new Parse.Error(Parse.Error.PASSWORD_MISSING, 'password is required.'); + throw new Parse.Error( + Parse.Error.PASSWORD_MISSING, + "password is required." + ); } if ( - typeof password !== 'string' || - (email && typeof email !== 'string') || - (username && typeof username !== 'string') + typeof password !== "string" || + (email && typeof email !== "string") || + (username && typeof username !== "string") ) { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid username/password.'); + throw new Parse.Error( + Parse.Error.OBJECT_NOT_FOUND, + "Invalid username/password." + ); } let user; @@ -103,10 +112,13 @@ export class UsersRouter extends ClassesRouter { query = { $or: [{ username }, { email: username }] }; } return req.config.database - .find('_User', query, {}, Auth.maintenance(req.config)) + .find("_User", query, {}, Auth.maintenance(req.config)) .then(results => { if (!results.length) { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid username/password.'); + throw new Parse.Error( + Parse.Error.OBJECT_NOT_FOUND, + "Invalid username/password." + ); } if (results.length > 1) { @@ -128,43 +140,67 @@ export class UsersRouter extends ClassesRouter { }) .then(async () => { if (!isValidPassword) { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid username/password.'); + throw new Parse.Error( + Parse.Error.OBJECT_NOT_FOUND, + "Invalid username/password." + ); } // Ensure the user isn't locked out // A locked out user won't be able to login // To lock a user out, just set the ACL to `masterKey` only ({}). // Empty ACL is OK - if (!req.auth.isMaster && user.ACL && Object.keys(user.ACL).length == 0) { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid username/password.'); + if ( + !req.auth.isMaster && + user.ACL && + Object.keys(user.ACL).length == 0 + ) { + throw new Parse.Error( + Parse.Error.OBJECT_NOT_FOUND, + "Invalid username/password." + ); } // Create request object for verification functions const request = { master: req.auth.isMaster, ip: req.config.ip, installationId: req.auth.installationId, - object: Parse.User.fromJSON(Object.assign({ className: '_User' }, user)), + object: Parse.User.fromJSON( + Object.assign({ className: "_User" }, user) + ), }; // If request doesn't use master or maintenance key with ignoring email verification - if (!((req.auth.isMaster || req.auth.isMaintenance) && ignoreEmailVerification)) { + if ( + !( + (req.auth.isMaster || req.auth.isMaintenance) && + ignoreEmailVerification + ) + ) { // Get verification conditions which can be booleans or functions; the purpose of this async/await // structure is to avoid unnecessarily executing subsequent functions if previous ones fail in the // conditional statement below, as a developer may decide to execute expensive operations in them const verifyUserEmails = async () => req.config.verifyUserEmails === true || - (typeof req.config.verifyUserEmails === 'function' && - (await Promise.resolve(req.config.verifyUserEmails(request))) === true); + (typeof req.config.verifyUserEmails === "function" && + (await Promise.resolve( + req.config.verifyUserEmails(request) + )) === true); const preventLoginWithUnverifiedEmail = async () => req.config.preventLoginWithUnverifiedEmail === true || - (typeof req.config.preventLoginWithUnverifiedEmail === 'function' && - (await Promise.resolve(req.config.preventLoginWithUnverifiedEmail(request))) === - true); + (typeof req.config.preventLoginWithUnverifiedEmail === + "function" && + (await Promise.resolve( + req.config.preventLoginWithUnverifiedEmail(request) + )) === true); if ( (await verifyUserEmails()) && (await preventLoginWithUnverifiedEmail()) && !user.emailVerified ) { - throw new Parse.Error(Parse.Error.EMAIL_NOT_FOUND, 'User email is not verified.'); + throw new Parse.Error( + Parse.Error.EMAIL_NOT_FOUND, + "User email is not verified." + ); } } @@ -180,22 +216,32 @@ export class UsersRouter extends ClassesRouter { handleMe(req) { if (!req.info || !req.info.sessionToken) { - throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid session token'); + throw new Parse.Error( + Parse.Error.INVALID_SESSION_TOKEN, + "Invalid session token" + ); } const sessionToken = req.info.sessionToken; return rest .find( req.config, Auth.master(req.config), - '_Session', + "_Session", { sessionToken }, - { include: 'user' }, + { include: "user" }, req.info.clientSDK, req.info.context ) .then(response => { - if (!response.results || response.results.length == 0 || !response.results[0].user) { - throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid session token'); + if ( + !response.results || + response.results.length == 0 || + !response.results[0].user + ) { + throw new Parse.Error( + Parse.Error.INVALID_SESSION_TOKEN, + "Invalid session token" + ); } else { const user = response.results[0].user; // Send token back on the login, because SDKs expect that. @@ -227,7 +273,7 @@ export class UsersRouter extends ClassesRouter { new RestWrite( req.config, req.auth, - '_User', + "_User", { objectId: user.objectId }, req.body || {}, user, @@ -249,24 +295,25 @@ export class UsersRouter extends ClassesRouter { // simply update _User object so that it will start enforcing from now changedAt = new Date(); req.config.database.update( - '_User', + "_User", { username: user.username }, { _password_changed_at: Parse._encode(changedAt) } ); } else { // check whether the password has expired - if (changedAt.__type == 'Date') { + if (changedAt.__type == "Date") { changedAt = new Date(changedAt.iso); } // Calculate the expiry time. const expiresAt = new Date( - changedAt.getTime() + 86400000 * req.config.passwordPolicy.maxPasswordAge + changedAt.getTime() + + 86400000 * req.config.passwordPolicy.maxPasswordAge ); if (expiresAt < new Date()) { // fail of current time is past password expiry time throw new Parse.Error( Parse.Error.OBJECT_NOT_FOUND, - 'Your password has expired. Please reset your password.' + "Your password has expired. Please reset your password." ); } } @@ -281,7 +328,7 @@ export class UsersRouter extends ClassesRouter { await maybeRunTrigger( TriggerTypes.beforeLogin, req.auth, - Parse.User.fromJSON(Object.assign({ className: '_User' }, user)), + Parse.User.fromJSON(Object.assign({ className: "_User" }, user)), null, req.config, req.info.context @@ -290,7 +337,7 @@ export class UsersRouter extends ClassesRouter { // If we have some new validated authData update directly if (validatedAuthData && Object.keys(validatedAuthData).length) { await req.config.database.update( - '_User', + "_User", { objectId: user.objectId }, { authData: validatedAuthData }, {} @@ -300,8 +347,8 @@ export class UsersRouter extends ClassesRouter { const { sessionData, createSession } = RestWrite.createSession(req.config, { userId: user.objectId, createdWith: { - action: 'login', - authProvider: 'password', + action: "login", + authProvider: "password", }, installationId: req.info.installationId, }); @@ -310,7 +357,9 @@ export class UsersRouter extends ClassesRouter { await createSession(); - const afterLoginUser = Parse.User.fromJSON(Object.assign({ className: '_User' }, user)); + const afterLoginUser = Parse.User.fromJSON( + Object.assign({ className: "_User" }, user) + ); await maybeRunTrigger( TriggerTypes.afterLogin, { ...req.auth, user: afterLoginUser }, @@ -344,21 +393,26 @@ export class UsersRouter extends ClassesRouter { */ async handleLogInAs(req) { if (!req.auth.isMaster) { - throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'master key is required'); + throw new Parse.Error( + Parse.Error.OPERATION_FORBIDDEN, + "master key is required" + ); } const userId = req.body?.userId || req.query.userId; if (!userId) { throw new Parse.Error( Parse.Error.INVALID_VALUE, - 'userId must not be empty, null, or undefined' + "userId must not be empty, null, or undefined" ); } - const queryResults = await req.config.database.find('_User', { objectId: userId }); + const queryResults = await req.config.database.find("_User", { + objectId: userId, + }); const user = queryResults[0]; if (!user) { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'user not found'); + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, "user not found"); } this._sanitizeAuthData(user); @@ -366,8 +420,8 @@ export class UsersRouter extends ClassesRouter { const { sessionData, createSession } = RestWrite.createSession(req.config, { userId, createdWith: { - action: 'login', - authProvider: 'masterkey', + action: "login", + authProvider: "masterkey", }, installationId: req.info.installationId, }); @@ -398,7 +452,7 @@ export class UsersRouter extends ClassesRouter { const records = await rest.find( req.config, Auth.master(req.config), - '_Session', + "_Session", { sessionToken: req.info.sessionToken }, undefined, req.info.clientSDK, @@ -408,14 +462,16 @@ export class UsersRouter extends ClassesRouter { await rest.del( req.config, Auth.master(req.config), - '_Session', + "_Session", records.results[0].objectId, req.info.context ); await maybeRunTrigger( TriggerTypes.afterLogout, req.auth, - Parse.Session.fromJSON(Object.assign({ className: '_Session' }, records.results[0])), + Parse.Session.fromJSON( + Object.assign({ className: "_Session" }, records.results[0]) + ), null, req.config ); @@ -430,15 +486,16 @@ export class UsersRouter extends ClassesRouter { emailAdapter: req.config.userController.adapter, appName: req.config.appName, publicServerURL: req.config.publicServerURL, - emailVerifyTokenValidityDuration: req.config.emailVerifyTokenValidityDuration, + emailVerifyTokenValidityDuration: + req.config.emailVerifyTokenValidityDuration, emailVerifyTokenReuseIfValid: req.config.emailVerifyTokenReuseIfValid, }); } catch (e) { - if (typeof e === 'string') { + if (typeof e === "string") { // Maybe we need a Bad Configuration error, but the SDKs won't understand it. For now, Internal Server Error. throw new Parse.Error( Parse.Error.INTERNAL_SERVER_ERROR, - 'An appName, publicServerURL, and emailAdapter are required for password reset and email verification functionality.' + "An appName, publicServerURL, and emailAdapter are required for password reset and email verification functionality." ); } else { throw e; @@ -453,10 +510,13 @@ export class UsersRouter extends ClassesRouter { const token = req.body?.token; if (!email && !token) { - throw new Parse.Error(Parse.Error.EMAIL_MISSING, 'you must provide an email'); + throw new Parse.Error( + Parse.Error.EMAIL_MISSING, + "you must provide an email" + ); } if (token) { - const results = await req.config.database.find('_User', { + const results = await req.config.database.find("_User", { _perishable_token: token, _perishable_token_expires_at: { $lt: Parse._encode(new Date()) }, }); @@ -464,10 +524,10 @@ export class UsersRouter extends ClassesRouter { email = results[0].email; } } - if (typeof email !== 'string') { + if (typeof email !== "string") { throw new Parse.Error( Parse.Error.INVALID_EMAIL_ADDRESS, - 'you must provide a valid email string' + "you must provide a valid email string" ); } const userController = req.config.userController; @@ -478,7 +538,10 @@ export class UsersRouter extends ClassesRouter { }; } catch (err) { if (err.code === Parse.Error.OBJECT_NOT_FOUND) { - if (req.config.passwordPolicy?.resetPasswordSuccessOnInvalidEmail ?? true) { + if ( + req.config.passwordPolicy?.resetPasswordSuccessOnInvalidEmail ?? + true + ) { return { response: {}, }; @@ -494,23 +557,29 @@ export class UsersRouter extends ClassesRouter { const { email } = req.body || {}; if (!email) { - throw new Parse.Error(Parse.Error.EMAIL_MISSING, 'you must provide an email'); + throw new Parse.Error( + Parse.Error.EMAIL_MISSING, + "you must provide an email" + ); } - if (typeof email !== 'string') { + if (typeof email !== "string") { throw new Parse.Error( Parse.Error.INVALID_EMAIL_ADDRESS, - 'you must provide a valid email string' + "you must provide a valid email string" ); } const results = await req.config.database.find( - '_User', + "_User", { email: email }, {}, Auth.maintenance(req.config) ); if (!results.length || results.length < 1) { - throw new Parse.Error(Parse.Error.EMAIL_NOT_FOUND, `No user found with email ${email}`); + throw new Parse.Error( + Parse.Error.EMAIL_NOT_FOUND, + `No user found with email ${email}` + ); } const user = results[0]; @@ -518,7 +587,10 @@ export class UsersRouter extends ClassesRouter { delete user.password; if (user.emailVerified) { - throw new Parse.Error(Parse.Error.OTHER_CAUSE, `Email ${email} is already verified.`); + throw new Parse.Error( + Parse.Error.OTHER_CAUSE, + `Email ${email} is already verified.` + ); } const userController = req.config.userController; @@ -535,7 +607,8 @@ export class UsersRouter extends ClassesRouter { } async handleChallenge(req) { - const { username, email, password, authData, challengeData } = req.body || {}; + const { username, email, password, authData, challengeData } = + req.body || {}; // if username or email provided with password try to authenticate the user by username let user; @@ -543,18 +616,21 @@ export class UsersRouter extends ClassesRouter { if (!password) { throw new Parse.Error( Parse.Error.OTHER_CAUSE, - 'You provided username or email, you need to also provide password.' + "You provided username or email, you need to also provide password." ); } user = await this._authenticateUserFromRequest(req); } if (!challengeData) { - throw new Parse.Error(Parse.Error.OTHER_CAUSE, 'Nothing to challenge.'); + throw new Parse.Error(Parse.Error.OTHER_CAUSE, "Nothing to challenge."); } - if (typeof challengeData !== 'object') { - throw new Parse.Error(Parse.Error.OTHER_CAUSE, 'challengeData should be an object.'); + if (typeof challengeData !== "object") { + throw new Parse.Error( + Parse.Error.OTHER_CAUSE, + "challengeData should be an object." + ); } let request; @@ -562,20 +638,23 @@ export class UsersRouter extends ClassesRouter { // Try to find user by authData if (authData) { - if (typeof authData !== 'object') { - throw new Parse.Error(Parse.Error.OTHER_CAUSE, 'authData should be an object.'); + if (typeof authData !== "object") { + throw new Parse.Error( + Parse.Error.OTHER_CAUSE, + "authData should be an object." + ); } if (user) { throw new Parse.Error( Parse.Error.OTHER_CAUSE, - 'You cannot provide username/email and authData, only use one identification method.' + "You cannot provide username/email and authData, only use one identification method." ); } if (Object.keys(authData).filter(key => authData[key].id).length > 1) { throw new Parse.Error( Parse.Error.OTHER_CAUSE, - 'You cannot provide more than one authData provider with an id.' + "You cannot provide more than one authData provider with an id." ); } @@ -583,33 +662,56 @@ export class UsersRouter extends ClassesRouter { try { if (!results[0] || results.length > 1) { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.'); + throw new Parse.Error( + Parse.Error.OBJECT_NOT_FOUND, + "User not found." + ); } // Find the provider used to find the user const provider = Object.keys(authData).find(key => authData[key].id); - parseUser = Parse.User.fromJSON({ className: '_User', ...results[0] }); - request = getRequestObject(undefined, req.auth, parseUser, parseUser, req.config); + parseUser = Parse.User.fromJSON({ className: "_User", ...results[0] }); + request = getRequestObject( + undefined, + req.auth, + parseUser, + parseUser, + req.config + ); request.isChallenge = true; // Validate authData used to identify the user to avoid brute-force attack on `id` - const { validator } = req.config.authDataManager.getValidatorForProvider(provider); - const validatorResponse = await validator(authData[provider], req, parseUser, request); + const { validator } = + req.config.authDataManager.getValidatorForProvider(provider); + const validatorResponse = await validator( + authData[provider], + req, + parseUser, + request + ); if (validatorResponse && validatorResponse.validator) { await validatorResponse.validator(); } } catch (e) { // Rewrite the error to avoid guess id attack logger.error(e); - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.'); + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, "User not found."); } } if (!parseUser) { - parseUser = user ? Parse.User.fromJSON({ className: '_User', ...user }) : undefined; + parseUser = user + ? Parse.User.fromJSON({ className: "_User", ...user }) + : undefined; } if (!request) { - request = getRequestObject(undefined, req.auth, parseUser, parseUser, req.config); + request = getRequestObject( + undefined, + req.auth, + parseUser, + parseUser, + req.config + ); request.isChallenge = true; } const acc = {}; @@ -617,14 +719,15 @@ export class UsersRouter extends ClassesRouter { // and to avoid to trigger others challenges if one of them fails for (const provider of Object.keys(challengeData).sort()) { try { - const authAdapter = req.config.authDataManager.getValidatorForProvider(provider); + const authAdapter = + req.config.authDataManager.getValidatorForProvider(provider); if (!authAdapter) { continue; } const { adapter: { challenge }, } = authAdapter; - if (typeof challenge === 'function') { + if (typeof challenge === "function") { const providerChallengeResponse = await challenge( challengeData[provider], authData && authData[provider], @@ -636,14 +739,15 @@ export class UsersRouter extends ClassesRouter { } catch (err) { const e = resolveError(err, { code: Parse.Error.SCRIPT_FAILED, - message: 'Challenge failed. Unknown error.', + message: "Challenge failed. Unknown error.", }); - const userString = req.auth && req.auth.user ? req.auth.user.id : undefined; + const userString = + req.auth && req.auth.user ? req.auth.user.id : undefined; logger.error( `Failed running auth step challenge for ${provider} for user ${userString} with Error: ` + JSON.stringify(e), { - authenticationStep: 'challenge', + authenticationStep: "challenge", error: e, user: userString, provider, @@ -656,49 +760,49 @@ export class UsersRouter extends ClassesRouter { } mountRoutes() { - this.route('GET', '/users', req => { + this.route("GET", "/users", req => { return this.handleFind(req); }); - this.route('POST', '/users', promiseEnsureIdempotency, req => { + this.route("POST", "/users", promiseEnsureIdempotency, req => { return this.handleCreate(req); }); - this.route('GET', '/users/me', req => { + this.route("GET", "/users/me", req => { return this.handleMe(req); }); - this.route('GET', '/users/:objectId', req => { + this.route("GET", "/users/:objectId", req => { return this.handleGet(req); }); - this.route('PUT', '/users/:objectId', promiseEnsureIdempotency, req => { + this.route("PUT", "/users/:objectId", promiseEnsureIdempotency, req => { return this.handleUpdate(req); }); - this.route('DELETE', '/users/:objectId', req => { + this.route("DELETE", "/users/:objectId", req => { return this.handleDelete(req); }); - this.route('GET', '/login', req => { + this.route("GET", "/login", req => { return this.handleLogIn(req); }); - this.route('POST', '/login', req => { + this.route("POST", "/login", req => { return this.handleLogIn(req); }); - this.route('POST', '/loginAs', req => { + this.route("POST", "/loginAs", req => { return this.handleLogInAs(req); }); - this.route('POST', '/logout', req => { + this.route("POST", "/logout", req => { return this.handleLogOut(req); }); - this.route('POST', '/requestPasswordReset', req => { + this.route("POST", "/requestPasswordReset", req => { return this.handleResetRequest(req); }); - this.route('POST', '/verificationEmailRequest', req => { + this.route("POST", "/verificationEmailRequest", req => { return this.handleVerificationEmailRequest(req); }); - this.route('GET', '/verifyPassword', req => { + this.route("GET", "/verifyPassword", req => { return this.handleVerifyPassword(req); }); - this.route('POST', '/verifyPassword', req => { + this.route("POST", "/verifyPassword", req => { return this.handleVerifyPassword(req); }); - this.route('POST', '/challenge', req => { + this.route("POST", "/challenge", req => { return this.handleChallenge(req); }); } diff --git a/src/SchemaMigrations/DefinedSchemas.js b/src/SchemaMigrations/DefinedSchemas.js index 14c975ee45..11fe31a9ef 100644 --- a/src/SchemaMigrations/DefinedSchemas.js +++ b/src/SchemaMigrations/DefinedSchemas.js @@ -1,14 +1,17 @@ // @flow // @flow-disable-next Cannot resolve module `parse/node`. -const Parse = require('parse/node'); -import { logger } from '../logger'; -import Config from '../Config'; -import { internalCreateSchema, internalUpdateSchema } from '../Routers/SchemasRouter'; -import { defaultColumns, systemClasses } from '../Controllers/SchemaController'; -import { ParseServerOptions } from '../Options'; -import * as Migrations from './Migrations'; -import Auth from '../Auth'; -import rest from '../rest'; +const Parse = require("parse/node"); +import { logger } from "../logger"; +import Config from "../Config"; +import { + internalCreateSchema, + internalUpdateSchema, +} from "../Routers/SchemasRouter"; +import { defaultColumns, systemClasses } from "../Controllers/SchemaController"; +import { ParseServerOptions } from "../Options"; +import * as Migrations from "./Migrations"; +import Auth from "../Auth"; +import rest from "../rest"; export class DefinedSchemas { config: ParseServerOptions; @@ -18,7 +21,10 @@ export class DefinedSchemas { maxRetries: number; allCloudSchemas: Parse.Schema[]; - constructor(schemaOptions: Migrations.SchemaOptions, config: ParseServerOptions) { + constructor( + schemaOptions: Migrations.SchemaOptions, + config: ParseServerOptions + ) { this.localSchemas = []; this.config = Config.get(config.appId); this.schemaOptions = schemaOptions; @@ -66,7 +72,7 @@ export class DefinedSchemas { async execute() { try { - logger.info('Running Migrations'); + logger.info("Running Migrations"); if (this.schemaOptions && this.schemaOptions.beforeMigration) { await Promise.resolve(this.schemaOptions.beforeMigration()); } @@ -77,10 +83,10 @@ export class DefinedSchemas { await Promise.resolve(this.schemaOptions.afterMigration()); } - logger.info('Running Migrations Completed'); + logger.info("Running Migrations Completed"); } catch (e) { logger.error(`Failed to run migrations: ${e}`); - if (process.env.NODE_ENV === 'production') { + if (process.env.NODE_ENV === "production") { process.exit(1); } } @@ -93,9 +99,11 @@ export class DefinedSchemas { // if we fail to get schema // pm2 or K8s and many other process managers will try to restart the process // after the exit - if (process.env.NODE_ENV === 'production') { + if (process.env.NODE_ENV === "production") { timeout = setTimeout(() => { - logger.error('Timeout occurred during execution of migrations. Exiting...'); + logger.error( + "Timeout occurred during execution of migrations. Exiting..." + ); process.exit(1); }, 20000); } @@ -105,7 +113,11 @@ export class DefinedSchemas { const schemaController = await this.config.database.loadSchema(); this.allCloudSchemas = await schemaController.getAllClasses(); clearTimeout(timeout); - await Promise.all(this.localSchemas.map(async localSchema => this.saveOrUpdate(localSchema))); + await Promise.all( + this.localSchemas.map(async localSchema => + this.saveOrUpdate(localSchema) + ) + ); this.checkForMissingSchemas(); await this.enforceCLPForNonProvidedClass(); @@ -122,7 +134,7 @@ export class DefinedSchemas { await this.executeMigrations(); } else { logger.error(`Failed to run migrations: ${e}`); - if (process.env.NODE_ENV === 'production') { + if (process.env.NODE_ENV === "production") { process.exit(1); } } @@ -166,7 +178,9 @@ export class DefinedSchemas { async enforceCLPForNonProvidedClass(): Promise { const nonProvidedClasses = this.allCloudSchemas.filter( cloudSchema => - !this.localSchemas.some(localSchema => localSchema.className === cloudSchema.className) + !this.localSchemas.some( + localSchema => localSchema.className === cloudSchema.className + ) ); await Promise.all( nonProvidedClasses.map(async schema => { @@ -180,12 +194,24 @@ export class DefinedSchemas { // Create a fake session since Parse do not create the _Session until // a session is created async createDeleteSession() { - const { response } = await rest.create(this.config, Auth.master(this.config), '_Session', {}); - await rest.del(this.config, Auth.master(this.config), '_Session', response.objectId); + const { response } = await rest.create( + this.config, + Auth.master(this.config), + "_Session", + {} + ); + await rest.del( + this.config, + Auth.master(this.config), + "_Session", + response.objectId + ); } async saveOrUpdate(localSchema: Migrations.JSONSchema) { - const cloudSchema = this.allCloudSchemas.find(sc => sc.className === localSchema.className); + const cloudSchema = this.allCloudSchemas.find( + sc => sc.className === localSchema.className + ); if (cloudSchema) { try { await this.updateSchema(localSchema, cloudSchema); @@ -206,7 +232,9 @@ export class DefinedSchemas { if (localSchema.fields) { // Handle fields Object.keys(localSchema.fields) - .filter(fieldName => !this.isProtectedFields(localSchema.className, fieldName)) + .filter( + fieldName => !this.isProtectedFields(localSchema.className, fieldName) + ) .forEach(fieldName => { if (localSchema.fields) { const field = localSchema.fields[fieldName]; @@ -217,7 +245,10 @@ export class DefinedSchemas { // Handle indexes if (localSchema.indexes) { Object.keys(localSchema.indexes).forEach(indexName => { - if (localSchema.indexes && !this.isProtectedIndex(localSchema.className, indexName)) { + if ( + localSchema.indexes && + !this.isProtectedIndex(localSchema.className, indexName) + ) { newLocalSchema.addIndex(indexName, localSchema.indexes[indexName]); } }); @@ -228,14 +259,19 @@ export class DefinedSchemas { return await this.saveSchemaToDB(newLocalSchema); } - async updateSchema(localSchema: Migrations.JSONSchema, cloudSchema: Parse.Schema) { + async updateSchema( + localSchema: Migrations.JSONSchema, + cloudSchema: Parse.Schema + ) { const newLocalSchema = new Parse.Schema(localSchema.className); // Handle fields // Check addition if (localSchema.fields) { Object.keys(localSchema.fields) - .filter(fieldName => !this.isProtectedFields(localSchema.className, fieldName)) + .filter( + fieldName => !this.isProtectedFields(localSchema.className, fieldName) + ) .forEach(fieldName => { // @flow-disable-next const field = localSchema.fields[fieldName]; @@ -255,7 +291,9 @@ export class DefinedSchemas { // Check deletion Object.keys(cloudSchema.fields) - .filter(fieldName => !this.isProtectedFields(localSchema.className, fieldName)) + .filter( + fieldName => !this.isProtectedFields(localSchema.className, fieldName) + ) .forEach(fieldName => { const field = cloudSchema.fields[fieldName]; if (!localSchema.fields || !localSchema.fields[fieldName]) { @@ -317,8 +355,11 @@ export class DefinedSchemas { } else if (this.schemaOptions.strict === true && fieldsToRecreate.length) { fieldsToRecreate.forEach(field => { const from = - field.from.type + (field.from.targetClass ? ` (${field.from.targetClass})` : ''); - const to = field.to.type + (field.to.targetClass ? ` (${field.to.targetClass})` : ''); + field.from.type + + (field.from.targetClass ? ` (${field.from.targetClass})` : ""); + const to = + field.to.type + + (field.to.targetClass ? ` (${field.to.targetClass})` : ""); logger.warn( `The field "${field.fieldName}" type differ between the schema and the database for "${localSchema.className}"; Schema is defined as "${to}" and current database type is "${from}"` @@ -357,7 +398,10 @@ export class DefinedSchemas { if (!localSchema.indexes || !localSchema.indexes[indexName]) { newLocalSchema.deleteIndex(indexName); } else if ( - !this.paramsAreEquals(localSchema.indexes[indexName], cloudSchema.indexes[indexName]) + !this.paramsAreEquals( + localSchema.indexes[indexName], + cloudSchema.indexes[indexName] + ) ) { newLocalSchema.deleteIndex(indexName); if (localSchema.indexes) { @@ -377,7 +421,7 @@ export class DefinedSchemas { // Apply new/changed indexes if (indexesToAdd.length) { logger.debug( - `Updating indexes for "${newLocalSchema.className}" : ${indexesToAdd.join(' ,')}` + `Updating indexes for "${newLocalSchema.className}" : ${indexesToAdd.join(" ,")}` ); indexesToAdd.forEach(o => newLocalSchema.addIndex(o.indexName, o.index)); await this.updateSchemaToDB(newLocalSchema); @@ -390,10 +434,14 @@ export class DefinedSchemas { cloudSchema: Parse.Schema ) { if (!localSchema.classLevelPermissions && !cloudSchema) { - logger.warn(`classLevelPermissions not provided for ${localSchema.className}.`); + logger.warn( + `classLevelPermissions not provided for ${localSchema.className}.` + ); } // Use spread to avoid read only issue (encountered by Moumouls using directAccess) - const clp = ({ ...(localSchema.classLevelPermissions || {}) }: Parse.CLP.PermissionsMap); + const clp = ({ + ...(localSchema.classLevelPermissions || {}), + }: Parse.CLP.PermissionsMap); // To avoid inconsistency we need to remove all rights on addField clp.addField = {}; newLocalSchema.setCLP(clp); @@ -407,22 +455,22 @@ export class DefinedSchemas { } isProtectedIndex(className: string, indexName: string) { - const indexes = ['_id_']; + const indexes = ["_id_"]; switch (className) { - case '_User': + case "_User": indexes.push( - 'case_insensitive_username', - 'case_insensitive_email', - 'username_1', - 'email_1' + "case_insensitive_username", + "case_insensitive_email", + "username_1", + "email_1" ); break; - case '_Role': - indexes.push('name_1'); + case "_Role": + indexes.push("name_1"); break; - case '_Idempotency': - indexes.push('reqId_1'); + case "_Idempotency": + indexes.push("reqId_1"); break; } @@ -440,10 +488,14 @@ export class DefinedSchemas { return keysA.every(k => objA[k] === objB[k]); } - handleFields(newLocalSchema: Parse.Schema, fieldName: string, field: Migrations.FieldType) { - if (field.type === 'Relation') { + handleFields( + newLocalSchema: Parse.Schema, + fieldName: string, + field: Migrations.FieldType + ) { + if (field.type === "Relation") { newLocalSchema.addRelation(fieldName, field.targetClass); - } else if (field.type === 'Pointer') { + } else if (field.type === "Pointer") { newLocalSchema.addPointer(fieldName, field.targetClass, field); } else { newLocalSchema.addField(fieldName, field.type, field); diff --git a/src/SchemaMigrations/Migrations.js b/src/SchemaMigrations/Migrations.js index 8768911189..fe358d5059 100644 --- a/src/SchemaMigrations/Migrations.js +++ b/src/SchemaMigrations/Migrations.js @@ -11,18 +11,18 @@ export interface SchemaOptions { } export type FieldValueType = - | 'String' - | 'Boolean' - | 'File' - | 'Number' - | 'Relation' - | 'Pointer' - | 'Date' - | 'GeoPoint' - | 'Polygon' - | 'Array' - | 'Object' - | 'ACL'; + | "String" + | "Boolean" + | "File" + | "Number" + | "Relation" + | "Pointer" + | "Date" + | "GeoPoint" + | "Polygon" + | "Array" + | "Object" + | "ACL"; export interface FieldType { type: FieldValueType; @@ -31,7 +31,7 @@ export interface FieldType { targetClass?: string; } -type ClassNameType = '_User' | '_Role' | string; +type ClassNameType = "_User" | "_Role" | string; export interface ProtectedFieldsInterface { [key: string]: string[]; @@ -45,7 +45,13 @@ export interface IndexesInterface { [key: string]: IndexInterface; } -export type CLPOperation = 'find' | 'count' | 'get' | 'update' | 'create' | 'delete'; +export type CLPOperation = + | "find" + | "count" + | "get" + | "update" + | "create" + | "delete"; // @Typescript 4.1+ // type CLPPermission = 'requiresAuthentication' | '*' | `user:${string}` | `role:${string}` type CLPValue = { [key: string]: boolean }; @@ -84,7 +90,10 @@ export class CLP { } } -export function makeSchema(className: ClassNameType, schema: JSONSchema): JSONSchema { +export function makeSchema( + className: ClassNameType, + schema: JSONSchema +): JSONSchema { // This function solve two things: // 1. It provides auto-completion to the users who are implementing schemas // 2. It allows forward-compatible point in order to allow future changes to the internal structure of JSONSchema without affecting all the users diff --git a/src/Security/Check.js b/src/Security/Check.js index c31480e73d..acbcd486e7 100644 --- a/src/Security/Check.js +++ b/src/Security/Check.js @@ -2,8 +2,8 @@ * @module SecurityCheck */ -import Utils from '../Utils'; -import { isFunction, isString } from 'lodash'; +import Utils from "../Utils"; +import { isFunction, isString } from "lodash"; /** * A security check. @@ -60,11 +60,11 @@ class Check { */ _validateParams(params) { Utils.validateParams(params, { - group: { t: 'string', v: isString }, - title: { t: 'string', v: isString }, - warning: { t: 'string', v: isString }, - solution: { t: 'string', v: isString }, - check: { t: 'function', v: isFunction }, + group: { t: "string", v: isString }, + title: { t: "string", v: isString }, + warning: { t: "string", v: isString }, + solution: { t: "string", v: isString }, + check: { t: "function", v: isFunction }, }); } } @@ -73,9 +73,9 @@ class Check { * The check state. */ const CheckState = Object.freeze({ - none: 'none', - fail: 'fail', - success: 'success', + none: "none", + fail: "fail", + success: "success", }); export default Check; diff --git a/src/Security/CheckRunner.js b/src/Security/CheckRunner.js index 4be1c3acbf..0e5cb22950 100644 --- a/src/Security/CheckRunner.js +++ b/src/Security/CheckRunner.js @@ -1,8 +1,8 @@ -import Utils from '../Utils'; -import { CheckState } from './Check'; -import * as CheckGroups from './CheckGroups/CheckGroups'; -import logger from '../logger'; -import { isArray, isBoolean } from 'lodash'; +import Utils from "../Utils"; +import { CheckState } from "./Check"; +import * as CheckGroups from "./CheckGroups/CheckGroups"; +import logger from "../logger"; +import { isArray, isBoolean } from "lodash"; /** * The security check runner. @@ -18,7 +18,11 @@ class CheckRunner { */ constructor(config = {}) { this._validateParams(config); - const { enableCheck = false, enableCheckLog = false, checkGroups = CheckGroups } = config; + const { + enableCheck = false, + enableCheckLog = false, + checkGroups = CheckGroups, + } = config; this.enableCheck = enableCheck; this.enableCheckLog = enableCheckLog; this.checkGroups = checkGroups; @@ -29,10 +33,10 @@ class CheckRunner { * @params * @returns {Object} The security check report. */ - async run({ version = '1.0.0' } = {}) { + async run({ version = "1.0.0" } = {}) { // Instantiate check groups const groups = Object.values(this.checkGroups) - .filter(c => typeof c === 'function') + .filter(c => typeof c === "function") .map(CheckGroup => new CheckGroup()); // Run checks @@ -88,7 +92,7 @@ class CheckRunner { // Identify report version switch (version) { - case '1.0.0': + case "1.0.0": default: // For each check group for (const group of groups) { @@ -127,11 +131,13 @@ class CheckRunner { _logReport(report) { // Determine log level depending on whether any check failed const log = - report.report.state == CheckState.success ? s => logger.info(s) : s => logger.warn(s); + report.report.state == CheckState.success + ? s => logger.info(s) + : s => logger.warn(s); // Declare output - const indent = ' '; - let output = ''; + const indent = " "; + let output = ""; let checksCount = 0; let failedChecksCount = 0; let skippedCheckCount = 0; @@ -163,8 +169,8 @@ class CheckRunner { `\n###################################` + `\n` + `\n${ - failedChecksCount > 0 ? 'Warning: ' : '' - }${failedChecksCount} weak security setting(s) found${failedChecksCount > 0 ? '!' : ''}` + + failedChecksCount > 0 ? "Warning: " : "" + }${failedChecksCount} weak security setting(s) found${failedChecksCount > 0 ? "!" : ""}` + `\n${checksCount} check(s) executed` + `\n${skippedCheckCount} check(s) skipped` + `\n` + @@ -182,11 +188,11 @@ class CheckRunner { _getLogIconForState(state) { switch (state) { case CheckState.success: - return '✅'; + return "✅"; case CheckState.fail: - return '❌'; + return "❌"; default: - return 'ℹ️'; + return "ℹ️"; } } @@ -196,9 +202,9 @@ class CheckRunner { */ _validateParams(params) { Utils.validateParams(params, { - enableCheck: { t: 'boolean', v: isBoolean, o: true }, - enableCheckLog: { t: 'boolean', v: isBoolean, o: true }, - checkGroups: { t: 'array', v: isArray, o: true }, + enableCheck: { t: "boolean", v: isBoolean, o: true }, + enableCheckLog: { t: "boolean", v: isBoolean, o: true }, + checkGroups: { t: "array", v: isArray, o: true }, }); } } diff --git a/src/SharedRest.js b/src/SharedRest.js index 0b4a07c320..19b3871f23 100644 --- a/src/SharedRest.js +++ b/src/SharedRest.js @@ -1,15 +1,15 @@ const classesWithMasterOnlyAccess = [ - '_JobStatus', - '_PushStatus', - '_Hooks', - '_GlobalConfig', - '_JobSchedule', - '_Idempotency', + "_JobStatus", + "_PushStatus", + "_Hooks", + "_GlobalConfig", + "_JobSchedule", + "_Idempotency", ]; // Disallowing access to the _Role collection except by master key function enforceRoleSecurity(method, className, auth) { - if (className === '_Installation' && !auth.isMaster && !auth.isMaintenance) { - if (method === 'delete' || method === 'find') { + if (className === "_Installation" && !auth.isMaster && !auth.isMaintenance) { + if (method === "delete" || method === "find") { const error = `Clients aren't allowed to perform the ${method} operation on the installation collection.`; throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, error); } @@ -26,7 +26,10 @@ function enforceRoleSecurity(method, className, auth) { } // readOnly masterKey is not allowed - if (auth.isReadOnly && (method === 'delete' || method === 'create' || method === 'update')) { + if ( + auth.isReadOnly && + (method === "delete" || method === "create" || method === "update") + ) { const error = `read-only masterKey isn't allowed to perform the ${method} operation.`; throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, error); } diff --git a/src/StatusHandler.js b/src/StatusHandler.js index 81b4d70c99..d2ee7606f1 100644 --- a/src/StatusHandler.js +++ b/src/StatusHandler.js @@ -1,18 +1,18 @@ -import { md5Hash, newObjectId } from './cryptoUtils'; -import { KeyPromiseQueue } from './KeyPromiseQueue'; -import { logger } from './logger'; -import rest from './rest'; -import Auth from './Auth'; +import { md5Hash, newObjectId } from "./cryptoUtils"; +import { KeyPromiseQueue } from "./KeyPromiseQueue"; +import { logger } from "./logger"; +import rest from "./rest"; +import Auth from "./Auth"; -const PUSH_STATUS_COLLECTION = '_PushStatus'; -const JOB_STATUS_COLLECTION = '_JobStatus'; +const PUSH_STATUS_COLLECTION = "_PushStatus"; +const JOB_STATUS_COLLECTION = "_JobStatus"; const pushPromiseQueue = new KeyPromiseQueue(); const jobPromiseQueue = new KeyPromiseQueue(); const incrementOp = function (object = {}, key, amount = 1) { if (!object[key]) { - object[key] = { __op: 'Increment', amount: amount }; + object[key] = { __op: "Increment", amount: amount }; } else { object[key].amount += amount; } @@ -39,7 +39,9 @@ function statusHandler(className, database) { } function update(where, object) { - return jobPromiseQueue.enqueue(where.objectId, () => database.update(className, where, object)); + return jobPromiseQueue.enqueue(where.objectId, () => + database.update(className, where, object) + ); } return Object.freeze({ @@ -82,8 +84,8 @@ export function jobStatusHandler(config) { jobStatus = { objectId, jobName, - status: 'running', - source: 'api', + status: "running", + source: "api", createdAt: now, // lockdown! ACL: {}, @@ -93,27 +95,27 @@ export function jobStatusHandler(config) { }; const setMessage = function (message) { - if (!message || typeof message !== 'string') { + if (!message || typeof message !== "string") { return Promise.resolve(); } return handler.update({ objectId }, { message }); }; const setSucceeded = function (message) { - return setFinalStatus('succeeded', message); + return setFinalStatus("succeeded", message); }; const setFailed = function (message) { - return setFinalStatus('failed', message); + return setFinalStatus("failed", message); }; const setFinalStatus = function (status, message = undefined) { const finishedAt = new Date(); const update = { status, finishedAt }; - if (message && typeof message === 'string') { + if (message && typeof message === "string") { update.message = message; } - if (message instanceof Error && typeof message.message === 'string') { + if (message instanceof Error && typeof message.message === "string") { update.message = message.message; } return handler.update({ objectId }, update); @@ -132,29 +134,31 @@ export function pushStatusHandler(config, existingObjectId) { const database = config.database; const handler = restStatusHandler(PUSH_STATUS_COLLECTION, config); let objectId = existingObjectId; - const setInitial = function (body = {}, where, options = { source: 'rest' }) { + const setInitial = function (body = {}, where, options = { source: "rest" }) { const now = new Date(); let pushTime = now.toISOString(); - let status = 'pending'; - if (Object.prototype.hasOwnProperty.call(body, 'push_time')) { + let status = "pending"; + if (Object.prototype.hasOwnProperty.call(body, "push_time")) { if (config.hasPushScheduledSupport) { pushTime = body.push_time; - status = 'scheduled'; + status = "scheduled"; } else { - logger.warn('Trying to schedule a push while server is not configured.'); - logger.warn('Push will be sent immediately'); + logger.warn( + "Trying to schedule a push while server is not configured." + ); + logger.warn("Push will be sent immediately"); } } const data = body.data || {}; const payloadString = JSON.stringify(data); let pushHash; - if (typeof data.alert === 'string') { + if (typeof data.alert === "string") { pushHash = md5Hash(data.alert); - } else if (typeof data.alert === 'object') { + } else if (typeof data.alert === "object") { pushHash = md5Hash(JSON.stringify(data.alert)); } else { - pushHash = 'd41d8cd98f00b204e9800998ecf8427e'; + pushHash = "d41d8cd98f00b204e9800998ecf8427e"; } const object = { pushTime, @@ -186,11 +190,11 @@ export function pushStatusHandler(config, existingObjectId) { ); return handler.update( { - status: 'pending', + status: "pending", objectId: objectId, }, { - status: 'running', + status: "running", count: batches, } ); @@ -199,7 +203,8 @@ export function pushStatusHandler(config, existingObjectId) { const trackSent = function ( results, UTCOffset, - cleanupInstallations = process.env.PARSE_SERVER_CLEANUP_INVALID_INSTALLATIONS + cleanupInstallations = process.env + .PARSE_SERVER_CLEANUP_INVALID_INSTALLATIONS ) { const update = { numSent: 0, @@ -218,7 +223,7 @@ export function pushStatusHandler(config, existingObjectId) { ? `sentPerType.${deviceType}` : `failedPerType.${deviceType}`; memo[key] = incrementOp(memo, key); - if (typeof UTCOffset !== 'undefined') { + if (typeof UTCOffset !== "undefined") { const offsetKey = result.transmitted ? `sentPerUTCOffset.${UTCOffset}` : `failedPerUTCOffset.${UTCOffset}`; @@ -238,23 +243,24 @@ export function pushStatusHandler(config, existingObjectId) { const error = result.response.error; // GCM / FCM HTTP v1 API errors; see: // https://firebase.google.com/docs/reference/fcm/rest/v1/ErrorCode - if (error === 'NotRegistered' || error === 'InvalidRegistration') { + if (error === "NotRegistered" || error === "InvalidRegistration") { devicesToRemove.push(token); } // FCM API v2 errors; see: // https://firebase.google.com/docs/cloud-messaging/manage-tokens // https://github.com/firebase/functions-samples/blob/703c0359eacf07a551751d1319d34f912a2cd828/Node/fcm-notifications/functions/index.js#L89-L93C16 if ( - error?.code === 'messaging/registration-token-not-registered' || - error?.code === 'messaging/invalid-registration-token' || - (error?.code === 'messaging/invalid-argument' && - error?.message === 'The registration token is not a valid FCM registration token') + error?.code === "messaging/registration-token-not-registered" || + error?.code === "messaging/invalid-registration-token" || + (error?.code === "messaging/invalid-argument" && + error?.message === + "The registration token is not a valid FCM registration token") ) { devicesToRemove.push(token); } // APNS errors; see: // https://developer.apple.com/documentation/usernotifications/handling-notification-responses-from-apns - if (error === 'Unregistered' || error === 'BadDeviceToken') { + if (error === "Unregistered" || error === "BadDeviceToken") { devicesToRemove.push(token); } } @@ -272,10 +278,10 @@ export function pushStatusHandler(config, existingObjectId) { logger.verbose(`_PushStatus ${objectId}: needs cleanup`, { devicesToRemove, }); - ['numSent', 'numFailed'].forEach(key => { + ["numSent", "numFailed"].forEach(key => { if (update[key] > 0) { update[key] = { - __op: 'Increment', + __op: "Increment", amount: update[key], }; } else { @@ -284,19 +290,21 @@ export function pushStatusHandler(config, existingObjectId) { }); if (devicesToRemove.length > 0 && cleanupInstallations) { - logger.info(`Removing device tokens on ${devicesToRemove.length} _Installations`); + logger.info( + `Removing device tokens on ${devicesToRemove.length} _Installations` + ); database.update( - '_Installation', + "_Installation", { deviceToken: { $in: devicesToRemove } }, - { deviceToken: { __op: 'Delete' } }, + { deviceToken: { __op: "Delete" } }, { acl: undefined, many: true, } ); } - incrementOp(update, 'count', -1); - update.status = 'running'; + incrementOp(update, "count", -1); + update.status = "running"; return handler.update({ objectId }, update).then(res => { if (res && res.count === 0) { @@ -309,19 +317,19 @@ export function pushStatusHandler(config, existingObjectId) { return handler.update( { objectId }, { - status: 'succeeded', - count: { __op: 'Delete' }, + status: "succeeded", + count: { __op: "Delete" }, } ); }; const fail = function (err) { - if (typeof err === 'string') { + if (typeof err === "string") { err = { message: err }; } const update = { errorMessage: err, - status: 'failed', + status: "failed", }; return handler.update({ objectId }, update); }; @@ -335,7 +343,7 @@ export function pushStatusHandler(config, existingObjectId) { }; // define objectId to be dynamic - Object.defineProperty(rval, 'objectId', { + Object.defineProperty(rval, "objectId", { get: () => objectId, }); diff --git a/src/TestUtils.js b/src/TestUtils.js index 133fb08eed..29713639a2 100644 --- a/src/TestUtils.js +++ b/src/TestUtils.js @@ -1,5 +1,5 @@ -import AppCache from './cache'; -import SchemaCache from './Adapters/Cache/SchemaCache'; +import AppCache from "./cache"; +import SchemaCache from "./Adapters/Cache/SchemaCache"; /** * Destroys all data in the database @@ -7,7 +7,7 @@ import SchemaCache from './Adapters/Cache/SchemaCache'; */ export function destroyAllDataPermanently(fast) { if (!process.env.TESTING) { - throw 'Only supported in test environment'; + throw "Only supported in test environment"; } return Promise.all( Object.keys(AppCache.cache).map(appId => { @@ -54,7 +54,7 @@ export function getConnectionsCount(server) { } }); }); -}; +} export class Connections { constructor() { @@ -62,9 +62,9 @@ export class Connections { } track(server) { - server.on('connection', socket => { + server.on("connection", socket => { this.sockets.add(socket); - socket.on('close', () => { + socket.on("close", () => { this.sockets.delete(socket); }); }); diff --git a/src/Utils.js b/src/Utils.js index b676cbc248..acd03fa068 100644 --- a/src/Utils.js +++ b/src/Utils.js @@ -4,8 +4,8 @@ * @description General purpose utilities. */ -const path = require('path'); -const fs = require('fs').promises; +const path = require("path"); +const fs = require("fs").promises; /** * The general purpose utilities. @@ -59,7 +59,7 @@ class Utils { } // Check file for language exists - const language = locale.split('-')[0]; + const language = locale.split("-")[0]; const languagePath = path.join(basePath, language, file); const languageFileExists = await Utils.fileExists(languagePath); @@ -104,12 +104,12 @@ class Utils { * @param {Object} result * @returns {Object} The flattened object. **/ - static flattenObject(obj, parentKey, delimiter = '.', result = {}) { + static flattenObject(obj, parentKey, delimiter = ".", result = {}) { for (const key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { const newKey = parentKey ? parentKey + delimiter + key : key; - if (typeof obj[key] === 'object' && obj[key] !== null) { + if (typeof obj[key] === "object" && obj[key] !== null) { this.flattenObject(obj[key], newKey, delimiter, result); } else { result[newKey] = obj[key]; @@ -152,7 +152,12 @@ class Utils { * @param {Object} [current={}] The current result entry being composed. * @param {Array} [results=[]] The resulting array of permutations. */ - static getObjectKeyPermutations(object, index = 0, current = {}, results = []) { + static getObjectKeyPermutations( + object, + index = 0, + current = {}, + results = [] + ) { const keys = Object.keys(object); const key = keys[index]; const values = object[key]; @@ -209,24 +214,24 @@ class Utils { **/ static relativeTimeToDate(text, now = new Date()) { text = text.toLowerCase(); - let parts = text.split(' '); + let parts = text.split(" "); // Filter out whitespace - parts = parts.filter(part => part !== ''); + parts = parts.filter(part => part !== ""); - const future = parts[0] === 'in'; - const past = parts[parts.length - 1] === 'ago'; + const future = parts[0] === "in"; + const past = parts[parts.length - 1] === "ago"; - if (!future && !past && text !== 'now') { + if (!future && !past && text !== "now") { return { - status: 'error', + status: "error", info: "Time should either start with 'in' or end with 'ago'", }; } if (future && past) { return { - status: 'error', + status: "error", info: "Time cannot have both 'in' and 'ago'", }; } @@ -239,10 +244,10 @@ class Utils { parts = parts.slice(0, parts.length - 1); } - if (parts.length % 2 !== 0 && text !== 'now') { + if (parts.length % 2 !== 0 && text !== "now") { return { - status: 'error', - info: 'Invalid time string. Dangling unit or number.', + status: "error", + info: "Invalid time string. Dangling unit or number.", }; } @@ -256,56 +261,56 @@ class Utils { const val = Number(num); if (!Number.isInteger(val)) { return { - status: 'error', + status: "error", info: `'${num}' is not an integer.`, }; } switch (interval) { - case 'yr': - case 'yrs': - case 'year': - case 'years': + case "yr": + case "yrs": + case "year": + case "years": seconds += val * 31536000; // 365 * 24 * 60 * 60 break; - case 'wk': - case 'wks': - case 'week': - case 'weeks': + case "wk": + case "wks": + case "week": + case "weeks": seconds += val * 604800; // 7 * 24 * 60 * 60 break; - case 'd': - case 'day': - case 'days': + case "d": + case "day": + case "days": seconds += val * 86400; // 24 * 60 * 60 break; - case 'hr': - case 'hrs': - case 'hour': - case 'hours': + case "hr": + case "hrs": + case "hour": + case "hours": seconds += val * 3600; // 60 * 60 break; - case 'min': - case 'mins': - case 'minute': - case 'minutes': + case "min": + case "mins": + case "minute": + case "minutes": seconds += val * 60; break; - case 'sec': - case 'secs': - case 'second': - case 'seconds': + case "sec": + case "secs": + case "second": + case "seconds": seconds += val; break; default: return { - status: 'error', + status: "error", info: `Invalid interval: '${interval}'`, }; } @@ -314,20 +319,20 @@ class Utils { const milliseconds = seconds * 1000; if (future) { return { - status: 'success', - info: 'future', + status: "success", + info: "future", result: new Date(now.valueOf() + milliseconds), }; } else if (past) { return { - status: 'success', - info: 'past', + status: "success", + info: "past", result: new Date(now.valueOf() - milliseconds), }; } else { return { - status: 'success', - info: 'present', + status: "success", + info: "present", result: new Date(now.valueOf()), }; } @@ -341,7 +346,8 @@ class Utils { * @returns {Boolean} True if a match was found, false otherwise. */ static objectContainsKeyValue(obj, key, value) { - const isMatch = (a, b) => (typeof a === 'string' && new RegExp(b).test(a)) || a === b; + const isMatch = (a, b) => + (typeof a === "string" && new RegExp(b).test(a)) || a === b; const isKeyMatch = k => isMatch(k, key); const isValueMatch = v => isMatch(v, value); for (const [k, v] of Object.entries(obj)) { @@ -349,10 +355,19 @@ class Utils { return true; } else if (key === undefined && value !== undefined && isValueMatch(v)) { return true; - } else if (key !== undefined && value !== undefined && isKeyMatch(k) && isValueMatch(v)) { + } else if ( + key !== undefined && + value !== undefined && + isKeyMatch(k) && + isValueMatch(v) + ) { return true; } - if (['[object Object]', '[object Array]'].includes(Object.prototype.toString.call(v))) { + if ( + ["[object Object]", "[object Array]"].includes( + Object.prototype.toString.call(v) + ) + ) { return Utils.objectContainsKeyValue(v, key, value); } } @@ -363,7 +378,11 @@ class Utils { if (config?.requestKeywordDenylist) { // Scan request data for denied keywords for (const keyword of config.requestKeywordDenylist) { - const match = Utils.objectContainsKeyValue(data, keyword.key, keyword.value); + const match = Utils.objectContainsKeyValue( + data, + keyword.key, + keyword.value + ); if (match) { throw `Prohibited keyword in request data: ${JSON.stringify(keyword)}.`; } @@ -391,7 +410,7 @@ class Utils { * // Output: { a: 1, e: 4, c: 2, d: 3 } */ static addNestedKeysToRoot(obj, key) { - if (obj[key] && typeof obj[key] === 'object') { + if (obj[key] && typeof obj[key] === "object") { // Add nested keys to root Object.assign(obj, { ...obj[key] }); // Delete original nested key @@ -408,7 +427,7 @@ class Utils { static encodeForUrl(input) { return encodeURIComponent(input).replace( /[!'.()*]/g, - char => '%' + char.charCodeAt(0).toString(16).toUpperCase() + char => "%" + char.charCodeAt(0).toString(16).toUpperCase() ); } } diff --git a/src/batch.js b/src/batch.js index 80fa028cc6..5525348f30 100644 --- a/src/batch.js +++ b/src/batch.js @@ -1,11 +1,11 @@ -const Parse = require('parse/node').Parse; -const path = require('path'); +const Parse = require("parse/node").Parse; +const path = require("path"); // These methods handle batch requests. -const batchPath = '/batch'; +const batchPath = "/batch"; // Mounts a batch-handler onto a PromiseRouter. function mountOnto(router) { - router.route('POST', batchPath, req => { + router.route("POST", batchPath, req => { return handleBatch(router, req); }); } @@ -28,12 +28,19 @@ function makeBatchRoutingPathFunction(originalUrl, serverURL, publicServerURL) { const makeRoutablePath = function (requestPath) { // The routablePath is the path minus the api prefix if (requestPath.slice(0, apiPrefix.length) != apiPrefix) { - throw new Parse.Error(Parse.Error.INVALID_JSON, 'cannot route batch path ' + requestPath); + throw new Parse.Error( + Parse.Error.INVALID_JSON, + "cannot route batch path " + requestPath + ); } - return path.posix.join('/', requestPath.slice(apiPrefix.length)); + return path.posix.join("/", requestPath.slice(apiPrefix.length)); }; - if (serverURL && publicServerURL && serverURL.pathname != publicServerURL.pathname) { + if ( + serverURL && + publicServerURL && + serverURL.pathname != publicServerURL.pathname + ) { const localPath = serverURL.pathname; const publicPath = publicServerURL.pathname; @@ -51,7 +58,12 @@ function makeBatchRoutingPathFunction(originalUrl, serverURL, publicServerURL) { ? localPath.length : publicPath.length; - const newPath = path.posix.join('/', localPath, '/', requestPath.slice(pathLengthToUse)); + const newPath = path.posix.join( + "/", + localPath, + "/", + requestPath.slice(pathLengthToUse) + ); // Use the method for local routing return makeRoutablePath(newPath); @@ -65,7 +77,10 @@ function makeBatchRoutingPathFunction(originalUrl, serverURL, publicServerURL) { // TODO: pass along auth correctly function handleBatch(router, req) { if (!Array.isArray(req.body?.requests)) { - throw new Parse.Error(Parse.Error.INVALID_JSON, 'requests must be an array'); + throw new Parse.Error( + Parse.Error.INVALID_JSON, + "requests must be an array" + ); } // The batch paths are all from the root of our domain. @@ -74,7 +89,7 @@ function handleBatch(router, req) { // we need to figure out the API prefix, so that we can strip it // from all the subrequests. if (!req.originalUrl.endsWith(batchPath)) { - throw 'internal routing problem - expected url to end with batch'; + throw "internal routing problem - expected url to end with batch"; } const makeRoutablePath = makeBatchRoutingPathFunction( @@ -101,27 +116,33 @@ function handleBatch(router, req) { info: req.info, }; - return router.tryRouteRequest(restRequest.method, routablePath, request).then( - response => { - return { success: response.response }; - }, - error => { - return { error: { code: error.code, error: error.message } }; - } - ); + return router + .tryRouteRequest(restRequest.method, routablePath, request) + .then( + response => { + return { success: response.response }; + }, + error => { + return { error: { code: error.code, error: error.message } }; + } + ); }); return Promise.all(promises) .then(results => { if (req.body?.transaction === true) { - if (results.find(result => typeof result.error === 'object')) { - return req.config.database.abortTransactionalSession().then(() => { - return Promise.reject({ response: results }); - }); + if (results.find(result => typeof result.error === "object")) { + return req.config.database + .abortTransactionalSession() + .then(() => { + return Promise.reject({ response: results }); + }); } else { - return req.config.database.commitTransactionalSession().then(() => { - return { response: results }; - }); + return req.config.database + .commitTransactionalSession() + .then(() => { + return { response: results }; + }); } } else { return { response: results }; @@ -132,7 +153,9 @@ function handleBatch(router, req) { error && error.response && error.response.find( - errorItem => typeof errorItem.error === 'object' && errorItem.error.code === 251 + errorItem => + typeof errorItem.error === "object" && + errorItem.error.code === 251 ) && transactionRetries > 0 ) { diff --git a/src/cache.js b/src/cache.js index 71c01f4a14..7c7400e37b 100644 --- a/src/cache.js +++ b/src/cache.js @@ -1,4 +1,4 @@ -import { InMemoryCache } from './Adapters/Cache/InMemoryCache'; +import { InMemoryCache } from "./Adapters/Cache/InMemoryCache"; export var AppCache = new InMemoryCache({ ttl: NaN }); export default AppCache; diff --git a/src/cli/parse-live-query-server.js b/src/cli/parse-live-query-server.js index 525a202a26..edb2443ece 100644 --- a/src/cli/parse-live-query-server.js +++ b/src/cli/parse-live-query-server.js @@ -1,6 +1,6 @@ -import definitions from './definitions/parse-live-query-server'; -import runner from './utils/runner'; -import { ParseServer } from '../index'; +import definitions from "./definitions/parse-live-query-server"; +import runner from "./utils/runner"; +import { ParseServer } from "../index"; runner({ definitions, diff --git a/src/cli/parse-server.js b/src/cli/parse-server.js index b0c574bfea..0b20d23f30 100755 --- a/src/cli/parse-server.js +++ b/src/cli/parse-server.js @@ -1,70 +1,85 @@ /* eslint-disable no-console */ -import ParseServer from '../index'; -import definitions from './definitions/parse-server'; -import cluster from 'cluster'; -import os from 'os'; -import runner from './utils/runner'; +import ParseServer from "../index"; +import definitions from "./definitions/parse-server"; +import cluster from "cluster"; +import os from "os"; +import runner from "./utils/runner"; const help = function () { - console.log(' Get Started guide:'); - console.log(''); - console.log(' Please have a look at the get started guide!'); - console.log(' http://docs.parseplatform.org/parse-server/guide/'); - console.log(''); - console.log(''); - console.log(' Usage with npm start'); - console.log(''); - console.log(' $ npm start -- path/to/config.json'); - console.log(' $ npm start -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL'); - console.log(' $ npm start -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL'); - console.log(''); - console.log(''); - console.log(' Usage:'); - console.log(''); - console.log(' $ parse-server path/to/config.json'); - console.log(' $ parse-server -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL'); - console.log(' $ parse-server -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL'); - console.log(''); + console.log(" Get Started guide:"); + console.log(""); + console.log(" Please have a look at the get started guide!"); + console.log(" http://docs.parseplatform.org/parse-server/guide/"); + console.log(""); + console.log(""); + console.log(" Usage with npm start"); + console.log(""); + console.log(" $ npm start -- path/to/config.json"); + console.log( + " $ npm start -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL" + ); + console.log( + " $ npm start -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL" + ); + console.log(""); + console.log(""); + console.log(" Usage:"); + console.log(""); + console.log(" $ parse-server path/to/config.json"); + console.log( + " $ parse-server -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL" + ); + console.log( + " $ parse-server -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL" + ); + console.log(""); }; runner({ definitions, help, - usage: '[options] ', + usage: "[options] ", start: function (program, options, logOptions) { if (!options.appId || !options.masterKey) { program.outputHelp(); - console.error(''); - console.error('\u001b[31mERROR: appId and masterKey are required\u001b[0m'); - console.error(''); + console.error(""); + console.error( + "\u001b[31mERROR: appId and masterKey are required\u001b[0m" + ); + console.error(""); process.exit(1); } - if (options['liveQuery.classNames']) { + if (options["liveQuery.classNames"]) { options.liveQuery = options.liveQuery || {}; - options.liveQuery.classNames = options['liveQuery.classNames']; - delete options['liveQuery.classNames']; + options.liveQuery.classNames = options["liveQuery.classNames"]; + delete options["liveQuery.classNames"]; } - if (options['liveQuery.redisURL']) { + if (options["liveQuery.redisURL"]) { options.liveQuery = options.liveQuery || {}; - options.liveQuery.redisURL = options['liveQuery.redisURL']; - delete options['liveQuery.redisURL']; + options.liveQuery.redisURL = options["liveQuery.redisURL"]; + delete options["liveQuery.redisURL"]; } - if (options['liveQuery.redisOptions']) { + if (options["liveQuery.redisOptions"]) { options.liveQuery = options.liveQuery || {}; - options.liveQuery.redisOptions = options['liveQuery.redisOptions']; - delete options['liveQuery.redisOptions']; + options.liveQuery.redisOptions = options["liveQuery.redisOptions"]; + delete options["liveQuery.redisOptions"]; } if (options.cluster) { - const numCPUs = typeof options.cluster === 'number' ? options.cluster : os.cpus().length; + const numCPUs = + typeof options.cluster === "number" + ? options.cluster + : os.cpus().length; if (cluster.isMaster) { logOptions(); for (let i = 0; i < numCPUs; i++) { cluster.fork(); } - cluster.on('exit', (worker, code) => { - console.log(`worker ${worker.process.pid} died (${code})... Restarting`); + cluster.on("exit", (worker, code) => { + console.log( + `worker ${worker.process.pid} died (${code})... Restarting` + ); cluster.fork(); }); } else { @@ -81,7 +96,7 @@ runner({ ParseServer.startApp(options) .then(() => { logOptions(); - console.log(''); + console.log(""); printSuccessMessage(); }) .catch(e => { @@ -91,21 +106,23 @@ runner({ } function printSuccessMessage() { - console.log('[' + process.pid + '] parse-server running on ' + options.serverURL); + console.log( + "[" + process.pid + "] parse-server running on " + options.serverURL + ); if (options.mountGraphQL) { console.log( - '[' + + "[" + process.pid + - '] GraphQL running on http://localhost:' + + "] GraphQL running on http://localhost:" + options.port + options.graphQLPath ); } if (options.mountPlayground) { console.log( - '[' + + "[" + process.pid + - '] Playground running on http://localhost:' + + "] Playground running on http://localhost:" + options.port + options.playgroundPath ); diff --git a/src/cloud-code/Parse.Cloud.js b/src/cloud-code/Parse.Cloud.js index c2389b5e85..11250bbb24 100644 --- a/src/cloud-code/Parse.Cloud.js +++ b/src/cloud-code/Parse.Cloud.js @@ -1,28 +1,31 @@ -import { Parse } from 'parse/node'; -import * as triggers from '../triggers'; -import { addRateLimit } from '../middlewares'; -const Config = require('../Config'); +import { Parse } from "parse/node"; +import * as triggers from "../triggers"; +import { addRateLimit } from "../middlewares"; +const Config = require("../Config"); function isParseObjectConstructor(object) { - return typeof object === 'function' && Object.prototype.hasOwnProperty.call(object, 'className'); + return ( + typeof object === "function" && + Object.prototype.hasOwnProperty.call(object, "className") + ); } function validateValidator(validator) { - if (!validator || typeof validator === 'function') { + if (!validator || typeof validator === "function") { return; } const fieldOptions = { - type: ['Any'], + type: ["Any"], constant: [Boolean], - default: ['Any'], - options: [Array, 'function', 'Any'], + default: ["Any"], + options: [Array, "function", "Any"], required: [Boolean], error: [String], }; const allowedKeys = { requireUser: [Boolean], - requireAnyUserRoles: [Array, 'function'], - requireAllUserRoles: [Array, 'function'], + requireAnyUserRoles: [Array, "function"], + requireAllUserRoles: [Array, "function"], requireMaster: [Boolean], validateMasterKey: [Boolean], skipWithMasterKey: [Boolean], @@ -32,15 +35,15 @@ function validateValidator(validator) { }; const getType = fn => { if (Array.isArray(fn)) { - return 'array'; + return "array"; } - if (fn === 'Any' || fn === 'function') { + if (fn === "Any" || fn === "function") { return fn; } const type = typeof fn; - if (typeof fn === 'function') { + if (typeof fn === "function") { const match = fn && fn.toString().match(/^\s*function (\w+)/); - return (match ? match[1] : 'function').toLowerCase(); + return (match ? match[1] : "function").toLowerCase(); } return type; }; @@ -51,15 +54,15 @@ function validateValidator(validator) { } const types = parameter.map(type => getType(type)); const type = getType(validatorParam); - if (!types.includes(type) && !types.includes('Any')) { + if (!types.includes(type) && !types.includes("Any")) { throw `Invalid type for Cloud Function validation key ${key}. Expected ${types.join( - '|' + "|" )}, actual ${type}`; } }; for (const key in validator) { checkKey(key, allowedKeys, validator[key]); - if (key === 'fields' || key === 'requireUserKeys') { + if (key === "fields" || key === "requireUserKeys") { const values = validator[key]; if (Array.isArray(values)) { continue; @@ -76,15 +79,15 @@ function validateValidator(validator) { const getRoute = parseClass => { const route = { - _User: 'users', - _Session: 'sessions', - '@File': 'files', - '@Config': 'config', - }[parseClass] || 'classes'; - if (parseClass === '@File') { + _User: "users", + _Session: "sessions", + "@File": "files", + "@Config": "config", + }[parseClass] || "classes"; + if (parseClass === "@File") { return `/${route}/:id?(.*)`; } - if (parseClass === '@Config') { + if (parseClass === "@Config") { return `/${route}`; } return `/${route}/${parseClass}/:id?(.*)`; @@ -127,10 +130,18 @@ var ParseCloud = {}; */ ParseCloud.define = function (functionName, handler, validationHandler) { validateValidator(validationHandler); - triggers.addFunction(functionName, handler, validationHandler, Parse.applicationId); + triggers.addFunction( + functionName, + handler, + validationHandler, + Parse.applicationId + ); if (validationHandler && validationHandler.rateLimit) { addRateLimit( - { requestPath: `/functions/${functionName}`, ...validationHandler.rateLimit }, + { + requestPath: `/functions/${functionName}`, + ...validationHandler.rateLimit, + }, Parse.applicationId, true ); @@ -192,7 +203,7 @@ ParseCloud.beforeSave = function (parseClass, handler, validationHandler) { addRateLimit( { requestPath: getRoute(className), - requestMethods: ['POST', 'PUT'], + requestMethods: ["POST", "PUT"], ...validationHandler.rateLimit, }, Parse.applicationId, @@ -239,7 +250,7 @@ ParseCloud.beforeDelete = function (parseClass, handler, validationHandler) { addRateLimit( { requestPath: getRoute(className), - requestMethods: 'DELETE', + requestMethods: "DELETE", ...validationHandler.rateLimit, }, Parse.applicationId, @@ -272,18 +283,27 @@ ParseCloud.beforeDelete = function (parseClass, handler, validationHandler) { * @param {Function} func The function to run before a login. This function can be async and should take one parameter a {@link Parse.Cloud.TriggerRequest}; */ ParseCloud.beforeLogin = function (handler, validationHandler) { - let className = '_User'; - if (typeof handler === 'string' || isParseObjectConstructor(handler)) { + let className = "_User"; + if (typeof handler === "string" || isParseObjectConstructor(handler)) { // validation will occur downstream, this is to maintain internal // code consistency with the other hook types. className = triggers.getClassName(handler); handler = arguments[1]; validationHandler = arguments.length >= 2 ? arguments[2] : null; } - triggers.addTrigger(triggers.Types.beforeLogin, className, handler, Parse.applicationId); + triggers.addTrigger( + triggers.Types.beforeLogin, + className, + handler, + Parse.applicationId + ); if (validationHandler && validationHandler.rateLimit) { addRateLimit( - { requestPath: `/login`, requestMethods: 'POST', ...validationHandler.rateLimit }, + { + requestPath: `/login`, + requestMethods: "POST", + ...validationHandler.rateLimit, + }, Parse.applicationId, true ); @@ -310,14 +330,19 @@ ParseCloud.beforeLogin = function (handler, validationHandler) { * @param {Function} func The function to run after a login. This function can be async and should take one parameter a {@link Parse.Cloud.TriggerRequest}; */ ParseCloud.afterLogin = function (handler) { - let className = '_User'; - if (typeof handler === 'string' || isParseObjectConstructor(handler)) { + let className = "_User"; + if (typeof handler === "string" || isParseObjectConstructor(handler)) { // validation will occur downstream, this is to maintain internal // code consistency with the other hook types. className = triggers.getClassName(handler); handler = arguments[1]; } - triggers.addTrigger(triggers.Types.afterLogin, className, handler, Parse.applicationId); + triggers.addTrigger( + triggers.Types.afterLogin, + className, + handler, + Parse.applicationId + ); }; /** @@ -339,14 +364,19 @@ ParseCloud.afterLogin = function (handler) { * @param {Function} func The function to run after a logout. This function can be async and should take one parameter a {@link Parse.Cloud.TriggerRequest}; */ ParseCloud.afterLogout = function (handler) { - let className = '_Session'; - if (typeof handler === 'string' || isParseObjectConstructor(handler)) { + let className = "_Session"; + if (typeof handler === "string" || isParseObjectConstructor(handler)) { // validation will occur downstream, this is to maintain internal // code consistency with the other hook types. className = triggers.getClassName(handler); handler = arguments[1]; } - triggers.addTrigger(triggers.Types.afterLogout, className, handler, Parse.applicationId); + triggers.addTrigger( + triggers.Types.afterLogout, + className, + handler, + Parse.applicationId + ); }; /** @@ -460,7 +490,7 @@ ParseCloud.beforeFind = function (parseClass, handler, validationHandler) { addRateLimit( { requestPath: getRoute(className), - requestMethods: 'GET', + requestMethods: "GET", ...validationHandler.rateLimit, }, Parse.applicationId, @@ -561,7 +591,7 @@ ParseCloud.sendEmail = function (data) { const emailAdapter = config.userController.adapter; if (!emailAdapter) { config.loggerController.error( - 'Failed to send email because no mail adapter is configured for Parse Server.' + "Failed to send email because no mail adapter is configured for Parse Server." ); return; } @@ -631,7 +661,11 @@ ParseCloud.onLiveQueryEvent = function (handler) { * @param {Function} func The function to run after a live query event. This function can be async and should take one parameter, a {@link Parse.Cloud.LiveQueryEventTrigger}. * @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.LiveQueryEventTrigger}, or a {@link Parse.Cloud.ValidatorObject}. */ -ParseCloud.afterLiveQueryEvent = function (parseClass, handler, validationHandler) { +ParseCloud.afterLiveQueryEvent = function ( + parseClass, + handler, + validationHandler +) { const className = triggers.getClassName(parseClass); validateValidator(validationHandler); triggers.addTrigger( @@ -652,7 +686,7 @@ ParseCloud._removeAllHooks = () => { ParseCloud.useMasterKey = () => { // eslint-disable-next-line console.warn( - 'Parse.Cloud.useMasterKey is deprecated (and has no effect anymore) on parse-server, please refer to the cloud code migration notes: http://docs.parseplatform.org/parse-server/guide/#master-key-must-be-passed-explicitly' + "Parse.Cloud.useMasterKey is deprecated (and has no effect anymore) on parse-server, please refer to the cloud code migration notes: http://docs.parseplatform.org/parse-server/guide/#master-key-must-be-passed-explicitly" ); }; diff --git a/src/cloud-code/Parse.Server.js b/src/cloud-code/Parse.Server.js index 71295618f2..607a3e850c 100644 --- a/src/cloud-code/Parse.Server.js +++ b/src/cloud-code/Parse.Server.js @@ -10,10 +10,10 @@ const ParseServer = {}; * ... */ ParseServer.RateLimitZone = Object.freeze({ - global: 'global', - session: 'session', - user: 'user', - ip: 'ip', + global: "global", + session: "session", + user: "user", + ip: "ip", }); module.exports = ParseServer; diff --git a/src/cryptoUtils.js b/src/cryptoUtils.js index f85b62c349..bde97ceebd 100644 --- a/src/cryptoUtils.js +++ b/src/cryptoUtils.js @@ -1,16 +1,16 @@ /* @flow */ -import { randomBytes, createHash } from 'crypto'; +import { randomBytes, createHash } from "crypto"; // Returns a new random hex string of the given even size. export function randomHexString(size: number): string { if (size === 0) { - throw new Error('Zero-length randomHexString is useless.'); + throw new Error("Zero-length randomHexString is useless."); } if (size % 2 !== 0) { - throw new Error('randomHexString size must be divisible by 2.'); + throw new Error("randomHexString size must be divisible by 2."); } - return randomBytes(size / 2).toString('hex'); + return randomBytes(size / 2).toString("hex"); } // Returns a new random alphanumeric string of the given size. @@ -21,10 +21,11 @@ export function randomHexString(size: number): string { // length is long enough and doesn't need to be uniform. export function randomString(size: number): string { if (size === 0) { - throw new Error('Zero-length randomString is useless.'); + throw new Error("Zero-length randomString is useless."); } - const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + 'abcdefghijklmnopqrstuvwxyz' + '0123456789'; - let objectId = ''; + const chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789"; + let objectId = ""; const bytes = randomBytes(size); for (let i = 0; i < bytes.length; ++i) { objectId += chars[bytes.readUInt8(i) % chars.length]; @@ -43,5 +44,5 @@ export function newToken(): string { } export function md5Hash(string: string): string { - return createHash('md5').update(string).digest('hex'); + return createHash("md5").update(string).digest("hex"); } diff --git a/src/defaults.js b/src/defaults.js index a2b105d8db..7d8c21166a 100644 --- a/src/defaults.js +++ b/src/defaults.js @@ -1,9 +1,9 @@ -import { nullParser } from './Options/parsers'; -const { ParseServerOptions } = require('./Options/Definitions'); +import { nullParser } from "./Options/parsers"; +const { ParseServerOptions } = require("./Options/Definitions"); const logsFolder = (() => { - let folder = './logs/'; - if (typeof process !== 'undefined' && process.env.TESTING === '1') { - folder = './test_logs/'; + let folder = "./logs/"; + if (typeof process !== "undefined" && process.env.TESTING === "1") { + folder = "./test_logs/"; } if (process.env.PARSE_SERVER_LOGS_FOLDER) { folder = nullParser(process.env.PARSE_SERVER_LOGS_FOLDER); @@ -13,16 +13,19 @@ const logsFolder = (() => { const { verbose, level } = (() => { const verbose = process.env.VERBOSE ? true : false; - return { verbose, level: verbose ? 'verbose' : undefined }; + return { verbose, level: verbose ? "verbose" : undefined }; })(); -const DefinitionDefaults = Object.keys(ParseServerOptions).reduce((memo, key) => { - const def = ParseServerOptions[key]; - if (Object.prototype.hasOwnProperty.call(def, 'default')) { - memo[key] = def.default; - } - return memo; -}, {}); +const DefinitionDefaults = Object.keys(ParseServerOptions).reduce( + (memo, key) => { + const def = ParseServerOptions[key]; + if (Object.prototype.hasOwnProperty.call(def, "default")) { + memo[key] = def.default; + } + return memo; + }, + {} +); const computedDefaults = { jsonLogs: process.env.JSON_LOGS || false, diff --git a/src/middlewares.js b/src/middlewares.js index 72b634219e..8e25cfac12 100644 --- a/src/middlewares.js +++ b/src/middlewares.js @@ -1,50 +1,50 @@ -import AppCache from './cache'; -import Parse from 'parse/node'; -import auth from './Auth'; -import Config from './Config'; -import ClientSDK from './ClientSDK'; -import defaultLogger from './logger'; -import rest from './rest'; -import MongoStorageAdapter from './Adapters/Storage/Mongo/MongoStorageAdapter'; -import PostgresStorageAdapter from './Adapters/Storage/Postgres/PostgresStorageAdapter'; -import rateLimit from 'express-rate-limit'; -import { RateLimitOptions } from './Options/Definitions'; -import { pathToRegexp } from 'path-to-regexp'; -import RedisStore from 'rate-limit-redis'; -import { createClient } from 'redis'; -import { BlockList, isIPv4 } from 'net'; +import AppCache from "./cache"; +import Parse from "parse/node"; +import auth from "./Auth"; +import Config from "./Config"; +import ClientSDK from "./ClientSDK"; +import defaultLogger from "./logger"; +import rest from "./rest"; +import MongoStorageAdapter from "./Adapters/Storage/Mongo/MongoStorageAdapter"; +import PostgresStorageAdapter from "./Adapters/Storage/Postgres/PostgresStorageAdapter"; +import rateLimit from "express-rate-limit"; +import { RateLimitOptions } from "./Options/Definitions"; +import { pathToRegexp } from "path-to-regexp"; +import RedisStore from "rate-limit-redis"; +import { createClient } from "redis"; +import { BlockList, isIPv4 } from "net"; export const DEFAULT_ALLOWED_HEADERS = - 'X-Parse-Master-Key, X-Parse-REST-API-Key, X-Parse-Javascript-Key, X-Parse-Application-Id, X-Parse-Client-Version, X-Parse-Session-Token, X-Requested-With, X-Parse-Revocable-Session, X-Parse-Request-Id, Content-Type, Pragma, Cache-Control'; + "X-Parse-Master-Key, X-Parse-REST-API-Key, X-Parse-Javascript-Key, X-Parse-Application-Id, X-Parse-Client-Version, X-Parse-Session-Token, X-Requested-With, X-Parse-Revocable-Session, X-Parse-Request-Id, Content-Type, Pragma, Cache-Control"; const getMountForRequest = function (req) { const mountPathLength = req.originalUrl.length - req.url.length; const mountPath = req.originalUrl.slice(0, mountPathLength); - return req.protocol + '://' + req.get('host') + mountPath; + return req.protocol + "://" + req.get("host") + mountPath; }; const getBlockList = (ipRangeList, store) => { - if (store.get('blockList')) { - return store.get('blockList'); + if (store.get("blockList")) { + return store.get("blockList"); } const blockList = new BlockList(); ipRangeList.forEach(fullIp => { - if (fullIp === '::/0' || fullIp === '::') { - store.set('allowAllIpv6', true); + if (fullIp === "::/0" || fullIp === "::") { + store.set("allowAllIpv6", true); return; } - if (fullIp === '0.0.0.0/0' || fullIp === '0.0.0.0') { - store.set('allowAllIpv4', true); + if (fullIp === "0.0.0.0/0" || fullIp === "0.0.0.0") { + store.set("allowAllIpv4", true); return; } - const [ip, mask] = fullIp.split('/'); + const [ip, mask] = fullIp.split("/"); if (!mask) { - blockList.addAddress(ip, isIPv4(ip) ? 'ipv4' : 'ipv6'); + blockList.addAddress(ip, isIPv4(ip) ? "ipv4" : "ipv6"); } else { - blockList.addSubnet(ip, Number(mask), isIPv4(ip) ? 'ipv4' : 'ipv6'); + blockList.addSubnet(ip, Number(mask), isIPv4(ip) ? "ipv4" : "ipv6"); } }); - store.set('blockList', blockList); + store.set("blockList", blockList); return blockList; }; @@ -55,13 +55,13 @@ export const checkIp = (ip, ipRangeList, store) => { if (store.get(ip)) { return true; } - if (store.get('allowAllIpv4') && incomingIpIsV4) { + if (store.get("allowAllIpv4") && incomingIpIsV4) { return true; } - if (store.get('allowAllIpv6') && !incomingIpIsV4) { + if (store.get("allowAllIpv6") && !incomingIpIsV4) { return true; } - const result = blockList.check(ip, incomingIpIsV4 ? 'ipv4' : 'ipv6'); + const result = blockList.check(ip, incomingIpIsV4 ? "ipv4" : "ipv6"); // If the ip is in the list, we store the result in the store // so we have a optimized path for the next request @@ -81,27 +81,27 @@ export async function handleParseHeaders(req, res, next) { var mount = getMountForRequest(req); let context = {}; - if (req.get('X-Parse-Cloud-Context') != null) { + if (req.get("X-Parse-Cloud-Context") != null) { try { - context = JSON.parse(req.get('X-Parse-Cloud-Context')); - if (Object.prototype.toString.call(context) !== '[object Object]') { - throw 'Context is not an object'; + context = JSON.parse(req.get("X-Parse-Cloud-Context")); + if (Object.prototype.toString.call(context) !== "[object Object]") { + throw "Context is not an object"; } } catch (e) { return malformedContext(req, res); } } var info = { - appId: req.get('X-Parse-Application-Id'), - sessionToken: req.get('X-Parse-Session-Token'), - masterKey: req.get('X-Parse-Master-Key'), - maintenanceKey: req.get('X-Parse-Maintenance-Key'), - installationId: req.get('X-Parse-Installation-Id'), - clientKey: req.get('X-Parse-Client-Key'), - javascriptKey: req.get('X-Parse-Javascript-Key'), - dotNetKey: req.get('X-Parse-Windows-Key'), - restAPIKey: req.get('X-Parse-REST-API-Key'), - clientVersion: req.get('X-Parse-Client-Version'), + appId: req.get("X-Parse-Application-Id"), + sessionToken: req.get("X-Parse-Session-Token"), + masterKey: req.get("X-Parse-Master-Key"), + maintenanceKey: req.get("X-Parse-Maintenance-Key"), + installationId: req.get("X-Parse-Installation-Id"), + clientKey: req.get("X-Parse-Client-Key"), + javascriptKey: req.get("X-Parse-Javascript-Key"), + dotNetKey: req.get("X-Parse-Windows-Key"), + restAPIKey: req.get("X-Parse-REST-API-Key"), + clientVersion: req.get("X-Parse-Client-Version"), context: context, }; @@ -148,10 +148,11 @@ export async function handleParseHeaders(req, res, next) { req.body && req.body._ApplicationId && AppCache.get(req.body._ApplicationId) && - (!info.masterKey || AppCache.get(req.body._ApplicationId).masterKey === info.masterKey) + (!info.masterKey || + AppCache.get(req.body._ApplicationId).masterKey === info.masterKey) ) { info.appId = req.body._ApplicationId; - info.javascriptKey = req.body._JavaScriptKey || ''; + info.javascriptKey = req.body._JavaScriptKey || ""; delete req.body._ApplicationId; delete req.body._JavaScriptKey; // TODO: test that the REST API formats generated by the other @@ -178,8 +179,10 @@ export async function handleParseHeaders(req, res, next) { } else { try { info.context = JSON.parse(req.body._context); - if (Object.prototype.toString.call(info.context) !== '[object Object]') { - throw 'Context is not an object'; + if ( + Object.prototype.toString.call(info.context) !== "[object Object]" + ) { + throw "Context is not an object"; } } catch (e) { return malformedContext(req, res); @@ -188,7 +191,7 @@ export async function handleParseHeaders(req, res, next) { delete req.body._context; } if (req.body._ContentType) { - req.headers['content-type'] = req.body._ContentType; + req.headers["content-type"] = req.body._ContentType; delete req.body._ContentType; } } else { @@ -196,7 +199,7 @@ export async function handleParseHeaders(req, res, next) { } } - if (info.sessionToken && typeof info.sessionToken !== 'string') { + if (info.sessionToken && typeof info.sessionToken !== "string") { info.sessionToken = info.sessionToken.toString(); } @@ -208,12 +211,12 @@ export async function handleParseHeaders(req, res, next) { req.fileData = req.body.fileData; // We need to repopulate req.body with a buffer var base64 = req.body.base64; - req.body = Buffer.from(base64, 'base64'); + req.body = Buffer.from(base64, "base64"); } const clientIp = getClientIp(req); const config = Config.get(info.appId, mount); - if (config.state && config.state !== 'ok') { + if (config.state && config.state !== "ok") { res.status(500); res.json({ code: Parse.Error.INTERNAL_SERVER_ERROR, @@ -229,9 +232,16 @@ export async function handleParseHeaders(req, res, next) { req.info = info; const isMaintenance = - req.config.maintenanceKey && info.maintenanceKey === req.config.maintenanceKey; + req.config.maintenanceKey && + info.maintenanceKey === req.config.maintenanceKey; if (isMaintenance) { - if (checkIp(clientIp, req.config.maintenanceKeyIps || [], req.config.maintenanceKeyIpsStore)) { + if ( + checkIp( + clientIp, + req.config.maintenanceKeyIps || [], + req.config.maintenanceKeyIpsStore + ) + ) { req.auth = new auth.Auth({ config: req.config, installationId: info.installationId, @@ -249,7 +259,14 @@ export async function handleParseHeaders(req, res, next) { const masterKey = await req.config.loadMasterKey(); let isMaster = info.masterKey === masterKey; - if (isMaster && !checkIp(clientIp, req.config.masterKeyIps || [], req.config.masterKeyIpsStore)) { + if ( + isMaster && + !checkIp( + clientIp, + req.config.masterKeyIps || [], + req.config.masterKeyIpsStore + ) + ) { const log = req.config?.loggerController || defaultLogger; log.error( `Request using master key rejected as the request IP address '${clientIp}' is not set in Parse Server option 'masterKeyIps'.` @@ -272,7 +289,7 @@ export async function handleParseHeaders(req, res, next) { var isReadOnlyMaster = info.masterKey === req.config.readOnlyMasterKey; if ( - typeof req.config.readOnlyMasterKey != 'undefined' && + typeof req.config.readOnlyMasterKey != "undefined" && req.config.readOnlyMasterKey && isReadOnlyMaster ) { @@ -287,7 +304,7 @@ export async function handleParseHeaders(req, res, next) { // Client keys are not required in parse-server, but if any have been configured in the server, validate them // to preserve original behavior. - const keys = ['clientKey', 'javascriptKey', 'dotNetKey', 'restAPIKey']; + const keys = ["clientKey", "javascriptKey", "dotNetKey", "restAPIKey"]; const oneKeyConfigured = keys.some(function (key) { return req.config[key] !== undefined; }); @@ -299,7 +316,7 @@ export async function handleParseHeaders(req, res, next) { return invalidRequest(req, res); } - if (req.url == '/login') { + if (req.url == "/login") { delete info.sessionToken; } @@ -336,7 +353,7 @@ const handleRateLimit = async (req, res, next) => { throw err; } req.config.loggerController.error( - 'An unknown error occured when attempting to apply the rate limiter: ', + "An unknown error occured when attempting to apply the rate limiter: ", err ); } @@ -355,15 +372,15 @@ const handleRateLimit = async (req, res, next) => { export const handleParseSession = async (req, res, next) => { try { const info = req.info; - if (req.auth || req.url === '/sessions/me') { + if (req.auth || req.url === "/sessions/me") { next(); return; } let requestAuth = null; if ( info.sessionToken && - req.url === '/upgradeToRevocableSession' && - info.sessionToken.indexOf('r:') != 0 + req.url === "/upgradeToRevocableSession" && + info.sessionToken.indexOf("r:") != 0 ) { requestAuth = await auth.getAuthForLegacySessionToken({ config: req.config, @@ -385,7 +402,10 @@ export const handleParseSession = async (req, res, next) => { return; } // TODO: Determine the correct error scenario. - req.config.loggerController.error('error getting auth for sessionToken', error); + req.config.loggerController.error( + "error getting auth for sessionToken", + error + ); throw new Parse.Error(Parse.Error.UNKNOWN_ERROR, error); } }; @@ -403,19 +423,19 @@ function httpAuth(req) { var appId, masterKey, javascriptKey; // parse header - var authPrefix = 'basic '; + var authPrefix = "basic "; var match = header.toLowerCase().indexOf(authPrefix); if (match == 0) { var encodedAuth = header.substring(authPrefix.length, header.length); - var credentials = decodeBase64(encodedAuth).split(':'); + var credentials = decodeBase64(encodedAuth).split(":"); if (credentials.length == 2) { appId = credentials[0]; var key = credentials[1]; - var jsKeyPrefix = 'javascript-key='; + var jsKeyPrefix = "javascript-key="; var matchKey = key.indexOf(jsKeyPrefix); if (matchKey == 0) { @@ -430,7 +450,7 @@ function httpAuth(req) { } function decodeBase64(str) { - return Buffer.from(str, 'base64').toString(); + return Buffer.from(str, "base64").toString(); } export function allowCrossDomain(appId) { @@ -438,22 +458,27 @@ export function allowCrossDomain(appId) { const config = Config.get(appId, getMountForRequest(req)); let allowHeaders = DEFAULT_ALLOWED_HEADERS; if (config && config.allowHeaders) { - allowHeaders += `, ${config.allowHeaders.join(', ')}`; + allowHeaders += `, ${config.allowHeaders.join(", ")}`; } const baseOrigins = - typeof config?.allowOrigin === 'string' + typeof config?.allowOrigin === "string" ? [config.allowOrigin] - : (config?.allowOrigin ?? ['*']); + : (config?.allowOrigin ?? ["*"]); const requestOrigin = req.headers.origin; const allowOrigins = - requestOrigin && baseOrigins.includes(requestOrigin) ? requestOrigin : baseOrigins[0]; - res.header('Access-Control-Allow-Origin', allowOrigins); - res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS'); - res.header('Access-Control-Allow-Headers', allowHeaders); - res.header('Access-Control-Expose-Headers', 'X-Parse-Job-Status-Id, X-Parse-Push-Status-Id'); + requestOrigin && baseOrigins.includes(requestOrigin) + ? requestOrigin + : baseOrigins[0]; + res.header("Access-Control-Allow-Origin", allowOrigins); + res.header("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE,OPTIONS"); + res.header("Access-Control-Allow-Headers", allowHeaders); + res.header( + "Access-Control-Expose-Headers", + "X-Parse-Job-Status-Id, X-Parse-Push-Status-Id" + ); // intercept OPTIONS method - if ('OPTIONS' == req.method) { + if ("OPTIONS" == req.method) { res.sendStatus(200); } else { next(); @@ -462,7 +487,7 @@ export function allowCrossDomain(appId) { } export function allowMethodOverride(req, res, next) { - if (req.method === 'POST' && req.body?._method) { + if (req.method === "POST" && req.body?._method) { req.originalMethod = req.method; req.method = req.body._method; delete req.body._method; @@ -490,7 +515,7 @@ export function handleParseErrors(err, req, res, next) { } res.status(httpStatus); res.json({ code: err.code, error: err.message }); - log.error('Parse error: ', err); + log.error("Parse error: ", err); } else if (err.status && err.message) { res.status(err.status); res.json({ error: err.message }); @@ -498,11 +523,11 @@ export function handleParseErrors(err, req, res, next) { next(err); } } else { - log.error('Uncaught internal server error.', err, err.stack); + log.error("Uncaught internal server error.", err, err.stack); res.status(500); res.json({ code: Parse.Error.INTERNAL_SERVER_ERROR, - message: 'Internal server error.', + message: "Internal server error.", }); if (!(process && process.env.TESTING)) { next(err); @@ -523,14 +548,14 @@ export function promiseEnforceMasterKeyAccess(request) { if (!request.auth.isMaster) { const error = new Error(); error.status = 403; - error.message = 'unauthorized: master key is required'; + error.message = "unauthorized: master key is required"; throw error; } return Promise.resolve(); } export const addRateLimit = (route, config, cloud) => { - if (typeof config === 'string') { + if (typeof config === "string") { config = Config.get(config); } for (const key in route) { @@ -550,12 +575,12 @@ export const addRateLimit = (route, config, cloud) => { const client = createClient({ url: route.redisUrl, }); - client.on('error', err => { - log.error('Middlewares addRateLimit Redis client error', { error: err }); + client.on("error", err => { + log.error("Middlewares addRateLimit Redis client error", { error: err }); }); - client.on('connect', () => {}); - client.on('reconnecting', () => {}); - client.on('ready', () => {}); + client.on("connect", () => {}); + client.on("reconnecting", () => {}); + client.on("ready", () => {}); redisStore.connectionPromise = async () => { if (client.isOpen) { return; @@ -574,16 +599,18 @@ export const addRateLimit = (route, config, cloud) => { }, }); } - let transformPath = route.requestPath.split('/*').join('/(.*)'); - if (transformPath === '*') { - transformPath = '(.*)'; + let transformPath = route.requestPath.split("/*").join("/(.*)"); + if (transformPath === "*") { + transformPath = "(.*)"; } config.rateLimits.push({ path: pathToRegexp(transformPath), handler: rateLimit({ windowMs: route.requestTimeWindow, max: route.requestCount, - message: route.errorResponseMessage || RateLimitOptions.errorResponseMessage.default, + message: + route.errorResponseMessage || + RateLimitOptions.errorResponseMessage.default, handler: (request, response, next, options) => { throw { code: Parse.Error.CONNECTION_FAILED, @@ -591,7 +618,7 @@ export const addRateLimit = (route, config, cloud) => { }; }, skip: request => { - if (request.ip === '127.0.0.1' && !route.includeInternalRequests) { + if (request.ip === "127.0.0.1" && !route.includeInternalRequests) { return true; } if (route.includeMasterKey) { @@ -621,9 +648,11 @@ export const addRateLimit = (route, config, cloud) => { } if (route.zone === Parse.Server.RateLimitZone.user && token) { if (!request.auth) { - await new Promise(resolve => handleParseSession(request, null, resolve)); + await new Promise(resolve => + handleParseSession(request, null, resolve) + ); } - if (request.auth?.user?.id && request.zone === 'user') { + if (request.auth?.user?.id && request.zone === "user") { return request.auth.user.id; } } @@ -654,19 +683,19 @@ export function promiseEnsureIdempotency(req) { } // Get parameters const config = req.config; - const requestId = ((req || {}).headers || {})['x-parse-request-id']; + const requestId = ((req || {}).headers || {})["x-parse-request-id"]; const { paths, ttl } = config.idempotencyOptions; if (!requestId || !config.idempotencyOptions) { return Promise.resolve(); } // Request path may contain trailing slashes, depending on the original request, so remove // leading and trailing slashes to make it easier to specify paths in the configuration - const reqPath = req.path.replace(/^\/|\/$/, ''); + const reqPath = req.path.replace(/^\/|\/$/, ""); // Determine whether idempotency is enabled for current request path let match = false; for (const path of paths) { // Assume one wants a path to always match from the beginning to prevent any mistakes - const regex = new RegExp(path.charAt(0) === '^' ? path : '^' + path); + const regex = new RegExp(path.charAt(0) === "^" ? path : "^" + path); if (reqPath.match(regex)) { match = true; break; @@ -676,15 +705,20 @@ export function promiseEnsureIdempotency(req) { return Promise.resolve(); } // Try to store request - const expiryDate = new Date(new Date().setSeconds(new Date().getSeconds() + ttl)); + const expiryDate = new Date( + new Date().setSeconds(new Date().getSeconds() + ttl) + ); return rest - .create(config, auth.master(config), '_Idempotency', { + .create(config, auth.master(config), "_Idempotency", { reqId: requestId, expire: Parse._encode(expiryDate), }) .catch(e => { if (e.code == Parse.Error.DUPLICATE_VALUE) { - throw new Parse.Error(Parse.Error.DUPLICATE_REQUEST, 'Duplicate request'); + throw new Parse.Error( + Parse.Error.DUPLICATE_REQUEST, + "Duplicate request" + ); } throw e; }); @@ -697,7 +731,10 @@ function invalidRequest(req, res) { function malformedContext(req, res) { res.status(400); - res.json({ code: Parse.Error.INVALID_JSON, error: 'Invalid object for context.' }); + res.json({ + code: Parse.Error.INVALID_JSON, + error: "Invalid object for context.", + }); } /** @@ -709,6 +746,6 @@ function malformedContext(req, res) { * http://localhost:1337/parse//functions/testFunction */ export function allowDoubleForwardSlash(req, res, next) { - req.url = req.url.startsWith('//') ? req.url.substring(1) : req.url; + req.url = req.url.startsWith("//") ? req.url.substring(1) : req.url; next(); } diff --git a/src/password.js b/src/password.js index eebec14368..02b3ccae69 100644 --- a/src/password.js +++ b/src/password.js @@ -1,9 +1,9 @@ // Tools for encrypting and decrypting passwords. // Basically promise-friendly wrappers for bcrypt. -var bcrypt = require('bcryptjs'); +var bcrypt = require("bcryptjs"); try { - const _bcrypt = require('@node-rs/bcrypt'); + const _bcrypt = require("@node-rs/bcrypt"); bcrypt = { hash: _bcrypt.hash, compare: _bcrypt.verify, diff --git a/src/request.js b/src/request.js index bc58ee40ac..83f494b63b 100644 --- a/src/request.js +++ b/src/request.js @@ -1,26 +1,26 @@ -import querystring from 'querystring'; -import log from './logger'; -import { http, https } from 'follow-redirects'; -import { parse } from 'url'; +import querystring from "querystring"; +import log from "./logger"; +import { http, https } from "follow-redirects"; +import { parse } from "url"; class HTTPResponse { constructor(response, body) { let _text, _data; this.status = response.statusCode; this.headers = response.headers || {}; - this.cookies = this.headers['set-cookie']; + this.cookies = this.headers["set-cookie"]; - if (typeof body == 'string') { + if (typeof body == "string") { _text = body; } else if (Buffer.isBuffer(body)) { this.buffer = body; - } else if (typeof body == 'object') { + } else if (typeof body == "object") { _data = body; } const getText = () => { if (!_text && this.buffer) { - _text = this.buffer.toString('utf-8'); + _text = this.buffer.toString("utf-8"); } else if (!_text && _data) { _text = JSON.stringify(_data); } @@ -38,18 +38,18 @@ class HTTPResponse { return _data; }; - Object.defineProperty(this, 'body', { + Object.defineProperty(this, "body", { get: () => { return body; }, }); - Object.defineProperty(this, 'text', { + Object.defineProperty(this, "text", { enumerable: true, get: getText, }); - Object.defineProperty(this, 'data', { + Object.defineProperty(this, "data", { enumerable: true, get: getData, }); @@ -57,17 +57,17 @@ class HTTPResponse { } const clients = { - 'http:': http, - 'https:': https, + "http:": http, + "https:": https, }; function makeCallback(resolve, reject) { return function (response) { const chunks = []; - response.on('data', chunk => { + response.on("data", chunk => { chunks.push(chunk); }); - response.on('end', () => { + response.on("end", () => { const body = Buffer.concat(chunks); const httpResponse = new HTTPResponse(response, body); @@ -78,12 +78,12 @@ function makeCallback(resolve, reject) { return resolve(httpResponse); } }); - response.on('error', reject); + response.on("error", reject); }; } const encodeBody = function ({ body, headers = {} }) { - if (typeof body !== 'object') { + if (typeof body !== "object") { return { body, headers }; } var contentTypeKeys = Object.keys(headers).filter(key => { @@ -95,17 +95,22 @@ const encodeBody = function ({ body, headers = {} }) { // As per https://parse.com/docs/cloudcode/guide#cloud-code-advanced-sending-a-post-request the default encoding is supposedly x-www-form-urlencoded body = querystring.stringify(body); - headers['Content-Type'] = 'application/x-www-form-urlencoded'; + headers["Content-Type"] = "application/x-www-form-urlencoded"; } else { /* istanbul ignore next */ if (contentTypeKeys.length > 1) { - log.error('Parse.Cloud.httpRequest', 'multiple content-type headers are set.'); + log.error( + "Parse.Cloud.httpRequest", + "multiple content-type headers are set." + ); } // There maybe many, we'll just take the 1st one var contentType = contentTypeKeys[0]; if (headers[contentType].match(/application\/json/i)) { body = JSON.stringify(body); - } else if (headers[contentType].match(/application\/x-www-form-urlencoded/i)) { + } else if ( + headers[contentType].match(/application\/x-www-form-urlencoded/i) + ) { body = querystring.stringify(body); } } @@ -121,9 +126,9 @@ function httpRequest(options) { } options = Object.assign(options, encodeBody(options)); // support params options - if (typeof options.params === 'object') { + if (typeof options.params === "object") { options.qs = options.params; - } else if (typeof options.params === 'string') { + } else if (typeof options.params === "string") { options.qs = querystring.parse(options.params); } const client = clients[url.protocol]; @@ -141,7 +146,7 @@ function httpRequest(options) { }; if (requestOptions.headers) { Object.keys(requestOptions.headers).forEach(key => { - if (typeof requestOptions.headers[key] === 'undefined') { + if (typeof requestOptions.headers[key] === "undefined") { delete requestOptions.headers[key]; } }); @@ -159,11 +164,14 @@ function httpRequest(options) { requestOptions.agent = options.agent; } return new Promise((resolve, reject) => { - const req = client.request(requestOptions, makeCallback(resolve, reject, options)); + const req = client.request( + requestOptions, + makeCallback(resolve, reject, options) + ); if (options.body) { req.write(options.body); } - req.on('error', error => { + req.on("error", error => { reject(error); }); req.end(); diff --git a/src/rest.js b/src/rest.js index 1f9dbacb73..4af242ca74 100644 --- a/src/rest.js +++ b/src/rest.js @@ -7,25 +7,40 @@ // routes. That's useful for the routes that do really similar // things. -var Parse = require('parse/node').Parse; +var Parse = require("parse/node").Parse; -var RestQuery = require('./RestQuery'); -var RestWrite = require('./RestWrite'); -var triggers = require('./triggers'); -const { enforceRoleSecurity } = require('./SharedRest'); +var RestQuery = require("./RestQuery"); +var RestWrite = require("./RestWrite"); +var triggers = require("./triggers"); +const { enforceRoleSecurity } = require("./SharedRest"); function checkTriggers(className, config, types) { return types.some(triggerType => { - return triggers.getTrigger(className, triggers.Types[triggerType], config.applicationId); + return triggers.getTrigger( + className, + triggers.Types[triggerType], + config.applicationId + ); }); } function checkLiveQuery(className, config) { - return config.liveQueryController && config.liveQueryController.hasLiveQuery(className); + return ( + config.liveQueryController && + config.liveQueryController.hasLiveQuery(className) + ); } // Returns a promise for an object with optional keys 'results' and 'count'. -const find = async (config, auth, className, restWhere, restOptions, clientSDK, context) => { +const find = async ( + config, + auth, + className, + restWhere, + restOptions, + clientSDK, + context +) => { const query = await RestQuery({ method: RestQuery.Method.find, config, @@ -40,7 +55,15 @@ const find = async (config, auth, className, restWhere, restOptions, clientSDK, }; // get is just like find but only queries an objectId. -const get = async (config, auth, className, objectId, restOptions, clientSDK, context) => { +const get = async ( + config, + auth, + className, + objectId, + restOptions, + clientSDK, + context +) => { var restWhere = { objectId }; const query = await RestQuery({ method: RestQuery.Method.get, @@ -57,24 +80,30 @@ const get = async (config, auth, className, objectId, restOptions, clientSDK, co // Returns a promise that doesn't resolve to any useful value. function del(config, auth, className, objectId, context) { - if (typeof objectId !== 'string') { - throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad objectId'); + if (typeof objectId !== "string") { + throw new Parse.Error(Parse.Error.INVALID_JSON, "bad objectId"); } - if (className === '_User' && auth.isUnauthenticated()) { - throw new Parse.Error(Parse.Error.SESSION_MISSING, 'Insufficient auth to delete user'); + if (className === "_User" && auth.isUnauthenticated()) { + throw new Parse.Error( + Parse.Error.SESSION_MISSING, + "Insufficient auth to delete user" + ); } - enforceRoleSecurity('delete', className, auth); + enforceRoleSecurity("delete", className, auth); let inflatedObject; let schemaController; return Promise.resolve() .then(async () => { - const hasTriggers = checkTriggers(className, config, ['beforeDelete', 'afterDelete']); + const hasTriggers = checkTriggers(className, config, [ + "beforeDelete", + "afterDelete", + ]); const hasLiveQuery = checkLiveQuery(className, config); - if (hasTriggers || hasLiveQuery || className == '_Session') { + if (hasTriggers || hasLiveQuery || className == "_Session") { const query = await RestQuery({ method: RestQuery.Method.get, config, @@ -82,13 +111,20 @@ function del(config, auth, className, objectId, context) { className, restWhere: { objectId }, }); - return query.execute({ op: 'delete' }).then(response => { + return query.execute({ op: "delete" }).then(response => { if (response && response.results && response.results.length) { const firstResult = response.results[0]; firstResult.className = className; - if (className === '_Session' && !auth.isMaster && !auth.isMaintenance) { + if ( + className === "_Session" && + !auth.isMaster && + !auth.isMaintenance + ) { if (!auth.user || firstResult.user.objectId !== auth.user.id) { - throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid session token'); + throw new Parse.Error( + Parse.Error.INVALID_SESSION_TOKEN, + "Invalid session token" + ); } } var cacheAdapter = config.cacheController; @@ -103,7 +139,10 @@ function del(config, auth, className, objectId, context) { context ); } - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found for delete.'); + throw new Parse.Error( + Parse.Error.OBJECT_NOT_FOUND, + "Object not found for delete." + ); }); } return Promise.resolve({}); @@ -120,7 +159,7 @@ function del(config, auth, className, objectId, context) { schemaController = s; const options = {}; if (!auth.isMaster && !auth.isMaintenance) { - options.acl = ['*']; + options.acl = ["*"]; if (auth.user) { options.acl.push(auth.user.id); options.acl = options.acl.concat(auth.userRoles); @@ -139,7 +178,12 @@ function del(config, auth, className, objectId, context) { .then(() => { // Notify LiveQuery server if possible const perms = schemaController.getClassLevelPermissions(className); - config.liveQueryController.onAfterDelete(className, inflatedObject, null, perms); + config.liveQueryController.onAfterDelete( + className, + inflatedObject, + null, + perms + ); return triggers.maybeRunTrigger( triggers.Types.afterDelete, auth, @@ -156,20 +200,40 @@ function del(config, auth, className, objectId, context) { // Returns a promise for a {response, status, location} object. function create(config, auth, className, restObject, clientSDK, context) { - enforceRoleSecurity('create', className, auth); - var write = new RestWrite(config, auth, className, null, restObject, null, clientSDK, context); + enforceRoleSecurity("create", className, auth); + var write = new RestWrite( + config, + auth, + className, + null, + restObject, + null, + clientSDK, + context + ); return write.execute(); } // Returns a promise that contains the fields of the update that the // REST API is supposed to return. // Usually, this is just updatedAt. -function update(config, auth, className, restWhere, restObject, clientSDK, context) { - enforceRoleSecurity('update', className, auth); +function update( + config, + auth, + className, + restWhere, + restObject, + clientSDK, + context +) { + enforceRoleSecurity("update", className, auth); return Promise.resolve() .then(async () => { - const hasTriggers = checkTriggers(className, config, ['beforeSave', 'afterSave']); + const hasTriggers = checkTriggers(className, config, [ + "beforeSave", + "afterSave", + ]); const hasLiveQuery = checkLiveQuery(className, config); if (hasTriggers || hasLiveQuery) { // Do not use find, as it runs the before finds @@ -184,7 +248,7 @@ function update(config, auth, className, restWhere, restObject, clientSDK, conte context, }); return query.execute({ - op: 'update', + op: "update", }); } return Promise.resolve({}); @@ -203,7 +267,7 @@ function update(config, auth, className, restWhere, restObject, clientSDK, conte originalRestObject, clientSDK, context, - 'update' + "update" ).execute(); }) .catch(error => { @@ -214,12 +278,12 @@ function update(config, auth, className, restWhere, restObject, clientSDK, conte function handleSessionMissingError(error, className, auth) { // If we're trying to update a user without / with bad session token if ( - className === '_User' && + className === "_User" && error.code === Parse.Error.OBJECT_NOT_FOUND && !auth.isMaster && !auth.isMaintenance ) { - throw new Parse.Error(Parse.Error.SESSION_MISSING, 'Insufficient auth.'); + throw new Parse.Error(Parse.Error.SESSION_MISSING, "Insufficient auth."); } throw error; } diff --git a/src/vendor/mongodbUrl.js b/src/vendor/mongodbUrl.js index 9fdc764629..b6d3ee7ef4 100644 --- a/src/vendor/mongodbUrl.js +++ b/src/vendor/mongodbUrl.js @@ -3,9 +3,9 @@ * See https://github.com/nodejs/node for licensing information. */ -'use strict'; +"use strict"; -import punycode from 'punycode/punycode.js'; +import punycode from "punycode/punycode.js"; exports.parse = urlParse; exports.resolve = urlResolve; @@ -42,27 +42,27 @@ const simplePathPattern = /^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/; // protocols that can allow "unsafe" and "unwise" chars. const unsafeProtocol = { javascript: true, - 'javascript:': true, + "javascript:": true, }; // protocols that never have a hostname. const hostlessProtocol = { javascript: true, - 'javascript:': true, + "javascript:": true, }; // protocols that always contain a // bit. const slashedProtocol = { http: true, - 'http:': true, + "http:": true, https: true, - 'https:': true, + "https:": true, ftp: true, - 'ftp:': true, + "ftp:": true, gopher: true, - 'gopher:': true, + "gopher:": true, file: true, - 'file:': true, + "file:": true, }; -const querystring = require('querystring'); +const querystring = require("querystring"); /* istanbul ignore next: improve coverage */ function urlParse(url, parseQueryString, slashesDenoteHost) { @@ -77,7 +77,7 @@ function urlParse(url, parseQueryString, slashesDenoteHost) { /* istanbul ignore next: improve coverage */ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { - if (typeof url !== 'string') { + if (typeof url !== "string") { throw new TypeError('Parameter "url" must be a string, not ' + typeof url); } @@ -87,7 +87,7 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { var hasHash = false; var start = -1; var end = -1; - var rest = ''; + var rest = ""; var lastPos = 0; var i = 0; for (var inWs = false, split = false; i < url.length; ++i) { @@ -132,7 +132,7 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { if (i - lastPos > 0) { rest += url.slice(lastPos, i); } - rest += '/'; + rest += "/"; lastPos = i + 1; break; } @@ -179,7 +179,7 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { this.query = this.search.slice(1); } } else if (parseQueryString) { - this.search = ''; + this.search = ""; this.query = {}; } return this; @@ -199,14 +199,18 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { // resolution will treat //foo/bar as host=foo,path=bar because that's // how the browser resolves relative URLs. if (slashesDenoteHost || proto || /^\/\/[^@\/]+@[^@\/]+/.test(rest)) { - var slashes = rest.charCodeAt(0) === 47 /*/*/ && rest.charCodeAt(1) === 47; /*/*/ + var slashes = + rest.charCodeAt(0) === 47 /*/*/ && rest.charCodeAt(1) === 47; /*/*/ if (slashes && !(proto && hostlessProtocol[proto])) { rest = rest.slice(2); this.slashes = true; } } - if (!hostlessProtocol[proto] && (slashes || (proto && !slashedProtocol[proto]))) { + if ( + !hostlessProtocol[proto] && + (slashes || (proto && !slashedProtocol[proto])) + ) { // there's a hostname. // the first instance of /, ?, ;, or # ends the host. // @@ -275,7 +279,7 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { } if (nonHost === -1) { this.host = rest.slice(start); - rest = ''; + rest = ""; } else { this.host = rest.slice(start, nonHost); rest = rest.slice(nonHost); @@ -286,8 +290,8 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { // we've indicated that there is a hostname, // so even if it's empty, it has to be present. - if (typeof this.hostname !== 'string') { - this.hostname = ''; + if (typeof this.hostname !== "string") { + this.hostname = ""; } var hostname = this.hostname; @@ -295,7 +299,8 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { // if hostname begins with [ and ends with ] // assume that it's an IPv6 address. var ipv6Hostname = - hostname.charCodeAt(0) === 91 /*[*/ && hostname.charCodeAt(hostname.length - 1) === 93; /*]*/ + hostname.charCodeAt(0) === 91 /*[*/ && + hostname.charCodeAt(hostname.length - 1) === 93; /*]*/ // validate a little. if (!ipv6Hostname) { @@ -316,16 +321,16 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { this.hostname = punycode.toASCII(this.hostname); } - var p = this.port ? ':' + this.port : ''; - var h = this.hostname || ''; + var p = this.port ? ":" + this.port : ""; + var h = this.hostname || ""; this.host = h + p; // strip [ and ] from the hostname // the host field still retains them, though if (ipv6Hostname) { this.hostname = this.hostname.slice(1, -1); - if (rest[0] !== '/') { - rest = '/' + rest; + if (rest[0] !== "/") { + rest = "/" + rest; } } } @@ -368,12 +373,14 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { } } else if (parseQueryString) { // no query string, but parseQueryString still requested - this.search = ''; + this.search = ""; this.query = {}; } var firstIdx = - questionIdx !== -1 && (hashIdx === -1 || questionIdx < hashIdx) ? questionIdx : hashIdx; + questionIdx !== -1 && (hashIdx === -1 || questionIdx < hashIdx) + ? questionIdx + : hashIdx; if (firstIdx === -1) { if (rest.length > 0) { this.pathname = rest; @@ -382,13 +389,13 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { this.pathname = rest.slice(0, firstIdx); } if (slashedProtocol[lowerProto] && this.hostname && !this.pathname) { - this.pathname = '/'; + this.pathname = "/"; } // to support http.request if (this.pathname || this.search) { - const p = this.pathname || ''; - const s = this.search || ''; + const p = this.pathname || ""; + const s = this.search || ""; this.path = p + s; } @@ -408,7 +415,7 @@ function validateHostname(self, rest, hostname) { if (i - lastPos > 0) { if (i - lastPos > 63) { self.hostname = hostname.slice(0, lastPos + 63); - return '/' + hostname.slice(lastPos + 63) + rest; + return "/" + hostname.slice(lastPos + 63) + rest; } } lastPos = i + 1; @@ -431,7 +438,7 @@ function validateHostname(self, rest, hostname) { // Invalid host character self.hostname = hostname.slice(0, i); if (i < hostname.length) { - return '/' + hostname.slice(i) + rest; + return "/" + hostname.slice(i) + rest; } break; } @@ -439,7 +446,7 @@ function validateHostname(self, rest, hostname) { /* istanbul ignore next: improve coverage */ function autoEscapeStr(rest) { - var newRest = ''; + var newRest = ""; var lastPos = 0; for (var i = 0; i < rest.length; ++i) { // Automatically escape all delimiters and unwise characters from RFC 2396 @@ -449,98 +456,98 @@ function autoEscapeStr(rest) { if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } - newRest += '%09'; + newRest += "%09"; lastPos = i + 1; break; case 10: // '\n' if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } - newRest += '%0A'; + newRest += "%0A"; lastPos = i + 1; break; case 13: // '\r' if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } - newRest += '%0D'; + newRest += "%0D"; lastPos = i + 1; break; case 32: // ' ' if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } - newRest += '%20'; + newRest += "%20"; lastPos = i + 1; break; case 34: // '"' if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } - newRest += '%22'; + newRest += "%22"; lastPos = i + 1; break; case 39: // '\'' if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } - newRest += '%27'; + newRest += "%27"; lastPos = i + 1; break; case 60: // '<' if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } - newRest += '%3C'; + newRest += "%3C"; lastPos = i + 1; break; case 62: // '>' if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } - newRest += '%3E'; + newRest += "%3E"; lastPos = i + 1; break; case 92: // '\\' if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } - newRest += '%5C'; + newRest += "%5C"; lastPos = i + 1; break; case 94: // '^' if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } - newRest += '%5E'; + newRest += "%5E"; lastPos = i + 1; break; case 96: // '`' if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } - newRest += '%60'; + newRest += "%60"; lastPos = i + 1; break; case 123: // '{' if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } - newRest += '%7B'; + newRest += "%7B"; lastPos = i + 1; break; case 124: // '|' if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } - newRest += '%7C'; + newRest += "%7C"; lastPos = i + 1; break; case 125: // '}' if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } - newRest += '%7D'; + newRest += "%7D"; lastPos = i + 1; break; } @@ -562,11 +569,12 @@ function urlFormat(obj) { // If it's an obj, this is a no-op. // this way, you can call url_format() on strings // to clean up potentially wonky urls. - if (typeof obj === 'string') { + if (typeof obj === "string") { obj = urlParse(obj); - } else if (typeof obj !== 'object' || obj === null) { + } else if (typeof obj !== "object" || obj === null) { throw new TypeError( - 'Parameter "urlObj" must be an object, not ' + (obj === null ? 'null' : typeof obj) + 'Parameter "urlObj" must be an object, not ' + + (obj === null ? "null" : typeof obj) ); } else if (!(obj instanceof Url)) { return Url.prototype.format.call(obj); @@ -577,38 +585,42 @@ function urlFormat(obj) { /* istanbul ignore next: improve coverage */ Url.prototype.format = function () { - var auth = this.auth || ''; + var auth = this.auth || ""; if (auth) { auth = encodeAuth(auth); - auth += '@'; + auth += "@"; } - var protocol = this.protocol || ''; - var pathname = this.pathname || ''; - var hash = this.hash || ''; + var protocol = this.protocol || ""; + var pathname = this.pathname || ""; + var hash = this.hash || ""; var host = false; - var query = ''; + var query = ""; if (this.host) { host = auth + this.host; } else if (this.hostname) { - host = auth + (this.hostname.indexOf(':') === -1 ? this.hostname : '[' + this.hostname + ']'); + host = + auth + + (this.hostname.indexOf(":") === -1 + ? this.hostname + : "[" + this.hostname + "]"); if (this.port) { - host += ':' + this.port; + host += ":" + this.port; } } - if (this.query !== null && typeof this.query === 'object') { + if (this.query !== null && typeof this.query === "object") { query = querystring.stringify(this.query); } - var search = this.search || (query && '?' + query) || ''; + var search = this.search || (query && "?" + query) || ""; if (protocol && protocol.charCodeAt(protocol.length - 1) !== 58 /*:*/) { - protocol += ':'; + protocol += ":"; } - var newPathname = ''; + var newPathname = ""; var lastPos = 0; for (var i = 0; i < pathname.length; ++i) { switch (pathname.charCodeAt(i)) { @@ -616,14 +628,14 @@ Url.prototype.format = function () { if (i - lastPos > 0) { newPathname += pathname.slice(lastPos, i); } - newPathname += '%23'; + newPathname += "%23"; lastPos = i + 1; break; case 63: // '?' if (i - lastPos > 0) { newPathname += pathname.slice(lastPos, i); } - newPathname += '%3F'; + newPathname += "%3F"; lastPos = i + 1; break; } @@ -638,22 +650,25 @@ Url.prototype.format = function () { // only the slashedProtocols get the //. Not mailto:, xmpp:, etc. // unless they had them to begin with. - if (this.slashes || ((!protocol || slashedProtocol[protocol]) && host !== false)) { - host = '//' + (host || ''); + if ( + this.slashes || + ((!protocol || slashedProtocol[protocol]) && host !== false) + ) { + host = "//" + (host || ""); if (pathname && pathname.charCodeAt(0) !== 47 /*/*/) { - pathname = '/' + pathname; + pathname = "/" + pathname; } } else if (!host) { - host = ''; + host = ""; } - search = search.replace('#', '%23'); + search = search.replace("#", "%23"); if (hash && hash.charCodeAt(0) !== 35 /*#*/) { - hash = '#' + hash; + hash = "#" + hash; } if (search && search.charCodeAt(0) !== 63 /*?*/) { - search = '?' + search; + search = "?" + search; } return protocol + host + pathname + search + hash; @@ -679,7 +694,7 @@ function urlResolveObject(source, relative) { /* istanbul ignore next: improve coverage */ Url.prototype.resolveObject = function (relative) { - if (typeof relative === 'string') { + if (typeof relative === "string") { var rel = new Url(); rel.parse(relative, false, true); relative = rel; @@ -697,7 +712,7 @@ Url.prototype.resolveObject = function (relative) { result.hash = relative.hash; // if the relative url is empty, then there's nothing left to do here. - if (relative.href === '') { + if (relative.href === "") { result.href = result.format(); return result; } @@ -708,14 +723,18 @@ Url.prototype.resolveObject = function (relative) { var rkeys = Object.keys(relative); for (var rk = 0; rk < rkeys.length; rk++) { var rkey = rkeys[rk]; - if (rkey !== 'protocol') { + if (rkey !== "protocol") { result[rkey] = relative[rkey]; } } //urlParse appends trailing / to urls like http://www.example.com - if (slashedProtocol[result.protocol] && result.hostname && !result.pathname) { - result.path = result.pathname = '/'; + if ( + slashedProtocol[result.protocol] && + result.hostname && + !result.pathname + ) { + result.path = result.pathname = "/"; } result.href = result.format(); @@ -747,7 +766,7 @@ Url.prototype.resolveObject = function (relative) { !/^file:?$/.test(relative.protocol) && !hostlessProtocol[relative.protocol] ) { - const relPath = (relative.pathname || '').split('/'); + const relPath = (relative.pathname || "").split("/"); while (relPath.length) { const shifted = relPath.shift(); if (shifted) { @@ -756,31 +775,31 @@ Url.prototype.resolveObject = function (relative) { } } if (!relative.host) { - relative.host = ''; + relative.host = ""; } if (!relative.hostname) { - relative.hostname = ''; + relative.hostname = ""; } - if (relPath[0] !== '') { - relPath.unshift(''); + if (relPath[0] !== "") { + relPath.unshift(""); } if (relPath.length < 2) { - relPath.unshift(''); + relPath.unshift(""); } - result.pathname = relPath.join('/'); + result.pathname = relPath.join("/"); } else { result.pathname = relative.pathname; } result.search = relative.search; result.query = relative.query; - result.host = relative.host || ''; + result.host = relative.host || ""; result.auth = relative.auth; result.hostname = relative.hostname || relative.host; result.port = relative.port; // to support http.request if (result.pathname || result.search) { - var p = result.pathname || ''; - var s = result.search || ''; + var p = result.pathname || ""; + var s = result.search || ""; result.path = p + s; } result.slashes = result.slashes || relative.slashes; @@ -788,12 +807,14 @@ Url.prototype.resolveObject = function (relative) { return result; } - var isSourceAbs = result.pathname && result.pathname.charAt(0) === '/'; - var isRelAbs = relative.host || (relative.pathname && relative.pathname.charAt(0) === '/'); - var mustEndAbs = isRelAbs || isSourceAbs || (result.host && relative.pathname); + var isSourceAbs = result.pathname && result.pathname.charAt(0) === "/"; + var isRelAbs = + relative.host || (relative.pathname && relative.pathname.charAt(0) === "/"); + var mustEndAbs = + isRelAbs || isSourceAbs || (result.host && relative.pathname); var removeAllDots = mustEndAbs; - var srcPath = (result.pathname && result.pathname.split('/')) || []; - var relPath = (relative.pathname && relative.pathname.split('/')) || []; + var srcPath = (result.pathname && result.pathname.split("/")) || []; + var relPath = (relative.pathname && relative.pathname.split("/")) || []; var psychotic = result.protocol && !slashedProtocol[result.protocol]; // if the url is a non-slashed url, then relative @@ -802,21 +823,21 @@ Url.prototype.resolveObject = function (relative) { // result.protocol has already been set by now. // Later on, put the first path part into the host field. if (psychotic) { - result.hostname = ''; + result.hostname = ""; result.port = null; if (result.host) { - if (srcPath[0] === '') { + if (srcPath[0] === "") { srcPath[0] = result.host; } else { srcPath.unshift(result.host); } } - result.host = ''; + result.host = ""; if (relative.protocol) { relative.hostname = null; relative.port = null; if (relative.host) { - if (relPath[0] === '') { + if (relPath[0] === "") { relPath[0] = relative.host; } else { relPath.unshift(relative.host); @@ -824,14 +845,17 @@ Url.prototype.resolveObject = function (relative) { } relative.host = null; } - mustEndAbs = mustEndAbs && (relPath[0] === '' || srcPath[0] === ''); + mustEndAbs = mustEndAbs && (relPath[0] === "" || srcPath[0] === ""); } if (isRelAbs) { // it's absolute. - result.host = relative.host || relative.host === '' ? relative.host : result.host; + result.host = + relative.host || relative.host === "" ? relative.host : result.host; result.hostname = - relative.hostname || relative.hostname === '' ? relative.hostname : result.hostname; + relative.hostname || relative.hostname === "" + ? relative.hostname + : result.hostname; result.search = relative.search; result.query = relative.query; srcPath = relPath; @@ -856,7 +880,9 @@ Url.prototype.resolveObject = function (relative) { //this especially happens in cases like //url.resolveObject('mailto:local1@domain1', 'local2@domain2') const authInHost = - result.host && result.host.indexOf('@') > 0 ? result.host.split('@') : false; + result.host && result.host.indexOf("@") > 0 + ? result.host.split("@") + : false; if (authInHost) { result.auth = authInHost.shift(); result.host = result.hostname = authInHost.shift(); @@ -866,7 +892,9 @@ Url.prototype.resolveObject = function (relative) { result.query = relative.query; //to support http.request if (result.pathname !== null || result.search !== null) { - result.path = (result.pathname ? result.pathname : '') + (result.search ? result.search : ''); + result.path = + (result.pathname ? result.pathname : "") + + (result.search ? result.search : ""); } result.href = result.format(); return result; @@ -878,7 +906,7 @@ Url.prototype.resolveObject = function (relative) { result.pathname = null; //to support http.request if (result.search) { - result.path = '/' + result.search; + result.path = "/" + result.search; } else { result.path = null; } @@ -891,17 +919,18 @@ Url.prototype.resolveObject = function (relative) { // then it must NOT get a trailing slash. var last = srcPath.slice(-1)[0]; var hasTrailingSlash = - ((result.host || relative.host || srcPath.length > 1) && (last === '.' || last === '..')) || - last === ''; + ((result.host || relative.host || srcPath.length > 1) && + (last === "." || last === "..")) || + last === ""; // strip single dots, resolve double dots to parent dir // if the path tries to go above the root, `up` ends up > 0 var up = 0; for (var i = srcPath.length; i >= 0; i--) { last = srcPath[i]; - if (last === '.') { + if (last === ".") { spliceOne(srcPath, i); - } else if (last === '..') { + } else if (last === "..") { spliceOne(srcPath, i); up++; } else if (up) { @@ -913,31 +942,39 @@ Url.prototype.resolveObject = function (relative) { // if the path is allowed to go above the root, restore leading ..s if (!mustEndAbs && !removeAllDots) { for (; up--; up) { - srcPath.unshift('..'); + srcPath.unshift(".."); } } - if (mustEndAbs && srcPath[0] !== '' && (!srcPath[0] || srcPath[0].charAt(0) !== '/')) { - srcPath.unshift(''); + if ( + mustEndAbs && + srcPath[0] !== "" && + (!srcPath[0] || srcPath[0].charAt(0) !== "/") + ) { + srcPath.unshift(""); } - if (hasTrailingSlash && srcPath.join('/').substr(-1) !== '/') { - srcPath.push(''); + if (hasTrailingSlash && srcPath.join("/").substr(-1) !== "/") { + srcPath.push(""); } - var isAbsolute = srcPath[0] === '' || (srcPath[0] && srcPath[0].charAt(0) === '/'); + var isAbsolute = + srcPath[0] === "" || (srcPath[0] && srcPath[0].charAt(0) === "/"); // put the host back if (psychotic) { if (isAbsolute) { - result.hostname = result.host = ''; + result.hostname = result.host = ""; } else { - result.hostname = result.host = srcPath.length ? srcPath.shift() : ''; + result.hostname = result.host = srcPath.length ? srcPath.shift() : ""; } //occasionally the auth can get stuck only in host //this especially happens in cases like //url.resolveObject('mailto:local1@domain1', 'local2@domain2') - const authInHost = result.host && result.host.indexOf('@') > 0 ? result.host.split('@') : false; + const authInHost = + result.host && result.host.indexOf("@") > 0 + ? result.host.split("@") + : false; if (authInHost) { result.auth = authInHost.shift(); result.host = result.hostname = authInHost.shift(); @@ -947,19 +984,21 @@ Url.prototype.resolveObject = function (relative) { mustEndAbs = mustEndAbs || (result.host && srcPath.length); if (mustEndAbs && !isAbsolute) { - srcPath.unshift(''); + srcPath.unshift(""); } if (!srcPath.length) { result.pathname = null; result.path = null; } else { - result.pathname = srcPath.join('/'); + result.pathname = srcPath.join("/"); } //to support request.http if (result.pathname !== null || result.search !== null) { - result.path = (result.pathname ? result.pathname : '') + (result.search ? result.search : ''); + result.path = + (result.pathname ? result.pathname : "") + + (result.search ? result.search : ""); } result.auth = relative.auth || result.auth; result.slashes = result.slashes || relative.slashes; @@ -973,7 +1012,7 @@ Url.prototype.parseHost = function () { var port = portPattern.exec(host); if (port) { port = port[0]; - if (port !== ':') { + if (port !== ":") { this.port = port.slice(1); } host = host.slice(0, host.length - port.length); @@ -994,12 +1033,12 @@ function spliceOne(list, index) { var hexTable = new Array(256); for (var i = 0; i < 256; ++i) { - hexTable[i] = '%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase(); + hexTable[i] = "%" + ((i < 16 ? "0" : "") + i.toString(16)).toUpperCase(); } /* istanbul ignore next: improve coverage */ function encodeAuth(str) { // faster encodeURIComponent alternative for encoding auth uri components - var out = ''; + var out = ""; var lastPos = 0; for (var i = 0; i < str.length; ++i) { var c = str.charCodeAt(i); From 67c751c44f8a285d1552b8957ab1bce8830aa4b6 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sun, 18 May 2025 20:45:32 +1000 Subject: [PATCH 5/7] change quote type --- .prettierrc | 2 +- spec/AccountLockoutPolicy.spec.js | 252 +- spec/AdaptableController.spec.js | 24 +- spec/AdapterLoader.spec.js | 106 +- spec/AggregateRouter.spec.js | 64 +- spec/Analytics.spec.js | 32 +- spec/AudienceRouter.spec.js | 210 +- spec/Auth.spec.js | 100 +- spec/AuthenticationAdapters.spec.js | 1278 +++--- spec/AuthenticationAdaptersV2.spec.js | 564 +-- spec/CLI.spec.js | 318 +- spec/CacheController.spec.js | 38 +- spec/Client.spec.js | 224 +- spec/ClientSDK.spec.js | 46 +- spec/CloudCode.Validator.spec.js | 1046 ++--- spec/CloudCode.spec.js | 2806 ++++++------- spec/CloudCodeLogger.spec.js | 316 +- spec/DatabaseController.spec.js | 338 +- spec/DefinedSchemas.spec.js | 498 +-- spec/Deprecator.spec.js | 38 +- spec/EmailVerificationToken.spec.js | 618 +-- spec/EnableExpressErrorHandler.spec.js | 24 +- spec/EventEmitterPubSub.spec.js | 30 +- spec/FilesController.spec.js | 138 +- spec/GridFSBucketStorageAdapter.spec.js | 252 +- spec/HTTPRequest.spec.js | 144 +- spec/Idempotency.spec.js | 208 +- spec/InMemoryCache.spec.js | 18 +- spec/InMemoryCacheAdapter.spec.js | 14 +- spec/InstallationsRouter.spec.js | 116 +- spec/JobSchedule.spec.js | 144 +- spec/LdapAuth.spec.js | 182 +- spec/Logger.spec.js | 40 +- spec/LoggerController.spec.js | 94 +- spec/LogsRouter.spec.js | 74 +- spec/Middlewares.spec.js | 302 +- spec/MongoSchemaCollectionAdapter.spec.js | 112 +- spec/MongoStorageAdapter.spec.js | 414 +- spec/MongoTransform.spec.js | 414 +- spec/NullCacheAdapter.spec.js | 12 +- spec/OAuth1.spec.js | 112 +- spec/PagesRouter.spec.js | 566 +-- spec/Parse.Push.spec.js | 166 +- spec/ParseACL.spec.js | 350 +- spec/ParseAPI.spec.js | 1144 ++--- spec/ParseCloudCodePublisher.spec.js | 54 +- spec/ParseConfigKey.spec.js | 48 +- spec/ParseFile.spec.js | 1090 ++--- spec/ParseGeoPoint.spec.js | 446 +- spec/ParseGlobalConfig.spec.js | 160 +- spec/ParseGraphQLClassNameTransformer.spec.js | 10 +- spec/ParseGraphQLController.spec.js | 232 +- spec/ParseGraphQLSchema.spec.js | 238 +- spec/ParseGraphQLServer.spec.js | 3694 ++++++++--------- spec/ParseHooks.spec.js | 402 +- spec/ParseInstallation.spec.js | 652 +-- spec/ParseLiveQuery.spec.js | 772 ++-- spec/ParseLiveQueryRedis.spec.js | 32 +- spec/ParseLiveQueryServer.spec.js | 796 ++-- spec/ParseObject.spec.js | 1318 +++--- spec/ParsePolygon.spec.js | 204 +- spec/ParsePubSub.spec.js | 78 +- spec/ParseQuery.Aggregate.spec.js | 662 +-- spec/ParseQuery.Comment.spec.js | 72 +- spec/ParseQuery.FullTextSearch.spec.js | 278 +- spec/ParseQuery.hint.spec.js | 234 +- spec/ParseQuery.spec.js | 3250 +++++++-------- spec/ParseRelation.spec.js | 458 +- spec/ParseRole.spec.js | 254 +- spec/ParseServer.spec.js | 42 +- spec/ParseServerRESTController.spec.js | 528 +-- spec/ParseSession.spec.js | 74 +- spec/ParseUser.spec.js | 2552 ++++++------ spec/ParseWebSocket.spec.js | 22 +- spec/ParseWebSocketServer.spec.js | 58 +- spec/PasswordPolicy.spec.js | 832 ++-- spec/PointerPermissions.spec.js | 1388 +++---- spec/PostgresConfigParser.spec.js | 34 +- spec/PostgresInitOptions.spec.js | 34 +- spec/PostgresStorageAdapter.spec.js | 374 +- spec/PromiseRouter.spec.js | 18 +- spec/ProtectedFields.spec.js | 1276 +++--- spec/PublicAPI.spec.js | 90 +- spec/PurchaseValidation.spec.js | 154 +- spec/PushController.spec.js | 690 +-- spec/PushQueue.spec.js | 30 +- spec/PushRouter.spec.js | 38 +- spec/PushWorker.spec.js | 240 +- spec/QueryTools.spec.js | 646 +-- spec/RateLimit.spec.js | 404 +- spec/ReadPreferenceOption.spec.js | 908 ++-- spec/RedisCacheAdapter.spec.js | 46 +- spec/RedisPubSub.spec.js | 30 +- spec/RegexVulnerabilities.spec.js | 134 +- spec/RestQuery.spec.js | 330 +- spec/RevocableSessionsUpgrade.spec.js | 72 +- spec/Schema.spec.js | 1146 ++--- spec/SchemaPerformance.spec.js | 152 +- spec/SecurityCheck.spec.js | 196 +- spec/SecurityCheckGroups.spec.js | 42 +- spec/SessionTokenCache.spec.js | 28 +- spec/Subscription.spec.js | 102 +- spec/Uniqueness.spec.js | 104 +- spec/UserController.spec.js | 54 +- spec/UserPII.spec.js | 600 +-- spec/Utils.spec.js | 24 +- spec/ValidationAndPasswordsReset.spec.js | 720 ++-- spec/VerifyUserPassword.spec.js | 422 +- spec/WinstonLoggerAdapter.spec.js | 166 +- spec/batch.spec.js | 472 +-- spec/cloud/cloudCodeAbsoluteFile.js | 4 +- spec/cloud/cloudCodeModuleFile.js | 4 +- spec/cloud/cloudCodeRelativeFile.js | 4 +- spec/cryptoUtils.spec.js | 50 +- spec/defaultGraphQLTypes.spec.js | 492 +-- spec/eslint.config.js | 88 +- spec/features.spec.js | 28 +- spec/graphQLObjectsQueries.js | 44 +- spec/helper.js | 192 +- spec/index.spec.js | 444 +- spec/parsers.spec.js | 60 +- spec/rest.spec.js | 670 +-- spec/schemas.spec.js | 2402 +++++------ spec/support/CurrentSpecReporter.js | 18 +- spec/support/CustomAuth.js | 2 +- spec/support/CustomMiddleware.js | 2 +- spec/support/FailingServer.js | 12 +- spec/support/MockEmailAdapterWithOptions.js | 2 +- spec/support/MockLdapServer.js | 26 +- spec/support/dev.js | 20 +- spec/support/myoauth.js | 2 +- spec/vulnerabilities.spec.js | 308 +- src/AccountLockout.js | 26 +- src/Auth.js | 102 +- src/ClientSDK.js | 6 +- src/Controllers/AdaptableController.js | 4 +- src/Controllers/AnalyticsController.js | 4 +- src/Controllers/CacheController.js | 12 +- src/Controllers/DatabaseController.js | 370 +- src/Controllers/FilesController.js | 50 +- src/Controllers/HooksController.js | 44 +- src/Controllers/LoggerController.js | 80 +- src/Controllers/SchemaController.js | 532 +-- src/Controllers/UserController.js | 108 +- src/Deprecator/Deprecations.js | 4 +- src/Deprecator/Deprecator.js | 14 +- src/GraphQL/parseGraphQLUtils.js | 22 +- src/LiveQuery/RequestSchema.js | 104 +- src/LiveQuery/equalObjects.js | 6 +- src/Options/Definitions.js | 880 ++-- src/Options/index.js | 18 +- src/Options/parsers.js | 20 +- src/ParseServerRESTController.js | 24 +- src/PromiseRouter.js | 38 +- src/Push/PushWorker.js | 28 +- src/Push/utils.js | 26 +- src/RestQuery.js | 226 +- src/RestWrite.js | 322 +- src/Routers/AggregateRouter.js | 30 +- src/Routers/AnalyticsRouter.js | 6 +- src/Routers/AudiencesRouter.js | 30 +- src/Routers/ClassesRouter.js | 98 +- src/Routers/CloudCodeRouter.js | 44 +- src/Routers/FeaturesRouter.js | 10 +- src/Routers/FilesRouter.js | 104 +- src/Routers/FunctionsRouter.js | 42 +- src/Routers/GlobalConfigRouter.js | 30 +- src/Routers/GraphQLRouter.js | 12 +- src/Routers/HooksRouter.js | 42 +- src/Routers/IAPValidationRouter.js | 50 +- src/Routers/InstallationsRouter.js | 22 +- src/Routers/LogsRouter.js | 12 +- src/Routers/PagesRouter.js | 130 +- src/Routers/PublicAPIRouter.js | 74 +- src/Routers/PurgeRouter.js | 14 +- src/Routers/PushRouter.js | 20 +- src/Routers/RolesRouter.js | 14 +- src/Routers/SchemasRouter.js | 36 +- src/Routers/SecurityRouter.js | 12 +- src/Routers/SessionsRouter.js | 40 +- src/Routers/UsersRouter.js | 178 +- src/SchemaMigrations/DefinedSchemas.js | 64 +- src/SchemaMigrations/Migrations.js | 38 +- src/Security/Check.js | 20 +- src/Security/CheckRunner.js | 36 +- src/SharedRest.js | 18 +- src/StatusHandler.js | 88 +- src/TestUtils.js | 10 +- src/Utils.js | 100 +- src/batch.js | 24 +- src/cache.js | 2 +- src/cli/parse-live-query-server.js | 6 +- src/cli/parse-server.js | 90 +- src/cloud-code/Parse.Cloud.js | 76 +- src/cloud-code/Parse.Server.js | 8 +- src/cryptoUtils.js | 16 +- src/defaults.js | 14 +- src/middlewares.js | 190 +- src/password.js | 4 +- src/request.js | 48 +- src/rest.js | 52 +- src/vendor/mongodbUrl.js | 236 +- 202 files changed, 27414 insertions(+), 27414 deletions(-) diff --git a/.prettierrc b/.prettierrc index e9e2d266c1..788c8595ea 100644 --- a/.prettierrc +++ b/.prettierrc @@ -9,7 +9,7 @@ "proseWrap": "preserve", "quoteProps": "as-needed", "semi": true, - "singleQuote": false, + "singleQuote": true, "tabWidth": 2, "trailingComma": "es5", "useTabs": false diff --git a/spec/AccountLockoutPolicy.spec.js b/spec/AccountLockoutPolicy.spec.js index 5e9620b645..9d7ba6d2c2 100644 --- a/spec/AccountLockoutPolicy.spec.js +++ b/spec/AccountLockoutPolicy.spec.js @@ -1,15 +1,15 @@ -"use strict"; +'use strict'; -const Config = require("../lib/Config"); -const Definitions = require("../lib/Options/Definitions"); -const request = require("../lib/request"); +const Config = require('../lib/Config'); +const Definitions = require('../lib/Options/Definitions'); +const request = require('../lib/request'); const loginWithWrongCredentialsShouldFail = function (username, password) { return new Promise((resolve, reject) => { Parse.User.logIn(username, password) - .then(() => reject("login should have failed")) + .then(() => reject('login should have failed')) .catch(err => { - if (err.message === "Invalid username/password.") { + if (err.message === 'Invalid username/password.') { resolve(); } else { reject(err); @@ -27,13 +27,13 @@ const isAccountLockoutError = function ( return new Promise((resolve, reject) => { setTimeout(() => { Parse.User.logIn(username, password) - .then(() => reject("login should have failed")) + .then(() => reject('login should have failed')) .catch(err => { if ( err.message === - "Your account is locked due to multiple failed login attempts. Please try again after " + + 'Your account is locked due to multiple failed login attempts. Please try again after ' + duration + - " minute(s)" + ' minute(s)' ) { resolve(); } else { @@ -44,69 +44,69 @@ const isAccountLockoutError = function ( }); }; -describe("Account Lockout Policy: ", () => { - it("account should not be locked even after failed login attempts if account lockout policy is not set", done => { +describe('Account Lockout Policy: ', () => { + it('account should not be locked even after failed login attempts if account lockout policy is not set', done => { reconfigureServer({ - appName: "unlimited", - publicServerURL: "http://localhost:1337/1", + appName: 'unlimited', + publicServerURL: 'http://localhost:1337/1', }) .then(() => { const user = new Parse.User(); - user.setUsername("username1"); - user.setPassword("password"); + user.setUsername('username1'); + user.setPassword('password'); return user.signUp(null); }) .then(() => { return loginWithWrongCredentialsShouldFail( - "username1", - "incorrect password 1" + 'username1', + 'incorrect password 1' ); }) .then(() => { return loginWithWrongCredentialsShouldFail( - "username1", - "incorrect password 2" + 'username1', + 'incorrect password 2' ); }) .then(() => { return loginWithWrongCredentialsShouldFail( - "username1", - "incorrect password 3" + 'username1', + 'incorrect password 3' ); }) .then(() => done()) .catch(err => { fail( - "allow unlimited failed login attempts failed: " + JSON.stringify(err) + 'allow unlimited failed login attempts failed: ' + JSON.stringify(err) ); done(); }); }); - it("throw error if duration is set to an invalid number", done => { + it('throw error if duration is set to an invalid number', done => { reconfigureServer({ - appName: "duration", + appName: 'duration', accountLockout: { - duration: "invalid value", + duration: 'invalid value', threshold: 5, }, - publicServerURL: "https://my.public.server.com/1", + publicServerURL: 'https://my.public.server.com/1', }) .then(() => { - Config.get("test"); - fail("set duration to an invalid number test failed"); + Config.get('test'); + fail('set duration to an invalid number test failed'); done(); }) .catch(err => { if ( err && err === - "Account lockout duration should be greater than 0 and less than 100000" + 'Account lockout duration should be greater than 0 and less than 100000' ) { done(); } else { fail( - "set duration to an invalid number test failed: " + + 'set duration to an invalid number test failed: ' + JSON.stringify(err) ); done(); @@ -114,30 +114,30 @@ describe("Account Lockout Policy: ", () => { }); }); - it("throw error if threshold is set to an invalid number", done => { + it('throw error if threshold is set to an invalid number', done => { reconfigureServer({ - appName: "threshold", + appName: 'threshold', accountLockout: { duration: 5, - threshold: "invalid number", + threshold: 'invalid number', }, - publicServerURL: "https://my.public.server.com/1", + publicServerURL: 'https://my.public.server.com/1', }) .then(() => { - Config.get("test"); - fail("set threshold to an invalid number test failed"); + Config.get('test'); + fail('set threshold to an invalid number test failed'); done(); }) .catch(err => { if ( err && err === - "Account lockout threshold should be an integer greater than 0 and less than 1000" + 'Account lockout threshold should be an integer greater than 0 and less than 1000' ) { done(); } else { fail( - "set threshold to an invalid number test failed: " + + 'set threshold to an invalid number test failed: ' + JSON.stringify(err) ); done(); @@ -145,60 +145,60 @@ describe("Account Lockout Policy: ", () => { }); }); - it("throw error if threshold is < 1", done => { + it('throw error if threshold is < 1', done => { reconfigureServer({ - appName: "threshold", + appName: 'threshold', accountLockout: { duration: 5, threshold: 0, }, - publicServerURL: "https://my.public.server.com/1", + publicServerURL: 'https://my.public.server.com/1', }) .then(() => { - Config.get("test"); - fail("threshold value < 1 is invalid test failed"); + Config.get('test'); + fail('threshold value < 1 is invalid test failed'); done(); }) .catch(err => { if ( err && err === - "Account lockout threshold should be an integer greater than 0 and less than 1000" + 'Account lockout threshold should be an integer greater than 0 and less than 1000' ) { done(); } else { fail( - "threshold value < 1 is invalid test failed: " + JSON.stringify(err) + 'threshold value < 1 is invalid test failed: ' + JSON.stringify(err) ); done(); } }); }); - it("throw error if threshold is > 999", done => { + it('throw error if threshold is > 999', done => { reconfigureServer({ - appName: "threshold", + appName: 'threshold', accountLockout: { duration: 5, threshold: 1000, }, - publicServerURL: "https://my.public.server.com/1", + publicServerURL: 'https://my.public.server.com/1', }) .then(() => { - Config.get("test"); - fail("threshold value > 999 is invalid test failed"); + Config.get('test'); + fail('threshold value > 999 is invalid test failed'); done(); }) .catch(err => { if ( err && err === - "Account lockout threshold should be an integer greater than 0 and less than 1000" + 'Account lockout threshold should be an integer greater than 0 and less than 1000' ) { done(); } else { fail( - "threshold value > 999 is invalid test failed: " + + 'threshold value > 999 is invalid test failed: ' + JSON.stringify(err) ); done(); @@ -206,60 +206,60 @@ describe("Account Lockout Policy: ", () => { }); }); - it("throw error if duration is <= 0", done => { + it('throw error if duration is <= 0', done => { reconfigureServer({ - appName: "duration", + appName: 'duration', accountLockout: { duration: 0, threshold: 5, }, - publicServerURL: "https://my.public.server.com/1", + publicServerURL: 'https://my.public.server.com/1', }) .then(() => { - Config.get("test"); - fail("duration value < 1 is invalid test failed"); + Config.get('test'); + fail('duration value < 1 is invalid test failed'); done(); }) .catch(err => { if ( err && err === - "Account lockout duration should be greater than 0 and less than 100000" + 'Account lockout duration should be greater than 0 and less than 100000' ) { done(); } else { fail( - "duration value < 1 is invalid test failed: " + JSON.stringify(err) + 'duration value < 1 is invalid test failed: ' + JSON.stringify(err) ); done(); } }); }); - it("throw error if duration is > 99999", done => { + it('throw error if duration is > 99999', done => { reconfigureServer({ - appName: "duration", + appName: 'duration', accountLockout: { duration: 100000, threshold: 5, }, - publicServerURL: "https://my.public.server.com/1", + publicServerURL: 'https://my.public.server.com/1', }) .then(() => { - Config.get("test"); - fail("duration value > 99999 is invalid test failed"); + Config.get('test'); + fail('duration value > 99999 is invalid test failed'); done(); }) .catch(err => { if ( err && err === - "Account lockout duration should be greater than 0 and less than 100000" + 'Account lockout duration should be greater than 0 and less than 100000' ) { done(); } else { fail( - "duration value > 99999 is invalid test failed: " + + 'duration value > 99999 is invalid test failed: ' + JSON.stringify(err) ); done(); @@ -267,126 +267,126 @@ describe("Account Lockout Policy: ", () => { }); }); - it("lock account if failed login attempts are above threshold", done => { + it('lock account if failed login attempts are above threshold', done => { reconfigureServer({ - appName: "lockout threshold", + appName: 'lockout threshold', accountLockout: { duration: 1, threshold: 2, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }) .then(() => { const user = new Parse.User(); - user.setUsername("username2"); - user.setPassword("failedLoginAttemptsThreshold"); + user.setUsername('username2'); + user.setPassword('failedLoginAttemptsThreshold'); return user.signUp(); }) .then(() => { return loginWithWrongCredentialsShouldFail( - "username2", - "wrong password" + 'username2', + 'wrong password' ); }) .then(() => { return loginWithWrongCredentialsShouldFail( - "username2", - "wrong password" + 'username2', + 'wrong password' ); }) .then(() => { - return isAccountLockoutError("username2", "wrong password", 1, 1); + return isAccountLockoutError('username2', 'wrong password', 1, 1); }) .then(() => { done(); }) .catch(err => { fail( - "lock account after failed login attempts test failed: " + + 'lock account after failed login attempts test failed: ' + JSON.stringify(err) ); done(); }); }); - it("lock account for accountPolicy.duration minutes if failed login attempts are above threshold", done => { + it('lock account for accountPolicy.duration minutes if failed login attempts are above threshold', done => { reconfigureServer({ - appName: "lockout threshold", + appName: 'lockout threshold', accountLockout: { duration: 0.05, // 0.05*60 = 3 secs threshold: 2, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }) .then(() => { const user = new Parse.User(); - user.setUsername("username3"); - user.setPassword("failedLoginAttemptsThreshold"); + user.setUsername('username3'); + user.setPassword('failedLoginAttemptsThreshold'); return user.signUp(); }) .then(() => { return loginWithWrongCredentialsShouldFail( - "username3", - "wrong password" + 'username3', + 'wrong password' ); }) .then(() => { return loginWithWrongCredentialsShouldFail( - "username3", - "wrong password" + 'username3', + 'wrong password' ); }) .then(() => { - return isAccountLockoutError("username3", "wrong password", 0.05, 1); + return isAccountLockoutError('username3', 'wrong password', 0.05, 1); }) .then(() => { // account should still be locked even after 2 seconds. - return isAccountLockoutError("username3", "wrong password", 0.05, 2000); + return isAccountLockoutError('username3', 'wrong password', 0.05, 2000); }) .then(() => { done(); }) .catch(err => { fail( - "account should be locked for duration mins test failed: " + + 'account should be locked for duration mins test failed: ' + JSON.stringify(err) ); done(); }); }); - it("allow login for locked account after accountPolicy.duration minutes", done => { + it('allow login for locked account after accountPolicy.duration minutes', done => { reconfigureServer({ - appName: "lockout threshold", + appName: 'lockout threshold', accountLockout: { duration: 0.05, // 0.05*60 = 3 secs threshold: 2, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }) .then(() => { const user = new Parse.User(); - user.setUsername("username4"); - user.setPassword("correct password"); + user.setUsername('username4'); + user.setPassword('correct password'); return user.signUp(); }) .then(() => { return loginWithWrongCredentialsShouldFail( - "username4", - "wrong password" + 'username4', + 'wrong password' ); }) .then(() => { return loginWithWrongCredentialsShouldFail( - "username4", - "wrong password" + 'username4', + 'wrong password' ); }) .then(() => { // allow locked user to login after 3 seconds with a valid userid and password return new Promise((resolve, reject) => { setTimeout(() => { - Parse.User.logIn("username4", "correct password") + Parse.User.logIn('username4', 'correct password') .then(() => resolve()) .catch(err => reject(err)); }, 3001); @@ -397,7 +397,7 @@ describe("Account Lockout Policy: ", () => { }) .catch(err => { fail( - "allow login for locked account after accountPolicy.duration minutes test failed: " + + 'allow login for locked account after accountPolicy.duration minutes test failed: ' + JSON.stringify(err) ); done(); @@ -405,7 +405,7 @@ describe("Account Lockout Policy: ", () => { }); }); -describe("lockout with password reset option", () => { +describe('lockout with password reset option', () => { let sendPasswordResetEmail; async function setup(options = {}) { @@ -417,9 +417,9 @@ describe("lockout with password reset option", () => { options ); const config = { - appName: "exampleApp", + appName: 'exampleApp', accountLockout: accountLockout, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', emailAdapter: { sendVerificationEmail: () => Promise.resolve(), sendPasswordResetEmail: () => Promise.resolve(), @@ -430,11 +430,11 @@ describe("lockout with password reset option", () => { sendPasswordResetEmail = spyOn( config.emailAdapter, - "sendPasswordResetEmail" + 'sendPasswordResetEmail' ).and.callThrough(); } - it("accepts valid unlockOnPasswordReset option", async () => { + it('accepts valid unlockOnPasswordReset option', async () => { const values = [true, false]; for (const value of values) { @@ -442,15 +442,15 @@ describe("lockout with password reset option", () => { } }); - it("rejects invalid unlockOnPasswordReset option", async () => { - const values = ["a", 0, {}, [], null]; + it('rejects invalid unlockOnPasswordReset option', async () => { + const values = ['a', 0, {}, [], null]; for (const value of values) { await expectAsync(setup({ unlockOnPasswordReset: value })).toBeRejected(); } }); - it("uses default value if unlockOnPasswordReset is not set", async () => { + it('uses default value if unlockOnPasswordReset is not set', async () => { await expectAsync( setup({ unlockOnPasswordReset: undefined }) ).toBeResolved(); @@ -461,20 +461,20 @@ describe("lockout with password reset option", () => { ); }); - it("allow login for locked account after password reset", async () => { + it('allow login for locked account after password reset', async () => { await setup({ unlockOnPasswordReset: true }); const config = Config.get(Parse.applicationId); const user = new Parse.User(); - const username = "exampleUsername"; - const password = "examplePassword"; + const username = 'exampleUsername'; + const password = 'examplePassword'; user.setUsername(username); user.setPassword(password); - user.setEmail("mail@example.com"); + user.setEmail('mail@example.com'); await user.signUp(); await expectAsync( - Parse.User.logIn(username, "incorrectPassword") + Parse.User.logIn(username, 'incorrectPassword') ).toBeRejected(); await expectAsync(Parse.User.logIn(username, password)).toBeRejected(); @@ -483,14 +483,14 @@ describe("lockout with password reset option", () => { const link = sendPasswordResetEmail.calls.all()[0].args[0].link; const linkUrl = new URL(link); - const token = linkUrl.searchParams.get("token"); - const newPassword = "newPassword"; + const token = linkUrl.searchParams.get('token'); + const newPassword = 'newPassword'; await request({ - method: "POST", + method: 'POST', url: `${config.publicServerURL}/apps/test/request_password_reset`, body: `new_password=${newPassword}&token=${token}`, headers: { - "Content-Type": "application/x-www-form-urlencoded", + 'Content-Type': 'application/x-www-form-urlencoded', }, followRedirects: false, }); @@ -498,20 +498,20 @@ describe("lockout with password reset option", () => { await expectAsync(Parse.User.logIn(username, newPassword)).toBeResolved(); }); - it("reject login for locked account after password reset (default)", async () => { + it('reject login for locked account after password reset (default)', async () => { await setup(); const config = Config.get(Parse.applicationId); const user = new Parse.User(); - const username = "exampleUsername"; - const password = "examplePassword"; + const username = 'exampleUsername'; + const password = 'examplePassword'; user.setUsername(username); user.setPassword(password); - user.setEmail("mail@example.com"); + user.setEmail('mail@example.com'); await user.signUp(); await expectAsync( - Parse.User.logIn(username, "incorrectPassword") + Parse.User.logIn(username, 'incorrectPassword') ).toBeRejected(); await expectAsync(Parse.User.logIn(username, password)).toBeRejected(); @@ -520,14 +520,14 @@ describe("lockout with password reset option", () => { const link = sendPasswordResetEmail.calls.all()[0].args[0].link; const linkUrl = new URL(link); - const token = linkUrl.searchParams.get("token"); - const newPassword = "newPassword"; + const token = linkUrl.searchParams.get('token'); + const newPassword = 'newPassword'; await request({ - method: "POST", + method: 'POST', url: `${config.publicServerURL}/apps/test/request_password_reset`, body: `new_password=${newPassword}&token=${token}`, headers: { - "Content-Type": "application/x-www-form-urlencoded", + 'Content-Type': 'application/x-www-form-urlencoded', }, followRedirects: false, }); diff --git a/spec/AdaptableController.spec.js b/spec/AdaptableController.spec.js index c0606fae6c..e82e1d37c9 100644 --- a/spec/AdaptableController.spec.js +++ b/spec/AdaptableController.spec.js @@ -1,8 +1,8 @@ const AdaptableController = - require("../lib/Controllers/AdaptableController").AdaptableController; -const FilesAdapter = require("../lib/Adapters/Files/FilesAdapter").default; + require('../lib/Controllers/AdaptableController').AdaptableController; +const FilesAdapter = require('../lib/Adapters/Files/FilesAdapter').default; const FilesController = - require("../lib/Controllers/FilesController").FilesController; + require('../lib/Controllers/FilesController').FilesController; const MockController = function (options) { AdaptableController.call(this, options); @@ -10,20 +10,20 @@ const MockController = function (options) { MockController.prototype = Object.create(AdaptableController.prototype); MockController.prototype.constructor = AdaptableController; -describe("AdaptableController", () => { - it("should use the provided adapter", done => { +describe('AdaptableController', () => { + it('should use the provided adapter', done => { const adapter = new FilesAdapter(); const controller = new FilesController(adapter); expect(controller.adapter).toBe(adapter); // make sure _adapter is private expect(controller._adapter).toBe(undefined); // Override _adapter is not doing anything - controller._adapter = "Hello"; + controller._adapter = 'Hello'; expect(controller.adapter).toBe(adapter); done(); }); - it("should throw when creating a new mock controller", done => { + it('should throw when creating a new mock controller', done => { const adapter = new FilesAdapter(); expect(() => { new MockController(adapter); @@ -31,7 +31,7 @@ describe("AdaptableController", () => { done(); }); - it("should fail setting the wrong adapter to the controller", done => { + it('should fail setting the wrong adapter to the controller', done => { function WrongAdapter() {} const adapter = new FilesAdapter(); const controller = new FilesController(adapter); @@ -42,7 +42,7 @@ describe("AdaptableController", () => { done(); }); - it("should fail to instantiate a controller with wrong adapter", done => { + it('should fail to instantiate a controller with wrong adapter', done => { function WrongAdapter() {} const adapter = new WrongAdapter(); expect(() => { @@ -51,14 +51,14 @@ describe("AdaptableController", () => { done(); }); - it("should fail to instantiate a controller without an adapter", done => { + it('should fail to instantiate a controller without an adapter', done => { expect(() => { new FilesController(); }).toThrow(); done(); }); - it("should accept an object adapter", done => { + it('should accept an object adapter', done => { const adapter = { createFile: function () {}, deleteFile: function () {}, @@ -72,7 +72,7 @@ describe("AdaptableController", () => { done(); }); - it("should accept an prototype based object adapter", done => { + it('should accept an prototype based object adapter', done => { function AGoodAdapter() {} AGoodAdapter.prototype.createFile = function () {}; AGoodAdapter.prototype.deleteFile = function () {}; diff --git a/spec/AdapterLoader.spec.js b/spec/AdapterLoader.spec.js index 72fe7016f6..2c8a518ef6 100644 --- a/spec/AdapterLoader.spec.js +++ b/spec/AdapterLoader.spec.js @@ -1,64 +1,64 @@ -const { loadAdapter, loadModule } = require("../lib/Adapters/AdapterLoader"); -const FilesAdapter = require("@parse/fs-files-adapter").default; -const MockFilesAdapter = require("mock-files-adapter"); -const Config = require("../lib/Config"); +const { loadAdapter, loadModule } = require('../lib/Adapters/AdapterLoader'); +const FilesAdapter = require('@parse/fs-files-adapter').default; +const MockFilesAdapter = require('mock-files-adapter'); +const Config = require('../lib/Config'); -describe("AdapterLoader", () => { - it("should instantiate an adapter from string in object", done => { - const adapterPath = require("path").resolve("./spec/support/MockAdapter"); +describe('AdapterLoader', () => { + it('should instantiate an adapter from string in object', done => { + const adapterPath = require('path').resolve('./spec/support/MockAdapter'); const adapter = loadAdapter({ adapter: adapterPath, options: { - key: "value", - foo: "bar", + key: 'value', + foo: 'bar', }, }); expect(adapter instanceof Object).toBe(true); - expect(adapter.options.key).toBe("value"); - expect(adapter.options.foo).toBe("bar"); + expect(adapter.options.key).toBe('value'); + expect(adapter.options.foo).toBe('bar'); done(); }); - it("should instantiate an adapter from string", done => { - const adapterPath = require("path").resolve("./spec/support/MockAdapter"); + it('should instantiate an adapter from string', done => { + const adapterPath = require('path').resolve('./spec/support/MockAdapter'); const adapter = loadAdapter(adapterPath); expect(adapter instanceof Object).toBe(true); done(); }); - it("should instantiate an adapter from string that is module", done => { - const adapterPath = require("path").resolve( - "./lib/Adapters/Files/FilesAdapter" + it('should instantiate an adapter from string that is module', done => { + const adapterPath = require('path').resolve( + './lib/Adapters/Files/FilesAdapter' ); const adapter = loadAdapter({ adapter: adapterPath, }); - expect(typeof adapter).toBe("object"); - expect(typeof adapter.createFile).toBe("function"); - expect(typeof adapter.deleteFile).toBe("function"); - expect(typeof adapter.getFileData).toBe("function"); - expect(typeof adapter.getFileLocation).toBe("function"); + expect(typeof adapter).toBe('object'); + expect(typeof adapter.createFile).toBe('function'); + expect(typeof adapter.deleteFile).toBe('function'); + expect(typeof adapter.getFileData).toBe('function'); + expect(typeof adapter.getFileLocation).toBe('function'); done(); }); - it("should instantiate an adapter from npm module", done => { + it('should instantiate an adapter from npm module', done => { const adapter = loadAdapter({ - module: "@parse/fs-files-adapter", + module: '@parse/fs-files-adapter', }); - expect(typeof adapter).toBe("object"); - expect(typeof adapter.createFile).toBe("function"); - expect(typeof adapter.deleteFile).toBe("function"); - expect(typeof adapter.getFileData).toBe("function"); - expect(typeof adapter.getFileLocation).toBe("function"); + expect(typeof adapter).toBe('object'); + expect(typeof adapter.createFile).toBe('function'); + expect(typeof adapter.deleteFile).toBe('function'); + expect(typeof adapter.getFileData).toBe('function'); + expect(typeof adapter.getFileLocation).toBe('function'); done(); }); - it("should instantiate an adapter from function/Class", done => { + it('should instantiate an adapter from function/Class', done => { const adapter = loadAdapter({ adapter: FilesAdapter, }); @@ -66,52 +66,52 @@ describe("AdapterLoader", () => { done(); }); - it("should instantiate the default adapter from Class", done => { + it('should instantiate the default adapter from Class', done => { const adapter = loadAdapter(null, FilesAdapter); expect(adapter instanceof FilesAdapter).toBe(true); done(); }); - it("should use the default adapter", done => { + it('should use the default adapter', done => { const defaultAdapter = new FilesAdapter(); const adapter = loadAdapter(null, defaultAdapter); expect(adapter instanceof FilesAdapter).toBe(true); done(); }); - it("should use the provided adapter", done => { + it('should use the provided adapter', done => { const originalAdapter = new FilesAdapter(); const adapter = loadAdapter(originalAdapter); expect(adapter).toBe(originalAdapter); done(); }); - it("should fail loading an improperly configured adapter", done => { + it('should fail loading an improperly configured adapter', done => { const Adapter = function (options) { if (!options.foo) { - throw "foo is required for that adapter"; + throw 'foo is required for that adapter'; } }; const adapterOptions = { - param: "key", + param: 'key', doSomething: function () {}, }; expect(() => { const adapter = loadAdapter(adapterOptions, Adapter); expect(adapter).toEqual(adapterOptions); - }).not.toThrow("foo is required for that adapter"); + }).not.toThrow('foo is required for that adapter'); done(); }); - it("should load push adapter from options", async () => { + it('should load push adapter from options', async () => { const options = { android: { - senderId: "yolo", - apiKey: "yolo", + senderId: 'yolo', + apiKey: 'yolo', }, }; - const ParsePushAdapter = await loadModule("@parse/push-adapter"); + const ParsePushAdapter = await loadModule('@parse/push-adapter'); expect(() => { const adapter = loadAdapter(undefined, ParsePushAdapter, options); expect(adapter.constructor).toBe(ParsePushAdapter); @@ -119,13 +119,13 @@ describe("AdapterLoader", () => { }).not.toThrow(); }); - it("should load custom push adapter from string (#3544)", done => { - const adapterPath = require("path").resolve( - "./spec/support/MockPushAdapter" + it('should load custom push adapter from string (#3544)', done => { + const adapterPath = require('path').resolve( + './spec/support/MockPushAdapter' ); const options = { ios: { - bundleId: "bundle.id", + bundleId: 'bundle.id', }, }; const pushAdapterOptions = { @@ -138,20 +138,20 @@ describe("AdapterLoader", () => { }).then(() => { const config = Config.get(Parse.applicationId); const pushAdapter = config.pushWorker.adapter; - expect(pushAdapter.getValidPushTypes()).toEqual(["ios"]); + expect(pushAdapter.getValidPushTypes()).toEqual(['ios']); expect(pushAdapter.options).toEqual(pushAdapterOptions); done(); }); }).not.toThrow(); }); - it("should load custom database adapter from config", done => { - const adapterPath = require("path").resolve( - "./spec/support/MockDatabaseAdapter" + it('should load custom database adapter from config', done => { + const adapterPath = require('path').resolve( + './spec/support/MockDatabaseAdapter' ); const options = { - databaseURI: "oracledb://user:password@localhost:1521/freepdb1", - collectionPrefix: "", + databaseURI: 'oracledb://user:password@localhost:1521/freepdb1', + collectionPrefix: '', }; const databaseAdapterOptions = { adapter: adapterPath, @@ -166,9 +166,9 @@ describe("AdapterLoader", () => { done(); }); - it("should load file adapter from direct passing", done => { - spyOn(console, "warn").and.callFake(() => {}); - const mockFilesAdapter = new MockFilesAdapter("key", "secret", "bucket"); + it('should load file adapter from direct passing', done => { + spyOn(console, 'warn').and.callFake(() => {}); + const mockFilesAdapter = new MockFilesAdapter('key', 'secret', 'bucket'); expect(() => { const adapter = loadAdapter(mockFilesAdapter, FilesAdapter); expect(adapter).toBe(mockFilesAdapter); diff --git a/spec/AggregateRouter.spec.js b/spec/AggregateRouter.spec.js index a58e21cdad..bd48c7afed 100644 --- a/spec/AggregateRouter.spec.js +++ b/spec/AggregateRouter.spec.js @@ -1,8 +1,8 @@ const AggregateRouter = - require("../lib/Routers/AggregateRouter").AggregateRouter; + require('../lib/Routers/AggregateRouter').AggregateRouter; -describe("AggregateRouter", () => { - it("get pipeline from Array", () => { +describe('AggregateRouter', () => { + it('get pipeline from Array', () => { const body = [ { $group: { _id: {} }, @@ -13,7 +13,7 @@ describe("AggregateRouter", () => { expect(result).toEqual(expected); }); - it("get pipeline from Object", () => { + it('get pipeline from Object', () => { const body = { $group: { _id: {} }, }; @@ -22,7 +22,7 @@ describe("AggregateRouter", () => { expect(result).toEqual(expected); }); - it("get pipeline from Pipeline Operator (Array)", () => { + it('get pipeline from Pipeline Operator (Array)', () => { const body = { pipeline: [ { @@ -35,7 +35,7 @@ describe("AggregateRouter", () => { expect(result).toEqual(expected); }); - it("get pipeline from Pipeline Operator (Object)", () => { + it('get pipeline from Pipeline Operator (Object)', () => { const body = { pipeline: { $group: { _id: {} }, @@ -46,39 +46,39 @@ describe("AggregateRouter", () => { expect(result).toEqual(expected); }); - it("get pipeline fails multiple keys in Array stage ", () => { + it('get pipeline fails multiple keys in Array stage ', () => { const body = [ { $group: { _id: {} }, - $match: { name: "Test" }, + $match: { name: 'Test' }, }, ]; expect(() => AggregateRouter.getPipeline(body)).toThrow( new Parse.Error( Parse.Error.INVALID_QUERY, - "Pipeline stages should only have one key but found $group, $match." + 'Pipeline stages should only have one key but found $group, $match.' ) ); }); - it("get pipeline fails multiple keys in Pipeline Operator Array stage ", () => { + it('get pipeline fails multiple keys in Pipeline Operator Array stage ', () => { const body = { pipeline: [ { $group: { _id: {} }, - $match: { name: "Test" }, + $match: { name: 'Test' }, }, ], }; expect(() => AggregateRouter.getPipeline(body)).toThrow( new Parse.Error( Parse.Error.INVALID_QUERY, - "Pipeline stages should only have one key but found $group, $match." + 'Pipeline stages should only have one key but found $group, $match.' ) ); }); - it("get search pipeline from Pipeline Operator (Array)", () => { + it('get search pipeline from Pipeline Operator (Array)', () => { const body = { pipeline: { $search: {}, @@ -89,27 +89,27 @@ describe("AggregateRouter", () => { expect(result).toEqual(expected); }); - it("support stage name starting with `$`", () => { + it('support stage name starting with `$`', () => { const body = { - $match: { someKey: "whatever" }, + $match: { someKey: 'whatever' }, }; - const expected = [{ $match: { someKey: "whatever" } }]; + const expected = [{ $match: { someKey: 'whatever' } }]; const result = AggregateRouter.getPipeline(body); expect(result).toEqual(expected); }); - it("support nested stage names starting with `$`", () => { + it('support nested stage names starting with `$`', () => { const body = [ { $lookup: { - from: "ACollection", - let: { id: "_id" }, - as: "results", + from: 'ACollection', + let: { id: '_id' }, + as: 'results', pipeline: [ { $match: { $expr: { - $eq: ["$_id", "$$id"], + $eq: ['$_id', '$$id'], }, }, }, @@ -120,14 +120,14 @@ describe("AggregateRouter", () => { const expected = [ { $lookup: { - from: "ACollection", - let: { id: "_id" }, - as: "results", + from: 'ACollection', + let: { id: '_id' }, + as: 'results', pipeline: [ { $match: { $expr: { - $eq: ["$_id", "$$id"], + $eq: ['$_id', '$$id'], }, }, }, @@ -139,16 +139,16 @@ describe("AggregateRouter", () => { expect(result).toEqual(expected); }); - it("support the use of `_id` in stages", () => { + it('support the use of `_id` in stages', () => { const body = [ - { $match: { _id: "randomId" } }, + { $match: { _id: 'randomId' } }, { $sort: { _id: -1 } }, { $addFields: { _id: 1 } }, { $group: { _id: {} } }, { $project: { _id: 0 } }, ]; const expected = [ - { $match: { _id: "randomId" } }, + { $match: { _id: 'randomId' } }, { $sort: { _id: -1 } }, { $addFields: { _id: 1 } }, { $group: { _id: {} } }, @@ -158,8 +158,8 @@ describe("AggregateRouter", () => { expect(result).toEqual(expected); }); - it("should throw with invalid stage", () => { - expect(() => AggregateRouter.getPipeline([{ foo: "bar" }])).toThrow( + it('should throw with invalid stage', () => { + expect(() => AggregateRouter.getPipeline([{ foo: 'bar' }])).toThrow( new Parse.Error( Parse.Error.INVALID_QUERY, `Invalid aggregate stage 'foo'.` @@ -167,9 +167,9 @@ describe("AggregateRouter", () => { ); }); - it("should throw with invalid group", () => { + it('should throw with invalid group', () => { expect(() => - AggregateRouter.getPipeline([{ $group: { objectId: "bar" } }]) + AggregateRouter.getPipeline([{ $group: { objectId: 'bar' } }]) ).toThrow( new Parse.Error( Parse.Error.INVALID_QUERY, diff --git a/spec/Analytics.spec.js b/spec/Analytics.spec.js index 0029ee3808..049a2795c8 100644 --- a/spec/Analytics.spec.js +++ b/spec/Analytics.spec.js @@ -3,16 +3,16 @@ const analyticsAdapter = { trackEvent: function () {}, }; -describe("AnalyticsController", () => { - it("should track a simple event", done => { - spyOn(analyticsAdapter, "trackEvent").and.callThrough(); +describe('AnalyticsController', () => { + it('should track a simple event', done => { + spyOn(analyticsAdapter, 'trackEvent').and.callThrough(); reconfigureServer({ analyticsAdapter, }) .then(() => { - return Parse.Analytics.track("MyEvent", { - key: "value", - count: "0", + return Parse.Analytics.track('MyEvent', { + key: 'value', + count: '0', }); }) .then( @@ -20,11 +20,11 @@ describe("AnalyticsController", () => { expect(analyticsAdapter.trackEvent).toHaveBeenCalled(); const lastCall = analyticsAdapter.trackEvent.calls.first(); const args = lastCall.args; - expect(args[0]).toEqual("MyEvent"); + expect(args[0]).toEqual('MyEvent'); expect(args[1]).toEqual({ dimensions: { - key: "value", - count: "0", + key: 'value', + count: '0', }, }); done(); @@ -36,15 +36,15 @@ describe("AnalyticsController", () => { ); }); - it("should track a app opened event", done => { - spyOn(analyticsAdapter, "appOpened").and.callThrough(); + it('should track a app opened event', done => { + spyOn(analyticsAdapter, 'appOpened').and.callThrough(); reconfigureServer({ analyticsAdapter, }) .then(() => { - return Parse.Analytics.track("AppOpened", { - key: "value", - count: "0", + return Parse.Analytics.track('AppOpened', { + key: 'value', + count: '0', }); }) .then( @@ -54,8 +54,8 @@ describe("AnalyticsController", () => { const args = lastCall.args; expect(args[0]).toEqual({ dimensions: { - key: "value", - count: "0", + key: 'value', + count: '0', }, }); done(); diff --git a/spec/AudienceRouter.spec.js b/spec/AudienceRouter.spec.js index da199ae460..e1f15a75ad 100644 --- a/spec/AudienceRouter.spec.js +++ b/spec/AudienceRouter.spec.js @@ -1,19 +1,19 @@ -const auth = require("../lib/Auth"); -const Config = require("../lib/Config"); -const rest = require("../lib/rest"); -const request = require("../lib/request"); +const auth = require('../lib/Auth'); +const Config = require('../lib/Config'); +const rest = require('../lib/rest'); +const request = require('../lib/request'); const AudiencesRouter = - require("../lib/Routers/AudiencesRouter").AudiencesRouter; + require('../lib/Routers/AudiencesRouter').AudiencesRouter; -describe("AudiencesRouter", () => { - it("uses find condition from request.body", done => { - const config = Config.get("test"); +describe('AudiencesRouter', () => { + it('uses find condition from request.body', done => { + const config = Config.get('test'); const androidAudienceRequest = { - name: "Android Users", + name: 'Android Users', query: '{ "test": "android" }', }; const iosAudienceRequest = { - name: "Iphone Users", + name: 'Iphone Users', query: '{ "test": "ios" }', }; const request = { @@ -30,12 +30,12 @@ describe("AudiencesRouter", () => { const router = new AudiencesRouter(); rest - .create(config, auth.nobody(config), "_Audience", androidAudienceRequest) + .create(config, auth.nobody(config), '_Audience', androidAudienceRequest) .then(() => { return rest.create( config, auth.nobody(config), - "_Audience", + '_Audience', iosAudienceRequest ); }) @@ -53,14 +53,14 @@ describe("AudiencesRouter", () => { }); }); - it("uses find condition from request.query", done => { - const config = Config.get("test"); + it('uses find condition from request.query', done => { + const config = Config.get('test'); const androidAudienceRequest = { - name: "Android Users", + name: 'Android Users', query: '{ "test": "android" }', }; const iosAudienceRequest = { - name: "Iphone Users", + name: 'Iphone Users', query: '{ "test": "ios" }', }; const request = { @@ -77,12 +77,12 @@ describe("AudiencesRouter", () => { const router = new AudiencesRouter(); rest - .create(config, auth.nobody(config), "_Audience", androidAudienceRequest) + .create(config, auth.nobody(config), '_Audience', androidAudienceRequest) .then(() => { return rest.create( config, auth.nobody(config), - "_Audience", + '_Audience', iosAudienceRequest ); }) @@ -100,14 +100,14 @@ describe("AudiencesRouter", () => { }); }); - it("query installations with limit = 0", done => { - const config = Config.get("test"); + it('query installations with limit = 0', done => { + const config = Config.get('test'); const androidAudienceRequest = { - name: "Android Users", + name: 'Android Users', query: '{ "test": "android" }', }; const iosAudienceRequest = { - name: "Iphone Users", + name: 'Iphone Users', query: '{ "test": "ios" }', }; const request = { @@ -120,15 +120,15 @@ describe("AudiencesRouter", () => { info: {}, }; - Config.get("test"); + Config.get('test'); const router = new AudiencesRouter(); rest - .create(config, auth.nobody(config), "_Audience", androidAudienceRequest) + .create(config, auth.nobody(config), '_Audience', androidAudienceRequest) .then(() => { return rest.create( config, auth.nobody(config), - "_Audience", + '_Audience', iosAudienceRequest ); }) @@ -146,14 +146,14 @@ describe("AudiencesRouter", () => { }); }); - it_exclude_dbs(["postgres"])("query installations with count = 1", done => { - const config = Config.get("test"); + it_exclude_dbs(['postgres'])('query installations with count = 1', done => { + const config = Config.get('test'); const androidAudienceRequest = { - name: "Android Users", + name: 'Android Users', query: '{ "test": "android" }', }; const iosAudienceRequest = { - name: "Iphone Users", + name: 'Iphone Users', query: '{ "test": "ios" }', }; const request = { @@ -168,12 +168,12 @@ describe("AudiencesRouter", () => { const router = new AudiencesRouter(); rest - .create(config, auth.nobody(config), "_Audience", androidAudienceRequest) + .create(config, auth.nobody(config), '_Audience', androidAudienceRequest) .then(() => rest.create( config, auth.nobody(config), - "_Audience", + '_Audience', iosAudienceRequest ) ) @@ -190,16 +190,16 @@ describe("AudiencesRouter", () => { }); }); - it_exclude_dbs(["postgres"])( - "query installations with limit = 0 and count = 1", + it_exclude_dbs(['postgres'])( + 'query installations with limit = 0 and count = 1', done => { - const config = Config.get("test"); + const config = Config.get('test'); const androidAudienceRequest = { - name: "Android Users", + name: 'Android Users', query: '{ "test": "android" }', }; const iosAudienceRequest = { - name: "Iphone Users", + name: 'Iphone Users', query: '{ "test": "ios" }', }; const request = { @@ -218,14 +218,14 @@ describe("AudiencesRouter", () => { .create( config, auth.nobody(config), - "_Audience", + '_Audience', androidAudienceRequest ) .then(() => { return rest.create( config, auth.nobody(config), - "_Audience", + '_Audience', iosAudienceRequest ); }) @@ -245,49 +245,49 @@ describe("AudiencesRouter", () => { } ); - it("should create, read, update and delete audiences throw api", done => { + it('should create, read, update and delete audiences throw api', done => { Parse._request( - "POST", - "push_audiences", - { name: "My Audience", query: JSON.stringify({ deviceType: "ios" }) }, + 'POST', + 'push_audiences', + { name: 'My Audience', query: JSON.stringify({ deviceType: 'ios' }) }, { useMasterKey: true } ).then(() => { - Parse._request("GET", "push_audiences", {}, { useMasterKey: true }).then( + Parse._request('GET', 'push_audiences', {}, { useMasterKey: true }).then( results => { expect(results.results.length).toEqual(1); - expect(results.results[0].name).toEqual("My Audience"); - expect(results.results[0].query.deviceType).toEqual("ios"); + expect(results.results[0].name).toEqual('My Audience'); + expect(results.results[0].query.deviceType).toEqual('ios'); Parse._request( - "GET", + 'GET', `push_audiences/${results.results[0].objectId}`, {}, { useMasterKey: true } ).then(results => { - expect(results.name).toEqual("My Audience"); - expect(results.query.deviceType).toEqual("ios"); + expect(results.name).toEqual('My Audience'); + expect(results.query.deviceType).toEqual('ios'); Parse._request( - "PUT", + 'PUT', `push_audiences/${results.objectId}`, - { name: "My Audience 2" }, + { name: 'My Audience 2' }, { useMasterKey: true } ).then(() => { Parse._request( - "GET", + 'GET', `push_audiences/${results.objectId}`, {}, { useMasterKey: true } ).then(results => { - expect(results.name).toEqual("My Audience 2"); - expect(results.query.deviceType).toEqual("ios"); + expect(results.name).toEqual('My Audience 2'); + expect(results.query.deviceType).toEqual('ios'); Parse._request( - "DELETE", + 'DELETE', `push_audiences/${results.objectId}`, {}, { useMasterKey: true } ).then(() => { Parse._request( - "GET", - "push_audiences", + 'GET', + 'push_audiences', {}, { useMasterKey: true } ).then(results => { @@ -303,75 +303,75 @@ describe("AudiencesRouter", () => { }); }); - it("should only create with master key", done => { - Parse._request("POST", "push_audiences", { - name: "My Audience", - query: JSON.stringify({ deviceType: "ios" }), + it('should only create with master key', done => { + Parse._request('POST', 'push_audiences', { + name: 'My Audience', + query: JSON.stringify({ deviceType: 'ios' }), }).then( () => {}, error => { - expect(error.message).toEqual("unauthorized: master key is required"); + expect(error.message).toEqual('unauthorized: master key is required'); done(); } ); }); - it("should only find with master key", done => { - Parse._request("GET", "push_audiences", {}).then( + it('should only find with master key', done => { + Parse._request('GET', 'push_audiences', {}).then( () => {}, error => { - expect(error.message).toEqual("unauthorized: master key is required"); + expect(error.message).toEqual('unauthorized: master key is required'); done(); } ); }); - it("should only get with master key", done => { - Parse._request("GET", `push_audiences/someId`, {}).then( + it('should only get with master key', done => { + Parse._request('GET', `push_audiences/someId`, {}).then( () => {}, error => { - expect(error.message).toEqual("unauthorized: master key is required"); + expect(error.message).toEqual('unauthorized: master key is required'); done(); } ); }); - it("should only update with master key", done => { - Parse._request("PUT", `push_audiences/someId`, { - name: "My Audience 2", + it('should only update with master key', done => { + Parse._request('PUT', `push_audiences/someId`, { + name: 'My Audience 2', }).then( () => {}, error => { - expect(error.message).toEqual("unauthorized: master key is required"); + expect(error.message).toEqual('unauthorized: master key is required'); done(); } ); }); - it("should only delete with master key", done => { - Parse._request("DELETE", `push_audiences/someId`, {}).then( + it('should only delete with master key', done => { + Parse._request('DELETE', `push_audiences/someId`, {}).then( () => {}, error => { - expect(error.message).toEqual("unauthorized: master key is required"); + expect(error.message).toEqual('unauthorized: master key is required'); done(); } ); }); - it_id("af1111b5-3251-4b40-8f06-fb0fc624fa91")(it_exclude_dbs(["postgres"]))( - "should support legacy parse.com audience fields", + it_id('af1111b5-3251-4b40-8f06-fb0fc624fa91')(it_exclude_dbs(['postgres']))( + 'should support legacy parse.com audience fields', done => { const database = Config.get(Parse.applicationId).database.adapter .database; const now = new Date(); Parse._request( - "POST", - "push_audiences", - { name: "My Audience", query: JSON.stringify({ deviceType: "ios" }) }, + 'POST', + 'push_audiences', + { name: 'My Audience', query: JSON.stringify({ deviceType: 'ios' }) }, { useMasterKey: true } ).then(audience => { database - .collection("test__Audience") + .collection('test__Audience') .updateOne( { _id: audience.objectId }, { @@ -385,21 +385,21 @@ describe("AudiencesRouter", () => { expect(result).toBeTruthy(); database - .collection("test__Audience") + .collection('test__Audience') .find({ _id: audience.objectId }) .toArray() .then(rows => { - expect(rows[0]["times_used"]).toEqual(1); - expect(rows[0]["_last_used"]).toEqual(now); + expect(rows[0]['times_used']).toEqual(1); + expect(rows[0]['_last_used']).toEqual(now); Parse._request( - "GET", - "push_audiences/" + audience.objectId, + 'GET', + 'push_audiences/' + audience.objectId, {}, { useMasterKey: true } ) .then(audience => { - expect(audience.name).toEqual("My Audience"); - expect(audience.query.deviceType).toEqual("ios"); + expect(audience.name).toEqual('My Audience'); + expect(audience.query.deviceType).toEqual('ios'); expect(audience.timesUsed).toEqual(1); expect(audience.lastUsed).toEqual(now.toISOString()); done(); @@ -416,11 +416,11 @@ describe("AudiencesRouter", () => { } ); - it("should be able to search on audiences", done => { + it('should be able to search on audiences', done => { Parse._request( - "POST", - "push_audiences", - { name: "neverUsed", query: JSON.stringify({ deviceType: "ios" }) }, + 'POST', + 'push_audiences', + { name: 'neverUsed', query: JSON.stringify({ deviceType: 'ios' }) }, { useMasterKey: true } ).then(() => { const query = { @@ -428,15 +428,15 @@ describe("AudiencesRouter", () => { lastUsed: { $exists: false }, }; Parse._request( - "GET", - "push_audiences?order=-createdAt&limit=1", + 'GET', + 'push_audiences?order=-createdAt&limit=1', { where: query }, { useMasterKey: true } ) .then(results => { expect(results.results.length).toEqual(1); const audience = results.results[0]; - expect(audience.name).toEqual("neverUsed"); + expect(audience.name).toEqual('neverUsed'); done(); }) .catch(error => { @@ -445,27 +445,27 @@ describe("AudiencesRouter", () => { }); }); - it("should handle _Audience invalid fields via rest", async () => { + it('should handle _Audience invalid fields via rest', async () => { await reconfigureServer({ - appId: "test", - restAPIKey: "test", - publicServerURL: "http://localhost:8378/1", + appId: 'test', + restAPIKey: 'test', + publicServerURL: 'http://localhost:8378/1', }); try { await request({ - method: "POST", - url: "http://localhost:8378/1/classes/_Audience", - body: { lorem: "ipsum", _method: "POST" }, + method: 'POST', + url: 'http://localhost:8378/1/classes/_Audience', + body: { lorem: 'ipsum', _method: 'POST' }, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "test", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'test', + 'Content-Type': 'application/json', }, }); expect(true).toBeFalsy(); } catch (e) { expect(e.data.code).toBe(107); - expect(e.data.error).toBe("Could not add field lorem"); + expect(e.data.error).toBe('Could not add field lorem'); } }); }); diff --git a/spec/Auth.spec.js b/spec/Auth.spec.js index 07f6daba00..a055cda5bc 100644 --- a/spec/Auth.spec.js +++ b/spec/Auth.spec.js @@ -1,26 +1,26 @@ -"use strict"; +'use strict'; -describe("Auth", () => { - const { Auth, getAuthForSessionToken } = require("../lib/Auth.js"); - const Config = require("../lib/Config"); - describe("getUserRoles", () => { +describe('Auth', () => { + const { Auth, getAuthForSessionToken } = require('../lib/Auth.js'); + const Config = require('../lib/Config'); + describe('getUserRoles', () => { let auth; let config; let currentRoles = null; - const currentUserId = "userId"; + const currentUserId = 'userId'; beforeEach(() => { - currentRoles = ["role:userId"]; + currentRoles = ['role:userId']; config = { cacheController: { role: { get: () => Promise.resolve(currentRoles), - set: jasmine.createSpy("set"), + set: jasmine.createSpy('set'), }, }, }; - spyOn(config.cacheController.role, "get").and.callThrough(); + spyOn(config.cacheController.role, 'get').and.callThrough(); auth = new Auth({ config: config, @@ -28,11 +28,11 @@ describe("Auth", () => { user: { id: currentUserId, }, - installationId: "installationId", + installationId: 'installationId', }); }); - it("should get user roles from the cache", done => { + it('should get user roles from the cache', done => { auth.getUserRoles().then(roles => { const firstSet = config.cacheController.role.set.calls.first(); expect(firstSet).toEqual(undefined); @@ -44,8 +44,8 @@ describe("Auth", () => { }); }); - it("should only query the roles once", done => { - const loadRolesSpy = spyOn(auth, "_loadRoles").and.callThrough(); + it('should only query the roles once', done => { + const loadRolesSpy = spyOn(auth, '_loadRoles').and.callThrough(); auth .getUserRoles() .then(roles => { @@ -66,7 +66,7 @@ describe("Auth", () => { }); }); - it("should not have any roles with no user", done => { + it('should not have any roles with no user', done => { auth.user = null; auth .getUserRoles() @@ -74,7 +74,7 @@ describe("Auth", () => { .then(() => done()); }); - it("should not have any user roles with master", done => { + it('should not have any user roles with master', done => { auth.isMaster = true; auth .getUserRoles() @@ -83,26 +83,26 @@ describe("Auth", () => { }); }); - it("can use extendSessionOnUse", async () => { + it('can use extendSessionOnUse', async () => { await reconfigureServer({ extendSessionOnUse: true, }); const user = new Parse.User(); await user.signUp({ - username: "hello", - password: "password", + username: 'hello', + password: 'password', }); const session = await new Parse.Query(Parse.Session).first(); - const updatedAt = new Date("2010"); + const updatedAt = new Date('2010'); const expiry = new Date(); expiry.setHours(expiry.getHours() + 1); await Parse.Server.database.update( - "_Session", + '_Session', { objectId: session.id }, { - expiresAt: { __type: "Date", iso: expiry.toISOString() }, + expiresAt: { __type: 'Date', iso: expiry.toISOString() }, updatedAt: updatedAt.toISOString(), } ); @@ -111,14 +111,14 @@ describe("Auth", () => { await session.fetch(); await new Promise(resolve => setTimeout(resolve, 1000)); await session.fetch(); - expect(session.get("expiresAt") > expiry).toBeTrue(); + expect(session.get('expiresAt') > expiry).toBeTrue(); }); - it("should load auth without a config", async () => { + it('should load auth without a config', async () => { const user = new Parse.User(); await user.signUp({ - username: "hello", - password: "password", + username: 'hello', + password: 'password', }); expect(user.getSessionToken()).not.toBeUndefined(); const userAuth = await getAuthForSessionToken({ @@ -128,29 +128,29 @@ describe("Auth", () => { expect(userAuth.user.id).toBe(user.id); }); - it("should load auth with a config", async () => { + it('should load auth with a config', async () => { const user = new Parse.User(); await user.signUp({ - username: "hello", - password: "password", + username: 'hello', + password: 'password', }); expect(user.getSessionToken()).not.toBeUndefined(); const userAuth = await getAuthForSessionToken({ sessionToken: user.getSessionToken(), - config: Config.get("test"), + config: Config.get('test'), }); expect(userAuth.user instanceof Parse.User).toBe(true); expect(userAuth.user.id).toBe(user.id); }); - describe("getRolesForUser", () => { + describe('getRolesForUser', () => { const rolesNumber = 100; - it("should load all roles without config", async () => { + it('should load all roles without config', async () => { const user = new Parse.User(); await user.signUp({ - username: "hello", - password: "password", + username: 'hello', + password: 'password', }); expect(user.getSessionToken()).not.toBeUndefined(); const userAuth = await getAuthForSessionToken({ @@ -159,7 +159,7 @@ describe("Auth", () => { const roles = []; for (let i = 0; i < rolesNumber; i++) { const acl = new Parse.ACL(); - const role = new Parse.Role("roleloadtest" + i, acl); + const role = new Parse.Role('roleloadtest' + i, acl); role.getUsers().add([user]); roles.push(role); } @@ -169,21 +169,21 @@ describe("Auth", () => { expect(cloudRoles.length).toBe(rolesNumber); }); - it("should load all roles with config", async () => { + it('should load all roles with config', async () => { const user = new Parse.User(); await user.signUp({ - username: "hello", - password: "password", + username: 'hello', + password: 'password', }); expect(user.getSessionToken()).not.toBeUndefined(); const userAuth = await getAuthForSessionToken({ sessionToken: user.getSessionToken(), - config: Config.get("test"), + config: Config.get('test'), }); const roles = []; for (let i = 0; i < rolesNumber; i++) { const acl = new Parse.ACL(); - const role = new Parse.Role("roleloadtest" + i, acl); + const role = new Parse.Role('roleloadtest' + i, acl); role.getUsers().add([user]); roles.push(role); } @@ -193,32 +193,32 @@ describe("Auth", () => { expect(cloudRoles.length).toBe(rolesNumber); }); - it("should load all roles for different users with config", async () => { + it('should load all roles for different users with config', async () => { const user = new Parse.User(); await user.signUp({ - username: "hello", - password: "password", + username: 'hello', + password: 'password', }); const user2 = new Parse.User(); await user2.signUp({ - username: "world", - password: "1234", + username: 'world', + password: '1234', }); expect(user.getSessionToken()).not.toBeUndefined(); const userAuth = await getAuthForSessionToken({ sessionToken: user.getSessionToken(), - config: Config.get("test"), + config: Config.get('test'), }); const user2Auth = await getAuthForSessionToken({ sessionToken: user2.getSessionToken(), - config: Config.get("test"), + config: Config.get('test'), }); const roles = []; for (let i = 0; i < rolesNumber; i += 1) { const acl = new Parse.ACL(); const acl2 = new Parse.ACL(); - const role = new Parse.Role("roleloadtest" + i, acl); - const role2 = new Parse.Role("role2loadtest" + i, acl2); + const role = new Parse.Role('roleloadtest' + i, acl); + const role2 = new Parse.Role('role2loadtest' + i, acl2); role.getUsers().add([user]); role2.getUsers().add([user2]); roles.push(role); @@ -234,9 +234,9 @@ describe("Auth", () => { }); }); -describe("extendSessionOnUse", () => { +describe('extendSessionOnUse', () => { it(`shouldUpdateSessionExpiry()`, async () => { - const { shouldUpdateSessionExpiry } = require("../lib/Auth"); + const { shouldUpdateSessionExpiry } = require('../lib/Auth'); let update = new Date(Date.now() - 86410 * 1000); const res = shouldUpdateSessionExpiry( diff --git a/spec/AuthenticationAdapters.spec.js b/spec/AuthenticationAdapters.spec.js index f5922d189d..28a29fcf85 100644 --- a/spec/AuthenticationAdapters.spec.js +++ b/spec/AuthenticationAdapters.spec.js @@ -1,16 +1,16 @@ -const request = require("../lib/request"); -const Config = require("../lib/Config"); +const request = require('../lib/request'); +const Config = require('../lib/Config'); const defaultColumns = - require("../lib/Controllers/SchemaController").defaultColumns; -const authenticationLoader = require("../lib/Adapters/Auth"); -const path = require("path"); + require('../lib/Controllers/SchemaController').defaultColumns; +const authenticationLoader = require('../lib/Adapters/Auth'); +const path = require('path'); -describe("AuthenticationProviders", function () { +describe('AuthenticationProviders', function () { const getMockMyOauthProvider = function () { return { authData: { - id: "12345", - access_token: "12345", + id: '12345', + access_token: '12345', expiration_date: new Date().toJSON(), }, shouldError: false, @@ -21,7 +21,7 @@ describe("AuthenticationProviders", function () { authenticate: function (options) { if (this.shouldError) { - options.error(this, "An error occurred"); + options.error(this, 'An error occurred'); } else if (this.shouldCancel) { options.error(this, null); } else { @@ -41,7 +41,7 @@ describe("AuthenticationProviders", function () { return true; }, getAuthType: function () { - return "myoauth"; + return 'myoauth'; }, deauthenticate: function () { this.loggedOut = true; @@ -68,15 +68,15 @@ describe("AuthenticationProviders", function () { }; const options = { - method: "POST", + method: 'POST', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "X-Parse-Installation-Id": "yolo", - "X-Parse-Session-Token": token, - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Installation-Id': 'yolo', + 'X-Parse-Session-Token': token, + 'Content-Type': 'application/json', }, - url: "http://localhost:8378/1/users", + url: 'http://localhost:8378/1/users', body: jsonBody, }; return request(options) @@ -97,7 +97,7 @@ describe("AuthenticationProviders", function () { }); }; - it("should create user with REST API", done => { + it('should create user with REST API', done => { createOAuthUser((error, response, body) => { expect(error).toBe(null); const b = body; @@ -105,26 +105,26 @@ describe("AuthenticationProviders", function () { expect(b.objectId).not.toBeNull(); expect(b.objectId).not.toBeUndefined(); const sessionToken = b.sessionToken; - const q = new Parse.Query("_Session"); - q.equalTo("sessionToken", sessionToken); + const q = new Parse.Query('_Session'); + q.equalTo('sessionToken', sessionToken); q.first({ useMasterKey: true }) .then(res => { if (!res) { - fail("should not fail fetching the session"); + fail('should not fail fetching the session'); done(); return; } - expect(res.get("installationId")).toEqual("yolo"); + expect(res.get('installationId')).toEqual('yolo'); done(); }) .catch(() => { - fail("should not fail fetching the session"); + fail('should not fail fetching the session'); done(); }); }); }); - it("should only create a single user with REST API", done => { + it('should only create a single user with REST API', done => { let objectId; createOAuthUser((error, response, body) => { expect(error).toBe(null); @@ -145,7 +145,7 @@ describe("AuthenticationProviders", function () { }); it("should fail to link if session token don't match user", done => { - Parse.User.signUp("myUser", "password") + Parse.User.signUp('myUser', 'password') .then(user => { return createOAuthUserWithSessionToken(user.getSessionToken()); }) @@ -153,86 +153,86 @@ describe("AuthenticationProviders", function () { return Parse.User.logOut(); }) .then(() => { - return Parse.User.signUp("myUser2", "password"); + return Parse.User.signUp('myUser2', 'password'); }) .then(user => { return createOAuthUserWithSessionToken(user.getSessionToken()); }) .then(fail, ({ data }) => { expect(data.code).toBe(208); - expect(data.error).toBe("this auth is already used"); + expect(data.error).toBe('this auth is already used'); done(); }) .catch(done.fail); }); - it("should support loginWith with session token and with/without mutated authData", async () => { + it('should support loginWith with session token and with/without mutated authData', async () => { const fakeAuthProvider = { validateAppId: () => Promise.resolve(), validateAuthData: () => Promise.resolve(), }; - const payload = { authData: { id: "user1", token: "fakeToken" } }; - const payload2 = { authData: { id: "user1", token: "fakeToken2" } }; + const payload = { authData: { id: 'user1', token: 'fakeToken' } }; + const payload2 = { authData: { id: 'user1', token: 'fakeToken2' } }; await reconfigureServer({ auth: { fakeAuthProvider } }); - const user = await Parse.User.logInWith("fakeAuthProvider", payload); - const user2 = await Parse.User.logInWith("fakeAuthProvider", payload, { + const user = await Parse.User.logInWith('fakeAuthProvider', payload); + const user2 = await Parse.User.logInWith('fakeAuthProvider', payload, { sessionToken: user.getSessionToken(), }); - const user3 = await Parse.User.logInWith("fakeAuthProvider", payload2, { + const user3 = await Parse.User.logInWith('fakeAuthProvider', payload2, { sessionToken: user2.getSessionToken(), }); expect(user.id).toEqual(user2.id); expect(user.id).toEqual(user3.id); }); - it("should support sync/async validateAppId", async () => { + it('should support sync/async validateAppId', async () => { const syncProvider = { validateAppId: () => true, - appIds: "test", + appIds: 'test', validateAuthData: () => Promise.resolve(), }; const asyncProvider = { - appIds: "test", + appIds: 'test', validateAppId: () => Promise.resolve(true), validateAuthData: () => Promise.resolve(), }; - const payload = { authData: { id: "user1", token: "fakeToken" } }; - const syncSpy = spyOn(syncProvider, "validateAppId"); - const asyncSpy = spyOn(asyncProvider, "validateAppId"); + const payload = { authData: { id: 'user1', token: 'fakeToken' } }; + const syncSpy = spyOn(syncProvider, 'validateAppId'); + const asyncSpy = spyOn(asyncProvider, 'validateAppId'); await reconfigureServer({ auth: { asyncProvider, syncProvider } }); - const user = await Parse.User.logInWith("asyncProvider", payload); - const user2 = await Parse.User.logInWith("syncProvider", payload); + const user = await Parse.User.logInWith('asyncProvider', payload); + const user2 = await Parse.User.logInWith('syncProvider', payload); expect(user.getSessionToken()).toBeDefined(); expect(user2.getSessionToken()).toBeDefined(); expect(syncSpy).toHaveBeenCalledTimes(1); expect(asyncSpy).toHaveBeenCalledTimes(1); }); - it("unlink and link with custom provider", async () => { + it('unlink and link with custom provider', async () => { const provider = getMockMyOauthProvider(); Parse.User._registerAuthenticationProvider(provider); - const model = await Parse.User._logInWith("myoauth"); - ok(model instanceof Parse.User, "Model should be a Parse.User"); + const model = await Parse.User._logInWith('myoauth'); + ok(model instanceof Parse.User, 'Model should be a Parse.User'); strictEqual(Parse.User.current(), model); - ok(model.extended(), "Should have used the subclass."); + ok(model.extended(), 'Should have used the subclass.'); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); strictEqual( provider.authData.expiration_date, provider.synchronizedExpiration ); - ok(model._isLinked("myoauth"), "User should be linked to myoauth"); + ok(model._isLinked('myoauth'), 'User should be linked to myoauth'); - await model._unlinkFrom("myoauth"); - ok(!model._isLinked("myoauth"), "User should not be linked to myoauth"); - ok(!provider.synchronizedUserId, "User id should be cleared"); - ok(!provider.synchronizedAuthToken, "Auth token should be cleared"); - ok(!provider.synchronizedExpiration, "Expiration should be cleared"); + await model._unlinkFrom('myoauth'); + ok(!model._isLinked('myoauth'), 'User should not be linked to myoauth'); + ok(!provider.synchronizedUserId, 'User id should be cleared'); + ok(!provider.synchronizedAuthToken, 'Auth token should be cleared'); + ok(!provider.synchronizedExpiration, 'Expiration should be cleared'); // make sure the auth data is properly deleted const config = Config.get(Parse.applicationId); const res = await config.database.adapter.find( - "_User", + '_User', { fields: Object.assign( {}, @@ -247,25 +247,25 @@ describe("AuthenticationProviders", function () { expect(res[0]._auth_data_myoauth).toBeUndefined(); expect(res[0]._auth_data_myoauth).not.toBeNull(); - await model._linkWith("myoauth"); + await model._linkWith('myoauth'); - ok(provider.synchronizedUserId, "User id should have a value"); - ok(provider.synchronizedAuthToken, "Auth token should have a value"); - ok(provider.synchronizedExpiration, "Expiration should have a value"); - ok(model._isLinked("myoauth"), "User should be linked to myoauth"); + ok(provider.synchronizedUserId, 'User id should have a value'); + ok(provider.synchronizedAuthToken, 'Auth token should have a value'); + ok(provider.synchronizedExpiration, 'Expiration should have a value'); + ok(model._isLinked('myoauth'), 'User should be linked to myoauth'); }); function validateValidator(validator) { - expect(typeof validator).toBe("function"); + expect(typeof validator).toBe('function'); } function validateAuthenticationHandler(authenticationHandler) { expect(authenticationHandler).not.toBeUndefined(); expect(typeof authenticationHandler.getValidatorForProvider).toBe( - "function" + 'function' ); expect(typeof authenticationHandler.getValidatorForProvider).toBe( - "function" + 'function' ); } @@ -274,14 +274,14 @@ describe("AuthenticationProviders", function () { if (!authAdapter) { return; } - expect(typeof authAdapter.validateAuthData).toBe("function"); - expect(typeof authAdapter.validateAppId).toBe("function"); + expect(typeof authAdapter.validateAuthData).toBe('function'); + expect(typeof authAdapter.validateAppId).toBe('function'); } - it("properly loads custom adapter", done => { + it('properly loads custom adapter', done => { const validAuthData = { - id: "hello", - token: "world", + id: 'hello', + token: 'world', }; const adapter = { validateAppId: function () { @@ -298,8 +298,8 @@ describe("AuthenticationProviders", function () { }, }; - const authDataSpy = spyOn(adapter, "validateAuthData").and.callThrough(); - const appIdSpy = spyOn(adapter, "validateAppId").and.callThrough(); + const authDataSpy = spyOn(adapter, 'validateAuthData').and.callThrough(); + const appIdSpy = spyOn(adapter, 'validateAppId').and.callThrough(); const authenticationHandler = authenticationLoader({ customAuthentication: adapter, @@ -307,7 +307,7 @@ describe("AuthenticationProviders", function () { validateAuthenticationHandler(authenticationHandler); const { validator } = authenticationHandler.getValidatorForProvider( - "customAuthentication" + 'customAuthentication' ); validateValidator(validator); @@ -325,19 +325,19 @@ describe("AuthenticationProviders", function () { ); }); - it("properly loads custom adapter module object", done => { + it('properly loads custom adapter module object', done => { const authenticationHandler = authenticationLoader({ - customAuthentication: path.resolve("./spec/support/CustomAuth.js"), + customAuthentication: path.resolve('./spec/support/CustomAuth.js'), }); validateAuthenticationHandler(authenticationHandler); const { validator } = authenticationHandler.getValidatorForProvider( - "customAuthentication" + 'customAuthentication' ); validateValidator(validator); validator( { - token: "my-token", + token: 'my-token', }, {}, {} @@ -352,23 +352,23 @@ describe("AuthenticationProviders", function () { ); }); - it("properly loads custom adapter module object (again)", done => { + it('properly loads custom adapter module object (again)', done => { const authenticationHandler = authenticationLoader({ customAuthentication: { - module: path.resolve("./spec/support/CustomAuthFunction.js"), - options: { token: "valid-token" }, + module: path.resolve('./spec/support/CustomAuthFunction.js'), + options: { token: 'valid-token' }, }, }); validateAuthenticationHandler(authenticationHandler); const { validator } = authenticationHandler.getValidatorForProvider( - "customAuthentication" + 'customAuthentication' ); validateValidator(validator); validator( { - token: "valid-token", + token: 'valid-token', }, {}, {} @@ -383,148 +383,148 @@ describe("AuthenticationProviders", function () { ); }); - it("properly loads a default adapter with options", () => { + it('properly loads a default adapter with options', () => { const options = { facebook: { - appIds: ["a", "b"], - appSecret: "secret", + appIds: ['a', 'b'], + appSecret: 'secret', }, }; const { adapter, appIds, providerOptions } = - authenticationLoader.loadAuthAdapter("facebook", options); + authenticationLoader.loadAuthAdapter('facebook', options); validateAuthenticationAdapter(adapter); - expect(appIds).toEqual(["a", "b"]); + expect(appIds).toEqual(['a', 'b']); expect(providerOptions).toEqual(options.facebook); }); - it("should handle Facebook appSecret for validating appIds", async () => { - const httpsRequest = require("../lib/Adapters/Auth/httpsRequest"); - spyOn(httpsRequest, "get").and.callFake(() => { - return Promise.resolve({ id: "a" }); + it('should handle Facebook appSecret for validating appIds', async () => { + const httpsRequest = require('../lib/Adapters/Auth/httpsRequest'); + spyOn(httpsRequest, 'get').and.callFake(() => { + return Promise.resolve({ id: 'a' }); }); const options = { facebook: { - appIds: ["a", "b"], - appSecret: "secret_sauce", + appIds: ['a', 'b'], + appSecret: 'secret_sauce', }, }; const authData = { - access_token: "badtoken", + access_token: 'badtoken', }; const { adapter, appIds, providerOptions } = - authenticationLoader.loadAuthAdapter("facebook", options); + authenticationLoader.loadAuthAdapter('facebook', options); await adapter.validateAppId(appIds, authData, providerOptions); expect( - httpsRequest.get.calls.first().args[0].includes("appsecret_proof") + httpsRequest.get.calls.first().args[0].includes('appsecret_proof') ).toBe(true); }); - it("should throw error when Facebook request appId is wrong data type", async () => { - const httpsRequest = require("../lib/Adapters/Auth/httpsRequest"); - spyOn(httpsRequest, "get").and.callFake(() => { - return Promise.resolve({ id: "a" }); + it('should throw error when Facebook request appId is wrong data type', async () => { + const httpsRequest = require('../lib/Adapters/Auth/httpsRequest'); + spyOn(httpsRequest, 'get').and.callFake(() => { + return Promise.resolve({ id: 'a' }); }); const options = { facebook: { - appIds: "abcd", - appSecret: "secret_sauce", + appIds: 'abcd', + appSecret: 'secret_sauce', }, }; const authData = { - access_token: "badtoken", + access_token: 'badtoken', }; const { adapter, appIds, providerOptions } = - authenticationLoader.loadAuthAdapter("facebook", options); + authenticationLoader.loadAuthAdapter('facebook', options); await expectAsync( adapter.validateAppId(appIds, authData, providerOptions) ).toBeRejectedWith( - new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, "appIds must be an array.") + new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'appIds must be an array.') ); }); - it("should handle Facebook appSecret for validating auth data", async () => { - const httpsRequest = require("../lib/Adapters/Auth/httpsRequest"); - spyOn(httpsRequest, "get").and.callFake(() => { + it('should handle Facebook appSecret for validating auth data', async () => { + const httpsRequest = require('../lib/Adapters/Auth/httpsRequest'); + spyOn(httpsRequest, 'get').and.callFake(() => { return Promise.resolve(); }); const options = { facebook: { - appIds: ["a", "b"], - appSecret: "secret_sauce", + appIds: ['a', 'b'], + appSecret: 'secret_sauce', }, }; const authData = { - id: "test", - access_token: "test", + id: 'test', + access_token: 'test', }; const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter( - "facebook", + 'facebook', options ); await adapter.validateAuthData(authData, providerOptions); expect( - httpsRequest.get.calls.first().args[0].includes("appsecret_proof") + httpsRequest.get.calls.first().args[0].includes('appsecret_proof') ).toBe(true); }); - it("properly loads a custom adapter with options", () => { + it('properly loads a custom adapter with options', () => { const options = { custom: { validateAppId: () => {}, validateAuthData: () => {}, - appIds: ["a", "b"], + appIds: ['a', 'b'], }, }; const { adapter, appIds, providerOptions } = - authenticationLoader.loadAuthAdapter("custom", options); + authenticationLoader.loadAuthAdapter('custom', options); validateAuthenticationAdapter(adapter); - expect(appIds).toEqual(["a", "b"]); + expect(appIds).toEqual(['a', 'b']); expect(providerOptions).toEqual(options.custom); }); - it("can disable provider", async () => { + it('can disable provider', async () => { await reconfigureServer({ auth: { myoauth: { enabled: false, - module: path.resolve(__dirname, "support/myoauth"), // relative path as it's run from src + module: path.resolve(__dirname, 'support/myoauth'), // relative path as it's run from src }, }, }); const provider = getMockMyOauthProvider(); Parse.User._registerAuthenticationProvider(provider); - await expectAsync(Parse.User._logInWith("myoauth")).toBeRejectedWith( + await expectAsync(Parse.User._logInWith('myoauth')).toBeRejectedWith( new Parse.Error( Parse.Error.UNSUPPORTED_SERVICE, - "This authentication method is unsupported." + 'This authentication method is unsupported.' ) ); }); }); -describe("google auth adapter", () => { - const google = require("../lib/Adapters/Auth/google"); - const jwt = require("jsonwebtoken"); - const authUtils = require("../lib/Adapters/Auth/utils"); +describe('google auth adapter', () => { + const google = require('../lib/Adapters/Auth/google'); + const jwt = require('jsonwebtoken'); + const authUtils = require('../lib/Adapters/Auth/utils'); - it("should throw error with missing id_token", async () => { + it('should throw error with missing id_token', async () => { try { await google.validateAuthData({}, {}); fail(); } catch (e) { - expect(e.message).toBe("id token is invalid for this user."); + expect(e.message).toBe('id token is invalid for this user.'); } }); - it("should not decode invalid id_token", async () => { + it('should not decode invalid id_token', async () => { try { await google.validateAuthData( - { id: "the_user_id", id_token: "the_token" }, + { id: 'the_user_id', id_token: 'the_token' }, {} ); fail(); } catch (e) { - expect(e.message).toBe("provided token does not decode as JWT"); + expect(e.message).toBe('provided token does not decode as JWT'); } }); @@ -542,385 +542,385 @@ describe("google auth adapter", () => { // } // }); - it("(using client id as string) should verify id_token (google.com)", async () => { + it('(using client id as string) should verify id_token (google.com)', async () => { const fakeClaim = { - iss: "https://accounts.google.com", - aud: "secret", + iss: 'https://accounts.google.com', + aud: 'secret', exp: Date.now(), - sub: "the_user_id", + sub: 'the_user_id', }; - const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; - spyOn(authUtils, "getHeaderFromToken").and.callFake(() => fakeDecodedToken); - spyOn(jwt, "verify").and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); + spyOn(jwt, 'verify').and.callFake(() => fakeClaim); const result = await google.validateAuthData( - { id: "the_user_id", id_token: "the_token" }, - { clientId: "secret" } + { id: 'the_user_id', id_token: 'the_token' }, + { clientId: 'secret' } ); expect(result).toEqual(fakeClaim); }); - it("(using client id as string) should throw error with with invalid jwt issuer (google.com)", async () => { + it('(using client id as string) should throw error with with invalid jwt issuer (google.com)', async () => { const fakeClaim = { - iss: "https://not.google.com", - sub: "the_user_id", + iss: 'https://not.google.com', + sub: 'the_user_id', }; - const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; - spyOn(authUtils, "getHeaderFromToken").and.callFake(() => fakeDecodedToken); - spyOn(jwt, "verify").and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); + spyOn(jwt, 'verify').and.callFake(() => fakeClaim); try { await google.validateAuthData( - { id: "the_user_id", id_token: "the_token" }, - { clientId: "secret" } + { id: 'the_user_id', id_token: 'the_token' }, + { clientId: 'secret' } ); fail(); } catch (e) { expect(e.message).toBe( - "id token not issued by correct provider - expected: accounts.google.com or https://accounts.google.com | from: https://not.google.com" + 'id token not issued by correct provider - expected: accounts.google.com or https://accounts.google.com | from: https://not.google.com' ); } }); - xit("(using client id as string) should throw error with invalid jwt client_id", async () => { + xit('(using client id as string) should throw error with invalid jwt client_id', async () => { const fakeClaim = { - iss: "https://accounts.google.com", - aud: "secret", + iss: 'https://accounts.google.com', + aud: 'secret', exp: Date.now(), - sub: "the_user_id", + sub: 'the_user_id', }; - const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; - spyOn(authUtils, "getHeaderFromToken").and.callFake(() => fakeDecodedToken); - spyOn(jwt, "verify").and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); + spyOn(jwt, 'verify').and.callFake(() => fakeClaim); try { await google.validateAuthData( - { id: "INSERT ID HERE", token: "INSERT APPLE TOKEN HERE" }, - { clientId: "secret" } + { id: 'INSERT ID HERE', token: 'INSERT APPLE TOKEN HERE' }, + { clientId: 'secret' } ); fail(); } catch (e) { - expect(e.message).toBe("jwt audience invalid. expected: secret"); + expect(e.message).toBe('jwt audience invalid. expected: secret'); } }); - xit("should throw error with invalid user id", async () => { + xit('should throw error with invalid user id', async () => { const fakeClaim = { - iss: "https://accounts.google.com", - aud: "secret", + iss: 'https://accounts.google.com', + aud: 'secret', exp: Date.now(), - sub: "the_user_id", + sub: 'the_user_id', }; - const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; - spyOn(authUtils, "getHeaderFromToken").and.callFake(() => fakeDecodedToken); - spyOn(jwt, "verify").and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); + spyOn(jwt, 'verify').and.callFake(() => fakeClaim); try { await google.validateAuthData( - { id: "invalid user", token: "INSERT APPLE TOKEN HERE" }, - { clientId: "INSERT CLIENT ID HERE" } + { id: 'invalid user', token: 'INSERT APPLE TOKEN HERE' }, + { clientId: 'INSERT CLIENT ID HERE' } ); fail(); } catch (e) { - expect(e.message).toBe("auth data is invalid for this user."); + expect(e.message).toBe('auth data is invalid for this user.'); } }); }); -describe("keycloak auth adapter", () => { - const keycloak = require("../lib/Adapters/Auth/keycloak"); - const httpsRequest = require("../lib/Adapters/Auth/httpsRequest"); +describe('keycloak auth adapter', () => { + const keycloak = require('../lib/Adapters/Auth/keycloak'); + const httpsRequest = require('../lib/Adapters/Auth/httpsRequest'); - it("validateAuthData should fail without access token", async () => { + it('validateAuthData should fail without access token', async () => { const authData = { - id: "fakeid", + id: 'fakeid', }; try { await keycloak.validateAuthData(authData); fail(); } catch (e) { - expect(e.message).toBe("Missing access token and/or User id"); + expect(e.message).toBe('Missing access token and/or User id'); } }); - it("validateAuthData should fail without user id", async () => { + it('validateAuthData should fail without user id', async () => { const authData = { - access_token: "sometoken", + access_token: 'sometoken', }; try { await keycloak.validateAuthData(authData); fail(); } catch (e) { - expect(e.message).toBe("Missing access token and/or User id"); + expect(e.message).toBe('Missing access token and/or User id'); } }); - it("validateAuthData should fail without config", async () => { + it('validateAuthData should fail without config', async () => { const options = { keycloak: { config: null, }, }; const authData = { - id: "fakeid", - access_token: "sometoken", + id: 'fakeid', + access_token: 'sometoken', }; const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter( - "keycloak", + 'keycloak', options ); try { await adapter.validateAuthData(authData, providerOptions); fail(); } catch (e) { - expect(e.message).toBe("Missing keycloak configuration"); + expect(e.message).toBe('Missing keycloak configuration'); } }); - it("validateAuthData should fail connect error", async () => { - spyOn(httpsRequest, "get").and.callFake(() => { + it('validateAuthData should fail connect error', async () => { + spyOn(httpsRequest, 'get').and.callFake(() => { return Promise.reject({ - text: JSON.stringify({ error: "hosting_error" }), + text: JSON.stringify({ error: 'hosting_error' }), }); }); const options = { keycloak: { config: { - "auth-server-url": "http://example.com", - realm: "new", + 'auth-server-url': 'http://example.com', + realm: 'new', }, }, }; const authData = { - id: "fakeid", - access_token: "sometoken", + id: 'fakeid', + access_token: 'sometoken', }; const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter( - "keycloak", + 'keycloak', options ); try { await adapter.validateAuthData(authData, providerOptions); fail(); } catch (e) { - expect(e.message).toBe("Could not connect to the authentication server"); + expect(e.message).toBe('Could not connect to the authentication server'); } }); - it("validateAuthData should fail with error description", async () => { - spyOn(httpsRequest, "get").and.callFake(() => { + it('validateAuthData should fail with error description', async () => { + spyOn(httpsRequest, 'get').and.callFake(() => { return Promise.reject({ - text: JSON.stringify({ error_description: "custom error message" }), + text: JSON.stringify({ error_description: 'custom error message' }), }); }); const options = { keycloak: { config: { - "auth-server-url": "http://example.com", - realm: "new", + 'auth-server-url': 'http://example.com', + realm: 'new', }, }, }; const authData = { - id: "fakeid", - access_token: "sometoken", + id: 'fakeid', + access_token: 'sometoken', }; const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter( - "keycloak", + 'keycloak', options ); try { await adapter.validateAuthData(authData, providerOptions); fail(); } catch (e) { - expect(e.message).toBe("custom error message"); + expect(e.message).toBe('custom error message'); } }); - it("validateAuthData should fail with invalid auth", async () => { - spyOn(httpsRequest, "get").and.callFake(() => { + it('validateAuthData should fail with invalid auth', async () => { + spyOn(httpsRequest, 'get').and.callFake(() => { return Promise.resolve({}); }); const options = { keycloak: { config: { - "auth-server-url": "http://example.com", - realm: "new", + 'auth-server-url': 'http://example.com', + realm: 'new', }, }, }; const authData = { - id: "fakeid", - access_token: "sometoken", + id: 'fakeid', + access_token: 'sometoken', }; const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter( - "keycloak", + 'keycloak', options ); try { await adapter.validateAuthData(authData, providerOptions); fail(); } catch (e) { - expect(e.message).toBe("Invalid authentication"); + expect(e.message).toBe('Invalid authentication'); } }); - it("validateAuthData should fail with invalid groups", async () => { - spyOn(httpsRequest, "get").and.callFake(() => { + it('validateAuthData should fail with invalid groups', async () => { + spyOn(httpsRequest, 'get').and.callFake(() => { return Promise.resolve({ data: { - sub: "fakeid", - roles: ["role1"], - groups: ["unknown"], + sub: 'fakeid', + roles: ['role1'], + groups: ['unknown'], }, }); }); const options = { keycloak: { config: { - "auth-server-url": "http://example.com", - realm: "new", + 'auth-server-url': 'http://example.com', + realm: 'new', }, }, }; const authData = { - id: "fakeid", - access_token: "sometoken", - roles: ["role1"], - groups: ["group1"], + id: 'fakeid', + access_token: 'sometoken', + roles: ['role1'], + groups: ['group1'], }; const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter( - "keycloak", + 'keycloak', options ); try { await adapter.validateAuthData(authData, providerOptions); fail(); } catch (e) { - expect(e.message).toBe("Invalid authentication"); + expect(e.message).toBe('Invalid authentication'); } }); - it("validateAuthData should fail with invalid roles", async () => { - spyOn(httpsRequest, "get").and.callFake(() => { + it('validateAuthData should fail with invalid roles', async () => { + spyOn(httpsRequest, 'get').and.callFake(() => { return Promise.resolve({ data: { - sub: "fakeid", - roles: "unknown", - groups: ["group1"], + sub: 'fakeid', + roles: 'unknown', + groups: ['group1'], }, }); }); const options = { keycloak: { config: { - "auth-server-url": "http://example.com", - realm: "new", + 'auth-server-url': 'http://example.com', + realm: 'new', }, }, }; const authData = { - id: "fakeid", - access_token: "sometoken", - roles: ["role1"], - groups: ["group1"], + id: 'fakeid', + access_token: 'sometoken', + roles: ['role1'], + groups: ['group1'], }; const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter( - "keycloak", + 'keycloak', options ); try { await adapter.validateAuthData(authData, providerOptions); fail(); } catch (e) { - expect(e.message).toBe("Invalid authentication"); + expect(e.message).toBe('Invalid authentication'); } }); - it("validateAuthData should handle authentication", async () => { - spyOn(httpsRequest, "get").and.callFake(() => { + it('validateAuthData should handle authentication', async () => { + spyOn(httpsRequest, 'get').and.callFake(() => { return Promise.resolve({ data: { - sub: "fakeid", - roles: ["role1"], - groups: ["group1"], + sub: 'fakeid', + roles: ['role1'], + groups: ['group1'], }, }); }); const options = { keycloak: { config: { - "auth-server-url": "http://example.com", - realm: "new", + 'auth-server-url': 'http://example.com', + realm: 'new', }, }, }; const authData = { - id: "fakeid", - access_token: "sometoken", - roles: ["role1"], - groups: ["group1"], + id: 'fakeid', + access_token: 'sometoken', + roles: ['role1'], + groups: ['group1'], }; const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter( - "keycloak", + 'keycloak', options ); await adapter.validateAuthData(authData, providerOptions); expect(httpsRequest.get).toHaveBeenCalledWith({ - host: "http://example.com", - path: "/realms/new/protocol/openid-connect/userinfo", + host: 'http://example.com', + path: '/realms/new/protocol/openid-connect/userinfo', headers: { - Authorization: "Bearer sometoken", + Authorization: 'Bearer sometoken', }, }); }); }); -describe("apple signin auth adapter", () => { - const apple = require("../lib/Adapters/Auth/apple"); - const jwt = require("jsonwebtoken"); - const authUtils = require("../lib/Adapters/Auth/utils"); +describe('apple signin auth adapter', () => { + const apple = require('../lib/Adapters/Auth/apple'); + const jwt = require('jsonwebtoken'); + const authUtils = require('../lib/Adapters/Auth/utils'); - it("(using client id as string) should throw error with missing id_token", async () => { + it('(using client id as string) should throw error with missing id_token', async () => { try { - await apple.validateAuthData({}, { clientId: "secret" }); + await apple.validateAuthData({}, { clientId: 'secret' }); fail(); } catch (e) { - expect(e.message).toBe("id token is invalid for this user."); + expect(e.message).toBe('id token is invalid for this user.'); } }); - it("(using client id as array) should throw error with missing id_token", async () => { + it('(using client id as array) should throw error with missing id_token', async () => { try { - await apple.validateAuthData({}, { clientId: ["secret"] }); + await apple.validateAuthData({}, { clientId: ['secret'] }); fail(); } catch (e) { - expect(e.message).toBe("id token is invalid for this user."); + expect(e.message).toBe('id token is invalid for this user.'); } }); - it("should not decode invalid id_token", async () => { + it('should not decode invalid id_token', async () => { try { await apple.validateAuthData( - { id: "the_user_id", token: "the_token" }, - { clientId: "secret" } + { id: 'the_user_id', token: 'the_token' }, + { clientId: 'secret' } ); fail(); } catch (e) { - expect(e.message).toBe("provided token does not decode as JWT"); + expect(e.message).toBe('provided token does not decode as JWT'); } }); - it("should throw error if public key used to encode token is not available", async () => { - const fakeDecodedToken = { header: { kid: "789", alg: "RS256" } }; + it('should throw error if public key used to encode token is not available', async () => { + const fakeDecodedToken = { header: { kid: '789', alg: 'RS256' } }; try { - spyOn(authUtils, "getHeaderFromToken").and.callFake( + spyOn(authUtils, 'getHeaderFromToken').and.callFake( () => fakeDecodedToken.header ); await apple.validateAuthData( - { id: "the_user_id", token: "the_token" }, - { clientId: "secret" } + { id: 'the_user_id', token: 'the_token' }, + { clientId: 'secret' } ); fail(); } catch (e) { @@ -930,24 +930,24 @@ describe("apple signin auth adapter", () => { } }); - it("should use algorithm from key header to verify id_token (apple.com)", async () => { + it('should use algorithm from key header to verify id_token (apple.com)', async () => { const fakeClaim = { - iss: "https://appleid.apple.com", - aud: "secret", + iss: 'https://appleid.apple.com', + aud: 'secret', exp: Date.now(), - sub: "the_user_id", + sub: 'the_user_id', }; - const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; - const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; - spyOn(authUtils, "getHeaderFromToken").and.callFake( + const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; + const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; + spyOn(authUtils, 'getHeaderFromToken').and.callFake( () => fakeDecodedToken.header ); - spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); - spyOn(jwt, "verify").and.callFake(() => fakeClaim); + spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); + spyOn(jwt, 'verify').and.callFake(() => fakeClaim); const result = await apple.validateAuthData( - { id: "the_user_id", token: "the_token" }, - { clientId: "secret" } + { id: 'the_user_id', token: 'the_token' }, + { clientId: 'secret' } ); expect(result).toEqual(fakeClaim); expect(jwt.verify.calls.first().args[2].algorithms).toEqual( @@ -955,245 +955,245 @@ describe("apple signin auth adapter", () => { ); }); - it("should not verify invalid id_token", async () => { - const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; - const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; - spyOn(authUtils, "getHeaderFromToken").and.callFake(() => fakeDecodedToken); - spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); + it('should not verify invalid id_token', async () => { + const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; + const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); + spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); try { await apple.validateAuthData( - { id: "the_user_id", token: "the_token" }, - { clientId: "secret" } + { id: 'the_user_id', token: 'the_token' }, + { clientId: 'secret' } ); fail(); } catch (e) { - expect(e.message).toBe("jwt malformed"); + expect(e.message).toBe('jwt malformed'); } }); - it("(using client id as array) should not verify invalid id_token", async () => { + it('(using client id as array) should not verify invalid id_token', async () => { try { await apple.validateAuthData( - { id: "the_user_id", token: "the_token" }, - { clientId: ["secret"] } + { id: 'the_user_id', token: 'the_token' }, + { clientId: ['secret'] } ); fail(); } catch (e) { - expect(e.message).toBe("provided token does not decode as JWT"); + expect(e.message).toBe('provided token does not decode as JWT'); } }); - it("(using client id as string) should verify id_token (apple.com)", async () => { + it('(using client id as string) should verify id_token (apple.com)', async () => { const fakeClaim = { - iss: "https://appleid.apple.com", - aud: "secret", + iss: 'https://appleid.apple.com', + aud: 'secret', exp: Date.now(), - sub: "the_user_id", + sub: 'the_user_id', }; - const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; - const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; - spyOn(authUtils, "getHeaderFromToken").and.callFake(() => fakeDecodedToken); - spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); - spyOn(jwt, "verify").and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; + const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); + spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); + spyOn(jwt, 'verify').and.callFake(() => fakeClaim); const result = await apple.validateAuthData( - { id: "the_user_id", token: "the_token" }, - { clientId: "secret" } + { id: 'the_user_id', token: 'the_token' }, + { clientId: 'secret' } ); expect(result).toEqual(fakeClaim); }); - it("(using client id as array) should verify id_token (apple.com)", async () => { + it('(using client id as array) should verify id_token (apple.com)', async () => { const fakeClaim = { - iss: "https://appleid.apple.com", - aud: "secret", + iss: 'https://appleid.apple.com', + aud: 'secret', exp: Date.now(), - sub: "the_user_id", + sub: 'the_user_id', }; - const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; - const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; - spyOn(authUtils, "getHeaderFromToken").and.callFake(() => fakeDecodedToken); - spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); - spyOn(jwt, "verify").and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; + const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); + spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); + spyOn(jwt, 'verify').and.callFake(() => fakeClaim); const result = await apple.validateAuthData( - { id: "the_user_id", token: "the_token" }, - { clientId: ["secret"] } + { id: 'the_user_id', token: 'the_token' }, + { clientId: ['secret'] } ); expect(result).toEqual(fakeClaim); }); - it("(using client id as array with multiple items) should verify id_token (apple.com)", async () => { + it('(using client id as array with multiple items) should verify id_token (apple.com)', async () => { const fakeClaim = { - iss: "https://appleid.apple.com", - aud: "secret", + iss: 'https://appleid.apple.com', + aud: 'secret', exp: Date.now(), - sub: "the_user_id", + sub: 'the_user_id', }; - const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; - const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; - spyOn(authUtils, "getHeaderFromToken").and.callFake(() => fakeDecodedToken); - spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); - spyOn(jwt, "verify").and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; + const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); + spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); + spyOn(jwt, 'verify').and.callFake(() => fakeClaim); const result = await apple.validateAuthData( - { id: "the_user_id", token: "the_token" }, - { clientId: ["secret", "secret 123"] } + { id: 'the_user_id', token: 'the_token' }, + { clientId: ['secret', 'secret 123'] } ); expect(result).toEqual(fakeClaim); }); - it("(using client id as string) should throw error with with invalid jwt issuer (apple.com)", async () => { + it('(using client id as string) should throw error with with invalid jwt issuer (apple.com)', async () => { const fakeClaim = { - iss: "https://not.apple.com", - sub: "the_user_id", + iss: 'https://not.apple.com', + sub: 'the_user_id', }; - const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; - const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; - spyOn(authUtils, "getHeaderFromToken").and.callFake(() => fakeDecodedToken); - spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); - spyOn(jwt, "verify").and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; + const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); + spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); + spyOn(jwt, 'verify').and.callFake(() => fakeClaim); try { await apple.validateAuthData( - { id: "the_user_id", token: "the_token" }, - { clientId: "secret" } + { id: 'the_user_id', token: 'the_token' }, + { clientId: 'secret' } ); fail(); } catch (e) { expect(e.message).toBe( - "id token not issued by correct OpenID provider - expected: https://appleid.apple.com | from: https://not.apple.com" + 'id token not issued by correct OpenID provider - expected: https://appleid.apple.com | from: https://not.apple.com' ); } }); // TODO: figure out a way to generate our own apple signed tokens, perhaps with a parse apple account // and a private key - xit("(using client id as array) should throw error with with invalid jwt issuer", async () => { + xit('(using client id as array) should throw error with with invalid jwt issuer', async () => { const fakeClaim = { - iss: "https://not.apple.com", - sub: "the_user_id", + iss: 'https://not.apple.com', + sub: 'the_user_id', }; - const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; - const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; - spyOn(authUtils, "getHeaderFromToken").and.callFake(() => fakeDecodedToken); - spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); - spyOn(jwt, "verify").and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; + const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); + spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); + spyOn(jwt, 'verify').and.callFake(() => fakeClaim); try { await apple.validateAuthData( { - id: "INSERT ID HERE", - token: "INSERT APPLE TOKEN HERE WITH INVALID JWT ISSUER", + id: 'INSERT ID HERE', + token: 'INSERT APPLE TOKEN HERE WITH INVALID JWT ISSUER', }, - { clientId: ["INSERT CLIENT ID HERE"] } + { clientId: ['INSERT CLIENT ID HERE'] } ); fail(); } catch (e) { expect(e.message).toBe( - "id token not issued by correct OpenID provider - expected: https://appleid.apple.com | from: https://not.apple.com" + 'id token not issued by correct OpenID provider - expected: https://appleid.apple.com | from: https://not.apple.com' ); } }); - it("(using client id as string) should throw error with with invalid jwt issuer with token (apple.com)", async () => { + it('(using client id as string) should throw error with with invalid jwt issuer with token (apple.com)', async () => { const fakeClaim = { - iss: "https://not.apple.com", - sub: "the_user_id", + iss: 'https://not.apple.com', + sub: 'the_user_id', }; - const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; - const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; - spyOn(authUtils, "getHeaderFromToken").and.callFake(() => fakeDecodedToken); - spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); - spyOn(jwt, "verify").and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; + const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); + spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); + spyOn(jwt, 'verify').and.callFake(() => fakeClaim); try { await apple.validateAuthData( { - id: "INSERT ID HERE", - token: "INSERT APPLE TOKEN HERE WITH INVALID JWT ISSUER", + id: 'INSERT ID HERE', + token: 'INSERT APPLE TOKEN HERE WITH INVALID JWT ISSUER', }, - { clientId: "INSERT CLIENT ID HERE" } + { clientId: 'INSERT CLIENT ID HERE' } ); fail(); } catch (e) { expect(e.message).toBe( - "id token not issued by correct OpenID provider - expected: https://appleid.apple.com | from: https://not.apple.com" + 'id token not issued by correct OpenID provider - expected: https://appleid.apple.com | from: https://not.apple.com' ); } }); // TODO: figure out a way to generate our own apple signed tokens, perhaps with a parse apple account // and a private key - xit("(using client id as string) should throw error with invalid jwt clientId", async () => { + xit('(using client id as string) should throw error with invalid jwt clientId', async () => { try { await apple.validateAuthData( - { id: "INSERT ID HERE", token: "INSERT APPLE TOKEN HERE" }, - { clientId: "secret" } + { id: 'INSERT ID HERE', token: 'INSERT APPLE TOKEN HERE' }, + { clientId: 'secret' } ); fail(); } catch (e) { - expect(e.message).toBe("jwt audience invalid. expected: secret"); + expect(e.message).toBe('jwt audience invalid. expected: secret'); } }); // TODO: figure out a way to generate our own apple signed tokens, perhaps with a parse apple account // and a private key - xit("(using client id as array) should throw error with invalid jwt clientId", async () => { + xit('(using client id as array) should throw error with invalid jwt clientId', async () => { try { await apple.validateAuthData( - { id: "INSERT ID HERE", token: "INSERT APPLE TOKEN HERE" }, - { clientId: ["secret"] } + { id: 'INSERT ID HERE', token: 'INSERT APPLE TOKEN HERE' }, + { clientId: ['secret'] } ); fail(); } catch (e) { - expect(e.message).toBe("jwt audience invalid. expected: secret"); + expect(e.message).toBe('jwt audience invalid. expected: secret'); } }); // TODO: figure out a way to generate our own apple signed tokens, perhaps with a parse apple account // and a private key - xit("should throw error with invalid user id", async () => { + xit('should throw error with invalid user id', async () => { try { await apple.validateAuthData( - { id: "invalid user", token: "INSERT APPLE TOKEN HERE" }, - { clientId: "INSERT CLIENT ID HERE" } + { id: 'invalid user', token: 'INSERT APPLE TOKEN HERE' }, + { clientId: 'INSERT CLIENT ID HERE' } ); fail(); } catch (e) { - expect(e.message).toBe("auth data is invalid for this user."); + expect(e.message).toBe('auth data is invalid for this user.'); } }); - it("should throw error with with invalid user id (apple.com)", async () => { + it('should throw error with with invalid user id (apple.com)', async () => { const fakeClaim = { - iss: "https://appleid.apple.com", - aud: "invalid_client_id", - sub: "a_different_user_id", + iss: 'https://appleid.apple.com', + aud: 'invalid_client_id', + sub: 'a_different_user_id', }; - const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; - const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; - spyOn(authUtils, "getHeaderFromToken").and.callFake(() => fakeDecodedToken); - spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); - spyOn(jwt, "verify").and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; + const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); + spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); + spyOn(jwt, 'verify').and.callFake(() => fakeClaim); try { await apple.validateAuthData( - { id: "the_user_id", token: "the_token" }, - { clientId: "secret" } + { id: 'the_user_id', token: 'the_token' }, + { clientId: 'secret' } ); fail(); } catch (e) { - expect(e.message).toBe("auth data is invalid for this user."); + expect(e.message).toBe('auth data is invalid for this user.'); } }); }); -describe("phant auth adapter", () => { - const httpsRequest = require("../lib/Adapters/Auth/httpsRequest"); +describe('phant auth adapter', () => { + const httpsRequest = require('../lib/Adapters/Auth/httpsRequest'); - it("validateAuthData should throw for invalid auth", async () => { + it('validateAuthData should throw for invalid auth', async () => { await reconfigureServer({ auth: { phantauth: { @@ -1202,72 +1202,72 @@ describe("phant auth adapter", () => { }, }); const authData = { - id: "fakeid", - access_token: "sometoken", + id: 'fakeid', + access_token: 'sometoken', }; - const { adapter } = authenticationLoader.loadAuthAdapter("phantauth", {}); + const { adapter } = authenticationLoader.loadAuthAdapter('phantauth', {}); - spyOn(httpsRequest, "get").and.callFake(() => - Promise.resolve({ sub: "invalidID" }) + spyOn(httpsRequest, 'get').and.callFake(() => + Promise.resolve({ sub: 'invalidID' }) ); try { await adapter.validateAuthData(authData); fail(); } catch (e) { - expect(e.message).toBe("PhantAuth auth is invalid for this user."); + expect(e.message).toBe('PhantAuth auth is invalid for this user.'); } }); }); -describe("facebook limited auth adapter", () => { - const facebook = require("../lib/Adapters/Auth/facebook"); - const jwt = require("jsonwebtoken"); - const authUtils = require("../lib/Adapters/Auth/utils"); +describe('facebook limited auth adapter', () => { + const facebook = require('../lib/Adapters/Auth/facebook'); + const jwt = require('jsonwebtoken'); + const authUtils = require('../lib/Adapters/Auth/utils'); // TODO: figure out a way to run this test alongside facebook classic tests - xit("(using client id as string) should throw error with missing id_token", async () => { + xit('(using client id as string) should throw error with missing id_token', async () => { try { - await facebook.validateAuthData({}, { clientId: "secret" }); + await facebook.validateAuthData({}, { clientId: 'secret' }); fail(); } catch (e) { - expect(e.message).toBe("Facebook auth is not configured."); + expect(e.message).toBe('Facebook auth is not configured.'); } }); // TODO: figure out a way to run this test alongside facebook classic tests - xit("(using client id as array) should throw error with missing id_token", async () => { + xit('(using client id as array) should throw error with missing id_token', async () => { try { - await facebook.validateAuthData({}, { clientId: ["secret"] }); + await facebook.validateAuthData({}, { clientId: ['secret'] }); fail(); } catch (e) { - expect(e.message).toBe("Facebook auth is not configured."); + expect(e.message).toBe('Facebook auth is not configured.'); } }); - it("should not decode invalid id_token", async () => { + it('should not decode invalid id_token', async () => { try { await facebook.validateAuthData( - { id: "the_user_id", token: "the_token" }, - { clientId: "secret" } + { id: 'the_user_id', token: 'the_token' }, + { clientId: 'secret' } ); fail(); } catch (e) { - expect(e.message).toBe("provided token does not decode as JWT"); + expect(e.message).toBe('provided token does not decode as JWT'); } }); - it("should throw error if public key used to encode token is not available", async () => { + it('should throw error if public key used to encode token is not available', async () => { const fakeDecodedToken = { - header: { kid: "789", alg: "RS256" }, + header: { kid: '789', alg: 'RS256' }, }; try { - spyOn(authUtils, "getHeaderFromToken").and.callFake( + spyOn(authUtils, 'getHeaderFromToken').and.callFake( () => fakeDecodedToken.header ); await facebook.validateAuthData( - { id: "the_user_id", token: "the_token" }, - { clientId: "secret" } + { id: 'the_user_id', token: 'the_token' }, + { clientId: 'secret' } ); fail(); } catch (e) { @@ -1277,26 +1277,26 @@ describe("facebook limited auth adapter", () => { } }); - it_id("7bfa55ab-8fd7-4526-992e-6de3df16bf9c")(it)( - "should use algorithm from key header to verify id_token (facebook.com)", + it_id('7bfa55ab-8fd7-4526-992e-6de3df16bf9c')(it)( + 'should use algorithm from key header to verify id_token (facebook.com)', async () => { const fakeClaim = { - iss: "https://www.facebook.com", - aud: "secret", + iss: 'https://www.facebook.com', + aud: 'secret', exp: Date.now(), - sub: "the_user_id", + sub: 'the_user_id', }; - const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; - const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; - spyOn(authUtils, "getHeaderFromToken").and.callFake( + const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; + const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; + spyOn(authUtils, 'getHeaderFromToken').and.callFake( () => fakeDecodedToken.header ); - spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); - spyOn(jwt, "verify").and.callFake(() => fakeClaim); + spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); + spyOn(jwt, 'verify').and.callFake(() => fakeClaim); const result = await facebook.validateAuthData( - { id: "the_user_id", token: "the_token" }, - { clientId: "secret" } + { id: 'the_user_id', token: 'the_token' }, + { clientId: 'secret' } ); expect(result).toEqual(fakeClaim); expect(jwt.verify.calls.first().args[2].algorithms).toEqual( @@ -1305,134 +1305,134 @@ describe("facebook limited auth adapter", () => { } ); - it("should not verify invalid id_token", async () => { - const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; - const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; - spyOn(authUtils, "getHeaderFromToken").and.callFake(() => fakeDecodedToken); - spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); + it('should not verify invalid id_token', async () => { + const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; + const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); + spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); try { await facebook.validateAuthData( - { id: "the_user_id", token: "the_token" }, - { clientId: "secret" } + { id: 'the_user_id', token: 'the_token' }, + { clientId: 'secret' } ); fail(); } catch (e) { - expect(e.message).toBe("jwt malformed"); + expect(e.message).toBe('jwt malformed'); } }); - it("(using client id as array) should not verify invalid id_token", async () => { + it('(using client id as array) should not verify invalid id_token', async () => { try { await facebook.validateAuthData( - { id: "the_user_id", token: "the_token" }, - { clientId: ["secret"] } + { id: 'the_user_id', token: 'the_token' }, + { clientId: ['secret'] } ); fail(); } catch (e) { - expect(e.message).toBe("provided token does not decode as JWT"); + expect(e.message).toBe('provided token does not decode as JWT'); } }); - it_id("4bcb1a1a-11f8-4e12-a3f6-73f7e25e355a")(it)( - "using client id as string) should verify id_token (facebook.com)", + it_id('4bcb1a1a-11f8-4e12-a3f6-73f7e25e355a')(it)( + 'using client id as string) should verify id_token (facebook.com)', async () => { const fakeClaim = { - iss: "https://www.facebook.com", - aud: "secret", + iss: 'https://www.facebook.com', + aud: 'secret', exp: Date.now(), - sub: "the_user_id", + sub: 'the_user_id', }; - const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; - const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; - spyOn(authUtils, "getHeaderFromToken").and.callFake( + const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; + const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; + spyOn(authUtils, 'getHeaderFromToken').and.callFake( () => fakeDecodedToken ); - spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); - spyOn(jwt, "verify").and.callFake(() => fakeClaim); + spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); + spyOn(jwt, 'verify').and.callFake(() => fakeClaim); const result = await facebook.validateAuthData( - { id: "the_user_id", token: "the_token" }, - { clientId: "secret" } + { id: 'the_user_id', token: 'the_token' }, + { clientId: 'secret' } ); expect(result).toEqual(fakeClaim); } ); - it_id("c521a272-2ac2-4d8b-b5ed-ea250336d8b1")(it)( - "(using client id as array) should verify id_token (facebook.com)", + it_id('c521a272-2ac2-4d8b-b5ed-ea250336d8b1')(it)( + '(using client id as array) should verify id_token (facebook.com)', async () => { const fakeClaim = { - iss: "https://www.facebook.com", - aud: "secret", + iss: 'https://www.facebook.com', + aud: 'secret', exp: Date.now(), - sub: "the_user_id", + sub: 'the_user_id', }; - const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; - const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; - spyOn(authUtils, "getHeaderFromToken").and.callFake( + const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; + const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; + spyOn(authUtils, 'getHeaderFromToken').and.callFake( () => fakeDecodedToken ); - spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); - spyOn(jwt, "verify").and.callFake(() => fakeClaim); + spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); + spyOn(jwt, 'verify').and.callFake(() => fakeClaim); const result = await facebook.validateAuthData( - { id: "the_user_id", token: "the_token" }, - { clientId: ["secret"] } + { id: 'the_user_id', token: 'the_token' }, + { clientId: ['secret'] } ); expect(result).toEqual(fakeClaim); } ); - it_id("e3f16404-18e9-4a87-a555-4710cfbdac67")(it)( - "(using client id as array with multiple items) should verify id_token (facebook.com)", + it_id('e3f16404-18e9-4a87-a555-4710cfbdac67')(it)( + '(using client id as array with multiple items) should verify id_token (facebook.com)', async () => { const fakeClaim = { - iss: "https://www.facebook.com", - aud: "secret", + iss: 'https://www.facebook.com', + aud: 'secret', exp: Date.now(), - sub: "the_user_id", + sub: 'the_user_id', }; - const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; - const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; - spyOn(authUtils, "getHeaderFromToken").and.callFake( + const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; + const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; + spyOn(authUtils, 'getHeaderFromToken').and.callFake( () => fakeDecodedToken ); - spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); - spyOn(jwt, "verify").and.callFake(() => fakeClaim); + spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); + spyOn(jwt, 'verify').and.callFake(() => fakeClaim); const result = await facebook.validateAuthData( - { id: "the_user_id", token: "the_token" }, - { clientId: ["secret", "secret 123"] } + { id: 'the_user_id', token: 'the_token' }, + { clientId: ['secret', 'secret 123'] } ); expect(result).toEqual(fakeClaim); } ); - it_id("549c33a1-3a6b-4732-8cf6-8f010ad4569c")(it)( - "(using client id as string) should throw error with with invalid jwt issuer (facebook.com)", + it_id('549c33a1-3a6b-4732-8cf6-8f010ad4569c')(it)( + '(using client id as string) should throw error with with invalid jwt issuer (facebook.com)', async () => { const fakeClaim = { - iss: "https://not.facebook.com", - sub: "the_user_id", + iss: 'https://not.facebook.com', + sub: 'the_user_id', }; - const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; - const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; - spyOn(authUtils, "getHeaderFromToken").and.callFake( + const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; + const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; + spyOn(authUtils, 'getHeaderFromToken').and.callFake( () => fakeDecodedToken ); - spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); - spyOn(jwt, "verify").and.callFake(() => fakeClaim); + spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); + spyOn(jwt, 'verify').and.callFake(() => fakeClaim); try { await facebook.validateAuthData( - { id: "the_user_id", token: "the_token" }, - { clientId: "secret" } + { id: 'the_user_id', token: 'the_token' }, + { clientId: 'secret' } ); fail(); } catch (e) { expect(e.message).toBe( - "id token not issued by correct OpenID provider - expected: https://www.facebook.com | from: https://not.facebook.com" + 'id token not issued by correct OpenID provider - expected: https://www.facebook.com | from: https://not.facebook.com' ); } } @@ -1440,153 +1440,153 @@ describe("facebook limited auth adapter", () => { // TODO: figure out a way to generate our own facebook signed tokens, perhaps with a parse facebook account // and a private key - xit("(using client id as array) should throw error with with invalid jwt issuer", async () => { + xit('(using client id as array) should throw error with with invalid jwt issuer', async () => { const fakeClaim = { - iss: "https://not.facebook.com", - sub: "the_user_id", + iss: 'https://not.facebook.com', + sub: 'the_user_id', }; - const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; - const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; - spyOn(authUtils, "getHeaderFromToken").and.callFake(() => fakeDecodedToken); - spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); - spyOn(jwt, "verify").and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; + const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); + spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); + spyOn(jwt, 'verify').and.callFake(() => fakeClaim); try { await facebook.validateAuthData( { - id: "INSERT ID HERE", - token: "INSERT FACEBOOK TOKEN HERE WITH INVALID JWT ISSUER", + id: 'INSERT ID HERE', + token: 'INSERT FACEBOOK TOKEN HERE WITH INVALID JWT ISSUER', }, - { clientId: ["INSERT CLIENT ID HERE"] } + { clientId: ['INSERT CLIENT ID HERE'] } ); fail(); } catch (e) { expect(e.message).toBe( - "id token not issued by correct OpenID provider - expected: https://www.facebook.com | from: https://not.facebook.com" + 'id token not issued by correct OpenID provider - expected: https://www.facebook.com | from: https://not.facebook.com' ); } }); - it("(using client id as string) with token", async () => { + it('(using client id as string) with token', async () => { const fakeClaim = { - iss: "https://not.facebook.com", - sub: "the_user_id", + iss: 'https://not.facebook.com', + sub: 'the_user_id', }; - const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; - const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; - spyOn(authUtils, "getHeaderFromToken").and.callFake(() => fakeDecodedToken); - spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); - spyOn(jwt, "verify").and.callFake(() => fakeClaim); + const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; + const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); + spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); + spyOn(jwt, 'verify').and.callFake(() => fakeClaim); try { await facebook.validateAuthData( { - id: "INSERT ID HERE", - token: "INSERT FACEBOOK TOKEN HERE WITH INVALID JWT ISSUER", + id: 'INSERT ID HERE', + token: 'INSERT FACEBOOK TOKEN HERE WITH INVALID JWT ISSUER', }, - { clientId: "INSERT CLIENT ID HERE" } + { clientId: 'INSERT CLIENT ID HERE' } ); fail(); } catch (e) { expect(e.message).toBe( - "id token not issued by correct OpenID provider - expected: https://www.facebook.com | from: https://not.facebook.com" + 'id token not issued by correct OpenID provider - expected: https://www.facebook.com | from: https://not.facebook.com' ); } }); // TODO: figure out a way to generate our own facebook signed tokens, perhaps with a parse facebook account // and a private key - xit("(using client id as string) should throw error with invalid jwt clientId", async () => { + xit('(using client id as string) should throw error with invalid jwt clientId', async () => { try { await facebook.validateAuthData( { - id: "INSERT ID HERE", - token: "INSERT FACEBOOK TOKEN HERE", + id: 'INSERT ID HERE', + token: 'INSERT FACEBOOK TOKEN HERE', }, - { clientId: "secret" } + { clientId: 'secret' } ); fail(); } catch (e) { - expect(e.message).toBe("jwt audience invalid. expected: secret"); + expect(e.message).toBe('jwt audience invalid. expected: secret'); } }); // TODO: figure out a way to generate our own facebook signed tokens, perhaps with a parse facebook account // and a private key - xit("(using client id as array) should throw error with invalid jwt clientId", async () => { + xit('(using client id as array) should throw error with invalid jwt clientId', async () => { try { await facebook.validateAuthData( { - id: "INSERT ID HERE", - token: "INSERT FACEBOOK TOKEN HERE", + id: 'INSERT ID HERE', + token: 'INSERT FACEBOOK TOKEN HERE', }, - { clientId: ["secret"] } + { clientId: ['secret'] } ); fail(); } catch (e) { - expect(e.message).toBe("jwt audience invalid. expected: secret"); + expect(e.message).toBe('jwt audience invalid. expected: secret'); } }); // TODO: figure out a way to generate our own facebook signed tokens, perhaps with a parse facebook account // and a private key - xit("should throw error with invalid user id", async () => { + xit('should throw error with invalid user id', async () => { try { await facebook.validateAuthData( { - id: "invalid user", - token: "INSERT FACEBOOK TOKEN HERE", + id: 'invalid user', + token: 'INSERT FACEBOOK TOKEN HERE', }, - { clientId: "INSERT CLIENT ID HERE" } + { clientId: 'INSERT CLIENT ID HERE' } ); fail(); } catch (e) { - expect(e.message).toBe("auth data is invalid for this user."); + expect(e.message).toBe('auth data is invalid for this user.'); } }); - it_id("c194d902-e697-46c9-a303-82c2d914473c")(it)( - "should throw error with with invalid user id (facebook.com)", + it_id('c194d902-e697-46c9-a303-82c2d914473c')(it)( + 'should throw error with with invalid user id (facebook.com)', async () => { const fakeClaim = { - iss: "https://www.facebook.com", - aud: "invalid_client_id", - sub: "a_different_user_id", + iss: 'https://www.facebook.com', + aud: 'invalid_client_id', + sub: 'a_different_user_id', }; - const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; - const fakeSigningKey = { kid: "123", rsaPublicKey: "the_rsa_public_key" }; - spyOn(authUtils, "getHeaderFromToken").and.callFake( + const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; + const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; + spyOn(authUtils, 'getHeaderFromToken').and.callFake( () => fakeDecodedToken ); - spyOn(authUtils, "getSigningKey").and.resolveTo(fakeSigningKey); - spyOn(jwt, "verify").and.callFake(() => fakeClaim); + spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); + spyOn(jwt, 'verify').and.callFake(() => fakeClaim); try { await facebook.validateAuthData( - { id: "the_user_id", token: "the_token" }, - { clientId: "secret" } + { id: 'the_user_id', token: 'the_token' }, + { clientId: 'secret' } ); fail(); } catch (e) { - expect(e.message).toBe("auth data is invalid for this user."); + expect(e.message).toBe('auth data is invalid for this user.'); } } ); }); -describe("OTP TOTP auth adatper", () => { +describe('OTP TOTP auth adatper', () => { const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; beforeEach(async () => { await reconfigureServer({ auth: { mfa: { enabled: true, - options: ["TOTP"], - algorithm: "SHA1", + options: ['TOTP'], + algorithm: 'SHA1', digits: 6, period: 30, }, @@ -1594,12 +1594,12 @@ describe("OTP TOTP auth adatper", () => { }); }); - it("can enroll", async () => { - const user = await Parse.User.signUp("username", "password"); - const OTPAuth = require("otpauth"); + it('can enroll', async () => { + const user = await Parse.User.signUp('username', 'password'); + const OTPAuth = require('otpauth'); const secret = new OTPAuth.Secret(); const totp = new OTPAuth.TOTP({ - algorithm: "SHA1", + algorithm: 'SHA1', digits: 6, period: 30, secret, @@ -1609,20 +1609,20 @@ describe("OTP TOTP auth adatper", () => { { authData: { mfa: { secret: secret.base32, token } } }, { sessionToken: user.getSessionToken() } ); - const response = user.get("authDataResponse"); + const response = user.get('authDataResponse'); expect(response.mfa).toBeDefined(); expect(response.mfa.recovery).toBeDefined(); - expect(response.mfa.recovery.split(",").length).toEqual(2); + expect(response.mfa.recovery.split(',').length).toEqual(2); await user.fetch(); - expect(user.get("authData").mfa).toEqual({ status: "enabled" }); + expect(user.get('authData').mfa).toEqual({ status: 'enabled' }); }); - it("can login with valid token", async () => { - const user = await Parse.User.signUp("username", "password"); - const OTPAuth = require("otpauth"); + it('can login with valid token', async () => { + const user = await Parse.User.signUp('username', 'password'); + const OTPAuth = require('otpauth'); const secret = new OTPAuth.Secret(); const totp = new OTPAuth.TOTP({ - algorithm: "SHA1", + algorithm: 'SHA1', digits: 6, period: 30, secret, @@ -1634,11 +1634,11 @@ describe("OTP TOTP auth adatper", () => { ); const response = await request({ headers, - method: "POST", - url: "http://localhost:8378/1/login", + method: 'POST', + url: 'http://localhost:8378/1/login', body: JSON.stringify({ - username: "username", - password: "password", + username: 'username', + password: 'password', authData: { mfa: { token: totp.generate(), @@ -1648,27 +1648,27 @@ describe("OTP TOTP auth adatper", () => { }).then(res => res.data); expect(response.objectId).toEqual(user.id); expect(response.sessionToken).toBeDefined(); - expect(response.authData).toEqual({ mfa: { status: "enabled" } }); + expect(response.authData).toEqual({ mfa: { status: 'enabled' } }); expect(Object.keys(response).sort()).toEqual( [ - "objectId", - "username", - "createdAt", - "updatedAt", - "authData", - "ACL", - "sessionToken", - "authDataResponse", + 'objectId', + 'username', + 'createdAt', + 'updatedAt', + 'authData', + 'ACL', + 'sessionToken', + 'authDataResponse', ].sort() ); }); - it("can change OTP with valid token", async () => { - const user = await Parse.User.signUp("username", "password"); - const OTPAuth = require("otpauth"); + it('can change OTP with valid token', async () => { + const user = await Parse.User.signUp('username', 'password'); + const OTPAuth = require('otpauth'); const secret = new OTPAuth.Secret(); const totp = new OTPAuth.TOTP({ - algorithm: "SHA1", + algorithm: 'SHA1', digits: 6, period: 30, secret, @@ -1681,7 +1681,7 @@ describe("OTP TOTP auth adatper", () => { const new_secret = new OTPAuth.Secret(); const new_totp = new OTPAuth.TOTP({ - algorithm: "SHA1", + algorithm: 'SHA1', digits: 6, period: 30, secret: new_secret, @@ -1700,15 +1700,15 @@ describe("OTP TOTP auth adatper", () => { { sessionToken: user.getSessionToken() } ); await user.fetch({ useMasterKey: true }); - expect(user.get("authData").mfa.secret).toEqual(new_secret.base32); + expect(user.get('authData').mfa.secret).toEqual(new_secret.base32); }); - it("cannot change OTP with invalid token", async () => { - const user = await Parse.User.signUp("username", "password"); - const OTPAuth = require("otpauth"); + it('cannot change OTP with invalid token', async () => { + const user = await Parse.User.signUp('username', 'password'); + const OTPAuth = require('otpauth'); const secret = new OTPAuth.Secret(); const totp = new OTPAuth.TOTP({ - algorithm: "SHA1", + algorithm: 'SHA1', digits: 6, period: 30, secret, @@ -1721,7 +1721,7 @@ describe("OTP TOTP auth adatper", () => { const new_secret = new OTPAuth.Secret(); const new_totp = new OTPAuth.TOTP({ - algorithm: "SHA1", + algorithm: 'SHA1', digits: 6, period: 30, secret: new_secret, @@ -1731,24 +1731,24 @@ describe("OTP TOTP auth adatper", () => { user.save( { authData: { - mfa: { secret: new_secret.base32, token: new_token, old: "123" }, + mfa: { secret: new_secret.base32, token: new_token, old: '123' }, }, }, { sessionToken: user.getSessionToken() } ) ).toBeRejectedWith( - new Parse.Error(Parse.Error.OTHER_CAUSE, "Invalid MFA token") + new Parse.Error(Parse.Error.OTHER_CAUSE, 'Invalid MFA token') ); await user.fetch({ useMasterKey: true }); - expect(user.get("authData").mfa.secret).toEqual(secret.base32); + expect(user.get('authData').mfa.secret).toEqual(secret.base32); }); - it("future logins require TOTP token", async () => { - const user = await Parse.User.signUp("username", "password"); - const OTPAuth = require("otpauth"); + it('future logins require TOTP token', async () => { + const user = await Parse.User.signUp('username', 'password'); + const OTPAuth = require('otpauth'); const secret = new OTPAuth.Secret(); const totp = new OTPAuth.TOTP({ - algorithm: "SHA1", + algorithm: 'SHA1', digits: 6, period: 30, secret, @@ -1759,21 +1759,21 @@ describe("OTP TOTP auth adatper", () => { { sessionToken: user.getSessionToken() } ); await expectAsync( - Parse.User.logIn("username", "password") + Parse.User.logIn('username', 'password') ).toBeRejectedWith( new Parse.Error( Parse.Error.OTHER_CAUSE, - "Missing additional authData mfa" + 'Missing additional authData mfa' ) ); }); - it("future logins reject incorrect TOTP token", async () => { - const user = await Parse.User.signUp("username", "password"); - const OTPAuth = require("otpauth"); + it('future logins reject incorrect TOTP token', async () => { + const user = await Parse.User.signUp('username', 'password'); + const OTPAuth = require('otpauth'); const secret = new OTPAuth.Secret(); const totp = new OTPAuth.TOTP({ - algorithm: "SHA1", + algorithm: 'SHA1', digits: 6, period: 30, secret, @@ -1786,14 +1786,14 @@ describe("OTP TOTP auth adatper", () => { await expectAsync( request({ headers, - method: "POST", - url: "http://localhost:8378/1/login", + method: 'POST', + url: 'http://localhost:8378/1/login', body: JSON.stringify({ - username: "username", - password: "password", + username: 'username', + password: 'password', authData: { mfa: { - token: "abcd", + token: 'abcd', }, }, }), @@ -1802,22 +1802,22 @@ describe("OTP TOTP auth adatper", () => { }) ).toBeRejectedWith({ code: Parse.Error.SCRIPT_FAILED, - error: "Invalid MFA token", + error: 'Invalid MFA token', }); }); }); -describe("OTP SMS auth adatper", () => { +describe('OTP SMS auth adatper', () => { const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; let code; let mobile; const mfa = { enabled: true, - options: ["SMS"], + options: ['SMS'], sendSMS(smsCode, number) { expect(smsCode).toBeDefined(); expect(number).toBeDefined(); @@ -1829,8 +1829,8 @@ describe("OTP SMS auth adatper", () => { period: 30, }; beforeEach(async () => { - code = ""; - mobile = ""; + code = ''; + mobile = ''; await reconfigureServer({ auth: { mfa, @@ -1838,36 +1838,36 @@ describe("OTP SMS auth adatper", () => { }); }); - it("can enroll", async () => { - const user = await Parse.User.signUp("username", "password"); + it('can enroll', async () => { + const user = await Parse.User.signUp('username', 'password'); const sessionToken = user.getSessionToken(); - const spy = spyOn(mfa, "sendSMS").and.callThrough(); + const spy = spyOn(mfa, 'sendSMS').and.callThrough(); await user.save( - { authData: { mfa: { mobile: "+11111111111" } } }, + { authData: { mfa: { mobile: '+11111111111' } } }, { sessionToken } ); await user.fetch({ sessionToken }); - expect(user.get("authData")).toEqual({ mfa: { status: "disabled" } }); - expect(spy).toHaveBeenCalledWith(code, "+11111111111"); + expect(user.get('authData')).toEqual({ mfa: { status: 'disabled' } }); + expect(spy).toHaveBeenCalledWith(code, '+11111111111'); await user.fetch({ useMasterKey: true }); - const authData = user.get("authData").mfa?.pending; + const authData = user.get('authData').mfa?.pending; expect(authData).toBeDefined(); - expect(authData["+11111111111"]).toBeDefined(); - expect(Object.keys(authData["+11111111111"])).toEqual(["token", "expiry"]); + expect(authData['+11111111111']).toBeDefined(); + expect(Object.keys(authData['+11111111111'])).toEqual(['token', 'expiry']); await user.save( { authData: { mfa: { mobile, token: code } } }, { sessionToken } ); await user.fetch({ sessionToken }); - expect(user.get("authData")).toEqual({ mfa: { status: "enabled" } }); + expect(user.get('authData')).toEqual({ mfa: { status: 'enabled' } }); }); - it("future logins require SMS code", async () => { - const user = await Parse.User.signUp("username", "password"); - const spy = spyOn(mfa, "sendSMS").and.callThrough(); + it('future logins require SMS code', async () => { + const user = await Parse.User.signUp('username', 'password'); + const spy = spyOn(mfa, 'sendSMS').and.callThrough(); await user.save( - { authData: { mfa: { mobile: "+11111111111" } } }, + { authData: { mfa: { mobile: '+11111111111' } } }, { sessionToken: user.getSessionToken() } ); @@ -1879,39 +1879,39 @@ describe("OTP SMS auth adatper", () => { spy.calls.reset(); await expectAsync( - Parse.User.logIn("username", "password") + Parse.User.logIn('username', 'password') ).toBeRejectedWith( new Parse.Error( Parse.Error.OTHER_CAUSE, - "Missing additional authData mfa" + 'Missing additional authData mfa' ) ); const res = await request({ headers, - method: "POST", - url: "http://localhost:8378/1/login", + method: 'POST', + url: 'http://localhost:8378/1/login', body: JSON.stringify({ - username: "username", - password: "password", + username: 'username', + password: 'password', authData: { mfa: { - token: "request", + token: 'request', }, }, }), }).catch(e => e.data); expect(res).toEqual({ code: Parse.Error.SCRIPT_FAILED, - error: "Please enter the token", + error: 'Please enter the token', }); - expect(spy).toHaveBeenCalledWith(code, "+11111111111"); + expect(spy).toHaveBeenCalledWith(code, '+11111111111'); const response = await request({ headers, - method: "POST", - url: "http://localhost:8378/1/login", + method: 'POST', + url: 'http://localhost:8378/1/login', body: JSON.stringify({ - username: "username", - password: "password", + username: 'username', + password: 'password', authData: { mfa: { token: code, @@ -1921,26 +1921,26 @@ describe("OTP SMS auth adatper", () => { }).then(res => res.data); expect(response.objectId).toEqual(user.id); expect(response.sessionToken).toBeDefined(); - expect(response.authData).toEqual({ mfa: { status: "enabled" } }); + expect(response.authData).toEqual({ mfa: { status: 'enabled' } }); expect(Object.keys(response).sort()).toEqual( [ - "objectId", - "username", - "createdAt", - "updatedAt", - "authData", - "ACL", - "sessionToken", - "authDataResponse", + 'objectId', + 'username', + 'createdAt', + 'updatedAt', + 'authData', + 'ACL', + 'sessionToken', + 'authDataResponse', ].sort() ); }); - it("partially enrolled users can still login", async () => { - const user = await Parse.User.signUp("username", "password"); - await user.save({ authData: { mfa: { mobile: "+11111111111" } } }); - const spy = spyOn(mfa, "sendSMS").and.callThrough(); - await Parse.User.logIn("username", "password"); + it('partially enrolled users can still login', async () => { + const user = await Parse.User.signUp('username', 'password'); + await user.save({ authData: { mfa: { mobile: '+11111111111' } } }); + const spy = spyOn(mfa, 'sendSMS').and.callThrough(); + await Parse.User.logIn('username', 'password'); expect(spy).not.toHaveBeenCalled(); }); }); diff --git a/spec/AuthenticationAdaptersV2.spec.js b/spec/AuthenticationAdaptersV2.spec.js index aad3359e12..486e6b4dc2 100644 --- a/spec/AuthenticationAdaptersV2.spec.js +++ b/spec/AuthenticationAdaptersV2.spec.js @@ -1,5 +1,5 @@ -const request = require("../lib/request"); -const Auth = require("../lib/Auth"); +const request = require('../lib/request'); +const Auth = require('../lib/Auth'); const requestWithExpectedError = async params => { try { return await request(params); @@ -7,16 +7,16 @@ const requestWithExpectedError = async params => { throw new Error(e.data.error); } }; -describe("Auth Adapter features", () => { +describe('Auth Adapter features', () => { const baseAdapter = { validateAppId: () => Promise.resolve(), validateAuthData: () => Promise.resolve(), }; const baseAdapter2 = { validateAppId: appIds => - appIds[0] === "test" ? Promise.resolve() : Promise.reject(), + appIds[0] === 'test' ? Promise.resolve() : Promise.reject(), validateAuthData: () => Promise.resolve(), - appIds: ["test"], + appIds: ['test'], options: { anOption: true }, }; @@ -28,19 +28,19 @@ describe("Auth Adapter features", () => { const additionalAdapter = { validateAppId: () => Promise.resolve(), validateAuthData: () => Promise.resolve(), - policy: "additional", + policy: 'additional', }; const soloAdapter = { validateAppId: () => Promise.resolve(), validateAuthData: () => Promise.resolve(), - policy: "solo", + policy: 'solo', }; const challengeAdapter = { validateAppId: () => Promise.resolve(), validateAuthData: () => Promise.resolve(), - challenge: () => Promise.resolve({ token: "test" }), + challenge: () => Promise.resolve({ token: 'test' }), options: { anOption: true, }, @@ -68,7 +68,7 @@ describe("Auth Adapter features", () => { validateOptions: () => Promise.resolve(), afterFind() { return { - foo: "bar", + foo: 'bar', }; }, }; @@ -78,50 +78,50 @@ describe("Auth Adapter features", () => { }; const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; - it("should ensure no duplicate auth data id after before save", async () => { + it('should ensure no duplicate auth data id after before save', async () => { await reconfigureServer({ auth: { baseAdapter }, cloud: () => { - Parse.Cloud.beforeSave("_User", async request => { - request.object.set("authData", { baseAdapter: { id: "test" } }); + Parse.Cloud.beforeSave('_User', async request => { + request.object.set('authData', { baseAdapter: { id: 'test' } }); }); }, }); const user = new Parse.User(); - await user.save({ authData: { baseAdapter: { id: "another" } } }); + await user.save({ authData: { baseAdapter: { id: 'another' } } }); await user.fetch({ useMasterKey: true }); - expect(user.get("authData")).toEqual({ baseAdapter: { id: "test" } }); + expect(user.get('authData')).toEqual({ baseAdapter: { id: 'test' } }); const user2 = new Parse.User(); await expectAsync( - user2.save({ authData: { baseAdapter: { id: "another" } } }) - ).toBeRejectedWithError("this auth is already used"); + user2.save({ authData: { baseAdapter: { id: 'another' } } }) + ).toBeRejectedWithError('this auth is already used'); }); - it("should ensure no duplicate auth data id after before save in case of more than one result", async () => { + it('should ensure no duplicate auth data id after before save in case of more than one result', async () => { await reconfigureServer({ auth: { baseAdapter }, cloud: () => { - Parse.Cloud.beforeSave("_User", async request => { - request.object.set("authData", { baseAdapter: { id: "test" } }); + Parse.Cloud.beforeSave('_User', async request => { + request.object.set('authData', { baseAdapter: { id: 'test' } }); }); }, }); const user = new Parse.User(); - await user.save({ authData: { baseAdapter: { id: "another" } } }); + await user.save({ authData: { baseAdapter: { id: 'another' } } }); await user.fetch({ useMasterKey: true }); - expect(user.get("authData")).toEqual({ baseAdapter: { id: "test" } }); + expect(user.get('authData')).toEqual({ baseAdapter: { id: 'test' } }); let i = 0; const originalFn = Auth.findUsersWithAuthData; - spyOn(Auth, "findUsersWithAuthData").and.callFake((...params) => { + spyOn(Auth, 'findUsersWithAuthData').and.callFake((...params) => { // First call is triggered during authData validation if (i === 0) { i++; @@ -136,37 +136,37 @@ describe("Auth Adapter features", () => { }); const user2 = new Parse.User(); await expectAsync( - user2.save({ authData: { baseAdapter: { id: "another" } } }) - ).toBeRejectedWithError("this auth is already used"); + user2.save({ authData: { baseAdapter: { id: 'another' } } }) + ).toBeRejectedWithError('this auth is already used'); }); - it("should ensure no duplicate auth data id during authData validation in case of more than one result", async () => { + it('should ensure no duplicate auth data id during authData validation in case of more than one result', async () => { await reconfigureServer({ auth: { baseAdapter }, cloud: () => { - Parse.Cloud.beforeSave("_User", async request => { - request.object.set("authData", { baseAdapter: { id: "test" } }); + Parse.Cloud.beforeSave('_User', async request => { + request.object.set('authData', { baseAdapter: { id: 'test' } }); }); }, }); - spyOn(Auth, "findUsersWithAuthData").and.resolveTo([true, true]); + spyOn(Auth, 'findUsersWithAuthData').and.resolveTo([true, true]); const user = new Parse.User(); await expectAsync( - user.save({ authData: { baseAdapter: { id: "another" } } }) - ).toBeRejectedWithError("this auth is already used"); + user.save({ authData: { baseAdapter: { id: 'another' } } }) + ).toBeRejectedWithError('this auth is already used'); }); - it("should pass authData, options, request to validateAuthData", async () => { - spyOn(baseAdapter, "validateAuthData").and.resolveTo({}); + it('should pass authData, options, request to validateAuthData', async () => { + spyOn(baseAdapter, 'validateAuthData').and.resolveTo({}); await reconfigureServer({ auth: { baseAdapter } }); const user = new Parse.User(); const payload = { someData: true }; await user.save({ - username: "test", - password: "password", + username: 'test', + password: 'password', authData: { baseAdapter: payload }, }); @@ -182,11 +182,11 @@ describe("Auth Adapter features", () => { await request({ headers: headers, - method: "POST", - url: "http://localhost:8378/1/login", + method: 'POST', + url: 'http://localhost:8378/1/login', body: JSON.stringify({ - username: "test", - password: "password", + username: 'test', + password: 'password', authData: { baseAdapter: payload }, }), }); @@ -204,36 +204,36 @@ describe("Auth Adapter features", () => { expect(secondCall.length).toEqual(3); }); - it("should trigger correctly validateSetUp", async () => { - spyOn(modernAdapter, "validateSetUp").and.resolveTo({}); - spyOn(modernAdapter, "validateUpdate").and.resolveTo({}); - spyOn(modernAdapter, "validateLogin").and.resolveTo({}); - spyOn(modernAdapter2, "validateSetUp").and.resolveTo({}); - spyOn(modernAdapter2, "validateUpdate").and.resolveTo({}); - spyOn(modernAdapter2, "validateLogin").and.resolveTo({}); + it('should trigger correctly validateSetUp', async () => { + spyOn(modernAdapter, 'validateSetUp').and.resolveTo({}); + spyOn(modernAdapter, 'validateUpdate').and.resolveTo({}); + spyOn(modernAdapter, 'validateLogin').and.resolveTo({}); + spyOn(modernAdapter2, 'validateSetUp').and.resolveTo({}); + spyOn(modernAdapter2, 'validateUpdate').and.resolveTo({}); + spyOn(modernAdapter2, 'validateLogin').and.resolveTo({}); await reconfigureServer({ auth: { modernAdapter, modernAdapter2 } }); const user = new Parse.User(); - await user.save({ authData: { modernAdapter: { id: "modernAdapter" } } }); + await user.save({ authData: { modernAdapter: { id: 'modernAdapter' } } }); expect(modernAdapter.validateUpdate).toHaveBeenCalledTimes(0); expect(modernAdapter.validateLogin).toHaveBeenCalledTimes(0); expect(modernAdapter.validateSetUp).toHaveBeenCalledTimes(1); const call = modernAdapter.validateSetUp.calls.argsFor(0); - expect(call[0]).toEqual({ id: "modernAdapter" }); + expect(call[0]).toEqual({ id: 'modernAdapter' }); expect(call[1]).toEqual(modernAdapter); expect(call[2].isChallenge).toBeUndefined(); expect(call[2].master).toBeDefined(); expect(call[2].object instanceof Parse.User).toBeTruthy(); expect(call[2].user).toBeUndefined(); expect(call[2].original).toBeUndefined(); - expect(call[2].triggerName).toBe("validateSetUp"); + expect(call[2].triggerName).toBe('validateSetUp'); expect(call.length).toEqual(3); expect(user.getSessionToken()).toBeDefined(); await user.save( - { authData: { modernAdapter2: { id: "modernAdapter2" } } }, + { authData: { modernAdapter2: { id: 'modernAdapter2' } } }, { sessionToken: user.getSessionToken() } ); @@ -241,7 +241,7 @@ describe("Auth Adapter features", () => { expect(modernAdapter2.validateLogin).toHaveBeenCalledTimes(0); expect(modernAdapter2.validateSetUp).toHaveBeenCalledTimes(1); const call2 = modernAdapter2.validateSetUp.calls.argsFor(0); - expect(call2[0]).toEqual({ id: "modernAdapter2" }); + expect(call2[0]).toEqual({ id: 'modernAdapter2' }); expect(call2[1]).toEqual(modernAdapter2); expect(call2[2].isChallenge).toBeUndefined(); expect(call2[2].master).toBeDefined(); @@ -251,22 +251,22 @@ describe("Auth Adapter features", () => { expect(call2[2].original.id).toEqual(call2[2].object.id); expect(call2[2].user.id).toEqual(call2[2].object.id); expect(call2[2].object.id).toEqual(user.id); - expect(call2[2].triggerName).toBe("validateSetUp"); + expect(call2[2].triggerName).toBe('validateSetUp'); expect(call2.length).toEqual(3); const user2 = new Parse.User(); user2.id = user.id; await user2.fetch({ useMasterKey: true }); - expect(user2.get("authData")).toEqual({ - modernAdapter: { id: "modernAdapter" }, - modernAdapter2: { id: "modernAdapter2" }, + expect(user2.get('authData')).toEqual({ + modernAdapter: { id: 'modernAdapter' }, + modernAdapter2: { id: 'modernAdapter2' }, }); }); - it("should trigger correctly validateLogin", async () => { - spyOn(modernAdapter, "validateSetUp").and.resolveTo({}); - spyOn(modernAdapter, "validateUpdate").and.resolveTo({}); - spyOn(modernAdapter, "validateLogin").and.resolveTo({}); + it('should trigger correctly validateLogin', async () => { + spyOn(modernAdapter, 'validateSetUp').and.resolveTo({}); + spyOn(modernAdapter, 'validateUpdate').and.resolveTo({}); + spyOn(modernAdapter, 'validateLogin').and.resolveTo({}); await reconfigureServer({ auth: { modernAdapter }, @@ -274,17 +274,17 @@ describe("Auth Adapter features", () => { }); const user = new Parse.User(); - await user.save({ authData: { modernAdapter: { id: "modernAdapter" } } }); + await user.save({ authData: { modernAdapter: { id: 'modernAdapter' } } }); expect(modernAdapter.validateSetUp).toHaveBeenCalledTimes(1); const user2 = new Parse.User(); - await user2.save({ authData: { modernAdapter: { id: "modernAdapter" } } }); + await user2.save({ authData: { modernAdapter: { id: 'modernAdapter' } } }); expect(modernAdapter.validateUpdate).toHaveBeenCalledTimes(0); expect(modernAdapter.validateSetUp).toHaveBeenCalledTimes(1); expect(modernAdapter.validateLogin).toHaveBeenCalledTimes(1); const call = modernAdapter.validateLogin.calls.argsFor(0); - expect(call[0]).toEqual({ id: "modernAdapter" }); + expect(call[0]).toEqual({ id: 'modernAdapter' }); expect(call[1]).toEqual(modernAdapter); expect(call[2].object instanceof Parse.User).toBeTruthy(); expect(call[2].original instanceof Parse.User).toBeTruthy(); @@ -298,26 +298,26 @@ describe("Auth Adapter features", () => { expect(user2.getSessionToken()).toBeDefined(); }); - it("should trigger correctly validateUpdate", async () => { - spyOn(modernAdapter, "validateSetUp").and.resolveTo({}); - spyOn(modernAdapter, "validateUpdate").and.resolveTo({}); - spyOn(modernAdapter, "validateLogin").and.resolveTo({}); + it('should trigger correctly validateUpdate', async () => { + spyOn(modernAdapter, 'validateSetUp').and.resolveTo({}); + spyOn(modernAdapter, 'validateUpdate').and.resolveTo({}); + spyOn(modernAdapter, 'validateLogin').and.resolveTo({}); await reconfigureServer({ auth: { modernAdapter } }); const user = new Parse.User(); - await user.save({ authData: { modernAdapter: { id: "modernAdapter" } } }); + await user.save({ authData: { modernAdapter: { id: 'modernAdapter' } } }); expect(modernAdapter.validateSetUp).toHaveBeenCalledTimes(1); // Save same data await user.save( - { authData: { modernAdapter: { id: "modernAdapter" } } }, + { authData: { modernAdapter: { id: 'modernAdapter' } } }, { sessionToken: user.getSessionToken() } ); // Save same data with master key await user.save( - { authData: { modernAdapter: { id: "modernAdapter" } } }, + { authData: { modernAdapter: { id: 'modernAdapter' } } }, { useMasterKey: true } ); @@ -327,7 +327,7 @@ describe("Auth Adapter features", () => { // Change authData await user.save( - { authData: { modernAdapter: { id: "modernAdapter2" } } }, + { authData: { modernAdapter: { id: 'modernAdapter2' } } }, { sessionToken: user.getSessionToken() } ); @@ -335,7 +335,7 @@ describe("Auth Adapter features", () => { expect(modernAdapter.validateSetUp).toHaveBeenCalledTimes(1); expect(modernAdapter.validateLogin).toHaveBeenCalledTimes(0); const call = modernAdapter.validateUpdate.calls.argsFor(0); - expect(call[0]).toEqual({ id: "modernAdapter2" }); + expect(call[0]).toEqual({ id: 'modernAdapter2' }); expect(call[1]).toEqual(modernAdapter); expect(call[2].isChallenge).toBeUndefined(); expect(call[2].master).toBeDefined(); @@ -349,17 +349,17 @@ describe("Auth Adapter features", () => { expect(user.getSessionToken()).toBeDefined(); }); - it("should strip out authData if required", async () => { - const spy = spyOn(modernAdapter3, "validateOptions").and.callThrough(); - const afterSpy = spyOn(modernAdapter3, "afterFind").and.callThrough(); + it('should strip out authData if required', async () => { + const spy = spyOn(modernAdapter3, 'validateOptions').and.callThrough(); + const afterSpy = spyOn(modernAdapter3, 'afterFind').and.callThrough(); await reconfigureServer({ auth: { modernAdapter3 } }); const user = new Parse.User(); await user.save({ - authData: { modernAdapter3: { id: "modernAdapter3Data" } }, + authData: { modernAdapter3: { id: 'modernAdapter3Data' } }, }); await user.fetch({ sessionToken: user.getSessionToken() }); - const authData = user.get("authData").modernAdapter3; - expect(authData).toEqual({ foo: "bar" }); + const authData = user.get('authData').modernAdapter3; + expect(authData).toEqual({ foo: 'bar' }); for (const call of afterSpy.calls.all()) { const args = call.args[2]; if (args.user) { @@ -368,10 +368,10 @@ describe("Auth Adapter features", () => { } } expect(afterSpy).toHaveBeenCalledWith( - { id: "modernAdapter3Data" }, + { id: 'modernAdapter3Data' }, undefined, { - ip: "127.0.0.1", + ip: '127.0.0.1', user, master: false, } @@ -379,35 +379,35 @@ describe("Auth Adapter features", () => { expect(spy).toHaveBeenCalled(); }); - it("should throw if policy does not match one of default/solo/additional", async () => { + it('should throw if policy does not match one of default/solo/additional', async () => { const adapterWithBadPolicy = { validateAppId: () => Promise.resolve(), validateAuthData: () => Promise.resolve(), - policy: "bad", + policy: 'bad', }; await reconfigureServer({ auth: { adapterWithBadPolicy } }); const user = new Parse.User(); await expectAsync( user.save({ - authData: { adapterWithBadPolicy: { id: "adapterWithBadPolicy" } }, + authData: { adapterWithBadPolicy: { id: 'adapterWithBadPolicy' } }, }) ).toBeRejectedWithError( 'AuthAdapter policy is not configured correctly. The value must be either "solo", "additional", "default" or undefined (will be handled as "default")' ); }); - it("should throw if no triggers found", async () => { + it('should throw if no triggers found', async () => { await reconfigureServer({ auth: { wrongAdapter } }); const user = new Parse.User(); await expectAsync( - user.save({ authData: { wrongAdapter: { id: "wrongAdapter" } } }) + user.save({ authData: { wrongAdapter: { id: 'wrongAdapter' } } }) ).toBeRejectedWithError( - "Adapter is not configured. Implement either validateAuthData or all of the following: validateSetUp, validateLogin and validateUpdate" + 'Adapter is not configured. Implement either validateAuthData or all of the following: validateSetUp, validateLogin and validateUpdate' ); }); - it("should not update authData if provider return doNotSave", async () => { - spyOn(doNotSaveAdapter, "validateAuthData").and.resolveTo({ + it('should not update authData if provider return doNotSave', async () => { + spyOn(doNotSaveAdapter, 'validateAuthData').and.resolveTo({ doNotSave: true, }); await reconfigureServer({ @@ -418,20 +418,20 @@ describe("Auth Adapter features", () => { await user.save({ authData: { - baseAdapter: { id: "baseAdapter" }, + baseAdapter: { id: 'baseAdapter' }, doNotSaveAdapter: { token: true }, }, }); await user.fetch({ useMasterKey: true }); - expect(user.get("authData")).toEqual({ - baseAdapter: { id: "baseAdapter" }, + expect(user.get('authData')).toEqual({ + baseAdapter: { id: 'baseAdapter' }, }); }); - it("should loginWith user with auth Adapter with do not save option, mutated authData and no additional auth adapter", async () => { - const spy = spyOn(doNotSaveAdapter, "validateAuthData").and.resolveTo({ + it('should loginWith user with auth Adapter with do not save option, mutated authData and no additional auth adapter', async () => { + const spy = spyOn(doNotSaveAdapter, 'validateAuthData').and.resolveTo({ doNotSave: false, }); await reconfigureServer({ @@ -441,28 +441,28 @@ describe("Auth Adapter features", () => { const user = new Parse.User(); await user.save({ - authData: { doNotSaveAdapter: { id: "doNotSaveAdapter" } }, + authData: { doNotSaveAdapter: { id: 'doNotSaveAdapter' } }, }); await user.fetch({ useMasterKey: true }); - expect(user.get("authData")).toEqual({ - doNotSaveAdapter: { id: "doNotSaveAdapter" }, + expect(user.get('authData')).toEqual({ + doNotSaveAdapter: { id: 'doNotSaveAdapter' }, }); spy.and.resolveTo({ doNotSave: true }); - const user2 = await Parse.User.logInWith("doNotSaveAdapter", { - authData: { id: "doNotSaveAdapter", example: "example" }, + const user2 = await Parse.User.logInWith('doNotSaveAdapter', { + authData: { id: 'doNotSaveAdapter', example: 'example' }, }); expect(user2.getSessionToken()).toBeDefined(); expect(user2.id).toEqual(user.id); }); - it("should perform authData validation only when its required", async () => { - spyOn(baseAdapter2, "validateAuthData").and.resolveTo({}); - spyOn(baseAdapter2, "validateAppId").and.resolveTo({}); - spyOn(baseAdapter, "validateAuthData").and.resolveTo({}); + it('should perform authData validation only when its required', async () => { + spyOn(baseAdapter2, 'validateAuthData').and.resolveTo({}); + spyOn(baseAdapter2, 'validateAppId').and.resolveTo({}); + spyOn(baseAdapter, 'validateAuthData').and.resolveTo({}); await reconfigureServer({ auth: { baseAdapter2, baseAdapter }, allowExpiredAuthDataToken: false, @@ -472,7 +472,7 @@ describe("Auth Adapter features", () => { await user.save({ authData: { - baseAdapter: { id: "baseAdapter" }, + baseAdapter: { id: 'baseAdapter' }, baseAdapter2: { token: true }, }, }); @@ -483,7 +483,7 @@ describe("Auth Adapter features", () => { const user2 = new Parse.User(); await user2.save({ authData: { - baseAdapter: { id: "baseAdapter" }, + baseAdapter: { id: 'baseAdapter' }, }, }); @@ -492,7 +492,7 @@ describe("Auth Adapter features", () => { const user3 = new Parse.User(); await user3.save({ authData: { - baseAdapter: { id: "baseAdapter" }, + baseAdapter: { id: 'baseAdapter' }, baseAdapter2: { token: true }, }, }); @@ -500,8 +500,8 @@ describe("Auth Adapter features", () => { expect(baseAdapter2.validateAuthData).toHaveBeenCalledTimes(2); }); - it("should not perform authData validation twice when data mutated", async () => { - spyOn(baseAdapter, "validateAuthData").and.resolveTo({}); + it('should not perform authData validation twice when data mutated', async () => { + spyOn(baseAdapter, 'validateAuthData').and.resolveTo({}); await reconfigureServer({ auth: { baseAdapter }, allowExpiredAuthDataToken: false, @@ -511,7 +511,7 @@ describe("Auth Adapter features", () => { await user.save({ authData: { - baseAdapter: { id: "baseAdapter", token: "sometoken1" }, + baseAdapter: { id: 'baseAdapter', token: 'sometoken1' }, }, }); @@ -520,14 +520,14 @@ describe("Auth Adapter features", () => { const user2 = new Parse.User(); await user2.save({ authData: { - baseAdapter: { id: "baseAdapter", token: "sometoken2" }, + baseAdapter: { id: 'baseAdapter', token: 'sometoken2' }, }, }); expect(baseAdapter.validateAuthData).toHaveBeenCalledTimes(2); }); - it("should require additional provider if configured", async () => { + it('should require additional provider if configured', async () => { await reconfigureServer({ auth: { baseAdapter, additionalAdapter }, }); @@ -536,7 +536,7 @@ describe("Auth Adapter features", () => { await user.save({ authData: { - baseAdapter: { id: "baseAdapter" }, + baseAdapter: { id: 'baseAdapter' }, additionalAdapter: { token: true }, }, }); @@ -545,15 +545,15 @@ describe("Auth Adapter features", () => { await expectAsync( user2.save({ authData: { - baseAdapter: { id: "baseAdapter" }, + baseAdapter: { id: 'baseAdapter' }, }, }) - ).toBeRejectedWithError("Missing additional authData additionalAdapter"); + ).toBeRejectedWithError('Missing additional authData additionalAdapter'); expect(user2.getSessionToken()).toBeUndefined(); await user2.save({ authData: { - baseAdapter: { id: "baseAdapter" }, + baseAdapter: { id: 'baseAdapter' }, additionalAdapter: { token: true }, }, }); @@ -561,7 +561,7 @@ describe("Auth Adapter features", () => { expect(user2.getSessionToken()).toBeDefined(); }); - it("should skip additional provider if used provider is solo", async () => { + it('should skip additional provider if used provider is solo', async () => { await reconfigureServer({ auth: { soloAdapter, additionalAdapter }, }); @@ -570,7 +570,7 @@ describe("Auth Adapter features", () => { await user.save({ authData: { - soloAdapter: { id: "soloAdapter" }, + soloAdapter: { id: 'soloAdapter' }, additionalAdapter: { token: true }, }, }); @@ -578,17 +578,17 @@ describe("Auth Adapter features", () => { const user2 = new Parse.User(); await user2.save({ authData: { - soloAdapter: { id: "soloAdapter" }, + soloAdapter: { id: 'soloAdapter' }, }, }); expect(user2.getSessionToken()).toBeDefined(); }); - it("should return authData response and save some info on non username login", async () => { - spyOn(baseAdapter, "validateAuthData").and.resolveTo({ + it('should return authData response and save some info on non username login', async () => { + spyOn(baseAdapter, 'validateAuthData').and.resolveTo({ response: { someData: true }, }); - spyOn(baseAdapter2, "validateAuthData").and.resolveTo({ + spyOn(baseAdapter2, 'validateAuthData').and.resolveTo({ response: { someData2: true }, save: { otherData: true }, }); @@ -600,12 +600,12 @@ describe("Auth Adapter features", () => { await user.save({ authData: { - baseAdapter: { id: "baseAdapter" }, + baseAdapter: { id: 'baseAdapter' }, baseAdapter2: { test: true }, }, }); - expect(user.get("authDataResponse")).toEqual({ + expect(user.get('authDataResponse')).toEqual({ baseAdapter: { someData: true }, baseAdapter2: { someData2: true }, }); @@ -615,27 +615,27 @@ describe("Auth Adapter features", () => { await user2.save( { authData: { - baseAdapter: { id: "baseAdapter" }, + baseAdapter: { id: 'baseAdapter' }, baseAdapter2: { test: true }, }, }, { sessionToken: user.getSessionToken() } ); - expect(user2.get("authDataResponse")).toEqual({ + expect(user2.get('authDataResponse')).toEqual({ baseAdapter2: { someData2: true }, }); const user3 = new Parse.User(); await user3.save({ authData: { - baseAdapter: { id: "baseAdapter" }, + baseAdapter: { id: 'baseAdapter' }, baseAdapter2: { test: true }, }, }); // On logIn all authData are revalidated - expect(user3.get("authDataResponse")).toEqual({ + expect(user3.get('authDataResponse')).toEqual({ baseAdapter: { someData: true }, baseAdapter2: { someData2: true }, }); @@ -643,17 +643,17 @@ describe("Auth Adapter features", () => { const userViaMasterKey = new Parse.User(); userViaMasterKey.id = user2.id; await userViaMasterKey.fetch({ useMasterKey: true }); - expect(userViaMasterKey.get("authData")).toEqual({ - baseAdapter: { id: "baseAdapter" }, + expect(userViaMasterKey.get('authData')).toEqual({ + baseAdapter: { id: 'baseAdapter' }, baseAdapter2: { otherData: true }, }); }); - it("should return authData response and save some info on username login", async () => { - spyOn(baseAdapter, "validateAuthData").and.resolveTo({ + it('should return authData response and save some info on username login', async () => { + spyOn(baseAdapter, 'validateAuthData').and.resolveTo({ response: { someData: true }, }); - spyOn(baseAdapter2, "validateAuthData").and.resolveTo({ + spyOn(baseAdapter2, 'validateAuthData').and.resolveTo({ response: { someData2: true }, save: { otherData: true }, }); @@ -664,29 +664,29 @@ describe("Auth Adapter features", () => { const user = new Parse.User(); await user.save({ - username: "username", - password: "password", + username: 'username', + password: 'password', authData: { - baseAdapter: { id: "baseAdapter" }, + baseAdapter: { id: 'baseAdapter' }, baseAdapter2: { test: true }, }, }); - expect(user.get("authDataResponse")).toEqual({ + expect(user.get('authDataResponse')).toEqual({ baseAdapter: { someData: true }, baseAdapter2: { someData2: true }, }); const res = await request({ headers: headers, - method: "POST", - url: "http://localhost:8378/1/login", + method: 'POST', + url: 'http://localhost:8378/1/login', body: JSON.stringify({ - username: "username", - password: "password", + username: 'username', + password: 'password', authData: { baseAdapter2: { test: true }, - baseAdapter: { id: "baseAdapter" }, + baseAdapter: { id: 'baseAdapter' }, }, }), }); @@ -697,18 +697,18 @@ describe("Auth Adapter features", () => { }); await user.fetch({ useMasterKey: true }); - expect(user.get("authData")).toEqual({ - baseAdapter: { id: "baseAdapter" }, + expect(user.get('authData')).toEqual({ + baseAdapter: { id: 'baseAdapter' }, baseAdapter2: { otherData: true }, }); }); - describe("should allow update of authData", () => { + describe('should allow update of authData', () => { beforeEach(async () => { - spyOn(baseAdapter, "validateAuthData").and.resolveTo({ + spyOn(baseAdapter, 'validateAuthData').and.resolveTo({ response: { someData: true }, }); - spyOn(baseAdapter2, "validateAuthData").and.resolveTo({ + spyOn(baseAdapter2, 'validateAuthData').and.resolveTo({ response: { someData2: true }, save: { otherData: true }, }); @@ -717,14 +717,14 @@ describe("Auth Adapter features", () => { }); }); - it("should not re validate the baseAdapter when user is already logged in and authData not changed", async () => { + it('should not re validate the baseAdapter when user is already logged in and authData not changed', async () => { const user = new Parse.User(); await user.save({ - username: "username", - password: "password", + username: 'username', + password: 'password', authData: { - baseAdapter: { id: "baseAdapter" }, + baseAdapter: { id: 'baseAdapter' }, baseAdapter2: { test: true }, }, }); @@ -736,7 +736,7 @@ describe("Auth Adapter features", () => { { authData: { baseAdapter2: { test: true }, - baseAdapter: { id: "baseAdapter" }, + baseAdapter: { id: 'baseAdapter' }, }, }, { sessionToken: user.getSessionToken() } @@ -745,13 +745,13 @@ describe("Auth Adapter features", () => { expect(baseAdapter.validateAuthData).toHaveBeenCalledTimes(1); }); - it("should not re-validate the baseAdapter when master key is used and authData has not changed", async () => { + it('should not re-validate the baseAdapter when master key is used and authData has not changed', async () => { const user = new Parse.User(); await user.save({ - username: "username", - password: "password", + username: 'username', + password: 'password', authData: { - baseAdapter: { id: "baseAdapter" }, + baseAdapter: { id: 'baseAdapter' }, baseAdapter2: { test: true }, }, }); @@ -759,7 +759,7 @@ describe("Auth Adapter features", () => { { authData: { baseAdapter2: { test: true }, - baseAdapter: { id: "baseAdapter" }, + baseAdapter: { id: 'baseAdapter' }, }, }, { useMasterKey: true } @@ -768,13 +768,13 @@ describe("Auth Adapter features", () => { expect(baseAdapter.validateAuthData).toHaveBeenCalledTimes(1); }); - it("should allow user to change authData", async () => { + it('should allow user to change authData', async () => { const user = new Parse.User(); await user.save({ - username: "username", - password: "password", + username: 'username', + password: 'password', authData: { - baseAdapter: { id: "baseAdapter" }, + baseAdapter: { id: 'baseAdapter' }, baseAdapter2: { test: true }, }, }); @@ -782,7 +782,7 @@ describe("Auth Adapter features", () => { { authData: { baseAdapter2: { test: true }, - baseAdapter: { id: "baseAdapter2" }, + baseAdapter: { id: 'baseAdapter2' }, }, }, { sessionToken: user.getSessionToken() } @@ -791,13 +791,13 @@ describe("Auth Adapter features", () => { expect(baseAdapter.validateAuthData).toHaveBeenCalledTimes(2); }); - it("should allow master key to change authData", async () => { + it('should allow master key to change authData', async () => { const user = new Parse.User(); await user.save({ - username: "username", - password: "password", + username: 'username', + password: 'password', authData: { - baseAdapter: { id: "baseAdapter" }, + baseAdapter: { id: 'baseAdapter' }, baseAdapter2: { test: true }, }, }); @@ -805,7 +805,7 @@ describe("Auth Adapter features", () => { { authData: { baseAdapter2: { test: true }, - baseAdapter: { id: "baseAdapter3" }, + baseAdapter: { id: 'baseAdapter3' }, }, }, { useMasterKey: true } @@ -814,15 +814,15 @@ describe("Auth Adapter features", () => { expect(baseAdapter.validateAuthData).toHaveBeenCalledTimes(2); await user.fetch({ useMasterKey: true }); - expect(user.get("authData")).toEqual({ - baseAdapter: { id: "baseAdapter3" }, + expect(user.get('authData')).toEqual({ + baseAdapter: { id: 'baseAdapter3' }, baseAdapter2: { otherData: true }, }); }); }); - it("should pass user to auth adapter on update by matching session", async () => { - spyOn(baseAdapter2, "validateAuthData").and.resolveTo({}); + it('should pass user to auth adapter on update by matching session', async () => { + spyOn(baseAdapter2, 'validateAuthData').and.resolveTo({}); await reconfigureServer({ auth: { baseAdapter2 } }); const user = new Parse.User(); @@ -830,8 +830,8 @@ describe("Auth Adapter features", () => { const payload = { someData: true }; await user.save({ - username: "test", - password: "password", + username: 'test', + password: 'password', }); expect(user.getSessionToken()).toBeDefined(); @@ -872,11 +872,11 @@ describe("Auth Adapter features", () => { expect(secondCall.length).toEqual(3); }); - it("should return custom errors", async () => { + it('should return custom errors', async () => { const throwInChallengeAdapter = { validateAppId: () => Promise.resolve(), validateAuthData: () => Promise.resolve(), - challenge: () => Promise.reject("Invalid challenge data: yolo"), + challenge: () => Promise.reject('Invalid challenge data: yolo'), options: { anOption: true, }, @@ -884,7 +884,7 @@ describe("Auth Adapter features", () => { const throwInSetup = { validateAppId: () => Promise.resolve(), validateSetUp: () => - Promise.reject("You cannot signup with that setup data."), + Promise.reject('You cannot signup with that setup data.'), validateUpdate: () => Promise.resolve(), validateLogin: () => Promise.resolve(), }; @@ -893,7 +893,7 @@ describe("Auth Adapter features", () => { validateAppId: () => Promise.resolve(), validateSetUp: () => Promise.resolve(), validateUpdate: () => - Promise.reject("You cannot update with that update data."), + Promise.reject('You cannot update with that update data.'), validateLogin: () => Promise.resolve(), }; @@ -902,90 +902,90 @@ describe("Auth Adapter features", () => { validateSetUp: () => Promise.resolve(), validateUpdate: () => Promise.resolve(), validateLogin: () => - Promise.reject("You cannot login with that login data."), + Promise.reject('You cannot login with that login data.'), }; await reconfigureServer({ auth: { challengeAdapter: throwInChallengeAdapter }, }); - let logger = require("../lib/logger").logger; - spyOn(logger, "error").and.callFake(() => {}); + let logger = require('../lib/logger').logger; + spyOn(logger, 'error').and.callFake(() => {}); await expectAsync( requestWithExpectedError({ headers: headers, - method: "POST", - url: "http://localhost:8378/1/challenge", + method: 'POST', + url: 'http://localhost:8378/1/challenge', body: JSON.stringify({ challengeData: { challengeAdapter: { someData: true }, }, }), }) - ).toBeRejectedWithError("Invalid challenge data: yolo"); + ).toBeRejectedWithError('Invalid challenge data: yolo'); expect(logger.error).toHaveBeenCalledWith( `Failed running auth step challenge for challengeAdapter for user undefined with Error: {"message":"Invalid challenge data: yolo","code":${Parse.Error.SCRIPT_FAILED}}`, { - authenticationStep: "challenge", + authenticationStep: 'challenge', error: new Parse.Error( Parse.Error.SCRIPT_FAILED, - "Invalid challenge data: yolo" + 'Invalid challenge data: yolo' ), user: undefined, - provider: "challengeAdapter", + provider: 'challengeAdapter', } ); await reconfigureServer({ auth: { modernAdapter: throwInSetup } }); - logger = require("../lib/logger").logger; - spyOn(logger, "error").and.callFake(() => {}); + logger = require('../lib/logger').logger; + spyOn(logger, 'error').and.callFake(() => {}); let user = new Parse.User(); await expectAsync( - user.save({ authData: { modernAdapter: { id: "modernAdapter" } } }) + user.save({ authData: { modernAdapter: { id: 'modernAdapter' } } }) ).toBeRejectedWith( new Parse.Error( Parse.Error.SCRIPT_FAILED, - "You cannot signup with that setup data." + 'You cannot signup with that setup data.' ) ); expect(logger.error).toHaveBeenCalledWith( `Failed running auth step validateSetUp for modernAdapter for user undefined with Error: {"message":"You cannot signup with that setup data.","code":${Parse.Error.SCRIPT_FAILED}}`, { - authenticationStep: "validateSetUp", + authenticationStep: 'validateSetUp', error: new Parse.Error( Parse.Error.SCRIPT_FAILED, - "You cannot signup with that setup data." + 'You cannot signup with that setup data.' ), user: undefined, - provider: "modernAdapter", + provider: 'modernAdapter', } ); await reconfigureServer({ auth: { modernAdapter: throwInUpdate } }); - logger = require("../lib/logger").logger; - spyOn(logger, "error").and.callFake(() => {}); + logger = require('../lib/logger').logger; + spyOn(logger, 'error').and.callFake(() => {}); user = new Parse.User(); - await user.save({ authData: { modernAdapter: { id: "updateAdapter" } } }); + await user.save({ authData: { modernAdapter: { id: 'updateAdapter' } } }); await expectAsync( user.save( - { authData: { modernAdapter: { id: "updateAdapter2" } } }, + { authData: { modernAdapter: { id: 'updateAdapter2' } } }, { sessionToken: user.getSessionToken() } ) ).toBeRejectedWith( new Parse.Error( Parse.Error.SCRIPT_FAILED, - "You cannot update with that update data." + 'You cannot update with that update data.' ) ); expect(logger.error).toHaveBeenCalledWith( `Failed running auth step validateUpdate for modernAdapter for user ${user.id} with Error: {"message":"You cannot update with that update data.","code":${Parse.Error.SCRIPT_FAILED}}`, { - authenticationStep: "validateUpdate", + authenticationStep: 'validateUpdate', error: new Parse.Error( Parse.Error.SCRIPT_FAILED, - "You cannot update with that update data." + 'You cannot update with that update data.' ), user: user.id, - provider: "modernAdapter", + provider: 'modernAdapter', } ); @@ -993,35 +993,35 @@ describe("Auth Adapter features", () => { auth: { modernAdapter: throwInLogin }, allowExpiredAuthDataToken: false, }); - logger = require("../lib/logger").logger; - spyOn(logger, "error").and.callFake(() => {}); + logger = require('../lib/logger').logger; + spyOn(logger, 'error').and.callFake(() => {}); user = new Parse.User(); - await user.save({ authData: { modernAdapter: { id: "modernAdapter" } } }); + await user.save({ authData: { modernAdapter: { id: 'modernAdapter' } } }); const user2 = new Parse.User(); await expectAsync( - user2.save({ authData: { modernAdapter: { id: "modernAdapter" } } }) + user2.save({ authData: { modernAdapter: { id: 'modernAdapter' } } }) ).toBeRejectedWith( new Parse.Error( Parse.Error.SCRIPT_FAILED, - "You cannot login with that login data." + 'You cannot login with that login data.' ) ); expect(logger.error).toHaveBeenCalledWith( `Failed running auth step validateLogin for modernAdapter for user ${user.id} with Error: {"message":"You cannot login with that login data.","code":${Parse.Error.SCRIPT_FAILED}}`, { - authenticationStep: "validateLogin", + authenticationStep: 'validateLogin', error: new Parse.Error( Parse.Error.SCRIPT_FAILED, - "You cannot login with that login data." + 'You cannot login with that login data.' ), user: user.id, - provider: "modernAdapter", + provider: 'modernAdapter', } ); }); - it("should return challenge with no logged user", async () => { - spyOn(challengeAdapter, "challenge").and.resolveTo({ token: "test" }); + it('should return challenge with no logged user', async () => { + spyOn(challengeAdapter, 'challenge').and.resolveTo({ token: 'test' }); await reconfigureServer({ auth: { challengeAdapter }, @@ -1030,34 +1030,34 @@ describe("Auth Adapter features", () => { await expectAsync( requestWithExpectedError({ headers: headers, - method: "POST", - url: "http://localhost:8378/1/challenge", + method: 'POST', + url: 'http://localhost:8378/1/challenge', body: {}, }) - ).toBeRejectedWithError("Nothing to challenge."); + ).toBeRejectedWithError('Nothing to challenge.'); await expectAsync( requestWithExpectedError({ headers: headers, - method: "POST", - url: "http://localhost:8378/1/challenge", + method: 'POST', + url: 'http://localhost:8378/1/challenge', body: { challengeData: true }, }) - ).toBeRejectedWithError("challengeData should be an object."); + ).toBeRejectedWithError('challengeData should be an object.'); await expectAsync( requestWithExpectedError({ headers: headers, - method: "POST", - url: "http://localhost:8378/1/challenge", + method: 'POST', + url: 'http://localhost:8378/1/challenge', body: { challengeData: { data: true }, authData: true }, }) - ).toBeRejectedWithError("authData should be an object."); + ).toBeRejectedWithError('authData should be an object.'); const res = await request({ headers: headers, - method: "POST", - url: "http://localhost:8378/1/challenge", + method: 'POST', + url: 'http://localhost:8378/1/challenge', body: JSON.stringify({ challengeData: { challengeAdapter: { someData: true }, @@ -1068,7 +1068,7 @@ describe("Auth Adapter features", () => { expect(res.data).toEqual({ challengeData: { challengeAdapter: { - token: "test", + token: 'test', }, }, }); @@ -1086,8 +1086,8 @@ describe("Auth Adapter features", () => { expect(challengeCall.length).toEqual(4); }); - it("should return empty challenge data response if challenged provider does not exists", async () => { - spyOn(challengeAdapter, "challenge").and.resolveTo({ token: "test" }); + it('should return empty challenge data response if challenged provider does not exists', async () => { + spyOn(challengeAdapter, 'challenge').and.resolveTo({ token: 'test' }); await reconfigureServer({ auth: { challengeAdapter }, @@ -1095,8 +1095,8 @@ describe("Auth Adapter features", () => { const res = await request({ headers: headers, - method: "POST", - url: "http://localhost:8378/1/challenge", + method: 'POST', + url: 'http://localhost:8378/1/challenge', body: JSON.stringify({ challengeData: { nonExistingProvider: { someData: true }, @@ -1106,40 +1106,40 @@ describe("Auth Adapter features", () => { expect(res.data).toEqual({ challengeData: {} }); }); - it("should return challenge with username created user", async () => { - spyOn(challengeAdapter, "challenge").and.resolveTo({ token: "test" }); + it('should return challenge with username created user', async () => { + spyOn(challengeAdapter, 'challenge').and.resolveTo({ token: 'test' }); await reconfigureServer({ auth: { challengeAdapter }, }); const user = new Parse.User(); - await user.save({ username: "username", password: "password" }); + await user.save({ username: 'username', password: 'password' }); await expectAsync( requestWithExpectedError({ headers: headers, - method: "POST", - url: "http://localhost:8378/1/challenge", + method: 'POST', + url: 'http://localhost:8378/1/challenge', body: JSON.stringify({ - username: "username", + username: 'username', challengeData: { challengeAdapter: { someData: true }, }, }), }) ).toBeRejectedWithError( - "You provided username or email, you need to also provide password." + 'You provided username or email, you need to also provide password.' ); await expectAsync( requestWithExpectedError({ headers: headers, - method: "POST", - url: "http://localhost:8378/1/challenge", + method: 'POST', + url: 'http://localhost:8378/1/challenge', body: JSON.stringify({ - username: "username", - password: "password", + username: 'username', + password: 'password', authData: { data: true }, challengeData: { challengeAdapter: { someData: true }, @@ -1147,16 +1147,16 @@ describe("Auth Adapter features", () => { }), }) ).toBeRejectedWithError( - "You cannot provide username/email and authData, only use one identification method." + 'You cannot provide username/email and authData, only use one identification method.' ); const res = await request({ headers: headers, - method: "POST", - url: "http://localhost:8378/1/challenge", + method: 'POST', + url: 'http://localhost:8378/1/challenge', body: JSON.stringify({ - username: "username", - password: "password", + username: 'username', + password: 'password', challengeData: { challengeAdapter: { someData: true }, }, @@ -1166,7 +1166,7 @@ describe("Auth Adapter features", () => { expect(res.data).toEqual({ challengeData: { challengeAdapter: { - token: "test", + token: 'test', }, }, }); @@ -1186,9 +1186,9 @@ describe("Auth Adapter features", () => { expect(challengeCall.length).toEqual(4); }); - it("should return challenge with authData created user", async () => { - spyOn(challengeAdapter, "challenge").and.resolveTo({ token: "test" }); - spyOn(challengeAdapter, "validateAuthData").and.callThrough(); + it('should return challenge with authData created user', async () => { + spyOn(challengeAdapter, 'challenge').and.resolveTo({ token: 'test' }); + spyOn(challengeAdapter, 'validateAuthData').and.callThrough(); await reconfigureServer({ auth: { challengeAdapter, soloAdapter }, @@ -1197,56 +1197,56 @@ describe("Auth Adapter features", () => { await expectAsync( requestWithExpectedError({ headers: headers, - method: "POST", - url: "http://localhost:8378/1/challenge", + method: 'POST', + url: 'http://localhost:8378/1/challenge', body: JSON.stringify({ challengeData: { challengeAdapter: { someData: true }, }, authData: { - challengeAdapter: { id: "challengeAdapter" }, + challengeAdapter: { id: 'challengeAdapter' }, }, }), }) - ).toBeRejectedWithError("User not found."); + ).toBeRejectedWithError('User not found.'); const user = new Parse.User(); await user.save({ - authData: { challengeAdapter: { id: "challengeAdapter" } }, + authData: { challengeAdapter: { id: 'challengeAdapter' } }, }); const user2 = new Parse.User(); - await user2.save({ authData: { soloAdapter: { id: "soloAdapter" } } }); + await user2.save({ authData: { soloAdapter: { id: 'soloAdapter' } } }); await expectAsync( requestWithExpectedError({ headers: headers, - method: "POST", - url: "http://localhost:8378/1/challenge", + method: 'POST', + url: 'http://localhost:8378/1/challenge', body: JSON.stringify({ challengeData: { challengeAdapter: { someData: true }, }, authData: { - challengeAdapter: { id: "challengeAdapter" }, - soloAdapter: { id: "soloAdapter" }, + challengeAdapter: { id: 'challengeAdapter' }, + soloAdapter: { id: 'soloAdapter' }, }, }), }) ).toBeRejectedWithError( - "You cannot provide more than one authData provider with an id." + 'You cannot provide more than one authData provider with an id.' ); const res = await request({ headers: headers, - method: "POST", - url: "http://localhost:8378/1/challenge", + method: 'POST', + url: 'http://localhost:8378/1/challenge', body: JSON.stringify({ challengeData: { challengeAdapter: { someData: true }, }, authData: { - challengeAdapter: { id: "challengeAdapter" }, + challengeAdapter: { id: 'challengeAdapter' }, }, }), }); @@ -1254,7 +1254,7 @@ describe("Auth Adapter features", () => { expect(res.data).toEqual({ challengeData: { challengeAdapter: { - token: "test", + token: 'test', }, }, }); @@ -1265,7 +1265,7 @@ describe("Auth Adapter features", () => { const challengeCall = challengeAdapter.challenge.calls.argsFor(0); expect(challengeAdapter.challenge).toHaveBeenCalledTimes(1); expect(challengeCall[0]).toEqual({ someData: true }); - expect(challengeCall[1]).toEqual({ id: "challengeAdapter" }); + expect(challengeCall[1]).toEqual({ id: 'challengeAdapter' }); expect(challengeCall[2]).toEqual(challengeAdapter); expect(challengeCall[3].master).toBeDefined(); expect(challengeCall[3].isChallenge).toBeTruthy(); @@ -1276,8 +1276,8 @@ describe("Auth Adapter features", () => { expect(challengeCall.length).toEqual(4); }); - it("should validate provided authData and prevent guess id attack", async () => { - spyOn(challengeAdapter, "challenge").and.resolveTo({ token: "test" }); + it('should validate provided authData and prevent guess id attack', async () => { + spyOn(challengeAdapter, 'challenge').and.resolveTo({ token: 'test' }); await reconfigureServer({ auth: { challengeAdapter, soloAdapter }, @@ -1286,45 +1286,45 @@ describe("Auth Adapter features", () => { await expectAsync( requestWithExpectedError({ headers: headers, - method: "POST", - url: "http://localhost:8378/1/challenge", + method: 'POST', + url: 'http://localhost:8378/1/challenge', body: JSON.stringify({ challengeData: { challengeAdapter: { someData: true }, }, authData: { - challengeAdapter: { id: "challengeAdapter" }, + challengeAdapter: { id: 'challengeAdapter' }, }, }), }) - ).toBeRejectedWithError("User not found."); + ).toBeRejectedWithError('User not found.'); const user = new Parse.User(); await user.save({ - authData: { challengeAdapter: { id: "challengeAdapter" } }, + authData: { challengeAdapter: { id: 'challengeAdapter' } }, }); - spyOn(challengeAdapter, "validateAuthData").and.rejectWith({}); + spyOn(challengeAdapter, 'validateAuthData').and.rejectWith({}); await expectAsync( requestWithExpectedError({ headers: headers, - method: "POST", - url: "http://localhost:8378/1/challenge", + method: 'POST', + url: 'http://localhost:8378/1/challenge', body: JSON.stringify({ challengeData: { challengeAdapter: { someData: true }, }, authData: { - challengeAdapter: { id: "challengeAdapter" }, + challengeAdapter: { id: 'challengeAdapter' }, }, }), }) - ).toBeRejectedWithError("User not found."); + ).toBeRejectedWithError('User not found.'); const validateCall = challengeAdapter.validateAuthData.calls.argsFor(0); expect(challengeAdapter.validateAuthData).toHaveBeenCalledTimes(1); - expect(validateCall[0]).toEqual({ id: "challengeAdapter" }); + expect(validateCall[0]).toEqual({ id: 'challengeAdapter' }); expect(validateCall[1]).toEqual(challengeAdapter); expect(validateCall[2].isChallenge).toBeTruthy(); expect(validateCall[2].master).toBeDefined(); @@ -1335,7 +1335,7 @@ describe("Auth Adapter features", () => { expect(validateCall.length).toEqual(3); }); - it("should work with multiple adapters", async () => { + it('should work with multiple adapters', async () => { const adapterA = { validateAppId: () => Promise.resolve(), validateAuthData: () => Promise.resolve(), @@ -1347,13 +1347,13 @@ describe("Auth Adapter features", () => { await reconfigureServer({ auth: { adapterA, adapterB } }); const user = new Parse.User(); await user.signUp({ - username: "test", - password: "password", + username: 'test', + password: 'password', }); - await user.save({ authData: { adapterA: { id: "testA" } } }); - expect(user.get("authData")).toEqual({ adapterA: { id: "testA" } }); - await user.save({ authData: { adapterA: null, adapterB: { id: "test" } } }); + await user.save({ authData: { adapterA: { id: 'testA' } } }); + expect(user.get('authData')).toEqual({ adapterA: { id: 'testA' } }); + await user.save({ authData: { adapterA: null, adapterB: { id: 'test' } } }); await user.fetch({ useMasterKey: true }); - expect(user.get("authData")).toEqual({ adapterB: { id: "test" } }); + expect(user.get('authData')).toEqual({ adapterB: { id: 'test' } }); }); }); diff --git a/spec/CLI.spec.js b/spec/CLI.spec.js index e3968f4946..06369fbdd4 100644 --- a/spec/CLI.spec.js +++ b/spec/CLI.spec.js @@ -1,36 +1,36 @@ -"use strict"; +'use strict'; let commander; -const definitions = require("../lib/cli/definitions/parse-server").default; +const definitions = require('../lib/cli/definitions/parse-server').default; const liveQueryDefinitions = - require("../lib/cli/definitions/parse-live-query-server").default; -const path = require("path"); -const { spawn } = require("child_process"); + require('../lib/cli/definitions/parse-live-query-server').default; +const path = require('path'); +const { spawn } = require('child_process'); const testDefinitions = { - arg0: "PROGRAM_ARG_0", + arg0: 'PROGRAM_ARG_0', arg1: { - env: "PROGRAM_ARG_1", + env: 'PROGRAM_ARG_1', required: true, }, arg2: { - env: "PROGRAM_ARG_2", + env: 'PROGRAM_ARG_2', action: function (value) { const intValue = parseInt(value); if (!Number.isInteger(intValue)) { - throw "arg2 is invalid"; + throw 'arg2 is invalid'; } return intValue; }, }, arg3: {}, arg4: { - default: "arg4Value", + default: 'arg4Value', }, }; -describe("commander additions", () => { +describe('commander additions', () => { beforeEach(() => { - const command = require("../lib/cli/utils/commander").default; + const command = require('../lib/cli/utils/commander').default; commander = new command.constructor(); commander.storeOptionsAsProperties(); commander.allowExcessArguments(); @@ -45,209 +45,209 @@ describe("commander additions", () => { done(); }); - it("should load properly definitions from args", done => { + it('should load properly definitions from args', done => { commander.loadDefinitions(testDefinitions); commander.parse([ - "node", - "./CLI.spec.js", - "--arg0", - "arg0Value", - "--arg1", - "arg1Value", - "--arg2", - "2", - "--arg3", - "some", + 'node', + './CLI.spec.js', + '--arg0', + 'arg0Value', + '--arg1', + 'arg1Value', + '--arg2', + '2', + '--arg3', + 'some', ]); - expect(commander.arg0).toEqual("arg0Value"); - expect(commander.arg1).toEqual("arg1Value"); + expect(commander.arg0).toEqual('arg0Value'); + expect(commander.arg1).toEqual('arg1Value'); expect(commander.arg2).toEqual(2); - expect(commander.arg3).toEqual("some"); - expect(commander.arg4).toEqual("arg4Value"); + expect(commander.arg3).toEqual('some'); + expect(commander.arg4).toEqual('arg4Value'); done(); }); - it("should load properly definitions from env", done => { + it('should load properly definitions from env', done => { commander.loadDefinitions(testDefinitions); commander.parse([], { - PROGRAM_ARG_0: "arg0ENVValue", - PROGRAM_ARG_1: "arg1ENVValue", - PROGRAM_ARG_2: "3", + PROGRAM_ARG_0: 'arg0ENVValue', + PROGRAM_ARG_1: 'arg1ENVValue', + PROGRAM_ARG_2: '3', }); - expect(commander.arg0).toEqual("arg0ENVValue"); - expect(commander.arg1).toEqual("arg1ENVValue"); + expect(commander.arg0).toEqual('arg0ENVValue'); + expect(commander.arg1).toEqual('arg1ENVValue'); expect(commander.arg2).toEqual(3); - expect(commander.arg4).toEqual("arg4Value"); + expect(commander.arg4).toEqual('arg4Value'); done(); }); - it("should load properly use args over env", () => { + it('should load properly use args over env', () => { commander.loadDefinitions(testDefinitions); commander.parse( - ["node", "./CLI.spec.js", "--arg0", "arg0Value", "--arg4", ""], + ['node', './CLI.spec.js', '--arg0', 'arg0Value', '--arg4', ''], { - PROGRAM_ARG_0: "arg0ENVValue", - PROGRAM_ARG_1: "arg1ENVValue", - PROGRAM_ARG_2: "4", - PROGRAM_ARG_4: "arg4ENVValue", + PROGRAM_ARG_0: 'arg0ENVValue', + PROGRAM_ARG_1: 'arg1ENVValue', + PROGRAM_ARG_2: '4', + PROGRAM_ARG_4: 'arg4ENVValue', } ); - expect(commander.arg0).toEqual("arg0Value"); - expect(commander.arg1).toEqual("arg1ENVValue"); + expect(commander.arg0).toEqual('arg0Value'); + expect(commander.arg1).toEqual('arg1ENVValue'); expect(commander.arg2).toEqual(4); - expect(commander.arg4).toEqual(""); + expect(commander.arg4).toEqual(''); }); - it("should fail in action as port is invalid", done => { + it('should fail in action as port is invalid', done => { commander.loadDefinitions(testDefinitions); expect(() => { - commander.parse(["node", "./CLI.spec.js", "--arg0", "arg0Value"], { - PROGRAM_ARG_0: "arg0ENVValue", - PROGRAM_ARG_1: "arg1ENVValue", - PROGRAM_ARG_2: "hello", + commander.parse(['node', './CLI.spec.js', '--arg0', 'arg0Value'], { + PROGRAM_ARG_0: 'arg0ENVValue', + PROGRAM_ARG_1: 'arg1ENVValue', + PROGRAM_ARG_2: 'hello', }); - }).toThrow("arg2 is invalid"); + }).toThrow('arg2 is invalid'); done(); }); - it("should not override config.json", done => { - spyOn(console, "log").and.callFake(() => {}); + it('should not override config.json', done => { + spyOn(console, 'log').and.callFake(() => {}); commander.loadDefinitions(testDefinitions); commander.parse( [ - "node", - "./CLI.spec.js", - "--arg0", - "arg0Value", - "./spec/configs/CLIConfig.json", + 'node', + './CLI.spec.js', + '--arg0', + 'arg0Value', + './spec/configs/CLIConfig.json', ], { - PROGRAM_ARG_0: "arg0ENVValue", - PROGRAM_ARG_1: "arg1ENVValue", + PROGRAM_ARG_0: 'arg0ENVValue', + PROGRAM_ARG_1: 'arg1ENVValue', } ); const options = commander.getOptions(); expect(options.arg2).toBe(8888); - expect(options.arg3).toBe("hello"); //config value - expect(options.arg4).toBe("/1"); + expect(options.arg3).toBe('hello'); //config value + expect(options.arg4).toBe('/1'); done(); }); - it("should fail with invalid values in JSON", done => { + it('should fail with invalid values in JSON', done => { commander.loadDefinitions(testDefinitions); expect(() => { commander.parse( [ - "node", - "./CLI.spec.js", - "--arg0", - "arg0Value", - "./spec/configs/CLIConfigFail.json", + 'node', + './CLI.spec.js', + '--arg0', + 'arg0Value', + './spec/configs/CLIConfigFail.json', ], { - PROGRAM_ARG_0: "arg0ENVValue", - PROGRAM_ARG_1: "arg1ENVValue", + PROGRAM_ARG_0: 'arg0ENVValue', + PROGRAM_ARG_1: 'arg1ENVValue', } ); - }).toThrow("arg2 is invalid"); + }).toThrow('arg2 is invalid'); done(); }); - it("should fail when too many apps are set", done => { + it('should fail when too many apps are set', done => { commander.loadDefinitions(testDefinitions); expect(() => { commander.parse([ - "node", - "./CLI.spec.js", - "./spec/configs/CLIConfigFailTooManyApps.json", + 'node', + './CLI.spec.js', + './spec/configs/CLIConfigFailTooManyApps.json', ]); - }).toThrow("Multiple apps are not supported"); + }).toThrow('Multiple apps are not supported'); done(); }); - it("should load config from apps", done => { - spyOn(console, "log").and.callFake(() => {}); + it('should load config from apps', done => { + spyOn(console, 'log').and.callFake(() => {}); commander.loadDefinitions(testDefinitions); commander.parse([ - "node", - "./CLI.spec.js", - "./spec/configs/CLIConfigApps.json", + 'node', + './CLI.spec.js', + './spec/configs/CLIConfigApps.json', ]); const options = commander.getOptions(); - expect(options.arg1).toBe("my_app"); + expect(options.arg1).toBe('my_app'); expect(options.arg2).toBe(8888); - expect(options.arg3).toBe("hello"); //config value - expect(options.arg4).toBe("/1"); + expect(options.arg3).toBe('hello'); //config value + expect(options.arg4).toBe('/1'); done(); }); - it("should fail when passing an invalid arguement", done => { + it('should fail when passing an invalid arguement', done => { commander.loadDefinitions(testDefinitions); expect(() => { commander.parse([ - "node", - "./CLI.spec.js", - "./spec/configs/CLIConfigUnknownArg.json", + 'node', + './CLI.spec.js', + './spec/configs/CLIConfigUnknownArg.json', ]); - }).toThrow("error: unknown option myArg"); + }).toThrow('error: unknown option myArg'); done(); }); }); -describe("definitions", () => { - it("should have valid types", () => { +describe('definitions', () => { + it('should have valid types', () => { for (const key in definitions) { const definition = definitions[key]; - expect(typeof definition).toBe("object"); - if (typeof definition.env !== "undefined") { - expect(typeof definition.env).toBe("string"); + expect(typeof definition).toBe('object'); + if (typeof definition.env !== 'undefined') { + expect(typeof definition.env).toBe('string'); } - expect(typeof definition.help).toBe("string"); - if (typeof definition.required !== "undefined") { - expect(typeof definition.required).toBe("boolean"); + expect(typeof definition.help).toBe('string'); + if (typeof definition.required !== 'undefined') { + expect(typeof definition.required).toBe('boolean'); } - if (typeof definition.action !== "undefined") { - expect(typeof definition.action).toBe("function"); + if (typeof definition.action !== 'undefined') { + expect(typeof definition.action).toBe('function'); } } }); - it("should throw when using deprecated facebookAppIds", () => { + it('should throw when using deprecated facebookAppIds', () => { expect(() => { definitions.facebookAppIds.action(); }).toThrow(); }); }); -describe("LiveQuery definitions", () => { - it("should have valid types", () => { +describe('LiveQuery definitions', () => { + it('should have valid types', () => { for (const key in liveQueryDefinitions) { const definition = liveQueryDefinitions[key]; - expect(typeof definition).toBe("object"); - if (typeof definition.env !== "undefined") { - expect(typeof definition.env).toBe("string"); + expect(typeof definition).toBe('object'); + if (typeof definition.env !== 'undefined') { + expect(typeof definition.env).toBe('string'); } expect(typeof definition.help).toBe( - "string", + 'string', `help for ${key} should be a string` ); - if (typeof definition.required !== "undefined") { - expect(typeof definition.required).toBe("boolean"); + if (typeof definition.required !== 'undefined') { + expect(typeof definition.required).toBe('boolean'); } - if (typeof definition.action !== "undefined") { - expect(typeof definition.action).toBe("function"); + if (typeof definition.action !== 'undefined') { + expect(typeof definition.action).toBe('function'); } } }); }); -describe("execution", () => { - const binPath = path.resolve(__dirname, "../bin/parse-server"); +describe('execution', () => { + const binPath = path.resolve(__dirname, '../bin/parse-server'); let childProcess; let aggregatedData; function handleStdout(childProcess, done, aggregatedData, requiredData) { - childProcess.stdout.on("data", data => { + childProcess.stdout.on('data', data => { data = data.toString(); aggregatedData.push(data); if ( @@ -261,16 +261,16 @@ describe("execution", () => { } function handleStderr(childProcess, done) { - childProcess.stderr.on("data", data => { + childProcess.stderr.on('data', data => { data = data.toString(); - if (!data.includes("[DEP0040] DeprecationWarning")) { + if (!data.includes('[DEP0040] DeprecationWarning')) { done.fail(data); } }); } function handleError(childProcess, done) { - childProcess.on("error", err => { + childProcess.on('error', err => { done.fail(err); }); } @@ -281,7 +281,7 @@ describe("execution", () => { afterEach(done => { if (childProcess) { - childProcess.on("close", () => { + childProcess.on('close', () => { childProcess = undefined; done(); }); @@ -289,105 +289,105 @@ describe("execution", () => { } }); - it_id("a0ab74b4-f805-4e03-b31d-b5cd59e64495")(it)( - "should start Parse Server", + it_id('a0ab74b4-f805-4e03-b31d-b5cd59e64495')(it)( + 'should start Parse Server', done => { const env = { ...process.env }; - env.NODE_OPTIONS = "--dns-result-order=ipv4first --trace-deprecation"; + env.NODE_OPTIONS = '--dns-result-order=ipv4first --trace-deprecation'; childProcess = spawn( binPath, [ - "--appId", - "test", - "--masterKey", - "test", - "--databaseURI", + '--appId', + 'test', + '--masterKey', + 'test', + '--databaseURI', databaseURI, - "--port", - "1339", + '--port', + '1339', ], { env } ); handleStdout(childProcess, done, aggregatedData, [ - "parse-server running on", + 'parse-server running on', ]); handleStderr(childProcess, done); handleError(childProcess, done); } ); - it_id("d7165081-b133-4cba-901b-19128ce41301")(it)( - "should start Parse Server with GraphQL", + it_id('d7165081-b133-4cba-901b-19128ce41301')(it)( + 'should start Parse Server with GraphQL', async done => { const env = { ...process.env }; - env.NODE_OPTIONS = "--dns-result-order=ipv4first --trace-deprecation"; + env.NODE_OPTIONS = '--dns-result-order=ipv4first --trace-deprecation'; childProcess = spawn( binPath, [ - "--appId", - "test", - "--masterKey", - "test", - "--databaseURI", + '--appId', + 'test', + '--masterKey', + 'test', + '--databaseURI', databaseURI, - "--port", - "1340", - "--mountGraphQL", + '--port', + '1340', + '--mountGraphQL', ], { env } ); handleStdout(childProcess, done, aggregatedData, [ - "parse-server running on", - "GraphQL running on", + 'parse-server running on', + 'GraphQL running on', ]); handleStderr(childProcess, done); handleError(childProcess, done); } ); - it_id("2769cdb4-ce8a-484d-8a91-635b5894ba7e")(it)( - "should start Parse Server with GraphQL and Playground", + it_id('2769cdb4-ce8a-484d-8a91-635b5894ba7e')(it)( + 'should start Parse Server with GraphQL and Playground', async done => { const env = { ...process.env }; - env.NODE_OPTIONS = "--dns-result-order=ipv4first --trace-deprecation"; + env.NODE_OPTIONS = '--dns-result-order=ipv4first --trace-deprecation'; childProcess = spawn( binPath, [ - "--appId", - "test", - "--masterKey", - "test", - "--databaseURI", + '--appId', + 'test', + '--masterKey', + 'test', + '--databaseURI', databaseURI, - "--port", - "1341", - "--mountGraphQL", - "--mountPlayground", + '--port', + '1341', + '--mountGraphQL', + '--mountPlayground', ], { env } ); handleStdout(childProcess, done, aggregatedData, [ - "parse-server running on", - "Playground running on", - "GraphQL running on", + 'parse-server running on', + 'Playground running on', + 'GraphQL running on', ]); handleStderr(childProcess, done); handleError(childProcess, done); } ); - it_id("23caddd7-bfea-4869-8bd4-0f2cd283c8bd")(it)( - "can start Parse Server with auth via CLI", + it_id('23caddd7-bfea-4869-8bd4-0f2cd283c8bd')(it)( + 'can start Parse Server with auth via CLI', done => { const env = { ...process.env }; - env.NODE_OPTIONS = "--dns-result-order=ipv4first --trace-deprecation"; + env.NODE_OPTIONS = '--dns-result-order=ipv4first --trace-deprecation'; childProcess = spawn( binPath, - ["--databaseURI", databaseURI, "./spec/configs/CLIConfigAuth.json"], + ['--databaseURI', databaseURI, './spec/configs/CLIConfigAuth.json'], { env } ); handleStdout(childProcess, done, aggregatedData, [ - "parse-server running on", + 'parse-server running on', ]); handleStderr(childProcess, done); handleError(childProcess, done); diff --git a/spec/CacheController.spec.js b/spec/CacheController.spec.js index 6b6678d01f..ff401f904e 100644 --- a/spec/CacheController.spec.js +++ b/spec/CacheController.spec.js @@ -1,23 +1,23 @@ const CacheController = - require("../lib/Controllers/CacheController.js").default; + require('../lib/Controllers/CacheController.js').default; -describe("CacheController", function () { +describe('CacheController', function () { let FakeCacheAdapter; - const FakeAppID = "foo"; - const KEY = "hello"; + const FakeAppID = 'foo'; + const KEY = 'hello'; beforeEach(() => { FakeCacheAdapter = { get: () => Promise.resolve(null), - put: jasmine.createSpy("put"), - del: jasmine.createSpy("del"), - clear: jasmine.createSpy("clear"), + put: jasmine.createSpy('put'), + del: jasmine.createSpy('del'), + clear: jasmine.createSpy('clear'), }; - spyOn(FakeCacheAdapter, "get").and.callThrough(); + spyOn(FakeCacheAdapter, 'get').and.callThrough(); }); - it("should expose role and user caches", done => { + it('should expose role and user caches', done => { const cache = new CacheController(FakeCacheAdapter, FakeAppID); expect(cache.role).not.toEqual(null); @@ -28,25 +28,25 @@ describe("CacheController", function () { done(); }); - ["role", "user"].forEach(cacheName => { - it("should prefix " + cacheName + " cache", () => { + ['role', 'user'].forEach(cacheName => { + it('should prefix ' + cacheName + ' cache', () => { const cache = new CacheController(FakeCacheAdapter, FakeAppID)[cacheName]; - cache.put(KEY, "world"); + cache.put(KEY, 'world'); const firstPut = FakeCacheAdapter.put.calls.first(); - expect(firstPut.args[0]).toEqual([FakeAppID, cacheName, KEY].join(":")); + expect(firstPut.args[0]).toEqual([FakeAppID, cacheName, KEY].join(':')); cache.get(KEY); const firstGet = FakeCacheAdapter.get.calls.first(); - expect(firstGet.args[0]).toEqual([FakeAppID, cacheName, KEY].join(":")); + expect(firstGet.args[0]).toEqual([FakeAppID, cacheName, KEY].join(':')); cache.del(KEY); const firstDel = FakeCacheAdapter.del.calls.first(); - expect(firstDel.args[0]).toEqual([FakeAppID, cacheName, KEY].join(":")); + expect(firstDel.args[0]).toEqual([FakeAppID, cacheName, KEY].join(':')); }); }); - it("should clear the entire cache", () => { + it('should clear the entire cache', () => { const cache = new CacheController(FakeCacheAdapter, FakeAppID); cache.clear(); @@ -59,13 +59,13 @@ describe("CacheController", function () { expect(FakeCacheAdapter.clear.calls.count()).toEqual(3); }); - it("should handle cache rejections", done => { + it('should handle cache rejections', done => { FakeCacheAdapter.get = () => Promise.reject(); const cache = new CacheController(FakeCacheAdapter, FakeAppID); - cache.get("foo").then(done, () => { - fail("Promise should not be rejected."); + cache.get('foo').then(done, () => { + fail('Promise should not be rejected.'); }); }); }); diff --git a/spec/Client.spec.js b/spec/Client.spec.js index 6b1e8283c7..5bf24cbcb8 100644 --- a/spec/Client.spec.js +++ b/spec/Client.spec.js @@ -1,9 +1,9 @@ -const Client = require("../lib/LiveQuery/Client").Client; +const Client = require('../lib/LiveQuery/Client').Client; const ParseWebSocket = - require("../lib/LiveQuery/ParseWebSocketServer").ParseWebSocket; + require('../lib/LiveQuery/ParseWebSocketServer').ParseWebSocket; -describe("Client", function () { - it("can be initialized", function () { +describe('Client', function () { + it('can be initialized', function () { const parseWebSocket = new ParseWebSocket({}); const client = new Client(1, parseWebSocket); @@ -12,32 +12,32 @@ describe("Client", function () { expect(client.subscriptionInfos.size).toBe(0); }); - it("can push response", function () { + it('can push response', function () { const parseWebSocket = { - send: jasmine.createSpy("send"), + send: jasmine.createSpy('send'), }; - Client.pushResponse(parseWebSocket, "message"); + Client.pushResponse(parseWebSocket, 'message'); - expect(parseWebSocket.send).toHaveBeenCalledWith("message"); + expect(parseWebSocket.send).toHaveBeenCalledWith('message'); }); - it("can push error", function () { + it('can push error', function () { const parseWebSocket = { - send: jasmine.createSpy("send"), + send: jasmine.createSpy('send'), }; - Client.pushError(parseWebSocket, 1, "error", true); + Client.pushError(parseWebSocket, 1, 'error', true); const lastCall = parseWebSocket.send.calls.first(); const messageJSON = JSON.parse(lastCall.args[0]); - expect(messageJSON.op).toBe("error"); - expect(messageJSON.error).toBe("error"); + expect(messageJSON.op).toBe('error'); + expect(messageJSON.error).toBe('error'); expect(messageJSON.code).toBe(1); expect(messageJSON.reconnect).toBe(true); }); - it("can add subscription information", function () { + it('can add subscription information', function () { const subscription = {}; - const fields = ["test"]; + const fields = ['test']; const subscriptionInfo = { subscription: subscription, fields: fields, @@ -49,9 +49,9 @@ describe("Client", function () { expect(client.subscriptionInfos.get(1)).toBe(subscriptionInfo); }); - it("can get subscription information", function () { + it('can get subscription information', function () { const subscription = {}; - const fields = ["test"]; + const fields = ['test']; const subscriptionInfo = { subscription: subscription, fields: fields, @@ -63,9 +63,9 @@ describe("Client", function () { expect(subscriptionInfoAgain).toBe(subscriptionInfo); }); - it("can delete subscription information", function () { + it('can delete subscription information', function () { const subscription = {}; - const fields = ["test"]; + const fields = ['test']; const subscriptionInfo = { subscription: subscription, fields: fields, @@ -77,14 +77,14 @@ describe("Client", function () { expect(client.subscriptionInfos.size).toBe(0); }); - it("can generate ParseObject JSON with null selected field", function () { + it('can generate ParseObject JSON with null selected field', function () { const parseObjectJSON = { - key: "value", - className: "test", - objectId: "test", - updatedAt: "2015-12-07T21:27:13.746Z", - createdAt: "2015-12-07T21:27:13.746Z", - ACL: "test", + key: 'value', + className: 'test', + objectId: 'test', + updatedAt: '2015-12-07T21:27:13.746Z', + createdAt: '2015-12-07T21:27:13.746Z', + ACL: 'test', }; const client = new Client(1, {}); @@ -93,14 +93,14 @@ describe("Client", function () { ); }); - it("can generate ParseObject JSON with undefined selected field", function () { + it('can generate ParseObject JSON with undefined selected field', function () { const parseObjectJSON = { - key: "value", - className: "test", - objectId: "test", - updatedAt: "2015-12-07T21:27:13.746Z", - createdAt: "2015-12-07T21:27:13.746Z", - ACL: "test", + key: 'value', + className: 'test', + objectId: 'test', + updatedAt: '2015-12-07T21:27:13.746Z', + createdAt: '2015-12-07T21:27:13.746Z', + ACL: 'test', }; const client = new Client(1, {}); @@ -109,185 +109,185 @@ describe("Client", function () { ); }); - it("can generate ParseObject JSON with selected fields", function () { + it('can generate ParseObject JSON with selected fields', function () { const parseObjectJSON = { - key: "value", - className: "test", - objectId: "test", - updatedAt: "2015-12-07T21:27:13.746Z", - createdAt: "2015-12-07T21:27:13.746Z", - ACL: "test", - test: "test", + key: 'value', + className: 'test', + objectId: 'test', + updatedAt: '2015-12-07T21:27:13.746Z', + createdAt: '2015-12-07T21:27:13.746Z', + ACL: 'test', + test: 'test', }; const client = new Client(1, {}); - expect(client._toJSONWithFields(parseObjectJSON, ["test"])).toEqual({ - className: "test", - objectId: "test", - updatedAt: "2015-12-07T21:27:13.746Z", - createdAt: "2015-12-07T21:27:13.746Z", - ACL: "test", - test: "test", + expect(client._toJSONWithFields(parseObjectJSON, ['test'])).toEqual({ + className: 'test', + objectId: 'test', + updatedAt: '2015-12-07T21:27:13.746Z', + createdAt: '2015-12-07T21:27:13.746Z', + ACL: 'test', + test: 'test', }); }); - it("can generate ParseObject JSON with nonexistent selected fields", function () { + it('can generate ParseObject JSON with nonexistent selected fields', function () { const parseObjectJSON = { - key: "value", - className: "test", - objectId: "test", - updatedAt: "2015-12-07T21:27:13.746Z", - createdAt: "2015-12-07T21:27:13.746Z", - ACL: "test", - test: "test", + key: 'value', + className: 'test', + objectId: 'test', + updatedAt: '2015-12-07T21:27:13.746Z', + createdAt: '2015-12-07T21:27:13.746Z', + ACL: 'test', + test: 'test', }; const client = new Client(1, {}); const limitedParseObject = client._toJSONWithFields(parseObjectJSON, [ - "name", + 'name', ]); expect(limitedParseObject).toEqual({ - className: "test", - objectId: "test", - updatedAt: "2015-12-07T21:27:13.746Z", - createdAt: "2015-12-07T21:27:13.746Z", - ACL: "test", + className: 'test', + objectId: 'test', + updatedAt: '2015-12-07T21:27:13.746Z', + createdAt: '2015-12-07T21:27:13.746Z', + ACL: 'test', }); - expect("name" in limitedParseObject).toBe(false); + expect('name' in limitedParseObject).toBe(false); }); - it("can push connect response", function () { + it('can push connect response', function () { const parseWebSocket = { - send: jasmine.createSpy("send"), + send: jasmine.createSpy('send'), }; const client = new Client(1, parseWebSocket); client.pushConnect(); const lastCall = parseWebSocket.send.calls.first(); const messageJSON = JSON.parse(lastCall.args[0]); - expect(messageJSON.op).toBe("connected"); + expect(messageJSON.op).toBe('connected'); expect(messageJSON.clientId).toBe(1); }); - it("can push subscribe response", function () { + it('can push subscribe response', function () { const parseWebSocket = { - send: jasmine.createSpy("send"), + send: jasmine.createSpy('send'), }; const client = new Client(1, parseWebSocket); client.pushSubscribe(2); const lastCall = parseWebSocket.send.calls.first(); const messageJSON = JSON.parse(lastCall.args[0]); - expect(messageJSON.op).toBe("subscribed"); + expect(messageJSON.op).toBe('subscribed'); expect(messageJSON.clientId).toBe(1); expect(messageJSON.requestId).toBe(2); }); - it("can push unsubscribe response", function () { + it('can push unsubscribe response', function () { const parseWebSocket = { - send: jasmine.createSpy("send"), + send: jasmine.createSpy('send'), }; const client = new Client(1, parseWebSocket); client.pushUnsubscribe(2); const lastCall = parseWebSocket.send.calls.first(); const messageJSON = JSON.parse(lastCall.args[0]); - expect(messageJSON.op).toBe("unsubscribed"); + expect(messageJSON.op).toBe('unsubscribed'); expect(messageJSON.clientId).toBe(1); expect(messageJSON.requestId).toBe(2); }); - it("can push create response", function () { + it('can push create response', function () { const parseObjectJSON = { - key: "value", - className: "test", - objectId: "test", - updatedAt: "2015-12-07T21:27:13.746Z", - createdAt: "2015-12-07T21:27:13.746Z", - ACL: "test", - test: "test", + key: 'value', + className: 'test', + objectId: 'test', + updatedAt: '2015-12-07T21:27:13.746Z', + createdAt: '2015-12-07T21:27:13.746Z', + ACL: 'test', + test: 'test', }; const parseWebSocket = { - send: jasmine.createSpy("send"), + send: jasmine.createSpy('send'), }; const client = new Client(1, parseWebSocket); client.pushCreate(2, parseObjectJSON); const lastCall = parseWebSocket.send.calls.first(); const messageJSON = JSON.parse(lastCall.args[0]); - expect(messageJSON.op).toBe("create"); + expect(messageJSON.op).toBe('create'); expect(messageJSON.clientId).toBe(1); expect(messageJSON.requestId).toBe(2); expect(messageJSON.object).toEqual(parseObjectJSON); }); - it("can push enter response", function () { + it('can push enter response', function () { const parseObjectJSON = { - key: "value", - className: "test", - objectId: "test", - updatedAt: "2015-12-07T21:27:13.746Z", - createdAt: "2015-12-07T21:27:13.746Z", - ACL: "test", - test: "test", + key: 'value', + className: 'test', + objectId: 'test', + updatedAt: '2015-12-07T21:27:13.746Z', + createdAt: '2015-12-07T21:27:13.746Z', + ACL: 'test', + test: 'test', }; const parseWebSocket = { - send: jasmine.createSpy("send"), + send: jasmine.createSpy('send'), }; const client = new Client(1, parseWebSocket); client.pushEnter(2, parseObjectJSON); const lastCall = parseWebSocket.send.calls.first(); const messageJSON = JSON.parse(lastCall.args[0]); - expect(messageJSON.op).toBe("enter"); + expect(messageJSON.op).toBe('enter'); expect(messageJSON.clientId).toBe(1); expect(messageJSON.requestId).toBe(2); expect(messageJSON.object).toEqual(parseObjectJSON); }); - it("can push update response", function () { + it('can push update response', function () { const parseObjectJSON = { - key: "value", - className: "test", - objectId: "test", - updatedAt: "2015-12-07T21:27:13.746Z", - createdAt: "2015-12-07T21:27:13.746Z", - ACL: "test", - test: "test", + key: 'value', + className: 'test', + objectId: 'test', + updatedAt: '2015-12-07T21:27:13.746Z', + createdAt: '2015-12-07T21:27:13.746Z', + ACL: 'test', + test: 'test', }; const parseWebSocket = { - send: jasmine.createSpy("send"), + send: jasmine.createSpy('send'), }; const client = new Client(1, parseWebSocket); client.pushUpdate(2, parseObjectJSON); const lastCall = parseWebSocket.send.calls.first(); const messageJSON = JSON.parse(lastCall.args[0]); - expect(messageJSON.op).toBe("update"); + expect(messageJSON.op).toBe('update'); expect(messageJSON.clientId).toBe(1); expect(messageJSON.requestId).toBe(2); expect(messageJSON.object).toEqual(parseObjectJSON); }); - it("can push leave response", function () { + it('can push leave response', function () { const parseObjectJSON = { - key: "value", - className: "test", - objectId: "test", - updatedAt: "2015-12-07T21:27:13.746Z", - createdAt: "2015-12-07T21:27:13.746Z", - ACL: "test", - test: "test", + key: 'value', + className: 'test', + objectId: 'test', + updatedAt: '2015-12-07T21:27:13.746Z', + createdAt: '2015-12-07T21:27:13.746Z', + ACL: 'test', + test: 'test', }; const parseWebSocket = { - send: jasmine.createSpy("send"), + send: jasmine.createSpy('send'), }; const client = new Client(1, parseWebSocket); client.pushLeave(2, parseObjectJSON); const lastCall = parseWebSocket.send.calls.first(); const messageJSON = JSON.parse(lastCall.args[0]); - expect(messageJSON.op).toBe("leave"); + expect(messageJSON.op).toBe('leave'); expect(messageJSON.clientId).toBe(1); expect(messageJSON.requestId).toBe(2); expect(messageJSON.object).toEqual(parseObjectJSON); diff --git a/spec/ClientSDK.spec.js b/spec/ClientSDK.spec.js index 80c0167417..987770833c 100644 --- a/spec/ClientSDK.spec.js +++ b/spec/ClientSDK.spec.js @@ -1,48 +1,48 @@ -const ClientSDK = require("../lib/ClientSDK"); +const ClientSDK = require('../lib/ClientSDK'); -describe("ClientSDK", () => { - it("should properly parse the SDK versions", () => { +describe('ClientSDK', () => { + it('should properly parse the SDK versions', () => { const clientSDKFromVersion = ClientSDK.fromString; - expect(clientSDKFromVersion("i1.1.1")).toEqual({ - sdk: "i", - version: "1.1.1", + expect(clientSDKFromVersion('i1.1.1')).toEqual({ + sdk: 'i', + version: '1.1.1', }); - expect(clientSDKFromVersion("i1")).toEqual({ - sdk: "i", - version: "1", + expect(clientSDKFromVersion('i1')).toEqual({ + sdk: 'i', + version: '1', }); - expect(clientSDKFromVersion("apple-tv1.13.0")).toEqual({ - sdk: "apple-tv", - version: "1.13.0", + expect(clientSDKFromVersion('apple-tv1.13.0')).toEqual({ + sdk: 'apple-tv', + version: '1.13.0', }); - expect(clientSDKFromVersion("js1.9.0")).toEqual({ - sdk: "js", - version: "1.9.0", + expect(clientSDKFromVersion('js1.9.0')).toEqual({ + sdk: 'js', + version: '1.9.0', }); }); - it("should properly sastisfy", () => { + it('should properly sastisfy', () => { expect( ClientSDK.compatible({ - js: ">=1.9.0", - })("js1.9.0") + js: '>=1.9.0', + })('js1.9.0') ).toBe(true); expect( ClientSDK.compatible({ - js: ">=1.9.0", - })("js2.0.0") + js: '>=1.9.0', + })('js2.0.0') ).toBe(true); expect( ClientSDK.compatible({ - js: ">=1.9.0", - })("js1.8.0") + js: '>=1.9.0', + })('js1.8.0') ).toBe(false); expect( ClientSDK.compatible({ - js: ">=1.9.0", + js: '>=1.9.0', })(undefined) ).toBe(true); }); diff --git a/spec/CloudCode.Validator.spec.js b/spec/CloudCode.Validator.spec.js index a52410e9c4..fce6f2c21e 100644 --- a/spec/CloudCode.Validator.spec.js +++ b/spec/CloudCode.Validator.spec.js @@ -1,88 +1,88 @@ -"use strict"; -const Parse = require("parse/node"); +'use strict'; +const Parse = require('parse/node'); const validatorFail = () => { - throw "you are not authorized"; + throw 'you are not authorized'; }; const validatorSuccess = () => { return true; }; function testConfig() { return Parse.Config.save( - { internal: "i", string: "s", number: 12 }, + { internal: 'i', string: 's', number: 12 }, { internal: true } ); } -describe("cloud validator", () => { - it("complete validator", async done => { +describe('cloud validator', () => { + it('complete validator', async done => { Parse.Cloud.define( - "myFunction", + 'myFunction', () => { - return "myFunc"; + return 'myFunc'; }, () => {} ); try { - const result = await Parse.Cloud.run("myFunction", {}); - expect(result).toBe("myFunc"); + const result = await Parse.Cloud.run('myFunction', {}); + expect(result).toBe('myFunc'); done(); } catch (e) { - fail("should not have thrown error"); + fail('should not have thrown error'); } }); - it("Throw from validator", async done => { + it('Throw from validator', async done => { Parse.Cloud.define( - "myFunction", + 'myFunction', () => { - return "myFunc"; + return 'myFunc'; }, () => { - throw "error"; + throw 'error'; } ); try { - await Parse.Cloud.run("myFunction"); - fail("cloud function should have failed."); + await Parse.Cloud.run('myFunction'); + fail('cloud function should have failed.'); } catch (e) { expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); done(); } }); - it("validator can throw parse error", async done => { + it('validator can throw parse error', async done => { Parse.Cloud.define( - "myFunction", + 'myFunction', () => { - return "myFunc"; + return 'myFunc'; }, () => { - throw new Parse.Error(Parse.Error.SCRIPT_FAILED, "It should fail"); + throw new Parse.Error(Parse.Error.SCRIPT_FAILED, 'It should fail'); } ); try { - await Parse.Cloud.run("myFunction"); - fail("should have validation error"); + await Parse.Cloud.run('myFunction'); + fail('should have validation error'); } catch (e) { expect(e.code).toBe(Parse.Error.SCRIPT_FAILED); - expect(e.message).toBe("It should fail"); + expect(e.message).toBe('It should fail'); done(); } }); - it("validator can throw parse error with no message", async done => { + it('validator can throw parse error with no message', async done => { Parse.Cloud.define( - "myFunction", + 'myFunction', () => { - return "myFunc"; + return 'myFunc'; }, () => { throw new Parse.Error(Parse.Error.SCRIPT_FAILED); } ); try { - await Parse.Cloud.run("myFunction"); - fail("should have validation error"); + await Parse.Cloud.run('myFunction'); + fail('should have validation error'); } catch (e) { expect(e.code).toBe(Parse.Error.SCRIPT_FAILED); expect(e.message).toBeUndefined(); @@ -90,30 +90,30 @@ describe("cloud validator", () => { } }); - it("async validator", async done => { + it('async validator', async done => { Parse.Cloud.define( - "myFunction", + 'myFunction', () => { - return "myFunc"; + return 'myFunc'; }, async () => { await new Promise(resolve => { setTimeout(resolve, 1000); }); - throw "async error"; + throw 'async error'; } ); try { - await Parse.Cloud.run("myFunction"); - fail("should have validation error"); + await Parse.Cloud.run('myFunction'); + fail('should have validation error'); } catch (e) { expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); - expect(e.message).toBe("async error"); + expect(e.message).toBe('async error'); done(); } }); - it("pass function to validator", async done => { + it('pass function to validator', async done => { const validator = request => { expect(request).toBeDefined(); expect(request.params).toBeDefined(); @@ -127,108 +127,108 @@ describe("cloud validator", () => { done(); }; Parse.Cloud.define( - "myFunction", + 'myFunction', () => { - return "myFunc"; + return 'myFunc'; }, validator ); - await Parse.Cloud.run("myFunction"); + await Parse.Cloud.run('myFunction'); }); - it("require user on cloud functions", async done => { + it('require user on cloud functions', async done => { Parse.Cloud.define( - "hello1", + 'hello1', () => { - return "Hello world!"; + return 'Hello world!'; }, { requireUser: true, } ); try { - await Parse.Cloud.run("hello1", {}); - fail("function should have failed."); + await Parse.Cloud.run('hello1', {}); + fail('function should have failed.'); } catch (error) { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); expect(error.message).toEqual( - "Validation failed. Please login to continue." + 'Validation failed. Please login to continue.' ); done(); } }); - it("require master on cloud functions", done => { + it('require master on cloud functions', done => { Parse.Cloud.define( - "hello2", + 'hello2', () => { - return "Hello world!"; + return 'Hello world!'; }, { requireMaster: true, } ); - Parse.Cloud.run("hello2", {}) + Parse.Cloud.run('hello2', {}) .then(() => { - fail("function should have failed."); + fail('function should have failed.'); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); expect(error.message).toEqual( - "Validation failed. Master key is required to complete this request." + 'Validation failed. Master key is required to complete this request.' ); done(); }); }); - it("set params on cloud functions", done => { + it('set params on cloud functions', done => { Parse.Cloud.define( - "hello", + 'hello', () => { - return "Hello world!"; + return 'Hello world!'; }, { - fields: ["a"], + fields: ['a'], } ); - Parse.Cloud.run("hello", {}) + Parse.Cloud.run('hello', {}) .then(() => { - fail("function should have failed."); + fail('function should have failed.'); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); expect(error.message).toEqual( - "Validation failed. Please specify data for a." + 'Validation failed. Please specify data for a.' ); done(); }); }); - it("allow params on cloud functions", done => { + it('allow params on cloud functions', done => { Parse.Cloud.define( - "hello", + 'hello', req => { - expect(req.params.a).toEqual("yolo"); - return "Hello world!"; + expect(req.params.a).toEqual('yolo'); + return 'Hello world!'; }, { - fields: ["a"], + fields: ['a'], } ); - Parse.Cloud.run("hello", { a: "yolo" }) + Parse.Cloud.run('hello', { a: 'yolo' }) .then(() => { done(); }) .catch(() => { - fail("Error should not have been called."); + fail('Error should not have been called.'); }); }); - it("set params type array", done => { + it('set params type array', done => { Parse.Cloud.define( - "hello", + 'hello', () => { - return "Hello world!"; + return 'Hello world!'; }, { fields: { @@ -238,24 +238,24 @@ describe("cloud validator", () => { }, } ); - Parse.Cloud.run("hello", { data: "" }) + Parse.Cloud.run('hello', { data: '' }) .then(() => { - fail("function should have failed."); + fail('function should have failed.'); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); expect(error.message).toEqual( - "Validation failed. Invalid type for data. Expected: array" + 'Validation failed. Invalid type for data. Expected: array' ); done(); }); }); - it("set params type allow array", async () => { + it('set params type allow array', async () => { Parse.Cloud.define( - "hello", + 'hello', () => { - return "Hello world!"; + return 'Hello world!'; }, { fields: { @@ -265,15 +265,15 @@ describe("cloud validator", () => { }, } ); - const result = await Parse.Cloud.run("hello", { data: [{ foo: "bar" }] }); - expect(result).toBe("Hello world!"); + const result = await Parse.Cloud.run('hello', { data: [{ foo: 'bar' }] }); + expect(result).toBe('Hello world!'); }); - it("set params type", done => { + it('set params type', done => { Parse.Cloud.define( - "hello", + 'hello', () => { - return "Hello world!"; + return 'Hello world!'; }, { fields: { @@ -283,50 +283,50 @@ describe("cloud validator", () => { }, } ); - Parse.Cloud.run("hello", { data: [] }) + Parse.Cloud.run('hello', { data: [] }) .then(() => { - fail("function should have failed."); + fail('function should have failed.'); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); expect(error.message).toEqual( - "Validation failed. Invalid type for data. Expected: string" + 'Validation failed. Invalid type for data. Expected: string' ); done(); }); }); - it("set params default", done => { + it('set params default', done => { Parse.Cloud.define( - "hello", + 'hello', req => { - expect(req.params.data).toBe("yolo"); - return "Hello world!"; + expect(req.params.data).toBe('yolo'); + return 'Hello world!'; }, { fields: { data: { type: String, - default: "yolo", + default: 'yolo', }, }, } ); - Parse.Cloud.run("hello") + Parse.Cloud.run('hello') .then(() => { done(); }) .catch(() => { - fail("function should not have failed."); + fail('function should not have failed.'); }); }); - it("set params required", done => { + it('set params required', done => { Parse.Cloud.define( - "hello", + 'hello', req => { - expect(req.params.data).toBe("yolo"); - return "Hello world!"; + expect(req.params.data).toBe('yolo'); + return 'Hello world!'; }, { fields: { @@ -337,25 +337,25 @@ describe("cloud validator", () => { }, } ); - Parse.Cloud.run("hello", {}) + Parse.Cloud.run('hello', {}) .then(() => { - fail("function should have failed."); + fail('function should have failed.'); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); expect(error.message).toEqual( - "Validation failed. Please specify data for data." + 'Validation failed. Please specify data for data.' ); done(); }); }); - it("set params not-required options data", done => { + it('set params not-required options data', done => { Parse.Cloud.define( - "hello", + 'hello', req => { - expect(req.params.data).toBe("abc"); - return "Hello world!"; + expect(req.params.data).toBe('abc'); + return 'Hello world!'; }, { fields: { @@ -366,30 +366,30 @@ describe("cloud validator", () => { return s.length >= 4 && s.length <= 50; }, error: - "Validation failed. Expected length of data to be between 4 and 50.", + 'Validation failed. Expected length of data to be between 4 and 50.', }, }, } ); - Parse.Cloud.run("hello", { data: "abc" }) + Parse.Cloud.run('hello', { data: 'abc' }) .then(() => { - fail("function should have failed."); + fail('function should have failed.'); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); expect(error.message).toEqual( - "Validation failed. Expected length of data to be between 4 and 50." + 'Validation failed. Expected length of data to be between 4 and 50.' ); done(); }); }); - it("set params not-required type", done => { + it('set params not-required type', done => { Parse.Cloud.define( - "hello", + 'hello', req => { expect(req.params.data).toBe(null); - return "Hello world!"; + return 'Hello world!'; }, { fields: { @@ -400,24 +400,24 @@ describe("cloud validator", () => { }, } ); - Parse.Cloud.run("hello", { data: null }) + Parse.Cloud.run('hello', { data: null }) .then(() => { - fail("function should have failed."); + fail('function should have failed.'); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); expect(error.message).toEqual( - "Validation failed. Invalid type for data. Expected: string" + 'Validation failed. Invalid type for data. Expected: string' ); done(); }); }); - it("set params not-required options", done => { + it('set params not-required options', done => { Parse.Cloud.define( - "hello", + 'hello', () => { - return "Hello world!"; + return 'Hello world!'; }, { fields: { @@ -431,20 +431,20 @@ describe("cloud validator", () => { }, } ); - Parse.Cloud.run("hello", {}) + Parse.Cloud.run('hello', {}) .then(() => { done(); }) .catch(() => { - fail("function should not have failed."); + fail('function should not have failed.'); }); }); - it("set params not-required no-options", done => { + it('set params not-required no-options', done => { Parse.Cloud.define( - "hello", + 'hello', () => { - return "Hello world!"; + return 'Hello world!'; }, { fields: { @@ -455,81 +455,81 @@ describe("cloud validator", () => { }, } ); - Parse.Cloud.run("hello", {}) + Parse.Cloud.run('hello', {}) .then(() => { done(); }) .catch(() => { - fail("function should not have failed."); + fail('function should not have failed.'); }); }); - it("set params option", done => { + it('set params option', done => { Parse.Cloud.define( - "hello", + 'hello', req => { - expect(req.params.data).toBe("yolo"); - return "Hello world!"; + expect(req.params.data).toBe('yolo'); + return 'Hello world!'; }, { fields: { data: { type: String, required: true, - options: "a", + options: 'a', }, }, } ); - Parse.Cloud.run("hello", { data: "f" }) + Parse.Cloud.run('hello', { data: 'f' }) .then(() => { - fail("function should have failed."); + fail('function should have failed.'); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); expect(error.message).toEqual( - "Validation failed. Invalid option for data. Expected: a" + 'Validation failed. Invalid option for data. Expected: a' ); done(); }); }); - it("set params options", done => { + it('set params options', done => { Parse.Cloud.define( - "hello", + 'hello', req => { - expect(req.params.data).toBe("yolo"); - return "Hello world!"; + expect(req.params.data).toBe('yolo'); + return 'Hello world!'; }, { fields: { data: { type: String, required: true, - options: ["a", "b"], + options: ['a', 'b'], }, }, } ); - Parse.Cloud.run("hello", { data: "f" }) + Parse.Cloud.run('hello', { data: 'f' }) .then(() => { - fail("function should have failed."); + fail('function should have failed.'); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); expect(error.message).toEqual( - "Validation failed. Invalid option for data. Expected: a, b" + 'Validation failed. Invalid option for data. Expected: a, b' ); done(); }); }); - it("set params options function", done => { + it('set params options function', done => { Parse.Cloud.define( - "hello", + 'hello', () => { - fail("cloud function should not run."); - return "Hello world!"; + fail('cloud function should not run.'); + return 'Hello world!'; }, { fields: { @@ -539,30 +539,30 @@ describe("cloud validator", () => { options: val => { return val > 1 && val < 5; }, - error: "Validation failed. Expected data to be between 1 and 5.", + error: 'Validation failed. Expected data to be between 1 and 5.', }, }, } ); - Parse.Cloud.run("hello", { data: 7 }) + Parse.Cloud.run('hello', { data: 7 }) .then(() => { - fail("function should have failed."); + fail('function should have failed.'); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); expect(error.message).toEqual( - "Validation failed. Expected data to be between 1 and 5." + 'Validation failed. Expected data to be between 1 and 5.' ); done(); }); }); - it("can run params function on null", done => { + it('can run params function on null', done => { Parse.Cloud.define( - "hello", + 'hello', () => { - fail("cloud function should not run."); - return "Hello world!"; + fail('cloud function should not run.'); + return 'Hello world!'; }, { fields: { @@ -570,58 +570,58 @@ describe("cloud validator", () => { options: val => { return val.length > 5; }, - error: "Validation failed. String should be at least 5 characters", + error: 'Validation failed. String should be at least 5 characters', }, }, } ); - Parse.Cloud.run("hello", { data: null }) + Parse.Cloud.run('hello', { data: null }) .then(() => { - fail("function should have failed."); + fail('function should have failed.'); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); expect(error.message).toEqual( - "Validation failed. String should be at least 5 characters" + 'Validation failed. String should be at least 5 characters' ); done(); }); }); - it("can throw from options validator", done => { + it('can throw from options validator', done => { Parse.Cloud.define( - "hello", + 'hello', () => { - fail("cloud function should not run."); - return "Hello world!"; + fail('cloud function should not run.'); + return 'Hello world!'; }, { fields: { data: { options: () => { - throw "validation failed."; + throw 'validation failed.'; }, }, }, } ); - Parse.Cloud.run("hello", { data: "a" }) + Parse.Cloud.run('hello', { data: 'a' }) .then(() => { - fail("function should have failed."); + fail('function should have failed.'); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual("validation failed."); + expect(error.message).toEqual('validation failed.'); done(); }); }); - it("can throw null from options validator", done => { + it('can throw null from options validator', done => { Parse.Cloud.define( - "hello", + 'hello', () => { - fail("cloud function should not run."); - return "Hello world!"; + fail('cloud function should not run.'); + return 'Hello world!'; }, { fields: { @@ -633,24 +633,24 @@ describe("cloud validator", () => { }, } ); - Parse.Cloud.run("hello", { data: "a" }) + Parse.Cloud.run('hello', { data: 'a' }) .then(() => { - fail("function should have failed."); + fail('function should have failed.'); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); expect(error.message).toEqual( - "Validation failed. Invalid value for data." + 'Validation failed. Invalid value for data.' ); done(); }); }); - it("can create functions", done => { + it('can create functions', done => { Parse.Cloud.define( - "hello", + 'hello', () => { - return "Hello world!"; + return 'Hello world!'; }, { requireUser: false, @@ -661,242 +661,242 @@ describe("cloud validator", () => { }, data1: { type: String, - default: "default", + default: 'default', }, }, } ); - Parse.Cloud.run("hello", { data: "str" }).then(result => { - expect(result).toEqual("Hello world!"); + Parse.Cloud.run('hello', { data: 'str' }).then(result => { + expect(result).toEqual('Hello world!'); done(); }); }); - it("basic beforeSave requireUserKey", async function (done) { - Parse.Cloud.beforeSave("BeforeSaveFail", () => {}, { + it('basic beforeSave requireUserKey', async function (done) { + Parse.Cloud.beforeSave('BeforeSaveFail', () => {}, { requireUser: true, - requireUserKeys: ["name"], + requireUserKeys: ['name'], }); - const user = await Parse.User.signUp("testuser", "p@ssword"); - user.set("name", "foo"); + const user = await Parse.User.signUp('testuser', 'p@ssword'); + user.set('name', 'foo'); await user.save(null, { sessionToken: user.getSessionToken() }); - const obj = new Parse.Object("BeforeSaveFail"); - obj.set("foo", "bar"); + const obj = new Parse.Object('BeforeSaveFail'); + obj.set('foo', 'bar'); await obj.save(null, { sessionToken: user.getSessionToken() }); - expect(obj.get("foo")).toBe("bar"); + expect(obj.get('foo')).toBe('bar'); done(); }); - it("basic beforeSave skipWithMasterKey", async function (done) { + it('basic beforeSave skipWithMasterKey', async function (done) { Parse.Cloud.beforeSave( - "BeforeSave", + 'BeforeSave', () => { - throw "before save should have resolved using masterKey."; + throw 'before save should have resolved using masterKey.'; }, { skipWithMasterKey: true, } ); - const obj = new Parse.Object("BeforeSave"); - obj.set("foo", "bar"); + const obj = new Parse.Object('BeforeSave'); + obj.set('foo', 'bar'); await obj.save(null, { useMasterKey: true }); - expect(obj.get("foo")).toBe("bar"); + expect(obj.get('foo')).toBe('bar'); done(); }); - it("basic beforeFind skipWithMasterKey", async function (done) { + it('basic beforeFind skipWithMasterKey', async function (done) { Parse.Cloud.beforeFind( - "beforeFind", + 'beforeFind', () => { - throw "before find should have resolved using masterKey."; + throw 'before find should have resolved using masterKey.'; }, { skipWithMasterKey: true, } ); - const obj = new Parse.Object("beforeFind"); - obj.set("foo", "bar"); + const obj = new Parse.Object('beforeFind'); + obj.set('foo', 'bar'); await obj.save(); - expect(obj.get("foo")).toBe("bar"); + expect(obj.get('foo')).toBe('bar'); - const query = new Parse.Query("beforeFind"); + const query = new Parse.Query('beforeFind'); const first = await query.first({ useMasterKey: true }); expect(first).toBeDefined(); expect(first.id).toBe(obj.id); done(); }); - it("basic beforeDelete skipWithMasterKey", async function (done) { + it('basic beforeDelete skipWithMasterKey', async function (done) { Parse.Cloud.beforeDelete( - "beforeFind", + 'beforeFind', () => { - throw "before find should have resolved using masterKey."; + throw 'before find should have resolved using masterKey.'; }, { skipWithMasterKey: true, } ); - const obj = new Parse.Object("beforeFind"); - obj.set("foo", "bar"); + const obj = new Parse.Object('beforeFind'); + obj.set('foo', 'bar'); await obj.save(); - expect(obj.get("foo")).toBe("bar"); + expect(obj.get('foo')).toBe('bar'); await obj.destroy({ useMasterKey: true }); done(); }); - it("basic beforeSaveFile skipWithMasterKey", async done => { + it('basic beforeSaveFile skipWithMasterKey', async done => { Parse.Cloud.beforeSave( Parse.File, () => { - throw "beforeSaveFile should have resolved using master key."; + throw 'beforeSaveFile should have resolved using master key.'; }, { skipWithMasterKey: true, } ); - const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); + const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); const result = await file.save({ useMasterKey: true }); expect(result).toBe(file); done(); }); - it_id("893eec0c-41bd-4adf-8f0a-306087ad8d61")(it)( - "basic beforeSave Parse.Config skipWithMasterKey", + it_id('893eec0c-41bd-4adf-8f0a-306087ad8d61')(it)( + 'basic beforeSave Parse.Config skipWithMasterKey', async () => { Parse.Cloud.beforeSave( Parse.Config, () => { - throw "beforeSaveFile should have resolved using master key."; + throw 'beforeSaveFile should have resolved using master key.'; }, { skipWithMasterKey: true, } ); const config = await testConfig(); - expect(config.get("internal")).toBe("i"); - expect(config.get("string")).toBe("s"); - expect(config.get("number")).toBe(12); + expect(config.get('internal')).toBe('i'); + expect(config.get('string')).toBe('s'); + expect(config.get('number')).toBe(12); } ); - it_id("91e739a4-6a38-405c-8f83-f36d48220734")(it)( - "basic afterSave Parse.Config skipWithMasterKey", + it_id('91e739a4-6a38-405c-8f83-f36d48220734')(it)( + 'basic afterSave Parse.Config skipWithMasterKey', async () => { Parse.Cloud.afterSave( Parse.Config, () => { - throw "beforeSaveFile should have resolved using master key."; + throw 'beforeSaveFile should have resolved using master key.'; }, { skipWithMasterKey: true, } ); const config = await testConfig(); - expect(config.get("internal")).toBe("i"); - expect(config.get("string")).toBe("s"); - expect(config.get("number")).toBe(12); + expect(config.get('internal')).toBe('i'); + expect(config.get('string')).toBe('s'); + expect(config.get('number')).toBe(12); } ); - it("beforeSave validateMasterKey and skipWithMasterKey fail", async function (done) { + it('beforeSave validateMasterKey and skipWithMasterKey fail', async function (done) { Parse.Cloud.beforeSave( - "BeforeSave", + 'BeforeSave', () => { - throw "beforeSaveFile should have resolved using master key."; + throw 'beforeSaveFile should have resolved using master key.'; }, { - fields: ["foo"], + fields: ['foo'], validateMasterKey: true, skipWithMasterKey: true, } ); - const obj = new Parse.Object("BeforeSave"); + const obj = new Parse.Object('BeforeSave'); try { await obj.save(null, { useMasterKey: true }); - fail("function should have failed."); + fail('function should have failed.'); } catch (error) { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); expect(error.message).toEqual( - "Validation failed. Please specify data for foo." + 'Validation failed. Please specify data for foo.' ); done(); } }); - it("beforeSave validateMasterKey and skipWithMasterKey success", async function (done) { + it('beforeSave validateMasterKey and skipWithMasterKey success', async function (done) { Parse.Cloud.beforeSave( - "BeforeSave", + 'BeforeSave', () => { - throw "beforeSaveFile should have resolved using master key."; + throw 'beforeSaveFile should have resolved using master key.'; }, { - fields: ["foo"], + fields: ['foo'], validateMasterKey: true, skipWithMasterKey: true, } ); - const obj = new Parse.Object("BeforeSave"); - obj.set("foo", "bar"); + const obj = new Parse.Object('BeforeSave'); + obj.set('foo', 'bar'); try { await obj.save(null, { useMasterKey: true }); done(); } catch (error) { - fail("error should not have been called."); + fail('error should not have been called.'); } }); - it("basic beforeSave requireUserKey on User Class", async function (done) { + it('basic beforeSave requireUserKey on User Class', async function (done) { Parse.Cloud.beforeSave(Parse.User, () => {}, { requireUser: true, - requireUserKeys: ["name"], + requireUserKeys: ['name'], }); const user = new Parse.User(); - user.set("username", "testuser"); - user.set("password", "p@ssword"); - user.set("name", "foo"); - expect(user.get("name")).toBe("foo"); + user.set('username', 'testuser'); + user.set('password', 'p@ssword'); + user.set('name', 'foo'); + expect(user.get('name')).toBe('foo'); done(); }); - it("basic beforeSave requireUserKey rejection", async function (done) { - Parse.Cloud.beforeSave("BeforeSaveFail", () => {}, { + it('basic beforeSave requireUserKey rejection', async function (done) { + Parse.Cloud.beforeSave('BeforeSaveFail', () => {}, { requireUser: true, - requireUserKeys: ["name"], + requireUserKeys: ['name'], }); - const user = await Parse.User.signUp("testuser", "p@ssword"); - const obj = new Parse.Object("BeforeSaveFail"); - obj.set("foo", "bar"); + const user = await Parse.User.signUp('testuser', 'p@ssword'); + const obj = new Parse.Object('BeforeSaveFail'); + obj.set('foo', 'bar'); try { await obj.save(null, { sessionToken: user.getSessionToken() }); - fail("should not have been able to save without userkey"); + fail('should not have been able to save without userkey'); } catch (error) { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); expect(error.message).toEqual( - "Validation failed. Please set data for name on your account." + 'Validation failed. Please set data for name on your account.' ); done(); } }); - it("basic beforeSave requireUserKey without user", async function (done) { - Parse.Cloud.beforeSave("BeforeSaveFail", () => {}, { - requireUserKeys: ["name"], + it('basic beforeSave requireUserKey without user', async function (done) { + Parse.Cloud.beforeSave('BeforeSaveFail', () => {}, { + requireUserKeys: ['name'], }); - const obj = new Parse.Object("BeforeSaveFail"); - obj.set("foo", "bar"); + const obj = new Parse.Object('BeforeSaveFail'); + obj.set('foo', 'bar'); try { await obj.save(); - fail("should not have been able to save without user"); + fail('should not have been able to save without user'); } catch (error) { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual("Please login to make this request."); + expect(error.message).toEqual('Please login to make this request.'); done(); } }); - it("basic beforeSave requireUserKey as admin", async function (done) { + it('basic beforeSave requireUserKey as admin', async function (done) { Parse.Cloud.beforeSave(Parse.User, () => {}, { fields: { admin: { @@ -906,7 +906,7 @@ describe("cloud validator", () => { }, }); Parse.Cloud.define( - "secureFunction", + 'secureFunction', () => { return "Here's all the secure data!"; }, @@ -914,38 +914,38 @@ describe("cloud validator", () => { requireUserKeys: { admin: { options: true, - error: "Unauthorized.", + error: 'Unauthorized.', }, }, } ); const user = new Parse.User(); - user.set("username", "testuser"); - user.set("password", "p@ssword"); - user.set("admin", true); + user.set('username', 'testuser'); + user.set('password', 'p@ssword'); + user.set('admin', true); await user.signUp(); - expect(user.get("admin")).toBe(false); + expect(user.get('admin')).toBe(false); try { - await Parse.Cloud.run("secureFunction"); - fail("function should only be available to admin users"); + await Parse.Cloud.run('secureFunction'); + fail('function should only be available to admin users'); } catch (error) { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual("Unauthorized."); + expect(error.message).toEqual('Unauthorized.'); } done(); }); - it("basic beforeSave requireUserKey as custom function", async function (done) { + it('basic beforeSave requireUserKey as custom function', async function (done) { Parse.Cloud.beforeSave(Parse.User, () => {}, { fields: { accType: { - default: "normal", + default: 'normal', constant: true, }, }, }); Parse.Cloud.define( - "secureFunction", + 'secureFunction', () => { return "Here's all the secure data!"; }, @@ -953,40 +953,40 @@ describe("cloud validator", () => { requireUserKeys: { accType: { options: val => { - return ["admin", "admin2"].includes(val); + return ['admin', 'admin2'].includes(val); }, - error: "Unauthorized.", + error: 'Unauthorized.', }, }, } ); const user = new Parse.User(); - user.set("username", "testuser"); - user.set("password", "p@ssword"); - user.set("accType", "admin"); + user.set('username', 'testuser'); + user.set('password', 'p@ssword'); + user.set('accType', 'admin'); await user.signUp(); - expect(user.get("accType")).toBe("normal"); + expect(user.get('accType')).toBe('normal'); try { - await Parse.Cloud.run("secureFunction"); - fail("function should only be available to admin users"); + await Parse.Cloud.run('secureFunction'); + fail('function should only be available to admin users'); } catch (error) { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual("Unauthorized."); + expect(error.message).toEqual('Unauthorized.'); } done(); }); - it("basic beforeSave allow requireUserKey as custom function", async function (done) { + it('basic beforeSave allow requireUserKey as custom function', async function (done) { Parse.Cloud.beforeSave(Parse.User, () => {}, { fields: { accType: { - default: "admin", + default: 'admin', constant: true, }, }, }); Parse.Cloud.define( - "secureFunction", + 'secureFunction', () => { return "Here's all the secure data!"; }, @@ -994,254 +994,254 @@ describe("cloud validator", () => { requireUserKeys: { accType: { options: val => { - return ["admin", "admin2"].includes(val); + return ['admin', 'admin2'].includes(val); }, - error: "Unauthorized.", + error: 'Unauthorized.', }, }, } ); const user = new Parse.User(); - user.set("username", "testuser"); - user.set("password", "p@ssword"); + user.set('username', 'testuser'); + user.set('password', 'p@ssword'); await user.signUp(); - expect(user.get("accType")).toBe("admin"); - const result = await Parse.Cloud.run("secureFunction"); + expect(user.get('accType')).toBe('admin'); + const result = await Parse.Cloud.run('secureFunction'); expect(result).toBe("Here's all the secure data!"); done(); }); - it("basic beforeSave requireUser", function (done) { - Parse.Cloud.beforeSave("BeforeSaveFail", () => {}, { + it('basic beforeSave requireUser', function (done) { + Parse.Cloud.beforeSave('BeforeSaveFail', () => {}, { requireUser: true, }); - const obj = new Parse.Object("BeforeSaveFail"); - obj.set("foo", "bar"); + const obj = new Parse.Object('BeforeSaveFail'); + obj.set('foo', 'bar'); obj .save() .then(() => { - fail("function should have failed."); + fail('function should have failed.'); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); expect(error.message).toEqual( - "Validation failed. Please login to continue." + 'Validation failed. Please login to continue.' ); done(); }); }); - it("basic validator requireAnyUserRoles", async function (done) { + it('basic validator requireAnyUserRoles', async function (done) { Parse.Cloud.define( - "cloudFunction", + 'cloudFunction', () => { return true; }, { requireUser: true, - requireAnyUserRoles: ["Admin"], + requireAnyUserRoles: ['Admin'], } ); - const user = await Parse.User.signUp("testuser", "p@ssword"); + const user = await Parse.User.signUp('testuser', 'p@ssword'); try { - await Parse.Cloud.run("cloudFunction"); - fail("cloud validator should have failed."); + await Parse.Cloud.run('cloudFunction'); + fail('cloud validator should have failed.'); } catch (e) { expect(e.message).toBe( - "Validation failed. User does not match the required roles." + 'Validation failed. User does not match the required roles.' ); } const roleACL = new Parse.ACL(); roleACL.setPublicReadAccess(true); - const role = new Parse.Role("Admin", roleACL); + const role = new Parse.Role('Admin', roleACL); role.getUsers().add(user); await role.save({ useMasterKey: true }); - await Parse.Cloud.run("cloudFunction"); + await Parse.Cloud.run('cloudFunction'); done(); }); - it("basic validator requireAllUserRoles", async function (done) { + it('basic validator requireAllUserRoles', async function (done) { Parse.Cloud.define( - "cloudFunction", + 'cloudFunction', () => { return true; }, { requireUser: true, - requireAllUserRoles: ["Admin", "Admin2"], + requireAllUserRoles: ['Admin', 'Admin2'], } ); - const user = await Parse.User.signUp("testuser", "p@ssword"); + const user = await Parse.User.signUp('testuser', 'p@ssword'); try { - await Parse.Cloud.run("cloudFunction"); - fail("cloud validator should have failed."); + await Parse.Cloud.run('cloudFunction'); + fail('cloud validator should have failed.'); } catch (e) { expect(e.message).toBe( - "Validation failed. User does not match all the required roles." + 'Validation failed. User does not match all the required roles.' ); } const roleACL = new Parse.ACL(); roleACL.setPublicReadAccess(true); - const role = new Parse.Role("Admin", roleACL); + const role = new Parse.Role('Admin', roleACL); role.getUsers().add(user); - const role2 = new Parse.Role("Admin2", roleACL); + const role2 = new Parse.Role('Admin2', roleACL); role2.getUsers().add(user); await role.save({ useMasterKey: true }); await role2.save({ useMasterKey: true }); - await Parse.Cloud.run("cloudFunction"); + await Parse.Cloud.run('cloudFunction'); done(); }); - it("allow requireAnyUserRoles to be a function", async function (done) { + it('allow requireAnyUserRoles to be a function', async function (done) { Parse.Cloud.define( - "cloudFunction", + 'cloudFunction', () => { return true; }, { requireUser: true, requireAnyUserRoles: () => { - return ["Admin Func"]; + return ['Admin Func']; }, } ); - const user = await Parse.User.signUp("testuser", "p@ssword"); + const user = await Parse.User.signUp('testuser', 'p@ssword'); try { - await Parse.Cloud.run("cloudFunction"); - fail("cloud validator should have failed."); + await Parse.Cloud.run('cloudFunction'); + fail('cloud validator should have failed.'); } catch (e) { expect(e.message).toBe( - "Validation failed. User does not match the required roles." + 'Validation failed. User does not match the required roles.' ); } const roleACL = new Parse.ACL(); roleACL.setPublicReadAccess(true); - const role = new Parse.Role("Admin Func", roleACL); + const role = new Parse.Role('Admin Func', roleACL); role.getUsers().add(user); await role.save({ useMasterKey: true }); - await Parse.Cloud.run("cloudFunction"); + await Parse.Cloud.run('cloudFunction'); done(); }); - it("allow requireAllUserRoles to be a function", async function (done) { + it('allow requireAllUserRoles to be a function', async function (done) { Parse.Cloud.define( - "cloudFunction", + 'cloudFunction', () => { return true; }, { requireUser: true, requireAllUserRoles: () => { - return ["AdminA", "AdminB"]; + return ['AdminA', 'AdminB']; }, } ); - const user = await Parse.User.signUp("testuser", "p@ssword"); + const user = await Parse.User.signUp('testuser', 'p@ssword'); try { - await Parse.Cloud.run("cloudFunction"); - fail("cloud validator should have failed."); + await Parse.Cloud.run('cloudFunction'); + fail('cloud validator should have failed.'); } catch (e) { expect(e.message).toBe( - "Validation failed. User does not match all the required roles." + 'Validation failed. User does not match all the required roles.' ); } const roleACL = new Parse.ACL(); roleACL.setPublicReadAccess(true); - const role = new Parse.Role("AdminA", roleACL); + const role = new Parse.Role('AdminA', roleACL); role.getUsers().add(user); - const role2 = new Parse.Role("AdminB", roleACL); + const role2 = new Parse.Role('AdminB', roleACL); role2.getUsers().add(user); await role.save({ useMasterKey: true }); await role2.save({ useMasterKey: true }); - await Parse.Cloud.run("cloudFunction"); + await Parse.Cloud.run('cloudFunction'); done(); }); - it("basic requireAllUserRoles but no user", async function (done) { + it('basic requireAllUserRoles but no user', async function (done) { Parse.Cloud.define( - "cloudFunction", + 'cloudFunction', () => { return true; }, { - requireAllUserRoles: ["Admin"], + requireAllUserRoles: ['Admin'], } ); try { - await Parse.Cloud.run("cloudFunction"); - fail("cloud validator should have failed."); + await Parse.Cloud.run('cloudFunction'); + fail('cloud validator should have failed.'); } catch (e) { - expect(e.message).toBe("Validation failed. Please login to continue."); + expect(e.message).toBe('Validation failed. Please login to continue.'); } - const user = await Parse.User.signUp("testuser", "p@ssword"); + const user = await Parse.User.signUp('testuser', 'p@ssword'); const roleACL = new Parse.ACL(); roleACL.setPublicReadAccess(true); - const role = new Parse.Role("Admin", roleACL); + const role = new Parse.Role('Admin', roleACL); role.getUsers().add(user); await role.save({ useMasterKey: true }); - await Parse.Cloud.run("cloudFunction"); + await Parse.Cloud.run('cloudFunction'); done(); }); - it("basic beforeSave requireMaster", function (done) { - Parse.Cloud.beforeSave("BeforeSaveFail", () => {}, { + it('basic beforeSave requireMaster', function (done) { + Parse.Cloud.beforeSave('BeforeSaveFail', () => {}, { requireMaster: true, }); - const obj = new Parse.Object("BeforeSaveFail"); - obj.set("foo", "bar"); + const obj = new Parse.Object('BeforeSaveFail'); + obj.set('foo', 'bar'); obj .save() .then(() => { - fail("function should have failed."); + fail('function should have failed.'); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); expect(error.message).toEqual( - "Validation failed. Master key is required to complete this request." + 'Validation failed. Master key is required to complete this request.' ); done(); }); }); - it("basic beforeSave master", async function (done) { - Parse.Cloud.beforeSave("BeforeSaveFail", () => {}, { + it('basic beforeSave master', async function (done) { + Parse.Cloud.beforeSave('BeforeSaveFail', () => {}, { requireUser: true, }); - const obj = new Parse.Object("BeforeSaveFail"); - obj.set("foo", "bar"); + const obj = new Parse.Object('BeforeSaveFail'); + obj.set('foo', 'bar'); await obj.save(null, { useMasterKey: true }); done(); }); - it("basic beforeSave validateMasterKey", function (done) { - Parse.Cloud.beforeSave("BeforeSaveFail", () => {}, { + it('basic beforeSave validateMasterKey', function (done) { + Parse.Cloud.beforeSave('BeforeSaveFail', () => {}, { requireUser: true, validateMasterKey: true, }); - const obj = new Parse.Object("BeforeSaveFail"); - obj.set("foo", "bar"); + const obj = new Parse.Object('BeforeSaveFail'); + obj.set('foo', 'bar'); obj .save(null, { useMasterKey: true }) .then(() => { - fail("function should have failed."); + fail('function should have failed.'); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); expect(error.message).toEqual( - "Validation failed. Please login to continue." + 'Validation failed. Please login to continue.' ); done(); }); }); - it("basic beforeSave requireKeys", function (done) { - Parse.Cloud.beforeSave("beforeSaveRequire", () => {}, { + it('basic beforeSave requireKeys', function (done) { + Parse.Cloud.beforeSave('beforeSaveRequire', () => {}, { fields: { foo: { required: true, @@ -1251,113 +1251,113 @@ describe("cloud validator", () => { }, }, }); - const obj = new Parse.Object("beforeSaveRequire"); - obj.set("foo", "bar"); + const obj = new Parse.Object('beforeSaveRequire'); + obj.set('foo', 'bar'); obj .save() .then(() => { - fail("function should have failed."); + fail('function should have failed.'); }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); expect(error.message).toEqual( - "Validation failed. Please specify data for bar." + 'Validation failed. Please specify data for bar.' ); done(); }); }); - it("basic beforeSave constantKeys", async function (done) { - Parse.Cloud.beforeSave("BeforeSave", () => {}, { + it('basic beforeSave constantKeys', async function (done) { + Parse.Cloud.beforeSave('BeforeSave', () => {}, { fields: { foo: { constant: true, - default: "bar", + default: 'bar', }, }, }); - const obj = new Parse.Object("BeforeSave"); - obj.set("foo", "far"); + const obj = new Parse.Object('BeforeSave'); + obj.set('foo', 'far'); await obj.save(); - expect(obj.get("foo")).toBe("bar"); - obj.set("foo", "yolo"); + expect(obj.get('foo')).toBe('bar'); + obj.set('foo', 'yolo'); await obj.save(); - expect(obj.get("foo")).toBe("bar"); + expect(obj.get('foo')).toBe('bar'); done(); }); - it("basic beforeSave defaultKeys", async function (done) { - Parse.Cloud.beforeSave("BeforeSave", () => {}, { + it('basic beforeSave defaultKeys', async function (done) { + Parse.Cloud.beforeSave('BeforeSave', () => {}, { fields: { foo: { - default: "bar", + default: 'bar', }, }, }); - const obj = new Parse.Object("BeforeSave"); + const obj = new Parse.Object('BeforeSave'); await obj.save(); - expect(obj.get("foo")).toBe("bar"); - obj.set("foo", "yolo"); + expect(obj.get('foo')).toBe('bar'); + obj.set('foo', 'yolo'); await obj.save(); - expect(obj.get("foo")).toBe("yolo"); + expect(obj.get('foo')).toBe('yolo'); done(); }); - it("validate beforeSave", async done => { - Parse.Cloud.beforeSave("MyObject", () => {}, validatorSuccess); + it('validate beforeSave', async done => { + Parse.Cloud.beforeSave('MyObject', () => {}, validatorSuccess); - const MyObject = Parse.Object.extend("MyObject"); + const MyObject = Parse.Object.extend('MyObject'); const myObject = new MyObject(); try { await myObject.save(); done(); } catch (e) { - fail("before save should not have failed."); + fail('before save should not have failed.'); } }); - it("validate beforeSave fail", async done => { - Parse.Cloud.beforeSave("MyObject", () => {}, validatorFail); + it('validate beforeSave fail', async done => { + Parse.Cloud.beforeSave('MyObject', () => {}, validatorFail); - const MyObject = Parse.Object.extend("MyObject"); + const MyObject = Parse.Object.extend('MyObject'); const myObject = new MyObject(); try { await myObject.save(); - fail("cloud function should have failed."); + fail('cloud function should have failed.'); } catch (e) { expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); done(); } }); - it("validate afterSave", async done => { + it('validate afterSave', async done => { Parse.Cloud.afterSave( - "MyObject", + 'MyObject', () => { done(); }, validatorSuccess ); - const MyObject = Parse.Object.extend("MyObject"); + const MyObject = Parse.Object.extend('MyObject'); const myObject = new MyObject(); try { await myObject.save(); } catch (e) { - fail("before save should not have failed."); + fail('before save should not have failed.'); } }); - it("validate afterSave fail", async done => { + it('validate afterSave fail', async done => { Parse.Cloud.afterSave( - "MyObject", + 'MyObject', () => { - fail("this should not be called."); + fail('this should not be called.'); }, validatorFail ); - const MyObject = Parse.Object.extend("MyObject"); + const MyObject = Parse.Object.extend('MyObject'); const myObject = new MyObject(); await myObject.save(); setTimeout(() => { @@ -1365,109 +1365,109 @@ describe("cloud validator", () => { }, 1000); }); - it("validate beforeDelete", async done => { - Parse.Cloud.beforeDelete("MyObject", () => {}, validatorSuccess); + it('validate beforeDelete', async done => { + Parse.Cloud.beforeDelete('MyObject', () => {}, validatorSuccess); - const MyObject = Parse.Object.extend("MyObject"); + const MyObject = Parse.Object.extend('MyObject'); const myObject = new MyObject(); await myObject.save(); try { await myObject.destroy(); done(); } catch (e) { - fail("before delete should not have failed."); + fail('before delete should not have failed.'); } }); - it("validate beforeDelete fail", async done => { + it('validate beforeDelete fail', async done => { Parse.Cloud.beforeDelete( - "MyObject", + 'MyObject', () => { - fail("this should not be called."); + fail('this should not be called.'); }, validatorFail ); - const MyObject = Parse.Object.extend("MyObject"); + const MyObject = Parse.Object.extend('MyObject'); const myObject = new MyObject(); await myObject.save(); try { await myObject.destroy(); - fail("cloud function should have failed."); + fail('cloud function should have failed.'); } catch (e) { expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); done(); } }); - it("validate afterDelete", async done => { + it('validate afterDelete', async done => { Parse.Cloud.afterDelete( - "MyObject", + 'MyObject', () => { done(); }, validatorSuccess ); - const MyObject = Parse.Object.extend("MyObject"); + const MyObject = Parse.Object.extend('MyObject'); const myObject = new MyObject(); await myObject.save(); try { await myObject.destroy(); } catch (e) { - fail("after delete should not have failed."); + fail('after delete should not have failed.'); } }); - it("validate afterDelete fail", async done => { + it('validate afterDelete fail', async done => { Parse.Cloud.afterDelete( - "MyObject", + 'MyObject', () => { - fail("this should not be called."); + fail('this should not be called.'); }, validatorFail ); - const MyObject = Parse.Object.extend("MyObject"); + const MyObject = Parse.Object.extend('MyObject'); const myObject = new MyObject(); await myObject.save(); try { await myObject.destroy(); - fail("cloud function should have failed."); + fail('cloud function should have failed.'); } catch (e) { expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); done(); } }); - it("validate beforeFind", async done => { - Parse.Cloud.beforeFind("MyObject", () => {}, validatorSuccess); + it('validate beforeFind', async done => { + Parse.Cloud.beforeFind('MyObject', () => {}, validatorSuccess); try { - const MyObject = Parse.Object.extend("MyObject"); + const MyObject = Parse.Object.extend('MyObject'); const myObjectQuery = new Parse.Query(MyObject); await myObjectQuery.find(); done(); } catch (e) { - fail("beforeFind should not have failed."); + fail('beforeFind should not have failed.'); } }); - it("validate beforeFind fail", async done => { - Parse.Cloud.beforeFind("MyObject", () => {}, validatorFail); + it('validate beforeFind fail', async done => { + Parse.Cloud.beforeFind('MyObject', () => {}, validatorFail); try { - const MyObject = Parse.Object.extend("MyObject"); + const MyObject = Parse.Object.extend('MyObject'); const myObjectQuery = new Parse.Query(MyObject); await myObjectQuery.find(); - fail("cloud function should have failed."); + fail('cloud function should have failed.'); } catch (e) { expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); done(); } }); - it("validate afterFind", async done => { - Parse.Cloud.afterFind("MyObject", () => {}, validatorSuccess); + it('validate afterFind', async done => { + Parse.Cloud.afterFind('MyObject', () => {}, validatorSuccess); - const MyObject = Parse.Object.extend("MyObject"); + const MyObject = Parse.Object.extend('MyObject'); const myObject = new MyObject(); await myObject.save(); try { @@ -1475,178 +1475,178 @@ describe("cloud validator", () => { await myObjectQuery.find(); done(); } catch (e) { - fail("beforeFind should not have failed."); + fail('beforeFind should not have failed.'); } }); - it("validate afterFind fail", async done => { - Parse.Cloud.afterFind("MyObject", () => {}, validatorFail); + it('validate afterFind fail', async done => { + Parse.Cloud.afterFind('MyObject', () => {}, validatorFail); - const MyObject = Parse.Object.extend("MyObject"); + const MyObject = Parse.Object.extend('MyObject'); const myObject = new MyObject(); await myObject.save(); try { const myObjectQuery = new Parse.Query(MyObject); await myObjectQuery.find(); - fail("cloud function should have failed."); + fail('cloud function should have failed.'); } catch (e) { expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); done(); } }); - it("validate beforeSaveFile", async done => { + it('validate beforeSaveFile', async done => { Parse.Cloud.beforeSave(Parse.File, () => {}, validatorSuccess); - const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); + const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); const result = await file.save({ useMasterKey: true }); expect(result).toBe(file); done(); }); - it("validate beforeSaveFile fail", async done => { + it('validate beforeSaveFile fail', async done => { Parse.Cloud.beforeSave(Parse.File, () => {}, validatorFail); try { - const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); + const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); await file.save({ useMasterKey: true }); - fail("cloud function should have failed."); + fail('cloud function should have failed.'); } catch (e) { expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); done(); } }); - it("validate afterSaveFile", async done => { + it('validate afterSaveFile', async done => { Parse.Cloud.afterSave(Parse.File, () => {}, validatorSuccess); - const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); + const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); const result = await file.save({ useMasterKey: true }); expect(result).toBe(file); done(); }); - it("validate afterSaveFile fail", async done => { + it('validate afterSaveFile fail', async done => { Parse.Cloud.afterSave(Parse.File, () => {}, validatorFail); try { - const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); + const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); await file.save({ useMasterKey: true }); - fail("cloud function should have failed."); + fail('cloud function should have failed.'); } catch (e) { expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); done(); } }); - it("validate beforeDeleteFile", async done => { + it('validate beforeDeleteFile', async done => { Parse.Cloud.beforeDelete(Parse.File, () => {}, validatorSuccess); - const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); + const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); await file.save(); await file.destroy(); done(); }); - it("validate beforeDeleteFile fail", async done => { + it('validate beforeDeleteFile fail', async done => { Parse.Cloud.beforeDelete(Parse.File, () => {}, validatorFail); try { - const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); + const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); await file.save(); await file.destroy(); - fail("cloud function should have failed."); + fail('cloud function should have failed.'); } catch (e) { expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); done(); } }); - it("validate afterDeleteFile", async done => { + it('validate afterDeleteFile', async done => { Parse.Cloud.afterDelete(Parse.File, () => {}, validatorSuccess); - const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); + const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); await file.save(); await file.destroy(); done(); }); - it("validate afterDeleteFile fail", async done => { + it('validate afterDeleteFile fail', async done => { Parse.Cloud.afterDelete(Parse.File, () => {}, validatorFail); try { - const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); + const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); await file.save(); await file.destroy(); - fail("cloud function should have failed."); + fail('cloud function should have failed.'); } catch (e) { expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); done(); } }); - it_id("32ca1a99-7f2b-429d-a7cf-62b6661d0af6")(it)( - "validate beforeSave Parse.Config", + it_id('32ca1a99-7f2b-429d-a7cf-62b6661d0af6')(it)( + 'validate beforeSave Parse.Config', async () => { Parse.Cloud.beforeSave(Parse.Config, () => {}, validatorSuccess); const config = await testConfig(); - expect(config.get("internal")).toBe("i"); - expect(config.get("string")).toBe("s"); - expect(config.get("number")).toBe(12); + expect(config.get('internal')).toBe('i'); + expect(config.get('string')).toBe('s'); + expect(config.get('number')).toBe(12); } ); - it_id("c84d11e7-d09c-4843-ad98-f671511bf612")(it)( - "validate beforeSave Parse.Config fail", + it_id('c84d11e7-d09c-4843-ad98-f671511bf612')(it)( + 'validate beforeSave Parse.Config fail', async () => { Parse.Cloud.beforeSave(Parse.Config, () => {}, validatorFail); try { await testConfig(); - fail("cloud function should have failed."); + fail('cloud function should have failed.'); } catch (e) { expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); } } ); - it_id("b18b9a6a-0e35-4b60-9771-30f53501df3c")(it)( - "validate afterSave Parse.Config", + it_id('b18b9a6a-0e35-4b60-9771-30f53501df3c')(it)( + 'validate afterSave Parse.Config', async () => { Parse.Cloud.afterSave(Parse.Config, () => {}, validatorSuccess); const config = await testConfig(); - expect(config.get("internal")).toBe("i"); - expect(config.get("string")).toBe("s"); - expect(config.get("number")).toBe(12); + expect(config.get('internal')).toBe('i'); + expect(config.get('string')).toBe('s'); + expect(config.get('number')).toBe(12); } ); - it_id("ef761222-1758-4614-b984-da84d73fc10c")(it)( - "validate afterSave Parse.Config fail", + it_id('ef761222-1758-4614-b984-da84d73fc10c')(it)( + 'validate afterSave Parse.Config fail', async () => { Parse.Cloud.afterSave(Parse.Config, () => {}, validatorFail); try { await testConfig(); - fail("cloud function should have failed."); + fail('cloud function should have failed.'); } catch (e) { expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); } } ); - it("Should have validator", async done => { + it('Should have validator', async done => { Parse.Cloud.define( - "myFunction", + 'myFunction', () => {}, () => { - throw "error"; + throw 'error'; } ); try { - await Parse.Cloud.run("myFunction"); + await Parse.Cloud.run('myFunction'); } catch (e) { expect(e.code).toBe(Parse.Error.VALIDATION_ERROR); done(); } }); - it("does not log on valid config", () => { - Parse.Cloud.define("myFunction", () => {}, { + it('does not log on valid config', () => { + Parse.Cloud.define('myFunction', () => {}, { requireUser: true, requireMaster: true, validateMasterKey: false, @@ -1654,85 +1654,85 @@ describe("cloud validator", () => { requireUserKeys: { Acc: { constant: true, - options: ["A", "B"], + options: ['A', 'B'], required: true, - default: "f", - error: "a", + default: 'f', + error: 'a', type: String, }, }, fields: { Acc: { constant: true, - options: ["A", "B"], + options: ['A', 'B'], required: true, - default: "f", - error: "a", + default: 'f', + error: 'a', type: String, }, }, }); }); - it("Logs on invalid config", () => { + it('Logs on invalid config', () => { const fields = [ { - field: "requiredUser", + field: 'requiredUser', value: true, error: - "requiredUser is not a supported parameter for Cloud Function validations.", + 'requiredUser is not a supported parameter for Cloud Function validations.', }, { - field: "requireUser", + field: 'requireUser', value: [], error: - "Invalid type for Cloud Function validation key requireUser. Expected boolean, actual array", + 'Invalid type for Cloud Function validation key requireUser. Expected boolean, actual array', }, { - field: "requireMaster", + field: 'requireMaster', value: [], error: - "Invalid type for Cloud Function validation key requireMaster. Expected boolean, actual array", + 'Invalid type for Cloud Function validation key requireMaster. Expected boolean, actual array', }, { - field: "validateMasterKey", + field: 'validateMasterKey', value: [], error: - "Invalid type for Cloud Function validation key validateMasterKey. Expected boolean, actual array", + 'Invalid type for Cloud Function validation key validateMasterKey. Expected boolean, actual array', }, { - field: "skipWithMasterKey", + field: 'skipWithMasterKey', value: [], error: - "Invalid type for Cloud Function validation key skipWithMasterKey. Expected boolean, actual array", + 'Invalid type for Cloud Function validation key skipWithMasterKey. Expected boolean, actual array', }, { - field: "requireAllUserRoles", + field: 'requireAllUserRoles', value: true, error: - "Invalid type for Cloud Function validation key requireAllUserRoles. Expected array|function, actual boolean", + 'Invalid type for Cloud Function validation key requireAllUserRoles. Expected array|function, actual boolean', }, { - field: "requireAnyUserRoles", + field: 'requireAnyUserRoles', value: true, error: - "Invalid type for Cloud Function validation key requireAnyUserRoles. Expected array|function, actual boolean", + 'Invalid type for Cloud Function validation key requireAnyUserRoles. Expected array|function, actual boolean', }, { - field: "fields", + field: 'fields', value: true, error: - "Invalid type for Cloud Function validation key fields. Expected array|object, actual boolean", + 'Invalid type for Cloud Function validation key fields. Expected array|object, actual boolean', }, { - field: "requireUserKeys", + field: 'requireUserKeys', value: true, error: - "Invalid type for Cloud Function validation key requireUserKeys. Expected array|object, actual boolean", + 'Invalid type for Cloud Function validation key requireUserKeys. Expected array|object, actual boolean', }, ]; for (const field of fields) { try { - Parse.Cloud.define("myFunction", () => {}, { + Parse.Cloud.define('myFunction', () => {}, { [field.field]: field.value, }); fail( @@ -1744,36 +1744,36 @@ describe("cloud validator", () => { } }); - it("Logs on multiple invalid configs", () => { + it('Logs on multiple invalid configs', () => { const fields = [ { - field: "otherKey", + field: 'otherKey', value: true, error: - "otherKey is not a supported parameter for Cloud Function validations.", + 'otherKey is not a supported parameter for Cloud Function validations.', }, { - field: "constant", + field: 'constant', value: [], error: - "Invalid type for Cloud Function validation key constant. Expected boolean, actual array", + 'Invalid type for Cloud Function validation key constant. Expected boolean, actual array', }, { - field: "required", + field: 'required', value: [], error: - "Invalid type for Cloud Function validation key required. Expected boolean, actual array", + 'Invalid type for Cloud Function validation key required. Expected boolean, actual array', }, { - field: "error", + field: 'error', value: [], error: - "Invalid type for Cloud Function validation key error. Expected string, actual array", + 'Invalid type for Cloud Function validation key error. Expected string, actual array', }, ]; for (const field of fields) { try { - Parse.Cloud.define("myFunction", () => {}, { + Parse.Cloud.define('myFunction', () => {}, { fields: { name: { [field.field]: field.value, @@ -1787,7 +1787,7 @@ describe("cloud validator", () => { expect(e).toBe(field.error); } try { - Parse.Cloud.define("myFunction", () => {}, { + Parse.Cloud.define('myFunction', () => {}, { requireUserKeys: { name: { [field.field]: field.value, @@ -1803,11 +1803,11 @@ describe("cloud validator", () => { } }); - it("set params options function async", async () => { + it('set params options function async', async () => { Parse.Cloud.define( - "hello", + 'hello', () => { - return "Hello world!"; + return 'Hello world!'; }, { fields: { @@ -1818,35 +1818,35 @@ describe("cloud validator", () => { await new Promise(resolve => { setTimeout(resolve, 500); }); - return val === "f"; + return val === 'f'; }, - error: "Validation failed.", + error: 'Validation failed.', }, }, } ); try { - await Parse.Cloud.run("hello", { data: "d" }); - fail("validation should have failed"); + await Parse.Cloud.run('hello', { data: 'd' }); + fail('validation should have failed'); } catch (error) { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual("Validation failed."); + expect(error.message).toEqual('Validation failed.'); } - const result = await Parse.Cloud.run("hello", { data: "f" }); - expect(result).toBe("Hello world!"); + const result = await Parse.Cloud.run('hello', { data: 'f' }); + expect(result).toBe('Hello world!'); }); - it("basic beforeSave requireUserKey as custom async function", async () => { + it('basic beforeSave requireUserKey as custom async function', async () => { Parse.Cloud.beforeSave(Parse.User, () => {}, { fields: { accType: { - default: "normal", + default: 'normal', constant: true, }, }, }); Parse.Cloud.define( - "secureFunction", + 'secureFunction', () => { return "Here's all the secure data!"; }, @@ -1857,25 +1857,25 @@ describe("cloud validator", () => { await new Promise(resolve => { setTimeout(resolve, 500); }); - return ["admin", "admin2"].includes(val); + return ['admin', 'admin2'].includes(val); }, - error: "Unauthorized.", + error: 'Unauthorized.', }, }, } ); const user = new Parse.User(); - user.set("username", "testuser"); - user.set("password", "p@ssword"); - user.set("accType", "admin"); + user.set('username', 'testuser'); + user.set('password', 'p@ssword'); + user.set('accType', 'admin'); await user.signUp(); - expect(user.get("accType")).toBe("normal"); + expect(user.get('accType')).toBe('normal'); try { - await Parse.Cloud.run("secureFunction"); - fail("function should only be available to admin users"); + await Parse.Cloud.run('secureFunction'); + fail('function should only be available to admin users'); } catch (error) { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual("Unauthorized."); + expect(error.message).toEqual('Unauthorized.'); } }); }); diff --git a/spec/CloudCode.spec.js b/spec/CloudCode.spec.js index 576632d07a..1c56c6dbe5 100644 --- a/spec/CloudCode.spec.js +++ b/spec/CloudCode.spec.js @@ -1,10 +1,10 @@ -"use strict"; -const Config = require("../lib/Config"); -const Parse = require("parse/node"); -const ParseServer = require("../lib/index").ParseServer; -const request = require("../lib/request"); +'use strict'; +const Config = require('../lib/Config'); +const Parse = require('parse/node'); +const ParseServer = require('../lib/index').ParseServer; +const request = require('../lib/request'); const InMemoryCacheAdapter = - require("../lib/Adapters/Cache/InMemoryCacheAdapter").InMemoryCacheAdapter; + require('../lib/Adapters/Cache/InMemoryCacheAdapter').InMemoryCacheAdapter; const mockAdapter = { createFile: async filename => ({ @@ -19,26 +19,26 @@ const mockAdapter = { }, }; -describe("Cloud Code", () => { - it("can load absolute cloud code file", done => { +describe('Cloud Code', () => { + it('can load absolute cloud code file', done => { reconfigureServer({ - cloud: __dirname + "/cloud/cloudCodeRelativeFile.js", + cloud: __dirname + '/cloud/cloudCodeRelativeFile.js', }).then(() => { - Parse.Cloud.run("cloudCodeInFile", {}).then(result => { + Parse.Cloud.run('cloudCodeInFile', {}).then(result => { expect(result).toEqual( - "It is possible to define cloud code in a file." + 'It is possible to define cloud code in a file.' ); done(); }); }); }); - it("can load relative cloud code file", done => { - reconfigureServer({ cloud: "./spec/cloud/cloudCodeAbsoluteFile.js" }).then( + it('can load relative cloud code file', done => { + reconfigureServer({ cloud: './spec/cloud/cloudCodeAbsoluteFile.js' }).then( () => { - Parse.Cloud.run("cloudCodeInFile", {}).then(result => { + Parse.Cloud.run('cloudCodeInFile', {}).then(result => { expect(result).toEqual( - "It is possible to define cloud code in a file." + 'It is possible to define cloud code in a file.' ); done(); }); @@ -46,115 +46,115 @@ describe("Cloud Code", () => { ); }); - it("can load cloud code as a module", async () => { - process.env.npm_package_type = "module"; + it('can load cloud code as a module', async () => { + process.env.npm_package_type = 'module'; await reconfigureServer({ - appId: "test1", - cloud: "./spec/cloud/cloudCodeModuleFile.js", + appId: 'test1', + cloud: './spec/cloud/cloudCodeModuleFile.js', }); - const result = await Parse.Cloud.run("cloudCodeInFile"); - expect(result).toEqual("It is possible to define cloud code in a file."); + const result = await Parse.Cloud.run('cloudCodeInFile'); + expect(result).toEqual('It is possible to define cloud code in a file.'); delete process.env.npm_package_type; }); - it("cloud code must be valid type", async () => { - spyOn(console, "error").and.callFake(() => {}); + it('cloud code must be valid type', async () => { + spyOn(console, 'error').and.callFake(() => {}); await expectAsync(reconfigureServer({ cloud: true })).toBeRejectedWith( "argument 'cloud' must either be a string or a function" ); }); - it("should wait for cloud code to load", async () => { - await reconfigureServer({ appId: "test3" }); + it('should wait for cloud code to load', async () => { + await reconfigureServer({ appId: 'test3' }); const initiated = new Date(); const parseServer = await new ParseServer({ ...defaultConfiguration, - appId: "test3", - masterKey: "test", - serverURL: "http://localhost:12668/parse", + appId: 'test3', + masterKey: 'test', + serverURL: 'http://localhost:12668/parse', async cloud() { await new Promise(resolve => setTimeout(resolve, 1000)); - Parse.Cloud.beforeSave("Test", () => { - throw "Cannot save."; + Parse.Cloud.beforeSave('Test', () => { + throw 'Cannot save.'; }); }, }).start(); - const express = require("express"); + const express = require('express'); const app = express(); - app.use("/parse", parseServer.app); + app.use('/parse', parseServer.app); const server = app.listen(12668); const now = new Date(); expect(now.getTime() - initiated.getTime() > 1000).toBeTrue(); - await expectAsync(new Parse.Object("Test").save()).toBeRejectedWith( - new Parse.Error(141, "Cannot save.") + await expectAsync(new Parse.Object('Test').save()).toBeRejectedWith( + new Parse.Error(141, 'Cannot save.') ); await new Promise(resolve => server.close(resolve)); }); - it("can create functions", done => { - Parse.Cloud.define("hello", () => { - return "Hello world!"; + it('can create functions', done => { + Parse.Cloud.define('hello', () => { + return 'Hello world!'; }); - Parse.Cloud.run("hello", {}).then(result => { - expect(result).toEqual("Hello world!"); + Parse.Cloud.run('hello', {}).then(result => { + expect(result).toEqual('Hello world!'); done(); }); }); - it("can get config", () => { + it('can get config', () => { const config = Parse.Server; - let currentConfig = Config.get("test"); - const server = require("../lib/cloud-code/Parse.Server"); + let currentConfig = Config.get('test'); + const server = require('../lib/cloud-code/Parse.Server'); expect(Object.keys(config)).toEqual( Object.keys({ ...currentConfig, ...server }) ); config.silent = false; Parse.Server = config; - currentConfig = Config.get("test"); + currentConfig = Config.get('test'); expect(currentConfig.silent).toBeFalse(); }); - it("can get curent version", () => { - const version = require("../package.json").version; - const currentConfig = Config.get("test"); + it('can get curent version', () => { + const version = require('../package.json').version; + const currentConfig = Config.get('test'); expect(Parse.Server.version).toBeDefined(); expect(currentConfig.version).toBeDefined(); expect(Parse.Server.version).toEqual(version); }); - it("show warning on duplicate cloud functions", done => { - const logger = require("../lib/logger").logger; - spyOn(logger, "warn").and.callFake(() => {}); - Parse.Cloud.define("hello", () => { - return "Hello world!"; + it('show warning on duplicate cloud functions', done => { + const logger = require('../lib/logger').logger; + spyOn(logger, 'warn').and.callFake(() => {}); + Parse.Cloud.define('hello', () => { + return 'Hello world!'; }); - Parse.Cloud.define("hello", () => { - return "Hello world!"; + Parse.Cloud.define('hello', () => { + return 'Hello world!'; }); expect(logger.warn).toHaveBeenCalledWith( - "Warning: Duplicate cloud functions exist for hello. Only the last one will be used and the others will be ignored." + 'Warning: Duplicate cloud functions exist for hello. Only the last one will be used and the others will be ignored.' ); done(); }); - it("is cleared cleared after the previous test", done => { - Parse.Cloud.run("hello", {}).catch(error => { + it('is cleared cleared after the previous test', done => { + Parse.Cloud.run('hello', {}).catch(error => { expect(error.code).toEqual(Parse.Error.SCRIPT_FAILED); done(); }); }); - it("basic beforeSave rejection", function (done) { - Parse.Cloud.beforeSave("BeforeSaveFail", function () { - throw new Error("You shall not pass!"); + it('basic beforeSave rejection', function (done) { + Parse.Cloud.beforeSave('BeforeSaveFail', function () { + throw new Error('You shall not pass!'); }); - const obj = new Parse.Object("BeforeSaveFail"); - obj.set("foo", "bar"); + const obj = new Parse.Object('BeforeSaveFail'); + obj.set('foo', 'bar'); obj.save().then( () => { - fail("Should not have been able to save BeforeSaveFailure class."); + fail('Should not have been able to save BeforeSaveFailure class.'); done(); }, () => { @@ -163,86 +163,86 @@ describe("Cloud Code", () => { ); }); - it("returns an error", done => { - Parse.Cloud.define("cloudCodeWithError", () => { + it('returns an error', done => { + Parse.Cloud.define('cloudCodeWithError', () => { /* eslint-disable no-undef */ foo.bar(); /* eslint-enable no-undef */ - return "I better throw an error."; + return 'I better throw an error.'; }); - Parse.Cloud.run("cloudCodeWithError").then( - () => done.fail("should not succeed"), + Parse.Cloud.run('cloudCodeWithError').then( + () => done.fail('should not succeed'), e => { expect(e).toEqual( - new Parse.Error(Parse.Error.SCRIPT_FAILED, "foo is not defined") + new Parse.Error(Parse.Error.SCRIPT_FAILED, 'foo is not defined') ); done(); } ); }); - it("returns an empty error", done => { - Parse.Cloud.define("cloudCodeWithError", () => { + it('returns an empty error', done => { + Parse.Cloud.define('cloudCodeWithError', () => { throw null; }); - Parse.Cloud.run("cloudCodeWithError").then( - () => done.fail("should not succeed"), + Parse.Cloud.run('cloudCodeWithError').then( + () => done.fail('should not succeed'), e => { expect(e.code).toEqual(Parse.Error.SCRIPT_FAILED); - expect(e.message).toEqual("Script failed."); + expect(e.message).toEqual('Script failed.'); done(); } ); }); - it("beforeFind can throw string", async function (done) { - Parse.Cloud.beforeFind("beforeFind", () => { - throw "throw beforeFind"; + it('beforeFind can throw string', async function (done) { + Parse.Cloud.beforeFind('beforeFind', () => { + throw 'throw beforeFind'; }); - const obj = new Parse.Object("beforeFind"); - obj.set("foo", "bar"); + const obj = new Parse.Object('beforeFind'); + obj.set('foo', 'bar'); await obj.save(); - expect(obj.get("foo")).toBe("bar"); + expect(obj.get('foo')).toBe('bar'); try { - const query = new Parse.Query("beforeFind"); + const query = new Parse.Query('beforeFind'); await query.first(); } catch (e) { expect(e.code).toBe(Parse.Error.SCRIPT_FAILED); - expect(e.message).toBe("throw beforeFind"); + expect(e.message).toBe('throw beforeFind'); done(); } }); - it("beforeSave rejection with custom error code", function (done) { - Parse.Cloud.beforeSave("BeforeSaveFailWithErrorCode", function () { - throw new Parse.Error(999, "Nope"); + it('beforeSave rejection with custom error code', function (done) { + Parse.Cloud.beforeSave('BeforeSaveFailWithErrorCode', function () { + throw new Parse.Error(999, 'Nope'); }); - const obj = new Parse.Object("BeforeSaveFailWithErrorCode"); - obj.set("foo", "bar"); + const obj = new Parse.Object('BeforeSaveFailWithErrorCode'); + obj.set('foo', 'bar'); obj.save().then( function () { fail( - "Should not have been able to save BeforeSaveFailWithErrorCode class." + 'Should not have been able to save BeforeSaveFailWithErrorCode class.' ); done(); }, function (error) { expect(error.code).toEqual(999); - expect(error.message).toEqual("Nope"); + expect(error.message).toEqual('Nope'); done(); } ); }); - it("basic beforeSave rejection via promise", function (done) { - Parse.Cloud.beforeSave("BeforeSaveFailWithPromise", function () { - const query = new Parse.Query("Yolo"); + it('basic beforeSave rejection via promise', function (done) { + Parse.Cloud.beforeSave('BeforeSaveFailWithPromise', function () { + const query = new Parse.Query('Yolo'); return query.find().then( () => { - throw "Nope"; + throw 'Nope'; }, () => { return Promise.response(); @@ -250,34 +250,34 @@ describe("Cloud Code", () => { ); }); - const obj = new Parse.Object("BeforeSaveFailWithPromise"); - obj.set("foo", "bar"); + const obj = new Parse.Object('BeforeSaveFailWithPromise'); + obj.set('foo', 'bar'); obj.save().then( function () { - fail("Should not have been able to save BeforeSaveFailure class."); + fail('Should not have been able to save BeforeSaveFailure class.'); done(); }, function (error) { expect(error.code).toEqual(Parse.Error.SCRIPT_FAILED); - expect(error.message).toEqual("Nope"); + expect(error.message).toEqual('Nope'); done(); } ); }); - it("test beforeSave changed object success", function (done) { - Parse.Cloud.beforeSave("BeforeSaveChanged", function (req) { - req.object.set("foo", "baz"); + it('test beforeSave changed object success', function (done) { + Parse.Cloud.beforeSave('BeforeSaveChanged', function (req) { + req.object.set('foo', 'baz'); }); - const obj = new Parse.Object("BeforeSaveChanged"); - obj.set("foo", "bar"); + const obj = new Parse.Object('BeforeSaveChanged'); + obj.set('foo', 'bar'); obj.save().then( function () { - const query = new Parse.Query("BeforeSaveChanged"); + const query = new Parse.Query('BeforeSaveChanged'); query.get(obj.id).then( function (objAgain) { - expect(objAgain.get("foo")).toEqual("baz"); + expect(objAgain.get('foo')).toEqual('baz'); done(); }, function (error) { @@ -293,187 +293,187 @@ describe("Cloud Code", () => { ); }); - it("test beforeSave with invalid field", async () => { - Parse.Cloud.beforeSave("BeforeSaveChanged", function (req) { - req.object.set("length", 0); + it('test beforeSave with invalid field', async () => { + Parse.Cloud.beforeSave('BeforeSaveChanged', function (req) { + req.object.set('length', 0); }); - const obj = new Parse.Object("BeforeSaveChanged"); - obj.set("foo", "bar"); + const obj = new Parse.Object('BeforeSaveChanged'); + obj.set('foo', 'bar'); try { await obj.save(); - fail("should not succeed"); + fail('should not succeed'); } catch (e) { - expect(e.message).toBe("Invalid field name: length."); + expect(e.message).toBe('Invalid field name: length.'); } }); it("test beforeSave changed object fail doesn't change object", async function () { - Parse.Cloud.beforeSave("BeforeSaveChanged", function (req) { - if (req.object.has("fail")) { - return Promise.reject(new Error("something went wrong")); + Parse.Cloud.beforeSave('BeforeSaveChanged', function (req) { + if (req.object.has('fail')) { + return Promise.reject(new Error('something went wrong')); } return Promise.resolve(); }); - const obj = new Parse.Object("BeforeSaveChanged"); - obj.set("foo", "bar"); + const obj = new Parse.Object('BeforeSaveChanged'); + obj.set('foo', 'bar'); await obj.save(); - obj.set("foo", "baz").set("fail", true); + obj.set('foo', 'baz').set('fail', true); try { await obj.save(); } catch (e) { await obj.fetch(); - expect(obj.get("foo")).toBe("bar"); + expect(obj.get('foo')).toBe('bar'); } }); - it("test beforeSave returns value on create and update", done => { - Parse.Cloud.beforeSave("BeforeSaveChanged", function (req) { - req.object.set("foo", "baz"); + it('test beforeSave returns value on create and update', done => { + Parse.Cloud.beforeSave('BeforeSaveChanged', function (req) { + req.object.set('foo', 'baz'); }); - const obj = new Parse.Object("BeforeSaveChanged"); - obj.set("foo", "bing"); + const obj = new Parse.Object('BeforeSaveChanged'); + obj.set('foo', 'bing'); obj.save().then(() => { - expect(obj.get("foo")).toEqual("baz"); - obj.set("foo", "bar"); + expect(obj.get('foo')).toEqual('baz'); + obj.set('foo', 'bar'); return obj.save().then(() => { - expect(obj.get("foo")).toEqual("baz"); + expect(obj.get('foo')).toEqual('baz'); done(); }); }); }); - it("test beforeSave applies changes when beforeSave returns true", done => { - Parse.Cloud.beforeSave("Insurance", function (req) { - req.object.set("rate", "$49.99/Month"); + it('test beforeSave applies changes when beforeSave returns true', done => { + Parse.Cloud.beforeSave('Insurance', function (req) { + req.object.set('rate', '$49.99/Month'); return true; }); - const insurance = new Parse.Object("Insurance"); - insurance.set("rate", "$5.00/Month"); + const insurance = new Parse.Object('Insurance'); + insurance.set('rate', '$5.00/Month'); insurance.save().then(insurance => { - expect(insurance.get("rate")).toEqual("$49.99/Month"); + expect(insurance.get('rate')).toEqual('$49.99/Month'); done(); }); }); - it("test beforeSave applies changes and resolves returned promise", done => { - Parse.Cloud.beforeSave("Insurance", function (req) { - req.object.set("rate", "$49.99/Month"); - return new Parse.Query("Pet").get(req.object.get("pet").id).then(pet => { - pet.set("healthy", true); + it('test beforeSave applies changes and resolves returned promise', done => { + Parse.Cloud.beforeSave('Insurance', function (req) { + req.object.set('rate', '$49.99/Month'); + return new Parse.Query('Pet').get(req.object.get('pet').id).then(pet => { + pet.set('healthy', true); return pet.save(); }); }); - const pet = new Parse.Object("Pet"); - pet.set("healthy", false); + const pet = new Parse.Object('Pet'); + pet.set('healthy', false); pet.save().then(pet => { - const insurance = new Parse.Object("Insurance"); - insurance.set("pet", pet); - insurance.set("rate", "$5.00/Month"); + const insurance = new Parse.Object('Insurance'); + insurance.set('pet', pet); + insurance.set('rate', '$5.00/Month'); insurance.save().then(insurance => { - expect(insurance.get("rate")).toEqual("$49.99/Month"); - new Parse.Query("Pet").get(insurance.get("pet").id).then(pet => { - expect(pet.get("healthy")).toEqual(true); + expect(insurance.get('rate')).toEqual('$49.99/Month'); + new Parse.Query('Pet').get(insurance.get('pet').id).then(pet => { + expect(pet.get('healthy')).toEqual(true); done(); }); }); }); }); - it("beforeSave should be called only if user fulfills permissions", async () => { + it('beforeSave should be called only if user fulfills permissions', async () => { const triggeruser = new Parse.User(); - triggeruser.setUsername("triggeruser"); - triggeruser.setPassword("triggeruser"); + triggeruser.setUsername('triggeruser'); + triggeruser.setPassword('triggeruser'); await triggeruser.signUp(); const triggeruser2 = new Parse.User(); - triggeruser2.setUsername("triggeruser2"); - triggeruser2.setPassword("triggeruser2"); + triggeruser2.setUsername('triggeruser2'); + triggeruser2.setPassword('triggeruser2'); await triggeruser2.signUp(); const triggeruser3 = new Parse.User(); - triggeruser3.setUsername("triggeruser3"); - triggeruser3.setPassword("triggeruser3"); + triggeruser3.setUsername('triggeruser3'); + triggeruser3.setPassword('triggeruser3'); await triggeruser3.signUp(); const triggeruser4 = new Parse.User(); - triggeruser4.setUsername("triggeruser4"); - triggeruser4.setPassword("triggeruser4"); + triggeruser4.setUsername('triggeruser4'); + triggeruser4.setPassword('triggeruser4'); await triggeruser4.signUp(); const triggeruser5 = new Parse.User(); - triggeruser5.setUsername("triggeruser5"); - triggeruser5.setPassword("triggeruser5"); + triggeruser5.setUsername('triggeruser5'); + triggeruser5.setPassword('triggeruser5'); await triggeruser5.signUp(); const triggerroleacl = new Parse.ACL(); triggerroleacl.setPublicReadAccess(true); const triggerrole = new Parse.Role(); - triggerrole.setName("triggerrole"); + triggerrole.setName('triggerrole'); triggerrole.setACL(triggerroleacl); triggerrole.getUsers().add(triggeruser); triggerrole.getUsers().add(triggeruser3); await triggerrole.save(); - const config = Config.get("test"); + const config = Config.get('test'); const schema = await config.database.loadSchema(); await schema.addClassIfNotExists( - "triggerclass", + 'triggerclass', { - someField: { type: "String" }, - pointerToUser: { type: "Pointer", targetClass: "_User" }, + someField: { type: 'String' }, + pointerToUser: { type: 'Pointer', targetClass: '_User' }, }, { find: { - "role:triggerrole": true, + 'role:triggerrole': true, [triggeruser.id]: true, [triggeruser2.id]: true, }, create: { - "role:triggerrole": true, + 'role:triggerrole': true, [triggeruser.id]: true, [triggeruser2.id]: true, }, get: { - "role:triggerrole": true, + 'role:triggerrole': true, [triggeruser.id]: true, [triggeruser2.id]: true, }, update: { - "role:triggerrole": true, + 'role:triggerrole': true, [triggeruser.id]: true, [triggeruser2.id]: true, }, addField: { - "role:triggerrole": true, + 'role:triggerrole': true, [triggeruser.id]: true, [triggeruser2.id]: true, }, delete: { - "role:triggerrole": true, + 'role:triggerrole': true, [triggeruser.id]: true, [triggeruser2.id]: true, }, - readUserFields: ["pointerToUser"], - writeUserFields: ["pointerToUser"], + readUserFields: ['pointerToUser'], + writeUserFields: ['pointerToUser'], }, {} ); let called = 0; - Parse.Cloud.beforeSave("triggerclass", () => { + Parse.Cloud.beforeSave('triggerclass', () => { called++; }); - const triggerobject = new Parse.Object("triggerclass"); - triggerobject.set("someField", "someValue"); - triggerobject.set("someField2", "someValue"); + const triggerobject = new Parse.Object('triggerclass'); + triggerobject.set('someField', 'someValue'); + triggerobject.set('someField2', 'someValue'); const triggerobjectacl = new Parse.ACL(); triggerobjectacl.setPublicReadAccess(false); triggerobjectacl.setPublicWriteAccess(false); @@ -502,9 +502,9 @@ describe("Cloud Code", () => { }); expect(called).toBe(4); - const triggerobject2 = new Parse.Object("triggerclass"); - triggerobject2.set("someField", "someValue"); - triggerobject2.set("someField22", "someValue"); + const triggerobject2 = new Parse.Object('triggerclass'); + triggerobject2.set('someField', 'someValue'); + triggerobject2.set('someField22', 'someValue'); const triggerobjectacl2 = new Parse.ACL(); triggerobjectacl2.setPublicReadAccess(false); triggerobjectacl2.setPublicWriteAccess(false); @@ -565,9 +565,9 @@ describe("Cloud Code", () => { expect(catched).toBe(true); expect(called).toBe(7); - const triggerobject3 = new Parse.Object("triggerclass"); - triggerobject3.set("someField", "someValue"); - triggerobject3.set("someField33", "someValue"); + const triggerobject3 = new Parse.Object('triggerclass'); + triggerobject3.set('someField', 'someValue'); + triggerobject3.set('someField33', 'someValue'); catched = false; try { @@ -594,19 +594,19 @@ describe("Cloud Code", () => { expect(called).toBe(7); }); - it("test afterSave ran and created an object", function (done) { - Parse.Cloud.afterSave("AfterSaveTest", function (req) { - const obj = new Parse.Object("AfterSaveProof"); - obj.set("proof", req.object.id); + it('test afterSave ran and created an object', function (done) { + Parse.Cloud.afterSave('AfterSaveTest', function (req) { + const obj = new Parse.Object('AfterSaveProof'); + obj.set('proof', req.object.id); obj.save().then(test); }); - const obj = new Parse.Object("AfterSaveTest"); + const obj = new Parse.Object('AfterSaveTest'); obj.save(); function test() { - const query = new Parse.Query("AfterSaveProof"); - query.equalTo("proof", obj.id); + const query = new Parse.Query('AfterSaveProof'); + query.equalTo('proof', obj.id); query.find().then( function (results) { expect(results.length).toEqual(1); @@ -620,13 +620,13 @@ describe("Cloud Code", () => { } }); - it("test afterSave ran on created object and returned a promise", function (done) { - Parse.Cloud.afterSave("AfterSaveTest2", function (req) { + it('test afterSave ran on created object and returned a promise', function (done) { + Parse.Cloud.afterSave('AfterSaveTest2', function (req) { const obj = req.object; if (!obj.existed()) { return new Promise(resolve => { setTimeout(function () { - obj.set("proof", obj.id); + obj.set('proof', obj.id); obj.save().then(function () { resolve(); }); @@ -635,15 +635,15 @@ describe("Cloud Code", () => { } }); - const obj = new Parse.Object("AfterSaveTest2"); + const obj = new Parse.Object('AfterSaveTest2'); obj.save().then(function () { - const query = new Parse.Query("AfterSaveTest2"); - query.equalTo("proof", obj.id); + const query = new Parse.Query('AfterSaveTest2'); + query.equalTo('proof', obj.id); query.find().then( function (results) { expect(results.length).toEqual(1); const savedObject = results[0]; - expect(savedObject.get("proof")).toEqual(obj.id); + expect(savedObject.get('proof')).toEqual(obj.id); done(); }, function (error) { @@ -655,13 +655,13 @@ describe("Cloud Code", () => { }); // TODO: Fails on CI randomly as racing - xit("test afterSave ignoring promise, object not found", function (done) { - Parse.Cloud.afterSave("AfterSaveTest2", function (req) { + xit('test afterSave ignoring promise, object not found', function (done) { + Parse.Cloud.afterSave('AfterSaveTest2', function (req) { const obj = req.object; if (!obj.existed()) { return new Promise(resolve => { setTimeout(function () { - obj.set("proof", obj.id); + obj.set('proof', obj.id); obj.save().then(function () { resolve(); }); @@ -670,13 +670,13 @@ describe("Cloud Code", () => { } }); - const obj = new Parse.Object("AfterSaveTest2"); + const obj = new Parse.Object('AfterSaveTest2'); obj.save().then(function () { done(); }); - const query = new Parse.Query("AfterSaveTest2"); - query.equalTo("proof", obj.id); + const query = new Parse.Query('AfterSaveTest2'); + query.equalTo('proof', obj.id); query.find().then( function (results) { expect(results.length).toEqual(0); @@ -687,16 +687,16 @@ describe("Cloud Code", () => { ); }); - it("test afterSave rejecting promise", function (done) { - Parse.Cloud.afterSave("AfterSaveTest2", function () { + it('test afterSave rejecting promise', function (done) { + Parse.Cloud.afterSave('AfterSaveTest2', function () { return new Promise((resolve, reject) => { setTimeout(function () { - reject("THIS SHOULD BE IGNORED"); + reject('THIS SHOULD BE IGNORED'); }, 1000); }); }); - const obj = new Parse.Object("AfterSaveTest2"); + const obj = new Parse.Object('AfterSaveTest2'); obj.save().then( function () { done(); @@ -708,12 +708,12 @@ describe("Cloud Code", () => { ); }); - it("test afterDelete returning promise, object is deleted when destroy resolves", function (done) { - Parse.Cloud.afterDelete("AfterDeleteTest2", function (req) { + it('test afterDelete returning promise, object is deleted when destroy resolves', function (done) { + Parse.Cloud.afterDelete('AfterDeleteTest2', function (req) { return new Promise(resolve => { setTimeout(function () { - const obj = new Parse.Object("AfterDeleteTestProof"); - obj.set("proof", req.object.id); + const obj = new Parse.Object('AfterDeleteTestProof'); + obj.set('proof', req.object.id); obj.save().then(function () { resolve(); }); @@ -726,27 +726,27 @@ describe("Cloud Code", () => { done(); }; - const obj = new Parse.Object("AfterDeleteTest2"); + const obj = new Parse.Object('AfterDeleteTest2'); obj.save().then(function () { obj.destroy().then(function () { - const query = new Parse.Query("AfterDeleteTestProof"); - query.equalTo("proof", obj.id); + const query = new Parse.Query('AfterDeleteTestProof'); + query.equalTo('proof', obj.id); query.find().then(function (results) { expect(results.length).toEqual(1); const deletedObject = results[0]; - expect(deletedObject.get("proof")).toEqual(obj.id); + expect(deletedObject.get('proof')).toEqual(obj.id); done(); }, errorHandler); }, errorHandler); }, errorHandler); }); - it("test afterDelete ignoring promise, object is not yet deleted", function (done) { - Parse.Cloud.afterDelete("AfterDeleteTest2", function (req) { + it('test afterDelete ignoring promise, object is not yet deleted', function (done) { + Parse.Cloud.afterDelete('AfterDeleteTest2', function (req) { return new Promise(resolve => { setTimeout(function () { - const obj = new Parse.Object("AfterDeleteTestProof"); - obj.set("proof", req.object.id); + const obj = new Parse.Object('AfterDeleteTestProof'); + obj.set('proof', req.object.id); obj.save().then(function () { resolve(); }); @@ -759,38 +759,38 @@ describe("Cloud Code", () => { done(); }; - const obj = new Parse.Object("AfterDeleteTest2"); + const obj = new Parse.Object('AfterDeleteTest2'); obj.save().then(function () { obj.destroy().then(function () { done(); }); - const query = new Parse.Query("AfterDeleteTestProof"); - query.equalTo("proof", obj.id); + const query = new Parse.Query('AfterDeleteTestProof'); + query.equalTo('proof', obj.id); query.find().then(function (results) { expect(results.length).toEqual(0); }, errorHandler); }, errorHandler); }); - it("test beforeSave happens on update", function (done) { - Parse.Cloud.beforeSave("BeforeSaveChanged", function (req) { - req.object.set("foo", "baz"); + it('test beforeSave happens on update', function (done) { + Parse.Cloud.beforeSave('BeforeSaveChanged', function (req) { + req.object.set('foo', 'baz'); }); - const obj = new Parse.Object("BeforeSaveChanged"); - obj.set("foo", "bar"); + const obj = new Parse.Object('BeforeSaveChanged'); + obj.set('foo', 'bar'); obj .save() .then(function () { - obj.set("foo", "bar"); + obj.set('foo', 'bar'); return obj.save(); }) .then( function () { - const query = new Parse.Query("BeforeSaveChanged"); + const query = new Parse.Query('BeforeSaveChanged'); return query.get(obj.id).then(function (objAgain) { - expect(objAgain.get("foo")).toEqual("baz"); + expect(objAgain.get('foo')).toEqual('baz'); done(); }); }, @@ -801,14 +801,14 @@ describe("Cloud Code", () => { ); }); - it("test beforeDelete failure", function (done) { - Parse.Cloud.beforeDelete("BeforeDeleteFail", function () { - throw "Nope"; + it('test beforeDelete failure', function (done) { + Parse.Cloud.beforeDelete('BeforeDeleteFail', function () { + throw 'Nope'; }); - const obj = new Parse.Object("BeforeDeleteFail"); + const obj = new Parse.Object('BeforeDeleteFail'); let id; - obj.set("foo", "bar"); + obj.set('foo', 'bar'); obj .save() .then(() => { @@ -817,14 +817,14 @@ describe("Cloud Code", () => { }) .then( () => { - fail("obj.destroy() should have failed, but it succeeded"); + fail('obj.destroy() should have failed, but it succeeded'); done(); }, error => { expect(error.code).toEqual(Parse.Error.SCRIPT_FAILED); - expect(error.message).toEqual("Nope"); + expect(error.message).toEqual('Nope'); - const objAgain = new Parse.Object("BeforeDeleteFail", { + const objAgain = new Parse.Object('BeforeDeleteFail', { objectId: id, }); return objAgain.fetch(); @@ -833,9 +833,9 @@ describe("Cloud Code", () => { .then( objAgain => { if (objAgain) { - expect(objAgain.get("foo")).toEqual("bar"); + expect(objAgain.get('foo')).toEqual('bar'); } else { - fail("unable to fetch the object ", id); + fail('unable to fetch the object ', id); } done(); }, @@ -846,45 +846,45 @@ describe("Cloud Code", () => { ); }); - it("basic beforeDelete rejection via promise", function (done) { - Parse.Cloud.beforeSave("BeforeDeleteFailWithPromise", function () { - const query = new Parse.Query("Yolo"); + it('basic beforeDelete rejection via promise', function (done) { + Parse.Cloud.beforeSave('BeforeDeleteFailWithPromise', function () { + const query = new Parse.Query('Yolo'); return query.find().then(() => { - throw "Nope"; + throw 'Nope'; }); }); - const obj = new Parse.Object("BeforeDeleteFailWithPromise"); - obj.set("foo", "bar"); + const obj = new Parse.Object('BeforeDeleteFailWithPromise'); + obj.set('foo', 'bar'); obj.save().then( function () { - fail("Should not have been able to save BeforeSaveFailure class."); + fail('Should not have been able to save BeforeSaveFailure class.'); done(); }, function (error) { expect(error.code).toEqual(Parse.Error.SCRIPT_FAILED); - expect(error.message).toEqual("Nope"); + expect(error.message).toEqual('Nope'); done(); } ); }); - it("test afterDelete ran and created an object", function (done) { - Parse.Cloud.afterDelete("AfterDeleteTest", function (req) { - const obj = new Parse.Object("AfterDeleteProof"); - obj.set("proof", req.object.id); + it('test afterDelete ran and created an object', function (done) { + Parse.Cloud.afterDelete('AfterDeleteTest', function (req) { + const obj = new Parse.Object('AfterDeleteProof'); + obj.set('proof', req.object.id); obj.save().then(test); }); - const obj = new Parse.Object("AfterDeleteTest"); + const obj = new Parse.Object('AfterDeleteTest'); obj.save().then(function () { obj.destroy(); }); function test() { - const query = new Parse.Query("AfterDeleteProof"); - query.equalTo("proof", obj.id); + const query = new Parse.Query('AfterDeleteProof'); + query.equalTo('proof', obj.id); query.find().then( function (results) { expect(results.length).toEqual(1); @@ -898,26 +898,26 @@ describe("Cloud Code", () => { } }); - it("test cloud function return types", function (done) { - Parse.Cloud.define("foo", function () { + it('test cloud function return types', function (done) { + Parse.Cloud.define('foo', function () { return { object: { - __type: "Object", - className: "Foo", - objectId: "123", + __type: 'Object', + className: 'Foo', + objectId: '123', x: 2, relation: { - __type: "Object", - className: "Bar", - objectId: "234", + __type: 'Object', + className: 'Bar', + objectId: '234', x: 3, }, }, array: [ { - __type: "Object", - className: "Bar", - objectId: "345", + __type: 'Object', + className: 'Bar', + objectId: '345', x: 2, }, ], @@ -925,28 +925,28 @@ describe("Cloud Code", () => { }; }); - Parse.Cloud.run("foo").then(result => { + Parse.Cloud.run('foo').then(result => { expect(result.object instanceof Parse.Object).toBeTruthy(); if (!result.object) { - fail("Unable to run foo"); + fail('Unable to run foo'); done(); return; } - expect(result.object.className).toEqual("Foo"); - expect(result.object.get("x")).toEqual(2); - const bar = result.object.get("relation"); + expect(result.object.className).toEqual('Foo'); + expect(result.object.get('x')).toEqual(2); + const bar = result.object.get('relation'); expect(bar instanceof Parse.Object).toBeTruthy(); - expect(bar.className).toEqual("Bar"); - expect(bar.get("x")).toEqual(3); + expect(bar.className).toEqual('Bar'); + expect(bar.get('x')).toEqual(3); expect(Array.isArray(result.array)).toEqual(true); expect(result.array[0] instanceof Parse.Object).toBeTruthy(); - expect(result.array[0].get("x")).toEqual(2); + expect(result.array[0].get('x')).toEqual(2); done(); }); }); - it("test cloud function request params types", function (done) { - Parse.Cloud.define("params", function (req) { + it('test cloud function request params types', function (done) { + Parse.Cloud.define('params', function (req) { expect(req.params.date instanceof Date).toBe(true); expect(req.params.date.getTime()).toBe(1463907600000); expect(req.params.dateList[0] instanceof Date).toBe(true); @@ -967,13 +967,13 @@ describe("Cloud Code", () => { ); // Regression for #2294 expect(req.params.file instanceof Parse.File).toBe(true); - expect(req.params.file.url()).toEqual("https://some.url"); + expect(req.params.file.url()).toEqual('https://some.url'); // Regression for #2204 - expect(req.params.array).toEqual(["a", "b", "c"]); + expect(req.params.array).toEqual(['a', 'b', 'c']); expect(Array.isArray(req.params.array)).toBe(true); expect(req.params.arrayOfArray).toEqual([ - ["a", "b", "c"], - ["d", "e", "f"], + ['a', 'b', 'c'], + ['d', 'e', 'f'], ]); expect(Array.isArray(req.params.arrayOfArray)).toBe(true); expect(Array.isArray(req.params.arrayOfArray[0])).toBe(true); @@ -983,58 +983,58 @@ describe("Cloud Code", () => { const params = { date: { - __type: "Date", - iso: "2016-05-22T09:00:00.000Z", + __type: 'Date', + iso: '2016-05-22T09:00:00.000Z', }, dateList: [ { - __type: "Date", - iso: "2016-05-22T09:00:00.000Z", + __type: 'Date', + iso: '2016-05-22T09:00:00.000Z', }, ], - lol: "hello", + lol: 'hello', complexStructure: { date: [ { - __type: "Date", - iso: "2016-05-22T09:00:00.000Z", + __type: 'Date', + iso: '2016-05-22T09:00:00.000Z', }, ], deepDate: { date: [ { - __type: "Date", - iso: "2016-05-22T09:00:00.000Z", + __type: 'Date', + iso: '2016-05-22T09:00:00.000Z', }, ], }, deepDate2: [ { date: { - __type: "Date", - iso: "2016-05-22T09:00:00.000Z", + __type: 'Date', + iso: '2016-05-22T09:00:00.000Z', }, }, ], }, file: Parse.File.fromJSON({ - __type: "File", - name: "name", - url: "https://some.url", + __type: 'File', + name: 'name', + url: 'https://some.url', }), - array: ["a", "b", "c"], + array: ['a', 'b', 'c'], arrayOfArray: [ - ["a", "b", "c"], - ["d", "e", "f"], + ['a', 'b', 'c'], + ['d', 'e', 'f'], ], }; - Parse.Cloud.run("params", params).then(() => { + Parse.Cloud.run('params', params).then(() => { done(); }); }); - it("test cloud function should echo keys", function (done) { - Parse.Cloud.define("echoKeys", function () { + it('test cloud function should echo keys', function (done) { + Parse.Cloud.define('echoKeys', function () { return { applicationId: Parse.applicationId, masterKey: Parse.masterKey, @@ -1042,7 +1042,7 @@ describe("Cloud Code", () => { }; }); - Parse.Cloud.run("echoKeys").then(result => { + Parse.Cloud.run('echoKeys').then(result => { expect(result.applicationId).toEqual(Parse.applicationId); expect(result.masterKey).toEqual(Parse.masterKey); expect(result.javascriptKey).toEqual(Parse.javascriptKey); @@ -1050,53 +1050,53 @@ describe("Cloud Code", () => { }); }); - it("should properly create an object in before save", done => { - Parse.Cloud.beforeSave("BeforeSaveChanged", function (req) { - req.object.set("foo", "baz"); + it('should properly create an object in before save', done => { + Parse.Cloud.beforeSave('BeforeSaveChanged', function (req) { + req.object.set('foo', 'baz'); }); - Parse.Cloud.define("createBeforeSaveChangedObject", function () { - const obj = new Parse.Object("BeforeSaveChanged"); + Parse.Cloud.define('createBeforeSaveChangedObject', function () { + const obj = new Parse.Object('BeforeSaveChanged'); return obj.save().then(() => { return obj; }); }); - Parse.Cloud.run("createBeforeSaveChangedObject").then(res => { - expect(res.get("foo")).toEqual("baz"); + Parse.Cloud.run('createBeforeSaveChangedObject').then(res => { + expect(res.get('foo')).toEqual('baz'); done(); }); }); - it("dirtyKeys are set on update", done => { + it('dirtyKeys are set on update', done => { let triggerTime = 0; // Register a mock beforeSave hook - Parse.Cloud.beforeSave("GameScore", req => { + Parse.Cloud.beforeSave('GameScore', req => { const object = req.object; expect(object instanceof Parse.Object).toBeTruthy(); - expect(object.get("fooAgain")).toEqual("barAgain"); + expect(object.get('fooAgain')).toEqual('barAgain'); if (triggerTime == 0) { // Create - expect(object.get("foo")).toEqual("bar"); + expect(object.get('foo')).toEqual('bar'); } else if (triggerTime == 1) { // Update - expect(object.dirtyKeys()).toEqual(["foo"]); - expect(object.dirty("foo")).toBeTruthy(); - expect(object.get("foo")).toEqual("baz"); + expect(object.dirtyKeys()).toEqual(['foo']); + expect(object.dirty('foo')).toBeTruthy(); + expect(object.get('foo')).toEqual('baz'); } else { throw new Error(); } triggerTime++; }); - const obj = new Parse.Object("GameScore"); - obj.set("foo", "bar"); - obj.set("fooAgain", "barAgain"); + const obj = new Parse.Object('GameScore'); + obj.set('foo', 'bar'); + obj.set('fooAgain', 'barAgain'); obj .save() .then(() => { // We only update foo - obj.set("foo", "baz"); + obj.set('foo', 'baz'); return obj.save(); }) .then( @@ -1112,13 +1112,13 @@ describe("Cloud Code", () => { ); }); - it("test beforeSave unchanged success", function (done) { - Parse.Cloud.beforeSave("BeforeSaveUnchanged", function () { + it('test beforeSave unchanged success', function (done) { + Parse.Cloud.beforeSave('BeforeSaveUnchanged', function () { return; }); - const obj = new Parse.Object("BeforeSaveUnchanged"); - obj.set("foo", "bar"); + const obj = new Parse.Object('BeforeSaveUnchanged'); + obj.set('foo', 'bar'); obj.save().then( function () { done(); @@ -1130,13 +1130,13 @@ describe("Cloud Code", () => { ); }); - it("test beforeDelete success", function (done) { - Parse.Cloud.beforeDelete("BeforeDeleteTest", function () { + it('test beforeDelete success', function (done) { + Parse.Cloud.beforeDelete('BeforeDeleteTest', function () { return; }); - const obj = new Parse.Object("BeforeDeleteTest"); - obj.set("foo", "bar"); + const obj = new Parse.Object('BeforeDeleteTest'); + obj.set('foo', 'bar'); obj .save() .then(function () { @@ -1144,7 +1144,7 @@ describe("Cloud Code", () => { }) .then( function () { - const objAgain = new Parse.Object("BeforeDeleteTest", obj.id); + const objAgain = new Parse.Object('BeforeDeleteTest', obj.id); return objAgain.fetch().then(fail, () => done()); }, function (error) { @@ -1154,27 +1154,27 @@ describe("Cloud Code", () => { ); }); - it("test save triggers get user", async done => { - Parse.Cloud.beforeSave("SaveTriggerUser", function (req) { + it('test save triggers get user', async done => { + Parse.Cloud.beforeSave('SaveTriggerUser', function (req) { if (req.user && req.user.id) { return; } else { - throw new Error("No user present on request object for beforeSave."); + throw new Error('No user present on request object for beforeSave.'); } }); - Parse.Cloud.afterSave("SaveTriggerUser", function (req) { + Parse.Cloud.afterSave('SaveTriggerUser', function (req) { if (!req.user || !req.user.id) { - console.log("No user present on request object for afterSave."); + console.log('No user present on request object for afterSave.'); } }); const user = new Parse.User(); - user.set("password", "asdf"); - user.set("email", "asdf@example.com"); - user.set("username", "zxcv"); + user.set('password', 'asdf'); + user.set('email', 'asdf@example.com'); + user.set('username', 'zxcv'); await user.signUp(); - const obj = new Parse.Object("SaveTriggerUser"); + const obj = new Parse.Object('SaveTriggerUser'); obj.save().then( function () { done(); @@ -1186,40 +1186,40 @@ describe("Cloud Code", () => { ); }); - it("beforeSave change propagates through the save response", done => { - Parse.Cloud.beforeSave("ChangingObject", function (request) { - request.object.set("foo", "baz"); + it('beforeSave change propagates through the save response', done => { + Parse.Cloud.beforeSave('ChangingObject', function (request) { + request.object.set('foo', 'baz'); }); - const obj = new Parse.Object("ChangingObject"); - obj.save({ foo: "bar" }).then( + const obj = new Parse.Object('ChangingObject'); + obj.save({ foo: 'bar' }).then( objAgain => { - expect(objAgain.get("foo")).toEqual("baz"); + expect(objAgain.get('foo')).toEqual('baz'); done(); }, () => { - fail("Should not have failed to save."); + fail('Should not have failed to save.'); done(); } ); }); - it("beforeSave change propagates through the afterSave #1931", done => { - Parse.Cloud.beforeSave("ChangingObject", function (request) { - request.object.unset("file"); - request.object.unset("date"); + it('beforeSave change propagates through the afterSave #1931', done => { + Parse.Cloud.beforeSave('ChangingObject', function (request) { + request.object.unset('file'); + request.object.unset('date'); }); - Parse.Cloud.afterSave("ChangingObject", function (request) { - expect(request.object.has("file")).toBe(false); - expect(request.object.has("date")).toBe(false); - expect(request.object.get("file")).toBeUndefined(); + Parse.Cloud.afterSave('ChangingObject', function (request) { + expect(request.object.has('file')).toBe(false); + expect(request.object.has('date')).toBe(false); + expect(request.object.get('file')).toBeUndefined(); return Promise.resolve(); }); - const file = new Parse.File("yolo.txt", [1, 2, 3], "text/plain"); + const file = new Parse.File('yolo.txt', [1, 2, 3], 'text/plain'); file .save() .then(() => { - const obj = new Parse.Object("ChangingObject"); + const obj = new Parse.Object('ChangingObject'); return obj.save({ file, date: new Date() }); }) .then( @@ -1233,99 +1233,99 @@ describe("Cloud Code", () => { ); }); - it("test cloud function parameter validation success", done => { + it('test cloud function parameter validation success', done => { // Register a function with validation Parse.Cloud.define( - "functionWithParameterValidation", + 'functionWithParameterValidation', () => { - return "works"; + return 'works'; }, request => { return request.params.success === 100; } ); - Parse.Cloud.run("functionWithParameterValidation", { success: 100 }).then( + Parse.Cloud.run('functionWithParameterValidation', { success: 100 }).then( () => { done(); }, () => { - fail("Validation should not have failed."); + fail('Validation should not have failed.'); done(); } ); }); - it("doesnt receive stale user in cloud code functions after user has been updated with master key (regression test for #1836)", done => { - Parse.Cloud.define("testQuery", function (request) { - return request.user.get("data"); + it('doesnt receive stale user in cloud code functions after user has been updated with master key (regression test for #1836)', done => { + Parse.Cloud.define('testQuery', function (request) { + return request.user.get('data'); }); - Parse.User.signUp("user", "pass") + Parse.User.signUp('user', 'pass') .then(user => { - user.set("data", "AAA"); + user.set('data', 'AAA'); return user.save(); }) - .then(() => Parse.Cloud.run("testQuery")) + .then(() => Parse.Cloud.run('testQuery')) .then(result => { - expect(result).toEqual("AAA"); - Parse.User.current().set("data", "BBB"); + expect(result).toEqual('AAA'); + Parse.User.current().set('data', 'BBB'); return Parse.User.current().save(null, { useMasterKey: true }); }) - .then(() => Parse.Cloud.run("testQuery")) + .then(() => Parse.Cloud.run('testQuery')) .then(result => { - expect(result).toEqual("BBB"); + expect(result).toEqual('BBB'); done(); }); }); - it("clears out the user cache for all sessions when the user is changed", done => { + it('clears out the user cache for all sessions when the user is changed', done => { let session1; let session2; let user; const cacheAdapter = new InMemoryCacheAdapter({ ttl: 100000000 }); reconfigureServer({ cacheAdapter }) .then(() => { - Parse.Cloud.define("checkStaleUser", request => { - return request.user.get("data"); + Parse.Cloud.define('checkStaleUser', request => { + return request.user.get('data'); }); user = new Parse.User(); - user.set("username", "test"); - user.set("password", "moon-y"); - user.set("data", "first data"); + user.set('username', 'test'); + user.set('password', 'moon-y'); + user.set('data', 'first data'); return user.signUp(); }) .then(user => { session1 = user.getSessionToken(); return request({ - url: "http://localhost:8378/1/login?username=test&password=moon-y", + url: 'http://localhost:8378/1/login?username=test&password=moon-y', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }, }); }) .then(response => { session2 = response.data.sessionToken; //Ensure both session tokens are in the cache - return Parse.Cloud.run("checkStaleUser", { sessionToken: session2 }); + return Parse.Cloud.run('checkStaleUser', { sessionToken: session2 }); }) .then(() => request({ - method: "POST", - url: "http://localhost:8378/1/functions/checkStaleUser", + method: 'POST', + url: 'http://localhost:8378/1/functions/checkStaleUser', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "X-Parse-Session-Token": session2, + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Session-Token': session2, }, }) ) .then(() => Promise.all([ - cacheAdapter.get("test:user:" + session1), - cacheAdapter.get("test:user:" + session2), + cacheAdapter.get('test:user:' + session1), + cacheAdapter.get('test:user:' + session2), ]) ) .then(cachedVals => { @@ -1333,165 +1333,165 @@ describe("Cloud Code", () => { expect(cachedVals[1].objectId).toEqual(user.id); //Change with session 1 and then read with session 2. - user.set("data", "second data"); + user.set('data', 'second data'); return user.save(); }) .then(() => request({ - method: "POST", - url: "http://localhost:8378/1/functions/checkStaleUser", + method: 'POST', + url: 'http://localhost:8378/1/functions/checkStaleUser', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "X-Parse-Session-Token": session2, + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Session-Token': session2, }, }) ) .then(response => { - expect(response.data.result).toEqual("second data"); + expect(response.data.result).toEqual('second data'); done(); }) .catch(done.fail); }); - it("trivial beforeSave should not affect fetched pointers (regression test for #1238)", done => { - Parse.Cloud.beforeSave("BeforeSaveUnchanged", () => {}); + it('trivial beforeSave should not affect fetched pointers (regression test for #1238)', done => { + Parse.Cloud.beforeSave('BeforeSaveUnchanged', () => {}); - const TestObject = Parse.Object.extend("TestObject"); - const NoBeforeSaveObject = Parse.Object.extend("NoBeforeSave"); - const BeforeSaveObject = Parse.Object.extend("BeforeSaveUnchanged"); + const TestObject = Parse.Object.extend('TestObject'); + const NoBeforeSaveObject = Parse.Object.extend('NoBeforeSave'); + const BeforeSaveObject = Parse.Object.extend('BeforeSaveUnchanged'); const aTestObject = new TestObject(); - aTestObject.set("foo", "bar"); + aTestObject.set('foo', 'bar'); aTestObject .save() .then(aTestObject => { const aNoBeforeSaveObj = new NoBeforeSaveObject(); - aNoBeforeSaveObj.set("aTestObject", aTestObject); - expect(aNoBeforeSaveObj.get("aTestObject").get("foo")).toEqual("bar"); + aNoBeforeSaveObj.set('aTestObject', aTestObject); + expect(aNoBeforeSaveObj.get('aTestObject').get('foo')).toEqual('bar'); return aNoBeforeSaveObj.save(); }) .then(aNoBeforeSaveObj => { - expect(aNoBeforeSaveObj.get("aTestObject").get("foo")).toEqual("bar"); + expect(aNoBeforeSaveObj.get('aTestObject').get('foo')).toEqual('bar'); const aBeforeSaveObj = new BeforeSaveObject(); - aBeforeSaveObj.set("aTestObject", aTestObject); - expect(aBeforeSaveObj.get("aTestObject").get("foo")).toEqual("bar"); + aBeforeSaveObj.set('aTestObject', aTestObject); + expect(aBeforeSaveObj.get('aTestObject').get('foo')).toEqual('bar'); return aBeforeSaveObj.save(); }) .then(aBeforeSaveObj => { - expect(aBeforeSaveObj.get("aTestObject").get("foo")).toEqual("bar"); + expect(aBeforeSaveObj.get('aTestObject').get('foo')).toEqual('bar'); done(); }); }); - it("should not encode Parse Objects", async () => { + it('should not encode Parse Objects', async () => { await reconfigureServer({ encodeParseObjectInCloudFunction: false }); const user = new Parse.User(); - user.setUsername("username"); - user.setPassword("password"); - user.set("deleted", false); + user.setUsername('username'); + user.setPassword('password'); + user.set('deleted', false); await user.signUp(); Parse.Cloud.define( - "deleteAccount", + 'deleteAccount', async req => { expect(req.params.object instanceof Parse.Object).not.toBeTrue(); - return "Object deleted"; + return 'Object deleted'; }, { requireMaster: true, } ); await Parse.Cloud.run( - "deleteAccount", + 'deleteAccount', { object: user.toPointer() }, { useMasterKey: true } ); }); - it("allow cloud to encode Parse Objects", async () => { + it('allow cloud to encode Parse Objects', async () => { await reconfigureServer({ encodeParseObjectInCloudFunction: true }); const user = new Parse.User(); - user.setUsername("username"); - user.setPassword("password"); - user.set("deleted", false); + user.setUsername('username'); + user.setPassword('password'); + user.set('deleted', false); await user.signUp(); Parse.Cloud.define( - "deleteAccount", + 'deleteAccount', async req => { expect(req.params.object instanceof Parse.Object).toBeTrue(); - req.params.object.set("deleted", true); + req.params.object.set('deleted', true); await req.params.object.save(null, { useMasterKey: true }); - return "Object deleted"; + return 'Object deleted'; }, { requireMaster: true, } ); await Parse.Cloud.run( - "deleteAccount", + 'deleteAccount', { object: user.toPointer() }, { useMasterKey: true } ); }); - it("beforeSave should not affect fetched pointers", done => { - Parse.Cloud.beforeSave("BeforeSaveUnchanged", () => {}); + it('beforeSave should not affect fetched pointers', done => { + Parse.Cloud.beforeSave('BeforeSaveUnchanged', () => {}); - Parse.Cloud.beforeSave("BeforeSaveChanged", function (req) { - req.object.set("foo", "baz"); + Parse.Cloud.beforeSave('BeforeSaveChanged', function (req) { + req.object.set('foo', 'baz'); }); - const TestObject = Parse.Object.extend("TestObject"); + const TestObject = Parse.Object.extend('TestObject'); const BeforeSaveUnchangedObject = Parse.Object.extend( - "BeforeSaveUnchanged" + 'BeforeSaveUnchanged' ); - const BeforeSaveChangedObject = Parse.Object.extend("BeforeSaveChanged"); + const BeforeSaveChangedObject = Parse.Object.extend('BeforeSaveChanged'); const aTestObject = new TestObject(); - aTestObject.set("foo", "bar"); + aTestObject.set('foo', 'bar'); aTestObject .save() .then(aTestObject => { const aBeforeSaveUnchangedObject = new BeforeSaveUnchangedObject(); - aBeforeSaveUnchangedObject.set("aTestObject", aTestObject); + aBeforeSaveUnchangedObject.set('aTestObject', aTestObject); expect( - aBeforeSaveUnchangedObject.get("aTestObject").get("foo") - ).toEqual("bar"); + aBeforeSaveUnchangedObject.get('aTestObject').get('foo') + ).toEqual('bar'); return aBeforeSaveUnchangedObject.save(); }) .then(aBeforeSaveUnchangedObject => { expect( - aBeforeSaveUnchangedObject.get("aTestObject").get("foo") - ).toEqual("bar"); + aBeforeSaveUnchangedObject.get('aTestObject').get('foo') + ).toEqual('bar'); const aBeforeSaveChangedObject = new BeforeSaveChangedObject(); - aBeforeSaveChangedObject.set("aTestObject", aTestObject); - expect(aBeforeSaveChangedObject.get("aTestObject").get("foo")).toEqual( - "bar" + aBeforeSaveChangedObject.set('aTestObject', aTestObject); + expect(aBeforeSaveChangedObject.get('aTestObject').get('foo')).toEqual( + 'bar' ); return aBeforeSaveChangedObject.save(); }) .then(aBeforeSaveChangedObject => { - expect(aBeforeSaveChangedObject.get("aTestObject").get("foo")).toEqual( - "bar" + expect(aBeforeSaveChangedObject.get('aTestObject').get('foo')).toEqual( + 'bar' ); - expect(aBeforeSaveChangedObject.get("foo")).toEqual("baz"); + expect(aBeforeSaveChangedObject.get('foo')).toEqual('baz'); done(); }); }); - it("should fully delete objects when using `unset` with beforeSave (regression test for #1840)", done => { - const TestObject = Parse.Object.extend("TestObject"); - const NoBeforeSaveObject = Parse.Object.extend("NoBeforeSave"); - const BeforeSaveObject = Parse.Object.extend("BeforeSaveChanged"); + it('should fully delete objects when using `unset` with beforeSave (regression test for #1840)', done => { + const TestObject = Parse.Object.extend('TestObject'); + const NoBeforeSaveObject = Parse.Object.extend('NoBeforeSave'); + const BeforeSaveObject = Parse.Object.extend('BeforeSaveChanged'); - Parse.Cloud.beforeSave("BeforeSaveChanged", req => { + Parse.Cloud.beforeSave('BeforeSaveChanged', req => { const object = req.object; - object.set("before", "save"); + object.set('before', 'save'); }); - Parse.Cloud.define("removeme", () => { + Parse.Cloud.define('removeme', () => { const testObject = new TestObject(); return testObject .save() @@ -1500,12 +1500,12 @@ describe("Cloud Code", () => { return object.save(); }) .then(object => { - object.unset("remove"); + object.unset('remove'); return object.save(); }); }); - Parse.Cloud.define("removeme2", () => { + Parse.Cloud.define('removeme2', () => { const testObject = new TestObject(); return testObject .save() @@ -1514,20 +1514,20 @@ describe("Cloud Code", () => { return object.save(); }) .then(object => { - object.unset("remove"); + object.unset('remove'); return object.save(); }); }); - Parse.Cloud.run("removeme") + Parse.Cloud.run('removeme') .then(aNoBeforeSaveObj => { - expect(aNoBeforeSaveObj.get("remove")).toEqual(undefined); + expect(aNoBeforeSaveObj.get('remove')).toEqual(undefined); - return Parse.Cloud.run("removeme2"); + return Parse.Cloud.run('removeme2'); }) .then(aBeforeSaveObj => { - expect(aBeforeSaveObj.get("before")).toEqual("save"); - expect(aBeforeSaveObj.get("remove")).toEqual(undefined); + expect(aBeforeSaveObj.get('before')).toEqual('save'); + expect(aBeforeSaveObj.get('remove')).toEqual(undefined); done(); }) .catch(err => { @@ -1540,20 +1540,20 @@ describe("Cloud Code", () => { TODO: fix for Postgres trying to delete a field that doesn't exists doesn't play nice */ - it_exclude_dbs(["postgres"])( - "should fully delete objects when using `unset` and `set` with beforeSave (regression test for #1840)", + it_exclude_dbs(['postgres'])( + 'should fully delete objects when using `unset` and `set` with beforeSave (regression test for #1840)', done => { - const TestObject = Parse.Object.extend("TestObject"); - const BeforeSaveObject = Parse.Object.extend("BeforeSaveChanged"); + const TestObject = Parse.Object.extend('TestObject'); + const BeforeSaveObject = Parse.Object.extend('BeforeSaveChanged'); - Parse.Cloud.beforeSave("BeforeSaveChanged", req => { + Parse.Cloud.beforeSave('BeforeSaveChanged', req => { const object = req.object; - object.set("before", "save"); - object.unset("remove"); + object.set('before', 'save'); + object.unset('remove'); }); let object; - const testObject = new TestObject({ key: "value" }); + const testObject = new TestObject({ key: 'value' }); testObject .save() .then(() => { @@ -1564,8 +1564,8 @@ describe("Cloud Code", () => { }); }) .then(objectAgain => { - expect(objectAgain.get("remove")).toBeUndefined(); - expect(object.get("remove")).toBeUndefined(); + expect(objectAgain.get('remove')).toBeUndefined(); + expect(object.get('remove')).toBeUndefined(); done(); }) .catch(err => { @@ -1575,16 +1575,16 @@ describe("Cloud Code", () => { } ); - it("should not include relation op (regression test for #1606)", done => { - const TestObject = Parse.Object.extend("TestObject"); - const BeforeSaveObject = Parse.Object.extend("BeforeSaveChanged"); + it('should not include relation op (regression test for #1606)', done => { + const TestObject = Parse.Object.extend('TestObject'); + const BeforeSaveObject = Parse.Object.extend('BeforeSaveChanged'); let testObj; - Parse.Cloud.beforeSave("BeforeSaveChanged", req => { + Parse.Cloud.beforeSave('BeforeSaveChanged', req => { const object = req.object; - object.set("before", "save"); + object.set('before', 'save'); testObj = new TestObject(); return testObj.save().then(() => { - object.relation("testsRelation").add(testObj); + object.relation('testsRelation').add(testObj); }); }); @@ -1594,7 +1594,7 @@ describe("Cloud Code", () => { .then(objectAgain => { // Originally it would throw as it would be a non-relation expect(() => { - objectAgain.relation("testsRelation"); + objectAgain.relation('testsRelation'); }).not.toThrow(); done(); }) @@ -1608,63 +1608,63 @@ describe("Cloud Code", () => { * Checks that incrementing a value to a zero in a beforeSave hook * does not result in that key being omitted from the response. */ - it("before save increment does not return undefined", done => { - Parse.Cloud.define("cloudIncrementClassFunction", function (req) { - const CloudIncrementClass = Parse.Object.extend("CloudIncrementClass"); + it('before save increment does not return undefined', done => { + Parse.Cloud.define('cloudIncrementClassFunction', function (req) { + const CloudIncrementClass = Parse.Object.extend('CloudIncrementClass'); const obj = new CloudIncrementClass(); obj.id = req.params.objectId; return obj.save(); }); - Parse.Cloud.beforeSave("CloudIncrementClass", function (req) { + Parse.Cloud.beforeSave('CloudIncrementClass', function (req) { const obj = req.object; if (!req.master) { - obj.increment("points", -10); - obj.increment("num", -9); + obj.increment('points', -10); + obj.increment('num', -9); } }); - const CloudIncrementClass = Parse.Object.extend("CloudIncrementClass"); + const CloudIncrementClass = Parse.Object.extend('CloudIncrementClass'); const obj = new CloudIncrementClass(); - obj.set("points", 10); - obj.set("num", 10); + obj.set('points', 10); + obj.set('num', 10); obj.save(null, { useMasterKey: true }).then(function () { - Parse.Cloud.run("cloudIncrementClassFunction", { objectId: obj.id }).then( + Parse.Cloud.run('cloudIncrementClassFunction', { objectId: obj.id }).then( function (savedObj) { - expect(savedObj.get("num")).toEqual(1); - expect(savedObj.get("points")).toEqual(0); + expect(savedObj.get('num')).toEqual(1); + expect(savedObj.get('points')).toEqual(0); done(); } ); }); }); - it("before save can revert fields", async () => { - Parse.Cloud.beforeSave("TestObject", ({ object }) => { - object.revert("foo"); + it('before save can revert fields', async () => { + Parse.Cloud.beforeSave('TestObject', ({ object }) => { + object.revert('foo'); return object; }); - Parse.Cloud.afterSave("TestObject", ({ object }) => { - expect(object.get("foo")).toBeUndefined(); + Parse.Cloud.afterSave('TestObject', ({ object }) => { + expect(object.get('foo')).toBeUndefined(); return object; }); const obj = new TestObject(); - obj.set("foo", "bar"); + obj.set('foo', 'bar'); await obj.save(); - expect(obj.get("foo")).toBeUndefined(); + expect(obj.get('foo')).toBeUndefined(); await obj.fetch(); - expect(obj.get("foo")).toBeUndefined(); + expect(obj.get('foo')).toBeUndefined(); }); - it("before save can revert fields with existing object", async () => { + it('before save can revert fields with existing object', async () => { Parse.Cloud.beforeSave( - "TestObject", + 'TestObject', ({ object }) => { - object.revert("foo"); + object.revert('foo'); return object; }, { @@ -1673,9 +1673,9 @@ describe("Cloud Code", () => { ); Parse.Cloud.afterSave( - "TestObject", + 'TestObject', ({ object }) => { - expect(object.get("foo")).toBe("bar"); + expect(object.get('foo')).toBe('bar'); return object; }, { @@ -1684,55 +1684,55 @@ describe("Cloud Code", () => { ); const obj = new TestObject(); - obj.set("foo", "bar"); + obj.set('foo', 'bar'); await obj.save(null, { useMasterKey: true }); - expect(obj.get("foo")).toBe("bar"); - obj.set("foo", "yolo"); + expect(obj.get('foo')).toBe('bar'); + obj.set('foo', 'yolo'); await obj.save(); - expect(obj.get("foo")).toBe("bar"); + expect(obj.get('foo')).toBe('bar'); }); - it("create role with name and ACL and a beforeSave", async () => { + it('create role with name and ACL and a beforeSave', async () => { Parse.Cloud.beforeSave(Parse.Role, ({ object }) => { return object; }); const obj = new Parse.Role( - "TestRole", - new Parse.ACL({ "*": { read: true, write: true } }) + 'TestRole', + new Parse.ACL({ '*': { read: true, write: true } }) ); await obj.save(); expect(obj.getACL()).toEqual( - new Parse.ACL({ "*": { read: true, write: true } }) + new Parse.ACL({ '*': { read: true, write: true } }) ); - expect(obj.get("name")).toEqual("TestRole"); + expect(obj.get('name')).toEqual('TestRole'); await obj.fetch(); expect(obj.getACL()).toEqual( - new Parse.ACL({ "*": { read: true, write: true } }) + new Parse.ACL({ '*': { read: true, write: true } }) ); - expect(obj.get("name")).toEqual("TestRole"); + expect(obj.get('name')).toEqual('TestRole'); }); - it("can unset in afterSave", async () => { - Parse.Cloud.beforeSave("TestObject", ({ object }) => { + it('can unset in afterSave', async () => { + Parse.Cloud.beforeSave('TestObject', ({ object }) => { if (!object.existed()) { - object.set("secret", true); + object.set('secret', true); return object; } - object.revert("secret"); + object.revert('secret'); }); - Parse.Cloud.afterSave("TestObject", ({ object }) => { - object.unset("secret"); + Parse.Cloud.afterSave('TestObject', ({ object }) => { + object.unset('secret'); }); Parse.Cloud.beforeFind( - "TestObject", + 'TestObject', ({ query }) => { - query.exclude("secret"); + query.exclude('secret'); }, { skipWithMasterKey: true, @@ -1741,45 +1741,45 @@ describe("Cloud Code", () => { const obj = new TestObject(); await obj.save(); - expect(obj.get("secret")).toBeUndefined(); + expect(obj.get('secret')).toBeUndefined(); await obj.fetch(); - expect(obj.get("secret")).toBeUndefined(); + expect(obj.get('secret')).toBeUndefined(); await obj.fetch({ useMasterKey: true }); - expect(obj.get("secret")).toBe(true); + expect(obj.get('secret')).toBe(true); }); - it("should revert in beforeSave", async () => { - Parse.Cloud.beforeSave("MyObject", ({ object }) => { + it('should revert in beforeSave', async () => { + Parse.Cloud.beforeSave('MyObject', ({ object }) => { if (!object.existed()) { - object.set("count", 0); + object.set('count', 0); return object; } - object.revert("count"); + object.revert('count'); return object; }); - const obj = await new Parse.Object("MyObject").save(); - expect(obj.get("count")).toBe(0); - obj.set("count", 10); + const obj = await new Parse.Object('MyObject').save(); + expect(obj.get('count')).toBe(0); + obj.set('count', 10); await obj.save(); - expect(obj.get("count")).toBe(0); + expect(obj.get('count')).toBe(0); await obj.fetch(); - expect(obj.get("count")).toBe(0); + expect(obj.get('count')).toBe(0); }); - it("pointer should not be cleared by triggers", async () => { - Parse.Cloud.afterSave("MyObject", () => {}); - const foo = await new Parse.Object("Test", { foo: "bar" }).save(); - const obj = await new Parse.Object("MyObject", { foo }).save(); - const foo2 = obj.get("foo"); - expect(foo2.get("foo")).toBe("bar"); + it('pointer should not be cleared by triggers', async () => { + Parse.Cloud.afterSave('MyObject', () => {}); + const foo = await new Parse.Object('Test', { foo: 'bar' }).save(); + const obj = await new Parse.Object('MyObject', { foo }).save(); + const foo2 = obj.get('foo'); + expect(foo2.get('foo')).toBe('bar'); }); - it("can set a pointer in triggers", async () => { - Parse.Cloud.beforeSave("MyObject", () => {}); + it('can set a pointer in triggers', async () => { + Parse.Cloud.beforeSave('MyObject', () => {}); Parse.Cloud.afterSave( - "MyObject", + 'MyObject', async ({ object }) => { - const foo = await new Parse.Object("Test", { foo: "bar" }).save(); + const foo = await new Parse.Object('Test', { foo: 'bar' }).save(); object.set({ foo }); await object.save(null, { useMasterKey: true }); }, @@ -1787,40 +1787,40 @@ describe("Cloud Code", () => { skipWithMasterKey: true, } ); - const obj = await new Parse.Object("MyObject").save(); - const foo2 = obj.get("foo"); - expect(foo2.get("foo")).toBe("bar"); + const obj = await new Parse.Object('MyObject').save(); + const foo2 = obj.get('foo'); + expect(foo2.get('foo')).toBe('bar'); }); - it("beforeSave should not sanitize database", async done => { + it('beforeSave should not sanitize database', async done => { const { adapter } = Config.get(Parse.applicationId).database; - const spy = spyOn(adapter, "findOneAndUpdate").and.callThrough(); + const spy = spyOn(adapter, 'findOneAndUpdate').and.callThrough(); spy.calls.saveArgumentsByValue(); let count = 0; - Parse.Cloud.beforeSave("CloudIncrementNested", req => { + Parse.Cloud.beforeSave('CloudIncrementNested', req => { count += 1; - req.object.set("foo", "baz"); - expect(typeof req.object.get("objectField").number).toBe("number"); + req.object.set('foo', 'baz'); + expect(typeof req.object.get('objectField').number).toBe('number'); }); - Parse.Cloud.afterSave("CloudIncrementNested", req => { - expect(typeof req.object.get("objectField").number).toBe("number"); + Parse.Cloud.afterSave('CloudIncrementNested', req => { + expect(typeof req.object.get('objectField').number).toBe('number'); }); - const obj = new Parse.Object("CloudIncrementNested"); - obj.set("objectField", { number: 5 }); - obj.set("foo", "bar"); + const obj = new Parse.Object('CloudIncrementNested'); + obj.set('objectField', { number: 5 }); + obj.set('foo', 'bar'); await obj.save(); - obj.increment("objectField.number", 10); + obj.increment('objectField.number', 10); await obj.save(); const [, , , /* className */ /* schema */ /* query */ update] = adapter.findOneAndUpdate.calls.first().args; expect(update).toEqual({ - "objectField.number": { __op: "Increment", amount: 10 }, - foo: "baz", + 'objectField.number': { __op: 'Increment', amount: 10 }, + foo: 'baz', updatedAt: obj.updatedAt.toISOString(), }); @@ -1831,38 +1831,38 @@ describe("Cloud Code", () => { * Verifies that an afterSave hook throwing an exception * will not prevent a successful save response from being returned */ - it("should succeed on afterSave exception", done => { - Parse.Cloud.afterSave("AfterSaveTestClass", function () { - throw "Exception"; + it('should succeed on afterSave exception', done => { + Parse.Cloud.afterSave('AfterSaveTestClass', function () { + throw 'Exception'; }); - const AfterSaveTestClass = Parse.Object.extend("AfterSaveTestClass"); + const AfterSaveTestClass = Parse.Object.extend('AfterSaveTestClass'); const obj = new AfterSaveTestClass(); obj.save().then(done, done.fail); }); - describe("cloud jobs", () => { - it("should define a job", done => { + describe('cloud jobs', () => { + it('should define a job', done => { expect(() => { - Parse.Cloud.job("myJob", ({ message }) => { - message("Hello, world!!!"); + Parse.Cloud.job('myJob', ({ message }) => { + message('Hello, world!!!'); }); }).not.toThrow(); request({ - method: "POST", - url: "http://localhost:8378/1/jobs/myJob", + method: 'POST', + url: 'http://localhost:8378/1/jobs/myJob', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Master-Key": Parse.masterKey, + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': Parse.masterKey, }, }) .then(async response => { - const jobStatusId = response.headers["x-parse-job-status-id"]; + const jobStatusId = response.headers['x-parse-job-status-id']; const checkJobStatus = async () => { const jobStatus = await getJobStatus(jobStatusId); return ( - jobStatus.get("finishedAt") && - jobStatus.get("message") === "Hello, world!!!" + jobStatus.get('finishedAt') && + jobStatus.get('message') === 'Hello, world!!!' ); }; while (!(await checkJobStatus())) { @@ -1873,21 +1873,21 @@ describe("Cloud Code", () => { .catch(done.fail); }); - it("should not run without master key", done => { + it('should not run without master key', done => { expect(() => { - Parse.Cloud.job("myJob", () => {}); + Parse.Cloud.job('myJob', () => {}); }).not.toThrow(); request({ - method: "POST", - url: "http://localhost:8378/1/jobs/myJob", + method: 'POST', + url: 'http://localhost:8378/1/jobs/myJob', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', }, }).then( () => { - fail("Expected to be unauthorized"); + fail('Expected to be unauthorized'); done(); }, err => { @@ -1897,30 +1897,30 @@ describe("Cloud Code", () => { ); }); - it("should run with master key", done => { + it('should run with master key', done => { expect(() => { - Parse.Cloud.job("myJob", (req, res) => { + Parse.Cloud.job('myJob', (req, res) => { expect(req.functionName).toBeUndefined(); - expect(req.jobName).toBe("myJob"); - expect(typeof req.jobId).toBe("string"); - expect(typeof req.message).toBe("function"); - expect(typeof res).toBe("undefined"); + expect(req.jobName).toBe('myJob'); + expect(typeof req.jobId).toBe('string'); + expect(typeof req.message).toBe('function'); + expect(typeof res).toBe('undefined'); }); }).not.toThrow(); request({ - method: "POST", - url: "http://localhost:8378/1/jobs/myJob", + method: 'POST', + url: 'http://localhost:8378/1/jobs/myJob', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Master-Key": Parse.masterKey, + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': Parse.masterKey, }, }) .then(async response => { - const jobStatusId = response.headers["x-parse-job-status-id"]; + const jobStatusId = response.headers['x-parse-job-status-id']; const checkJobStatus = async () => { const jobStatus = await getJobStatus(jobStatusId); - return jobStatus.get("finishedAt"); + return jobStatus.get('finishedAt'); }; while (!(await checkJobStatus())) { await new Promise(resolve => setTimeout(resolve, 100)); @@ -1930,26 +1930,26 @@ describe("Cloud Code", () => { .catch(done.fail); }); - it("should run with master key basic auth", done => { + it('should run with master key basic auth', done => { expect(() => { - Parse.Cloud.job("myJob", (req, res) => { + Parse.Cloud.job('myJob', (req, res) => { expect(req.functionName).toBeUndefined(); - expect(req.jobName).toBe("myJob"); - expect(typeof req.jobId).toBe("string"); - expect(typeof req.message).toBe("function"); - expect(typeof res).toBe("undefined"); + expect(req.jobName).toBe('myJob'); + expect(typeof req.jobId).toBe('string'); + expect(typeof req.message).toBe('function'); + expect(typeof res).toBe('undefined'); }); }).not.toThrow(); request({ - method: "POST", + method: 'POST', url: `http://${Parse.applicationId}:${Parse.masterKey}@localhost:8378/1/jobs/myJob`, }) .then(async response => { - const jobStatusId = response.headers["x-parse-job-status-id"]; + const jobStatusId = response.headers['x-parse-job-status-id']; const checkJobStatus = async () => { const jobStatus = await getJobStatus(jobStatusId); - return jobStatus.get("finishedAt"); + return jobStatus.get('finishedAt'); }; while (!(await checkJobStatus())) { await new Promise(resolve => setTimeout(resolve, 100)); @@ -1959,35 +1959,35 @@ describe("Cloud Code", () => { .catch(done.fail); }); - it("should set the message / success on the job", done => { - Parse.Cloud.job("myJob", req => { + it('should set the message / success on the job', done => { + Parse.Cloud.job('myJob', req => { return req - .message("hello") + .message('hello') .then(() => { return getJobStatus(req.jobId); }) .then(jobStatus => { - expect(jobStatus.get("message")).toEqual("hello"); - expect(jobStatus.get("status")).toEqual("running"); + expect(jobStatus.get('message')).toEqual('hello'); + expect(jobStatus.get('status')).toEqual('running'); }); }); request({ - method: "POST", - url: "http://localhost:8378/1/jobs/myJob", + method: 'POST', + url: 'http://localhost:8378/1/jobs/myJob', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Master-Key": Parse.masterKey, + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': Parse.masterKey, }, }) .then(async response => { - const jobStatusId = response.headers["x-parse-job-status-id"]; + const jobStatusId = response.headers['x-parse-job-status-id']; const checkJobStatus = async () => { const jobStatus = await getJobStatus(jobStatusId); return ( - jobStatus.get("finishedAt") && - jobStatus.get("message") === "hello" && - jobStatus.get("status") === "succeeded" + jobStatus.get('finishedAt') && + jobStatus.get('message') === 'hello' && + jobStatus.get('status') === 'succeeded' ); }; while (!(await checkJobStatus())) { @@ -1998,27 +1998,27 @@ describe("Cloud Code", () => { .catch(done.fail); }); - it("should set the failure on the job", done => { - Parse.Cloud.job("myJob", () => { - return Promise.reject("Something went wrong"); + it('should set the failure on the job', done => { + Parse.Cloud.job('myJob', () => { + return Promise.reject('Something went wrong'); }); request({ - method: "POST", - url: "http://localhost:8378/1/jobs/myJob", + method: 'POST', + url: 'http://localhost:8378/1/jobs/myJob', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Master-Key": Parse.masterKey, + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': Parse.masterKey, }, }) .then(async response => { - const jobStatusId = response.headers["x-parse-job-status-id"]; + const jobStatusId = response.headers['x-parse-job-status-id']; const checkJobStatus = async () => { const jobStatus = await getJobStatus(jobStatusId); return ( - jobStatus.get("finishedAt") && - jobStatus.get("message") === "Something went wrong" && - jobStatus.get("status") === "failed" + jobStatus.get('finishedAt') && + jobStatus.get('message') === 'Something went wrong' && + jobStatus.get('status') === 'failed' ); }; while (!(await checkJobStatus())) { @@ -2029,157 +2029,157 @@ describe("Cloud Code", () => { .catch(done.fail); }); - it("should set the failure message on the job error", async () => { - Parse.Cloud.job("myJobError", () => { + it('should set the failure message on the job error', async () => { + Parse.Cloud.job('myJobError', () => { throw new Parse.Error( Parse.Error.OBJECT_NOT_FOUND, - "Something went wrong" + 'Something went wrong' ); }); - const job = await Parse.Cloud.startJob("myJobError"); + const job = await Parse.Cloud.startJob('myJobError'); let jobStatus, status; - while (status !== "failed") { + while (status !== 'failed') { if (jobStatus) { await new Promise(resolve => setTimeout(resolve, 10)); } jobStatus = await Parse.Cloud.getJobStatus(job); - status = jobStatus.get("status"); + status = jobStatus.get('status'); } - expect(jobStatus.get("message")).toEqual("Something went wrong"); + expect(jobStatus.get('message')).toEqual('Something went wrong'); }); function getJobStatus(jobId) { - const q = new Parse.Query("_JobStatus"); + const q = new Parse.Query('_JobStatus'); return q.get(jobId, { useMasterKey: true }); } }); }); -describe("cloud functions", () => { - it("Should have request ip", done => { - Parse.Cloud.define("myFunction", req => { +describe('cloud functions', () => { + it('Should have request ip', done => { + Parse.Cloud.define('myFunction', req => { expect(req.ip).toBeDefined(); - return "success"; + return 'success'; }); - Parse.Cloud.run("myFunction", {}).then(() => done()); + Parse.Cloud.run('myFunction', {}).then(() => done()); }); }); -describe("beforeSave hooks", () => { - it("should have request headers", done => { - Parse.Cloud.beforeSave("MyObject", req => { +describe('beforeSave hooks', () => { + it('should have request headers', done => { + Parse.Cloud.beforeSave('MyObject', req => { expect(req.headers).toBeDefined(); }); - const MyObject = Parse.Object.extend("MyObject"); + const MyObject = Parse.Object.extend('MyObject'); const myObject = new MyObject(); myObject.save().then(() => done()); }); - it("should have request ip", done => { - Parse.Cloud.beforeSave("MyObject", req => { + it('should have request ip', done => { + Parse.Cloud.beforeSave('MyObject', req => { expect(req.ip).toBeDefined(); }); - const MyObject = Parse.Object.extend("MyObject"); + const MyObject = Parse.Object.extend('MyObject'); const myObject = new MyObject(); myObject.save().then(() => done()); }); - it("should respect custom object ids (#6733)", async () => { - Parse.Cloud.beforeSave("TestObject", req => { - expect(req.object.id).toEqual("test_6733"); + it('should respect custom object ids (#6733)', async () => { + Parse.Cloud.beforeSave('TestObject', req => { + expect(req.object.id).toEqual('test_6733'); }); await reconfigureServer({ allowCustomObjectId: true }); const req = request({ // Parse JS SDK does not currently support custom object ids (see #1097), so we do a REST request - method: "POST", - url: "http://localhost:8378/1/classes/TestObject", + method: 'POST', + url: 'http://localhost:8378/1/classes/TestObject', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }, body: { - objectId: "test_6733", - foo: "bar", + objectId: 'test_6733', + foo: 'bar', }, }); { const res = await req; - expect(res.data.objectId).toEqual("test_6733"); + expect(res.data.objectId).toEqual('test_6733'); } - const query = new Parse.Query("TestObject"); - query.equalTo("objectId", "test_6733"); + const query = new Parse.Query('TestObject'); + query.equalTo('objectId', 'test_6733'); const res = await query.find(); expect(res.length).toEqual(1); - expect(res[0].get("foo")).toEqual("bar"); + expect(res[0].get('foo')).toEqual('bar'); }); }); -describe("afterSave hooks", () => { - it("should have request headers", done => { - Parse.Cloud.afterSave("MyObject", req => { +describe('afterSave hooks', () => { + it('should have request headers', done => { + Parse.Cloud.afterSave('MyObject', req => { expect(req.headers).toBeDefined(); }); - const MyObject = Parse.Object.extend("MyObject"); + const MyObject = Parse.Object.extend('MyObject'); const myObject = new MyObject(); myObject.save().then(() => done()); }); - it("should have request ip", done => { - Parse.Cloud.afterSave("MyObject", req => { + it('should have request ip', done => { + Parse.Cloud.afterSave('MyObject', req => { expect(req.ip).toBeDefined(); }); - const MyObject = Parse.Object.extend("MyObject"); + const MyObject = Parse.Object.extend('MyObject'); const myObject = new MyObject(); myObject.save().then(() => done()); }); - it("should unset in afterSave", async () => { + it('should unset in afterSave', async () => { Parse.Cloud.afterSave( - "MyObject", + 'MyObject', ({ object }) => { - object.unset("secret"); + object.unset('secret'); }, { skipWithMasterKey: true, } ); - const obj = new Parse.Object("MyObject"); - obj.set("secret", "bar"); + const obj = new Parse.Object('MyObject'); + obj.set('secret', 'bar'); await obj.save(); - expect(obj.get("secret")).toBeUndefined(); + expect(obj.get('secret')).toBeUndefined(); await obj.fetch(); - expect(obj.get("secret")).toBe("bar"); + expect(obj.get('secret')).toBe('bar'); }); - it("should unset", async () => { - Parse.Cloud.beforeSave("MyObject", ({ object }) => { - object.set("secret", "hidden"); + it('should unset', async () => { + Parse.Cloud.beforeSave('MyObject', ({ object }) => { + object.set('secret', 'hidden'); }); - Parse.Cloud.afterSave("MyObject", ({ object }) => { - object.unset("secret"); + Parse.Cloud.afterSave('MyObject', ({ object }) => { + object.unset('secret'); }); - const obj = await new Parse.Object("MyObject").save(); - expect(obj.get("secret")).toBeUndefined(); + const obj = await new Parse.Object('MyObject').save(); + expect(obj.get('secret')).toBeUndefined(); }); }); -describe("beforeDelete hooks", () => { - it("should have request headers", done => { - Parse.Cloud.beforeDelete("MyObject", req => { +describe('beforeDelete hooks', () => { + it('should have request headers', done => { + Parse.Cloud.beforeDelete('MyObject', req => { expect(req.headers).toBeDefined(); }); - const MyObject = Parse.Object.extend("MyObject"); + const MyObject = Parse.Object.extend('MyObject'); const myObject = new MyObject(); myObject .save() @@ -2187,12 +2187,12 @@ describe("beforeDelete hooks", () => { .then(() => done()); }); - it("should have request ip", done => { - Parse.Cloud.beforeDelete("MyObject", req => { + it('should have request ip', done => { + Parse.Cloud.beforeDelete('MyObject', req => { expect(req.ip).toBeDefined(); }); - const MyObject = Parse.Object.extend("MyObject"); + const MyObject = Parse.Object.extend('MyObject'); const myObject = new MyObject(); myObject .save() @@ -2201,13 +2201,13 @@ describe("beforeDelete hooks", () => { }); }); -describe("afterDelete hooks", () => { - it("should have request headers", done => { - Parse.Cloud.afterDelete("MyObject", req => { +describe('afterDelete hooks', () => { + it('should have request headers', done => { + Parse.Cloud.afterDelete('MyObject', req => { expect(req.headers).toBeDefined(); }); - const MyObject = Parse.Object.extend("MyObject"); + const MyObject = Parse.Object.extend('MyObject'); const myObject = new MyObject(); myObject .save() @@ -2215,12 +2215,12 @@ describe("afterDelete hooks", () => { .then(() => done()); }); - it("should have request ip", done => { - Parse.Cloud.afterDelete("MyObject", req => { + it('should have request ip', done => { + Parse.Cloud.afterDelete('MyObject', req => { expect(req.ip).toBeDefined(); }); - const MyObject = Parse.Object.extend("MyObject"); + const MyObject = Parse.Object.extend('MyObject'); const myObject = new MyObject(); myObject .save() @@ -2229,80 +2229,80 @@ describe("afterDelete hooks", () => { }); }); -describe("beforeFind hooks", () => { - it("should add beforeFind trigger", done => { - Parse.Cloud.beforeFind("MyObject", req => { +describe('beforeFind hooks', () => { + it('should add beforeFind trigger', done => { + Parse.Cloud.beforeFind('MyObject', req => { const q = req.query; expect(q instanceof Parse.Query).toBe(true); const jsonQuery = q.toJSON(); - expect(jsonQuery.where.key).toEqual("value"); + expect(jsonQuery.where.key).toEqual('value'); expect(jsonQuery.where.some).toEqual({ $gt: 10 }); - expect(jsonQuery.include).toEqual("otherKey,otherValue"); - expect(jsonQuery.excludeKeys).toBe("exclude"); + expect(jsonQuery.include).toEqual('otherKey,otherValue'); + expect(jsonQuery.excludeKeys).toBe('exclude'); expect(jsonQuery.limit).toEqual(100); expect(jsonQuery.skip).toBe(undefined); - expect(jsonQuery.order).toBe("key"); - expect(jsonQuery.keys).toBe("select"); - expect(jsonQuery.readPreference).toBe("PRIMARY"); - expect(jsonQuery.includeReadPreference).toBe("SECONDARY"); - expect(jsonQuery.subqueryReadPreference).toBe("SECONDARY_PREFERRED"); + expect(jsonQuery.order).toBe('key'); + expect(jsonQuery.keys).toBe('select'); + expect(jsonQuery.readPreference).toBe('PRIMARY'); + expect(jsonQuery.includeReadPreference).toBe('SECONDARY'); + expect(jsonQuery.subqueryReadPreference).toBe('SECONDARY_PREFERRED'); expect(req.isGet).toEqual(false); }); - const query = new Parse.Query("MyObject"); - query.equalTo("key", "value"); - query.greaterThan("some", 10); - query.include("otherKey"); - query.include("otherValue"); - query.ascending("key"); - query.select("select"); - query.exclude("exclude"); - query.readPreference("PRIMARY", "SECONDARY", "SECONDARY_PREFERRED"); + const query = new Parse.Query('MyObject'); + query.equalTo('key', 'value'); + query.greaterThan('some', 10); + query.include('otherKey'); + query.include('otherValue'); + query.ascending('key'); + query.select('select'); + query.exclude('exclude'); + query.readPreference('PRIMARY', 'SECONDARY', 'SECONDARY_PREFERRED'); query.find().then(() => { done(); }); }); - it("should use modify", done => { - Parse.Cloud.beforeFind("MyObject", req => { + it('should use modify', done => { + Parse.Cloud.beforeFind('MyObject', req => { const q = req.query; - q.equalTo("forced", true); + q.equalTo('forced', true); }); - const obj0 = new Parse.Object("MyObject"); - obj0.set("forced", false); + const obj0 = new Parse.Object('MyObject'); + obj0.set('forced', false); - const obj1 = new Parse.Object("MyObject"); - obj1.set("forced", true); + const obj1 = new Parse.Object('MyObject'); + obj1.set('forced', true); Parse.Object.saveAll([obj0, obj1]).then(() => { - const query = new Parse.Query("MyObject"); - query.equalTo("forced", false); + const query = new Parse.Query('MyObject'); + query.equalTo('forced', false); query.find().then(results => { expect(results.length).toBe(1); const firstResult = results[0]; - expect(firstResult.get("forced")).toBe(true); + expect(firstResult.get('forced')).toBe(true); done(); }); }); }); - it("should use the modified the query", done => { - Parse.Cloud.beforeFind("MyObject", req => { + it('should use the modified the query', done => { + Parse.Cloud.beforeFind('MyObject', req => { const q = req.query; - const otherQuery = new Parse.Query("MyObject"); - otherQuery.equalTo("forced", true); + const otherQuery = new Parse.Query('MyObject'); + otherQuery.equalTo('forced', true); return Parse.Query.or(q, otherQuery); }); - const obj0 = new Parse.Object("MyObject"); - obj0.set("forced", false); + const obj0 = new Parse.Object('MyObject'); + obj0.set('forced', false); - const obj1 = new Parse.Object("MyObject"); - obj1.set("forced", true); + const obj1 = new Parse.Object('MyObject'); + obj1.set('forced', true); Parse.Object.saveAll([obj0, obj1]).then(() => { - const query = new Parse.Query("MyObject"); - query.equalTo("forced", false); + const query = new Parse.Query('MyObject'); + query.equalTo('forced', false); query.find().then(results => { expect(results.length).toBe(2); done(); @@ -2310,77 +2310,77 @@ describe("beforeFind hooks", () => { }); }); - it("should have object found with nested relational data query", async () => { - const obj1 = Parse.Object.extend("TestObject"); - const obj2 = Parse.Object.extend("TestObject2"); + it('should have object found with nested relational data query', async () => { + const obj1 = Parse.Object.extend('TestObject'); + const obj2 = Parse.Object.extend('TestObject2'); let item2 = new obj2(); item2 = await item2.save(); let item1 = new obj1(); - const relation = item1.relation("rel"); + const relation = item1.relation('rel'); relation.add(item2); item1 = await item1.save(); - Parse.Cloud.beforeFind("TestObject", req => { - const additionalQ = new Parse.Query("TestObject"); - additionalQ.equalTo("rel", item2); + Parse.Cloud.beforeFind('TestObject', req => { + const additionalQ = new Parse.Query('TestObject'); + additionalQ.equalTo('rel', item2); return Parse.Query.and(req.query, additionalQ); }); - const q = new Parse.Query("TestObject"); + const q = new Parse.Query('TestObject'); const res = await q.first(); expect(res.id).toEqual(item1.id); }); - it("should use the modified exclude query", async () => { - Parse.Cloud.beforeFind("MyObject", req => { + it('should use the modified exclude query', async () => { + Parse.Cloud.beforeFind('MyObject', req => { const q = req.query; - q.exclude("number"); + q.exclude('number'); }); - const obj = new Parse.Object("MyObject"); - obj.set("number", 100); - obj.set("string", "hello"); + const obj = new Parse.Object('MyObject'); + obj.set('number', 100); + obj.set('string', 'hello'); await obj.save(); - const query = new Parse.Query("MyObject"); - query.equalTo("objectId", obj.id); + const query = new Parse.Query('MyObject'); + query.equalTo('objectId', obj.id); const results = await query.find(); expect(results.length).toBe(1); - expect(results[0].get("number")).toBeUndefined(); - expect(results[0].get("string")).toBe("hello"); + expect(results[0].get('number')).toBeUndefined(); + expect(results[0].get('string')).toBe('hello'); }); - it("should reject queries", done => { - Parse.Cloud.beforeFind("MyObject", () => { - return Promise.reject("Do not run that query"); + it('should reject queries', done => { + Parse.Cloud.beforeFind('MyObject', () => { + return Promise.reject('Do not run that query'); }); - const query = new Parse.Query("MyObject"); + const query = new Parse.Query('MyObject'); query.find().then( () => { - fail("should not succeed"); + fail('should not succeed'); done(); }, err => { expect(err.code).toBe(Parse.Error.SCRIPT_FAILED); - expect(err.message).toEqual("Do not run that query"); + expect(err.message).toEqual('Do not run that query'); done(); } ); }); - it_id("6ef0d226-af30-4dfd-8306-972a1b4becd3")(it)( - "should handle empty where", + it_id('6ef0d226-af30-4dfd-8306-972a1b4becd3')(it)( + 'should handle empty where', done => { - Parse.Cloud.beforeFind("MyObject", req => { - const otherQuery = new Parse.Query("MyObject"); - otherQuery.equalTo("some", true); + Parse.Cloud.beforeFind('MyObject', req => { + const otherQuery = new Parse.Query('MyObject'); + otherQuery.equalTo('some', true); return Parse.Query.or(req.query, otherQuery); }); request({ - url: "http://localhost:8378/1/classes/MyObject", + url: 'http://localhost:8378/1/classes/MyObject', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', }, }).then( () => { @@ -2394,251 +2394,251 @@ describe("beforeFind hooks", () => { } ); - it("should handle sorting where", done => { - Parse.Cloud.beforeFind("MyObject", req => { + it('should handle sorting where', done => { + Parse.Cloud.beforeFind('MyObject', req => { const query = req.query; - query.ascending("score"); + query.ascending('score'); return query; }); const count = 20; const objects = []; while (objects.length != count) { - const object = new Parse.Object("MyObject"); - object.set("score", Math.floor(Math.random() * 100)); + const object = new Parse.Object('MyObject'); + object.set('score', Math.floor(Math.random() * 100)); objects.push(object); } Parse.Object.saveAll(objects) .then(() => { - const query = new Parse.Query("MyObject"); + const query = new Parse.Query('MyObject'); return query.find(); }) .then(objects => { let lastScore = -1; objects.forEach(element => { - expect(element.get("score") >= lastScore).toBe(true); - lastScore = element.get("score"); + expect(element.get('score') >= lastScore).toBe(true); + lastScore = element.get('score'); }); }) .then(done) .catch(done.fail); }); - it("should add beforeFind trigger using get API", done => { + it('should add beforeFind trigger using get API', done => { const hook = { method: function (req) { expect(req.isGet).toEqual(true); return Promise.resolve(); }, }; - spyOn(hook, "method").and.callThrough(); - Parse.Cloud.beforeFind("MyObject", hook.method); - const obj = new Parse.Object("MyObject"); - obj.set("secretField", "SSID"); + spyOn(hook, 'method').and.callThrough(); + Parse.Cloud.beforeFind('MyObject', hook.method); + const obj = new Parse.Object('MyObject'); + obj.set('secretField', 'SSID'); obj.save().then(function () { request({ - method: "GET", - url: "http://localhost:8378/1/classes/MyObject/" + obj.id, + method: 'GET', + url: 'http://localhost:8378/1/classes/MyObject/' + obj.id, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }, json: true, }).then(response => { const body = response.data; - expect(body.secretField).toEqual("SSID"); + expect(body.secretField).toEqual('SSID'); expect(hook.method).toHaveBeenCalled(); done(); }); }); }); - it("sets correct beforeFind trigger isGet parameter for Parse.Object.fetch request", async () => { + it('sets correct beforeFind trigger isGet parameter for Parse.Object.fetch request', async () => { const hook = { method: req => { expect(req.isGet).toEqual(true); return Promise.resolve(); }, }; - spyOn(hook, "method").and.callThrough(); - Parse.Cloud.beforeFind("MyObject", hook.method); - const obj = new Parse.Object("MyObject"); + spyOn(hook, 'method').and.callThrough(); + Parse.Cloud.beforeFind('MyObject', hook.method); + const obj = new Parse.Object('MyObject'); await obj.save(); const getObj = await obj.fetch(); expect(getObj).toBeInstanceOf(Parse.Object); expect(hook.method).toHaveBeenCalledTimes(1); }); - it("sets correct beforeFind trigger isGet parameter for Parse.Query.get request", async () => { + it('sets correct beforeFind trigger isGet parameter for Parse.Query.get request', async () => { const hook = { method: req => { expect(req.isGet).toEqual(false); return Promise.resolve(); }, }; - spyOn(hook, "method").and.callThrough(); - Parse.Cloud.beforeFind("MyObject", hook.method); - const obj = new Parse.Object("MyObject"); + spyOn(hook, 'method').and.callThrough(); + Parse.Cloud.beforeFind('MyObject', hook.method); + const obj = new Parse.Object('MyObject'); await obj.save(); - const query = new Parse.Query("MyObject"); + const query = new Parse.Query('MyObject'); const getObj = await query.get(obj.id); expect(getObj).toBeInstanceOf(Parse.Object); expect(hook.method).toHaveBeenCalledTimes(1); }); - it("sets correct beforeFind trigger isGet parameter for Parse.Query.find request", async () => { + it('sets correct beforeFind trigger isGet parameter for Parse.Query.find request', async () => { const hook = { method: req => { expect(req.isGet).toEqual(false); return Promise.resolve(); }, }; - spyOn(hook, "method").and.callThrough(); - Parse.Cloud.beforeFind("MyObject", hook.method); - const obj = new Parse.Object("MyObject"); + spyOn(hook, 'method').and.callThrough(); + Parse.Cloud.beforeFind('MyObject', hook.method); + const obj = new Parse.Object('MyObject'); await obj.save(); - const query = new Parse.Query("MyObject"); + const query = new Parse.Query('MyObject'); const findObjs = await query.find(); expect(findObjs?.[0]).toBeInstanceOf(Parse.Object); expect(hook.method).toHaveBeenCalledTimes(1); }); - it("should have request headers", done => { - Parse.Cloud.beforeFind("MyObject", req => { + it('should have request headers', done => { + Parse.Cloud.beforeFind('MyObject', req => { expect(req.headers).toBeDefined(); }); - const MyObject = Parse.Object.extend("MyObject"); + const MyObject = Parse.Object.extend('MyObject'); const myObject = new MyObject(); myObject .save() .then(myObj => { - const query = new Parse.Query("MyObject"); - query.equalTo("objectId", myObj.id); + const query = new Parse.Query('MyObject'); + query.equalTo('objectId', myObj.id); return Promise.all([query.get(myObj.id), query.first(), query.find()]); }) .then(() => done()); }); - it("should have request ip", done => { - Parse.Cloud.beforeFind("MyObject", req => { + it('should have request ip', done => { + Parse.Cloud.beforeFind('MyObject', req => { expect(req.ip).toBeDefined(); }); - const MyObject = Parse.Object.extend("MyObject"); + const MyObject = Parse.Object.extend('MyObject'); const myObject = new MyObject(); myObject .save() .then(myObj => { - const query = new Parse.Query("MyObject"); - query.equalTo("objectId", myObj.id); + const query = new Parse.Query('MyObject'); + query.equalTo('objectId', myObj.id); return Promise.all([query.get(myObj.id), query.first(), query.find()]); }) .then(() => done()); }); - it("should run beforeFind on pointers and array of pointers from an object", async () => { - const obj1 = new Parse.Object("TestObject"); - const obj2 = new Parse.Object("TestObject2"); - const obj3 = new Parse.Object("TestObject"); - obj2.set("aField", "aFieldValue"); + it('should run beforeFind on pointers and array of pointers from an object', async () => { + const obj1 = new Parse.Object('TestObject'); + const obj2 = new Parse.Object('TestObject2'); + const obj3 = new Parse.Object('TestObject'); + obj2.set('aField', 'aFieldValue'); await obj2.save(); - obj1.set("pointerField", obj2); - obj3.set("pointerFieldArray", [obj2]); + obj1.set('pointerField', obj2); + obj3.set('pointerFieldArray', [obj2]); await obj1.save(); await obj3.save(); - const spy = jasmine.createSpy("beforeFindSpy"); - Parse.Cloud.beforeFind("TestObject2", spy); - const query = new Parse.Query("TestObject"); + const spy = jasmine.createSpy('beforeFindSpy'); + Parse.Cloud.beforeFind('TestObject2', spy); + const query = new Parse.Query('TestObject'); await query.get(obj1.id); // Pointer not included in query so we don't expect beforeFind to be called expect(spy).not.toHaveBeenCalled(); - const query2 = new Parse.Query("TestObject"); - query2.include("pointerField"); + const query2 = new Parse.Query('TestObject'); + query2.include('pointerField'); const res = await query2.get(obj1.id); - expect(res.get("pointerField").get("aField")).toBe("aFieldValue"); + expect(res.get('pointerField').get('aField')).toBe('aFieldValue'); // Pointer included in query so we expect beforeFind to be called expect(spy).toHaveBeenCalledTimes(1); - const query3 = new Parse.Query("TestObject"); - query3.include("pointerFieldArray"); + const query3 = new Parse.Query('TestObject'); + query3.include('pointerFieldArray'); const res2 = await query3.get(obj3.id); - expect(res2.get("pointerFieldArray")[0].get("aField")).toBe("aFieldValue"); + expect(res2.get('pointerFieldArray')[0].get('aField')).toBe('aFieldValue'); expect(spy).toHaveBeenCalledTimes(2); }); - it("should have access to context in include query in beforeFind hook", async () => { + it('should have access to context in include query in beforeFind hook', async () => { let beforeFindTestObjectCalled = false; let beforeFindTestObject2Called = false; - const obj1 = new Parse.Object("TestObject"); - const obj2 = new Parse.Object("TestObject2"); - obj2.set("aField", "aFieldValue"); + const obj1 = new Parse.Object('TestObject'); + const obj2 = new Parse.Object('TestObject2'); + obj2.set('aField', 'aFieldValue'); await obj2.save(); - obj1.set("pointerField", obj2); + obj1.set('pointerField', obj2); await obj1.save(); - Parse.Cloud.beforeFind("TestObject", req => { + Parse.Cloud.beforeFind('TestObject', req => { expect(req.context).toBeDefined(); - expect(req.context.a).toEqual("a"); + expect(req.context.a).toEqual('a'); beforeFindTestObjectCalled = true; }); - Parse.Cloud.beforeFind("TestObject2", req => { + Parse.Cloud.beforeFind('TestObject2', req => { expect(req.context).toBeDefined(); - expect(req.context.a).toEqual("a"); + expect(req.context.a).toEqual('a'); beforeFindTestObject2Called = true; }); - const query = new Parse.Query("TestObject"); - await query.include("pointerField").find({ context: { a: "a" } }); + const query = new Parse.Query('TestObject'); + await query.include('pointerField').find({ context: { a: 'a' } }); expect(beforeFindTestObjectCalled).toBeTrue(); expect(beforeFindTestObject2Called).toBeTrue(); }); }); -describe("afterFind hooks", () => { - it("should add afterFind trigger", done => { - Parse.Cloud.afterFind("MyObject", req => { +describe('afterFind hooks', () => { + it('should add afterFind trigger', done => { + Parse.Cloud.afterFind('MyObject', req => { const q = req.query; expect(q instanceof Parse.Query).toBe(true); const jsonQuery = q.toJSON(); - expect(jsonQuery.where.key).toEqual("value"); + expect(jsonQuery.where.key).toEqual('value'); expect(jsonQuery.where.some).toEqual({ $gt: 10 }); - expect(jsonQuery.include).toEqual("otherKey,otherValue"); - expect(jsonQuery.excludeKeys).toBe("exclude"); + expect(jsonQuery.include).toEqual('otherKey,otherValue'); + expect(jsonQuery.excludeKeys).toBe('exclude'); expect(jsonQuery.limit).toEqual(100); expect(jsonQuery.skip).toBe(undefined); - expect(jsonQuery.order).toBe("key"); - expect(jsonQuery.keys).toBe("select"); - expect(jsonQuery.readPreference).toBe("PRIMARY"); - expect(jsonQuery.includeReadPreference).toBe("SECONDARY"); - expect(jsonQuery.subqueryReadPreference).toBe("SECONDARY_PREFERRED"); - }); - - const query = new Parse.Query("MyObject"); - query.equalTo("key", "value"); - query.greaterThan("some", 10); - query.include("otherKey"); - query.include("otherValue"); - query.ascending("key"); - query.select("select"); - query.exclude("exclude"); - query.readPreference("PRIMARY", "SECONDARY", "SECONDARY_PREFERRED"); + expect(jsonQuery.order).toBe('key'); + expect(jsonQuery.keys).toBe('select'); + expect(jsonQuery.readPreference).toBe('PRIMARY'); + expect(jsonQuery.includeReadPreference).toBe('SECONDARY'); + expect(jsonQuery.subqueryReadPreference).toBe('SECONDARY_PREFERRED'); + }); + + const query = new Parse.Query('MyObject'); + query.equalTo('key', 'value'); + query.greaterThan('some', 10); + query.include('otherKey'); + query.include('otherValue'); + query.ascending('key'); + query.select('select'); + query.exclude('exclude'); + query.readPreference('PRIMARY', 'SECONDARY', 'SECONDARY_PREFERRED'); query.find().then(() => { done(); }); }); - it("should add afterFind trigger using get", done => { - Parse.Cloud.afterFind("MyObject", req => { + it('should add afterFind trigger using get', done => { + Parse.Cloud.afterFind('MyObject', req => { for (let i = 0; i < req.objects.length; i++) { - req.objects[i].set("secretField", "###"); + req.objects[i].set('secretField', '###'); } return req.objects; }); - const obj = new Parse.Object("MyObject"); - obj.set("secretField", "SSID"); + const obj = new Parse.Object('MyObject'); + obj.set('secretField', 'SSID'); obj.save().then( function () { - const query = new Parse.Query("MyObject"); + const query = new Parse.Query('MyObject'); query.get(obj.id).then( function (result) { - expect(result.get("secretField")).toEqual("###"); + expect(result.get('secretField')).toEqual('###'); done(); }, function (error) { @@ -2654,22 +2654,22 @@ describe("afterFind hooks", () => { ); }); - it("should add afterFind trigger using find", done => { - Parse.Cloud.afterFind("MyObject", req => { + it('should add afterFind trigger using find', done => { + Parse.Cloud.afterFind('MyObject', req => { for (let i = 0; i < req.objects.length; i++) { - req.objects[i].set("secretField", "###"); + req.objects[i].set('secretField', '###'); } return req.objects; }); - const obj = new Parse.Object("MyObject"); - obj.set("secretField", "SSID"); + const obj = new Parse.Object('MyObject'); + obj.set('secretField', 'SSID'); obj.save().then( function () { - const query = new Parse.Query("MyObject"); - query.equalTo("objectId", obj.id); + const query = new Parse.Query('MyObject'); + query.equalTo('objectId', obj.id); query.find().then( function (results) { - expect(results[0].get("secretField")).toEqual("###"); + expect(results[0].get('secretField')).toEqual('###'); done(); }, function (error) { @@ -2685,26 +2685,26 @@ describe("afterFind hooks", () => { ); }); - it("should filter out results", done => { - Parse.Cloud.afterFind("MyObject", req => { + it('should filter out results', done => { + Parse.Cloud.afterFind('MyObject', req => { const filteredResults = []; for (let i = 0; i < req.objects.length; i++) { - if (req.objects[i].get("secretField") === "SSID1") { + if (req.objects[i].get('secretField') === 'SSID1') { filteredResults.push(req.objects[i]); } } return filteredResults; }); - const obj0 = new Parse.Object("MyObject"); - obj0.set("secretField", "SSID1"); - const obj1 = new Parse.Object("MyObject"); - obj1.set("secretField", "SSID2"); + const obj0 = new Parse.Object('MyObject'); + obj0.set('secretField', 'SSID1'); + const obj1 = new Parse.Object('MyObject'); + obj1.set('secretField', 'SSID2'); Parse.Object.saveAll([obj0, obj1]).then( function () { - const query = new Parse.Query("MyObject"); + const query = new Parse.Query('MyObject'); query.find().then( function (results) { - expect(results[0].get("secretField")).toEqual("SSID1"); + expect(results[0].get('secretField')).toEqual('SSID1'); expect(results.length).toEqual(1); done(); }, @@ -2721,19 +2721,19 @@ describe("afterFind hooks", () => { ); }); - it("should handle failures", done => { - Parse.Cloud.afterFind("MyObject", () => { - throw new Parse.Error(Parse.Error.SCRIPT_FAILED, "It should fail"); + it('should handle failures', done => { + Parse.Cloud.afterFind('MyObject', () => { + throw new Parse.Error(Parse.Error.SCRIPT_FAILED, 'It should fail'); }); - const obj = new Parse.Object("MyObject"); - obj.set("secretField", "SSID"); + const obj = new Parse.Object('MyObject'); + obj.set('secretField', 'SSID'); obj.save().then( function () { - const query = new Parse.Query("MyObject"); - query.equalTo("objectId", obj.id); + const query = new Parse.Query('MyObject'); + query.equalTo('objectId', obj.id); query.find().then( function () { - fail("AfterFind should handle response failure correctly"); + fail('AfterFind should handle response failure correctly'); done(); }, function () { @@ -2747,26 +2747,26 @@ describe("afterFind hooks", () => { ); }); - it("should also work with promise", done => { - Parse.Cloud.afterFind("MyObject", req => { + it('should also work with promise', done => { + Parse.Cloud.afterFind('MyObject', req => { return new Promise(resolve => { setTimeout(function () { for (let i = 0; i < req.objects.length; i++) { - req.objects[i].set("secretField", "###"); + req.objects[i].set('secretField', '###'); } resolve(req.objects); }, 1000); }); }); - const obj = new Parse.Object("MyObject"); - obj.set("secretField", "SSID"); + const obj = new Parse.Object('MyObject'); + obj.set('secretField', 'SSID'); obj.save().then( function () { - const query = new Parse.Query("MyObject"); - query.equalTo("objectId", obj.id); + const query = new Parse.Query('MyObject'); + query.equalTo('objectId', obj.id); query.find().then( function (results) { - expect(results[0].get("secretField")).toEqual("###"); + expect(results[0].get('secretField')).toEqual('###'); done(); }, function (error) { @@ -2780,237 +2780,237 @@ describe("afterFind hooks", () => { ); }); - it("should alter select", done => { - Parse.Cloud.beforeFind("MyObject", req => { - req.query.select("white"); + it('should alter select', done => { + Parse.Cloud.beforeFind('MyObject', req => { + req.query.select('white'); return req.query; }); - const obj0 = new Parse.Object("MyObject") - .set("white", true) - .set("black", true); + const obj0 = new Parse.Object('MyObject') + .set('white', true) + .set('black', true); obj0.save().then(() => { - new Parse.Query("MyObject").first().then(result => { - expect(result.get("white")).toBe(true); - expect(result.get("black")).toBe(undefined); + new Parse.Query('MyObject').first().then(result => { + expect(result.get('white')).toBe(true); + expect(result.get('black')).toBe(undefined); done(); }); }); }); - it("should not alter select", done => { - const obj0 = new Parse.Object("MyObject") - .set("white", true) - .set("black", true); + it('should not alter select', done => { + const obj0 = new Parse.Object('MyObject') + .set('white', true) + .set('black', true); obj0.save().then(() => { - new Parse.Query("MyObject").first().then(result => { - expect(result.get("white")).toBe(true); - expect(result.get("black")).toBe(true); + new Parse.Query('MyObject').first().then(result => { + expect(result.get('white')).toBe(true); + expect(result.get('black')).toBe(true); done(); }); }); }); - it("should set count to true on beforeFind hooks if query is count", done => { + it('should set count to true on beforeFind hooks if query is count', done => { const hook = { method: function (req) { expect(req.count).toBe(true); return Promise.resolve(); }, }; - spyOn(hook, "method").and.callThrough(); - Parse.Cloud.beforeFind("Stuff", hook.method); - new Parse.Query("Stuff").count().then(count => { + spyOn(hook, 'method').and.callThrough(); + Parse.Cloud.beforeFind('Stuff', hook.method); + new Parse.Query('Stuff').count().then(count => { expect(count).toBe(0); expect(hook.method).toHaveBeenCalled(); done(); }); }); - it("should set count to false on beforeFind hooks if query is not count", done => { + it('should set count to false on beforeFind hooks if query is not count', done => { const hook = { method: function (req) { expect(req.count).toBe(false); return Promise.resolve(); }, }; - spyOn(hook, "method").and.callThrough(); - Parse.Cloud.beforeFind("Stuff", hook.method); - new Parse.Query("Stuff").find().then(res => { + spyOn(hook, 'method').and.callThrough(); + Parse.Cloud.beforeFind('Stuff', hook.method); + new Parse.Query('Stuff').find().then(res => { expect(res.length).toBe(0); expect(hook.method).toHaveBeenCalled(); done(); }); }); - it("can set a pointer object in afterFind", async () => { - const obj = new Parse.Object("MyObject"); + it('can set a pointer object in afterFind', async () => { + const obj = new Parse.Object('MyObject'); await obj.save(); - Parse.Cloud.afterFind("MyObject", async ({ objects }) => { - const otherObject = new Parse.Object("Test"); - otherObject.set("foo", "bar"); + Parse.Cloud.afterFind('MyObject', async ({ objects }) => { + const otherObject = new Parse.Object('Test'); + otherObject.set('foo', 'bar'); await otherObject.save(); - objects[0].set("Pointer", otherObject); - objects[0].set("xyz", "yolo"); - expect(objects[0].get("Pointer").get("foo")).toBe("bar"); + objects[0].set('Pointer', otherObject); + objects[0].set('xyz', 'yolo'); + expect(objects[0].get('Pointer').get('foo')).toBe('bar'); }); - const query = new Parse.Query("MyObject"); - query.equalTo("objectId", obj.id); + const query = new Parse.Query('MyObject'); + query.equalTo('objectId', obj.id); const obj2 = await query.first(); - expect(obj2.get("xyz")).toBe("yolo"); - const pointer = obj2.get("Pointer"); - expect(pointer.get("foo")).toBe("bar"); + expect(obj2.get('xyz')).toBe('yolo'); + const pointer = obj2.get('Pointer'); + expect(pointer.get('foo')).toBe('bar'); }); - it("can set invalid object in afterFind", async () => { - const obj = new Parse.Object("MyObject"); + it('can set invalid object in afterFind', async () => { + const obj = new Parse.Object('MyObject'); await obj.save(); - Parse.Cloud.afterFind("MyObject", () => [{}]); - const query = new Parse.Query("MyObject"); - query.equalTo("objectId", obj.id); + Parse.Cloud.afterFind('MyObject', () => [{}]); + const query = new Parse.Query('MyObject'); + query.equalTo('objectId', obj.id); const obj2 = await query.first(); expect(obj2).toBeDefined(); expect(obj2.toJSON()).toEqual({}); expect(obj2.id).toBeUndefined(); }); - it("can return a unsaved object in afterFind", async () => { - const obj = new Parse.Object("MyObject"); + it('can return a unsaved object in afterFind', async () => { + const obj = new Parse.Object('MyObject'); await obj.save(); - Parse.Cloud.afterFind("MyObject", async () => { - const otherObject = new Parse.Object("Test"); - otherObject.set("foo", "bar"); + Parse.Cloud.afterFind('MyObject', async () => { + const otherObject = new Parse.Object('Test'); + otherObject.set('foo', 'bar'); return [otherObject]; }); - const query = new Parse.Query("MyObject"); + const query = new Parse.Query('MyObject'); const obj2 = await query.first(); - expect(obj2.get("foo")).toEqual("bar"); + expect(obj2.get('foo')).toEqual('bar'); expect(obj2.id).toBeUndefined(); await obj2.save(); expect(obj2.id).toBeDefined(); }); - it("should have request headers", done => { - Parse.Cloud.afterFind("MyObject", req => { + it('should have request headers', done => { + Parse.Cloud.afterFind('MyObject', req => { expect(req.headers).toBeDefined(); }); - const MyObject = Parse.Object.extend("MyObject"); + const MyObject = Parse.Object.extend('MyObject'); const myObject = new MyObject(); myObject .save() .then(myObj => { - const query = new Parse.Query("MyObject"); - query.equalTo("objectId", myObj.id); + const query = new Parse.Query('MyObject'); + query.equalTo('objectId', myObj.id); return Promise.all([query.get(myObj.id), query.first(), query.find()]); }) .then(() => done()); }); - it("should have request ip", done => { - Parse.Cloud.afterFind("MyObject", req => { + it('should have request ip', done => { + Parse.Cloud.afterFind('MyObject', req => { expect(req.ip).toBeDefined(); }); - const MyObject = Parse.Object.extend("MyObject"); + const MyObject = Parse.Object.extend('MyObject'); const myObject = new MyObject(); myObject .save() .then(myObj => { - const query = new Parse.Query("MyObject"); - query.equalTo("objectId", myObj.id); + const query = new Parse.Query('MyObject'); + query.equalTo('objectId', myObj.id); return Promise.all([query.get(myObj.id), query.first(), query.find()]); }) .then(() => done()) .catch(done.fail); }); - it("should validate triggers correctly", () => { + it('should validate triggers correctly', () => { expect(() => { - Parse.Cloud.beforeSave("_Session", () => {}); + Parse.Cloud.beforeSave('_Session', () => {}); }).toThrow( - "Only the afterLogout trigger is allowed for the _Session class." + 'Only the afterLogout trigger is allowed for the _Session class.' ); expect(() => { - Parse.Cloud.afterSave("_Session", () => {}); + Parse.Cloud.afterSave('_Session', () => {}); }).toThrow( - "Only the afterLogout trigger is allowed for the _Session class." + 'Only the afterLogout trigger is allowed for the _Session class.' ); expect(() => { - Parse.Cloud.beforeSave("_PushStatus", () => {}); - }).toThrow("Only afterSave is allowed on _PushStatus"); + Parse.Cloud.beforeSave('_PushStatus', () => {}); + }).toThrow('Only afterSave is allowed on _PushStatus'); expect(() => { - Parse.Cloud.afterSave("_PushStatus", () => {}); + Parse.Cloud.afterSave('_PushStatus', () => {}); }).not.toThrow(); expect(() => { Parse.Cloud.beforeLogin(() => {}); }).not.toThrow( - "Only the _User class is allowed for the beforeLogin and afterLogin triggers" + 'Only the _User class is allowed for the beforeLogin and afterLogin triggers' ); expect(() => { - Parse.Cloud.beforeLogin("_User", () => {}); + Parse.Cloud.beforeLogin('_User', () => {}); }).not.toThrow( - "Only the _User class is allowed for the beforeLogin and afterLogin triggers" + 'Only the _User class is allowed for the beforeLogin and afterLogin triggers' ); expect(() => { Parse.Cloud.beforeLogin(Parse.User, () => {}); }).not.toThrow( - "Only the _User class is allowed for the beforeLogin and afterLogin triggers" + 'Only the _User class is allowed for the beforeLogin and afterLogin triggers' ); expect(() => { - Parse.Cloud.beforeLogin("SomeClass", () => {}); + Parse.Cloud.beforeLogin('SomeClass', () => {}); }).toThrow( - "Only the _User class is allowed for the beforeLogin and afterLogin triggers" + 'Only the _User class is allowed for the beforeLogin and afterLogin triggers' ); expect(() => { Parse.Cloud.afterLogin(() => {}); }).not.toThrow( - "Only the _User class is allowed for the beforeLogin and afterLogin triggers" + 'Only the _User class is allowed for the beforeLogin and afterLogin triggers' ); expect(() => { - Parse.Cloud.afterLogin("_User", () => {}); + Parse.Cloud.afterLogin('_User', () => {}); }).not.toThrow( - "Only the _User class is allowed for the beforeLogin and afterLogin triggers" + 'Only the _User class is allowed for the beforeLogin and afterLogin triggers' ); expect(() => { Parse.Cloud.afterLogin(Parse.User, () => {}); }).not.toThrow( - "Only the _User class is allowed for the beforeLogin and afterLogin triggers" + 'Only the _User class is allowed for the beforeLogin and afterLogin triggers' ); expect(() => { - Parse.Cloud.afterLogin("SomeClass", () => {}); + Parse.Cloud.afterLogin('SomeClass', () => {}); }).toThrow( - "Only the _User class is allowed for the beforeLogin and afterLogin triggers" + 'Only the _User class is allowed for the beforeLogin and afterLogin triggers' ); expect(() => { Parse.Cloud.afterLogout(() => {}); }).not.toThrow(); expect(() => { - Parse.Cloud.afterLogout("_Session", () => {}); + Parse.Cloud.afterLogout('_Session', () => {}); }).not.toThrow(); expect(() => { - Parse.Cloud.afterLogout("_User", () => {}); + Parse.Cloud.afterLogout('_User', () => {}); }).toThrow( - "Only the _Session class is allowed for the afterLogout trigger." + 'Only the _Session class is allowed for the afterLogout trigger.' ); expect(() => { - Parse.Cloud.afterLogout("SomeClass", () => {}); + Parse.Cloud.afterLogout('SomeClass', () => {}); }).toThrow( - "Only the _Session class is allowed for the afterLogout trigger." + 'Only the _Session class is allowed for the afterLogout trigger.' ); }); - it_id("c16159b5-e8ee-42d5-8fe3-e2f7c006881d")(it)( - "should skip afterFind hooks for aggregate", + it_id('c16159b5-e8ee-42d5-8fe3-e2f7c006881d')(it)( + 'should skip afterFind hooks for aggregate', done => { const hook = { method: function () { return Promise.reject(); }, }; - spyOn(hook, "method").and.callThrough(); - Parse.Cloud.afterFind("MyObject", hook.method); - const obj = new Parse.Object("MyObject"); + spyOn(hook, 'method').and.callThrough(); + Parse.Cloud.afterFind('MyObject', hook.method); + const obj = new Parse.Object('MyObject'); const pipeline = [ { $group: { _id: {} }, @@ -3019,7 +3019,7 @@ describe("afterFind hooks", () => { obj .save() .then(() => { - const query = new Parse.Query("MyObject"); + const query = new Parse.Query('MyObject'); return query.aggregate(pipeline); }) .then(results => { @@ -3030,23 +3030,23 @@ describe("afterFind hooks", () => { } ); - it_id("ca55c90d-36db-422c-9060-a30583ce5224")(it)( - "should skip afterFind hooks for distinct", + it_id('ca55c90d-36db-422c-9060-a30583ce5224')(it)( + 'should skip afterFind hooks for distinct', done => { const hook = { method: function () { return Promise.reject(); }, }; - spyOn(hook, "method").and.callThrough(); - Parse.Cloud.afterFind("MyObject", hook.method); - const obj = new Parse.Object("MyObject"); - obj.set("score", 10); + spyOn(hook, 'method').and.callThrough(); + Parse.Cloud.afterFind('MyObject', hook.method); + const obj = new Parse.Object('MyObject'); + obj.set('score', 10); obj .save() .then(() => { - const query = new Parse.Query("MyObject"); - return query.distinct("score"); + const query = new Parse.Query('MyObject'); + return query.distinct('score'); }) .then(results => { expect(results[0]).toEqual(10); @@ -3056,30 +3056,30 @@ describe("afterFind hooks", () => { } ); - it("should throw error if context header is malformed", async () => { + it('should throw error if context header is malformed', async () => { let calledBefore = false; let calledAfter = false; - Parse.Cloud.beforeSave("TestObject", () => { + Parse.Cloud.beforeSave('TestObject', () => { calledBefore = true; }); - Parse.Cloud.afterSave("TestObject", () => { + Parse.Cloud.afterSave('TestObject', () => { calledAfter = true; }); const req = request({ - method: "POST", - url: "http://localhost:8378/1/classes/TestObject", + method: 'POST', + url: 'http://localhost:8378/1/classes/TestObject', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "X-Parse-Cloud-Context": "key", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Cloud-Context': 'key', }, body: { - foo: "bar", + foo: 'bar', }, }); try { await req; - fail("Should have thrown error"); + fail('Should have thrown error'); } catch (e) { expect(e).toBeDefined(); expect(e.data.code).toEqual(Parse.Error.INVALID_JSON); @@ -3091,27 +3091,27 @@ describe("afterFind hooks", () => { it('should throw error if context header is string "1"', async () => { let calledBefore = false; let calledAfter = false; - Parse.Cloud.beforeSave("TestObject", () => { + Parse.Cloud.beforeSave('TestObject', () => { calledBefore = true; }); - Parse.Cloud.afterSave("TestObject", () => { + Parse.Cloud.afterSave('TestObject', () => { calledAfter = true; }); const req = request({ - method: "POST", - url: "http://localhost:8378/1/classes/TestObject", + method: 'POST', + url: 'http://localhost:8378/1/classes/TestObject', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "X-Parse-Cloud-Context": "1", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Cloud-Context': '1', }, body: { - foo: "bar", + foo: 'bar', }, }); try { await req; - fail("Should have thrown error"); + fail('Should have thrown error'); } catch (e) { expect(e).toBeDefined(); expect(e.data.code).toEqual(Parse.Error.INVALID_JSON); @@ -3120,33 +3120,33 @@ describe("afterFind hooks", () => { expect(calledAfter).toBe(false); }); - it_id("55ef1741-cf72-4a7c-a029-00cb75f53233")(it)( - "should expose context in beforeSave/afterSave via header", + it_id('55ef1741-cf72-4a7c-a029-00cb75f53233')(it)( + 'should expose context in beforeSave/afterSave via header', async () => { let calledBefore = false; let calledAfter = false; - Parse.Cloud.beforeSave("TestObject", req => { - expect(req.object.get("foo")).toEqual("bar"); + Parse.Cloud.beforeSave('TestObject', req => { + expect(req.object.get('foo')).toEqual('bar'); expect(req.context.otherKey).toBe(1); - expect(req.context.key).toBe("value"); + expect(req.context.key).toBe('value'); calledBefore = true; }); - Parse.Cloud.afterSave("TestObject", req => { - expect(req.object.get("foo")).toEqual("bar"); + Parse.Cloud.afterSave('TestObject', req => { + expect(req.object.get('foo')).toEqual('bar'); expect(req.context.otherKey).toBe(1); - expect(req.context.key).toBe("value"); + expect(req.context.key).toBe('value'); calledAfter = true; }); const req = request({ - method: "POST", - url: "http://localhost:8378/1/classes/TestObject", + method: 'POST', + url: 'http://localhost:8378/1/classes/TestObject', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "X-Parse-Cloud-Context": '{"key":"value","otherKey":1}', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Cloud-Context': '{"key":"value","otherKey":1}', }, body: { - foo: "bar", + foo: 'bar', }, }); await req; @@ -3155,31 +3155,31 @@ describe("afterFind hooks", () => { } ); - it("should override header context with body context in beforeSave/afterSave", async () => { + it('should override header context with body context in beforeSave/afterSave', async () => { let calledBefore = false; let calledAfter = false; - Parse.Cloud.beforeSave("TestObject", req => { - expect(req.object.get("foo")).toEqual("bar"); + Parse.Cloud.beforeSave('TestObject', req => { + expect(req.object.get('foo')).toEqual('bar'); expect(req.context.otherKey).toBe(10); - expect(req.context.key).toBe("hello"); + expect(req.context.key).toBe('hello'); calledBefore = true; }); - Parse.Cloud.afterSave("TestObject", req => { - expect(req.object.get("foo")).toEqual("bar"); + Parse.Cloud.afterSave('TestObject', req => { + expect(req.object.get('foo')).toEqual('bar'); expect(req.context.otherKey).toBe(10); - expect(req.context.key).toBe("hello"); + expect(req.context.key).toBe('hello'); calledAfter = true; }); const req = request({ - method: "POST", - url: "http://localhost:8378/1/classes/TestObject", + method: 'POST', + url: 'http://localhost:8378/1/classes/TestObject', headers: { - "X-Parse-REST-API-Key": "rest", - "X-Parse-Cloud-Context": '{"key":"value","otherKey":1}', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Cloud-Context': '{"key":"value","otherKey":1}', }, body: { - foo: "bar", - _ApplicationId: "test", + foo: 'bar', + _ApplicationId: 'test', _context: '{"key":"hello","otherKey":10}', }, }); @@ -3188,31 +3188,31 @@ describe("afterFind hooks", () => { expect(calledAfter).toBe(true); }); - it("should throw error if context body is malformed", async () => { + it('should throw error if context body is malformed', async () => { let calledBefore = false; let calledAfter = false; - Parse.Cloud.beforeSave("TestObject", () => { + Parse.Cloud.beforeSave('TestObject', () => { calledBefore = true; }); - Parse.Cloud.afterSave("TestObject", () => { + Parse.Cloud.afterSave('TestObject', () => { calledAfter = true; }); const req = request({ - method: "POST", - url: "http://localhost:8378/1/classes/TestObject", + method: 'POST', + url: 'http://localhost:8378/1/classes/TestObject', headers: { - "X-Parse-REST-API-Key": "rest", - "X-Parse-Cloud-Context": '{"key":"value","otherKey":1}', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Cloud-Context': '{"key":"value","otherKey":1}', }, body: { - foo: "bar", - _ApplicationId: "test", - _context: "key", + foo: 'bar', + _ApplicationId: 'test', + _context: 'key', }, }); try { await req; - fail("Should have thrown error"); + fail('Should have thrown error'); } catch (e) { expect(e).toBeDefined(); expect(e.data.code).toEqual(Parse.Error.INVALID_JSON); @@ -3224,28 +3224,28 @@ describe("afterFind hooks", () => { it('should throw error if context body is string "true"', async () => { let calledBefore = false; let calledAfter = false; - Parse.Cloud.beforeSave("TestObject", () => { + Parse.Cloud.beforeSave('TestObject', () => { calledBefore = true; }); - Parse.Cloud.afterSave("TestObject", () => { + Parse.Cloud.afterSave('TestObject', () => { calledAfter = true; }); const req = request({ - method: "POST", - url: "http://localhost:8378/1/classes/TestObject", + method: 'POST', + url: 'http://localhost:8378/1/classes/TestObject', headers: { - "X-Parse-REST-API-Key": "rest", - "X-Parse-Cloud-Context": '{"key":"value","otherKey":1}', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Cloud-Context': '{"key":"value","otherKey":1}', }, body: { - foo: "bar", - _ApplicationId: "test", - _context: "true", + foo: 'bar', + _ApplicationId: 'test', + _context: 'true', }, }); try { await req; - fail("Should have thrown error"); + fail('Should have thrown error'); } catch (e) { expect(e).toBeDefined(); expect(e.data.code).toEqual(Parse.Error.INVALID_JSON); @@ -3254,123 +3254,123 @@ describe("afterFind hooks", () => { expect(calledAfter).toBe(false); }); - it("should expose context in before and afterSave", async () => { + it('should expose context in before and afterSave', async () => { let calledBefore = false; let calledAfter = false; - Parse.Cloud.beforeSave("MyClass", req => { + Parse.Cloud.beforeSave('MyClass', req => { req.context = { - key: "value", + key: 'value', otherKey: 1, }; calledBefore = true; }); - Parse.Cloud.afterSave("MyClass", req => { + Parse.Cloud.afterSave('MyClass', req => { expect(req.context.otherKey).toBe(1); - expect(req.context.key).toBe("value"); + expect(req.context.key).toBe('value'); calledAfter = true; }); - const object = new Parse.Object("MyClass"); + const object = new Parse.Object('MyClass'); await object.save(); expect(calledBefore).toBe(true); expect(calledAfter).toBe(true); }); - it("should expose context in before and afterSave and let keys be set individually", async () => { + it('should expose context in before and afterSave and let keys be set individually', async () => { let calledBefore = false; let calledAfter = false; - Parse.Cloud.beforeSave("MyClass", req => { - req.context.some = "value"; + Parse.Cloud.beforeSave('MyClass', req => { + req.context.some = 'value'; req.context.yolo = 1; calledBefore = true; }); - Parse.Cloud.afterSave("MyClass", req => { + Parse.Cloud.afterSave('MyClass', req => { expect(req.context.yolo).toBe(1); - expect(req.context.some).toBe("value"); + expect(req.context.some).toBe('value'); calledAfter = true; }); - const object = new Parse.Object("MyClass"); + const object = new Parse.Object('MyClass'); await object.save(); expect(calledBefore).toBe(true); expect(calledAfter).toBe(true); }); }); -describe("beforeLogin hook", () => { - it("should run beforeLogin with correct credentials", async done => { +describe('beforeLogin hook', () => { + it('should run beforeLogin with correct credentials', async done => { let hit = 0; Parse.Cloud.beforeLogin(req => { hit++; - expect(req.object.get("username")).toEqual("tupac"); + expect(req.object.get('username')).toEqual('tupac'); }); - await Parse.User.signUp("tupac", "shakur"); - const user = await Parse.User.logIn("tupac", "shakur"); + await Parse.User.signUp('tupac', 'shakur'); + const user = await Parse.User.logIn('tupac', 'shakur'); expect(hit).toBe(1); expect(user).toBeDefined(); - expect(user.getUsername()).toBe("tupac"); + expect(user.getUsername()).toBe('tupac'); expect(user.getSessionToken()).toBeDefined(); done(); }); - it("should be able to block login if an error is thrown", async done => { + it('should be able to block login if an error is thrown', async done => { let hit = 0; Parse.Cloud.beforeLogin(req => { hit++; - if (req.object.get("isBanned")) { - throw new Error("banned account"); + if (req.object.get('isBanned')) { + throw new Error('banned account'); } }); - const user = await Parse.User.signUp("tupac", "shakur"); + const user = await Parse.User.signUp('tupac', 'shakur'); await user.save({ isBanned: true }); try { - await Parse.User.logIn("tupac", "shakur"); - throw new Error("should not have been logged in."); + await Parse.User.logIn('tupac', 'shakur'); + throw new Error('should not have been logged in.'); } catch (e) { - expect(e.message).toBe("banned account"); + expect(e.message).toBe('banned account'); } expect(hit).toBe(1); done(); }); - it("should be able to block login if an error is thrown even if the user has a attached file", async done => { + it('should be able to block login if an error is thrown even if the user has a attached file', async done => { let hit = 0; Parse.Cloud.beforeLogin(req => { hit++; - if (req.object.get("isBanned")) { - throw new Error("banned account"); + if (req.object.get('isBanned')) { + throw new Error('banned account'); } }); - const user = await Parse.User.signUp("tupac", "shakur"); - const base64 = "V29ya2luZyBhdCBQYXJzZSBpcyBncmVhdCE="; - const file = new Parse.File("myfile.txt", { base64 }); + const user = await Parse.User.signUp('tupac', 'shakur'); + const base64 = 'V29ya2luZyBhdCBQYXJzZSBpcyBncmVhdCE='; + const file = new Parse.File('myfile.txt', { base64 }); await file.save(); await user.save({ isBanned: true, file }); try { - await Parse.User.logIn("tupac", "shakur"); - throw new Error("should not have been logged in."); + await Parse.User.logIn('tupac', 'shakur'); + throw new Error('should not have been logged in.'); } catch (e) { - expect(e.message).toBe("banned account"); + expect(e.message).toBe('banned account'); } expect(hit).toBe(1); done(); }); - it("should not run beforeLogin with incorrect credentials", async done => { + it('should not run beforeLogin with incorrect credentials', async done => { let hit = 0; Parse.Cloud.beforeLogin(req => { hit++; - expect(req.object.get("username")).toEqual("tupac"); + expect(req.object.get('username')).toEqual('tupac'); }); - await Parse.User.signUp("tupac", "shakur"); + await Parse.User.signUp('tupac', 'shakur'); try { - await Parse.User.logIn("tony", "shakur"); + await Parse.User.logIn('tony', 'shakur'); } catch (e) { expect(e.code).toBe(Parse.Error.OBJECT_NOT_FOUND); } @@ -3378,67 +3378,67 @@ describe("beforeLogin hook", () => { done(); }); - it("should not run beforeLogin on sign up", async done => { + it('should not run beforeLogin on sign up', async done => { let hit = 0; Parse.Cloud.beforeLogin(req => { hit++; - expect(req.object.get("username")).toEqual("tupac"); + expect(req.object.get('username')).toEqual('tupac'); }); - const user = await Parse.User.signUp("tupac", "shakur"); + const user = await Parse.User.signUp('tupac', 'shakur'); expect(user).toBeDefined(); expect(hit).toBe(0); done(); }); - it("should trigger afterLogout hook on logout", async done => { + it('should trigger afterLogout hook on logout', async done => { let userId; Parse.Cloud.afterLogout(req => { - expect(req.object.className).toEqual("_Session"); + expect(req.object.className).toEqual('_Session'); expect(req.object.id).toBeDefined(); - const user = req.object.get("user"); + const user = req.object.get('user'); expect(user).toBeDefined(); userId = user.id; }); - const user = await Parse.User.signUp("user", "pass"); + const user = await Parse.User.signUp('user', 'pass'); await Parse.User.logOut(); expect(user.id).toBe(userId); done(); }); - it("does not crash server when throwing in afterLogin hook", async () => { - const error = new Parse.Error(2000, "afterLogin error"); + it('does not crash server when throwing in afterLogin hook', async () => { + const error = new Parse.Error(2000, 'afterLogin error'); const trigger = { afterLogin() { throw error; }, }; - const spy = spyOn(trigger, "afterLogin").and.callThrough(); + const spy = spyOn(trigger, 'afterLogin').and.callThrough(); Parse.Cloud.afterLogin(trigger.afterLogin); - await Parse.User.signUp("user", "pass"); - const response = await Parse.User.logIn("user", "pass").catch(e => e); + await Parse.User.signUp('user', 'pass'); + const response = await Parse.User.logIn('user', 'pass').catch(e => e); expect(spy).toHaveBeenCalled(); expect(response).toEqual(error); }); - it("does not crash server when throwing in afterLogout hook", async () => { - const error = new Parse.Error(2000, "afterLogout error"); + it('does not crash server when throwing in afterLogout hook', async () => { + const error = new Parse.Error(2000, 'afterLogout error'); const trigger = { afterLogout() { throw error; }, }; - const spy = spyOn(trigger, "afterLogout").and.callThrough(); + const spy = spyOn(trigger, 'afterLogout').and.callThrough(); Parse.Cloud.afterLogout(trigger.afterLogout); - await Parse.User.signUp("user", "pass"); + await Parse.User.signUp('user', 'pass'); const response = await Parse.User.logOut().catch(e => e); expect(spy).toHaveBeenCalled(); expect(response).toEqual(error); }); - it_id("5656d6d7-65ef-43d1-8ca6-6942ae3614d5")(it)( - "should have expected data in request in beforeLogin", + it_id('5656d6d7-65ef-43d1-8ca6-6942ae3614d5')(it)( + 'should have expected data in request in beforeLogin', async done => { Parse.Cloud.beforeLogin(req => { expect(req.object).toBeDefined(); @@ -3449,35 +3449,35 @@ describe("beforeLogin hook", () => { expect(req.context).toBeDefined(); }); - await Parse.User.signUp("tupac", "shakur"); - await Parse.User.logIn("tupac", "shakur"); + await Parse.User.signUp('tupac', 'shakur'); + await Parse.User.logIn('tupac', 'shakur'); done(); } ); - it("afterFind should not be triggered when saving an object", async () => { + it('afterFind should not be triggered when saving an object', async () => { let beforeSaves = 0; - Parse.Cloud.beforeSave("SavingTest", () => { + Parse.Cloud.beforeSave('SavingTest', () => { beforeSaves++; }); let afterSaves = 0; - Parse.Cloud.afterSave("SavingTest", () => { + Parse.Cloud.afterSave('SavingTest', () => { afterSaves++; }); let beforeFinds = 0; - Parse.Cloud.beforeFind("SavingTest", () => { + Parse.Cloud.beforeFind('SavingTest', () => { beforeFinds++; }); let afterFinds = 0; - Parse.Cloud.afterFind("SavingTest", () => { + Parse.Cloud.afterFind('SavingTest', () => { afterFinds++; }); - const obj = new Parse.Object("SavingTest"); - obj.set("someField", "some value 1"); + const obj = new Parse.Object('SavingTest'); + obj.set('someField', 'some value 1'); await obj.save(); expect(beforeSaves).toEqual(1); @@ -3485,7 +3485,7 @@ describe("beforeLogin hook", () => { expect(beforeFinds).toEqual(0); expect(afterFinds).toEqual(0); - obj.set("someField", "some value 2"); + obj.set('someField', 'some value 2'); await obj.save(); expect(beforeSaves).toEqual(2); @@ -3500,7 +3500,7 @@ describe("beforeLogin hook", () => { expect(beforeFinds).toEqual(1); expect(afterFinds).toEqual(1); - obj.set("someField", "some value 3"); + obj.set('someField', 'some value 3'); await obj.save(); expect(beforeSaves).toEqual(3); @@ -3510,33 +3510,33 @@ describe("beforeLogin hook", () => { }); }); -describe("afterLogin hook", () => { - it("should run afterLogin after successful login", async done => { +describe('afterLogin hook', () => { + it('should run afterLogin after successful login', async done => { let hit = 0; Parse.Cloud.afterLogin(req => { hit++; - expect(req.object.get("username")).toEqual("testuser"); + expect(req.object.get('username')).toEqual('testuser'); }); - await Parse.User.signUp("testuser", "p@ssword"); - const user = await Parse.User.logIn("testuser", "p@ssword"); + await Parse.User.signUp('testuser', 'p@ssword'); + const user = await Parse.User.logIn('testuser', 'p@ssword'); expect(hit).toBe(1); expect(user).toBeDefined(); - expect(user.getUsername()).toBe("testuser"); + expect(user.getUsername()).toBe('testuser'); expect(user.getSessionToken()).toBeDefined(); done(); }); - it("should not run afterLogin after unsuccessful login", async done => { + it('should not run afterLogin after unsuccessful login', async done => { let hit = 0; Parse.Cloud.afterLogin(req => { hit++; - expect(req.object.get("username")).toEqual("testuser"); + expect(req.object.get('username')).toEqual('testuser'); }); - await Parse.User.signUp("testuser", "p@ssword"); + await Parse.User.signUp('testuser', 'p@ssword'); try { - await Parse.User.logIn("testuser", "badpassword"); + await Parse.User.logIn('testuser', 'badpassword'); } catch (e) { expect(e.code).toBe(Parse.Error.OBJECT_NOT_FOUND); } @@ -3544,21 +3544,21 @@ describe("afterLogin hook", () => { done(); }); - it("should not run afterLogin on sign up", async done => { + it('should not run afterLogin on sign up', async done => { let hit = 0; Parse.Cloud.afterLogin(req => { hit++; - expect(req.object.get("username")).toEqual("testuser"); + expect(req.object.get('username')).toEqual('testuser'); }); - const user = await Parse.User.signUp("testuser", "p@ssword"); + const user = await Parse.User.signUp('testuser', 'p@ssword'); expect(user).toBeDefined(); expect(hit).toBe(0); done(); }); - it_id("e86155c4-62e1-4c6e-ab4a-9ac6c87c60f2")(it)( - "should have expected data in request in afterLogin", + it_id('e86155c4-62e1-4c6e-ab4a-9ac6c87c60f2')(it)( + 'should have expected data in request in afterLogin', async done => { Parse.Cloud.afterLogin(req => { expect(req.object).toBeDefined(); @@ -3569,264 +3569,264 @@ describe("afterLogin hook", () => { expect(req.context).toBeDefined(); }); - await Parse.User.signUp("testuser", "p@ssword"); - await Parse.User.logIn("testuser", "p@ssword"); + await Parse.User.signUp('testuser', 'p@ssword'); + await Parse.User.logIn('testuser', 'p@ssword'); done(); } ); - it("context options should override _context object property when saving a new object", async () => { - Parse.Cloud.beforeSave("TestObject", req => { - expect(req.context.a).toEqual("a"); + it('context options should override _context object property when saving a new object', async () => { + Parse.Cloud.beforeSave('TestObject', req => { + expect(req.context.a).toEqual('a'); expect(req.context.hello).not.toBeDefined(); expect(req._context).not.toBeDefined(); expect(req.object._context).not.toBeDefined(); expect(req.object.context).not.toBeDefined(); }); - Parse.Cloud.afterSave("TestObject", req => { - expect(req.context.a).toEqual("a"); + Parse.Cloud.afterSave('TestObject', req => { + expect(req.context.a).toEqual('a'); expect(req.context.hello).not.toBeDefined(); expect(req._context).not.toBeDefined(); expect(req.object._context).not.toBeDefined(); expect(req.object.context).not.toBeDefined(); }); await request({ - url: "http://localhost:8378/1/classes/TestObject", - method: "POST", + url: 'http://localhost:8378/1/classes/TestObject', + method: 'POST', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "X-Parse-Cloud-Context": '{"a":"a"}', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Cloud-Context': '{"a":"a"}', }, - body: JSON.stringify({ _context: { hello: "world" } }), + body: JSON.stringify({ _context: { hello: 'world' } }), }); }); - it("should have access to context when saving a new object", async () => { - Parse.Cloud.beforeSave("TestObject", req => { - expect(req.context.a).toEqual("a"); + it('should have access to context when saving a new object', async () => { + Parse.Cloud.beforeSave('TestObject', req => { + expect(req.context.a).toEqual('a'); }); - Parse.Cloud.afterSave("TestObject", req => { - expect(req.context.a).toEqual("a"); + Parse.Cloud.afterSave('TestObject', req => { + expect(req.context.a).toEqual('a'); }); const obj = new TestObject(); - await obj.save(null, { context: { a: "a" } }); + await obj.save(null, { context: { a: 'a' } }); }); - it("should have access to context when saving an existing object", async () => { + it('should have access to context when saving an existing object', async () => { const obj = new TestObject(); await obj.save(null); - Parse.Cloud.beforeSave("TestObject", req => { - expect(req.context.a).toEqual("a"); + Parse.Cloud.beforeSave('TestObject', req => { + expect(req.context.a).toEqual('a'); }); - Parse.Cloud.afterSave("TestObject", req => { - expect(req.context.a).toEqual("a"); + Parse.Cloud.afterSave('TestObject', req => { + expect(req.context.a).toEqual('a'); }); - await obj.save(null, { context: { a: "a" } }); + await obj.save(null, { context: { a: 'a' } }); }); - it("should have access to context when saving a new object in a trigger", async () => { - Parse.Cloud.beforeSave("TestObject", req => { - expect(req.context.a).toEqual("a"); + it('should have access to context when saving a new object in a trigger', async () => { + Parse.Cloud.beforeSave('TestObject', req => { + expect(req.context.a).toEqual('a'); }); - Parse.Cloud.afterSave("TestObject", req => { - expect(req.context.a).toEqual("a"); + Parse.Cloud.afterSave('TestObject', req => { + expect(req.context.a).toEqual('a'); }); - Parse.Cloud.afterSave("TriggerObject", async () => { + Parse.Cloud.afterSave('TriggerObject', async () => { const obj = new TestObject(); - await obj.save(null, { context: { a: "a" } }); + await obj.save(null, { context: { a: 'a' } }); }); - const obj = new Parse.Object("TriggerObject"); + const obj = new Parse.Object('TriggerObject'); await obj.save(null); }); - it("should have access to context when cascade-saving objects", async () => { - Parse.Cloud.beforeSave("TestObject", req => { - expect(req.context.a).toEqual("a"); + it('should have access to context when cascade-saving objects', async () => { + Parse.Cloud.beforeSave('TestObject', req => { + expect(req.context.a).toEqual('a'); }); - Parse.Cloud.afterSave("TestObject", req => { - expect(req.context.a).toEqual("a"); + Parse.Cloud.afterSave('TestObject', req => { + expect(req.context.a).toEqual('a'); }); - Parse.Cloud.beforeSave("TestObject2", req => { - expect(req.context.a).toEqual("a"); + Parse.Cloud.beforeSave('TestObject2', req => { + expect(req.context.a).toEqual('a'); }); - Parse.Cloud.afterSave("TestObject2", req => { - expect(req.context.a).toEqual("a"); + Parse.Cloud.afterSave('TestObject2', req => { + expect(req.context.a).toEqual('a'); }); - const obj = new Parse.Object("TestObject"); - const obj2 = new Parse.Object("TestObject2"); - obj.set("obj2", obj2); - await obj.save(null, { context: { a: "a" } }); + const obj = new Parse.Object('TestObject'); + const obj2 = new Parse.Object('TestObject2'); + obj.set('obj2', obj2); + await obj.save(null, { context: { a: 'a' } }); }); - it("should have access to context as saveAll argument", async () => { - Parse.Cloud.beforeSave("TestObject", req => { - expect(req.context.a).toEqual("a"); + it('should have access to context as saveAll argument', async () => { + Parse.Cloud.beforeSave('TestObject', req => { + expect(req.context.a).toEqual('a'); }); - Parse.Cloud.afterSave("TestObject", req => { - expect(req.context.a).toEqual("a"); + Parse.Cloud.afterSave('TestObject', req => { + expect(req.context.a).toEqual('a'); }); const obj1 = new TestObject(); const obj2 = new TestObject(); - await Parse.Object.saveAll([obj1, obj2], { context: { a: "a" } }); + await Parse.Object.saveAll([obj1, obj2], { context: { a: 'a' } }); }); - it("should have access to context as destroyAll argument", async () => { - Parse.Cloud.beforeDelete("TestObject", req => { - expect(req.context.a).toEqual("a"); + it('should have access to context as destroyAll argument', async () => { + Parse.Cloud.beforeDelete('TestObject', req => { + expect(req.context.a).toEqual('a'); }); - Parse.Cloud.afterDelete("TestObject", req => { - expect(req.context.a).toEqual("a"); + Parse.Cloud.afterDelete('TestObject', req => { + expect(req.context.a).toEqual('a'); }); const obj1 = new TestObject(); const obj2 = new TestObject(); await Parse.Object.saveAll([obj1, obj2]); - await Parse.Object.destroyAll([obj1, obj2], { context: { a: "a" } }); + await Parse.Object.destroyAll([obj1, obj2], { context: { a: 'a' } }); }); - it("should have access to context as destroy a object", async () => { - Parse.Cloud.beforeDelete("TestObject", req => { - expect(req.context.a).toEqual("a"); + it('should have access to context as destroy a object', async () => { + Parse.Cloud.beforeDelete('TestObject', req => { + expect(req.context.a).toEqual('a'); }); - Parse.Cloud.afterDelete("TestObject", req => { - expect(req.context.a).toEqual("a"); + Parse.Cloud.afterDelete('TestObject', req => { + expect(req.context.a).toEqual('a'); }); const obj = new TestObject(); await obj.save(); - await obj.destroy({ context: { a: "a" } }); + await obj.destroy({ context: { a: 'a' } }); }); - it("should have access to context in beforeFind hook", async () => { - Parse.Cloud.beforeFind("TestObject", req => { - expect(req.context.a).toEqual("a"); + it('should have access to context in beforeFind hook', async () => { + Parse.Cloud.beforeFind('TestObject', req => { + expect(req.context.a).toEqual('a'); }); - const query = new Parse.Query("TestObject"); - return query.find({ context: { a: "a" } }); + const query = new Parse.Query('TestObject'); + return query.find({ context: { a: 'a' } }); }); - it("should have access to context when cloud function is called.", async () => { - Parse.Cloud.define("contextTest", async req => { - expect(req.context.a).toEqual("a"); + it('should have access to context when cloud function is called.', async () => { + Parse.Cloud.define('contextTest', async req => { + expect(req.context.a).toEqual('a'); return {}; }); - await Parse.Cloud.run("contextTest", {}, { context: { a: "a" } }); + await Parse.Cloud.run('contextTest', {}, { context: { a: 'a' } }); }); - it("afterFind should have access to context", async () => { - Parse.Cloud.afterFind("TestObject", req => { - expect(req.context.a).toEqual("a"); + it('afterFind should have access to context', async () => { + Parse.Cloud.afterFind('TestObject', req => { + expect(req.context.a).toEqual('a'); }); const obj = new TestObject(); await obj.save(); const query = new Parse.Query(TestObject); - await query.find({ context: { a: "a" } }); + await query.find({ context: { a: 'a' } }); }); - it("beforeFind and afterFind should have access to context while making fetch call", async () => { - Parse.Cloud.beforeFind("TestObject", req => { - expect(req.context.a).toEqual("a"); + it('beforeFind and afterFind should have access to context while making fetch call', async () => { + Parse.Cloud.beforeFind('TestObject', req => { + expect(req.context.a).toEqual('a'); expect(req.context.b).toBeUndefined(); - req.context.b = "b"; + req.context.b = 'b'; }); - Parse.Cloud.afterFind("TestObject", req => { - expect(req.context.a).toEqual("a"); - expect(req.context.b).toEqual("b"); + Parse.Cloud.afterFind('TestObject', req => { + expect(req.context.a).toEqual('a'); + expect(req.context.b).toEqual('b'); }); const obj = new TestObject(); await obj.save(); - await obj.fetch({ context: { a: "a" } }); + await obj.fetch({ context: { a: 'a' } }); }); }); -describe("saveFile hooks", () => { - it("beforeSave(Parse.File) should return file that is already saved and not save anything to files adapter", async () => { +describe('saveFile hooks', () => { + it('beforeSave(Parse.File) should return file that is already saved and not save anything to files adapter', async () => { await reconfigureServer({ filesAdapter: mockAdapter }); - const createFileSpy = spyOn(mockAdapter, "createFile").and.callThrough(); + const createFileSpy = spyOn(mockAdapter, 'createFile').and.callThrough(); Parse.Cloud.beforeSave(Parse.File, () => { - const newFile = new Parse.File("some-file.txt"); + const newFile = new Parse.File('some-file.txt'); newFile._url = - "http://www.somewhere.com/parse/files/some-app-id/some-file.txt"; + 'http://www.somewhere.com/parse/files/some-app-id/some-file.txt'; return newFile; }); - const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); + const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); const result = await file.save({ useMasterKey: true }); expect(result).toBe(file); - expect(result._name).toBe("some-file.txt"); + expect(result._name).toBe('some-file.txt'); expect(result._url).toBe( - "http://www.somewhere.com/parse/files/some-app-id/some-file.txt" + 'http://www.somewhere.com/parse/files/some-app-id/some-file.txt' ); expect(createFileSpy).not.toHaveBeenCalled(); }); - it("beforeSave(Parse.File) should throw error", async () => { + it('beforeSave(Parse.File) should throw error', async () => { await reconfigureServer({ filesAdapter: mockAdapter }); Parse.Cloud.beforeSave(Parse.File, () => { - throw new Parse.Error(400, "some-error-message"); + throw new Parse.Error(400, 'some-error-message'); }); - const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); + const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); try { await file.save({ useMasterKey: true }); } catch (error) { - expect(error.message).toBe("some-error-message"); + expect(error.message).toBe('some-error-message'); } }); - it("beforeSave(Parse.File) should change values of uploaded file by editing fileObject directly", async () => { + it('beforeSave(Parse.File) should change values of uploaded file by editing fileObject directly', async () => { await reconfigureServer({ filesAdapter: mockAdapter }); - const createFileSpy = spyOn(mockAdapter, "createFile").and.callThrough(); + const createFileSpy = spyOn(mockAdapter, 'createFile').and.callThrough(); Parse.Cloud.beforeSave(Parse.File, async req => { - expect(req.triggerName).toEqual("beforeSave"); + expect(req.triggerName).toEqual('beforeSave'); expect(req.master).toBe(true); - req.file.addMetadata("foo", "bar"); - req.file.addTag("tagA", "some-tag"); + req.file.addMetadata('foo', 'bar'); + req.file.addTag('tagA', 'some-tag'); }); - const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); + const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); const result = await file.save({ useMasterKey: true }); expect(result).toBe(file); const newData = new Buffer([1, 2, 3]); const newOptions = { tags: { - tagA: "some-tag", + tagA: 'some-tag', }, metadata: { - foo: "bar", + foo: 'bar', }, }; expect(createFileSpy).toHaveBeenCalledWith( jasmine.any(String), newData, - "text/plain", + 'text/plain', newOptions ); }); - it("beforeSave(Parse.File) should change values by returning new fileObject", async () => { + it('beforeSave(Parse.File) should change values by returning new fileObject', async () => { await reconfigureServer({ filesAdapter: mockAdapter }); - const createFileSpy = spyOn(mockAdapter, "createFile").and.callThrough(); + const createFileSpy = spyOn(mockAdapter, 'createFile').and.callThrough(); Parse.Cloud.beforeSave(Parse.File, async req => { - expect(req.triggerName).toEqual("beforeSave"); + expect(req.triggerName).toEqual('beforeSave'); expect(req.fileSize).toBe(3); const newFile = new Parse.File( - "donald_duck.pdf", + 'donald_duck.pdf', [4, 5, 6], - "application/pdf" + 'application/pdf' ); - newFile.setMetadata({ foo: "bar" }); - newFile.setTags({ tagA: "some-tag" }); + newFile.setMetadata({ foo: 'bar' }); + newFile.setTags({ tagA: 'some-tag' }); return newFile; }); - const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); + const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); const result = await file.save({ useMasterKey: true }); expect(result).toBeInstanceOf(Parse.File); const newData = new Buffer([4, 5, 6]); - const newContentType = "application/pdf"; + const newContentType = 'application/pdf'; const newOptions = { tags: { - tagA: "some-tag", + tagA: 'some-tag', }, metadata: { - foo: "bar", + foo: 'bar', }, }; expect(createFileSpy).toHaveBeenCalledWith( @@ -3835,117 +3835,117 @@ describe("saveFile hooks", () => { newContentType, newOptions ); - const expectedFileName = "donald_duck.pdf"; + const expectedFileName = 'donald_duck.pdf'; expect(file._name.indexOf(expectedFileName)).toBe( file._name.length - expectedFileName.length ); }); - it("beforeSave(Parse.File) should contain metadata and tags saved from client", async () => { + it('beforeSave(Parse.File) should contain metadata and tags saved from client', async () => { await reconfigureServer({ filesAdapter: mockAdapter }); - const createFileSpy = spyOn(mockAdapter, "createFile").and.callThrough(); + const createFileSpy = spyOn(mockAdapter, 'createFile').and.callThrough(); Parse.Cloud.beforeSave(Parse.File, async req => { - expect(req.triggerName).toEqual("beforeSave"); + expect(req.triggerName).toEqual('beforeSave'); expect(req.fileSize).toBe(3); expect(req.file).toBeInstanceOf(Parse.File); - expect(req.file.name()).toBe("popeye.txt"); - expect(req.file.metadata()).toEqual({ foo: "bar" }); - expect(req.file.tags()).toEqual({ bar: "foo" }); + expect(req.file.name()).toBe('popeye.txt'); + expect(req.file.metadata()).toEqual({ foo: 'bar' }); + expect(req.file.tags()).toEqual({ bar: 'foo' }); }); - const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); - file.setMetadata({ foo: "bar" }); - file.setTags({ bar: "foo" }); + const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); + file.setMetadata({ foo: 'bar' }); + file.setTags({ bar: 'foo' }); const result = await file.save({ useMasterKey: true }); expect(result).toBeInstanceOf(Parse.File); const options = { - metadata: { foo: "bar" }, - tags: { bar: "foo" }, + metadata: { foo: 'bar' }, + tags: { bar: 'foo' }, }; expect(createFileSpy).toHaveBeenCalledWith( jasmine.any(String), jasmine.any(Buffer), - "text/plain", + 'text/plain', options ); }); - it("beforeSave(Parse.File) should return same file data with new file name", async () => { + it('beforeSave(Parse.File) should return same file data with new file name', async () => { await reconfigureServer({ filesAdapter: mockAdapter }); - const config = Config.get("test"); + const config = Config.get('test'); config.filesController.options.preserveFileName = true; Parse.Cloud.beforeSave(Parse.File, async ({ file }) => { - expect(file.name()).toBe("popeye.txt"); + expect(file.name()).toBe('popeye.txt'); const fileData = await file.getData(); - const newFile = new Parse.File("2020-04-01.txt", { base64: fileData }); + const newFile = new Parse.File('2020-04-01.txt', { base64: fileData }); return newFile; }); - const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); + const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); const result = await file.save({ useMasterKey: true }); - expect(result.name()).toBe("2020-04-01.txt"); + expect(result.name()).toBe('2020-04-01.txt'); }); - it("afterSave(Parse.File) should set fileSize to null if beforeSave returns an already saved file", async () => { + it('afterSave(Parse.File) should set fileSize to null if beforeSave returns an already saved file', async () => { await reconfigureServer({ filesAdapter: mockAdapter }); - const createFileSpy = spyOn(mockAdapter, "createFile").and.callThrough(); + const createFileSpy = spyOn(mockAdapter, 'createFile').and.callThrough(); Parse.Cloud.beforeSave(Parse.File, req => { expect(req.fileSize).toBe(3); - const newFile = new Parse.File("some-file.txt"); + const newFile = new Parse.File('some-file.txt'); newFile._url = - "http://www.somewhere.com/parse/files/some-app-id/some-file.txt"; + 'http://www.somewhere.com/parse/files/some-app-id/some-file.txt'; return newFile; }); Parse.Cloud.afterSave(Parse.File, req => { expect(req.fileSize).toBe(null); }); - const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); + const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); const result = await file.save({ useMasterKey: true }); expect(result).toBe(result); - expect(result._name).toBe("some-file.txt"); + expect(result._name).toBe('some-file.txt'); expect(result._url).toBe( - "http://www.somewhere.com/parse/files/some-app-id/some-file.txt" + 'http://www.somewhere.com/parse/files/some-app-id/some-file.txt' ); expect(createFileSpy).not.toHaveBeenCalled(); }); - it("afterSave(Parse.File) should throw error", async () => { + it('afterSave(Parse.File) should throw error', async () => { await reconfigureServer({ filesAdapter: mockAdapter }); Parse.Cloud.afterSave(Parse.File, async () => { - throw new Parse.Error(400, "some-error-message"); + throw new Parse.Error(400, 'some-error-message'); }); - const filename = "donald_duck.pdf"; - const file = new Parse.File(filename, [1, 2, 3], "text/plain"); + const filename = 'donald_duck.pdf'; + const file = new Parse.File(filename, [1, 2, 3], 'text/plain'); try { await file.save({ useMasterKey: true }); } catch (error) { - expect(error.message).toBe("some-error-message"); + expect(error.message).toBe('some-error-message'); } }); - it("afterSave(Parse.File) should call with fileObject", async done => { + it('afterSave(Parse.File) should call with fileObject', async done => { await reconfigureServer({ filesAdapter: mockAdapter }); Parse.Cloud.beforeSave(Parse.File, async req => { - req.file.setTags({ tagA: "some-tag" }); - req.file.setMetadata({ foo: "bar" }); + req.file.setTags({ tagA: 'some-tag' }); + req.file.setMetadata({ foo: 'bar' }); }); Parse.Cloud.afterSave(Parse.File, async req => { expect(req.master).toBe(true); - expect(req.file._tags).toEqual({ tagA: "some-tag" }); - expect(req.file._metadata).toEqual({ foo: "bar" }); + expect(req.file._tags).toEqual({ tagA: 'some-tag' }); + expect(req.file._metadata).toEqual({ foo: 'bar' }); done(); }); - const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); + const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); await file.save({ useMasterKey: true }); }); - it("afterSave(Parse.File) should change fileSize when file data changes", async done => { + it('afterSave(Parse.File) should change fileSize when file data changes', async done => { await reconfigureServer({ filesAdapter: mockAdapter }); Parse.Cloud.beforeSave(Parse.File, async req => { expect(req.fileSize).toBe(3); expect(req.master).toBe(true); const newFile = new Parse.File( - "donald_duck.pdf", + 'donald_duck.pdf', [4, 5, 6, 7, 8, 9], - "application/pdf" + 'application/pdf' ); return newFile; }); @@ -3954,85 +3954,85 @@ describe("saveFile hooks", () => { expect(req.master).toBe(true); done(); }); - const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); + const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); await file.save({ useMasterKey: true }); }); - it("beforeDelete(Parse.File) should call with fileObject", async () => { + it('beforeDelete(Parse.File) should call with fileObject', async () => { await reconfigureServer({ filesAdapter: mockAdapter }); Parse.Cloud.beforeDelete(Parse.File, req => { expect(req.file).toBeInstanceOf(Parse.File); - expect(req.file._name).toEqual("popeye.txt"); - expect(req.file._url).toEqual("http://www.somewhere.com/popeye.txt"); + expect(req.file._name).toEqual('popeye.txt'); + expect(req.file._url).toEqual('http://www.somewhere.com/popeye.txt'); expect(req.fileSize).toBe(null); }); - const file = new Parse.File("popeye.txt"); + const file = new Parse.File('popeye.txt'); await file.destroy({ useMasterKey: true }); }); - it("beforeDelete(Parse.File) should throw error", async done => { + it('beforeDelete(Parse.File) should throw error', async done => { await reconfigureServer({ filesAdapter: mockAdapter }); Parse.Cloud.beforeDelete(Parse.File, () => { - throw new Error("some error message"); + throw new Error('some error message'); }); - const file = new Parse.File("popeye.txt"); + const file = new Parse.File('popeye.txt'); try { await file.destroy({ useMasterKey: true }); } catch (error) { - expect(error.message).toBe("some error message"); + expect(error.message).toBe('some error message'); done(); } }); - it("afterDelete(Parse.File) should call with fileObject", async done => { + it('afterDelete(Parse.File) should call with fileObject', async done => { await reconfigureServer({ filesAdapter: mockAdapter }); Parse.Cloud.beforeDelete(Parse.File, req => { expect(req.file).toBeInstanceOf(Parse.File); - expect(req.file._name).toEqual("popeye.txt"); - expect(req.file._url).toEqual("http://www.somewhere.com/popeye.txt"); + expect(req.file._name).toEqual('popeye.txt'); + expect(req.file._url).toEqual('http://www.somewhere.com/popeye.txt'); }); Parse.Cloud.afterDelete(Parse.File, req => { expect(req.file).toBeInstanceOf(Parse.File); - expect(req.file._name).toEqual("popeye.txt"); - expect(req.file._url).toEqual("http://www.somewhere.com/popeye.txt"); + expect(req.file._name).toEqual('popeye.txt'); + expect(req.file._url).toEqual('http://www.somewhere.com/popeye.txt'); done(); }); - const file = new Parse.File("popeye.txt"); + const file = new Parse.File('popeye.txt'); await file.destroy({ useMasterKey: true }); }); - it("beforeSave(Parse.File) should not change file if nothing is returned", async () => { + it('beforeSave(Parse.File) should not change file if nothing is returned', async () => { await reconfigureServer({ filesAdapter: mockAdapter }); Parse.Cloud.beforeSave(Parse.File, () => { return; }); - const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); + const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); const result = await file.save({ useMasterKey: true }); expect(result).toBe(file); }); - it("throw custom error from beforeSave(Parse.File) ", async done => { + it('throw custom error from beforeSave(Parse.File) ', async done => { Parse.Cloud.beforeSave(Parse.File, () => { - throw new Parse.Error(Parse.Error.SCRIPT_FAILED, "It should fail"); + throw new Parse.Error(Parse.Error.SCRIPT_FAILED, 'It should fail'); }); try { - const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); + const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); await file.save({ useMasterKey: true }); - fail("error should have thrown"); + fail('error should have thrown'); } catch (e) { expect(e.code).toBe(Parse.Error.SCRIPT_FAILED); done(); } }); - it("throw empty error from beforeSave(Parse.File)", async done => { + it('throw empty error from beforeSave(Parse.File)', async done => { Parse.Cloud.beforeSave(Parse.File, () => { throw null; }); try { - const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); + const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); await file.save({ useMasterKey: true }); - fail("error should have thrown"); + fail('error should have thrown'); } catch (e) { expect(e.code).toBe(130); done(); @@ -4040,23 +4040,23 @@ describe("saveFile hooks", () => { }); }); -describe("Parse.File hooks", () => { - it("find hooks should run", async () => { - const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); +describe('Parse.File hooks', () => { + it('find hooks should run', async () => { + const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); await file.save({ useMasterKey: true }); - const user = await Parse.User.signUp("username", "password"); + const user = await Parse.User.signUp('username', 'password'); const hooks = { beforeFind(req) { expect(req).toBeDefined(); expect(req.file).toBeDefined(); - expect(req.triggerName).toBe("beforeFind"); + expect(req.triggerName).toBe('beforeFind'); expect(req.master).toBeFalse(); expect(req.log).toBeDefined(); }, afterFind(req) { expect(req).toBeDefined(); expect(req.file).toBeDefined(); - expect(req.triggerName).toBe("afterFind"); + expect(req.triggerName).toBe('afterFind'); expect(req.master).toBeFalse(); expect(req.log).toBeDefined(); expect(req.forceDownload).toBeFalse(); @@ -4069,9 +4069,9 @@ describe("Parse.File hooks", () => { await request({ url: file.url(), headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "X-Parse-Session-Token": user.getSessionToken(), + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Session-Token': user.getSessionToken(), }, }); for (const hook in hooks) { @@ -4079,13 +4079,13 @@ describe("Parse.File hooks", () => { } }); - it("beforeFind can throw", async () => { - const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); + it('beforeFind can throw', async () => { + const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); await file.save({ useMasterKey: true }); - const user = await Parse.User.signUp("username", "password"); + const user = await Parse.User.signUp('username', 'password'); const hooks = { beforeFind() { - throw "unauthorized"; + throw 'unauthorized'; }, afterFind() {}, }; @@ -4097,29 +4097,29 @@ describe("Parse.File hooks", () => { request({ url: file.url(), headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "X-Parse-Session-Token": user.getSessionToken(), + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Session-Token': user.getSessionToken(), }, }).catch(e => { throw new Parse.Error(e.data.code, e.data.error); }) ).toBeRejectedWith( - new Parse.Error(Parse.Error.SCRIPT_FAILED, "unauthorized") + new Parse.Error(Parse.Error.SCRIPT_FAILED, 'unauthorized') ); expect(hooks.beforeFind).toHaveBeenCalled(); expect(hooks.afterFind).not.toHaveBeenCalled(); }); - it("afterFind can throw", async () => { - const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); + it('afterFind can throw', async () => { + const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); await file.save({ useMasterKey: true }); - const user = await Parse.User.signUp("username", "password"); + const user = await Parse.User.signUp('username', 'password'); const hooks = { beforeFind() {}, afterFind() { - throw "unauthorized"; + throw 'unauthorized'; }, }; for (const hook in hooks) { @@ -4130,52 +4130,52 @@ describe("Parse.File hooks", () => { request({ url: file.url(), headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "X-Parse-Session-Token": user.getSessionToken(), + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Session-Token': user.getSessionToken(), }, }).catch(e => { throw new Parse.Error(e.data.code, e.data.error); }) ).toBeRejectedWith( - new Parse.Error(Parse.Error.SCRIPT_FAILED, "unauthorized") + new Parse.Error(Parse.Error.SCRIPT_FAILED, 'unauthorized') ); for (const hook in hooks) { expect(hooks[hook]).toHaveBeenCalled(); } }); - it("can force download", async () => { - const file = new Parse.File("popeye.txt", [1, 2, 3], "text/plain"); + it('can force download', async () => { + const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); await file.save({ useMasterKey: true }); - const user = await Parse.User.signUp("username", "password"); + const user = await Parse.User.signUp('username', 'password'); Parse.Cloud.afterFind(Parse.File, req => { req.forceDownload = true; }); const response = await request({ url: file.url(), headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "X-Parse-Session-Token": user.getSessionToken(), + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Session-Token': user.getSessionToken(), }, }); - expect(response.headers["content-disposition"]).toBe( + expect(response.headers['content-disposition']).toBe( `attachment;filename=${file._name}` ); }); }); -describe("Cloud Config hooks", () => { +describe('Cloud Config hooks', () => { function testConfig() { return Parse.Config.save( - { internal: "i", string: "s", number: 12 }, + { internal: 'i', string: 's', number: 12 }, { internal: true } ); } - it_id("997fe20a-96f7-454a-a5b0-c155b8d02f05")(it)( - "beforeSave(Parse.Config) can run hook with new config", + it_id('997fe20a-96f7-454a-a5b0-c155b8d02f05')(it)( + 'beforeSave(Parse.Config) can run hook with new config', async () => { let count = 0; Parse.Cloud.beforeSave(Parse.Config, req => { @@ -4187,32 +4187,32 @@ describe("Cloud Config hooks", () => { expect(req.installationId).toBeDefined(); expect(req.context).toBeDefined(); const config = req.object; - expect(config.get("internal")).toBe("i"); - expect(config.get("string")).toBe("s"); - expect(config.get("number")).toBe(12); + expect(config.get('internal')).toBe('i'); + expect(config.get('string')).toBe('s'); + expect(config.get('number')).toBe(12); count += 1; }); await testConfig(); const config = await Parse.Config.get({ useMasterKey: true }); - expect(config.get("internal")).toBe("i"); - expect(config.get("string")).toBe("s"); - expect(config.get("number")).toBe(12); + expect(config.get('internal')).toBe('i'); + expect(config.get('string')).toBe('s'); + expect(config.get('number')).toBe(12); expect(count).toBe(1); } ); - it_id("06a9b66c-ffb4-43d1-a025-f7d2192500e7")(it)( - "beforeSave(Parse.Config) can run hook with existing config", + it_id('06a9b66c-ffb4-43d1-a025-f7d2192500e7')(it)( + 'beforeSave(Parse.Config) can run hook with existing config', async () => { let count = 0; Parse.Cloud.beforeSave(Parse.Config, req => { if (count === 0) { - expect(req.object.get("number")).toBe(12); + expect(req.object.get('number')).toBe(12); expect(req.original).toBeUndefined(); } if (count === 1) { - expect(req.object.get("number")).toBe(13); - expect(req.original.get("number")).toBe(12); + expect(req.object.get('number')).toBe(13); + expect(req.original.get('number')).toBe(12); } count += 1; }); @@ -4222,8 +4222,8 @@ describe("Cloud Config hooks", () => { } ); - it_id("ca76de8e-671b-4c2d-9535-bd28a855fa1a")(it)( - "beforeSave(Parse.Config) should not change config if nothing is returned", + it_id('ca76de8e-671b-4c2d-9535-bd28a855fa1a')(it)( + 'beforeSave(Parse.Config) should not change config if nothing is returned', async () => { let count = 0; Parse.Cloud.beforeSave(Parse.Config, () => { @@ -4232,54 +4232,54 @@ describe("Cloud Config hooks", () => { }); await testConfig(); const config = await Parse.Config.get({ useMasterKey: true }); - expect(config.get("internal")).toBe("i"); - expect(config.get("string")).toBe("s"); - expect(config.get("number")).toBe(12); + expect(config.get('internal')).toBe('i'); + expect(config.get('string')).toBe('s'); + expect(config.get('number')).toBe(12); expect(count).toBe(1); } ); - it("beforeSave(Parse.Config) throw custom error", async () => { + it('beforeSave(Parse.Config) throw custom error', async () => { Parse.Cloud.beforeSave(Parse.Config, () => { - throw new Parse.Error(Parse.Error.SCRIPT_FAILED, "It should fail"); + throw new Parse.Error(Parse.Error.SCRIPT_FAILED, 'It should fail'); }); try { await testConfig(); - fail("error should have thrown"); + fail('error should have thrown'); } catch (e) { expect(e.code).toBe(Parse.Error.SCRIPT_FAILED); - expect(e.message).toBe("It should fail"); + expect(e.message).toBe('It should fail'); } }); - it("beforeSave(Parse.Config) throw string error", async () => { + it('beforeSave(Parse.Config) throw string error', async () => { Parse.Cloud.beforeSave(Parse.Config, () => { - throw "before save failed"; + throw 'before save failed'; }); try { await testConfig(); - fail("error should have thrown"); + fail('error should have thrown'); } catch (e) { expect(e.code).toBe(Parse.Error.SCRIPT_FAILED); - expect(e.message).toBe("before save failed"); + expect(e.message).toBe('before save failed'); } }); - it("beforeSave(Parse.Config) throw empty error", async () => { + it('beforeSave(Parse.Config) throw empty error', async () => { Parse.Cloud.beforeSave(Parse.Config, () => { throw null; }); try { await testConfig(); - fail("error should have thrown"); + fail('error should have thrown'); } catch (e) { expect(e.code).toBe(Parse.Error.SCRIPT_FAILED); - expect(e.message).toBe("Script failed. Unknown error."); + expect(e.message).toBe('Script failed. Unknown error.'); } }); - it_id("3e7a75c0-6c2e-4c7e-b042-6eb5f23acf94")(it)( - "afterSave(Parse.Config) can run hook with new config", + it_id('3e7a75c0-6c2e-4c7e-b042-6eb5f23acf94')(it)( + 'afterSave(Parse.Config) can run hook with new config', async () => { let count = 0; Parse.Cloud.afterSave(Parse.Config, req => { @@ -4291,32 +4291,32 @@ describe("Cloud Config hooks", () => { expect(req.installationId).toBeDefined(); expect(req.context).toBeDefined(); const config = req.object; - expect(config.get("internal")).toBe("i"); - expect(config.get("string")).toBe("s"); - expect(config.get("number")).toBe(12); + expect(config.get('internal')).toBe('i'); + expect(config.get('string')).toBe('s'); + expect(config.get('number')).toBe(12); count += 1; }); await testConfig(); const config = await Parse.Config.get({ useMasterKey: true }); - expect(config.get("internal")).toBe("i"); - expect(config.get("string")).toBe("s"); - expect(config.get("number")).toBe(12); + expect(config.get('internal')).toBe('i'); + expect(config.get('string')).toBe('s'); + expect(config.get('number')).toBe(12); expect(count).toBe(1); } ); - it_id("5cffb28a-2924-4857-84bb-f5778d80372a")(it)( - "afterSave(Parse.Config) can run hook with existing config", + it_id('5cffb28a-2924-4857-84bb-f5778d80372a')(it)( + 'afterSave(Parse.Config) can run hook with existing config', async () => { let count = 0; Parse.Cloud.afterSave(Parse.Config, req => { if (count === 0) { - expect(req.object.get("number")).toBe(12); + expect(req.object.get('number')).toBe(12); expect(req.original).toBeUndefined(); } if (count === 1) { - expect(req.object.get("number")).toBe(13); - expect(req.original.get("number")).toBe(12); + expect(req.object.get('number')).toBe(13); + expect(req.original.get('number')).toBe(12); } count += 1; }); @@ -4326,45 +4326,45 @@ describe("Cloud Config hooks", () => { } ); - it_id("49883992-ce91-4797-85f9-7cce1f819407")(it)( - "afterSave(Parse.Config) should throw error", + it_id('49883992-ce91-4797-85f9-7cce1f819407')(it)( + 'afterSave(Parse.Config) should throw error', async () => { Parse.Cloud.afterSave(Parse.Config, () => { - throw new Parse.Error(400, "It should fail"); + throw new Parse.Error(400, 'It should fail'); }); try { await testConfig(); - fail("error should have thrown"); + fail('error should have thrown'); } catch (e) { expect(e.code).toBe(400); - expect(e.message).toBe("It should fail"); + expect(e.message).toBe('It should fail'); } } ); }); -describe("sendEmail", () => { - it("can send email via Parse.Cloud", async done => { +describe('sendEmail', () => { + it('can send email via Parse.Cloud', async done => { const emailAdapter = { sendMail: mailData => { expect(mailData).toBeDefined(); - expect(mailData.to).toBe("test"); + expect(mailData.to).toBe('test'); reconfigureServer().then(done, done); }, }; await reconfigureServer({ emailAdapter: emailAdapter, }); - const mailData = { to: "test" }; + const mailData = { to: 'test' }; await Parse.Cloud.sendEmail(mailData); }); - it("cannot send email without adapter", async () => { - const logger = require("../lib/logger").logger; - spyOn(logger, "error").and.callFake(() => {}); + it('cannot send email without adapter', async () => { + const logger = require('../lib/logger').logger; + spyOn(logger, 'error').and.callFake(() => {}); await Parse.Cloud.sendEmail({}); expect(logger.error).toHaveBeenCalledWith( - "Failed to send email because no mail adapter is configured for Parse Server." + 'Failed to send email because no mail adapter is configured for Parse Server.' ); }); }); diff --git a/spec/CloudCodeLogger.spec.js b/spec/CloudCodeLogger.spec.js index 24f7cfc8b4..6768063083 100644 --- a/spec/CloudCodeLogger.spec.js +++ b/spec/CloudCodeLogger.spec.js @@ -1,13 +1,13 @@ const LoggerController = - require("../lib/Controllers/LoggerController").LoggerController; + require('../lib/Controllers/LoggerController').LoggerController; const WinstonLoggerAdapter = - require("../lib/Adapters/Logger/WinstonLoggerAdapter").WinstonLoggerAdapter; -const fs = require("fs"); -const Config = require("../lib/Config"); + require('../lib/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter; +const fs = require('fs'); +const Config = require('../lib/Config'); -const loremFile = __dirname + "/support/lorem.txt"; +const loremFile = __dirname + '/support/lorem.txt'; -describe("Cloud Code Logger", () => { +describe('Cloud Code Logger', () => { let user; let spy; beforeEach(async () => { @@ -17,23 +17,23 @@ describe("Cloud Code Logger", () => { silent: true, logLevel: undefined, logLevels: { - cloudFunctionError: "error", - cloudFunctionSuccess: "info", - triggerAfter: "info", - triggerBeforeError: "error", - triggerBeforeSuccess: "info", + cloudFunctionError: 'error', + cloudFunctionSuccess: 'info', + triggerAfter: 'info', + triggerBeforeError: 'error', + triggerBeforeSuccess: 'info', }, }) .then(() => { - return Parse.User.signUp("tester", "abc") + return Parse.User.signUp('tester', 'abc') .catch(() => {}) .then(loggedInUser => (user = loggedInUser)) - .then(() => Parse.User.logIn(user.get("username"), "abc")); + .then(() => Parse.User.logIn(user.get('username'), 'abc')); }) .then(() => { spy = spyOn( - Config.get("test").loggerController.adapter, - "log" + Config.get('test').loggerController.adapter, + 'log' ).and.callThrough(); }); }); @@ -41,52 +41,52 @@ describe("Cloud Code Logger", () => { // Note that helpers takes care of logout. // see helpers.js:afterEach - it_id("02d53b97-3ec7-46fb-abb6-176fd6e85590")(it)( - "should expose log to functions", + it_id('02d53b97-3ec7-46fb-abb6-176fd6e85590')(it)( + 'should expose log to functions', () => { const spy = spyOn( - Config.get("test").loggerController, - "log" + Config.get('test').loggerController, + 'log' ).and.callThrough(); - Parse.Cloud.define("loggerTest", req => { - req.log.info("logTest", "info log", { info: "some log" }); - req.log.error("logTest", "error log", { error: "there was an error" }); + Parse.Cloud.define('loggerTest', req => { + req.log.info('logTest', 'info log', { info: 'some log' }); + req.log.error('logTest', 'error log', { error: 'there was an error' }); return {}; }); - return Parse.Cloud.run("loggerTest").then(() => { + return Parse.Cloud.run('loggerTest').then(() => { expect(spy).toHaveBeenCalledTimes(3); const cloudFunctionMessage = spy.calls.all()[2]; const errorMessage = spy.calls.all()[1]; const infoMessage = spy.calls.all()[0]; - expect(cloudFunctionMessage.args[0]).toBe("info"); + expect(cloudFunctionMessage.args[0]).toBe('info'); expect(cloudFunctionMessage.args[1][1].params).toEqual({}); expect(cloudFunctionMessage.args[1][0]).toMatch( /Ran cloud function loggerTest for user [^ ]* with:\n {2}Input: {}\n {2}Result: {}/ ); expect(cloudFunctionMessage.args[1][1].functionName).toEqual( - "loggerTest" + 'loggerTest' ); - expect(errorMessage.args[0]).toBe("error"); - expect(errorMessage.args[1][2].error).toBe("there was an error"); - expect(errorMessage.args[1][0]).toBe("logTest"); - expect(errorMessage.args[1][1]).toBe("error log"); - expect(infoMessage.args[0]).toBe("info"); - expect(infoMessage.args[1][2].info).toBe("some log"); - expect(infoMessage.args[1][0]).toBe("logTest"); - expect(infoMessage.args[1][1]).toBe("info log"); + expect(errorMessage.args[0]).toBe('error'); + expect(errorMessage.args[1][2].error).toBe('there was an error'); + expect(errorMessage.args[1][0]).toBe('logTest'); + expect(errorMessage.args[1][1]).toBe('error log'); + expect(infoMessage.args[0]).toBe('info'); + expect(infoMessage.args[1][2].info).toBe('some log'); + expect(infoMessage.args[1][0]).toBe('logTest'); + expect(infoMessage.args[1][1]).toBe('info log'); }); } ); - it_id("768412f5-d32f-4134-89a6-08949781a6c0")(it)( - "trigger should obfuscate password", + it_id('768412f5-d32f-4134-89a6-08949781a6c0')(it)( + 'trigger should obfuscate password', done => { Parse.Cloud.beforeSave(Parse.User, req => { return req.object; }); - Parse.User.signUp("tester123", "abc") + Parse.User.signUp('tester123', 'abc') .then(() => { const entry = spy.calls.mostRecent().args; expect(entry[1]).not.toMatch(/password":"abc/); @@ -97,63 +97,63 @@ describe("Cloud Code Logger", () => { } ); - it_id("3c394047-272e-4728-9d02-9eaa660d2ed2")(it)( - "should expose log to trigger", + it_id('3c394047-272e-4728-9d02-9eaa660d2ed2')(it)( + 'should expose log to trigger', done => { - Parse.Cloud.beforeSave("MyObject", req => { - req.log.info("beforeSave MyObject", "info log", { info: "some log" }); - req.log.error("beforeSave MyObject", "error log", { - error: "there was an error", + Parse.Cloud.beforeSave('MyObject', req => { + req.log.info('beforeSave MyObject', 'info log', { info: 'some log' }); + req.log.error('beforeSave MyObject', 'error log', { + error: 'there was an error', }); return {}; }); - const obj = new Parse.Object("MyObject"); + const obj = new Parse.Object('MyObject'); obj.save().then(() => { const lastCalls = spy.calls.all().reverse(); const cloudTriggerMessage = lastCalls[0].args; const errorMessage = lastCalls[1].args; const infoMessage = lastCalls[2].args; - expect(cloudTriggerMessage[0]).toBe("info"); - expect(cloudTriggerMessage[2].triggerType).toEqual("beforeSave"); + expect(cloudTriggerMessage[0]).toBe('info'); + expect(cloudTriggerMessage[2].triggerType).toEqual('beforeSave'); expect(cloudTriggerMessage[1]).toMatch( /beforeSave triggered for MyObject for user [^ ]*\n {2}Input: {}\n {2}Result: {"object":{}}/ ); expect(cloudTriggerMessage[2].user).toBe(user.id); - expect(errorMessage[0]).toBe("error"); - expect(errorMessage[3].error).toBe("there was an error"); - expect(errorMessage[1] + " " + errorMessage[2]).toBe( - "beforeSave MyObject error log" + expect(errorMessage[0]).toBe('error'); + expect(errorMessage[3].error).toBe('there was an error'); + expect(errorMessage[1] + ' ' + errorMessage[2]).toBe( + 'beforeSave MyObject error log' ); - expect(infoMessage[0]).toBe("info"); - expect(infoMessage[3].info).toBe("some log"); - expect(infoMessage[1] + " " + infoMessage[2]).toBe( - "beforeSave MyObject info log" + expect(infoMessage[0]).toBe('info'); + expect(infoMessage[3].info).toBe('some log'); + expect(infoMessage[1] + ' ' + infoMessage[2]).toBe( + 'beforeSave MyObject info log' ); done(); }); } ); - it("should truncate really long lines when asked to", () => { + it('should truncate really long lines when asked to', () => { const logController = new LoggerController(new WinstonLoggerAdapter()); - const longString = fs.readFileSync(loremFile, "utf8"); + const longString = fs.readFileSync(loremFile, 'utf8'); const truncatedString = logController.truncateLogMessage(longString); expect(truncatedString.length).toBe(1015); // truncate length + the string '... (truncated)' }); - it_id("4a009b1f-9203-49ca-8d48-5b45f4eedbdf")(it)( - "should truncate input and result of long lines", + it_id('4a009b1f-9203-49ca-8d48-5b45f4eedbdf')(it)( + 'should truncate input and result of long lines', done => { - const longString = fs.readFileSync(loremFile, "utf8"); - Parse.Cloud.define("aFunction", req => { + const longString = fs.readFileSync(loremFile, 'utf8'); + Parse.Cloud.define('aFunction', req => { return req.params; }); - Parse.Cloud.run("aFunction", { longString }) + Parse.Cloud.run('aFunction', { longString }) .then(() => { const log = spy.calls.mostRecent().args; - expect(log[0]).toEqual("info"); + expect(log[0]).toEqual('info'); expect(log[1]).toMatch( /Ran cloud function aFunction for user [^ ]* with:\n {2}Input: {.*?\(truncated\)$/m ); @@ -163,15 +163,15 @@ describe("Cloud Code Logger", () => { } ); - it_id("9857e15d-bb18-478d-8a67-fdaad3e89565")(it)( - "should log an afterSave", + it_id('9857e15d-bb18-478d-8a67-fdaad3e89565')(it)( + 'should log an afterSave', done => { - Parse.Cloud.afterSave("MyObject", () => {}); - new Parse.Object("MyObject") + Parse.Cloud.afterSave('MyObject', () => {}); + new Parse.Object('MyObject') .save() .then(() => { const log = spy.calls.mostRecent().args; - expect(log[2].triggerType).toEqual("afterSave"); + expect(log[2].triggerType).toEqual('afterSave'); done(); }) // catch errors - not that the error is actually useful :( @@ -179,42 +179,42 @@ describe("Cloud Code Logger", () => { } ); - it_id("ec13a296-f8b1-4fc6-985a-3593462edd9c")(it)( - "should log a denied beforeSave", + it_id('ec13a296-f8b1-4fc6-985a-3593462edd9c')(it)( + 'should log a denied beforeSave', done => { - Parse.Cloud.beforeSave("MyObject", () => { - throw "uh oh!"; + Parse.Cloud.beforeSave('MyObject', () => { + throw 'uh oh!'; }); - new Parse.Object("MyObject") + new Parse.Object('MyObject') .save() .then( - () => done.fail("this is not supposed to succeed"), + () => done.fail('this is not supposed to succeed'), () => new Promise(resolve => setTimeout(resolve, 100)) ) .then(() => { const logs = spy.calls.all().reverse(); const log = logs[1].args; // 0 is the 'uh oh!' from rejection... - expect(log[0]).toEqual("error"); + expect(log[0]).toEqual('error'); const error = log[2].error; expect(error instanceof Parse.Error).toBeTruthy(); expect(error.code).toBe(Parse.Error.SCRIPT_FAILED); - expect(error.message).toBe("uh oh!"); + expect(error.message).toBe('uh oh!'); done(); }); } ); - it_id("3e0caa45-60d6-41af-829a-fd389710c132")(it)( - "should log cloud function success", + it_id('3e0caa45-60d6-41af-829a-fd389710c132')(it)( + 'should log cloud function success', done => { - Parse.Cloud.define("aFunction", () => { - return "it worked!"; + Parse.Cloud.define('aFunction', () => { + return 'it worked!'; }); - Parse.Cloud.run("aFunction", { foo: "bar" }).then(() => { + Parse.Cloud.run('aFunction', { foo: 'bar' }).then(() => { const log = spy.calls.mostRecent().args; - expect(log[0]).toEqual("info"); + expect(log[0]).toEqual('info'); expect(log[1]).toMatch( /Ran cloud function aFunction for user [^ ]* with:\n {2}Input: {"foo":"bar"}\n {2}Result: "it worked!/ ); @@ -223,55 +223,55 @@ describe("Cloud Code Logger", () => { } ); - it_id("8088de8a-7cba-4035-8b05-4a903307e674")(it)( - "should log cloud function execution using the custom log level", + it_id('8088de8a-7cba-4035-8b05-4a903307e674')(it)( + 'should log cloud function execution using the custom log level', async done => { - Parse.Cloud.define("aFunction", () => { - return "it worked!"; + Parse.Cloud.define('aFunction', () => { + return 'it worked!'; }); - Parse.Cloud.define("bFunction", () => { - throw new Error("Failed"); + Parse.Cloud.define('bFunction', () => { + throw new Error('Failed'); }); - await Parse.Cloud.run("aFunction", { foo: "bar" }).then(() => { + await Parse.Cloud.run('aFunction', { foo: 'bar' }).then(() => { const log = spy.calls .allArgs() - .find(log => log[1].startsWith("Ran cloud function "))?.[0]; - expect(log).toEqual("info"); + .find(log => log[1].startsWith('Ran cloud function '))?.[0]; + expect(log).toEqual('info'); }); await reconfigureServer({ silent: true, logLevels: { - cloudFunctionSuccess: "warn", - cloudFunctionError: "info", + cloudFunctionSuccess: 'warn', + cloudFunctionError: 'info', }, }); spy = spyOn( - Config.get("test").loggerController.adapter, - "log" + Config.get('test').loggerController.adapter, + 'log' ).and.callThrough(); try { - await Parse.Cloud.run("bFunction", { foo: "bar" }); - throw new Error("bFunction should have failed"); + await Parse.Cloud.run('bFunction', { foo: 'bar' }); + throw new Error('bFunction should have failed'); } catch { const log = spy.calls .allArgs() .find(log => - log[1].startsWith("Failed running cloud function bFunction for ") + log[1].startsWith('Failed running cloud function bFunction for ') )?.[0]; - expect(log).toEqual("info"); + expect(log).toEqual('info'); done(); } } ); - it("should log cloud function triggers using the custom log level", async () => { - Parse.Cloud.beforeSave("TestClass", () => {}); - Parse.Cloud.afterSave("TestClass", () => {}); + it('should log cloud function triggers using the custom log level', async () => { + Parse.Cloud.beforeSave('TestClass', () => {}); + Parse.Cloud.afterSave('TestClass', () => {}); const execTest = async (logLevel, triggerBeforeSuccess, triggerAfter) => { await reconfigureServer({ @@ -284,54 +284,54 @@ describe("Cloud Code Logger", () => { }); spy = spyOn( - Config.get("test").loggerController.adapter, - "log" + Config.get('test').loggerController.adapter, + 'log' ).and.callThrough(); - const obj = new Parse.Object("TestClass"); + const obj = new Parse.Object('TestClass'); await obj.save(); return { beforeSave: spy.calls .allArgs() .find(log => - log[1].startsWith("beforeSave triggered for TestClass for user ") + log[1].startsWith('beforeSave triggered for TestClass for user ') )?.[0], afterSave: spy.calls .allArgs() .find(log => - log[1].startsWith("afterSave triggered for TestClass for user ") + log[1].startsWith('afterSave triggered for TestClass for user ') )?.[0], }; }; - let calls = await execTest("silly", "silly", "debug"); - expect(calls).toEqual({ beforeSave: "silly", afterSave: "debug" }); + let calls = await execTest('silly', 'silly', 'debug'); + expect(calls).toEqual({ beforeSave: 'silly', afterSave: 'debug' }); - calls = await execTest("info", "warn", "debug"); - expect(calls).toEqual({ beforeSave: "warn", afterSave: undefined }); + calls = await execTest('info', 'warn', 'debug'); + expect(calls).toEqual({ beforeSave: 'warn', afterSave: undefined }); }); - it_id("97e0eafa-cde6-4a9a-9e53-7db98bacbc62")(it)( - "should log cloud function failure", + it_id('97e0eafa-cde6-4a9a-9e53-7db98bacbc62')(it)( + 'should log cloud function failure', done => { - Parse.Cloud.define("aFunction", () => { - throw "it failed!"; + Parse.Cloud.define('aFunction', () => { + throw 'it failed!'; }); - Parse.Cloud.run("aFunction", { foo: "bar" }) + Parse.Cloud.run('aFunction', { foo: 'bar' }) .catch(() => {}) .then(() => { const logs = spy.calls.all().reverse(); - expect(logs[0].args[1]).toBe("Parse error: "); - expect(logs[0].args[2].message).toBe("it failed!"); + expect(logs[0].args[1]).toBe('Parse error: '); + expect(logs[0].args[2].message).toBe('it failed!'); const log = logs[1].args; - expect(log[0]).toEqual("error"); + expect(log[0]).toEqual('error'); expect(log[1]).toMatch( /Failed running cloud function aFunction for user [^ ]* with:\n {2}Input: {"foo":"bar"}\n {2}Error:/ ); const errorString = JSON.stringify( - new Parse.Error(Parse.Error.SCRIPT_FAILED, "it failed!") + new Parse.Error(Parse.Error.SCRIPT_FAILED, 'it failed!') ); expect(log[1].indexOf(errorString)).toBeGreaterThan(0); done(); @@ -340,17 +340,17 @@ describe("Cloud Code Logger", () => { } ); - xit("should log a changed beforeSave indicating a change", done => { - pending("needs more work....."); + xit('should log a changed beforeSave indicating a change', done => { + pending('needs more work.....'); const logController = new LoggerController(new WinstonLoggerAdapter()); - Parse.Cloud.beforeSave("MyObject", req => { + Parse.Cloud.beforeSave('MyObject', req => { const myObj = req.object; - myObj.set("aChange", true); + myObj.set('aChange', true); return myObj; }); - new Parse.Object("MyObject") + new Parse.Object('MyObject') .save() .then(() => logController.getLogs({ from: Date.now() - 500, size: 1000 })) .then(() => { @@ -366,14 +366,14 @@ describe("Cloud Code Logger", () => { .then(null, e => done.fail(JSON.stringify(e))); }); - it_id("b86e8168-8370-4730-a4ba-24ca3016ad66")(it)( - "cloud function should obfuscate password", + it_id('b86e8168-8370-4730-a4ba-24ca3016ad66')(it)( + 'cloud function should obfuscate password', done => { - Parse.Cloud.define("testFunction", () => { - return "verify code success"; + Parse.Cloud.define('testFunction', () => { + return 'verify code success'; }); - Parse.Cloud.run("testFunction", { username: "hawk", password: "123456" }) + Parse.Cloud.run('testFunction', { username: 'hawk', password: '123456' }) .then(() => { const entry = spy.calls.mostRecent().args; expect(entry[2].params.password).toMatch(/\*\*\*\*\*\*\*\*/); @@ -383,12 +383,12 @@ describe("Cloud Code Logger", () => { } ); - it("should only log once for object not found", async () => { - const config = Config.get("test"); - const spy = spyOn(config.loggerController, "error").and.callThrough(); + it('should only log once for object not found', async () => { + const config = Config.get('test'); + const spy = spyOn(config.loggerController, 'error').and.callThrough(); try { - const object = new Parse.Object("Object"); - object.id = "invalid"; + const object = new Parse.Object('Object'); + object.id = 'invalid'; await object.fetch(); } catch (e) { /**/ @@ -396,62 +396,62 @@ describe("Cloud Code Logger", () => { expect(spy).toHaveBeenCalled(); expect(spy.calls.count()).toBe(1); const { args } = spy.calls.mostRecent(); - expect(args[0]).toBe("Parse error: "); - expect(args[1].message).toBe("Object not found."); + expect(args[0]).toBe('Parse error: '); + expect(args[1].message).toBe('Object not found.'); }); - it("should log cloud function execution using the silent log level", async () => { + it('should log cloud function execution using the silent log level', async () => { await reconfigureServer({ logLevels: { - cloudFunctionSuccess: "silent", - cloudFunctionError: "silent", + cloudFunctionSuccess: 'silent', + cloudFunctionError: 'silent', }, }); - Parse.Cloud.define("aFunction", () => { - return "it worked!"; + Parse.Cloud.define('aFunction', () => { + return 'it worked!'; }); - Parse.Cloud.define("bFunction", () => { - throw new Error("Failed"); + Parse.Cloud.define('bFunction', () => { + throw new Error('Failed'); }); spy = spyOn( - Config.get("test").loggerController.adapter, - "log" + Config.get('test').loggerController.adapter, + 'log' ).and.callThrough(); - await Parse.Cloud.run("aFunction", { foo: "bar" }); + await Parse.Cloud.run('aFunction', { foo: 'bar' }); expect(spy).toHaveBeenCalledTimes(0); await expectAsync( - Parse.Cloud.run("bFunction", { foo: "bar" }) + Parse.Cloud.run('bFunction', { foo: 'bar' }) ).toBeRejected(); // Not "Failed running cloud function message..." expect(spy).toHaveBeenCalledTimes(1); }); - it("should log cloud function triggers using the silent log level", async () => { + it('should log cloud function triggers using the silent log level', async () => { await reconfigureServer({ logLevels: { - triggerAfter: "silent", - triggerBeforeSuccess: "silent", - triggerBeforeError: "silent", + triggerAfter: 'silent', + triggerBeforeSuccess: 'silent', + triggerBeforeError: 'silent', }, }); - Parse.Cloud.beforeSave("TestClassError", () => { - throw new Error("Failed"); + Parse.Cloud.beforeSave('TestClassError', () => { + throw new Error('Failed'); }); - Parse.Cloud.beforeSave("TestClass", () => {}); - Parse.Cloud.afterSave("TestClass", () => {}); + Parse.Cloud.beforeSave('TestClass', () => {}); + Parse.Cloud.afterSave('TestClass', () => {}); spy = spyOn( - Config.get("test").loggerController.adapter, - "log" + Config.get('test').loggerController.adapter, + 'log' ).and.callThrough(); - const obj = new Parse.Object("TestClass"); + const obj = new Parse.Object('TestClass'); await obj.save(); expect(spy).toHaveBeenCalledTimes(0); - const objError = new Parse.Object("TestClassError"); + const objError = new Parse.Object('TestClassError'); await expectAsync(objError.save()).toBeRejected(); // Not "beforeSave failed for TestClassError for user ..." expect(spy).toHaveBeenCalledTimes(1); diff --git a/spec/DatabaseController.spec.js b/spec/DatabaseController.spec.js index e5a615bf63..9fdc656217 100644 --- a/spec/DatabaseController.spec.js +++ b/spec/DatabaseController.spec.js @@ -1,25 +1,25 @@ -const Config = require("../lib/Config"); -const DatabaseController = require("../lib/Controllers/DatabaseController.js"); +const Config = require('../lib/Config'); +const DatabaseController = require('../lib/Controllers/DatabaseController.js'); const validateQuery = DatabaseController._validateQuery; -describe("DatabaseController", function () { - describe("validateQuery", function () { - it("should not restructure simple cases of SERVER-13732", done => { +describe('DatabaseController', function () { + describe('validateQuery', function () { + it('should not restructure simple cases of SERVER-13732', done => { const query = { $or: [{ a: 1 }, { a: 2 }], - _rperm: { $in: ["a", "b"] }, + _rperm: { $in: ['a', 'b'] }, foo: 3, }; validateQuery(query); expect(query).toEqual({ $or: [{ a: 1 }, { a: 2 }], - _rperm: { $in: ["a", "b"] }, + _rperm: { $in: ['a', 'b'] }, foo: 3, }); done(); }); - it("should not restructure SERVER-13732 queries with $nears", done => { + it('should not restructure SERVER-13732 queries with $nears', done => { let query = { $or: [{ a: 1 }, { b: 1 }], c: { $nearSphere: {} } }; validateQuery(query); expect(query).toEqual({ @@ -32,7 +32,7 @@ describe("DatabaseController", function () { done(); }); - it("should not push refactored keys down a tree for SERVER-13732", done => { + it('should not push refactored keys down a tree for SERVER-13732', done => { const query = { a: 1, $or: [{ $or: [{ b: 1 }, { b: 2 }] }, { $or: [{ c: 1 }, { c: 2 }] }], @@ -46,33 +46,33 @@ describe("DatabaseController", function () { done(); }); - it("should reject invalid queries", done => { + it('should reject invalid queries', done => { expect(() => validateQuery({ $or: { a: 1 } })).toThrow(); done(); }); - it("should accept valid queries", done => { + it('should accept valid queries', done => { expect(() => validateQuery({ $or: [{ a: 1 }, { b: 2 }] })).not.toThrow(); done(); }); }); - describe("addPointerPermissions", function () { - const CLASS_NAME = "Foo"; - const USER_ID = "userId"; + describe('addPointerPermissions', function () { + const CLASS_NAME = 'Foo'; + const USER_ID = 'userId'; const ACL_GROUP = [USER_ID]; - const OPERATION = "find"; + const OPERATION = 'find'; const databaseController = new DatabaseController(); - const schemaController = jasmine.createSpyObj("SchemaController", [ - "testPermissionsForClassName", - "getClassLevelPermissions", - "getExpectedType", + const schemaController = jasmine.createSpyObj('SchemaController', [ + 'testPermissionsForClassName', + 'getClassLevelPermissions', + 'getExpectedType', ]); - it("should not decorate query if no pointer CLPs are present", done => { + it('should not decorate query if no pointer CLPs are present', done => { const clp = buildCLP(); - const query = { a: "b" }; + const query = { a: 'b' }; schemaController.testPermissionsForClassName .withArgs(CLASS_NAME, ACL_GROUP, OPERATION) @@ -94,9 +94,9 @@ describe("DatabaseController", function () { done(); }); - it("should decorate query if a pointer CLP entry is present", done => { - const clp = buildCLP(["user"]); - const query = { a: "b" }; + it('should decorate query if a pointer CLP entry is present', done => { + const clp = buildCLP(['user']); + const query = { a: 'b' }; schemaController.testPermissionsForClassName .withArgs(CLASS_NAME, ACL_GROUP, OPERATION) @@ -105,8 +105,8 @@ describe("DatabaseController", function () { .withArgs(CLASS_NAME) .and.returnValue(clp); schemaController.getExpectedType - .withArgs(CLASS_NAME, "user") - .and.returnValue({ type: "Pointer" }); + .withArgs(CLASS_NAME, 'user') + .and.returnValue({ type: 'Pointer' }); const output = databaseController.addPointerPermissions( schemaController, @@ -121,9 +121,9 @@ describe("DatabaseController", function () { done(); }); - it("should decorate query if an array CLP entry is present", done => { - const clp = buildCLP(["users"]); - const query = { a: "b" }; + it('should decorate query if an array CLP entry is present', done => { + const clp = buildCLP(['users']); + const query = { a: 'b' }; schemaController.testPermissionsForClassName .withArgs(CLASS_NAME, ACL_GROUP, OPERATION) @@ -132,8 +132,8 @@ describe("DatabaseController", function () { .withArgs(CLASS_NAME) .and.returnValue(clp); schemaController.getExpectedType - .withArgs(CLASS_NAME, "users") - .and.returnValue({ type: "Array" }); + .withArgs(CLASS_NAME, 'users') + .and.returnValue({ type: 'Array' }); const output = databaseController.addPointerPermissions( schemaController, @@ -151,9 +151,9 @@ describe("DatabaseController", function () { done(); }); - it("should decorate query if an object CLP entry is present", done => { - const clp = buildCLP(["user"]); - const query = { a: "b" }; + it('should decorate query if an object CLP entry is present', done => { + const clp = buildCLP(['user']); + const query = { a: 'b' }; schemaController.testPermissionsForClassName .withArgs(CLASS_NAME, ACL_GROUP, OPERATION) @@ -162,8 +162,8 @@ describe("DatabaseController", function () { .withArgs(CLASS_NAME) .and.returnValue(clp); schemaController.getExpectedType - .withArgs(CLASS_NAME, "user") - .and.returnValue({ type: "Object" }); + .withArgs(CLASS_NAME, 'user') + .and.returnValue({ type: 'Object' }); const output = databaseController.addPointerPermissions( schemaController, @@ -181,9 +181,9 @@ describe("DatabaseController", function () { done(); }); - it("should decorate query if a pointer CLP is present and the same field is part of the query", done => { - const clp = buildCLP(["user"]); - const query = { a: "b", user: "a" }; + it('should decorate query if a pointer CLP is present and the same field is part of the query', done => { + const clp = buildCLP(['user']); + const query = { a: 'b', user: 'a' }; schemaController.testPermissionsForClassName .withArgs(CLASS_NAME, ACL_GROUP, OPERATION) @@ -192,8 +192,8 @@ describe("DatabaseController", function () { .withArgs(CLASS_NAME) .and.returnValue(clp); schemaController.getExpectedType - .withArgs(CLASS_NAME, "user") - .and.returnValue({ type: "Pointer" }); + .withArgs(CLASS_NAME, 'user') + .and.returnValue({ type: 'Pointer' }); const output = databaseController.addPointerPermissions( schemaController, @@ -210,9 +210,9 @@ describe("DatabaseController", function () { done(); }); - it("should transform the query to an $or query if multiple array/pointer CLPs are present", done => { - const clp = buildCLP(["user", "users", "userObject"]); - const query = { a: "b" }; + it('should transform the query to an $or query if multiple array/pointer CLPs are present', done => { + const clp = buildCLP(['user', 'users', 'userObject']); + const query = { a: 'b' }; schemaController.testPermissionsForClassName .withArgs(CLASS_NAME, ACL_GROUP, OPERATION) @@ -221,14 +221,14 @@ describe("DatabaseController", function () { .withArgs(CLASS_NAME) .and.returnValue(clp); schemaController.getExpectedType - .withArgs(CLASS_NAME, "user") - .and.returnValue({ type: "Pointer" }); + .withArgs(CLASS_NAME, 'user') + .and.returnValue({ type: 'Pointer' }); schemaController.getExpectedType - .withArgs(CLASS_NAME, "users") - .and.returnValue({ type: "Array" }); + .withArgs(CLASS_NAME, 'users') + .and.returnValue({ type: 'Array' }); schemaController.getExpectedType - .withArgs(CLASS_NAME, "userObject") - .and.returnValue({ type: "Object" }); + .withArgs(CLASS_NAME, 'userObject') + .and.returnValue({ type: 'Object' }); const output = databaseController.addPointerPermissions( schemaController, @@ -249,9 +249,9 @@ describe("DatabaseController", function () { done(); }); - it("should not return a $or operation if the query involves one of the two fields also used as array/pointer permissions", done => { - const clp = buildCLP(["users", "user"]); - const query = { a: "b", user: createUserPointer(USER_ID) }; + it('should not return a $or operation if the query involves one of the two fields also used as array/pointer permissions', done => { + const clp = buildCLP(['users', 'user']); + const query = { a: 'b', user: createUserPointer(USER_ID) }; schemaController.testPermissionsForClassName .withArgs(CLASS_NAME, ACL_GROUP, OPERATION) .and.returnValue(false); @@ -259,11 +259,11 @@ describe("DatabaseController", function () { .withArgs(CLASS_NAME) .and.returnValue(clp); schemaController.getExpectedType - .withArgs(CLASS_NAME, "user") - .and.returnValue({ type: "Pointer" }); + .withArgs(CLASS_NAME, 'user') + .and.returnValue({ type: 'Pointer' }); schemaController.getExpectedType - .withArgs(CLASS_NAME, "users") - .and.returnValue({ type: "Array" }); + .withArgs(CLASS_NAME, 'users') + .and.returnValue({ type: 'Array' }); const output = databaseController.addPointerPermissions( schemaController, CLASS_NAME, @@ -275,9 +275,9 @@ describe("DatabaseController", function () { done(); }); - it("should not return a $or operation if the query involves one of the fields also used as array/pointer permissions", done => { - const clp = buildCLP(["user", "users", "userObject"]); - const query = { a: "b", user: createUserPointer(USER_ID) }; + it('should not return a $or operation if the query involves one of the fields also used as array/pointer permissions', done => { + const clp = buildCLP(['user', 'users', 'userObject']); + const query = { a: 'b', user: createUserPointer(USER_ID) }; schemaController.testPermissionsForClassName .withArgs(CLASS_NAME, ACL_GROUP, OPERATION) .and.returnValue(false); @@ -285,14 +285,14 @@ describe("DatabaseController", function () { .withArgs(CLASS_NAME) .and.returnValue(clp); schemaController.getExpectedType - .withArgs(CLASS_NAME, "user") - .and.returnValue({ type: "Pointer" }); + .withArgs(CLASS_NAME, 'user') + .and.returnValue({ type: 'Pointer' }); schemaController.getExpectedType - .withArgs(CLASS_NAME, "users") - .and.returnValue({ type: "Array" }); + .withArgs(CLASS_NAME, 'users') + .and.returnValue({ type: 'Array' }); schemaController.getExpectedType - .withArgs(CLASS_NAME, "userObject") - .and.returnValue({ type: "Object" }); + .withArgs(CLASS_NAME, 'userObject') + .and.returnValue({ type: 'Object' }); const output = databaseController.addPointerPermissions( schemaController, CLASS_NAME, @@ -304,9 +304,9 @@ describe("DatabaseController", function () { done(); }); - it("should throw an error if for some unexpected reason the property specified in the CLP is neither a pointer nor an array", done => { - const clp = buildCLP(["user"]); - const query = { a: "b" }; + it('should throw an error if for some unexpected reason the property specified in the CLP is neither a pointer nor an array', done => { + const clp = buildCLP(['user']); + const query = { a: 'b' }; schemaController.testPermissionsForClassName .withArgs(CLASS_NAME, ACL_GROUP, OPERATION) @@ -315,8 +315,8 @@ describe("DatabaseController", function () { .withArgs(CLASS_NAME) .and.returnValue(clp); schemaController.getExpectedType - .withArgs(CLASS_NAME, "user") - .and.returnValue({ type: "Number" }); + .withArgs(CLASS_NAME, 'user') + .and.returnValue({ type: 'Number' }); expect(() => { databaseController.addPointerPermissions( @@ -336,10 +336,10 @@ describe("DatabaseController", function () { }); }); - describe("reduceOperations", function () { + describe('reduceOperations', function () { const databaseController = new DatabaseController(); - it("objectToEntriesStrings", done => { + it('objectToEntriesStrings', done => { const output = databaseController.objectToEntriesStrings({ a: 1, b: 2, @@ -349,7 +349,7 @@ describe("DatabaseController", function () { done(); }); - it("reduceOrOperation", done => { + it('reduceOrOperation', done => { expect(databaseController.reduceOrOperation({ a: 1 })).toEqual({ a: 1 }); expect( databaseController.reduceOrOperation({ $or: [{ a: 1 }, { b: 2 }] }) @@ -377,7 +377,7 @@ describe("DatabaseController", function () { done(); }); - it("reduceAndOperation", done => { + it('reduceAndOperation', done => { expect(databaseController.reduceAndOperation({ a: 1 })).toEqual({ a: 1 }); expect( databaseController.reduceAndOperation({ $and: [{ a: 1 }, { b: 2 }] }) @@ -403,7 +403,7 @@ describe("DatabaseController", function () { }); }); - describe("enableCollationCaseComparison", () => { + describe('enableCollationCaseComparison', () => { const dummyStorageAdapter = { find: () => Promise.resolve([]), watch: () => Promise.resolve(), @@ -414,44 +414,44 @@ describe("DatabaseController", function () { Config.get(Parse.applicationId).schemaCache.clear(); }); - it("should force caseInsensitive to false with enableCollationCaseComparison option", async () => { + it('should force caseInsensitive to false with enableCollationCaseComparison option', async () => { const databaseController = new DatabaseController(dummyStorageAdapter, { enableCollationCaseComparison: true, }); - const spy = spyOn(dummyStorageAdapter, "find"); + const spy = spyOn(dummyStorageAdapter, 'find'); spy.and.callThrough(); - await databaseController.find("SomeClass", {}, { caseInsensitive: true }); + await databaseController.find('SomeClass', {}, { caseInsensitive: true }); expect(spy.calls.all()[0].args[3].caseInsensitive).toEqual(false); }); - it("should support caseInsensitive without enableCollationCaseComparison option", async () => { + it('should support caseInsensitive without enableCollationCaseComparison option', async () => { const databaseController = new DatabaseController( dummyStorageAdapter, {} ); - const spy = spyOn(dummyStorageAdapter, "find"); + const spy = spyOn(dummyStorageAdapter, 'find'); spy.and.callThrough(); - await databaseController.find("_User", {}, { caseInsensitive: true }); + await databaseController.find('_User', {}, { caseInsensitive: true }); expect(spy.calls.all()[0].args[3].caseInsensitive).toEqual(true); }); - it_only_db("mongo")( - "should create insensitive indexes without enableCollationCaseComparison", + it_only_db('mongo')( + 'should create insensitive indexes without enableCollationCaseComparison', async () => { await reconfigureServer({ databaseURI: - "mongodb://localhost:27017/enableCollationCaseComparisonFalse", + 'mongodb://localhost:27017/enableCollationCaseComparisonFalse', databaseAdapter: undefined, }); const user = new Parse.User(); await user.save({ - username: "example", - password: "password", - email: "example@example.com", + username: 'example', + password: 'password', + email: 'example@example.com', }); const schemas = await Parse.Schema.all(); const UserSchema = schemas.find( - ({ className }) => className === "_User" + ({ className }) => className === '_User' ); expect(UserSchema.indexes).toEqual({ _id_: { _id: 1 }, @@ -463,24 +463,24 @@ describe("DatabaseController", function () { } ); - it_only_db("mongo")( - "should not create insensitive indexes with enableCollationCaseComparison", + it_only_db('mongo')( + 'should not create insensitive indexes with enableCollationCaseComparison', async () => { await reconfigureServer({ enableCollationCaseComparison: true, databaseURI: - "mongodb://localhost:27017/enableCollationCaseComparisonTrue", + 'mongodb://localhost:27017/enableCollationCaseComparisonTrue', databaseAdapter: undefined, }); const user = new Parse.User(); await user.save({ - username: "example", - password: "password", - email: "example@example.com", + username: 'example', + password: 'password', + email: 'example@example.com', }); const schemas = await Parse.Schema.all(); const UserSchema = schemas.find( - ({ className }) => className === "_User" + ({ className }) => className === '_User' ); expect(UserSchema.indexes).toEqual({ _id_: { _id: 1 }, @@ -491,7 +491,7 @@ describe("DatabaseController", function () { ); }); - describe("convertEmailToLowercase", () => { + describe('convertEmailToLowercase', () => { const dummyStorageAdapter = { createObject: () => Promise.resolve({ ops: [{}] }), findOneAndUpdate: () => Promise.resolve({}), @@ -499,104 +499,104 @@ describe("DatabaseController", function () { getAllClasses: () => Promise.resolve([ { - className: "_User", - fields: { email: "String" }, + className: '_User', + fields: { email: 'String' }, indexes: {}, classLevelPermissions: { protectedFields: {} }, }, ]), }; const dates = { - createdAt: { iso: undefined, __type: "Date" }, - updatedAt: { iso: undefined, __type: "Date" }, + createdAt: { iso: undefined, __type: 'Date' }, + updatedAt: { iso: undefined, __type: 'Date' }, }; - it("should not transform email to lower case without convertEmailToLowercase option on create", async () => { + it('should not transform email to lower case without convertEmailToLowercase option on create', async () => { const databaseController = new DatabaseController( dummyStorageAdapter, {} ); - const spy = spyOn(dummyStorageAdapter, "createObject"); + const spy = spyOn(dummyStorageAdapter, 'createObject'); spy.and.callThrough(); - await databaseController.create("_User", { - email: "EXAMPLE@EXAMPLE.COM", + await databaseController.create('_User', { + email: 'EXAMPLE@EXAMPLE.COM', }); expect(spy.calls.all()[0].args[2]).toEqual({ - email: "EXAMPLE@EXAMPLE.COM", + email: 'EXAMPLE@EXAMPLE.COM', ...dates, }); }); - it("should transform email to lower case with convertEmailToLowercase option on create", async () => { + it('should transform email to lower case with convertEmailToLowercase option on create', async () => { const databaseController = new DatabaseController(dummyStorageAdapter, { convertEmailToLowercase: true, }); - const spy = spyOn(dummyStorageAdapter, "createObject"); + const spy = spyOn(dummyStorageAdapter, 'createObject'); spy.and.callThrough(); - await databaseController.create("_User", { - email: "EXAMPLE@EXAMPLE.COM", + await databaseController.create('_User', { + email: 'EXAMPLE@EXAMPLE.COM', }); expect(spy.calls.all()[0].args[2]).toEqual({ - email: "example@example.com", + email: 'example@example.com', ...dates, }); }); - it("should not transform email to lower case without convertEmailToLowercase option on update", async () => { + it('should not transform email to lower case without convertEmailToLowercase option on update', async () => { const databaseController = new DatabaseController( dummyStorageAdapter, {} ); - const spy = spyOn(dummyStorageAdapter, "findOneAndUpdate"); + const spy = spyOn(dummyStorageAdapter, 'findOneAndUpdate'); spy.and.callThrough(); await databaseController.update( - "_User", - { id: "example" }, - { email: "EXAMPLE@EXAMPLE.COM" } + '_User', + { id: 'example' }, + { email: 'EXAMPLE@EXAMPLE.COM' } ); expect(spy.calls.all()[0].args[3]).toEqual({ - email: "EXAMPLE@EXAMPLE.COM", + email: 'EXAMPLE@EXAMPLE.COM', }); }); - it("should transform email to lower case with convertEmailToLowercase option on update", async () => { + it('should transform email to lower case with convertEmailToLowercase option on update', async () => { const databaseController = new DatabaseController(dummyStorageAdapter, { convertEmailToLowercase: true, }); - const spy = spyOn(dummyStorageAdapter, "findOneAndUpdate"); + const spy = spyOn(dummyStorageAdapter, 'findOneAndUpdate'); spy.and.callThrough(); await databaseController.update( - "_User", - { id: "example" }, - { email: "EXAMPLE@EXAMPLE.COM" } + '_User', + { id: 'example' }, + { email: 'EXAMPLE@EXAMPLE.COM' } ); expect(spy.calls.all()[0].args[3]).toEqual({ - email: "example@example.com", + email: 'example@example.com', }); }); - it("should not find a case insensitive user by email with convertEmailToLowercase", async () => { + it('should not find a case insensitive user by email with convertEmailToLowercase', async () => { await reconfigureServer({ convertEmailToLowercase: true }); const user = new Parse.User(); await user.save({ - username: "EXAMPLE", - email: "EXAMPLE@EXAMPLE.COM", - password: "password", + username: 'EXAMPLE', + email: 'EXAMPLE@EXAMPLE.COM', + password: 'password', }); const query = new Parse.Query(Parse.User); - query.equalTo("email", "EXAMPLE@EXAMPLE.COM"); + query.equalTo('email', 'EXAMPLE@EXAMPLE.COM'); const result = await query.find({ useMasterKey: true }); expect(result.length).toEqual(0); const query2 = new Parse.Query(Parse.User); - query2.equalTo("email", "example@example.com"); + query2.equalTo('email', 'example@example.com'); const result2 = await query2.find({ useMasterKey: true }); expect(result2.length).toEqual(1); }); }); - describe("convertUsernameToLowercase", () => { + describe('convertUsernameToLowercase', () => { const dummyStorageAdapter = { createObject: () => Promise.resolve({ ops: [{}] }), findOneAndUpdate: () => Promise.resolve({}), @@ -604,94 +604,94 @@ describe("DatabaseController", function () { getAllClasses: () => Promise.resolve([ { - className: "_User", - fields: { username: "String" }, + className: '_User', + fields: { username: 'String' }, indexes: {}, classLevelPermissions: { protectedFields: {} }, }, ]), }; const dates = { - createdAt: { iso: undefined, __type: "Date" }, - updatedAt: { iso: undefined, __type: "Date" }, + createdAt: { iso: undefined, __type: 'Date' }, + updatedAt: { iso: undefined, __type: 'Date' }, }; - it("should not transform username to lower case without convertUsernameToLowercase option on create", async () => { + it('should not transform username to lower case without convertUsernameToLowercase option on create', async () => { const databaseController = new DatabaseController( dummyStorageAdapter, {} ); - const spy = spyOn(dummyStorageAdapter, "createObject"); + const spy = spyOn(dummyStorageAdapter, 'createObject'); spy.and.callThrough(); - await databaseController.create("_User", { - username: "EXAMPLE", + await databaseController.create('_User', { + username: 'EXAMPLE', }); expect(spy.calls.all()[0].args[2]).toEqual({ - username: "EXAMPLE", + username: 'EXAMPLE', ...dates, }); }); - it("should transform username to lower case with convertUsernameToLowercase option on create", async () => { + it('should transform username to lower case with convertUsernameToLowercase option on create', async () => { const databaseController = new DatabaseController(dummyStorageAdapter, { convertUsernameToLowercase: true, }); - const spy = spyOn(dummyStorageAdapter, "createObject"); + const spy = spyOn(dummyStorageAdapter, 'createObject'); spy.and.callThrough(); - await databaseController.create("_User", { - username: "EXAMPLE", + await databaseController.create('_User', { + username: 'EXAMPLE', }); expect(spy.calls.all()[0].args[2]).toEqual({ - username: "example", + username: 'example', ...dates, }); }); - it("should not transform username to lower case without convertUsernameToLowercase option on update", async () => { + it('should not transform username to lower case without convertUsernameToLowercase option on update', async () => { const databaseController = new DatabaseController( dummyStorageAdapter, {} ); - const spy = spyOn(dummyStorageAdapter, "findOneAndUpdate"); + const spy = spyOn(dummyStorageAdapter, 'findOneAndUpdate'); spy.and.callThrough(); await databaseController.update( - "_User", - { id: "example" }, - { username: "EXAMPLE" } + '_User', + { id: 'example' }, + { username: 'EXAMPLE' } ); expect(spy.calls.all()[0].args[3]).toEqual({ - username: "EXAMPLE", + username: 'EXAMPLE', }); }); - it("should transform username to lower case with convertUsernameToLowercase option on update", async () => { + it('should transform username to lower case with convertUsernameToLowercase option on update', async () => { const databaseController = new DatabaseController(dummyStorageAdapter, { convertUsernameToLowercase: true, }); - const spy = spyOn(dummyStorageAdapter, "findOneAndUpdate"); + const spy = spyOn(dummyStorageAdapter, 'findOneAndUpdate'); spy.and.callThrough(); await databaseController.update( - "_User", - { id: "example" }, - { username: "EXAMPLE" } + '_User', + { id: 'example' }, + { username: 'EXAMPLE' } ); expect(spy.calls.all()[0].args[3]).toEqual({ - username: "example", + username: 'example', }); }); - it("should not find a case insensitive user by username with convertUsernameToLowercase", async () => { + it('should not find a case insensitive user by username with convertUsernameToLowercase', async () => { await reconfigureServer({ convertUsernameToLowercase: true }); const user = new Parse.User(); - await user.save({ username: "EXAMPLE", password: "password" }); + await user.save({ username: 'EXAMPLE', password: 'password' }); const query = new Parse.Query(Parse.User); - query.equalTo("username", "EXAMPLE"); + query.equalTo('username', 'EXAMPLE'); const result = await query.find({ useMasterKey: true }); expect(result.length).toEqual(0); const query2 = new Parse.Query(Parse.User); - query2.equalTo("username", "example"); + query2.equalTo('username', 'example'); const result2 = await query2.find({ useMasterKey: true }); expect(result2.length).toEqual(1); }); @@ -700,13 +700,13 @@ describe("DatabaseController", function () { function buildCLP(pointerNames) { const OPERATIONS = [ - "count", - "find", - "get", - "create", - "update", - "delete", - "addField", + 'count', + 'find', + 'get', + 'create', + 'update', + 'delete', + 'addField', ]; const clp = OPERATIONS.reduce((acc, op) => { @@ -728,8 +728,8 @@ function buildCLP(pointerNames) { function createUserPointer(userId) { return { - __type: "Pointer", - className: "_User", + __type: 'Pointer', + className: '_User', objectId: userId, }; } diff --git a/spec/DefinedSchemas.spec.js b/spec/DefinedSchemas.spec.js index fa72cc97a8..2be26f69c7 100644 --- a/spec/DefinedSchemas.spec.js +++ b/spec/DefinedSchemas.spec.js @@ -1,5 +1,5 @@ -const { DefinedSchemas } = require("../lib/SchemaMigrations/DefinedSchemas"); -const Config = require("../lib/Config"); +const { DefinedSchemas } = require('../lib/SchemaMigrations/DefinedSchemas'); +const Config = require('../lib/Config'); const cleanUpIndexes = schema => { if (schema.indexes) { @@ -10,184 +10,184 @@ const cleanUpIndexes = schema => { } }; -describe("DefinedSchemas", () => { +describe('DefinedSchemas', () => { let config; afterEach(async () => { - config = Config.get("test"); + config = Config.get('test'); if (config) { await config.database.adapter.deleteAllClasses(); } }); - describe("Fields", () => { - it("should keep default fields if not provided", async () => { + describe('Fields', () => { + it('should keep default fields if not provided', async () => { const server = await reconfigureServer(); // Will perform create await new DefinedSchemas( - { definitions: [{ className: "Test" }] }, + { definitions: [{ className: 'Test' }] }, server.config ).execute(); - let schema = await new Parse.Schema("Test").get(); + let schema = await new Parse.Schema('Test').get(); const expectedFields = { - objectId: { type: "String" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - ACL: { type: "ACL" }, + objectId: { type: 'String' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + ACL: { type: 'ACL' }, }; expect(schema.fields).toEqual(expectedFields); await server.config.schemaCache.clear(); // Will perform update await new DefinedSchemas( - { definitions: [{ className: "Test" }] }, + { definitions: [{ className: 'Test' }] }, server.config ).execute(); - schema = await new Parse.Schema("Test").get(); + schema = await new Parse.Schema('Test').get(); expect(schema.fields).toEqual(expectedFields); }); - it("should protect default fields", async () => { + it('should protect default fields', async () => { const server = await reconfigureServer(); const schemas = { definitions: [ { - className: "_User", + className: '_User', fields: { - email: "Object", + email: 'Object', }, }, { - className: "_Role", + className: '_Role', fields: { - users: "Object", + users: 'Object', }, }, { - className: "_Installation", + className: '_Installation', fields: { - installationId: "Object", + installationId: 'Object', }, }, { - className: "Test", + className: 'Test', fields: { - createdAt: { type: "Object" }, - objectId: { type: "Number" }, - updatedAt: { type: "String" }, - ACL: { type: "String" }, + createdAt: { type: 'Object' }, + objectId: { type: 'Number' }, + updatedAt: { type: 'String' }, + ACL: { type: 'String' }, }, }, ], }; const expectedFields = { - objectId: { type: "String" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - ACL: { type: "ACL" }, + objectId: { type: 'String' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + ACL: { type: 'ACL' }, }; const expectedUserFields = { - objectId: { type: "String" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - ACL: { type: "ACL" }, - username: { type: "String" }, - password: { type: "String" }, - email: { type: "String" }, - emailVerified: { type: "Boolean" }, - authData: { type: "Object" }, + objectId: { type: 'String' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + ACL: { type: 'ACL' }, + username: { type: 'String' }, + password: { type: 'String' }, + email: { type: 'String' }, + emailVerified: { type: 'Boolean' }, + authData: { type: 'Object' }, }; const expectedRoleFields = { - objectId: { type: "String" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - ACL: { type: "ACL" }, - name: { type: "String" }, - users: { type: "Relation", targetClass: "_User" }, - roles: { type: "Relation", targetClass: "_Role" }, + objectId: { type: 'String' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + ACL: { type: 'ACL' }, + name: { type: 'String' }, + users: { type: 'Relation', targetClass: '_User' }, + roles: { type: 'Relation', targetClass: '_Role' }, }; const expectedInstallationFields = { - objectId: { type: "String" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - ACL: { type: "ACL" }, - installationId: { type: "String" }, - deviceToken: { type: "String" }, - channels: { type: "Array" }, - deviceType: { type: "String" }, - pushType: { type: "String" }, - GCMSenderId: { type: "String" }, - timeZone: { type: "String" }, - localeIdentifier: { type: "String" }, - badge: { type: "Number" }, - appVersion: { type: "String" }, - appName: { type: "String" }, - appIdentifier: { type: "String" }, - parseVersion: { type: "String" }, + objectId: { type: 'String' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + ACL: { type: 'ACL' }, + installationId: { type: 'String' }, + deviceToken: { type: 'String' }, + channels: { type: 'Array' }, + deviceType: { type: 'String' }, + pushType: { type: 'String' }, + GCMSenderId: { type: 'String' }, + timeZone: { type: 'String' }, + localeIdentifier: { type: 'String' }, + badge: { type: 'Number' }, + appVersion: { type: 'String' }, + appName: { type: 'String' }, + appIdentifier: { type: 'String' }, + parseVersion: { type: 'String' }, }; // Perform create await new DefinedSchemas(schemas, server.config).execute(); - let schema = await new Parse.Schema("Test").get(); + let schema = await new Parse.Schema('Test').get(); expect(schema.fields).toEqual(expectedFields); - let userSchema = await new Parse.Schema("_User").get(); + let userSchema = await new Parse.Schema('_User').get(); expect(userSchema.fields).toEqual(expectedUserFields); - let roleSchema = await new Parse.Schema("_Role").get(); + let roleSchema = await new Parse.Schema('_Role').get(); expect(roleSchema.fields).toEqual(expectedRoleFields); - let installationSchema = await new Parse.Schema("_Installation").get(); + let installationSchema = await new Parse.Schema('_Installation').get(); expect(installationSchema.fields).toEqual(expectedInstallationFields); await server.config.schemaCache.clear(); // Perform update await new DefinedSchemas(schemas, server.config).execute(); - schema = await new Parse.Schema("Test").get(); + schema = await new Parse.Schema('Test').get(); expect(schema.fields).toEqual(expectedFields); - userSchema = await new Parse.Schema("_User").get(); + userSchema = await new Parse.Schema('_User').get(); expect(userSchema.fields).toEqual(expectedUserFields); - roleSchema = await new Parse.Schema("_Role").get(); + roleSchema = await new Parse.Schema('_Role').get(); expect(roleSchema.fields).toEqual(expectedRoleFields); - installationSchema = await new Parse.Schema("_Installation").get(); + installationSchema = await new Parse.Schema('_Installation').get(); expect(installationSchema.fields).toEqual(expectedInstallationFields); }); - it("should create new fields", async () => { + it('should create new fields', async () => { const server = await reconfigureServer(); const fields = { - objectId: { type: "String" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - ACL: { type: "ACL" }, - aString: { type: "String" }, - aStringWithDefault: { type: "String", defaultValue: "Test" }, - aStringWithRequired: { type: "String", required: true }, + objectId: { type: 'String' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + ACL: { type: 'ACL' }, + aString: { type: 'String' }, + aStringWithDefault: { type: 'String', defaultValue: 'Test' }, + aStringWithRequired: { type: 'String', required: true }, aStringWithRequiredAndDefault: { - type: "String", + type: 'String', required: true, - defaultValue: "Test", + defaultValue: 'Test', }, - aBoolean: { type: "Boolean" }, - aFile: { type: "File" }, - aNumber: { type: "Number" }, - aRelation: { type: "Relation", targetClass: "_User" }, - aPointer: { type: "Pointer", targetClass: "_Role" }, - aDate: { type: "Date" }, - aGeoPoint: { type: "GeoPoint" }, - aPolygon: { type: "Polygon" }, - aArray: { type: "Array" }, - aObject: { type: "Object" }, + aBoolean: { type: 'Boolean' }, + aFile: { type: 'File' }, + aNumber: { type: 'Number' }, + aRelation: { type: 'Relation', targetClass: '_User' }, + aPointer: { type: 'Pointer', targetClass: '_Role' }, + aDate: { type: 'Date' }, + aGeoPoint: { type: 'GeoPoint' }, + aPolygon: { type: 'Polygon' }, + aArray: { type: 'Array' }, + aObject: { type: 'Object' }, }; const schemas = { definitions: [ { - className: "Test", + className: 'Test', fields, }, ], @@ -195,13 +195,13 @@ describe("DefinedSchemas", () => { // Create await new DefinedSchemas(schemas, server.config).execute(); - let schema = await new Parse.Schema("Test").get(); + let schema = await new Parse.Schema('Test').get(); expect(schema.fields).toEqual(fields); - fields.anotherObject = { type: "Object" }; + fields.anotherObject = { type: 'Object' }; // Update await new DefinedSchemas(schemas, server.config).execute(); - schema = await new Parse.Schema("Test").get(); + schema = await new Parse.Schema('Test').get(); expect(schema.fields).toEqual(fields); }); it('should not delete removed fields when "deleteExtraFields" is false', async () => { @@ -210,27 +210,27 @@ describe("DefinedSchemas", () => { await new DefinedSchemas( { definitions: [ - { className: "Test", fields: { aField: { type: "String" } } }, + { className: 'Test', fields: { aField: { type: 'String' } } }, ], }, server.config ).execute(); - let schema = await new Parse.Schema("Test").get(); + let schema = await new Parse.Schema('Test').get(); expect(schema.fields.aField).toBeDefined(); await new DefinedSchemas( - { definitions: [{ className: "Test" }] }, + { definitions: [{ className: 'Test' }] }, server.config ).execute(); - schema = await new Parse.Schema("Test").get(); + schema = await new Parse.Schema('Test').get(); expect(schema.fields).toEqual({ - objectId: { type: "String" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - aField: { type: "String" }, - ACL: { type: "ACL" }, + objectId: { type: 'String' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + aField: { type: 'String' }, + ACL: { type: 'ACL' }, }); }); it('should delete removed fields when "deleteExtraFields" is true', async () => { @@ -239,26 +239,26 @@ describe("DefinedSchemas", () => { await new DefinedSchemas( { definitions: [ - { className: "Test", fields: { aField: { type: "String" } } }, + { className: 'Test', fields: { aField: { type: 'String' } } }, ], }, server.config ).execute(); - let schema = await new Parse.Schema("Test").get(); + let schema = await new Parse.Schema('Test').get(); expect(schema.fields.aField).toBeDefined(); await new DefinedSchemas( - { deleteExtraFields: true, definitions: [{ className: "Test" }] }, + { deleteExtraFields: true, definitions: [{ className: 'Test' }] }, server.config ).execute(); - schema = await new Parse.Schema("Test").get(); + schema = await new Parse.Schema('Test').get(); expect(schema.fields).toEqual({ - objectId: { type: "String" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - ACL: { type: "ACL" }, + objectId: { type: 'String' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + ACL: { type: 'ACL' }, }); }); it('should re create fields with changed type when "recreateModifiedFields" is true', async () => { @@ -267,33 +267,33 @@ describe("DefinedSchemas", () => { await new DefinedSchemas( { definitions: [ - { className: "Test", fields: { aField: { type: "String" } } }, + { className: 'Test', fields: { aField: { type: 'String' } } }, ], }, server.config ).execute(); - let schema = await new Parse.Schema("Test").get(); - expect(schema.fields.aField).toEqual({ type: "String" }); + let schema = await new Parse.Schema('Test').get(); + expect(schema.fields.aField).toEqual({ type: 'String' }); - const object = new Parse.Object("Test"); - await object.save({ aField: "Hello" }, { useMasterKey: true }); + const object = new Parse.Object('Test'); + await object.save({ aField: 'Hello' }, { useMasterKey: true }); await new DefinedSchemas( { recreateModifiedFields: true, definitions: [ - { className: "Test", fields: { aField: { type: "Number" } } }, + { className: 'Test', fields: { aField: { type: 'Number' } } }, ], }, server.config ).execute(); - schema = await new Parse.Schema("Test").get(); - expect(schema.fields.aField).toEqual({ type: "Number" }); + schema = await new Parse.Schema('Test').get(); + expect(schema.fields.aField).toEqual({ type: 'Number' }); await object.fetch({ useMasterKey: true }); - expect(object.get("aField")).toBeUndefined(); + expect(object.get('aField')).toBeUndefined(); }); it('should not re create fields with changed type when "recreateModifiedFields" is not true', async () => { const server = await reconfigureServer(); @@ -301,73 +301,73 @@ describe("DefinedSchemas", () => { await new DefinedSchemas( { definitions: [ - { className: "Test", fields: { aField: { type: "String" } } }, + { className: 'Test', fields: { aField: { type: 'String' } } }, ], }, server.config ).execute(); - let schema = await new Parse.Schema("Test").get(); - expect(schema.fields.aField).toEqual({ type: "String" }); + let schema = await new Parse.Schema('Test').get(); + expect(schema.fields.aField).toEqual({ type: 'String' }); - const object = new Parse.Object("Test"); - await object.save({ aField: "Hello" }, { useMasterKey: true }); + const object = new Parse.Object('Test'); + await object.save({ aField: 'Hello' }, { useMasterKey: true }); await new DefinedSchemas( { definitions: [ - { className: "Test", fields: { aField: { type: "Number" } } }, + { className: 'Test', fields: { aField: { type: 'Number' } } }, ], }, server.config ).execute(); - schema = await new Parse.Schema("Test").get(); - expect(schema.fields.aField).toEqual({ type: "String" }); + schema = await new Parse.Schema('Test').get(); + expect(schema.fields.aField).toEqual({ type: 'String' }); await object.fetch({ useMasterKey: true }); - expect(object.get("aField")).toBeDefined(); + expect(object.get('aField')).toBeDefined(); }); - it("should just update classic fields with changed params", async () => { + it('should just update classic fields with changed params', async () => { const server = await reconfigureServer(); await new DefinedSchemas( { definitions: [ - { className: "Test", fields: { aField: { type: "String" } } }, + { className: 'Test', fields: { aField: { type: 'String' } } }, ], }, server.config ).execute(); - let schema = await new Parse.Schema("Test").get(); - expect(schema.fields.aField).toEqual({ type: "String" }); + let schema = await new Parse.Schema('Test').get(); + expect(schema.fields.aField).toEqual({ type: 'String' }); - const object = new Parse.Object("Test"); - await object.save({ aField: "Hello" }, { useMasterKey: true }); + const object = new Parse.Object('Test'); + await object.save({ aField: 'Hello' }, { useMasterKey: true }); await new DefinedSchemas( { definitions: [ { - className: "Test", - fields: { aField: { type: "String", required: true } }, + className: 'Test', + fields: { aField: { type: 'String', required: true } }, }, ], }, server.config ).execute(); - schema = await new Parse.Schema("Test").get(); - expect(schema.fields.aField).toEqual({ type: "String", required: true }); + schema = await new Parse.Schema('Test').get(); + expect(schema.fields.aField).toEqual({ type: 'String', required: true }); await object.fetch({ useMasterKey: true }); - expect(object.get("aField")).toEqual("Hello"); + expect(object.get('aField')).toEqual('Hello'); }); }); - describe("Indexes", () => { - it("should create new indexes", async () => { + describe('Indexes', () => { + it('should create new indexes', async () => { const server = await reconfigureServer(); const indexes = { complex: { createdAt: 1, updatedAt: 1 } }; @@ -375,71 +375,71 @@ describe("DefinedSchemas", () => { const schemas = { definitions: [ { - className: "Test", - fields: { aField: { type: "String" } }, + className: 'Test', + fields: { aField: { type: 'String' } }, indexes, }, ], }; await new DefinedSchemas(schemas, server.config).execute(); - let schema = await new Parse.Schema("Test").get(); + let schema = await new Parse.Schema('Test').get(); cleanUpIndexes(schema); expect(schema.indexes).toEqual(indexes); indexes.complex2 = { createdAt: 1, aField: 1 }; await new DefinedSchemas(schemas, server.config).execute(); - schema = await new Parse.Schema("Test").get(); + schema = await new Parse.Schema('Test').get(); cleanUpIndexes(schema); expect(schema.indexes).toEqual(indexes); }); - it("should re create changed indexes", async () => { + it('should re create changed indexes', async () => { const server = await reconfigureServer(); let indexes = { complex: { createdAt: 1, updatedAt: 1 } }; - let schemas = { definitions: [{ className: "Test", indexes }] }; + let schemas = { definitions: [{ className: 'Test', indexes }] }; await new DefinedSchemas(schemas, server.config).execute(); indexes = { complex: { createdAt: 1 } }; - schemas = { definitions: [{ className: "Test", indexes }] }; + schemas = { definitions: [{ className: 'Test', indexes }] }; // Change indexes await new DefinedSchemas(schemas, server.config).execute(); - let schema = await new Parse.Schema("Test").get(); + let schema = await new Parse.Schema('Test').get(); cleanUpIndexes(schema); expect(schema.indexes).toEqual(indexes); // Update await new DefinedSchemas(schemas, server.config).execute(); - schema = await new Parse.Schema("Test").get(); + schema = await new Parse.Schema('Test').get(); cleanUpIndexes(schema); expect(schema.indexes).toEqual(indexes); }); - it("should delete removed indexes", async () => { + it('should delete removed indexes', async () => { const server = await reconfigureServer(); let indexes = { complex: { createdAt: 1, updatedAt: 1 } }; - let schemas = { definitions: [{ className: "Test", indexes }] }; + let schemas = { definitions: [{ className: 'Test', indexes }] }; await new DefinedSchemas(schemas, server.config).execute(); indexes = {}; - schemas = { definitions: [{ className: "Test", indexes }] }; + schemas = { definitions: [{ className: 'Test', indexes }] }; // Change indexes await new DefinedSchemas(schemas, server.config).execute(); - let schema = await new Parse.Schema("Test").get(); + let schema = await new Parse.Schema('Test').get(); cleanUpIndexes(schema); expect(schema.indexes).toBeUndefined(); // Update await new DefinedSchemas(schemas, server.config).execute(); - schema = await new Parse.Schema("Test").get(); + schema = await new Parse.Schema('Test').get(); cleanUpIndexes(schema); expect(schema.indexes).toBeUndefined(); }); - xit("should keep protected indexes", async () => { + xit('should keep protected indexes', async () => { const server = await reconfigureServer(); const expectedIndexes = { @@ -451,19 +451,19 @@ describe("DefinedSchemas", () => { const schemas = { definitions: [ { - className: "_User", + className: '_User', indexes: { case_insensitive_username: { password: true }, case_insensitive_email: { password: true }, }, }, - { className: "Test" }, + { className: 'Test' }, ], }; // Create await new DefinedSchemas(schemas, server.config).execute(); - let userSchema = await new Parse.Schema("_User").get(); - let testSchema = await new Parse.Schema("Test").get(); + let userSchema = await new Parse.Schema('_User').get(); + let testSchema = await new Parse.Schema('Test').get(); cleanUpIndexes(userSchema); cleanUpIndexes(testSchema); expect(testSchema.indexes).toBeUndefined(); @@ -471,68 +471,68 @@ describe("DefinedSchemas", () => { // Update await new DefinedSchemas(schemas, server.config).execute(); - userSchema = await new Parse.Schema("_User").get(); - testSchema = await new Parse.Schema("Test").get(); + userSchema = await new Parse.Schema('_User').get(); + testSchema = await new Parse.Schema('Test').get(); cleanUpIndexes(userSchema); cleanUpIndexes(testSchema); expect(testSchema.indexes).toBeUndefined(); expect(userSchema.indexes).toEqual(expectedIndexes); }); - it("should detect protected indexes for _User class", () => { + it('should detect protected indexes for _User class', () => { const definedSchema = new DefinedSchemas({}, {}); const protectedUserIndexes = [ - "_id_", - "case_insensitive_email", - "username_1", - "email_1", + '_id_', + 'case_insensitive_email', + 'username_1', + 'email_1', ]; protectedUserIndexes.forEach(field => { - expect(definedSchema.isProtectedIndex("_User", field)).toEqual(true); + expect(definedSchema.isProtectedIndex('_User', field)).toEqual(true); }); - expect(definedSchema.isProtectedIndex("_User", "test")).toEqual(false); + expect(definedSchema.isProtectedIndex('_User', 'test')).toEqual(false); }); - it("should detect protected indexes for _Role class", () => { + it('should detect protected indexes for _Role class', () => { const definedSchema = new DefinedSchemas({}, {}); - expect(definedSchema.isProtectedIndex("_Role", "name_1")).toEqual(true); - expect(definedSchema.isProtectedIndex("_Role", "test")).toEqual(false); + expect(definedSchema.isProtectedIndex('_Role', 'name_1')).toEqual(true); + expect(definedSchema.isProtectedIndex('_Role', 'test')).toEqual(false); }); - it("should detect protected indexes for _Idempotency class", () => { + it('should detect protected indexes for _Idempotency class', () => { const definedSchema = new DefinedSchemas({}, {}); - expect(definedSchema.isProtectedIndex("_Idempotency", "reqId_1")).toEqual( + expect(definedSchema.isProtectedIndex('_Idempotency', 'reqId_1')).toEqual( true ); - expect(definedSchema.isProtectedIndex("_Idempotency", "test")).toEqual( + expect(definedSchema.isProtectedIndex('_Idempotency', 'test')).toEqual( false ); }); - it("should not detect protected indexes on user defined class", () => { + it('should not detect protected indexes on user defined class', () => { const definedSchema = new DefinedSchemas({}, {}); const protectedIndexes = [ - "case_insensitive_email", - "username_1", - "email_1", - "reqId_1", - "name_1", + 'case_insensitive_email', + 'username_1', + 'email_1', + 'reqId_1', + 'name_1', ]; protectedIndexes.forEach(field => { - expect(definedSchema.isProtectedIndex("ExampleClass", field)).toEqual( + expect(definedSchema.isProtectedIndex('ExampleClass', field)).toEqual( false ); }); - expect(definedSchema.isProtectedIndex("ExampleClass", "_id_")).toEqual( + expect(definedSchema.isProtectedIndex('ExampleClass', '_id_')).toEqual( true ); }); }); - describe("ClassLevelPermissions", () => { - it("should use default CLP", async () => { + describe('ClassLevelPermissions', () => { + it('should use default CLP', async () => { const server = await reconfigureServer(); - const schemas = { definitions: [{ className: "Test" }] }; + const schemas = { definitions: [{ className: 'Test' }] }; await new DefinedSchemas(schemas, server.config).execute(); const expectedTestCLP = { @@ -545,33 +545,33 @@ describe("DefinedSchemas", () => { addField: {}, protectedFields: {}, }; - let testSchema = await new Parse.Schema("Test").get(); + let testSchema = await new Parse.Schema('Test').get(); expect(testSchema.classLevelPermissions).toEqual(expectedTestCLP); await new DefinedSchemas(schemas, server.config).execute(); - testSchema = await new Parse.Schema("Test").get(); + testSchema = await new Parse.Schema('Test').get(); expect(testSchema.classLevelPermissions).toEqual(expectedTestCLP); }); - it("should save CLP", async () => { + it('should save CLP', async () => { const server = await reconfigureServer(); const expectedTestCLP = { find: {}, count: { requiresAuthentication: true }, - get: { "role:Admin": true }, - create: { "role:ARole": true, requiresAuthentication: true }, + get: { 'role:Admin': true }, + create: { 'role:ARole': true, requiresAuthentication: true }, update: { requiresAuthentication: true }, delete: { requiresAuthentication: true }, addField: {}, - protectedFields: { "*": ["aField"], "role:Admin": ["anotherField"] }, + protectedFields: { '*': ['aField'], 'role:Admin': ['anotherField'] }, }; const schemas = { definitions: [ { - className: "Test", + className: 'Test', fields: { - aField: { type: "String" }, - anotherField: { type: "Object" }, + aField: { type: 'String' }, + anotherField: { type: 'Object' }, }, classLevelPermissions: expectedTestCLP, }, @@ -579,23 +579,23 @@ describe("DefinedSchemas", () => { }; await new DefinedSchemas(schemas, server.config).execute(); - let testSchema = await new Parse.Schema("Test").get(); + let testSchema = await new Parse.Schema('Test').get(); expect(testSchema.classLevelPermissions).toEqual(expectedTestCLP); expectedTestCLP.update = {}; expectedTestCLP.create = { requiresAuthentication: true }; await new DefinedSchemas(schemas, server.config).execute(); - testSchema = await new Parse.Schema("Test").get(); + testSchema = await new Parse.Schema('Test').get(); expect(testSchema.classLevelPermissions).toEqual(expectedTestCLP); }); - it("should force addField to empty", async () => { + it('should force addField to empty', async () => { const server = await reconfigureServer(); const schemas = { definitions: [ { - className: "Test", - classLevelPermissions: { addField: { "*": true } }, + className: 'Test', + classLevelPermissions: { addField: { '*': true } }, }, ], }; @@ -612,110 +612,110 @@ describe("DefinedSchemas", () => { protectedFields: {}, }; - let testSchema = await new Parse.Schema("Test").get(); + let testSchema = await new Parse.Schema('Test').get(); expect(testSchema.classLevelPermissions).toEqual(expectedTestCLP); await new DefinedSchemas(schemas, server.config).execute(); - testSchema = await new Parse.Schema("Test").get(); + testSchema = await new Parse.Schema('Test').get(); expect(testSchema.classLevelPermissions).toEqual(expectedTestCLP); }); }); - it("should not delete classes automatically", async () => { + it('should not delete classes automatically', async () => { await reconfigureServer({ - schema: { definitions: [{ className: "_User" }, { className: "Test" }] }, + schema: { definitions: [{ className: '_User' }, { className: 'Test' }] }, }); await reconfigureServer({ - schema: { definitions: [{ className: "_User" }] }, + schema: { definitions: [{ className: '_User' }] }, }); - const schema = await new Parse.Schema("Test").get(); - expect(schema.className).toEqual("Test"); + const schema = await new Parse.Schema('Test').get(); + expect(schema.className).toEqual('Test'); }); - it("should disable class PUT/POST endpoint when lockSchemas provided to avoid dual source of truth", async () => { + it('should disable class PUT/POST endpoint when lockSchemas provided to avoid dual source of truth', async () => { await reconfigureServer({ schema: { lockSchemas: true, - definitions: [{ className: "_User" }, { className: "Test" }], + definitions: [{ className: '_User' }, { className: 'Test' }], }, }); - const schema = await new Parse.Schema("Test").get(); - expect(schema.className).toEqual("Test"); + const schema = await new Parse.Schema('Test').get(); + expect(schema.className).toEqual('Test'); const schemas = await Parse.Schema.all(); // Role could be flaky since all system classes are not ensured // at start up by the DefinedSchema system expect( - schemas.filter(({ className }) => className !== "_Role").length + schemas.filter(({ className }) => className !== '_Role').length ).toEqual(3); await expectAsync( - new Parse.Schema("TheNewTest").save() + new Parse.Schema('TheNewTest').save() ).toBeRejectedWithError( - "Cannot perform this operation when schemas options is used." + 'Cannot perform this operation when schemas options is used.' ); - await expectAsync(new Parse.Schema("_User").update()).toBeRejectedWithError( - "Cannot perform this operation when schemas options is used." + await expectAsync(new Parse.Schema('_User').update()).toBeRejectedWithError( + 'Cannot perform this operation when schemas options is used.' ); }); - it("should only enable delete class endpoint since", async () => { + it('should only enable delete class endpoint since', async () => { await reconfigureServer({ - schema: { definitions: [{ className: "_User" }, { className: "Test" }] }, + schema: { definitions: [{ className: '_User' }, { className: 'Test' }] }, }); await reconfigureServer({ - schema: { definitions: [{ className: "_User" }] }, + schema: { definitions: [{ className: '_User' }] }, }); let schemas = await Parse.Schema.all(); expect(schemas.length).toEqual(4); - await new Parse.Schema("_User").delete(); + await new Parse.Schema('_User').delete(); schemas = await Parse.Schema.all(); expect(schemas.length).toEqual(3); }); - it("should run beforeMigration before execution of DefinedSchemas", async () => { + it('should run beforeMigration before execution of DefinedSchemas', async () => { const config = { schema: { - definitions: [{ className: "_User" }, { className: "Test" }], + definitions: [{ className: '_User' }, { className: 'Test' }], beforeMigration: async () => {}, }, }; - const spy = spyOn(config.schema, "beforeMigration"); + const spy = spyOn(config.schema, 'beforeMigration'); await reconfigureServer(config); expect(spy).toHaveBeenCalledTimes(1); }); - it("should run afterMigration after execution of DefinedSchemas", async () => { + it('should run afterMigration after execution of DefinedSchemas', async () => { const config = { schema: { - definitions: [{ className: "_User" }, { className: "Test" }], + definitions: [{ className: '_User' }, { className: 'Test' }], afterMigration: async () => {}, }, }; - const spy = spyOn(config.schema, "afterMigration"); + const spy = spyOn(config.schema, 'afterMigration'); await reconfigureServer(config); expect(spy).toHaveBeenCalledTimes(1); }); - it("should use logger in case of error", async () => { + it('should use logger in case of error', async () => { const server = await reconfigureServer({ - schema: { definitions: [{ className: "_User" }] }, + schema: { definitions: [{ className: '_User' }] }, }); - const error = new Error("A test error"); - const logger = require("../lib/logger").logger; - spyOn(DefinedSchemas.prototype, "wait").and.resolveTo(); - spyOn(logger, "error").and.callThrough(); - spyOn(DefinedSchemas.prototype, "createDeleteSession").and.callFake(() => { + const error = new Error('A test error'); + const logger = require('../lib/logger').logger; + spyOn(DefinedSchemas.prototype, 'wait').and.resolveTo(); + spyOn(logger, 'error').and.callThrough(); + spyOn(DefinedSchemas.prototype, 'createDeleteSession').and.callFake(() => { throw error; }); await new DefinedSchemas( { definitions: [ - { className: "Test", fields: { aField: { type: "String" } } }, + { className: 'Test', fields: { aField: { type: 'String' } } }, ], }, server.config @@ -726,17 +726,17 @@ describe("DefinedSchemas", () => { ); }); - it_id("a18bf4f2-25c8-4de3-b986-19cb1ab163b8")(it)( - "should perform migration in parallel without failing", + it_id('a18bf4f2-25c8-4de3-b986-19cb1ab163b8')(it)( + 'should perform migration in parallel without failing', async () => { const server = await reconfigureServer(); - const logger = require("../lib/logger").logger; - spyOn(logger, "error").and.callThrough(); + const logger = require('../lib/logger').logger; + spyOn(logger, 'error').and.callThrough(); const migrationOptions = { definitions: [ { - className: "Test", - fields: { aField: { type: "String" } }, + className: 'Test', + fields: { aField: { type: 'String' } }, indexes: { aField: { aField: 1 } }, classLevelPermissions: { create: { requiresAuthentication: true }, @@ -760,7 +760,7 @@ describe("DefinedSchemas", () => { ); expect(testSchema.indexes.aField).toEqual({ aField: 1 }); - expect(testSchema.fields.aField).toEqual({ type: "String" }); + expect(testSchema.fields.aField).toEqual({ type: 'String' }); expect(testSchema.classLevelPermissions.create).toEqual({ requiresAuthentication: true, }); @@ -768,15 +768,15 @@ describe("DefinedSchemas", () => { } ); - it("should not affect cacheAdapter", async () => { + it('should not affect cacheAdapter', async () => { const server = await reconfigureServer(); - const logger = require("../lib/logger").logger; - spyOn(logger, "error").and.callThrough(); + const logger = require('../lib/logger').logger; + spyOn(logger, 'error').and.callThrough(); const migrationOptions = { definitions: [ { - className: "Test", - fields: { aField: { type: "String" } }, + className: 'Test', + fields: { aField: { type: 'String' } }, indexes: { aField: { aField: 1 } }, classLevelPermissions: { create: { requiresAuthentication: true }, @@ -790,7 +790,7 @@ describe("DefinedSchemas", () => { put: () => {}, del: () => {}, clear: () => {}, - connect: jasmine.createSpy("clear"), + connect: jasmine.createSpy('clear'), }; server.config.cacheAdapter = cacheAdapter; await new DefinedSchemas(migrationOptions, server.config).execute(); diff --git a/spec/Deprecator.spec.js b/spec/Deprecator.spec.js index 261ba6fc18..d84a0f8f6c 100644 --- a/spec/Deprecator.spec.js +++ b/spec/Deprecator.spec.js @@ -1,28 +1,28 @@ -"use strict"; +'use strict'; -const Deprecator = require("../lib/Deprecator/Deprecator"); +const Deprecator = require('../lib/Deprecator/Deprecator'); -describe("Deprecator", () => { +describe('Deprecator', () => { let deprecations = []; beforeEach(async () => { deprecations = [ - { optionKey: "exampleKey", changeNewDefault: "exampleNewDefault" }, + { optionKey: 'exampleKey', changeNewDefault: 'exampleNewDefault' }, ]; }); - it("deprecations are an array", async () => { + it('deprecations are an array', async () => { expect(Deprecator._getDeprecations()).toBeInstanceOf(Array); }); - it("logs deprecation for new default", async () => { + it('logs deprecation for new default', async () => { deprecations = [ - { optionKey: "exampleKey", changeNewDefault: "exampleNewDefault" }, + { optionKey: 'exampleKey', changeNewDefault: 'exampleNewDefault' }, ]; - spyOn(Deprecator, "_getDeprecations").and.callFake(() => deprecations); - const logger = require("../lib/logger").logger; - const logSpy = spyOn(logger, "warn").and.callFake(() => {}); + spyOn(Deprecator, '_getDeprecations').and.callFake(() => deprecations); + const logger = require('../lib/logger').logger; + const logSpy = spyOn(logger, 'warn').and.callFake(() => {}); await reconfigureServer(); expect(logSpy.calls.all()[0].args[0]).toEqual( @@ -30,21 +30,21 @@ describe("Deprecator", () => { ); }); - it("does not log deprecation for new default if option is set manually", async () => { + it('does not log deprecation for new default if option is set manually', async () => { deprecations = [ - { optionKey: "exampleKey", changeNewDefault: "exampleNewDefault" }, + { optionKey: 'exampleKey', changeNewDefault: 'exampleNewDefault' }, ]; - spyOn(Deprecator, "_getDeprecations").and.callFake(() => deprecations); - const logSpy = spyOn(Deprecator, "_logOption").and.callFake(() => {}); - await reconfigureServer({ [deprecations[0].optionKey]: "manuallySet" }); + spyOn(Deprecator, '_getDeprecations').and.callFake(() => deprecations); + const logSpy = spyOn(Deprecator, '_logOption').and.callFake(() => {}); + await reconfigureServer({ [deprecations[0].optionKey]: 'manuallySet' }); expect(logSpy).not.toHaveBeenCalled(); }); - it("logs runtime deprecation", async () => { - const logger = require("../lib/logger").logger; - const logSpy = spyOn(logger, "warn").and.callFake(() => {}); - const options = { usage: "Doing this", solution: "Do that instead." }; + it('logs runtime deprecation', async () => { + const logger = require('../lib/logger').logger; + const logSpy = spyOn(logger, 'warn').and.callFake(() => {}); + const options = { usage: 'Doing this', solution: 'Do that instead.' }; Deprecator.logRuntimeDeprecation(options); expect(logSpy.calls.all()[0].args[0]).toEqual( diff --git a/spec/EmailVerificationToken.spec.js b/spec/EmailVerificationToken.spec.js index bd95688edf..597ef68388 100644 --- a/spec/EmailVerificationToken.spec.js +++ b/spec/EmailVerificationToken.spec.js @@ -1,13 +1,13 @@ -"use strict"; +'use strict'; -const Auth = require("../lib/Auth"); -const Config = require("../lib/Config"); -const request = require("../lib/request"); -const { resolvingPromise, sleep } = require("../lib/TestUtils"); -const MockEmailAdapterWithOptions = require("./support/MockEmailAdapterWithOptions"); +const Auth = require('../lib/Auth'); +const Config = require('../lib/Config'); +const request = require('../lib/request'); +const { resolvingPromise, sleep } = require('../lib/TestUtils'); +const MockEmailAdapterWithOptions = require('./support/MockEmailAdapterWithOptions'); -describe("Email Verification Token Expiration:", () => { - it("show the invalid verification link page, if the user clicks on the verify email link after the email verify token expires", async () => { +describe('Email Verification Token Expiration:', () => { + it('show the invalid verification link page, if the user clicks on the verify email link after the email verify token expires', async () => { const user = new Parse.User(); let sendEmailOptions; const sendPromise = resolvingPromise(); @@ -20,15 +20,15 @@ describe("Email Verification Token Expiration:", () => { sendMail: () => {}, }; await reconfigureServer({ - appName: "emailVerifyToken", + appName: 'emailVerifyToken', verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 0.5, // 0.5 second - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); - user.setUsername("testEmailVerifyTokenValidity"); - user.setPassword("expiringToken"); - user.set("email", "user@parse.com"); + user.setUsername('testEmailVerifyTokenValidity'); + user.setPassword('expiringToken'); + user.set('email', 'user@parse.com'); await user.signUp(); await sendPromise; // wait for 1 second - simulate user behavior to some extent @@ -42,13 +42,13 @@ describe("Email Verification Token Expiration:", () => { }); expect(response.status).toEqual(302); const url = new URL(sendEmailOptions.link); - const token = url.searchParams.get("token"); + const token = url.searchParams.get('token'); expect(response.text).toEqual( `Found. Redirecting to http://localhost:8378/1/apps/invalid_verification_link.html?appId=test&token=${token}` ); }); - it("emailVerified should set to false, if the user does not verify their email before the email verify token expires", async () => { + it('emailVerified should set to false, if the user does not verify their email before the email verify token expires', async () => { const user = new Parse.User(); let sendEmailOptions; const sendPromise = resolvingPromise(); @@ -61,15 +61,15 @@ describe("Email Verification Token Expiration:", () => { sendMail: () => {}, }; await reconfigureServer({ - appName: "emailVerifyToken", + appName: 'emailVerifyToken', verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 0.5, // 0.5 second - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); - user.setUsername("testEmailVerifyTokenValidity"); - user.setPassword("expiringToken"); - user.set("email", "user@parse.com"); + user.setUsername('testEmailVerifyTokenValidity'); + user.setPassword('expiringToken'); + user.set('email', 'user@parse.com'); await user.signUp(); await sendPromise; // wait for 1 second - simulate user behavior to some extent @@ -83,11 +83,11 @@ describe("Email Verification Token Expiration:", () => { }); expect(response.status).toEqual(302); await user.fetch(); - expect(user.get("emailVerified")).toEqual(false); + expect(user.get('emailVerified')).toEqual(false); }); - it_id("f20dd3c2-87d9-4bc6-a51d-4ea2834acbcc")(it)( - "if user clicks on the email verify link before email verification token expiration then show the verify email success page", + it_id('f20dd3c2-87d9-4bc6-a51d-4ea2834acbcc')(it)( + 'if user clicks on the email verify link before email verification token expiration then show the verify email success page', async () => { const user = new Parse.User(); let sendEmailOptions; @@ -101,15 +101,15 @@ describe("Email Verification Token Expiration:", () => { sendMail: () => {}, }; await reconfigureServer({ - appName: "emailVerifyToken", + appName: 'emailVerifyToken', verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); - user.setUsername("testEmailVerifyTokenValidity"); - user.setPassword("expiringToken"); - user.set("email", "user@parse.com"); + user.setUsername('testEmailVerifyTokenValidity'); + user.setPassword('expiringToken'); + user.set('email', 'user@parse.com'); await user.signUp(); await sendPromise; const response = await request({ @@ -118,13 +118,13 @@ describe("Email Verification Token Expiration:", () => { }); expect(response.status).toEqual(302); expect(response.text).toEqual( - "Found. Redirecting to http://localhost:8378/1/apps/verify_email_success.html" + 'Found. Redirecting to http://localhost:8378/1/apps/verify_email_success.html' ); } ); - it_id("94956799-c85e-4297-b879-e2d1f985394c")(it)( - "if user clicks on the email verify link before email verification token expiration then emailVerified should be true", + it_id('94956799-c85e-4297-b879-e2d1f985394c')(it)( + 'if user clicks on the email verify link before email verification token expiration then emailVerified should be true', async () => { const user = new Parse.User(); let sendEmailOptions; @@ -138,15 +138,15 @@ describe("Email Verification Token Expiration:", () => { sendMail: () => {}, }; await reconfigureServer({ - appName: "emailVerifyToken", + appName: 'emailVerifyToken', verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); - user.setUsername("testEmailVerifyTokenValidity"); - user.setPassword("expiringToken"); - user.set("email", "user@parse.com"); + user.setUsername('testEmailVerifyTokenValidity'); + user.setPassword('expiringToken'); + user.set('email', 'user@parse.com'); await user.signUp(); await sendPromise; const response = await request({ @@ -155,12 +155,12 @@ describe("Email Verification Token Expiration:", () => { }); expect(response.status).toEqual(302); await user.fetch(); - expect(user.get("emailVerified")).toEqual(true); + expect(user.get('emailVerified')).toEqual(true); } ); - it_id("25f3f895-c987-431c-9841-17cb6aaf18b5")(it)( - "if user clicks on the email verify link before email verification token expiration then user should be able to login", + it_id('25f3f895-c987-431c-9841-17cb6aaf18b5')(it)( + 'if user clicks on the email verify link before email verification token expiration then user should be able to login', async () => { const user = new Parse.User(); let sendEmailOptions; @@ -174,15 +174,15 @@ describe("Email Verification Token Expiration:", () => { sendMail: () => {}, }; await reconfigureServer({ - appName: "emailVerifyToken", + appName: 'emailVerifyToken', verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); - user.setUsername("testEmailVerifyTokenValidity"); - user.setPassword("expiringToken"); - user.set("email", "user@parse.com"); + user.setUsername('testEmailVerifyTokenValidity'); + user.setPassword('expiringToken'); + user.set('email', 'user@parse.com'); await user.signUp(); await sendPromise; const response = await request({ @@ -191,16 +191,16 @@ describe("Email Verification Token Expiration:", () => { }); expect(response.status).toEqual(302); const verifiedUser = await Parse.User.logIn( - "testEmailVerifyTokenValidity", - "expiringToken" + 'testEmailVerifyTokenValidity', + 'expiringToken' ); - expect(typeof verifiedUser).toBe("object"); - expect(verifiedUser.get("emailVerified")).toBe(true); + expect(typeof verifiedUser).toBe('object'); + expect(verifiedUser.get('emailVerified')).toBe(true); } ); - it_id("c6a3e188-9065-4f50-842d-454d1e82f289")(it)( - "sets the _email_verify_token_expires_at and _email_verify_token fields after user SignUp", + it_id('c6a3e188-9065-4f50-842d-454d1e82f289')(it)( + 'sets the _email_verify_token_expires_at and _email_verify_token fields after user SignUp', async () => { const user = new Parse.User(); let sendEmailOptions; @@ -214,37 +214,37 @@ describe("Email Verification Token Expiration:", () => { sendMail: () => {}, }; await reconfigureServer({ - appName: "emailVerifyToken", + appName: 'emailVerifyToken', verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); - user.setUsername("sets_email_verify_token_expires_at"); - user.setPassword("expiringToken"); - user.set("email", "user@parse.com"); + user.setUsername('sets_email_verify_token_expires_at'); + user.setPassword('expiringToken'); + user.set('email', 'user@parse.com'); await user.signUp(); await sendPromise; - const config = Config.get("test"); + const config = Config.get('test'); const results = await config.database.find( - "_User", + '_User', { - username: "sets_email_verify_token_expires_at", + username: 'sets_email_verify_token_expires_at', }, {}, Auth.maintenance(config) ); expect(results.length).toBe(1); const verifiedUser = results[0]; - expect(typeof verifiedUser).toBe("object"); + expect(typeof verifiedUser).toBe('object'); expect(verifiedUser.emailVerified).toEqual(false); - expect(typeof verifiedUser._email_verify_token).toBe("string"); - expect(typeof verifiedUser._email_verify_token_expires_at).toBe("object"); + expect(typeof verifiedUser._email_verify_token).toBe('string'); + expect(typeof verifiedUser._email_verify_token_expires_at).toBe('object'); expect(sendEmailOptions).toBeDefined(); } ); - it("can resend email using an expired token", async () => { + it('can resend email using an expired token', async () => { const user = new Parse.User(); const emailAdapter = { sendVerificationEmail: () => {}, @@ -252,27 +252,27 @@ describe("Email Verification Token Expiration:", () => { sendMail: () => {}, }; await reconfigureServer({ - appName: "emailVerifyToken", + appName: 'emailVerifyToken', verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); - user.setUsername("test"); - user.setPassword("password"); - user.set("email", "user@example.com"); + user.setUsername('test'); + user.setPassword('password'); + user.set('email', 'user@example.com'); await user.signUp(); await Parse.Server.database.update( - "_User", + '_User', { objectId: user.id }, { - _email_verify_token_expires_at: Parse._encode(new Date("2000")), + _email_verify_token_expires_at: Parse._encode(new Date('2000')), } ); const obj = await Parse.Server.database.find( - "_User", + '_User', { objectId: user.id }, {}, Auth.maintenance(Parse.Server) @@ -281,7 +281,7 @@ describe("Email Verification Token Expiration:", () => { const res = await request({ url: `http://localhost:8378/1/apps/test/verify_email?token=${token}`, - method: "GET", + method: 'GET', }); expect(res.text).toEqual( `Found. Redirecting to http://localhost:8378/1/apps/invalid_verification_link.html?appId=test&token=${token}` @@ -290,11 +290,11 @@ describe("Email Verification Token Expiration:", () => { const formUrl = `http://localhost:8378/1/apps/test/resend_verification_email`; const formResponse = await request({ url: formUrl, - method: "POST", + method: 'POST', body: { token: token, }, - headers: { "Content-Type": "application/x-www-form-urlencoded" }, + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, followRedirects: false, }); expect(formResponse.text).toEqual( @@ -302,8 +302,8 @@ describe("Email Verification Token Expiration:", () => { ); }); - it_id("9365c53c-b8b4-41f7-a3c1-77882f76a89c")(it)( - "can conditionally send emails", + it_id('9365c53c-b8b4-41f7-a3c1-77882f76a89c')(it)( + 'can conditionally send emails', async () => { let sendEmailOptions; const emailAdapter = { @@ -316,45 +316,45 @@ describe("Email Verification Token Expiration:", () => { const verifyUserEmails = { method(req) { expect(Object.keys(req)).toEqual([ - "original", - "object", - "master", - "ip", - "installationId", + 'original', + 'object', + 'master', + 'ip', + 'installationId', ]); return false; }, }; - const verifySpy = spyOn(verifyUserEmails, "method").and.callThrough(); + const verifySpy = spyOn(verifyUserEmails, 'method').and.callThrough(); await reconfigureServer({ - appName: "emailVerifyToken", + appName: 'emailVerifyToken', verifyUserEmails: verifyUserEmails.method, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); const beforeSave = { method(req) { - req.object.set("emailVerified", true); + req.object.set('emailVerified', true); }, }; - const saveSpy = spyOn(beforeSave, "method").and.callThrough(); + const saveSpy = spyOn(beforeSave, 'method').and.callThrough(); const emailSpy = spyOn( emailAdapter, - "sendVerificationEmail" + 'sendVerificationEmail' ).and.callThrough(); Parse.Cloud.beforeSave(Parse.User, beforeSave.method); const user = new Parse.User(); - user.setUsername("sets_email_verify_token_expires_at"); - user.setPassword("expiringToken"); - user.set("email", "user@example.com"); + user.setUsername('sets_email_verify_token_expires_at'); + user.setPassword('expiringToken'); + user.set('email', 'user@example.com'); await user.signUp(); - const config = Config.get("test"); + const config = Config.get('test'); const results = await config.database.find( - "_User", + '_User', { - username: "sets_email_verify_token_expires_at", + username: 'sets_email_verify_token_expires_at', }, {}, Auth.maintenance(config) @@ -362,7 +362,7 @@ describe("Email Verification Token Expiration:", () => { expect(results.length).toBe(1); const user_data = results[0]; - expect(typeof user_data).toBe("object"); + expect(typeof user_data).toBe('object'); expect(user_data.emailVerified).toEqual(true); expect(user_data._email_verify_token).toBeUndefined(); expect(user_data._email_verify_token_expires_at).toBeUndefined(); @@ -373,8 +373,8 @@ describe("Email Verification Token Expiration:", () => { } ); - it_id("b3549300-bed7-4a5e-bed5-792dbfead960")(it)( - "can conditionally send emails and allow conditional login", + it_id('b3549300-bed7-4a5e-bed5-792dbfead960')(it)( + 'can conditionally send emails and allow conditional login', async () => { let sendEmailOptions; const sendPromise = resolvingPromise(); @@ -389,39 +389,39 @@ describe("Email Verification Token Expiration:", () => { const verifyUserEmails = { method(req) { expect(Object.keys(req)).toEqual([ - "original", - "object", - "master", - "ip", - "installationId", + 'original', + 'object', + 'master', + 'ip', + 'installationId', ]); - if (req.object.get("username") === "no_email") { + if (req.object.get('username') === 'no_email') { return false; } return true; }, }; - const verifySpy = spyOn(verifyUserEmails, "method").and.callThrough(); + const verifySpy = spyOn(verifyUserEmails, 'method').and.callThrough(); await reconfigureServer({ - appName: "emailVerifyToken", + appName: 'emailVerifyToken', verifyUserEmails: verifyUserEmails.method, preventLoginWithUnverifiedEmail: verifyUserEmails.method, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); const user = new Parse.User(); - user.setUsername("no_email"); - user.setPassword("expiringToken"); - user.set("email", "user@example.com"); + user.setUsername('no_email'); + user.setPassword('expiringToken'); + user.set('email', 'user@example.com'); await user.signUp(); expect(sendEmailOptions).toBeUndefined(); expect(user.getSessionToken()).toBeDefined(); expect(verifySpy).toHaveBeenCalledTimes(2); const user2 = new Parse.User(); - user2.setUsername("email"); - user2.setPassword("expiringToken"); - user2.set("email", "user2@example.com"); + user2.setUsername('email'); + user2.setPassword('expiringToken'); + user2.set('email', 'user2@example.com'); await user2.signUp(); await sendPromise; expect(user2.getSessionToken()).toBeUndefined(); @@ -430,8 +430,8 @@ describe("Email Verification Token Expiration:", () => { } ); - it_id("d812de87-33d1-495e-a6e8-3485f6dc3589")(it)( - "can conditionally send user email verification", + it_id('d812de87-33d1-495e-a6e8-3485f6dc3589')(it)( + 'can conditionally send user email verification', async () => { const emailAdapter = { sendVerificationEmail: () => {}, @@ -445,33 +445,33 @@ describe("Email Verification Token Expiration:", () => { return false; }, }; - const sendSpy = spyOn(sendVerificationEmail, "method").and.callThrough(); + const sendSpy = spyOn(sendVerificationEmail, 'method').and.callThrough(); await reconfigureServer({ - appName: "emailVerifyToken", + appName: 'emailVerifyToken', verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', sendUserEmailVerification: sendVerificationEmail.method, }); const emailSpy = spyOn( emailAdapter, - "sendVerificationEmail" + 'sendVerificationEmail' ).and.callThrough(); const newUser = new Parse.User(); - newUser.setUsername("unsets_email_verify_token_expires_at"); - newUser.setPassword("expiringToken"); - newUser.set("email", "user@example.com"); + newUser.setUsername('unsets_email_verify_token_expires_at'); + newUser.setPassword('expiringToken'); + newUser.set('email', 'user@example.com'); await newUser.signUp(); - await Parse.User.requestEmailVerification("user@example.com"); + await Parse.User.requestEmailVerification('user@example.com'); await sleep(100); expect(sendSpy).toHaveBeenCalledTimes(2); expect(emailSpy).toHaveBeenCalledTimes(0); } ); - it_id("d98babc1-feb8-4b5e-916c-57dc0a6ed9fb")(it)( - "provides full user object in email verification function on email and username change", + it_id('d98babc1-feb8-4b5e-916c-57dc0a6ed9fb')(it)( + 'provides full user object in email verification function on email and username change', async () => { const emailAdapter = { sendVerificationEmail: () => {}, @@ -482,35 +482,35 @@ describe("Email Verification Token Expiration:", () => { method(req) { expect(req.user).toBeDefined(); expect(req.user.id).toBeDefined(); - expect(req.user.get("createdAt")).toBeDefined(); - expect(req.user.get("updatedAt")).toBeDefined(); + expect(req.user.get('createdAt')).toBeDefined(); + expect(req.user.get('updatedAt')).toBeDefined(); expect(req.master).toBeDefined(); return false; }, }; await reconfigureServer({ - appName: "emailVerifyToken", + appName: 'emailVerifyToken', verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 5, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', sendUserEmailVerification: sendVerificationEmail.method, }); const user = new Parse.User(); - user.setPassword("password"); - user.setUsername("new@example.com"); - user.setEmail("user@example.com"); + user.setPassword('password'); + user.setUsername('new@example.com'); + user.setEmail('user@example.com'); await user.save(null, { useMasterKey: true }); // Update email and username - user.setUsername("new@example.com"); - user.setEmail("new@example.com"); + user.setUsername('new@example.com'); + user.setEmail('new@example.com'); await user.save(null, { useMasterKey: true }); } ); - it_id("a8c1f820-822f-4a37-9d08-a968cac8369d")(it)( - "beforeSave options do not change existing behaviour", + it_id('a8c1f820-822f-4a37-9d08-a968cac8369d')(it)( + 'beforeSave options do not change existing behaviour', async () => { let sendEmailOptions; const sendPromise = resolvingPromise(); @@ -523,20 +523,20 @@ describe("Email Verification Token Expiration:", () => { sendMail: () => {}, }; await reconfigureServer({ - appName: "emailVerifyToken", + appName: 'emailVerifyToken', verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); const emailSpy = spyOn( emailAdapter, - "sendVerificationEmail" + 'sendVerificationEmail' ).and.callThrough(); const newUser = new Parse.User(); - newUser.setUsername("unsets_email_verify_token_expires_at"); - newUser.setPassword("expiringToken"); - newUser.set("email", "user@parse.com"); + newUser.setUsername('unsets_email_verify_token_expires_at'); + newUser.setPassword('expiringToken'); + newUser.set('email', 'user@parse.com'); await newUser.signUp(); await sendPromise; const response = await request({ @@ -544,23 +544,23 @@ describe("Email Verification Token Expiration:", () => { followRedirects: false, }); expect(response.status).toEqual(302); - const config = Config.get("test"); - const results = await config.database.find("_User", { - username: "unsets_email_verify_token_expires_at", + const config = Config.get('test'); + const results = await config.database.find('_User', { + username: 'unsets_email_verify_token_expires_at', }); expect(results.length).toBe(1); const user = results[0]; - expect(typeof user).toBe("object"); + expect(typeof user).toBe('object'); expect(user.emailVerified).toEqual(true); - expect(typeof user._email_verify_token).toBe("undefined"); - expect(typeof user._email_verify_token_expires_at).toBe("undefined"); + expect(typeof user._email_verify_token).toBe('undefined'); + expect(typeof user._email_verify_token_expires_at).toBe('undefined'); expect(emailSpy).toHaveBeenCalled(); } ); - it_id("36d277eb-ec7c-4a39-9108-435b68228741")(it)( - "unsets the _email_verify_token_expires_at and _email_verify_token fields in the User class if email verification is successful", + it_id('36d277eb-ec7c-4a39-9108-435b68228741')(it)( + 'unsets the _email_verify_token_expires_at and _email_verify_token fields in the User class if email verification is successful', async () => { const user = new Parse.User(); let sendEmailOptions; @@ -574,15 +574,15 @@ describe("Email Verification Token Expiration:", () => { sendMail: () => {}, }; await reconfigureServer({ - appName: "emailVerifyToken", + appName: 'emailVerifyToken', verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); - user.setUsername("unsets_email_verify_token_expires_at"); - user.setPassword("expiringToken"); - user.set("email", "user@parse.com"); + user.setUsername('unsets_email_verify_token_expires_at'); + user.setPassword('expiringToken'); + user.set('email', 'user@parse.com'); await user.signUp(); await sendPromise; const response = await request({ @@ -590,24 +590,24 @@ describe("Email Verification Token Expiration:", () => { followRedirects: false, }); expect(response.status).toEqual(302); - const config = Config.get("test"); - const results = await config.database.find("_User", { - username: "unsets_email_verify_token_expires_at", + const config = Config.get('test'); + const results = await config.database.find('_User', { + username: 'unsets_email_verify_token_expires_at', }); expect(results.length).toBe(1); const verifiedUser = results[0]; - expect(typeof verifiedUser).toBe("object"); + expect(typeof verifiedUser).toBe('object'); expect(verifiedUser.emailVerified).toEqual(true); - expect(typeof verifiedUser._email_verify_token).toBe("undefined"); + expect(typeof verifiedUser._email_verify_token).toBe('undefined'); expect(typeof verifiedUser._email_verify_token_expires_at).toBe( - "undefined" + 'undefined' ); } ); - it_id("4f444704-ec4b-4dff-b947-614b1c6971c4")(it)( - "clicking on the email verify link by an email VERIFIED user that was setup before enabling the expire email verify token should show email verify email success", + it_id('4f444704-ec4b-4dff-b947-614b1c6971c4')(it)( + 'clicking on the email verify link by an email VERIFIED user that was setup before enabling the expire email verify token should show email verify email success', async () => { const user = new Parse.User(); let sendEmailOptions; @@ -621,17 +621,17 @@ describe("Email Verification Token Expiration:", () => { sendMail: () => {}, }; const serverConfig = { - appName: "emailVerifyToken", + appName: 'emailVerifyToken', verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }; // setup server WITHOUT enabling the expire email verify token flag await reconfigureServer(serverConfig); - user.setUsername("testEmailVerifyTokenValidity"); - user.setPassword("expiringToken"); - user.set("email", "user@parse.com"); + user.setUsername('testEmailVerifyTokenValidity'); + user.setPassword('expiringToken'); + user.set('email', 'user@parse.com'); await user.signUp(); await sendPromise; let response = await request({ @@ -640,7 +640,7 @@ describe("Email Verification Token Expiration:", () => { }); expect(response.status).toEqual(302); await user.fetch(); - expect(user.get("emailVerified")).toEqual(true); + expect(user.get('emailVerified')).toEqual(true); // RECONFIGURE the server i.e., ENABLE the expire email verify token flag serverConfig.emailVerifyTokenValidityDuration = 5; // 5 seconds await reconfigureServer(serverConfig); @@ -651,14 +651,14 @@ describe("Email Verification Token Expiration:", () => { }); expect(response.status).toEqual(302); const url = new URL(sendEmailOptions.link); - const token = url.searchParams.get("token"); + const token = url.searchParams.get('token'); expect(response.text).toEqual( `Found. Redirecting to http://localhost:8378/1/apps/invalid_verification_link.html?appId=test&token=${token}` ); } ); - it("clicking on the email verify link by an email UNVERIFIED user that was setup before enabling the expire email verify token should show invalid verficiation link page", async () => { + it('clicking on the email verify link by an email UNVERIFIED user that was setup before enabling the expire email verify token should show invalid verficiation link page', async () => { const user = new Parse.User(); let sendEmailOptions; const sendPromise = resolvingPromise(); @@ -671,23 +671,23 @@ describe("Email Verification Token Expiration:", () => { sendMail: () => {}, }; const serverConfig = { - appName: "emailVerifyToken", + appName: 'emailVerifyToken', verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }; // setup server WITHOUT enabling the expire email verify token flag await reconfigureServer(serverConfig); - user.setUsername("testEmailVerifyTokenValidity"); - user.setPassword("expiringToken"); - user.set("email", "user@parse.com"); + user.setUsername('testEmailVerifyTokenValidity'); + user.setPassword('expiringToken'); + user.set('email', 'user@parse.com'); await user.signUp(); await sendPromise; // just get the user again - DO NOT email verify the user await user.fetch(); - expect(user.get("emailVerified")).toEqual(false); + expect(user.get('emailVerified')).toEqual(false); // RECONFIGURE the server i.e., ENABLE the expire email verify token flag serverConfig.emailVerifyTokenValidityDuration = 5; // 5 seconds await reconfigureServer(serverConfig); @@ -698,14 +698,14 @@ describe("Email Verification Token Expiration:", () => { }); expect(response.status).toEqual(302); const url = new URL(sendEmailOptions.link); - const token = url.searchParams.get("token"); + const token = url.searchParams.get('token'); expect(response.text).toEqual( `Found. Redirecting to http://localhost:8378/1/apps/invalid_verification_link.html?appId=test&token=${token}` ); }); - it_id("b6c87f35-d887-477d-bc86-a9217a424f53")(it)( - "setting the email on the user should set a new email verification token and new expiration date for the token when expire email verify token flag is set", + it_id('b6c87f35-d887-477d-bc86-a9217a424f53')(it)( + 'setting the email on the user should set a new email verification token and new expiration date for the token when expire email verify token flag is set', async () => { const user = new Parse.User(); let userBeforeEmailReset; @@ -721,38 +721,38 @@ describe("Email Verification Token Expiration:", () => { sendMail: () => {}, }; const serverConfig = { - appName: "emailVerifyToken", + appName: 'emailVerifyToken', verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }; await reconfigureServer(serverConfig); - user.setUsername("newEmailVerifyTokenOnEmailReset"); - user.setPassword("expiringToken"); - user.set("email", "user@parse.com"); + user.setUsername('newEmailVerifyTokenOnEmailReset'); + user.setPassword('expiringToken'); + user.set('email', 'user@parse.com'); await user.signUp(); await sendPromise; - const config = Config.get("test"); + const config = Config.get('test'); const userFromDb = await config.database - .find("_User", { username: "newEmailVerifyTokenOnEmailReset" }) + .find('_User', { username: 'newEmailVerifyTokenOnEmailReset' }) .then(results => { return results[0]; }); - expect(typeof userFromDb).toBe("object"); + expect(typeof userFromDb).toBe('object'); userBeforeEmailReset = userFromDb; // trigger another token generation by setting the email - user.set("email", "user@parse.com"); + user.set('email', 'user@parse.com'); await new Promise(resolve => { // wait for half a sec to get a new expiration time setTimeout(() => resolve(user.save()), 500); }); const userAfterEmailReset = await config.database .find( - "_User", - { username: "newEmailVerifyTokenOnEmailReset" }, + '_User', + { username: 'newEmailVerifyTokenOnEmailReset' }, {}, Auth.maintenance(config) ) @@ -760,7 +760,7 @@ describe("Email Verification Token Expiration:", () => { return results[0]; }); - expect(typeof userAfterEmailReset).toBe("object"); + expect(typeof userAfterEmailReset).toBe('object'); expect(userBeforeEmailReset._email_verify_token).not.toEqual( userAfterEmailReset._email_verify_token ); @@ -771,8 +771,8 @@ describe("Email Verification Token Expiration:", () => { } ); - it_id("28f2140d-48bd-44ac-a141-ba60ea8d9713")(it)( - "should send a new verification email when a resend is requested and the user is UNVERIFIED", + it_id('28f2140d-48bd-44ac-a141-ba60ea8d9713')(it)( + 'should send a new verification email when a resend is requested and the user is UNVERIFIED', async () => { const user = new Parse.User(); let sendEmailOptions; @@ -794,20 +794,20 @@ describe("Email Verification Token Expiration:", () => { sendMail: () => {}, }; await reconfigureServer({ - appName: "emailVerifyToken", + appName: 'emailVerifyToken', verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); - user.setUsername("resends_verification_token"); - user.setPassword("expiringToken"); - user.set("email", "user@parse.com"); + user.setUsername('resends_verification_token'); + user.setPassword('expiringToken'); + user.set('email', 'user@parse.com'); await user.signUp(); await promise1; - const config = Config.get("test"); + const config = Config.get('test'); const newUser = await config.database - .find("_User", { username: "resends_verification_token" }) + .find('_User', { username: 'resends_verification_token' }) .then(results => { return results[0]; }); @@ -817,15 +817,15 @@ describe("Email Verification Token Expiration:", () => { expect(sendVerificationEmailCallCount).toBe(1); const response = await request({ - url: "http://localhost:8378/1/verificationEmailRequest", - method: "POST", + url: 'http://localhost:8378/1/verificationEmailRequest', + method: 'POST', body: { - email: "user@parse.com", + email: 'user@parse.com', }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, }); expect(response.status).toBe(200); @@ -836,8 +836,8 @@ describe("Email Verification Token Expiration:", () => { // query for this user again const userAfterRequest = await config.database .find( - "_User", - { username: "resends_verification_token" }, + '_User', + { username: 'resends_verification_token' }, {}, Auth.maintenance(config) ) @@ -845,7 +845,7 @@ describe("Email Verification Token Expiration:", () => { return results[0]; }); // verify that our token & expiration has been changed for this new request - expect(typeof userAfterRequest).toBe("object"); + expect(typeof userAfterRequest).toBe('object'); expect(userBeforeRequest._email_verify_token).not.toEqual( userAfterRequest._email_verify_token ); @@ -855,11 +855,11 @@ describe("Email Verification Token Expiration:", () => { } ); - it("provides function arguments in verifyUserEmails on verificationEmailRequest", async () => { + it('provides function arguments in verifyUserEmails on verificationEmailRequest', async () => { const user = new Parse.User(); - user.setUsername("user"); - user.setPassword("pass"); - user.set("email", "test@example.com"); + user.setUsername('user'); + user.setPassword('pass'); + user.set('email', 'test@example.com'); await user.signUp(); const verifyUserEmails = { @@ -874,28 +874,28 @@ describe("Email Verification Token Expiration:", () => { }; const verifyUserEmailsSpy = spyOn( verifyUserEmails, - "method" + 'method' ).and.callThrough(); await reconfigureServer({ - appName: "test", - publicServerURL: "http://localhost:1337/1", + appName: 'test', + publicServerURL: 'http://localhost:1337/1', verifyUserEmails: verifyUserEmails.method, preventLoginWithUnverifiedEmail: verifyUserEmails.method, preventSignupWithUnverifiedEmail: true, emailAdapter: MockEmailAdapterWithOptions({ - fromAddress: "parse@example.com", - apiKey: "k", - domain: "d", + fromAddress: 'parse@example.com', + apiKey: 'k', + domain: 'd', }), }); await expectAsync( - Parse.User.requestEmailVerification("test@example.com") + Parse.User.requestEmailVerification('test@example.com') ).toBeResolved(); expect(verifyUserEmailsSpy).toHaveBeenCalledTimes(1); }); - it("should throw with invalid emailVerifyTokenReuseIfValid", async () => { + it('should throw with invalid emailVerifyTokenReuseIfValid', async () => { const sendEmailOptions = []; const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), @@ -906,35 +906,35 @@ describe("Email Verification Token Expiration:", () => { }; try { await reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 5 * 60, // 5 minutes emailVerifyTokenReuseIfValid: [], - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); - fail("should have thrown."); + fail('should have thrown.'); } catch (e) { - expect(e).toBe("emailVerifyTokenReuseIfValid must be a boolean value"); + expect(e).toBe('emailVerifyTokenReuseIfValid must be a boolean value'); } try { await reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenReuseIfValid: true, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); - fail("should have thrown."); + fail('should have thrown.'); } catch (e) { expect(e).toBe( - "You cannot use emailVerifyTokenReuseIfValid without emailVerifyTokenValidityDuration" + 'You cannot use emailVerifyTokenReuseIfValid without emailVerifyTokenValidityDuration' ); } }); - it_id("0e66b7f6-2c07-4117-a8b9-605aa31a1e29")(it)( - "should match codes with emailVerifyTokenReuseIfValid", + it_id('0e66b7f6-2c07-4117-a8b9-605aa31a1e29')(it)( + 'should match codes with emailVerifyTokenReuseIfValid', async () => { let sendEmailOptions; let sendVerificationEmailCallCount = 0; @@ -954,24 +954,24 @@ describe("Email Verification Token Expiration:", () => { sendMail: () => {}, }; await reconfigureServer({ - appName: "emailVerifyToken", + appName: 'emailVerifyToken', verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 5 * 60, // 5 minutes - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', emailVerifyTokenReuseIfValid: true, }); const user = new Parse.User(); - user.setUsername("resends_verification_token"); - user.setPassword("expiringToken"); - user.set("email", "user@example.com"); + user.setUsername('resends_verification_token'); + user.setPassword('expiringToken'); + user.set('email', 'user@example.com'); await user.signUp(); await promise1; - const config = Config.get("test"); + const config = Config.get('test'); const [userBeforeRequest] = await config.database.find( - "_User", + '_User', { - username: "resends_verification_token", + username: 'resends_verification_token', }, {}, Auth.maintenance(config) @@ -984,15 +984,15 @@ describe("Email Verification Token Expiration:", () => { }, 1000); }); const response = await request({ - url: "http://localhost:8378/1/verificationEmailRequest", - method: "POST", + url: 'http://localhost:8378/1/verificationEmailRequest', + method: 'POST', body: { - email: "user@example.com", + email: 'user@example.com', }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, }); await promise2; @@ -1001,16 +1001,16 @@ describe("Email Verification Token Expiration:", () => { expect(sendEmailOptions).toBeDefined(); const [userAfterRequest] = await config.database.find( - "_User", + '_User', { - username: "resends_verification_token", + username: 'resends_verification_token', }, {}, Auth.maintenance(config) ); // Verify that token & expiration haven't been changed for this new request - expect(typeof userAfterRequest).toBe("object"); + expect(typeof userAfterRequest).toBe('object'); expect(userBeforeRequest._email_verify_token).toBeDefined(); expect(userBeforeRequest._email_verify_token).toEqual( userAfterRequest._email_verify_token @@ -1022,8 +1022,8 @@ describe("Email Verification Token Expiration:", () => { } ); - it_id("1ed9a6c2-bebc-4813-af30-4f4a212544c2")(it)( - "should not send a new verification email when a resend is requested and the user is VERIFIED", + it_id('1ed9a6c2-bebc-4813-af30-4f4a212544c2')(it)( + 'should not send a new verification email when a resend is requested and the user is VERIFIED', async () => { const user = new Parse.User(); let sendEmailOptions; @@ -1039,15 +1039,15 @@ describe("Email Verification Token Expiration:", () => { sendMail: () => {}, }; await reconfigureServer({ - appName: "emailVerifyToken", + appName: 'emailVerifyToken', verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); - user.setUsername("no_new_verification_token_once_verified"); - user.setPassword("expiringToken"); - user.set("email", "user@parse.com"); + user.setUsername('no_new_verification_token_once_verified'); + user.setPassword('expiringToken'); + user.set('email', 'user@parse.com'); await user.signUp(); await sendPromise; let response = await request({ @@ -1058,15 +1058,15 @@ describe("Email Verification Token Expiration:", () => { expect(sendVerificationEmailCallCount).toBe(1); response = await request({ - url: "http://localhost:8378/1/verificationEmailRequest", - method: "POST", + url: 'http://localhost:8378/1/verificationEmailRequest', + method: 'POST', body: { - email: "user@parse.com", + email: 'user@parse.com', }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, }).then(fail, res => res); expect(response.status).toBe(400); @@ -1074,7 +1074,7 @@ describe("Email Verification Token Expiration:", () => { } ); - it("should not send a new verification email if this user does not exist", async () => { + it('should not send a new verification email if this user does not exist', async () => { let sendEmailOptions; let sendVerificationEmailCallCount = 0; const emailAdapter = { @@ -1086,22 +1086,22 @@ describe("Email Verification Token Expiration:", () => { sendMail: () => {}, }; await reconfigureServer({ - appName: "emailVerifyToken", + appName: 'emailVerifyToken', verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); const response = await request({ - url: "http://localhost:8378/1/verificationEmailRequest", - method: "POST", + url: 'http://localhost:8378/1/verificationEmailRequest', + method: 'POST', body: { - email: "user@parse.com", + email: 'user@parse.com', }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, }) .then(fail) @@ -1112,7 +1112,7 @@ describe("Email Verification Token Expiration:", () => { expect(sendEmailOptions).not.toBeDefined(); }); - it("should fail if no email is supplied", async () => { + it('should fail if no email is supplied', async () => { let sendEmailOptions; let sendVerificationEmailCallCount = 0; const emailAdapter = { @@ -1124,30 +1124,30 @@ describe("Email Verification Token Expiration:", () => { sendMail: () => {}, }; await reconfigureServer({ - appName: "emailVerifyToken", + appName: 'emailVerifyToken', verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); const response = await request({ - url: "http://localhost:8378/1/verificationEmailRequest", - method: "POST", + url: 'http://localhost:8378/1/verificationEmailRequest', + method: 'POST', body: {}, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, }).then(fail, response => response); expect(response.status).toBe(400); expect(response.data.code).toBe(Parse.Error.EMAIL_MISSING); - expect(response.data.error).toBe("you must provide an email"); + expect(response.data.error).toBe('you must provide an email'); expect(sendVerificationEmailCallCount).toBe(0); expect(sendEmailOptions).not.toBeDefined(); }); - it("should fail if email is not a string", async () => { + it('should fail if email is not a string', async () => { let sendEmailOptions; let sendVerificationEmailCallCount = 0; const emailAdapter = { @@ -1159,30 +1159,30 @@ describe("Email Verification Token Expiration:", () => { sendMail: () => {}, }; await reconfigureServer({ - appName: "emailVerifyToken", + appName: 'emailVerifyToken', verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); const response = await request({ - url: "http://localhost:8378/1/verificationEmailRequest", - method: "POST", + url: 'http://localhost:8378/1/verificationEmailRequest', + method: 'POST', body: { email: 3 }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, }).then(fail, res => res); expect(response.status).toBe(400); expect(response.data.code).toBe(Parse.Error.INVALID_EMAIL_ADDRESS); - expect(response.data.error).toBe("you must provide a valid email string"); + expect(response.data.error).toBe('you must provide a valid email string'); expect(sendVerificationEmailCallCount).toBe(0); expect(sendEmailOptions).not.toBeDefined(); }); - it("client should not see the _email_verify_token_expires_at field", async () => { + it('client should not see the _email_verify_token_expires_at field', async () => { const user = new Parse.User(); let sendEmailOptions; const sendPromise = resolvingPromise(); @@ -1195,25 +1195,25 @@ describe("Email Verification Token Expiration:", () => { sendMail: () => {}, }; await reconfigureServer({ - appName: "emailVerifyToken", + appName: 'emailVerifyToken', verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); - user.setUsername("testEmailVerifyTokenValidity"); - user.setPassword("expiringToken"); - user.set("email", "user@parse.com"); + user.setUsername('testEmailVerifyTokenValidity'); + user.setPassword('expiringToken'); + user.set('email', 'user@parse.com'); await user.signUp(); await sendPromise; await user.fetch(); - expect(user.get("emailVerified")).toEqual(false); - expect(typeof user.get("_email_verify_token_expires_at")).toBe("undefined"); + expect(user.get('emailVerified')).toEqual(false); + expect(typeof user.get('_email_verify_token_expires_at')).toBe('undefined'); expect(sendEmailOptions).toBeDefined(); }); - it_id("b082d387-4974-4d45-a0d9-0c85ca2d7cbf")(it)( - "emailVerified should be set to false after changing from an already verified email", + it_id('b082d387-4974-4d45-a0d9-0c85ca2d7cbf')(it)( + 'emailVerified should be set to false after changing from an already verified email', async () => { let user = new Parse.User(); let sendEmailOptions; @@ -1227,15 +1227,15 @@ describe("Email Verification Token Expiration:", () => { sendMail: () => {}, }; await reconfigureServer({ - appName: "emailVerifyToken", + appName: 'emailVerifyToken', verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); - user.setUsername("testEmailVerifyTokenValidity"); - user.setPassword("expiringToken"); - user.set("email", "user@parse.com"); + user.setUsername('testEmailVerifyTokenValidity'); + user.setPassword('expiringToken'); + user.set('email', 'user@parse.com'); await user.signUp(); await sendPromise; let response = await request({ @@ -1244,18 +1244,18 @@ describe("Email Verification Token Expiration:", () => { }); expect(response.status).toEqual(302); user = await Parse.User.logIn( - "testEmailVerifyTokenValidity", - "expiringToken" + 'testEmailVerifyTokenValidity', + 'expiringToken' ); - expect(typeof user).toBe("object"); - expect(user.get("emailVerified")).toBe(true); + expect(typeof user).toBe('object'); + expect(user.get('emailVerified')).toBe(true); - user.set("email", "newEmail@parse.com"); + user.set('email', 'newEmail@parse.com'); await user.save(); await user.fetch(); - expect(typeof user).toBe("object"); - expect(user.get("email")).toBe("newEmail@parse.com"); - expect(user.get("emailVerified")).toBe(false); + expect(typeof user).toBe('object'); + expect(user.get('email')).toBe('newEmail@parse.com'); + expect(user.get('emailVerified')).toBe(false); response = await request({ url: sendEmailOptions.link, diff --git a/spec/EnableExpressErrorHandler.spec.js b/spec/EnableExpressErrorHandler.spec.js index f3a5a56f77..64c250628b 100644 --- a/spec/EnableExpressErrorHandler.spec.js +++ b/spec/EnableExpressErrorHandler.spec.js @@ -1,28 +1,28 @@ -const request = require("../lib/request"); +const request = require('../lib/request'); -describe("Enable express error handler", () => { - it("should call the default handler in case of error, like updating a non existing object", async done => { - spyOn(console, "error"); +describe('Enable express error handler', () => { + it('should call the default handler in case of error, like updating a non existing object', async done => { + spyOn(console, 'error'); const parseServer = await reconfigureServer({ enableExpressErrorHandler: true, }); parseServer.app.use(function (err, req, res, next) { - expect(err.message).toBe("Object not found."); + expect(err.message).toBe('Object not found.'); next(err); }); try { await request({ - method: "PUT", - url: defaultConfiguration.serverURL + "/classes/AnyClass/nonExistingId", + method: 'PUT', + url: defaultConfiguration.serverURL + '/classes/AnyClass/nonExistingId', headers: { - "X-Parse-Application-Id": defaultConfiguration.appId, - "X-Parse-Master-Key": defaultConfiguration.masterKey, - "Content-Type": "application/json", + 'X-Parse-Application-Id': defaultConfiguration.appId, + 'X-Parse-Master-Key': defaultConfiguration.masterKey, + 'Content-Type': 'application/json', }, - body: { someField: "blablabla" }, + body: { someField: 'blablabla' }, }); - fail("Should throw error"); + fail('Should throw error'); } catch (response) { expect(response).toBeDefined(); expect(response.status).toEqual(500); diff --git a/spec/EventEmitterPubSub.spec.js b/spec/EventEmitterPubSub.spec.js index 2d3eaf31bd..f14f51ce25 100644 --- a/spec/EventEmitterPubSub.spec.js +++ b/spec/EventEmitterPubSub.spec.js @@ -1,44 +1,44 @@ const EventEmitterPubSub = - require("../lib/Adapters/PubSub/EventEmitterPubSub").EventEmitterPubSub; + require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub; -describe("EventEmitterPubSub", function () { - it("can publish and subscribe", function () { +describe('EventEmitterPubSub', function () { + it('can publish and subscribe', function () { const publisher = EventEmitterPubSub.createPublisher(); const subscriber = EventEmitterPubSub.createSubscriber(); - subscriber.subscribe("testChannel"); + subscriber.subscribe('testChannel'); // Register mock checked for subscriber let isChecked = false; - subscriber.on("message", function (channel, message) { + subscriber.on('message', function (channel, message) { isChecked = true; - expect(channel).toBe("testChannel"); - expect(message).toBe("testMessage"); + expect(channel).toBe('testChannel'); + expect(message).toBe('testMessage'); }); - publisher.publish("testChannel", "testMessage"); + publisher.publish('testChannel', 'testMessage'); // Make sure the callback is checked expect(isChecked).toBe(true); }); - it("can unsubscribe", function () { + it('can unsubscribe', function () { const publisher = EventEmitterPubSub.createPublisher(); const subscriber = EventEmitterPubSub.createSubscriber(); - subscriber.subscribe("testChannel"); - subscriber.unsubscribe("testChannel"); + subscriber.subscribe('testChannel'); + subscriber.unsubscribe('testChannel'); // Register mock checked for subscriber let isCalled = false; - subscriber.on("message", function () { + subscriber.on('message', function () { isCalled = true; }); - publisher.publish("testChannel", "testMessage"); + publisher.publish('testChannel', 'testMessage'); // Make sure the callback is not called expect(isCalled).toBe(false); }); - it("can unsubscribe not subscribing channel", function () { + it('can unsubscribe not subscribing channel', function () { const subscriber = EventEmitterPubSub.createSubscriber(); // Make sure subscriber does not throw exception - subscriber.unsubscribe("testChannel"); + subscriber.unsubscribe('testChannel'); }); }); diff --git a/spec/FilesController.spec.js b/spec/FilesController.spec.js index b7857c648f..4c5880ccf4 100644 --- a/spec/FilesController.spec.js +++ b/spec/FilesController.spec.js @@ -1,38 +1,38 @@ const LoggerController = - require("../lib/Controllers/LoggerController").LoggerController; + require('../lib/Controllers/LoggerController').LoggerController; const WinstonLoggerAdapter = - require("../lib/Adapters/Logger/WinstonLoggerAdapter").WinstonLoggerAdapter; + require('../lib/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter; const GridFSBucketAdapter = - require("../lib/Adapters/Files/GridFSBucketAdapter").GridFSBucketAdapter; -const Config = require("../lib/Config"); -const FilesController = require("../lib/Controllers/FilesController").default; -const databaseURI = "mongodb://localhost:27017/parse"; + require('../lib/Adapters/Files/GridFSBucketAdapter').GridFSBucketAdapter; +const Config = require('../lib/Config'); +const FilesController = require('../lib/Controllers/FilesController').default; +const databaseURI = 'mongodb://localhost:27017/parse'; const mockAdapter = { createFile: () => { - return Promise.reject(new Error("it failed with xyz")); + return Promise.reject(new Error('it failed with xyz')); }, deleteFile: () => {}, getFileData: () => {}, - getFileLocation: () => "xyz", + getFileLocation: () => 'xyz', validateFilename: () => { return null; }, }; // Small additional tests to improve overall coverage -describe("FilesController", () => { - it("should properly expand objects with sync getFileLocation", async () => { +describe('FilesController', () => { + it('should properly expand objects with sync getFileLocation', async () => { const config = Config.get(Parse.applicationId); const gridFSAdapter = new GridFSBucketAdapter( - "mongodb://localhost:27017/parse" + 'mongodb://localhost:27017/parse' ); gridFSAdapter.getFileLocation = (config, filename) => { return ( config.mount + - "/files/" + + '/files/' + config.applicationId + - "/" + + '/' + encodeURIComponent(filename) ); }; @@ -45,29 +45,29 @@ describe("FilesController", () => { expect(result).toBeUndefined(); const fullFile = { - type: "__type", - url: "http://an.url", + type: '__type', + url: 'http://an.url', }; const anObject = { aFile: fullFile, }; await filesController.expandFilesInObject(config, anObject); - expect(anObject.aFile.url).toEqual("http://an.url"); + expect(anObject.aFile.url).toEqual('http://an.url'); }); - it("should properly expand objects with async getFileLocation", async () => { + it('should properly expand objects with async getFileLocation', async () => { const config = Config.get(Parse.applicationId); const gridFSAdapter = new GridFSBucketAdapter( - "mongodb://localhost:27017/parse" + 'mongodb://localhost:27017/parse' ); gridFSAdapter.getFileLocation = async (config, filename) => { await Promise.resolve(); return ( config.mount + - "/files/" + + '/files/' + config.applicationId + - "/" + + '/' + encodeURIComponent(filename) ); }; @@ -80,30 +80,30 @@ describe("FilesController", () => { expect(result).toBeUndefined(); const fullFile = { - type: "__type", - url: "http://an.url", + type: '__type', + url: 'http://an.url', }; const anObject = { aFile: fullFile, }; await filesController.expandFilesInObject(config, anObject); - expect(anObject.aFile.url).toEqual("http://an.url"); + expect(anObject.aFile.url).toEqual('http://an.url'); }); - it("should call getFileLocation when config.fileKey is undefined", async () => { + it('should call getFileLocation when config.fileKey is undefined', async () => { const config = {}; const gridFSAdapter = new GridFSBucketAdapter( - "mongodb://localhost:27017/parse" + 'mongodb://localhost:27017/parse' ); const fullFile = { - name: "mock-name", - __type: "File", + name: 'mock-name', + __type: 'File', }; gridFSAdapter.getFileLocation = jasmine - .createSpy("getFileLocation") - .and.returnValue(Promise.resolve("mock-url")); + .createSpy('getFileLocation') + .and.returnValue(Promise.resolve('mock-url')); const filesController = new FilesController(gridFSAdapter); const anObject = { aFile: fullFile }; @@ -112,22 +112,22 @@ describe("FilesController", () => { config, fullFile.name ); - expect(anObject.aFile.url).toEqual("mock-url"); + expect(anObject.aFile.url).toEqual('mock-url'); }); - it("should call getFileLocation when config.fileKey is defined", async () => { - const config = { fileKey: "mock-key" }; + it('should call getFileLocation when config.fileKey is defined', async () => { + const config = { fileKey: 'mock-key' }; const gridFSAdapter = new GridFSBucketAdapter( - "mongodb://localhost:27017/parse" + 'mongodb://localhost:27017/parse' ); const fullFile = { - name: "mock-name", - __type: "File", + name: 'mock-name', + __type: 'File', }; gridFSAdapter.getFileLocation = jasmine - .createSpy("getFileLocation") - .and.returnValue(Promise.resolve("mock-url")); + .createSpy('getFileLocation') + .and.returnValue(Promise.resolve('mock-url')); const filesController = new FilesController(gridFSAdapter); const anObject = { aFile: fullFile }; @@ -136,14 +136,14 @@ describe("FilesController", () => { config, fullFile.name ); - expect(anObject.aFile.url).toEqual("mock-url"); + expect(anObject.aFile.url).toEqual('mock-url'); }); - it_only_db("mongo")( - "should pass databaseOptions to GridFSBucketAdapter", + it_only_db('mongo')( + 'should pass databaseOptions to GridFSBucketAdapter', async () => { await reconfigureServer({ - databaseURI: "mongodb://localhost:27017/parse", + databaseURI: 'mongodb://localhost:27017/parse', filesAdapter: null, databaseAdapter: null, databaseOptions: { @@ -164,14 +164,14 @@ describe("FilesController", () => { } ); - it("should create a server log on failure", done => { + it('should create a server log on failure', done => { const logController = new LoggerController(new WinstonLoggerAdapter()); reconfigureServer({ filesAdapter: mockAdapter }) - .then(() => new Parse.File("yolo.txt", [1, 2, 3], "text/plain").save()) + .then(() => new Parse.File('yolo.txt', [1, 2, 3], 'text/plain').save()) .then( - () => done.fail("should not succeed"), - () => setImmediate(() => Promise.resolve("done")) + () => done.fail('should not succeed'), + () => setImmediate(() => Promise.resolve('done')) ) .then(() => new Promise(resolve => setTimeout(resolve, 200))) .then(() => @@ -182,27 +182,27 @@ describe("FilesController", () => { // and 2 the message that will be sent back to the client. const log1 = logs.find( - x => x.message === "Error creating a file: it failed with xyz" + x => x.message === 'Error creating a file: it failed with xyz' ); - expect(log1.level).toBe("error"); + expect(log1.level).toBe('error'); - const log2 = logs.find(x => x.message === "it failed with xyz"); - expect(log2.level).toBe("error"); + const log2 = logs.find(x => x.message === 'it failed with xyz'); + expect(log2.level).toBe('error'); expect(log2.code).toBe(130); done(); }); }); - it("should create a parse error when a string is returned", done => { + it('should create a parse error when a string is returned', done => { const mock2 = mockAdapter; mock2.validateFilename = () => { - return "Bad file! No biscuit!"; + return 'Bad file! No biscuit!'; }; const filesController = new FilesController(mockAdapter); const error = filesController.validateFilename(); - expect(typeof error).toBe("object"); - expect(error.message.indexOf("biscuit")).toBe(13); + expect(typeof error).toBe('object'); + expect(error.message.indexOf('biscuit')).toBe(13); expect(error.code).toBe(Parse.Error.INVALID_FILE_NAME); mockAdapter.validateFilename = () => { return null; @@ -210,15 +210,15 @@ describe("FilesController", () => { done(); }); - it("should add a unique hash to the file name when the preserveFileName option is false", async () => { + it('should add a unique hash to the file name when the preserveFileName option is false', async () => { const config = Config.get(Parse.applicationId); const gridFSAdapter = new GridFSBucketAdapter( - "mongodb://localhost:27017/parse" + 'mongodb://localhost:27017/parse' ); - spyOn(gridFSAdapter, "createFile"); + spyOn(gridFSAdapter, 'createFile'); gridFSAdapter.createFile.and.returnValue(Promise.resolve()); - const fileName = "randomFileName.pdf"; - const regexEscapedFileName = fileName.replace(/\./g, "\\$&"); + const fileName = 'randomFileName.pdf'; + const regexEscapedFileName = fileName.replace(/\./g, '\\$&'); const filesController = new FilesController(gridFSAdapter, null, { preserveFileName: false, }); @@ -231,14 +231,14 @@ describe("FilesController", () => { ); }); - it("should not add a unique hash to the file name when the preserveFileName option is true", async () => { + it('should not add a unique hash to the file name when the preserveFileName option is true', async () => { const config = Config.get(Parse.applicationId); const gridFSAdapter = new GridFSBucketAdapter( - "mongodb://localhost:27017/parse" + 'mongodb://localhost:27017/parse' ); - spyOn(gridFSAdapter, "createFile"); + spyOn(gridFSAdapter, 'createFile'); gridFSAdapter.createFile.and.returnValue(Promise.resolve()); - const fileName = "randomFileName.pdf"; + const fileName = 'randomFileName.pdf'; const filesController = new FilesController(gridFSAdapter, null, { preserveFileName: true, }); @@ -251,7 +251,7 @@ describe("FilesController", () => { ); }); - it("should handle adapter without getMetadata", async () => { + it('should handle adapter without getMetadata', async () => { const gridFSAdapter = new GridFSBucketAdapter(databaseURI); gridFSAdapter.getMetadata = null; const filesController = new FilesController(gridFSAdapter); @@ -260,20 +260,20 @@ describe("FilesController", () => { expect(result).toEqual({}); }); - it("should reject slashes in file names", done => { + it('should reject slashes in file names', done => { const gridFSAdapter = new GridFSBucketAdapter( - "mongodb://localhost:27017/parse" + 'mongodb://localhost:27017/parse' ); - const fileName = "foo/randomFileName.pdf"; + const fileName = 'foo/randomFileName.pdf'; expect(gridFSAdapter.validateFilename(fileName)).not.toBe(null); done(); }); - it("should also reject slashes in file names", done => { + it('should also reject slashes in file names', done => { const gridFSAdapter = new GridFSBucketAdapter( - "mongodb://localhost:27017/parse" + 'mongodb://localhost:27017/parse' ); - const fileName = "foo/randomFileName.pdf"; + const fileName = 'foo/randomFileName.pdf'; expect(gridFSAdapter.validateFilename(fileName)).not.toBe(null); done(); }); diff --git a/spec/GridFSBucketStorageAdapter.spec.js b/spec/GridFSBucketStorageAdapter.spec.js index 164c60e5be..bbcf17f37d 100644 --- a/spec/GridFSBucketStorageAdapter.spec.js +++ b/spec/GridFSBucketStorageAdapter.spec.js @@ -1,27 +1,27 @@ const GridFSBucketAdapter = - require("../lib/Adapters/Files/GridFSBucketAdapter").GridFSBucketAdapter; -const { randomString } = require("../lib/cryptoUtils"); -const databaseURI = "mongodb://localhost:27017/parse"; -const request = require("../lib/request"); + require('../lib/Adapters/Files/GridFSBucketAdapter').GridFSBucketAdapter; +const { randomString } = require('../lib/cryptoUtils'); +const databaseURI = 'mongodb://localhost:27017/parse'; +const request = require('../lib/request'); async function expectMissingFile(gfsAdapter, name) { try { await gfsAdapter.getFileData(name); - fail("should have thrown"); + fail('should have thrown'); } catch (e) { - expect(e.message).toEqual("FileNotFound: file myFileName was not found"); + expect(e.message).toEqual('FileNotFound: file myFileName was not found'); } } -describe_only_db("mongo")("GridFSBucket", () => { +describe_only_db('mongo')('GridFSBucket', () => { beforeEach(async () => { const gsAdapter = new GridFSBucketAdapter(databaseURI); const db = await gsAdapter._connect(); await db.dropDatabase(); }); - it("should connect to mongo with the supported database options", async () => { - const databaseURI = "mongodb://localhost:27017/parse"; + it('should connect to mongo with the supported database options', async () => { + const databaseURI = 'mongodb://localhost:27017/parse'; const gfsAdapter = new GridFSBucketAdapter(databaseURI, { retryWrites: true, // these are not supported by the mongo client @@ -36,41 +36,41 @@ describe_only_db("mongo")("GridFSBucket", () => { expect(db.options?.retryWrites).toEqual(true); }); - it("should save an encrypted file that can only be decrypted by a GridFS adapter with the encryptionKey", async () => { + it('should save an encrypted file that can only be decrypted by a GridFS adapter with the encryptionKey', async () => { const unencryptedAdapter = new GridFSBucketAdapter(databaseURI); const encryptedAdapter = new GridFSBucketAdapter( databaseURI, {}, - "89E4AFF1-DFE4-4603-9574-BFA16BB446FD" + '89E4AFF1-DFE4-4603-9574-BFA16BB446FD' ); - await expectMissingFile(encryptedAdapter, "myFileName"); - const originalString = "abcdefghi"; - await encryptedAdapter.createFile("myFileName", originalString); + await expectMissingFile(encryptedAdapter, 'myFileName'); + const originalString = 'abcdefghi'; + await encryptedAdapter.createFile('myFileName', originalString); const unencryptedResult = - await unencryptedAdapter.getFileData("myFileName"); - expect(unencryptedResult.toString("utf8")).not.toBe(originalString); - const encryptedResult = await encryptedAdapter.getFileData("myFileName"); - expect(encryptedResult.toString("utf8")).toBe(originalString); + await unencryptedAdapter.getFileData('myFileName'); + expect(unencryptedResult.toString('utf8')).not.toBe(originalString); + const encryptedResult = await encryptedAdapter.getFileData('myFileName'); + expect(encryptedResult.toString('utf8')).toBe(originalString); }); - it("should rotate key of all unencrypted GridFS files to encrypted files", async () => { + it('should rotate key of all unencrypted GridFS files to encrypted files', async () => { const unencryptedAdapter = new GridFSBucketAdapter(databaseURI); const encryptedAdapter = new GridFSBucketAdapter( databaseURI, {}, - "89E4AFF1-DFE4-4603-9574-BFA16BB446FD" + '89E4AFF1-DFE4-4603-9574-BFA16BB446FD' ); - const fileName1 = "file1.txt"; - const data1 = "hello world"; - const fileName2 = "file2.txt"; - const data2 = "hello new world"; + const fileName1 = 'file1.txt'; + const data1 = 'hello world'; + const fileName2 = 'file2.txt'; + const data2 = 'hello new world'; //Store unecrypted files await unencryptedAdapter.createFile(fileName1, data1); const unencryptedResult1 = await unencryptedAdapter.getFileData(fileName1); - expect(unencryptedResult1.toString("utf8")).toBe(data1); + expect(unencryptedResult1.toString('utf8')).toBe(data1); await unencryptedAdapter.createFile(fileName2, data2); const unencryptedResult2 = await unencryptedAdapter.getFileData(fileName2); - expect(unencryptedResult2.toString("utf8")).toBe(data2); + expect(unencryptedResult2.toString('utf8')).toBe(data2); //Check if encrypted adapter can read data and make sure it's not the same as unEncrypted adapter const { rotated, notRotated } = await encryptedAdapter.rotateEncryptionKey(); @@ -88,18 +88,18 @@ describe_only_db("mongo")("GridFSBucket", () => { expect(notRotated.length).toEqual(0); let result = await encryptedAdapter.getFileData(fileName1); expect(result instanceof Buffer).toBe(true); - expect(result.toString("utf-8")).toEqual(data1); + expect(result.toString('utf-8')).toEqual(data1); const encryptedData1 = await unencryptedAdapter.getFileData(fileName1); - expect(encryptedData1.toString("utf-8")).not.toEqual(unencryptedResult1); + expect(encryptedData1.toString('utf-8')).not.toEqual(unencryptedResult1); result = await encryptedAdapter.getFileData(fileName2); expect(result instanceof Buffer).toBe(true); - expect(result.toString("utf-8")).toEqual(data2); + expect(result.toString('utf-8')).toEqual(data2); const encryptedData2 = await unencryptedAdapter.getFileData(fileName2); - expect(encryptedData2.toString("utf-8")).not.toEqual(unencryptedResult2); + expect(encryptedData2.toString('utf-8')).not.toEqual(unencryptedResult2); }); - it("should rotate key of all old encrypted GridFS files to encrypted files", async () => { - const oldEncryptionKey = "oldKeyThatILoved"; + it('should rotate key of all old encrypted GridFS files to encrypted files', async () => { + const oldEncryptionKey = 'oldKeyThatILoved'; const oldEncryptedAdapter = new GridFSBucketAdapter( databaseURI, {}, @@ -108,21 +108,21 @@ describe_only_db("mongo")("GridFSBucket", () => { const encryptedAdapter = new GridFSBucketAdapter( databaseURI, {}, - "newKeyThatILove" + 'newKeyThatILove' ); - const fileName1 = "file1.txt"; - const data1 = "hello world"; - const fileName2 = "file2.txt"; - const data2 = "hello new world"; + const fileName1 = 'file1.txt'; + const data1 = 'hello world'; + const fileName2 = 'file2.txt'; + const data2 = 'hello new world'; //Store unecrypted files await oldEncryptedAdapter.createFile(fileName1, data1); const oldEncryptedResult1 = await oldEncryptedAdapter.getFileData(fileName1); - expect(oldEncryptedResult1.toString("utf8")).toBe(data1); + expect(oldEncryptedResult1.toString('utf8')).toBe(data1); await oldEncryptedAdapter.createFile(fileName2, data2); const oldEncryptedResult2 = await oldEncryptedAdapter.getFileData(fileName2); - expect(oldEncryptedResult2.toString("utf8")).toBe(data2); + expect(oldEncryptedResult2.toString('utf8')).toBe(data2); //Check if encrypted adapter can read data and make sure it's not the same as unEncrypted adapter const { rotated, notRotated } = await encryptedAdapter.rotateEncryptionKey({ oldKey: oldEncryptionKey, @@ -141,7 +141,7 @@ describe_only_db("mongo")("GridFSBucket", () => { expect(notRotated.length).toEqual(0); let result = await encryptedAdapter.getFileData(fileName1); expect(result instanceof Buffer).toBe(true); - expect(result.toString("utf-8")).toEqual(data1); + expect(result.toString('utf-8')).toEqual(data1); let decryptionError1; let encryptedData1; try { @@ -149,11 +149,11 @@ describe_only_db("mongo")("GridFSBucket", () => { } catch (err) { decryptionError1 = err; } - expect(decryptionError1).toMatch("Error"); + expect(decryptionError1).toMatch('Error'); expect(encryptedData1).toBeUndefined(); result = await encryptedAdapter.getFileData(fileName2); expect(result instanceof Buffer).toBe(true); - expect(result.toString("utf-8")).toEqual(data2); + expect(result.toString('utf-8')).toEqual(data2); let decryptionError2; let encryptedData2; try { @@ -161,31 +161,31 @@ describe_only_db("mongo")("GridFSBucket", () => { } catch (err) { decryptionError2 = err; } - expect(decryptionError2).toMatch("Error"); + expect(decryptionError2).toMatch('Error'); expect(encryptedData2).toBeUndefined(); }); - it("should rotate key of all old encrypted GridFS files to unencrypted files", async () => { - const oldEncryptionKey = "oldKeyThatILoved"; + it('should rotate key of all old encrypted GridFS files to unencrypted files', async () => { + const oldEncryptionKey = 'oldKeyThatILoved'; const oldEncryptedAdapter = new GridFSBucketAdapter( databaseURI, {}, oldEncryptionKey ); const unEncryptedAdapter = new GridFSBucketAdapter(databaseURI); - const fileName1 = "file1.txt"; - const data1 = "hello world"; - const fileName2 = "file2.txt"; - const data2 = "hello new world"; + const fileName1 = 'file1.txt'; + const data1 = 'hello world'; + const fileName2 = 'file2.txt'; + const data2 = 'hello new world'; //Store unecrypted files await oldEncryptedAdapter.createFile(fileName1, data1); const oldEncryptedResult1 = await oldEncryptedAdapter.getFileData(fileName1); - expect(oldEncryptedResult1.toString("utf8")).toBe(data1); + expect(oldEncryptedResult1.toString('utf8')).toBe(data1); await oldEncryptedAdapter.createFile(fileName2, data2); const oldEncryptedResult2 = await oldEncryptedAdapter.getFileData(fileName2); - expect(oldEncryptedResult2.toString("utf8")).toBe(data2); + expect(oldEncryptedResult2.toString('utf8')).toBe(data2); //Check if unEncrypted adapter can read data and make sure it's not the same as oldEncrypted adapter const { rotated, notRotated } = await unEncryptedAdapter.rotateEncryptionKey({ @@ -205,7 +205,7 @@ describe_only_db("mongo")("GridFSBucket", () => { expect(notRotated.length).toEqual(0); let result = await unEncryptedAdapter.getFileData(fileName1); expect(result instanceof Buffer).toBe(true); - expect(result.toString("utf-8")).toEqual(data1); + expect(result.toString('utf-8')).toEqual(data1); let decryptionError1; let encryptedData1; try { @@ -213,11 +213,11 @@ describe_only_db("mongo")("GridFSBucket", () => { } catch (err) { decryptionError1 = err; } - expect(decryptionError1).toMatch("Error"); + expect(decryptionError1).toMatch('Error'); expect(encryptedData1).toBeUndefined(); result = await unEncryptedAdapter.getFileData(fileName2); expect(result instanceof Buffer).toBe(true); - expect(result.toString("utf-8")).toEqual(data2); + expect(result.toString('utf-8')).toEqual(data2); let decryptionError2; let encryptedData2; try { @@ -225,12 +225,12 @@ describe_only_db("mongo")("GridFSBucket", () => { } catch (err) { decryptionError2 = err; } - expect(decryptionError2).toMatch("Error"); + expect(decryptionError2).toMatch('Error'); expect(encryptedData2).toBeUndefined(); }); - it("should only encrypt specified fileNames", async () => { - const oldEncryptionKey = "oldKeyThatILoved"; + it('should only encrypt specified fileNames', async () => { + const oldEncryptionKey = 'oldKeyThatILoved'; const oldEncryptedAdapter = new GridFSBucketAdapter( databaseURI, {}, @@ -239,26 +239,26 @@ describe_only_db("mongo")("GridFSBucket", () => { const encryptedAdapter = new GridFSBucketAdapter( databaseURI, {}, - "newKeyThatILove" + 'newKeyThatILove' ); const unEncryptedAdapter = new GridFSBucketAdapter(databaseURI); - const fileName1 = "file1.txt"; - const data1 = "hello world"; - const fileName2 = "file2.txt"; - const data2 = "hello new world"; + const fileName1 = 'file1.txt'; + const data1 = 'hello world'; + const fileName2 = 'file2.txt'; + const data2 = 'hello new world'; //Store unecrypted files await oldEncryptedAdapter.createFile(fileName1, data1); const oldEncryptedResult1 = await oldEncryptedAdapter.getFileData(fileName1); - expect(oldEncryptedResult1.toString("utf8")).toBe(data1); + expect(oldEncryptedResult1.toString('utf8')).toBe(data1); await oldEncryptedAdapter.createFile(fileName2, data2); const oldEncryptedResult2 = await oldEncryptedAdapter.getFileData(fileName2); - expect(oldEncryptedResult2.toString("utf8")).toBe(data2); + expect(oldEncryptedResult2.toString('utf8')).toBe(data2); //Inject unecrypted file to see if causes an issue - const fileName3 = "file3.txt"; - const data3 = "hello past world"; - await unEncryptedAdapter.createFile(fileName3, data3, "text/utf8"); + const fileName3 = 'file3.txt'; + const data3 = 'hello past world'; + await unEncryptedAdapter.createFile(fileName3, data3, 'text/utf8'); //Check if encrypted adapter can read data and make sure it's not the same as unEncrypted adapter const { rotated, notRotated } = await encryptedAdapter.rotateEncryptionKey({ oldKey: oldEncryptionKey, @@ -283,7 +283,7 @@ describe_only_db("mongo")("GridFSBucket", () => { ).toEqual(0); let result = await encryptedAdapter.getFileData(fileName1); expect(result instanceof Buffer).toBe(true); - expect(result.toString("utf-8")).toEqual(data1); + expect(result.toString('utf-8')).toEqual(data1); let decryptionError1; let encryptedData1; try { @@ -291,11 +291,11 @@ describe_only_db("mongo")("GridFSBucket", () => { } catch (err) { decryptionError1 = err; } - expect(decryptionError1).toMatch("Error"); + expect(decryptionError1).toMatch('Error'); expect(encryptedData1).toBeUndefined(); result = await encryptedAdapter.getFileData(fileName2); expect(result instanceof Buffer).toBe(true); - expect(result.toString("utf-8")).toEqual(data2); + expect(result.toString('utf-8')).toEqual(data2); let decryptionError2; let encryptedData2; try { @@ -303,12 +303,12 @@ describe_only_db("mongo")("GridFSBucket", () => { } catch (err) { decryptionError2 = err; } - expect(decryptionError2).toMatch("Error"); + expect(decryptionError2).toMatch('Error'); expect(encryptedData2).toBeUndefined(); }); it("should return fileNames of those it can't encrypt with the new key", async () => { - const oldEncryptionKey = "oldKeyThatILoved"; + const oldEncryptionKey = 'oldKeyThatILoved'; const oldEncryptedAdapter = new GridFSBucketAdapter( databaseURI, {}, @@ -317,26 +317,26 @@ describe_only_db("mongo")("GridFSBucket", () => { const encryptedAdapter = new GridFSBucketAdapter( databaseURI, {}, - "newKeyThatILove" + 'newKeyThatILove' ); const unEncryptedAdapter = new GridFSBucketAdapter(databaseURI); - const fileName1 = "file1.txt"; - const data1 = "hello world"; - const fileName2 = "file2.txt"; - const data2 = "hello new world"; + const fileName1 = 'file1.txt'; + const data1 = 'hello world'; + const fileName2 = 'file2.txt'; + const data2 = 'hello new world'; //Store unecrypted files await oldEncryptedAdapter.createFile(fileName1, data1); const oldEncryptedResult1 = await oldEncryptedAdapter.getFileData(fileName1); - expect(oldEncryptedResult1.toString("utf8")).toBe(data1); + expect(oldEncryptedResult1.toString('utf8')).toBe(data1); await oldEncryptedAdapter.createFile(fileName2, data2); const oldEncryptedResult2 = await oldEncryptedAdapter.getFileData(fileName2); - expect(oldEncryptedResult2.toString("utf8")).toBe(data2); + expect(oldEncryptedResult2.toString('utf8')).toBe(data2); //Inject unecrypted file to see if causes an issue - const fileName3 = "file3.txt"; - const data3 = "hello past world"; - await unEncryptedAdapter.createFile(fileName3, data3, "text/utf8"); + const fileName3 = 'file3.txt'; + const data3 = 'hello past world'; + await unEncryptedAdapter.createFile(fileName3, data3, 'text/utf8'); //Check if encrypted adapter can read data and make sure it's not the same as unEncrypted adapter const { rotated, notRotated } = await encryptedAdapter.rotateEncryptionKey({ oldKey: oldEncryptionKey, @@ -360,7 +360,7 @@ describe_only_db("mongo")("GridFSBucket", () => { ).toEqual(1); let result = await encryptedAdapter.getFileData(fileName1); expect(result instanceof Buffer).toBe(true); - expect(result.toString("utf-8")).toEqual(data1); + expect(result.toString('utf-8')).toEqual(data1); let decryptionError1; let encryptedData1; try { @@ -368,11 +368,11 @@ describe_only_db("mongo")("GridFSBucket", () => { } catch (err) { decryptionError1 = err; } - expect(decryptionError1).toMatch("Error"); + expect(decryptionError1).toMatch('Error'); expect(encryptedData1).toBeUndefined(); result = await encryptedAdapter.getFileData(fileName2); expect(result instanceof Buffer).toBe(true); - expect(result.toString("utf-8")).toEqual(data2); + expect(result.toString('utf-8')).toEqual(data2); let decryptionError2; let encryptedData2; try { @@ -380,54 +380,54 @@ describe_only_db("mongo")("GridFSBucket", () => { } catch (err) { decryptionError2 = err; } - expect(decryptionError2).toMatch("Error"); + expect(decryptionError2).toMatch('Error'); expect(encryptedData2).toBeUndefined(); }); - it("should save metadata", async () => { + it('should save metadata', async () => { const gfsAdapter = new GridFSBucketAdapter(databaseURI); - const originalString = "abcdefghi"; - const metadata = { hello: "world" }; - await gfsAdapter.createFile("myFileName", originalString, null, { + const originalString = 'abcdefghi'; + const metadata = { hello: 'world' }; + await gfsAdapter.createFile('myFileName', originalString, null, { metadata, }); - const gfsResult = await gfsAdapter.getFileData("myFileName"); - expect(gfsResult.toString("utf8")).toBe(originalString); - let gfsMetadata = await gfsAdapter.getMetadata("myFileName"); + const gfsResult = await gfsAdapter.getFileData('myFileName'); + expect(gfsResult.toString('utf8')).toBe(originalString); + let gfsMetadata = await gfsAdapter.getMetadata('myFileName'); expect(gfsMetadata.metadata).toEqual(metadata); // Empty json for file not found - gfsMetadata = await gfsAdapter.getMetadata("myUnknownFile"); + gfsMetadata = await gfsAdapter.getMetadata('myUnknownFile'); expect(gfsMetadata).toEqual({}); }); - it("should save metadata with file", async () => { + it('should save metadata with file', async () => { const gfsAdapter = new GridFSBucketAdapter(databaseURI); await reconfigureServer({ filesAdapter: gfsAdapter }); - const str = "Hello World!"; + const str = 'Hello World!'; const data = []; for (let i = 0; i < str.length; i++) { data.push(str.charCodeAt(i)); } - const metadata = { foo: "bar" }; - const file = new Parse.File("hello.txt", data, "text/plain"); - file.addMetadata("foo", "bar"); + const metadata = { foo: 'bar' }; + const file = new Parse.File('hello.txt', data, 'text/plain'); + file.addMetadata('foo', 'bar'); await file.save(); let fileData = await gfsAdapter.getMetadata(file.name()); expect(fileData.metadata).toEqual(metadata); // Can only add metadata on create - file.addMetadata("hello", "world"); + file.addMetadata('hello', 'world'); await file.save(); fileData = await gfsAdapter.getMetadata(file.name()); expect(fileData.metadata).toEqual(metadata); const headers = { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; const response = await request({ - method: "GET", + method: 'GET', headers, url: `http://localhost:8378/1/files/test/metadata/${file.name()}`, }); @@ -435,53 +435,53 @@ describe_only_db("mongo")("GridFSBucket", () => { expect(fileData.metadata).toEqual(metadata); }); - it("should handle getMetadata error", async () => { + it('should handle getMetadata error', async () => { const gfsAdapter = new GridFSBucketAdapter(databaseURI); await reconfigureServer({ filesAdapter: gfsAdapter }); gfsAdapter.getMetadata = () => Promise.reject(); const headers = { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; const response = await request({ - method: "GET", + method: 'GET', headers, url: `http://localhost:8378/1/files/test/metadata/filename.txt`, }); expect(response.data).toEqual({}); }); - it("properly fetches a large file from GridFS", async () => { + it('properly fetches a large file from GridFS', async () => { const gfsAdapter = new GridFSBucketAdapter(databaseURI); const twoMegabytesFile = randomString(2048 * 1024); - await gfsAdapter.createFile("myFileName", twoMegabytesFile); - const gfsResult = await gfsAdapter.getFileData("myFileName"); - expect(gfsResult.toString("utf8")).toBe(twoMegabytesFile); + await gfsAdapter.createFile('myFileName', twoMegabytesFile); + const gfsResult = await gfsAdapter.getFileData('myFileName'); + expect(gfsResult.toString('utf8')).toBe(twoMegabytesFile); }); - it("properly deletes a file from GridFS", async () => { + it('properly deletes a file from GridFS', async () => { const gfsAdapter = new GridFSBucketAdapter(databaseURI); - await gfsAdapter.createFile("myFileName", "a simple file"); - await gfsAdapter.deleteFile("myFileName"); - await expectMissingFile(gfsAdapter, "myFileName"); + await gfsAdapter.createFile('myFileName', 'a simple file'); + await gfsAdapter.deleteFile('myFileName'); + await expectMissingFile(gfsAdapter, 'myFileName'); }, 1000000); - it("properly overrides files", async () => { + it('properly overrides files', async () => { const gfsAdapter = new GridFSBucketAdapter(databaseURI); - await gfsAdapter.createFile("myFileName", "a simple file"); - await gfsAdapter.createFile("myFileName", "an overrided simple file"); - const data = await gfsAdapter.getFileData("myFileName"); - expect(data.toString("utf8")).toBe("an overrided simple file"); + await gfsAdapter.createFile('myFileName', 'a simple file'); + await gfsAdapter.createFile('myFileName', 'an overrided simple file'); + const data = await gfsAdapter.getFileData('myFileName'); + expect(data.toString('utf8')).toBe('an overrided simple file'); const bucket = await gfsAdapter._getBucket(); - const documents = await bucket.find({ filename: "myFileName" }).toArray(); + const documents = await bucket.find({ filename: 'myFileName' }).toArray(); expect(documents.length).toBe(2); - await gfsAdapter.deleteFile("myFileName"); - await expectMissingFile(gfsAdapter, "myFileName"); + await gfsAdapter.deleteFile('myFileName'); + await expectMissingFile(gfsAdapter, 'myFileName'); }); - it("handleShutdown, close connection", async () => { - const databaseURI = "mongodb://localhost:27017/parse"; + it('handleShutdown, close connection', async () => { + const databaseURI = 'mongodb://localhost:27017/parse'; const gfsAdapter = new GridFSBucketAdapter(databaseURI); const db = await gfsAdapter._connect(); @@ -494,7 +494,7 @@ describe_only_db("mongo")("GridFSBucket", () => { expect(false).toBe(true); } catch (e) { expect(e.message).toEqual( - "Client must be connected before running operations" + 'Client must be connected before running operations' ); } }); diff --git a/spec/HTTPRequest.spec.js b/spec/HTTPRequest.spec.js index 943217105e..c3162a6178 100644 --- a/spec/HTTPRequest.spec.js +++ b/spec/HTTPRequest.spec.js @@ -1,42 +1,42 @@ -"use strict"; +'use strict'; -const httpRequest = require("../lib/request"), - HTTPResponse = require("../lib/request").HTTPResponse, - express = require("express"); +const httpRequest = require('../lib/request'), + HTTPResponse = require('../lib/request').HTTPResponse, + express = require('express'); const port = 13371; const httpRequestServer = `http://localhost:${port}`; function startServer(done) { const app = express(); - app.use(express.json({ type: "*/*" })); - app.get("/hello", function (req, res) { - res.json({ response: "OK" }); + app.use(express.json({ type: '*/*' })); + app.get('/hello', function (req, res) { + res.json({ response: 'OK' }); }); - app.get("/404", function (req, res) { + app.get('/404', function (req, res) { res.status(404); - res.send("NO"); + res.send('NO'); }); - app.get("/301", function (req, res) { + app.get('/301', function (req, res) { res.status(301); - res.location("/hello"); + res.location('/hello'); res.send(); }); - app.post("/echo", function (req, res) { + app.post('/echo', function (req, res) { res.json(req.body); }); - app.get("/qs", function (req, res) { + app.get('/qs', function (req, res) { res.json(req.query); }); return app.listen(13371, undefined, done); } -describe("httpRequest", () => { +describe('httpRequest', () => { let server; beforeEach(done => { if (!server) { @@ -50,7 +50,7 @@ describe("httpRequest", () => { server.close(done); }); - it("should do /hello", async () => { + it('should do /hello', async () => { const httpResponse = await httpRequest({ url: `${httpRequestServer}/hello`, }); @@ -58,10 +58,10 @@ describe("httpRequest", () => { expect(httpResponse.status).toBe(200); expect(httpResponse.buffer).toEqual(Buffer.from('{"response":"OK"}')); expect(httpResponse.text).toEqual('{"response":"OK"}'); - expect(httpResponse.data.response).toEqual("OK"); + expect(httpResponse.data.response).toEqual('OK'); }); - it("should do not follow redirects by default", async () => { + it('should do not follow redirects by default', async () => { const httpResponse = await httpRequest({ url: `${httpRequestServer}/301`, }); @@ -69,7 +69,7 @@ describe("httpRequest", () => { expect(httpResponse.status).toBe(301); }); - it("should follow redirects when set", async () => { + it('should follow redirects when set', async () => { const httpResponse = await httpRequest({ url: `${httpRequestServer}/301`, followRedirects: true, @@ -78,10 +78,10 @@ describe("httpRequest", () => { expect(httpResponse.status).toBe(200); expect(httpResponse.buffer).toEqual(Buffer.from('{"response":"OK"}')); expect(httpResponse.text).toEqual('{"response":"OK"}'); - expect(httpResponse.data.response).toEqual("OK"); + expect(httpResponse.data.response).toEqual('OK'); }); - it("should fail on 404", async () => { + it('should fail on 404', async () => { await expectAsync( httpRequest({ url: `${httpRequestServer}/404`, @@ -89,102 +89,102 @@ describe("httpRequest", () => { ).toBeRejectedWith( jasmine.objectContaining({ status: 404, - buffer: Buffer.from("NO"), - text: "NO", + buffer: Buffer.from('NO'), + text: 'NO', data: undefined, }) ); }); - it("should post on echo", async () => { + it('should post on echo', async () => { const httpResponse = await httpRequest({ - method: "POST", + method: 'POST', url: `${httpRequestServer}/echo`, body: { - foo: "bar", + foo: 'bar', }, headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, }); expect(httpResponse.status).toBe(200); - expect(httpResponse.data).toEqual({ foo: "bar" }); + expect(httpResponse.data).toEqual({ foo: 'bar' }); }); - it("should encode a query string body by default", () => { + it('should encode a query string body by default', () => { const options = { - body: { foo: "bar" }, + body: { foo: 'bar' }, }; const result = httpRequest.encodeBody(options); - expect(result.body).toEqual("foo=bar"); - expect(result.headers["Content-Type"]).toEqual( - "application/x-www-form-urlencoded" + expect(result.body).toEqual('foo=bar'); + expect(result.headers['Content-Type']).toEqual( + 'application/x-www-form-urlencoded' ); }); - it("should encode a JSON body", () => { + it('should encode a JSON body', () => { const options = { - body: { foo: "bar" }, - headers: { "Content-Type": "application/json" }, + body: { foo: 'bar' }, + headers: { 'Content-Type': 'application/json' }, }; const result = httpRequest.encodeBody(options); expect(result.body).toEqual('{"foo":"bar"}'); }); - it("should encode a www-form body", () => { + it('should encode a www-form body', () => { const options = { - body: { foo: "bar", bar: "baz" }, - headers: { "cOntent-tYpe": "application/x-www-form-urlencoded" }, + body: { foo: 'bar', bar: 'baz' }, + headers: { 'cOntent-tYpe': 'application/x-www-form-urlencoded' }, }; const result = httpRequest.encodeBody(options); - expect(result.body).toEqual("foo=bar&bar=baz"); + expect(result.body).toEqual('foo=bar&bar=baz'); }); - it("should not encode a wrong content type", () => { + it('should not encode a wrong content type', () => { const options = { - body: { foo: "bar", bar: "baz" }, - headers: { "cOntent-tYpe": "mime/jpeg" }, + body: { foo: 'bar', bar: 'baz' }, + headers: { 'cOntent-tYpe': 'mime/jpeg' }, }; const result = httpRequest.encodeBody(options); - expect(result.body).toEqual({ foo: "bar", bar: "baz" }); + expect(result.body).toEqual({ foo: 'bar', bar: 'baz' }); }); - it("should fail gracefully", async () => { + it('should fail gracefully', async () => { await expectAsync( httpRequest({ - url: "http://not a good url", + url: 'http://not a good url', }) ).toBeRejected(); }); - it("should params object to query string", async () => { + it('should params object to query string', async () => { const httpResponse = await httpRequest({ url: `${httpRequestServer}/qs`, params: { - foo: "bar", + foo: 'bar', }, }); expect(httpResponse.status).toBe(200); - expect(httpResponse.data).toEqual({ foo: "bar" }); + expect(httpResponse.data).toEqual({ foo: 'bar' }); }); - it("should params string to query string", async () => { + it('should params string to query string', async () => { const httpResponse = await httpRequest({ url: `${httpRequestServer}/qs`, - params: "foo=bar&foo2=bar2", + params: 'foo=bar&foo2=bar2', }); expect(httpResponse.status).toBe(200); - expect(httpResponse.data).toEqual({ foo: "bar", foo2: "bar2" }); + expect(httpResponse.data).toEqual({ foo: 'bar', foo2: 'bar2' }); }); - it("should not crash with undefined body", () => { + it('should not crash with undefined body', () => { const httpResponse = new HTTPResponse({}); expect(httpResponse.body).toBeUndefined(); expect(httpResponse.data).toBeUndefined(); @@ -192,58 +192,58 @@ describe("httpRequest", () => { expect(httpResponse.buffer).toBeUndefined(); }); - it("serialized httpResponse correctly with body string", () => { - const httpResponse = new HTTPResponse({}, "hello"); - expect(httpResponse.text).toBe("hello"); + it('serialized httpResponse correctly with body string', () => { + const httpResponse = new HTTPResponse({}, 'hello'); + expect(httpResponse.text).toBe('hello'); expect(httpResponse.data).toBe(undefined); - expect(httpResponse.body).toBe("hello"); + expect(httpResponse.body).toBe('hello'); const serialized = JSON.stringify(httpResponse); const result = JSON.parse(serialized); - expect(result.text).toBe("hello"); + expect(result.text).toBe('hello'); expect(result.data).toBe(undefined); expect(result.body).toBe(undefined); }); - it("serialized httpResponse correctly with body object", () => { - const httpResponse = new HTTPResponse({}, { foo: "bar" }); + it('serialized httpResponse correctly with body object', () => { + const httpResponse = new HTTPResponse({}, { foo: 'bar' }); Parse._encode(httpResponse); const serialized = JSON.stringify(httpResponse); const result = JSON.parse(serialized); expect(httpResponse.text).toEqual('{"foo":"bar"}'); - expect(httpResponse.data).toEqual({ foo: "bar" }); - expect(httpResponse.body).toEqual({ foo: "bar" }); + expect(httpResponse.data).toEqual({ foo: 'bar' }); + expect(httpResponse.body).toEqual({ foo: 'bar' }); expect(result.text).toEqual('{"foo":"bar"}'); - expect(result.data).toEqual({ foo: "bar" }); + expect(result.data).toEqual({ foo: 'bar' }); expect(result.body).toEqual(undefined); }); - it("serialized httpResponse correctly with body buffer string", () => { - const httpResponse = new HTTPResponse({}, Buffer.from("hello")); - expect(httpResponse.text).toBe("hello"); + it('serialized httpResponse correctly with body buffer string', () => { + const httpResponse = new HTTPResponse({}, Buffer.from('hello')); + expect(httpResponse.text).toBe('hello'); expect(httpResponse.data).toBe(undefined); const serialized = JSON.stringify(httpResponse); const result = JSON.parse(serialized); - expect(result.text).toBe("hello"); + expect(result.text).toBe('hello'); expect(result.data).toBe(undefined); }); - it("serialized httpResponse correctly with body buffer JSON Object", () => { + it('serialized httpResponse correctly with body buffer JSON Object', () => { const json = '{"foo":"bar"}'; const httpResponse = new HTTPResponse({}, Buffer.from(json)); const serialized = JSON.stringify(httpResponse); const result = JSON.parse(serialized); expect(result.text).toEqual('{"foo":"bar"}'); - expect(result.data).toEqual({ foo: "bar" }); + expect(result.data).toEqual({ foo: 'bar' }); }); - it("serialized httpResponse with Parse._encode should be allright", () => { + it('serialized httpResponse with Parse._encode should be allright', () => { const json = '{"foo":"bar"}'; const httpResponse = new HTTPResponse({}, Buffer.from(json)); const encoded = Parse._encode(httpResponse); @@ -252,13 +252,13 @@ describe("httpRequest", () => { foundBody = false; for (const key in encoded) { - if (key === "data") { + if (key === 'data') { foundData = true; } - if (key === "text") { + if (key === 'text') { foundText = true; } - if (key === "body") { + if (key === 'body') { foundBody = true; } } diff --git a/spec/Idempotency.spec.js b/spec/Idempotency.spec.js index 8dd52108fc..49b7b14ea9 100644 --- a/spec/Idempotency.spec.js +++ b/spec/Idempotency.spec.js @@ -1,12 +1,12 @@ -"use strict"; -const Config = require("../lib/Config"); -const Definitions = require("../lib/Options/Definitions"); -const request = require("../lib/request"); -const rest = require("../lib/rest"); -const auth = require("../lib/Auth"); -const uuid = require("uuid"); +'use strict'; +const Config = require('../lib/Config'); +const Definitions = require('../lib/Options/Definitions'); +const request = require('../lib/request'); +const rest = require('../lib/rest'); +const auth = require('../lib/Auth'); +const uuid = require('uuid'); -describe("Idempotency", () => { +describe('Idempotency', () => { // Parameters /** Enable TTL expiration simulated by removing entry instead of waiting for MongoDB TTL monitor which runs only every 60s, so it can take up to 119s until entry removal - ain't nobody got time for that */ @@ -20,14 +20,14 @@ describe("Idempotency", () => { const res = await rest.find( config, auth.master(config), - "_Idempotency", + '_Idempotency', { reqId: reqId }, { limit: 1 } ); await rest.del( config, auth.master(config), - "_Idempotency", + '_Idempotency', res.results[0].objectId ); } @@ -46,11 +46,11 @@ describe("Idempotency", () => { } await setup({ paths: [ - "functions/.*", - "jobs/.*", - "classes/.*", - "users", - "installations", + 'functions/.*', + 'jobs/.*', + 'classes/.*', + 'users', + 'installations', ], ttl: ttl, }); @@ -62,51 +62,51 @@ describe("Idempotency", () => { }); // Tests - it_id("e25955fd-92eb-4b22-b8b7-38980e5cb223")(it)( - "should enforce idempotency for cloud code function", + it_id('e25955fd-92eb-4b22-b8b7-38980e5cb223')(it)( + 'should enforce idempotency for cloud code function', async () => { let counter = 0; - Parse.Cloud.define("myFunction", () => { + Parse.Cloud.define('myFunction', () => { counter++; }); const params = { - method: "POST", - url: "http://localhost:8378/1/functions/myFunction", + method: 'POST', + url: 'http://localhost:8378/1/functions/myFunction', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Master-Key": Parse.masterKey, - "X-Parse-Request-Id": "abc-123", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': Parse.masterKey, + 'X-Parse-Request-Id': 'abc-123', }, }; expect(Config.get(Parse.applicationId).idempotencyOptions.ttl).toBe(ttl); await request(params); await request(params).then(fail, e => { expect(e.status).toEqual(400); - expect(e.data.error).toEqual("Duplicate request"); + expect(e.data.error).toEqual('Duplicate request'); }); expect(counter).toBe(1); } ); - it_id("be2fbe16-8178-485e-9a12-6fb541096480")(it)( - "should delete request entry after TTL", + it_id('be2fbe16-8178-485e-9a12-6fb541096480')(it)( + 'should delete request entry after TTL', async () => { let counter = 0; - Parse.Cloud.define("myFunction", () => { + Parse.Cloud.define('myFunction', () => { counter++; }); const params = { - method: "POST", - url: "http://localhost:8378/1/functions/myFunction", + method: 'POST', + url: 'http://localhost:8378/1/functions/myFunction', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Master-Key": Parse.masterKey, - "X-Parse-Request-Id": "abc-123", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': Parse.masterKey, + 'X-Parse-Request-Id': 'abc-123', }, }; await expectAsync(request(params)).toBeResolved(); if (SIMULATE_TTL) { - await deleteRequestEntry("abc-123"); + await deleteRequestEntry('abc-123'); } else { await new Promise(resolve => setTimeout(resolve, maxTimeOut)); } @@ -115,155 +115,155 @@ describe("Idempotency", () => { } ); - it_only_db("postgres")( - "should delete request entry when postgress ttl function is called", + it_only_db('postgres')( + 'should delete request entry when postgress ttl function is called', async () => { const client = Config.get(Parse.applicationId).database.adapter._client; let counter = 0; - Parse.Cloud.define("myFunction", () => { + Parse.Cloud.define('myFunction', () => { counter++; }); const params = { - method: "POST", - url: "http://localhost:8378/1/functions/myFunction", + method: 'POST', + url: 'http://localhost:8378/1/functions/myFunction', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Master-Key": Parse.masterKey, - "X-Parse-Request-Id": "abc-123", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': Parse.masterKey, + 'X-Parse-Request-Id': 'abc-123', }, }; await expectAsync(request(params)).toBeResolved(); await expectAsync(request(params)).toBeRejected(); await new Promise(resolve => setTimeout(resolve, maxTimeOut)); - await client.one("SELECT idempotency_delete_expired_records()"); + await client.one('SELECT idempotency_delete_expired_records()'); await expectAsync(request(params)).toBeResolved(); expect(counter).toBe(2); } ); - it_id("e976d0cc-a57f-45d4-9472-b9b052db6490")(it)( - "should enforce idempotency for cloud code jobs", + it_id('e976d0cc-a57f-45d4-9472-b9b052db6490')(it)( + 'should enforce idempotency for cloud code jobs', async () => { let counter = 0; - Parse.Cloud.job("myJob", () => { + Parse.Cloud.job('myJob', () => { counter++; }); const params = { - method: "POST", - url: "http://localhost:8378/1/jobs/myJob", + method: 'POST', + url: 'http://localhost:8378/1/jobs/myJob', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Master-Key": Parse.masterKey, - "X-Parse-Request-Id": "abc-123", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': Parse.masterKey, + 'X-Parse-Request-Id': 'abc-123', }, }; await expectAsync(request(params)).toBeResolved(); await request(params).then(fail, e => { expect(e.status).toEqual(400); - expect(e.data.error).toEqual("Duplicate request"); + expect(e.data.error).toEqual('Duplicate request'); }); expect(counter).toBe(1); } ); - it_id("7c84a3d4-e1b6-4a0d-99f1-af3cf1a6b3d8")(it)( - "should enforce idempotency for class object creation", + it_id('7c84a3d4-e1b6-4a0d-99f1-af3cf1a6b3d8')(it)( + 'should enforce idempotency for class object creation', async () => { let counter = 0; - Parse.Cloud.afterSave("MyClass", () => { + Parse.Cloud.afterSave('MyClass', () => { counter++; }); const params = { - method: "POST", - url: "http://localhost:8378/1/classes/MyClass", + method: 'POST', + url: 'http://localhost:8378/1/classes/MyClass', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Master-Key": Parse.masterKey, - "X-Parse-Request-Id": "abc-123", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': Parse.masterKey, + 'X-Parse-Request-Id': 'abc-123', }, }; await expectAsync(request(params)).toBeResolved(); await request(params).then(fail, e => { expect(e.status).toEqual(400); - expect(e.data.error).toEqual("Duplicate request"); + expect(e.data.error).toEqual('Duplicate request'); }); expect(counter).toBe(1); } ); - it_id("a030f2dd-5d21-46ac-b53d-9d714f35d72a")(it)( - "should enforce idempotency for user object creation", + it_id('a030f2dd-5d21-46ac-b53d-9d714f35d72a')(it)( + 'should enforce idempotency for user object creation', async () => { let counter = 0; - Parse.Cloud.afterSave("_User", () => { + Parse.Cloud.afterSave('_User', () => { counter++; }); const params = { - method: "POST", - url: "http://localhost:8378/1/users", + method: 'POST', + url: 'http://localhost:8378/1/users', body: { - username: "user", - password: "pass", + username: 'user', + password: 'pass', }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Master-Key": Parse.masterKey, - "X-Parse-Request-Id": "abc-123", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': Parse.masterKey, + 'X-Parse-Request-Id': 'abc-123', }, }; await expectAsync(request(params)).toBeResolved(); await request(params).then(fail, e => { expect(e.status).toEqual(400); - expect(e.data.error).toEqual("Duplicate request"); + expect(e.data.error).toEqual('Duplicate request'); }); expect(counter).toBe(1); } ); - it_id("064c469b-091c-4ba9-9043-be461f26a3eb")(it)( - "should enforce idempotency for installation object creation", + it_id('064c469b-091c-4ba9-9043-be461f26a3eb')(it)( + 'should enforce idempotency for installation object creation', async () => { let counter = 0; - Parse.Cloud.afterSave("_Installation", () => { + Parse.Cloud.afterSave('_Installation', () => { counter++; }); const params = { - method: "POST", - url: "http://localhost:8378/1/installations", + method: 'POST', + url: 'http://localhost:8378/1/installations', body: { - installationId: "1", - deviceType: "ios", + installationId: '1', + deviceType: 'ios', }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Master-Key": Parse.masterKey, - "X-Parse-Request-Id": "abc-123", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': Parse.masterKey, + 'X-Parse-Request-Id': 'abc-123', }, }; await expectAsync(request(params)).toBeResolved(); await request(params).then(fail, e => { expect(e.status).toEqual(400); - expect(e.data.error).toEqual("Duplicate request"); + expect(e.data.error).toEqual('Duplicate request'); }); expect(counter).toBe(1); } ); - it_id("f11670b6-fa9c-4f21-a268-ae4b6bbff7fd")(it)( - "should not interfere with calls of different request ID", + it_id('f11670b6-fa9c-4f21-a268-ae4b6bbff7fd')(it)( + 'should not interfere with calls of different request ID', async () => { let counter = 0; - Parse.Cloud.afterSave("MyClass", () => { + Parse.Cloud.afterSave('MyClass', () => { counter++; }); const promises = [...Array(100).keys()].map(() => { const params = { - method: "POST", - url: "http://localhost:8378/1/classes/MyClass", + method: 'POST', + url: 'http://localhost:8378/1/classes/MyClass', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Master-Key": Parse.masterKey, - "X-Parse-Request-Id": uuid.v4(), + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': Parse.masterKey, + 'X-Parse-Request-Id': uuid.v4(), }, }; return request(params); @@ -273,30 +273,30 @@ describe("Idempotency", () => { } ); - it_id("0ecd2cd2-dafb-4a2b-bb2b-9ad4c9aca777")(it)( - "should re-throw any other error unchanged when writing request entry fails for any other reason", + it_id('0ecd2cd2-dafb-4a2b-bb2b-9ad4c9aca777')(it)( + 'should re-throw any other error unchanged when writing request entry fails for any other reason', async () => { - spyOn(rest, "create").and.rejectWith( - new Parse.Error(0, "some other error") + spyOn(rest, 'create').and.rejectWith( + new Parse.Error(0, 'some other error') ); - Parse.Cloud.define("myFunction", () => {}); + Parse.Cloud.define('myFunction', () => {}); const params = { - method: "POST", - url: "http://localhost:8378/1/functions/myFunction", + method: 'POST', + url: 'http://localhost:8378/1/functions/myFunction', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Master-Key": Parse.masterKey, - "X-Parse-Request-Id": "abc-123", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': Parse.masterKey, + 'X-Parse-Request-Id': 'abc-123', }, }; await request(params).then(fail, e => { expect(e.status).toEqual(400); - expect(e.data.error).toEqual("some other error"); + expect(e.data.error).toEqual('some other error'); }); } ); - it("should use default configuration when none is set", async () => { + it('should use default configuration when none is set', async () => { await setup({}); expect(Config.get(Parse.applicationId).idempotencyOptions.ttl).toBe( Definitions.IdempotencyOptions.ttl.default @@ -306,9 +306,9 @@ describe("Idempotency", () => { ); }); - it("should throw on invalid configuration", async () => { + it('should throw on invalid configuration', async () => { await expectAsync(setup({ paths: 1 })).toBeRejected(); - await expectAsync(setup({ ttl: "a" })).toBeRejected(); + await expectAsync(setup({ ttl: 'a' })).toBeRejected(); await expectAsync(setup({ ttl: 0 })).toBeRejected(); await expectAsync(setup({ ttl: -1 })).toBeRejected(); }); diff --git a/spec/InMemoryCache.spec.js b/spec/InMemoryCache.spec.js index a63f388be2..4a474b7fa2 100644 --- a/spec/InMemoryCache.spec.js +++ b/spec/InMemoryCache.spec.js @@ -1,16 +1,16 @@ -const InMemoryCache = require("../lib/Adapters/Cache/InMemoryCache").default; +const InMemoryCache = require('../lib/Adapters/Cache/InMemoryCache').default; -describe("InMemoryCache", function () { +describe('InMemoryCache', function () { const BASE_TTL = { ttl: 100, }; const NO_EXPIRE_TTL = { ttl: NaN, }; - const KEY = "hello"; - const KEY_2 = KEY + "_2"; + const KEY = 'hello'; + const KEY_2 = KEY + '_2'; - const VALUE = "world"; + const VALUE = 'world'; function wait(sleep) { return new Promise(function (resolve) { @@ -18,7 +18,7 @@ describe("InMemoryCache", function () { }); } - it("should destroy a expire items in the cache", done => { + it('should destroy a expire items in the cache', done => { const cache = new InMemoryCache(BASE_TTL); cache.put(KEY, VALUE); @@ -33,7 +33,7 @@ describe("InMemoryCache", function () { }); }); - it("should delete items", done => { + it('should delete items', done => { const cache = new InMemoryCache(NO_EXPIRE_TTL); cache.put(KEY, VALUE); cache.put(KEY_2, VALUE); @@ -50,7 +50,7 @@ describe("InMemoryCache", function () { done(); }); - it("should clear all items", done => { + it('should clear all items', done => { const cache = new InMemoryCache(NO_EXPIRE_TTL); cache.put(KEY, VALUE); cache.put(KEY_2, VALUE); @@ -64,7 +64,7 @@ describe("InMemoryCache", function () { done(); }); - it("should deafult TTL to 5 seconds", () => { + it('should deafult TTL to 5 seconds', () => { const cache = new InMemoryCache({}); expect(cache.ttl).toEqual(5 * 1000); }); diff --git a/spec/InMemoryCacheAdapter.spec.js b/spec/InMemoryCacheAdapter.spec.js index 2ed888ea3a..ebbf957f9f 100644 --- a/spec/InMemoryCacheAdapter.spec.js +++ b/spec/InMemoryCacheAdapter.spec.js @@ -1,9 +1,9 @@ const InMemoryCacheAdapter = - require("../lib/Adapters/Cache/InMemoryCacheAdapter").default; + require('../lib/Adapters/Cache/InMemoryCacheAdapter').default; -describe("InMemoryCacheAdapter", function () { - const KEY = "hello"; - const VALUE = "world"; +describe('InMemoryCacheAdapter', function () { + const KEY = 'hello'; + const VALUE = 'world'; function wait(sleep) { return new Promise(function (resolve) { @@ -11,7 +11,7 @@ describe("InMemoryCacheAdapter", function () { }); } - it("should expose promisifyed methods", done => { + it('should expose promisifyed methods', done => { const cache = new InMemoryCacheAdapter({ ttl: NaN, }); @@ -27,7 +27,7 @@ describe("InMemoryCacheAdapter", function () { }); }); - it("should get/set/clear", done => { + it('should get/set/clear', done => { const cache = new InMemoryCacheAdapter({ ttl: NaN, }); @@ -42,7 +42,7 @@ describe("InMemoryCacheAdapter", function () { .then(done); }); - it("should expire after ttl", done => { + it('should expire after ttl', done => { const cache = new InMemoryCacheAdapter({ ttl: 10, }); diff --git a/spec/InstallationsRouter.spec.js b/spec/InstallationsRouter.spec.js index 1792c9d513..4df5f89f72 100644 --- a/spec/InstallationsRouter.spec.js +++ b/spec/InstallationsRouter.spec.js @@ -1,26 +1,26 @@ -const auth = require("../lib/Auth"); -const Config = require("../lib/Config"); -const rest = require("../lib/rest"); +const auth = require('../lib/Auth'); +const Config = require('../lib/Config'); +const rest = require('../lib/rest'); const InstallationsRouter = - require("../lib/Routers/InstallationsRouter").InstallationsRouter; + require('../lib/Routers/InstallationsRouter').InstallationsRouter; -describe("InstallationsRouter", () => { - it("uses find condition from request.body", done => { - const config = Config.get("test"); +describe('InstallationsRouter', () => { + it('uses find condition from request.body', done => { + const config = Config.get('test'); const androidDeviceRequest = { - installationId: "12345678-abcd-abcd-abcd-123456789abc", - deviceType: "android", + installationId: '12345678-abcd-abcd-abcd-123456789abc', + deviceType: 'android', }; const iosDeviceRequest = { - installationId: "12345678-abcd-abcd-abcd-123456789abd", - deviceType: "ios", + installationId: '12345678-abcd-abcd-abcd-123456789abd', + deviceType: 'ios', }; const request = { config: config, auth: auth.master(config), body: { where: { - deviceType: "android", + deviceType: 'android', }, }, query: {}, @@ -32,14 +32,14 @@ describe("InstallationsRouter", () => { .create( config, auth.nobody(config), - "_Installation", + '_Installation', androidDeviceRequest ) .then(() => { return rest.create( config, auth.nobody(config), - "_Installation", + '_Installation', iosDeviceRequest ); }) @@ -57,15 +57,15 @@ describe("InstallationsRouter", () => { }); }); - it("uses find condition from request.query", done => { - const config = Config.get("test"); + it('uses find condition from request.query', done => { + const config = Config.get('test'); const androidDeviceRequest = { - installationId: "12345678-abcd-abcd-abcd-123456789abc", - deviceType: "android", + installationId: '12345678-abcd-abcd-abcd-123456789abc', + deviceType: 'android', }; const iosDeviceRequest = { - installationId: "12345678-abcd-abcd-abcd-123456789abd", - deviceType: "ios", + installationId: '12345678-abcd-abcd-abcd-123456789abd', + deviceType: 'ios', }; const request = { config: config, @@ -73,7 +73,7 @@ describe("InstallationsRouter", () => { body: {}, query: { where: { - deviceType: "android", + deviceType: 'android', }, }, info: {}, @@ -84,14 +84,14 @@ describe("InstallationsRouter", () => { .create( config, auth.nobody(config), - "_Installation", + '_Installation', androidDeviceRequest ) .then(() => { return rest.create( config, auth.nobody(config), - "_Installation", + '_Installation', iosDeviceRequest ); }) @@ -109,15 +109,15 @@ describe("InstallationsRouter", () => { }); }); - it("query installations with limit = 0", done => { - const config = Config.get("test"); + it('query installations with limit = 0', done => { + const config = Config.get('test'); const androidDeviceRequest = { - installationId: "12345678-abcd-abcd-abcd-123456789abc", - deviceType: "android", + installationId: '12345678-abcd-abcd-abcd-123456789abc', + deviceType: 'android', }; const iosDeviceRequest = { - installationId: "12345678-abcd-abcd-abcd-123456789abd", - deviceType: "ios", + installationId: '12345678-abcd-abcd-abcd-123456789abd', + deviceType: 'ios', }; const request = { config: config, @@ -129,20 +129,20 @@ describe("InstallationsRouter", () => { info: {}, }; - Config.get("test"); + Config.get('test'); const router = new InstallationsRouter(); rest .create( config, auth.nobody(config), - "_Installation", + '_Installation', androidDeviceRequest ) .then(() => { return rest.create( config, auth.nobody(config), - "_Installation", + '_Installation', iosDeviceRequest ); }) @@ -160,15 +160,15 @@ describe("InstallationsRouter", () => { }); }); - it_exclude_dbs(["postgres"])("query installations with count = 1", done => { - const config = Config.get("test"); + it_exclude_dbs(['postgres'])('query installations with count = 1', done => { + const config = Config.get('test'); const androidDeviceRequest = { - installationId: "12345678-abcd-abcd-abcd-123456789abc", - deviceType: "android", + installationId: '12345678-abcd-abcd-abcd-123456789abc', + deviceType: 'android', }; const iosDeviceRequest = { - installationId: "12345678-abcd-abcd-abcd-123456789abd", - deviceType: "ios", + installationId: '12345678-abcd-abcd-abcd-123456789abd', + deviceType: 'ios', }; const request = { config: config, @@ -185,14 +185,14 @@ describe("InstallationsRouter", () => { .create( config, auth.nobody(config), - "_Installation", + '_Installation', androidDeviceRequest ) .then(() => rest.create( config, auth.nobody(config), - "_Installation", + '_Installation', iosDeviceRequest ) ) @@ -209,15 +209,15 @@ describe("InstallationsRouter", () => { }); }); - it_only_db("postgres")("query installations with count = 1", async () => { - const config = Config.get("test"); + it_only_db('postgres')('query installations with count = 1', async () => { + const config = Config.get('test'); const androidDeviceRequest = { - installationId: "12345678-abcd-abcd-abcd-123456789abc", - deviceType: "android", + installationId: '12345678-abcd-abcd-abcd-123456789abc', + deviceType: 'android', }; const iosDeviceRequest = { - installationId: "12345678-abcd-abcd-abcd-123456789abd", - deviceType: "ios", + installationId: '12345678-abcd-abcd-abcd-123456789abd', + deviceType: 'ios', }; const request = { config: config, @@ -233,13 +233,13 @@ describe("InstallationsRouter", () => { await rest.create( config, auth.nobody(config), - "_Installation", + '_Installation', androidDeviceRequest ); await rest.create( config, auth.nobody(config), - "_Installation", + '_Installation', iosDeviceRequest ); let res = await router.handleFind(request); @@ -248,7 +248,7 @@ describe("InstallationsRouter", () => { expect(response.count).toEqual(0); // estimate count is zero const pgAdapter = config.database.adapter; - await pgAdapter.updateEstimatedCount("_Installation"); + await pgAdapter.updateEstimatedCount('_Installation'); res = await router.handleFind(request); response = res.response; @@ -256,17 +256,17 @@ describe("InstallationsRouter", () => { expect(response.count).toEqual(2); }); - it_exclude_dbs(["postgres"])( - "query installations with limit = 0 and count = 1", + it_exclude_dbs(['postgres'])( + 'query installations with limit = 0 and count = 1', done => { - const config = Config.get("test"); + const config = Config.get('test'); const androidDeviceRequest = { - installationId: "12345678-abcd-abcd-abcd-123456789abc", - deviceType: "android", + installationId: '12345678-abcd-abcd-abcd-123456789abc', + deviceType: 'android', }; const iosDeviceRequest = { - installationId: "12345678-abcd-abcd-abcd-123456789abd", - deviceType: "ios", + installationId: '12345678-abcd-abcd-abcd-123456789abd', + deviceType: 'ios', }; const request = { config: config, @@ -284,14 +284,14 @@ describe("InstallationsRouter", () => { .create( config, auth.nobody(config), - "_Installation", + '_Installation', androidDeviceRequest ) .then(() => { return rest.create( config, auth.nobody(config), - "_Installation", + '_Installation', iosDeviceRequest ); }) diff --git a/spec/JobSchedule.spec.js b/spec/JobSchedule.spec.js index c722ed4e3a..feb37ec6a2 100644 --- a/spec/JobSchedule.spec.js +++ b/spec/JobSchedule.spec.js @@ -1,15 +1,15 @@ -const request = require("../lib/request"); +const request = require('../lib/request'); const defaultHeaders = { - "X-Parse-Application-Id": "test", - "X-Parse-Rest-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Rest-API-Key': 'rest', + 'Content-Type': 'application/json', }; const masterKeyHeaders = { - "X-Parse-Application-Id": "test", - "X-Parse-Rest-API-Key": "rest", - "X-Parse-Master-Key": "test", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Rest-API-Key': 'rest', + 'X-Parse-Master-Key': 'test', + 'Content-Type': 'application/json', }; const defaultOptions = { headers: defaultHeaders, @@ -20,11 +20,11 @@ const masterKeyOptions = { json: true, }; -describe("JobSchedule", () => { - it("should create _JobSchedule with masterKey", done => { - const jobSchedule = new Parse.Object("_JobSchedule"); +describe('JobSchedule', () => { + it('should create _JobSchedule with masterKey', done => { + const jobSchedule = new Parse.Object('_JobSchedule'); jobSchedule.set({ - jobName: "MY Cool Job", + jobName: 'MY Cool Job', }); jobSchedule .save(null, { useMasterKey: true }) @@ -34,10 +34,10 @@ describe("JobSchedule", () => { .catch(done.fail); }); - it("should fail creating _JobSchedule without masterKey", done => { - const jobSchedule = new Parse.Object("_JobSchedule"); + it('should fail creating _JobSchedule without masterKey', done => { + const jobSchedule = new Parse.Object('_JobSchedule'); jobSchedule.set({ - jobName: "SomeJob", + jobName: 'SomeJob', }); jobSchedule .save(null) @@ -45,59 +45,59 @@ describe("JobSchedule", () => { .catch(() => done()); }); - it("should reject access when not using masterKey (/jobs)", done => { + it('should reject access when not using masterKey (/jobs)', done => { request( Object.assign( - { url: Parse.serverURL + "/cloud_code/jobs" }, + { url: Parse.serverURL + '/cloud_code/jobs' }, defaultOptions ) ).then(done.fail, () => done()); }); - it("should reject access when not using masterKey (/jobs/data)", done => { + it('should reject access when not using masterKey (/jobs/data)', done => { request( Object.assign( - { url: Parse.serverURL + "/cloud_code/jobs/data" }, + { url: Parse.serverURL + '/cloud_code/jobs/data' }, defaultOptions ) ).then(done.fail, () => done()); }); - it("should reject access when not using masterKey (PUT /jobs/id)", done => { + it('should reject access when not using masterKey (PUT /jobs/id)', done => { request( Object.assign( - { method: "PUT", url: Parse.serverURL + "/cloud_code/jobs/jobId" }, + { method: 'PUT', url: Parse.serverURL + '/cloud_code/jobs/jobId' }, defaultOptions ) ).then(done.fail, () => done()); }); - it("should reject access when not using masterKey (DELETE /jobs/id)", done => { + it('should reject access when not using masterKey (DELETE /jobs/id)', done => { request( Object.assign( - { method: "DELETE", url: Parse.serverURL + "/cloud_code/jobs/jobId" }, + { method: 'DELETE', url: Parse.serverURL + '/cloud_code/jobs/jobId' }, defaultOptions ) ).then(done.fail, () => done()); }); - it("should allow access when using masterKey (GET /jobs)", done => { + it('should allow access when using masterKey (GET /jobs)', done => { request( Object.assign( - { url: Parse.serverURL + "/cloud_code/jobs" }, + { url: Parse.serverURL + '/cloud_code/jobs' }, masterKeyOptions ) ).then(done, done.fail); }); - it("should create a job schedule", done => { - Parse.Cloud.job("job", () => {}); + it('should create a job schedule', done => { + Parse.Cloud.job('job', () => {}); const options = Object.assign({}, masterKeyOptions, { - method: "POST", - url: Parse.serverURL + "/cloud_code/jobs", + method: 'POST', + url: Parse.serverURL + '/cloud_code/jobs', body: { job_schedule: { - jobName: "job", + jobName: 'job', }, }, }); @@ -108,7 +108,7 @@ describe("JobSchedule", () => { .then(() => { return request( Object.assign( - { url: Parse.serverURL + "/cloud_code/jobs" }, + { url: Parse.serverURL + '/cloud_code/jobs' }, masterKeyOptions ) ); @@ -120,13 +120,13 @@ describe("JobSchedule", () => { .catch(done.fail); }); - it("should fail creating a job with an invalid name", done => { + it('should fail creating a job with an invalid name', done => { const options = Object.assign({}, masterKeyOptions, { - url: Parse.serverURL + "/cloud_code/jobs", - method: "POST", + url: Parse.serverURL + '/cloud_code/jobs', + method: 'POST', body: { job_schedule: { - jobName: "job", + jobName: 'job', }, }, }); @@ -135,15 +135,15 @@ describe("JobSchedule", () => { .catch(() => done()); }); - it("should update a job", done => { - Parse.Cloud.job("job1", () => {}); - Parse.Cloud.job("job2", () => {}); + it('should update a job', done => { + Parse.Cloud.job('job1', () => {}); + Parse.Cloud.job('job2', () => {}); const options = Object.assign({}, masterKeyOptions, { - method: "POST", - url: Parse.serverURL + "/cloud_code/jobs", + method: 'POST', + url: Parse.serverURL + '/cloud_code/jobs', body: { job_schedule: { - jobName: "job1", + jobName: 'job1', }, }, }); @@ -152,11 +152,11 @@ describe("JobSchedule", () => { expect(res.data.objectId).not.toBeUndefined(); return request( Object.assign(options, { - url: Parse.serverURL + "/cloud_code/jobs/" + res.data.objectId, - method: "PUT", + url: Parse.serverURL + '/cloud_code/jobs/' + res.data.objectId, + method: 'PUT', body: { job_schedule: { - jobName: "job2", + jobName: 'job2', }, }, }) @@ -165,26 +165,26 @@ describe("JobSchedule", () => { .then(() => { return request( Object.assign({}, masterKeyOptions, { - url: Parse.serverURL + "/cloud_code/jobs", + url: Parse.serverURL + '/cloud_code/jobs', }) ); }) .then(res => { expect(res.data.length).toBe(1); - expect(res.data[0].jobName).toBe("job2"); + expect(res.data[0].jobName).toBe('job2'); }) .then(done) .catch(done.fail); }); - it("should fail updating a job with an invalid name", done => { - Parse.Cloud.job("job1", () => {}); + it('should fail updating a job with an invalid name', done => { + Parse.Cloud.job('job1', () => {}); const options = Object.assign({}, masterKeyOptions, { - method: "POST", - url: Parse.serverURL + "/cloud_code/jobs", + method: 'POST', + url: Parse.serverURL + '/cloud_code/jobs', body: { job_schedule: { - jobName: "job1", + jobName: 'job1', }, }, }); @@ -193,11 +193,11 @@ describe("JobSchedule", () => { expect(res.data.objectId).not.toBeUndefined(); return request( Object.assign(options, { - method: "PUT", - url: Parse.serverURL + "/cloud_code/jobs/" + res.data.objectId, + method: 'PUT', + url: Parse.serverURL + '/cloud_code/jobs/' + res.data.objectId, body: { job_schedule: { - jobName: "job2", + jobName: 'job2', }, }, }) @@ -207,14 +207,14 @@ describe("JobSchedule", () => { .catch(() => done()); }); - it("should destroy a job", done => { - Parse.Cloud.job("job", () => {}); + it('should destroy a job', done => { + Parse.Cloud.job('job', () => {}); const options = Object.assign({}, masterKeyOptions, { - method: "POST", - url: Parse.serverURL + "/cloud_code/jobs", + method: 'POST', + url: Parse.serverURL + '/cloud_code/jobs', body: { job_schedule: { - jobName: "job", + jobName: 'job', }, }, }); @@ -224,8 +224,8 @@ describe("JobSchedule", () => { return request( Object.assign( { - method: "DELETE", - url: Parse.serverURL + "/cloud_code/jobs/" + res.data.objectId, + method: 'DELETE', + url: Parse.serverURL + '/cloud_code/jobs/' + res.data.objectId, }, masterKeyOptions ) @@ -235,7 +235,7 @@ describe("JobSchedule", () => { return request( Object.assign( { - url: Parse.serverURL + "/cloud_code/jobs", + url: Parse.serverURL + '/cloud_code/jobs', }, masterKeyOptions ) @@ -248,15 +248,15 @@ describe("JobSchedule", () => { .catch(done.fail); }); - it("should properly return job data", done => { - Parse.Cloud.job("job1", () => {}); - Parse.Cloud.job("job2", () => {}); + it('should properly return job data', done => { + Parse.Cloud.job('job1', () => {}); + Parse.Cloud.job('job2', () => {}); const options = Object.assign({}, masterKeyOptions, { - method: "POST", - url: Parse.serverURL + "/cloud_code/jobs", + method: 'POST', + url: Parse.serverURL + '/cloud_code/jobs', body: { job_schedule: { - jobName: "job1", + jobName: 'job1', }, }, }); @@ -268,16 +268,16 @@ describe("JobSchedule", () => { .then(() => { return request( Object.assign( - { url: Parse.serverURL + "/cloud_code/jobs/data" }, + { url: Parse.serverURL + '/cloud_code/jobs/data' }, masterKeyOptions ) ); }) .then(response => { const res = response.data; - expect(res.in_use).toEqual(["job1"]); - expect(res.jobs).toContain("job1"); - expect(res.jobs).toContain("job2"); + expect(res.in_use).toEqual(['job1']); + expect(res.jobs).toContain('job1'); + expect(res.jobs).toContain('job2'); expect(res.jobs.length).toBe(2); }) .then(done) diff --git a/spec/LdapAuth.spec.js b/spec/LdapAuth.spec.js index 576fb660c2..4540487236 100644 --- a/spec/LdapAuth.spec.js +++ b/spec/LdapAuth.spec.js @@ -1,268 +1,268 @@ -const ldap = require("../lib/Adapters/Auth/ldap"); -const mockLdapServer = require("./support/MockLdapServer"); -const fs = require("fs"); +const ldap = require('../lib/Adapters/Auth/ldap'); +const mockLdapServer = require('./support/MockLdapServer'); +const fs = require('fs'); const port = 12345; const sslport = 12346; -describe("Ldap Auth", () => { - it("Should fail with missing options", done => { +describe('Ldap Auth', () => { + it('Should fail with missing options', done => { ldap - .validateAuthData({ id: "testuser", password: "testpw" }) + .validateAuthData({ id: 'testuser', password: 'testpw' }) .then(done.fail) .catch(err => { - jequal(err.message, "LDAP auth configuration missing"); + jequal(err.message, 'LDAP auth configuration missing'); done(); }); }); - it("Should return a resolved promise when validating the app id", done => { + it('Should return a resolved promise when validating the app id', done => { ldap.validateAppId().then(done).catch(done.fail); }); - it("Should succeed with right credentials", async done => { - const server = await mockLdapServer(port, "uid=testuser, o=example"); + it('Should succeed with right credentials', async done => { + const server = await mockLdapServer(port, 'uid=testuser, o=example'); const options = { - suffix: "o=example", + suffix: 'o=example', url: `ldap://localhost:${port}`, - dn: "uid={{id}}, o=example", + dn: 'uid={{id}}, o=example', }; await ldap.validateAuthData( - { id: "testuser", password: "secret" }, + { id: 'testuser', password: 'secret' }, options ); server.close(done); }); - it("Should succeed with right credentials when LDAPS is used and certifcate is not checked", async done => { + it('Should succeed with right credentials when LDAPS is used and certifcate is not checked', async done => { const server = await mockLdapServer( sslport, - "uid=testuser, o=example", + 'uid=testuser, o=example', false, true ); const options = { - suffix: "o=example", + suffix: 'o=example', url: `ldaps://localhost:${sslport}`, - dn: "uid={{id}}, o=example", + dn: 'uid={{id}}, o=example', tlsOptions: { rejectUnauthorized: false }, }; await ldap.validateAuthData( - { id: "testuser", password: "secret" }, + { id: 'testuser', password: 'secret' }, options ); server.close(done); }); - it("Should succeed when LDAPS is used and the presented certificate is the expected certificate", async done => { + it('Should succeed when LDAPS is used and the presented certificate is the expected certificate', async done => { const server = await mockLdapServer( sslport, - "uid=testuser, o=example", + 'uid=testuser, o=example', false, true ); const options = { - suffix: "o=example", + suffix: 'o=example', url: `ldaps://localhost:${sslport}`, - dn: "uid={{id}}, o=example", + dn: 'uid={{id}}, o=example', tlsOptions: { - ca: fs.readFileSync(__dirname + "/support/cert/cert.pem"), + ca: fs.readFileSync(__dirname + '/support/cert/cert.pem'), rejectUnauthorized: true, }, }; await ldap.validateAuthData( - { id: "testuser", password: "secret" }, + { id: 'testuser', password: 'secret' }, options ); server.close(done); }); - it("Should fail when LDAPS is used and the presented certificate is not the expected certificate", async done => { + it('Should fail when LDAPS is used and the presented certificate is not the expected certificate', async done => { const server = await mockLdapServer( sslport, - "uid=testuser, o=example", + 'uid=testuser, o=example', false, true ); const options = { - suffix: "o=example", + suffix: 'o=example', url: `ldaps://localhost:${sslport}`, - dn: "uid={{id}}, o=example", + dn: 'uid={{id}}, o=example', tlsOptions: { - ca: fs.readFileSync(__dirname + "/support/cert/anothercert.pem"), + ca: fs.readFileSync(__dirname + '/support/cert/anothercert.pem'), rejectUnauthorized: true, }, }; try { await ldap.validateAuthData( - { id: "testuser", password: "secret" }, + { id: 'testuser', password: 'secret' }, options ); fail(); } catch (err) { - expect(err.message).toBe("LDAPS: Certificate mismatch"); + expect(err.message).toBe('LDAPS: Certificate mismatch'); } server.close(done); }); - it("Should fail when LDAPS is used certifcate matches but credentials are wrong", async done => { + it('Should fail when LDAPS is used certifcate matches but credentials are wrong', async done => { const server = await mockLdapServer( sslport, - "uid=testuser, o=example", + 'uid=testuser, o=example', false, true ); const options = { - suffix: "o=example", + suffix: 'o=example', url: `ldaps://localhost:${sslport}`, - dn: "uid={{id}}, o=example", + dn: 'uid={{id}}, o=example', tlsOptions: { - ca: fs.readFileSync(__dirname + "/support/cert/cert.pem"), + ca: fs.readFileSync(__dirname + '/support/cert/cert.pem'), rejectUnauthorized: true, }, }; try { await ldap.validateAuthData( - { id: "testuser", password: "wrong!" }, + { id: 'testuser', password: 'wrong!' }, options ); fail(); } catch (err) { - expect(err.message).toBe("LDAP: Wrong username or password"); + expect(err.message).toBe('LDAP: Wrong username or password'); } server.close(done); }); - it("Should fail with wrong credentials", async done => { - const server = await mockLdapServer(port, "uid=testuser, o=example"); + it('Should fail with wrong credentials', async done => { + const server = await mockLdapServer(port, 'uid=testuser, o=example'); const options = { - suffix: "o=example", + suffix: 'o=example', url: `ldap://localhost:${port}`, - dn: "uid={{id}}, o=example", + dn: 'uid={{id}}, o=example', }; try { await ldap.validateAuthData( - { id: "testuser", password: "wrong!" }, + { id: 'testuser', password: 'wrong!' }, options ); fail(); } catch (err) { - expect(err.message).toBe("LDAP: Wrong username or password"); + expect(err.message).toBe('LDAP: Wrong username or password'); } server.close(done); }); - it("Should succeed if user is in given group", async done => { - const server = await mockLdapServer(port, "uid=testuser, o=example"); + it('Should succeed if user is in given group', async done => { + const server = await mockLdapServer(port, 'uid=testuser, o=example'); const options = { - suffix: "o=example", + suffix: 'o=example', url: `ldap://localhost:${port}`, - dn: "uid={{id}}, o=example", - groupCn: "powerusers", + dn: 'uid={{id}}, o=example', + groupCn: 'powerusers', groupFilter: - "(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))", + '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))', }; await ldap.validateAuthData( - { id: "testuser", password: "secret" }, + { id: 'testuser', password: 'secret' }, options ); server.close(done); }); - it("Should fail if user is not in given group", async done => { - const server = await mockLdapServer(port, "uid=testuser, o=example"); + it('Should fail if user is not in given group', async done => { + const server = await mockLdapServer(port, 'uid=testuser, o=example'); const options = { - suffix: "o=example", + suffix: 'o=example', url: `ldap://localhost:${port}`, - dn: "uid={{id}}, o=example", - groupCn: "groupTheUserIsNotIn", + dn: 'uid={{id}}, o=example', + groupCn: 'groupTheUserIsNotIn', groupFilter: - "(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))", + '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))', }; try { await ldap.validateAuthData( - { id: "testuser", password: "secret" }, + { id: 'testuser', password: 'secret' }, options ); fail(); } catch (err) { - expect(err.message).toBe("LDAP: User not in group"); + expect(err.message).toBe('LDAP: User not in group'); } server.close(done); }); - it("Should fail if the LDAP server does not allow searching inside the provided suffix", async done => { - const server = await mockLdapServer(port, "uid=testuser, o=example"); + it('Should fail if the LDAP server does not allow searching inside the provided suffix', async done => { + const server = await mockLdapServer(port, 'uid=testuser, o=example'); const options = { - suffix: "o=invalid", + suffix: 'o=invalid', url: `ldap://localhost:${port}`, - dn: "uid={{id}}, o=example", - groupCn: "powerusers", + dn: 'uid={{id}}, o=example', + groupCn: 'powerusers', groupFilter: - "(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))", + '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))', }; try { await ldap.validateAuthData( - { id: "testuser", password: "secret" }, + { id: 'testuser', password: 'secret' }, options ); fail(); } catch (err) { - expect(err.message).toBe("LDAP group search failed"); + expect(err.message).toBe('LDAP group search failed'); } server.close(done); }); - it("Should fail if the LDAP server encounters an error while searching", async done => { - const server = await mockLdapServer(port, "uid=testuser, o=example", true); + it('Should fail if the LDAP server encounters an error while searching', async done => { + const server = await mockLdapServer(port, 'uid=testuser, o=example', true); const options = { - suffix: "o=example", + suffix: 'o=example', url: `ldap://localhost:${port}`, - dn: "uid={{id}}, o=example", - groupCn: "powerusers", + dn: 'uid={{id}}, o=example', + groupCn: 'powerusers', groupFilter: - "(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))", + '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))', }; try { await ldap.validateAuthData( - { id: "testuser", password: "secret" }, + { id: 'testuser', password: 'secret' }, options ); fail(); } catch (err) { - expect(err.message).toBe("LDAP group search failed"); + expect(err.message).toBe('LDAP group search failed'); } server.close(done); }); - it("Should delete the password from authData after validation", async done => { - const server = await mockLdapServer(port, "uid=testuser, o=example", true); + it('Should delete the password from authData after validation', async done => { + const server = await mockLdapServer(port, 'uid=testuser, o=example', true); const options = { - suffix: "o=example", + suffix: 'o=example', url: `ldap://localhost:${port}`, - dn: "uid={{id}}, o=example", + dn: 'uid={{id}}, o=example', }; - const authData = { id: "testuser", password: "secret" }; + const authData = { id: 'testuser', password: 'secret' }; await ldap.validateAuthData(authData, options); - expect(authData).toEqual({ id: "testuser" }); + expect(authData).toEqual({ id: 'testuser' }); server.close(done); }); - it("Should not save the password in the user record after authentication", async done => { - const server = await mockLdapServer(port, "uid=testuser, o=example", true); + it('Should not save the password in the user record after authentication', async done => { + const server = await mockLdapServer(port, 'uid=testuser, o=example', true); const options = { - suffix: "o=example", + suffix: 'o=example', url: `ldap://localhost:${port}`, - dn: "uid={{id}}, o=example", + dn: 'uid={{id}}, o=example', }; await reconfigureServer({ auth: { ldap: options } }); - const authData = { authData: { id: "testuser", password: "secret" } }; - const returnedUser = await Parse.User.logInWith("ldap", authData); - const query = new Parse.Query("User"); + const authData = { authData: { id: 'testuser', password: 'secret' } }; + const returnedUser = await Parse.User.logInWith('ldap', authData); + const query = new Parse.Query('User'); const user = await query - .equalTo("objectId", returnedUser.id) + .equalTo('objectId', returnedUser.id) .first({ useMasterKey: true }); - expect(user.get("authData")).toEqual({ ldap: { id: "testuser" } }); - expect(user.get("authData").ldap.password).toBeUndefined(); + expect(user.get('authData')).toEqual({ ldap: { id: 'testuser' } }); + expect(user.get('authData').ldap.password).toBeUndefined(); server.close(done); }); }); diff --git a/spec/Logger.spec.js b/spec/Logger.spec.js index e49bcfa244..865c5b0c5c 100644 --- a/spec/Logger.spec.js +++ b/spec/Logger.spec.js @@ -1,5 +1,5 @@ -const logging = require("../lib/Adapters/Logger/WinstonLogger"); -const Transport = require("winston-transport"); +const logging = require('../lib/Adapters/Logger/WinstonLogger'); +const Transport = require('winston-transport'); class TestTransport extends Transport { log(info, callback) { @@ -7,21 +7,21 @@ class TestTransport extends Transport { } } -describe("WinstonLogger", () => { - it("should add transport", () => { +describe('WinstonLogger', () => { + it('should add transport', () => { const testTransport = new TestTransport(); - spyOn(testTransport, "log"); + spyOn(testTransport, 'log'); logging.addTransport(testTransport); expect(logging.logger.transports.length).toBe(4); - logging.logger.info("hi"); + logging.logger.info('hi'); expect(testTransport.log).toHaveBeenCalled(); - logging.logger.error("error"); + logging.logger.error('error'); expect(testTransport.log).toHaveBeenCalled(); logging.removeTransport(testTransport); expect(logging.logger.transports.length).toBe(3); }); - it("should have files transports", done => { + it('should have files transports', done => { reconfigureServer().then(() => { const transports = logging.logger.transports; expect(transports.length).toBe(3); @@ -29,7 +29,7 @@ describe("WinstonLogger", () => { }); }); - it("should disable files logs", done => { + it('should disable files logs', done => { reconfigureServer({ logsFolder: null, }) @@ -41,29 +41,29 @@ describe("WinstonLogger", () => { .then(done); }); - it("should have a timestamp", done => { - logging.logger.info("hi"); + it('should have a timestamp', done => { + logging.logger.info('hi'); logging.logger.query({ limit: 1 }, (err, results) => { if (err) { done.fail(err); } - expect(results["parse-server"][0].timestamp).toBeDefined(); + expect(results['parse-server'][0].timestamp).toBeDefined(); done(); }); }); - it("console should not be json", done => { + it('console should not be json', done => { // Force console transport reconfigureServer({ logsFolder: null, silent: false, }) .then(() => { - spyOn(process.stdout, "write"); - logging.logger.info("hi", { key: "value" }); + spyOn(process.stdout, 'write'); + logging.logger.info('hi', { key: 'value' }); expect(process.stdout.write).toHaveBeenCalled(); const firstLog = process.stdout.write.calls.first().args[0]; - expect(firstLog).toEqual('info: hi {"key":"value"}' + "\n"); + expect(firstLog).toEqual('info: hi {"key":"value"}' + '\n'); return reconfigureServer(); }) .then(() => { @@ -71,7 +71,7 @@ describe("WinstonLogger", () => { }); }); - it("should enable JSON logs", done => { + it('should enable JSON logs', done => { // Force console transport reconfigureServer({ logsFolder: null, @@ -79,12 +79,12 @@ describe("WinstonLogger", () => { silent: false, }) .then(() => { - spyOn(process.stdout, "write"); - logging.logger.info("hi", { key: "value" }); + spyOn(process.stdout, 'write'); + logging.logger.info('hi', { key: 'value' }); expect(process.stdout.write).toHaveBeenCalled(); const firstLog = process.stdout.write.calls.first().args[0]; expect(firstLog).toEqual( - JSON.stringify({ key: "value", level: "info", message: "hi" }) + "\n" + JSON.stringify({ key: 'value', level: 'info', message: 'hi' }) + '\n' ); return reconfigureServer({ jsonLogs: false, diff --git a/spec/LoggerController.spec.js b/spec/LoggerController.spec.js index 51e8ca87bd..cb7fc1453c 100644 --- a/spec/LoggerController.spec.js +++ b/spec/LoggerController.spec.js @@ -1,10 +1,10 @@ const LoggerController = - require("../lib/Controllers/LoggerController").LoggerController; + require('../lib/Controllers/LoggerController').LoggerController; const WinstonLoggerAdapter = - require("../lib/Adapters/Logger/WinstonLoggerAdapter").WinstonLoggerAdapter; + require('../lib/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter; -describe("LoggerController", () => { - it("can process an empty query without throwing", done => { +describe('LoggerController', () => { + it('can process an empty query without throwing', done => { // Make mock request const query = {}; @@ -24,34 +24,34 @@ describe("LoggerController", () => { }).not.toThrow(); }); - it("properly validates dateTimes", done => { + it('properly validates dateTimes', done => { expect(LoggerController.validDateTime()).toBe(null); - expect(LoggerController.validDateTime("String")).toBe(null); + expect(LoggerController.validDateTime('String')).toBe(null); expect(LoggerController.validDateTime(123456).getTime()).toBe(123456); expect( - LoggerController.validDateTime("2016-01-01Z00:00:00").getTime() + LoggerController.validDateTime('2016-01-01Z00:00:00').getTime() ).toBe(1451606400000); done(); }); - it("can set the proper default values", done => { + it('can set the proper default values', done => { // Make mock request const result = LoggerController.parseOptions(); expect(result.size).toEqual(10); - expect(result.order).toEqual("desc"); - expect(result.level).toEqual("info"); + expect(result.order).toEqual('desc'); + expect(result.level).toEqual('info'); done(); }); - it("can parse an ascending query without throwing", done => { + it('can parse an ascending query without throwing', done => { // Make mock request const query = { - from: "2016-01-01Z00:00:00", - until: "2016-01-01Z00:00:00", + from: '2016-01-01Z00:00:00', + until: '2016-01-01Z00:00:00', size: 5, - order: "asc", - level: "error", + order: 'asc', + level: 'error', }; const result = LoggerController.parseOptions(query); @@ -59,21 +59,21 @@ describe("LoggerController", () => { expect(result.from.getTime()).toEqual(1451606400000); expect(result.until.getTime()).toEqual(1451606400000); expect(result.size).toEqual(5); - expect(result.order).toEqual("asc"); - expect(result.level).toEqual("error"); + expect(result.order).toEqual('asc'); + expect(result.level).toEqual('error'); done(); }); - it("can process an ascending query without throwing", done => { + it('can process an ascending query without throwing', done => { const query = { size: 5, - order: "asc", - level: "error", + order: 'asc', + level: 'error', }; const loggerController = new LoggerController(new WinstonLoggerAdapter()); - loggerController.error("can process an ascending query without throwing"); + loggerController.error('can process an ascending query without throwing'); expect(() => { loggerController @@ -84,20 +84,20 @@ describe("LoggerController", () => { }) .catch(err => { jfail(err); - fail("should not fail"); + fail('should not fail'); done(); }); }).not.toThrow(); }); - it("can parse a descending query without throwing", done => { + it('can parse a descending query without throwing', done => { // Make mock request const query = { - from: "2016-01-01Z00:00:00", - until: "2016-01-01Z00:00:00", + from: '2016-01-01Z00:00:00', + until: '2016-01-01Z00:00:00', size: 5, - order: "desc", - level: "error", + order: 'desc', + level: 'error', }; const result = LoggerController.parseOptions(query); @@ -105,21 +105,21 @@ describe("LoggerController", () => { expect(result.from.getTime()).toEqual(1451606400000); expect(result.until.getTime()).toEqual(1451606400000); expect(result.size).toEqual(5); - expect(result.order).toEqual("desc"); - expect(result.level).toEqual("error"); + expect(result.order).toEqual('desc'); + expect(result.level).toEqual('error'); done(); }); - it("can process a descending query without throwing", done => { + it('can process a descending query without throwing', done => { const query = { size: 5, - order: "desc", - level: "error", + order: 'desc', + level: 'error', }; const loggerController = new LoggerController(new WinstonLoggerAdapter()); - loggerController.error("can process a descending query without throwing"); + loggerController.error('can process a descending query without throwing'); expect(() => { loggerController @@ -130,39 +130,39 @@ describe("LoggerController", () => { }) .catch(err => { jfail(err); - fail("should not fail"); + fail('should not fail'); done(); }); }).not.toThrow(); }); - it("should throw without an adapter", done => { + it('should throw without an adapter', done => { expect(() => { new LoggerController(); }).toThrow(); done(); }); - it("should replace implementations with verbose", done => { + it('should replace implementations with verbose', done => { const adapter = new WinstonLoggerAdapter(); const logger = new LoggerController(adapter, null, { verbose: true }); - spyOn(adapter, "log"); - logger.silly("yo!"); + spyOn(adapter, 'log'); + logger.silly('yo!'); expect(adapter.log).not.toHaveBeenCalled(); done(); }); - it("should replace implementations with logLevel", done => { + it('should replace implementations with logLevel', done => { const adapter = new WinstonLoggerAdapter(); - const logger = new LoggerController(adapter, null, { logLevel: "error" }); - spyOn(adapter, "log"); - logger.warn("yo!"); - logger.info("yo!"); - logger.debug("yo!"); - logger.verbose("yo!"); - logger.silly("yo!"); + const logger = new LoggerController(adapter, null, { logLevel: 'error' }); + spyOn(adapter, 'log'); + logger.warn('yo!'); + logger.info('yo!'); + logger.debug('yo!'); + logger.verbose('yo!'); + logger.silly('yo!'); expect(adapter.log).not.toHaveBeenCalled(); - logger.error("error"); + logger.error('error'); expect(adapter.log).toHaveBeenCalled(); done(); }); diff --git a/spec/LogsRouter.spec.js b/spec/LogsRouter.spec.js index 93121bcc82..ffe886abef 100644 --- a/spec/LogsRouter.spec.js +++ b/spec/LogsRouter.spec.js @@ -1,18 +1,18 @@ -"use strict"; +'use strict'; -const request = require("../lib/request"); -const LogsRouter = require("../lib/Routers/LogsRouter").LogsRouter; +const request = require('../lib/request'); +const LogsRouter = require('../lib/Routers/LogsRouter').LogsRouter; const LoggerController = - require("../lib/Controllers/LoggerController").LoggerController; + require('../lib/Controllers/LoggerController').LoggerController; const WinstonLoggerAdapter = - require("../lib/Adapters/Logger/WinstonLoggerAdapter").WinstonLoggerAdapter; + require('../lib/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter; const loggerController = new LoggerController(new WinstonLoggerAdapter()); describe_only(() => { - return process.env.PARSE_SERVER_LOG_LEVEL !== "debug"; -})("LogsRouter", () => { - it("can check valid master key of request", done => { + return process.env.PARSE_SERVER_LOG_LEVEL !== 'debug'; +})('LogsRouter', () => { + it('can check valid master key of request', done => { // Make mock request const request = { auth: { @@ -32,7 +32,7 @@ describe_only(() => { done(); }); - it("can check invalid construction of controller", done => { + it('can check invalid construction of controller', done => { // Make mock request const request = { auth: { @@ -52,54 +52,54 @@ describe_only(() => { done(); }); - it("can check invalid master key of request", done => { + it('can check invalid master key of request', done => { request({ - url: "http://localhost:8378/1/scriptlog", + url: 'http://localhost:8378/1/scriptlog', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }, }).then(fail, response => { const body = response.data; expect(response.status).toEqual(403); - expect(body.error).toEqual("unauthorized: master key is required"); + expect(body.error).toEqual('unauthorized: master key is required'); done(); }); }); const headers = { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "X-Parse-Master-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Master-Key': 'test', }; /** * Verifies simple passwords in GET login requests with special characters are scrubbed from the verbose log */ - it_id("e36d6141-2a20-41d0-85fc-d1534c3e4bae")(it)( - "does scrub simple passwords on GET login", + it_id('e36d6141-2a20-41d0-85fc-d1534c3e4bae')(it)( + 'does scrub simple passwords on GET login', done => { reconfigureServer({ verbose: true, }).then(function () { request({ headers: headers, - url: "http://localhost:8378/1/login?username=test&password=simplepass.com", + url: 'http://localhost:8378/1/login?username=test&password=simplepass.com', }) .catch(() => {}) .then(() => { request({ - url: "http://localhost:8378/1/scriptlog?size=4&level=verbose", + url: 'http://localhost:8378/1/scriptlog?size=4&level=verbose', headers: headers, }).then(response => { const body = response.data; expect(response.status).toEqual(200); // 4th entry is our actual GET request expect(body[2].url).toEqual( - "/1/login?username=test&password=********" + '/1/login?username=test&password=********' ); expect(body[2].message).toEqual( - "REQUEST for [GET] /1/login?username=test&password=********: {}" + 'REQUEST for [GET] /1/login?username=test&password=********: {}' ); done(); }); @@ -111,8 +111,8 @@ describe_only(() => { /** * Verifies complex passwords in GET login requests with special characters are scrubbed from the verbose log */ - it_id("24b277c5-250f-4a35-a449-2c8c519d4c03")(it)( - "does scrub complex passwords on GET login", + it_id('24b277c5-250f-4a35-a449-2c8c519d4c03')(it)( + 'does scrub complex passwords on GET login', done => { reconfigureServer({ verbose: true, @@ -121,22 +121,22 @@ describe_only(() => { return request({ headers: headers, // using urlencoded password, 'simple @,/?:&=+$#pass.com' - url: "http://localhost:8378/1/login?username=test&password=simple%20%40%2C%2F%3F%3A%26%3D%2B%24%23pass.com", + url: 'http://localhost:8378/1/login?username=test&password=simple%20%40%2C%2F%3F%3A%26%3D%2B%24%23pass.com', }) .catch(() => {}) .then(() => { return request({ - url: "http://localhost:8378/1/scriptlog?size=4&level=verbose", + url: 'http://localhost:8378/1/scriptlog?size=4&level=verbose', headers: headers, }).then(response => { const body = response.data; expect(response.status).toEqual(200); // 4th entry is our actual GET request expect(body[2].url).toEqual( - "/1/login?username=test&password=********" + '/1/login?username=test&password=********' ); expect(body[2].message).toEqual( - "REQUEST for [GET] /1/login?username=test&password=********: {}" + 'REQUEST for [GET] /1/login?username=test&password=********: {}' ); done(); }); @@ -149,31 +149,31 @@ describe_only(() => { /** * Verifies fields in POST login requests are NOT present in the verbose log */ - it_id("33143ec9-b32d-467c-ba65-ff2bbefdaadd")(it)( - "does not have password field in POST login", + it_id('33143ec9-b32d-467c-ba65-ff2bbefdaadd')(it)( + 'does not have password field in POST login', done => { reconfigureServer({ verbose: true, }).then(function () { request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/login", + url: 'http://localhost:8378/1/login', body: { - username: "test", - password: "simplepass.com", + username: 'test', + password: 'simplepass.com', }, }) .catch(() => {}) .then(() => { request({ - url: "http://localhost:8378/1/scriptlog?size=4&level=verbose", + url: 'http://localhost:8378/1/scriptlog?size=4&level=verbose', headers: headers, }).then(response => { const body = response.data; expect(response.status).toEqual(200); // 4th entry is our actual GET request - expect(body[2].url).toEqual("/1/login"); + expect(body[2].url).toEqual('/1/login'); expect(body[2].message).toEqual( 'REQUEST for [POST] /1/login: {\n "username": "test",\n "password": "********"\n}' ); diff --git a/spec/Middlewares.spec.js b/spec/Middlewares.spec.js index 3c25692657..b0e6110637 100644 --- a/spec/Middlewares.spec.js +++ b/spec/Middlewares.spec.js @@ -1,6 +1,6 @@ -const middlewares = require("../lib/middlewares"); -const AppCache = require("../lib/cache").AppCache; -const { BlockList } = require("net"); +const middlewares = require('../lib/middlewares'); +const AppCache = require('../lib/cache').AppCache; +const { BlockList } = require('net'); const AppCachePut = (appId, config) => AppCache.put(appId, { @@ -9,22 +9,22 @@ const AppCachePut = (appId, config) => masterKeyIpsStore: new Map(), }); -describe("middlewares", () => { +describe('middlewares', () => { let fakeReq, fakeRes; beforeEach(() => { fakeReq = { - ip: "127.0.0.1", - originalUrl: "http://example.com/parse/", - url: "http://example.com/", + ip: '127.0.0.1', + originalUrl: 'http://example.com/parse/', + url: 'http://example.com/', body: { - _ApplicationId: "FakeAppId", + _ApplicationId: 'FakeAppId', }, headers: {}, get: key => { return fakeReq.headers[key.toLowerCase()]; }, }; - fakeRes = jasmine.createSpyObj("fakeRes", ["end", "status"]); + fakeRes = jasmine.createSpyObj('fakeRes', ['end', 'status']); AppCachePut(fakeReq.body._ApplicationId, {}); }); @@ -32,81 +32,81 @@ describe("middlewares", () => { AppCache.del(fakeReq.body._ApplicationId); }); - it_id("4cc18d90-1763-4725-97fa-f63fb4692fc4")(it)( - "should use _ContentType if provided", + it_id('4cc18d90-1763-4725-97fa-f63fb4692fc4')(it)( + 'should use _ContentType if provided', done => { AppCachePut(fakeReq.body._ApplicationId, { - masterKeyIps: ["127.0.0.1"], + masterKeyIps: ['127.0.0.1'], }); - expect(fakeReq.headers["content-type"]).toEqual(undefined); - const contentType = "image/jpeg"; + expect(fakeReq.headers['content-type']).toEqual(undefined); + const contentType = 'image/jpeg'; fakeReq.body._ContentType = contentType; middlewares.handleParseHeaders(fakeReq, fakeRes, () => { - expect(fakeReq.headers["content-type"]).toEqual(contentType); + expect(fakeReq.headers['content-type']).toEqual(contentType); expect(fakeReq.body._ContentType).toEqual(undefined); done(); }); } ); - it("should give invalid response when keys are configured but no key supplied", async () => { + it('should give invalid response when keys are configured but no key supplied', async () => { AppCachePut(fakeReq.body._ApplicationId, { - masterKey: "masterKey", - restAPIKey: "restAPIKey", + masterKey: 'masterKey', + restAPIKey: 'restAPIKey', }); await middlewares.handleParseHeaders(fakeReq, fakeRes); expect(fakeRes.status).toHaveBeenCalledWith(403); }); - it("should give invalid response when keys are configured but supplied key is incorrect", async () => { + it('should give invalid response when keys are configured but supplied key is incorrect', async () => { AppCachePut(fakeReq.body._ApplicationId, { - masterKey: "masterKey", - restAPIKey: "restAPIKey", + masterKey: 'masterKey', + restAPIKey: 'restAPIKey', }); - fakeReq.headers["x-parse-rest-api-key"] = "wrongKey"; + fakeReq.headers['x-parse-rest-api-key'] = 'wrongKey'; await middlewares.handleParseHeaders(fakeReq, fakeRes); expect(fakeRes.status).toHaveBeenCalledWith(403); }); - it("should give invalid response when keys are configured but different key is supplied", async () => { + it('should give invalid response when keys are configured but different key is supplied', async () => { AppCachePut(fakeReq.body._ApplicationId, { - masterKey: "masterKey", - restAPIKey: "restAPIKey", + masterKey: 'masterKey', + restAPIKey: 'restAPIKey', }); - fakeReq.headers["x-parse-client-key"] = "clientKey"; + fakeReq.headers['x-parse-client-key'] = 'clientKey'; await middlewares.handleParseHeaders(fakeReq, fakeRes); expect(fakeRes.status).toHaveBeenCalledWith(403); }); - it("should succeed when any one of the configured keys supplied", done => { + it('should succeed when any one of the configured keys supplied', done => { AppCachePut(fakeReq.body._ApplicationId, { - clientKey: "clientKey", - masterKey: "masterKey", - restAPIKey: "restAPIKey", + clientKey: 'clientKey', + masterKey: 'masterKey', + restAPIKey: 'restAPIKey', }); - fakeReq.headers["x-parse-rest-api-key"] = "restAPIKey"; + fakeReq.headers['x-parse-rest-api-key'] = 'restAPIKey'; middlewares.handleParseHeaders(fakeReq, fakeRes, () => { expect(fakeRes.status).not.toHaveBeenCalled(); done(); }); }); - it("should succeed when client key supplied but empty", done => { + it('should succeed when client key supplied but empty', done => { AppCachePut(fakeReq.body._ApplicationId, { - clientKey: "", - masterKey: "masterKey", - restAPIKey: "restAPIKey", + clientKey: '', + masterKey: 'masterKey', + restAPIKey: 'restAPIKey', }); - fakeReq.headers["x-parse-client-key"] = ""; + fakeReq.headers['x-parse-client-key'] = ''; middlewares.handleParseHeaders(fakeReq, fakeRes, () => { expect(fakeRes.status).not.toHaveBeenCalled(); done(); }); }); - it("should succeed when no keys are configured and none supplied", done => { + it('should succeed when no keys are configured and none supplied', done => { AppCachePut(fakeReq.body._ApplicationId, { - masterKey: "masterKey", + masterKey: 'masterKey', }); middlewares.handleParseHeaders(fakeReq, fakeRes, () => { expect(fakeRes.status).not.toHaveBeenCalled(); @@ -115,29 +115,29 @@ describe("middlewares", () => { }); const BodyParams = { - clientVersion: "_ClientVersion", - installationId: "_InstallationId", - sessionToken: "_SessionToken", - masterKey: "_MasterKey", - javascriptKey: "_JavaScriptKey", + clientVersion: '_ClientVersion', + installationId: '_InstallationId', + sessionToken: '_SessionToken', + masterKey: '_MasterKey', + javascriptKey: '_JavaScriptKey', }; const BodyKeys = Object.keys(BodyParams); BodyKeys.forEach(infoKey => { const bodyKey = BodyParams[infoKey]; - const keyValue = "Fake" + bodyKey; + const keyValue = 'Fake' + bodyKey; // javascriptKey is the only one that gets defaulted, const otherKeys = BodyKeys.filter( - otherKey => otherKey !== infoKey && otherKey !== "javascriptKey" + otherKey => otherKey !== infoKey && otherKey !== 'javascriptKey' ); - it_id("f9abd7ac-b1f4-4607-b9b0-365ff0559d84")(it)( + it_id('f9abd7ac-b1f4-4607-b9b0-365ff0559d84')(it)( `it should pull ${bodyKey} into req.info`, done => { AppCachePut(fakeReq.body._ApplicationId, { - masterKeyIps: ["0.0.0.0/0"], + masterKeyIps: ['0.0.0.0/0'], }); - fakeReq.ip = "127.0.0.1"; + fakeReq.ip = '127.0.0.1'; fakeReq.body[bodyKey] = keyValue; middlewares.handleParseHeaders(fakeReq, fakeRes, () => { expect(fakeReq.body[bodyKey]).toEqual(undefined); @@ -153,17 +153,17 @@ describe("middlewares", () => { ); }); - it_id("4a0bce41-c536-4482-a873-12ed023380e2")(it)( - "should not succeed and log if the ip does not belong to masterKeyIps list", + it_id('4a0bce41-c536-4482-a873-12ed023380e2')(it)( + 'should not succeed and log if the ip does not belong to masterKeyIps list', async () => { - const logger = require("../lib/logger").logger; - spyOn(logger, "error").and.callFake(() => {}); + const logger = require('../lib/logger').logger; + spyOn(logger, 'error').and.callFake(() => {}); AppCachePut(fakeReq.body._ApplicationId, { - masterKey: "masterKey", - masterKeyIps: ["10.0.0.1"], + masterKey: 'masterKey', + masterKeyIps: ['10.0.0.1'], }); - fakeReq.ip = "127.0.0.1"; - fakeReq.headers["x-parse-master-key"] = "masterKey"; + fakeReq.ip = '127.0.0.1'; + fakeReq.headers['x-parse-master-key'] = 'masterKey'; const error = await middlewares .handleParseHeaders(fakeReq, fakeRes, () => {}) @@ -177,15 +177,15 @@ describe("middlewares", () => { } ); - it("should not succeed and log if the ip does not belong to maintenanceKeyIps list", async () => { - const logger = require("../lib/logger").logger; - spyOn(logger, "error").and.callFake(() => {}); + it('should not succeed and log if the ip does not belong to maintenanceKeyIps list', async () => { + const logger = require('../lib/logger').logger; + spyOn(logger, 'error').and.callFake(() => {}); AppCachePut(fakeReq.body._ApplicationId, { - maintenanceKey: "masterKey", - maintenanceKeyIps: ["10.0.0.0", "10.0.0.1"], + maintenanceKey: 'masterKey', + maintenanceKeyIps: ['10.0.0.0', '10.0.0.1'], }); - fakeReq.ip = "10.0.0.2"; - fakeReq.headers["x-parse-maintenance-key"] = "masterKey"; + fakeReq.ip = '10.0.0.2'; + fakeReq.headers['x-parse-maintenance-key'] = 'masterKey'; const error = await middlewares .handleParseHeaders(fakeReq, fakeRes, () => {}) @@ -198,15 +198,15 @@ describe("middlewares", () => { ); }); - it_id("2f7fadec-a87c-4626-90d1-65c75653aea9")(it)( - "should succeed if the ip does belong to masterKeyIps list", + it_id('2f7fadec-a87c-4626-90d1-65c75653aea9')(it)( + 'should succeed if the ip does belong to masterKeyIps list', async () => { AppCachePut(fakeReq.body._ApplicationId, { - masterKey: "masterKey", - masterKeyIps: ["10.0.0.1"], + masterKey: 'masterKey', + masterKeyIps: ['10.0.0.1'], }); - fakeReq.ip = "10.0.0.1"; - fakeReq.headers["x-parse-master-key"] = "masterKey"; + fakeReq.ip = '10.0.0.1'; + fakeReq.headers['x-parse-master-key'] = 'masterKey'; await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve) ); @@ -214,15 +214,15 @@ describe("middlewares", () => { } ); - it_id("2b251fd4-d43c-48f4-ada9-c8458e40c12a")(it)( - "should allow any ip to use masterKey if masterKeyIps is empty", + it_id('2b251fd4-d43c-48f4-ada9-c8458e40c12a')(it)( + 'should allow any ip to use masterKey if masterKeyIps is empty', async () => { AppCachePut(fakeReq.body._ApplicationId, { - masterKey: "masterKey", - masterKeyIps: ["0.0.0.0/0"], + masterKey: 'masterKey', + masterKeyIps: ['0.0.0.0/0'], }); - fakeReq.ip = "10.0.0.1"; - fakeReq.headers["x-parse-master-key"] = "masterKey"; + fakeReq.ip = '10.0.0.1'; + fakeReq.headers['x-parse-master-key'] = 'masterKey'; await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve) ); @@ -230,12 +230,12 @@ describe("middlewares", () => { } ); - it("can set trust proxy", async () => { + it('can set trust proxy', async () => { const server = await reconfigureServer({ trustProxy: 1 }); - expect(server.app.parent.settings["trust proxy"]).toBe(1); + expect(server.app.parent.settings['trust proxy']).toBe(1); }); - it("should properly expose the headers", () => { + it('should properly expose the headers', () => { const headers = {}; const res = { header: (key, value) => { @@ -247,12 +247,12 @@ describe("middlewares", () => { ); allowCrossDomain(fakeReq, res, () => {}); expect(Object.keys(headers).length).toBe(4); - expect(headers["Access-Control-Expose-Headers"]).toBe( - "X-Parse-Job-Status-Id, X-Parse-Push-Status-Id" + expect(headers['Access-Control-Expose-Headers']).toBe( + 'X-Parse-Job-Status-Id, X-Parse-Push-Status-Id' ); }); - it("should set default Access-Control-Allow-Headers if allowHeaders are empty", () => { + it('should set default Access-Control-Allow-Headers if allowHeaders are empty', () => { AppCachePut(fakeReq.body._ApplicationId, { allowHeaders: undefined, }); @@ -266,7 +266,7 @@ describe("middlewares", () => { fakeReq.body._ApplicationId ); allowCrossDomain(fakeReq, res, () => {}); - expect(headers["Access-Control-Allow-Headers"]).toContain( + expect(headers['Access-Control-Allow-Headers']).toContain( middlewares.DEFAULT_ALLOWED_HEADERS ); @@ -274,14 +274,14 @@ describe("middlewares", () => { allowHeaders: [], }); allowCrossDomain(fakeReq, res, () => {}); - expect(headers["Access-Control-Allow-Headers"]).toContain( + expect(headers['Access-Control-Allow-Headers']).toContain( middlewares.DEFAULT_ALLOWED_HEADERS ); }); - it("should append custom headers to Access-Control-Allow-Headers if allowHeaders provided", () => { + it('should append custom headers to Access-Control-Allow-Headers if allowHeaders provided', () => { AppCachePut(fakeReq.body._ApplicationId, { - allowHeaders: ["Header-1", "Header-2"], + allowHeaders: ['Header-1', 'Header-2'], }); const headers = {}; const res = { @@ -293,15 +293,15 @@ describe("middlewares", () => { fakeReq.body._ApplicationId ); allowCrossDomain(fakeReq, res, () => {}); - expect(headers["Access-Control-Allow-Headers"]).toContain( - "Header-1, Header-2" + expect(headers['Access-Control-Allow-Headers']).toContain( + 'Header-1, Header-2' ); - expect(headers["Access-Control-Allow-Headers"]).toContain( + expect(headers['Access-Control-Allow-Headers']).toContain( middlewares.DEFAULT_ALLOWED_HEADERS ); }); - it("should set default Access-Control-Allow-Origin if allowOrigin is empty", () => { + it('should set default Access-Control-Allow-Origin if allowOrigin is empty', () => { AppCachePut(fakeReq.body._ApplicationId, { allowOrigin: undefined, }); @@ -315,12 +315,12 @@ describe("middlewares", () => { fakeReq.body._ApplicationId ); allowCrossDomain(fakeReq, res, () => {}); - expect(headers["Access-Control-Allow-Origin"]).toEqual("*"); + expect(headers['Access-Control-Allow-Origin']).toEqual('*'); }); - it("should set custom origin to Access-Control-Allow-Origin if allowOrigin is provided", () => { + it('should set custom origin to Access-Control-Allow-Origin if allowOrigin is provided', () => { AppCachePut(fakeReq.body._ApplicationId, { - allowOrigin: "https://parseplatform.org/", + allowOrigin: 'https://parseplatform.org/', }); const headers = {}; const res = { @@ -332,14 +332,14 @@ describe("middlewares", () => { fakeReq.body._ApplicationId ); allowCrossDomain(fakeReq, res, () => {}); - expect(headers["Access-Control-Allow-Origin"]).toEqual( - "https://parseplatform.org/" + expect(headers['Access-Control-Allow-Origin']).toEqual( + 'https://parseplatform.org/' ); }); - it("should support multiple origins if several are defined in allowOrigin as an array", () => { + it('should support multiple origins if several are defined in allowOrigin as an array', () => { AppCache.put(fakeReq.body._ApplicationId, { - allowOrigin: ["https://a.com", "https://b.com", "https://c.com"], + allowOrigin: ['https://a.com', 'https://b.com', 'https://c.com'], }); const headers = {}; const res = { @@ -351,58 +351,58 @@ describe("middlewares", () => { fakeReq.body._ApplicationId ); // Test with the first domain - fakeReq.headers.origin = "https://a.com"; + fakeReq.headers.origin = 'https://a.com'; allowCrossDomain(fakeReq, res, () => {}); - expect(headers["Access-Control-Allow-Origin"]).toEqual("https://a.com"); + expect(headers['Access-Control-Allow-Origin']).toEqual('https://a.com'); // Test with the second domain - fakeReq.headers.origin = "https://b.com"; + fakeReq.headers.origin = 'https://b.com'; allowCrossDomain(fakeReq, res, () => {}); - expect(headers["Access-Control-Allow-Origin"]).toEqual("https://b.com"); + expect(headers['Access-Control-Allow-Origin']).toEqual('https://b.com'); // Test with the third domain - fakeReq.headers.origin = "https://c.com"; + fakeReq.headers.origin = 'https://c.com'; allowCrossDomain(fakeReq, res, () => {}); - expect(headers["Access-Control-Allow-Origin"]).toEqual("https://c.com"); + expect(headers['Access-Control-Allow-Origin']).toEqual('https://c.com'); // Test with an unauthorized domain - fakeReq.headers.origin = "https://unauthorized.com"; + fakeReq.headers.origin = 'https://unauthorized.com'; allowCrossDomain(fakeReq, res, () => {}); - expect(headers["Access-Control-Allow-Origin"]).toEqual("https://a.com"); + expect(headers['Access-Control-Allow-Origin']).toEqual('https://a.com'); }); - it("should use user provided on field userFromJWT", done => { + it('should use user provided on field userFromJWT', done => { AppCachePut(fakeReq.body._ApplicationId, { - masterKey: "masterKey", + masterKey: 'masterKey', }); - fakeReq.userFromJWT = "fake-user"; + fakeReq.userFromJWT = 'fake-user'; middlewares.handleParseHeaders(fakeReq, fakeRes, () => { - expect(fakeReq.auth.user).toEqual("fake-user"); + expect(fakeReq.auth.user).toEqual('fake-user'); done(); }); }); - it("should give invalid response when upload file without x-parse-application-id in header", () => { + it('should give invalid response when upload file without x-parse-application-id in header', () => { AppCachePut(fakeReq.body._ApplicationId, { - masterKey: "masterKey", + masterKey: 'masterKey', }); - fakeReq.body = Buffer.from("fake-file"); + fakeReq.body = Buffer.from('fake-file'); middlewares.handleParseHeaders(fakeReq, fakeRes); expect(fakeRes.status).toHaveBeenCalledWith(403); }); - it("should match address", () => { - const ipv6 = "2001:0db8:85a3:0000:0000:8a2e:0370:7334"; - const anotherIpv6 = "::ffff:101.10.0.1"; - const ipv4 = "192.168.0.101"; - const localhostV6 = "::1"; - const localhostV62 = "::ffff:127.0.0.1"; - const localhostV4 = "127.0.0.1"; + it('should match address', () => { + const ipv6 = '2001:0db8:85a3:0000:0000:8a2e:0370:7334'; + const anotherIpv6 = '::ffff:101.10.0.1'; + const ipv4 = '192.168.0.101'; + const localhostV6 = '::1'; + const localhostV62 = '::ffff:127.0.0.1'; + const localhostV4 = '127.0.0.1'; const v6 = [ipv6, anotherIpv6]; v6.forEach(ip => { - expect(middlewares.checkIp(ip, ["::/0"], new Map())).toBe(true); - expect(middlewares.checkIp(ip, ["::"], new Map())).toBe(true); - expect(middlewares.checkIp(ip, ["0.0.0.0"], new Map())).toBe(false); - expect(middlewares.checkIp(ip, ["0.0.0.0/0"], new Map())).toBe(false); - expect(middlewares.checkIp(ip, ["123.123.123.123"], new Map())).toBe( + expect(middlewares.checkIp(ip, ['::/0'], new Map())).toBe(true); + expect(middlewares.checkIp(ip, ['::'], new Map())).toBe(true); + expect(middlewares.checkIp(ip, ['0.0.0.0'], new Map())).toBe(false); + expect(middlewares.checkIp(ip, ['0.0.0.0/0'], new Map())).toBe(false); + expect(middlewares.checkIp(ip, ['123.123.123.123'], new Map())).toBe( false ); }); @@ -410,70 +410,70 @@ describe("middlewares", () => { expect(middlewares.checkIp(ipv6, [anotherIpv6], new Map())).toBe(false); expect(middlewares.checkIp(ipv6, [ipv6], new Map())).toBe(true); expect( - middlewares.checkIp(ipv6, ["2001:db8:85a3:0:0:8a2e:0:0/100"], new Map()) + middlewares.checkIp(ipv6, ['2001:db8:85a3:0:0:8a2e:0:0/100'], new Map()) ).toBe(true); - expect(middlewares.checkIp(ipv4, ["::"], new Map())).toBe(false); - expect(middlewares.checkIp(ipv4, ["::/0"], new Map())).toBe(false); - expect(middlewares.checkIp(ipv4, ["0.0.0.0"], new Map())).toBe(true); - expect(middlewares.checkIp(ipv4, ["0.0.0.0/0"], new Map())).toBe(true); - expect(middlewares.checkIp(ipv4, ["123.123.123.123"], new Map())).toBe( + expect(middlewares.checkIp(ipv4, ['::'], new Map())).toBe(false); + expect(middlewares.checkIp(ipv4, ['::/0'], new Map())).toBe(false); + expect(middlewares.checkIp(ipv4, ['0.0.0.0'], new Map())).toBe(true); + expect(middlewares.checkIp(ipv4, ['0.0.0.0/0'], new Map())).toBe(true); + expect(middlewares.checkIp(ipv4, ['123.123.123.123'], new Map())).toBe( false ); expect(middlewares.checkIp(ipv4, [ipv4], new Map())).toBe(true); - expect(middlewares.checkIp(ipv4, ["192.168.0.0/24"], new Map())).toBe(true); + expect(middlewares.checkIp(ipv4, ['192.168.0.0/24'], new Map())).toBe(true); - expect(middlewares.checkIp(localhostV4, ["::1"], new Map())).toBe(false); - expect(middlewares.checkIp(localhostV6, ["::1"], new Map())).toBe(true); + expect(middlewares.checkIp(localhostV4, ['::1'], new Map())).toBe(false); + expect(middlewares.checkIp(localhostV6, ['::1'], new Map())).toBe(true); // ::ffff:127.0.0.1 is a padded ipv4 address but not ::1 - expect(middlewares.checkIp(localhostV62, ["::1"], new Map())).toBe(false); + expect(middlewares.checkIp(localhostV62, ['::1'], new Map())).toBe(false); // ::ffff:127.0.0.1 is a padded ipv4 address and is a match for 127.0.0.1 - expect(middlewares.checkIp(localhostV62, ["127.0.0.1"], new Map())).toBe( + expect(middlewares.checkIp(localhostV62, ['127.0.0.1'], new Map())).toBe( true ); }); - it("should match address with cache", () => { - const ipv6 = "2001:0db8:85a3:0000:0000:8a2e:0370:7334"; + it('should match address with cache', () => { + const ipv6 = '2001:0db8:85a3:0000:0000:8a2e:0370:7334'; const cache1 = new Map(); const spyBlockListCheck = spyOn( BlockList.prototype, - "check" + 'check' ).and.callThrough(); - expect(middlewares.checkIp(ipv6, ["::"], cache1)).toBe(true); - expect(cache1.get("2001:0db8:85a3:0000:0000:8a2e:0370:7334")).toBe( + expect(middlewares.checkIp(ipv6, ['::'], cache1)).toBe(true); + expect(cache1.get('2001:0db8:85a3:0000:0000:8a2e:0370:7334')).toBe( undefined ); - expect(cache1.get("allowAllIpv6")).toBe(true); + expect(cache1.get('allowAllIpv6')).toBe(true); expect(spyBlockListCheck).toHaveBeenCalledTimes(0); const cache2 = new Map(); - expect(middlewares.checkIp("::1", ["::1"], cache2)).toBe(true); - expect(cache2.get("::1")).toBe(true); + expect(middlewares.checkIp('::1', ['::1'], cache2)).toBe(true); + expect(cache2.get('::1')).toBe(true); expect(spyBlockListCheck).toHaveBeenCalledTimes(1); - expect(middlewares.checkIp("::1", ["::1"], cache2)).toBe(true); + expect(middlewares.checkIp('::1', ['::1'], cache2)).toBe(true); expect(spyBlockListCheck).toHaveBeenCalledTimes(1); spyBlockListCheck.calls.reset(); const cache3 = new Map(); - expect(middlewares.checkIp("127.0.0.1", ["127.0.0.1"], cache3)).toBe(true); - expect(cache3.get("127.0.0.1")).toBe(true); + expect(middlewares.checkIp('127.0.0.1', ['127.0.0.1'], cache3)).toBe(true); + expect(cache3.get('127.0.0.1')).toBe(true); expect(spyBlockListCheck).toHaveBeenCalledTimes(1); - expect(middlewares.checkIp("127.0.0.1", ["127.0.0.1"], cache3)).toBe(true); + expect(middlewares.checkIp('127.0.0.1', ['127.0.0.1'], cache3)).toBe(true); expect(spyBlockListCheck).toHaveBeenCalledTimes(1); spyBlockListCheck.calls.reset(); const cache4 = new Map(); - const ranges = ["127.0.0.1", "192.168.0.0/24"]; + const ranges = ['127.0.0.1', '192.168.0.0/24']; // should not cache negative match - expect(middlewares.checkIp("123.123.123.123", ranges, cache4)).toBe(false); - expect(cache4.get("123.123.123.123")).toBe(undefined); + expect(middlewares.checkIp('123.123.123.123', ranges, cache4)).toBe(false); + expect(cache4.get('123.123.123.123')).toBe(undefined); expect(spyBlockListCheck).toHaveBeenCalledTimes(1); spyBlockListCheck.calls.reset(); // should not cache cidr - expect(middlewares.checkIp("192.168.0.101", ranges, cache4)).toBe(true); - expect(cache4.get("192.168.0.101")).toBe(undefined); + expect(middlewares.checkIp('192.168.0.101', ranges, cache4)).toBe(true); + expect(cache4.get('192.168.0.101')).toBe(undefined); expect(spyBlockListCheck).toHaveBeenCalledTimes(1); }); }); diff --git a/spec/MongoSchemaCollectionAdapter.spec.js b/spec/MongoSchemaCollectionAdapter.spec.js index 1f63a37b4e..f89151d2bd 100644 --- a/spec/MongoSchemaCollectionAdapter.spec.js +++ b/spec/MongoSchemaCollectionAdapter.spec.js @@ -1,13 +1,13 @@ -"use strict"; +'use strict'; const MongoSchemaCollection = - require("../lib/Adapters/Storage/Mongo/MongoSchemaCollection").default; + require('../lib/Adapters/Storage/Mongo/MongoSchemaCollection').default; -describe("MongoSchemaCollection", () => { - it("can transform legacy _client_permissions keys to parse format", done => { +describe('MongoSchemaCollection', () => { + it('can transform legacy _client_permissions keys to parse format', done => { expect( MongoSchemaCollection._TESTmongoSchemaToParseSchema({ - _id: "_Installation", + _id: '_Installation', _client_permissions: { get: true, find: true, @@ -19,76 +19,76 @@ describe("MongoSchemaCollection", () => { _metadata: { class_permissions: { ACL: { - "*": { + '*': { read: true, write: true, }, }, - get: { "*": true }, - find: { "*": true }, - count: { "*": true }, - update: { "*": true }, - create: { "*": true }, - delete: { "*": true }, - addField: { "*": true }, - protectedFields: { "*": [] }, + get: { '*': true }, + find: { '*': true }, + count: { '*': true }, + update: { '*': true }, + create: { '*': true }, + delete: { '*': true }, + addField: { '*': true }, + protectedFields: { '*': [] }, }, indexes: { name1: { deviceToken: 1 }, }, }, - installationId: "string", - deviceToken: "string", - deviceType: "string", - channels: "array", - user: "*_User", - pushType: "string", - GCMSenderId: "string", - timeZone: "string", - localeIdentifier: "string", - badge: "number", - appVersion: "string", - appName: "string", - appIdentifier: "string", - parseVersion: "string", + installationId: 'string', + deviceToken: 'string', + deviceType: 'string', + channels: 'array', + user: '*_User', + pushType: 'string', + GCMSenderId: 'string', + timeZone: 'string', + localeIdentifier: 'string', + badge: 'number', + appVersion: 'string', + appName: 'string', + appIdentifier: 'string', + parseVersion: 'string', }) ).toEqual({ - className: "_Installation", + className: '_Installation', fields: { - installationId: { type: "String" }, - deviceToken: { type: "String" }, - deviceType: { type: "String" }, - channels: { type: "Array" }, - user: { type: "Pointer", targetClass: "_User" }, - pushType: { type: "String" }, - GCMSenderId: { type: "String" }, - timeZone: { type: "String" }, - localeIdentifier: { type: "String" }, - badge: { type: "Number" }, - appVersion: { type: "String" }, - appName: { type: "String" }, - appIdentifier: { type: "String" }, - parseVersion: { type: "String" }, - ACL: { type: "ACL" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - objectId: { type: "String" }, + installationId: { type: 'String' }, + deviceToken: { type: 'String' }, + deviceType: { type: 'String' }, + channels: { type: 'Array' }, + user: { type: 'Pointer', targetClass: '_User' }, + pushType: { type: 'String' }, + GCMSenderId: { type: 'String' }, + timeZone: { type: 'String' }, + localeIdentifier: { type: 'String' }, + badge: { type: 'Number' }, + appVersion: { type: 'String' }, + appName: { type: 'String' }, + appIdentifier: { type: 'String' }, + parseVersion: { type: 'String' }, + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + objectId: { type: 'String' }, }, classLevelPermissions: { ACL: { - "*": { + '*': { read: true, write: true, }, }, - find: { "*": true }, - get: { "*": true }, - count: { "*": true }, - create: { "*": true }, - update: { "*": true }, - delete: { "*": true }, - addField: { "*": true }, - protectedFields: { "*": [] }, + find: { '*': true }, + get: { '*': true }, + count: { '*': true }, + create: { '*': true }, + update: { '*': true }, + delete: { '*': true }, + addField: { '*': true }, + protectedFields: { '*': [] }, }, indexes: { name1: { deviceToken: 1 }, diff --git a/spec/MongoStorageAdapter.spec.js b/spec/MongoStorageAdapter.spec.js index 9ab7d64c2e..2d9b0220b1 100644 --- a/spec/MongoStorageAdapter.spec.js +++ b/spec/MongoStorageAdapter.spec.js @@ -1,13 +1,13 @@ -"use strict"; +'use strict'; const MongoStorageAdapter = - require("../lib/Adapters/Storage/Mongo/MongoStorageAdapter").default; -const { MongoClient, Collection } = require("mongodb"); + require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter').default; +const { MongoClient, Collection } = require('mongodb'); const databaseURI = - "mongodb://localhost:27017/parseServerMongoAdapterTestDatabase"; -const request = require("../lib/request"); -const Config = require("../lib/Config"); -const TestUtils = require("../lib/TestUtils"); + 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase'; +const request = require('../lib/request'); +const Config = require('../lib/Config'); +const TestUtils = require('../lib/TestUtils'); const fakeClient = { s: { options: { dbName: null } }, @@ -16,70 +16,70 @@ const fakeClient = { // These tests are specific to the mongo storage adapter + mongo storage format // and will eventually be moved into their own repo -describe_only_db("mongo")("MongoStorageAdapter", () => { +describe_only_db('mongo')('MongoStorageAdapter', () => { beforeEach(async () => { await new MongoStorageAdapter({ uri: databaseURI }).deleteAllClasses(); Config.get(Parse.applicationId).schemaCache.clear(); }); - it("auto-escapes symbols in auth information", () => { - spyOn(MongoClient, "connect").and.returnValue(Promise.resolve(fakeClient)); + it('auto-escapes symbols in auth information', () => { + spyOn(MongoClient, 'connect').and.returnValue(Promise.resolve(fakeClient)); new MongoStorageAdapter({ - uri: "mongodb://user!with@+ symbols:password!with@+ symbols@localhost:1234/parse", + uri: 'mongodb://user!with@+ symbols:password!with@+ symbols@localhost:1234/parse', }).connect(); expect(MongoClient.connect).toHaveBeenCalledWith( - "mongodb://user!with%40%2B%20symbols:password!with%40%2B%20symbols@localhost:1234/parse", + 'mongodb://user!with%40%2B%20symbols:password!with%40%2B%20symbols@localhost:1234/parse', jasmine.any(Object) ); }); it("doesn't double escape already URI-encoded information", () => { - spyOn(MongoClient, "connect").and.returnValue(Promise.resolve(fakeClient)); + spyOn(MongoClient, 'connect').and.returnValue(Promise.resolve(fakeClient)); new MongoStorageAdapter({ - uri: "mongodb://user!with%40%2B%20symbols:password!with%40%2B%20symbols@localhost:1234/parse", + uri: 'mongodb://user!with%40%2B%20symbols:password!with%40%2B%20symbols@localhost:1234/parse', }).connect(); expect(MongoClient.connect).toHaveBeenCalledWith( - "mongodb://user!with%40%2B%20symbols:password!with%40%2B%20symbols@localhost:1234/parse", + 'mongodb://user!with%40%2B%20symbols:password!with%40%2B%20symbols@localhost:1234/parse', jasmine.any(Object) ); }); // https://github.com/parse-community/parse-server/pull/148#issuecomment-180407057 - it("preserves replica sets", () => { - spyOn(MongoClient, "connect").and.returnValue(Promise.resolve(fakeClient)); + it('preserves replica sets', () => { + spyOn(MongoClient, 'connect').and.returnValue(Promise.resolve(fakeClient)); new MongoStorageAdapter({ - uri: "mongodb://test:testpass@ds056315-a0.mongolab.com:59325,ds059315-a1.mongolab.com:59315/testDBname?replicaSet=rs-ds059415", + uri: 'mongodb://test:testpass@ds056315-a0.mongolab.com:59325,ds059315-a1.mongolab.com:59315/testDBname?replicaSet=rs-ds059415', }).connect(); expect(MongoClient.connect).toHaveBeenCalledWith( - "mongodb://test:testpass@ds056315-a0.mongolab.com:59325,ds059315-a1.mongolab.com:59315/testDBname?replicaSet=rs-ds059415", + 'mongodb://test:testpass@ds056315-a0.mongolab.com:59325,ds059315-a1.mongolab.com:59315/testDBname?replicaSet=rs-ds059415', jasmine.any(Object) ); }); - it("stores objectId in _id", done => { + it('stores objectId in _id', done => { const adapter = new MongoStorageAdapter({ uri: databaseURI }); adapter - .createObject("Foo", { fields: {} }, { objectId: "abcde" }) - .then(() => adapter._rawFind("Foo", {})) + .createObject('Foo', { fields: {} }, { objectId: 'abcde' }) + .then(() => adapter._rawFind('Foo', {})) .then(results => { expect(results.length).toEqual(1); const obj = results[0]; - expect(obj._id).toEqual("abcde"); + expect(obj._id).toEqual('abcde'); expect(obj.objectId).toBeUndefined(); done(); }); }); - it("find succeeds when query is within maxTimeMS", done => { + it('find succeeds when query is within maxTimeMS', done => { const maxTimeMS = 250; const adapter = new MongoStorageAdapter({ uri: databaseURI, mongoOptions: { maxTimeMS }, }); adapter - .createObject("Foo", { fields: {} }, { objectId: "abcde" }) + .createObject('Foo', { fields: {} }, { objectId: 'abcde' }) .then(() => - adapter._rawFind("Foo", { $where: `sleep(${maxTimeMS / 2})` }) + adapter._rawFind('Foo', { $where: `sleep(${maxTimeMS / 2})` }) ) .then( () => done(), @@ -89,125 +89,125 @@ describe_only_db("mongo")("MongoStorageAdapter", () => { ); }); - it("find fails when query exceeds maxTimeMS", done => { + it('find fails when query exceeds maxTimeMS', done => { const maxTimeMS = 250; const adapter = new MongoStorageAdapter({ uri: databaseURI, mongoOptions: { maxTimeMS }, }); adapter - .createObject("Foo", { fields: {} }, { objectId: "abcde" }) + .createObject('Foo', { fields: {} }, { objectId: 'abcde' }) .then(() => - adapter._rawFind("Foo", { $where: `sleep(${maxTimeMS * 2})` }) + adapter._rawFind('Foo', { $where: `sleep(${maxTimeMS * 2})` }) ) .then( () => { - done.fail("Find succeeded despite taking too long!"); + done.fail('Find succeeded despite taking too long!'); }, err => { - expect(err.name).toEqual("MongoServerError"); + expect(err.name).toEqual('MongoServerError'); expect(err.code).toEqual(50); - expect(err.message).toMatch("operation exceeded time limit"); + expect(err.message).toMatch('operation exceeded time limit'); done(); } ); }); - it("stores pointers with a _p_ prefix", done => { + it('stores pointers with a _p_ prefix', done => { const obj = { - objectId: "bar", + objectId: 'bar', aPointer: { - __type: "Pointer", - className: "JustThePointer", - objectId: "qwerty", + __type: 'Pointer', + className: 'JustThePointer', + objectId: 'qwerty', }, }; const adapter = new MongoStorageAdapter({ uri: databaseURI }); adapter .createObject( - "APointerDarkly", + 'APointerDarkly', { fields: { - objectId: { type: "String" }, - aPointer: { type: "Pointer", targetClass: "JustThePointer" }, + objectId: { type: 'String' }, + aPointer: { type: 'Pointer', targetClass: 'JustThePointer' }, }, }, obj ) - .then(() => adapter._rawFind("APointerDarkly", {})) + .then(() => adapter._rawFind('APointerDarkly', {})) .then(results => { expect(results.length).toEqual(1); const output = results[0]; - expect(typeof output._id).toEqual("string"); - expect(typeof output._p_aPointer).toEqual("string"); - expect(output._p_aPointer).toEqual("JustThePointer$qwerty"); + expect(typeof output._id).toEqual('string'); + expect(typeof output._p_aPointer).toEqual('string'); + expect(output._p_aPointer).toEqual('JustThePointer$qwerty'); expect(output.aPointer).toBeUndefined(); done(); }); }); - it("handles object and subdocument", done => { + it('handles object and subdocument', done => { const adapter = new MongoStorageAdapter({ uri: databaseURI }); - const schema = { fields: { subdoc: { type: "Object" } } }; - const obj = { subdoc: { foo: "bar", wu: "tan" } }; + const schema = { fields: { subdoc: { type: 'Object' } } }; + const obj = { subdoc: { foo: 'bar', wu: 'tan' } }; adapter - .createObject("MyClass", schema, obj) - .then(() => adapter._rawFind("MyClass", {})) + .createObject('MyClass', schema, obj) + .then(() => adapter._rawFind('MyClass', {})) .then(results => { expect(results.length).toEqual(1); const mob = results[0]; - expect(typeof mob.subdoc).toBe("object"); - expect(mob.subdoc.foo).toBe("bar"); - expect(mob.subdoc.wu).toBe("tan"); - const obj = { "subdoc.wu": "clan" }; - return adapter.findOneAndUpdate("MyClass", schema, {}, obj); + expect(typeof mob.subdoc).toBe('object'); + expect(mob.subdoc.foo).toBe('bar'); + expect(mob.subdoc.wu).toBe('tan'); + const obj = { 'subdoc.wu': 'clan' }; + return adapter.findOneAndUpdate('MyClass', schema, {}, obj); }) - .then(() => adapter._rawFind("MyClass", {})) + .then(() => adapter._rawFind('MyClass', {})) .then(results => { expect(results.length).toEqual(1); const mob = results[0]; - expect(typeof mob.subdoc).toBe("object"); - expect(mob.subdoc.foo).toBe("bar"); - expect(mob.subdoc.wu).toBe("clan"); + expect(typeof mob.subdoc).toBe('object'); + expect(mob.subdoc.foo).toBe('bar'); + expect(mob.subdoc.wu).toBe('clan'); done(); }); }); - it("handles creating an array, object, date", done => { + it('handles creating an array, object, date', done => { const adapter = new MongoStorageAdapter({ uri: databaseURI }); const obj = { array: [1, 2, 3], - object: { foo: "bar" }, + object: { foo: 'bar' }, date: { - __type: "Date", - iso: "2016-05-26T20:55:01.154Z", + __type: 'Date', + iso: '2016-05-26T20:55:01.154Z', }, }; const schema = { fields: { - array: { type: "Array" }, - object: { type: "Object" }, - date: { type: "Date" }, + array: { type: 'Array' }, + object: { type: 'Object' }, + date: { type: 'Date' }, }, }; adapter - .createObject("MyClass", schema, obj) - .then(() => adapter._rawFind("MyClass", {})) + .createObject('MyClass', schema, obj) + .then(() => adapter._rawFind('MyClass', {})) .then(results => { expect(results.length).toEqual(1); const mob = results[0]; expect(mob.array instanceof Array).toBe(true); - expect(typeof mob.object).toBe("object"); + expect(typeof mob.object).toBe('object'); expect(mob.date instanceof Date).toBe(true); - return adapter.find("MyClass", schema, {}, {}); + return adapter.find('MyClass', schema, {}, {}); }) .then(results => { expect(results.length).toEqual(1); const mob = results[0]; expect(mob.array instanceof Array).toBe(true); - expect(typeof mob.object).toBe("object"); - expect(mob.date.__type).toBe("Date"); - expect(mob.date.iso).toBe("2016-05-26T20:55:01.154Z"); + expect(typeof mob.object).toBe('object'); + expect(mob.date.__type).toBe('Date'); + expect(mob.date.iso).toBe('2016-05-26T20:55:01.154Z'); done(); }) .catch(error => { @@ -217,8 +217,8 @@ describe_only_db("mongo")("MongoStorageAdapter", () => { }); }); - it("handles nested dates", async () => { - await new Parse.Object("MyClass", { + it('handles nested dates', async () => { + await new Parse.Object('MyClass', { foo: { test: { date: new Date(), @@ -230,14 +230,14 @@ describe_only_db("mongo")("MongoStorageAdapter", () => { date: new Date(), }).save(); const adapter = Config.get(Parse.applicationId).database.adapter; - const [object] = await adapter._rawFind("MyClass", {}); + const [object] = await adapter._rawFind('MyClass', {}); expect(object.date instanceof Date).toBeTrue(); expect(object.bar.date instanceof Date).toBeTrue(); expect(object.foo.test.date instanceof Date).toBeTrue(); }); - it("handles nested dates in array ", async () => { - await new Parse.Object("MyClass", { + it('handles nested dates in array ', async () => { + await new Parse.Object('MyClass', { foo: { test: { date: [new Date()], @@ -249,25 +249,25 @@ describe_only_db("mongo")("MongoStorageAdapter", () => { date: [new Date()], }).save(); const adapter = Config.get(Parse.applicationId).database.adapter; - const [object] = await adapter._rawFind("MyClass", {}); + const [object] = await adapter._rawFind('MyClass', {}); expect(object.date[0] instanceof Date).toBeTrue(); expect(object.bar.date[0] instanceof Date).toBeTrue(); expect(object.foo.test.date[0] instanceof Date).toBeTrue(); - const obj = await new Parse.Query("MyClass").first({ useMasterKey: true }); - expect(obj.get("date")[0] instanceof Date).toBeTrue(); - expect(obj.get("bar").date[0] instanceof Date).toBeTrue(); - expect(obj.get("foo").test.date[0] instanceof Date).toBeTrue(); + const obj = await new Parse.Query('MyClass').first({ useMasterKey: true }); + expect(obj.get('date')[0] instanceof Date).toBeTrue(); + expect(obj.get('bar').date[0] instanceof Date).toBeTrue(); + expect(obj.get('foo').test.date[0] instanceof Date).toBeTrue(); }); - it("upserts with $setOnInsert", async () => { - const uuid = require("uuid"); + it('upserts with $setOnInsert', async () => { + const uuid = require('uuid'); const uuid1 = uuid.v4(); const uuid2 = uuid.v4(); const schema = { - className: "MyClass", + className: 'MyClass', fields: { - x: { type: "Number" }, - count: { type: "Number" }, + x: { type: 'Number' }, + count: { type: 'Number' }, }, classLevelPermissions: {}, }; @@ -281,19 +281,19 @@ describe_only_db("mongo")("MongoStorageAdapter", () => { }; const update = { objectId: { - __op: "SetOnInsert", + __op: 'SetOnInsert', amount: uuid1, }, count: { - __op: "Increment", + __op: 'Increment', amount: 1, }, }; - await Parse.Server.database.update("MyClass", query, update, { + await Parse.Server.database.update('MyClass', query, update, { upsert: true, }); update.objectId.amount = uuid2; - await Parse.Server.database.update("MyClass", query, update, { + await Parse.Server.database.update('MyClass', query, update, { upsert: true, }); @@ -304,46 +304,46 @@ describe_only_db("mongo")("MongoStorageAdapter", () => { expect(res[0].x).toBe(1); }); - it("handles updating a single object with array, object date", done => { + it('handles updating a single object with array, object date', done => { const adapter = new MongoStorageAdapter({ uri: databaseURI }); const schema = { fields: { - array: { type: "Array" }, - object: { type: "Object" }, - date: { type: "Date" }, + array: { type: 'Array' }, + object: { type: 'Object' }, + date: { type: 'Date' }, }, }; adapter - .createObject("MyClass", schema, {}) - .then(() => adapter._rawFind("MyClass", {})) + .createObject('MyClass', schema, {}) + .then(() => adapter._rawFind('MyClass', {})) .then(results => { expect(results.length).toEqual(1); const update = { array: [1, 2, 3], - object: { foo: "bar" }, + object: { foo: 'bar' }, date: { - __type: "Date", - iso: "2016-05-26T20:55:01.154Z", + __type: 'Date', + iso: '2016-05-26T20:55:01.154Z', }, }; const query = {}; - return adapter.findOneAndUpdate("MyClass", schema, query, update); + return adapter.findOneAndUpdate('MyClass', schema, query, update); }) .then(results => { const mob = results; expect(mob.array instanceof Array).toBe(true); - expect(typeof mob.object).toBe("object"); - expect(mob.date.__type).toBe("Date"); - expect(mob.date.iso).toBe("2016-05-26T20:55:01.154Z"); - return adapter._rawFind("MyClass", {}); + expect(typeof mob.object).toBe('object'); + expect(mob.date.__type).toBe('Date'); + expect(mob.date.iso).toBe('2016-05-26T20:55:01.154Z'); + return adapter._rawFind('MyClass', {}); }) .then(results => { expect(results.length).toEqual(1); const mob = results[0]; expect(mob.array instanceof Array).toBe(true); - expect(typeof mob.object).toBe("object"); + expect(typeof mob.object).toBe('object'); expect(mob.date instanceof Date).toBe(true); done(); }) @@ -354,18 +354,18 @@ describe_only_db("mongo")("MongoStorageAdapter", () => { }); }); - it("handleShutdown, close connection", async () => { + it('handleShutdown, close connection', async () => { const adapter = new MongoStorageAdapter({ uri: databaseURI }); const schema = { fields: { - array: { type: "Array" }, - object: { type: "Object" }, - date: { type: "Date" }, + array: { type: 'Array' }, + object: { type: 'Object' }, + date: { type: 'Date' }, }, }; - await adapter.createObject("MyClass", schema, {}); + await adapter.createObject('MyClass', schema, {}); const status = await adapter.database.admin().serverStatus(); expect(status.connections.current > 0).toEqual(true); @@ -375,137 +375,137 @@ describe_only_db("mongo")("MongoStorageAdapter", () => { expect(false).toBe(true); } catch (e) { expect(e.message).toEqual( - "Client must be connected before running operations" + 'Client must be connected before running operations' ); } }); - it("getClass if exists", async () => { + it('getClass if exists', async () => { const adapter = new MongoStorageAdapter({ uri: databaseURI }); const schema = { fields: { - array: { type: "Array" }, - object: { type: "Object" }, - date: { type: "Date" }, + array: { type: 'Array' }, + object: { type: 'Object' }, + date: { type: 'Date' }, }, }; - await adapter.createClass("MyClass", schema); - const myClassSchema = await adapter.getClass("MyClass"); + await adapter.createClass('MyClass', schema); + const myClassSchema = await adapter.getClass('MyClass'); expect(myClassSchema).toBeDefined(); }); - it("getClass if not exists", async () => { + it('getClass if not exists', async () => { const adapter = new MongoStorageAdapter({ uri: databaseURI }); - await expectAsync(adapter.getClass("UnknownClass")).toBeRejectedWith( + await expectAsync(adapter.getClass('UnknownClass')).toBeRejectedWith( undefined ); }); - it_only_mongodb_version("<5.1 || >=6")( - "should use index for caseInsensitive query", + it_only_mongodb_version('<5.1 || >=6')( + 'should use index for caseInsensitive query', async () => { const user = new Parse.User(); - user.set("username", "Bugs"); - user.set("password", "Bunny"); + user.set('username', 'Bugs'); + user.set('password', 'Bunny'); await user.signUp(); const database = Config.get(Parse.applicationId).database; - await database.adapter.dropAllIndexes("_User"); + await database.adapter.dropAllIndexes('_User'); const preIndexPlan = await database.find( - "_User", - { username: "bugs" }, + '_User', + { username: 'bugs' }, { caseInsensitive: true, explain: true } ); - const schema = await new Parse.Schema("_User").get(); + const schema = await new Parse.Schema('_User').get(); await database.adapter.ensureIndex( - "_User", + '_User', schema, - ["username"], - "case_insensitive_username", + ['username'], + 'case_insensitive_username', true ); const postIndexPlan = await database.find( - "_User", - { username: "bugs" }, + '_User', + { username: 'bugs' }, { caseInsensitive: true, explain: true } ); expect(preIndexPlan.executionStats.executionStages.stage).toBe( - "COLLSCAN" + 'COLLSCAN' ); - expect(postIndexPlan.executionStats.executionStages.stage).toBe("FETCH"); + expect(postIndexPlan.executionStats.executionStages.stage).toBe('FETCH'); } ); - it("should delete field without index", async () => { + it('should delete field without index', async () => { const database = Config.get(Parse.applicationId).database; - const obj = new Parse.Object("MyObject"); - obj.set("test", 1); + const obj = new Parse.Object('MyObject'); + obj.set('test', 1); await obj.save(); - const schemaBeforeDeletion = await new Parse.Schema("MyObject").get(); - await database.adapter.deleteFields("MyObject", schemaBeforeDeletion, [ - "test", + const schemaBeforeDeletion = await new Parse.Schema('MyObject').get(); + await database.adapter.deleteFields('MyObject', schemaBeforeDeletion, [ + 'test', ]); - const schemaAfterDeletion = await new Parse.Schema("MyObject").get(); + const schemaAfterDeletion = await new Parse.Schema('MyObject').get(); expect(schemaBeforeDeletion.fields.test).toBeDefined(); expect(schemaAfterDeletion.fields.test).toBeUndefined(); }); - it("should delete field with index", async () => { + it('should delete field with index', async () => { const database = Config.get(Parse.applicationId).database; - const obj = new Parse.Object("MyObject"); - obj.set("test", 1); + const obj = new Parse.Object('MyObject'); + obj.set('test', 1); await obj.save(); - const schemaBeforeDeletion = await new Parse.Schema("MyObject").get(); - await database.adapter.ensureIndex("MyObject", schemaBeforeDeletion, [ - "test", + const schemaBeforeDeletion = await new Parse.Schema('MyObject').get(); + await database.adapter.ensureIndex('MyObject', schemaBeforeDeletion, [ + 'test', ]); - await database.adapter.deleteFields("MyObject", schemaBeforeDeletion, [ - "test", + await database.adapter.deleteFields('MyObject', schemaBeforeDeletion, [ + 'test', ]); - const schemaAfterDeletion = await new Parse.Schema("MyObject").get(); + const schemaAfterDeletion = await new Parse.Schema('MyObject').get(); expect(schemaBeforeDeletion.fields.test).toBeDefined(); expect(schemaAfterDeletion.fields.test).toBeUndefined(); }); - if (process.env.MONGODB_TOPOLOGY === "replicaset") { - describe("transactions", () => { + if (process.env.MONGODB_TOPOLOGY === 'replicaset') { + describe('transactions', () => { const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; beforeEach(async () => { await reconfigureServer({ databaseAdapter: undefined, databaseURI: - "mongodb://localhost:27017/parseServerMongoAdapterTestDatabase?replicaSet=replicaset", + 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase?replicaSet=replicaset', }); await TestUtils.destroyAllDataPermanently(true); }); - it("should use transaction in a batch with transaction = true", async () => { - const myObject = new Parse.Object("MyObject"); + it('should use transaction in a batch with transaction = true', async () => { + const myObject = new Parse.Object('MyObject'); await myObject.save(); - spyOn(Collection.prototype, "findOneAndUpdate").and.callThrough(); + spyOn(Collection.prototype, 'findOneAndUpdate').and.callThrough(); await request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/batch", + url: 'http://localhost:8378/1/batch', body: JSON.stringify({ requests: [ { - method: "PUT", - path: "/1/classes/MyObject/" + myObject.id, - body: { myAttribute: "myValue" }, + method: 'PUT', + path: '/1/classes/MyObject/' + myObject.id, + body: { myAttribute: 'myValue' }, }, ], transaction: true, @@ -516,28 +516,28 @@ describe_only_db("mongo")("MongoStorageAdapter", () => { Collection.prototype.findOneAndUpdate.calls.all().forEach(call => { found = true; expect(call.args[2].session.transaction.state).toBe( - "TRANSACTION_COMMITTED" + 'TRANSACTION_COMMITTED' ); }); expect(found).toBe(true); }); - it("should not use transaction in a batch with transaction = false", async () => { - const myObject = new Parse.Object("MyObject"); + it('should not use transaction in a batch with transaction = false', async () => { + const myObject = new Parse.Object('MyObject'); await myObject.save(); - spyOn(Collection.prototype, "findOneAndUpdate").and.callThrough(); + spyOn(Collection.prototype, 'findOneAndUpdate').and.callThrough(); await request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/batch", + url: 'http://localhost:8378/1/batch', body: JSON.stringify({ requests: [ { - method: "PUT", - path: "/1/classes/MyObject/" + myObject.id, - body: { myAttribute: "myValue" }, + method: 'PUT', + path: '/1/classes/MyObject/' + myObject.id, + body: { myAttribute: 'myValue' }, }, ], transaction: false, @@ -552,22 +552,22 @@ describe_only_db("mongo")("MongoStorageAdapter", () => { expect(found).toBe(true); }); - it("should not use transaction in a batch with no transaction option sent", async () => { - const myObject = new Parse.Object("MyObject"); + it('should not use transaction in a batch with no transaction option sent', async () => { + const myObject = new Parse.Object('MyObject'); await myObject.save(); - spyOn(Collection.prototype, "findOneAndUpdate").and.callThrough(); + spyOn(Collection.prototype, 'findOneAndUpdate').and.callThrough(); await request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/batch", + url: 'http://localhost:8378/1/batch', body: JSON.stringify({ requests: [ { - method: "PUT", - path: "/1/classes/MyObject/" + myObject.id, - body: { myAttribute: "myValue" }, + method: 'PUT', + path: '/1/classes/MyObject/' + myObject.id, + body: { myAttribute: 'myValue' }, }, ], }), @@ -581,17 +581,17 @@ describe_only_db("mongo")("MongoStorageAdapter", () => { expect(found).toBe(true); }); - it("should not use transaction in a put request", async () => { - const myObject = new Parse.Object("MyObject"); + it('should not use transaction in a put request', async () => { + const myObject = new Parse.Object('MyObject'); await myObject.save(); - spyOn(Collection.prototype, "findOneAndUpdate").and.callThrough(); + spyOn(Collection.prototype, 'findOneAndUpdate').and.callThrough(); await request({ - method: "PUT", + method: 'PUT', headers: headers, - url: "http://localhost:8378/1/classes/MyObject/" + myObject.id, - body: { myAttribute: "myValue" }, + url: 'http://localhost:8378/1/classes/MyObject/' + myObject.id, + body: { myAttribute: 'myValue' }, }); let found = false; @@ -602,10 +602,10 @@ describe_only_db("mongo")("MongoStorageAdapter", () => { expect(found).toBe(true); }); - it("should not use transactions when using SDK insert", async () => { - spyOn(Collection.prototype, "insertOne").and.callThrough(); + it('should not use transactions when using SDK insert', async () => { + spyOn(Collection.prototype, 'insertOne').and.callThrough(); - const myObject = new Parse.Object("MyObject"); + const myObject = new Parse.Object('MyObject'); await myObject.save(); const calls = Collection.prototype.insertOne.calls.all(); @@ -615,13 +615,13 @@ describe_only_db("mongo")("MongoStorageAdapter", () => { }); }); - it("should not use transactions when using SDK update", async () => { - spyOn(Collection.prototype, "findOneAndUpdate").and.callThrough(); + it('should not use transactions when using SDK update', async () => { + spyOn(Collection.prototype, 'findOneAndUpdate').and.callThrough(); - const myObject = new Parse.Object("MyObject"); + const myObject = new Parse.Object('MyObject'); await myObject.save(); - myObject.set("myAttribute", "myValue"); + myObject.set('myAttribute', 'myValue'); await myObject.save(); const calls = Collection.prototype.findOneAndUpdate.calls.all(); @@ -631,10 +631,10 @@ describe_only_db("mongo")("MongoStorageAdapter", () => { }); }); - it("should not use transactions when using SDK delete", async () => { - spyOn(Collection.prototype, "deleteMany").and.callThrough(); + it('should not use transactions when using SDK delete', async () => { + spyOn(Collection.prototype, 'deleteMany').and.callThrough(); - const myObject = new Parse.Object("MyObject"); + const myObject = new Parse.Object('MyObject'); await myObject.save(); await myObject.destroy(); @@ -647,26 +647,26 @@ describe_only_db("mongo")("MongoStorageAdapter", () => { }); }); - describe("watch _SCHEMA", () => { - it("should change", async done => { + describe('watch _SCHEMA', () => { + it('should change', async done => { const adapter = new MongoStorageAdapter({ uri: databaseURI, - collectionPrefix: "", + collectionPrefix: '', mongoOptions: { enableSchemaHooks: true }, }); await reconfigureServer({ databaseAdapter: adapter }); expect(adapter.enableSchemaHooks).toBe(true); - spyOn(adapter, "_onchange"); + spyOn(adapter, '_onchange'); const schema = { fields: { - array: { type: "Array" }, - object: { type: "Object" }, - date: { type: "Date" }, + array: { type: 'Array' }, + object: { type: 'Object' }, + date: { type: 'Date' }, }, }; - await adapter.createClass("Stuff", schema); - const myClassSchema = await adapter.getClass("Stuff"); + await adapter.createClass('Stuff', schema); + const myClassSchema = await adapter.getClass('Stuff'); expect(myClassSchema).toBeDefined(); setTimeout(() => { expect(adapter._onchange).toHaveBeenCalled(); diff --git a/spec/MongoTransform.spec.js b/spec/MongoTransform.spec.js index 8491e85311..8d30b897ee 100644 --- a/spec/MongoTransform.spec.js +++ b/spec/MongoTransform.spec.js @@ -1,34 +1,34 @@ // These tests are unit tests designed to only test transform.js. -"use strict"; +'use strict'; -const transform = require("../lib/Adapters/Storage/Mongo/MongoTransform"); -const dd = require("deep-diff"); -const mongodb = require("mongodb"); -const Utils = require("../lib/Utils"); +const transform = require('../lib/Adapters/Storage/Mongo/MongoTransform'); +const dd = require('deep-diff'); +const mongodb = require('mongodb'); +const Utils = require('../lib/Utils'); -describe("parseObjectToMongoObjectForCreate", () => { - it("a basic number", done => { +describe('parseObjectToMongoObjectForCreate', () => { + it('a basic number', done => { const input = { five: 5 }; const output = transform.parseObjectToMongoObjectForCreate(null, input, { - fields: { five: { type: "Number" } }, + fields: { five: { type: 'Number' } }, }); jequal(input, output); done(); }); - it("an object with null values", done => { + it('an object with null values', done => { const input = { objectWithNullValues: { isNull: null, notNull: 3 } }; const output = transform.parseObjectToMongoObjectForCreate(null, input, { - fields: { objectWithNullValues: { type: "object" } }, + fields: { objectWithNullValues: { type: 'object' } }, }); jequal(input, output); done(); }); - it("built-in timestamps with date", done => { + it('built-in timestamps with date', done => { const input = { - createdAt: "2015-10-06T21:24:50.332Z", - updatedAt: "2015-10-06T21:24:50.332Z", + createdAt: '2015-10-06T21:24:50.332Z', + updatedAt: '2015-10-06T21:24:50.332Z', }; const output = transform.parseObjectToMongoObjectForCreate(null, input, { fields: {}, @@ -38,17 +38,17 @@ describe("parseObjectToMongoObjectForCreate", () => { done(); }); - it("array of pointers", done => { + it('array of pointers', done => { const pointer = { - __type: "Pointer", - objectId: "myId", - className: "Blah", + __type: 'Pointer', + objectId: 'myId', + className: 'Blah', }; const out = transform.parseObjectToMongoObjectForCreate( null, { pointers: [pointer] }, { - fields: { pointers: { type: "Array" } }, + fields: { pointers: { type: 'Array' } }, } ); jequal([pointer], out.pointers); @@ -57,8 +57,8 @@ describe("parseObjectToMongoObjectForCreate", () => { //TODO: object creation requests shouldn't be seeing __op delete, it makes no sense to //have __op delete in a new object. Figure out what this should actually be testing. - xit("a delete op", done => { - const input = { deleteMe: { __op: "Delete" } }; + xit('a delete op', done => { + const input = { deleteMe: { __op: 'Delete' } }; const output = transform.parseObjectToMongoObjectForCreate(null, input, { fields: {}, }); @@ -66,30 +66,30 @@ describe("parseObjectToMongoObjectForCreate", () => { done(); }); - it("Doesnt allow ACL, as Parse Server should tranform ACL to _wperm + _rperm", done => { - const input = { ACL: { "0123": { read: true, write: true } } }; + it('Doesnt allow ACL, as Parse Server should tranform ACL to _wperm + _rperm', done => { + const input = { ACL: { '0123': { read: true, write: true } } }; expect(() => transform.parseObjectToMongoObjectForCreate(null, input, { fields: {} }) ).toThrow(); done(); }); - it("parse geopoint to mongo", done => { + it('parse geopoint to mongo', done => { const lat = -45; const lng = 45; - const geoPoint = { __type: "GeoPoint", latitude: lat, longitude: lng }; + const geoPoint = { __type: 'GeoPoint', latitude: lat, longitude: lng }; const out = transform.parseObjectToMongoObjectForCreate( null, { location: geoPoint }, { - fields: { location: { type: "GeoPoint" } }, + fields: { location: { type: 'GeoPoint' } }, } ); expect(out.location).toEqual([lng, lat]); done(); }); - it("parse polygon to mongo", done => { + it('parse polygon to mongo', done => { const lat1 = -45; const lng1 = 45; const lat2 = -55; @@ -97,7 +97,7 @@ describe("parseObjectToMongoObjectForCreate", () => { const lat3 = -65; const lng3 = 65; const polygon = { - __type: "Polygon", + __type: 'Polygon', coordinates: [ [lat1, lng1], [lat2, lng2], @@ -108,7 +108,7 @@ describe("parseObjectToMongoObjectForCreate", () => { null, { location: polygon }, { - fields: { location: { type: "Polygon" } }, + fields: { location: { type: 'Polygon' } }, } ); expect(out.location.coordinates).toEqual([ @@ -122,113 +122,113 @@ describe("parseObjectToMongoObjectForCreate", () => { done(); }); - it("in array", done => { - const geoPoint = { __type: "GeoPoint", longitude: 180, latitude: -180 }; + it('in array', done => { + const geoPoint = { __type: 'GeoPoint', longitude: 180, latitude: -180 }; const out = transform.parseObjectToMongoObjectForCreate( null, { locations: [geoPoint, geoPoint] }, { - fields: { locations: { type: "Array" } }, + fields: { locations: { type: 'Array' } }, } ); expect(out.locations).toEqual([geoPoint, geoPoint]); done(); }); - it("in sub-object", done => { - const geoPoint = { __type: "GeoPoint", longitude: 180, latitude: -180 }; + it('in sub-object', done => { + const geoPoint = { __type: 'GeoPoint', longitude: 180, latitude: -180 }; const out = transform.parseObjectToMongoObjectForCreate( null, { locations: { start: geoPoint } }, { - fields: { locations: { type: "Object" } }, + fields: { locations: { type: 'Object' } }, } ); expect(out).toEqual({ locations: { start: geoPoint } }); done(); }); - it("objectId", done => { - const out = transform.transformWhere(null, { objectId: "foo" }); - expect(out._id).toEqual("foo"); + it('objectId', done => { + const out = transform.transformWhere(null, { objectId: 'foo' }); + expect(out._id).toEqual('foo'); done(); }); - it("objectId in a list", done => { + it('objectId in a list', done => { const input = { - objectId: { $in: ["one", "two", "three"] }, + objectId: { $in: ['one', 'two', 'three'] }, }; const output = transform.transformWhere(null, input); jequal(input.objectId, output._id); done(); }); - it("built-in timestamps", done => { + it('built-in timestamps', done => { const input = { createdAt: new Date(), updatedAt: new Date() }; const output = transform.mongoObjectToParseObject(null, input, { fields: {}, }); - expect(typeof output.createdAt).toEqual("string"); - expect(typeof output.updatedAt).toEqual("string"); + expect(typeof output.createdAt).toEqual('string'); + expect(typeof output.updatedAt).toEqual('string'); done(); }); - it("pointer", done => { - const input = { _p_userPointer: "_User$123" }; + it('pointer', done => { + const input = { _p_userPointer: '_User$123' }; const output = transform.mongoObjectToParseObject(null, input, { - fields: { userPointer: { type: "Pointer", targetClass: "_User" } }, + fields: { userPointer: { type: 'Pointer', targetClass: '_User' } }, }); - expect(typeof output.userPointer).toEqual("object"); + expect(typeof output.userPointer).toEqual('object'); expect(output.userPointer).toEqual({ - __type: "Pointer", - className: "_User", - objectId: "123", + __type: 'Pointer', + className: '_User', + objectId: '123', }); done(); }); - it("null pointer", done => { + it('null pointer', done => { const input = { _p_userPointer: null }; const output = transform.mongoObjectToParseObject(null, input, { - fields: { userPointer: { type: "Pointer", targetClass: "_User" } }, + fields: { userPointer: { type: 'Pointer', targetClass: '_User' } }, }); expect(output.userPointer).toBeUndefined(); done(); }); - it("file", done => { - const input = { picture: "pic.jpg" }; + it('file', done => { + const input = { picture: 'pic.jpg' }; const output = transform.mongoObjectToParseObject(null, input, { - fields: { picture: { type: "File" } }, + fields: { picture: { type: 'File' } }, }); - expect(typeof output.picture).toEqual("object"); - expect(output.picture).toEqual({ __type: "File", name: "pic.jpg" }); + expect(typeof output.picture).toEqual('object'); + expect(output.picture).toEqual({ __type: 'File', name: 'pic.jpg' }); done(); }); - it("mongo geopoint to parse", done => { + it('mongo geopoint to parse', done => { const lat = -45; const lng = 45; const input = { location: [lng, lat] }; const output = transform.mongoObjectToParseObject(null, input, { - fields: { location: { type: "GeoPoint" } }, + fields: { location: { type: 'GeoPoint' } }, }); - expect(typeof output.location).toEqual("object"); + expect(typeof output.location).toEqual('object'); expect(output.location).toEqual({ - __type: "GeoPoint", + __type: 'GeoPoint', latitude: lat, longitude: lng, }); done(); }); - it("mongo polygon to parse", done => { + it('mongo polygon to parse', done => { const lat = -45; const lng = 45; // Mongo stores polygon in WGS84 lng/lat const input = { location: { - type: "Polygon", + type: 'Polygon', coordinates: [ [ [lat, lng], @@ -238,11 +238,11 @@ describe("parseObjectToMongoObjectForCreate", () => { }, }; const output = transform.mongoObjectToParseObject(null, input, { - fields: { location: { type: "Polygon" } }, + fields: { location: { type: 'Polygon' } }, }); - expect(typeof output.location).toEqual("object"); + expect(typeof output.location).toEqual('object'); expect(output.location).toEqual({ - __type: "Polygon", + __type: 'Polygon', coordinates: [ [lng, lat], [lng, lat], @@ -251,151 +251,151 @@ describe("parseObjectToMongoObjectForCreate", () => { done(); }); - it("bytes", done => { - const input = { binaryData: "aGVsbG8gd29ybGQ=" }; + it('bytes', done => { + const input = { binaryData: 'aGVsbG8gd29ybGQ=' }; const output = transform.mongoObjectToParseObject(null, input, { - fields: { binaryData: { type: "Bytes" } }, + fields: { binaryData: { type: 'Bytes' } }, }); - expect(typeof output.binaryData).toEqual("object"); + expect(typeof output.binaryData).toEqual('object'); expect(output.binaryData).toEqual({ - __type: "Bytes", - base64: "aGVsbG8gd29ybGQ=", + __type: 'Bytes', + base64: 'aGVsbG8gd29ybGQ=', }); done(); }); - it("nested array", done => { - const input = { arr: [{ _testKey: "testValue" }] }; + it('nested array', done => { + const input = { arr: [{ _testKey: 'testValue' }] }; const output = transform.mongoObjectToParseObject(null, input, { - fields: { arr: { type: "Array" } }, + fields: { arr: { type: 'Array' } }, }); expect(Array.isArray(output.arr)).toEqual(true); - expect(output.arr).toEqual([{ _testKey: "testValue" }]); + expect(output.arr).toEqual([{ _testKey: 'testValue' }]); done(); }); - it("untransforms objects containing nested special keys", done => { + it('untransforms objects containing nested special keys', done => { const input = { array: [ { - _id: "Test ID", + _id: 'Test ID', _hashed_password: "I Don't know why you would name a key this, but if you do it should work", _tombstone: { _updated_at: "I'm sure people will nest keys like this", _acl: 7, - _id: { someString: "str", someNumber: 7 }, + _id: { someString: 'str', someNumber: 7 }, regularKey: { moreContents: [1, 2, 3] }, }, - regularKey: "some data", + regularKey: 'some data', }, ], }; const output = transform.mongoObjectToParseObject(null, input, { - fields: { array: { type: "Array" } }, + fields: { array: { type: 'Array' } }, }); expect(dd(output, input)).toEqual(undefined); done(); }); - it("changes new pointer key", done => { + it('changes new pointer key', done => { const input = { - somePointer: { __type: "Pointer", className: "Micro", objectId: "oft" }, + somePointer: { __type: 'Pointer', className: 'Micro', objectId: 'oft' }, }; const output = transform.parseObjectToMongoObjectForCreate(null, input, { - fields: { somePointer: { type: "Pointer" } }, + fields: { somePointer: { type: 'Pointer' } }, }); - expect(typeof output._p_somePointer).toEqual("string"); - expect(output._p_somePointer).toEqual("Micro$oft"); + expect(typeof output._p_somePointer).toEqual('string'); + expect(output._p_somePointer).toEqual('Micro$oft'); done(); }); - it("changes existing pointer keys", done => { + it('changes existing pointer keys', done => { const input = { userPointer: { - __type: "Pointer", - className: "_User", - objectId: "qwerty", + __type: 'Pointer', + className: '_User', + objectId: 'qwerty', }, }; const output = transform.parseObjectToMongoObjectForCreate(null, input, { - fields: { userPointer: { type: "Pointer" } }, + fields: { userPointer: { type: 'Pointer' } }, }); - expect(typeof output._p_userPointer).toEqual("string"); - expect(output._p_userPointer).toEqual("_User$qwerty"); + expect(typeof output._p_userPointer).toEqual('string'); + expect(output._p_userPointer).toEqual('_User$qwerty'); done(); }); - it("writes the old ACL format in addition to rperm and wperm on create", done => { + it('writes the old ACL format in addition to rperm and wperm on create', done => { const input = { - _rperm: ["*"], - _wperm: ["Kevin"], + _rperm: ['*'], + _wperm: ['Kevin'], }; const output = transform.parseObjectToMongoObjectForCreate(null, input, { fields: {}, }); - expect(typeof output._acl).toEqual("object"); - expect(output._acl["Kevin"].w).toBeTruthy(); - expect(output._acl["Kevin"].r).toBeUndefined(); + expect(typeof output._acl).toEqual('object'); + expect(output._acl['Kevin'].w).toBeTruthy(); + expect(output._acl['Kevin'].r).toBeUndefined(); expect(output._rperm).toEqual(input._rperm); expect(output._wperm).toEqual(input._wperm); done(); }); - it("removes Relation types", done => { + it('removes Relation types', done => { const input = { - aRelation: { __type: "Relation", className: "Stuff" }, + aRelation: { __type: 'Relation', className: 'Stuff' }, }; const output = transform.parseObjectToMongoObjectForCreate(null, input, { fields: { - aRelation: { __type: "Relation", className: "Stuff" }, + aRelation: { __type: 'Relation', className: 'Stuff' }, }, }); expect(output).toEqual({}); done(); }); - it("writes the old ACL format in addition to rperm and wperm on update", done => { + it('writes the old ACL format in addition to rperm and wperm on update', done => { const input = { - _rperm: ["*"], - _wperm: ["Kevin"], + _rperm: ['*'], + _wperm: ['Kevin'], }; const output = transform.transformUpdate(null, input, { fields: {} }); const set = output.$set; - expect(typeof set).toEqual("object"); - expect(typeof set._acl).toEqual("object"); - expect(set._acl["Kevin"].w).toBeTruthy(); - expect(set._acl["Kevin"].r).toBeUndefined(); + expect(typeof set).toEqual('object'); + expect(typeof set._acl).toEqual('object'); + expect(set._acl['Kevin'].w).toBeTruthy(); + expect(set._acl['Kevin'].r).toBeUndefined(); expect(set._rperm).toEqual(input._rperm); expect(set._wperm).toEqual(input._wperm); done(); }); - it("untransforms from _rperm and _wperm to ACL", done => { + it('untransforms from _rperm and _wperm to ACL', done => { const input = { - _rperm: ["*"], - _wperm: ["Kevin"], + _rperm: ['*'], + _wperm: ['Kevin'], }; const output = transform.mongoObjectToParseObject(null, input, { fields: {}, }); - expect(output._rperm).toEqual(["*"]); - expect(output._wperm).toEqual(["Kevin"]); + expect(output._rperm).toEqual(['*']); + expect(output._wperm).toEqual(['Kevin']); expect(output.ACL).toBeUndefined(); done(); }); - it("untransforms mongodb number types", done => { + it('untransforms mongodb number types', done => { const input = { long: mongodb.Long.fromNumber(Number.MAX_SAFE_INTEGER), double: new mongodb.Double(Number.MAX_VALUE), }; const output = transform.mongoObjectToParseObject(null, input, { fields: { - long: { type: "Number" }, - double: { type: "Number" }, + long: { type: 'Number' }, + double: { type: 'Number' }, }, }); expect(output.long).toBe(Number.MAX_SAFE_INTEGER); @@ -403,68 +403,68 @@ describe("parseObjectToMongoObjectForCreate", () => { done(); }); - it("Date object where iso attribute is of type Date", done => { + it('Date object where iso attribute is of type Date', done => { const input = { - ts: { __type: "Date", iso: new Date("2017-01-18T00:00:00.000Z") }, + ts: { __type: 'Date', iso: new Date('2017-01-18T00:00:00.000Z') }, }; const output = transform.mongoObjectToParseObject(null, input, { fields: { - ts: { type: "Date" }, + ts: { type: 'Date' }, }, }); - expect(output.ts.iso).toEqual("2017-01-18T00:00:00.000Z"); + expect(output.ts.iso).toEqual('2017-01-18T00:00:00.000Z'); done(); }); - it("Date object where iso attribute is of type String", done => { + it('Date object where iso attribute is of type String', done => { const input = { - ts: { __type: "Date", iso: "2017-01-18T00:00:00.000Z" }, + ts: { __type: 'Date', iso: '2017-01-18T00:00:00.000Z' }, }; const output = transform.mongoObjectToParseObject(null, input, { fields: { - ts: { type: "Date" }, + ts: { type: 'Date' }, }, }); - expect(output.ts.iso).toEqual("2017-01-18T00:00:00.000Z"); + expect(output.ts.iso).toEqual('2017-01-18T00:00:00.000Z'); done(); }); - it("object with undefined nested values", () => { + it('object with undefined nested values', () => { const input = { - _id: "vQHyinCW1l", - urls: { firstUrl: "https://", secondUrl: undefined }, + _id: 'vQHyinCW1l', + urls: { firstUrl: 'https://', secondUrl: undefined }, }; const output = transform.mongoObjectToParseObject(null, input, { fields: { - urls: { type: "Object" }, + urls: { type: 'Object' }, }, }); expect(output.urls).toEqual({ - firstUrl: "https://", + firstUrl: 'https://', secondUrl: undefined, }); }); - it("undefined objects", () => { + it('undefined objects', () => { const input = { - _id: "vQHyinCW1l", + _id: 'vQHyinCW1l', urls: undefined, }; const output = transform.mongoObjectToParseObject(null, input, { fields: { - urls: { type: "Object" }, + urls: { type: 'Object' }, }, }); expect(output.urls).toBeUndefined(); }); - it("$regex in $all list", done => { + it('$regex in $all list', done => { const input = { arrayField: { $all: [ - { $regex: "^\\Qone\\E" }, - { $regex: "^\\Qtwo\\E" }, - { $regex: "^\\Qthree\\E" }, + { $regex: '^\\Qone\\E' }, + { $regex: '^\\Qtwo\\E' }, + { $regex: '^\\Qthree\\E' }, ], }, }; @@ -488,10 +488,10 @@ describe("parseObjectToMongoObjectForCreate", () => { done(); }); - it("all values in $all must be $regex (start with string) or non $regex (start with string)", done => { + it('all values in $all must be $regex (start with string) or non $regex (start with string)', done => { const input = { arrayField: { - $all: [{ $regex: "^\\Qone\\E" }, { $unknown: "^\\Qtwo\\E" }], + $all: [{ $regex: '^\\Qone\\E' }, { $unknown: '^\\Qtwo\\E' }], }, }; @@ -501,39 +501,39 @@ describe("parseObjectToMongoObjectForCreate", () => { done(); }); - it("ignores User authData field in DB so it can be synthesized in code", done => { + it('ignores User authData field in DB so it can be synthesized in code', done => { const input = { - _id: "123", - _auth_data_acme: { id: "abc" }, + _id: '123', + _auth_data_acme: { id: 'abc' }, authData: null, }; - const output = transform.mongoObjectToParseObject("_User", input, { + const output = transform.mongoObjectToParseObject('_User', input, { fields: {}, }); - expect(output.authData.acme.id).toBe("abc"); + expect(output.authData.acme.id).toBe('abc'); done(); }); - it("can set authData when not User class", done => { + it('can set authData when not User class', done => { const input = { - _id: "123", - authData: "random", + _id: '123', + authData: 'random', }; - const output = transform.mongoObjectToParseObject("TestObject", input, { + const output = transform.mongoObjectToParseObject('TestObject', input, { fields: {}, }); - expect(output.authData).toBe("random"); + expect(output.authData).toBe('random'); done(); }); }); -it("cannot have a custom field name beginning with underscore", done => { +it('cannot have a custom field name beginning with underscore', done => { const input = { - _id: "123", - _thisFieldNameIs: "invalid", + _id: '123', + _thisFieldNameIs: 'invalid', }; try { - transform.mongoObjectToParseObject("TestObject", input, { + transform.mongoObjectToParseObject('TestObject', input, { fields: {}, }); } catch (e) { @@ -542,14 +542,14 @@ it("cannot have a custom field name beginning with underscore", done => { done(); }); -describe("transformUpdate", () => { - it("removes Relation types", done => { +describe('transformUpdate', () => { + it('removes Relation types', done => { const input = { - aRelation: { __type: "Relation", className: "Stuff" }, + aRelation: { __type: 'Relation', className: 'Stuff' }, }; const output = transform.transformUpdate(null, input, { fields: { - aRelation: { __type: "Relation", className: "Stuff" }, + aRelation: { __type: 'Relation', className: 'Stuff' }, }, }); expect(output).toEqual({}); @@ -557,14 +557,14 @@ describe("transformUpdate", () => { }); }); -describe("transformConstraint", () => { - describe("$relativeTime", () => { - it("should error on $eq, $ne, and $exists", () => { +describe('transformConstraint', () => { + describe('$relativeTime', () => { + it('should error on $eq, $ne, and $exists', () => { expect(() => { transform.transformConstraint({ $eq: { ttl: { - $relativeTime: "12 days ago", + $relativeTime: '12 days ago', }, }, }); @@ -574,7 +574,7 @@ describe("transformConstraint", () => { transform.transformConstraint({ $ne: { ttl: { - $relativeTime: "12 days ago", + $relativeTime: '12 days ago', }, }, }); @@ -583,7 +583,7 @@ describe("transformConstraint", () => { expect(() => { transform.transformConstraint({ $exists: { - $relativeTime: "12 days ago", + $relativeTime: '12 days ago', }, }); }).toThrow(); @@ -591,92 +591,92 @@ describe("transformConstraint", () => { }); }); -describe("relativeTimeToDate", () => { - const now = new Date("2017-09-26T13:28:16.617Z"); +describe('relativeTimeToDate', () => { + const now = new Date('2017-09-26T13:28:16.617Z'); - describe("In the future", () => { - it("should parse valid natural time", () => { - const text = "in 1 year 2 weeks 12 days 10 hours 24 minutes 30 seconds"; + describe('In the future', () => { + it('should parse valid natural time', () => { + const text = 'in 1 year 2 weeks 12 days 10 hours 24 minutes 30 seconds'; const { result, status, info } = Utils.relativeTimeToDate(text, now); - expect(result.toISOString()).toBe("2018-10-22T23:52:46.617Z"); - expect(status).toBe("success"); - expect(info).toBe("future"); + expect(result.toISOString()).toBe('2018-10-22T23:52:46.617Z'); + expect(status).toBe('success'); + expect(info).toBe('future'); }); }); - describe("In the past", () => { - it("should parse valid natural time", () => { - const text = "2 days 12 hours 1 minute 12 seconds ago"; + describe('In the past', () => { + it('should parse valid natural time', () => { + const text = '2 days 12 hours 1 minute 12 seconds ago'; const { result, status, info } = Utils.relativeTimeToDate(text, now); - expect(result.toISOString()).toBe("2017-09-24T01:27:04.617Z"); - expect(status).toBe("success"); - expect(info).toBe("past"); + expect(result.toISOString()).toBe('2017-09-24T01:27:04.617Z'); + expect(status).toBe('success'); + expect(info).toBe('past'); }); }); - describe("From now", () => { - it("should equal current time", () => { - const text = "now"; + describe('From now', () => { + it('should equal current time', () => { + const text = 'now'; const { result, status, info } = Utils.relativeTimeToDate(text, now); - expect(result.toISOString()).toBe("2017-09-26T13:28:16.617Z"); - expect(status).toBe("success"); - expect(info).toBe("present"); + expect(result.toISOString()).toBe('2017-09-26T13:28:16.617Z'); + expect(status).toBe('success'); + expect(info).toBe('present'); }); }); - describe("Error cases", () => { - it("should error if string is completely gibberish", () => { + describe('Error cases', () => { + it('should error if string is completely gibberish', () => { expect( - Utils.relativeTimeToDate("gibberishasdnklasdnjklasndkl123j123") + Utils.relativeTimeToDate('gibberishasdnklasdnjklasndkl123j123') ).toEqual({ - status: "error", + status: 'error', info: "Time should either start with 'in' or end with 'ago'", }); }); - it("should error if string contains neither `ago` nor `in`", () => { - expect(Utils.relativeTimeToDate("12 hours 1 minute")).toEqual({ - status: "error", + it('should error if string contains neither `ago` nor `in`', () => { + expect(Utils.relativeTimeToDate('12 hours 1 minute')).toEqual({ + status: 'error', info: "Time should either start with 'in' or end with 'ago'", }); }); - it("should error if there are missing units or numbers", () => { - expect(Utils.relativeTimeToDate("in 12 hours 1")).toEqual({ - status: "error", - info: "Invalid time string. Dangling unit or number.", + it('should error if there are missing units or numbers', () => { + expect(Utils.relativeTimeToDate('in 12 hours 1')).toEqual({ + status: 'error', + info: 'Invalid time string. Dangling unit or number.', }); - expect(Utils.relativeTimeToDate("12 hours minute ago")).toEqual({ - status: "error", - info: "Invalid time string. Dangling unit or number.", + expect(Utils.relativeTimeToDate('12 hours minute ago')).toEqual({ + status: 'error', + info: 'Invalid time string. Dangling unit or number.', }); }); - it("should error on floating point numbers", () => { - expect(Utils.relativeTimeToDate("in 12.3 hours")).toEqual({ - status: "error", + it('should error on floating point numbers', () => { + expect(Utils.relativeTimeToDate('in 12.3 hours')).toEqual({ + status: 'error', info: "'12.3' is not an integer.", }); }); - it("should error if numbers are invalid", () => { - expect(Utils.relativeTimeToDate("12 hours 123a minute ago")).toEqual({ - status: "error", + it('should error if numbers are invalid', () => { + expect(Utils.relativeTimeToDate('12 hours 123a minute ago')).toEqual({ + status: 'error', info: "'123a' is not an integer.", }); }); - it("should error on invalid interval units", () => { - expect(Utils.relativeTimeToDate("4 score 7 years ago")).toEqual({ - status: "error", + it('should error on invalid interval units', () => { + expect(Utils.relativeTimeToDate('4 score 7 years ago')).toEqual({ + status: 'error', info: "Invalid interval: 'score'", }); }); it("should error when string contains 'ago' and 'in'", () => { - expect(Utils.relativeTimeToDate("in 1 day 2 minutes ago")).toEqual({ - status: "error", + expect(Utils.relativeTimeToDate('in 1 day 2 minutes ago')).toEqual({ + status: 'error', info: "Time cannot have both 'in' and 'ago'", }); }); diff --git a/spec/NullCacheAdapter.spec.js b/spec/NullCacheAdapter.spec.js index 4d3dd40c62..26d7322299 100644 --- a/spec/NullCacheAdapter.spec.js +++ b/spec/NullCacheAdapter.spec.js @@ -1,11 +1,11 @@ const NullCacheAdapter = - require("../lib/Adapters/Cache/NullCacheAdapter").default; + require('../lib/Adapters/Cache/NullCacheAdapter').default; -describe("NullCacheAdapter", function () { - const KEY = "hello"; - const VALUE = "world"; +describe('NullCacheAdapter', function () { + const KEY = 'hello'; + const VALUE = 'world'; - it("should expose promisifyed methods", done => { + it('should expose promisifyed methods', done => { const cache = new NullCacheAdapter({ ttl: NaN, }); @@ -21,7 +21,7 @@ describe("NullCacheAdapter", function () { }); }); - it("should get/set/clear", done => { + it('should get/set/clear', done => { const cache = new NullCacheAdapter({ ttl: NaN, }); diff --git a/spec/OAuth1.spec.js b/spec/OAuth1.spec.js index 522b389d58..6d61ca054b 100644 --- a/spec/OAuth1.spec.js +++ b/spec/OAuth1.spec.js @@ -1,44 +1,44 @@ -const OAuth = require("../lib/Adapters/Auth/OAuth1Client"); +const OAuth = require('../lib/Adapters/Auth/OAuth1Client'); -describe("OAuth", function () { - it("Nonce should have right length", done => { +describe('OAuth', function () { + it('Nonce should have right length', done => { jequal(OAuth.nonce().length, 30); done(); }); - it("Should properly build parameter string", done => { + it('Should properly build parameter string', done => { const string = OAuth.buildParameterString({ c: 1, a: 2, b: 3 }); - jequal(string, "a=2&b=3&c=1"); + jequal(string, 'a=2&b=3&c=1'); done(); }); - it("Should properly build empty parameter string", done => { + it('Should properly build empty parameter string', done => { const string = OAuth.buildParameterString(); - jequal(string, ""); + jequal(string, ''); done(); }); - it("Should properly build signature string", done => { - const string = OAuth.buildSignatureString("get", "http://dummy.com", ""); - jequal(string, "GET&http%3A%2F%2Fdummy.com&"); + it('Should properly build signature string', done => { + const string = OAuth.buildSignatureString('get', 'http://dummy.com', ''); + jequal(string, 'GET&http%3A%2F%2Fdummy.com&'); done(); }); - it("Should properly generate request signature", done => { + it('Should properly generate request signature', done => { let request = { - host: "dummy.com", - path: "path", + host: 'dummy.com', + path: 'path', }; const oauth_params = { oauth_timestamp: 123450000, - oauth_nonce: "AAAAAAAAAAAAAAAAA", - oauth_consumer_key: "hello", - oauth_token: "token", + oauth_nonce: 'AAAAAAAAAAAAAAAAA', + oauth_consumer_key: 'hello', + oauth_token: 'token', }; - const consumer_secret = "world"; - const auth_token_secret = "secret"; + const consumer_secret = 'world'; + const auth_token_secret = 'secret'; request = OAuth.signRequest( request, oauth_params, @@ -46,84 +46,84 @@ describe("OAuth", function () { auth_token_secret ); jequal( - request.headers["Authorization"], + request.headers['Authorization'], 'OAuth oauth_consumer_key="hello", oauth_nonce="AAAAAAAAAAAAAAAAA", oauth_signature="8K95bpQcDi9Nd2GkhumTVcw4%2BXw%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="123450000", oauth_token="token", oauth_version="1.0"' ); done(); }); - it("Should properly build request", done => { + it('Should properly build request', done => { const options = { - host: "dummy.com", - consumer_key: "hello", - consumer_secret: "world", - auth_token: "token", - auth_token_secret: "secret", + host: 'dummy.com', + consumer_key: 'hello', + consumer_secret: 'world', + auth_token: 'token', + auth_token_secret: 'secret', // Custom oauth params for tests oauth_params: { oauth_timestamp: 123450000, - oauth_nonce: "AAAAAAAAAAAAAAAAA", + oauth_nonce: 'AAAAAAAAAAAAAAAAA', }, }; - const path = "path"; - const method = "get"; + const path = 'path'; + const method = 'get'; const oauthClient = new OAuth(options); - const req = oauthClient.buildRequest(method, path, { query: "param" }); + const req = oauthClient.buildRequest(method, path, { query: 'param' }); jequal(req.host, options.host); - jequal(req.path, "/" + path + "?query=param"); - jequal(req.method, "GET"); - jequal(req.headers["Content-Type"], "application/x-www-form-urlencoded"); + jequal(req.path, '/' + path + '?query=param'); + jequal(req.method, 'GET'); + jequal(req.headers['Content-Type'], 'application/x-www-form-urlencoded'); jequal( - req.headers["Authorization"], + req.headers['Authorization'], 'OAuth oauth_consumer_key="hello", oauth_nonce="AAAAAAAAAAAAAAAAA", oauth_signature="wNkyEkDE%2F0JZ2idmqyrgHdvC0rs%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="123450000", oauth_token="token", oauth_version="1.0"' ); done(); }); function validateCannotAuthenticateError(data, done) { - jequal(typeof data, "object"); - jequal(typeof data.errors, "object"); + jequal(typeof data, 'object'); + jequal(typeof data.errors, 'object'); const errors = data.errors; - jequal(typeof errors[0], "object"); + jequal(typeof errors[0], 'object'); // Cannot authenticate error jequal(errors[0].code, 32); done(); } - xit("GET request for a resource that requires OAuth should fail with invalid credentials", done => { + xit('GET request for a resource that requires OAuth should fail with invalid credentials', done => { /* This endpoint has been chosen to make a request to an endpoint that requires OAuth which fails due to missing authentication. Any other endpoint from the Twitter API that requires OAuth can be used instead in case the currently used endpoint deprecates. */ const options = { - host: "api.twitter.com", - consumer_key: "invalid_consumer_key", - consumer_secret: "invalid_consumer_secret", + host: 'api.twitter.com', + consumer_key: 'invalid_consumer_key', + consumer_secret: 'invalid_consumer_secret', }; - const path = "/1.1/favorites/list.json"; - const params = { lang: "en" }; + const path = '/1.1/favorites/list.json'; + const params = { lang: 'en' }; const oauthClient = new OAuth(options); oauthClient.get(path, params).then(function (data) { validateCannotAuthenticateError(data, done); }); }); - xit("POST request for a resource that requires OAuth should fail with invalid credentials", done => { + xit('POST request for a resource that requires OAuth should fail with invalid credentials', done => { /* This endpoint has been chosen to make a request to an endpoint that requires OAuth which fails due to missing authentication. Any other endpoint from the Twitter API that requires OAuth can be used instead in case the currently used endpoint deprecates. */ const options = { - host: "api.twitter.com", - consumer_key: "invalid_consumer_key", - consumer_secret: "invalid_consumer_secret", + host: 'api.twitter.com', + consumer_key: 'invalid_consumer_key', + consumer_secret: 'invalid_consumer_secret', }; const body = { - lang: "en", + lang: 'en', }; - const path = "/1.1/account/settings.json"; + const path = '/1.1/account/settings.json'; const oauthClient = new OAuth(options); oauthClient.post(path, null, body).then(function (data) { @@ -131,16 +131,16 @@ describe("OAuth", function () { }); }); - it("Should fail a request", done => { + it('Should fail a request', done => { const options = { - host: "localhost", - consumer_key: "XXXXXXXXXXXXXXXXXXXXXXXXX", - consumer_secret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + host: 'localhost', + consumer_key: 'XXXXXXXXXXXXXXXXXXXXXXXXX', + consumer_secret: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', }; const body = { - lang: "en", + lang: 'en', }; - const path = "/"; + const path = '/'; const oauthClient = new OAuth(options); oauthClient @@ -155,12 +155,12 @@ describe("OAuth", function () { }); }); - it("Should fail with missing options", done => { + it('Should fail with missing options', done => { const options = undefined; try { new OAuth(options); } catch (error) { - jequal(error.message, "No options passed to OAuth"); + jequal(error.message, 'No options passed to OAuth'); done(); } }); diff --git a/spec/PagesRouter.spec.js b/spec/PagesRouter.spec.js index a23b41ced1..8ac5aa4d64 100644 --- a/spec/PagesRouter.spec.js +++ b/spec/PagesRouter.spec.js @@ -1,42 +1,42 @@ -"use strict"; - -const request = require("../lib/request"); -const fs = require("fs").promises; -const mustache = require("mustache"); -const Utils = require("../lib/Utils"); -const { Page } = require("../lib/Page"); -const Config = require("../lib/Config"); -const Definitions = require("../lib/Options/Definitions"); +'use strict'; + +const request = require('../lib/request'); +const fs = require('fs').promises; +const mustache = require('mustache'); +const Utils = require('../lib/Utils'); +const { Page } = require('../lib/Page'); +const Config = require('../lib/Config'); +const Definitions = require('../lib/Options/Definitions'); const UserController = - require("../lib/Controllers/UserController").UserController; + require('../lib/Controllers/UserController').UserController; const { PagesRouter, pages, pageParams, pageParamHeaderPrefix, -} = require("../lib/Routers/PagesRouter"); +} = require('../lib/Routers/PagesRouter'); -describe("Pages Router", () => { - describe("basic request", () => { +describe('Pages Router', () => { + describe('basic request', () => { let config; beforeEach(async () => { config = { - appId: "test", - appName: "exampleAppname", - publicServerURL: "http://localhost:8378/1", + appId: 'test', + appName: 'exampleAppname', + publicServerURL: 'http://localhost:8378/1', pages: { enableRouter: true }, }; await reconfigureServer(config); }); - it("responds with file content on direct page request", async () => { + it('responds with file content on direct page request', async () => { const urls = [ - "http://localhost:8378/1/apps/email_verification_link_invalid.html", - "http://localhost:8378/1/apps/choose_password?appId=test", - "http://localhost:8378/1/apps/email_verification_success.html", - "http://localhost:8378/1/apps/password_reset_success.html", - "http://localhost:8378/1/apps/custom_json.html", + 'http://localhost:8378/1/apps/email_verification_link_invalid.html', + 'http://localhost:8378/1/apps/choose_password?appId=test', + 'http://localhost:8378/1/apps/email_verification_success.html', + 'http://localhost:8378/1/apps/password_reset_success.html', + 'http://localhost:8378/1/apps/custom_json.html', ]; for (const url of urls) { const response = await request({ url }).catch(e => e); @@ -44,18 +44,18 @@ describe("Pages Router", () => { } }); - it("can load file from custom pages path", async () => { - config.pages.pagesPath = "./public"; + it('can load file from custom pages path', async () => { + config.pages.pagesPath = './public'; await reconfigureServer(config); const response = await request({ - url: "http://localhost:8378/1/apps/email_verification_link_invalid.html", + url: 'http://localhost:8378/1/apps/email_verification_link_invalid.html', }).catch(e => e); expect(response.status).toBe(200); }); - it("can load file from custom pages endpoint", async () => { - config.pages.pagesEndpoint = "pages"; + it('can load file from custom pages endpoint', async () => { + config.pages.pagesEndpoint = 'pages'; await reconfigureServer(config); const response = await request({ @@ -64,15 +64,15 @@ describe("Pages Router", () => { expect(response.status).toBe(200); }); - it("responds with 404 if publicServerURL is not configured", async () => { + it('responds with 404 if publicServerURL is not configured', async () => { await reconfigureServer({ - appName: "unused", + appName: 'unused', pages: { enableRouter: true }, }); const urls = [ - "http://localhost:8378/1/apps/test/verify_email", - "http://localhost:8378/1/apps/choose_password?appId=test", - "http://localhost:8378/1/apps/test/request_password_reset", + 'http://localhost:8378/1/apps/test/verify_email', + 'http://localhost:8378/1/apps/choose_password?appId=test', + 'http://localhost:8378/1/apps/test/request_password_reset', ]; for (const url of urls) { const response = await request({ url }).catch(e => e); @@ -80,27 +80,27 @@ describe("Pages Router", () => { } }); - it("responds with 403 access denied with invalid appId", async () => { + it('responds with 403 access denied with invalid appId', async () => { const reqs = [ { - url: "http://localhost:8378/1/apps/invalid/verify_email", - method: "GET", + url: 'http://localhost:8378/1/apps/invalid/verify_email', + method: 'GET', }, { - url: "http://localhost:8378/1/apps/choose_password?id=invalid", - method: "GET", + url: 'http://localhost:8378/1/apps/choose_password?id=invalid', + method: 'GET', }, { - url: "http://localhost:8378/1/apps/invalid/request_password_reset", - method: "GET", + url: 'http://localhost:8378/1/apps/invalid/request_password_reset', + method: 'GET', }, { - url: "http://localhost:8378/1/apps/invalid/request_password_reset", - method: "POST", + url: 'http://localhost:8378/1/apps/invalid/request_password_reset', + method: 'POST', }, { - url: "http://localhost:8378/1/apps/invalid/resend_verification_email", - method: "POST", + url: 'http://localhost:8378/1/apps/invalid/resend_verification_email', + method: 'POST', }, ]; for (const req of reqs) { @@ -110,26 +110,26 @@ describe("Pages Router", () => { }); }); - describe("AJAX requests", () => { + describe('AJAX requests', () => { beforeEach(async () => { await reconfigureServer({ - appName: "exampleAppname", - publicServerURL: "http://localhost:8378/1", + appName: 'exampleAppname', + publicServerURL: 'http://localhost:8378/1', pages: { enableRouter: true }, }); }); - it("request_password_reset: responds with AJAX success", async () => { - spyOn(UserController.prototype, "updatePassword").and.callFake(() => + it('request_password_reset: responds with AJAX success', async () => { + spyOn(UserController.prototype, 'updatePassword').and.callFake(() => Promise.resolve() ); const res = await request({ - method: "POST", - url: "http://localhost:8378/1/apps/test/request_password_reset", + method: 'POST', + url: 'http://localhost:8378/1/apps/test/request_password_reset', body: `new_password=user1&token=43634643`, headers: { - "Content-Type": "application/x-www-form-urlencoded", - "X-Requested-With": "XMLHttpRequest", + 'Content-Type': 'application/x-www-form-urlencoded', + 'X-Requested-With': 'XMLHttpRequest', }, followRedirects: false, }).catch(e => e); @@ -137,15 +137,15 @@ describe("Pages Router", () => { expect(res.text).toEqual('"Password successfully reset"'); }); - it("request_password_reset: responds with AJAX error on missing password", async () => { + it('request_password_reset: responds with AJAX error on missing password', async () => { try { await request({ - method: "POST", - url: "http://localhost:8378/1/apps/test/request_password_reset", + method: 'POST', + url: 'http://localhost:8378/1/apps/test/request_password_reset', body: `new_password=&token=132414`, headers: { - "Content-Type": "application/x-www-form-urlencoded", - "X-Requested-With": "XMLHttpRequest", + 'Content-Type': 'application/x-www-form-urlencoded', + 'X-Requested-With': 'XMLHttpRequest', }, followRedirects: false, }); @@ -155,15 +155,15 @@ describe("Pages Router", () => { } }); - it("request_password_reset: responds with AJAX error on missing token", async () => { + it('request_password_reset: responds with AJAX error on missing token', async () => { try { await request({ - method: "POST", - url: "http://localhost:8378/1/apps/test/request_password_reset", + method: 'POST', + url: 'http://localhost:8378/1/apps/test/request_password_reset', body: `new_password=user1&token=`, headers: { - "Content-Type": "application/x-www-form-urlencoded", - "X-Requested-With": "XMLHttpRequest", + 'Content-Type': 'application/x-www-form-urlencoded', + 'X-Requested-With': 'XMLHttpRequest', }, followRedirects: false, }); @@ -174,7 +174,7 @@ describe("Pages Router", () => { }); }); - describe("pages", () => { + describe('pages', () => { let router = new PagesRouter(); let req; let config; @@ -193,27 +193,27 @@ describe("Pages Router", () => { beforeEach(async () => { router = new PagesRouter(); - readFile = spyOn(fs, "readFile").and.callThrough(); - goToPage = spyOn(PagesRouter.prototype, "goToPage").and.callThrough(); + readFile = spyOn(fs, 'readFile').and.callThrough(); + goToPage = spyOn(PagesRouter.prototype, 'goToPage').and.callThrough(); pageResponse = spyOn( PagesRouter.prototype, - "pageResponse" + 'pageResponse' ).and.callThrough(); redirectResponse = spyOn( PagesRouter.prototype, - "redirectResponse" + 'redirectResponse' ).and.callThrough(); - exampleLocale = "de-AT"; + exampleLocale = 'de-AT'; config = { - appId: "test", - appName: "ExampleAppName", + appId: 'test', + appName: 'ExampleAppName', verifyUserEmails: true, emailAdapter: { sendVerificationEmail: () => Promise.resolve(), sendPasswordResetEmail: () => Promise.resolve(), sendMail: () => {}, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', pages: { enableRouter: true, enableLocalization: true, @@ -221,7 +221,7 @@ describe("Pages Router", () => { }, }; req = { - method: "GET", + method: 'GET', config, query: { locale: exampleLocale, @@ -229,8 +229,8 @@ describe("Pages Router", () => { }; }); - describe("server options", () => { - it("uses default configuration when none is set", async () => { + describe('server options', () => { + it('uses default configuration when none is set', async () => { await reconfigureServerWithPagesConfig({}); expect(Config.get(Parse.applicationId).pages.enableRouter).toBe( Definitions.PagesOptions.enableRouter.default @@ -264,26 +264,26 @@ describe("Pages Router", () => { ); }); - it("throws on invalid configuration", async () => { + it('throws on invalid configuration', async () => { const options = [ [], - "a", + 'a', 0, true, - { enableRouter: "a" }, + { enableRouter: 'a' }, { enableRouter: 0 }, { enableRouter: {} }, { enableRouter: [] }, - { enableLocalization: "a" }, + { enableLocalization: 'a' }, { enableLocalization: 0 }, { enableLocalization: {} }, { enableLocalization: [] }, - { forceRedirect: "a" }, + { forceRedirect: 'a' }, { forceRedirect: 0 }, { forceRedirect: {} }, { forceRedirect: [] }, { placeholders: true }, - { placeholders: "a" }, + { placeholders: 'a' }, { placeholders: 0 }, { placeholders: [] }, { pagesPath: true }, @@ -296,7 +296,7 @@ describe("Pages Router", () => { { pagesEndpoint: [] }, { customUrls: true }, { customUrls: 0 }, - { customUrls: "a" }, + { customUrls: 'a' }, { customUrls: [] }, { localizationJsonPath: true }, { localizationJsonPath: 0 }, @@ -308,7 +308,7 @@ describe("Pages Router", () => { { localizationFallbackLocale: [] }, { customRoutes: true }, { customRoutes: 0 }, - { customRoutes: "a" }, + { customRoutes: 'a' }, { customRoutes: {} }, ]; for (const option of options) { @@ -319,78 +319,78 @@ describe("Pages Router", () => { }); }); - describe("placeholders", () => { - it("replaces placeholder in response content", async () => { + describe('placeholders', () => { + it('replaces placeholder in response content', async () => { await expectAsync( router.goToPage(req, pages.passwordResetLinkInvalid) ).toBeResolved(); expect(readFile.calls.all()[0].returnValue).toBeDefined(); const originalContent = await readFile.calls.all()[0].returnValue; - expect(originalContent).toContain("{{appName}}"); + expect(originalContent).toContain('{{appName}}'); expect(pageResponse.calls.all()[0].returnValue).toBeDefined(); const replacedContent = await pageResponse.calls.all()[0].returnValue; - expect(replacedContent.text).not.toContain("{{appName}}"); + expect(replacedContent.text).not.toContain('{{appName}}'); expect(replacedContent.text).toContain(req.config.appName); }); - it("removes undefined placeholder in response content", async () => { + it('removes undefined placeholder in response content', async () => { await expectAsync( router.goToPage(req, pages.passwordReset) ).toBeResolved(); expect(readFile.calls.all()[0].returnValue).toBeDefined(); const originalContent = await readFile.calls.all()[0].returnValue; - expect(originalContent).toContain("{{error}}"); + expect(originalContent).toContain('{{error}}'); // There is no error placeholder value set by default, so the // {{error}} placeholder should just be removed from content expect(pageResponse.calls.all()[0].returnValue).toBeDefined(); const replacedContent = await pageResponse.calls.all()[0].returnValue; - expect(replacedContent.text).not.toContain("{{error}}"); + expect(replacedContent.text).not.toContain('{{error}}'); }); - it("fills placeholders from config object", async () => { + it('fills placeholders from config object', async () => { config.pages.enableLocalization = false; config.pages.placeholders = { - title: "setViaConfig", + title: 'setViaConfig', }; await reconfigureServer(config); const response = await request({ - url: "http://localhost:8378/1/apps/custom_json.html", + url: 'http://localhost:8378/1/apps/custom_json.html', followRedirects: false, - method: "GET", + method: 'GET', }); expect(response.status).toEqual(200); expect(response.text).toContain(config.pages.placeholders.title); }); - it("fills placeholders from config function", async () => { + it('fills placeholders from config function', async () => { config.pages.enableLocalization = false; config.pages.placeholders = () => { - return { title: "setViaConfig" }; + return { title: 'setViaConfig' }; }; await reconfigureServer(config); const response = await request({ - url: "http://localhost:8378/1/apps/custom_json.html", + url: 'http://localhost:8378/1/apps/custom_json.html', followRedirects: false, - method: "GET", + method: 'GET', }); expect(response.status).toEqual(200); expect(response.text).toContain(config.pages.placeholders().title); }); - it("fills placeholders from config promise", async () => { + it('fills placeholders from config promise', async () => { config.pages.enableLocalization = false; config.pages.placeholders = async () => { - return { title: "setViaConfig" }; + return { title: 'setViaConfig' }; }; await reconfigureServer(config); const response = await request({ - url: "http://localhost:8378/1/apps/custom_json.html", + url: 'http://localhost:8378/1/apps/custom_json.html', followRedirects: false, - method: "GET", + method: 'GET', }); expect(response.status).toEqual(200); expect(response.text).toContain( @@ -399,8 +399,8 @@ describe("Pages Router", () => { }); }); - describe("localization", () => { - it("returns default file if localization is disabled", async () => { + describe('localization', () => { + it('returns default file if localization is disabled', async () => { delete req.config.pages.enableLocalization; await expectAsync( @@ -414,7 +414,7 @@ describe("Pages Router", () => { ); }); - it("returns default file if no locale is specified", async () => { + it('returns default file if no locale is specified', async () => { delete req.query.locale; await expectAsync( @@ -428,9 +428,9 @@ describe("Pages Router", () => { ); }); - it("returns custom page regardless of localization enabled", async () => { + it('returns custom page regardless of localization enabled', async () => { req.config.pages.customUrls = { - passwordResetLinkInvalid: "http://invalid-link.example.com", + passwordResetLinkInvalid: 'http://invalid-link.example.com', }; await expectAsync( @@ -442,7 +442,7 @@ describe("Pages Router", () => { ); }); - it("returns file for locale match", async () => { + it('returns file for locale match', async () => { await expectAsync( router.goToPage(req, pages.passwordResetLinkInvalid) ).toBeResolved(); @@ -454,9 +454,9 @@ describe("Pages Router", () => { ); }); - it("returns file for language match", async () => { + it('returns file for language match', async () => { // Pretend no locale matching file exists - spyOn(Utils, "fileExists").and.callFake(async path => { + spyOn(Utils, 'fileExists').and.callFake(async path => { return !path.includes( `/${req.query.locale}/${pages.passwordResetLinkInvalid.defaultFile}` ); @@ -471,8 +471,8 @@ describe("Pages Router", () => { ); }); - it("returns default file for neither locale nor language match", async () => { - req.query.locale = "yo-LO"; + it('returns default file for neither locale nor language match', async () => { + req.query.locale = 'yo-LO'; await expectAsync( router.goToPage(req, pages.passwordResetLinkInvalid) @@ -486,26 +486,26 @@ describe("Pages Router", () => { }); }); - describe("localization with JSON resource", () => { + describe('localization with JSON resource', () => { let jsonPageFile; let jsonPageUrl; let jsonResource; beforeEach(async () => { - jsonPageFile = "custom_json.html"; + jsonPageFile = 'custom_json.html'; jsonPageUrl = new URL(`${config.publicServerURL}/apps/${jsonPageFile}`); - jsonResource = require("../public/custom_json.json"); + jsonResource = require('../public/custom_json.json'); config.pages.enableLocalization = true; - config.pages.localizationJsonPath = "./public/custom_json.json"; - config.pages.localizationFallbackLocale = "en"; + config.pages.localizationJsonPath = './public/custom_json.json'; + config.pages.localizationFallbackLocale = 'en'; await reconfigureServer(config); }); - it("does not localize with JSON resource if localization is disabled", async () => { + it('does not localize with JSON resource if localization is disabled', async () => { config.pages.enableLocalization = false; - config.pages.localizationJsonPath = "./public/custom_json.json"; - config.pages.localizationFallbackLocale = "en"; + config.pages.localizationJsonPath = './public/custom_json.json'; + config.pages.localizationFallbackLocale = 'en'; await reconfigureServer(config); const response = await request({ @@ -525,12 +525,12 @@ describe("Pages Router", () => { // Ensure page response does not contain any translation const flattenedJson = Utils.flattenObject(jsonResource); for (const value of Object.values(flattenedJson)) { - const valueWithoutPlaceholder = fillPlaceholders(value, ""); + const valueWithoutPlaceholder = fillPlaceholders(value, ''); expect(response.text).not.toContain(valueWithoutPlaceholder); } }); - it("localizes static page with JSON resource and fallback locale", async () => { + it('localizes static page with JSON resource and fallback locale', async () => { const response = await request({ url: jsonPageUrl.toString(), followRedirects: false, @@ -541,14 +541,14 @@ describe("Pages Router", () => { const translation = jsonResource[config.pages.localizationFallbackLocale].translation; for (const value of Object.values(translation)) { - const valueWithoutPlaceholder = fillPlaceholders(value, ""); + const valueWithoutPlaceholder = fillPlaceholders(value, ''); expect(response.text).toContain(valueWithoutPlaceholder); } }); - it("localizes static page with JSON resource and request locale", async () => { + it('localizes static page with JSON resource and request locale', async () => { // Add locale to request URL - jsonPageUrl.searchParams.set("locale", exampleLocale); + jsonPageUrl.searchParams.set('locale', exampleLocale); const response = await request({ url: jsonPageUrl.toString(), @@ -559,15 +559,15 @@ describe("Pages Router", () => { // Ensure page response contains translations of request locale const translation = jsonResource[exampleLocale].translation; for (const value of Object.values(translation)) { - const valueWithoutPlaceholder = fillPlaceholders(value, ""); + const valueWithoutPlaceholder = fillPlaceholders(value, ''); expect(response.text).toContain(valueWithoutPlaceholder); } }); - it("localizes static page with JSON resource and language matching request locale", async () => { + it('localizes static page with JSON resource and language matching request locale', async () => { // Add locale to request URL that has no locale match but only a language // match in the JSON resource - jsonPageUrl.searchParams.set("locale", "de-CH"); + jsonPageUrl.searchParams.set('locale', 'de-CH'); const response = await request({ url: jsonPageUrl.toString(), @@ -576,19 +576,19 @@ describe("Pages Router", () => { expect(response.status).toBe(200); // Ensure page response contains translations of requst language - const translation = jsonResource["de"].translation; + const translation = jsonResource['de'].translation; for (const value of Object.values(translation)) { - const valueWithoutPlaceholder = fillPlaceholders(value, ""); + const valueWithoutPlaceholder = fillPlaceholders(value, ''); expect(response.text).toContain(valueWithoutPlaceholder); } }); - it("localizes static page with JSON resource and fills placeholders in JSON values", async () => { + it('localizes static page with JSON resource and fills placeholders in JSON values', async () => { // Add app ID to request URL so that the request is assigned to a Parse Server app // and placeholders within translations strings can be replaced with default page // parameters such as `appId` - jsonPageUrl.searchParams.set("appId", config.appId); - jsonPageUrl.searchParams.set("locale", exampleLocale); + jsonPageUrl.searchParams.set('appId', config.appId); + jsonPageUrl.searchParams.set('locale', exampleLocale); const response = await request({ url: jsonPageUrl.toString(), @@ -608,9 +608,9 @@ describe("Pages Router", () => { } }); - it("localizes feature page with JSON resource and fills placeholders in JSON values", async () => { + it('localizes feature page with JSON resource and fills placeholders in JSON values', async () => { // Fake any page to load the JSON page file - spyOnProperty(Page.prototype, "defaultFile").and.returnValue( + spyOnProperty(Page.prototype, 'defaultFile').and.returnValue( jsonPageFile ); @@ -633,8 +633,8 @@ describe("Pages Router", () => { }); }); - describe("response type", () => { - it("returns a file for GET request", async () => { + describe('response type', () => { + it('returns a file for GET request', async () => { await expectAsync( router.goToPage(req, pages.passwordResetLinkInvalid) ).toBeResolved(); @@ -642,8 +642,8 @@ describe("Pages Router", () => { expect(redirectResponse).not.toHaveBeenCalled(); }); - it("returns a redirect for POST request", async () => { - req.method = "POST"; + it('returns a redirect for POST request', async () => { + req.method = 'POST'; await expectAsync( router.goToPage(req, pages.passwordResetLinkInvalid) ).toBeResolved(); @@ -651,12 +651,12 @@ describe("Pages Router", () => { expect(redirectResponse).toHaveBeenCalled(); }); - it("returns a redirect for custom pages for GET and POST request", async () => { + it('returns a redirect for custom pages for GET and POST request', async () => { req.config.pages.customUrls = { - passwordResetLinkInvalid: "http://invalid-link.example.com", + passwordResetLinkInvalid: 'http://invalid-link.example.com', }; - for (const method of ["GET", "POST"]) { + for (const method of ['GET', 'POST']) { req.method = method; await expectAsync( router.goToPage(req, pages.passwordResetLinkInvalid) @@ -666,42 +666,42 @@ describe("Pages Router", () => { } }); - it("responds to POST request with redirect response", async () => { + it('responds to POST request with redirect response', async () => { await reconfigureServer(config); const response = await request({ - url: "http://localhost:8378/1/apps/test/request_password_reset?token=exampleToken&locale=de-AT", + url: 'http://localhost:8378/1/apps/test/request_password_reset?token=exampleToken&locale=de-AT', followRedirects: false, - method: "POST", + method: 'POST', }); expect(response.status).toEqual(303); expect(response.headers.location).toContain( - "http://localhost:8378/1/apps/de-AT/password_reset_link_invalid.html" + 'http://localhost:8378/1/apps/de-AT/password_reset_link_invalid.html' ); }); - it("responds to GET request with content response", async () => { + it('responds to GET request with content response', async () => { await reconfigureServer(config); const response = await request({ - url: "http://localhost:8378/1/apps/test/request_password_reset?token=exampleToken&locale=de-AT", + url: 'http://localhost:8378/1/apps/test/request_password_reset?token=exampleToken&locale=de-AT', followRedirects: false, - method: "GET", + method: 'GET', }); expect(response.status).toEqual(200); - expect(response.text).toContain(""); + expect(response.text).toContain(''); }); }); - describe("end-to-end tests", () => { - it("localizes end-to-end for password reset: success", async () => { + describe('end-to-end tests', () => { + it('localizes end-to-end for password reset: success', async () => { await reconfigureServer(config); const sendPasswordResetEmail = spyOn( config.emailAdapter, - "sendPasswordResetEmail" + 'sendPasswordResetEmail' ).and.callThrough(); const user = new Parse.User(); - user.setUsername("exampleUsername"); - user.setPassword("examplePassword"); - user.set("email", "mail@example.com"); + user.setUsername('exampleUsername'); + user.setPassword('examplePassword'); + user.set('email', 'mail@example.com'); await user.signUp(); await Parse.User.requestPasswordReset(user.getEmail()); @@ -715,11 +715,11 @@ describe("Pages Router", () => { }); expect(linkResponse.status).toBe(200); - const appId = linkResponse.headers["x-parse-page-param-appid"]; - const token = linkResponse.headers["x-parse-page-param-token"]; - const locale = linkResponse.headers["x-parse-page-param-locale"]; + const appId = linkResponse.headers['x-parse-page-param-appid']; + const token = linkResponse.headers['x-parse-page-param-token']; + const locale = linkResponse.headers['x-parse-page-param-locale']; const publicServerUrl = - linkResponse.headers["x-parse-page-param-publicserverurl"]; + linkResponse.headers['x-parse-page-param-publicserverurl']; const passwordResetPagePath = pageResponse.calls.all()[0].args[0]; expect(appId).toBeDefined(); expect(token).toBeDefined(); @@ -733,13 +733,13 @@ describe("Pages Router", () => { const formUrl = `${publicServerUrl}/apps/${appId}/request_password_reset`; const formResponse = await request({ url: formUrl, - method: "POST", + method: 'POST', body: { token, locale, - new_password: "newPassword", + new_password: 'newPassword', }, - headers: { "Content-Type": "application/x-www-form-urlencoded" }, + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, followRedirects: false, }); expect(formResponse.status).toEqual(200); @@ -748,23 +748,23 @@ describe("Pages Router", () => { ); }); - it("localizes end-to-end for password reset: invalid link", async () => { + it('localizes end-to-end for password reset: invalid link', async () => { await reconfigureServer(config); const sendPasswordResetEmail = spyOn( config.emailAdapter, - "sendPasswordResetEmail" + 'sendPasswordResetEmail' ).and.callThrough(); const user = new Parse.User(); - user.setUsername("exampleUsername"); - user.setPassword("examplePassword"); - user.set("email", "mail@example.com"); + user.setUsername('exampleUsername'); + user.setPassword('examplePassword'); + user.set('email', 'mail@example.com'); await user.signUp(); await Parse.User.requestPasswordReset(user.getEmail()); const link = sendPasswordResetEmail.calls.all()[0].args[0].link; const linkWithLocale = new URL(link); linkWithLocale.searchParams.append(pageParams.locale, exampleLocale); - linkWithLocale.searchParams.set(pageParams.token, "invalidToken"); + linkWithLocale.searchParams.set(pageParams.token, 'invalidToken'); const linkResponse = await request({ url: linkWithLocale.toString(), @@ -780,18 +780,18 @@ describe("Pages Router", () => { ); }); - it_id("2845c2ea-23ba-45d2-a33f-63181d419bca")(it)( - "localizes end-to-end for verify email: success", + it_id('2845c2ea-23ba-45d2-a33f-63181d419bca')(it)( + 'localizes end-to-end for verify email: success', async () => { await reconfigureServer(config); const sendVerificationEmail = spyOn( config.emailAdapter, - "sendVerificationEmail" + 'sendVerificationEmail' ).and.callThrough(); const user = new Parse.User(); - user.setUsername("exampleUsername"); - user.setPassword("examplePassword"); - user.set("email", "mail@example.com"); + user.setUsername('exampleUsername'); + user.setPassword('examplePassword'); + user.set('email', 'mail@example.com'); await user.signUp(); await jasmine.timeout(); @@ -814,25 +814,25 @@ describe("Pages Router", () => { } ); - it_id("f2272b94-b4ac-474f-8e47-1ca74de136f5")(it)( - "localizes end-to-end for verify email: invalid verification link - link send success", + it_id('f2272b94-b4ac-474f-8e47-1ca74de136f5')(it)( + 'localizes end-to-end for verify email: invalid verification link - link send success', async () => { await reconfigureServer(config); const sendVerificationEmail = spyOn( config.emailAdapter, - "sendVerificationEmail" + 'sendVerificationEmail' ).and.callThrough(); const user = new Parse.User(); - user.setUsername("exampleUsername"); - user.setPassword("examplePassword"); - user.set("email", "mail@example.com"); + user.setUsername('exampleUsername'); + user.setPassword('examplePassword'); + user.set('email', 'mail@example.com'); await user.signUp(); await jasmine.timeout(); const link = sendVerificationEmail.calls.all()[0].args[0].link; const linkWithLocale = new URL(link); linkWithLocale.searchParams.append(pageParams.locale, exampleLocale); - linkWithLocale.searchParams.set(pageParams.token, "invalidToken"); + linkWithLocale.searchParams.set(pageParams.token, 'invalidToken'); const linkResponse = await request({ url: linkWithLocale.toString(), @@ -840,10 +840,10 @@ describe("Pages Router", () => { }); expect(linkResponse.status).toBe(200); - const appId = linkResponse.headers["x-parse-page-param-appid"]; - const locale = linkResponse.headers["x-parse-page-param-locale"]; + const appId = linkResponse.headers['x-parse-page-param-appid']; + const locale = linkResponse.headers['x-parse-page-param-locale']; const publicServerUrl = - linkResponse.headers["x-parse-page-param-publicserverurl"]; + linkResponse.headers['x-parse-page-param-publicserverurl']; const invalidVerificationPagePath = pageResponse.calls.all()[0].args[0]; expect(appId).toBeDefined(); @@ -858,12 +858,12 @@ describe("Pages Router", () => { const formUrl = `${publicServerUrl}/apps/${appId}/resend_verification_email`; const formResponse = await request({ url: formUrl, - method: "POST", + method: 'POST', body: { locale, - username: "exampleUsername", + username: 'exampleUsername', }, - headers: { "Content-Type": "application/x-www-form-urlencoded" }, + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, followRedirects: false, }); expect(formResponse.status).toEqual(303); @@ -873,25 +873,25 @@ describe("Pages Router", () => { } ); - it_id("1d46d36a-e455-4ae7-8717-e0d286e95f02")(it)( - "localizes end-to-end for verify email: invalid verification link - link send fail", + it_id('1d46d36a-e455-4ae7-8717-e0d286e95f02')(it)( + 'localizes end-to-end for verify email: invalid verification link - link send fail', async () => { await reconfigureServer(config); const sendVerificationEmail = spyOn( config.emailAdapter, - "sendVerificationEmail" + 'sendVerificationEmail' ).and.callThrough(); const user = new Parse.User(); - user.setUsername("exampleUsername"); - user.setPassword("examplePassword"); - user.set("email", "mail@example.com"); + user.setUsername('exampleUsername'); + user.setPassword('examplePassword'); + user.set('email', 'mail@example.com'); await user.signUp(); await jasmine.timeout(); const link = sendVerificationEmail.calls.all()[0].args[0].link; const linkWithLocale = new URL(link); linkWithLocale.searchParams.append(pageParams.locale, exampleLocale); - linkWithLocale.searchParams.set(pageParams.token, "invalidToken"); + linkWithLocale.searchParams.set(pageParams.token, 'invalidToken'); const linkResponse = await request({ url: linkWithLocale.toString(), @@ -899,10 +899,10 @@ describe("Pages Router", () => { }); expect(linkResponse.status).toBe(200); - const appId = linkResponse.headers["x-parse-page-param-appid"]; - const locale = linkResponse.headers["x-parse-page-param-locale"]; + const appId = linkResponse.headers['x-parse-page-param-appid']; + const locale = linkResponse.headers['x-parse-page-param-locale']; const publicServerUrl = - linkResponse.headers["x-parse-page-param-publicserverurl"]; + linkResponse.headers['x-parse-page-param-publicserverurl']; await jasmine.timeout(); const invalidVerificationPagePath = @@ -918,20 +918,20 @@ describe("Pages Router", () => { spyOn( UserController.prototype, - "resendVerificationEmail" + 'resendVerificationEmail' ).and.callFake(() => - Promise.reject("failed to resend verification email") + Promise.reject('failed to resend verification email') ); const formUrl = `${publicServerUrl}/apps/${appId}/resend_verification_email`; const formResponse = await request({ url: formUrl, - method: "POST", + method: 'POST', body: { locale, - username: "exampleUsername", + username: 'exampleUsername', }, - headers: { "Content-Type": "application/x-www-form-urlencoded" }, + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, followRedirects: false, }); expect(formResponse.status).toEqual(303); @@ -941,16 +941,16 @@ describe("Pages Router", () => { } ); - it("localizes end-to-end for resend verification email: invalid link", async () => { + it('localizes end-to-end for resend verification email: invalid link', async () => { await reconfigureServer(config); const formUrl = `${config.publicServerURL}/apps/${config.appId}/resend_verification_email`; const formResponse = await request({ url: formUrl, - method: "POST", + method: 'POST', body: { locale: exampleLocale, }, - headers: { "Content-Type": "application/x-www-form-urlencoded" }, + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, followRedirects: false, }); expect(formResponse.status).toEqual(303); @@ -960,37 +960,37 @@ describe("Pages Router", () => { }); }); - describe("failing with missing parameters", () => { - it("verifyEmail: throws on missing server configuration", async () => { + describe('failing with missing parameters', () => { + it('verifyEmail: throws on missing server configuration', async () => { delete req.config; const verifyEmail = req => (() => new PagesRouter().verifyEmail(req)).bind(null); expect(verifyEmail(req)).toThrow(); }); - it("resendVerificationEmail: throws on missing server configuration", async () => { + it('resendVerificationEmail: throws on missing server configuration', async () => { delete req.config; const resendVerificationEmail = req => (() => new PagesRouter().resendVerificationEmail(req)).bind(null); expect(resendVerificationEmail(req)).toThrow(); }); - it("requestResetPassword: throws on missing server configuration", async () => { + it('requestResetPassword: throws on missing server configuration', async () => { delete req.config; const requestResetPassword = req => (() => new PagesRouter().requestResetPassword(req)).bind(null); expect(requestResetPassword(req)).toThrow(); }); - it("resetPassword: throws on missing server configuration", async () => { + it('resetPassword: throws on missing server configuration', async () => { delete req.config; const resetPassword = req => (() => new PagesRouter().resetPassword(req)).bind(null); expect(resetPassword(req)).toThrow(); }); - it("verifyEmail: responds with invalid link on missing username", async () => { - req.query.token = "exampleToken"; + it('verifyEmail: responds with invalid link on missing username', async () => { + req.query.token = 'exampleToken'; req.params = {}; req.config.userController = { verifyEmail: () => Promise.reject() }; const verifyEmail = req => new PagesRouter().verifyEmail(req); @@ -1001,13 +1001,13 @@ describe("Pages Router", () => { ); }); - it("resetPassword: responds with page choose password with error message on failed password update", async () => { + it('resetPassword: responds with page choose password with error message on failed password update', async () => { req.body = { - token: "exampleToken", - username: "exampleUsername", - new_password: "examplePassword", + token: 'exampleToken', + username: 'exampleUsername', + new_password: 'examplePassword', }; - const error = "exampleError"; + const error = 'exampleError'; req.config.userController = { updatePassword: () => Promise.reject(error), }; @@ -1018,14 +1018,14 @@ describe("Pages Router", () => { expect(goToPage.calls.all()[0].args[2].error).toBe(error); }); - it("resetPassword: responds with AJAX error with error message on failed password update", async () => { + it('resetPassword: responds with AJAX error with error message on failed password update', async () => { req.xhr = true; req.body = { - token: "exampleToken", - username: "exampleUsername", - new_password: "examplePassword", + token: 'exampleToken', + username: 'exampleUsername', + new_password: 'examplePassword', }; - const error = "exampleError"; + const error = 'exampleError'; req.config.userController = { updatePassword: () => Promise.reject(error), }; @@ -1037,8 +1037,8 @@ describe("Pages Router", () => { }); }); - describe("exploits", () => { - it("rejects requesting file outside of pages scope with UNIX path patterns", async () => { + describe('exploits', () => { + it('rejects requesting file outside of pages scope with UNIX path patterns', async () => { await reconfigureServer(config); // Do not compose this URL with `new URL(...)` because that would normalize @@ -1049,27 +1049,27 @@ describe("Pages Router", () => { followRedirects: false, }).catch(e => e); expect(response.status).toBe(404); - expect(response.text).toBe("Not found."); + expect(response.text).toBe('Not found.'); }); }); - describe("custom route", () => { - it("handles custom route with GET", async () => { + describe('custom route', () => { + it('handles custom route with GET', async () => { config.pages.customRoutes = [ { - method: "GET", - path: "custom_page", + method: 'GET', + path: 'custom_page', handler: async req => { expect(req).toBeDefined(); - expect(req.method).toBe("GET"); - return { file: "custom_page.html" }; + expect(req.method).toBe('GET'); + return { file: 'custom_page.html' }; }, }, ]; await reconfigureServer(config); const handlerSpy = spyOn( config.pages.customRoutes[0], - "handler" + 'handler' ).and.callThrough(); const url = `${config.publicServerURL}/apps/${config.appId}/custom_page`; @@ -1082,21 +1082,21 @@ describe("Pages Router", () => { expect(handlerSpy).toHaveBeenCalled(); }); - it("handles custom route with POST", async () => { + it('handles custom route with POST', async () => { config.pages.customRoutes = [ { - method: "POST", - path: "custom_page", + method: 'POST', + path: 'custom_page', handler: async req => { expect(req).toBeDefined(); - expect(req.method).toBe("POST"); - return { file: "custom_page.html" }; + expect(req.method).toBe('POST'); + return { file: 'custom_page.html' }; }, }, ]; const handlerSpy = spyOn( config.pages.customRoutes[0], - "handler" + 'handler' ).and.callThrough(); await reconfigureServer(config); @@ -1104,41 +1104,41 @@ describe("Pages Router", () => { const response = await request({ url: url, followRedirects: false, - method: "POST", + method: 'POST', }).catch(e => e); expect(response.status).toBe(200); expect(response.text).toMatch(config.appName); expect(handlerSpy).toHaveBeenCalled(); }); - it("handles multiple custom routes", async () => { + it('handles multiple custom routes', async () => { config.pages.customRoutes = [ { - method: "GET", - path: "custom_page", + method: 'GET', + path: 'custom_page', handler: async req => { expect(req).toBeDefined(); - expect(req.method).toBe("GET"); - return { file: "custom_page.html" }; + expect(req.method).toBe('GET'); + return { file: 'custom_page.html' }; }, }, { - method: "POST", - path: "custom_page", + method: 'POST', + path: 'custom_page', handler: async req => { expect(req).toBeDefined(); - expect(req.method).toBe("POST"); - return { file: "custom_page.html" }; + expect(req.method).toBe('POST'); + return { file: 'custom_page.html' }; }, }, ]; const getHandlerSpy = spyOn( config.pages.customRoutes[0], - "handler" + 'handler' ).and.callThrough(); const postHandlerSpy = spyOn( config.pages.customRoutes[1], - "handler" + 'handler' ).and.callThrough(); await reconfigureServer(config); @@ -1146,7 +1146,7 @@ describe("Pages Router", () => { const getResponse = await request({ url: url, followRedirects: false, - method: "GET", + method: 'GET', }).catch(e => e); expect(getResponse.status).toBe(200); expect(getResponse.text).toMatch(config.appName); @@ -1155,23 +1155,23 @@ describe("Pages Router", () => { const postResponse = await request({ url: url, followRedirects: false, - method: "POST", + method: 'POST', }).catch(e => e); expect(postResponse.status).toBe(200); expect(postResponse.text).toMatch(config.appName); expect(postHandlerSpy).toHaveBeenCalled(); }); - it("handles custom route with async handler", async () => { + it('handles custom route with async handler', async () => { config.pages.customRoutes = [ { - method: "GET", - path: "custom_page", + method: 'GET', + path: 'custom_page', handler: async req => { expect(req).toBeDefined(); - expect(req.method).toBe("GET"); + expect(req.method).toBe('GET'); const file = await new Promise(resolve => - setTimeout(resolve("custom_page.html"), 1000) + setTimeout(resolve('custom_page.html'), 1000) ); return { file }; }, @@ -1180,7 +1180,7 @@ describe("Pages Router", () => { await reconfigureServer(config); const handlerSpy = spyOn( config.pages.customRoutes[0], - "handler" + 'handler' ).and.callThrough(); const url = `${config.publicServerURL}/apps/${config.appId}/custom_page`; @@ -1193,18 +1193,18 @@ describe("Pages Router", () => { expect(handlerSpy).toHaveBeenCalled(); }); - it("returns 404 if custom route does not return page", async () => { + it('returns 404 if custom route does not return page', async () => { config.pages.customRoutes = [ { - method: "GET", - path: "custom_page", + method: 'GET', + path: 'custom_page', handler: async () => {}, }, ]; await reconfigureServer(config); const handlerSpy = spyOn( config.pages.customRoutes[0], - "handler" + 'handler' ).and.callThrough(); const url = `${config.publicServerURL}/apps/${config.appId}/custom_page`; @@ -1213,23 +1213,23 @@ describe("Pages Router", () => { followRedirects: false, }).catch(e => e); expect(response.status).toBe(404); - expect(response.text).toMatch("Not found"); + expect(response.text).toMatch('Not found'); expect(handlerSpy).toHaveBeenCalled(); }); }); - describe("custom endpoint", () => { - it("password reset works with custom endpoint", async () => { - config.pages.pagesEndpoint = "customEndpoint"; + describe('custom endpoint', () => { + it('password reset works with custom endpoint', async () => { + config.pages.pagesEndpoint = 'customEndpoint'; await reconfigureServer(config); const sendPasswordResetEmail = spyOn( config.emailAdapter, - "sendPasswordResetEmail" + 'sendPasswordResetEmail' ).and.callThrough(); const user = new Parse.User(); - user.setUsername("exampleUsername"); - user.setPassword("examplePassword"); - user.set("email", "mail@example.com"); + user.setUsername('exampleUsername'); + user.setPassword('examplePassword'); + user.set('email', 'mail@example.com'); await user.signUp(); await Parse.User.requestPasswordReset(user.getEmail()); @@ -1240,10 +1240,10 @@ describe("Pages Router", () => { }); expect(linkResponse.status).toBe(200); - const appId = linkResponse.headers["x-parse-page-param-appid"]; - const token = linkResponse.headers["x-parse-page-param-token"]; + const appId = linkResponse.headers['x-parse-page-param-appid']; + const token = linkResponse.headers['x-parse-page-param-token']; const publicServerUrl = - linkResponse.headers["x-parse-page-param-publicserverurl"]; + linkResponse.headers['x-parse-page-param-publicserverurl']; const passwordResetPagePath = pageResponse.calls.all()[0].args[0]; expect(appId).toBeDefined(); expect(token).toBeDefined(); @@ -1256,12 +1256,12 @@ describe("Pages Router", () => { const formUrl = `${publicServerUrl}/${config.pages.pagesEndpoint}/${appId}/request_password_reset`; const formResponse = await request({ url: formUrl, - method: "POST", + method: 'POST', body: { token, - new_password: "newPassword", + new_password: 'newPassword', }, - headers: { "Content-Type": "application/x-www-form-urlencoded" }, + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, followRedirects: false, }); expect(formResponse.status).toEqual(200); @@ -1270,19 +1270,19 @@ describe("Pages Router", () => { ); }); - it_id("81c1c28e-5dfd-4ffb-a09b-283156c08483")(it)( - "email verification works with custom endpoint", + it_id('81c1c28e-5dfd-4ffb-a09b-283156c08483')(it)( + 'email verification works with custom endpoint', async () => { - config.pages.pagesEndpoint = "customEndpoint"; + config.pages.pagesEndpoint = 'customEndpoint'; await reconfigureServer(config); const sendVerificationEmail = spyOn( config.emailAdapter, - "sendVerificationEmail" + 'sendVerificationEmail' ).and.callThrough(); const user = new Parse.User(); - user.setUsername("exampleUsername"); - user.setPassword("examplePassword"); - user.set("email", "mail@example.com"); + user.setUsername('exampleUsername'); + user.setPassword('examplePassword'); + user.set('email', 'mail@example.com'); await user.signUp(); await jasmine.timeout(); diff --git a/spec/Parse.Push.spec.js b/spec/Parse.Push.spec.js index 8a93332da6..9edbe43c62 100644 --- a/spec/Parse.Push.spec.js +++ b/spec/Parse.Push.spec.js @@ -1,12 +1,12 @@ -"use strict"; +'use strict'; -const request = require("../lib/request"); +const request = require('../lib/request'); const pushCompleted = async pushId => { - const query = new Parse.Query("_PushStatus"); - query.equalTo("objectId", pushId); + const query = new Parse.Query('_PushStatus'); + query.equalTo('objectId', pushId); let result = await query.first({ useMasterKey: true }); - while (!(result && result.get("status") === "succeeded")) { + while (!(result && result.get('status') === 'succeeded')) { await jasmine.timeout(); result = await query.first({ useMasterKey: true }); } @@ -31,10 +31,10 @@ const provideInstallations = function (num) { const installations = []; while (installations.length !== num) { // add Android installations - const installation = new Parse.Object("_Installation"); - installation.set("installationId", "installation_" + installations.length); - installation.set("deviceToken", "device_token_" + installations.length); - installation.set("deviceType", "android"); + const installation = new Parse.Object('_Installation'); + installation.set('installationId', 'installation_' + installations.length); + installation.set('deviceToken', 'device_token_' + installations.length); + installation.set('deviceType', 'android'); installations.push(installation); } @@ -50,7 +50,7 @@ const losingAdapter = { return successfulAny(body, installations); }, getValidPushTypes: function () { - return ["android"]; + return ['android']; }, }; @@ -63,7 +63,7 @@ const setup = function () { const promises = installations.map(installation => { sendToInstallationSpy(installation); - if (installation.deviceType == "ios") { + if (installation.deviceType == 'ios') { expect(installation.badge).toEqual(badge); expect(installation.originalBadge + 1).toEqual(installation.badge); } else { @@ -78,7 +78,7 @@ const setup = function () { return Promise.all(promises); }, getValidPushTypes: function () { - return ["ios", "android"]; + return ['ios', 'android']; }, }; @@ -93,15 +93,15 @@ const setup = function () { .then(() => { const installations = []; while (installations.length != 10) { - const installation = new Parse.Object("_Installation"); + const installation = new Parse.Object('_Installation'); installation.set( - "installationId", - "installation_" + installations.length + 'installationId', + 'installation_' + installations.length ); - installation.set("deviceToken", "device_token_" + installations.length); - installation.set("badge", installations.length); - installation.set("originalBadge", installations.length); - installation.set("deviceType", "ios"); + installation.set('deviceToken', 'device_token_' + installations.length); + installation.set('badge', installations.length); + installation.set('originalBadge', installations.length); + installation.set('deviceType', 'ios'); installations.push(installation); } return Parse.Object.saveAll(installations); @@ -113,18 +113,18 @@ const setup = function () { }); }; -describe("Parse.Push", () => { - it_id("d1e591c4-2b21-466b-9ee2-5be467b6b771")(it)( - "should properly send push", +describe('Parse.Push', () => { + it_id('d1e591c4-2b21-466b-9ee2-5be467b6b771')(it)( + 'should properly send push', async () => { const { sendToInstallationSpy } = await setup(); const pushStatusId = await Parse.Push.send({ where: { - deviceType: "ios", + deviceType: 'ios', }, data: { - badge: "Increment", - alert: "Hello world!", + badge: 'Increment', + alert: 'Hello world!', }, }); await pushCompleted(pushStatusId); @@ -132,72 +132,72 @@ describe("Parse.Push", () => { } ); - it_id("2a58e3c7-b6f3-4261-a384-6c893b2ac3f3")(it)( - "should properly send push with lowercaseIncrement", + it_id('2a58e3c7-b6f3-4261-a384-6c893b2ac3f3')(it)( + 'should properly send push with lowercaseIncrement', async () => { await setup(); const pushStatusId = await Parse.Push.send({ where: { - deviceType: "ios", + deviceType: 'ios', }, data: { - badge: "increment", - alert: "Hello world!", + badge: 'increment', + alert: 'Hello world!', }, }); await pushCompleted(pushStatusId); } ); - it_id("e21780b6-2cdd-467e-8013-81030f3288e9")(it)( - "should not allow clients to query _PushStatus", + it_id('e21780b6-2cdd-467e-8013-81030f3288e9')(it)( + 'should not allow clients to query _PushStatus', async () => { await setup(); const pushStatusId = await Parse.Push.send({ where: { - deviceType: "ios", + deviceType: 'ios', }, data: { - badge: "increment", - alert: "Hello world!", + badge: 'increment', + alert: 'Hello world!', }, }); await pushCompleted(pushStatusId); try { await request({ - url: "http://localhost:8378/1/classes/_PushStatus", + url: 'http://localhost:8378/1/classes/_PushStatus', json: true, headers: { - "X-Parse-Application-Id": "test", + 'X-Parse-Application-Id': 'test', }, }); fail(); } catch (response) { - expect(response.data.error).toEqual("unauthorized"); + expect(response.data.error).toEqual('unauthorized'); } } ); - it_id("924cf5f5-f684-4925-978a-e52c0c457366")(it)( - "should allow master key to query _PushStatus", + it_id('924cf5f5-f684-4925-978a-e52c0c457366')(it)( + 'should allow master key to query _PushStatus', async () => { await setup(); const pushStatusId = await Parse.Push.send({ where: { - deviceType: "ios", + deviceType: 'ios', }, data: { - badge: "increment", - alert: "Hello world!", + badge: 'increment', + alert: 'Hello world!', }, }); await pushCompleted(pushStatusId); const response = await request({ - url: "http://localhost:8378/1/classes/_PushStatus", + url: 'http://localhost:8378/1/classes/_PushStatus', json: true, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Master-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Master-Key': 'test', }, }); const body = response.data; @@ -209,16 +209,16 @@ describe("Parse.Push", () => { } ); - it("should throw error if missing push configuration", async () => { + it('should throw error if missing push configuration', async () => { await reconfigureServer({ push: null }); try { await Parse.Push.send({ where: { - deviceType: "ios", + deviceType: 'ios', }, data: { - badge: "increment", - alert: "Hello world!", + badge: 'increment', + alert: 'Hello world!', }, }); fail(); @@ -238,14 +238,14 @@ describe("Parse.Push", () => { }); await Parse.Object.saveAll(provideInstallations()); const pushStatusId = await Parse.Push.send({ - data: { alert: "We fixed our status!" }, - where: { deviceType: "android" }, + data: { alert: 'We fixed our status!' }, + where: { deviceType: 'android' }, }); await pushCompleted(pushStatusId); const result = await Parse.Push.getPushStatus(pushStatusId); - expect(result.get("status")).toEqual("succeeded"); - expect(result.get("numSent")).toEqual(1); - expect(result.get("count")).toEqual(undefined); + expect(result.get('status')).toEqual('succeeded'); + expect(result.get('numSent')).toEqual(1); + expect(result.get('count')).toEqual(undefined); }); /** @@ -257,13 +257,13 @@ describe("Parse.Push", () => { const installations = provideInstallations(); // add 1 iOS installation which we will omit & add later on - const iOSInstallation = new Parse.Object("_Installation"); + const iOSInstallation = new Parse.Object('_Installation'); iOSInstallation.set( - "installationId", - "installation_" + installations.length + 'installationId', + 'installation_' + installations.length ); - iOSInstallation.set("deviceToken", "device_token_" + installations.length); - iOSInstallation.set("deviceType", "ios"); + iOSInstallation.set('deviceToken', 'device_token_' + installations.length); + iOSInstallation.set('deviceType', 'ios'); installations.push(iOSInstallation); await reconfigureServer({ @@ -276,21 +276,21 @@ describe("Parse.Push", () => { return successfulAny(body, installations); }, getValidPushTypes: function () { - return ["android"]; + return ['android']; }, }, }, }); await Parse.Object.saveAll(installations); const pushStatusId = await Parse.Push.send({ - data: { alert: "We fixed our status!" }, - where: { deviceType: { $ne: "random" } }, + data: { alert: 'We fixed our status!' }, + where: { deviceType: { $ne: 'random' } }, }); await pushCompleted(pushStatusId); const result = await Parse.Push.getPushStatus(pushStatusId); - expect(result.get("status")).toEqual("succeeded"); - expect(result.get("numSent")).toEqual(3); - expect(result.get("count")).toEqual(undefined); + expect(result.get('status')).toEqual('succeeded'); + expect(result.get('numSent')).toEqual(3); + expect(result.get('count')).toEqual(undefined); }); /** @@ -307,15 +307,15 @@ describe("Parse.Push", () => { }); await Parse.Object.saveAll(installations); const pushStatusId = await Parse.Push.send({ - data: { alert: "We fixed our status!" }, - where: { deviceType: "android" }, + data: { alert: 'We fixed our status!' }, + where: { deviceType: 'android' }, }); await pushCompleted(pushStatusId); const result = await Parse.Push.getPushStatus(pushStatusId); - expect(result.get("status")).toEqual("succeeded"); + expect(result.get('status')).toEqual('succeeded'); // expect # less than # of batches used, assuming each batch is 100 pushes - expect(result.get("numSent")).toEqual(devices - devices / 100); - expect(result.get("count")).toEqual(undefined); + expect(result.get('numSent')).toEqual(devices - devices / 100); + expect(result.get('count')).toEqual(undefined); }); /** @@ -330,16 +330,16 @@ describe("Parse.Push", () => { // add 1 iOS installation which we will omit & add later on const iOSInstallations = []; while (iOSInstallations.length !== devices / 100) { - const iOSInstallation = new Parse.Object("_Installation"); + const iOSInstallation = new Parse.Object('_Installation'); iOSInstallation.set( - "installationId", - "installation_" + installations.length + 'installationId', + 'installation_' + installations.length ); iOSInstallation.set( - "deviceToken", - "device_token_" + installations.length + 'deviceToken', + 'device_token_' + installations.length ); - iOSInstallation.set("deviceType", "ios"); + iOSInstallation.set('deviceType', 'ios'); installations.push(iOSInstallation); iOSInstallations.push(iOSInstallation); } @@ -353,7 +353,7 @@ describe("Parse.Push", () => { return successfulAny(body, installations); }, getValidPushTypes: function () { - return ["android"]; + return ['android']; }, }, }, @@ -361,14 +361,14 @@ describe("Parse.Push", () => { await Parse.Object.saveAll(installations); const pushStatusId = await Parse.Push.send({ - data: { alert: "We fixed our status!" }, - where: { deviceType: { $ne: "random" } }, + data: { alert: 'We fixed our status!' }, + where: { deviceType: { $ne: 'random' } }, }); await pushCompleted(pushStatusId); const result = await Parse.Push.getPushStatus(pushStatusId); - expect(result.get("status")).toEqual("succeeded"); + expect(result.get('status')).toEqual('succeeded'); // expect # less than # of batches used, assuming each batch is 100 pushes - expect(result.get("numSent")).toEqual(devices + devices / 100); - expect(result.get("count")).toEqual(undefined); + expect(result.get('numSent')).toEqual(devices + devices / 100); + expect(result.get('count')).toEqual(undefined); }); }); diff --git a/spec/ParseACL.spec.js b/spec/ParseACL.spec.js index 2197199a30..b8a982ec45 100644 --- a/spec/ParseACL.spec.js +++ b/spec/ParseACL.spec.js @@ -1,22 +1,22 @@ // This is a port of the test suite: // hungry/js/test/parse_acl_test.js -const rest = require("../lib/rest"); -const Config = require("../lib/Config"); -const auth = require("../lib/Auth"); +const rest = require('../lib/rest'); +const Config = require('../lib/Config'); +const auth = require('../lib/Auth'); -describe("Parse.ACL", () => { - it("acl must be valid", () => { +describe('Parse.ACL', () => { + it('acl must be valid', () => { const user = new Parse.User(); - expect(() => user.setACL("ACL")).toThrow( - new Parse.Error(Parse.Error.OTHER_CAUSE, "ACL must be a Parse ACL.") + expect(() => user.setACL('ACL')).toThrow( + new Parse.Error(Parse.Error.OTHER_CAUSE, 'ACL must be a Parse ACL.') ); }); - it("refresh object with acl", async done => { + it('refresh object with acl', async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set("username", "alice"); - user.set("password", "wonderland"); + user.set('username', 'alice'); + user.set('password', 'wonderland'); await user.signUp(null); const object = new TestObject(); const acl = new Parse.ACL(user); @@ -26,11 +26,11 @@ describe("Parse.ACL", () => { done(); }); - it("acl an object owned by one user and public get", async done => { + it('acl an object owned by one user and public get', async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set("username", "alice"); - user.set("password", "wonderland"); + user.set('username', 'alice'); + user.set('password', 'wonderland'); await user.signUp(); const object = new TestObject(); const acl = new Parse.ACL(user); @@ -40,23 +40,23 @@ describe("Parse.ACL", () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get("ACL")); + ok(object.get('ACL')); await Parse.User.logOut(); const query = new Parse.Query(TestObject); try { await query.get(object.id); - done.fail("Should not have retrieved the object."); + done.fail('Should not have retrieved the object.'); } catch (error) { equal(error.code, Parse.Error.OBJECT_NOT_FOUND); done(); } }); - it("acl an object owned by one user and public find", async done => { + it('acl an object owned by one user and public find', async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set("username", "alice"); - user.set("password", "wonderland"); + user.set('username', 'alice'); + user.set('password', 'wonderland'); await user.signUp(); const object = new TestObject(); @@ -67,7 +67,7 @@ describe("Parse.ACL", () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get("ACL")); + ok(object.get('ACL')); // Start making requests by the public, which should all fail. await Parse.User.logOut(); @@ -78,11 +78,11 @@ describe("Parse.ACL", () => { done(); }); - it("acl an object owned by one user and public update", async done => { + it('acl an object owned by one user and public update', async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set("username", "alice"); - user.set("password", "wonderland"); + user.set('username', 'alice'); + user.set('password', 'wonderland'); await user.signUp(); const object = new TestObject(); @@ -93,26 +93,26 @@ describe("Parse.ACL", () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get("ACL")); + ok(object.get('ACL')); // Start making requests by the public, which should all fail. await Parse.User.logOut(); // Update - object.set("foo", "bar"); + object.set('foo', 'bar'); try { await object.save(); - done.fail("Should not have been able to update the object."); + done.fail('Should not have been able to update the object.'); } catch (err) { equal(err.code, Parse.Error.OBJECT_NOT_FOUND); done(); } }); - it("acl an object owned by one user and public delete", async done => { + it('acl an object owned by one user and public delete', async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set("username", "alice"); - user.set("password", "wonderland"); + user.set('username', 'alice'); + user.set('password', 'wonderland'); await user.signUp(); const object = new TestObject(); @@ -124,24 +124,24 @@ describe("Parse.ACL", () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get("ACL")); + ok(object.get('ACL')); // Start making requests by the public, which should all fail. await Parse.User.logOut(); try { await object.destroy(); - done.fail("destroy should fail"); + done.fail('destroy should fail'); } catch (error) { expect(error.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); done(); } }); - it("acl an object owned by one user and logged in get", async done => { + it('acl an object owned by one user and logged in get', async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set("username", "alice"); - user.set("password", "wonderland"); + user.set('username', 'alice'); + user.set('password', 'wonderland'); await user.signUp(); const object = new TestObject(); const acl = new Parse.ACL(user); @@ -151,10 +151,10 @@ describe("Parse.ACL", () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get("ACL")); + ok(object.get('ACL')); await Parse.User.logOut(); - await Parse.User.logIn("alice", "wonderland"); + await Parse.User.logIn('alice', 'wonderland'); // Get const query = new Parse.Query(TestObject); const result = await query.get(object.id); @@ -164,15 +164,15 @@ describe("Parse.ACL", () => { equal(result.getACL().getWriteAccess(user), true); equal(result.getACL().getPublicReadAccess(), false); equal(result.getACL().getPublicWriteAccess(), false); - ok(object.get("ACL")); + ok(object.get('ACL')); done(); }); - it("acl an object owned by one user and logged in find", async done => { + it('acl an object owned by one user and logged in find', async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set("username", "alice"); - user.set("password", "wonderland"); + user.set('username', 'alice'); + user.set('password', 'wonderland'); await user.signUp(); const object = new TestObject(); const acl = new Parse.ACL(user); @@ -182,9 +182,9 @@ describe("Parse.ACL", () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get("ACL")); + ok(object.get('ACL')); await Parse.User.logOut(); - await Parse.User.logIn("alice", "wonderland"); + await Parse.User.logIn('alice', 'wonderland'); // Find const query = new Parse.Query(TestObject); const results = await query.find(); @@ -199,15 +199,15 @@ describe("Parse.ACL", () => { equal(result.getACL().getWriteAccess(user), true); equal(result.getACL().getPublicReadAccess(), false); equal(result.getACL().getPublicWriteAccess(), false); - ok(object.get("ACL")); + ok(object.get('ACL')); done(); }); - it("acl an object owned by one user and logged in update", async done => { + it('acl an object owned by one user and logged in update', async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set("username", "alice"); - user.set("password", "wonderland"); + user.set('username', 'alice'); + user.set('password', 'wonderland'); await user.signUp(); const object = new TestObject(); const acl = new Parse.ACL(user); @@ -217,21 +217,21 @@ describe("Parse.ACL", () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get("ACL")); + ok(object.get('ACL')); await Parse.User.logOut(); - await Parse.User.logIn("alice", "wonderland"); + await Parse.User.logIn('alice', 'wonderland'); // Update - object.set("foo", "bar"); + object.set('foo', 'bar'); await object.save(); done(); }); - it("acl an object owned by one user and logged in delete", async done => { + it('acl an object owned by one user and logged in delete', async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set("username", "alice"); - user.set("password", "wonderland"); + user.set('username', 'alice'); + user.set('password', 'wonderland'); await user.signUp(); const object = new TestObject(); const acl = new Parse.ACL(user); @@ -241,19 +241,19 @@ describe("Parse.ACL", () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get("ACL")); + ok(object.get('ACL')); await Parse.User.logOut(); - await Parse.User.logIn("alice", "wonderland"); + await Parse.User.logIn('alice', 'wonderland'); // Delete await object.destroy(); done(); }); - it("acl making an object publicly readable and public get", async done => { + it('acl making an object publicly readable and public get', async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set("username", "alice"); - user.set("password", "wonderland"); + user.set('username', 'alice'); + user.set('password', 'wonderland'); await user.signUp(); const object = new TestObject(); const acl = new Parse.ACL(user); @@ -263,7 +263,7 @@ describe("Parse.ACL", () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get("ACL")); + ok(object.get('ACL')); // Now make it public. object.getACL().setPublicReadAccess(true); @@ -272,7 +272,7 @@ describe("Parse.ACL", () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), true); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get("ACL")); + ok(object.get('ACL')); await Parse.User.logOut(); // Get @@ -283,11 +283,11 @@ describe("Parse.ACL", () => { done(); }); - it("acl making an object publicly readable and public find", async done => { + it('acl making an object publicly readable and public find', async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set("username", "alice"); - user.set("password", "wonderland"); + user.set('username', 'alice'); + user.set('password', 'wonderland'); await user.signUp(); const object = new TestObject(); const acl = new Parse.ACL(user); @@ -297,7 +297,7 @@ describe("Parse.ACL", () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get("ACL")); + ok(object.get('ACL')); // Now make it public. object.getACL().setPublicReadAccess(true); @@ -306,7 +306,7 @@ describe("Parse.ACL", () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), true); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get("ACL")); + ok(object.get('ACL')); await Parse.User.logOut(); // Find @@ -319,11 +319,11 @@ describe("Parse.ACL", () => { done(); }); - it("acl making an object publicly readable and public update", async done => { + it('acl making an object publicly readable and public update', async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set("username", "alice"); - user.set("password", "wonderland"); + user.set('username', 'alice'); + user.set('password', 'wonderland'); await user.signUp(); const object = new TestObject(); const acl = new Parse.ACL(user); @@ -333,7 +333,7 @@ describe("Parse.ACL", () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get("ACL")); + ok(object.get('ACL')); // Now make it public. object.getACL().setPublicReadAccess(true); @@ -342,13 +342,13 @@ describe("Parse.ACL", () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), true); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get("ACL")); + ok(object.get('ACL')); await Parse.User.logOut(); - object.set("foo", "bar"); + object.set('foo', 'bar'); object.save().then( () => { - fail("the save should fail"); + fail('the save should fail'); }, error => { expect(error.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); @@ -357,11 +357,11 @@ describe("Parse.ACL", () => { ); }); - it("acl making an object publicly readable and public delete", async done => { + it('acl making an object publicly readable and public delete', async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set("username", "alice"); - user.set("password", "wonderland"); + user.set('username', 'alice'); + user.set('password', 'wonderland'); await user.signUp(); const object = new TestObject(); const acl = new Parse.ACL(user); @@ -371,7 +371,7 @@ describe("Parse.ACL", () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get("ACL")); + ok(object.get('ACL')); // Now make it public. object.getACL().setPublicReadAccess(true); @@ -380,13 +380,13 @@ describe("Parse.ACL", () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), true); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get("ACL")); + ok(object.get('ACL')); Parse.User.logOut() .then(() => object.destroy()) .then( () => { - fail("expected failure"); + fail('expected failure'); }, error => { expect(error.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); @@ -395,11 +395,11 @@ describe("Parse.ACL", () => { ); }); - it("acl making an object publicly writable and public get", async done => { + it('acl making an object publicly writable and public get', async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set("username", "alice"); - user.set("password", "wonderland"); + user.set('username', 'alice'); + user.set('password', 'wonderland'); await user.signUp(); const object = new TestObject(); const acl = new Parse.ACL(user); @@ -409,7 +409,7 @@ describe("Parse.ACL", () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get("ACL")); + ok(object.get('ACL')); // Now make it public. object.getACL().setPublicWriteAccess(true); @@ -418,7 +418,7 @@ describe("Parse.ACL", () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), true); - ok(object.get("ACL")); + ok(object.get('ACL')); await Parse.User.logOut(); // Get @@ -432,11 +432,11 @@ describe("Parse.ACL", () => { }); }); - it("acl making an object publicly writable and public find", async done => { + it('acl making an object publicly writable and public find', async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set("username", "alice"); - user.set("password", "wonderland"); + user.set('username', 'alice'); + user.set('password', 'wonderland'); await user.signUp(); const object = new TestObject(); const acl = new Parse.ACL(user); @@ -446,7 +446,7 @@ describe("Parse.ACL", () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get("ACL")); + ok(object.get('ACL')); // Now make it public. object.getACL().setPublicWriteAccess(true); @@ -455,7 +455,7 @@ describe("Parse.ACL", () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), true); - ok(object.get("ACL")); + ok(object.get('ACL')); await Parse.User.logOut(); // Find @@ -466,11 +466,11 @@ describe("Parse.ACL", () => { }); }); - it("acl making an object publicly writable and public update", async done => { + it('acl making an object publicly writable and public update', async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set("username", "alice"); - user.set("password", "wonderland"); + user.set('username', 'alice'); + user.set('password', 'wonderland'); await user.signUp(); const object = new TestObject(); const acl = new Parse.ACL(user); @@ -480,7 +480,7 @@ describe("Parse.ACL", () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get("ACL")); + ok(object.get('ACL')); // Now make it public. object.getACL().setPublicWriteAccess(true); @@ -489,20 +489,20 @@ describe("Parse.ACL", () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), true); - ok(object.get("ACL")); + ok(object.get('ACL')); Parse.User.logOut().then(() => { // Update - object.set("foo", "bar"); + object.set('foo', 'bar'); object.save().then(done); }); }); - it("acl making an object publicly writable and public delete", async done => { + it('acl making an object publicly writable and public delete', async done => { // Create an object owned by Alice. const user = new Parse.User(); - user.set("username", "alice"); - user.set("password", "wonderland"); + user.set('username', 'alice'); + user.set('password', 'wonderland'); await user.signUp(); const object = new TestObject(); const acl = new Parse.ACL(user); @@ -512,7 +512,7 @@ describe("Parse.ACL", () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), false); - ok(object.get("ACL")); + ok(object.get('ACL')); // Now make it public. object.getACL().setPublicWriteAccess(true); @@ -521,7 +521,7 @@ describe("Parse.ACL", () => { equal(object.getACL().getWriteAccess(user), true); equal(object.getACL().getPublicReadAccess(), false); equal(object.getACL().getPublicWriteAccess(), true); - ok(object.get("ACL")); + ok(object.get('ACL')); Parse.User.logOut().then(() => { // Delete @@ -529,13 +529,13 @@ describe("Parse.ACL", () => { }); }); - it("acl making an object privately writable (#3194)", done => { + it('acl making an object privately writable (#3194)', done => { // Create an object owned by Alice. let object; let user2; const user = new Parse.User(); - user.set("username", "alice"); - user.set("password", "wonderland"); + user.set('username', 'alice'); + user.set('password', 'wonderland'); user .signUp() .then(() => { @@ -550,8 +550,8 @@ describe("Parse.ACL", () => { }) .then(() => { user2 = new Parse.User(); - user2.set("username", "bob"); - user2.set("password", "burger"); + user2.set('username', 'bob'); + user2.set('password', 'burger'); return user2.signUp(); }) .then(() => { @@ -559,7 +559,7 @@ describe("Parse.ACL", () => { }) .then( () => { - fail("should not be able to destroy the object"); + fail('should not be able to destroy the object'); done(); }, err => { @@ -569,12 +569,12 @@ describe("Parse.ACL", () => { ); }); - it("acl sharing with another user and get", async done => { + it('acl sharing with another user and get', async done => { // Sign in as Bob. - const bob = await Parse.User.signUp("bob", "pass"); + const bob = await Parse.User.signUp('bob', 'pass'); await Parse.User.logOut(); - const alice = await Parse.User.signUp("alice", "wonderland"); + const alice = await Parse.User.signUp('alice', 'wonderland'); // Create an object shared by Bob and Alice. const object = new TestObject(); const acl = new Parse.ACL(alice); @@ -590,7 +590,7 @@ describe("Parse.ACL", () => { equal(object.getACL().getPublicWriteAccess(), false); // Sign in as Bob again. - await Parse.User.logIn("bob", "pass"); + await Parse.User.logIn('bob', 'pass'); const query = new Parse.Query(TestObject); query.get(object.id).then(result => { ok(result); @@ -599,12 +599,12 @@ describe("Parse.ACL", () => { }); }); - it("acl sharing with another user and find", async done => { + it('acl sharing with another user and find', async done => { // Sign in as Bob. - const bob = await Parse.User.signUp("bob", "pass"); + const bob = await Parse.User.signUp('bob', 'pass'); await Parse.User.logOut(); // Sign in as Alice. - const alice = await Parse.User.signUp("alice", "wonderland"); + const alice = await Parse.User.signUp('alice', 'wonderland'); // Create an object shared by Bob and Alice. const object = new TestObject(); const acl = new Parse.ACL(alice); @@ -620,14 +620,14 @@ describe("Parse.ACL", () => { equal(object.getACL().getPublicWriteAccess(), false); // Sign in as Bob again. - await Parse.User.logIn("bob", "pass"); + await Parse.User.logIn('bob', 'pass'); const query = new Parse.Query(TestObject); query.find().then(results => { equal(results.length, 1); const result = results[0]; ok(result); if (!result) { - fail("should have result"); + fail('should have result'); } else { equal(result.id, object.id); } @@ -635,12 +635,12 @@ describe("Parse.ACL", () => { }); }); - it("acl sharing with another user and update", async done => { + it('acl sharing with another user and update', async done => { // Sign in as Bob. - const bob = await Parse.User.signUp("bob", "pass"); + const bob = await Parse.User.signUp('bob', 'pass'); await Parse.User.logOut(); // Sign in as Alice. - const alice = await Parse.User.signUp("alice", "wonderland"); + const alice = await Parse.User.signUp('alice', 'wonderland'); // Create an object shared by Bob and Alice. const object = new TestObject(); const acl = new Parse.ACL(alice); @@ -656,17 +656,17 @@ describe("Parse.ACL", () => { equal(object.getACL().getPublicWriteAccess(), false); // Sign in as Bob again. - await Parse.User.logIn("bob", "pass"); - object.set("foo", "bar"); + await Parse.User.logIn('bob', 'pass'); + object.set('foo', 'bar'); object.save().then(done); }); - it("acl sharing with another user and delete", async done => { + it('acl sharing with another user and delete', async done => { // Sign in as Bob. - const bob = await Parse.User.signUp("bob", "pass"); + const bob = await Parse.User.signUp('bob', 'pass'); await Parse.User.logOut(); // Sign in as Alice. - const alice = await Parse.User.signUp("alice", "wonderland"); + const alice = await Parse.User.signUp('alice', 'wonderland'); // Create an object shared by Bob and Alice. const object = new TestObject(); const acl = new Parse.ACL(alice); @@ -682,16 +682,16 @@ describe("Parse.ACL", () => { equal(object.getACL().getPublicWriteAccess(), false); // Sign in as Bob again. - await Parse.User.logIn("bob", "pass"); - object.set("foo", "bar"); + await Parse.User.logIn('bob', 'pass'); + object.set('foo', 'bar'); object.destroy().then(done); }); - it("acl sharing with another user and public get", async done => { - const bob = await Parse.User.signUp("bob", "pass"); + it('acl sharing with another user and public get', async done => { + const bob = await Parse.User.signUp('bob', 'pass'); await Parse.User.logOut(); // Sign in as Alice. - const alice = await Parse.User.signUp("alice", "wonderland"); + const alice = await Parse.User.signUp('alice', 'wonderland'); // Create an object shared by Bob and Alice. const object = new TestObject(); const acl = new Parse.ACL(alice); @@ -719,11 +719,11 @@ describe("Parse.ACL", () => { ); }); - it("acl sharing with another user and public find", async done => { - const bob = await Parse.User.signUp("bob", "pass"); + it('acl sharing with another user and public find', async done => { + const bob = await Parse.User.signUp('bob', 'pass'); await Parse.User.logOut(); // Sign in as Alice. - const alice = await Parse.User.signUp("alice", "wonderland"); + const alice = await Parse.User.signUp('alice', 'wonderland'); // Create an object shared by Bob and Alice. const object = new TestObject(); const acl = new Parse.ACL(alice); @@ -748,12 +748,12 @@ describe("Parse.ACL", () => { }); }); - it("acl sharing with another user and public update", async done => { + it('acl sharing with another user and public update', async done => { // Sign in as Bob. - const bob = await Parse.User.signUp("bob", "pass"); + const bob = await Parse.User.signUp('bob', 'pass'); await Parse.User.logOut(); // Sign in as Alice. - const alice = await Parse.User.signUp("alice", "wonderland"); + const alice = await Parse.User.signUp('alice', 'wonderland'); // Create an object shared by Bob and Alice. const object = new TestObject(); const acl = new Parse.ACL(alice); @@ -770,10 +770,10 @@ describe("Parse.ACL", () => { // Start making requests by the public. Parse.User.logOut().then(() => { - object.set("foo", "bar"); + object.set('foo', 'bar'); object.save().then( () => { - fail("expected failure"); + fail('expected failure'); }, error => { expect(error.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); @@ -783,12 +783,12 @@ describe("Parse.ACL", () => { }); }); - it("acl sharing with another user and public delete", async done => { + it('acl sharing with another user and public delete', async done => { // Sign in as Bob. - const bob = await Parse.User.signUp("bob", "pass"); + const bob = await Parse.User.signUp('bob', 'pass'); await Parse.User.logOut(); // Sign in as Alice. - const alice = await Parse.User.signUp("alice", "wonderland"); + const alice = await Parse.User.signUp('alice', 'wonderland'); // Create an object shared by Bob and Alice. const object = new TestObject(); const acl = new Parse.ACL(alice); @@ -808,7 +808,7 @@ describe("Parse.ACL", () => { .then(() => object.destroy()) .then( () => { - fail("expected failure"); + fail('expected failure'); }, error => { expect(error.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); @@ -817,8 +817,8 @@ describe("Parse.ACL", () => { ); }); - it("acl saveAll with permissions", async done => { - const alice = await Parse.User.signUp("alice", "wonderland"); + it('acl saveAll with permissions', async done => { + const alice = await Parse.User.signUp('alice', 'wonderland'); const acl = new Parse.ACL(alice); const object1 = new TestObject(); const object2 = new TestObject(); @@ -835,70 +835,70 @@ describe("Parse.ACL", () => { equal(object2.getACL().getPublicWriteAccess(), false); // Save all the objects after updating them. - object1.set("foo", "bar"); - object2.set("foo", "bar"); + object1.set('foo', 'bar'); + object2.set('foo', 'bar'); await Parse.Object.saveAll([object1, object2]); const query = new Parse.Query(TestObject); - query.equalTo("foo", "bar"); + query.equalTo('foo', 'bar'); query.find().then(function (results) { equal(results.length, 2); done(); }); }); - it("empty acl works", async done => { - await Parse.User.signUp("tdurden", "mayhem", { + it('empty acl works', async done => { + await Parse.User.signUp('tdurden', 'mayhem', { ACL: new Parse.ACL(), - foo: "bar", + foo: 'bar', }); await Parse.User.logOut(); - const user = await Parse.User.logIn("tdurden", "mayhem"); - equal(user.get("foo"), "bar"); + const user = await Parse.User.logIn('tdurden', 'mayhem'); + equal(user.get('foo'), 'bar'); done(); }); - it("query for included object with ACL works", async done => { - const obj1 = new Parse.Object("TestClass1"); - const obj2 = new Parse.Object("TestClass2"); + it('query for included object with ACL works', async done => { + const obj1 = new Parse.Object('TestClass1'); + const obj2 = new Parse.Object('TestClass2'); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); - obj2.set("ACL", acl); - obj1.set("other", obj2); + obj2.set('ACL', acl); + obj1.set('other', obj2); await obj1.save(); obj2._clearServerData(); - const query = new Parse.Query("TestClass1"); + const query = new Parse.Query('TestClass1'); const obj1Again = await query.first(); - ok(!obj1Again.get("other").get("ACL")); + ok(!obj1Again.get('other').get('ACL')); - query.include("other"); + query.include('other'); const obj1AgainWithInclude = await query.first(); - ok(obj1AgainWithInclude.get("other").get("ACL")); + ok(obj1AgainWithInclude.get('other').get('ACL')); done(); }); - it("restricted ACL does not have public access", done => { - const obj = new Parse.Object("TestClassMasterACL"); + it('restricted ACL does not have public access', done => { + const obj = new Parse.Object('TestClassMasterACL'); const acl = new Parse.ACL(); - obj.set("ACL", acl); + obj.set('ACL', acl); obj .save() .then(() => { - const query = new Parse.Query("TestClassMasterACL"); + const query = new Parse.Query('TestClassMasterACL'); return query.find(); }) .then(results => { - ok(!results.length, "Should not have returned object with secure ACL."); + ok(!results.length, 'Should not have returned object with secure ACL.'); done(); }); }); - it("regression test #701", done => { - const config = Config.get("test"); + it('regression test #701', done => { + const config = Config.get('test'); const anonUser = { authData: { anonymous: { - id: "00000000-0000-0000-0000-000000000001", + id: '00000000-0000-0000-0000-000000000001', }, }, }; @@ -909,9 +909,9 @@ describe("Parse.ACL", () => { const acl = new Parse.ACL(user); user.setACL(acl); user.save(null, { useMasterKey: true }).then(user => { - new Parse.Query("_User").get(user.objectId).then( + new Parse.Query('_User').get(user.objectId).then( () => { - fail("should not have fetched user without public read enabled"); + fail('should not have fetched user without public read enabled'); done(); }, error => { @@ -923,33 +923,33 @@ describe("Parse.ACL", () => { } }); - rest.create(config, auth.nobody(config), "_User", anonUser); + rest.create(config, auth.nobody(config), '_User', anonUser); }); - it("support defaultACL in schema", async () => { - await new Parse.Object("TestObject").save(); + it('support defaultACL in schema', async () => { + await new Parse.Object('TestObject').save(); const schema = await Parse.Server.database.loadSchema(); await schema.updateClass( - "TestObject", + 'TestObject', {}, { create: { - "*": true, + '*': true, }, ACL: { - "*": { read: true }, + '*': { read: true }, currentUser: { read: true, write: true }, }, } ); const acls = new Parse.ACL(); acls.setPublicReadAccess(true); - const user = await Parse.User.signUp("testuser", "p@ssword"); - const obj = new Parse.Object("TestObject"); + const user = await Parse.User.signUp('testuser', 'p@ssword'); + const obj = new Parse.Object('TestObject'); await obj.save(null, { sessionToken: user.getSessionToken() }); expect(obj.getACL()).toBeDefined(); const acl = obj.getACL().toJSON(); - expect(acl["*"]).toEqual({ read: true }); + expect(acl['*']).toEqual({ read: true }); expect(acl[user.id].write).toBeTrue(); expect(acl[user.id].read).toBeTrue(); }); diff --git a/spec/ParseAPI.spec.js b/spec/ParseAPI.spec.js index c1c6b51cd0..b7d3a8f350 100644 --- a/spec/ParseAPI.spec.js +++ b/spec/ParseAPI.spec.js @@ -1,15 +1,15 @@ // A bunch of different tests are in here - it isn't very thematic. // It would probably be better to refactor them into different files. -"use strict"; +'use strict'; -const request = require("../lib/request"); -const Parse = require("parse/node"); -const Config = require("../lib/Config"); -const SchemaController = require("../lib/Controllers/SchemaController"); -const TestUtils = require("../lib/TestUtils"); +const request = require('../lib/request'); +const Parse = require('parse/node'); +const Config = require('../lib/Config'); +const SchemaController = require('../lib/Controllers/SchemaController'); +const TestUtils = require('../lib/TestUtils'); const userSchema = SchemaController.convertSchemaToAdapterSchema({ - className: "_User", + className: '_User', fields: Object.assign( {}, SchemaController.defaultColumns._Default, @@ -17,69 +17,69 @@ const userSchema = SchemaController.convertSchemaToAdapterSchema({ ), }); const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "X-Parse-Installation-Id": "yolo", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Installation-Id': 'yolo', }; -describe("miscellaneous", () => { - it("db contains document after successful save", async () => { - const obj = new Parse.Object("TestObject"); - obj.set("foo", "bar"); +describe('miscellaneous', () => { + it('db contains document after successful save', async () => { + const obj = new Parse.Object('TestObject'); + obj.set('foo', 'bar'); await obj.save(); const config = Config.get(defaultConfiguration.appId); const results = await config.database.adapter.find( - "TestObject", + 'TestObject', { fields: {} }, {}, {} ); expect(results.length).toEqual(1); - expect(results[0]["foo"]).toEqual("bar"); + expect(results[0]['foo']).toEqual('bar'); }); - it("create a GameScore object", function (done) { - const obj = new Parse.Object("GameScore"); - obj.set("score", 1337); + it('create a GameScore object', function (done) { + const obj = new Parse.Object('GameScore'); + obj.set('score', 1337); obj.save().then(function (obj) { - expect(typeof obj.id).toBe("string"); - expect(typeof obj.createdAt.toGMTString()).toBe("string"); + expect(typeof obj.id).toBe('string'); + expect(typeof obj.createdAt.toGMTString()).toBe('string'); done(); }, done.fail); }); - it("get a TestObject", function (done) { - create({ bloop: "blarg" }, async function (obj) { + it('get a TestObject', function (done) { + create({ bloop: 'blarg' }, async function (obj) { const t2 = new TestObject({ objectId: obj.id }); const obj2 = await t2.fetch(); - expect(obj2.get("bloop")).toEqual("blarg"); + expect(obj2.get('bloop')).toEqual('blarg'); expect(obj2.id).toBeTruthy(); expect(obj2.id).toEqual(obj.id); done(); }); }); - it("create a valid parse user", function (done) { + it('create a valid parse user', function (done) { createTestUser().then(function (data) { expect(data.id).not.toBeUndefined(); expect(data.getSessionToken()).not.toBeUndefined(); - expect(data.get("password")).toBeUndefined(); + expect(data.get('password')).toBeUndefined(); done(); }, done.fail); }); - it("fail to create a duplicate username", async () => { + it('fail to create a duplicate username', async () => { await reconfigureServer(); let numFailed = 0; let numCreated = 0; const p1 = request({ - method: "POST", - url: Parse.serverURL + "/users", + method: 'POST', + url: Parse.serverURL + '/users', body: { - password: "asdf", - username: "u1", - email: "dupe@dupe.dupe", + password: 'asdf', + username: 'u1', + email: 'dupe@dupe.dupe', }, headers, }).then( @@ -94,12 +94,12 @@ describe("miscellaneous", () => { ); const p2 = request({ - method: "POST", - url: Parse.serverURL + "/users", + method: 'POST', + url: Parse.serverURL + '/users', body: { - password: "otherpassword", - username: "u1", - email: "email@other.email", + password: 'otherpassword', + username: 'u1', + email: 'email@other.email', }, headers, }).then( @@ -117,17 +117,17 @@ describe("miscellaneous", () => { expect(numCreated).toBe(1); }); - it("ensure that email is uniquely indexed", async () => { + it('ensure that email is uniquely indexed', async () => { await reconfigureServer(); let numFailed = 0; let numCreated = 0; const p1 = request({ - method: "POST", - url: Parse.serverURL + "/users", + method: 'POST', + url: Parse.serverURL + '/users', body: { - password: "asdf", - username: "u1", - email: "dupe@dupe.dupe", + password: 'asdf', + username: 'u1', + email: 'dupe@dupe.dupe', }, headers, }).then( @@ -142,12 +142,12 @@ describe("miscellaneous", () => { ); const p2 = request({ - url: Parse.serverURL + "/users", - method: "POST", + url: Parse.serverURL + '/users', + method: 'POST', body: { - password: "asdf", - username: "u2", - email: "dupe@dupe.dupe", + password: 'asdf', + username: 'u2', + email: 'dupe@dupe.dupe', }, headers, }).then( @@ -166,15 +166,15 @@ describe("miscellaneous", () => { expect(numCreated).toBe(1); }); - it_id("be1b9ac7-5e5f-4e91-b044-2bd8fb7622ad")(it)( - "ensure that if people already have duplicate users, they can still sign up new users", + it_id('be1b9ac7-5e5f-4e91-b044-2bd8fb7622ad')(it)( + 'ensure that if people already have duplicate users, they can still sign up new users', async done => { try { await Parse.User.logOut(); } catch (e) { /* ignore */ } - const config = Config.get("test"); + const config = Config.get('test'); // Remove existing data to clear out unique index TestUtils.destroyAllDataPermanently() .then(() => @@ -182,15 +182,15 @@ describe("miscellaneous", () => { VolatileClassesSchemas: [], }) ) - .then(() => config.database.adapter.createClass("_User", userSchema)) + .then(() => config.database.adapter.createClass('_User', userSchema)) .then(() => config.database.adapter - .createObject("_User", userSchema, { objectId: "x", username: "u" }) + .createObject('_User', userSchema, { objectId: 'x', username: 'u' }) .catch(fail) ) .then(() => config.database.adapter - .createObject("_User", userSchema, { objectId: "y", username: "u" }) + .createObject('_User', userSchema, { objectId: 'y', username: 'u' }) .catch(fail) ) // Create a new server to try to recreate the unique indexes @@ -198,18 +198,18 @@ describe("miscellaneous", () => { .catch(error => { expect(error.code).toEqual(Parse.Error.DUPLICATE_VALUE); const user = new Parse.User(); - user.setPassword("asdf"); - user.setUsername("zxcv"); + user.setPassword('asdf'); + user.setUsername('zxcv'); return user.signUp().catch(fail); }) .then(() => { const user = new Parse.User(); - user.setPassword("asdf"); - user.setUsername("u"); + user.setPassword('asdf'); + user.setUsername('u'); return user.signUp(); }) .then(() => { - fail("should not have been able to sign up"); + fail('should not have been able to sign up'); done(); }) .catch(error => { @@ -219,10 +219,10 @@ describe("miscellaneous", () => { } ); - it_id("d00f907e-41b9-40f6-8168-63e832199a8c")(it)( - "ensure that if people already have duplicate emails, they can still sign up new users", + it_id('d00f907e-41b9-40f6-8168-63e832199a8c')(it)( + 'ensure that if people already have duplicate emails, they can still sign up new users', done => { - const config = Config.get("test"); + const config = Config.get('test'); // Remove existing data to clear out unique index TestUtils.destroyAllDataPermanently() .then(() => @@ -230,32 +230,32 @@ describe("miscellaneous", () => { VolatileClassesSchemas: [], }) ) - .then(() => config.database.adapter.createClass("_User", userSchema)) + .then(() => config.database.adapter.createClass('_User', userSchema)) .then(() => - config.database.adapter.createObject("_User", userSchema, { - objectId: "x", - email: "a@b.c", + config.database.adapter.createObject('_User', userSchema, { + objectId: 'x', + email: 'a@b.c', }) ) .then(() => - config.database.adapter.createObject("_User", userSchema, { - objectId: "y", - email: "a@b.c", + config.database.adapter.createObject('_User', userSchema, { + objectId: 'y', + email: 'a@b.c', }) ) .then(reconfigureServer) .catch(() => { const user = new Parse.User(); - user.setPassword("asdf"); - user.setUsername("qqq"); - user.setEmail("unique@unique.unique"); + user.setPassword('asdf'); + user.setUsername('qqq'); + user.setEmail('unique@unique.unique'); return user.signUp().catch(fail); }) .then(() => { const user = new Parse.User(); - user.setPassword("asdf"); - user.setUsername("www"); - user.setEmail("a@b.c"); + user.setPassword('asdf'); + user.setUsername('www'); + user.setEmail('a@b.c'); return user.signUp(); }) .catch(error => { @@ -265,30 +265,30 @@ describe("miscellaneous", () => { } ); - it("ensure that if you try to sign up a user with a unique username and email, but duplicates in some other field that has a uniqueness constraint, you get a regular duplicate value error", async done => { + it('ensure that if you try to sign up a user with a unique username and email, but duplicates in some other field that has a uniqueness constraint, you get a regular duplicate value error', async done => { await reconfigureServer(); - const config = Config.get("test"); + const config = Config.get('test'); config.database.adapter - .addFieldIfNotExists("_User", "randomField", { type: "String" }) + .addFieldIfNotExists('_User', 'randomField', { type: 'String' }) .then(() => - config.database.adapter.ensureUniqueness("_User", userSchema, [ - "randomField", + config.database.adapter.ensureUniqueness('_User', userSchema, [ + 'randomField', ]) ) .then(() => { const user = new Parse.User(); - user.setPassword("asdf"); - user.setUsername("1"); - user.setEmail("1@b.c"); - user.set("randomField", "a"); + user.setPassword('asdf'); + user.setUsername('1'); + user.setEmail('1@b.c'); + user.set('randomField', 'a'); return user.signUp(); }) .then(() => { const user = new Parse.User(); - user.setPassword("asdf"); - user.setUsername("2"); - user.setEmail("2@b.c"); - user.set("randomField", "a"); + user.setPassword('asdf'); + user.setUsername('2'); + user.setEmail('2@b.c'); + user.set('randomField', 'a'); return user.signUp(); }) .catch(error => { @@ -297,40 +297,40 @@ describe("miscellaneous", () => { }); }); - it("succeed in logging in", function (done) { + it('succeed in logging in', function (done) { createTestUser().then(async function (u) { - expect(typeof u.id).toEqual("string"); + expect(typeof u.id).toEqual('string'); - const user = await Parse.User.logIn("test", "moon-y"); - expect(typeof user.id).toEqual("string"); - expect(user.get("password")).toBeUndefined(); + const user = await Parse.User.logIn('test', 'moon-y'); + expect(typeof user.id).toEqual('string'); + expect(user.get('password')).toBeUndefined(); expect(user.getSessionToken()).not.toBeUndefined(); await Parse.User.logOut(); done(); }, fail); }); - it_id("33db6efe-7c02-496c-8595-0ef627a94103")(it)( - "increment with a user object", + it_id('33db6efe-7c02-496c-8595-0ef627a94103')(it)( + 'increment with a user object', function (done) { createTestUser() .then(user => { - user.increment("foo"); + user.increment('foo'); return user.save(); }) .then(() => { - return Parse.User.logIn("test", "moon-y"); + return Parse.User.logIn('test', 'moon-y'); }) .then(user => { - expect(user.get("foo")).toEqual(1); - user.increment("foo"); + expect(user.get('foo')).toEqual(1); + user.increment('foo'); return user.save(); }) .then(() => Parse.User.logOut()) - .then(() => Parse.User.logIn("test", "moon-y")) + .then(() => Parse.User.logIn('test', 'moon-y')) .then( user => { - expect(user.get("foo")).toEqual(2); + expect(user.get('foo')).toEqual(2); Parse.User.logOut().then(done); }, error => { @@ -341,13 +341,13 @@ describe("miscellaneous", () => { } ); - it_id("bef99522-bcfd-4f79-ba9e-3c3845550401")(it)( - "save various data types", + it_id('bef99522-bcfd-4f79-ba9e-3c3845550401')(it)( + 'save various data types', function (done) { const obj = new TestObject(); - obj.set("date", new Date()); - obj.set("array", [1, 2, 3]); - obj.set("object", { one: 1, two: 2 }); + obj.set('date', new Date()); + obj.set('array', [1, 2, 3]); + obj.set('object', { one: 1, two: 2 }); obj .save() .then(() => { @@ -355,18 +355,18 @@ describe("miscellaneous", () => { return obj2.fetch(); }) .then(obj2 => { - expect(obj2.get("date") instanceof Date).toBe(true); - expect(obj2.get("array") instanceof Array).toBe(true); - expect(obj2.get("object") instanceof Array).toBe(false); - expect(obj2.get("object") instanceof Object).toBe(true); + expect(obj2.get('date') instanceof Date).toBe(true); + expect(obj2.get('array') instanceof Array).toBe(true); + expect(obj2.get('object') instanceof Array).toBe(false); + expect(obj2.get('object') instanceof Object).toBe(true); done(); }); } ); - it("query with limit", function (done) { - const baz = new TestObject({ foo: "baz" }); - const qux = new TestObject({ foo: "qux" }); + it('query with limit', function (done) { + const baz = new TestObject({ foo: 'baz' }); + const qux = new TestObject({ foo: 'qux' }); baz .save() .then(() => { @@ -389,10 +389,10 @@ describe("miscellaneous", () => { ); }); - it("query without limit get default 100 records", function (done) { + it('query without limit get default 100 records', function (done) { const objects = []; for (let i = 0; i < 150; i++) { - objects.push(new TestObject({ name: "name" + i })); + objects.push(new TestObject({ name: 'name' + i })); } Parse.Object.saveAll(objects) .then(() => { @@ -410,9 +410,9 @@ describe("miscellaneous", () => { ); }); - it("basic saveAll", function (done) { - const alpha = new TestObject({ letter: "alpha" }); - const beta = new TestObject({ letter: "beta" }); + it('basic saveAll', function (done) { + const alpha = new TestObject({ letter: 'alpha' }); + const beta = new TestObject({ letter: 'beta' }); Parse.Object.saveAll([alpha, beta]) .then(() => { expect(alpha.id).toBeTruthy(); @@ -431,22 +431,22 @@ describe("miscellaneous", () => { ); }); - it("test beforeSave set object acl success", function (done) { + it('test beforeSave set object acl success', function (done) { const acl = new Parse.ACL({ - "*": { read: true, write: false }, + '*': { read: true, write: false }, }); - Parse.Cloud.beforeSave("BeforeSaveAddACL", function (req) { + Parse.Cloud.beforeSave('BeforeSaveAddACL', function (req) { req.object.setACL(acl); }); - const obj = new Parse.Object("BeforeSaveAddACL"); - obj.set("lol", true); + const obj = new Parse.Object('BeforeSaveAddACL'); + obj.set('lol', true); obj.save().then( function () { - const query = new Parse.Query("BeforeSaveAddACL"); + const query = new Parse.Query('BeforeSaveAddACL'); query.get(obj.id).then( function (objAgain) { - expect(objAgain.get("lol")).toBeTruthy(); + expect(objAgain.get('lol')).toBeTruthy(); expect(objAgain.getACL().equals(acl)); done(); }, @@ -463,23 +463,23 @@ describe("miscellaneous", () => { ); }); - it("object is set on create and update", done => { + it('object is set on create and update', done => { let triggerTime = 0; // Register a mock beforeSave hook - Parse.Cloud.beforeSave("GameScore", req => { + Parse.Cloud.beforeSave('GameScore', req => { const object = req.object; expect(object instanceof Parse.Object).toBeTruthy(); - expect(object.get("fooAgain")).toEqual("barAgain"); + expect(object.get('fooAgain')).toEqual('barAgain'); if (triggerTime == 0) { // Create - expect(object.get("foo")).toEqual("bar"); + expect(object.get('foo')).toEqual('bar'); // No objectId/createdAt/updatedAt expect(object.id).toBeUndefined(); expect(object.createdAt).toBeUndefined(); expect(object.updatedAt).toBeUndefined(); } else if (triggerTime == 1) { // Update - expect(object.get("foo")).toEqual("baz"); + expect(object.get('foo')).toEqual('baz'); expect(object.id).not.toBeUndefined(); expect(object.createdAt).not.toBeUndefined(); expect(object.updatedAt).not.toBeUndefined(); @@ -489,14 +489,14 @@ describe("miscellaneous", () => { triggerTime++; }); - const obj = new Parse.Object("GameScore"); - obj.set("foo", "bar"); - obj.set("fooAgain", "barAgain"); + const obj = new Parse.Object('GameScore'); + obj.set('foo', 'bar'); + obj.set('fooAgain', 'barAgain'); obj .save() .then(() => { // We only update foo - obj.set("foo", "baz"); + obj.set('foo', 'baz'); return obj.save(); }) .then( @@ -511,22 +511,22 @@ describe("miscellaneous", () => { } ); }); - it("works when object is passed to success", done => { + it('works when object is passed to success', done => { let triggerTime = 0; // Register a mock beforeSave hook - Parse.Cloud.beforeSave("GameScore", req => { + Parse.Cloud.beforeSave('GameScore', req => { const object = req.object; - object.set("foo", "bar"); + object.set('foo', 'bar'); triggerTime++; return object; }); - const obj = new Parse.Object("GameScore"); - obj.set("foo", "baz"); + const obj = new Parse.Object('GameScore'); + obj.set('foo', 'baz'); obj.save().then( () => { expect(triggerTime).toBe(1); - expect(obj.get("foo")).toEqual("bar"); + expect(obj.get('foo')).toEqual('bar'); done(); }, error => { @@ -536,13 +536,13 @@ describe("miscellaneous", () => { ); }); - it("original object is set on update", done => { + it('original object is set on update', done => { let triggerTime = 0; // Register a mock beforeSave hook - Parse.Cloud.beforeSave("GameScore", req => { + Parse.Cloud.beforeSave('GameScore', req => { const object = req.object; expect(object instanceof Parse.Object).toBeTruthy(); - expect(object.get("fooAgain")).toEqual("barAgain"); + expect(object.get('fooAgain')).toEqual('barAgain'); const originalObject = req.original; if (triggerTime == 0) { // No id/createdAt/updatedAt @@ -550,7 +550,7 @@ describe("miscellaneous", () => { expect(object.createdAt).toBeUndefined(); expect(object.updatedAt).toBeUndefined(); // Create - expect(object.get("foo")).toEqual("bar"); + expect(object.get('foo')).toEqual('bar'); // Check the originalObject is undefined expect(originalObject).toBeUndefined(); } else if (triggerTime == 1) { @@ -558,28 +558,28 @@ describe("miscellaneous", () => { expect(object.id).not.toBeUndefined(); expect(object.createdAt).not.toBeUndefined(); expect(object.updatedAt).not.toBeUndefined(); - expect(object.get("foo")).toEqual("baz"); + expect(object.get('foo')).toEqual('baz'); // Check the originalObject expect(originalObject instanceof Parse.Object).toBeTruthy(); - expect(originalObject.get("fooAgain")).toEqual("barAgain"); + expect(originalObject.get('fooAgain')).toEqual('barAgain'); expect(originalObject.id).not.toBeUndefined(); expect(originalObject.createdAt).not.toBeUndefined(); expect(originalObject.updatedAt).not.toBeUndefined(); - expect(originalObject.get("foo")).toEqual("bar"); + expect(originalObject.get('foo')).toEqual('bar'); } else { throw new Error(); } triggerTime++; }); - const obj = new Parse.Object("GameScore"); - obj.set("foo", "bar"); - obj.set("fooAgain", "barAgain"); + const obj = new Parse.Object('GameScore'); + obj.set('foo', 'bar'); + obj.set('fooAgain', 'barAgain'); obj .save() .then(() => { // We only update foo - obj.set("foo", "baz"); + obj.set('foo', 'baz'); return obj.save(); }) .then( @@ -595,38 +595,38 @@ describe("miscellaneous", () => { ); }); - it("pointer mutation properly saves object", done => { - const className = "GameScore"; + it('pointer mutation properly saves object', done => { + const className = 'GameScore'; Parse.Cloud.beforeSave(className, req => { const object = req.object; expect(object instanceof Parse.Object).toBeTruthy(); - const child = object.get("child"); + const child = object.get('child'); expect(child instanceof Parse.Object).toBeTruthy(); - child.set("a", "b"); + child.set('a', 'b'); return child.save(); }); const obj = new Parse.Object(className); - obj.set("foo", "bar"); + obj.set('foo', 'bar'); - const child = new Parse.Object("Child"); + const child = new Parse.Object('Child'); child .save() .then(() => { - obj.set("child", child); + obj.set('child', child); return obj.save(); }) .then(() => { const query = new Parse.Query(className); - query.include("child"); + query.include('child'); return query.get(obj.id).then(objAgain => { - expect(objAgain.get("foo")).toEqual("bar"); + expect(objAgain.get('foo')).toEqual('bar'); - const childAgain = objAgain.get("child"); + const childAgain = objAgain.get('child'); expect(childAgain instanceof Parse.Object).toBeTruthy(); - expect(childAgain.get("a")).toEqual("b"); + expect(childAgain.get('a')).toEqual('b'); return Promise.resolve(); }); @@ -642,92 +642,92 @@ describe("miscellaneous", () => { ); }); - it("pointer reassign is working properly (#1288)", done => { - Parse.Cloud.beforeSave("GameScore", req => { + it('pointer reassign is working properly (#1288)', done => { + Parse.Cloud.beforeSave('GameScore', req => { const obj = req.object; - if (obj.get("point")) { + if (obj.get('point')) { return; } - const TestObject1 = Parse.Object.extend("TestObject1"); + const TestObject1 = Parse.Object.extend('TestObject1'); const newObj = new TestObject1({ key1: 1 }); return newObj.save().then(newObj => { - obj.set("point", newObj); + obj.set('point', newObj); }); }); let pointId; - const obj = new Parse.Object("GameScore"); - obj.set("foo", "bar"); + const obj = new Parse.Object('GameScore'); + obj.set('foo', 'bar'); obj .save() .then(() => { - expect(obj.get("point")).not.toBeUndefined(); - pointId = obj.get("point").id; + expect(obj.get('point')).not.toBeUndefined(); + pointId = obj.get('point').id; expect(pointId).not.toBeUndefined(); - obj.set("foo", "baz"); + obj.set('foo', 'baz'); return obj.save(); }) .then(obj => { - expect(obj.get("point").id).toEqual(pointId); + expect(obj.get('point').id).toEqual(pointId); done(); }); }); - it_only_db("mongo")( - "pointer reassign on nested fields is working properly (#7391)", + it_only_db('mongo')( + 'pointer reassign on nested fields is working properly (#7391)', async () => { - const obj = new Parse.Object("GameScore"); // This object will include nested pointers - const ptr1 = new Parse.Object("GameScore"); + const obj = new Parse.Object('GameScore'); // This object will include nested pointers + const ptr1 = new Parse.Object('GameScore'); await ptr1.save(); // Obtain a unique id - const ptr2 = new Parse.Object("GameScore"); + const ptr2 = new Parse.Object('GameScore'); await ptr2.save(); // Obtain a unique id - obj.set("data", { ptr: ptr1 }); + obj.set('data', { ptr: ptr1 }); await obj.save(); - obj.set("data.ptr", ptr2); + obj.set('data.ptr', ptr2); await obj.save(); - const obj2 = await new Parse.Query("GameScore").get(obj.id); - expect(obj2.get("data").ptr.id).toBe(ptr2.id); + const obj2 = await new Parse.Query('GameScore').get(obj.id); + expect(obj2.get('data').ptr.id).toBe(ptr2.id); - const query = new Parse.Query("GameScore"); - query.equalTo("data.ptr", ptr2); + const query = new Parse.Query('GameScore'); + query.equalTo('data.ptr', ptr2); const res = await query.find(); expect(res.length).toBe(1); - expect(res[0].get("data").ptr.id).toBe(ptr2.id); + expect(res[0].get('data').ptr.id).toBe(ptr2.id); } ); - it("test afterSave get full object on create and update", function (done) { + it('test afterSave get full object on create and update', function (done) { let triggerTime = 0; // Register a mock beforeSave hook - Parse.Cloud.afterSave("GameScore", function (req) { + Parse.Cloud.afterSave('GameScore', function (req) { const object = req.object; expect(object instanceof Parse.Object).toBeTruthy(); expect(object.id).not.toBeUndefined(); expect(object.createdAt).not.toBeUndefined(); expect(object.updatedAt).not.toBeUndefined(); - expect(object.get("fooAgain")).toEqual("barAgain"); + expect(object.get('fooAgain')).toEqual('barAgain'); if (triggerTime == 0) { // Create - expect(object.get("foo")).toEqual("bar"); + expect(object.get('foo')).toEqual('bar'); } else if (triggerTime == 1) { // Update - expect(object.get("foo")).toEqual("baz"); + expect(object.get('foo')).toEqual('baz'); } else { throw new Error(); } triggerTime++; }); - const obj = new Parse.Object("GameScore"); - obj.set("foo", "bar"); - obj.set("fooAgain", "barAgain"); + const obj = new Parse.Object('GameScore'); + obj.set('foo', 'bar'); + obj.set('fooAgain', 'barAgain'); obj .save() .then(function () { // We only update foo - obj.set("foo", "baz"); + obj.set('foo', 'baz'); return obj.save(); }) .then( @@ -743,47 +743,47 @@ describe("miscellaneous", () => { ); }); - it("test afterSave get original object on update", function (done) { + it('test afterSave get original object on update', function (done) { let triggerTime = 0; // Register a mock beforeSave hook - Parse.Cloud.afterSave("GameScore", function (req) { + Parse.Cloud.afterSave('GameScore', function (req) { const object = req.object; expect(object instanceof Parse.Object).toBeTruthy(); - expect(object.get("fooAgain")).toEqual("barAgain"); + expect(object.get('fooAgain')).toEqual('barAgain'); expect(object.id).not.toBeUndefined(); expect(object.createdAt).not.toBeUndefined(); expect(object.updatedAt).not.toBeUndefined(); const originalObject = req.original; if (triggerTime == 0) { // Create - expect(object.get("foo")).toEqual("bar"); + expect(object.get('foo')).toEqual('bar'); // Check the originalObject is undefined expect(originalObject).toBeUndefined(); } else if (triggerTime == 1) { // Update - expect(object.get("foo")).toEqual("baz"); + expect(object.get('foo')).toEqual('baz'); // Check the originalObject expect(originalObject instanceof Parse.Object).toBeTruthy(); - expect(originalObject.get("fooAgain")).toEqual("barAgain"); + expect(originalObject.get('fooAgain')).toEqual('barAgain'); expect(originalObject.id).not.toBeUndefined(); expect(originalObject.createdAt).not.toBeUndefined(); expect(originalObject.updatedAt).not.toBeUndefined(); - expect(originalObject.get("foo")).toEqual("bar"); + expect(originalObject.get('foo')).toEqual('bar'); } else { throw new Error(); } triggerTime++; }); - const obj = new Parse.Object("GameScore"); - obj.set("foo", "bar"); - obj.set("fooAgain", "barAgain"); + const obj = new Parse.Object('GameScore'); + obj.set('foo', 'bar'); + obj.set('fooAgain', 'barAgain'); obj .save() .then(function () { // We only update foo - obj.set("foo", "baz"); + obj.set('foo', 'baz'); return obj.save(); }) .then( @@ -799,33 +799,33 @@ describe("miscellaneous", () => { ); }); - it("test afterSave get full original object even req auth can not query it", done => { + it('test afterSave get full original object even req auth can not query it', done => { let triggerTime = 0; // Register a mock beforeSave hook - Parse.Cloud.afterSave("GameScore", function (req) { + Parse.Cloud.afterSave('GameScore', function (req) { const object = req.object; const originalObject = req.original; if (triggerTime == 0) { // Create } else if (triggerTime == 1) { // Update - expect(object.get("foo")).toEqual("baz"); + expect(object.get('foo')).toEqual('baz'); // Make sure we get the full originalObject expect(originalObject instanceof Parse.Object).toBeTruthy(); - expect(originalObject.get("fooAgain")).toEqual("barAgain"); + expect(originalObject.get('fooAgain')).toEqual('barAgain'); expect(originalObject.id).not.toBeUndefined(); expect(originalObject.createdAt).not.toBeUndefined(); expect(originalObject.updatedAt).not.toBeUndefined(); - expect(originalObject.get("foo")).toEqual("bar"); + expect(originalObject.get('foo')).toEqual('bar'); } else { throw new Error(); } triggerTime++; }); - const obj = new Parse.Object("GameScore"); - obj.set("foo", "bar"); - obj.set("fooAgain", "barAgain"); + const obj = new Parse.Object('GameScore'); + obj.set('foo', 'bar'); + obj.set('fooAgain', 'barAgain'); const acl = new Parse.ACL(); // Make sure our update request can not query the object acl.setPublicReadAccess(false); @@ -835,7 +835,7 @@ describe("miscellaneous", () => { .save() .then(function () { // We only update foo - obj.set("foo", "baz"); + obj.set('foo', 'baz'); return obj.save(); }) .then( @@ -851,33 +851,33 @@ describe("miscellaneous", () => { ); }); - it("afterSave flattens custom operations", done => { + it('afterSave flattens custom operations', done => { let triggerTime = 0; // Register a mock beforeSave hook - Parse.Cloud.afterSave("GameScore", function (req) { + Parse.Cloud.afterSave('GameScore', function (req) { const object = req.object; expect(object instanceof Parse.Object).toBeTruthy(); const originalObject = req.original; if (triggerTime == 0) { // Create - expect(object.get("yolo")).toEqual(1); + expect(object.get('yolo')).toEqual(1); } else if (triggerTime == 1) { // Update - expect(object.get("yolo")).toEqual(2); + expect(object.get('yolo')).toEqual(2); // Check the originalObject - expect(originalObject.get("yolo")).toEqual(1); + expect(originalObject.get('yolo')).toEqual(1); } else { throw new Error(); } triggerTime++; }); - const obj = new Parse.Object("GameScore"); - obj.increment("yolo", 1); + const obj = new Parse.Object('GameScore'); + obj.increment('yolo', 1); obj .save() .then(() => { - obj.increment("yolo", 1); + obj.increment('yolo', 1); return obj.save(); }) .then( @@ -893,10 +893,10 @@ describe("miscellaneous", () => { ); }); - it("beforeSave receives ACL", done => { + it('beforeSave receives ACL', done => { let triggerTime = 0; // Register a mock beforeSave hook - Parse.Cloud.beforeSave("GameScore", function (req) { + Parse.Cloud.beforeSave('GameScore', function (req) { const object = req.object; if (triggerTime == 0) { const acl = object.getACL(); @@ -912,7 +912,7 @@ describe("miscellaneous", () => { triggerTime++; }); - const obj = new Parse.Object("GameScore"); + const obj = new Parse.Object('GameScore'); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); acl.setPublicWriteAccess(true); @@ -937,10 +937,10 @@ describe("miscellaneous", () => { ); }); - it("afterSave receives ACL", done => { + it('afterSave receives ACL', done => { let triggerTime = 0; // Register a mock beforeSave hook - Parse.Cloud.afterSave("GameScore", function (req) { + Parse.Cloud.afterSave('GameScore', function (req) { const object = req.object; if (triggerTime == 0) { const acl = object.getACL(); @@ -956,7 +956,7 @@ describe("miscellaneous", () => { triggerTime++; }); - const obj = new Parse.Object("GameScore"); + const obj = new Parse.Object('GameScore'); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); acl.setPublicWriteAccess(true); @@ -981,294 +981,294 @@ describe("miscellaneous", () => { ); }); - it_id("e9e718a9-4465-4158-b13e-f146855a8892")(it)( - "return the updated fields on PUT", + it_id('e9e718a9-4465-4158-b13e-f146855a8892')(it)( + 'return the updated fields on PUT', async () => { - const obj = new Parse.Object("GameScore"); - const pointer = new Parse.Object("Child"); + const obj = new Parse.Object('GameScore'); + const pointer = new Parse.Object('Child'); await pointer.save(); obj.set( - "point", + 'point', new Parse.GeoPoint({ latitude: 37.4848, longitude: -122.1483, }) ); - obj.set("array", ["obj1", "obj2"]); - obj.set("objects", { a: "b" }); - obj.set("string", "abc"); - obj.set("bool", true); - obj.set("number", 1); - obj.set("date", new Date()); - obj.set("pointer", pointer); + obj.set('array', ['obj1', 'obj2']); + obj.set('objects', { a: 'b' }); + obj.set('string', 'abc'); + obj.set('bool', true); + obj.set('number', 1); + obj.set('date', new Date()); + obj.set('pointer', pointer); const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "X-Parse-Installation-Id": "yolo", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Installation-Id': 'yolo', }; const saveResponse = await request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/classes/GameScore", + url: 'http://localhost:8378/1/classes/GameScore', body: JSON.stringify({ - a: "hello", + a: 'hello', c: 1, - d: ["1"], - e: ["1"], - f: ["1", "2"], + d: ['1'], + e: ['1'], + f: ['1', '2'], ...obj.toJSON(), }), }); expect(Object.keys(saveResponse.data).sort()).toEqual([ - "createdAt", - "objectId", + 'createdAt', + 'objectId', ]); obj.id = saveResponse.data.objectId; const response = await request({ - method: "PUT", + method: 'PUT', headers: headers, - url: "http://localhost:8378/1/classes/GameScore/" + obj.id, + url: 'http://localhost:8378/1/classes/GameScore/' + obj.id, body: JSON.stringify({ - a: "b", - c: { __op: "Increment", amount: 2 }, - d: { __op: "Add", objects: ["2"] }, - e: { __op: "AddUnique", objects: ["1", "2"] }, - f: { __op: "Remove", objects: ["2"] }, + a: 'b', + c: { __op: 'Increment', amount: 2 }, + d: { __op: 'Add', objects: ['2'] }, + e: { __op: 'AddUnique', objects: ['1', '2'] }, + f: { __op: 'Remove', objects: ['2'] }, selfThing: { - __type: "Pointer", - className: "GameScore", + __type: 'Pointer', + className: 'GameScore', objectId: obj.id, }, }), }); const body = response.data; expect(Object.keys(body).sort()).toEqual([ - "c", - "d", - "e", - "f", - "updatedAt", + 'c', + 'd', + 'e', + 'f', + 'updatedAt', ]); expect(body.a).toBeUndefined(); expect(body.c).toEqual(3); // 2+1 expect(body.d.length).toBe(2); - expect(body.d.indexOf("1") > -1).toBe(true); - expect(body.d.indexOf("2") > -1).toBe(true); + expect(body.d.indexOf('1') > -1).toBe(true); + expect(body.d.indexOf('2') > -1).toBe(true); expect(body.e.length).toBe(2); - expect(body.e.indexOf("1") > -1).toBe(true); - expect(body.e.indexOf("2") > -1).toBe(true); + expect(body.e.indexOf('1') > -1).toBe(true); + expect(body.e.indexOf('2') > -1).toBe(true); expect(body.f.length).toBe(1); - expect(body.f.indexOf("1") > -1).toBe(true); + expect(body.f.indexOf('1') > -1).toBe(true); expect(body.selfThing).toBeUndefined(); expect(body.updatedAt).not.toBeUndefined(); } ); - it_id("ea358b59-03c0-45c9-abc7-1fdd67573029")(it)( - "should response should not change with triggers", + it_id('ea358b59-03c0-45c9-abc7-1fdd67573029')(it)( + 'should response should not change with triggers', async () => { - const obj = new Parse.Object("GameScore"); - const pointer = new Parse.Object("Child"); - Parse.Cloud.beforeSave("GameScore", request => { + const obj = new Parse.Object('GameScore'); + const pointer = new Parse.Object('Child'); + Parse.Cloud.beforeSave('GameScore', request => { return request.object; }); - Parse.Cloud.afterSave("GameScore", request => { + Parse.Cloud.afterSave('GameScore', request => { return request.object; }); await pointer.save(); obj.set( - "point", + 'point', new Parse.GeoPoint({ latitude: 37.4848, longitude: -122.1483, }) ); - obj.set("array", ["obj1", "obj2"]); - obj.set("objects", { a: "b" }); - obj.set("string", "abc"); - obj.set("bool", true); - obj.set("number", 1); - obj.set("date", new Date()); - obj.set("pointer", pointer); + obj.set('array', ['obj1', 'obj2']); + obj.set('objects', { a: 'b' }); + obj.set('string', 'abc'); + obj.set('bool', true); + obj.set('number', 1); + obj.set('date', new Date()); + obj.set('pointer', pointer); const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "X-Parse-Installation-Id": "yolo", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Installation-Id': 'yolo', }; const saveResponse = await request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/classes/GameScore", + url: 'http://localhost:8378/1/classes/GameScore', body: JSON.stringify({ - a: "hello", + a: 'hello', c: 1, - d: ["1"], - e: ["1"], - f: ["1", "2"], + d: ['1'], + e: ['1'], + f: ['1', '2'], ...obj.toJSON(), }), }); expect(Object.keys(saveResponse.data).sort()).toEqual([ - "createdAt", - "objectId", + 'createdAt', + 'objectId', ]); obj.id = saveResponse.data.objectId; const response = await request({ - method: "PUT", + method: 'PUT', headers: headers, - url: "http://localhost:8378/1/classes/GameScore/" + obj.id, + url: 'http://localhost:8378/1/classes/GameScore/' + obj.id, body: JSON.stringify({ - a: "b", - c: { __op: "Increment", amount: 2 }, - d: { __op: "Add", objects: ["2"] }, - e: { __op: "AddUnique", objects: ["1", "2"] }, - f: { __op: "Remove", objects: ["2"] }, + a: 'b', + c: { __op: 'Increment', amount: 2 }, + d: { __op: 'Add', objects: ['2'] }, + e: { __op: 'AddUnique', objects: ['1', '2'] }, + f: { __op: 'Remove', objects: ['2'] }, selfThing: { - __type: "Pointer", - className: "GameScore", + __type: 'Pointer', + className: 'GameScore', objectId: obj.id, }, }), }); const body = response.data; expect(Object.keys(body).sort()).toEqual([ - "c", - "d", - "e", - "f", - "updatedAt", + 'c', + 'd', + 'e', + 'f', + 'updatedAt', ]); expect(body.a).toBeUndefined(); expect(body.c).toEqual(3); // 2+1 expect(body.d.length).toBe(2); - expect(body.d.indexOf("1") > -1).toBe(true); - expect(body.d.indexOf("2") > -1).toBe(true); + expect(body.d.indexOf('1') > -1).toBe(true); + expect(body.d.indexOf('2') > -1).toBe(true); expect(body.e.length).toBe(2); - expect(body.e.indexOf("1") > -1).toBe(true); - expect(body.e.indexOf("2") > -1).toBe(true); + expect(body.e.indexOf('1') > -1).toBe(true); + expect(body.e.indexOf('2') > -1).toBe(true); expect(body.f.length).toBe(1); - expect(body.f.indexOf("1") > -1).toBe(true); + expect(body.f.indexOf('1') > -1).toBe(true); expect(body.selfThing).toBeUndefined(); expect(body.updatedAt).not.toBeUndefined(); } ); - it("test cloud function error handling", done => { + it('test cloud function error handling', done => { // Register a function which will fail - Parse.Cloud.define("willFail", () => { - throw new Error("noway"); + Parse.Cloud.define('willFail', () => { + throw new Error('noway'); }); - Parse.Cloud.run("willFail").then( + Parse.Cloud.run('willFail').then( () => { - fail("Should not have succeeded."); + fail('Should not have succeeded.'); done(); }, e => { expect(e.code).toEqual(Parse.Error.SCRIPT_FAILED); - expect(e.message).toEqual("noway"); + expect(e.message).toEqual('noway'); done(); } ); }); - it("test cloud function error handling with custom error code", done => { + it('test cloud function error handling with custom error code', done => { // Register a function which will fail - Parse.Cloud.define("willFail", () => { - throw new Parse.Error(999, "noway"); + Parse.Cloud.define('willFail', () => { + throw new Parse.Error(999, 'noway'); }); - Parse.Cloud.run("willFail").then( + Parse.Cloud.run('willFail').then( () => { - fail("Should not have succeeded."); + fail('Should not have succeeded.'); done(); }, e => { expect(e.code).toEqual(999); - expect(e.message).toEqual("noway"); + expect(e.message).toEqual('noway'); done(); } ); }); - it("test cloud function error handling with standard error code", done => { + it('test cloud function error handling with standard error code', done => { // Register a function which will fail - Parse.Cloud.define("willFail", () => { - throw new Error("noway"); + Parse.Cloud.define('willFail', () => { + throw new Error('noway'); }); - Parse.Cloud.run("willFail").then( + Parse.Cloud.run('willFail').then( () => { - fail("Should not have succeeded."); + fail('Should not have succeeded.'); done(); }, e => { expect(e.code).toEqual(Parse.Error.SCRIPT_FAILED); - expect(e.message).toEqual("noway"); + expect(e.message).toEqual('noway'); done(); } ); }); - it("test beforeSave/afterSave get installationId", function (done) { + it('test beforeSave/afterSave get installationId', function (done) { let triggerTime = 0; - Parse.Cloud.beforeSave("GameScore", function (req) { + Parse.Cloud.beforeSave('GameScore', function (req) { triggerTime++; expect(triggerTime).toEqual(1); - expect(req.installationId).toEqual("yolo"); + expect(req.installationId).toEqual('yolo'); }); - Parse.Cloud.afterSave("GameScore", function (req) { + Parse.Cloud.afterSave('GameScore', function (req) { triggerTime++; expect(triggerTime).toEqual(2); - expect(req.installationId).toEqual("yolo"); + expect(req.installationId).toEqual('yolo'); }); const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "X-Parse-Installation-Id": "yolo", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Installation-Id': 'yolo', }; request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/classes/GameScore", - body: JSON.stringify({ a: "b" }), + url: 'http://localhost:8378/1/classes/GameScore', + body: JSON.stringify({ a: 'b' }), }).then(() => { expect(triggerTime).toEqual(2); done(); }); }); - it("test beforeDelete/afterDelete get installationId", function (done) { + it('test beforeDelete/afterDelete get installationId', function (done) { let triggerTime = 0; - Parse.Cloud.beforeDelete("GameScore", function (req) { + Parse.Cloud.beforeDelete('GameScore', function (req) { triggerTime++; expect(triggerTime).toEqual(1); - expect(req.installationId).toEqual("yolo"); + expect(req.installationId).toEqual('yolo'); }); - Parse.Cloud.afterDelete("GameScore", function (req) { + Parse.Cloud.afterDelete('GameScore', function (req) { triggerTime++; expect(triggerTime).toEqual(2); - expect(req.installationId).toEqual("yolo"); + expect(req.installationId).toEqual('yolo'); }); const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "X-Parse-Installation-Id": "yolo", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Installation-Id': 'yolo', }; request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/classes/GameScore", - body: JSON.stringify({ a: "b" }), + url: 'http://localhost:8378/1/classes/GameScore', + body: JSON.stringify({ a: 'b' }), }).then(response => { request({ - method: "DELETE", + method: 'DELETE', headers: headers, url: - "http://localhost:8378/1/classes/GameScore/" + response.data.objectId, + 'http://localhost:8378/1/classes/GameScore/' + response.data.objectId, }).then(() => { expect(triggerTime).toEqual(2); done(); @@ -1276,15 +1276,15 @@ describe("miscellaneous", () => { }); }); - it("test beforeDelete with locked down ACL", async () => { + it('test beforeDelete with locked down ACL', async () => { let called = false; - Parse.Cloud.beforeDelete("GameScore", () => { + Parse.Cloud.beforeDelete('GameScore', () => { called = true; }); - const object = new Parse.Object("GameScore"); + const object = new Parse.Object('GameScore'); object.setACL(new Parse.ACL()); await object.save(); - const objects = await new Parse.Query("GameScore").find(); + const objects = await new Parse.Query('GameScore').find(); expect(objects.length).toBe(0); try { await object.destroy(); @@ -1294,19 +1294,19 @@ describe("miscellaneous", () => { expect(called).toBe(false); }); - it("test cloud function query parameters", done => { - Parse.Cloud.define("echoParams", req => { + it('test cloud function query parameters', done => { + Parse.Cloud.define('echoParams', req => { return req.params; }); const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-Javascript-Key": "test", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-Javascript-Key': 'test', }; request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/functions/echoParams", //?option=1&other=2 + url: 'http://localhost:8378/1/functions/echoParams', //?option=1&other=2 qs: { option: 1, other: 2, @@ -1314,138 +1314,138 @@ describe("miscellaneous", () => { body: '{"foo":"bar", "other": 1}', }).then(response => { const res = response.data.result; - expect(res.option).toEqual("1"); + expect(res.option).toEqual('1'); // Make sure query string params override body params - expect(res.other).toEqual("2"); - expect(res.foo).toEqual("bar"); + expect(res.other).toEqual('2'); + expect(res.foo).toEqual('bar'); done(); }); }); - it("test cloud function query parameters with array of pointers", async () => { + it('test cloud function query parameters with array of pointers', async () => { await reconfigureServer({ encodeParseObjectInCloudFunction: false }); - Parse.Cloud.define("echoParams", req => { + Parse.Cloud.define('echoParams', req => { return req.params; }); const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-Javascript-Key": "test", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-Javascript-Key': 'test', }; const response = await request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/functions/echoParams", + url: 'http://localhost:8378/1/functions/echoParams', body: '{"arr": [{ "__type": "Pointer", "className": "PointerTest" }]}', }); const res = response.data.result; expect(res.arr.length).toEqual(1); }); - it("can handle null params in cloud functions (regression test for #1742)", done => { - Parse.Cloud.define("func", request => { + it('can handle null params in cloud functions (regression test for #1742)', done => { + Parse.Cloud.define('func', request => { expect(request.params.nullParam).toEqual(null); - return "yay"; + return 'yay'; }); - Parse.Cloud.run("func", { nullParam: null }).then( + Parse.Cloud.run('func', { nullParam: null }).then( () => { done(); }, () => { - fail("cloud code call failed"); + fail('cloud code call failed'); done(); } ); }); - it("can handle date params in cloud functions (#2214)", done => { + it('can handle date params in cloud functions (#2214)', done => { const date = new Date(); - Parse.Cloud.define("dateFunc", request => { - expect(request.params.date.__type).toEqual("Date"); + Parse.Cloud.define('dateFunc', request => { + expect(request.params.date.__type).toEqual('Date'); expect(request.params.date.iso).toEqual(date.toISOString()); - return "yay"; + return 'yay'; }); - Parse.Cloud.run("dateFunc", { date: date }).then( + Parse.Cloud.run('dateFunc', { date: date }).then( () => { done(); }, () => { - fail("cloud code call failed"); + fail('cloud code call failed'); done(); } ); }); - it("fails on invalid client key", done => { + it('fails on invalid client key', done => { const headers = { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - "X-Parse-Client-Key": "notclient", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + 'X-Parse-Client-Key': 'notclient', }; request({ headers: headers, - url: "http://localhost:8378/1/classes/TestObject", + url: 'http://localhost:8378/1/classes/TestObject', }).then(fail, response => { const b = response.data; - expect(b.error).toEqual("unauthorized"); + expect(b.error).toEqual('unauthorized'); done(); }); }); - it("fails on invalid windows key", done => { + it('fails on invalid windows key', done => { const headers = { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - "X-Parse-Windows-Key": "notwindows", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + 'X-Parse-Windows-Key': 'notwindows', }; request({ headers: headers, - url: "http://localhost:8378/1/classes/TestObject", + url: 'http://localhost:8378/1/classes/TestObject', }).then(fail, response => { const b = response.data; - expect(b.error).toEqual("unauthorized"); + expect(b.error).toEqual('unauthorized'); done(); }); }); - it("fails on invalid javascript key", done => { + it('fails on invalid javascript key', done => { const headers = { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - "X-Parse-Javascript-Key": "notjavascript", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + 'X-Parse-Javascript-Key': 'notjavascript', }; request({ headers: headers, - url: "http://localhost:8378/1/classes/TestObject", + url: 'http://localhost:8378/1/classes/TestObject', }).then(fail, response => { const b = response.data; - expect(b.error).toEqual("unauthorized"); + expect(b.error).toEqual('unauthorized'); done(); }); }); - it("fails on invalid rest api key", done => { + it('fails on invalid rest api key', done => { const headers = { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "notrest", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'notrest', }; request({ headers: headers, - url: "http://localhost:8378/1/classes/TestObject", + url: 'http://localhost:8378/1/classes/TestObject', }).then(fail, response => { const b = response.data; - expect(b.error).toEqual("unauthorized"); + expect(b.error).toEqual('unauthorized'); done(); }); }); - it("fails on invalid function", done => { - Parse.Cloud.run("somethingThatDoesDefinitelyNotExist").then( + it('fails on invalid function', done => { + Parse.Cloud.run('somethingThatDoesDefinitelyNotExist').then( () => { - fail("This should have never suceeded"); + fail('This should have never suceeded'); done(); }, e => { @@ -1458,77 +1458,77 @@ describe("miscellaneous", () => { ); }); - it("dedupes an installation properly and returns updatedAt", done => { + it('dedupes an installation properly and returns updatedAt', done => { const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; const data = { - installationId: "lkjsahdfkjhsdfkjhsdfkjhsdf", - deviceType: "embedded", + installationId: 'lkjsahdfkjhsdfkjhsdfkjhsdf', + deviceType: 'embedded', }; const requestOptions = { headers: headers, - method: "POST", - url: "http://localhost:8378/1/installations", + method: 'POST', + url: 'http://localhost:8378/1/installations', body: JSON.stringify(data), }; request(requestOptions).then(response => { const b = response.data; - expect(typeof b.objectId).toEqual("string"); + expect(typeof b.objectId).toEqual('string'); request(requestOptions).then(response => { const b = response.data; - expect(typeof b.updatedAt).toEqual("string"); + expect(typeof b.updatedAt).toEqual('string'); done(); }); }); }); - it("android login providing empty authData block works", done => { + it('android login providing empty authData block works', done => { const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; const data = { - username: "pulse1989", - password: "password1234", + username: 'pulse1989', + password: 'password1234', authData: {}, }; const requestOptions = { - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/users", + url: 'http://localhost:8378/1/users', body: JSON.stringify(data), }; request(requestOptions).then(() => { - requestOptions.url = "http://localhost:8378/1/login"; + requestOptions.url = 'http://localhost:8378/1/login'; request(requestOptions).then(response => { const b = response.data; - expect(typeof b["sessionToken"]).toEqual("string"); + expect(typeof b['sessionToken']).toEqual('string'); done(); }); }); }); - it("gets relation fields", done => { - const object = new Parse.Object("AnObject"); - const relatedObject = new Parse.Object("RelatedObject"); + it('gets relation fields', done => { + const object = new Parse.Object('AnObject'); + const relatedObject = new Parse.Object('RelatedObject'); Parse.Object.saveAll([object, relatedObject]) .then(() => { - object.relation("related").add(relatedObject); + object.relation('related').add(relatedObject); return object.save(); }) .then(() => { const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; const requestOptions = { headers: headers, - url: "http://localhost:8378/1/classes/AnObject", + url: 'http://localhost:8378/1/classes/AnObject', json: true, }; request(requestOptions).then(res => { @@ -1536,8 +1536,8 @@ describe("miscellaneous", () => { expect(body.results.length).toBe(1); const result = body.results[0]; expect(result.related).toEqual({ - __type: "Relation", - className: "RelatedObject", + __type: 'Relation', + className: 'RelatedObject', }); done(); }); @@ -1548,31 +1548,31 @@ describe("miscellaneous", () => { }); }); - it_id("b2cd9cf2-13fa-4acd-aaa9-6f81fc1858db")(it)( - "properly returns incremented values (#1554)", + it_id('b2cd9cf2-13fa-4acd-aaa9-6f81fc1858db')(it)( + 'properly returns incremented values (#1554)', done => { const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; const requestOptions = { headers: headers, - url: "http://localhost:8378/1/classes/AnObject", + url: 'http://localhost:8378/1/classes/AnObject', json: true, }; - const object = new Parse.Object("AnObject"); + const object = new Parse.Object('AnObject'); function runIncrement(amount) { const options = Object.assign({}, requestOptions, { body: { key: { - __op: "Increment", + __op: 'Increment', amount: amount, }, }, - url: "http://localhost:8378/1/classes/AnObject/" + object.id, - method: "PUT", + url: 'http://localhost:8378/1/classes/AnObject/' + object.id, + method: 'PUT', }); return request(options).then(res => res.data); } @@ -1594,20 +1594,20 @@ describe("miscellaneous", () => { ); it('ignores _RevocableSession "header" send by JS SDK', done => { - const object = new Parse.Object("AnObject"); - object.set("a", "b"); + const object = new Parse.Object('AnObject'); + object.set('a', 'b'); object.save().then(() => { request({ - method: "POST", - headers: { "Content-Type": "application/json" }, - url: "http://localhost:8378/1/classes/AnObject", + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + url: 'http://localhost:8378/1/classes/AnObject', body: { - _method: "GET", - _ApplicationId: "test", - _JavaScriptKey: "test", - _ClientVersion: "js1.8.3", - _InstallationId: "iid", - _RevocableSession: "1", + _method: 'GET', + _ApplicationId: 'test', + _JavaScriptKey: 'test', + _ClientVersion: 'js1.8.3', + _InstallationId: 'iid', + _RevocableSession: '1', }, }).then(res => { const body = res.data; @@ -1615,64 +1615,64 @@ describe("miscellaneous", () => { expect(body.results).not.toBeUndefined(); expect(body.results.length).toBe(1); const result = body.results[0]; - expect(result.a).toBe("b"); + expect(result.a).toBe('b'); done(); }); }); }); - it("doesnt convert interior keys of objects that use special names", done => { - const obj = new Parse.Object("Obj"); - obj.set("val", { createdAt: "a", updatedAt: 1 }); + it('doesnt convert interior keys of objects that use special names', done => { + const obj = new Parse.Object('Obj'); + obj.set('val', { createdAt: 'a', updatedAt: 1 }); obj .save() - .then(obj => new Parse.Query("Obj").get(obj.id)) + .then(obj => new Parse.Query('Obj').get(obj.id)) .then(obj => { - expect(obj.get("val").createdAt).toEqual("a"); - expect(obj.get("val").updatedAt).toEqual(1); + expect(obj.get('val').createdAt).toEqual('a'); + expect(obj.get('val').updatedAt).toEqual(1); done(); }); }); - it("bans interior keys containing . or $", done => { - new Parse.Object("Obj") - .save({ innerObj: { "key with a $": "fails" } }) + it('bans interior keys containing . or $', done => { + new Parse.Object('Obj') + .save({ innerObj: { 'key with a $': 'fails' } }) .then( () => { - fail("should not succeed"); + fail('should not succeed'); }, error => { expect(error.code).toEqual(Parse.Error.INVALID_NESTED_KEY); - return new Parse.Object("Obj").save({ - innerObj: { "key with a .": "fails" }, + return new Parse.Object('Obj').save({ + innerObj: { 'key with a .': 'fails' }, }); } ) .then( () => { - fail("should not succeed"); + fail('should not succeed'); }, error => { expect(error.code).toEqual(Parse.Error.INVALID_NESTED_KEY); - return new Parse.Object("Obj").save({ - innerObj: { innerInnerObj: { "key with $": "fails" } }, + return new Parse.Object('Obj').save({ + innerObj: { innerInnerObj: { 'key with $': 'fails' } }, }); } ) .then( () => { - fail("should not succeed"); + fail('should not succeed'); }, error => { expect(error.code).toEqual(Parse.Error.INVALID_NESTED_KEY); - return new Parse.Object("Obj").save({ - innerObj: { innerInnerObj: { "key with .": "fails" } }, + return new Parse.Object('Obj').save({ + innerObj: { innerInnerObj: { 'key with .': 'fails' } }, }); } ) .then( () => { - fail("should not succeed"); + fail('should not succeed'); done(); }, error => { @@ -1682,54 +1682,54 @@ describe("miscellaneous", () => { ); }); - it("does not change inner object keys named _auth_data_something", done => { - new Parse.Object("O") + it('does not change inner object keys named _auth_data_something', done => { + new Parse.Object('O') .save({ innerObj: { _auth_data_facebook: 7 } }) - .then(object => new Parse.Query("O").get(object.id)) + .then(object => new Parse.Query('O').get(object.id)) .then(object => { - expect(object.get("innerObj")).toEqual({ _auth_data_facebook: 7 }); + expect(object.get('innerObj')).toEqual({ _auth_data_facebook: 7 }); done(); }); }); - it("does not change inner object key names _p_somethign", done => { - new Parse.Object("O") + it('does not change inner object key names _p_somethign', done => { + new Parse.Object('O') .save({ innerObj: { _p_data: 7 } }) - .then(object => new Parse.Query("O").get(object.id)) + .then(object => new Parse.Query('O').get(object.id)) .then(object => { - expect(object.get("innerObj")).toEqual({ _p_data: 7 }); + expect(object.get('innerObj')).toEqual({ _p_data: 7 }); done(); }); }); - it("does not change inner object key names _rperm, _wperm", done => { - new Parse.Object("O") + it('does not change inner object key names _rperm, _wperm', done => { + new Parse.Object('O') .save({ innerObj: { _rperm: 7, _wperm: 8 } }) - .then(object => new Parse.Query("O").get(object.id)) + .then(object => new Parse.Query('O').get(object.id)) .then(object => { - expect(object.get("innerObj")).toEqual({ _rperm: 7, _wperm: 8 }); + expect(object.get('innerObj')).toEqual({ _rperm: 7, _wperm: 8 }); done(); }); }); - it("does not change inner objects if the key has the same name as a geopoint field on the class, and the value is an array of length 2, or if the key has the same name as a file field on the class, and the value is a string", done => { - const file = new Parse.File("myfile.txt", { base64: "eAo=" }); + it('does not change inner objects if the key has the same name as a geopoint field on the class, and the value is an array of length 2, or if the key has the same name as a file field on the class, and the value is a string', done => { + const file = new Parse.File('myfile.txt', { base64: 'eAo=' }); file .save() .then(f => { - const obj = new Parse.Object("O"); - obj.set("fileField", f); - obj.set("geoField", new Parse.GeoPoint(0, 0)); - obj.set("innerObj", { - fileField: "data", + const obj = new Parse.Object('O'); + obj.set('fileField', f); + obj.set('geoField', new Parse.GeoPoint(0, 0)); + obj.set('innerObj', { + fileField: 'data', geoField: [1, 2], }); return obj.save(); }) .then(object => object.fetch()) .then(object => { - expect(object.get("innerObj")).toEqual({ - fileField: "data", + expect(object.get('innerObj')).toEqual({ + fileField: 'data', geoField: [1, 2], }); done(); @@ -1740,13 +1740,13 @@ describe("miscellaneous", () => { }); }); - it_id("8f99ee20-3da7-45ec-b867-ea0eb87524a9")(it)( - "purge all objects in class", + it_id('8f99ee20-3da7-45ec-b867-ea0eb87524a9')(it)( + 'purge all objects in class', done => { - const object = new Parse.Object("TestObject"); - object.set("foo", "bar"); - const object2 = new Parse.Object("TestObject"); - object2.set("alice", "wonderland"); + const object = new Parse.Object('TestObject'); + object.set('foo', 'bar'); + const object2 = new Parse.Object('TestObject'); + object2.set('alice', 'wonderland'); Parse.Object.saveAll([object, object2]) .then(() => { const query = new Parse.Query(TestObject); @@ -1755,14 +1755,14 @@ describe("miscellaneous", () => { .then(count => { expect(count).toBe(2); const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-Master-Key": "test", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-Master-Key': 'test', }; request({ - method: "DELETE", + method: 'DELETE', headers: headers, - url: "http://localhost:8378/1/purge/TestObject", + url: 'http://localhost:8378/1/purge/TestObject', }).then(() => { const query = new Parse.Query(TestObject); return query.count().then(count => { @@ -1774,33 +1774,33 @@ describe("miscellaneous", () => { } ); - it("fail on purge all objects in class without master key", done => { + it('fail on purge all objects in class without master key', done => { const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; request({ - method: "DELETE", + method: 'DELETE', headers: headers, - url: "http://localhost:8378/1/purge/TestObject", + url: 'http://localhost:8378/1/purge/TestObject', }) .then(() => { - fail("Should not succeed"); + fail('Should not succeed'); }) .catch(response => { expect(response.data.error).toEqual( - "unauthorized: master key is required" + 'unauthorized: master key is required' ); done(); }); }); - it("purge all objects in _Role also purge cache", done => { + it('purge all objects in _Role also purge cache', done => { const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-Master-Key": "test", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-Master-Key': 'test', }; let user, object; createTestUser() @@ -1809,53 +1809,53 @@ describe("miscellaneous", () => { const acl = new Parse.ACL(); acl.setPublicReadAccess(true); acl.setPublicWriteAccess(false); - const role = new Parse.Object("_Role"); - role.set("name", "TestRole"); + const role = new Parse.Object('_Role'); + role.set('name', 'TestRole'); role.setACL(acl); - const users = role.relation("users"); + const users = role.relation('users'); users.add(user); return role.save({}, { useMasterKey: true }); }) .then(() => { - const query = new Parse.Query("_Role"); + const query = new Parse.Query('_Role'); return query.find({ useMasterKey: true }); }) .then(x => { expect(x.length).toEqual(1); - const relation = x[0].relation("users").query(); + const relation = x[0].relation('users').query(); return relation.first({ useMasterKey: true }); }) .then(x => { expect(x.id).toEqual(user.id); - object = new Parse.Object("TestObject"); + object = new Parse.Object('TestObject'); const acl = new Parse.ACL(); acl.setPublicReadAccess(false); acl.setPublicWriteAccess(false); - acl.setRoleReadAccess("TestRole", true); - acl.setRoleWriteAccess("TestRole", true); + acl.setRoleReadAccess('TestRole', true); + acl.setRoleWriteAccess('TestRole', true); object.setACL(acl); return object.save(); }) .then(() => { - const query = new Parse.Query("TestObject"); + const query = new Parse.Query('TestObject'); return query.find({ sessionToken: user.getSessionToken() }); }) .then(x => { expect(x.length).toEqual(1); return request({ - method: "DELETE", + method: 'DELETE', headers: headers, - url: "http://localhost:8378/1/purge/_Role", + url: 'http://localhost:8378/1/purge/_Role', json: true, }); }) .then(() => { - const query = new Parse.Query("TestObject"); + const query = new Parse.Query('TestObject'); return query.get(object.id, { sessionToken: user.getSessionToken() }); }) .then( () => { - fail("Should not succeed"); + fail('Should not succeed'); }, e => { expect(e.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); @@ -1864,24 +1864,24 @@ describe("miscellaneous", () => { ); }); - it("purge empty class", done => { - const testSchema = new Parse.Schema("UnknownClass"); + it('purge empty class', done => { + const testSchema = new Parse.Schema('UnknownClass'); testSchema.purge().then(done).catch(done.fail); }); - it("should not update schema beforeSave #2672", done => { - Parse.Cloud.beforeSave("MyObject", request => { - if (request.object.get("secret")) { - throw "cannot set secret here"; + it('should not update schema beforeSave #2672', done => { + Parse.Cloud.beforeSave('MyObject', request => { + if (request.object.get('secret')) { + throw 'cannot set secret here'; } }); - const object = new Parse.Object("MyObject"); - object.set("key", "value"); + const object = new Parse.Object('MyObject'); + object.set('key', 'value'); object .save() .then(() => { - return object.save({ secret: "should not update schema" }); + return object.save({ secret: 'should not update schema' }); }) .then( () => { @@ -1890,12 +1890,12 @@ describe("miscellaneous", () => { }, () => { return request({ - method: "GET", + method: 'GET', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Master-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Master-Key': 'test', }, - url: "http://localhost:8378/1/schemas/MyObject", + url: 'http://localhost:8378/1/schemas/MyObject', json: true, }); } @@ -1914,34 +1914,34 @@ describe("miscellaneous", () => { }); }); -describe_only_db("mongo")("legacy _acl", () => { - it("should have _acl when locking down (regression for #2465)", done => { +describe_only_db('mongo')('legacy _acl', () => { + it('should have _acl when locking down (regression for #2465)', done => { const headers = { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }; request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/classes/Report", + url: 'http://localhost:8378/1/classes/Report', body: { ACL: {}, - name: "My Report", + name: 'My Report', }, json: true, }) .then(() => { - const config = Config.get("test"); + const config = Config.get('test'); const adapter = config.database.adapter; return adapter - ._adaptiveCollection("Report") + ._adaptiveCollection('Report') .then(collection => collection.find({})); }) .then(results => { expect(results.length).toBe(1); const result = results[0]; - expect(result.name).toEqual("My Report"); + expect(result.name).toEqual('My Report'); expect(result._wperm).toEqual([]); expect(result._rperm).toEqual([]); expect(result._acl).toEqual({}); diff --git a/spec/ParseCloudCodePublisher.spec.js b/spec/ParseCloudCodePublisher.spec.js index 21f0ebadaf..cc4e123320 100644 --- a/spec/ParseCloudCodePublisher.spec.js +++ b/spec/ParseCloudCodePublisher.spec.js @@ -1,80 +1,80 @@ const ParseCloudCodePublisher = - require("../lib/LiveQuery/ParseCloudCodePublisher").ParseCloudCodePublisher; -const Parse = require("parse/node"); + require('../lib/LiveQuery/ParseCloudCodePublisher').ParseCloudCodePublisher; +const Parse = require('parse/node'); -describe("ParseCloudCodePublisher", function () { +describe('ParseCloudCodePublisher', function () { beforeEach(function (done) { // Mock ParsePubSub const mockParsePubSub = { - createPublisher: jasmine.createSpy("publish").and.returnValue({ - publish: jasmine.createSpy("publish"), - on: jasmine.createSpy("on"), + createPublisher: jasmine.createSpy('publish').and.returnValue({ + publish: jasmine.createSpy('publish'), + on: jasmine.createSpy('on'), }), - createSubscriber: jasmine.createSpy("publish").and.returnValue({ - subscribe: jasmine.createSpy("subscribe"), - on: jasmine.createSpy("on"), + createSubscriber: jasmine.createSpy('publish').and.returnValue({ + subscribe: jasmine.createSpy('subscribe'), + on: jasmine.createSpy('on'), }), }; jasmine.mockLibrary( - "../lib/LiveQuery/ParsePubSub", - "ParsePubSub", + '../lib/LiveQuery/ParsePubSub', + 'ParsePubSub', mockParsePubSub ); done(); }); - it("can initialize", function () { + it('can initialize', function () { const config = {}; new ParseCloudCodePublisher(config); - const ParsePubSub = require("../lib/LiveQuery/ParsePubSub").ParsePubSub; + const ParsePubSub = require('../lib/LiveQuery/ParsePubSub').ParsePubSub; expect(ParsePubSub.createPublisher).toHaveBeenCalledWith(config); }); - it("can handle cloud code afterSave request", function () { + it('can handle cloud code afterSave request', function () { const publisher = new ParseCloudCodePublisher({}); - publisher._onCloudCodeMessage = jasmine.createSpy("onCloudCodeMessage"); + publisher._onCloudCodeMessage = jasmine.createSpy('onCloudCodeMessage'); const request = {}; publisher.onCloudCodeAfterSave(request); expect(publisher._onCloudCodeMessage).toHaveBeenCalledWith( - Parse.applicationId + "afterSave", + Parse.applicationId + 'afterSave', request ); }); - it("can handle cloud code afterDelete request", function () { + it('can handle cloud code afterDelete request', function () { const publisher = new ParseCloudCodePublisher({}); - publisher._onCloudCodeMessage = jasmine.createSpy("onCloudCodeMessage"); + publisher._onCloudCodeMessage = jasmine.createSpy('onCloudCodeMessage'); const request = {}; publisher.onCloudCodeAfterDelete(request); expect(publisher._onCloudCodeMessage).toHaveBeenCalledWith( - Parse.applicationId + "afterDelete", + Parse.applicationId + 'afterDelete', request ); }); - it("can handle cloud code request", function () { + it('can handle cloud code request', function () { const publisher = new ParseCloudCodePublisher({}); - const currentParseObject = new Parse.Object("Test"); - currentParseObject.set("key", "value"); - const originalParseObject = new Parse.Object("Test"); - originalParseObject.set("key", "originalValue"); + const currentParseObject = new Parse.Object('Test'); + currentParseObject.set('key', 'value'); + const originalParseObject = new Parse.Object('Test'); + originalParseObject.set('key', 'originalValue'); const request = { object: currentParseObject, original: originalParseObject, }; - publisher._onCloudCodeMessage("afterSave", request); + publisher._onCloudCodeMessage('afterSave', request); const args = publisher.parsePublisher.publish.calls.mostRecent().args; - expect(args[0]).toBe("afterSave"); + expect(args[0]).toBe('afterSave'); const message = JSON.parse(args[1]); expect(message.currentParseObject).toEqual(request.object._toFullJSON()); expect(message.originalParseObject).toEqual(request.original._toFullJSON()); }); afterEach(function () { - jasmine.restoreLibrary("../lib/LiveQuery/ParsePubSub", "ParsePubSub"); + jasmine.restoreLibrary('../lib/LiveQuery/ParsePubSub', 'ParsePubSub'); }); }); diff --git a/spec/ParseConfigKey.spec.js b/spec/ParseConfigKey.spec.js index dea37c1dee..1b6ee32dff 100644 --- a/spec/ParseConfigKey.spec.js +++ b/spec/ParseConfigKey.spec.js @@ -1,17 +1,17 @@ -const Config = require("../lib/Config"); +const Config = require('../lib/Config'); -describe("Config Keys", () => { +describe('Config Keys', () => { const invalidKeyErrorMessage = - "Invalid key\\(s\\) found in Parse Server configuration"; + 'Invalid key\\(s\\) found in Parse Server configuration'; let loggerErrorSpy; beforeEach(async () => { - const logger = require("../lib/logger").logger; - loggerErrorSpy = spyOn(logger, "error").and.callThrough(); - spyOn(Config, "validateOptions").and.callFake(() => {}); + const logger = require('../lib/logger').logger; + loggerErrorSpy = spyOn(logger, 'error').and.callThrough(); + spyOn(Config, 'validateOptions').and.callFake(() => {}); }); - it("recognizes invalid keys in root", async () => { + it('recognizes invalid keys in root', async () => { await expectAsync( reconfigureServer({ invalidKey: 1, @@ -19,11 +19,11 @@ describe("Config Keys", () => { ).toBeResolved(); const error = loggerErrorSpy.calls .all() - .reduce((s, call) => (s += call.args[0]), ""); + .reduce((s, call) => (s += call.args[0]), ''); expect(error).toMatch(invalidKeyErrorMessage); }); - it("recognizes invalid keys in pages.customUrls", async () => { + it('recognizes invalid keys in pages.customUrls', async () => { await expectAsync( reconfigureServer({ pages: { @@ -36,13 +36,13 @@ describe("Config Keys", () => { ).toBeResolved(); const error = loggerErrorSpy.calls .all() - .reduce((s, call) => (s += call.args[0]), ""); + .reduce((s, call) => (s += call.args[0]), ''); expect(error).toMatch(invalidKeyErrorMessage); expect(error).toMatch(`invalidKey`); expect(error).toMatch(`EmailVerificationSendFail`); }); - it("recognizes invalid keys in liveQueryServerOptions", async () => { + it('recognizes invalid keys in liveQueryServerOptions', async () => { await expectAsync( reconfigureServer({ liveQueryServerOptions: { @@ -53,12 +53,12 @@ describe("Config Keys", () => { ).toBeResolved(); const error = loggerErrorSpy.calls .all() - .reduce((s, call) => (s += call.args[0]), ""); + .reduce((s, call) => (s += call.args[0]), ''); expect(error).toMatch(invalidKeyErrorMessage); expect(error).toMatch(`MasterKey`); }); - it("recognizes invalid keys in rateLimit", async () => { + it('recognizes invalid keys in rateLimit', async () => { await expectAsync( reconfigureServer({ rateLimit: [ @@ -70,15 +70,15 @@ describe("Config Keys", () => { ).toBeRejected(); const error = loggerErrorSpy.calls .all() - .reduce((s, call) => (s += call.args[0]), ""); + .reduce((s, call) => (s += call.args[0]), ''); expect(error).toMatch(invalidKeyErrorMessage); - expect(error).toMatch("rateLimit\\[0\\]\\.invalidKey"); - expect(error).toMatch("rateLimit\\[1\\]\\.RequestPath"); - expect(error).toMatch("rateLimit\\[2\\]\\.RequestTimeWindow"); + expect(error).toMatch('rateLimit\\[0\\]\\.invalidKey'); + expect(error).toMatch('rateLimit\\[1\\]\\.RequestPath'); + expect(error).toMatch('rateLimit\\[2\\]\\.RequestTimeWindow'); }); - it_only_db("mongo")( - "recognizes valid keys in default configuration", + it_only_db('mongo')( + 'recognizes valid keys in default configuration', async () => { await expectAsync( reconfigureServer({ @@ -86,17 +86,17 @@ describe("Config Keys", () => { }) ).toBeResolved(); expect( - loggerErrorSpy.calls.all().reduce((s, call) => (s += call.args[0]), "") + loggerErrorSpy.calls.all().reduce((s, call) => (s += call.args[0]), '') ).not.toMatch(invalidKeyErrorMessage); } ); - it_only_db("mongo")( - "recognizes valid keys in databaseOptions (MongoDB)", + it_only_db('mongo')( + 'recognizes valid keys in databaseOptions (MongoDB)', async () => { await expectAsync( reconfigureServer({ - databaseURI: "mongodb://localhost:27017/parse", + databaseURI: 'mongodb://localhost:27017/parse', filesAdapter: null, databaseAdapter: null, databaseOptions: { @@ -113,7 +113,7 @@ describe("Config Keys", () => { }) ).toBeResolved(); expect( - loggerErrorSpy.calls.all().reduce((s, call) => (s += call.args[0]), "") + loggerErrorSpy.calls.all().reduce((s, call) => (s += call.args[0]), '') ).not.toMatch(invalidKeyErrorMessage); } ); diff --git a/spec/ParseFile.spec.js b/spec/ParseFile.spec.js index 4c4628358e..6cc5ac3d89 100644 --- a/spec/ParseFile.spec.js +++ b/spec/ParseFile.spec.js @@ -1,30 +1,30 @@ // This is a port of the test suite: // hungry/js/test/parse_file_test.js -"use strict"; +'use strict'; -const { FilesController } = require("../lib/Controllers/FilesController"); -const request = require("../lib/request"); +const { FilesController } = require('../lib/Controllers/FilesController'); +const request = require('../lib/request'); -const str = "Hello World!"; +const str = 'Hello World!'; const data = []; for (let i = 0; i < str.length; i++) { data.push(str.charCodeAt(i)); } -describe("Parse.File testing", () => { - describe("creating files", () => { - it("works with Content-Type", done => { +describe('Parse.File testing', () => { + describe('creating files', () => { + it('works with Content-Type', done => { const headers = { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/files/file.txt", - body: "argle bargle", + url: 'http://localhost:8378/1/files/file.txt', + body: 'argle bargle', }).then(response => { const b = response.data; expect(b.name).toMatch(/_file.txt$/); @@ -33,27 +33,27 @@ describe("Parse.File testing", () => { ); request({ url: b.url }).then(response => { const body = response.text; - expect(body).toEqual("argle bargle"); + expect(body).toEqual('argle bargle'); done(); }); }); }); - it("works with _ContentType", async () => { + it('works with _ContentType', async () => { await reconfigureServer({ fileUpload: { enableForPublic: true, - fileExtensions: ["*"], + fileExtensions: ['*'], }, }); let response = await request({ - method: "POST", - url: "http://localhost:8378/1/files/file", + method: 'POST', + url: 'http://localhost:8378/1/files/file', body: JSON.stringify({ - _ApplicationId: "test", - _JavaScriptKey: "test", - _ContentType: "text/html", - base64: "PGh0bWw+PC9odG1sPgo=", + _ApplicationId: 'test', + _JavaScriptKey: 'test', + _ContentType: 'text/html', + base64: 'PGh0bWw+PC9odG1sPgo=', }), }); const b = response.data; @@ -64,23 +64,23 @@ describe("Parse.File testing", () => { response = await request({ url: b.url }); const body = response.text; try { - expect(response.headers["content-type"]).toMatch("^text/html"); - expect(body).toEqual("\n"); + expect(response.headers['content-type']).toMatch('^text/html'); + expect(body).toEqual('\n'); } catch (e) { jfail(e); } }); - it("works without Content-Type", done => { + it('works without Content-Type', done => { const headers = { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/files/file.txt", - body: "argle bargle", + url: 'http://localhost:8378/1/files/file.txt', + body: 'argle bargle', }).then(response => { const b = response.data; expect(b.name).toMatch(/_file.txt$/); @@ -88,23 +88,23 @@ describe("Parse.File testing", () => { /^http:\/\/localhost:8378\/1\/files\/test\/.*file.txt$/ ); request({ url: b.url }).then(response => { - expect(response.text).toEqual("argle bargle"); + expect(response.text).toEqual('argle bargle'); done(); }); }); }); - it("supports REST end-to-end file create, read, delete, read", done => { + it('supports REST end-to-end file create, read, delete, read', done => { const headers = { - "Content-Type": "image/jpeg", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'image/jpeg', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/files/testfile.txt", - body: "check one two", + url: 'http://localhost:8378/1/files/testfile.txt', + body: 'check one two', }).then(response => { const b = response.data; expect(b.name).toMatch(/_testfile.txt$/); @@ -113,21 +113,21 @@ describe("Parse.File testing", () => { ); request({ url: b.url }).then(response => { const body = response.text; - expect(body).toEqual("check one two"); + expect(body).toEqual('check one two'); request({ - method: "DELETE", + method: 'DELETE', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "X-Parse-Master-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Master-Key': 'test', }, - url: "http://localhost:8378/1/files/" + b.name, + url: 'http://localhost:8378/1/files/' + b.name, }).then(response => { expect(response.status).toEqual(200); request({ headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }, url: b.url, }).then(fail, response => { @@ -139,17 +139,17 @@ describe("Parse.File testing", () => { }); }); - it("blocks file deletions with missing or incorrect master-key header", done => { + it('blocks file deletions with missing or incorrect master-key header', done => { const headers = { - "Content-Type": "image/jpeg", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'image/jpeg', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/files/thefile.jpg", - body: "the file body", + url: 'http://localhost:8378/1/files/thefile.jpg', + body: 'the file body', }).then(response => { const b = response.data; expect(b.url).toMatch( @@ -157,25 +157,25 @@ describe("Parse.File testing", () => { ); // missing X-Parse-Master-Key header request({ - method: "DELETE", + method: 'DELETE', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }, - url: "http://localhost:8378/1/files/" + b.name, + url: 'http://localhost:8378/1/files/' + b.name, }).then(fail, response => { const del_b = response.data; expect(response.status).toEqual(403); expect(del_b.error).toMatch(/unauthorized/); // incorrect X-Parse-Master-Key header request({ - method: "DELETE", + method: 'DELETE', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "X-Parse-Master-Key": "tryagain", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Master-Key': 'tryagain', }, - url: "http://localhost:8378/1/files/" + b.name, + url: 'http://localhost:8378/1/files/' + b.name, }).then(fail, response => { const del_b2 = response.data; expect(response.status).toEqual(403); @@ -186,17 +186,17 @@ describe("Parse.File testing", () => { }); }); - it("handles other filetypes", done => { + it('handles other filetypes', done => { const headers = { - "Content-Type": "image/jpeg", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'image/jpeg', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/files/file.jpg", - body: "argle bargle", + url: 'http://localhost:8378/1/files/file.jpg', + body: 'argle bargle', }).then(response => { const b = response.data; expect(b.name).toMatch(/_file.jpg$/); @@ -205,26 +205,26 @@ describe("Parse.File testing", () => { ); request({ url: b.url }).then(response => { const body = response.text; - expect(body).toEqual("argle bargle"); + expect(body).toEqual('argle bargle'); done(); }); }); }); - it("save file", async () => { - const file = new Parse.File("hello.txt", data, "text/plain"); + it('save file', async () => { + const file = new Parse.File('hello.txt', data, 'text/plain'); ok(!file.url()); const result = await file.save(); strictEqual(result, file); ok(file.name()); ok(file.url()); - notEqual(file.name(), "hello.txt"); + notEqual(file.name(), 'hello.txt'); }); - it("saves the file with tags", async () => { - spyOn(FilesController.prototype, "createFile").and.callThrough(); - const file = new Parse.File("hello.txt", data, "text/plain"); - const tags = { hello: "world" }; + it('saves the file with tags', async () => { + spyOn(FilesController.prototype, 'createFile').and.callThrough(); + const file = new Parse.File('hello.txt', data, 'text/plain'); + const tags = { hello: 'world' }; file.setTags(tags); expect(file.url()).toBeUndefined(); const result = await file.save(); @@ -237,9 +237,9 @@ describe("Parse.File testing", () => { }); }); - it("does not pass empty file tags while saving", async () => { - spyOn(FilesController.prototype, "createFile").and.callThrough(); - const file = new Parse.File("hello.txt", data, "text/plain"); + it('does not pass empty file tags while saving', async () => { + spyOn(FilesController.prototype, 'createFile').and.callThrough(); + const file = new Parse.File('hello.txt', data, 'text/plain'); expect(file.url()).toBeUndefined(); expect(file.name()).toBeDefined(); await file.save(); @@ -249,90 +249,90 @@ describe("Parse.File testing", () => { }); }); - it("save file in object", async done => { - const file = new Parse.File("hello.txt", data, "text/plain"); + it('save file in object', async done => { + const file = new Parse.File('hello.txt', data, 'text/plain'); ok(!file.url()); const result = await file.save(); strictEqual(result, file); ok(file.name()); ok(file.url()); - notEqual(file.name(), "hello.txt"); + notEqual(file.name(), 'hello.txt'); - const object = new Parse.Object("TestObject"); + const object = new Parse.Object('TestObject'); await object.save({ file: file }); - const objectAgain = await new Parse.Query("TestObject").get(object.id); - ok(objectAgain.get("file") instanceof Parse.File); + const objectAgain = await new Parse.Query('TestObject').get(object.id); + ok(objectAgain.get('file') instanceof Parse.File); done(); }); - it("save file in object with escaped characters in filename", async () => { - const file = new Parse.File("hello . txt", data, "text/plain"); + it('save file in object with escaped characters in filename', async () => { + const file = new Parse.File('hello . txt', data, 'text/plain'); ok(!file.url()); const result = await file.save(); strictEqual(result, file); ok(file.name()); ok(file.url()); - notEqual(file.name(), "hello . txt"); + notEqual(file.name(), 'hello . txt'); - const object = new Parse.Object("TestObject"); + const object = new Parse.Object('TestObject'); await object.save({ file }); - const objectAgain = await new Parse.Query("TestObject").get(object.id); - ok(objectAgain.get("file") instanceof Parse.File); + const objectAgain = await new Parse.Query('TestObject').get(object.id); + ok(objectAgain.get('file') instanceof Parse.File); }); - it("autosave file in object", async done => { - let file = new Parse.File("hello.txt", data, "text/plain"); + it('autosave file in object', async done => { + let file = new Parse.File('hello.txt', data, 'text/plain'); ok(!file.url()); - const object = new Parse.Object("TestObject"); + const object = new Parse.Object('TestObject'); await object.save({ file }); - const objectAgain = await new Parse.Query("TestObject").get(object.id); - file = objectAgain.get("file"); + const objectAgain = await new Parse.Query('TestObject').get(object.id); + file = objectAgain.get('file'); ok(file instanceof Parse.File); ok(file.name()); ok(file.url()); - notEqual(file.name(), "hello.txt"); + notEqual(file.name(), 'hello.txt'); done(); }); - it("autosave file in object in object", async done => { - let file = new Parse.File("hello.txt", data, "text/plain"); + it('autosave file in object in object', async done => { + let file = new Parse.File('hello.txt', data, 'text/plain'); ok(!file.url()); - const child = new Parse.Object("Child"); - child.set("file", file); + const child = new Parse.Object('Child'); + child.set('file', file); - const parent = new Parse.Object("Parent"); - parent.set("child", child); + const parent = new Parse.Object('Parent'); + parent.set('child', child); await parent.save(); - const query = new Parse.Query("Parent"); - query.include("child"); + const query = new Parse.Query('Parent'); + query.include('child'); const parentAgain = await query.get(parent.id); - const childAgain = parentAgain.get("child"); - file = childAgain.get("file"); + const childAgain = parentAgain.get('child'); + file = childAgain.get('file'); ok(file instanceof Parse.File); ok(file.name()); ok(file.url()); - notEqual(file.name(), "hello.txt"); + notEqual(file.name(), 'hello.txt'); done(); }); - it("saving an already saved file", async () => { - const file = new Parse.File("hello.txt", data, "text/plain"); + it('saving an already saved file', async () => { + const file = new Parse.File('hello.txt', data, 'text/plain'); ok(!file.url()); const result = await file.save(); strictEqual(result, file); ok(file.name()); ok(file.url()); - notEqual(file.name(), "hello.txt"); + notEqual(file.name(), 'hello.txt'); const previousName = file.name(); await file.save(); equal(file.name(), previousName); }); - it("two saves at the same time", done => { - const file = new Parse.File("hello.txt", data, "text/plain"); + it('two saves at the same time', done => { + const file = new Parse.File('hello.txt', data, 'text/plain'); let firstName; let secondName; @@ -356,71 +356,71 @@ describe("Parse.File testing", () => { ); }); - it("file toJSON testing", async () => { - const file = new Parse.File("hello.txt", data, "text/plain"); + it('file toJSON testing', async () => { + const file = new Parse.File('hello.txt', data, 'text/plain'); ok(!file.url()); - const object = new Parse.Object("TestObject"); + const object = new Parse.Object('TestObject'); await object.save({ file: file, }); ok(object.toJSON().file.url); }); - it("content-type used with no extension", async () => { + it('content-type used with no extension', async () => { await reconfigureServer({ fileUpload: { enableForPublic: true, - fileExtensions: ["*"], + fileExtensions: ['*'], }, }); const headers = { - "Content-Type": "text/html", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'text/html', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; let response = await request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/files/file", - body: "fee fi fo", + url: 'http://localhost:8378/1/files/file', + body: 'fee fi fo', }); const b = response.data; expect(b.name).toMatch(/\.html$/); response = await request({ url: b.url }); - expect(response.headers["content-type"]).toMatch(/^text\/html/); + expect(response.headers['content-type']).toMatch(/^text\/html/); }); - it("works without Content-Type and extension", async () => { + it('works without Content-Type and extension', async () => { await reconfigureServer({ fileUpload: { enableForPublic: true, }, }); const headers = { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; const result = await request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/files/file", - body: "\n", + url: 'http://localhost:8378/1/files/file', + body: '\n', }); - expect(result.data.url.includes("file.txt")).toBeTrue(); - expect(result.data.name.includes("file.txt")).toBeTrue(); + expect(result.data.url.includes('file.txt')).toBeTrue(); + expect(result.data.name.includes('file.txt')).toBeTrue(); }); - it("filename is url encoded", done => { + it('filename is url encoded', done => { const headers = { - "Content-Type": "text/html", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'text/html', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/files/hello world.txt", - body: "oh emm gee", + url: 'http://localhost:8378/1/files/hello world.txt', + body: 'oh emm gee', }).then(response => { const b = response.data; expect(b.url).toMatch(/hello%20world/); @@ -428,41 +428,41 @@ describe("Parse.File testing", () => { }); }); - it("supports array of files", done => { + it('supports array of files', done => { const file = { - __type: "File", - url: "http://meep.meep", - name: "meep", + __type: 'File', + url: 'http://meep.meep', + name: 'meep', }; const files = [file, file]; - const obj = new Parse.Object("FilesArrayTest"); - obj.set("files", files); + const obj = new Parse.Object('FilesArrayTest'); + obj.set('files', files); obj .save() .then(() => { - const query = new Parse.Query("FilesArrayTest"); + const query = new Parse.Query('FilesArrayTest'); return query.first(); }) .then(result => { - const filesAgain = result.get("files"); + const filesAgain = result.get('files'); expect(filesAgain.length).toEqual(2); - expect(filesAgain[0].name()).toEqual("meep"); - expect(filesAgain[0].url()).toEqual("http://meep.meep"); + expect(filesAgain[0].name()).toEqual('meep'); + expect(filesAgain[0].url()).toEqual('http://meep.meep'); done(); }); }); - it("validates filename characters", done => { + it('validates filename characters', done => { const headers = { - "Content-Type": "text/plain", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'text/plain', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/files/di$avowed.txt", - body: "will fail", + url: 'http://localhost:8378/1/files/di$avowed.txt', + body: 'will fail', }).then(fail, response => { const b = response.data; expect(b.code).toEqual(122); @@ -470,22 +470,22 @@ describe("Parse.File testing", () => { }); }); - it("validates filename length", done => { + it('validates filename length', done => { const headers = { - "Content-Type": "text/plain", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'text/plain', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; const fileName = - "Onceuponamidnightdrearywhileiponderedweak" + - "andwearyOveramanyquaintandcuriousvolumeof" + - "forgottenloreWhileinoddednearlynappingsud" + - "denlytherecameatapping"; + 'Onceuponamidnightdrearywhileiponderedweak' + + 'andwearyOveramanyquaintandcuriousvolumeof' + + 'forgottenloreWhileinoddednearlynappingsud' + + 'denlytherecameatapping'; request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/files/" + fileName, - body: "will fail", + url: 'http://localhost:8378/1/files/' + fileName, + body: 'will fail', }).then(fail, response => { const b = response.data; expect(b.code).toEqual(122); @@ -493,29 +493,29 @@ describe("Parse.File testing", () => { }); }); - it("supports a dictionary with file", done => { + it('supports a dictionary with file', done => { const file = { - __type: "File", - url: "http://meep.meep", - name: "meep", + __type: 'File', + url: 'http://meep.meep', + name: 'meep', }; const dict = { file: file, }; - const obj = new Parse.Object("FileObjTest"); - obj.set("obj", dict); + const obj = new Parse.Object('FileObjTest'); + obj.set('obj', dict); obj .save() .then(() => { - const query = new Parse.Query("FileObjTest"); + const query = new Parse.Query('FileObjTest'); return query.first(); }) .then(result => { - const dictAgain = result.get("obj"); - expect(typeof dictAgain).toEqual("object"); - const fileAgain = dictAgain["file"]; - expect(fileAgain.name()).toEqual("meep"); - expect(fileAgain.url()).toEqual("http://meep.meep"); + const dictAgain = result.get('obj'); + expect(typeof dictAgain).toEqual('object'); + const fileAgain = dictAgain['file']; + expect(fileAgain.name()).toEqual('meep'); + expect(fileAgain.url()).toEqual('http://meep.meep'); done(); }) .catch(e => { @@ -524,24 +524,24 @@ describe("Parse.File testing", () => { }); }); - it("creates correct url for old files hosted on files.parsetfss.com", done => { + it('creates correct url for old files hosted on files.parsetfss.com', done => { const file = { - __type: "File", - url: "http://irrelevant.elephant/", - name: "tfss-123.txt", + __type: 'File', + url: 'http://irrelevant.elephant/', + name: 'tfss-123.txt', }; - const obj = new Parse.Object("OldFileTest"); - obj.set("oldfile", file); + const obj = new Parse.Object('OldFileTest'); + obj.set('oldfile', file); obj .save() .then(() => { - const query = new Parse.Query("OldFileTest"); + const query = new Parse.Query('OldFileTest'); return query.first(); }) .then(result => { - const fileAgain = result.get("oldfile"); + const fileAgain = result.get('oldfile'); expect(fileAgain.url()).toEqual( - "http://files.parsetfss.com/test/tfss-123.txt" + 'http://files.parsetfss.com/test/tfss-123.txt' ); done(); }) @@ -551,24 +551,24 @@ describe("Parse.File testing", () => { }); }); - it("creates correct url for old files hosted on files.parse.com", done => { + it('creates correct url for old files hosted on files.parse.com', done => { const file = { - __type: "File", - url: "http://irrelevant.elephant/", - name: "d6e80979-a128-4c57-a167-302f874700dc-123.txt", + __type: 'File', + url: 'http://irrelevant.elephant/', + name: 'd6e80979-a128-4c57-a167-302f874700dc-123.txt', }; - const obj = new Parse.Object("OldFileTest"); - obj.set("oldfile", file); + const obj = new Parse.Object('OldFileTest'); + obj.set('oldfile', file); obj .save() .then(() => { - const query = new Parse.Query("OldFileTest"); + const query = new Parse.Query('OldFileTest'); return query.first(); }) .then(result => { - const fileAgain = result.get("oldfile"); + const fileAgain = result.get('oldfile'); expect(fileAgain.url()).toEqual( - "http://files.parse.com/test/d6e80979-a128-4c57-a167-302f874700dc-123.txt" + 'http://files.parse.com/test/d6e80979-a128-4c57-a167-302f874700dc-123.txt' ); done(); }) @@ -578,21 +578,21 @@ describe("Parse.File testing", () => { }); }); - it("supports files in objects without urls", done => { + it('supports files in objects without urls', done => { const file = { - __type: "File", - name: "123.txt", + __type: 'File', + name: '123.txt', }; - const obj = new Parse.Object("FileTest"); - obj.set("file", file); + const obj = new Parse.Object('FileTest'); + obj.set('file', file); obj .save() .then(() => { - const query = new Parse.Query("FileTest"); + const query = new Parse.Query('FileTest'); return query.first(); }) .then(result => { - const fileAgain = result.get("file"); + const fileAgain = result.get('file'); expect(fileAgain.url()).toMatch(/123.txt$/); done(); }) @@ -602,26 +602,26 @@ describe("Parse.File testing", () => { }); }); - it("return with publicServerURL when provided", done => { + it('return with publicServerURL when provided', done => { reconfigureServer({ - publicServerURL: "https://mydomain/parse", + publicServerURL: 'https://mydomain/parse', }) .then(() => { const file = { - __type: "File", - name: "123.txt", + __type: 'File', + name: '123.txt', }; - const obj = new Parse.Object("FileTest"); - obj.set("file", file); + const obj = new Parse.Object('FileTest'); + obj.set('file', file); return obj.save(); }) .then(() => { - const query = new Parse.Query("FileTest"); + const query = new Parse.Query('FileTest'); return query.first(); }) .then(result => { - const fileAgain = result.get("file"); - expect(fileAgain.url().indexOf("https://mydomain/parse")).toBe(0); + const fileAgain = result.get('file'); + expect(fileAgain.url().indexOf('https://mydomain/parse')).toBe(0); done(); }) .catch(e => { @@ -630,17 +630,17 @@ describe("Parse.File testing", () => { }); }); - it("fails to upload an empty file", done => { + it('fails to upload an empty file', done => { const headers = { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/files/file.txt", - body: "", + url: 'http://localhost:8378/1/files/file.txt', + body: '', }).then(fail, response => { expect(response.status).toBe(400); const body = response.text; @@ -649,17 +649,17 @@ describe("Parse.File testing", () => { }); }); - it("fails to upload without a file name", done => { + it('fails to upload without a file name', done => { const headers = { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/files/", - body: "yolo", + url: 'http://localhost:8378/1/files/', + body: 'yolo', }).then(fail, response => { expect(response.status).toBe(400); const body = response.text; @@ -669,358 +669,358 @@ describe("Parse.File testing", () => { }); }); - describe("deleting files", () => { - it("fails to delete an unkown file", done => { + describe('deleting files', () => { + it('fails to delete an unkown file', done => { const headers = { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "X-Parse-Master-Key": "test", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Master-Key': 'test', }; request({ - method: "DELETE", + method: 'DELETE', headers: headers, - url: "http://localhost:8378/1/files/file.txt", + url: 'http://localhost:8378/1/files/file.txt', }).then(fail, response => { expect(response.status).toBe(400); const body = response.text; - expect(typeof body).toBe("string"); + expect(typeof body).toBe('string'); const { code, error } = JSON.parse(body); expect(code).toBe(153); - expect(typeof error).toBe("string"); + expect(typeof error).toBe('string'); expect(error.length).toBeGreaterThan(0); done(); }); }); }); - describe("getting files", () => { - it("does not crash on file request with invalid app ID", async () => { + describe('getting files', () => { + it('does not crash on file request with invalid app ID', async () => { const res1 = await request({ - url: "http://localhost:8378/1/files/invalid-id/invalid-file.txt", + url: 'http://localhost:8378/1/files/invalid-id/invalid-file.txt', }).catch(e => e); expect(res1.status).toBe(403); expect(res1.data).toEqual({ code: 119, - error: "Invalid application ID.", + error: 'Invalid application ID.', }); // Ensure server did not crash - const res2 = await request({ url: "http://localhost:8378/1/health" }); + const res2 = await request({ url: 'http://localhost:8378/1/health' }); expect(res2.status).toEqual(200); - expect(res2.data).toEqual({ status: "ok" }); + expect(res2.data).toEqual({ status: 'ok' }); }); - it("does not crash on file request with invalid path", async () => { + it('does not crash on file request with invalid path', async () => { const res1 = await request({ - url: "http://localhost:8378/1/files/invalid-id//invalid-path/%20/invalid-file.txt", + url: 'http://localhost:8378/1/files/invalid-id//invalid-path/%20/invalid-file.txt', }).catch(e => e); expect(res1.status).toBe(403); - expect(res1.data).toEqual({ error: "unauthorized" }); + expect(res1.data).toEqual({ error: 'unauthorized' }); // Ensure server did not crash - const res2 = await request({ url: "http://localhost:8378/1/health" }); + const res2 = await request({ url: 'http://localhost:8378/1/health' }); expect(res2.status).toEqual(200); - expect(res2.data).toEqual({ status: "ok" }); + expect(res2.data).toEqual({ status: 'ok' }); }); - it("does not crash on file metadata request with invalid app ID", async () => { + it('does not crash on file metadata request with invalid app ID', async () => { const res1 = await request({ url: `http://localhost:8378/1/files/invalid-id/metadata/invalid-file.txt`, }); expect(res1.status).toBe(200); expect(res1.data).toEqual({}); // Ensure server did not crash - const res2 = await request({ url: "http://localhost:8378/1/health" }); + const res2 = await request({ url: 'http://localhost:8378/1/health' }); expect(res2.status).toEqual(200); - expect(res2.data).toEqual({ status: "ok" }); + expect(res2.data).toEqual({ status: 'ok' }); }); }); - describe_only_db("mongo")("Gridstore Range", () => { - it("supports bytes range out of range", async () => { + describe_only_db('mongo')('Gridstore Range', () => { + it('supports bytes range out of range', async () => { const headers = { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; const response = await request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1//files/file.txt ", - body: repeat("argle bargle", 100), + url: 'http://localhost:8378/1//files/file.txt ', + body: repeat('argle bargle', 100), }); const b = response.data; const file = await request({ url: b.url, headers: { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - Range: "bytes=15000-18000", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + Range: 'bytes=15000-18000', }, }); - expect(file.headers["content-range"]).toBe("bytes 1212-1212/1212"); + expect(file.headers['content-range']).toBe('bytes 1212-1212/1212'); }); - it("supports bytes range if end greater than start", async () => { + it('supports bytes range if end greater than start', async () => { const headers = { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; const response = await request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1//files/file.txt ", - body: repeat("argle bargle", 100), + url: 'http://localhost:8378/1//files/file.txt ', + body: repeat('argle bargle', 100), }); const b = response.data; const file = await request({ url: b.url, headers: { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - Range: "bytes=15000-100", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + Range: 'bytes=15000-100', }, }); - expect(file.headers["content-range"]).toBe("bytes 100-1212/1212"); + expect(file.headers['content-range']).toBe('bytes 100-1212/1212'); }); - it("supports bytes range if end is undefined", async () => { + it('supports bytes range if end is undefined', async () => { const headers = { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; const response = await request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1//files/file.txt ", - body: repeat("argle bargle", 100), + url: 'http://localhost:8378/1//files/file.txt ', + body: repeat('argle bargle', 100), }); const b = response.data; const file = await request({ url: b.url, headers: { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - Range: "bytes=100-", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + Range: 'bytes=100-', }, }); - expect(file.headers["content-range"]).toBe("bytes 100-1212/1212"); + expect(file.headers['content-range']).toBe('bytes 100-1212/1212'); }); - it("supports bytes range if start and end undefined", async () => { + it('supports bytes range if start and end undefined', async () => { const headers = { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; const response = await request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1//files/file.txt ", - body: repeat("argle bargle", 100), + url: 'http://localhost:8378/1//files/file.txt ', + body: repeat('argle bargle', 100), }); const b = response.data; const file = await request({ url: b.url, headers: { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', }, }).catch(e => e); - expect(file.headers["content-range"]).toBeUndefined(); + expect(file.headers['content-range']).toBeUndefined(); }); - it("supports bytes range if end is greater than size", async () => { + it('supports bytes range if end is greater than size', async () => { const headers = { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; const response = await request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1//files/file.txt ", - body: repeat("argle bargle", 100), + url: 'http://localhost:8378/1//files/file.txt ', + body: repeat('argle bargle', 100), }); const b = response.data; const file = await request({ url: b.url, headers: { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - Range: "bytes=0-2000", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + Range: 'bytes=0-2000', }, }).catch(e => e); - expect(file.headers["content-range"]).toBe("bytes 0-1212/1212"); + expect(file.headers['content-range']).toBe('bytes 0-1212/1212'); }); - it("supports bytes range with 0 length", async () => { + it('supports bytes range with 0 length', async () => { const headers = { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; const response = await request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1//files/file.txt ", - body: "a", + url: 'http://localhost:8378/1//files/file.txt ', + body: 'a', }).catch(e => e); const b = response.data; const file = await request({ url: b.url, headers: { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - Range: "bytes=-2000", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + Range: 'bytes=-2000', }, }).catch(e => e); - expect(file.headers["content-range"]).toBe("bytes 0-1/1"); + expect(file.headers['content-range']).toBe('bytes 0-1/1'); }); - it("supports range requests", done => { + it('supports range requests', done => { const headers = { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/files/file.txt", - body: "argle bargle", + url: 'http://localhost:8378/1/files/file.txt', + body: 'argle bargle', }).then(response => { const b = response.data; request({ url: b.url, headers: { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - Range: "bytes=0-5", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + Range: 'bytes=0-5', }, }).then(response => { const body = response.text; - expect(body).toEqual("argle "); + expect(body).toEqual('argle '); done(); }); }); }); - it("supports small range requests", done => { + it('supports small range requests', done => { const headers = { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/files/file.txt", - body: "argle bargle", + url: 'http://localhost:8378/1/files/file.txt', + body: 'argle bargle', }).then(response => { const b = response.data; request({ url: b.url, headers: { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - Range: "bytes=0-2", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + Range: 'bytes=0-2', }, }).then(response => { const body = response.text; - expect(body).toEqual("arg"); + expect(body).toEqual('arg'); done(); }); }); }); // See specs https://www.greenbytes.de/tech/webdav/draft-ietf-httpbis-p5-range-latest.html#byte.ranges - it("supports getting one byte", done => { + it('supports getting one byte', done => { const headers = { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/files/file.txt", - body: "argle bargle", + url: 'http://localhost:8378/1/files/file.txt', + body: 'argle bargle', }).then(response => { const b = response.data; request({ url: b.url, headers: { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - Range: "bytes=2-2", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + Range: 'bytes=2-2', }, }).then(response => { const body = response.text; - expect(body).toEqual("g"); + expect(body).toEqual('g'); done(); }); }); }); - it("supports getting last n bytes", done => { + it('supports getting last n bytes', done => { const headers = { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/files/file.txt", - body: "something different", + url: 'http://localhost:8378/1/files/file.txt', + body: 'something different', }).then(response => { const b = response.data; request({ url: b.url, headers: { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - Range: "bytes=-4", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + Range: 'bytes=-4', }, }).then(response => { const body = response.text; expect(body.length).toBe(4); - expect(body).toEqual("rent"); + expect(body).toEqual('rent'); done(); }); }); }); - it("supports getting first n bytes", done => { + it('supports getting first n bytes', done => { const headers = { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/files/file.txt", - body: "something different", + url: 'http://localhost:8378/1/files/file.txt', + body: 'something different', }).then(response => { const b = response.data; request({ url: b.url, headers: { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - Range: "bytes=10-", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + Range: 'bytes=10-', }, }).then(response => { const body = response.text; - expect(body).toEqual("different"); + expect(body).toEqual('different'); done(); }); }); @@ -1035,116 +1035,116 @@ describe("Parse.File testing", () => { return s; } - it("supports large range requests", done => { + it('supports large range requests', done => { const headers = { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/files/file.txt", - body: repeat("argle bargle", 100), + url: 'http://localhost:8378/1/files/file.txt', + body: repeat('argle bargle', 100), }).then(response => { const b = response.data; request({ url: b.url, headers: { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - Range: "bytes=13-240", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + Range: 'bytes=13-240', }, }).then(response => { const body = response.text; expect(body.length).toEqual(228); - expect(body.indexOf("rgle barglea")).toBe(0); + expect(body.indexOf('rgle barglea')).toBe(0); done(); }); }); }); - it("fails to stream unknown file", async () => { + it('fails to stream unknown file', async () => { const response = await request({ - url: "http://localhost:8378/1/files/test/file.txt", + url: 'http://localhost:8378/1/files/test/file.txt', headers: { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - Range: "bytes=13-240", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + Range: 'bytes=13-240', }, }).catch(e => e); expect(response.status).toBe(404); const body = response.text; - expect(body).toEqual("File not found."); + expect(body).toEqual('File not found.'); }); }); // Because GridStore is not loaded on PG, those are perfect // for fallback tests - describe_only_db("postgres")("Default Range tests", () => { - it("fallback to regular request", async done => { + describe_only_db('postgres')('Default Range tests', () => { + it('fallback to regular request', async done => { await reconfigureServer(); const headers = { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/files/file.txt", - body: "argle bargle", + url: 'http://localhost:8378/1/files/file.txt', + body: 'argle bargle', }).then(response => { const b = response.data; request({ url: b.url, headers: { - "Content-Type": "application/octet-stream", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - Range: "bytes=0-5", + 'Content-Type': 'application/octet-stream', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + Range: 'bytes=0-5', }, }).then(response => { const body = response.text; - expect(body).toEqual("argle bargle"); + expect(body).toEqual('argle bargle'); done(); }); }); }); }); - describe("file upload configuration", () => { - it("allows file upload only for authenticated user by default", async () => { + describe('file upload configuration', () => { + it('allows file upload only for authenticated user by default', async () => { await reconfigureServer({ fileUpload: {}, }); - let file = new Parse.File("hello.txt", data, "text/plain"); + let file = new Parse.File('hello.txt', data, 'text/plain'); await expectAsync(file.save()).toBeRejectedWith( new Parse.Error( Parse.Error.FILE_SAVE_ERROR, - "File upload by public is disabled." + 'File upload by public is disabled.' ) ); - file = new Parse.File("hello.txt", data, "text/plain"); + file = new Parse.File('hello.txt', data, 'text/plain'); const anonUser = await Parse.AnonymousUtils.logIn(); await expectAsync( file.save({ sessionToken: anonUser.getSessionToken() }) ).toBeRejectedWith( new Parse.Error( Parse.Error.FILE_SAVE_ERROR, - "File upload by anonymous user is disabled." + 'File upload by anonymous user is disabled.' ) ); - file = new Parse.File("hello.txt", data, "text/plain"); - const authUser = await Parse.User.signUp("user", "password"); + file = new Parse.File('hello.txt', data, 'text/plain'); + const authUser = await Parse.User.signUp('user', 'password'); await expectAsync( file.save({ sessionToken: authUser.getSessionToken() }) ).toBeResolved(); }); - it("allows file upload with master key", async () => { + it('allows file upload with master key', async () => { await reconfigureServer({ fileUpload: { enableForPublic: false, @@ -1152,11 +1152,11 @@ describe("Parse.File testing", () => { enableForAuthenticatedUser: false, }, }); - const file = new Parse.File("hello.txt", data, "text/plain"); + const file = new Parse.File('hello.txt', data, 'text/plain'); await expectAsync(file.save({ useMasterKey: true })).toBeResolved(); }); - it("rejects all file uploads", async () => { + it('rejects all file uploads', async () => { await reconfigureServer({ fileUpload: { enableForPublic: false, @@ -1164,36 +1164,36 @@ describe("Parse.File testing", () => { enableForAuthenticatedUser: false, }, }); - let file = new Parse.File("hello.txt", data, "text/plain"); + let file = new Parse.File('hello.txt', data, 'text/plain'); await expectAsync(file.save()).toBeRejectedWith( new Parse.Error( Parse.Error.FILE_SAVE_ERROR, - "File upload by public is disabled." + 'File upload by public is disabled.' ) ); - file = new Parse.File("hello.txt", data, "text/plain"); + file = new Parse.File('hello.txt', data, 'text/plain'); const anonUser = await Parse.AnonymousUtils.logIn(); await expectAsync( file.save({ sessionToken: anonUser.getSessionToken() }) ).toBeRejectedWith( new Parse.Error( Parse.Error.FILE_SAVE_ERROR, - "File upload by anonymous user is disabled." + 'File upload by anonymous user is disabled.' ) ); - file = new Parse.File("hello.txt", data, "text/plain"); - const authUser = await Parse.User.signUp("user", "password"); + file = new Parse.File('hello.txt', data, 'text/plain'); + const authUser = await Parse.User.signUp('user', 'password'); await expectAsync( file.save({ sessionToken: authUser.getSessionToken() }) ).toBeRejectedWith( new Parse.Error( Parse.Error.FILE_SAVE_ERROR, - "File upload by authenticated user is disabled." + 'File upload by authenticated user is disabled.' ) ); }); - it("allows all file uploads", async () => { + it('allows all file uploads', async () => { await reconfigureServer({ fileUpload: { enableForPublic: true, @@ -1201,21 +1201,21 @@ describe("Parse.File testing", () => { enableForAuthenticatedUser: true, }, }); - let file = new Parse.File("hello.txt", data, "text/plain"); + let file = new Parse.File('hello.txt', data, 'text/plain'); await expectAsync(file.save()).toBeResolved(); - file = new Parse.File("hello.txt", data, "text/plain"); + file = new Parse.File('hello.txt', data, 'text/plain'); const anonUser = await Parse.AnonymousUtils.logIn(); await expectAsync( file.save({ sessionToken: anonUser.getSessionToken() }) ).toBeResolved(); - file = new Parse.File("hello.txt", data, "text/plain"); - const authUser = await Parse.User.signUp("user", "password"); + file = new Parse.File('hello.txt', data, 'text/plain'); + const authUser = await Parse.User.signUp('user', 'password'); await expectAsync( file.save({ sessionToken: authUser.getSessionToken() }) ).toBeResolved(); }); - it("allows file upload only for public", async () => { + it('allows file upload only for public', async () => { await reconfigureServer({ fileUpload: { enableForPublic: true, @@ -1223,31 +1223,31 @@ describe("Parse.File testing", () => { enableForAuthenticatedUser: false, }, }); - let file = new Parse.File("hello.txt", data, "text/plain"); + let file = new Parse.File('hello.txt', data, 'text/plain'); await expectAsync(file.save()).toBeResolved(); - file = new Parse.File("hello.txt", data, "text/plain"); + file = new Parse.File('hello.txt', data, 'text/plain'); const anonUser = await Parse.AnonymousUtils.logIn(); await expectAsync( file.save({ sessionToken: anonUser.getSessionToken() }) ).toBeRejectedWith( new Parse.Error( Parse.Error.FILE_SAVE_ERROR, - "File upload by anonymous user is disabled." + 'File upload by anonymous user is disabled.' ) ); - file = new Parse.File("hello.txt", data, "text/plain"); - const authUser = await Parse.User.signUp("user", "password"); + file = new Parse.File('hello.txt', data, 'text/plain'); + const authUser = await Parse.User.signUp('user', 'password'); await expectAsync( file.save({ sessionToken: authUser.getSessionToken() }) ).toBeRejectedWith( new Parse.Error( Parse.Error.FILE_SAVE_ERROR, - "File upload by authenticated user is disabled." + 'File upload by authenticated user is disabled.' ) ); }); - it("allows file upload only for anonymous user", async () => { + it('allows file upload only for anonymous user', async () => { await reconfigureServer({ fileUpload: { enableForPublic: false, @@ -1255,31 +1255,31 @@ describe("Parse.File testing", () => { enableForAuthenticatedUser: false, }, }); - let file = new Parse.File("hello.txt", data, "text/plain"); + let file = new Parse.File('hello.txt', data, 'text/plain'); await expectAsync(file.save()).toBeRejectedWith( new Parse.Error( Parse.Error.FILE_SAVE_ERROR, - "File upload by public is disabled." + 'File upload by public is disabled.' ) ); - file = new Parse.File("hello.txt", data, "text/plain"); + file = new Parse.File('hello.txt', data, 'text/plain'); const anonUser = await Parse.AnonymousUtils.logIn(); await expectAsync( file.save({ sessionToken: anonUser.getSessionToken() }) ).toBeResolved(); - file = new Parse.File("hello.txt", data, "text/plain"); - const authUser = await Parse.User.signUp("user", "password"); + file = new Parse.File('hello.txt', data, 'text/plain'); + const authUser = await Parse.User.signUp('user', 'password'); await expectAsync( file.save({ sessionToken: authUser.getSessionToken() }) ).toBeRejectedWith( new Parse.Error( Parse.Error.FILE_SAVE_ERROR, - "File upload by authenticated user is disabled." + 'File upload by authenticated user is disabled.' ) ); }); - it("allows file upload only for authenticated user", async () => { + it('allows file upload only for authenticated user', async () => { await reconfigureServer({ fileUpload: { enableForPublic: false, @@ -1287,49 +1287,49 @@ describe("Parse.File testing", () => { enableForAuthenticatedUser: true, }, }); - let file = new Parse.File("hello.txt", data, "text/plain"); + let file = new Parse.File('hello.txt', data, 'text/plain'); await expectAsync(file.save()).toBeRejectedWith( new Parse.Error( Parse.Error.FILE_SAVE_ERROR, - "File upload by public is disabled." + 'File upload by public is disabled.' ) ); - file = new Parse.File("hello.txt", data, "text/plain"); + file = new Parse.File('hello.txt', data, 'text/plain'); const anonUser = await Parse.AnonymousUtils.logIn(); await expectAsync( file.save({ sessionToken: anonUser.getSessionToken() }) ).toBeRejectedWith( new Parse.Error( Parse.Error.FILE_SAVE_ERROR, - "File upload by anonymous user is disabled." + 'File upload by anonymous user is disabled.' ) ); - file = new Parse.File("hello.txt", data, "text/plain"); - const authUser = await Parse.User.signUp("user", "password"); + file = new Parse.File('hello.txt', data, 'text/plain'); + const authUser = await Parse.User.signUp('user', 'password'); await expectAsync( file.save({ sessionToken: authUser.getSessionToken() }) ).toBeResolved(); }); - it("rejects invalid fileUpload configuration", async () => { + it('rejects invalid fileUpload configuration', async () => { const invalidConfigs = [ { fileUpload: undefined }, { fileUpload: null }, { fileUpload: [] }, { fileUpload: 1 }, - { fileUpload: "string" }, + { fileUpload: 'string' }, ]; const validConfigs = [{ fileUpload: {} }]; const keys = [ - "enableForPublic", - "enableForAnonymousUser", - "enableForAuthenticatedUser", + 'enableForPublic', + 'enableForAnonymousUser', + 'enableForAuthenticatedUser', ]; - const invalidValues = [[], {}, 1, "string", null]; + const invalidValues = [[], {}, 1, 'string', null]; const validValues = [undefined, true, false]; for (const config of invalidConfigs) { await expectAsync(reconfigureServer(config)).toBeRejectedWith( - "fileUpload must be an object value." + 'fileUpload must be an object value.' ); } for (const config of validConfigs) { @@ -1353,27 +1353,27 @@ describe("Parse.File testing", () => { fileExtensions: 1, }, }) - ).toBeRejectedWith("fileUpload.fileExtensions must be an array."); + ).toBeRejectedWith('fileUpload.fileExtensions must be an array.'); }); }); - describe("fileExtensions", () => { - it("works with _ContentType", async () => { + describe('fileExtensions', () => { + it('works with _ContentType', async () => { await reconfigureServer({ fileUpload: { enableForPublic: true, - fileExtensions: ["png"], + fileExtensions: ['png'], }, }); await expectAsync( request({ - method: "POST", - url: "http://localhost:8378/1/files/file", + method: 'POST', + url: 'http://localhost:8378/1/files/file', body: JSON.stringify({ - _ApplicationId: "test", - _JavaScriptKey: "test", - _ContentType: "text/html", - base64: "PGh0bWw+PC9odG1sPgo=", + _ApplicationId: 'test', + _JavaScriptKey: 'test', + _ContentType: 'text/html', + base64: 'PGh0bWw+PC9odG1sPgo=', }), }).catch(e => { throw new Error(e.data.error); @@ -1386,22 +1386,22 @@ describe("Parse.File testing", () => { ); }); - it("works without Content-Type", async () => { + it('works without Content-Type', async () => { await reconfigureServer({ fileUpload: { enableForPublic: true, }, }); const headers = { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; await expectAsync( request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/files/file.html", - body: "\n", + url: 'http://localhost:8378/1/files/file.html', + body: '\n', }).catch(e => { throw new Error(e.data.error); }) @@ -1413,45 +1413,45 @@ describe("Parse.File testing", () => { ); }); - it("default should allow common types", async () => { + it('default should allow common types', async () => { await reconfigureServer({ fileUpload: { enableForPublic: true, }, }); - for (const type of ["plain", "txt", "png", "jpg", "gif", "doc"]) { + for (const type of ['plain', 'txt', 'png', 'jpg', 'gif', 'doc']) { const file = new Parse.File(`parse-server-logo.${type}`, { - base64: "ParseA==", + base64: 'ParseA==', }); await file.save(); } }); - it("works with a period in the file name", async () => { + it('works with a period in the file name', async () => { await reconfigureServer({ fileUpload: { enableForPublic: true, - fileExtensions: ["^[^hH][^tT][^mM][^lL]?$"], + fileExtensions: ['^[^hH][^tT][^mM][^lL]?$'], }, }); const headers = { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; const values = [ - "file.png.html", - "file.txt.png.html", - "file.png.txt.html", + 'file.png.html', + 'file.txt.png.html', + 'file.png.txt.html', ]; for (const value of values) { await expectAsync( request({ - method: "POST", + method: 'POST', headers: headers, url: `http://localhost:8378/1/files/${value}`, - body: "\n", + body: '\n', }).catch(e => { throw new Error(e.data.error); }) @@ -1464,35 +1464,35 @@ describe("Parse.File testing", () => { } }); - it("works to stop invalid filenames", async () => { + it('works to stop invalid filenames', async () => { await reconfigureServer({ fileUpload: { enableForPublic: true, - fileExtensions: ["^[^hH][^tT][^mM][^lL]?$"], + fileExtensions: ['^[^hH][^tT][^mM][^lL]?$'], }, }); const headers = { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; const values = [ - "!invalid.png", - ".png", - ".html", - " .html", - ".png.html", - "~invalid.png", - "-invalid.png", + '!invalid.png', + '.png', + '.html', + ' .html', + '.png.html', + '~invalid.png', + '-invalid.png', ]; for (const value of values) { await expectAsync( request({ - method: "POST", + method: 'POST', headers: headers, url: `http://localhost:8378/1/files/${value}`, - body: "\n", + body: '\n', }).catch(e => { throw new Error(e.data.error); }) @@ -1505,27 +1505,27 @@ describe("Parse.File testing", () => { } }); - it("allows file without extension", async () => { + it('allows file without extension', async () => { await reconfigureServer({ fileUpload: { enableForPublic: true, - fileExtensions: ["^[^hH][^tT][^mM][^lL]?$"], + fileExtensions: ['^[^hH][^tT][^mM][^lL]?$'], }, }); const headers = { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; - const values = ["filenamewithoutextension"]; + const values = ['filenamewithoutextension']; for (const value of values) { await expectAsync( request({ - method: "POST", + method: 'POST', headers: headers, url: `http://localhost:8378/1/files/${value}`, - body: "\n", + body: '\n', }).catch(e => { throw new Error(e.data.error); }) @@ -1533,22 +1533,22 @@ describe("Parse.File testing", () => { } }); - it("works with array", async () => { + it('works with array', async () => { await reconfigureServer({ fileUpload: { enableForPublic: true, - fileExtensions: ["jpg", "wav"], + fileExtensions: ['jpg', 'wav'], }, }); await expectAsync( request({ - method: "POST", - url: "http://localhost:8378/1/files/file", + method: 'POST', + url: 'http://localhost:8378/1/files/file', body: JSON.stringify({ - _ApplicationId: "test", - _JavaScriptKey: "test", - _ContentType: "text/html", - base64: "PGh0bWw+PC9odG1sPgo=", + _ApplicationId: 'test', + _JavaScriptKey: 'test', + _ContentType: 'text/html', + base64: 'PGh0bWw+PC9odG1sPgo=', }), }).catch(e => { throw new Error(e.data.error); @@ -1561,48 +1561,48 @@ describe("Parse.File testing", () => { ); await expectAsync( request({ - method: "POST", - url: "http://localhost:8378/1/files/file", + method: 'POST', + url: 'http://localhost:8378/1/files/file', body: JSON.stringify({ - _ApplicationId: "test", - _JavaScriptKey: "test", - _ContentType: "image/jpg", - base64: "PGh0bWw+PC9odG1sPgo=", + _ApplicationId: 'test', + _JavaScriptKey: 'test', + _ContentType: 'image/jpg', + base64: 'PGh0bWw+PC9odG1sPgo=', }), }) ).toBeResolved(); await expectAsync( request({ - method: "POST", - url: "http://localhost:8378/1/files/file", + method: 'POST', + url: 'http://localhost:8378/1/files/file', body: JSON.stringify({ - _ApplicationId: "test", - _JavaScriptKey: "test", - _ContentType: "audio/wav", + _ApplicationId: 'test', + _JavaScriptKey: 'test', + _ContentType: 'audio/wav', base64: - "UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA", + 'UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA', }), }) ).toBeResolved(); }); - it("works with array without Content-Type", async () => { + it('works with array without Content-Type', async () => { await reconfigureServer({ fileUpload: { enableForPublic: true, - fileExtensions: ["jpg"], + fileExtensions: ['jpg'], }, }); const headers = { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; await expectAsync( request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/files/file.html", - body: "\n", + url: 'http://localhost:8378/1/files/file.html', + body: '\n', }).catch(e => { throw new Error(e.data.error); }) @@ -1614,21 +1614,21 @@ describe("Parse.File testing", () => { ); }); - it("works with array with correct file type", async () => { + it('works with array with correct file type', async () => { await reconfigureServer({ fileUpload: { enableForPublic: true, - fileExtensions: ["html"], + fileExtensions: ['html'], }, }); const response = await request({ - method: "POST", - url: "http://localhost:8378/1/files/file", + method: 'POST', + url: 'http://localhost:8378/1/files/file', body: JSON.stringify({ - _ApplicationId: "test", - _JavaScriptKey: "test", - _ContentType: "text/html", - base64: "PGh0bWw+PC9odG1sPgo=", + _ApplicationId: 'test', + _JavaScriptKey: 'test', + _ContentType: 'text/html', + base64: 'PGh0bWw+PC9odG1sPgo=', }), }); const b = response.data; diff --git a/spec/ParseGeoPoint.spec.js b/spec/ParseGeoPoint.spec.js index 2ca318dbc2..e20a6cc4c1 100644 --- a/spec/ParseGeoPoint.spec.js +++ b/spec/ParseGeoPoint.spec.js @@ -1,32 +1,32 @@ // This is a port of the test suite: // hungry/js/test/parse_geo_point_test.js -const request = require("../lib/request"); -const TestObject = Parse.Object.extend("TestObject"); +const request = require('../lib/request'); +const TestObject = Parse.Object.extend('TestObject'); -describe("Parse.GeoPoint testing", () => { - it("geo point roundtrip", async () => { +describe('Parse.GeoPoint testing', () => { + it('geo point roundtrip', async () => { const point = new Parse.GeoPoint(44.0, -11.0); const obj = new TestObject(); - obj.set("location", point); - obj.set("name", "Ferndale"); + obj.set('location', point); + obj.set('name', 'Ferndale'); await obj.save(); const result = await new Parse.Query(TestObject).get(obj.id); - const pointAgain = result.get("location"); + const pointAgain = result.get('location'); ok(pointAgain); equal(pointAgain.latitude, 44.0); equal(pointAgain.longitude, -11.0); }); - it("update geopoint", done => { + it('update geopoint', done => { const oldPoint = new Parse.GeoPoint(44.0, -11.0); const newPoint = new Parse.GeoPoint(24.0, 19.0); const obj = new TestObject(); - obj.set("location", oldPoint); + obj.set('location', oldPoint); obj .save() .then(() => { - obj.set("location", newPoint); + obj.set('location', newPoint); return obj.save(); }) .then(() => { @@ -34,39 +34,39 @@ describe("Parse.GeoPoint testing", () => { return query.get(obj.id); }) .then(result => { - const point = result.get("location"); + const point = result.get('location'); equal(point.latitude, newPoint.latitude); equal(point.longitude, newPoint.longitude); done(); }); }); - it("has the correct __type field in the json response", async done => { + it('has the correct __type field in the json response', async done => { const point = new Parse.GeoPoint(44.0, -11.0); const obj = new TestObject(); - obj.set("location", point); - obj.set("name", "Zhoul"); + obj.set('location', point); + obj.set('name', 'Zhoul'); await obj.save(); request({ - url: "http://localhost:8378/1/classes/TestObject/" + obj.id, + url: 'http://localhost:8378/1/classes/TestObject/' + obj.id, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Master-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Master-Key': 'test', }, }).then(response => { - equal(response.data.location.__type, "GeoPoint"); + equal(response.data.location.__type, 'GeoPoint'); done(); }); }); - it("creating geo point exception two fields", done => { + it('creating geo point exception two fields', done => { const point = new Parse.GeoPoint(20, 20); const obj = new TestObject(); - obj.set("locationOne", point); - obj.set("locationTwo", point); + obj.set('locationOne', point); + obj.set('locationTwo', point); obj.save().then( () => { - fail("expected error"); + fail('expected error'); }, err => { equal(err.code, Parse.Error.INCORRECT_TYPE); @@ -76,17 +76,17 @@ describe("Parse.GeoPoint testing", () => { }); // TODO: This should also have support in postgres, or higher level database agnostic support. - it_exclude_dbs(["postgres"])( - "updating geo point exception two fields", + it_exclude_dbs(['postgres'])( + 'updating geo point exception two fields', async done => { const point = new Parse.GeoPoint(20, 20); const obj = new TestObject(); - obj.set("locationOne", point); + obj.set('locationOne', point); await obj.save(); - obj.set("locationTwo", point); + obj.set('locationTwo', point); obj.save().then( () => { - fail("expected error"); + fail('expected error'); }, err => { equal(err.code, Parse.Error.INCORRECT_TYPE); @@ -96,42 +96,42 @@ describe("Parse.GeoPoint testing", () => { } ); - it_id("bbd9e2f6-7f61-458f-98f2-4a563586cd8d")(it)("geo line", async done => { + it_id('bbd9e2f6-7f61-458f-98f2-4a563586cd8d')(it)('geo line', async done => { const line = []; for (let i = 0; i < 10; ++i) { const obj = new TestObject(); const point = new Parse.GeoPoint(i * 4.0 - 12.0, i * 3.2 - 11.0); - obj.set("location", point); - obj.set("construct", "line"); - obj.set("seq", i); + obj.set('location', point); + obj.set('construct', 'line'); + obj.set('seq', i); line.push(obj); } await Parse.Object.saveAll(line); const query = new Parse.Query(TestObject); const point = new Parse.GeoPoint(24, 19); - query.equalTo("construct", "line"); - query.withinMiles("location", point, 10000); + query.equalTo('construct', 'line'); + query.withinMiles('location', point, 10000); const results = await query.find(); equal(results.length, 10); - equal(results[0].get("seq"), 9); - equal(results[3].get("seq"), 6); + equal(results[0].get('seq'), 9); + equal(results[3].get('seq'), 6); done(); }); - it("geo max distance large", done => { + it('geo max distance large', done => { const objects = []; [0, 1, 2].map(function (i) { const obj = new TestObject(); const point = new Parse.GeoPoint(0.0, i * 45.0); - obj.set("location", point); - obj.set("index", i); + obj.set('location', point); + obj.set('index', i); objects.push(obj); }); Parse.Object.saveAll(objects) .then(() => { const query = new Parse.Query(TestObject); const point = new Parse.GeoPoint(1.0, -1.0); - query.withinRadians("location", point, 3.14); + query.withinRadians('location', point, 3.14); return query.find(); }) .then( @@ -146,176 +146,176 @@ describe("Parse.GeoPoint testing", () => { ); }); - it_id("e1e86b38-b8a4-4109-8330-a324fe628e0c")(it)( - "geo max distance medium", + it_id('e1e86b38-b8a4-4109-8330-a324fe628e0c')(it)( + 'geo max distance medium', async () => { const objects = []; [0, 1, 2].map(function (i) { const obj = new TestObject(); const point = new Parse.GeoPoint(0.0, i * 45.0); - obj.set("location", point); - obj.set("index", i); + obj.set('location', point); + obj.set('index', i); objects.push(obj); }); await Parse.Object.saveAll(objects); const query = new Parse.Query(TestObject); const point = new Parse.GeoPoint(1.0, -1.0); - query.withinRadians("location", point, 3.14 * 0.5); + query.withinRadians('location', point, 3.14 * 0.5); const results = await query.find(); equal(results.length, 2); - equal(results[0].get("index"), 0); - equal(results[1].get("index"), 1); + equal(results[0].get('index'), 0); + equal(results[1].get('index'), 1); } ); - it("geo max distance small", async () => { + it('geo max distance small', async () => { const objects = []; [0, 1, 2].map(function (i) { const obj = new TestObject(); const point = new Parse.GeoPoint(0.0, i * 45.0); - obj.set("location", point); - obj.set("index", i); + obj.set('location', point); + obj.set('index', i); objects.push(obj); }); await Parse.Object.saveAll(objects); const query = new Parse.Query(TestObject); const point = new Parse.GeoPoint(1.0, -1.0); - query.withinRadians("location", point, 3.14 * 0.25); + query.withinRadians('location', point, 3.14 * 0.25); const results = await query.find(); equal(results.length, 1); - equal(results[0].get("index"), 0); + equal(results[0].get('index'), 0); }); const makeSomeGeoPoints = function () { const sacramento = new TestObject(); - sacramento.set("location", new Parse.GeoPoint(38.52, -121.5)); - sacramento.set("name", "Sacramento"); + sacramento.set('location', new Parse.GeoPoint(38.52, -121.5)); + sacramento.set('name', 'Sacramento'); const honolulu = new TestObject(); - honolulu.set("location", new Parse.GeoPoint(21.35, -157.93)); - honolulu.set("name", "Honolulu"); + honolulu.set('location', new Parse.GeoPoint(21.35, -157.93)); + honolulu.set('name', 'Honolulu'); const sf = new TestObject(); - sf.set("location", new Parse.GeoPoint(37.75, -122.68)); - sf.set("name", "San Francisco"); + sf.set('location', new Parse.GeoPoint(37.75, -122.68)); + sf.set('name', 'San Francisco'); return Parse.Object.saveAll([sacramento, sf, honolulu]); }; - it("geo max distance in km everywhere", async done => { + it('geo max distance in km everywhere', async done => { await makeSomeGeoPoints(); const sfo = new Parse.GeoPoint(37.6189722, -122.3748889); const query = new Parse.Query(TestObject); // Honolulu is 4300 km away from SFO on a sphere ;) - query.withinKilometers("location", sfo, 4800.0); + query.withinKilometers('location', sfo, 4800.0); const results = await query.find(); equal(results.length, 3); done(); }); - it_id("05f1a454-56b1-4f2e-908e-408a9222cbae")(it)( - "geo max distance in km california", + it_id('05f1a454-56b1-4f2e-908e-408a9222cbae')(it)( + 'geo max distance in km california', async () => { await makeSomeGeoPoints(); const sfo = new Parse.GeoPoint(37.6189722, -122.3748889); const query = new Parse.Query(TestObject); - query.withinKilometers("location", sfo, 3700.0); + query.withinKilometers('location', sfo, 3700.0); const results = await query.find(); equal(results.length, 2); - equal(results[0].get("name"), "San Francisco"); - equal(results[1].get("name"), "Sacramento"); + equal(results[0].get('name'), 'San Francisco'); + equal(results[1].get('name'), 'Sacramento'); } ); - it("geo max distance in km bay area", async () => { + it('geo max distance in km bay area', async () => { await makeSomeGeoPoints(); const sfo = new Parse.GeoPoint(37.6189722, -122.3748889); const query = new Parse.Query(TestObject); - query.withinKilometers("location", sfo, 100.0); + query.withinKilometers('location', sfo, 100.0); const results = await query.find(); equal(results.length, 1); - equal(results[0].get("name"), "San Francisco"); + equal(results[0].get('name'), 'San Francisco'); }); - it("geo max distance in km mid peninsula", async () => { + it('geo max distance in km mid peninsula', async () => { await makeSomeGeoPoints(); const sfo = new Parse.GeoPoint(37.6189722, -122.3748889); const query = new Parse.Query(TestObject); - query.withinKilometers("location", sfo, 10.0); + query.withinKilometers('location', sfo, 10.0); const results = await query.find(); equal(results.length, 0); }); - it("geo max distance in miles everywhere", async () => { + it('geo max distance in miles everywhere', async () => { await makeSomeGeoPoints(); const sfo = new Parse.GeoPoint(37.6189722, -122.3748889); const query = new Parse.Query(TestObject); - query.withinMiles("location", sfo, 2600.0); + query.withinMiles('location', sfo, 2600.0); const results = await query.find(); equal(results.length, 3); }); - it_id("9ee376ad-dd6c-4c17-ad28-c7899a4411f1")(it)( - "geo max distance in miles california", + it_id('9ee376ad-dd6c-4c17-ad28-c7899a4411f1')(it)( + 'geo max distance in miles california', async () => { await makeSomeGeoPoints(); const sfo = new Parse.GeoPoint(37.6189722, -122.3748889); const query = new Parse.Query(TestObject); - query.withinMiles("location", sfo, 2200.0); + query.withinMiles('location', sfo, 2200.0); const results = await query.find(); equal(results.length, 2); - equal(results[0].get("name"), "San Francisco"); - equal(results[1].get("name"), "Sacramento"); + equal(results[0].get('name'), 'San Francisco'); + equal(results[1].get('name'), 'Sacramento'); } ); - it("geo max distance in miles bay area", async () => { + it('geo max distance in miles bay area', async () => { await makeSomeGeoPoints(); const sfo = new Parse.GeoPoint(37.6189722, -122.3748889); const query = new Parse.Query(TestObject); - query.withinMiles("location", sfo, 62.0); + query.withinMiles('location', sfo, 62.0); const results = await query.find(); equal(results.length, 1); - equal(results[0].get("name"), "San Francisco"); + equal(results[0].get('name'), 'San Francisco'); }); - it("geo max distance in miles mid peninsula", async () => { + it('geo max distance in miles mid peninsula', async () => { await makeSomeGeoPoints(); const sfo = new Parse.GeoPoint(37.6189722, -122.3748889); const query = new Parse.Query(TestObject); - query.withinMiles("location", sfo, 10.0); + query.withinMiles('location', sfo, 10.0); const results = await query.find(); equal(results.length, 0); }); - it_id("9e35a89e-bc2c-4ec5-b25a-8d1890a55233")(it)( - "returns nearest location", + it_id('9e35a89e-bc2c-4ec5-b25a-8d1890a55233')(it)( + 'returns nearest location', async () => { await makeSomeGeoPoints(); const sfo = new Parse.GeoPoint(37.6189722, -122.3748889); const query = new Parse.Query(TestObject); - query.near("location", sfo); + query.near('location', sfo); const results = await query.find(); - equal(results[0].get("name"), "San Francisco"); - equal(results[1].get("name"), "Sacramento"); + equal(results[0].get('name'), 'San Francisco'); + equal(results[1].get('name'), 'Sacramento'); } ); - it_id("6df434b0-142d-4302-bbc6-a6ec5a9d9c68")(it)( - "works with geobox queries", + it_id('6df434b0-142d-4302-bbc6-a6ec5a9d9c68')(it)( + 'works with geobox queries', done => { const inbound = new Parse.GeoPoint(1.5, 1.5); const onbound = new Parse.GeoPoint(10, 10); const outbound = new Parse.GeoPoint(20, 20); - const obj1 = new Parse.Object("TestObject", { location: inbound }); - const obj2 = new Parse.Object("TestObject", { location: onbound }); - const obj3 = new Parse.Object("TestObject", { location: outbound }); + const obj1 = new Parse.Object('TestObject', { location: inbound }); + const obj2 = new Parse.Object('TestObject', { location: onbound }); + const obj3 = new Parse.Object('TestObject', { location: outbound }); Parse.Object.saveAll([obj1, obj2, obj3]) .then(() => { const sw = new Parse.GeoPoint(0, 0); const ne = new Parse.GeoPoint(10, 10); const query = new Parse.Query(TestObject); - query.withinGeoBox("location", sw, ne); + query.withinGeoBox('location', sw, ne); return query.find(); }) .then(results => { @@ -325,86 +325,86 @@ describe("Parse.GeoPoint testing", () => { } ); - it("supports a sub-object with a geo point", async () => { + it('supports a sub-object with a geo point', async () => { const point = new Parse.GeoPoint(44.0, -11.0); const obj = new TestObject(); - obj.set("subobject", { location: point }); + obj.set('subobject', { location: point }); await obj.save(); const query = new Parse.Query(TestObject); const results = await query.find(); equal(results.length, 1); - const pointAgain = results[0].get("subobject")["location"]; + const pointAgain = results[0].get('subobject')['location']; ok(pointAgain); equal(pointAgain.latitude, 44.0); equal(pointAgain.longitude, -11.0); }); - it("supports array of geo points", async () => { + it('supports array of geo points', async () => { const point1 = new Parse.GeoPoint(44.0, -11.0); const point2 = new Parse.GeoPoint(22.0, -55.0); const obj = new TestObject(); - obj.set("locations", [point1, point2]); + obj.set('locations', [point1, point2]); await obj.save(); const query = new Parse.Query(TestObject); const results = await query.find(); equal(results.length, 1); - const locations = results[0].get("locations"); + const locations = results[0].get('locations'); expect(locations.length).toEqual(2); expect(locations[0]).toEqual(point1); expect(locations[1]).toEqual(point2); }); - it("equalTo geopoint", done => { + it('equalTo geopoint', done => { const point = new Parse.GeoPoint(44.0, -11.0); const obj = new TestObject(); - obj.set("location", point); + obj.set('location', point); obj .save() .then(() => { const query = new Parse.Query(TestObject); - query.equalTo("location", point); + query.equalTo('location', point); return query.find(); }) .then(results => { equal(results.length, 1); - const loc = results[0].get("location"); + const loc = results[0].get('location'); equal(loc.latitude, point.latitude); equal(loc.longitude, point.longitude); done(); }); }); - it_id("d9fbc5c6-f767-47d6-bb44-3858eb9df15a")(it)( - "supports withinPolygon open path", + it_id('d9fbc5c6-f767-47d6-bb44-3858eb9df15a')(it)( + 'supports withinPolygon open path', done => { const inbound = new Parse.GeoPoint(1.5, 1.5); const onbound = new Parse.GeoPoint(10, 10); const outbound = new Parse.GeoPoint(20, 20); - const obj1 = new Parse.Object("Polygon", { location: inbound }); - const obj2 = new Parse.Object("Polygon", { location: onbound }); - const obj3 = new Parse.Object("Polygon", { location: outbound }); + const obj1 = new Parse.Object('Polygon', { location: inbound }); + const obj2 = new Parse.Object('Polygon', { location: onbound }); + const obj3 = new Parse.Object('Polygon', { location: outbound }); Parse.Object.saveAll([obj1, obj2, obj3]) .then(() => { const where = { location: { $geoWithin: { $polygon: [ - { __type: "GeoPoint", latitude: 0, longitude: 0 }, - { __type: "GeoPoint", latitude: 0, longitude: 10 }, - { __type: "GeoPoint", latitude: 10, longitude: 10 }, - { __type: "GeoPoint", latitude: 10, longitude: 0 }, + { __type: 'GeoPoint', latitude: 0, longitude: 0 }, + { __type: 'GeoPoint', latitude: 0, longitude: 10 }, + { __type: 'GeoPoint', latitude: 10, longitude: 10 }, + { __type: 'GeoPoint', latitude: 10, longitude: 0 }, ], }, }, }; return request({ - method: "POST", - url: Parse.serverURL + "/classes/Polygon", - body: { where, _method: "GET" }, + method: 'POST', + url: Parse.serverURL + '/classes/Polygon', + body: { where, _method: 'GET' }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Javascript-Key": Parse.javaScriptKey, - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, + 'Content-Type': 'application/json', }, }); }) @@ -415,38 +415,38 @@ describe("Parse.GeoPoint testing", () => { } ); - it_id("3ec537bd-839a-4c93-a48b-b4a249820074")(it)( - "supports withinPolygon closed path", + it_id('3ec537bd-839a-4c93-a48b-b4a249820074')(it)( + 'supports withinPolygon closed path', done => { const inbound = new Parse.GeoPoint(1.5, 1.5); const onbound = new Parse.GeoPoint(10, 10); const outbound = new Parse.GeoPoint(20, 20); - const obj1 = new Parse.Object("Polygon", { location: inbound }); - const obj2 = new Parse.Object("Polygon", { location: onbound }); - const obj3 = new Parse.Object("Polygon", { location: outbound }); + const obj1 = new Parse.Object('Polygon', { location: inbound }); + const obj2 = new Parse.Object('Polygon', { location: onbound }); + const obj3 = new Parse.Object('Polygon', { location: outbound }); Parse.Object.saveAll([obj1, obj2, obj3]) .then(() => { const where = { location: { $geoWithin: { $polygon: [ - { __type: "GeoPoint", latitude: 0, longitude: 0 }, - { __type: "GeoPoint", latitude: 0, longitude: 10 }, - { __type: "GeoPoint", latitude: 10, longitude: 10 }, - { __type: "GeoPoint", latitude: 10, longitude: 0 }, - { __type: "GeoPoint", latitude: 0, longitude: 0 }, + { __type: 'GeoPoint', latitude: 0, longitude: 0 }, + { __type: 'GeoPoint', latitude: 0, longitude: 10 }, + { __type: 'GeoPoint', latitude: 10, longitude: 10 }, + { __type: 'GeoPoint', latitude: 10, longitude: 0 }, + { __type: 'GeoPoint', latitude: 0, longitude: 0 }, ], }, }, }; return request({ - method: "POST", - url: Parse.serverURL + "/classes/Polygon", - body: { where, _method: "GET" }, + method: 'POST', + url: Parse.serverURL + '/classes/Polygon', + body: { where, _method: 'GET' }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Javascript-Key": Parse.javaScriptKey, - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, + 'Content-Type': 'application/json', }, }); }) @@ -457,17 +457,17 @@ describe("Parse.GeoPoint testing", () => { } ); - it_id("0a248e11-3598-480a-9ab5-8a0b259258e4")(it)( - "supports withinPolygon Polygon object", + it_id('0a248e11-3598-480a-9ab5-8a0b259258e4')(it)( + 'supports withinPolygon Polygon object', done => { const inbound = new Parse.GeoPoint(1.5, 1.5); const onbound = new Parse.GeoPoint(10, 10); const outbound = new Parse.GeoPoint(20, 20); - const obj1 = new Parse.Object("Polygon", { location: inbound }); - const obj2 = new Parse.Object("Polygon", { location: onbound }); - const obj3 = new Parse.Object("Polygon", { location: outbound }); + const obj1 = new Parse.Object('Polygon', { location: inbound }); + const obj2 = new Parse.Object('Polygon', { location: onbound }); + const obj3 = new Parse.Object('Polygon', { location: outbound }); const polygon = { - __type: "Polygon", + __type: 'Polygon', coordinates: [ [0, 0], [10, 0], @@ -486,13 +486,13 @@ describe("Parse.GeoPoint testing", () => { }, }; return request({ - method: "POST", - url: Parse.serverURL + "/classes/Polygon", - body: { where, _method: "GET" }, + method: 'POST', + url: Parse.serverURL + '/classes/Polygon', + body: { where, _method: 'GET' }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Javascript-Key": Parse.javaScriptKey, - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, + 'Content-Type': 'application/json', }, }); }) @@ -503,11 +503,11 @@ describe("Parse.GeoPoint testing", () => { } ); - it("invalid Polygon object withinPolygon", done => { + it('invalid Polygon object withinPolygon', done => { const point = new Parse.GeoPoint(1.5, 1.5); - const obj = new Parse.Object("Polygon", { location: point }); + const obj = new Parse.Object('Polygon', { location: point }); const polygon = { - __type: "Polygon", + __type: 'Polygon', coordinates: [ [0, 0], [10, 0], @@ -524,13 +524,13 @@ describe("Parse.GeoPoint testing", () => { }, }; return request({ - method: "POST", - url: Parse.serverURL + "/classes/Polygon", - body: { where, _method: "GET" }, + method: 'POST', + url: Parse.serverURL + '/classes/Polygon', + body: { where, _method: 'GET' }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Javascript-Key": Parse.javaScriptKey, - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, + 'Content-Type': 'application/json', }, }); }) @@ -544,11 +544,11 @@ describe("Parse.GeoPoint testing", () => { }); }); - it("out of bounds Polygon object withinPolygon", done => { + it('out of bounds Polygon object withinPolygon', done => { const point = new Parse.GeoPoint(1.5, 1.5); - const obj = new Parse.Object("Polygon", { location: point }); + const obj = new Parse.Object('Polygon', { location: point }); const polygon = { - __type: "Polygon", + __type: 'Polygon', coordinates: [ [0, 0], [181, 0], @@ -566,13 +566,13 @@ describe("Parse.GeoPoint testing", () => { }, }; return request({ - method: "POST", - url: Parse.serverURL + "/classes/Polygon", - body: { where, _method: "GET" }, + method: 'POST', + url: Parse.serverURL + '/classes/Polygon', + body: { where, _method: 'GET' }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Javascript-Key": Parse.javaScriptKey, - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, + 'Content-Type': 'application/json', }, }); }) @@ -586,9 +586,9 @@ describe("Parse.GeoPoint testing", () => { }); }); - it("invalid input withinPolygon", done => { + it('invalid input withinPolygon', done => { const point = new Parse.GeoPoint(1.5, 1.5); - const obj = new Parse.Object("Polygon", { location: point }); + const obj = new Parse.Object('Polygon', { location: point }); obj .save() .then(() => { @@ -600,13 +600,13 @@ describe("Parse.GeoPoint testing", () => { }, }; return request({ - method: "POST", - url: Parse.serverURL + "/classes/Polygon", - body: { where, _method: "GET" }, + method: 'POST', + url: Parse.serverURL + '/classes/Polygon', + body: { where, _method: 'GET' }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Javascript-Key": Parse.javaScriptKey, - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, + 'Content-Type': 'application/json', }, }); }) @@ -620,9 +620,9 @@ describe("Parse.GeoPoint testing", () => { }); }); - it("invalid geoPoint withinPolygon", done => { + it('invalid geoPoint withinPolygon', done => { const point = new Parse.GeoPoint(1.5, 1.5); - const obj = new Parse.Object("Polygon", { location: point }); + const obj = new Parse.Object('Polygon', { location: point }); obj .save() .then(() => { @@ -634,13 +634,13 @@ describe("Parse.GeoPoint testing", () => { }, }; return request({ - method: "POST", - url: Parse.serverURL + "/classes/Polygon", - body: { where, _method: "GET" }, + method: 'POST', + url: Parse.serverURL + '/classes/Polygon', + body: { where, _method: 'GET' }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Javascript-Key": Parse.javaScriptKey, - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, + 'Content-Type': 'application/json', }, }); }) @@ -654,9 +654,9 @@ describe("Parse.GeoPoint testing", () => { }); }); - it("invalid latitude withinPolygon", done => { + it('invalid latitude withinPolygon', done => { const point = new Parse.GeoPoint(1.5, 1.5); - const obj = new Parse.Object("Polygon", { location: point }); + const obj = new Parse.Object('Polygon', { location: point }); obj .save() .then(() => { @@ -664,21 +664,21 @@ describe("Parse.GeoPoint testing", () => { location: { $geoWithin: { $polygon: [ - { __type: "GeoPoint", latitude: 0, longitude: 0 }, - { __type: "GeoPoint", latitude: 181, longitude: 0 }, - { __type: "GeoPoint", latitude: 0, longitude: 0 }, + { __type: 'GeoPoint', latitude: 0, longitude: 0 }, + { __type: 'GeoPoint', latitude: 181, longitude: 0 }, + { __type: 'GeoPoint', latitude: 0, longitude: 0 }, ], }, }, }; return request({ - method: "POST", - url: Parse.serverURL + "/classes/Polygon", - body: { where, _method: "GET" }, + method: 'POST', + url: Parse.serverURL + '/classes/Polygon', + body: { where, _method: 'GET' }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Javascript-Key": Parse.javaScriptKey, - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, + 'Content-Type': 'application/json', }, }); }) @@ -692,9 +692,9 @@ describe("Parse.GeoPoint testing", () => { }); }); - it("invalid longitude withinPolygon", done => { + it('invalid longitude withinPolygon', done => { const point = new Parse.GeoPoint(1.5, 1.5); - const obj = new Parse.Object("Polygon", { location: point }); + const obj = new Parse.Object('Polygon', { location: point }); obj .save() .then(() => { @@ -702,21 +702,21 @@ describe("Parse.GeoPoint testing", () => { location: { $geoWithin: { $polygon: [ - { __type: "GeoPoint", latitude: 0, longitude: 0 }, - { __type: "GeoPoint", latitude: 0, longitude: 181 }, - { __type: "GeoPoint", latitude: 0, longitude: 0 }, + { __type: 'GeoPoint', latitude: 0, longitude: 0 }, + { __type: 'GeoPoint', latitude: 0, longitude: 181 }, + { __type: 'GeoPoint', latitude: 0, longitude: 0 }, ], }, }, }; return request({ - method: "POST", - url: Parse.serverURL + "/classes/Polygon", - body: { where, _method: "GET" }, + method: 'POST', + url: Parse.serverURL + '/classes/Polygon', + body: { where, _method: 'GET' }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Javascript-Key": Parse.javaScriptKey, - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, + 'Content-Type': 'application/json', }, }); }) @@ -730,9 +730,9 @@ describe("Parse.GeoPoint testing", () => { }); }); - it("minimum 3 points withinPolygon", done => { + it('minimum 3 points withinPolygon', done => { const point = new Parse.GeoPoint(1.5, 1.5); - const obj = new Parse.Object("Polygon", { location: point }); + const obj = new Parse.Object('Polygon', { location: point }); obj .save() .then(() => { @@ -744,13 +744,13 @@ describe("Parse.GeoPoint testing", () => { }, }; return request({ - method: "POST", - url: Parse.serverURL + "/classes/Polygon", - body: { where, _method: "GET" }, + method: 'POST', + url: Parse.serverURL + '/classes/Polygon', + body: { where, _method: 'GET' }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Javascript-Key": Parse.javaScriptKey, - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, + 'Content-Type': 'application/json', }, }); }) @@ -764,17 +764,17 @@ describe("Parse.GeoPoint testing", () => { }); }); - it("withinKilometers supports count", async () => { + it('withinKilometers supports count', async () => { const inside = new Parse.GeoPoint(10, 10); const outside = new Parse.GeoPoint(20, 20); - const obj1 = new Parse.Object("TestObject", { location: inside }); - const obj2 = new Parse.Object("TestObject", { location: outside }); + const obj1 = new Parse.Object('TestObject', { location: inside }); + const obj2 = new Parse.Object('TestObject', { location: outside }); await Parse.Object.saveAll([obj1, obj2]); const q = new Parse.Query(TestObject).withinKilometers( - "location", + 'location', inside, 5 ); @@ -783,25 +783,25 @@ describe("Parse.GeoPoint testing", () => { equal(count, 1); }); - it_id("0b073d31-0d41-41e7-bd60-f636ffb759dc")(it)( - "withinKilometers complex supports count", + it_id('0b073d31-0d41-41e7-bd60-f636ffb759dc')(it)( + 'withinKilometers complex supports count', async () => { const inside = new Parse.GeoPoint(10, 10); const middle = new Parse.GeoPoint(20, 20); const outside = new Parse.GeoPoint(30, 30); - const obj1 = new Parse.Object("TestObject", { location: inside }); - const obj2 = new Parse.Object("TestObject", { location: middle }); - const obj3 = new Parse.Object("TestObject", { location: outside }); + const obj1 = new Parse.Object('TestObject', { location: inside }); + const obj2 = new Parse.Object('TestObject', { location: middle }); + const obj3 = new Parse.Object('TestObject', { location: outside }); await Parse.Object.saveAll([obj1, obj2, obj3]); const q1 = new Parse.Query(TestObject).withinKilometers( - "location", + 'location', inside, 5 ); const q2 = new Parse.Query(TestObject).withinKilometers( - "location", + 'location', middle, 5 ); @@ -812,8 +812,8 @@ describe("Parse.GeoPoint testing", () => { } ); - it_id("26c9a13d-3d71-452e-a91c-9a4589be021c")(it)( - "fails to fetch geopoints that are specifically not at (0,0)", + it_id('26c9a13d-3d71-452e-a91c-9a4589be021c')(it)( + 'fails to fetch geopoints that are specifically not at (0,0)', async () => { const tmp = new TestObject({ location: new Parse.GeoPoint({ latitude: 0, longitude: 0 }), @@ -827,7 +827,7 @@ describe("Parse.GeoPoint testing", () => { await Parse.Object.saveAll([tmp, tmp2]); const query = new Parse.Query(TestObject); query.notEqualTo( - "location", + 'location', new Parse.GeoPoint({ latitude: 0, longitude: 0 }) ); const results = await query.find(); diff --git a/spec/ParseGlobalConfig.spec.js b/spec/ParseGlobalConfig.spec.js index 9eec7a21ed..b118a27638 100644 --- a/spec/ParseGlobalConfig.spec.js +++ b/spec/ParseGlobalConfig.spec.js @@ -1,36 +1,36 @@ -"use strict"; +'use strict'; -const request = require("../lib/request"); -const Config = require("../lib/Config"); +const request = require('../lib/request'); +const Config = require('../lib/Config'); -describe("a GlobalConfig", () => { +describe('a GlobalConfig', () => { beforeEach(async () => { - const config = Config.get("test"); + const config = Config.get('test'); const query = on_db( - "mongo", + 'mongo', () => { // Legacy is with an int... return { objectId: 1 }; }, () => { - return { objectId: "1" }; + return { objectId: '1' }; } ); await config.database.adapter.upsertOneObject( - "_GlobalConfig", + '_GlobalConfig', { fields: { - objectId: { type: "Number" }, - params: { type: "Object" }, - masterKeyOnly: { type: "Object" }, + objectId: { type: 'Number' }, + params: { type: 'Object' }, + masterKeyOnly: { type: 'Object' }, }, }, query, { params: { - companies: ["US", "DK"], + companies: ['US', 'DK'], counter: 20, - internalParam: "internal", + internalParam: 'internal', }, masterKeyOnly: { internalParam: true }, } @@ -38,21 +38,21 @@ describe("a GlobalConfig", () => { }); const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-Master-Key": "test", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-Master-Key': 'test', }; - it("can be retrieved", done => { + it('can be retrieved', done => { request({ - url: "http://localhost:8378/1/config", + url: 'http://localhost:8378/1/config', json: true, headers, }).then(response => { const body = response.data; try { expect(response.status).toEqual(200); - expect(body.params.companies).toEqual(["US", "DK"]); + expect(body.params.companies).toEqual(['US', 'DK']); } catch (e) { jfail(e); } @@ -60,16 +60,16 @@ describe("a GlobalConfig", () => { }); }); - it("internal parameter can be retrieved with master key", done => { + it('internal parameter can be retrieved with master key', done => { request({ - url: "http://localhost:8378/1/config", + url: 'http://localhost:8378/1/config', json: true, headers, }).then(response => { const body = response.data; try { expect(response.status).toEqual(200); - expect(body.params.internalParam).toEqual("internal"); + expect(body.params.internalParam).toEqual('internal'); } catch (e) { jfail(e); } @@ -77,14 +77,14 @@ describe("a GlobalConfig", () => { }); }); - it("internal parameter cannot be retrieved without master key", done => { + it('internal parameter cannot be retrieved without master key', done => { request({ - url: "http://localhost:8378/1/config", + url: 'http://localhost:8378/1/config', json: true, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, }).then(response => { const body = response.data; @@ -98,12 +98,12 @@ describe("a GlobalConfig", () => { }); }); - it("can be updated when a master key exists", done => { + it('can be updated when a master key exists', done => { request({ - method: "PUT", - url: "http://localhost:8378/1/config", + method: 'PUT', + url: 'http://localhost:8378/1/config', json: true, - body: { params: { companies: ["US", "DK", "SE"] } }, + body: { params: { companies: ['US', 'DK', 'SE'] } }, headers, }).then(response => { const body = response.data; @@ -113,43 +113,43 @@ describe("a GlobalConfig", () => { }); }); - it_only_db("mongo")("can addUnique", async () => { + it_only_db('mongo')('can addUnique', async () => { await Parse.Config.save({ - companies: { __op: "AddUnique", objects: ["PA", "RS", "E"] }, + companies: { __op: 'AddUnique', objects: ['PA', 'RS', 'E'] }, }); const config = await Parse.Config.get(); - const companies = config.get("companies"); - expect(companies).toEqual(["US", "DK", "PA", "RS", "E"]); + const companies = config.get('companies'); + expect(companies).toEqual(['US', 'DK', 'PA', 'RS', 'E']); }); - it_only_db("mongo")("can add to array", async () => { - await Parse.Config.save({ companies: { __op: "Add", objects: ["PA"] } }); + it_only_db('mongo')('can add to array', async () => { + await Parse.Config.save({ companies: { __op: 'Add', objects: ['PA'] } }); const config = await Parse.Config.get(); - const companies = config.get("companies"); - expect(companies).toEqual(["US", "DK", "PA"]); + const companies = config.get('companies'); + expect(companies).toEqual(['US', 'DK', 'PA']); }); - it_only_db("mongo")("can remove from array", async () => { - await Parse.Config.save({ companies: { __op: "Remove", objects: ["US"] } }); + it_only_db('mongo')('can remove from array', async () => { + await Parse.Config.save({ companies: { __op: 'Remove', objects: ['US'] } }); const config = await Parse.Config.get(); - const companies = config.get("companies"); - expect(companies).toEqual(["DK"]); + const companies = config.get('companies'); + expect(companies).toEqual(['DK']); }); - it("can increment", async () => { - await Parse.Config.save({ counter: { __op: "Increment", amount: 49 } }); + it('can increment', async () => { + await Parse.Config.save({ counter: { __op: 'Increment', amount: 49 } }); const config = await Parse.Config.get(); - const counter = config.get("counter"); + const counter = config.get('counter'); expect(counter).toEqual(69); }); - it("can add and retrive files", done => { + it('can add and retrive files', done => { request({ - method: "PUT", - url: "http://localhost:8378/1/config", + method: 'PUT', + url: 'http://localhost:8378/1/config', json: true, body: { - params: { file: { __type: "File", name: "name", url: "http://url" } }, + params: { file: { __type: 'File', name: 'name', url: 'http://url' } }, }, headers, }).then(response => { @@ -157,19 +157,19 @@ describe("a GlobalConfig", () => { expect(response.status).toEqual(200); expect(body.result).toEqual(true); Parse.Config.get().then(res => { - const file = res.get("file"); - expect(file.name()).toBe("name"); - expect(file.url()).toBe("http://url"); + const file = res.get('file'); + expect(file.name()).toBe('name'); + expect(file.url()).toBe('http://url'); done(); }); }); }); - it("can add and retrive Geopoints", done => { + it('can add and retrive Geopoints', done => { const geopoint = new Parse.GeoPoint(10, -20); request({ - method: "PUT", - url: "http://localhost:8378/1/config", + method: 'PUT', + url: 'http://localhost:8378/1/config', json: true, body: { params: { point: geopoint.toJSON() } }, headers, @@ -178,7 +178,7 @@ describe("a GlobalConfig", () => { expect(response.status).toEqual(200); expect(body.result).toEqual(true); Parse.Config.get().then(res => { - const point = res.get("point"); + const point = res.get('point'); expect(point.latitude).toBe(10); expect(point.longitude).toBe(-20); done(); @@ -186,19 +186,19 @@ describe("a GlobalConfig", () => { }); }); - it_id("5ebbd0cf-d1a5-49d9-aac7-5216abc5cb62")(it)( - "properly handles delete op", + it_id('5ebbd0cf-d1a5-49d9-aac7-5216abc5cb62')(it)( + 'properly handles delete op', done => { request({ - method: "PUT", - url: "http://localhost:8378/1/config", + method: 'PUT', + url: 'http://localhost:8378/1/config', json: true, body: { params: { - companies: { __op: "Delete" }, - counter: { __op: "Delete" }, - internalParam: { __op: "Delete" }, - foo: "bar", + companies: { __op: 'Delete' }, + counter: { __op: 'Delete' }, + internalParam: { __op: 'Delete' }, + foo: 'bar', }, }, headers, @@ -207,7 +207,7 @@ describe("a GlobalConfig", () => { expect(response.status).toEqual(200); expect(body.result).toEqual(true); request({ - url: "http://localhost:8378/1/config", + url: 'http://localhost:8378/1/config', json: true, headers, }).then(response => { @@ -216,7 +216,7 @@ describe("a GlobalConfig", () => { expect(response.status).toEqual(200); expect(body.params.companies).toBeUndefined(); expect(body.params.counter).toBeUndefined(); - expect(body.params.foo).toBe("bar"); + expect(body.params.foo).toBe('bar'); expect(Object.keys(body.params).length).toBe(1); } catch (e) { jfail(e); @@ -227,36 +227,36 @@ describe("a GlobalConfig", () => { } ); - it("fail to update if master key is missing", done => { + it('fail to update if master key is missing', done => { request({ - method: "PUT", - url: "http://localhost:8378/1/config", + method: 'PUT', + url: 'http://localhost:8378/1/config', json: true, body: { params: { companies: [] } }, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, }).then(fail, response => { const body = response.data; expect(response.status).toEqual(403); - expect(body.error).toEqual("unauthorized: master key is required"); + expect(body.error).toEqual('unauthorized: master key is required'); done(); }); }); - it("failed getting config when it is missing", done => { - const config = Config.get("test"); + it('failed getting config when it is missing', done => { + const config = Config.get('test'); config.database.adapter .deleteObjectsByQuery( - "_GlobalConfig", - { fields: { params: { __type: "String" } } }, - { objectId: "1" } + '_GlobalConfig', + { fields: { params: { __type: 'String' } } }, + { objectId: '1' } ) .then(() => { request({ - url: "http://localhost:8378/1/config", + url: 'http://localhost:8378/1/config', json: true, headers, }).then(response => { diff --git a/spec/ParseGraphQLClassNameTransformer.spec.js b/spec/ParseGraphQLClassNameTransformer.spec.js index 94a4b328ef..c2a89e2f60 100644 --- a/spec/ParseGraphQLClassNameTransformer.spec.js +++ b/spec/ParseGraphQLClassNameTransformer.spec.js @@ -1,11 +1,11 @@ const { transformClassNameToGraphQL, -} = require("../lib/GraphQL/transformers/className"); +} = require('../lib/GraphQL/transformers/className'); -describe("transformClassNameToGraphQL", () => { - it("should remove starting _ and tansform first letter to upper case", () => { +describe('transformClassNameToGraphQL', () => { + it('should remove starting _ and tansform first letter to upper case', () => { expect( - ["_User", "_user", "User", "user"].map(transformClassNameToGraphQL) - ).toEqual(["User", "User", "User", "User"]); + ['_User', '_user', 'User', 'user'].map(transformClassNameToGraphQL) + ).toEqual(['User', 'User', 'User', 'User']); }); }); diff --git a/spec/ParseGraphQLController.spec.js b/spec/ParseGraphQLController.spec.js index c08925ca0e..4ff28ffc93 100644 --- a/spec/ParseGraphQLController.spec.js +++ b/spec/ParseGraphQLController.spec.js @@ -3,10 +3,10 @@ const { GraphQLConfigClassName, GraphQLConfigId, GraphQLConfigKey, -} = require("../lib/Controllers/ParseGraphQLController"); -const { isEqual } = require("lodash"); +} = require('../lib/Controllers/ParseGraphQLController'); +const { isEqual } = require('lodash'); -describe("ParseGraphQLController", () => { +describe('ParseGraphQLController', () => { let parseServer; let databaseController; let cacheController; @@ -72,8 +72,8 @@ describe("ParseGraphQLController", () => { databaseUpdateArgs = null; }); - describe("constructor", () => { - it("should require a databaseController", () => { + describe('constructor', () => { + it('should require a databaseController', () => { expect(() => new ParseGraphQLController()).toThrow( 'ParseGraphQLController requires a "databaseController" to be instantiated.' ); @@ -90,7 +90,7 @@ describe("ParseGraphQLController", () => { 'ParseGraphQLController requires a "databaseController" to be instantiated.' ); }); - it("should construct without a cacheController", () => { + it('should construct without a cacheController', () => { expect( () => new ParseGraphQLController({ @@ -105,7 +105,7 @@ describe("ParseGraphQLController", () => { }) ).not.toThrow(); }); - it("should set isMounted to true if config.mountGraphQL is true", () => { + it('should set isMounted to true if config.mountGraphQL is true', () => { const mountedController = new ParseGraphQLController({ databaseController, mountGraphQL: true, @@ -123,8 +123,8 @@ describe("ParseGraphQLController", () => { }); }); - describe("getGraphQLConfig", () => { - it("should return an empty graphQLConfig if collection has none", async () => { + describe('getGraphQLConfig', () => { + it('should return an empty graphQLConfig if collection has none', async () => { removeConfigFromDb(); const parseGraphQLController = new ParseGraphQLController({ @@ -135,17 +135,17 @@ describe("ParseGraphQLController", () => { const graphQLConfig = await parseGraphQLController.getGraphQLConfig(); expect(graphQLConfig).toEqual({}); }); - it("should return an existing graphQLConfig", async () => { - setConfigOnDb({ enabledForClasses: ["_User"] }); + it('should return an existing graphQLConfig', async () => { + setConfigOnDb({ enabledForClasses: ['_User'] }); const parseGraphQLController = new ParseGraphQLController({ databaseController, mountGraphQL: false, }); const graphQLConfig = await parseGraphQLController.getGraphQLConfig(); - expect(graphQLConfig).toEqual({ enabledForClasses: ["_User"] }); + expect(graphQLConfig).toEqual({ enabledForClasses: ['_User'] }); }); - it("should use the cache if mounted, and return the stored graphQLConfig", async () => { + it('should use the cache if mounted, and return the stored graphQLConfig', async () => { removeConfigFromDb(); cacheController.graphQL.clear(); const parseGraphQLController = new ParseGraphQLController({ @@ -154,14 +154,14 @@ describe("ParseGraphQLController", () => { mountGraphQL: true, }); cacheController.graphQL.put(parseGraphQLController.configCacheKey, { - enabledForClasses: ["SuperCar"], + enabledForClasses: ['SuperCar'], }); const graphQLConfig = await parseGraphQLController.getGraphQLConfig(); - expect(graphQLConfig).toEqual({ enabledForClasses: ["SuperCar"] }); + expect(graphQLConfig).toEqual({ enabledForClasses: ['SuperCar'] }); }); - it("should use the database when mounted and cache is empty", async () => { - setConfigOnDb({ disabledForClasses: ["SuperCar"] }); + it('should use the database when mounted and cache is empty', async () => { + setConfigOnDb({ disabledForClasses: ['SuperCar'] }); cacheController.graphQL.clear(); const parseGraphQLController = new ParseGraphQLController({ databaseController, @@ -169,10 +169,10 @@ describe("ParseGraphQLController", () => { mountGraphQL: true, }); const graphQLConfig = await parseGraphQLController.getGraphQLConfig(); - expect(graphQLConfig).toEqual({ disabledForClasses: ["SuperCar"] }); + expect(graphQLConfig).toEqual({ disabledForClasses: ['SuperCar'] }); }); - it("should store the graphQLConfig in cache if mounted", async () => { - setConfigOnDb({ enabledForClasses: ["SuperCar"] }); + it('should store the graphQLConfig in cache if mounted', async () => { + setConfigOnDb({ enabledForClasses: ['SuperCar'] }); cacheController.graphQL.clear(); const parseGraphQLController = new ParseGraphQLController({ databaseController, @@ -187,32 +187,32 @@ describe("ParseGraphQLController", () => { const cachedValueAfter = await cacheController.graphQL.get( parseGraphQLController.configCacheKey ); - expect(cachedValueAfter).toEqual({ enabledForClasses: ["SuperCar"] }); + expect(cachedValueAfter).toEqual({ enabledForClasses: ['SuperCar'] }); }); }); - describe("updateGraphQLConfig", () => { + describe('updateGraphQLConfig', () => { const successfulUpdateResponse = { response: { result: true } }; - it("should throw if graphQLConfig is not provided", async function () { + it('should throw if graphQLConfig is not provided', async function () { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); expectAsync( parseGraphQLController.updateGraphQLConfig() - ).toBeRejectedWith("You must provide a graphQLConfig!"); + ).toBeRejectedWith('You must provide a graphQLConfig!'); }); - it("should correct update the graphQLConfig object using the databaseController", async () => { + it('should correct update the graphQLConfig object using the databaseController', async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); const graphQLConfig = { - enabledForClasses: ["ClassA", "ClassB"], + enabledForClasses: ['ClassA', 'ClassB'], disabledForClasses: [], classConfigs: [ - { className: "ClassA", query: { get: false } }, - { className: "ClassB", mutation: { destroy: false }, type: {} }, + { className: 'ClassA', query: { get: false } }, + { className: 'ClassB', mutation: { destroy: false }, type: {} }, ], }; @@ -228,7 +228,7 @@ describe("ParseGraphQLController", () => { expect(op).toEqual({ upsert: true }); }); - it("should throw if graphQLConfig is not an object", async () => { + it('should throw if graphQLConfig is not an object', async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); @@ -242,13 +242,13 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig(Promise.resolve({})) ).toBeRejected(); expectAsync( - parseGraphQLController.updateGraphQLConfig("") + parseGraphQLController.updateGraphQLConfig('') ).toBeRejected(); expectAsync( parseGraphQLController.updateGraphQLConfig({}) ).toBeResolvedTo(successfulUpdateResponse); }); - it("should throw if graphQLConfig has an invalid root key", async () => { + it('should throw if graphQLConfig has an invalid root key', async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); @@ -259,7 +259,7 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({}) ).toBeResolvedTo(successfulUpdateResponse); }); - it("should throw if graphQLConfig has invalid class filters", async () => { + it('should throw if graphQLConfig has invalid class filters', async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); @@ -278,20 +278,20 @@ describe("ParseGraphQLController", () => { ).toBeRejected(); expectAsync( parseGraphQLController.updateGraphQLConfig({ - enabledForClasses: ["_User", null], + enabledForClasses: ['_User', null], }) ).toBeRejected(); expectAsync( - parseGraphQLController.updateGraphQLConfig({ disabledForClasses: [""] }) + parseGraphQLController.updateGraphQLConfig({ disabledForClasses: [''] }) ).toBeRejected(); expectAsync( parseGraphQLController.updateGraphQLConfig({ enabledForClasses: [], - disabledForClasses: ["_User"], + disabledForClasses: ['_User'], }) ).toBeResolvedTo(successfulUpdateResponse); }); - it("should throw if classConfigs array is invalid", async () => { + it('should throw if classConfigs array is invalid', async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); @@ -308,7 +308,7 @@ describe("ParseGraphQLController", () => { ).toBeRejected(); expectAsync( parseGraphQLController.updateGraphQLConfig({ - classConfigs: [{ className: "ValidClass" }, null], + classConfigs: [{ className: 'ValidClass' }, null], }) ).toBeRejected(); expectAsync( @@ -318,13 +318,13 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', }, ], }) ).toBeResolvedTo(successfulUpdateResponse); }); - it("should throw if a classConfig has invalid type settings", async () => { + it('should throw if a classConfig has invalid type settings', async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); @@ -332,7 +332,7 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', type: [], }, ], @@ -342,7 +342,7 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', type: { invalidKey: true, }, @@ -354,14 +354,14 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', type: {}, }, ], }) ).toBeResolvedTo(successfulUpdateResponse); }); - it("should throw if a classConfig has invalid type.inputFields settings", async () => { + it('should throw if a classConfig has invalid type.inputFields settings', async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); @@ -369,7 +369,7 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "SuperCar", + className: 'SuperCar', type: { inputFields: [], }, @@ -381,7 +381,7 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "SuperCar", + className: 'SuperCar', type: { inputFields: { invalidKey: true, @@ -395,7 +395,7 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "SuperCar", + className: 'SuperCar', type: { inputFields: { create: {}, @@ -409,7 +409,7 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "SuperCar", + className: 'SuperCar', type: { inputFields: { update: [null], @@ -423,7 +423,7 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "SuperCar", + className: 'SuperCar', type: { inputFields: { create: [], @@ -438,10 +438,10 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "SuperCar", + className: 'SuperCar', type: { inputFields: { - create: ["make", "model"], + create: ['make', 'model'], update: [], }, }, @@ -450,7 +450,7 @@ describe("ParseGraphQLController", () => { }) ).toBeResolvedTo(successfulUpdateResponse); }); - it("should throw if a classConfig has invalid type.outputFields settings", async () => { + it('should throw if a classConfig has invalid type.outputFields settings', async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); @@ -458,7 +458,7 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', type: { outputFields: {}, }, @@ -470,7 +470,7 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', type: { outputFields: [null], }, @@ -482,9 +482,9 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', type: { - outputFields: ["name", undefined], + outputFields: ['name', undefined], }, }, ], @@ -494,9 +494,9 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', type: { - outputFields: [""], + outputFields: [''], }, }, ], @@ -506,7 +506,7 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', type: { outputFields: [], }, @@ -518,16 +518,16 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', type: { - outputFields: ["name"], + outputFields: ['name'], }, }, ], }) ).toBeResolvedTo(successfulUpdateResponse); }); - it("should throw if a classConfig has invalid type.constraintFields settings", async () => { + it('should throw if a classConfig has invalid type.constraintFields settings', async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); @@ -535,7 +535,7 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', type: { constraintFields: {}, }, @@ -547,7 +547,7 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', type: { constraintFields: [null], }, @@ -559,9 +559,9 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', type: { - constraintFields: ["name", undefined], + constraintFields: ['name', undefined], }, }, ], @@ -571,9 +571,9 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', type: { - constraintFields: [""], + constraintFields: [''], }, }, ], @@ -583,7 +583,7 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', type: { constraintFields: [], }, @@ -595,16 +595,16 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', type: { - constraintFields: ["name"], + constraintFields: ['name'], }, }, ], }) ).toBeResolvedTo(successfulUpdateResponse); }); - it("should throw if a classConfig has invalid type.sortFields settings", async () => { + it('should throw if a classConfig has invalid type.sortFields settings', async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); @@ -612,7 +612,7 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', type: { sortFields: {}, }, @@ -624,7 +624,7 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', type: { sortFields: [null], }, @@ -636,7 +636,7 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', type: { sortFields: [ { @@ -654,11 +654,11 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', type: { sortFields: [ { - field: "", + field: '', asc: true, desc: false, }, @@ -672,13 +672,13 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', type: { sortFields: [ { - field: "name", + field: 'name', asc: true, - desc: "false", + desc: 'false', }, ], }, @@ -690,11 +690,11 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', type: { sortFields: [ { - field: "name", + field: 'name', asc: true, desc: true, }, @@ -709,7 +709,7 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', type: { sortFields: [], }, @@ -721,11 +721,11 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', type: { sortFields: [ { - field: "name", + field: 'name', asc: true, desc: true, }, @@ -736,7 +736,7 @@ describe("ParseGraphQLController", () => { }) ).toBeResolvedTo(successfulUpdateResponse); }); - it("should throw if a classConfig has invalid query params", async () => { + it('should throw if a classConfig has invalid query params', async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); @@ -744,7 +744,7 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', query: [], }, ], @@ -754,7 +754,7 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', query: { invalidKey: true, }, @@ -766,7 +766,7 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', query: { get: 1, }, @@ -778,9 +778,9 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', query: { - find: "true", + find: 'true', }, }, ], @@ -790,7 +790,7 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', query: { get: false, find: true, @@ -803,14 +803,14 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', query: {}, }, ], }) ).toBeResolvedTo(successfulUpdateResponse); }); - it("should throw if a classConfig has invalid mutation params", async () => { + it('should throw if a classConfig has invalid mutation params', async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); @@ -818,7 +818,7 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', mutation: [], }, ], @@ -828,7 +828,7 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', mutation: { invalidKey: true, }, @@ -840,7 +840,7 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', mutation: { destroy: 1, }, @@ -852,9 +852,9 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', mutation: { - update: "true", + update: 'true', }, }, ], @@ -864,7 +864,7 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', mutation: {}, }, ], @@ -874,7 +874,7 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', mutation: { create: true, update: true, @@ -886,7 +886,7 @@ describe("ParseGraphQLController", () => { ).toBeResolvedTo(successfulUpdateResponse); }); - it("should throw if _User create fields is missing username or password", async () => { + it('should throw if _User create fields is missing username or password', async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); @@ -894,10 +894,10 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', type: { inputFields: { - create: ["username", "no-password"], + create: ['username', 'no-password'], }, }, }, @@ -908,10 +908,10 @@ describe("ParseGraphQLController", () => { parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "_User", + className: '_User', type: { inputFields: { - create: ["username", "password"], + create: ['username', 'password'], }, }, }, @@ -919,7 +919,7 @@ describe("ParseGraphQLController", () => { }) ).toBeResolved(successfulUpdateResponse); }); - it("should update the cache if mounted", async () => { + it('should update the cache if mounted', async () => { removeConfigFromDb(); cacheController.graphQL.clear(); const mountedController = new ParseGraphQLController({ @@ -942,12 +942,12 @@ describe("ParseGraphQLController", () => { expect(cacheBeforeValue).toBeNull(); await mountedController.updateGraphQLConfig({ - enabledForClasses: ["SuperCar"], + enabledForClasses: ['SuperCar'], }); cacheAfterValue = await cacheController.graphQL.get( mountedController.configCacheKey ); - expect(cacheAfterValue).toEqual({ enabledForClasses: ["SuperCar"] }); + expect(cacheAfterValue).toEqual({ enabledForClasses: ['SuperCar'] }); // reset removeConfigFromDb(); @@ -959,7 +959,7 @@ describe("ParseGraphQLController", () => { expect(cacheBeforeValue).toBeNull(); await unmountedController.updateGraphQLConfig({ - enabledForClasses: ["SuperCar"], + enabledForClasses: ['SuperCar'], }); cacheAfterValue = await cacheController.graphQL.get( unmountedController.configCacheKey @@ -968,13 +968,13 @@ describe("ParseGraphQLController", () => { }); }); - describe("alias", () => { - it("should fail if query alias is not a string", async () => { + describe('alias', () => { + it('should fail if query alias is not a string', async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); - const className = "Bar"; + const className = 'Bar'; expectAsync( parseGraphQLController.updateGraphQLConfig({ @@ -999,7 +999,7 @@ describe("ParseGraphQLController", () => { className, query: { find: true, - findAlias: { not: "valid" }, + findAlias: { not: 'valid' }, }, }, ], @@ -1009,12 +1009,12 @@ describe("ParseGraphQLController", () => { ); }); - it("should fail if mutation alias is not a string", async () => { + it('should fail if mutation alias is not a string', async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); - const className = "Bar"; + const className = 'Bar'; expectAsync( parseGraphQLController.updateGraphQLConfig({ @@ -1055,7 +1055,7 @@ describe("ParseGraphQLController", () => { className, mutation: { destroy: true, - destroyAlias: { not: "valid" }, + destroyAlias: { not: 'valid' }, }, }, ], diff --git a/spec/ParseGraphQLSchema.spec.js b/spec/ParseGraphQLSchema.spec.js index 5c69eea1af..212f87092f 100644 --- a/spec/ParseGraphQLSchema.spec.js +++ b/spec/ParseGraphQLSchema.spec.js @@ -1,13 +1,13 @@ -const { GraphQLObjectType } = require("graphql"); -const defaultLogger = require("../lib/logger").default; -const { ParseGraphQLSchema } = require("../lib/GraphQL/ParseGraphQLSchema"); +const { GraphQLObjectType } = require('graphql'); +const defaultLogger = require('../lib/logger').default; +const { ParseGraphQLSchema } = require('../lib/GraphQL/ParseGraphQLSchema'); -describe("ParseGraphQLSchema", () => { +describe('ParseGraphQLSchema', () => { let parseServer; let databaseController; let parseGraphQLController; let parseGraphQLSchema; - const appId = "test"; + const appId = 'test'; beforeEach(async () => { parseServer = await global.reconfigureServer(); @@ -21,21 +21,21 @@ describe("ParseGraphQLSchema", () => { }); }); - describe("constructor", () => { - it("should require a parseGraphQLController, databaseController, a log instance, and the appId", () => { + describe('constructor', () => { + it('should require a parseGraphQLController, databaseController, a log instance, and the appId', () => { expect(() => new ParseGraphQLSchema()).toThrow( - "You must provide a parseGraphQLController instance!" + 'You must provide a parseGraphQLController instance!' ); expect( () => new ParseGraphQLSchema({ parseGraphQLController: {} }) - ).toThrow("You must provide a databaseController instance!"); + ).toThrow('You must provide a databaseController instance!'); expect( () => new ParseGraphQLSchema({ parseGraphQLController: {}, databaseController: {}, }) - ).toThrow("You must provide a log instance!"); + ).toThrow('You must provide a log instance!'); expect( () => new ParseGraphQLSchema({ @@ -43,18 +43,18 @@ describe("ParseGraphQLSchema", () => { databaseController: {}, log: {}, }) - ).toThrow("You must provide the appId!"); + ).toThrow('You must provide the appId!'); }); }); - describe("load", () => { - it("should cache schema", async () => { + describe('load', () => { + it('should cache schema', async () => { const graphQLSchema = await parseGraphQLSchema.load(); const updatedGraphQLSchema = await parseGraphQLSchema.load(); expect(graphQLSchema).toBe(updatedGraphQLSchema); }); - it("should load a brand new GraphQL Schema if Parse Schema changes", async () => { + it('should load a brand new GraphQL Schema if Parse Schema changes', async () => { await parseGraphQLSchema.load(); const parseClasses = parseGraphQLSchema.parseClasses; const parseClassTypes = parseGraphQLSchema.parseClassTypes; @@ -63,7 +63,7 @@ describe("ParseGraphQLSchema", () => { const graphQLQueries = parseGraphQLSchema.graphQLQueries; const graphQLMutations = parseGraphQLSchema.graphQLMutations; const graphQLSubscriptions = parseGraphQLSchema.graphQLSubscriptions; - const newClassObject = new Parse.Object("NewClass"); + const newClassObject = new Parse.Object('NewClass'); await newClassObject.save(); await parseServer.config.schemaCache.clear(); await new Promise(resolve => setTimeout(resolve, 200)); @@ -79,7 +79,7 @@ describe("ParseGraphQLSchema", () => { ); }); - it("should load a brand new GraphQL Schema if graphQLConfig changes", async () => { + it('should load a brand new GraphQL Schema if graphQLConfig changes', async () => { const parseGraphQLController = { graphQLConfig: { enabledForClasses: [] }, getGraphQLConfig() { @@ -102,7 +102,7 @@ describe("ParseGraphQLSchema", () => { const graphQLSubscriptions = parseGraphQLSchema.graphQLSubscriptions; parseGraphQLController.graphQLConfig = { - enabledForClasses: ["_User"], + enabledForClasses: ['_User'], }; await new Promise(resolve => setTimeout(resolve, 200)); @@ -119,8 +119,8 @@ describe("ParseGraphQLSchema", () => { }); }); - describe("addGraphQLType", () => { - it("should not load and warn duplicated types", async () => { + describe('addGraphQLType', () => { + it('should not load and warn duplicated types', async () => { let logged = false; const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, @@ -129,50 +129,50 @@ describe("ParseGraphQLSchema", () => { warn: message => { logged = true; expect(message).toEqual( - "Type SomeClass could not be added to the auto schema because it collided with an existing type." + 'Type SomeClass could not be added to the auto schema because it collided with an existing type.' ); }, }, appId, }); await parseGraphQLSchema.load(); - const type = new GraphQLObjectType({ name: "SomeClass" }); + const type = new GraphQLObjectType({ name: 'SomeClass' }); expect(parseGraphQLSchema.addGraphQLType(type)).toBe(type); expect(parseGraphQLSchema.graphQLTypes).toContain(type); expect( parseGraphQLSchema.addGraphQLType( - new GraphQLObjectType({ name: "SomeClass" }) + new GraphQLObjectType({ name: 'SomeClass' }) ) ).toBeUndefined(); expect(logged).toBeTruthy(); }); - it("should throw error when required", async () => { + it('should throw error when required', async () => { const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, parseGraphQLController, log: { warn: () => { - fail("Should not warn"); + fail('Should not warn'); }, }, appId, }); await parseGraphQLSchema.load(); - const type = new GraphQLObjectType({ name: "SomeClass" }); + const type = new GraphQLObjectType({ name: 'SomeClass' }); expect(parseGraphQLSchema.addGraphQLType(type, true)).toBe(type); expect(parseGraphQLSchema.graphQLTypes).toContain(type); expect(() => parseGraphQLSchema.addGraphQLType( - new GraphQLObjectType({ name: "SomeClass" }), + new GraphQLObjectType({ name: 'SomeClass' }), true ) ).toThrowError( - "Type SomeClass could not be added to the auto schema because it collided with an existing type." + 'Type SomeClass could not be added to the auto schema because it collided with an existing type.' ); }); - it("should warn reserved name collision", async () => { + it('should warn reserved name collision', async () => { let logged = false; const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, @@ -181,7 +181,7 @@ describe("ParseGraphQLSchema", () => { warn: message => { logged = true; expect(message).toEqual( - "Type String could not be added to the auto schema because it collided with an existing type." + 'Type String could not be added to the auto schema because it collided with an existing type.' ); }, }, @@ -190,32 +190,32 @@ describe("ParseGraphQLSchema", () => { await parseGraphQLSchema.load(); expect( parseGraphQLSchema.addGraphQLType( - new GraphQLObjectType({ name: "String" }) + new GraphQLObjectType({ name: 'String' }) ) ).toBeUndefined(); expect(logged).toBeTruthy(); }); - it("should ignore collision when necessary", async () => { + it('should ignore collision when necessary', async () => { const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, parseGraphQLController, log: { warn: () => { - fail("Should not warn"); + fail('Should not warn'); }, }, appId, }); await parseGraphQLSchema.load(); - const type = new GraphQLObjectType({ name: "String" }); + const type = new GraphQLObjectType({ name: 'String' }); expect(parseGraphQLSchema.addGraphQLType(type, true, true)).toBe(type); expect(parseGraphQLSchema.graphQLTypes).toContain(type); }); }); - describe("addGraphQLQuery", () => { - it("should not load and warn duplicated queries", async () => { + describe('addGraphQLQuery', () => { + it('should not load and warn duplicated queries', async () => { let logged = false; const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, @@ -224,7 +224,7 @@ describe("ParseGraphQLSchema", () => { warn: message => { logged = true; expect(message).toEqual( - "Query someClasses could not be added to the auto schema because it collided with an existing field." + 'Query someClasses could not be added to the auto schema because it collided with an existing field.' ); }, }, @@ -232,41 +232,41 @@ describe("ParseGraphQLSchema", () => { }); await parseGraphQLSchema.load(); const field = {}; - expect(parseGraphQLSchema.addGraphQLQuery("someClasses", field)).toBe( + expect(parseGraphQLSchema.addGraphQLQuery('someClasses', field)).toBe( field ); - expect(parseGraphQLSchema.graphQLQueries["someClasses"]).toBe(field); + expect(parseGraphQLSchema.graphQLQueries['someClasses']).toBe(field); expect( - parseGraphQLSchema.addGraphQLQuery("someClasses", {}) + parseGraphQLSchema.addGraphQLQuery('someClasses', {}) ).toBeUndefined(); expect(logged).toBeTruthy(); }); - it("should throw error when required", async () => { + it('should throw error when required', async () => { const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, parseGraphQLController, log: { warn: () => { - fail("Should not warn"); + fail('Should not warn'); }, }, appId, }); await parseGraphQLSchema.load(); const field = {}; - expect(parseGraphQLSchema.addGraphQLQuery("someClasses", field)).toBe( + expect(parseGraphQLSchema.addGraphQLQuery('someClasses', field)).toBe( field ); - expect(parseGraphQLSchema.graphQLQueries["someClasses"]).toBe(field); + expect(parseGraphQLSchema.graphQLQueries['someClasses']).toBe(field); expect(() => - parseGraphQLSchema.addGraphQLQuery("someClasses", {}, true) + parseGraphQLSchema.addGraphQLQuery('someClasses', {}, true) ).toThrowError( - "Query someClasses could not be added to the auto schema because it collided with an existing field." + 'Query someClasses could not be added to the auto schema because it collided with an existing field.' ); }); - it("should warn reserved name collision", async () => { + it('should warn reserved name collision', async () => { let logged = false; const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, @@ -275,24 +275,24 @@ describe("ParseGraphQLSchema", () => { warn: message => { logged = true; expect(message).toEqual( - "Query viewer could not be added to the auto schema because it collided with an existing field." + 'Query viewer could not be added to the auto schema because it collided with an existing field.' ); }, }, appId, }); await parseGraphQLSchema.load(); - expect(parseGraphQLSchema.addGraphQLQuery("viewer", {})).toBeUndefined(); + expect(parseGraphQLSchema.addGraphQLQuery('viewer', {})).toBeUndefined(); expect(logged).toBeTruthy(); }); - it("should ignore collision when necessary", async () => { + it('should ignore collision when necessary', async () => { const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, parseGraphQLController, log: { warn: () => { - fail("Should not warn"); + fail('Should not warn'); }, }, appId, @@ -301,14 +301,14 @@ describe("ParseGraphQLSchema", () => { delete parseGraphQLSchema.graphQLQueries.viewer; const field = {}; expect( - parseGraphQLSchema.addGraphQLQuery("viewer", field, true, true) + parseGraphQLSchema.addGraphQLQuery('viewer', field, true, true) ).toBe(field); - expect(parseGraphQLSchema.graphQLQueries["viewer"]).toBe(field); + expect(parseGraphQLSchema.graphQLQueries['viewer']).toBe(field); }); }); - describe("addGraphQLMutation", () => { - it("should not load and warn duplicated mutations", async () => { + describe('addGraphQLMutation', () => { + it('should not load and warn duplicated mutations', async () => { let logged = false; const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, @@ -317,7 +317,7 @@ describe("ParseGraphQLSchema", () => { warn: message => { logged = true; expect(message).toEqual( - "Mutation createSomeClass could not be added to the auto schema because it collided with an existing field." + 'Mutation createSomeClass could not be added to the auto schema because it collided with an existing field.' ); }, }, @@ -326,24 +326,24 @@ describe("ParseGraphQLSchema", () => { await parseGraphQLSchema.load(); const field = {}; expect( - parseGraphQLSchema.addGraphQLMutation("createSomeClass", field) + parseGraphQLSchema.addGraphQLMutation('createSomeClass', field) ).toBe(field); - expect(parseGraphQLSchema.graphQLMutations["createSomeClass"]).toBe( + expect(parseGraphQLSchema.graphQLMutations['createSomeClass']).toBe( field ); expect( - parseGraphQLSchema.addGraphQLMutation("createSomeClass", {}) + parseGraphQLSchema.addGraphQLMutation('createSomeClass', {}) ).toBeUndefined(); expect(logged).toBeTruthy(); }); - it("should throw error when required", async () => { + it('should throw error when required', async () => { const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, parseGraphQLController, log: { warn: () => { - fail("Should not warn"); + fail('Should not warn'); }, }, appId, @@ -351,19 +351,19 @@ describe("ParseGraphQLSchema", () => { await parseGraphQLSchema.load(); const field = {}; expect( - parseGraphQLSchema.addGraphQLMutation("createSomeClass", field) + parseGraphQLSchema.addGraphQLMutation('createSomeClass', field) ).toBe(field); - expect(parseGraphQLSchema.graphQLMutations["createSomeClass"]).toBe( + expect(parseGraphQLSchema.graphQLMutations['createSomeClass']).toBe( field ); expect(() => - parseGraphQLSchema.addGraphQLMutation("createSomeClass", {}, true) + parseGraphQLSchema.addGraphQLMutation('createSomeClass', {}, true) ).toThrowError( - "Mutation createSomeClass could not be added to the auto schema because it collided with an existing field." + 'Mutation createSomeClass could not be added to the auto schema because it collided with an existing field.' ); }); - it("should warn reserved name collision", async () => { + it('should warn reserved name collision', async () => { let logged = false; const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, @@ -372,7 +372,7 @@ describe("ParseGraphQLSchema", () => { warn: message => { logged = true; expect(message).toEqual( - "Mutation signUp could not be added to the auto schema because it collided with an existing field." + 'Mutation signUp could not be added to the auto schema because it collided with an existing field.' ); }, }, @@ -380,18 +380,18 @@ describe("ParseGraphQLSchema", () => { }); await parseGraphQLSchema.load(); expect( - parseGraphQLSchema.addGraphQLMutation("signUp", {}) + parseGraphQLSchema.addGraphQLMutation('signUp', {}) ).toBeUndefined(); expect(logged).toBeTruthy(); }); - it("should ignore collision when necessary", async () => { + it('should ignore collision when necessary', async () => { const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, parseGraphQLController, log: { warn: () => { - fail("Should not warn"); + fail('Should not warn'); }, }, appId, @@ -400,20 +400,20 @@ describe("ParseGraphQLSchema", () => { delete parseGraphQLSchema.graphQLMutations.signUp; const field = {}; expect( - parseGraphQLSchema.addGraphQLMutation("signUp", field, true, true) + parseGraphQLSchema.addGraphQLMutation('signUp', field, true, true) ).toBe(field); - expect(parseGraphQLSchema.graphQLMutations["signUp"]).toBe(field); + expect(parseGraphQLSchema.graphQLMutations['signUp']).toBe(field); }); }); - describe("_getParseClassesWithConfig", () => { - it("should sort classes", () => { + describe('_getParseClassesWithConfig', () => { + it('should sort classes', () => { const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, parseGraphQLController, log: { warn: () => { - fail("Should not warn"); + fail('Should not warn'); }, }, appId, @@ -422,14 +422,14 @@ describe("ParseGraphQLSchema", () => { parseGraphQLSchema ._getParseClassesWithConfig( [ - { className: "b" }, - { className: "_b" }, - { className: "B" }, - { className: "_B" }, - { className: "a" }, - { className: "_a" }, - { className: "A" }, - { className: "_A" }, + { className: 'b' }, + { className: '_b' }, + { className: 'B' }, + { className: '_B' }, + { className: 'a' }, + { className: '_a' }, + { className: 'A' }, + { className: '_A' }, ], { classConfigs: [], @@ -437,20 +437,20 @@ describe("ParseGraphQLSchema", () => { ) .map(item => item[0]) ).toEqual([ - { className: "_A" }, - { className: "_B" }, - { className: "_a" }, - { className: "_b" }, - { className: "A" }, - { className: "B" }, - { className: "a" }, - { className: "b" }, + { className: '_A' }, + { className: '_B' }, + { className: '_a' }, + { className: '_b' }, + { className: 'A' }, + { className: 'B' }, + { className: 'a' }, + { className: 'b' }, ]); }); }); - describe("name collision", () => { - it("should not generate duplicate types when colliding to default classes", async () => { + describe('name collision', () => { + it('should not generate duplicate types when colliding to default classes', async () => { const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, parseGraphQLController, @@ -462,7 +462,7 @@ describe("ParseGraphQLSchema", () => { const types1 = parseGraphQLSchema.graphQLTypes; const queries1 = parseGraphQLSchema.graphQLQueries; const mutations1 = parseGraphQLSchema.graphQLMutations; - const user = new Parse.Object("User"); + const user = new Parse.Object('User'); await user.save(); await parseGraphQLSchema.schemaCache.clear(); const schema2 = await parseGraphQLSchema.load(); @@ -484,21 +484,21 @@ describe("ParseGraphQLSchema", () => { ); }); - it("should not generate duplicate types when colliding the same name", async () => { + it('should not generate duplicate types when colliding the same name', async () => { const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, parseGraphQLController, log: defaultLogger, appId, }); - const car1 = new Parse.Object("Car"); + const car1 = new Parse.Object('Car'); await car1.save(); await parseGraphQLSchema.schemaCache.clear(); const schema1 = await parseGraphQLSchema.load(); const types1 = parseGraphQLSchema.graphQLTypes; const queries1 = parseGraphQLSchema.graphQLQueries; const mutations1 = parseGraphQLSchema.graphQLMutations; - const car2 = new Parse.Object("car"); + const car2 = new Parse.Object('car'); await car2.save(); await parseGraphQLSchema.schemaCache.clear(); const schema2 = await parseGraphQLSchema.load(); @@ -520,20 +520,20 @@ describe("ParseGraphQLSchema", () => { ); }); - it("should not generate duplicate queries when query name collide", async () => { + it('should not generate duplicate queries when query name collide', async () => { const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, parseGraphQLController, log: defaultLogger, appId, }); - const car = new Parse.Object("Car"); + const car = new Parse.Object('Car'); await car.save(); await parseGraphQLSchema.schemaCache.clear(); const schema1 = await parseGraphQLSchema.load(); const queries1 = parseGraphQLSchema.graphQLQueries; const mutations1 = parseGraphQLSchema.graphQLMutations; - const cars = new Parse.Object("cars"); + const cars = new Parse.Object('cars'); await cars.save(); await parseGraphQLSchema.schemaCache.clear(); const schema2 = await parseGraphQLSchema.load(); @@ -547,14 +547,14 @@ describe("ParseGraphQLSchema", () => { expect(mutations1).not.toBe(mutations2); expect( Object.keys(mutations1) - .concat("createCars", "updateCars", "deleteCars") + .concat('createCars', 'updateCars', 'deleteCars') .sort() ).toEqual(Object.keys(mutations2).sort()); }); }); - describe("alias", () => { - it_id("45282d26-f4c7-4d2d-a7b6-cd8741d5322f")(it)( - "Should be able to define alias for get and find query", + describe('alias', () => { + it_id('45282d26-f4c7-4d2d-a7b6-cd8741d5322f')(it)( + 'Should be able to define alias for get and find query', async () => { const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, @@ -566,18 +566,18 @@ describe("ParseGraphQLSchema", () => { await parseGraphQLSchema.parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "Data", + className: 'Data', query: { get: true, - getAlias: "precious_data", + getAlias: 'precious_data', find: true, - findAlias: "data_results", + findAlias: 'data_results', }, }, ], }); - const data = new Parse.Object("Data"); + const data = new Parse.Object('Data'); await data.save(); @@ -586,13 +586,13 @@ describe("ParseGraphQLSchema", () => { const queries1 = parseGraphQLSchema.graphQLQueries; - expect(Object.keys(queries1)).toContain("data_results"); - expect(Object.keys(queries1)).toContain("precious_data"); + expect(Object.keys(queries1)).toContain('data_results'); + expect(Object.keys(queries1)).toContain('precious_data'); } ); - it_id("f04b46e3-a25d-401d-a315-3298cfee1df8")(it)( - "Should be able to define alias for mutation", + it_id('f04b46e3-a25d-401d-a315-3298cfee1df8')(it)( + 'Should be able to define alias for mutation', async () => { const parseGraphQLSchema = new ParseGraphQLSchema({ databaseController, @@ -604,20 +604,20 @@ describe("ParseGraphQLSchema", () => { await parseGraphQLSchema.parseGraphQLController.updateGraphQLConfig({ classConfigs: [ { - className: "Track", + className: 'Track', mutation: { create: true, - createAlias: "addTrack", + createAlias: 'addTrack', update: true, - updateAlias: "modifyTrack", + updateAlias: 'modifyTrack', destroy: true, - destroyAlias: "eraseTrack", + destroyAlias: 'eraseTrack', }, }, ], }); - const data = new Parse.Object("Track"); + const data = new Parse.Object('Track'); await data.save(); @@ -626,9 +626,9 @@ describe("ParseGraphQLSchema", () => { const mutations = parseGraphQLSchema.graphQLMutations; - expect(Object.keys(mutations)).toContain("addTrack"); - expect(Object.keys(mutations)).toContain("modifyTrack"); - expect(Object.keys(mutations)).toContain("eraseTrack"); + expect(Object.keys(mutations)).toContain('addTrack'); + expect(Object.keys(mutations)).toContain('modifyTrack'); + expect(Object.keys(mutations)).toContain('eraseTrack'); } ); }); diff --git a/spec/ParseGraphQLServer.spec.js b/spec/ParseGraphQLServer.spec.js index 20e735f743..3fb4486a30 100644 --- a/spec/ParseGraphQLServer.spec.js +++ b/spec/ParseGraphQLServer.spec.js @@ -1,31 +1,31 @@ -const http = require("http"); -const express = require("express"); -const req = require("../lib/request"); +const http = require('http'); +const express = require('express'); +const req = require('../lib/request'); const fetch = (...args) => - import("node-fetch").then(({ default: fetch }) => fetch(...args)); -const FormData = require("form-data"); -const ws = require("ws"); -require("./helper"); -const { updateCLP } = require("./support/dev"); - -const pluralize = require("pluralize"); -const { getMainDefinition } = require("@apollo/client/utilities"); + import('node-fetch').then(({ default: fetch }) => fetch(...args)); +const FormData = require('form-data'); +const ws = require('ws'); +require('./helper'); +const { updateCLP } = require('./support/dev'); + +const pluralize = require('pluralize'); +const { getMainDefinition } = require('@apollo/client/utilities'); const createUploadLink = (...args) => - import("apollo-upload-client/createUploadLink.mjs").then(({ default: fn }) => + import('apollo-upload-client/createUploadLink.mjs').then(({ default: fn }) => fn(...args) ); -const { SubscriptionClient } = require("subscriptions-transport-ws"); -const { WebSocketLink } = require("@apollo/client/link/ws"); -const { mergeSchemas } = require("@graphql-tools/schema"); +const { SubscriptionClient } = require('subscriptions-transport-ws'); +const { WebSocketLink } = require('@apollo/client/link/ws'); +const { mergeSchemas } = require('@graphql-tools/schema'); const { ApolloClient, InMemoryCache, ApolloLink, split, createHttpLink, -} = require("@apollo/client/core"); -const gql = require("graphql-tag"); -const { toGlobalId } = require("graphql-relay"); +} = require('@apollo/client/core'); +const gql = require('graphql-tag'); +const { toGlobalId } = require('graphql-relay'); const { GraphQLObjectType, GraphQLString, @@ -34,11 +34,11 @@ const { GraphQLInputObjectType, GraphQLSchema, GraphQLList, -} = require("graphql"); -const { ParseServer } = require("../"); -const { ParseGraphQLServer } = require("../lib/GraphQL/ParseGraphQLServer"); -const { ReadPreference, Collection } = require("mongodb"); -const { v4: uuidv4 } = require("uuid"); +} = require('graphql'); +const { ParseServer } = require('../'); +const { ParseGraphQLServer } = require('../lib/GraphQL/ParseGraphQLServer'); +const { ReadPreference, Collection } = require('mongodb'); +const { v4: uuidv4 } = require('uuid'); function handleError(e) { if ( @@ -53,42 +53,42 @@ function handleError(e) { } } -describe("ParseGraphQLServer", () => { +describe('ParseGraphQLServer', () => { let parseServer; let parseGraphQLServer; beforeEach(async () => { parseServer = await global.reconfigureServer({ - maxUploadSize: "1kb", + maxUploadSize: '1kb', }); parseGraphQLServer = new ParseGraphQLServer(parseServer, { - graphQLPath: "/graphql", - playgroundPath: "/playground", - subscriptionsPath: "/subscriptions", + graphQLPath: '/graphql', + playgroundPath: '/playground', + subscriptionsPath: '/subscriptions', }); }); - describe("constructor", () => { - it("should require a parseServer instance", () => { + describe('constructor', () => { + it('should require a parseServer instance', () => { expect(() => new ParseGraphQLServer()).toThrow( - "You must provide a parseServer instance!" + 'You must provide a parseServer instance!' ); }); - it("should require config.graphQLPath", () => { + it('should require config.graphQLPath', () => { expect(() => new ParseGraphQLServer(parseServer)).toThrow( - "You must provide a config.graphQLPath!" + 'You must provide a config.graphQLPath!' ); expect(() => new ParseGraphQLServer(parseServer, {})).toThrow( - "You must provide a config.graphQLPath!" + 'You must provide a config.graphQLPath!' ); }); - it("should only require parseServer and config.graphQLPath args", () => { + it('should only require parseServer and config.graphQLPath args', () => { let parseGraphQLServer; expect(() => { parseGraphQLServer = new ParseGraphQLServer(parseServer, { - graphQLPath: "graphql", + graphQLPath: 'graphql', }); }).not.toThrow(); expect(parseGraphQLServer.parseGraphQLSchema).toBeDefined(); @@ -97,7 +97,7 @@ describe("ParseGraphQLServer", () => { ); }); - it("should initialize parseGraphQLSchema with a log controller", async () => { + it('should initialize parseGraphQLSchema with a log controller', async () => { const loggerAdapter = { log: () => {}, error: () => {}, @@ -106,7 +106,7 @@ describe("ParseGraphQLServer", () => { loggerAdapter, }); const parseGraphQLServer = new ParseGraphQLServer(parseServer, { - graphQLPath: "graphql", + graphQLPath: 'graphql', }); expect(parseGraphQLServer.parseGraphQLSchema.log.adapter).toBe( loggerAdapter @@ -114,15 +114,15 @@ describe("ParseGraphQLServer", () => { }); }); - describe("_getServer", () => { - it("should only return new server on schema changes", async () => { + describe('_getServer', () => { + it('should only return new server on schema changes', async () => { parseGraphQLServer.server = undefined; const server1 = await parseGraphQLServer._getServer(); const server2 = await parseGraphQLServer._getServer(); expect(server1).toBe(server2); // Trigger a schema change - const obj = new Parse.Object("SomeClass"); + const obj = new Parse.Object('SomeClass'); await obj.save(); const server3 = await parseGraphQLServer._getServer(); @@ -132,7 +132,7 @@ describe("ParseGraphQLServer", () => { }); }); - describe("_getGraphQLOptions", () => { + describe('_getGraphQLOptions', () => { const req = { info: new Object(), config: new Object(), @@ -143,7 +143,7 @@ describe("ParseGraphQLServer", () => { set: () => {}, }; - it_id("0696675e-060f-414f-bc77-9d57f31807f5")(it)( + it_id('0696675e-060f-414f-bc77-9d57f31807f5')(it)( "should return schema and context with req's info, config and auth", async () => { const options = await parseGraphQLServer._getGraphQLOptions(); @@ -157,7 +157,7 @@ describe("ParseGraphQLServer", () => { } ); - it("should load GraphQL schema in every call", async () => { + it('should load GraphQL schema in every call', async () => { const originalLoad = parseGraphQLServer.parseGraphQLSchema.load; let counter = 0; parseGraphQLServer.parseGraphQLSchema.load = () => ++counter; @@ -174,42 +174,42 @@ describe("ParseGraphQLServer", () => { }); }); - describe("_transformMaxUploadSizeToBytes", () => { - it("should transform to bytes", () => { - expect(parseGraphQLServer._transformMaxUploadSizeToBytes("20mb")).toBe( + describe('_transformMaxUploadSizeToBytes', () => { + it('should transform to bytes', () => { + expect(parseGraphQLServer._transformMaxUploadSizeToBytes('20mb')).toBe( 20971520 ); - expect(parseGraphQLServer._transformMaxUploadSizeToBytes("333Gb")).toBe( + expect(parseGraphQLServer._transformMaxUploadSizeToBytes('333Gb')).toBe( 357556027392 ); expect( - parseGraphQLServer._transformMaxUploadSizeToBytes("123456KB") + parseGraphQLServer._transformMaxUploadSizeToBytes('123456KB') ).toBe(126418944); }); }); - describe("applyGraphQL", () => { - it("should require an Express.js app instance", () => { + describe('applyGraphQL', () => { + it('should require an Express.js app instance', () => { expect(() => parseGraphQLServer.applyGraphQL()).toThrow( - "You must provide an Express.js app instance!" + 'You must provide an Express.js app instance!' ); expect(() => parseGraphQLServer.applyGraphQL({})).toThrow( - "You must provide an Express.js app instance!" + 'You must provide an Express.js app instance!' ); expect(() => parseGraphQLServer.applyGraphQL(new express()) ).not.toThrow(); }); - it("should apply middlewares at config.graphQLPath", () => { + it('should apply middlewares at config.graphQLPath', () => { let useCount = 0; expect(() => new ParseGraphQLServer(parseServer, { - graphQLPath: "somepath", + graphQLPath: 'somepath', }).applyGraphQL({ use: path => { useCount++; - expect(path).toEqual("somepath"); + expect(path).toEqual('somepath'); }, }) ).not.toThrow(); @@ -217,37 +217,37 @@ describe("ParseGraphQLServer", () => { }); }); - describe("applyPlayground", () => { - it("should require an Express.js app instance", () => { + describe('applyPlayground', () => { + it('should require an Express.js app instance', () => { expect(() => parseGraphQLServer.applyPlayground()).toThrow( - "You must provide an Express.js app instance!" + 'You must provide an Express.js app instance!' ); expect(() => parseGraphQLServer.applyPlayground({})).toThrow( - "You must provide an Express.js app instance!" + 'You must provide an Express.js app instance!' ); expect(() => parseGraphQLServer.applyPlayground(new express()) ).not.toThrow(); }); - it("should require initialization with config.playgroundPath", () => { + it('should require initialization with config.playgroundPath', () => { expect(() => new ParseGraphQLServer(parseServer, { - graphQLPath: "graphql", + graphQLPath: 'graphql', }).applyPlayground(new express()) - ).toThrow("You must provide a config.playgroundPath to applyPlayground!"); + ).toThrow('You must provide a config.playgroundPath to applyPlayground!'); }); - it("should apply middlewares at config.playgroundPath", () => { + it('should apply middlewares at config.playgroundPath', () => { let useCount = 0; expect(() => new ParseGraphQLServer(parseServer, { - graphQLPath: "graphQL", - playgroundPath: "somepath", + graphQLPath: 'graphQL', + playgroundPath: 'somepath', }).applyPlayground({ get: path => { useCount++; - expect(path).toEqual("somepath"); + expect(path).toEqual('somepath'); }, }) ).not.toThrow(); @@ -255,26 +255,26 @@ describe("ParseGraphQLServer", () => { }); }); - describe("createSubscriptions", () => { - it("should require initialization with config.subscriptionsPath", () => { + describe('createSubscriptions', () => { + it('should require initialization with config.subscriptionsPath', () => { expect(() => new ParseGraphQLServer(parseServer, { - graphQLPath: "graphql", + graphQLPath: 'graphql', }).createSubscriptions({}) ).toThrow( - "You must provide a config.subscriptionsPath to createSubscriptions!" + 'You must provide a config.subscriptionsPath to createSubscriptions!' ); }); }); - describe("setGraphQLConfig", () => { + describe('setGraphQLConfig', () => { let parseGraphQLServer; beforeEach(() => { parseGraphQLServer = new ParseGraphQLServer(parseServer, { - graphQLPath: "graphql", + graphQLPath: 'graphql', }); }); - it("should pass the graphQLConfig onto the parseGraphQLController", async () => { + it('should pass the graphQLConfig onto the parseGraphQLController', async () => { let received; parseGraphQLServer.parseGraphQLController = { async updateGraphQLConfig(graphQLConfig) { @@ -286,17 +286,17 @@ describe("ParseGraphQLServer", () => { await parseGraphQLServer.setGraphQLConfig(graphQLConfig); expect(received).toBe(graphQLConfig); }); - it("should not absorb exceptions from parseGraphQLController", async () => { + it('should not absorb exceptions from parseGraphQLController', async () => { parseGraphQLServer.parseGraphQLController = { async updateGraphQLConfig() { - throw new Error("Network request failed"); + throw new Error('Network request failed'); }, }; await expectAsync( parseGraphQLServer.setGraphQLConfig({}) - ).toBeRejectedWith(new Error("Network request failed")); + ).toBeRejectedWith(new Error('Network request failed')); }); - it("should return the response from parseGraphQLController", async () => { + it('should return the response from parseGraphQLController', async () => { parseGraphQLServer.parseGraphQLController = { async updateGraphQLConfig() { return { response: { result: true } }; @@ -310,12 +310,12 @@ describe("ParseGraphQLServer", () => { }); }); - describe("Auto API", () => { + describe('Auto API', () => { let httpServer; let parseLiveQueryServer; const headers = { - "X-Parse-Application-Id": "test", - "X-Parse-Javascript-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Javascript-Key': 'test', }; let apolloClient; @@ -336,40 +336,40 @@ describe("ParseGraphQLServer", () => { const acl = new Parse.ACL(); acl.setPublicReadAccess(true); user1 = new Parse.User(); - user1.setUsername("user1"); - user1.setPassword("user1"); - user1.setEmail("user1@user1.user1"); + user1.setUsername('user1'); + user1.setPassword('user1'); + user1.setEmail('user1@user1.user1'); user1.setACL(acl); await user1.signUp(); user2 = new Parse.User(); - user2.setUsername("user2"); - user2.setPassword("user2"); + user2.setUsername('user2'); + user2.setPassword('user2'); user2.setACL(acl); await user2.signUp(); user3 = new Parse.User(); - user3.setUsername("user3"); - user3.setPassword("user3"); + user3.setUsername('user3'); + user3.setPassword('user3'); user3.setACL(acl); await user3.signUp(); user4 = new Parse.User(); - user4.setUsername("user4"); - user4.setPassword("user4"); + user4.setUsername('user4'); + user4.setPassword('user4'); user4.setACL(acl); await user4.signUp(); user5 = new Parse.User(); - user5.setUsername("user5"); - user5.setPassword("user5"); + user5.setUsername('user5'); + user5.setPassword('user5'); user5.setACL(acl); await user5.signUp(); const roleACL = new Parse.ACL(); roleACL.setPublicReadAccess(true); role = new Parse.Role(); - role.setName("role"); + role.setName('role'); role.setACL(roleACL); role.getUsers().add(user1); role.getUsers().add(user3); @@ -379,59 +379,59 @@ describe("ParseGraphQLServer", () => { await parseServer.config.databaseController.loadSchema(); try { await schemaController.addClassIfNotExists( - "GraphQLClass", + 'GraphQLClass', { - someField: { type: "String" }, - pointerToUser: { type: "Pointer", targetClass: "_User" }, + someField: { type: 'String' }, + pointerToUser: { type: 'Pointer', targetClass: '_User' }, }, { find: { - "role:role": true, + 'role:role': true, [user1.id]: true, [user2.id]: true, }, create: { - "role:role": true, + 'role:role': true, [user1.id]: true, [user2.id]: true, }, get: { - "role:role": true, + 'role:role': true, [user1.id]: true, [user2.id]: true, }, update: { - "role:role": true, + 'role:role': true, [user1.id]: true, [user2.id]: true, }, addField: { - "role:role": true, + 'role:role': true, [user1.id]: true, [user2.id]: true, }, delete: { - "role:role": true, + 'role:role': true, [user1.id]: true, [user2.id]: true, }, - readUserFields: ["pointerToUser"], - writeUserFields: ["pointerToUser"], + readUserFields: ['pointerToUser'], + writeUserFields: ['pointerToUser'], }, {} ); } catch (err) { if ( !(err instanceof Parse.Error) || - err.message !== "Class GraphQLClass already exists." + err.message !== 'Class GraphQLClass already exists.' ) { throw err; } } - object1 = new Parse.Object("GraphQLClass"); - object1.set("someField", "someValue1"); - object1.set("someOtherField", "A"); + object1 = new Parse.Object('GraphQLClass'); + object1.set('someField', 'someValue1'); + object1.set('someOtherField', 'A'); const object1ACL = new Parse.ACL(); object1ACL.setPublicReadAccess(false); object1ACL.setPublicWriteAccess(false); @@ -444,9 +444,9 @@ describe("ParseGraphQLServer", () => { object1.setACL(object1ACL); await object1.save(undefined, { useMasterKey: true }); - object2 = new Parse.Object("GraphQLClass"); - object2.set("someField", "someValue2"); - object2.set("someOtherField", "A"); + object2 = new Parse.Object('GraphQLClass'); + object2.set('someField', 'someValue2'); + object2.set('someOtherField', 'A'); const object2ACL = new Parse.ACL(); object2ACL.setPublicReadAccess(false); object2ACL.setPublicWriteAccess(false); @@ -459,14 +459,14 @@ describe("ParseGraphQLServer", () => { object2.setACL(object2ACL); await object2.save(undefined, { useMasterKey: true }); - object3 = new Parse.Object("GraphQLClass"); - object3.set("someField", "someValue3"); - object3.set("someOtherField", "B"); - object3.set("pointerToUser", user5); + object3 = new Parse.Object('GraphQLClass'); + object3.set('someField', 'someValue3'); + object3.set('someOtherField', 'B'); + object3.set('pointerToUser', user5); await object3.save(undefined, { useMasterKey: true }); - object4 = new Parse.Object("PublicClass"); - object4.set("someField", "someValue4"); + object4 = new Parse.Object('PublicClass'); + object4.set('someField', 'someValue4'); await object4.save(); objects = []; @@ -482,7 +482,7 @@ describe("ParseGraphQLServer", () => { } const expressApp = express(); httpServer = http.createServer(expressApp); - expressApp.use("/parse", _parseServer.app); + expressApp.use('/parse', _parseServer.app); parseLiveQueryServer = await ParseServer.createLiveQueryServer( httpServer, { @@ -490,9 +490,9 @@ describe("ParseGraphQLServer", () => { } ); parseGraphQLServer = new ParseGraphQLServer(_parseServer, { - graphQLPath: "/graphql", - playgroundPath: "/playground", - subscriptionsPath: "/subscriptions", + graphQLPath: '/graphql', + playgroundPath: '/playground', + subscriptionsPath: '/subscriptions', }); parseGraphQLServer.applyGraphQL(expressApp); parseGraphQLServer.applyPlayground(expressApp); @@ -504,7 +504,7 @@ describe("ParseGraphQLServer", () => { await createGQLFromParseServer(parseServer); const subscriptionClient = new SubscriptionClient( - "ws://localhost:13377/subscriptions", + 'ws://localhost:13377/subscriptions', { reconnect: true, connectionParams: headers, @@ -513,7 +513,7 @@ describe("ParseGraphQLServer", () => { ); const wsLink = new WebSocketLink(subscriptionClient); const httpLink = await createUploadLink({ - uri: "http://localhost:13377/graphql", + uri: 'http://localhost:13377/graphql', fetch, headers, }); @@ -522,7 +522,7 @@ describe("ParseGraphQLServer", () => { ({ query }) => { const { kind, operation } = getMainDefinition(query); return ( - kind === "OperationDefinition" && operation === "subscription" + kind === 'OperationDefinition' && operation === 'subscription' ); }, wsLink, @@ -531,12 +531,12 @@ describe("ParseGraphQLServer", () => { cache: new InMemoryCache(), defaultOptions: { query: { - fetchPolicy: "no-cache", + fetchPolicy: 'no-cache', }, }, }); - spyOn(console, "warn").and.callFake(() => {}); - spyOn(console, "error").and.callFake(() => {}); + spyOn(console, 'warn').and.callFake(() => {}); + spyOn(console, 'error').and.callFake(() => {}); }); afterEach(async () => { @@ -544,8 +544,8 @@ describe("ParseGraphQLServer", () => { await httpServer.close(); }); - describe("GraphQL", () => { - it("should be healthy", async () => { + describe('GraphQL', () => { + it('should be healthy', async () => { try { const health = ( await apolloClient.query({ @@ -562,7 +562,7 @@ describe("ParseGraphQLServer", () => { } }); - it("should be cors enabled and scope the response within the source origin", async () => { + it('should be cors enabled and scope the response within the source origin', async () => { let checked = false; const apolloClient = new ApolloClient({ link: new ApolloLink((operation, forward) => { @@ -571,19 +571,19 @@ describe("ParseGraphQLServer", () => { const { response: { headers }, } = context; - expect(headers.get("access-control-allow-origin")).toEqual( - "http://example.com" + expect(headers.get('access-control-allow-origin')).toEqual( + 'http://example.com' ); checked = true; return response; }); }).concat( createHttpLink({ - uri: "http://localhost:13377/graphql", + uri: 'http://localhost:13377/graphql', fetch, headers: { ...headers, - Origin: "http://example.com", + Origin: 'http://example.com', }, }) ), @@ -600,7 +600,7 @@ describe("ParseGraphQLServer", () => { expect(checked).toBeTruthy(); }); - it("should handle Parse headers", async () => { + it('should handle Parse headers', async () => { const test = { context: ({ req: { info, config, auth } }) => { expect(req.info).toBeDefined(); @@ -613,7 +613,7 @@ describe("ParseGraphQLServer", () => { }; }, }; - const contextSpy = spyOn(test, "context"); + const contextSpy = spyOn(test, 'context'); const originalGetGraphQLOptions = parseGraphQLServer._getGraphQLOptions; parseGraphQLServer._getGraphQLOptions = async () => { return { @@ -636,17 +636,17 @@ describe("ParseGraphQLServer", () => { }); }); - describe("Playground", () => { - it("should mount playground", async () => { + describe('Playground', () => { + it('should mount playground', async () => { const res = await req({ - method: "GET", - url: "http://localhost:13377/playground", + method: 'GET', + url: 'http://localhost:13377/playground', }); expect(res.status).toEqual(200); }); }); - describe("Schema", () => { + describe('Schema', () => { const resetGraphQLCache = async () => { await Promise.all([ parseGraphQLServer.parseGraphQLController.cacheController.graphQL.clear(), @@ -654,8 +654,8 @@ describe("ParseGraphQLServer", () => { ]); }; - describe("Default Types", () => { - it("should have Object scalar type", async () => { + describe('Default Types', () => { + it('should have Object scalar type', async () => { const objectType = ( await apolloClient.query({ query: gql` @@ -666,11 +666,11 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"]; - expect(objectType.kind).toEqual("SCALAR"); + ).data['__type']; + expect(objectType.kind).toEqual('SCALAR'); }); - it("should have Date scalar type", async () => { + it('should have Date scalar type', async () => { const dateType = ( await apolloClient.query({ query: gql` @@ -681,11 +681,11 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"]; - expect(dateType.kind).toEqual("SCALAR"); + ).data['__type']; + expect(dateType.kind).toEqual('SCALAR'); }); - it("should have ArrayResult type", async () => { + it('should have ArrayResult type', async () => { const arrayResultType = ( await apolloClient.query({ query: gql` @@ -696,11 +696,11 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"]; - expect(arrayResultType.kind).toEqual("UNION"); + ).data['__type']; + expect(arrayResultType.kind).toEqual('UNION'); }); - it("should have File object type", async () => { + it('should have File object type', async () => { const fileType = ( await apolloClient.query({ query: gql` @@ -714,15 +714,15 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"]; - expect(fileType.kind).toEqual("OBJECT"); + ).data['__type']; + expect(fileType.kind).toEqual('OBJECT'); expect(fileType.fields.map(field => field.name).sort()).toEqual([ - "name", - "url", + 'name', + 'url', ]); }); - it("should have Class interface type", async () => { + it('should have Class interface type', async () => { const classType = ( await apolloClient.query({ query: gql` @@ -736,17 +736,17 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"]; - expect(classType.kind).toEqual("INTERFACE"); + ).data['__type']; + expect(classType.kind).toEqual('INTERFACE'); expect(classType.fields.map(field => field.name).sort()).toEqual([ - "ACL", - "createdAt", - "objectId", - "updatedAt", + 'ACL', + 'createdAt', + 'objectId', + 'updatedAt', ]); }); - it("should have ReadPreference enum type", async () => { + it('should have ReadPreference enum type', async () => { const readPreferenceType = ( await apolloClient.query({ query: gql` @@ -760,20 +760,20 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"]; - expect(readPreferenceType.kind).toEqual("ENUM"); + ).data['__type']; + expect(readPreferenceType.kind).toEqual('ENUM'); expect( readPreferenceType.enumValues.map(value => value.name).sort() ).toEqual([ - "NEAREST", - "PRIMARY", - "PRIMARY_PREFERRED", - "SECONDARY", - "SECONDARY_PREFERRED", + 'NEAREST', + 'PRIMARY', + 'PRIMARY_PREFERRED', + 'SECONDARY', + 'SECONDARY_PREFERRED', ]); }); - it("should have GraphQLUpload object type", async () => { + it('should have GraphQLUpload object type', async () => { const graphQLUploadType = ( await apolloClient.query({ query: gql` @@ -787,11 +787,11 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"]; - expect(graphQLUploadType.kind).toEqual("SCALAR"); + ).data['__type']; + expect(graphQLUploadType.kind).toEqual('SCALAR'); }); - it("should have all expected types", async () => { + it('should have all expected types', async () => { const schemaTypes = ( await apolloClient.query({ query: gql` @@ -804,14 +804,14 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__schema"].types.map(type => type.name); + ).data['__schema'].types.map(type => type.name); const expectedTypes = [ - "ParseObject", - "Date", - "FileInfo", - "ReadPreference", - "Upload", + 'ParseObject', + 'Date', + 'FileInfo', + 'ReadPreference', + 'Upload', ]; expect( expectedTypes.every(type => schemaTypes.indexOf(type) !== -1) @@ -819,7 +819,7 @@ describe("ParseGraphQLServer", () => { }); }); - describe("Relay Specific Types", () => { + describe('Relay Specific Types', () => { let clearCache; beforeEach(async () => { if (!clearCache) { @@ -828,7 +828,7 @@ describe("ParseGraphQLServer", () => { } }); - it("should have Node interface", async () => { + it('should have Node interface', async () => { const schemaTypes = ( await apolloClient.query({ query: gql` @@ -841,12 +841,12 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__schema"].types.map(type => type.name); + ).data['__schema'].types.map(type => type.name); - expect(schemaTypes).toContain("Node"); + expect(schemaTypes).toContain('Node'); }); - it("should have node query", async () => { + it('should have node query', async () => { const queryFields = ( await apolloClient.query({ query: gql` @@ -859,12 +859,12 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"].fields.map(field => field.name); + ).data['__type'].fields.map(field => field.name); - expect(queryFields).toContain("node"); + expect(queryFields).toContain('node'); }); - it("should return global id", async () => { + it('should return global id', async () => { const userFields = ( await apolloClient.query({ query: gql` @@ -877,13 +877,13 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"].fields.map(field => field.name); + ).data['__type'].fields.map(field => field.name); - expect(userFields).toContain("id"); - expect(userFields).toContain("objectId"); + expect(userFields).toContain('id'); + expect(userFields).toContain('objectId'); }); - it("should have clientMutationId in create file input", async () => { + it('should have clientMutationId in create file input', async () => { const createFileInputFields = ( await apolloClient.query({ query: gql` @@ -896,14 +896,14 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"].inputFields + ).data['__type'].inputFields .map(field => field.name) .sort(); - expect(createFileInputFields).toEqual(["clientMutationId", "upload"]); + expect(createFileInputFields).toEqual(['clientMutationId', 'upload']); }); - it("should have clientMutationId in create file payload", async () => { + it('should have clientMutationId in create file payload', async () => { const createFilePayloadFields = ( await apolloClient.query({ query: gql` @@ -916,18 +916,18 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"].fields + ).data['__type'].fields .map(field => field.name) .sort(); expect(createFilePayloadFields).toEqual([ - "clientMutationId", - "fileInfo", + 'clientMutationId', + 'fileInfo', ]); }); - it("should have clientMutationId in call function input", async () => { - Parse.Cloud.define("hello", () => {}); + it('should have clientMutationId in call function input', async () => { + Parse.Cloud.define('hello', () => {}); const callFunctionInputFields = ( await apolloClient.query({ @@ -941,19 +941,19 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"].inputFields + ).data['__type'].inputFields .map(field => field.name) .sort(); expect(callFunctionInputFields).toEqual([ - "clientMutationId", - "functionName", - "params", + 'clientMutationId', + 'functionName', + 'params', ]); }); - it("should have clientMutationId in call function payload", async () => { - Parse.Cloud.define("hello", () => {}); + it('should have clientMutationId in call function payload', async () => { + Parse.Cloud.define('hello', () => {}); const callFunctionPayloadFields = ( await apolloClient.query({ @@ -967,17 +967,17 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"].fields + ).data['__type'].fields .map(field => field.name) .sort(); expect(callFunctionPayloadFields).toEqual([ - "clientMutationId", - "result", + 'clientMutationId', + 'result', ]); }); - it("should have clientMutationId in sign up mutation input", async () => { + it('should have clientMutationId in sign up mutation input', async () => { const inputFields = ( await apolloClient.query({ query: gql` @@ -990,14 +990,14 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"].inputFields + ).data['__type'].inputFields .map(field => field.name) .sort(); - expect(inputFields).toEqual(["clientMutationId", "fields"]); + expect(inputFields).toEqual(['clientMutationId', 'fields']); }); - it("should have clientMutationId in sign up mutation payload", async () => { + it('should have clientMutationId in sign up mutation payload', async () => { const payloadFields = ( await apolloClient.query({ query: gql` @@ -1010,14 +1010,14 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"].fields + ).data['__type'].fields .map(field => field.name) .sort(); - expect(payloadFields).toEqual(["clientMutationId", "viewer"]); + expect(payloadFields).toEqual(['clientMutationId', 'viewer']); }); - it("should have clientMutationId in log in mutation input", async () => { + it('should have clientMutationId in log in mutation input', async () => { const inputFields = ( await apolloClient.query({ query: gql` @@ -1030,18 +1030,18 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"].inputFields + ).data['__type'].inputFields .map(field => field.name) .sort(); expect(inputFields).toEqual([ - "authData", - "clientMutationId", - "password", - "username", + 'authData', + 'clientMutationId', + 'password', + 'username', ]); }); - it("should have clientMutationId in log in mutation payload", async () => { + it('should have clientMutationId in log in mutation payload', async () => { const payloadFields = ( await apolloClient.query({ query: gql` @@ -1054,14 +1054,14 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"].fields + ).data['__type'].fields .map(field => field.name) .sort(); - expect(payloadFields).toEqual(["clientMutationId", "viewer"]); + expect(payloadFields).toEqual(['clientMutationId', 'viewer']); }); - it("should have clientMutationId in log out mutation input", async () => { + it('should have clientMutationId in log out mutation input', async () => { const inputFields = ( await apolloClient.query({ query: gql` @@ -1074,14 +1074,14 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"].inputFields + ).data['__type'].inputFields .map(field => field.name) .sort(); - expect(inputFields).toEqual(["clientMutationId"]); + expect(inputFields).toEqual(['clientMutationId']); }); - it("should have clientMutationId in log out mutation payload", async () => { + it('should have clientMutationId in log out mutation payload', async () => { const payloadFields = ( await apolloClient.query({ query: gql` @@ -1094,14 +1094,14 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"].fields + ).data['__type'].fields .map(field => field.name) .sort(); - expect(payloadFields).toEqual(["clientMutationId", "ok"]); + expect(payloadFields).toEqual(['clientMutationId', 'ok']); }); - it("should have clientMutationId in createClass mutation input", async () => { + it('should have clientMutationId in createClass mutation input', async () => { const inputFields = ( await apolloClient.query({ query: gql` @@ -1114,18 +1114,18 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"].inputFields + ).data['__type'].inputFields .map(field => field.name) .sort(); expect(inputFields).toEqual([ - "clientMutationId", - "name", - "schemaFields", + 'clientMutationId', + 'name', + 'schemaFields', ]); }); - it("should have clientMutationId in createClass mutation payload", async () => { + it('should have clientMutationId in createClass mutation payload', async () => { const payloadFields = ( await apolloClient.query({ query: gql` @@ -1138,14 +1138,14 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"].fields + ).data['__type'].fields .map(field => field.name) .sort(); - expect(payloadFields).toEqual(["class", "clientMutationId"]); + expect(payloadFields).toEqual(['class', 'clientMutationId']); }); - it("should have clientMutationId in updateClass mutation input", async () => { + it('should have clientMutationId in updateClass mutation input', async () => { const inputFields = ( await apolloClient.query({ query: gql` @@ -1158,18 +1158,18 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"].inputFields + ).data['__type'].inputFields .map(field => field.name) .sort(); expect(inputFields).toEqual([ - "clientMutationId", - "name", - "schemaFields", + 'clientMutationId', + 'name', + 'schemaFields', ]); }); - it("should have clientMutationId in updateClass mutation payload", async () => { + it('should have clientMutationId in updateClass mutation payload', async () => { const payloadFields = ( await apolloClient.query({ query: gql` @@ -1182,14 +1182,14 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"].fields + ).data['__type'].fields .map(field => field.name) .sort(); - expect(payloadFields).toEqual(["class", "clientMutationId"]); + expect(payloadFields).toEqual(['class', 'clientMutationId']); }); - it("should have clientMutationId in deleteClass mutation input", async () => { + it('should have clientMutationId in deleteClass mutation input', async () => { const inputFields = ( await apolloClient.query({ query: gql` @@ -1202,14 +1202,14 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"].inputFields + ).data['__type'].inputFields .map(field => field.name) .sort(); - expect(inputFields).toEqual(["clientMutationId", "name"]); + expect(inputFields).toEqual(['clientMutationId', 'name']); }); - it("should have clientMutationId in deleteClass mutation payload", async () => { + it('should have clientMutationId in deleteClass mutation payload', async () => { const payloadFields = ( await apolloClient.query({ query: gql` @@ -1222,15 +1222,15 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"].fields + ).data['__type'].fields .map(field => field.name) .sort(); - expect(payloadFields).toEqual(["class", "clientMutationId"]); + expect(payloadFields).toEqual(['class', 'clientMutationId']); }); - it("should have clientMutationId in custom create object mutation input", async () => { - const obj = new Parse.Object("SomeClass"); + it('should have clientMutationId in custom create object mutation input', async () => { + const obj = new Parse.Object('SomeClass'); await obj.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -1247,18 +1247,18 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"].inputFields + ).data['__type'].inputFields .map(field => field.name) .sort(); expect(createObjectInputFields).toEqual([ - "clientMutationId", - "fields", + 'clientMutationId', + 'fields', ]); }); - it("should have clientMutationId in custom create object mutation payload", async () => { - const obj = new Parse.Object("SomeClass"); + it('should have clientMutationId in custom create object mutation payload', async () => { + const obj = new Parse.Object('SomeClass'); await obj.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -1275,18 +1275,18 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"].fields + ).data['__type'].fields .map(field => field.name) .sort(); expect(createObjectPayloadFields).toEqual([ - "clientMutationId", - "someClass", + 'clientMutationId', + 'someClass', ]); }); - it("should have clientMutationId in custom update object mutation input", async () => { - const obj = new Parse.Object("SomeClass"); + it('should have clientMutationId in custom update object mutation input', async () => { + const obj = new Parse.Object('SomeClass'); await obj.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -1303,19 +1303,19 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"].inputFields + ).data['__type'].inputFields .map(field => field.name) .sort(); expect(createObjectInputFields).toEqual([ - "clientMutationId", - "fields", - "id", + 'clientMutationId', + 'fields', + 'id', ]); }); - it("should have clientMutationId in custom update object mutation payload", async () => { - const obj = new Parse.Object("SomeClass"); + it('should have clientMutationId in custom update object mutation payload', async () => { + const obj = new Parse.Object('SomeClass'); await obj.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -1332,18 +1332,18 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"].fields + ).data['__type'].fields .map(field => field.name) .sort(); expect(createObjectPayloadFields).toEqual([ - "clientMutationId", - "someClass", + 'clientMutationId', + 'someClass', ]); }); - it("should have clientMutationId in custom delete object mutation input", async () => { - const obj = new Parse.Object("SomeClass"); + it('should have clientMutationId in custom delete object mutation input', async () => { + const obj = new Parse.Object('SomeClass'); await obj.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -1360,15 +1360,15 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"].inputFields + ).data['__type'].inputFields .map(field => field.name) .sort(); - expect(createObjectInputFields).toEqual(["clientMutationId", "id"]); + expect(createObjectInputFields).toEqual(['clientMutationId', 'id']); }); - it("should have clientMutationId in custom delete object mutation payload", async () => { - const obj = new Parse.Object("SomeClass"); + it('should have clientMutationId in custom delete object mutation payload', async () => { + const obj = new Parse.Object('SomeClass'); await obj.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -1385,19 +1385,19 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"].fields + ).data['__type'].fields .map(field => field.name) .sort(); expect(createObjectPayloadFields).toEqual([ - "clientMutationId", - "someClass", + 'clientMutationId', + 'someClass', ]); }); }); - describe("Parse Class Types", () => { - it("should have all expected types", async () => { + describe('Parse Class Types', () => { + it('should have all expected types', async () => { await parseServer.config.databaseController.loadSchema(); const schemaTypes = ( @@ -1412,26 +1412,26 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__schema"].types.map(type => type.name); + ).data['__schema'].types.map(type => type.name); const expectedTypes = [ - "Role", - "RoleWhereInput", - "CreateRoleFieldsInput", - "UpdateRoleFieldsInput", - "RoleConnection", - "User", - "UserWhereInput", - "UserConnection", - "CreateUserFieldsInput", - "UpdateUserFieldsInput", + 'Role', + 'RoleWhereInput', + 'CreateRoleFieldsInput', + 'UpdateRoleFieldsInput', + 'RoleConnection', + 'User', + 'UserWhereInput', + 'UserConnection', + 'CreateUserFieldsInput', + 'UpdateUserFieldsInput', ]; expect( expectedTypes.every(type => schemaTypes.indexOf(type) !== -1) ).toBeTruthy(JSON.stringify(schemaTypes)); }); - it("should ArrayResult contains all types", async () => { + it('should ArrayResult contains all types', async () => { const objectType = ( await apolloClient.query({ query: gql` @@ -1445,18 +1445,18 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"]; + ).data['__type']; const possibleTypes = objectType.possibleTypes.map(o => o.name); - expect(possibleTypes).toContain("User"); - expect(possibleTypes).toContain("Role"); - expect(possibleTypes).toContain("Element"); + expect(possibleTypes).toContain('User'); + expect(possibleTypes).toContain('Role'); + expect(possibleTypes).toContain('Element'); }); - it("should update schema when it changes", async () => { + it('should update schema when it changes', async () => { const schemaController = await parseServer.config.databaseController.loadSchema(); - await schemaController.updateClass("_User", { - foo: { type: "String" }, + await schemaController.updateClass('_User', { + foo: { type: 'String' }, }); const userFields = ( @@ -1471,11 +1471,11 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"].fields.map(field => field.name); - expect(userFields.indexOf("foo") !== -1).toBeTruthy(); + ).data['__type'].fields.map(field => field.name); + expect(userFields.indexOf('foo') !== -1).toBeTruthy(); }); - it("should not contain password field from _User class", async () => { + it('should not contain password field from _User class', async () => { const userFields = ( await apolloClient.query({ query: gql` @@ -1488,12 +1488,12 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"].fields.map(field => field.name); - expect(userFields.includes("password")).toBeFalsy(); + ).data['__type'].fields.map(field => field.name); + expect(userFields.includes('password')).toBeFalsy(); }); }); - describe("Configuration", function () { + describe('Configuration', function () { const resetGraphQLCache = async () => { await Promise.all([ parseGraphQLServer.parseGraphQLController.cacheController.graphQL.clear(), @@ -1506,17 +1506,17 @@ describe("ParseGraphQLServer", () => { await resetGraphQLCache(); }); - it_id("d6a23a2f-ca18-4b15-bc73-3e636f99e6bc")(it)( - "should only include types in the enabledForClasses list", + it_id('d6a23a2f-ca18-4b15-bc73-3e636f99e6bc')(it)( + 'should only include types in the enabledForClasses list', async () => { const schemaController = await parseServer.config.databaseController.loadSchema(); - await schemaController.addClassIfNotExists("SuperCar", { - foo: { type: "String" }, + await schemaController.addClassIfNotExists('SuperCar', { + foo: { type: 'String' }, }); const graphQLConfig = { - enabledForClasses: ["SuperCar"], + enabledForClasses: ['SuperCar'], }; await parseGraphQLServer.setGraphQLConfig(graphQLConfig); await resetGraphQLCache(); @@ -1541,17 +1541,17 @@ describe("ParseGraphQLServer", () => { expect(data.superCarType).toBeTruthy(); } ); - it_id("1db2aceb-d24e-4929-ba43-8dbb5d0395e1")(it)( - "should not include types in the disabledForClasses list", + it_id('1db2aceb-d24e-4929-ba43-8dbb5d0395e1')(it)( + 'should not include types in the disabledForClasses list', async () => { const schemaController = await parseServer.config.databaseController.loadSchema(); - await schemaController.addClassIfNotExists("SuperCar", { - foo: { type: "String" }, + await schemaController.addClassIfNotExists('SuperCar', { + foo: { type: 'String' }, }); const graphQLConfig = { - disabledForClasses: ["SuperCar"], + disabledForClasses: ['SuperCar'], }; await parseGraphQLServer.setGraphQLConfig(graphQLConfig); await resetGraphQLCache(); @@ -1576,13 +1576,13 @@ describe("ParseGraphQLServer", () => { expect(data.userType).toBeTruthy(); } ); - it_id("85c2e02f-0239-4819-b66e-392e0125f6c5")(it)( - "should remove query operations when disabled", + it_id('85c2e02f-0239-4819-b66e-392e0125f6c5')(it)( + 'should remove query operations when disabled', async () => { - const superCar = new Parse.Object("SuperCar"); - await superCar.save({ foo: "bar" }); - const customer = new Parse.Object("Customer"); - await customer.save({ foo: "bar" }); + const superCar = new Parse.Object('SuperCar'); + await superCar.save({ foo: 'bar' }); + const customer = new Parse.Object('Customer'); + await customer.save({ foo: 'bar' }); await expectAsync( apolloClient.query({ @@ -1614,14 +1614,14 @@ describe("ParseGraphQLServer", () => { const graphQLConfig = { classConfigs: [ { - className: "SuperCar", + className: 'SuperCar', query: { get: false, find: true, }, }, { - className: "Customer", + className: 'Customer', query: { get: true, find: false, @@ -1685,13 +1685,13 @@ describe("ParseGraphQLServer", () => { } ); - it_id("972161a6-8108-4e99-a1a5-71d0267d26c2")(it)( - "should remove mutation operations, create, update and delete, when disabled", + it_id('972161a6-8108-4e99-a1a5-71d0267d26c2')(it)( + 'should remove mutation operations, create, update and delete, when disabled', async () => { - const superCar1 = new Parse.Object("SuperCar"); - await superCar1.save({ foo: "bar" }); - const customer1 = new Parse.Object("Customer"); - await customer1.save({ foo: "bar" }); + const superCar1 = new Parse.Object('SuperCar'); + await superCar1.save({ foo: 'bar' }); + const customer1 = new Parse.Object('Customer'); + await customer1.save({ foo: 'bar' }); await expectAsync( apolloClient.query({ @@ -1704,7 +1704,7 @@ describe("ParseGraphQLServer", () => { `, variables: { id: superCar1.id, - foo: "lah", + foo: 'lah', }, }) ).toBeResolved(); @@ -1735,7 +1735,7 @@ describe("ParseGraphQLServer", () => { } `, variables: { - foo: "rah", + foo: 'rah', }, }); expect(customerData.createCustomer.customer).toBeTruthy(); @@ -1746,7 +1746,7 @@ describe("ParseGraphQLServer", () => { await parseGraphQLServer.setGraphQLConfig({ classConfigs: [ { - className: "SuperCar", + className: 'SuperCar', mutation: { create: true, update: false, @@ -1754,7 +1754,7 @@ describe("ParseGraphQLServer", () => { }, }, { - className: "Customer", + className: 'Customer', mutation: { create: false, update: true, @@ -1776,7 +1776,7 @@ describe("ParseGraphQLServer", () => { } `, variables: { - foo: "mah", + foo: 'mah', }, }); expect(superCarData.createSuperCar).toBeTruthy(); @@ -1824,7 +1824,7 @@ describe("ParseGraphQLServer", () => { } `, variables: { - foo: "rah", + foo: 'rah', }, }) ).toBeRejected(); @@ -1839,7 +1839,7 @@ describe("ParseGraphQLServer", () => { `, variables: { id: customer2Id, - foo: "tah", + foo: 'tah', }, }) ).toBeResolved(); @@ -1860,26 +1860,26 @@ describe("ParseGraphQLServer", () => { } ); - it_id("4af763b1-ff86-43c7-ba30-060a1c07e730")(it)( - "should only allow the supplied create and update fields for a class", + it_id('4af763b1-ff86-43c7-ba30-060a1c07e730')(it)( + 'should only allow the supplied create and update fields for a class', async () => { const schemaController = await parseServer.config.databaseController.loadSchema(); - await schemaController.addClassIfNotExists("SuperCar", { - engine: { type: "String" }, - doors: { type: "Number" }, - price: { type: "String" }, - mileage: { type: "Number" }, + await schemaController.addClassIfNotExists('SuperCar', { + engine: { type: 'String' }, + doors: { type: 'Number' }, + price: { type: 'String' }, + mileage: { type: 'Number' }, }); await parseGraphQLServer.setGraphQLConfig({ classConfigs: [ { - className: "SuperCar", + className: 'SuperCar', type: { inputFields: { - create: ["engine", "doors", "price"], - update: ["price", "mileage"], + create: ['engine', 'doors', 'price'], + update: ['price', 'mileage'], }, }, }, @@ -1960,16 +1960,16 @@ describe("ParseGraphQLServer", () => { } ); - it_id("fc9237e9-3e63-4b55-9c1d-e6269f613a93")(it)( - "should handle required fields from the Parse class", + it_id('fc9237e9-3e63-4b55-9c1d-e6269f613a93')(it)( + 'should handle required fields from the Parse class', async () => { const schemaController = await parseServer.config.databaseController.loadSchema(); - await schemaController.addClassIfNotExists("SuperCar", { - engine: { type: "String", required: true }, - doors: { type: "Number", required: true }, - price: { type: "String" }, - mileage: { type: "Number" }, + await schemaController.addClassIfNotExists('SuperCar', { + engine: { type: 'String', required: true }, + doors: { type: 'Number', required: true }, + price: { type: 'String' }, + mileage: { type: 'Number' }, }); await resetGraphQLCache(); @@ -1991,14 +1991,14 @@ describe("ParseGraphQLServer", () => { `, }); expect( - __type.inputFields.find(o => o.name === "price").type.kind - ).toEqual("SCALAR"); + __type.inputFields.find(o => o.name === 'price').type.kind + ).toEqual('SCALAR'); expect( - __type.inputFields.find(o => o.name === "engine").type.kind - ).toEqual("NON_NULL"); + __type.inputFields.find(o => o.name === 'engine').type.kind + ).toEqual('NON_NULL'); expect( - __type.inputFields.find(o => o.name === "doors").type.kind - ).toEqual("NON_NULL"); + __type.inputFields.find(o => o.name === 'doors').type.kind + ).toEqual('NON_NULL'); const { data: { __type: __type2 }, @@ -2017,45 +2017,45 @@ describe("ParseGraphQLServer", () => { `, }); expect( - __type2.fields.find(o => o.name === "price").type.kind - ).toEqual("SCALAR"); + __type2.fields.find(o => o.name === 'price').type.kind + ).toEqual('SCALAR'); expect( - __type2.fields.find(o => o.name === "engine").type.kind - ).toEqual("NON_NULL"); + __type2.fields.find(o => o.name === 'engine').type.kind + ).toEqual('NON_NULL'); expect( - __type2.fields.find(o => o.name === "doors").type.kind - ).toEqual("NON_NULL"); + __type2.fields.find(o => o.name === 'doors').type.kind + ).toEqual('NON_NULL'); } ); - it_id("83b6895a-7dfd-4e3b-a5ce-acdb1fa39705")(it)( - "should only allow the supplied output fields for a class", + it_id('83b6895a-7dfd-4e3b-a5ce-acdb1fa39705')(it)( + 'should only allow the supplied output fields for a class', async () => { const schemaController = await parseServer.config.databaseController.loadSchema(); - await schemaController.addClassIfNotExists("SuperCar", { - engine: { type: "String" }, - doors: { type: "Number" }, - price: { type: "String" }, - mileage: { type: "Number" }, - insuranceClaims: { type: "Number" }, + await schemaController.addClassIfNotExists('SuperCar', { + engine: { type: 'String' }, + doors: { type: 'Number' }, + price: { type: 'String' }, + mileage: { type: 'Number' }, + insuranceClaims: { type: 'Number' }, }); - const superCar = await new Parse.Object("SuperCar").save({ - engine: "petrol", + const superCar = await new Parse.Object('SuperCar').save({ + engine: 'petrol', doors: 3, - price: "£7500", + price: '£7500', mileage: 0, - insuranceCertificate: "private-file.pdf", + insuranceCertificate: 'private-file.pdf', }); await parseGraphQLServer.setGraphQLConfig({ classConfigs: [ { - className: "SuperCar", + className: 'SuperCar', type: { - outputFields: ["engine", "doors", "price", "mileage"], + outputFields: ['engine', 'doors', 'price', 'mileage'], }, }, ], @@ -2107,7 +2107,7 @@ describe("ParseGraphQLServer", () => { await parseGraphQLServer.setGraphQLConfig({ classConfigs: [ { - className: "SuperCar", + className: 'SuperCar', type: { outputFields: [], }, @@ -2149,37 +2149,37 @@ describe("ParseGraphQLServer", () => { } ); - it_id("67dfcf94-92fb-45a3-a012-3b22c81899ba")(it)( - "should only allow the supplied constraint fields for a class", + it_id('67dfcf94-92fb-45a3-a012-3b22c81899ba')(it)( + 'should only allow the supplied constraint fields for a class', async () => { try { const schemaController = await parseServer.config.databaseController.loadSchema(); - await schemaController.addClassIfNotExists("SuperCar", { - model: { type: "String" }, - engine: { type: "String" }, - doors: { type: "Number" }, - price: { type: "String" }, - mileage: { type: "Number" }, - insuranceCertificate: { type: "String" }, + await schemaController.addClassIfNotExists('SuperCar', { + model: { type: 'String' }, + engine: { type: 'String' }, + doors: { type: 'Number' }, + price: { type: 'String' }, + mileage: { type: 'Number' }, + insuranceCertificate: { type: 'String' }, }); - await new Parse.Object("SuperCar").save({ - model: "McLaren", - engine: "petrol", + await new Parse.Object('SuperCar').save({ + model: 'McLaren', + engine: 'petrol', doors: 3, - price: "£7500", + price: '£7500', mileage: 0, - insuranceCertificate: "private-file.pdf", + insuranceCertificate: 'private-file.pdf', }); await parseGraphQLServer.setGraphQLConfig({ classConfigs: [ { - className: "SuperCar", + className: 'SuperCar', type: { - constraintFields: ["engine", "doors", "price"], + constraintFields: ['engine', 'doors', 'price'], }, }, ], @@ -2232,44 +2232,44 @@ describe("ParseGraphQLServer", () => { } ); - it_id("a3bdbd5d-8779-42fe-91a1-7a7f90a6177b")(it)( - "should only allow the supplied sort fields for a class", + it_id('a3bdbd5d-8779-42fe-91a1-7a7f90a6177b')(it)( + 'should only allow the supplied sort fields for a class', async () => { const schemaController = await parseServer.config.databaseController.loadSchema(); - await schemaController.addClassIfNotExists("SuperCar", { - engine: { type: "String" }, - doors: { type: "Number" }, - price: { type: "String" }, - mileage: { type: "Number" }, + await schemaController.addClassIfNotExists('SuperCar', { + engine: { type: 'String' }, + doors: { type: 'Number' }, + price: { type: 'String' }, + mileage: { type: 'Number' }, }); - await new Parse.Object("SuperCar").save({ - engine: "petrol", + await new Parse.Object('SuperCar').save({ + engine: 'petrol', doors: 3, - price: "£7500", + price: '£7500', mileage: 0, }); await parseGraphQLServer.setGraphQLConfig({ classConfigs: [ { - className: "SuperCar", + className: 'SuperCar', type: { sortFields: [ { - field: "doors", + field: 'doors', asc: true, desc: true, }, { - field: "price", + field: 'price', asc: true, desc: true, }, { - field: "mileage", + field: 'mileage', asc: true, desc: false, }, @@ -2391,15 +2391,15 @@ describe("ParseGraphQLServer", () => { ); }); - describe("Relay Spec", () => { + describe('Relay Spec', () => { beforeEach(async () => { await resetGraphQLCache(); }); - describe("Object Identification", () => { - it("Class get custom method should return valid gobal id", async () => { - const obj = new Parse.Object("SomeClass"); - obj.set("someField", "some value"); + describe('Object Identification', () => { + it('Class get custom method should return valid gobal id', async () => { + const obj = new Parse.Object('SomeClass'); + obj.set('someField', 'some value'); await obj.save(); const getResult = await apolloClient.query({ @@ -2437,16 +2437,16 @@ describe("ParseGraphQLServer", () => { expect(nodeResult.data.node.id).toBe(getResult.data.someClass.id); expect(nodeResult.data.node.objectId).toBe(obj.id); - expect(nodeResult.data.node.someField).toBe("some value"); + expect(nodeResult.data.node.someField).toBe('some value'); }); - it("Class find custom method should return valid gobal id", async () => { - const obj1 = new Parse.Object("SomeClass"); - obj1.set("someField", "some value 1"); + it('Class find custom method should return valid gobal id', async () => { + const obj1 = new Parse.Object('SomeClass'); + obj1.set('someField', 'some value 1'); await obj1.save(); - const obj2 = new Parse.Object("SomeClass"); - obj2.set("someField", "some value 2"); + const obj2 = new Parse.Object('SomeClass'); + obj2.set('someField', 'some value 2'); await obj2.save(); const findResult = await apolloClient.query({ @@ -2500,14 +2500,14 @@ describe("ParseGraphQLServer", () => { findResult.data.someClasses.edges[0].node.id ); expect(nodeResult.data.node1.objectId).toBe(obj1.id); - expect(nodeResult.data.node1.someField).toBe("some value 1"); + expect(nodeResult.data.node1.someField).toBe('some value 1'); expect(nodeResult.data.node2.id).toBe( findResult.data.someClasses.edges[1].node.id ); expect(nodeResult.data.node2.objectId).toBe(obj2.id); - expect(nodeResult.data.node2.someField).toBe("some value 2"); + expect(nodeResult.data.node2.someField).toBe('some value 2'); }); - it("Id inputs should work either with global id or object id", async () => { + it('Id inputs should work either with global id or object id', async () => { try { await apolloClient.mutate({ mutation: gql` @@ -2547,7 +2547,7 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -2615,7 +2615,7 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -2713,7 +2713,7 @@ describe("ParseGraphQLServer", () => { }, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -2771,7 +2771,7 @@ describe("ParseGraphQLServer", () => { }, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -2798,7 +2798,7 @@ describe("ParseGraphQLServer", () => { }, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -2861,7 +2861,7 @@ describe("ParseGraphQLServer", () => { }, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -2873,7 +2873,7 @@ describe("ParseGraphQLServer", () => { findSecondaryObjectsResult.data.secondaryObjects.edges .map(value => value.node.someField) .sort() - ).toEqual(["some value 22", "some value 44"]); + ).toEqual(['some value 22', 'some value 44']); // NOTE: Here @davimacedo tried to test RelayID order, but the test is wrong since // "objectId1" < "objectId2" do not always keep the order when objectId is transformed // to base64 by Relay @@ -2954,8 +2954,8 @@ describe("ParseGraphQLServer", () => { `, variables: { pointer: { - __type: "Pointer", - className: "SecondaryObject", + __type: 'Pointer', + className: 'SecondaryObject', objectId: getSecondaryObjectsResult.data.secondaryObject4.objectId, }, @@ -2966,7 +2966,7 @@ describe("ParseGraphQLServer", () => { }, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -3028,7 +3028,7 @@ describe("ParseGraphQLServer", () => { }, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -3036,40 +3036,40 @@ describe("ParseGraphQLServer", () => { expect( createPrimaryObjectResult.data.createPrimaryObject.primaryObject .stringField - ).toEqual("some value"); + ).toEqual('some value'); expect( createPrimaryObjectResult.data.createPrimaryObject.primaryObject .arrayField ).toEqual([ - { __typename: "Element", value: 1 }, - { __typename: "Element", value: "abc" }, - { __typename: "SecondaryObject", someField: "some value 44" }, + { __typename: 'Element', value: 1 }, + { __typename: 'Element', value: 'abc' }, + { __typename: 'SecondaryObject', someField: 'some value 44' }, ]); expect( createPrimaryObjectResult.data.createPrimaryObject.primaryObject .pointerField.someField - ).toEqual("some value 22"); + ).toEqual('some value 22'); expect( createPrimaryObjectResult.data.createPrimaryObject.primaryObject.relationField.edges .map(value => value.node.someField) .sort() - ).toEqual(["some value 22", "some value 44"]); + ).toEqual(['some value 22', 'some value 44']); expect( updatePrimaryObjectResult.data.updatePrimaryObject.primaryObject .stringField - ).toEqual("some value"); + ).toEqual('some value'); expect( updatePrimaryObjectResult.data.updatePrimaryObject.primaryObject .arrayField ).toEqual([ - { __typename: "Element", value: 1 }, - { __typename: "Element", value: "abc" }, - { __typename: "SecondaryObject", someField: "some value 44" }, + { __typename: 'Element', value: 1 }, + { __typename: 'Element', value: 'abc' }, + { __typename: 'SecondaryObject', someField: 'some value 44' }, ]); expect( updatePrimaryObjectResult.data.updatePrimaryObject.primaryObject .pointerField.someField - ).toEqual("some value 44"); + ).toEqual('some value 44'); expect( updatePrimaryObjectResult.data.updatePrimaryObject.primaryObject .relationField.edges @@ -3078,11 +3078,11 @@ describe("ParseGraphQLServer", () => { handleError(e); } }); - it("Id inputs should work either with global id or object id with objectId higher than 19", async () => { + it('Id inputs should work either with global id or object id with objectId higher than 19', async () => { const parseServer = await reconfigureServer({ objectIdSize: 20 }); await createGQLFromParseServer(parseServer); - const obj = new Parse.Object("SomeClass"); - await obj.save({ name: "aname", type: "robot" }); + const obj = new Parse.Object('SomeClass'); + await obj.save({ name: 'aname', type: 'robot' }); const result = await apolloClient.query({ query: gql` query getSomeClass($id: ID!) { @@ -3099,8 +3099,8 @@ describe("ParseGraphQLServer", () => { }); }); - describe("Class Schema Mutations", () => { - it("should create a new class", async () => { + describe('Class Schema Mutations', () => { + it('should create a new class', async () => { try { const result = await apolloClient.mutate({ mutation: gql` @@ -3303,7 +3303,7 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -3320,130 +3320,130 @@ describe("ParseGraphQLServer", () => { })); expect(classes).toEqual([ { - clientMutationId: "cmid1", + clientMutationId: 'cmid1', class: { - name: "Class1", + name: 'Class1', schemaFields: [ - { name: "ACL", __typename: "SchemaACLField" }, - { name: "createdAt", __typename: "SchemaDateField" }, - { name: "objectId", __typename: "SchemaStringField" }, - { name: "updatedAt", __typename: "SchemaDateField" }, + { name: 'ACL', __typename: 'SchemaACLField' }, + { name: 'createdAt', __typename: 'SchemaDateField' }, + { name: 'objectId', __typename: 'SchemaStringField' }, + { name: 'updatedAt', __typename: 'SchemaDateField' }, ], - __typename: "Class", + __typename: 'Class', }, - __typename: "CreateClassPayload", + __typename: 'CreateClassPayload', }, { - clientMutationId: "cmid2", + clientMutationId: 'cmid2', class: { - name: "Class2", + name: 'Class2', schemaFields: [ - { name: "ACL", __typename: "SchemaACLField" }, - { name: "createdAt", __typename: "SchemaDateField" }, - { name: "objectId", __typename: "SchemaStringField" }, - { name: "updatedAt", __typename: "SchemaDateField" }, + { name: 'ACL', __typename: 'SchemaACLField' }, + { name: 'createdAt', __typename: 'SchemaDateField' }, + { name: 'objectId', __typename: 'SchemaStringField' }, + { name: 'updatedAt', __typename: 'SchemaDateField' }, ], - __typename: "Class", + __typename: 'Class', }, - __typename: "CreateClassPayload", + __typename: 'CreateClassPayload', }, { - clientMutationId: "cmid3", + clientMutationId: 'cmid3', class: { - name: "Class3", + name: 'Class3', schemaFields: [ - { name: "ACL", __typename: "SchemaACLField" }, - { name: "createdAt", __typename: "SchemaDateField" }, - { name: "objectId", __typename: "SchemaStringField" }, - { name: "updatedAt", __typename: "SchemaDateField" }, + { name: 'ACL', __typename: 'SchemaACLField' }, + { name: 'createdAt', __typename: 'SchemaDateField' }, + { name: 'objectId', __typename: 'SchemaStringField' }, + { name: 'updatedAt', __typename: 'SchemaDateField' }, ], - __typename: "Class", + __typename: 'Class', }, - __typename: "CreateClassPayload", + __typename: 'CreateClassPayload', }, { - clientMutationId: "cmid4", + clientMutationId: 'cmid4', class: { - name: "Class4", + name: 'Class4', schemaFields: [ - { name: "ACL", __typename: "SchemaACLField" }, - { name: "createdAt", __typename: "SchemaDateField" }, - { name: "objectId", __typename: "SchemaStringField" }, - { name: "updatedAt", __typename: "SchemaDateField" }, + { name: 'ACL', __typename: 'SchemaACLField' }, + { name: 'createdAt', __typename: 'SchemaDateField' }, + { name: 'objectId', __typename: 'SchemaStringField' }, + { name: 'updatedAt', __typename: 'SchemaDateField' }, ], - __typename: "Class", + __typename: 'Class', }, - __typename: "CreateClassPayload", + __typename: 'CreateClassPayload', }, { - clientMutationId: "cmid5", + clientMutationId: 'cmid5', class: { - name: "Class5", + name: 'Class5', schemaFields: [ - { name: "ACL", __typename: "SchemaACLField" }, - { name: "createdAt", __typename: "SchemaDateField" }, - { name: "objectId", __typename: "SchemaStringField" }, - { name: "updatedAt", __typename: "SchemaDateField" }, + { name: 'ACL', __typename: 'SchemaACLField' }, + { name: 'createdAt', __typename: 'SchemaDateField' }, + { name: 'objectId', __typename: 'SchemaStringField' }, + { name: 'updatedAt', __typename: 'SchemaDateField' }, ], - __typename: "Class", + __typename: 'Class', }, - __typename: "CreateClassPayload", + __typename: 'CreateClassPayload', }, { - clientMutationId: "cmid6", + clientMutationId: 'cmid6', class: { - name: "Class6", + name: 'Class6', schemaFields: [ - { name: "ACL", __typename: "SchemaACLField" }, - { name: "arrayField1", __typename: "SchemaArrayField" }, - { name: "arrayField2", __typename: "SchemaArrayField" }, - { name: "booleanField1", __typename: "SchemaBooleanField" }, - { name: "booleanField2", __typename: "SchemaBooleanField" }, - { name: "bytesField1", __typename: "SchemaBytesField" }, - { name: "bytesField2", __typename: "SchemaBytesField" }, - { name: "createdAt", __typename: "SchemaDateField" }, - { name: "dateField1", __typename: "SchemaDateField" }, - { name: "dateField2", __typename: "SchemaDateField" }, - { name: "fileField1", __typename: "SchemaFileField" }, - { name: "fileField2", __typename: "SchemaFileField" }, + { name: 'ACL', __typename: 'SchemaACLField' }, + { name: 'arrayField1', __typename: 'SchemaArrayField' }, + { name: 'arrayField2', __typename: 'SchemaArrayField' }, + { name: 'booleanField1', __typename: 'SchemaBooleanField' }, + { name: 'booleanField2', __typename: 'SchemaBooleanField' }, + { name: 'bytesField1', __typename: 'SchemaBytesField' }, + { name: 'bytesField2', __typename: 'SchemaBytesField' }, + { name: 'createdAt', __typename: 'SchemaDateField' }, + { name: 'dateField1', __typename: 'SchemaDateField' }, + { name: 'dateField2', __typename: 'SchemaDateField' }, + { name: 'fileField1', __typename: 'SchemaFileField' }, + { name: 'fileField2', __typename: 'SchemaFileField' }, { - name: "geoPointField", - __typename: "SchemaGeoPointField", + name: 'geoPointField', + __typename: 'SchemaGeoPointField', }, - { name: "numberField1", __typename: "SchemaNumberField" }, - { name: "numberField2", __typename: "SchemaNumberField" }, - { name: "objectField1", __typename: "SchemaObjectField" }, - { name: "objectField2", __typename: "SchemaObjectField" }, - { name: "objectId", __typename: "SchemaStringField" }, + { name: 'numberField1', __typename: 'SchemaNumberField' }, + { name: 'numberField2', __typename: 'SchemaNumberField' }, + { name: 'objectField1', __typename: 'SchemaObjectField' }, + { name: 'objectField2', __typename: 'SchemaObjectField' }, + { name: 'objectId', __typename: 'SchemaStringField' }, { - name: "pointerField1", - __typename: "SchemaPointerField", - targetClassName: "Class1", + name: 'pointerField1', + __typename: 'SchemaPointerField', + targetClassName: 'Class1', }, { - name: "pointerField2", - __typename: "SchemaPointerField", - targetClassName: "Class6", + name: 'pointerField2', + __typename: 'SchemaPointerField', + targetClassName: 'Class6', }, - { name: "polygonField1", __typename: "SchemaPolygonField" }, - { name: "polygonField2", __typename: "SchemaPolygonField" }, + { name: 'polygonField1', __typename: 'SchemaPolygonField' }, + { name: 'polygonField2', __typename: 'SchemaPolygonField' }, { - name: "relationField1", - __typename: "SchemaRelationField", - targetClassName: "Class1", + name: 'relationField1', + __typename: 'SchemaRelationField', + targetClassName: 'Class1', }, { - name: "relationField2", - __typename: "SchemaRelationField", - targetClassName: "Class6", + name: 'relationField2', + __typename: 'SchemaRelationField', + targetClassName: 'Class6', }, - { name: "stringField1", __typename: "SchemaStringField" }, - { name: "stringField2", __typename: "SchemaStringField" }, - { name: "updatedAt", __typename: "SchemaDateField" }, + { name: 'stringField1', __typename: 'SchemaStringField' }, + { name: 'stringField2', __typename: 'SchemaStringField' }, + { name: 'updatedAt', __typename: 'SchemaDateField' }, ], - __typename: "Class", + __typename: 'Class', }, - __typename: "CreateClassPayload", + __typename: 'CreateClassPayload', }, ]); @@ -3467,12 +3467,12 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); findResult.data.classes = findResult.data.classes - .filter(schemaClass => !schemaClass.name.startsWith("_")) + .filter(schemaClass => !schemaClass.name.startsWith('_')) .sort((a, b) => (a.name > b.name ? 1 : -1)); findResult.data.classes.forEach(schemaClass => { schemaClass.schemaFields = schemaClass.schemaFields.sort( @@ -3481,106 +3481,106 @@ describe("ParseGraphQLServer", () => { }); expect(findResult.data.classes).toEqual([ { - name: "Class1", + name: 'Class1', schemaFields: [ - { name: "ACL", __typename: "SchemaACLField" }, - { name: "createdAt", __typename: "SchemaDateField" }, - { name: "objectId", __typename: "SchemaStringField" }, - { name: "updatedAt", __typename: "SchemaDateField" }, + { name: 'ACL', __typename: 'SchemaACLField' }, + { name: 'createdAt', __typename: 'SchemaDateField' }, + { name: 'objectId', __typename: 'SchemaStringField' }, + { name: 'updatedAt', __typename: 'SchemaDateField' }, ], - __typename: "Class", + __typename: 'Class', }, { - name: "Class2", + name: 'Class2', schemaFields: [ - { name: "ACL", __typename: "SchemaACLField" }, - { name: "createdAt", __typename: "SchemaDateField" }, - { name: "objectId", __typename: "SchemaStringField" }, - { name: "updatedAt", __typename: "SchemaDateField" }, + { name: 'ACL', __typename: 'SchemaACLField' }, + { name: 'createdAt', __typename: 'SchemaDateField' }, + { name: 'objectId', __typename: 'SchemaStringField' }, + { name: 'updatedAt', __typename: 'SchemaDateField' }, ], - __typename: "Class", + __typename: 'Class', }, { - name: "Class3", + name: 'Class3', schemaFields: [ - { name: "ACL", __typename: "SchemaACLField" }, - { name: "createdAt", __typename: "SchemaDateField" }, - { name: "objectId", __typename: "SchemaStringField" }, - { name: "updatedAt", __typename: "SchemaDateField" }, + { name: 'ACL', __typename: 'SchemaACLField' }, + { name: 'createdAt', __typename: 'SchemaDateField' }, + { name: 'objectId', __typename: 'SchemaStringField' }, + { name: 'updatedAt', __typename: 'SchemaDateField' }, ], - __typename: "Class", + __typename: 'Class', }, { - name: "Class4", + name: 'Class4', schemaFields: [ - { name: "ACL", __typename: "SchemaACLField" }, - { name: "createdAt", __typename: "SchemaDateField" }, - { name: "objectId", __typename: "SchemaStringField" }, - { name: "updatedAt", __typename: "SchemaDateField" }, + { name: 'ACL', __typename: 'SchemaACLField' }, + { name: 'createdAt', __typename: 'SchemaDateField' }, + { name: 'objectId', __typename: 'SchemaStringField' }, + { name: 'updatedAt', __typename: 'SchemaDateField' }, ], - __typename: "Class", + __typename: 'Class', }, { - name: "Class5", + name: 'Class5', schemaFields: [ - { name: "ACL", __typename: "SchemaACLField" }, - { name: "createdAt", __typename: "SchemaDateField" }, - { name: "objectId", __typename: "SchemaStringField" }, - { name: "updatedAt", __typename: "SchemaDateField" }, + { name: 'ACL', __typename: 'SchemaACLField' }, + { name: 'createdAt', __typename: 'SchemaDateField' }, + { name: 'objectId', __typename: 'SchemaStringField' }, + { name: 'updatedAt', __typename: 'SchemaDateField' }, ], - __typename: "Class", + __typename: 'Class', }, { - name: "Class6", + name: 'Class6', schemaFields: [ - { name: "ACL", __typename: "SchemaACLField" }, - { name: "arrayField1", __typename: "SchemaArrayField" }, - { name: "arrayField2", __typename: "SchemaArrayField" }, - { name: "booleanField1", __typename: "SchemaBooleanField" }, - { name: "booleanField2", __typename: "SchemaBooleanField" }, - { name: "bytesField1", __typename: "SchemaBytesField" }, - { name: "bytesField2", __typename: "SchemaBytesField" }, - { name: "createdAt", __typename: "SchemaDateField" }, - { name: "dateField1", __typename: "SchemaDateField" }, - { name: "dateField2", __typename: "SchemaDateField" }, - { name: "fileField1", __typename: "SchemaFileField" }, - { name: "fileField2", __typename: "SchemaFileField" }, + { name: 'ACL', __typename: 'SchemaACLField' }, + { name: 'arrayField1', __typename: 'SchemaArrayField' }, + { name: 'arrayField2', __typename: 'SchemaArrayField' }, + { name: 'booleanField1', __typename: 'SchemaBooleanField' }, + { name: 'booleanField2', __typename: 'SchemaBooleanField' }, + { name: 'bytesField1', __typename: 'SchemaBytesField' }, + { name: 'bytesField2', __typename: 'SchemaBytesField' }, + { name: 'createdAt', __typename: 'SchemaDateField' }, + { name: 'dateField1', __typename: 'SchemaDateField' }, + { name: 'dateField2', __typename: 'SchemaDateField' }, + { name: 'fileField1', __typename: 'SchemaFileField' }, + { name: 'fileField2', __typename: 'SchemaFileField' }, { - name: "geoPointField", - __typename: "SchemaGeoPointField", + name: 'geoPointField', + __typename: 'SchemaGeoPointField', }, - { name: "numberField1", __typename: "SchemaNumberField" }, - { name: "numberField2", __typename: "SchemaNumberField" }, - { name: "objectField1", __typename: "SchemaObjectField" }, - { name: "objectField2", __typename: "SchemaObjectField" }, - { name: "objectId", __typename: "SchemaStringField" }, + { name: 'numberField1', __typename: 'SchemaNumberField' }, + { name: 'numberField2', __typename: 'SchemaNumberField' }, + { name: 'objectField1', __typename: 'SchemaObjectField' }, + { name: 'objectField2', __typename: 'SchemaObjectField' }, + { name: 'objectId', __typename: 'SchemaStringField' }, { - name: "pointerField1", - __typename: "SchemaPointerField", - targetClassName: "Class1", + name: 'pointerField1', + __typename: 'SchemaPointerField', + targetClassName: 'Class1', }, { - name: "pointerField2", - __typename: "SchemaPointerField", - targetClassName: "Class6", + name: 'pointerField2', + __typename: 'SchemaPointerField', + targetClassName: 'Class6', }, - { name: "polygonField1", __typename: "SchemaPolygonField" }, - { name: "polygonField2", __typename: "SchemaPolygonField" }, + { name: 'polygonField1', __typename: 'SchemaPolygonField' }, + { name: 'polygonField2', __typename: 'SchemaPolygonField' }, { - name: "relationField1", - __typename: "SchemaRelationField", - targetClassName: "Class1", + name: 'relationField1', + __typename: 'SchemaRelationField', + targetClassName: 'Class1', }, { - name: "relationField2", - __typename: "SchemaRelationField", - targetClassName: "Class6", + name: 'relationField2', + __typename: 'SchemaRelationField', + targetClassName: 'Class6', }, - { name: "stringField1", __typename: "SchemaStringField" }, - { name: "stringField2", __typename: "SchemaStringField" }, - { name: "updatedAt", __typename: "SchemaDateField" }, + { name: 'stringField1', __typename: 'SchemaStringField' }, + { name: 'stringField2', __typename: 'SchemaStringField' }, + { name: 'updatedAt', __typename: 'SchemaDateField' }, ], - __typename: "Class", + __typename: 'Class', }, ]); } catch (e) { @@ -3588,7 +3588,7 @@ describe("ParseGraphQLServer", () => { } }); - it("should require master key to create a new class", async () => { + it('should require master key to create a new class', async () => { try { await apolloClient.mutate({ mutation: gql` @@ -3599,18 +3599,18 @@ describe("ParseGraphQLServer", () => { } `, }); - fail("should fail"); + fail('should fail'); } catch (e) { expect(e.graphQLErrors[0].extensions.code).toEqual( Parse.Error.OPERATION_FORBIDDEN ); expect(e.graphQLErrors[0].message).toEqual( - "unauthorized: master key is required" + 'unauthorized: master key is required' ); } }); - it("should not allow duplicated field names when creating", async () => { + it('should not allow duplicated field names when creating', async () => { try { await apolloClient.mutate({ mutation: gql` @@ -3630,22 +3630,22 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); - fail("should fail"); + fail('should fail'); } catch (e) { expect(e.graphQLErrors[0].extensions.code).toEqual( Parse.Error.INVALID_KEY_NAME ); expect(e.graphQLErrors[0].message).toEqual( - "Duplicated field name: someField" + 'Duplicated field name: someField' ); } }); - it("should update an existing class", async () => { + it('should update an existing class', async () => { try { const clientMutationId = uuidv4(); const result = await apolloClient.mutate({ @@ -3761,7 +3761,7 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -3777,88 +3777,88 @@ describe("ParseGraphQLServer", () => { data: { createClass: { class: { - name: "MyNewClass", + name: 'MyNewClass', schemaFields: [ - { name: "ACL", __typename: "SchemaACLField" }, - { name: "createdAt", __typename: "SchemaDateField" }, - { name: "objectId", __typename: "SchemaStringField" }, - { name: "updatedAt", __typename: "SchemaDateField" }, + { name: 'ACL', __typename: 'SchemaACLField' }, + { name: 'createdAt', __typename: 'SchemaDateField' }, + { name: 'objectId', __typename: 'SchemaStringField' }, + { name: 'updatedAt', __typename: 'SchemaDateField' }, { - name: "willBeRemoved", - __typename: "SchemaStringField", + name: 'willBeRemoved', + __typename: 'SchemaStringField', }, ], - __typename: "Class", + __typename: 'Class', }, - __typename: "CreateClassPayload", + __typename: 'CreateClassPayload', }, updateClass: { clientMutationId, class: { - name: "MyNewClass", + name: 'MyNewClass', schemaFields: [ - { name: "ACL", __typename: "SchemaACLField" }, - { name: "arrayField1", __typename: "SchemaArrayField" }, - { name: "arrayField2", __typename: "SchemaArrayField" }, + { name: 'ACL', __typename: 'SchemaACLField' }, + { name: 'arrayField1', __typename: 'SchemaArrayField' }, + { name: 'arrayField2', __typename: 'SchemaArrayField' }, { - name: "booleanField1", - __typename: "SchemaBooleanField", + name: 'booleanField1', + __typename: 'SchemaBooleanField', }, { - name: "booleanField2", - __typename: "SchemaBooleanField", + name: 'booleanField2', + __typename: 'SchemaBooleanField', }, - { name: "bytesField1", __typename: "SchemaBytesField" }, - { name: "bytesField2", __typename: "SchemaBytesField" }, - { name: "createdAt", __typename: "SchemaDateField" }, - { name: "dateField1", __typename: "SchemaDateField" }, - { name: "dateField2", __typename: "SchemaDateField" }, - { name: "fileField1", __typename: "SchemaFileField" }, - { name: "fileField2", __typename: "SchemaFileField" }, + { name: 'bytesField1', __typename: 'SchemaBytesField' }, + { name: 'bytesField2', __typename: 'SchemaBytesField' }, + { name: 'createdAt', __typename: 'SchemaDateField' }, + { name: 'dateField1', __typename: 'SchemaDateField' }, + { name: 'dateField2', __typename: 'SchemaDateField' }, + { name: 'fileField1', __typename: 'SchemaFileField' }, + { name: 'fileField2', __typename: 'SchemaFileField' }, { - name: "geoPointField", - __typename: "SchemaGeoPointField", + name: 'geoPointField', + __typename: 'SchemaGeoPointField', }, - { name: "numberField1", __typename: "SchemaNumberField" }, - { name: "numberField2", __typename: "SchemaNumberField" }, - { name: "objectField1", __typename: "SchemaObjectField" }, - { name: "objectField2", __typename: "SchemaObjectField" }, - { name: "objectId", __typename: "SchemaStringField" }, + { name: 'numberField1', __typename: 'SchemaNumberField' }, + { name: 'numberField2', __typename: 'SchemaNumberField' }, + { name: 'objectField1', __typename: 'SchemaObjectField' }, + { name: 'objectField2', __typename: 'SchemaObjectField' }, + { name: 'objectId', __typename: 'SchemaStringField' }, { - name: "pointerField1", - __typename: "SchemaPointerField", - targetClassName: "Class1", + name: 'pointerField1', + __typename: 'SchemaPointerField', + targetClassName: 'Class1', }, { - name: "pointerField2", - __typename: "SchemaPointerField", - targetClassName: "Class6", + name: 'pointerField2', + __typename: 'SchemaPointerField', + targetClassName: 'Class6', }, { - name: "polygonField1", - __typename: "SchemaPolygonField", + name: 'polygonField1', + __typename: 'SchemaPolygonField', }, { - name: "polygonField2", - __typename: "SchemaPolygonField", + name: 'polygonField2', + __typename: 'SchemaPolygonField', }, { - name: "relationField1", - __typename: "SchemaRelationField", - targetClassName: "Class1", + name: 'relationField1', + __typename: 'SchemaRelationField', + targetClassName: 'Class1', }, { - name: "relationField2", - __typename: "SchemaRelationField", - targetClassName: "Class6", + name: 'relationField2', + __typename: 'SchemaRelationField', + targetClassName: 'Class6', }, - { name: "stringField1", __typename: "SchemaStringField" }, - { name: "stringField2", __typename: "SchemaStringField" }, - { name: "updatedAt", __typename: "SchemaDateField" }, + { name: 'stringField1', __typename: 'SchemaStringField' }, + { name: 'stringField2', __typename: 'SchemaStringField' }, + { name: 'updatedAt', __typename: 'SchemaDateField' }, ], - __typename: "Class", + __typename: 'Class', }, - __typename: "UpdateClassPayload", + __typename: 'UpdateClassPayload', }, }, }); @@ -3883,7 +3883,7 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -3893,56 +3893,56 @@ describe("ParseGraphQLServer", () => { ); expect(getResult.data).toEqual({ class: { - name: "MyNewClass", + name: 'MyNewClass', schemaFields: [ - { name: "ACL", __typename: "SchemaACLField" }, - { name: "arrayField1", __typename: "SchemaArrayField" }, - { name: "arrayField2", __typename: "SchemaArrayField" }, - { name: "booleanField1", __typename: "SchemaBooleanField" }, - { name: "booleanField2", __typename: "SchemaBooleanField" }, - { name: "bytesField1", __typename: "SchemaBytesField" }, - { name: "bytesField2", __typename: "SchemaBytesField" }, - { name: "createdAt", __typename: "SchemaDateField" }, - { name: "dateField1", __typename: "SchemaDateField" }, - { name: "dateField2", __typename: "SchemaDateField" }, - { name: "fileField1", __typename: "SchemaFileField" }, - { name: "fileField2", __typename: "SchemaFileField" }, + { name: 'ACL', __typename: 'SchemaACLField' }, + { name: 'arrayField1', __typename: 'SchemaArrayField' }, + { name: 'arrayField2', __typename: 'SchemaArrayField' }, + { name: 'booleanField1', __typename: 'SchemaBooleanField' }, + { name: 'booleanField2', __typename: 'SchemaBooleanField' }, + { name: 'bytesField1', __typename: 'SchemaBytesField' }, + { name: 'bytesField2', __typename: 'SchemaBytesField' }, + { name: 'createdAt', __typename: 'SchemaDateField' }, + { name: 'dateField1', __typename: 'SchemaDateField' }, + { name: 'dateField2', __typename: 'SchemaDateField' }, + { name: 'fileField1', __typename: 'SchemaFileField' }, + { name: 'fileField2', __typename: 'SchemaFileField' }, { - name: "geoPointField", - __typename: "SchemaGeoPointField", + name: 'geoPointField', + __typename: 'SchemaGeoPointField', }, - { name: "numberField1", __typename: "SchemaNumberField" }, - { name: "numberField2", __typename: "SchemaNumberField" }, - { name: "objectField1", __typename: "SchemaObjectField" }, - { name: "objectField2", __typename: "SchemaObjectField" }, - { name: "objectId", __typename: "SchemaStringField" }, + { name: 'numberField1', __typename: 'SchemaNumberField' }, + { name: 'numberField2', __typename: 'SchemaNumberField' }, + { name: 'objectField1', __typename: 'SchemaObjectField' }, + { name: 'objectField2', __typename: 'SchemaObjectField' }, + { name: 'objectId', __typename: 'SchemaStringField' }, { - name: "pointerField1", - __typename: "SchemaPointerField", - targetClassName: "Class1", + name: 'pointerField1', + __typename: 'SchemaPointerField', + targetClassName: 'Class1', }, { - name: "pointerField2", - __typename: "SchemaPointerField", - targetClassName: "Class6", + name: 'pointerField2', + __typename: 'SchemaPointerField', + targetClassName: 'Class6', }, - { name: "polygonField1", __typename: "SchemaPolygonField" }, - { name: "polygonField2", __typename: "SchemaPolygonField" }, + { name: 'polygonField1', __typename: 'SchemaPolygonField' }, + { name: 'polygonField2', __typename: 'SchemaPolygonField' }, { - name: "relationField1", - __typename: "SchemaRelationField", - targetClassName: "Class1", + name: 'relationField1', + __typename: 'SchemaRelationField', + targetClassName: 'Class1', }, { - name: "relationField2", - __typename: "SchemaRelationField", - targetClassName: "Class6", + name: 'relationField2', + __typename: 'SchemaRelationField', + targetClassName: 'Class6', }, - { name: "stringField1", __typename: "SchemaStringField" }, - { name: "stringField2", __typename: "SchemaStringField" }, - { name: "updatedAt", __typename: "SchemaDateField" }, + { name: 'stringField1', __typename: 'SchemaStringField' }, + { name: 'stringField2', __typename: 'SchemaStringField' }, + { name: 'updatedAt', __typename: 'SchemaDateField' }, ], - __typename: "Class", + __typename: 'Class', }, }); } catch (e) { @@ -3950,7 +3950,7 @@ describe("ParseGraphQLServer", () => { } }); - it("should require master key to update an existing class", async () => { + it('should require master key to update an existing class', async () => { try { await apolloClient.mutate({ mutation: gql` @@ -3962,7 +3962,7 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -3980,18 +3980,18 @@ describe("ParseGraphQLServer", () => { } `, }); - fail("should fail"); + fail('should fail'); } catch (e) { expect(e.graphQLErrors[0].extensions.code).toEqual( Parse.Error.OPERATION_FORBIDDEN ); expect(e.graphQLErrors[0].message).toEqual( - "unauthorized: master key is required" + 'unauthorized: master key is required' ); } }); - it("should not allow duplicated field names when updating", async () => { + it('should not allow duplicated field names when updating', async () => { try { await apolloClient.mutate({ mutation: gql` @@ -4008,7 +4008,7 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -4032,22 +4032,22 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); - fail("should fail"); + fail('should fail'); } catch (e) { expect(e.graphQLErrors[0].extensions.code).toEqual( Parse.Error.INVALID_KEY_NAME ); expect(e.graphQLErrors[0].message).toEqual( - "Duplicated field name: someField" + 'Duplicated field name: someField' ); } }); - it("should fail if updating an inexistent class", async () => { + it('should fail if updating an inexistent class', async () => { try { await apolloClient.mutate({ mutation: gql` @@ -4064,22 +4064,22 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); - fail("should fail"); + fail('should fail'); } catch (e) { expect(e.graphQLErrors[0].extensions.code).toEqual( Parse.Error.INVALID_CLASS_NAME ); expect(e.graphQLErrors[0].message).toEqual( - "Class SomeInexistentClass does not exist." + 'Class SomeInexistentClass does not exist.' ); } }); - it("should delete an existing class", async () => { + it('should delete an existing class', async () => { try { const clientMutationId = uuidv4(); const result = await apolloClient.mutate({ @@ -4112,7 +4112,7 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -4128,38 +4128,38 @@ describe("ParseGraphQLServer", () => { data: { createClass: { class: { - name: "MyNewClass", + name: 'MyNewClass', schemaFields: [ - { name: "ACL", __typename: "SchemaACLField" }, - { name: "createdAt", __typename: "SchemaDateField" }, - { name: "objectId", __typename: "SchemaStringField" }, - { name: "updatedAt", __typename: "SchemaDateField" }, + { name: 'ACL', __typename: 'SchemaACLField' }, + { name: 'createdAt', __typename: 'SchemaDateField' }, + { name: 'objectId', __typename: 'SchemaStringField' }, + { name: 'updatedAt', __typename: 'SchemaDateField' }, { - name: "willBeRemoved", - __typename: "SchemaStringField", + name: 'willBeRemoved', + __typename: 'SchemaStringField', }, ], - __typename: "Class", + __typename: 'Class', }, - __typename: "CreateClassPayload", + __typename: 'CreateClassPayload', }, deleteClass: { clientMutationId, class: { - name: "MyNewClass", + name: 'MyNewClass', schemaFields: [ - { name: "ACL", __typename: "SchemaACLField" }, - { name: "createdAt", __typename: "SchemaDateField" }, - { name: "objectId", __typename: "SchemaStringField" }, - { name: "updatedAt", __typename: "SchemaDateField" }, + { name: 'ACL', __typename: 'SchemaACLField' }, + { name: 'createdAt', __typename: 'SchemaDateField' }, + { name: 'objectId', __typename: 'SchemaStringField' }, + { name: 'updatedAt', __typename: 'SchemaDateField' }, { - name: "willBeRemoved", - __typename: "SchemaStringField", + name: 'willBeRemoved', + __typename: 'SchemaStringField', }, ], - __typename: "Class", + __typename: 'Class', }, - __typename: "DeleteClassPayload", + __typename: 'DeleteClassPayload', }, }, }); @@ -4175,17 +4175,17 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); - fail("should fail"); + fail('should fail'); } catch (e) { expect(e.graphQLErrors[0].extensions.code).toEqual( Parse.Error.INVALID_CLASS_NAME ); expect(e.graphQLErrors[0].message).toEqual( - "Class MyNewClass does not exist." + 'Class MyNewClass does not exist.' ); } } catch (e) { @@ -4193,7 +4193,7 @@ describe("ParseGraphQLServer", () => { } }); - it("should require master key to delete an existing class", async () => { + it('should require master key to delete an existing class', async () => { try { await apolloClient.mutate({ mutation: gql` @@ -4205,7 +4205,7 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -4223,18 +4223,18 @@ describe("ParseGraphQLServer", () => { } `, }); - fail("should fail"); + fail('should fail'); } catch (e) { expect(e.graphQLErrors[0].extensions.code).toEqual( Parse.Error.OPERATION_FORBIDDEN ); expect(e.graphQLErrors[0].message).toEqual( - "unauthorized: master key is required" + 'unauthorized: master key is required' ); } }); - it("should fail if deleting an inexistent class", async () => { + it('should fail if deleting an inexistent class', async () => { try { await apolloClient.mutate({ mutation: gql` @@ -4246,22 +4246,22 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); - fail("should fail"); + fail('should fail'); } catch (e) { expect(e.graphQLErrors[0].extensions.code).toEqual( Parse.Error.INVALID_CLASS_NAME ); expect(e.graphQLErrors[0].message).toEqual( - "Class SomeInexistentClass does not exist." + 'Class SomeInexistentClass does not exist.' ); } }); - it("should require master key to get an existing class", async () => { + it('should require master key to get an existing class', async () => { try { await apolloClient.query({ query: gql` @@ -4272,18 +4272,18 @@ describe("ParseGraphQLServer", () => { } `, }); - fail("should fail"); + fail('should fail'); } catch (e) { expect(e.graphQLErrors[0].extensions.code).toEqual( Parse.Error.OPERATION_FORBIDDEN ); expect(e.graphQLErrors[0].message).toEqual( - "unauthorized: master key is required" + 'unauthorized: master key is required' ); } }); - it("should require master key to find the existing classes", async () => { + it('should require master key to find the existing classes', async () => { try { await apolloClient.query({ query: gql` @@ -4294,23 +4294,23 @@ describe("ParseGraphQLServer", () => { } `, }); - fail("should fail"); + fail('should fail'); } catch (e) { expect(e.graphQLErrors[0].extensions.code).toEqual( Parse.Error.OPERATION_FORBIDDEN ); expect(e.graphQLErrors[0].message).toEqual( - "unauthorized: master key is required" + 'unauthorized: master key is required' ); } }); }); - describe("Objects Queries", () => { - describe("Get", () => { - it("should return a class object using class specific query", async () => { - const obj = new Parse.Object("Customer"); - obj.set("someField", "someValue"); + describe('Objects Queries', () => { + describe('Get', () => { + it('should return a class object using class specific query', async () => { + const obj = new Parse.Object('Customer'); + obj.set('someField', 'someValue'); await obj.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -4335,27 +4335,27 @@ describe("ParseGraphQLServer", () => { ).data.customer; expect(result.objectId).toEqual(obj.id); - expect(result.someField).toEqual("someValue"); + expect(result.someField).toEqual('someValue'); expect(new Date(result.createdAt)).toEqual(obj.createdAt); expect(new Date(result.updatedAt)).toEqual(obj.updatedAt); }); - it_only_db("mongo")( - "should return child objects in array fields", + it_only_db('mongo')( + 'should return child objects in array fields', async () => { - const obj1 = new Parse.Object("Customer"); - const obj2 = new Parse.Object("SomeClass"); - const obj3 = new Parse.Object("Customer"); + const obj1 = new Parse.Object('Customer'); + const obj2 = new Parse.Object('SomeClass'); + const obj3 = new Parse.Object('Customer'); - obj1.set("someCustomerField", "imCustomerOne"); - const arrayField = [42.42, 42, "string", true]; - obj1.set("arrayField", arrayField); + obj1.set('someCustomerField', 'imCustomerOne'); + const arrayField = [42.42, 42, 'string', true]; + obj1.set('arrayField', arrayField); await obj1.save(); - obj2.set("someClassField", "imSomeClassTwo"); + obj2.set('someClassField', 'imSomeClassTwo'); await obj2.save(); - obj3.set("manyRelations", [obj1, obj2]); + obj3.set('manyRelations', [obj1, obj2]); await obj3.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -4405,41 +4405,41 @@ describe("ParseGraphQLServer", () => { expect(customerSubObject).toBeDefined(); expect(someClassSubObject).toBeDefined(); expect(customerSubObject.someCustomerField).toEqual( - "imCustomerOne" + 'imCustomerOne' ); const formatedArrayField = customerSubObject.arrayField.map( elem => elem.value ); expect(formatedArrayField).toEqual(arrayField); expect(someClassSubObject.someClassField).toEqual( - "imSomeClassTwo" + 'imSomeClassTwo' ); } ); - it("should return many child objects in allow cyclic query", async () => { - const obj1 = new Parse.Object("Employee"); - const obj2 = new Parse.Object("Team"); - const obj3 = new Parse.Object("Company"); - const obj4 = new Parse.Object("Country"); + it('should return many child objects in allow cyclic query', async () => { + const obj1 = new Parse.Object('Employee'); + const obj2 = new Parse.Object('Team'); + const obj3 = new Parse.Object('Company'); + const obj4 = new Parse.Object('Country'); - obj1.set("name", "imAnEmployee"); + obj1.set('name', 'imAnEmployee'); await obj1.save(); - obj2.set("name", "imATeam"); - obj2.set("employees", [obj1]); + obj2.set('name', 'imATeam'); + obj2.set('employees', [obj1]); await obj2.save(); - obj3.set("name", "imACompany"); - obj3.set("teams", [obj2]); - obj3.set("employees", [obj1]); + obj3.set('name', 'imACompany'); + obj3.set('teams', [obj2]); + obj3.set('employees', [obj1]); await obj3.save(); - obj4.set("name", "imACountry"); - obj4.set("companies", [obj3]); + obj4.set('name', 'imACountry'); + obj4.set('companies', [obj3]); await obj4.save(); - obj1.set("country", obj4); + obj1.set('country', obj4); await obj1.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -4490,34 +4490,34 @@ describe("ParseGraphQLServer", () => { const expectedResult = { objectId: obj4.id, - name: "imACountry", - __typename: "Country", + name: 'imACountry', + __typename: 'Country', companies: [ { objectId: obj3.id, - name: "imACompany", - __typename: "Company", + name: 'imACompany', + __typename: 'Company', employees: [ { objectId: obj1.id, - name: "imAnEmployee", - __typename: "Employee", + name: 'imAnEmployee', + __typename: 'Employee', }, ], teams: [ { objectId: obj2.id, - name: "imATeam", - __typename: "Team", + name: 'imATeam', + __typename: 'Team', employees: [ { objectId: obj1.id, - name: "imAnEmployee", - __typename: "Employee", + name: 'imAnEmployee', + __typename: 'Employee', country: { objectId: obj4.id, - name: "imACountry", - __typename: "Country", + name: 'imACountry', + __typename: 'Country', }, }, ], @@ -4529,7 +4529,7 @@ describe("ParseGraphQLServer", () => { expect(result).toEqual(expectedResult); }); - it("should respect level permissions", async () => { + it('should respect level permissions', async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -4564,22 +4564,22 @@ describe("ParseGraphQLServer", () => { .map(obj => expectAsync( getObject(obj.className, obj.id) - ).toBeRejectedWith(jasmine.stringMatching("Object not found")) + ).toBeRejectedWith(jasmine.stringMatching('Object not found')) ) ); expect( (await getObject(object4.className, object4.id)).data.get .someField - ).toEqual("someValue4"); + ).toEqual('someValue4'); await Promise.all( objects.map(async obj => expect( ( await getObject(obj.className, obj.id, { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }) ).data.get.someField - ).toEqual(obj.get("someField")) + ).toEqual(obj.get('someField')) ) ); await Promise.all( @@ -4587,10 +4587,10 @@ describe("ParseGraphQLServer", () => { expect( ( await getObject(obj.className, obj.id, { - "X-Parse-Session-Token": user1.getSessionToken(), + 'X-Parse-Session-Token': user1.getSessionToken(), }) ).data.get.someField - ).toEqual(obj.get("someField")) + ).toEqual(obj.get('someField')) ) ); await Promise.all( @@ -4598,70 +4598,70 @@ describe("ParseGraphQLServer", () => { expect( ( await getObject(obj.className, obj.id, { - "X-Parse-Session-Token": user2.getSessionToken(), + 'X-Parse-Session-Token': user2.getSessionToken(), }) ).data.get.someField - ).toEqual(obj.get("someField")) + ).toEqual(obj.get('someField')) ) ); await expectAsync( getObject(object2.className, object2.id, { - "X-Parse-Session-Token": user3.getSessionToken(), + 'X-Parse-Session-Token': user3.getSessionToken(), }) - ).toBeRejectedWith(jasmine.stringMatching("Object not found")); + ).toBeRejectedWith(jasmine.stringMatching('Object not found')); await Promise.all( [object1, object3, object4].map(async obj => expect( ( await getObject(obj.className, obj.id, { - "X-Parse-Session-Token": user3.getSessionToken(), + 'X-Parse-Session-Token': user3.getSessionToken(), }) ).data.get.someField - ).toEqual(obj.get("someField")) + ).toEqual(obj.get('someField')) ) ); await Promise.all( objects.slice(0, 3).map(obj => expectAsync( getObject(obj.className, obj.id, { - "X-Parse-Session-Token": user4.getSessionToken(), + 'X-Parse-Session-Token': user4.getSessionToken(), }) - ).toBeRejectedWith(jasmine.stringMatching("Object not found")) + ).toBeRejectedWith(jasmine.stringMatching('Object not found')) ) ); expect( ( await getObject(object4.className, object4.id, { - "X-Parse-Session-Token": user4.getSessionToken(), + 'X-Parse-Session-Token': user4.getSessionToken(), }) ).data.get.someField - ).toEqual("someValue4"); + ).toEqual('someValue4'); await Promise.all( objects.slice(0, 2).map(obj => expectAsync( getObject(obj.className, obj.id, { - "X-Parse-Session-Token": user5.getSessionToken(), + 'X-Parse-Session-Token': user5.getSessionToken(), }) - ).toBeRejectedWith(jasmine.stringMatching("Object not found")) + ).toBeRejectedWith(jasmine.stringMatching('Object not found')) ) ); expect( ( await getObject(object3.className, object3.id, { - "X-Parse-Session-Token": user5.getSessionToken(), + 'X-Parse-Session-Token': user5.getSessionToken(), }) ).data.get.someField - ).toEqual("someValue3"); + ).toEqual('someValue3'); expect( ( await getObject(object4.className, object4.id, { - "X-Parse-Session-Token": user5.getSessionToken(), + 'X-Parse-Session-Token': user5.getSessionToken(), }) ).data.get.someField - ).toEqual("someValue4"); + ).toEqual('someValue4'); }); - it("should support keys argument", async () => { + it('should support keys argument', async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -4679,7 +4679,7 @@ describe("ParseGraphQLServer", () => { }, context: { headers: { - "X-Parse-Session-Token": user1.getSessionToken(), + 'X-Parse-Session-Token': user1.getSessionToken(), }, }, }); @@ -4700,7 +4700,7 @@ describe("ParseGraphQLServer", () => { }, context: { headers: { - "X-Parse-Session-Token": user1.getSessionToken(), + 'X-Parse-Session-Token': user1.getSessionToken(), }, }, }); @@ -4711,7 +4711,7 @@ describe("ParseGraphQLServer", () => { expect(result2.data.get.pointerToUser).toBeDefined(); }); - it("should support include argument", async () => { + it('should support include argument', async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -4731,7 +4731,7 @@ describe("ParseGraphQLServer", () => { }, context: { headers: { - "X-Parse-Session-Token": user1.getSessionToken(), + 'X-Parse-Session-Token': user1.getSessionToken(), }, }, }); @@ -4751,7 +4751,7 @@ describe("ParseGraphQLServer", () => { }, context: { headers: { - "X-Parse-Session-Token": user1.getSessionToken(), + 'X-Parse-Session-Token': user1.getSessionToken(), }, }, }); @@ -4762,21 +4762,21 @@ describe("ParseGraphQLServer", () => { ).toBeDefined(); }); - it("should respect protectedFields", async done => { + it('should respect protectedFields', async done => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const className = "GraphQLClass"; + const className = 'GraphQLClass'; await updateCLP( { - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, protectedFields: { - "*": ["someField", "someOtherField"], - authenticated: ["someField"], - "userField:pointerToUser": [], + '*': ['someField', 'someOtherField'], + authenticated: ['someField'], + 'userField:pointerToUser': [], [user2.id]: [], }, }, @@ -4785,7 +4785,7 @@ describe("ParseGraphQLServer", () => { const getObject = async (className, id, user) => { const headers = user - ? { ["X-Parse-Session-Token"]: user.getSessionToken() } + ? { ['X-Parse-Session-Token']: user.getSessionToken() } : undefined; const specificQueryResult = await apolloClient.query({ @@ -4824,30 +4824,30 @@ describe("ParseGraphQLServer", () => { const objectAuth = await getObject(className, id, user1); expect(objectAuth.someField).toBeNull(); - expect(objectAuth.someOtherField).toBe("B"); + expect(objectAuth.someOtherField).toBe('B'); /* pointer field */ const objectPointed = await getObject(className, id, user5); - expect(objectPointed.someField).toBe("someValue3"); - expect(objectPointed.someOtherField).toBe("B"); + expect(objectPointed.someField).toBe('someValue3'); + expect(objectPointed.someOtherField).toBe('B'); /* for user id */ const objectForUser = await getObject(className, id, user2); - expect(objectForUser.someField).toBe("someValue3"); - expect(objectForUser.someOtherField).toBe("B"); + expect(objectForUser.someField).toBe('someValue3'); + expect(objectForUser.someOtherField).toBe('B'); done(); }); - describe_only_db("mongo")("read preferences", () => { - it("should read from primary by default", async () => { + describe_only_db('mongo')('read preferences', () => { + it('should read from primary by default', async () => { try { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); await apolloClient.query({ query: gql` @@ -4864,7 +4864,7 @@ describe("ParseGraphQLServer", () => { }, context: { headers: { - "X-Parse-Session-Token": user1.getSessionToken(), + 'X-Parse-Session-Token': user1.getSessionToken(), }, }, }); @@ -4874,7 +4874,7 @@ describe("ParseGraphQLServer", () => { Collection.prototype.find.calls.all().forEach(call => { if ( call.object.s.namespace.collection.indexOf( - "GraphQLClass" + 'GraphQLClass' ) >= 0 ) { foundGraphQLClassReadPreference = true; @@ -4882,7 +4882,7 @@ describe("ParseGraphQLServer", () => { ReadPreference.PRIMARY ); } else if ( - call.object.s.namespace.collection.indexOf("_User") >= 0 + call.object.s.namespace.collection.indexOf('_User') >= 0 ) { foundUserClassReadPreference = true; expect(call.object.s.readPreference.mode).toBe( @@ -4898,12 +4898,12 @@ describe("ParseGraphQLServer", () => { } }); - it("should support readPreference argument", async () => { + it('should support readPreference argument', async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); await apolloClient.query({ query: gql` @@ -4923,7 +4923,7 @@ describe("ParseGraphQLServer", () => { }, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -4932,7 +4932,7 @@ describe("ParseGraphQLServer", () => { let foundUserClassReadPreference = false; Collection.prototype.find.calls.all().forEach(call => { if ( - call.object.s.namespace.collection.indexOf("GraphQLClass") >= + call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0 ) { foundGraphQLClassReadPreference = true; @@ -4940,7 +4940,7 @@ describe("ParseGraphQLServer", () => { ReadPreference.SECONDARY ); } else if ( - call.object.s.namespace.collection.indexOf("_User") >= 0 + call.object.s.namespace.collection.indexOf('_User') >= 0 ) { foundUserClassReadPreference = true; expect(call.args[1].readPreference).toBe( @@ -4953,12 +4953,12 @@ describe("ParseGraphQLServer", () => { expect(foundUserClassReadPreference).toBe(true); }); - it("should support includeReadPreference argument", async () => { + it('should support includeReadPreference argument', async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); await apolloClient.query({ query: gql` @@ -4981,7 +4981,7 @@ describe("ParseGraphQLServer", () => { }, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -4990,7 +4990,7 @@ describe("ParseGraphQLServer", () => { let foundUserClassReadPreference = false; Collection.prototype.find.calls.all().forEach(call => { if ( - call.object.s.namespace.collection.indexOf("GraphQLClass") >= + call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0 ) { foundGraphQLClassReadPreference = true; @@ -4998,7 +4998,7 @@ describe("ParseGraphQLServer", () => { ReadPreference.SECONDARY ); } else if ( - call.object.s.namespace.collection.indexOf("_User") >= 0 + call.object.s.namespace.collection.indexOf('_User') >= 0 ) { foundUserClassReadPreference = true; expect(call.args[1].readPreference).toBe( @@ -5013,13 +5013,13 @@ describe("ParseGraphQLServer", () => { }); }); - describe("Find", () => { - it("should return class objects using class specific query", async () => { - const obj1 = new Parse.Object("Customer"); - obj1.set("someField", "someValue1"); + describe('Find', () => { + it('should return class objects using class specific query', async () => { + const obj1 = new Parse.Object('Customer'); + obj1.set('someField', 'someValue1'); await obj1.save(); - const obj2 = new Parse.Object("Customer"); - obj2.set("someField", "someValue1"); + const obj2 = new Parse.Object('Customer'); + obj2.set('someField', 'someValue1'); await obj2.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -5046,13 +5046,13 @@ describe("ParseGraphQLServer", () => { result.data.customers.edges.forEach(resultObj => { const obj = resultObj.node.objectId === obj1.id ? obj1 : obj2; expect(resultObj.node.objectId).toEqual(obj.id); - expect(resultObj.node.someField).toEqual(obj.get("someField")); + expect(resultObj.node.someField).toEqual(obj.get('someField')); expect(new Date(resultObj.node.createdAt)).toEqual(obj.createdAt); expect(new Date(resultObj.node.updatedAt)).toEqual(obj.updatedAt); }); }); - it("should respect level permissions", async () => { + it('should respect level permissions', async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -5083,82 +5083,82 @@ describe("ParseGraphQLServer", () => { } expect( - (await findObjects("GraphQLClass")).data.find.edges.map( + (await findObjects('GraphQLClass')).data.find.edges.map( object => object.node.someField ) ).toEqual([]); expect( - (await findObjects("PublicClass")).data.find.edges.map( + (await findObjects('PublicClass')).data.find.edges.map( object => object.node.someField ) - ).toEqual(["someValue4"]); + ).toEqual(['someValue4']); expect( ( - await findObjects("GraphQLClass", { - "X-Parse-Master-Key": "test", + await findObjects('GraphQLClass', { + 'X-Parse-Master-Key': 'test', }) ).data.find.edges .map(object => object.node.someField) .sort() - ).toEqual(["someValue1", "someValue2", "someValue3"]); + ).toEqual(['someValue1', 'someValue2', 'someValue3']); expect( ( - await findObjects("PublicClass", { - "X-Parse-Master-Key": "test", + await findObjects('PublicClass', { + 'X-Parse-Master-Key': 'test', }) ).data.find.edges.map(object => object.node.someField) - ).toEqual(["someValue4"]); + ).toEqual(['someValue4']); expect( ( - await findObjects("GraphQLClass", { - "X-Parse-Session-Token": user1.getSessionToken(), + await findObjects('GraphQLClass', { + 'X-Parse-Session-Token': user1.getSessionToken(), }) ).data.find.edges .map(object => object.node.someField) .sort() - ).toEqual(["someValue1", "someValue2", "someValue3"]); + ).toEqual(['someValue1', 'someValue2', 'someValue3']); expect( ( - await findObjects("PublicClass", { - "X-Parse-Session-Token": user1.getSessionToken(), + await findObjects('PublicClass', { + 'X-Parse-Session-Token': user1.getSessionToken(), }) ).data.find.edges.map(object => object.node.someField) - ).toEqual(["someValue4"]); + ).toEqual(['someValue4']); expect( ( - await findObjects("GraphQLClass", { - "X-Parse-Session-Token": user2.getSessionToken(), + await findObjects('GraphQLClass', { + 'X-Parse-Session-Token': user2.getSessionToken(), }) ).data.find.edges .map(object => object.node.someField) .sort() - ).toEqual(["someValue1", "someValue2", "someValue3"]); + ).toEqual(['someValue1', 'someValue2', 'someValue3']); expect( ( - await findObjects("GraphQLClass", { - "X-Parse-Session-Token": user3.getSessionToken(), + await findObjects('GraphQLClass', { + 'X-Parse-Session-Token': user3.getSessionToken(), }) ).data.find.edges .map(object => object.node.someField) .sort() - ).toEqual(["someValue1", "someValue3"]); + ).toEqual(['someValue1', 'someValue3']); expect( ( - await findObjects("GraphQLClass", { - "X-Parse-Session-Token": user4.getSessionToken(), + await findObjects('GraphQLClass', { + 'X-Parse-Session-Token': user4.getSessionToken(), }) ).data.find.edges.map(object => object.node.someField) ).toEqual([]); expect( ( - await findObjects("GraphQLClass", { - "X-Parse-Session-Token": user5.getSessionToken(), + await findObjects('GraphQLClass', { + 'X-Parse-Session-Token': user5.getSessionToken(), }) ).data.find.edges.map(object => object.node.someField) - ).toEqual(["someValue3"]); + ).toEqual(['someValue3']); }); - it("should support where argument using class specific query", async () => { + it('should support where argument using class specific query', async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -5178,7 +5178,7 @@ describe("ParseGraphQLServer", () => { variables: { where: { someField: { - in: ["someValue1", "someValue2", "someValue3"], + in: ['someValue1', 'someValue2', 'someValue3'], }, OR: [ { @@ -5200,7 +5200,7 @@ describe("ParseGraphQLServer", () => { }, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -5209,10 +5209,10 @@ describe("ParseGraphQLServer", () => { result.data.graphQLClasses.edges .map(object => object.node.someField) .sort() - ).toEqual(["someValue1", "someValue3"]); + ).toEqual(['someValue1', 'someValue3']); }); - it("should support in pointer operator using class specific query", async () => { + it('should support in pointer operator using class specific query', async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -5242,17 +5242,17 @@ describe("ParseGraphQLServer", () => { }, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); const { edges } = result.data.graphQLClasses; expect(edges.length).toBe(1); - expect(edges[0].node.someField).toEqual("someValue3"); + expect(edges[0].node.someField).toEqual('someValue3'); }); - it("should support OR operation", async () => { + it('should support OR operation', async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -5278,7 +5278,7 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -5287,16 +5287,16 @@ describe("ParseGraphQLServer", () => { result.data.graphQLClasses.edges .map(object => object.node.someField) .sort() - ).toEqual(["someValue1", "someValue2"]); + ).toEqual(['someValue1', 'someValue2']); }); - it_id("accc59be-fd13-46c5-a103-ec63f2ad6670")(it)( - "should support full text search", + it_id('accc59be-fd13-46c5-a103-ec63f2ad6670')(it)( + 'should support full text search', async () => { try { - const obj = new Parse.Object("FullTextSearchTest"); - obj.set("field1", "Parse GraphQL Server"); - obj.set("field2", "It rocks!"); + const obj = new Parse.Object('FullTextSearchTest'); + obj.set('field1', 'Parse GraphQL Server'); + obj.set('field2', 'It rocks!'); await obj.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -5317,7 +5317,7 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, variables: { @@ -5325,7 +5325,7 @@ describe("ParseGraphQLServer", () => { field1: { text: { search: { - term: "graphql", + term: 'graphql', }, }, }, @@ -5342,24 +5342,24 @@ describe("ParseGraphQLServer", () => { } ); - it("should support in query key", async () => { + it('should support in query key', async () => { try { - const country = new Parse.Object("Country"); - country.set("code", "FR"); + const country = new Parse.Object('Country'); + country.set('code', 'FR'); await country.save(); - const country2 = new Parse.Object("Country"); - country2.set("code", "US"); + const country2 = new Parse.Object('Country'); + country2.set('code', 'US'); await country2.save(); - const city = new Parse.Object("City"); - city.set("country", "FR"); - city.set("name", "city1"); + const city = new Parse.Object('City'); + city.set('country', 'FR'); + city.set('name', 'city1'); await city.save(); - const city2 = new Parse.Object("City"); - city2.set("country", "US"); - city2.set("name", "city2"); + const city2 = new Parse.Object('City'); + city2.set('country', 'US'); + city2.set('name', 'city2'); await city2.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -5383,7 +5383,7 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, variables: { @@ -5391,10 +5391,10 @@ describe("ParseGraphQLServer", () => { country: { inQueryKey: { query: { - className: "Country", - where: { code: { equalTo: "US" } }, + className: 'Country', + where: { code: { equalTo: 'US' } }, }, - key: "code", + key: 'code', }, }, }, @@ -5402,20 +5402,20 @@ describe("ParseGraphQLServer", () => { }); expect(result.length).toEqual(1); - expect(result[0].node.name).toEqual("city2"); + expect(result[0].node.name).toEqual('city2'); } catch (e) { handleError(e); } }); - it_id("0fd03d3c-a2c8-4fac-95cc-2391a3032ca2")(it)( - "should support order, skip and first arguments", + it_id('0fd03d3c-a2c8-4fac-95cc-2391a3032ca2')(it)( + 'should support order, skip and first arguments', async () => { const promises = []; for (let i = 0; i < 100; i++) { - const obj = new Parse.Object("SomeClass"); - obj.set("someField", `someValue${i < 10 ? "0" : ""}${i}`); - obj.set("numberField", i % 3); + const obj = new Parse.Object('SomeClass'); + obj.set('someField', `someValue${i < 10 ? '0' : ''}${i}`); + obj.set('numberField', i % 3); promises.push(obj.save()); } await Promise.all(promises); @@ -5447,10 +5447,10 @@ describe("ParseGraphQLServer", () => { variables: { where: { someField: { - matchesRegex: "^someValue", + matchesRegex: '^someValue', }, }, - order: ["numberField_DESC", "someField_ASC"], + order: ['numberField_DESC', 'someField_ASC'], skip: 4, first: 2, }, @@ -5458,12 +5458,12 @@ describe("ParseGraphQLServer", () => { expect( result.data.find.edges.map(obj => obj.node.someField) - ).toEqual(["someValue14", "someValue17"]); + ).toEqual(['someValue14', 'someValue17']); } ); - it_id("588a70c6-2932-4d3b-a838-a74c59d8cffb")(it)( - "should support pagination", + it_id('588a70c6-2932-4d3b-a838-a74c59d8cffb')(it)( + 'should support pagination', async () => { const numberArray = (first, last) => { const array = []; @@ -5475,8 +5475,8 @@ describe("ParseGraphQLServer", () => { const promises = []; for (let i = 0; i < 100; i++) { - const obj = new Parse.Object("SomeClass"); - obj.set("numberField", i); + const obj = new Parse.Object('SomeClass'); + obj.set('numberField', i); promises.push(obj.save()); } await Promise.all(promises); @@ -5525,7 +5525,7 @@ describe("ParseGraphQLServer", () => { } `, variables: { - order: ["numberField_ASC"], + order: ['numberField_ASC'], skip, after, first, @@ -5633,8 +5633,8 @@ describe("ParseGraphQLServer", () => { } ); - it_id("4f6a5f20-9642-4cf0-b31d-e739672a9096")(it)( - "should support count", + it_id('4f6a5f20-9642-4cf0-b31d-e739672a9096')(it)( + 'should support count', async () => { await prepareData(); @@ -5642,7 +5642,7 @@ describe("ParseGraphQLServer", () => { const where = { someField: { - in: ["someValue1", "someValue2", "someValue3"], + in: ['someValue1', 'someValue2', 'someValue3'], }, OR: [ { @@ -5684,7 +5684,7 @@ describe("ParseGraphQLServer", () => { }, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -5694,13 +5694,13 @@ describe("ParseGraphQLServer", () => { } ); - it("should only count", async () => { + it('should only count', async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); const where = { someField: { - in: ["someValue1", "someValue2", "someValue3"], + in: ['someValue1', 'someValue2', 'someValue3'], }, OR: [ { @@ -5733,7 +5733,7 @@ describe("ParseGraphQLServer", () => { }, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -5742,8 +5742,8 @@ describe("ParseGraphQLServer", () => { expect(result.data.find.count).toEqual(2); }); - it_id("942b57be-ca8a-4a5b-8104-2adef8743b1a")(it)( - "should respect max limit", + it_id('942b57be-ca8a-4a5b-8104-2adef8743b1a')(it)( + 'should respect max limit', async () => { parseServer = await global.reconfigureServer({ maxLimit: 10, @@ -5751,7 +5751,7 @@ describe("ParseGraphQLServer", () => { await createGQLFromParseServer(parseServer); const promises = []; for (let i = 0; i < 100; i++) { - const obj = new Parse.Object("SomeClass"); + const obj = new Parse.Object('SomeClass'); promises.push(obj.save()); } await Promise.all(promises); @@ -5779,7 +5779,7 @@ describe("ParseGraphQLServer", () => { }, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -5789,8 +5789,8 @@ describe("ParseGraphQLServer", () => { } ); - it_id("952634f0-0ad5-4a08-8da2-187c1bd9ee94")(it)( - "should support keys argument", + it_id('952634f0-0ad5-4a08-8da2-187c1bd9ee94')(it)( + 'should support keys argument', async () => { await prepareData(); @@ -5815,7 +5815,7 @@ describe("ParseGraphQLServer", () => { }, context: { headers: { - "X-Parse-Session-Token": user1.getSessionToken(), + 'X-Parse-Session-Token': user1.getSessionToken(), }, }, }); @@ -5842,7 +5842,7 @@ describe("ParseGraphQLServer", () => { }, context: { headers: { - "X-Parse-Session-Token": user1.getSessionToken(), + 'X-Parse-Session-Token': user1.getSessionToken(), }, }, }); @@ -5858,7 +5858,7 @@ describe("ParseGraphQLServer", () => { } ); - it("should support include argument", async () => { + it('should support include argument', async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -5888,7 +5888,7 @@ describe("ParseGraphQLServer", () => { }, context: { headers: { - "X-Parse-Session-Token": user1.getSessionToken(), + 'X-Parse-Session-Token': user1.getSessionToken(), }, }, }); @@ -5912,7 +5912,7 @@ describe("ParseGraphQLServer", () => { }, context: { headers: { - "X-Parse-Session-Token": user1.getSessionToken(), + 'X-Parse-Session-Token': user1.getSessionToken(), }, }, }); @@ -5924,13 +5924,13 @@ describe("ParseGraphQLServer", () => { ).toBeDefined(); }); - describe_only_db("mongo")("read preferences", () => { - it("should read from primary by default", async () => { + describe_only_db('mongo')('read preferences', () => { + it('should read from primary by default', async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); await apolloClient.query({ query: gql` @@ -5948,7 +5948,7 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Session-Token": user1.getSessionToken(), + 'X-Parse-Session-Token': user1.getSessionToken(), }, }, }); @@ -5957,7 +5957,7 @@ describe("ParseGraphQLServer", () => { let foundUserClassReadPreference = false; Collection.prototype.find.calls.all().forEach(call => { if ( - call.object.s.namespace.collection.indexOf("GraphQLClass") >= + call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0 ) { foundGraphQLClassReadPreference = true; @@ -5965,7 +5965,7 @@ describe("ParseGraphQLServer", () => { ReadPreference.PRIMARY ); } else if ( - call.object.s.namespace.collection.indexOf("_User") >= 0 + call.object.s.namespace.collection.indexOf('_User') >= 0 ) { foundUserClassReadPreference = true; expect(call.object.s.readPreference.mode).toBe( @@ -5978,12 +5978,12 @@ describe("ParseGraphQLServer", () => { expect(foundUserClassReadPreference).toBe(true); }); - it("should support readPreference argument", async () => { + it('should support readPreference argument', async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); await apolloClient.query({ query: gql` @@ -6003,7 +6003,7 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -6012,7 +6012,7 @@ describe("ParseGraphQLServer", () => { let foundUserClassReadPreference = false; Collection.prototype.find.calls.all().forEach(call => { if ( - call.object.s.namespace.collection.indexOf("GraphQLClass") >= + call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0 ) { foundGraphQLClassReadPreference = true; @@ -6020,7 +6020,7 @@ describe("ParseGraphQLServer", () => { ReadPreference.SECONDARY ); } else if ( - call.object.s.namespace.collection.indexOf("_User") >= 0 + call.object.s.namespace.collection.indexOf('_User') >= 0 ) { foundUserClassReadPreference = true; expect(call.args[1].readPreference).toBe( @@ -6033,12 +6033,12 @@ describe("ParseGraphQLServer", () => { expect(foundUserClassReadPreference).toBe(true); }); - it("should support includeReadPreference argument", async () => { + it('should support includeReadPreference argument', async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); await apolloClient.query({ query: gql` @@ -6061,7 +6061,7 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -6070,7 +6070,7 @@ describe("ParseGraphQLServer", () => { let foundUserClassReadPreference = false; Collection.prototype.find.calls.all().forEach(call => { if ( - call.object.s.namespace.collection.indexOf("GraphQLClass") >= + call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0 ) { foundGraphQLClassReadPreference = true; @@ -6078,7 +6078,7 @@ describe("ParseGraphQLServer", () => { ReadPreference.SECONDARY ); } else if ( - call.object.s.namespace.collection.indexOf("_User") >= 0 + call.object.s.namespace.collection.indexOf('_User') >= 0 ) { foundUserClassReadPreference = true; expect(call.args[1].readPreference).toBe( @@ -6091,13 +6091,13 @@ describe("ParseGraphQLServer", () => { expect(foundUserClassReadPreference).toBe(true); }); - it("should support subqueryReadPreference argument", async () => { + it('should support subqueryReadPreference argument', async () => { try { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); await apolloClient.query({ query: gql` @@ -6122,7 +6122,7 @@ describe("ParseGraphQLServer", () => { pointerToUser: { have: { objectId: { - equalTo: "xxxx", + equalTo: 'xxxx', }, }, }, @@ -6130,7 +6130,7 @@ describe("ParseGraphQLServer", () => { }, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -6140,7 +6140,7 @@ describe("ParseGraphQLServer", () => { Collection.prototype.find.calls.all().forEach(call => { if ( call.object.s.namespace.collection.indexOf( - "GraphQLClass" + 'GraphQLClass' ) >= 0 ) { foundGraphQLClassReadPreference = true; @@ -6148,7 +6148,7 @@ describe("ParseGraphQLServer", () => { ReadPreference.SECONDARY ); } else if ( - call.object.s.namespace.collection.indexOf("_User") >= 0 + call.object.s.namespace.collection.indexOf('_User') >= 0 ) { foundUserClassReadPreference = true; expect(call.args[1].readPreference).toBe( @@ -6165,7 +6165,7 @@ describe("ParseGraphQLServer", () => { }); }); - it("should order by multiple fields", async () => { + it('should order by multiple fields', async () => { await prepareData(); await resetGraphQLCache(); @@ -6185,11 +6185,11 @@ describe("ParseGraphQLServer", () => { } `, variables: { - order: ["someOtherField_DESC", "someField_ASC"], + order: ['someOtherField_DESC', 'someField_ASC'], }, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -6202,13 +6202,13 @@ describe("ParseGraphQLServer", () => { ).toEqual([object3.id, object1.id, object2.id]); }); - it_only_db("mongo")( - "should order by multiple fields on a relation field", + it_only_db('mongo')( + 'should order by multiple fields on a relation field', async () => { await prepareData(); - const parentObject = new Parse.Object("ParentClass"); - const relation = parentObject.relation("graphQLClasses"); + const parentObject = new Parse.Object('ParentClass'); + const relation = parentObject.relation('graphQLClasses'); relation.add(object1); relation.add(object2); relation.add(object3); @@ -6237,11 +6237,11 @@ describe("ParseGraphQLServer", () => { `, variables: { id: parentObject.id, - order: ["someOtherField_DESC", "someField_ASC"], + order: ['someOtherField_DESC', 'someField_ASC'], }, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -6257,8 +6257,8 @@ describe("ParseGraphQLServer", () => { } ); - it_id("47a6adf3-1cb4-4d92-b74c-e480363f9cb5")(it)( - "should support including relation", + it_id('47a6adf3-1cb4-4d92-b74c-e480363f9cb5')(it)( + 'should support including relation', async () => { await prepareData(); @@ -6279,7 +6279,7 @@ describe("ParseGraphQLServer", () => { variables: {}, context: { headers: { - "X-Parse-Session-Token": user1.getSessionToken(), + 'X-Parse-Session-Token': user1.getSessionToken(), }, }, }); @@ -6306,7 +6306,7 @@ describe("ParseGraphQLServer", () => { variables: {}, context: { headers: { - "X-Parse-Session-Token": user1.getSessionToken(), + 'X-Parse-Session-Token': user1.getSessionToken(), }, }, }); @@ -6325,12 +6325,12 @@ describe("ParseGraphQLServer", () => { }); }); - describe("Objects Mutations", () => { - describe("Create", () => { - it("should return specific type object using class specific mutation", async () => { + describe('Objects Mutations', () => { + describe('Create', () => { + it('should return specific type object using class specific mutation', async () => { const clientMutationId = uuidv4(); - const customerSchema = new Parse.Schema("Customer"); - customerSchema.addString("someField"); + const customerSchema = new Parse.Schema('Customer'); + customerSchema.addString('someField'); await customerSchema.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -6353,7 +6353,7 @@ describe("ParseGraphQLServer", () => { input: { clientMutationId, fields: { - someField: "someValue", + someField: 'someValue', }, }, }, @@ -6364,20 +6364,20 @@ describe("ParseGraphQLServer", () => { ); expect(result.data.createCustomer.customer.id).toBeDefined(); expect(result.data.createCustomer.customer.someField).toEqual( - "someValue" + 'someValue' ); - const customer = await new Parse.Query("Customer").get( + const customer = await new Parse.Query('Customer').get( result.data.createCustomer.customer.objectId ); expect(customer.createdAt).toEqual( new Date(result.data.createCustomer.customer.createdAt) ); - expect(customer.get("someField")).toEqual("someValue"); + expect(customer.get('someField')).toEqual('someValue'); }); - it("should respect level permissions", async () => { + it('should respect level permissions', async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -6409,61 +6409,61 @@ describe("ParseGraphQLServer", () => { return result; } - await expectAsync(createObject("GraphQLClass")).toBeRejectedWith( + await expectAsync(createObject('GraphQLClass')).toBeRejectedWith( jasmine.stringMatching( - "Permission denied for action create on class GraphQLClass" + 'Permission denied for action create on class GraphQLClass' ) ); - await expectAsync(createObject("PublicClass")).toBeResolved(); + await expectAsync(createObject('PublicClass')).toBeResolved(); await expectAsync( - createObject("GraphQLClass", { "X-Parse-Master-Key": "test" }) + createObject('GraphQLClass', { 'X-Parse-Master-Key': 'test' }) ).toBeResolved(); await expectAsync( - createObject("PublicClass", { "X-Parse-Master-Key": "test" }) + createObject('PublicClass', { 'X-Parse-Master-Key': 'test' }) ).toBeResolved(); await expectAsync( - createObject("GraphQLClass", { - "X-Parse-Session-Token": user1.getSessionToken(), + createObject('GraphQLClass', { + 'X-Parse-Session-Token': user1.getSessionToken(), }) ).toBeResolved(); await expectAsync( - createObject("PublicClass", { - "X-Parse-Session-Token": user1.getSessionToken(), + createObject('PublicClass', { + 'X-Parse-Session-Token': user1.getSessionToken(), }) ).toBeResolved(); await expectAsync( - createObject("GraphQLClass", { - "X-Parse-Session-Token": user2.getSessionToken(), + createObject('GraphQLClass', { + 'X-Parse-Session-Token': user2.getSessionToken(), }) ).toBeResolved(); await expectAsync( - createObject("PublicClass", { - "X-Parse-Session-Token": user2.getSessionToken(), + createObject('PublicClass', { + 'X-Parse-Session-Token': user2.getSessionToken(), }) ).toBeResolved(); await expectAsync( - createObject("GraphQLClass", { - "X-Parse-Session-Token": user4.getSessionToken(), + createObject('GraphQLClass', { + 'X-Parse-Session-Token': user4.getSessionToken(), }) ).toBeRejectedWith( jasmine.stringMatching( - "Permission denied for action create on class GraphQLClass" + 'Permission denied for action create on class GraphQLClass' ) ); await expectAsync( - createObject("PublicClass", { - "X-Parse-Session-Token": user4.getSessionToken(), + createObject('PublicClass', { + 'X-Parse-Session-Token': user4.getSessionToken(), }) ).toBeResolved(); }); }); - describe("Update", () => { - it("should return specific type object using class specific mutation", async () => { + describe('Update', () => { + it('should return specific type object using class specific mutation', async () => { const clientMutationId = uuidv4(); - const obj = new Parse.Object("Customer"); - obj.set("someField1", "someField1Value1"); - obj.set("someField2", "someField2Value1"); + const obj = new Parse.Object('Customer'); + obj.set('someField1', 'someField1Value1'); + obj.set('someField2', 'someField2Value1'); await obj.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -6486,7 +6486,7 @@ describe("ParseGraphQLServer", () => { clientMutationId, id: obj.id, fields: { - someField1: "someField1Value2", + someField1: 'someField1Value2', }, }, }, @@ -6497,22 +6497,22 @@ describe("ParseGraphQLServer", () => { ); expect(result.data.updateCustomer.customer.updatedAt).toBeDefined(); expect(result.data.updateCustomer.customer.someField1).toEqual( - "someField1Value2" + 'someField1Value2' ); expect(result.data.updateCustomer.customer.someField2).toEqual( - "someField2Value1" + 'someField2Value1' ); await obj.fetch(); - expect(obj.get("someField1")).toEqual("someField1Value2"); - expect(obj.get("someField2")).toEqual("someField2Value1"); + expect(obj.get('someField1')).toEqual('someField1Value2'); + expect(obj.get('someField2')).toEqual('someField2Value1'); }); - it("should return only id using class specific mutation", async () => { - const obj = new Parse.Object("Customer"); - obj.set("someField1", "someField1Value1"); - obj.set("someField2", "someField2Value1"); + it('should return only id using class specific mutation', async () => { + const obj = new Parse.Object('Customer'); + obj.set('someField1', 'someField1Value1'); + obj.set('someField2', 'someField2Value1'); await obj.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -6534,7 +6534,7 @@ describe("ParseGraphQLServer", () => { variables: { id: obj.id, fields: { - someField1: "someField1Value2", + someField1: 'someField1Value2', }, }, }); @@ -6545,11 +6545,11 @@ describe("ParseGraphQLServer", () => { await obj.fetch(); - expect(obj.get("someField1")).toEqual("someField1Value2"); - expect(obj.get("someField2")).toEqual("someField2Value1"); + expect(obj.get('someField1')).toEqual('someField1Value2'); + expect(obj.get('someField2')).toEqual('someField2Value1'); }); - it("should respect level permissions", async () => { + it('should respect level permissions', async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -6582,25 +6582,25 @@ describe("ParseGraphQLServer", () => { await Promise.all( objects.slice(0, 3).map(async obj => { - const originalFieldValue = obj.get("someField"); + const originalFieldValue = obj.get('someField'); await expectAsync( updateObject(obj.className, obj.id, { - someField: "changedValue1", + someField: 'changedValue1', }) - ).toBeRejectedWith(jasmine.stringMatching("Object not found")); + ).toBeRejectedWith(jasmine.stringMatching('Object not found')); await obj.fetch({ useMasterKey: true }); - expect(obj.get("someField")).toEqual(originalFieldValue); + expect(obj.get('someField')).toEqual(originalFieldValue); }) ); expect( ( await updateObject(object4.className, object4.id, { - someField: "changedValue1", + someField: 'changedValue1', }) ).data.update.clientMutationId ).toBeDefined(); await object4.fetch({ useMasterKey: true }); - expect(object4.get("someField")).toEqual("changedValue1"); + expect(object4.get('someField')).toEqual('changedValue1'); await Promise.all( objects.map(async obj => { expect( @@ -6608,13 +6608,13 @@ describe("ParseGraphQLServer", () => { await updateObject( obj.className, obj.id, - { someField: "changedValue2" }, - { "X-Parse-Master-Key": "test" } + { someField: 'changedValue2' }, + { 'X-Parse-Master-Key': 'test' } ) ).data.update.clientMutationId ).toBeDefined(); await obj.fetch({ useMasterKey: true }); - expect(obj.get("someField")).toEqual("changedValue2"); + expect(obj.get('someField')).toEqual('changedValue2'); }) ); await Promise.all( @@ -6624,13 +6624,13 @@ describe("ParseGraphQLServer", () => { await updateObject( obj.className, obj.id, - { someField: "changedValue3" }, - { "X-Parse-Session-Token": user1.getSessionToken() } + { someField: 'changedValue3' }, + { 'X-Parse-Session-Token': user1.getSessionToken() } ) ).data.update.clientMutationId ).toBeDefined(); await obj.fetch({ useMasterKey: true }); - expect(obj.get("someField")).toEqual("changedValue3"); + expect(obj.get('someField')).toEqual('changedValue3'); }) ); await Promise.all( @@ -6640,13 +6640,13 @@ describe("ParseGraphQLServer", () => { await updateObject( obj.className, obj.id, - { someField: "changedValue4" }, - { "X-Parse-Session-Token": user2.getSessionToken() } + { someField: 'changedValue4' }, + { 'X-Parse-Session-Token': user2.getSessionToken() } ) ).data.update.clientMutationId ).toBeDefined(); await obj.fetch({ useMasterKey: true }); - expect(obj.get("someField")).toEqual("changedValue4"); + expect(obj.get('someField')).toEqual('changedValue4'); }) ); await Promise.all( @@ -6656,39 +6656,39 @@ describe("ParseGraphQLServer", () => { await updateObject( obj.className, obj.id, - { someField: "changedValue5" }, - { "X-Parse-Session-Token": user3.getSessionToken() } + { someField: 'changedValue5' }, + { 'X-Parse-Session-Token': user3.getSessionToken() } ) ).data.update.clientMutationId ).toBeDefined(); await obj.fetch({ useMasterKey: true }); - expect(obj.get("someField")).toEqual("changedValue5"); + expect(obj.get('someField')).toEqual('changedValue5'); }) ); - const originalFieldValue = object2.get("someField"); + const originalFieldValue = object2.get('someField'); await expectAsync( updateObject( object2.className, object2.id, - { someField: "changedValue5" }, - { "X-Parse-Session-Token": user3.getSessionToken() } + { someField: 'changedValue5' }, + { 'X-Parse-Session-Token': user3.getSessionToken() } ) - ).toBeRejectedWith(jasmine.stringMatching("Object not found")); + ).toBeRejectedWith(jasmine.stringMatching('Object not found')); await object2.fetch({ useMasterKey: true }); - expect(object2.get("someField")).toEqual(originalFieldValue); + expect(object2.get('someField')).toEqual(originalFieldValue); await Promise.all( objects.slice(0, 3).map(async obj => { - const originalFieldValue = obj.get("someField"); + const originalFieldValue = obj.get('someField'); await expectAsync( updateObject( obj.className, obj.id, - { someField: "changedValue6" }, - { "X-Parse-Session-Token": user4.getSessionToken() } + { someField: 'changedValue6' }, + { 'X-Parse-Session-Token': user4.getSessionToken() } ) - ).toBeRejectedWith(jasmine.stringMatching("Object not found")); + ).toBeRejectedWith(jasmine.stringMatching('Object not found')); await obj.fetch({ useMasterKey: true }); - expect(obj.get("someField")).toEqual(originalFieldValue); + expect(obj.get('someField')).toEqual(originalFieldValue); }) ); expect( @@ -6696,26 +6696,26 @@ describe("ParseGraphQLServer", () => { await updateObject( object4.className, object4.id, - { someField: "changedValue6" }, - { "X-Parse-Session-Token": user4.getSessionToken() } + { someField: 'changedValue6' }, + { 'X-Parse-Session-Token': user4.getSessionToken() } ) ).data.update.clientMutationId ).toBeDefined(); await object4.fetch({ useMasterKey: true }); - expect(object4.get("someField")).toEqual("changedValue6"); + expect(object4.get('someField')).toEqual('changedValue6'); await Promise.all( objects.slice(0, 2).map(async obj => { - const originalFieldValue = obj.get("someField"); + const originalFieldValue = obj.get('someField'); await expectAsync( updateObject( obj.className, obj.id, - { someField: "changedValue7" }, - { "X-Parse-Session-Token": user5.getSessionToken() } + { someField: 'changedValue7' }, + { 'X-Parse-Session-Token': user5.getSessionToken() } ) - ).toBeRejectedWith(jasmine.stringMatching("Object not found")); + ).toBeRejectedWith(jasmine.stringMatching('Object not found')); await obj.fetch({ useMasterKey: true }); - expect(obj.get("someField")).toEqual(originalFieldValue); + expect(obj.get('someField')).toEqual(originalFieldValue); }) ); expect( @@ -6723,28 +6723,28 @@ describe("ParseGraphQLServer", () => { await updateObject( object3.className, object3.id, - { someField: "changedValue7" }, - { "X-Parse-Session-Token": user5.getSessionToken() } + { someField: 'changedValue7' }, + { 'X-Parse-Session-Token': user5.getSessionToken() } ) ).data.update.clientMutationId ).toBeDefined(); await object3.fetch({ useMasterKey: true }); - expect(object3.get("someField")).toEqual("changedValue7"); + expect(object3.get('someField')).toEqual('changedValue7'); expect( ( await updateObject( object4.className, object4.id, - { someField: "changedValue7" }, - { "X-Parse-Session-Token": user5.getSessionToken() } + { someField: 'changedValue7' }, + { 'X-Parse-Session-Token': user5.getSessionToken() } ) ).data.update.clientMutationId ).toBeDefined(); await object4.fetch({ useMasterKey: true }); - expect(object4.get("someField")).toEqual("changedValue7"); + expect(object4.get('someField')).toEqual('changedValue7'); }); - it("should respect level permissions with specific class mutation", async () => { + it('should respect level permissions with specific class mutation', async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -6781,20 +6781,20 @@ describe("ParseGraphQLServer", () => { await Promise.all( objects.slice(0, 3).map(async obj => { - const originalFieldValue = obj.get("someField"); + const originalFieldValue = obj.get('someField'); await expectAsync( updateObject(obj.className, obj.id, { - someField: "changedValue1", + someField: 'changedValue1', }) - ).toBeRejectedWith(jasmine.stringMatching("Object not found")); + ).toBeRejectedWith(jasmine.stringMatching('Object not found')); await obj.fetch({ useMasterKey: true }); - expect(obj.get("someField")).toEqual(originalFieldValue); + expect(obj.get('someField')).toEqual(originalFieldValue); }) ); expect( ( await updateObject(object4.className, object4.id, { - someField: "changedValue1", + someField: 'changedValue1', }) ).data[`update${object4.className}`][ object4.className.charAt(0).toLowerCase() + @@ -6802,7 +6802,7 @@ describe("ParseGraphQLServer", () => { ].updatedAt ).toBeDefined(); await object4.fetch({ useMasterKey: true }); - expect(object4.get("someField")).toEqual("changedValue1"); + expect(object4.get('someField')).toEqual('changedValue1'); await Promise.all( objects.map(async obj => { expect( @@ -6810,8 +6810,8 @@ describe("ParseGraphQLServer", () => { await updateObject( obj.className, obj.id, - { someField: "changedValue2" }, - { "X-Parse-Master-Key": "test" } + { someField: 'changedValue2' }, + { 'X-Parse-Master-Key': 'test' } ) ).data[`update${obj.className}`][ obj.className.charAt(0).toLowerCase() + @@ -6819,7 +6819,7 @@ describe("ParseGraphQLServer", () => { ].updatedAt ).toBeDefined(); await obj.fetch({ useMasterKey: true }); - expect(obj.get("someField")).toEqual("changedValue2"); + expect(obj.get('someField')).toEqual('changedValue2'); }) ); await Promise.all( @@ -6829,8 +6829,8 @@ describe("ParseGraphQLServer", () => { await updateObject( obj.className, obj.id, - { someField: "changedValue3" }, - { "X-Parse-Session-Token": user1.getSessionToken() } + { someField: 'changedValue3' }, + { 'X-Parse-Session-Token': user1.getSessionToken() } ) ).data[`update${obj.className}`][ obj.className.charAt(0).toLowerCase() + @@ -6838,7 +6838,7 @@ describe("ParseGraphQLServer", () => { ].updatedAt ).toBeDefined(); await obj.fetch({ useMasterKey: true }); - expect(obj.get("someField")).toEqual("changedValue3"); + expect(obj.get('someField')).toEqual('changedValue3'); }) ); await Promise.all( @@ -6848,8 +6848,8 @@ describe("ParseGraphQLServer", () => { await updateObject( obj.className, obj.id, - { someField: "changedValue4" }, - { "X-Parse-Session-Token": user2.getSessionToken() } + { someField: 'changedValue4' }, + { 'X-Parse-Session-Token': user2.getSessionToken() } ) ).data[`update${obj.className}`][ obj.className.charAt(0).toLowerCase() + @@ -6857,7 +6857,7 @@ describe("ParseGraphQLServer", () => { ].updatedAt ).toBeDefined(); await obj.fetch({ useMasterKey: true }); - expect(obj.get("someField")).toEqual("changedValue4"); + expect(obj.get('someField')).toEqual('changedValue4'); }) ); await Promise.all( @@ -6867,8 +6867,8 @@ describe("ParseGraphQLServer", () => { await updateObject( obj.className, obj.id, - { someField: "changedValue5" }, - { "X-Parse-Session-Token": user3.getSessionToken() } + { someField: 'changedValue5' }, + { 'X-Parse-Session-Token': user3.getSessionToken() } ) ).data[`update${obj.className}`][ obj.className.charAt(0).toLowerCase() + @@ -6876,33 +6876,33 @@ describe("ParseGraphQLServer", () => { ].updatedAt ).toBeDefined(); await obj.fetch({ useMasterKey: true }); - expect(obj.get("someField")).toEqual("changedValue5"); + expect(obj.get('someField')).toEqual('changedValue5'); }) ); - const originalFieldValue = object2.get("someField"); + const originalFieldValue = object2.get('someField'); await expectAsync( updateObject( object2.className, object2.id, - { someField: "changedValue5" }, - { "X-Parse-Session-Token": user3.getSessionToken() } + { someField: 'changedValue5' }, + { 'X-Parse-Session-Token': user3.getSessionToken() } ) - ).toBeRejectedWith(jasmine.stringMatching("Object not found")); + ).toBeRejectedWith(jasmine.stringMatching('Object not found')); await object2.fetch({ useMasterKey: true }); - expect(object2.get("someField")).toEqual(originalFieldValue); + expect(object2.get('someField')).toEqual(originalFieldValue); await Promise.all( objects.slice(0, 3).map(async obj => { - const originalFieldValue = obj.get("someField"); + const originalFieldValue = obj.get('someField'); await expectAsync( updateObject( obj.className, obj.id, - { someField: "changedValue6" }, - { "X-Parse-Session-Token": user4.getSessionToken() } + { someField: 'changedValue6' }, + { 'X-Parse-Session-Token': user4.getSessionToken() } ) - ).toBeRejectedWith(jasmine.stringMatching("Object not found")); + ).toBeRejectedWith(jasmine.stringMatching('Object not found')); await obj.fetch({ useMasterKey: true }); - expect(obj.get("someField")).toEqual(originalFieldValue); + expect(obj.get('someField')).toEqual(originalFieldValue); }) ); expect( @@ -6910,8 +6910,8 @@ describe("ParseGraphQLServer", () => { await updateObject( object4.className, object4.id, - { someField: "changedValue6" }, - { "X-Parse-Session-Token": user4.getSessionToken() } + { someField: 'changedValue6' }, + { 'X-Parse-Session-Token': user4.getSessionToken() } ) ).data[`update${object4.className}`][ object4.className.charAt(0).toLowerCase() + @@ -6919,20 +6919,20 @@ describe("ParseGraphQLServer", () => { ].updatedAt ).toBeDefined(); await object4.fetch({ useMasterKey: true }); - expect(object4.get("someField")).toEqual("changedValue6"); + expect(object4.get('someField')).toEqual('changedValue6'); await Promise.all( objects.slice(0, 2).map(async obj => { - const originalFieldValue = obj.get("someField"); + const originalFieldValue = obj.get('someField'); await expectAsync( updateObject( obj.className, obj.id, - { someField: "changedValue7" }, - { "X-Parse-Session-Token": user5.getSessionToken() } + { someField: 'changedValue7' }, + { 'X-Parse-Session-Token': user5.getSessionToken() } ) - ).toBeRejectedWith(jasmine.stringMatching("Object not found")); + ).toBeRejectedWith(jasmine.stringMatching('Object not found')); await obj.fetch({ useMasterKey: true }); - expect(obj.get("someField")).toEqual(originalFieldValue); + expect(obj.get('someField')).toEqual(originalFieldValue); }) ); expect( @@ -6940,8 +6940,8 @@ describe("ParseGraphQLServer", () => { await updateObject( object3.className, object3.id, - { someField: "changedValue7" }, - { "X-Parse-Session-Token": user5.getSessionToken() } + { someField: 'changedValue7' }, + { 'X-Parse-Session-Token': user5.getSessionToken() } ) ).data[`update${object3.className}`][ object3.className.charAt(0).toLowerCase() + @@ -6949,14 +6949,14 @@ describe("ParseGraphQLServer", () => { ].updatedAt ).toBeDefined(); await object3.fetch({ useMasterKey: true }); - expect(object3.get("someField")).toEqual("changedValue7"); + expect(object3.get('someField')).toEqual('changedValue7'); expect( ( await updateObject( object4.className, object4.id, - { someField: "changedValue7" }, - { "X-Parse-Session-Token": user5.getSessionToken() } + { someField: 'changedValue7' }, + { 'X-Parse-Session-Token': user5.getSessionToken() } ) ).data[`update${object4.className}`][ object4.className.charAt(0).toLowerCase() + @@ -6964,16 +6964,16 @@ describe("ParseGraphQLServer", () => { ].updatedAt ).toBeDefined(); await object4.fetch({ useMasterKey: true }); - expect(object4.get("someField")).toEqual("changedValue7"); + expect(object4.get('someField')).toEqual('changedValue7'); }); }); - describe("Delete", () => { - it("should return a specific type using class specific mutation", async () => { + describe('Delete', () => { + it('should return a specific type using class specific mutation', async () => { const clientMutationId = uuidv4(); - const obj = new Parse.Object("Customer"); - obj.set("someField1", "someField1Value1"); - obj.set("someField2", "someField2Value1"); + const obj = new Parse.Object('Customer'); + obj.set('someField1', 'someField1Value1'); + obj.set('someField2', 'someField2Value1'); await obj.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -7007,18 +7007,18 @@ describe("ParseGraphQLServer", () => { obj.id ); expect(result.data.deleteCustomer.customer.someField1).toEqual( - "someField1Value1" + 'someField1Value1' ); expect(result.data.deleteCustomer.customer.someField2).toEqual( - "someField2Value1" + 'someField2Value1' ); await expectAsync( obj.fetch({ useMasterKey: true }) - ).toBeRejectedWith(jasmine.stringMatching("Object not found")); + ).toBeRejectedWith(jasmine.stringMatching('Object not found')); }); - it("should respect level permissions", async () => { + it('should respect level permissions', async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -7049,24 +7049,24 @@ describe("ParseGraphQLServer", () => { await Promise.all( objects.slice(0, 3).map(async obj => { - const originalFieldValue = obj.get("someField"); + const originalFieldValue = obj.get('someField'); await expectAsync( deleteObject(obj.className, obj.id) - ).toBeRejectedWith(jasmine.stringMatching("Object not found")); + ).toBeRejectedWith(jasmine.stringMatching('Object not found')); await obj.fetch({ useMasterKey: true }); - expect(obj.get("someField")).toEqual(originalFieldValue); + expect(obj.get('someField')).toEqual(originalFieldValue); }) ); await Promise.all( objects.slice(0, 3).map(async obj => { - const originalFieldValue = obj.get("someField"); + const originalFieldValue = obj.get('someField'); await expectAsync( deleteObject(obj.className, obj.id, { - "X-Parse-Session-Token": user4.getSessionToken(), + 'X-Parse-Session-Token': user4.getSessionToken(), }) - ).toBeRejectedWith(jasmine.stringMatching("Object not found")); + ).toBeRejectedWith(jasmine.stringMatching('Object not found')); await obj.fetch({ useMasterKey: true }); - expect(obj.get("someField")).toEqual(originalFieldValue); + expect(obj.get('someField')).toEqual(originalFieldValue); }) ); expect( @@ -7074,52 +7074,52 @@ describe("ParseGraphQLServer", () => { object4.className.charAt(0).toLowerCase() + object4.className.slice(1) ] - ).toEqual({ objectId: object4.id, __typename: "PublicClass" }); + ).toEqual({ objectId: object4.id, __typename: 'PublicClass' }); await expectAsync( object4.fetch({ useMasterKey: true }) - ).toBeRejectedWith(jasmine.stringMatching("Object not found")); + ).toBeRejectedWith(jasmine.stringMatching('Object not found')); expect( ( await deleteObject(object1.className, object1.id, { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }) ).data.delete[ object1.className.charAt(0).toLowerCase() + object1.className.slice(1) ] - ).toEqual({ objectId: object1.id, __typename: "GraphQLClass" }); + ).toEqual({ objectId: object1.id, __typename: 'GraphQLClass' }); await expectAsync( object1.fetch({ useMasterKey: true }) - ).toBeRejectedWith(jasmine.stringMatching("Object not found")); + ).toBeRejectedWith(jasmine.stringMatching('Object not found')); expect( ( await deleteObject(object2.className, object2.id, { - "X-Parse-Session-Token": user2.getSessionToken(), + 'X-Parse-Session-Token': user2.getSessionToken(), }) ).data.delete[ object2.className.charAt(0).toLowerCase() + object2.className.slice(1) ] - ).toEqual({ objectId: object2.id, __typename: "GraphQLClass" }); + ).toEqual({ objectId: object2.id, __typename: 'GraphQLClass' }); await expectAsync( object2.fetch({ useMasterKey: true }) - ).toBeRejectedWith(jasmine.stringMatching("Object not found")); + ).toBeRejectedWith(jasmine.stringMatching('Object not found')); expect( ( await deleteObject(object3.className, object3.id, { - "X-Parse-Session-Token": user5.getSessionToken(), + 'X-Parse-Session-Token': user5.getSessionToken(), }) ).data.delete[ object3.className.charAt(0).toLowerCase() + object3.className.slice(1) ] - ).toEqual({ objectId: object3.id, __typename: "GraphQLClass" }); + ).toEqual({ objectId: object3.id, __typename: 'GraphQLClass' }); await expectAsync( object3.fetch({ useMasterKey: true }) - ).toBeRejectedWith(jasmine.stringMatching("Object not found")); + ).toBeRejectedWith(jasmine.stringMatching('Object not found')); }); - it("should respect level permissions with specific class mutation", async () => { + it('should respect level permissions with specific class mutation', async () => { await prepareData(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -7150,24 +7150,24 @@ describe("ParseGraphQLServer", () => { await Promise.all( objects.slice(0, 3).map(async obj => { - const originalFieldValue = obj.get("someField"); + const originalFieldValue = obj.get('someField'); await expectAsync( deleteObject(obj.className, obj.id) - ).toBeRejectedWith(jasmine.stringMatching("Object not found")); + ).toBeRejectedWith(jasmine.stringMatching('Object not found')); await obj.fetch({ useMasterKey: true }); - expect(obj.get("someField")).toEqual(originalFieldValue); + expect(obj.get('someField')).toEqual(originalFieldValue); }) ); await Promise.all( objects.slice(0, 3).map(async obj => { - const originalFieldValue = obj.get("someField"); + const originalFieldValue = obj.get('someField'); await expectAsync( deleteObject(obj.className, obj.id, { - "X-Parse-Session-Token": user4.getSessionToken(), + 'X-Parse-Session-Token': user4.getSessionToken(), }) - ).toBeRejectedWith(jasmine.stringMatching("Object not found")); + ).toBeRejectedWith(jasmine.stringMatching('Object not found')); await obj.fetch({ useMasterKey: true }); - expect(obj.get("someField")).toEqual(originalFieldValue); + expect(obj.get('someField')).toEqual(originalFieldValue); }) ); expect( @@ -7180,11 +7180,11 @@ describe("ParseGraphQLServer", () => { ).toEqual(object4.id); await expectAsync( object4.fetch({ useMasterKey: true }) - ).toBeRejectedWith(jasmine.stringMatching("Object not found")); + ).toBeRejectedWith(jasmine.stringMatching('Object not found')); expect( ( await deleteObject(object1.className, object1.id, { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }) ).data[`delete${object1.className}`][ object1.className.charAt(0).toLowerCase() + @@ -7193,11 +7193,11 @@ describe("ParseGraphQLServer", () => { ).toEqual(object1.id); await expectAsync( object1.fetch({ useMasterKey: true }) - ).toBeRejectedWith(jasmine.stringMatching("Object not found")); + ).toBeRejectedWith(jasmine.stringMatching('Object not found')); expect( ( await deleteObject(object2.className, object2.id, { - "X-Parse-Session-Token": user2.getSessionToken(), + 'X-Parse-Session-Token': user2.getSessionToken(), }) ).data[`delete${object2.className}`][ object2.className.charAt(0).toLowerCase() + @@ -7206,11 +7206,11 @@ describe("ParseGraphQLServer", () => { ).toEqual(object2.id); await expectAsync( object2.fetch({ useMasterKey: true }) - ).toBeRejectedWith(jasmine.stringMatching("Object not found")); + ).toBeRejectedWith(jasmine.stringMatching('Object not found')); expect( ( await deleteObject(object3.className, object3.id, { - "X-Parse-Session-Token": user5.getSessionToken(), + 'X-Parse-Session-Token': user5.getSessionToken(), }) ).data[`delete${object3.className}`][ object3.className.charAt(0).toLowerCase() + @@ -7219,37 +7219,37 @@ describe("ParseGraphQLServer", () => { ).toEqual(object3.id); await expectAsync( object3.fetch({ useMasterKey: true }) - ).toBeRejectedWith(jasmine.stringMatching("Object not found")); + ).toBeRejectedWith(jasmine.stringMatching('Object not found')); }); }); - it_id("f722e98e-1fd7-45c5-ade3-5177e3d542e8")(it)( - "should unset fields when null used on update/create", + it_id('f722e98e-1fd7-45c5-ade3-5177e3d542e8')(it)( + 'should unset fields when null used on update/create', async () => { - const customerSchema = new Parse.Schema("Customer"); - customerSchema.addString("aString"); - customerSchema.addBoolean("aBoolean"); - customerSchema.addDate("aDate"); - customerSchema.addArray("aArray"); - customerSchema.addGeoPoint("aGeoPoint"); - customerSchema.addPointer("aPointer", "Customer"); - customerSchema.addObject("aObject"); - customerSchema.addPolygon("aPolygon"); + const customerSchema = new Parse.Schema('Customer'); + customerSchema.addString('aString'); + customerSchema.addBoolean('aBoolean'); + customerSchema.addDate('aDate'); + customerSchema.addArray('aArray'); + customerSchema.addGeoPoint('aGeoPoint'); + customerSchema.addPointer('aPointer', 'Customer'); + customerSchema.addObject('aObject'); + customerSchema.addPolygon('aPolygon'); await customerSchema.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const cus = new Parse.Object("Customer"); - await cus.save({ aString: "hello" }); + const cus = new Parse.Object('Customer'); + await cus.save({ aString: 'hello' }); const fields = { aString: "i'm string", aBoolean: true, aDate: new Date().toISOString(), - aArray: ["hello", 1], + aArray: ['hello', 1], aGeoPoint: { latitude: 30, longitude: 30 }, aPointer: { link: cus.id }, - aObject: { prop: { subprop: 1 }, prop2: "test" }, + aObject: { prop: { subprop: 1 }, prop2: 'test' }, aPolygon: [ { latitude: 30, longitude: 30 }, { latitude: 31, longitude: 31 }, @@ -7386,18 +7386,18 @@ describe("ParseGraphQLServer", () => { ); }); - describe("Files Mutations", () => { - describe("Create", () => { - it("should return File object", async () => { + describe('Files Mutations', () => { + describe('Create', () => { + it('should return File object', async () => { const clientMutationId = uuidv4(); parseServer = await global.reconfigureServer({ - publicServerURL: "http://localhost:13377/parse", + publicServerURL: 'http://localhost:13377/parse', }); await createGQLFromParseServer(parseServer); const body = new FormData(); body.append( - "operations", + 'operations', JSON.stringify({ query: ` mutation CreateFile($input: CreateFileInput!) { @@ -7419,16 +7419,16 @@ describe("ParseGraphQLServer", () => { }) ); body.append( - "map", - JSON.stringify({ 1: ["variables.input.upload"] }) + 'map', + JSON.stringify({ 1: ['variables.input.upload'] }) ); - body.append("1", "My File Content", { - filename: "myFileName.txt", - contentType: "text/plain", + body.append('1', 'My File Content', { + filename: 'myFileName.txt', + contentType: 'text/plain', }); - let res = await fetch("http://localhost:13377/graphql", { - method: "POST", + let res = await fetch('http://localhost:13377/graphql', { + method: 'POST', headers, body, }); @@ -7450,16 +7450,16 @@ describe("ParseGraphQLServer", () => { res = await fetch(result.data.createFile.fileInfo.url); expect(res.status).toEqual(200); - expect(await res.text()).toEqual("My File Content"); + expect(await res.text()).toEqual('My File Content'); }); }); }); - describe("Users Queries", () => { - it("should return current logged user", async () => { - const userName = "user1", - password = "user1", - email = "emailUser1@parse.com"; + describe('Users Queries', () => { + it('should return current logged user', async () => { + const userName = 'user1', + password = 'user1', + email = 'emailUser1@parse.com'; const user = new Parse.User(); user.setUsername(userName); @@ -7482,7 +7482,7 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Session-Token": session.getSessionToken(), + 'X-Parse-Session-Token': session.getSessionToken(), }, }, }); @@ -7497,19 +7497,19 @@ describe("ParseGraphQLServer", () => { expect(resultEmail).toEqual(email); }); - it("should return logged user including pointer", async () => { - const foo = new Parse.Object("Foo"); - foo.set("bar", "hello"); + it('should return logged user including pointer', async () => { + const foo = new Parse.Object('Foo'); + foo.set('bar', 'hello'); - const userName = "user1", - password = "user1", - email = "emailUser1@parse.com"; + const userName = 'user1', + password = 'user1', + email = 'emailUser1@parse.com'; const user = new Parse.User(); user.setUsername(userName); user.setPassword(password); user.setEmail(email); - user.set("userFoo", foo); + user.set('userFoo', foo); await user.signUp(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -7532,7 +7532,7 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Session-Token": session.getSessionToken(), + 'X-Parse-Session-Token': session.getSessionToken(), }, }, }); @@ -7542,25 +7542,25 @@ describe("ParseGraphQLServer", () => { expect(objectId).toEqual(user.id); expect(sessionToken).toBeDefined(); expect(resultFoo).toBeDefined(); - expect(resultFoo.bar).toEqual("hello"); + expect(resultFoo.bar).toEqual('hello'); }); - it("should return logged user and do not by pass pointer security", async () => { + it('should return logged user and do not by pass pointer security', async () => { const masterKeyOnlyACL = new Parse.ACL(); masterKeyOnlyACL.setPublicReadAccess(false); masterKeyOnlyACL.setPublicWriteAccess(false); - const foo = new Parse.Object("Foo"); + const foo = new Parse.Object('Foo'); foo.setACL(masterKeyOnlyACL); - foo.set("bar", "hello"); + foo.set('bar', 'hello'); await foo.save(null, { useMasterKey: true }); - const userName = "userx1", - password = "user1", - email = "emailUserx1@parse.com"; + const userName = 'userx1', + password = 'user1', + email = 'emailUserx1@parse.com'; const user = new Parse.User(); user.setUsername(userName); user.setPassword(password); user.setEmail(email); - user.set("userFoo", foo); + user.set('userFoo', foo); await user.signUp(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -7583,7 +7583,7 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Session-Token": session.getSessionToken(), + 'X-Parse-Session-Token': session.getSessionToken(), }, }, }); @@ -7596,7 +7596,7 @@ describe("ParseGraphQLServer", () => { }); }); - describe("Users Mutations", () => { + describe('Users Mutations', () => { const challengeAdapter = { validateAuthData: () => Promise.resolve({ response: { someData: true } }), @@ -7605,9 +7605,9 @@ describe("ParseGraphQLServer", () => { options: { anOption: true }, }; - it("should create user and return authData response", async () => { + it('should create user and return authData response', async () => { parseServer = await global.reconfigureServer({ - publicServerURL: "http://localhost:13377/parse", + publicServerURL: 'http://localhost:13377/parse', auth: { challengeAdapter, }, @@ -7633,7 +7633,7 @@ describe("ParseGraphQLServer", () => { fields: { authData: { challengeAdapter: { - id: "challengeAdapter", + id: 'challengeAdapter', }, }, }, @@ -7641,7 +7641,7 @@ describe("ParseGraphQLServer", () => { }, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -7654,18 +7654,18 @@ describe("ParseGraphQLServer", () => { }); }); - it("should sign user up", async () => { + it('should sign user up', async () => { parseServer = await global.reconfigureServer({ - publicServerURL: "http://localhost:13377/parse", + publicServerURL: 'http://localhost:13377/parse', auth: { challengeAdapter, }, }); await createGQLFromParseServer(parseServer); const clientMutationId = uuidv4(); - const userSchema = new Parse.Schema("_User"); - userSchema.addString("someField"); - userSchema.addPointer("aPointer", "_User"); + const userSchema = new Parse.Schema('_User'); + userSchema.addString('someField'); + userSchema.addPointer('aPointer', '_User'); await userSchema.update(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -7692,22 +7692,22 @@ describe("ParseGraphQLServer", () => { input: { clientMutationId, fields: { - username: "user1", - password: "user1", + username: 'user1', + password: 'user1', authData: { challengeAdapter: { - id: "challengeAdapter", + id: 'challengeAdapter', }, }, aPointer: { createAndLink: { - username: "user2", - password: "user2", - someField: "someValue2", + username: 'user2', + password: 'user2', + someField: 'someValue2', ACL: { public: { read: true, write: true } }, }, }, - someField: "someValue", + someField: 'someValue', }, }, }, @@ -7715,32 +7715,32 @@ describe("ParseGraphQLServer", () => { expect(result.data.signUp.clientMutationId).toEqual(clientMutationId); expect(result.data.signUp.viewer.sessionToken).toBeDefined(); - expect(result.data.signUp.viewer.user.someField).toEqual("someValue"); + expect(result.data.signUp.viewer.user.someField).toEqual('someValue'); expect(result.data.signUp.viewer.user.aPointer.id).toBeDefined(); expect(result.data.signUp.viewer.user.aPointer.username).toEqual( - "user2" + 'user2' ); - expect(typeof result.data.signUp.viewer.sessionToken).toBe("string"); + expect(typeof result.data.signUp.viewer.sessionToken).toBe('string'); expect(result.data.signUp.viewer.user.authDataResponse).toEqual({ challengeAdapter: { someData: true }, }); }); - it("should login with user", async () => { + it('should login with user', async () => { const clientMutationId = uuidv4(); - const userSchema = new Parse.Schema("_User"); + const userSchema = new Parse.Schema('_User'); parseServer = await global.reconfigureServer({ - publicServerURL: "http://localhost:13377/parse", + publicServerURL: 'http://localhost:13377/parse', auth: { challengeAdapter, myAuth: { - module: global.mockCustomAuthenticator("parse", "graphql"), + module: global.mockCustomAuthenticator('parse', 'graphql'), }, }, }); await createGQLFromParseServer(parseServer); - userSchema.addString("someField"); - userSchema.addPointer("aPointer", "_User"); + userSchema.addString('someField'); + userSchema.addPointer('aPointer', '_User'); await userSchema.update(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); const result = await apolloClient.mutate({ @@ -7766,19 +7766,19 @@ describe("ParseGraphQLServer", () => { input: { clientMutationId, authData: { - challengeAdapter: { id: "challengeAdapter" }, + challengeAdapter: { id: 'challengeAdapter' }, myAuth: { - id: "parse", - password: "graphql", + id: 'parse', + password: 'graphql', }, }, fields: { - someField: "someValue", + someField: 'someValue', aPointer: { createAndLink: { - username: "user2", - password: "user2", - someField: "someValue2", + username: 'user2', + password: 'user2', + someField: 'someValue2', ACL: { public: { read: true, write: true } }, }, }, @@ -7792,33 +7792,33 @@ describe("ParseGraphQLServer", () => { ); expect(result.data.logInWith.viewer.sessionToken).toBeDefined(); expect(result.data.logInWith.viewer.user.someField).toEqual( - "someValue" + 'someValue' ); expect(typeof result.data.logInWith.viewer.sessionToken).toBe( - "string" + 'string' ); expect(result.data.logInWith.viewer.user.aPointer.id).toBeDefined(); expect(result.data.logInWith.viewer.user.aPointer.username).toEqual( - "user2" + 'user2' ); expect(result.data.logInWith.viewer.user.authDataResponse).toEqual({ challengeAdapter: { someData: true }, }); }); - it("should handle challenge", async () => { + it('should handle challenge', async () => { const clientMutationId = uuidv4(); - spyOn(challengeAdapter, "challenge").and.callThrough(); + spyOn(challengeAdapter, 'challenge').and.callThrough(); parseServer = await global.reconfigureServer({ - publicServerURL: "http://localhost:13377/parse", + publicServerURL: 'http://localhost:13377/parse', auth: { challengeAdapter, }, }); await createGQLFromParseServer(parseServer); const user = new Parse.User(); - await user.save({ username: "username", password: "password" }); + await user.save({ username: 'username', password: 'password' }); const result = await apolloClient.mutate({ mutation: gql` @@ -7832,8 +7832,8 @@ describe("ParseGraphQLServer", () => { variables: { input: { clientMutationId, - username: "username", - password: "password", + username: 'username', + password: 'password', challengeData: { challengeAdapter: { someChallengeData: true }, }, @@ -7871,8 +7871,8 @@ describe("ParseGraphQLServer", () => { variables: { input: { clientMutationId, - username: "username", - password: "wrongPassword", + username: 'username', + password: 'wrongPassword', challengeData: { challengeAdapter: { someChallengeData: true }, }, @@ -7882,9 +7882,9 @@ describe("ParseGraphQLServer", () => { ).toBeRejected(); }); - it("should log the user in", async () => { + it('should log the user in', async () => { parseServer = await global.reconfigureServer({ - publicServerURL: "http://localhost:13377/parse", + publicServerURL: 'http://localhost:13377/parse', auth: { challengeAdapter, }, @@ -7892,9 +7892,9 @@ describe("ParseGraphQLServer", () => { await createGQLFromParseServer(parseServer); const clientMutationId = uuidv4(); const user = new Parse.User(); - user.setUsername("user1"); - user.setPassword("user1"); - user.set("someField", "someValue"); + user.setUsername('user1'); + user.setPassword('user1'); + user.set('someField', 'someValue'); await user.signUp(); await Parse.User.logOut(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -7916,8 +7916,8 @@ describe("ParseGraphQLServer", () => { variables: { input: { clientMutationId, - username: "user1", - password: "user1", + username: 'user1', + password: 'user1', authData: { challengeAdapter: { token: true } }, }, }, @@ -7925,18 +7925,18 @@ describe("ParseGraphQLServer", () => { expect(result.data.logIn.clientMutationId).toEqual(clientMutationId); expect(result.data.logIn.viewer.sessionToken).toBeDefined(); - expect(result.data.logIn.viewer.user.someField).toEqual("someValue"); - expect(typeof result.data.logIn.viewer.sessionToken).toBe("string"); + expect(result.data.logIn.viewer.user.someField).toEqual('someValue'); + expect(typeof result.data.logIn.viewer.sessionToken).toBe('string'); expect(result.data.logIn.viewer.user.authDataResponse).toEqual({ challengeAdapter: { someData: true }, }); }); - it("should log the user out", async () => { + it('should log the user out', async () => { const clientMutationId = uuidv4(); const user = new Parse.User(); - user.setUsername("user1"); - user.setPassword("user1"); + user.setUsername('user1'); + user.setPassword('user1'); await user.signUp(); await Parse.User.logOut(); @@ -7952,8 +7952,8 @@ describe("ParseGraphQLServer", () => { `, variables: { input: { - username: "user1", - password: "user1", + username: 'user1', + password: 'user1', }, }, }); @@ -7971,7 +7971,7 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Session-Token": sessionToken, + 'X-Parse-Session-Token': sessionToken, }, }, variables: { @@ -7994,22 +7994,22 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Session-Token": sessionToken, + 'X-Parse-Session-Token': sessionToken, }, }, }); - fail("should not retrieve current user due to session token"); + fail('should not retrieve current user due to session token'); } catch (err) { const { statusCode, result } = err.networkError; expect(statusCode).toBe(400); expect(result).toEqual({ code: 209, - error: "Invalid session token", + error: 'Invalid session token', }); } }); - it("should send reset password", async () => { + it('should send reset password', async () => { const clientMutationId = uuidv4(); const emailAdapter = { sendVerificationEmail: () => {}, @@ -8017,15 +8017,15 @@ describe("ParseGraphQLServer", () => { sendMail: () => {}, }; parseServer = await global.reconfigureServer({ - appName: "test", + appName: 'test', emailAdapter: emailAdapter, - publicServerURL: "http://test.test", + publicServerURL: 'http://test.test', }); await createGQLFromParseServer(parseServer); const user = new Parse.User(); - user.setUsername("user1"); - user.setPassword("user1"); - user.setEmail("user1@user1.user1"); + user.setUsername('user1'); + user.setPassword('user1'); + user.setEmail('user1@user1.user1'); await user.signUp(); await Parse.User.logOut(); const result = await apolloClient.mutate({ @@ -8040,7 +8040,7 @@ describe("ParseGraphQLServer", () => { variables: { input: { clientMutationId, - email: "user1@user1.user1", + email: 'user1@user1.user1', }, }, }); @@ -8051,34 +8051,34 @@ describe("ParseGraphQLServer", () => { expect(result.data.resetPassword.ok).toBeTruthy(); }); - it("should reset password", async () => { + it('should reset password', async () => { const clientMutationId = uuidv4(); let resetPasswordToken; const emailAdapter = { sendVerificationEmail: () => {}, sendPasswordResetEmail: ({ link }) => { - resetPasswordToken = link.split("token=")[1].split("&")[0]; + resetPasswordToken = link.split('token=')[1].split('&')[0]; }, sendMail: () => {}, }; parseServer = await global.reconfigureServer({ - appName: "test", + appName: 'test', emailAdapter: emailAdapter, - publicServerURL: "http://localhost:13377/parse", + publicServerURL: 'http://localhost:13377/parse', auth: { myAuth: { - module: global.mockCustomAuthenticator("parse", "graphql"), + module: global.mockCustomAuthenticator('parse', 'graphql'), }, }, }); await createGQLFromParseServer(parseServer); const user = new Parse.User(); - user.setUsername("user1"); - user.setPassword("user1"); - user.setEmail("user1@user1.user1"); + user.setUsername('user1'); + user.setPassword('user1'); + user.setEmail('user1@user1.user1'); await user.signUp(); await Parse.User.logOut(); - await Parse.User.requestPasswordReset("user1@user1.user1"); + await Parse.User.requestPasswordReset('user1@user1.user1'); await apolloClient.mutate({ mutation: gql` mutation ConfirmResetPassword( @@ -8093,8 +8093,8 @@ describe("ParseGraphQLServer", () => { variables: { input: { clientMutationId, - username: "user1", - password: "newPassword", + username: 'user1', + password: 'newPassword', token: resetPasswordToken, }, }, @@ -8113,18 +8113,18 @@ describe("ParseGraphQLServer", () => { variables: { input: { clientMutationId, - username: "user1", - password: "newPassword", + username: 'user1', + password: 'newPassword', }, }, }); expect(result.data.logIn.clientMutationId).toEqual(clientMutationId); expect(result.data.logIn.viewer.sessionToken).toBeDefined(); - expect(typeof result.data.logIn.viewer.sessionToken).toBe("string"); + expect(typeof result.data.logIn.viewer.sessionToken).toBe('string'); }); - it("should send verification email again", async () => { + it('should send verification email again', async () => { const clientMutationId = uuidv4(); const emailAdapter = { sendVerificationEmail: () => {}, @@ -8132,15 +8132,15 @@ describe("ParseGraphQLServer", () => { sendMail: () => {}, }; parseServer = await global.reconfigureServer({ - appName: "test", + appName: 'test', emailAdapter: emailAdapter, - publicServerURL: "http://test.test", + publicServerURL: 'http://test.test', }); await createGQLFromParseServer(parseServer); const user = new Parse.User(); - user.setUsername("user1"); - user.setPassword("user1"); - user.setEmail("user1@user1.user1"); + user.setUsername('user1'); + user.setPassword('user1'); + user.setEmail('user1@user1.user1'); await user.signUp(); await Parse.User.logOut(); const result = await apolloClient.mutate({ @@ -8157,7 +8157,7 @@ describe("ParseGraphQLServer", () => { variables: { input: { clientMutationId, - email: "user1@user1.user1", + email: 'user1@user1.user1', }, }, }); @@ -8169,8 +8169,8 @@ describe("ParseGraphQLServer", () => { }); }); - describe("Session Token", () => { - it("should fail due to invalid session token", async () => { + describe('Session Token', () => { + it('should fail due to invalid session token', async () => { try { await apolloClient.query({ query: gql` @@ -8182,22 +8182,22 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Session-Token": "foo", + 'X-Parse-Session-Token': 'foo', }, }, }); - fail("should not retrieve current user due to session token"); + fail('should not retrieve current user due to session token'); } catch (err) { const { statusCode, result } = err.networkError; expect(statusCode).toBe(400); expect(result).toEqual({ code: 209, - error: "Invalid session token", + error: 'Invalid session token', }); } }); - it("should fail due to empty session token", async () => { + it('should fail due to empty session token', async () => { try { await apolloClient.query({ query: gql` @@ -8211,20 +8211,20 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Session-Token": "", + 'X-Parse-Session-Token': '', }, }, }); - fail("should not retrieve current user due to session token"); + fail('should not retrieve current user due to session token'); } catch (err) { const { graphQLErrors } = err; expect(graphQLErrors.length).toBe(1); - expect(graphQLErrors[0].message).toBe("Invalid session token"); + expect(graphQLErrors[0].message).toBe('Invalid session token'); } }); - it("should find a user and fail due to empty session token", async () => { - const car = new Parse.Object("Car"); + it('should find a user and fail due to empty session token', async () => { + const car = new Parse.Object('Car'); await car.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -8249,26 +8249,26 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Session-Token": "", + 'X-Parse-Session-Token': '', }, }, }); - fail("should not retrieve current user due to session token"); + fail('should not retrieve current user due to session token'); } catch (err) { const { graphQLErrors } = err; expect(graphQLErrors.length).toBe(1); - expect(graphQLErrors[0].message).toBe("Invalid session token"); + expect(graphQLErrors[0].message).toBe('Invalid session token'); } }); }); - describe("Functions Mutations", () => { - it("can be called", async () => { + describe('Functions Mutations', () => { + it('can be called', async () => { try { const clientMutationId = uuidv4(); - Parse.Cloud.define("hello", async () => { - return "Hello world!"; + Parse.Cloud.define('hello', async () => { + return 'Hello world!'; }); const result = await apolloClient.mutate({ @@ -8283,7 +8283,7 @@ describe("ParseGraphQLServer", () => { variables: { input: { clientMutationId, - functionName: "hello", + functionName: 'hello', }, }, }); @@ -8291,15 +8291,15 @@ describe("ParseGraphQLServer", () => { expect(result.data.callCloudCode.clientMutationId).toEqual( clientMutationId ); - expect(result.data.callCloudCode.result).toEqual("Hello world!"); + expect(result.data.callCloudCode.result).toEqual('Hello world!'); } catch (e) { handleError(e); } }); - it("can throw errors", async () => { - Parse.Cloud.define("hello", async () => { - throw new Error("Some error message."); + it('can throw errors', async () => { + Parse.Cloud.define('hello', async () => { + throw new Error('Some error message.'); }); try { @@ -8312,16 +8312,16 @@ describe("ParseGraphQLServer", () => { } `, }); - fail("Should throw an error"); + fail('Should throw an error'); } catch (e) { const { graphQLErrors } = e; expect(graphQLErrors.length).toBe(1); - expect(graphQLErrors[0].message).toBe("Some error message."); + expect(graphQLErrors[0].message).toBe('Some error message.'); } }); - it("should accept different params", done => { - Parse.Cloud.define("hello", async req => { + it('should accept different params', done => { + Parse.Cloud.define('hello', async req => { expect(req.params.date instanceof Date).toBe(true); expect(req.params.date.getTime()).toBe(1463907600000); expect(req.params.dateList[0] instanceof Date).toBe(true); @@ -8346,13 +8346,13 @@ describe("ParseGraphQLServer", () => { ).toBe(1463907600000); // Regression for #2294 expect(req.params.file instanceof Parse.File).toBe(true); - expect(req.params.file.url()).toEqual("https://some.url"); + expect(req.params.file.url()).toEqual('https://some.url'); // Regression for #2204 - expect(req.params.array).toEqual(["a", "b", "c"]); + expect(req.params.array).toEqual(['a', 'b', 'c']); expect(Array.isArray(req.params.array)).toBe(true); expect(req.params.arrayOfArray).toEqual([ - ["a", "b", "c"], - ["d", "e", "f"], + ['a', 'b', 'c'], + ['d', 'e', 'f'], ]); expect(Array.isArray(req.params.arrayOfArray)).toBe(true); expect(Array.isArray(req.params.arrayOfArray[0])).toBe(true); @@ -8363,49 +8363,49 @@ describe("ParseGraphQLServer", () => { const params = { date: { - __type: "Date", - iso: "2016-05-22T09:00:00.000Z", + __type: 'Date', + iso: '2016-05-22T09:00:00.000Z', }, dateList: [ { - __type: "Date", - iso: "2016-05-22T09:00:00.000Z", + __type: 'Date', + iso: '2016-05-22T09:00:00.000Z', }, ], - lol: "hello", + lol: 'hello', complexStructure: { date: [ { - __type: "Date", - iso: "2016-05-22T09:00:00.000Z", + __type: 'Date', + iso: '2016-05-22T09:00:00.000Z', }, ], deepDate: { date: [ { - __type: "Date", - iso: "2016-05-22T09:00:00.000Z", + __type: 'Date', + iso: '2016-05-22T09:00:00.000Z', }, ], }, deepDate2: [ { date: { - __type: "Date", - iso: "2016-05-22T09:00:00.000Z", + __type: 'Date', + iso: '2016-05-22T09:00:00.000Z', }, }, ], }, file: Parse.File.fromJSON({ - __type: "File", - name: "name", - url: "https://some.url", + __type: 'File', + name: 'name', + url: 'https://some.url', }), - array: ["a", "b", "c"], + array: ['a', 'b', 'c'], arrayOfArray: [ - ["a", "b", "c"], - ["d", "e", "f"], + ['a', 'b', 'c'], + ['d', 'e', 'f'], ], }; @@ -8423,22 +8423,22 @@ describe("ParseGraphQLServer", () => { }); }); - it("should list all functions in the enum type", async () => { + it('should list all functions in the enum type', async () => { try { - Parse.Cloud.define("a", async () => { - return "hello a"; + Parse.Cloud.define('a', async () => { + return 'hello a'; }); - Parse.Cloud.define("b", async () => { - return "hello b"; + Parse.Cloud.define('b', async () => { + return 'hello b'; }); - Parse.Cloud.define("_underscored", async () => { - return "hello _underscored"; + Parse.Cloud.define('_underscored', async () => { + return 'hello _underscored'; }); - Parse.Cloud.define("contains1Number", async () => { - return "hello contains1Number"; + Parse.Cloud.define('contains1Number', async () => { + return 'hello contains1Number'; }); const functionEnum = ( @@ -8454,33 +8454,33 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"]; - expect(functionEnum.kind).toEqual("ENUM"); + ).data['__type']; + expect(functionEnum.kind).toEqual('ENUM'); expect( functionEnum.enumValues.map(value => value.name).sort() - ).toEqual(["_underscored", "a", "b", "contains1Number"]); + ).toEqual(['_underscored', 'a', 'b', 'contains1Number']); } catch (e) { handleError(e); } }); - it("should warn functions not matching GraphQL allowed names", async () => { + it('should warn functions not matching GraphQL allowed names', async () => { try { spyOn( parseGraphQLServer.parseGraphQLSchema.log, - "warn" + 'warn' ).and.callThrough(); - Parse.Cloud.define("a", async () => { - return "hello a"; + Parse.Cloud.define('a', async () => { + return 'hello a'; }); - Parse.Cloud.define("double-barrelled", async () => { - return "hello b"; + Parse.Cloud.define('double-barrelled', async () => { + return 'hello b'; }); - Parse.Cloud.define("1NumberInTheBeggning", async () => { - return "hello contains1Number"; + Parse.Cloud.define('1NumberInTheBeggning', async () => { + return 'hello contains1Number'; }); const functionEnum = ( @@ -8496,19 +8496,19 @@ describe("ParseGraphQLServer", () => { } `, }) - ).data["__type"]; - expect(functionEnum.kind).toEqual("ENUM"); + ).data['__type']; + expect(functionEnum.kind).toEqual('ENUM'); expect( functionEnum.enumValues.map(value => value.name).sort() - ).toEqual(["a"]); + ).toEqual(['a']); expect( parseGraphQLServer.parseGraphQLSchema.log.warn.calls .all() .map(call => call.args[0]) .sort() ).toEqual([ - "Function 1NumberInTheBeggning could not be added to the auto schema because GraphQL names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/.", - "Function double-barrelled could not be added to the auto schema because GraphQL names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/.", + 'Function 1NumberInTheBeggning could not be added to the auto schema because GraphQL names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/.', + 'Function double-barrelled could not be added to the auto schema because GraphQL names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/.', ]); } catch (e) { handleError(e); @@ -8516,10 +8516,10 @@ describe("ParseGraphQLServer", () => { }); }); - describe("Data Types", () => { - it("should support String", async () => { + describe('Data Types', () => { + it('should support String', async () => { try { - const someFieldValue = "some string"; + const someFieldValue = 'some string'; await apolloClient.mutate({ mutation: gql` @@ -8533,20 +8533,20 @@ describe("ParseGraphQLServer", () => { `, variables: { schemaFields: { - addStrings: [{ name: "someField" }], + addStrings: [{ name: 'someField' }], }, }, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const schema = await new Parse.Schema("SomeClass").get(); - expect(schema.fields.someField.type).toEqual("String"); + const schema = await new Parse.Schema('SomeClass').get(); + expect(schema.fields.someField.type).toEqual('String'); const createResult = await apolloClient.mutate({ mutation: gql` @@ -8588,7 +8588,7 @@ describe("ParseGraphQLServer", () => { }, }); - expect(typeof getResult.data.someClass.someField).toEqual("string"); + expect(typeof getResult.data.someClass.someField).toEqual('string'); expect(getResult.data.someClass.someField).toEqual(someFieldValue); expect(getResult.data.someClasses.edges.length).toEqual(1); } catch (e) { @@ -8596,7 +8596,7 @@ describe("ParseGraphQLServer", () => { } }); - it("should support Int numbers", async () => { + it('should support Int numbers', async () => { try { const someFieldValue = 123; @@ -8612,12 +8612,12 @@ describe("ParseGraphQLServer", () => { `, variables: { schemaFields: { - addNumbers: [{ name: "someField" }], + addNumbers: [{ name: 'someField' }], }, }, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -8641,8 +8641,8 @@ describe("ParseGraphQLServer", () => { }, }); - const schema = await new Parse.Schema("SomeClass").get(); - expect(schema.fields.someField.type).toEqual("Number"); + const schema = await new Parse.Schema('SomeClass').get(); + expect(schema.fields.someField.type).toEqual('Number'); const getResult = await apolloClient.query({ query: gql` @@ -8667,7 +8667,7 @@ describe("ParseGraphQLServer", () => { }, }); - expect(typeof getResult.data.someClass.someField).toEqual("number"); + expect(typeof getResult.data.someClass.someField).toEqual('number'); expect(getResult.data.someClass.someField).toEqual(someFieldValue); expect(getResult.data.someClasses.edges.length).toEqual(1); } catch (e) { @@ -8675,7 +8675,7 @@ describe("ParseGraphQLServer", () => { } }); - it("should support Float numbers", async () => { + it('should support Float numbers', async () => { try { const someFieldValue = 123.4; @@ -8691,20 +8691,20 @@ describe("ParseGraphQLServer", () => { `, variables: { schemaFields: { - addNumbers: [{ name: "someField" }], + addNumbers: [{ name: 'someField' }], }, }, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const schema = await new Parse.Schema("SomeClass").get(); - expect(schema.fields.someField.type).toEqual("Number"); + const schema = await new Parse.Schema('SomeClass').get(); + expect(schema.fields.someField.type).toEqual('Number'); const createResult = await apolloClient.mutate({ mutation: gql` @@ -8746,7 +8746,7 @@ describe("ParseGraphQLServer", () => { }, }); - expect(typeof getResult.data.someClass.someField).toEqual("number"); + expect(typeof getResult.data.someClass.someField).toEqual('number'); expect(getResult.data.someClass.someField).toEqual(someFieldValue); expect(getResult.data.someClasses.edges.length).toEqual(1); } catch (e) { @@ -8754,7 +8754,7 @@ describe("ParseGraphQLServer", () => { } }); - it("should support Boolean", async () => { + it('should support Boolean', async () => { try { const someFieldValueTrue = true; const someFieldValueFalse = false; @@ -8772,23 +8772,23 @@ describe("ParseGraphQLServer", () => { variables: { schemaFields: { addBooleans: [ - { name: "someFieldTrue" }, - { name: "someFieldFalse" }, + { name: 'someFieldTrue' }, + { name: 'someFieldFalse' }, ], }, }, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const schema = await new Parse.Schema("SomeClass").get(); - expect(schema.fields.someFieldTrue.type).toEqual("Boolean"); - expect(schema.fields.someFieldFalse.type).toEqual("Boolean"); + const schema = await new Parse.Schema('SomeClass').get(); + expect(schema.fields.someFieldTrue.type).toEqual('Boolean'); + expect(schema.fields.someFieldFalse.type).toEqual('Boolean'); const createResult = await apolloClient.mutate({ mutation: gql` @@ -8841,10 +8841,10 @@ describe("ParseGraphQLServer", () => { }); expect(typeof getResult.data.someClass.someFieldTrue).toEqual( - "boolean" + 'boolean' ); expect(typeof getResult.data.someClass.someFieldFalse).toEqual( - "boolean" + 'boolean' ); expect(getResult.data.someClass.someFieldTrue).toEqual(true); expect(getResult.data.someClass.someFieldFalse).toEqual(false); @@ -8854,7 +8854,7 @@ describe("ParseGraphQLServer", () => { } }); - it("should support Date", async () => { + it('should support Date', async () => { try { const someFieldValue = new Date(); @@ -8870,20 +8870,20 @@ describe("ParseGraphQLServer", () => { `, variables: { schemaFields: { - addDates: [{ name: "someField" }], + addDates: [{ name: 'someField' }], }, }, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const schema = await new Parse.Schema("SomeClass").get(); - expect(schema.fields.someField.type).toEqual("Date"); + const schema = await new Parse.Schema('SomeClass').get(); + expect(schema.fields.someField.type).toEqual('Date'); const createResult = await apolloClient.mutate({ mutation: gql` @@ -8931,7 +8931,7 @@ describe("ParseGraphQLServer", () => { } }); - it("should support createdAt and updatedAt", async () => { + it('should support createdAt and updatedAt', async () => { await apolloClient.mutate({ mutation: gql` mutation CreateClass { @@ -8942,41 +8942,41 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); - const schema = await new Parse.Schema("SomeClass").get(); - expect(schema.fields.createdAt.type).toEqual("Date"); - expect(schema.fields.updatedAt.type).toEqual("Date"); + const schema = await new Parse.Schema('SomeClass').get(); + expect(schema.fields.createdAt.type).toEqual('Date'); + expect(schema.fields.updatedAt.type).toEqual('Date'); }); - it_id("93e748f6-ad9b-4c31-8e1e-c5685e2382fb")(it)( - "should support ACL", + it_id('93e748f6-ad9b-4c31-8e1e-c5685e2382fb')(it)( + 'should support ACL', async () => { - const someClass = new Parse.Object("SomeClass"); + const someClass = new Parse.Object('SomeClass'); await someClass.save(); const roleACL = new Parse.ACL(); roleACL.setPublicReadAccess(true); const user = new Parse.User(); - user.set("username", "username"); - user.set("password", "password"); + user.set('username', 'username'); + user.set('password', 'password'); user.setACL(roleACL); await user.signUp(); const user2 = new Parse.User(); - user2.set("username", "username2"); - user2.set("password", "password2"); + user2.set('username', 'username2'); + user2.set('password', 'password2'); user2.setACL(roleACL); await user2.signUp(); - const role = new Parse.Role("aRole", roleACL); + const role = new Parse.Role('aRole', roleACL); await role.save(); - const role2 = new Parse.Role("aRole2", roleACL); + const role2 = new Parse.Role('aRole2', roleACL); await role2.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -9030,8 +9030,8 @@ describe("ParseGraphQLServer", () => { { userId: user2.id, read: true, write: false }, ], roles: [ - { roleName: "aRole", read: true, write: false }, - { roleName: "aRole2", read: false, write: true }, + { roleName: 'aRole', read: true, write: false }, + { roleName: 'aRole2', read: false, write: true }, ], public: { read: true, write: true }, }, @@ -9040,38 +9040,38 @@ describe("ParseGraphQLServer", () => { }); const expectedCreateACL = { - __typename: "ACL", + __typename: 'ACL', users: [ { - userId: toGlobalId("_User", user.id), + userId: toGlobalId('_User', user.id), read: true, write: true, - __typename: "UserACL", + __typename: 'UserACL', }, { - userId: toGlobalId("_User", user2.id), + userId: toGlobalId('_User', user2.id), read: true, write: false, - __typename: "UserACL", + __typename: 'UserACL', }, ], roles: [ { - roleName: "aRole", + roleName: 'aRole', read: true, write: false, - __typename: "RoleACL", + __typename: 'RoleACL', }, { - roleName: "aRole2", + roleName: 'aRole2', read: false, write: true, - __typename: "RoleACL", + __typename: 'RoleACL', }, ], - public: { read: true, write: true, __typename: "PublicACL" }, + public: { read: true, write: true, __typename: 'PublicACL' }, }; - const query1 = new Parse.Query("SomeClass"); + const query1 = new Parse.Query('SomeClass'); const obj1 = ( await query1.get(createSomeClass.someClass.objectId, { useMasterKey: true, @@ -9079,9 +9079,9 @@ describe("ParseGraphQLServer", () => { ).toJSON(); expect(obj1.ACL[user.id]).toEqual({ read: true, write: true }); expect(obj1.ACL[user2.id]).toEqual({ read: true }); - expect(obj1.ACL["role:aRole"]).toEqual({ read: true }); - expect(obj1.ACL["role:aRole2"]).toEqual({ write: true }); - expect(obj1.ACL["*"]).toEqual({ read: true, write: true }); + expect(obj1.ACL['role:aRole']).toEqual({ read: true }); + expect(obj1.ACL['role:aRole2']).toEqual({ write: true }); + expect(obj1.ACL['*']).toEqual({ read: true, write: true }); expect(createSomeClass.someClass.ACL).toEqual(expectedCreateACL); const { @@ -9117,7 +9117,7 @@ describe("ParseGraphQLServer", () => { id: createSomeClass.someClass.id, fields: { ACL: { - roles: [{ roleName: "aRole", write: true, read: true }], + roles: [{ roleName: 'aRole', write: true, read: true }], public: { read: true, write: false }, }, }, @@ -9125,45 +9125,45 @@ describe("ParseGraphQLServer", () => { }); const expectedUpdateACL = { - __typename: "ACL", + __typename: 'ACL', users: null, roles: [ { - roleName: "aRole", + roleName: 'aRole', read: true, write: true, - __typename: "RoleACL", + __typename: 'RoleACL', }, ], - public: { read: true, write: false, __typename: "PublicACL" }, + public: { read: true, write: false, __typename: 'PublicACL' }, }; - const query2 = new Parse.Query("SomeClass"); + const query2 = new Parse.Query('SomeClass'); const obj2 = ( await query2.get(createSomeClass.someClass.objectId, { useMasterKey: true, }) ).toJSON(); - expect(obj2.ACL["role:aRole"]).toEqual({ write: true, read: true }); + expect(obj2.ACL['role:aRole']).toEqual({ write: true, read: true }); expect(obj2.ACL[user.id]).toBeUndefined(); - expect(obj2.ACL["*"]).toEqual({ read: true }); + expect(obj2.ACL['*']).toEqual({ read: true }); expect(updateSomeClass.someClass.ACL).toEqual(expectedUpdateACL); } ); - it("should support pointer on create", async () => { - const company = new Parse.Object("Company"); - company.set("name", "imACompany1"); + it('should support pointer on create', async () => { + const company = new Parse.Object('Company'); + company.set('name', 'imACompany1'); await company.save(); - const country = new Parse.Object("Country"); - country.set("name", "imACountry"); - country.set("company", company); + const country = new Parse.Object('Country'); + country.set('name', 'imACountry'); + country.set('company', company); await country.save(); - const company2 = new Parse.Object("Company"); - company2.set("name", "imACompany2"); + const company2 = new Parse.Object('Company'); + company2.set('name', 'imACompany2'); await company2.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -9190,7 +9190,7 @@ describe("ParseGraphQLServer", () => { `, variables: { fields: { - name: "imCountry2", + name: 'imCountry2', company: { link: company2.id }, }, }, @@ -9198,17 +9198,17 @@ describe("ParseGraphQLServer", () => { expect(result.id).toBeDefined(); expect(result.company.objectId).toEqual(company2.id); - expect(result.company.name).toEqual("imACompany2"); + expect(result.company.name).toEqual('imACompany2'); }); - it("should support nested pointer on create", async () => { - const company = new Parse.Object("Company"); - company.set("name", "imACompany1"); + it('should support nested pointer on create', async () => { + const company = new Parse.Object('Company'); + company.set('name', 'imACompany1'); await company.save(); - const country = new Parse.Object("Country"); - country.set("name", "imACountry"); - country.set("company", company); + const country = new Parse.Object('Country'); + country.set('name', 'imACountry'); + country.set('company', company); await country.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -9233,10 +9233,10 @@ describe("ParseGraphQLServer", () => { `, variables: { fields: { - name: "imCountry2", + name: 'imCountry2', company: { createAndLink: { - name: "imACompany2", + name: 'imACompany2', }, }, }, @@ -9245,21 +9245,21 @@ describe("ParseGraphQLServer", () => { expect(result.id).toBeDefined(); expect(result.company.id).toBeDefined(); - expect(result.company.name).toEqual("imACompany2"); + expect(result.company.name).toEqual('imACompany2'); }); - it("should support pointer on update", async () => { - const company = new Parse.Object("Company"); - company.set("name", "imACompany1"); + it('should support pointer on update', async () => { + const company = new Parse.Object('Company'); + company.set('name', 'imACompany1'); await company.save(); - const country = new Parse.Object("Country"); - country.set("name", "imACountry"); - country.set("company", company); + const country = new Parse.Object('Country'); + country.set('name', 'imACountry'); + country.set('company', company); await country.save(); - const company2 = new Parse.Object("Company"); - company2.set("name", "imACompany2"); + const company2 = new Parse.Object('Company'); + company2.set('name', 'imACompany2'); await company2.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -9294,17 +9294,17 @@ describe("ParseGraphQLServer", () => { expect(result.id).toBeDefined(); expect(result.company.objectId).toEqual(company2.id); - expect(result.company.name).toEqual("imACompany2"); + expect(result.company.name).toEqual('imACompany2'); }); - it("should support nested pointer on update", async () => { - const company = new Parse.Object("Company"); - company.set("name", "imACompany1"); + it('should support nested pointer on update', async () => { + const company = new Parse.Object('Company'); + company.set('name', 'imACompany1'); await company.save(); - const country = new Parse.Object("Country"); - country.set("name", "imACountry"); - country.set("company", company); + const country = new Parse.Object('Country'); + country.set('name', 'imACountry'); + country.set('company', company); await country.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -9332,7 +9332,7 @@ describe("ParseGraphQLServer", () => { fields: { company: { createAndLink: { - name: "imACompany2", + name: 'imACompany2', }, }, }, @@ -9341,19 +9341,19 @@ describe("ParseGraphQLServer", () => { expect(result.id).toBeDefined(); expect(result.company.id).toBeDefined(); - expect(result.company.name).toEqual("imACompany2"); + expect(result.company.name).toEqual('imACompany2'); }); - it_only_db("mongo")( - "should support relation and nested relation on create", + it_only_db('mongo')( + 'should support relation and nested relation on create', async () => { - const company = new Parse.Object("Company"); - company.set("name", "imACompany1"); + const company = new Parse.Object('Company'); + company.set('name', 'imACompany1'); await company.save(); - const country = new Parse.Object("Country"); - country.set("name", "imACountry"); - country.relation("companies").add(company); + const country = new Parse.Object('Country'); + country.set('name', 'imACountry'); + country.relation('companies').add(company); await country.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -9385,15 +9385,15 @@ describe("ParseGraphQLServer", () => { `, variables: { fields: { - name: "imACountry2", + name: 'imACountry2', companies: { add: [company.id], createAndAdd: [ { - name: "imACompany2", + name: 'imACompany2', }, { - name: "imACompany3", + name: 'imACompany3', }, ], }, @@ -9402,33 +9402,33 @@ describe("ParseGraphQLServer", () => { }); expect(result.id).toBeDefined(); - expect(result.name).toEqual("imACountry2"); + expect(result.name).toEqual('imACountry2'); expect(result.companies.edges.length).toEqual(3); expect( result.companies.edges.some(o => o.node.objectId === company.id) ).toBeTruthy(); expect( - result.companies.edges.some(o => o.node.name === "imACompany2") + result.companies.edges.some(o => o.node.name === 'imACompany2') ).toBeTruthy(); expect( - result.companies.edges.some(o => o.node.name === "imACompany3") + result.companies.edges.some(o => o.node.name === 'imACompany3') ).toBeTruthy(); } ); - it_only_db("mongo")("should support deep nested creation", async () => { - const team = new Parse.Object("Team"); - team.set("name", "imATeam1"); + it_only_db('mongo')('should support deep nested creation', async () => { + const team = new Parse.Object('Team'); + team.set('name', 'imATeam1'); await team.save(); - const company = new Parse.Object("Company"); - company.set("name", "imACompany1"); - company.relation("teams").add(team); + const company = new Parse.Object('Company'); + company.set('name', 'imACompany1'); + company.relation('teams').add(team); await company.save(); - const country = new Parse.Object("Country"); - country.set("name", "imACountry"); - country.relation("companies").add(company); + const country = new Parse.Object('Country'); + country.set('name', 'imACountry'); + country.relation('companies').add(company); await country.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -9466,22 +9466,22 @@ describe("ParseGraphQLServer", () => { `, variables: { fields: { - name: "imACountry2", + name: 'imACountry2', companies: { createAndAdd: [ { - name: "imACompany2", + name: 'imACompany2', teams: { createAndAdd: { - name: "imATeam2", + name: 'imATeam2', }, }, }, { - name: "imACompany3", + name: 'imACompany3', teams: { createAndAdd: { - name: "imATeam3", + name: 'imATeam3', }, }, }, @@ -9492,38 +9492,38 @@ describe("ParseGraphQLServer", () => { }); expect(result.id).toBeDefined(); - expect(result.name).toEqual("imACountry2"); + expect(result.name).toEqual('imACountry2'); expect(result.companies.edges.length).toEqual(2); expect( result.companies.edges.some( c => - c.node.name === "imACompany2" && - c.node.teams.edges.some(t => t.node.name === "imATeam2") + c.node.name === 'imACompany2' && + c.node.teams.edges.some(t => t.node.name === 'imATeam2') ) ).toBeTruthy(); expect( result.companies.edges.some( c => - c.node.name === "imACompany3" && - c.node.teams.edges.some(t => t.node.name === "imATeam3") + c.node.name === 'imACompany3' && + c.node.teams.edges.some(t => t.node.name === 'imATeam3') ) ).toBeTruthy(); }); - it_only_db("mongo")( - "should support relation and nested relation on update", + it_only_db('mongo')( + 'should support relation and nested relation on update', async () => { - const company1 = new Parse.Object("Company"); - company1.set("name", "imACompany1"); + const company1 = new Parse.Object('Company'); + company1.set('name', 'imACompany1'); await company1.save(); - const company2 = new Parse.Object("Company"); - company2.set("name", "imACompany2"); + const company2 = new Parse.Object('Company'); + company2.set('name', 'imACompany2'); await company2.save(); - const country = new Parse.Object("Country"); - country.set("name", "imACountry"); - country.relation("companies").add(company1); + const country = new Parse.Object('Country'); + country.set('name', 'imACountry'); + country.relation('companies').add(company1); await country.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -9563,7 +9563,7 @@ describe("ParseGraphQLServer", () => { remove: [company1.id], createAndAdd: [ { - name: "imACompany3", + name: 'imACompany3', }, ], }, @@ -9577,7 +9577,7 @@ describe("ParseGraphQLServer", () => { result.companies.edges.some(o => o.node.objectId === company2.id) ).toBeTruthy(); expect( - result.companies.edges.some(o => o.node.name === "imACompany3") + result.companies.edges.some(o => o.node.name === 'imACompany3') ).toBeTruthy(); expect( result.companies.edges.some(o => o.node.objectId === company1.id) @@ -9585,16 +9585,16 @@ describe("ParseGraphQLServer", () => { } ); - it_only_db("mongo")( - "should support nested relation on create with filter", + it_only_db('mongo')( + 'should support nested relation on create with filter', async () => { - const company = new Parse.Object("Company"); - company.set("name", "imACompany1"); + const company = new Parse.Object('Company'); + company.set('name', 'imACompany1'); await company.save(); - const country = new Parse.Object("Country"); - country.set("name", "imACountry"); - country.relation("companies").add(company); + const country = new Parse.Object('Country'); + country.set('name', 'imACountry'); + country.relation('companies').add(company); await country.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -9628,19 +9628,19 @@ describe("ParseGraphQLServer", () => { variables: { where: { name: { - equalTo: "imACompany2", + equalTo: 'imACompany2', }, }, fields: { - name: "imACountry2", + name: 'imACountry2', companies: { add: [company.id], createAndAdd: [ { - name: "imACompany2", + name: 'imACompany2', }, { - name: "imACompany3", + name: 'imACompany3', }, ], }, @@ -9649,26 +9649,26 @@ describe("ParseGraphQLServer", () => { }); expect(result.id).toBeDefined(); - expect(result.name).toEqual("imACountry2"); + expect(result.name).toEqual('imACountry2'); expect(result.companies.edges.length).toEqual(1); expect( - result.companies.edges.some(o => o.node.name === "imACompany2") + result.companies.edges.some(o => o.node.name === 'imACompany2') ).toBeTruthy(); } ); - it_only_db("mongo")("should support relation on query", async () => { - const company1 = new Parse.Object("Company"); - company1.set("name", "imACompany1"); + it_only_db('mongo')('should support relation on query', async () => { + const company1 = new Parse.Object('Company'); + company1.set('name', 'imACompany1'); await company1.save(); - const company2 = new Parse.Object("Company"); - company2.set("name", "imACompany2"); + const company2 = new Parse.Object('Company'); + company2.set('name', 'imACompany2'); await company2.save(); - const country = new Parse.Object("Country"); - country.set("name", "imACountry"); - country.relation("companies").add([company1, company2]); + const country = new Parse.Object('Country'); + country.set('name', 'imACountry'); + country.relation('companies').add([company1, company2]); await country.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -9733,7 +9733,7 @@ describe("ParseGraphQLServer", () => { variables: { id: country.id, where: { - name: { equalTo: "imACompany1" }, + name: { equalTo: 'imACompany1' }, }, }, }); @@ -9742,39 +9742,39 @@ describe("ParseGraphQLServer", () => { expect(result2.companies.edges[0].node.objectId).toEqual(company1.id); }); - it_id("f4312f2c-90bb-4583-b033-02078ae0ce84")(it)( - "should support relational where query", + it_id('f4312f2c-90bb-4583-b033-02078ae0ce84')(it)( + 'should support relational where query', async () => { - const president = new Parse.Object("President"); - president.set("name", "James"); + const president = new Parse.Object('President'); + president.set('name', 'James'); await president.save(); - const employee = new Parse.Object("Employee"); - employee.set("name", "John"); + const employee = new Parse.Object('Employee'); + employee.set('name', 'John'); await employee.save(); - const company1 = new Parse.Object("Company"); - company1.set("name", "imACompany1"); + const company1 = new Parse.Object('Company'); + company1.set('name', 'imACompany1'); await company1.save(); - const company2 = new Parse.Object("Company"); - company2.set("name", "imACompany2"); - company2.relation("employees").add([employee]); + const company2 = new Parse.Object('Company'); + company2.set('name', 'imACompany2'); + company2.relation('employees').add([employee]); await company2.save(); - const country = new Parse.Object("Country"); - country.set("name", "imACountry"); - country.relation("companies").add([company1, company2]); + const country = new Parse.Object('Country'); + country.set('name', 'imACountry'); + country.relation('companies').add([company1, company2]); await country.save(); - const country2 = new Parse.Object("Country"); - country2.set("name", "imACountry2"); - country2.relation("companies").add([company1]); + const country2 = new Parse.Object('Country'); + country2.set('name', 'imACountry2'); + country2.relation('companies').add([company1]); await country2.save(); - const country3 = new Parse.Object("Country"); - country3.set("name", "imACountry3"); - country3.set("president", president); + const country3 = new Parse.Object('Country'); + country3.set('name', 'imACountry3'); + country3.set('president', president); await country3.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -9809,7 +9809,7 @@ describe("ParseGraphQLServer", () => { where: { companies: { have: { - employees: { have: { name: { equalTo: "John" } } }, + employees: { have: { name: { equalTo: 'John' } } }, }, }, }, @@ -9851,8 +9851,8 @@ describe("ParseGraphQLServer", () => { companies: { have: { OR: [ - { name: { equalTo: "imACompany1" } }, - { name: { equalTo: "imACompany2" } }, + { name: { equalTo: 'imACompany1' } }, + { name: { equalTo: 'imACompany2' } }, ], }, }, @@ -9885,7 +9885,7 @@ describe("ParseGraphQLServer", () => { }, }); expect(result3.length).toEqual(1); - expect(result3[0].node.name).toEqual("imACountry3"); + expect(result3[0].node.name).toEqual('imACountry3'); const { data: { @@ -9958,8 +9958,8 @@ describe("ParseGraphQLServer", () => { companies: { haveNot: { OR: [ - { name: { equalTo: "imACompany1" } }, - { name: { equalTo: "imACompany2" } }, + { name: { equalTo: 'imACompany1' } }, + { name: { equalTo: 'imACompany2' } }, ], }, }, @@ -9968,19 +9968,19 @@ describe("ParseGraphQLServer", () => { }); expect(result6.length).toEqual(1); expect(result6.length).toEqual(1); - expect(result6[0].node.name).toEqual("imACountry3"); + expect(result6[0].node.name).toEqual('imACountry3'); } ); - it("should support files", async () => { + it('should support files', async () => { try { parseServer = await global.reconfigureServer({ - publicServerURL: "http://localhost:13377/parse", + publicServerURL: 'http://localhost:13377/parse', }); await createGQLFromParseServer(parseServer); const body = new FormData(); body.append( - "operations", + 'operations', JSON.stringify({ query: ` mutation CreateFile($input: CreateFileInput!) { @@ -10000,16 +10000,16 @@ describe("ParseGraphQLServer", () => { }) ); body.append( - "map", - JSON.stringify({ 1: ["variables.input.upload"] }) + 'map', + JSON.stringify({ 1: ['variables.input.upload'] }) ); - body.append("1", "My File Content", { - filename: "myFileName.txt", - contentType: "text/plain", + body.append('1', 'My File Content', { + filename: 'myFileName.txt', + contentType: 'text/plain', }); - let res = await fetch("http://localhost:13377/graphql", { - method: "POST", + let res = await fetch('http://localhost:13377/graphql', { + method: 'POST', headers, body, }); @@ -10038,12 +10038,12 @@ describe("ParseGraphQLServer", () => { `, variables: { schemaFields: { - addFiles: [{ name: "someField" }], + addFiles: [{ name: 'someField' }], }, }, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -10052,7 +10052,7 @@ describe("ParseGraphQLServer", () => { const body2 = new FormData(); body2.append( - "operations", + 'operations', JSON.stringify({ query: ` mutation CreateSomeObject( @@ -10104,7 +10104,7 @@ describe("ParseGraphQLServer", () => { file: { name: someFieldObjectValue.name, url: someFieldObjectValue.url, - __type: "File", + __type: 'File', }, }, }, @@ -10115,16 +10115,16 @@ describe("ParseGraphQLServer", () => { }) ); body2.append( - "map", - JSON.stringify({ 1: ["variables.fields3.someField.upload"] }) + 'map', + JSON.stringify({ 1: ['variables.fields3.someField.upload'] }) ); - body2.append("1", "My File Content", { - filename: "myFileName.txt", - contentType: "text/plain", + body2.append('1', 'My File Content', { + filename: 'myFileName.txt', + contentType: 'text/plain', }); - res = await fetch("http://localhost:13377/graphql", { - method: "POST", + res = await fetch('http://localhost:13377/graphql', { + method: 'POST', headers, body: body2, }); @@ -10149,8 +10149,8 @@ describe("ParseGraphQLServer", () => { result2.data.createSomeClass3.someClass.someField.url ).toEqual(jasmine.stringMatching(/_myFileName.txt$/)); - const schema = await new Parse.Schema("SomeClass").get(); - expect(schema.fields.someField.type).toEqual("File"); + const schema = await new Parse.Schema('SomeClass').get(); + expect(schema.fields.someField.type).toEqual('File'); const getResult = await apolloClient.query({ query: gql` @@ -10192,7 +10192,7 @@ describe("ParseGraphQLServer", () => { }, }); - expect(typeof getResult.data.someClass.someField).toEqual("object"); + expect(typeof getResult.data.someClass.someField).toEqual('object'); expect(getResult.data.someClass.someField.name).toEqual( result.data.createFile.fileInfo.name ); @@ -10205,7 +10205,7 @@ describe("ParseGraphQLServer", () => { res = await fetch(getResult.data.someClass.someField.url); expect(res.status).toEqual(200); - expect(await res.text()).toEqual("My File Content"); + expect(await res.text()).toEqual('My File Content'); const mutationResult = await apolloClient.mutate({ mutation: gql` @@ -10234,18 +10234,18 @@ describe("ParseGraphQLServer", () => { } }); - it("should support files on required file", async () => { + it('should support files on required file', async () => { try { parseServer = await global.reconfigureServer({ - publicServerURL: "http://localhost:13377/parse", + publicServerURL: 'http://localhost:13377/parse', }); await createGQLFromParseServer(parseServer); const schemaController = await parseServer.config.databaseController.loadSchema(); await schemaController.addClassIfNotExists( - "SomeClassWithRequiredFile", + 'SomeClassWithRequiredFile', { - someField: { type: "File", required: true }, + someField: { type: 'File', required: true }, } ); await resetGraphQLCache(); @@ -10253,7 +10253,7 @@ describe("ParseGraphQLServer", () => { const body = new FormData(); body.append( - "operations", + 'operations', JSON.stringify({ query: ` mutation CreateSomeObject( @@ -10280,16 +10280,16 @@ describe("ParseGraphQLServer", () => { }) ); body.append( - "map", - JSON.stringify({ 1: ["variables.fields.someField.upload"] }) + 'map', + JSON.stringify({ 1: ['variables.fields.someField.upload'] }) ); - body.append("1", "My File Content", { - filename: "myFileName.txt", - contentType: "text/plain", + body.append('1', 'My File Content', { + filename: 'myFileName.txt', + contentType: 'text/plain', }); - const res = await fetch("http://localhost:13377/graphql", { - method: "POST", + const res = await fetch('http://localhost:13377/graphql', { + method: 'POST', headers, body, }); @@ -10309,20 +10309,20 @@ describe("ParseGraphQLServer", () => { } }); - it("should support file upload for on fly creation through pointer and relation", async () => { + it('should support file upload for on fly creation through pointer and relation', async () => { parseServer = await global.reconfigureServer({ - publicServerURL: "http://localhost:13377/parse", + publicServerURL: 'http://localhost:13377/parse', }); await createGQLFromParseServer(parseServer); - const schema = new Parse.Schema("SomeClass"); - schema.addFile("someFileField"); - schema.addPointer("somePointerField", "SomeClass"); - schema.addRelation("someRelationField", "SomeClass"); + const schema = new Parse.Schema('SomeClass'); + schema.addFile('someFileField'); + schema.addPointer('somePointerField', 'SomeClass'); + schema.addRelation('someRelationField', 'SomeClass'); await schema.save(); const body = new FormData(); body.append( - "operations", + 'operations', JSON.stringify({ query: ` mutation UploadFiles( @@ -10379,32 +10379,32 @@ describe("ParseGraphQLServer", () => { }) ); body.append( - "map", + 'map', JSON.stringify({ - 1: ["variables.fields.someFileField.upload"], + 1: ['variables.fields.someFileField.upload'], 2: [ - "variables.fields.somePointerField.createAndLink.someFileField.upload", + 'variables.fields.somePointerField.createAndLink.someFileField.upload', ], 3: [ - "variables.fields.someRelationField.createAndAdd.0.someFileField.upload", + 'variables.fields.someRelationField.createAndAdd.0.someFileField.upload', ], }) ); - body.append("1", "My File Content someFileField", { - filename: "someFileField.txt", - contentType: "text/plain", + body.append('1', 'My File Content someFileField', { + filename: 'someFileField.txt', + contentType: 'text/plain', }); - body.append("2", "My File Content somePointerField", { - filename: "somePointerField.txt", - contentType: "text/plain", + body.append('2', 'My File Content somePointerField', { + filename: 'somePointerField.txt', + contentType: 'text/plain', }); - body.append("3", "My File Content someRelationField", { - filename: "someRelationField.txt", - contentType: "text/plain", + body.append('3', 'My File Content someRelationField', { + filename: 'someRelationField.txt', + contentType: 'text/plain', }); - const res = await fetch("http://localhost:13377/graphql", { - method: "POST", + const res = await fetch('http://localhost:13377/graphql', { + method: 'POST', headers, body, }); @@ -10423,15 +10423,15 @@ describe("ParseGraphQLServer", () => { ).toEqual(jasmine.stringMatching(/_someRelationField.txt$/)); }); - it("should support files and add extension from mimetype", async () => { + it('should support files and add extension from mimetype', async () => { try { parseServer = await global.reconfigureServer({ - publicServerURL: "http://localhost:13377/parse", + publicServerURL: 'http://localhost:13377/parse', }); await createGQLFromParseServer(parseServer); const body = new FormData(); body.append( - "operations", + 'operations', JSON.stringify({ query: ` mutation CreateFile($input: CreateFileInput!) { @@ -10451,17 +10451,17 @@ describe("ParseGraphQLServer", () => { }) ); body.append( - "map", - JSON.stringify({ 1: ["variables.input.upload"] }) + 'map', + JSON.stringify({ 1: ['variables.input.upload'] }) ); - body.append("1", "My File Content", { + body.append('1', 'My File Content', { // No extension, the system should add it from mimetype - filename: "myFileName", - contentType: "text/plain", + filename: 'myFileName', + contentType: 'text/plain', }); - const res = await fetch("http://localhost:13377/graphql", { - method: "POST", + const res = await fetch('http://localhost:13377/graphql', { + method: 'POST', headers, body, }); @@ -10480,10 +10480,10 @@ describe("ParseGraphQLServer", () => { } }); - it("should not upload if file is too large", async () => { + it('should not upload if file is too large', async () => { const body = new FormData(); body.append( - "operations", + 'operations', JSON.stringify({ query: ` mutation CreateFile($input: CreateFileInput!) { @@ -10502,22 +10502,22 @@ describe("ParseGraphQLServer", () => { }, }) ); - body.append("map", JSON.stringify({ 1: ["variables.input.upload"] })); + body.append('map', JSON.stringify({ 1: ['variables.input.upload'] })); body.append( - "1", + '1', // In this test file parse server is setup with 1kb limit Buffer.alloc( - parseGraphQLServer._transformMaxUploadSizeToBytes("2kb"), + parseGraphQLServer._transformMaxUploadSizeToBytes('2kb'), 1 ), { - filename: "myFileName.txt", - contentType: "text/plain", + filename: 'myFileName.txt', + contentType: 'text/plain', } ); - const res = await fetch("http://localhost:13377/graphql", { - method: "POST", + const res = await fetch('http://localhost:13377/graphql', { + method: 'POST', headers, body, }); @@ -10525,14 +10525,14 @@ describe("ParseGraphQLServer", () => { const result = JSON.parse(await res.text()); expect(res.status).toEqual(200); expect(result.errors[0].message).toEqual( - "File truncated as it exceeds the 1024 byte size limit." + 'File truncated as it exceeds the 1024 byte size limit.' ); }); - it("should support object values", async () => { + it('should support object values', async () => { try { const someObjectFieldValue = { - foo: { bar: "baz" }, + foo: { bar: 'baz' }, number: 10, }; @@ -10548,19 +10548,19 @@ describe("ParseGraphQLServer", () => { `, variables: { schemaFields: { - addObjects: [{ name: "someObjectField" }], + addObjects: [{ name: 'someObjectField' }], }, }, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const schema = await new Parse.Schema("SomeClass").get(); - expect(schema.fields.someObjectField.type).toEqual("Object"); + const schema = await new Parse.Schema('SomeClass').get(); + expect(schema.fields.someObjectField.type).toEqual('Object'); const createResult = await apolloClient.mutate({ mutation: gql` @@ -10581,10 +10581,10 @@ describe("ParseGraphQLServer", () => { const where = { someObjectField: { - equalTo: { key: "foo.bar", value: "baz" }, - notEqualTo: { key: "foo.bar", value: "bat" }, - greaterThan: { key: "number", value: 9 }, - lessThan: { key: "number", value: 11 }, + equalTo: { key: 'foo.bar', value: 'baz' }, + notEqualTo: { key: 'foo.bar', value: 'bat' }, + greaterThan: { key: 'number', value: 9 }, + lessThan: { key: 'number', value: 11 }, }, }; const queryResult = await apolloClient.query({ @@ -10613,7 +10613,7 @@ describe("ParseGraphQLServer", () => { const { someClass: getResult, someClasses } = queryResult.data; const { someObjectField } = getResult; - expect(typeof someObjectField).toEqual("object"); + expect(typeof someObjectField).toEqual('object'); expect(someObjectField).toEqual(someObjectFieldValue); // Checks class query results @@ -10626,7 +10626,7 @@ describe("ParseGraphQLServer", () => { } }); - it("should support where argument on object field that contains false boolean value or 0 number value", async () => { + it('should support where argument on object field that contains false boolean value or 0 number value', async () => { try { const someObjectFieldValue1 = { foo: { bar: true, baz: 100 }, @@ -10636,38 +10636,38 @@ describe("ParseGraphQLServer", () => { foo: { bar: false, baz: 0 }, }; - const object1 = new Parse.Object("SomeClass"); + const object1 = new Parse.Object('SomeClass'); await object1.save({ someObjectField: someObjectFieldValue1, }); - const object2 = new Parse.Object("SomeClass"); + const object2 = new Parse.Object('SomeClass'); await object2.save({ someObjectField: someObjectFieldValue2, }); const whereToObject1 = { someObjectField: { - equalTo: { key: "foo.bar", value: true }, - notEqualTo: { key: "foo.baz", value: 0 }, + equalTo: { key: 'foo.bar', value: true }, + notEqualTo: { key: 'foo.baz', value: 0 }, }, }; const whereToObject2 = { someObjectField: { - notEqualTo: { key: "foo.bar", value: true }, - equalTo: { key: "foo.baz", value: 0 }, + notEqualTo: { key: 'foo.bar', value: true }, + equalTo: { key: 'foo.baz', value: 0 }, }, }; const whereToAll = { someObjectField: { - lessThan: { key: "foo.baz", value: 101 }, + lessThan: { key: 'foo.baz', value: 101 }, }, }; const whereToNone = { someObjectField: { - notEqualTo: { key: "foo.bar", value: true }, - equalTo: { key: "foo.baz", value: 1 }, + notEqualTo: { key: 'foo.bar', value: true }, + equalTo: { key: 'foo.baz', value: 1 }, }, }; @@ -10755,15 +10755,15 @@ describe("ParseGraphQLServer", () => { } }); - it("should support object composed queries", async () => { + it('should support object composed queries', async () => { try { const someObjectFieldValue1 = { - lorem: "ipsum", + lorem: 'ipsum', number: 10, }; const someObjectFieldValue2 = { foo: { - test: "bar", + test: 'bar', }, number: 10, }; @@ -10785,7 +10785,7 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -10824,24 +10824,24 @@ describe("ParseGraphQLServer", () => { AND: [ { someObjectField: { - greaterThan: { key: "number", value: 9 }, + greaterThan: { key: 'number', value: 9 }, }, }, { someObjectField: { - lessThan: { key: "number", value: 11 }, + lessThan: { key: 'number', value: 11 }, }, }, { OR: [ { someObjectField: { - equalTo: { key: "lorem", value: "ipsum" }, + equalTo: { key: 'lorem', value: 'ipsum' }, }, }, { someObjectField: { - equalTo: { key: "foo.test", value: "bar" }, + equalTo: { key: 'foo.test', value: 'bar' }, }, }, ], @@ -10885,13 +10885,13 @@ describe("ParseGraphQLServer", () => { } }); - it("should support array values", async () => { + it('should support array values', async () => { try { const someArrayFieldValue = [ 1, - "foo", - ["bar"], - { lorem: "ipsum" }, + 'foo', + ['bar'], + { lorem: 'ipsum' }, true, ]; @@ -10907,20 +10907,20 @@ describe("ParseGraphQLServer", () => { `, variables: { schemaFields: { - addArrays: [{ name: "someArrayField" }], + addArrays: [{ name: 'someArrayField' }], }, }, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const schema = await new Parse.Schema("SomeClass").get(); - expect(schema.fields.someArrayField.type).toEqual("Array"); + const schema = await new Parse.Schema('SomeClass').get(); + expect(schema.fields.someArrayField.type).toEqual('Array'); const createResult = await apolloClient.mutate({ mutation: gql` @@ -10979,12 +10979,12 @@ describe("ParseGraphQLServer", () => { } }); - it("should support undefined array", async () => { - const schema = await new Parse.Schema("SomeClass"); - schema.addArray("someArray"); + it('should support undefined array', async () => { + const schema = await new Parse.Schema('SomeClass'); + schema.addArray('someArray'); await schema.save(); - const obj = new Parse.Object("SomeClass"); + const obj = new Parse.Object('SomeClass'); await obj.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -11009,7 +11009,7 @@ describe("ParseGraphQLServer", () => { expect(getResult.data.someClass.someArray).toEqual(null); }); - it("should support null values", async () => { + it('should support null values', async () => { try { await apolloClient.mutate({ mutation: gql` @@ -11034,7 +11034,7 @@ describe("ParseGraphQLServer", () => { `, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -11053,10 +11053,10 @@ describe("ParseGraphQLServer", () => { `, variables: { fields: { - someStringField: "some string", + someStringField: 'some string', someNumberField: 123, someBooleanField: true, - someObjectField: { someField: "some value" }, + someObjectField: { someField: 'some value' }, someNullField: null, }, }, @@ -11080,7 +11080,7 @@ describe("ParseGraphQLServer", () => { someNumberField: null, someBooleanField: null, someObjectField: null, - someNullField: "now it has a string", + someNullField: 'now it has a string', }, }, }); @@ -11107,18 +11107,18 @@ describe("ParseGraphQLServer", () => { expect(getResult.data.someClass.someBooleanField).toBeFalsy(); expect(getResult.data.someClass.someObjectField).toBeFalsy(); expect(getResult.data.someClass.someNullField).toEqual( - "now it has a string" + 'now it has a string' ); } catch (e) { handleError(e); } }); - it_id("43303db7-c5a7-4bc0-91c3-57e03fffa225")(it)( - "should support Bytes", + it_id('43303db7-c5a7-4bc0-91c3-57e03fffa225')(it)( + 'should support Bytes', async () => { try { - const someFieldValue = "aGVsbG8gd29ybGQ="; + const someFieldValue = 'aGVsbG8gd29ybGQ='; await apolloClient.mutate({ mutation: gql` @@ -11132,20 +11132,20 @@ describe("ParseGraphQLServer", () => { `, variables: { schemaFields: { - addBytes: [{ name: "someField" }], + addBytes: [{ name: 'someField' }], }, }, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const schema = await new Parse.Schema("SomeClass").get(); - expect(schema.fields.someField.type).toEqual("Bytes"); + const schema = await new Parse.Schema('SomeClass').get(); + expect(schema.fields.someField.type).toEqual('Bytes'); const createResult = await apolloClient.mutate({ mutation: gql` @@ -11204,7 +11204,7 @@ describe("ParseGraphQLServer", () => { }); expect(typeof getResult.data.someClass.someField).toEqual( - "string" + 'string' ); expect(getResult.data.someClass.someField).toEqual( someFieldValue @@ -11216,12 +11216,12 @@ describe("ParseGraphQLServer", () => { } ); - it_id("6a253e47-6959-4427-b841-c0c1fa77cf01")(it)( - "should support Geo Points", + it_id('6a253e47-6959-4427-b841-c0c1fa77cf01')(it)( + 'should support Geo Points', async () => { try { const someFieldValue = { - __typename: "GeoPoint", + __typename: 'GeoPoint', latitude: 45, longitude: 45, }; @@ -11238,20 +11238,20 @@ describe("ParseGraphQLServer", () => { `, variables: { schemaFields: { - addGeoPoint: { name: "someField" }, + addGeoPoint: { name: 'someField' }, }, }, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const schema = await new Parse.Schema("SomeClass").get(); - expect(schema.fields.someField.type).toEqual("GeoPoint"); + const schema = await new Parse.Schema('SomeClass').get(); + expect(schema.fields.someField.type).toEqual('GeoPoint'); const createResult = await apolloClient.mutate({ mutation: gql` @@ -11303,7 +11303,7 @@ describe("ParseGraphQLServer", () => { }); expect(typeof getResult.data.someClass.someField).toEqual( - "object" + 'object' ); expect(getResult.data.someClass.someField).toEqual( someFieldValue @@ -11396,7 +11396,7 @@ describe("ParseGraphQLServer", () => { } ); - it("should support Polygons", async () => { + it('should support Polygons', async () => { try { const somePolygonFieldValue = [ [44, 45], @@ -11420,20 +11420,20 @@ describe("ParseGraphQLServer", () => { `, variables: { schemaFields: { - addPolygons: [{ name: "somePolygonField" }], + addPolygons: [{ name: 'somePolygonField' }], }, }, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const schema = await new Parse.Schema("SomeClass").get(); - expect(schema.fields.somePolygonField.type).toEqual("Polygon"); + const schema = await new Parse.Schema('SomeClass').get(); + expect(schema.fields.somePolygonField.type).toEqual('Polygon'); const createResult = await apolloClient.mutate({ mutation: gql` @@ -11480,12 +11480,12 @@ describe("ParseGraphQLServer", () => { }); expect(typeof getResult.data.someClass.somePolygonField).toEqual( - "object" + 'object' ); expect(getResult.data.someClass.somePolygonField).toEqual( somePolygonFieldValue.map(geoPoint => ({ ...geoPoint, - __typename: "GeoPoint", + __typename: 'GeoPoint', })) ); expect(getResult.data.someClasses.edges.length).toEqual(1); @@ -11522,22 +11522,22 @@ describe("ParseGraphQLServer", () => { } }); - it_only_db("mongo")("should support bytes values", async () => { - const SomeClass = Parse.Object.extend("SomeClass"); + it_only_db('mongo')('should support bytes values', async () => { + const SomeClass = Parse.Object.extend('SomeClass'); const someClass = new SomeClass(); - someClass.set("someField", { - __type: "Bytes", - base64: "foo", + someClass.set('someField', { + __type: 'Bytes', + base64: 'foo', }); await someClass.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const schema = await new Parse.Schema("SomeClass").get(); - expect(schema.fields.someField.type).toEqual("Bytes"); + const schema = await new Parse.Schema('SomeClass').get(); + expect(schema.fields.someField.type).toEqual('Bytes'); const someFieldValue = { - __type: "Bytes", - base64: "bytesContent", + __type: 'Bytes', + base64: 'bytesContent', }; const createResult = await apolloClient.mutate({ @@ -11575,8 +11575,8 @@ describe("ParseGraphQLServer", () => { ); const updatedSomeFieldValue = { - __type: "Bytes", - base64: "newBytesContent", + __type: 'Bytes', + base64: 'newBytesContent', }; const updatedResult = await apolloClient.mutate({ @@ -11631,11 +11631,11 @@ describe("ParseGraphQLServer", () => { }); }); - describe("Special Classes", () => { - it("should support User class", async () => { + describe('Special Classes', () => { + it('should support User class', async () => { const user = new Parse.User(); - user.setUsername("user1"); - user.setPassword("user1"); + user.setUsername('user1'); + user.setPassword('user1'); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); user.setACL(acl); @@ -11659,10 +11659,10 @@ describe("ParseGraphQLServer", () => { expect(getResult.data.get.objectId).toEqual(user.id); }); - it("should support Installation class", async () => { + it('should support Installation class', async () => { const installation = new Parse.Installation(); await installation.save({ - deviceType: "foo", + deviceType: 'foo', }); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -11683,10 +11683,10 @@ describe("ParseGraphQLServer", () => { expect(getResult.data.get.objectId).toEqual(installation.id); }); - it("should support Role class", async () => { + it('should support Role class', async () => { const roleACL = new Parse.ACL(); roleACL.setPublicReadAccess(true); - const role = new Parse.Role("MyRole", roleACL); + const role = new Parse.Role('MyRole', roleACL); await role.save(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -11707,10 +11707,10 @@ describe("ParseGraphQLServer", () => { expect(getResult.data.get.objectId).toEqual(role.id); }); - it("should support Session class", async () => { + it('should support Session class', async () => { const user = new Parse.User(); - user.setUsername("user1"); - user.setPassword("user1"); + user.setUsername('user1'); + user.setPassword('user1'); await user.signUp(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -11730,7 +11730,7 @@ describe("ParseGraphQLServer", () => { }, context: { headers: { - "X-Parse-Session-Token": session.getSessionToken(), + 'X-Parse-Session-Token': session.getSessionToken(), }, }, }); @@ -11738,16 +11738,16 @@ describe("ParseGraphQLServer", () => { expect(getResult.data.get.objectId).toEqual(session.id); }); - it("should support Product class", async () => { - const Product = Parse.Object.extend("_Product"); + it('should support Product class', async () => { + const Product = Parse.Object.extend('_Product'); const product = new Product(); await product.save( { - productIdentifier: "foo", - icon: new Parse.File("icon", ["foo"]), + productIdentifier: 'foo', + icon: new Parse.File('icon', ['foo']), order: 1, - title: "Foo", - subtitle: "My product", + title: 'Foo', + subtitle: 'My product', }, { useMasterKey: true } ); @@ -11767,7 +11767,7 @@ describe("ParseGraphQLServer", () => { }, context: { headers: { - "X-Parse-Master-Key": "test", + 'X-Parse-Master-Key': 'test', }, }, }); @@ -11778,19 +11778,19 @@ describe("ParseGraphQLServer", () => { }); }); - describe("Custom API", () => { - describe("SDL based", () => { + describe('Custom API', () => { + describe('SDL based', () => { let httpServer; const headers = { - "X-Parse-Application-Id": "test", - "X-Parse-Javascript-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Javascript-Key': 'test', }; let apolloClient; beforeEach(async () => { const expressApp = express(); httpServer = http.createServer(expressApp); parseGraphQLServer = new ParseGraphQLServer(parseServer, { - graphQLPath: "/graphql", + graphQLPath: '/graphql', graphQLCustomTypeDefs: gql` extend type Query { hello: String @resolve @@ -11806,7 +11806,7 @@ describe("ParseGraphQLServer", () => { httpServer.listen({ port: 13377 }, resolve) ); const httpLink = await createUploadLink({ - uri: "http://localhost:13377/graphql", + uri: 'http://localhost:13377/graphql', fetch, headers, }); @@ -11815,7 +11815,7 @@ describe("ParseGraphQLServer", () => { cache: new InMemoryCache(), defaultOptions: { query: { - fetchPolicy: "no-cache", + fetchPolicy: 'no-cache', }, }, }); @@ -11825,9 +11825,9 @@ describe("ParseGraphQLServer", () => { await httpServer.close(); }); - it("can resolve a custom query using default function name", async () => { - Parse.Cloud.define("hello", async () => { - return "Hello world!"; + it('can resolve a custom query using default function name', async () => { + Parse.Cloud.define('hello', async () => { + return 'Hello world!'; }); const result = await apolloClient.query({ @@ -11838,12 +11838,12 @@ describe("ParseGraphQLServer", () => { `, }); - expect(result.data.hello).toEqual("Hello world!"); + expect(result.data.hello).toEqual('Hello world!'); }); it('can resolve a custom query using function name set by "to" argument', async () => { - Parse.Cloud.define("hello", async () => { - return "Hello world!"; + Parse.Cloud.define('hello', async () => { + return 'Hello world!'; }); const result = await apolloClient.query({ @@ -11854,31 +11854,31 @@ describe("ParseGraphQLServer", () => { `, }); - expect(result.data.hello2).toEqual("Hello world!"); + expect(result.data.hello2).toEqual('Hello world!'); }); - it("order option should continue working", async () => { + it('order option should continue working', async () => { const schemaController = await parseServer.config.databaseController.loadSchema(); - await schemaController.addClassIfNotExists("SuperCar", { - engine: { type: "String" }, - doors: { type: "Number" }, - price: { type: "String" }, - mileage: { type: "Number" }, + await schemaController.addClassIfNotExists('SuperCar', { + engine: { type: 'String' }, + doors: { type: 'Number' }, + price: { type: 'String' }, + mileage: { type: 'Number' }, }); - await new Parse.Object("SuperCar").save({ - engine: "petrol", + await new Parse.Object('SuperCar').save({ + engine: 'petrol', doors: 3, - price: "£7500", + price: '£7500', mileage: 0, }); - await new Parse.Object("SuperCar").save({ - engine: "petrol", + await new Parse.Object('SuperCar').save({ + engine: 'petrol', doors: 3, - price: "£7500", + price: '£7500', mileage: 10000, }); @@ -11905,11 +11905,11 @@ describe("ParseGraphQLServer", () => { }); }); - describe("GraphQL Schema Based", () => { + describe('GraphQL Schema Based', () => { let httpServer; const headers = { - "X-Parse-Application-Id": "test", - "X-Parse-Javascript-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Javascript-Key': 'test', }; let apolloClient; @@ -11917,26 +11917,26 @@ describe("ParseGraphQLServer", () => { const expressApp = express(); httpServer = http.createServer(expressApp); const TypeEnum = new GraphQLEnumType({ - name: "TypeEnum", + name: 'TypeEnum', values: { - human: { value: "human" }, - robot: { value: "robot" }, + human: { value: 'human' }, + robot: { value: 'robot' }, }, }); const TypeEnumWhereInput = new GraphQLInputObjectType({ - name: "TypeEnumWhereInput", + name: 'TypeEnumWhereInput', fields: { equalTo: { type: TypeEnum }, }, }); const SomeClass2WhereInput = new GraphQLInputObjectType({ - name: "SomeClass2WhereInput", + name: 'SomeClass2WhereInput', fields: { type: { type: TypeEnumWhereInput }, }, }); const SomeClassType = new GraphQLObjectType({ - name: "SomeClass", + name: 'SomeClass', fields: { nameUpperCase: { type: new GraphQLNonNull(GraphQLString), @@ -11945,21 +11945,21 @@ describe("ParseGraphQLServer", () => { type: { type: TypeEnum }, language: { type: new GraphQLEnumType({ - name: "LanguageEnum", + name: 'LanguageEnum', values: { - fr: { value: "fr" }, - en: { value: "en" }, + fr: { value: 'fr' }, + en: { value: 'en' }, }, }), - resolve: () => "fr", + resolve: () => 'fr', }, }, }), parseGraphQLServer = new ParseGraphQLServer(parseServer, { - graphQLPath: "/graphql", + graphQLPath: '/graphql', graphQLCustomTypeDefs: new GraphQLSchema({ query: new GraphQLObjectType({ - name: "Query", + name: 'Query', fields: { customQuery: { type: new GraphQLNonNull(GraphQLString), @@ -11971,7 +11971,7 @@ describe("ParseGraphQLServer", () => { errorQuery: { type: new GraphQLNonNull(GraphQLString), resolve: () => { - throw new Error("A test error"); + throw new Error('A test error'); }, }, customQueryWithAutoTypeReturn: { @@ -11980,7 +11980,7 @@ describe("ParseGraphQLServer", () => { id: { type: new GraphQLNonNull(GraphQLString) }, }, resolve: async (p, { id }) => { - const obj = new Parse.Object("SomeClass"); + const obj = new Parse.Object('SomeClass'); obj.id = id; await obj.fetch(); return obj.toJSON(); @@ -11992,7 +11992,7 @@ describe("ParseGraphQLServer", () => { id: { type: new GraphQLNonNull(GraphQLString) }, }, resolve: async (p, { id }) => { - const obj = new Parse.Object("SomeClass"); + const obj = new Parse.Object('SomeClass'); obj.id = id; await obj.fetch(); return [obj.toJSON(), obj.toJSON(), obj.toJSON()]; @@ -12002,20 +12002,20 @@ describe("ParseGraphQLServer", () => { }), types: [ new GraphQLInputObjectType({ - name: "CreateSomeClassFieldsInput", + name: 'CreateSomeClassFieldsInput', fields: { type: { type: TypeEnum }, }, }), new GraphQLInputObjectType({ - name: "UpdateSomeClassFieldsInput", + name: 'UpdateSomeClassFieldsInput', fields: { type: { type: TypeEnum }, }, }), // Enhanced where input with a extended enum new GraphQLInputObjectType({ - name: "SomeClassWhereInput", + name: 'SomeClassWhereInput', fields: { type: { type: TypeEnumWhereInput, @@ -12033,7 +12033,7 @@ describe("ParseGraphQLServer", () => { httpServer.listen({ port: 13377 }, resolve) ); const httpLink = await createUploadLink({ - uri: "http://localhost:13377/graphql", + uri: 'http://localhost:13377/graphql', fetch, headers, }); @@ -12042,7 +12042,7 @@ describe("ParseGraphQLServer", () => { cache: new InMemoryCache(), defaultOptions: { query: { - fetchPolicy: "no-cache", + fetchPolicy: 'no-cache', }, }, }); @@ -12052,19 +12052,19 @@ describe("ParseGraphQLServer", () => { await httpServer.close(); }); - it("can resolve a custom query", async () => { + it('can resolve a custom query', async () => { const result = await apolloClient.query({ - variables: { message: "hello" }, + variables: { message: 'hello' }, query: gql` query CustomQuery($message: String!) { customQuery(message: $message) } `, }); - expect(result.data.customQuery).toEqual("hello"); + expect(result.data.customQuery).toEqual('hello'); }); - it("can forward original error of a custom query", async () => { + it('can forward original error of a custom query', async () => { await expectAsync( apolloClient.query({ query: gql` @@ -12073,12 +12073,12 @@ describe("ParseGraphQLServer", () => { } `, }) - ).toBeRejectedWithError("A test error"); + ).toBeRejectedWithError('A test error'); }); - it("can resolve a custom query with auto type return", async () => { - const obj = new Parse.Object("SomeClass"); - await obj.save({ name: "aname", type: "robot" }); + it('can resolve a custom query with auto type return', async () => { + const obj = new Parse.Object('SomeClass'); + await obj.save({ name: 'aname', type: 'robot' }); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); const result = await apolloClient.query({ variables: { id: obj.id }, @@ -12096,16 +12096,16 @@ describe("ParseGraphQLServer", () => { expect(result.data.customQueryWithAutoTypeReturn.objectId).toEqual( obj.id ); - expect(result.data.customQueryWithAutoTypeReturn.name).toEqual("aname"); + expect(result.data.customQueryWithAutoTypeReturn.name).toEqual('aname'); expect(result.data.customQueryWithAutoTypeReturn.nameUpperCase).toEqual( - "ANAME" + 'ANAME' ); - expect(result.data.customQueryWithAutoTypeReturn.type).toEqual("robot"); + expect(result.data.customQueryWithAutoTypeReturn.type).toEqual('robot'); }); - it("can resolve a custom query with auto type list return", async () => { - const obj = new Parse.Object("SomeClass"); - await obj.save({ name: "aname", type: "robot" }); + it('can resolve a custom query with auto type list return', async () => { + const obj = new Parse.Object('SomeClass'); + await obj.save({ name: 'aname', type: 'robot' }); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); const result = await apolloClient.query({ variables: { id: obj.id }, @@ -12124,17 +12124,17 @@ describe("ParseGraphQLServer", () => { result.data.customQueryWithAutoTypeReturnList.forEach(rObj => { expect(rObj.objectId).toBeDefined(); expect(rObj.objectId).toEqual(obj.id); - expect(rObj.name).toEqual("aname"); - expect(rObj.nameUpperCase).toEqual("ANAME"); - expect(rObj.type).toEqual("robot"); + expect(rObj.name).toEqual('aname'); + expect(rObj.nameUpperCase).toEqual('ANAME'); + expect(rObj.type).toEqual('robot'); }); }); - it("can resolve a stacked query with same where variables on overloaded where input", async () => { - const objPointer = new Parse.Object("SomeClass2"); - await objPointer.save({ name: "aname", type: "robot" }); - const obj = new Parse.Object("SomeClass"); - await obj.save({ name: "aname", type: "robot", pointer: objPointer }); + it('can resolve a stacked query with same where variables on overloaded where input', async () => { + const objPointer = new Parse.Object('SomeClass2'); + await objPointer.save({ name: 'aname', type: 'robot' }); + const obj = new Parse.Object('SomeClass'); + await obj.save({ name: 'aname', type: 'robot', pointer: objPointer }); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); const result = await apolloClient.query({ variables: { @@ -12168,9 +12168,9 @@ describe("ParseGraphQLServer", () => { ); }); - it("can resolve a custom extend type", async () => { - const obj = new Parse.Object("SomeClass"); - await obj.save({ name: "aname", type: "robot" }); + it('can resolve a custom extend type', async () => { + const obj = new Parse.Object('SomeClass'); + await obj.save({ name: 'aname', type: 'robot' }); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); const result = await apolloClient.query({ variables: { id: obj.id }, @@ -12184,9 +12184,9 @@ describe("ParseGraphQLServer", () => { } `, }); - expect(result.data.someClass.nameUpperCase).toEqual("ANAME"); - expect(result.data.someClass.language).toEqual("fr"); - expect(result.data.someClass.type).toEqual("robot"); + expect(result.data.someClass.nameUpperCase).toEqual('ANAME'); + expect(result.data.someClass.language).toEqual('fr'); + expect(result.data.someClass.type).toEqual('robot'); const result2 = await apolloClient.query({ variables: { id: obj.id }, @@ -12199,10 +12199,10 @@ describe("ParseGraphQLServer", () => { } `, }); - expect(result2.data.someClass.name).toEqual("aname"); - expect(result.data.someClass.language).toEqual("fr"); + expect(result2.data.someClass.name).toEqual('aname'); + expect(result.data.someClass.language).toEqual('fr'); const result3 = await apolloClient.mutate({ - variables: { id: obj.id, name: "anewname", type: "human" }, + variables: { id: obj.id, name: 'anewname', type: 'human' }, mutation: gql` mutation someClass($id: ID!, $name: String!, $type: TypeEnum!) { updateSomeClass( @@ -12217,16 +12217,16 @@ describe("ParseGraphQLServer", () => { `, }); expect(result3.data.updateSomeClass.someClass.nameUpperCase).toEqual( - "ANEWNAME" + 'ANEWNAME' ); - expect(result3.data.updateSomeClass.someClass.type).toEqual("human"); + expect(result3.data.updateSomeClass.someClass.type).toEqual('human'); }); }); - describe("Async Function Based Merge", () => { + describe('Async Function Based Merge', () => { let httpServer; const headers = { - "X-Parse-Application-Id": "test", - "X-Parse-Javascript-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Javascript-Key': 'test', }; let apolloClient; @@ -12235,7 +12235,7 @@ describe("ParseGraphQLServer", () => { const expressApp = express(); httpServer = http.createServer(expressApp); parseGraphQLServer = new ParseGraphQLServer(parseServer, { - graphQLPath: "/graphql", + graphQLPath: '/graphql', graphQLCustomTypeDefs: ({ autoSchema }) => mergeSchemas({ schemas: [autoSchema] }), }); @@ -12245,7 +12245,7 @@ describe("ParseGraphQLServer", () => { httpServer.listen({ port: 13377 }, resolve) ); const httpLink = await createUploadLink({ - uri: "http://localhost:13377/graphql", + uri: 'http://localhost:13377/graphql', fetch, headers, }); @@ -12254,7 +12254,7 @@ describe("ParseGraphQLServer", () => { cache: new InMemoryCache(), defaultOptions: { query: { - fetchPolicy: "no-cache", + fetchPolicy: 'no-cache', }, }, }); @@ -12265,7 +12265,7 @@ describe("ParseGraphQLServer", () => { await httpServer.close(); }); - it("can resolve a query", async () => { + it('can resolve a query', async () => { const result = await apolloClient.query({ query: gql` query Health { diff --git a/spec/ParseHooks.spec.js b/spec/ParseHooks.spec.js index ef2b69ffc6..a3958ee9b0 100644 --- a/spec/ParseHooks.spec.js +++ b/spec/ParseHooks.spec.js @@ -1,22 +1,22 @@ -"use strict"; +'use strict'; -const request = require("../lib/request"); -const triggers = require("../lib/triggers"); -const HooksController = require("../lib/Controllers/HooksController").default; -const express = require("express"); -const auth = require("../lib/Auth"); -const Config = require("../lib/Config"); +const request = require('../lib/request'); +const triggers = require('../lib/triggers'); +const HooksController = require('../lib/Controllers/HooksController').default; +const express = require('express'); +const auth = require('../lib/Auth'); +const Config = require('../lib/Config'); const port = 34567; -const hookServerURL = "http://localhost:" + port; +const hookServerURL = 'http://localhost:' + port; -describe("Hooks", () => { +describe('Hooks', () => { let server; let app; beforeEach(done => { if (!app) { app = express(); - app.use(express.json({ type: "*/*" })); + app.use(express.json({ type: '*/*' })); server = app.listen(port, undefined, done); } else { done(); @@ -27,7 +27,7 @@ describe("Hooks", () => { server.close(done); }); - it("should have no hooks registered", done => { + it('should have no hooks registered', done => { Parse.Hooks.getFunctions().then( res => { expect(res.constructor).toBe(Array.prototype.constructor); @@ -40,7 +40,7 @@ describe("Hooks", () => { ); }); - it("should have no triggers registered", done => { + it('should have no triggers registered', done => { Parse.Hooks.getTriggers().then( res => { expect(res.constructor).toBe(Array.prototype.constructor); @@ -53,37 +53,37 @@ describe("Hooks", () => { ); }); - it_id("26c9a13d-3d71-452e-a91c-9a4589be021c")(it)( - "should CRUD a function registration", + it_id('26c9a13d-3d71-452e-a91c-9a4589be021c')(it)( + 'should CRUD a function registration', done => { // Create - Parse.Hooks.createFunction("My-Test-Function", "http://someurl") + Parse.Hooks.createFunction('My-Test-Function', 'http://someurl') .then(response => { - expect(response.functionName).toBe("My-Test-Function"); - expect(response.url).toBe("http://someurl"); + expect(response.functionName).toBe('My-Test-Function'); + expect(response.url).toBe('http://someurl'); // Find - return Parse.Hooks.getFunction("My-Test-Function"); + return Parse.Hooks.getFunction('My-Test-Function'); }) .then(response => { expect(response.objectId).toBeUndefined(); - expect(response.url).toBe("http://someurl"); + expect(response.url).toBe('http://someurl'); return Parse.Hooks.updateFunction( - "My-Test-Function", - "http://anotherurl" + 'My-Test-Function', + 'http://anotherurl' ); }) .then(res => { expect(res.objectId).toBeUndefined(); - expect(res.functionName).toBe("My-Test-Function"); - expect(res.url).toBe("http://anotherurl"); + expect(res.functionName).toBe('My-Test-Function'); + expect(res.url).toBe('http://anotherurl'); // delete - return Parse.Hooks.removeFunction("My-Test-Function"); + return Parse.Hooks.removeFunction('My-Test-Function'); }) .then(() => { // Find again! but should be deleted - return Parse.Hooks.getFunction("My-Test-Function").then( + return Parse.Hooks.getFunction('My-Test-Function').then( res => { - fail("Failed to delete hook"); + fail('Failed to delete hook'); fail(res); done(); return Promise.resolve(); @@ -91,7 +91,7 @@ describe("Hooks", () => { err => { expect(err.code).toBe(143); expect(err.message).toBe( - "no function named: My-Test-Function is defined" + 'no function named: My-Test-Function is defined' ); done(); return Promise.resolve(); @@ -105,18 +105,18 @@ describe("Hooks", () => { } ); - it_id("7a81069e-2ee9-47fb-8e27-1120eda09e99")(it)( - "should CRUD a trigger registration", + it_id('7a81069e-2ee9-47fb-8e27-1120eda09e99')(it)( + 'should CRUD a trigger registration', done => { // Create - Parse.Hooks.createTrigger("MyClass", "beforeDelete", "http://someurl") + Parse.Hooks.createTrigger('MyClass', 'beforeDelete', 'http://someurl') .then( res => { - expect(res.className).toBe("MyClass"); - expect(res.triggerName).toBe("beforeDelete"); - expect(res.url).toBe("http://someurl"); + expect(res.className).toBe('MyClass'); + expect(res.triggerName).toBe('beforeDelete'); + expect(res.url).toBe('http://someurl'); // Find - return Parse.Hooks.getTrigger("MyClass", "beforeDelete"); + return Parse.Hooks.getTrigger('MyClass', 'beforeDelete'); }, err => { fail(err); @@ -128,12 +128,12 @@ describe("Hooks", () => { expect(res).not.toBe(null); expect(res).not.toBe(undefined); expect(res.objectId).toBeUndefined(); - expect(res.url).toBe("http://someurl"); + expect(res.url).toBe('http://someurl'); // delete return Parse.Hooks.updateTrigger( - "MyClass", - "beforeDelete", - "http://anotherurl" + 'MyClass', + 'beforeDelete', + 'http://anotherurl' ); }, err => { @@ -143,11 +143,11 @@ describe("Hooks", () => { ) .then( res => { - expect(res.className).toBe("MyClass"); - expect(res.url).toBe("http://anotherurl"); + expect(res.className).toBe('MyClass'); + expect(res.url).toBe('http://anotherurl'); expect(res.objectId).toBeUndefined(); - return Parse.Hooks.removeTrigger("MyClass", "beforeDelete"); + return Parse.Hooks.removeTrigger('MyClass', 'beforeDelete'); }, err => { jfail(err); @@ -157,7 +157,7 @@ describe("Hooks", () => { .then( () => { // Find again! but should be deleted - return Parse.Hooks.getTrigger("MyClass", "beforeDelete"); + return Parse.Hooks.getTrigger('MyClass', 'beforeDelete'); }, err => { jfail(err); @@ -166,7 +166,7 @@ describe("Hooks", () => { ) .then( function () { - fail("should not succeed"); + fail('should not succeed'); done(); }, err => { @@ -174,9 +174,9 @@ describe("Hooks", () => { expect(err).not.toBe(null); expect(err).not.toBe(undefined); expect(err.code).toBe(143); - expect(err.message).toBe("class MyClass does not exist"); + expect(err.message).toBe('class MyClass does not exist'); } else { - fail("should have errored"); + fail('should have errored'); } done(); } @@ -184,43 +184,43 @@ describe("Hooks", () => { } ); - it("should fail to register hooks without Master Key", done => { + it('should fail to register hooks without Master Key', done => { request({ - method: "POST", - url: Parse.serverURL + "/hooks/functions", + method: 'POST', + url: Parse.serverURL + '/hooks/functions', headers: { - "X-Parse-Application-Id": Parse.applicationId, + 'X-Parse-Application-Id': Parse.applicationId, }, body: JSON.stringify({ - url: "http://hello.word", - functionName: "SomeFunction", + url: 'http://hello.word', + functionName: 'SomeFunction', }), }).then(fail, response => { const body = response.data; - expect(body.error).toBe("unauthorized"); + expect(body.error).toBe('unauthorized'); done(); }); }); - it_id("f7ad092f-81dc-4729-afd1-3b02db2f0948")(it)( - "should fail trying to create two times the same function", + it_id('f7ad092f-81dc-4729-afd1-3b02db2f0948')(it)( + 'should fail trying to create two times the same function', done => { - Parse.Hooks.createFunction("my_new_function", "http://url.com") + Parse.Hooks.createFunction('my_new_function', 'http://url.com') .then(() => jasmine.timeout()) .then( () => { return Parse.Hooks.createFunction( - "my_new_function", - "http://url.com" + 'my_new_function', + 'http://url.com' ); }, () => { - fail("should create a new function"); + fail('should create a new function'); } ) .then( () => { - fail("should not be able to create the same function"); + fail('should not be able to create the same function'); }, err => { expect(err).not.toBe(undefined); @@ -228,10 +228,10 @@ describe("Hooks", () => { if (err) { expect(err.code).toBe(143); expect(err.message).toBe( - "function name: my_new_function already exists" + 'function name: my_new_function already exists' ); } - return Parse.Hooks.removeFunction("my_new_function"); + return Parse.Hooks.removeFunction('my_new_function'); } ) .then( @@ -246,25 +246,25 @@ describe("Hooks", () => { } ); - it_id("4db8c249-9174-4e8e-b959-55c8ea959a02")(it)( - "should fail trying to create two times the same trigger", + it_id('4db8c249-9174-4e8e-b959-55c8ea959a02')(it)( + 'should fail trying to create two times the same trigger', done => { - Parse.Hooks.createTrigger("MyClass", "beforeSave", "http://url.com") + Parse.Hooks.createTrigger('MyClass', 'beforeSave', 'http://url.com') .then( () => { return Parse.Hooks.createTrigger( - "MyClass", - "beforeSave", - "http://url.com" + 'MyClass', + 'beforeSave', + 'http://url.com' ); }, () => { - fail("should create a new trigger"); + fail('should create a new trigger'); } ) .then( () => { - fail("should not be able to create the same trigger"); + fail('should not be able to create the same trigger'); }, err => { expect(err).not.toBe(undefined); @@ -272,10 +272,10 @@ describe("Hooks", () => { if (err) { expect(err.code).toBe(143); expect(err.message).toBe( - "class MyClass already has trigger beforeSave" + 'class MyClass already has trigger beforeSave' ); } - return Parse.Hooks.removeTrigger("MyClass", "beforeSave"); + return Parse.Hooks.removeTrigger('MyClass', 'beforeSave'); } ) .then( @@ -291,10 +291,10 @@ describe("Hooks", () => { ); it("should fail trying to update a function that don't exist", done => { - Parse.Hooks.updateFunction("A_COOL_FUNCTION", "http://url.com") + Parse.Hooks.updateFunction('A_COOL_FUNCTION', 'http://url.com') .then( () => { - fail("Should not succeed"); + fail('Should not succeed'); }, err => { expect(err).not.toBe(undefined); @@ -302,15 +302,15 @@ describe("Hooks", () => { if (err) { expect(err.code).toBe(143); expect(err.message).toBe( - "no function named: A_COOL_FUNCTION is defined" + 'no function named: A_COOL_FUNCTION is defined' ); } - return Parse.Hooks.getFunction("A_COOL_FUNCTION"); + return Parse.Hooks.getFunction('A_COOL_FUNCTION'); } ) .then( () => { - fail("the function should not exist"); + fail('the function should not exist'); done(); }, err => { @@ -319,7 +319,7 @@ describe("Hooks", () => { if (err) { expect(err.code).toBe(143); expect(err.message).toBe( - "no function named: A_COOL_FUNCTION is defined" + 'no function named: A_COOL_FUNCTION is defined' ); } done(); @@ -328,24 +328,24 @@ describe("Hooks", () => { }); it("should fail trying to update a trigger that don't exist", done => { - Parse.Hooks.updateTrigger("AClassName", "beforeSave", "http://url.com") + Parse.Hooks.updateTrigger('AClassName', 'beforeSave', 'http://url.com') .then( () => { - fail("Should not succeed"); + fail('Should not succeed'); }, err => { expect(err).not.toBe(undefined); expect(err).not.toBe(null); if (err) { expect(err.code).toBe(143); - expect(err.message).toBe("class AClassName does not exist"); + expect(err.message).toBe('class AClassName does not exist'); } - return Parse.Hooks.getTrigger("AClassName", "beforeSave"); + return Parse.Hooks.getTrigger('AClassName', 'beforeSave'); } ) .then( () => { - fail("the function should not exist"); + fail('the function should not exist'); done(); }, err => { @@ -353,15 +353,15 @@ describe("Hooks", () => { expect(err).not.toBe(null); if (err) { expect(err.code).toBe(143); - expect(err.message).toBe("class AClassName does not exist"); + expect(err.message).toBe('class AClassName does not exist'); } done(); } ); }); - it("should fail trying to create a malformed function", done => { - Parse.Hooks.createFunction("MyFunction").then( + it('should fail trying to create a malformed function', done => { + Parse.Hooks.createFunction('MyFunction').then( res => { fail(res); }, @@ -370,46 +370,46 @@ describe("Hooks", () => { expect(err).not.toBe(null); if (err) { expect(err.code).toBe(143); - expect(err.error).toBe("invalid hook declaration"); + expect(err.error).toBe('invalid hook declaration'); } done(); } ); }); - it("should fail trying to create a malformed function (REST)", done => { + it('should fail trying to create a malformed function (REST)', done => { request({ - method: "POST", - url: Parse.serverURL + "/hooks/functions", + method: 'POST', + url: Parse.serverURL + '/hooks/functions', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Master-Key": Parse.masterKey, + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': Parse.masterKey, }, - body: JSON.stringify({ functionName: "SomeFunction" }), + body: JSON.stringify({ functionName: 'SomeFunction' }), }).then(fail, response => { const body = response.data; - expect(body.error).toBe("invalid hook declaration"); + expect(body.error).toBe('invalid hook declaration'); expect(body.code).toBe(143); done(); }); }); - it_id("96d99414-b739-4e36-b3f4-8135e0be83ea")(it)( - "should create hooks and properly preload them", + it_id('96d99414-b739-4e36-b3f4-8135e0be83ea')(it)( + 'should create hooks and properly preload them', done => { const promises = []; for (let i = 0; i < 5; i++) { promises.push( Parse.Hooks.createTrigger( - "MyClass" + i, - "beforeSave", - "http://url.com/beforeSave/" + i + 'MyClass' + i, + 'beforeSave', + 'http://url.com/beforeSave/' + i ) ); promises.push( Parse.Hooks.createFunction( - "AFunction" + i, - "http://url.com/function" + i + 'AFunction' + i, + 'http://url.com/function' + i ) ); } @@ -420,31 +420,31 @@ describe("Hooks", () => { for (let i = 0; i < 5; i++) { // Delete everything from memory, as the server just started triggers.removeTrigger( - "beforeSave", - "MyClass" + i, + 'beforeSave', + 'MyClass' + i, Parse.applicationId ); - triggers.removeFunction("AFunction" + i, Parse.applicationId); + triggers.removeFunction('AFunction' + i, Parse.applicationId); expect( triggers.getTrigger( - "MyClass" + i, - "beforeSave", + 'MyClass' + i, + 'beforeSave', Parse.applicationId ) ).toBeUndefined(); expect( - triggers.getFunction("AFunction" + i, Parse.applicationId) + triggers.getFunction('AFunction' + i, Parse.applicationId) ).toBeUndefined(); } const hooksController = new HooksController( Parse.applicationId, - Config.get("test").database + Config.get('test').database ); return hooksController.load(); }, err => { jfail(err); - fail("Should properly create all hooks"); + fail('Should properly create all hooks'); done(); } ) @@ -453,85 +453,85 @@ describe("Hooks", () => { for (let i = 0; i < 5; i++) { expect( triggers.getTrigger( - "MyClass" + i, - "beforeSave", + 'MyClass' + i, + 'beforeSave', Parse.applicationId ) ).not.toBeUndefined(); expect( - triggers.getFunction("AFunction" + i, Parse.applicationId) + triggers.getFunction('AFunction' + i, Parse.applicationId) ).not.toBeUndefined(); } done(); }, err => { jfail(err); - fail("should properly load all hooks"); + fail('should properly load all hooks'); done(); } ); } ); - it_id("fe7d41eb-e570-4804-ac1f-8b6c407fdafe")(it)( - "should run the function on the test server", + it_id('fe7d41eb-e570-4804-ac1f-8b6c407fdafe')(it)( + 'should run the function on the test server', done => { - app.post("/SomeFunction", function (req, res) { - res.json({ success: "OK!" }); + app.post('/SomeFunction', function (req, res) { + res.json({ success: 'OK!' }); }); Parse.Hooks.createFunction( - "SOME_TEST_FUNCTION", - hookServerURL + "/SomeFunction" + 'SOME_TEST_FUNCTION', + hookServerURL + '/SomeFunction' ) .then( function () { - return Parse.Cloud.run("SOME_TEST_FUNCTION"); + return Parse.Cloud.run('SOME_TEST_FUNCTION'); }, err => { jfail(err); - fail("Should not fail creating a function"); + fail('Should not fail creating a function'); done(); } ) .then( function (res) { - expect(res).toBe("OK!"); + expect(res).toBe('OK!'); done(); }, err => { jfail(err); - fail("Should not fail calling a function"); + fail('Should not fail calling a function'); done(); } ); } ); - it_id("63985b4c-a212-4a86-aa0e-eb4600bb485b")(it)( - "should run the function on the test server (error handling)", + it_id('63985b4c-a212-4a86-aa0e-eb4600bb485b')(it)( + 'should run the function on the test server (error handling)', done => { - app.post("/SomeFunctionError", function (req, res) { - res.json({ error: { code: 1337, error: "hacking that one!" } }); + app.post('/SomeFunctionError', function (req, res) { + res.json({ error: { code: 1337, error: 'hacking that one!' } }); }); // The function is deleted as the DB is dropped between calls Parse.Hooks.createFunction( - "SOME_TEST_FUNCTION", - hookServerURL + "/SomeFunctionError" + 'SOME_TEST_FUNCTION', + hookServerURL + '/SomeFunctionError' ) .then( function () { - return Parse.Cloud.run("SOME_TEST_FUNCTION"); + return Parse.Cloud.run('SOME_TEST_FUNCTION'); }, err => { jfail(err); - fail("Should not fail creating a function"); + fail('Should not fail creating a function'); done(); } ) .then( function () { - fail("Should not succeed calling that function"); + fail('Should not succeed calling that function'); done(); }, err => { @@ -540,7 +540,7 @@ describe("Hooks", () => { if (err) { expect(err.code).toBe(Parse.Error.SCRIPT_FAILED); expect(err.message.code).toEqual(1337); - expect(err.message.error).toEqual("hacking that one!"); + expect(err.message.error).toEqual('hacking that one!'); } done(); } @@ -548,74 +548,74 @@ describe("Hooks", () => { } ); - it_id("bacc1754-2a3a-4a7a-8d0e-f80af36da1ef")(it)( - "should provide X-Parse-Webhook-Key when defined", + it_id('bacc1754-2a3a-4a7a-8d0e-f80af36da1ef')(it)( + 'should provide X-Parse-Webhook-Key when defined', done => { - app.post("/ExpectingKey", function (req, res) { - if (req.get("X-Parse-Webhook-Key") === "hook") { - res.json({ success: "correct key provided" }); + app.post('/ExpectingKey', function (req, res) { + if (req.get('X-Parse-Webhook-Key') === 'hook') { + res.json({ success: 'correct key provided' }); } else { - res.json({ error: "incorrect key provided" }); + res.json({ error: 'incorrect key provided' }); } }); Parse.Hooks.createFunction( - "SOME_TEST_FUNCTION", - hookServerURL + "/ExpectingKey" + 'SOME_TEST_FUNCTION', + hookServerURL + '/ExpectingKey' ) .then( function () { - return Parse.Cloud.run("SOME_TEST_FUNCTION"); + return Parse.Cloud.run('SOME_TEST_FUNCTION'); }, err => { jfail(err); - fail("Should not fail creating a function"); + fail('Should not fail creating a function'); done(); } ) .then( function (res) { - expect(res).toBe("correct key provided"); + expect(res).toBe('correct key provided'); done(); }, err => { jfail(err); - fail("Should not fail calling a function"); + fail('Should not fail calling a function'); done(); } ); } ); - it_id("eeb67946-42c6-4581-89af-2abb4927913e")(it)( - "should not pass X-Parse-Webhook-Key if not provided", + it_id('eeb67946-42c6-4581-89af-2abb4927913e')(it)( + 'should not pass X-Parse-Webhook-Key if not provided', done => { reconfigureServer({ webhookKey: undefined }).then(() => { - app.post("/ExpectingKeyAlso", function (req, res) { - if (req.get("X-Parse-Webhook-Key") === "hook") { - res.json({ success: "correct key provided" }); + app.post('/ExpectingKeyAlso', function (req, res) { + if (req.get('X-Parse-Webhook-Key') === 'hook') { + res.json({ success: 'correct key provided' }); } else { - res.json({ error: "incorrect key provided" }); + res.json({ error: 'incorrect key provided' }); } }); Parse.Hooks.createFunction( - "SOME_TEST_FUNCTION", - hookServerURL + "/ExpectingKeyAlso" + 'SOME_TEST_FUNCTION', + hookServerURL + '/ExpectingKeyAlso' ) .then( function () { - return Parse.Cloud.run("SOME_TEST_FUNCTION"); + return Parse.Cloud.run('SOME_TEST_FUNCTION'); }, err => { jfail(err); - fail("Should not fail creating a function"); + fail('Should not fail creating a function'); done(); } ) .then( function () { - fail("Should not succeed calling that function"); + fail('Should not succeed calling that function'); done(); }, err => { @@ -623,7 +623,7 @@ describe("Hooks", () => { expect(err).not.toBe(null); if (err) { expect(err.code).toBe(Parse.Error.SCRIPT_FAILED); - expect(err.message).toEqual("incorrect key provided"); + expect(err.message).toEqual('incorrect key provided'); } done(); } @@ -632,26 +632,26 @@ describe("Hooks", () => { } ); - it_id("21decb65-4b93-4791-85a3-ab124a9ea3ac")(it)( - "should run the beforeSave hook on the test server", + it_id('21decb65-4b93-4791-85a3-ab124a9ea3ac')(it)( + 'should run the beforeSave hook on the test server', done => { let triggerCount = 0; - app.post("/BeforeSaveSome", function (req, res) { + app.post('/BeforeSaveSome', function (req, res) { triggerCount++; const object = req.body.object; - object.hello = "world"; + object.hello = 'world'; // Would need parse cloud express to set much more // But this should override the key upon return res.json({ success: object }); }); // The function is deleted as the DB is dropped between calls Parse.Hooks.createTrigger( - "SomeRandomObject", - "beforeSave", - hookServerURL + "/BeforeSaveSome" + 'SomeRandomObject', + 'beforeSave', + hookServerURL + '/BeforeSaveSome' ) .then(function () { - const obj = new Parse.Object("SomeRandomObject"); + const obj = new Parse.Object('SomeRandomObject'); return obj.save(); }) .then(function (res) { @@ -659,39 +659,39 @@ describe("Hooks", () => { return res.fetch(); }) .then(function (res) { - expect(res.get("hello")).toEqual("world"); + expect(res.get('hello')).toEqual('world'); done(); }) .catch(err => { jfail(err); - fail("Should not fail creating a function"); + fail('Should not fail creating a function'); done(); }); } ); - it_id("52e3152b-5514-4418-9e76-1f394368b8fb")(it)( - "beforeSave hooks should correctly handle responses containing entire object", + it_id('52e3152b-5514-4418-9e76-1f394368b8fb')(it)( + 'beforeSave hooks should correctly handle responses containing entire object', done => { - app.post("/BeforeSaveSome2", function (req, res) { + app.post('/BeforeSaveSome2', function (req, res) { const object = Parse.Object.fromJSON(req.body.object); - object.set("hello", "world"); + object.set('hello', 'world'); res.json({ success: object }); }); Parse.Hooks.createTrigger( - "SomeRandomObject2", - "beforeSave", - hookServerURL + "/BeforeSaveSome2" + 'SomeRandomObject2', + 'beforeSave', + hookServerURL + '/BeforeSaveSome2' ) .then(function () { - const obj = new Parse.Object("SomeRandomObject2"); + const obj = new Parse.Object('SomeRandomObject2'); return obj.save(); }) .then(function (res) { return res.save(); }) .then(function (res) { - expect(res.get("hello")).toEqual("world"); + expect(res.get('hello')).toEqual('world'); done(); }) .catch(err => { @@ -701,15 +701,15 @@ describe("Hooks", () => { } ); - it_id("d27a7587-abb5-40d5-9805-051ee91de474")(it)( - "should run the afterSave hook on the test server", + it_id('d27a7587-abb5-40d5-9805-051ee91de474')(it)( + 'should run the afterSave hook on the test server', done => { let triggerCount = 0; let newObjectId; - app.post("/AfterSaveSome", function (req, res) { + app.post('/AfterSaveSome', function (req, res) { triggerCount++; - const obj = new Parse.Object("AnotherObject"); - obj.set("foo", "bar"); + const obj = new Parse.Object('AnotherObject'); + obj.set('foo', 'bar'); obj.save().then(function (obj) { newObjectId = obj.id; res.json({ success: {} }); @@ -717,43 +717,43 @@ describe("Hooks", () => { }); // The function is deleted as the DB is dropped between calls Parse.Hooks.createTrigger( - "SomeRandomObject", - "afterSave", - hookServerURL + "/AfterSaveSome" + 'SomeRandomObject', + 'afterSave', + hookServerURL + '/AfterSaveSome' ) .then(function () { - const obj = new Parse.Object("SomeRandomObject"); + const obj = new Parse.Object('SomeRandomObject'); return obj.save(); }) .then(function () { return new Promise(resolve => { setTimeout(() => { expect(triggerCount).toBe(1); - new Parse.Query("AnotherObject") + new Parse.Query('AnotherObject') .get(newObjectId) .then(r => resolve(r)); }, 500); }); }) .then(function (res) { - expect(res.get("foo")).toEqual("bar"); + expect(res.get('foo')).toEqual('bar'); done(); }) .catch(err => { jfail(err); - fail("Should not fail creating a function"); + fail('Should not fail creating a function'); done(); }); } ); }); -describe("triggers", () => { - it("should produce a proper request object with context in beforeSave", () => { - const config = Config.get("test"); +describe('triggers', () => { + it('should produce a proper request object with context in beforeSave', () => { + const config = Config.get('test'); const master = auth.master(config); const context = { - originalKey: "original", + originalKey: 'original', }; const req = triggers.getRequestObject( triggers.Types.beforeSave, @@ -763,19 +763,19 @@ describe("triggers", () => { config, context ); - expect(req.context.originalKey).toBe("original"); + expect(req.context.originalKey).toBe('original'); req.context = { - key: "value", + key: 'value', }; expect(context.key).toBe(undefined); req.context = { - key: "newValue", + key: 'newValue', }; expect(context.key).toBe(undefined); }); - it("should produce a proper request object with context in afterSave", () => { - const config = Config.get("test"); + it('should produce a proper request object with context in afterSave', () => { + const config = Config.get('test'); const master = auth.master(config); const context = {}; const req = triggers.getRequestObject( @@ -789,8 +789,8 @@ describe("triggers", () => { expect(req.context).not.toBeUndefined(); }); - it("should not set context on beforeFind", () => { - const config = Config.get("test"); + it('should not set context on beforeFind', () => { + const config = Config.get('test'); const master = auth.master(config); const context = {}; const req = triggers.getRequestObject( @@ -805,13 +805,13 @@ describe("triggers", () => { }); }); -describe("sanitizing names", () => { +describe('sanitizing names', () => { const invalidNames = [ `test'%3bdeclare%20@q%20varchar(99)%3bset%20@q%3d'%5c%5cxxxxxxxxxxxxxxx.yyyyy'%2b'fy.com%5cxus'%3b%20exec%20master.dbo.xp_dirtree%20@q%3b--%20`, `test.function.name`, ]; - it("should not crash server and return error on invalid Cloud Function name", async () => { + it('should not crash server and return error on invalid Cloud Function name', async () => { for (const invalidName of invalidNames) { let error; try { @@ -824,7 +824,7 @@ describe("sanitizing names", () => { } }); - it("should not crash server and return error on invalid Cloud Job name", async () => { + it('should not crash server and return error on invalid Cloud Job name', async () => { for (const invalidName of invalidNames) { let error; try { diff --git a/spec/ParseInstallation.spec.js b/spec/ParseInstallation.spec.js index 8de0ccc598..23baca454f 100644 --- a/spec/ParseInstallation.spec.js +++ b/spec/ParseInstallation.spec.js @@ -1,17 +1,17 @@ -"use strict"; +'use strict'; // These tests check the Installations functionality of the REST API. // Ported from installation_collection_test.go -const auth = require("../lib/Auth"); -const Config = require("../lib/Config"); -const Parse = require("parse/node").Parse; -const rest = require("../lib/rest"); -const request = require("../lib/request"); +const auth = require('../lib/Auth'); +const Config = require('../lib/Config'); +const Parse = require('parse/node').Parse; +const rest = require('../lib/rest'); +const request = require('../lib/request'); let config; let database; const defaultColumns = - require("../lib/Controllers/SchemaController").defaultColumns; + require('../lib/Controllers/SchemaController').defaultColumns; const delay = function delay(delay) { return new Promise(resolve => setTimeout(resolve, delay)); @@ -25,23 +25,23 @@ const installationSchema = { ), }; -describe("Installations", () => { +describe('Installations', () => { beforeEach(() => { - config = Config.get("test"); + config = Config.get('test'); database = config.database; }); - it("creates an android installation with ids", done => { - const installId = "12345678-abcd-abcd-abcd-123456789abc"; - const device = "android"; + it('creates an android installation with ids', done => { + const installId = '12345678-abcd-abcd-abcd-123456789abc'; + const device = 'android'; const input = { installationId: installId, deviceType: device, }; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(() => - database.adapter.find("_Installation", installationSchema, {}, {}) + database.adapter.find('_Installation', installationSchema, {}, {}) ) .then(results => { expect(results.length).toEqual(1); @@ -57,18 +57,18 @@ describe("Installations", () => { }); }); - it("creates an ios installation with ids", done => { + it('creates an ios installation with ids', done => { const t = - "11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306"; - const device = "ios"; + '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; + const device = 'ios'; const input = { deviceToken: t, deviceType: device, }; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(() => - database.adapter.find("_Installation", installationSchema, {}, {}) + database.adapter.find('_Installation', installationSchema, {}, {}) ) .then(results => { expect(results.length).toEqual(1); @@ -84,17 +84,17 @@ describe("Installations", () => { }); }); - it("creates an embedded installation with ids", done => { - const installId = "12345678-abcd-abcd-abcd-123456789abc"; - const device = "embedded"; + it('creates an embedded installation with ids', done => { + const installId = '12345678-abcd-abcd-abcd-123456789abc'; + const device = 'embedded'; const input = { installationId: installId, deviceType: device, }; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(() => - database.adapter.find("_Installation", installationSchema, {}, {}) + database.adapter.find('_Installation', installationSchema, {}, {}) ) .then(results => { expect(results.length).toEqual(1); @@ -110,28 +110,28 @@ describe("Installations", () => { }); }); - it("creates an android installation with all fields", done => { - const installId = "12345678-abcd-abcd-abcd-123456789abc"; - const device = "android"; + it('creates an android installation with all fields', done => { + const installId = '12345678-abcd-abcd-abcd-123456789abc'; + const device = 'android'; const input = { installationId: installId, deviceType: device, - channels: ["foo", "bar"], + channels: ['foo', 'bar'], }; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(() => - database.adapter.find("_Installation", installationSchema, {}, {}) + database.adapter.find('_Installation', installationSchema, {}, {}) ) .then(results => { expect(results.length).toEqual(1); const obj = results[0]; expect(obj.installationId).toEqual(installId); expect(obj.deviceType).toEqual(device); - expect(typeof obj.channels).toEqual("object"); + expect(typeof obj.channels).toEqual('object'); expect(obj.channels.length).toEqual(2); - expect(obj.channels[0]).toEqual("foo"); - expect(obj.channels[1]).toEqual("bar"); + expect(obj.channels[0]).toEqual('foo'); + expect(obj.channels[1]).toEqual('bar'); done(); }) .catch(error => { @@ -141,29 +141,29 @@ describe("Installations", () => { }); }); - it("creates an ios installation with all fields", done => { + it('creates an ios installation with all fields', done => { const t = - "11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306"; - const device = "ios"; + '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; + const device = 'ios'; const input = { deviceToken: t, deviceType: device, - channels: ["foo", "bar"], + channels: ['foo', 'bar'], }; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(() => - database.adapter.find("_Installation", installationSchema, {}, {}) + database.adapter.find('_Installation', installationSchema, {}, {}) ) .then(results => { expect(results.length).toEqual(1); const obj = results[0]; expect(obj.deviceToken).toEqual(t); expect(obj.deviceType).toEqual(device); - expect(typeof obj.channels).toEqual("object"); + expect(typeof obj.channels).toEqual('object'); expect(obj.channels.length).toEqual(2); - expect(obj.channels[0]).toEqual("foo"); - expect(obj.channels[1]).toEqual("bar"); + expect(obj.channels[0]).toEqual('foo'); + expect(obj.channels[1]).toEqual('bar'); done(); }) .catch(error => { @@ -173,21 +173,21 @@ describe("Installations", () => { }); }); - it("should properly fail queying installations", done => { - const installId = "12345678-abcd-abcd-abcd-123456789abc"; - const device = "android"; + it('should properly fail queying installations', done => { + const installId = '12345678-abcd-abcd-abcd-123456789abc'; + const device = 'android'; const input = { installationId: installId, deviceType: device, }; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(() => { const query = new Parse.Query(Parse.Installation); return query.find(); }) .then(() => { - fail("Should not succeed!"); + fail('Should not succeed!'); done(); }) .catch(error => { @@ -199,15 +199,15 @@ describe("Installations", () => { }); }); - it("should properly queying installations with masterKey", done => { - const installId = "12345678-abcd-abcd-abcd-123456789abc"; - const device = "android"; + it('should properly queying installations with masterKey', done => { + const installId = '12345678-abcd-abcd-abcd-123456789abc'; + const device = 'android'; const input = { installationId: installId, deviceType: device, }; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(() => { const query = new Parse.Query(Parse.Installation); return query.find({ useMasterKey: true }); @@ -220,20 +220,20 @@ describe("Installations", () => { done(); }) .catch(() => { - fail("Should not fail"); + fail('Should not fail'); done(); }); }); - it("fails with missing ids", done => { + it('fails with missing ids', done => { const input = { - deviceType: "android", - channels: ["foo", "bar"], + deviceType: 'android', + channels: ['foo', 'bar'], }; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(() => { - fail("Should not have been able to create an Installation."); + fail('Should not have been able to create an Installation.'); done(); }) .catch(error => { @@ -242,16 +242,16 @@ describe("Installations", () => { }); }); - it("fails for android with missing type", done => { - const installId = "12345678-abcd-abcd-abcd-123456789abc"; + it('fails for android with missing type', done => { + const installId = '12345678-abcd-abcd-abcd-123456789abc'; const input = { installationId: installId, - channels: ["foo", "bar"], + channels: ['foo', 'bar'], }; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(() => { - fail("Should not have been able to create an Installation."); + fail('Should not have been able to create an Installation.'); done(); }) .catch(error => { @@ -260,24 +260,24 @@ describe("Installations", () => { }); }); - it("creates an object with custom fields", done => { + it('creates an object with custom fields', done => { const t = - "11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306"; + '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; const input = { deviceToken: t, - deviceType: "ios", - channels: ["foo", "bar"], - custom: "allowed", + deviceType: 'ios', + channels: ['foo', 'bar'], + custom: 'allowed', }; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(() => - database.adapter.find("_Installation", installationSchema, {}, {}) + database.adapter.find('_Installation', installationSchema, {}, {}) ) .then(results => { expect(results.length).toEqual(1); const obj = results[0]; - expect(obj.custom).toEqual("allowed"); + expect(obj.custom).toEqual('allowed'); done(); }) .catch(error => { @@ -287,40 +287,40 @@ describe("Installations", () => { // Note: did not port test 'TestObjectIDForIdentifiers' - it("merging when installationId already exists", done => { - const installId1 = "12345678-abcd-abcd-abcd-123456789abc"; + it('merging when installationId already exists', done => { + const installId1 = '12345678-abcd-abcd-abcd-123456789abc'; const t = - "11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306"; + '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; const input = { deviceToken: t, - deviceType: "ios", + deviceType: 'ios', installationId: installId1, - channels: ["foo", "bar"], + channels: ['foo', 'bar'], }; let firstObject; let secondObject; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(() => - database.adapter.find("_Installation", installationSchema, {}, {}) + database.adapter.find('_Installation', installationSchema, {}, {}) ) .then(results => { expect(results.length).toEqual(1); firstObject = results[0]; delete input.deviceToken; delete input.channels; - input["foo"] = "bar"; - return rest.create(config, auth.nobody(config), "_Installation", input); + input['foo'] = 'bar'; + return rest.create(config, auth.nobody(config), '_Installation', input); }) .then(() => - database.adapter.find("_Installation", installationSchema, {}, {}) + database.adapter.find('_Installation', installationSchema, {}, {}) ) .then(results => { expect(results.length).toEqual(1); secondObject = results[0]; expect(firstObject._id).toEqual(secondObject._id); expect(secondObject.channels.length).toEqual(2); - expect(secondObject.foo).toEqual("bar"); + expect(secondObject.foo).toEqual('bar'); done(); }) .catch(error => { @@ -328,29 +328,29 @@ describe("Installations", () => { }); }); - it("merging when two objects both only have one id", done => { - const installId = "12345678-abcd-abcd-abcd-123456789abc"; + it('merging when two objects both only have one id', done => { + const installId = '12345678-abcd-abcd-abcd-123456789abc'; const t = - "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; const input1 = { installationId: installId, - deviceType: "ios", + deviceType: 'ios', }; const input2 = { deviceToken: t, - deviceType: "ios", + deviceType: 'ios', }; const input3 = { deviceToken: t, installationId: installId, - deviceType: "ios", + deviceType: 'ios', }; let firstObject; let secondObject; rest - .create(config, auth.nobody(config), "_Installation", input1) + .create(config, auth.nobody(config), '_Installation', input1) .then(() => - database.adapter.find("_Installation", installationSchema, {}, {}) + database.adapter.find('_Installation', installationSchema, {}, {}) ) .then(results => { expect(results.length).toEqual(1); @@ -358,16 +358,16 @@ describe("Installations", () => { return rest.create( config, auth.nobody(config), - "_Installation", + '_Installation', input2 ); }) .then(() => - database.adapter.find("_Installation", installationSchema, {}, {}) + database.adapter.find('_Installation', installationSchema, {}, {}) ) .then(results => { expect(results.length).toEqual(2); - if (results[0]["_id"] == firstObject._id) { + if (results[0]['_id'] == firstObject._id) { secondObject = results[1]; } else { secondObject = results[0]; @@ -375,16 +375,16 @@ describe("Installations", () => { return rest.create( config, auth.nobody(config), - "_Installation", + '_Installation', input3 ); }) .then(() => - database.adapter.find("_Installation", installationSchema, {}, {}) + database.adapter.find('_Installation', installationSchema, {}, {}) ) .then(results => { expect(results.length).toEqual(1); - expect(results[0]["_id"]).toEqual(secondObject._id); + expect(results[0]['_id']).toEqual(secondObject._id); done(); }) .catch(error => { @@ -393,30 +393,30 @@ describe("Installations", () => { }); }); - xit("creating multiple devices with same device token works", done => { - const installId1 = "11111111-abcd-abcd-abcd-123456789abc"; - const installId2 = "22222222-abcd-abcd-abcd-123456789abc"; - const installId3 = "33333333-abcd-abcd-abcd-123456789abc"; + xit('creating multiple devices with same device token works', done => { + const installId1 = '11111111-abcd-abcd-abcd-123456789abc'; + const installId2 = '22222222-abcd-abcd-abcd-123456789abc'; + const installId3 = '33333333-abcd-abcd-abcd-123456789abc'; const t = - "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; const input = { installationId: installId1, - deviceType: "ios", + deviceType: 'ios', deviceToken: t, }; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(() => { input.installationId = installId2; - return rest.create(config, auth.nobody(config), "_Installation", input); + return rest.create(config, auth.nobody(config), '_Installation', input); }) .then(() => { input.installationId = installId3; - return rest.create(config, auth.nobody(config), "_Installation", input); + return rest.create(config, auth.nobody(config), '_Installation', input); }) .then(() => database.adapter.find( - "_Installation", + '_Installation', { installationId: installId1 }, installationSchema, {} @@ -425,7 +425,7 @@ describe("Installations", () => { .then(results => { expect(results.length).toEqual(1); return database.adapter.find( - "_Installation", + '_Installation', { installationId: installId2 }, installationSchema, {} @@ -434,7 +434,7 @@ describe("Installations", () => { .then(results => { expect(results.length).toEqual(1); return database.adapter.find( - "_Installation", + '_Installation', { installationId: installId3 }, installationSchema, {} @@ -449,40 +449,40 @@ describe("Installations", () => { }); }); - it_id("95955e90-04bc-4437-920e-b84bc30dba01")(it)( - "updating with new channels", + it_id('95955e90-04bc-4437-920e-b84bc30dba01')(it)( + 'updating with new channels', done => { const input = { - installationId: "12345678-abcd-abcd-abcd-123456789abc", - deviceType: "android", - channels: ["foo", "bar"], + installationId: '12345678-abcd-abcd-abcd-123456789abc', + deviceType: 'android', + channels: ['foo', 'bar'], }; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(() => - database.adapter.find("_Installation", installationSchema, {}, {}) + database.adapter.find('_Installation', installationSchema, {}, {}) ) .then(results => { expect(results.length).toEqual(1); const objectId = results[0].objectId; const update = { - channels: ["baz"], + channels: ['baz'], }; return rest.update( config, auth.nobody(config), - "_Installation", + '_Installation', { objectId }, update ); }) .then(() => - database.adapter.find("_Installation", installationSchema, {}, {}) + database.adapter.find('_Installation', installationSchema, {}, {}) ) .then(results => { expect(results.length).toEqual(1); expect(results[0].channels.length).toEqual(1); - expect(results[0].channels[0]).toEqual("baz"); + expect(results[0].channels[0]).toEqual('baz'); done(); }) .catch(error => { @@ -492,18 +492,18 @@ describe("Installations", () => { } ); - it("update android fails with new installation id", done => { - const installId1 = "12345678-abcd-abcd-abcd-123456789abc"; - const installId2 = "87654321-abcd-abcd-abcd-123456789abc"; + it('update android fails with new installation id', done => { + const installId1 = '12345678-abcd-abcd-abcd-123456789abc'; + const installId2 = '87654321-abcd-abcd-abcd-123456789abc'; let input = { installationId: installId1, - deviceType: "android", - channels: ["foo", "bar"], + deviceType: 'android', + channels: ['foo', 'bar'], }; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(() => - database.adapter.find("_Installation", installationSchema, {}, {}) + database.adapter.find('_Installation', installationSchema, {}, {}) ) .then(results => { expect(results.length).toEqual(1); @@ -511,13 +511,13 @@ describe("Installations", () => { return rest.update( config, auth.nobody(config), - "_Installation", + '_Installation', { objectId: results[0].objectId }, input ); }) .then(() => { - fail("Updating the installation should have failed."); + fail('Updating the installation should have failed.'); done(); }) .catch(error => { @@ -526,20 +526,20 @@ describe("Installations", () => { }); }); - it("update ios fails with new deviceToken and no installationId", done => { + it('update ios fails with new deviceToken and no installationId', done => { const a = - "11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306"; + '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; const b = - "91433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306"; + '91433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; let input = { deviceToken: a, - deviceType: "ios", - channels: ["foo", "bar"], + deviceType: 'ios', + channels: ['foo', 'bar'], }; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(() => - database.adapter.find("_Installation", installationSchema, {}, {}) + database.adapter.find('_Installation', installationSchema, {}, {}) ) .then(results => { expect(results.length).toEqual(1); @@ -547,13 +547,13 @@ describe("Installations", () => { return rest.update( config, auth.nobody(config), - "_Installation", + '_Installation', { objectId: results[0].objectId }, input ); }) .then(() => { - fail("Updating the installation should have failed."); + fail('Updating the installation should have failed.'); }) .catch(error => { expect(error.code).toEqual(136); @@ -561,40 +561,40 @@ describe("Installations", () => { }); }); - it("update ios updates device token", done => { - const installId = "12345678-abcd-abcd-abcd-123456789abc"; + it('update ios updates device token', done => { + const installId = '12345678-abcd-abcd-abcd-123456789abc'; const t = - "11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306"; + '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; const u = - "91433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306"; + '91433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; let input = { installationId: installId, - deviceType: "ios", + deviceType: 'ios', deviceToken: t, - channels: ["foo", "bar"], + channels: ['foo', 'bar'], }; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(() => - database.adapter.find("_Installation", installationSchema, {}, {}) + database.adapter.find('_Installation', installationSchema, {}, {}) ) .then(results => { expect(results.length).toEqual(1); input = { installationId: installId, deviceToken: u, - deviceType: "ios", + deviceType: 'ios', }; return rest.update( config, auth.nobody(config), - "_Installation", + '_Installation', { objectId: results[0].objectId }, input ); }) .then(() => - database.adapter.find("_Installation", installationSchema, {}, {}) + database.adapter.find('_Installation', installationSchema, {}, {}) ) .then(results => { expect(results.length).toEqual(1); @@ -607,33 +607,33 @@ describe("Installations", () => { }); }); - it("update fails to change deviceType", done => { - const installId = "12345678-abcd-abcd-abcd-123456789abc"; + it('update fails to change deviceType', done => { + const installId = '12345678-abcd-abcd-abcd-123456789abc'; let input = { installationId: installId, - deviceType: "android", - channels: ["foo", "bar"], + deviceType: 'android', + channels: ['foo', 'bar'], }; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(() => - database.adapter.find("_Installation", installationSchema, {}, {}) + database.adapter.find('_Installation', installationSchema, {}, {}) ) .then(results => { expect(results.length).toEqual(1); input = { - deviceType: "ios", + deviceType: 'ios', }; return rest.update( config, auth.nobody(config), - "_Installation", + '_Installation', { objectId: results[0].objectId }, input ); }) .then(() => { - fail("Should not have been able to update Installation."); + fail('Should not have been able to update Installation.'); done(); }) .catch(error => { @@ -642,63 +642,63 @@ describe("Installations", () => { }); }); - it("update android with custom field", done => { - const installId = "12345678-abcd-abcd-abcd-123456789abc"; + it('update android with custom field', done => { + const installId = '12345678-abcd-abcd-abcd-123456789abc'; let input = { installationId: installId, - deviceType: "android", - channels: ["foo", "bar"], + deviceType: 'android', + channels: ['foo', 'bar'], }; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(() => - database.adapter.find("_Installation", installationSchema, {}, {}) + database.adapter.find('_Installation', installationSchema, {}, {}) ) .then(results => { expect(results.length).toEqual(1); input = { - custom: "allowed", + custom: 'allowed', }; return rest.update( config, auth.nobody(config), - "_Installation", + '_Installation', { objectId: results[0].objectId }, input ); }) .then(() => - database.adapter.find("_Installation", installationSchema, {}, {}) + database.adapter.find('_Installation', installationSchema, {}, {}) ) .then(results => { expect(results.length).toEqual(1); - expect(results[0]["custom"]).toEqual("allowed"); + expect(results[0]['custom']).toEqual('allowed'); done(); }); }); - it("update android device token with duplicate device token", async () => { - const installId1 = "11111111-abcd-abcd-abcd-123456789abc"; - const installId2 = "22222222-abcd-abcd-abcd-123456789abc"; + it('update android device token with duplicate device token', async () => { + const installId1 = '11111111-abcd-abcd-abcd-123456789abc'; + const installId2 = '22222222-abcd-abcd-abcd-123456789abc'; const t = - "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; let input = { installationId: installId1, deviceToken: t, - deviceType: "android", + deviceType: 'android', }; - await rest.create(config, auth.nobody(config), "_Installation", input); + await rest.create(config, auth.nobody(config), '_Installation', input); input = { installationId: installId2, - deviceType: "android", + deviceType: 'android', }; - await rest.create(config, auth.nobody(config), "_Installation", input); + await rest.create(config, auth.nobody(config), '_Installation', input); await delay(100); let results = await database.adapter.find( - "_Installation", + '_Installation', installationSchema, { installationId: installId1 }, {} @@ -707,7 +707,7 @@ describe("Installations", () => { const firstObject = results[0]; results = await database.adapter.find( - "_Installation", + '_Installation', installationSchema, { installationId: installId2 }, {} @@ -723,13 +723,13 @@ describe("Installations", () => { await rest.update( config, auth.nobody(config), - "_Installation", + '_Installation', { objectId: secondObject.objectId }, input ); await delay(100); results = await database.adapter.find( - "_Installation", + '_Installation', installationSchema, { objectId: firstObject.objectId }, {} @@ -737,31 +737,31 @@ describe("Installations", () => { expect(results.length).toEqual(0); }); - it("update ios device token with duplicate device token", done => { - const installId1 = "11111111-abcd-abcd-abcd-123456789abc"; - const installId2 = "22222222-abcd-abcd-abcd-123456789abc"; + it('update ios device token with duplicate device token', done => { + const installId1 = '11111111-abcd-abcd-abcd-123456789abc'; + const installId2 = '22222222-abcd-abcd-abcd-123456789abc'; const t = - "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; let input = { installationId: installId1, deviceToken: t, - deviceType: "ios", + deviceType: 'ios', }; let firstObject; let secondObject; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(() => { input = { installationId: installId2, - deviceType: "ios", + deviceType: 'ios', }; - return rest.create(config, auth.nobody(config), "_Installation", input); + return rest.create(config, auth.nobody(config), '_Installation', input); }) .then(() => delay(100)) .then(() => database.adapter.find( - "_Installation", + '_Installation', installationSchema, { installationId: installId1 }, {} @@ -774,7 +774,7 @@ describe("Installations", () => { .then(() => delay(100)) .then(() => database.adapter.find( - "_Installation", + '_Installation', installationSchema, { installationId: installId2 }, {} @@ -791,7 +791,7 @@ describe("Installations", () => { return rest.update( config, auth.nobody(config), - "_Installation", + '_Installation', { objectId: secondObject.objectId }, input ); @@ -799,7 +799,7 @@ describe("Installations", () => { .then(() => delay(100)) .then(() => database.adapter.find( - "_Installation", + '_Installation', installationSchema, { objectId: firstObject.objectId }, {} @@ -816,26 +816,26 @@ describe("Installations", () => { }); }); - xit("update ios device token with duplicate token different app", done => { - const installId1 = "11111111-abcd-abcd-abcd-123456789abc"; - const installId2 = "22222222-abcd-abcd-abcd-123456789abc"; + xit('update ios device token with duplicate token different app', done => { + const installId1 = '11111111-abcd-abcd-abcd-123456789abc'; + const installId2 = '22222222-abcd-abcd-abcd-123456789abc'; const t = - "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; const input = { installationId: installId1, deviceToken: t, - deviceType: "ios", - appIdentifier: "foo", + deviceType: 'ios', + appIdentifier: 'foo', }; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(() => { input.installationId = installId2; - input.appIdentifier = "bar"; - return rest.create(config, auth.nobody(config), "_Installation", input); + input.appIdentifier = 'bar'; + return rest.create(config, auth.nobody(config), '_Installation', input); }) .then(() => - database.adapter.find("_Installation", installationSchema, {}, {}) + database.adapter.find('_Installation', installationSchema, {}, {}) ) .then(results => { // The first object should have been deleted during merge @@ -849,18 +849,18 @@ describe("Installations", () => { }); }); - it("update ios token and channels", done => { - const installId = "12345678-abcd-abcd-abcd-123456789abc"; + it('update ios token and channels', done => { + const installId = '12345678-abcd-abcd-abcd-123456789abc'; const t = - "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; let input = { installationId: installId, - deviceType: "ios", + deviceType: 'ios', }; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(() => - database.adapter.find("_Installation", installationSchema, {}, {}) + database.adapter.find('_Installation', installationSchema, {}, {}) ) .then(results => { expect(results.length).toEqual(1); @@ -871,13 +871,13 @@ describe("Installations", () => { return rest.update( config, auth.nobody(config), - "_Installation", + '_Installation', { objectId: results[0].objectId }, input ); }) .then(() => - database.adapter.find("_Installation", installationSchema, {}, {}) + database.adapter.find('_Installation', installationSchema, {}, {}) ) .then(results => { expect(results.length).toEqual(1); @@ -892,26 +892,26 @@ describe("Installations", () => { }); }); - it("update ios linking two existing objects", done => { - const installId = "12345678-abcd-abcd-abcd-123456789abc"; + it('update ios linking two existing objects', done => { + const installId = '12345678-abcd-abcd-abcd-123456789abc'; const t = - "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; let input = { installationId: installId, - deviceType: "ios", + deviceType: 'ios', }; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(() => { input = { deviceToken: t, - deviceType: "ios", + deviceType: 'ios', }; - return rest.create(config, auth.nobody(config), "_Installation", input); + return rest.create(config, auth.nobody(config), '_Installation', input); }) .then(() => database.adapter.find( - "_Installation", + '_Installation', installationSchema, { deviceToken: t }, {} @@ -922,24 +922,24 @@ describe("Installations", () => { input = { deviceToken: t, installationId: installId, - deviceType: "ios", + deviceType: 'ios', }; return rest.update( config, auth.nobody(config), - "_Installation", + '_Installation', { objectId: results[0].objectId }, input ); }) .then(() => - database.adapter.find("_Installation", installationSchema, {}, {}) + database.adapter.find('_Installation', installationSchema, {}, {}) ) .then(results => { expect(results.length).toEqual(1); expect(results[0].installationId).toEqual(installId); expect(results[0].deviceToken).toEqual(t); - expect(results[0].deviceType).toEqual("ios"); + expect(results[0].deviceType).toEqual('ios'); done(); }) .catch(error => { @@ -948,33 +948,33 @@ describe("Installations", () => { }); }); - it_id("22311bc7-3f4f-42c1-a958-57083929e80d")(it)( - "update is linking two existing objects w/ increment", + it_id('22311bc7-3f4f-42c1-a958-57083929e80d')(it)( + 'update is linking two existing objects w/ increment', done => { - const installId = "12345678-abcd-abcd-abcd-123456789abc"; + const installId = '12345678-abcd-abcd-abcd-123456789abc'; const t = - "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; let input = { installationId: installId, - deviceType: "ios", + deviceType: 'ios', }; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(() => { input = { deviceToken: t, - deviceType: "ios", + deviceType: 'ios', }; return rest.create( config, auth.nobody(config), - "_Installation", + '_Installation', input ); }) .then(() => database.adapter.find( - "_Installation", + '_Installation', installationSchema, { deviceToken: t }, {} @@ -985,28 +985,28 @@ describe("Installations", () => { input = { deviceToken: t, installationId: installId, - deviceType: "ios", + deviceType: 'ios', score: { - __op: "Increment", + __op: 'Increment', amount: 1, }, }; return rest.update( config, auth.nobody(config), - "_Installation", + '_Installation', { objectId: results[0].objectId }, input ); }) .then(() => - database.adapter.find("_Installation", installationSchema, {}, {}) + database.adapter.find('_Installation', installationSchema, {}, {}) ) .then(results => { expect(results.length).toEqual(1); expect(results[0].installationId).toEqual(installId); expect(results[0].deviceToken).toEqual(t); - expect(results[0].deviceType).toEqual("ios"); + expect(results[0].deviceType).toEqual('ios'); expect(results[0].score).toEqual(1); done(); }) @@ -1017,33 +1017,33 @@ describe("Installations", () => { } ); - it("update is linking two existing with installation id", done => { - const installId = "12345678-abcd-abcd-abcd-123456789abc"; + it('update is linking two existing with installation id', done => { + const installId = '12345678-abcd-abcd-abcd-123456789abc'; const t = - "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; let input = { installationId: installId, - deviceType: "ios", + deviceType: 'ios', }; let installObj; let tokenObj; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(() => - database.adapter.find("_Installation", installationSchema, {}, {}) + database.adapter.find('_Installation', installationSchema, {}, {}) ) .then(results => { expect(results.length).toEqual(1); installObj = results[0]; input = { deviceToken: t, - deviceType: "ios", + deviceType: 'ios', }; - return rest.create(config, auth.nobody(config), "_Installation", input); + return rest.create(config, auth.nobody(config), '_Installation', input); }) .then(() => database.adapter.find( - "_Installation", + '_Installation', installationSchema, { deviceToken: t }, {} @@ -1055,19 +1055,19 @@ describe("Installations", () => { input = { installationId: installId, deviceToken: t, - deviceType: "ios", + deviceType: 'ios', }; return rest.update( config, auth.nobody(config), - "_Installation", + '_Installation', { objectId: installObj.objectId }, input ); }) .then(() => database.adapter.find( - "_Installation", + '_Installation', installationSchema, { objectId: tokenObj.objectId }, {} @@ -1085,40 +1085,40 @@ describe("Installations", () => { }); }); - it_id("f2975078-eab7-4287-a932-288842e3cfb9")(it)( - "update is linking two existing with installation id w/ op", + it_id('f2975078-eab7-4287-a932-288842e3cfb9')(it)( + 'update is linking two existing with installation id w/ op', done => { - const installId = "12345678-abcd-abcd-abcd-123456789abc"; + const installId = '12345678-abcd-abcd-abcd-123456789abc'; const t = - "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; let input = { installationId: installId, - deviceType: "ios", + deviceType: 'ios', }; let installObj; let tokenObj; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(() => - database.adapter.find("_Installation", installationSchema, {}, {}) + database.adapter.find('_Installation', installationSchema, {}, {}) ) .then(results => { expect(results.length).toEqual(1); installObj = results[0]; input = { deviceToken: t, - deviceType: "ios", + deviceType: 'ios', }; return rest.create( config, auth.nobody(config), - "_Installation", + '_Installation', input ); }) .then(() => database.adapter.find( - "_Installation", + '_Installation', installationSchema, { deviceToken: t }, {} @@ -1130,23 +1130,23 @@ describe("Installations", () => { input = { installationId: installId, deviceToken: t, - deviceType: "ios", + deviceType: 'ios', score: { - __op: "Increment", + __op: 'Increment', amount: 1, }, }; return rest.update( config, auth.nobody(config), - "_Installation", + '_Installation', { objectId: installObj.objectId }, input ); }) .then(() => database.adapter.find( - "_Installation", + '_Installation', installationSchema, { objectId: tokenObj.objectId }, {} @@ -1166,7 +1166,7 @@ describe("Installations", () => { } ); - it("ios merge existing same token no installation id", done => { + it('ios merge existing same token no installation id', done => { // Test creating installation when there is an existing object with the // same device token but no installation ID. This is possible when // developers import device tokens from another push provider; the import @@ -1178,28 +1178,28 @@ describe("Installations", () => { // object in case the developer already added additional fields via Data // Browser or REST API (e.g. channel targeting info). const t = - "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; - const installId = "12345678-abcd-abcd-abcd-123456789abc"; + '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; + const installId = '12345678-abcd-abcd-abcd-123456789abc'; let input = { deviceToken: t, - deviceType: "ios", + deviceType: 'ios', }; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(() => - database.adapter.find("_Installation", installationSchema, {}, {}) + database.adapter.find('_Installation', installationSchema, {}, {}) ) .then(results => { expect(results.length).toEqual(1); input = { installationId: installId, deviceToken: t, - deviceType: "ios", + deviceType: 'ios', }; - return rest.create(config, auth.nobody(config), "_Installation", input); + return rest.create(config, auth.nobody(config), '_Installation', input); }) .then(() => - database.adapter.find("_Installation", installationSchema, {}, {}) + database.adapter.find('_Installation', installationSchema, {}, {}) ) .then(results => { expect(results.length).toEqual(1); @@ -1214,24 +1214,24 @@ describe("Installations", () => { }); }); - it("allows you to get your own installation (regression test for #1718)", done => { - const installId = "12345678-abcd-abcd-abcd-123456789abc"; - const device = "android"; + it('allows you to get your own installation (regression test for #1718)', done => { + const installId = '12345678-abcd-abcd-abcd-123456789abc'; + const device = 'android'; const input = { installationId: installId, deviceType: device, }; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(createResult => { const headers = { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; return request({ headers: headers, url: - "http://localhost:8378/1/installations/" + + 'http://localhost:8378/1/installations/' + createResult.response.objectId, }).then(response => { const body = response.data; @@ -1241,30 +1241,30 @@ describe("Installations", () => { }) .catch(error => { console.log(error); - fail("failed"); + fail('failed'); done(); }); }); - it("allows you to update installation from header (#2090)", done => { - const installId = "12345678-abcd-abcd-abcd-123456789abc"; - const device = "android"; + it('allows you to update installation from header (#2090)', done => { + const installId = '12345678-abcd-abcd-abcd-123456789abc'; + const device = 'android'; const input = { installationId: installId, deviceType: device, }; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(() => { const headers = { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "X-Parse-Installation-Id": installId, + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Installation-Id': installId, }; request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/classes/_Installation", + url: 'http://localhost:8378/1/classes/_Installation', json: true, body: { date: new Date(), @@ -1278,57 +1278,57 @@ describe("Installations", () => { }) .catch(error => { console.log(error); - fail("failed"); + fail('failed'); done(); }); }); - it("allows you to update installation with masterKey", done => { - const installId = "12345678-abcd-abcd-abcd-123456789abc"; - const device = "android"; + it('allows you to update installation with masterKey', done => { + const installId = '12345678-abcd-abcd-abcd-123456789abc'; + const device = 'android'; const input = { installationId: installId, deviceType: device, }; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(createResult => { const installationObj = Parse.Installation.createWithoutData( createResult.response.objectId ); - installationObj.set("customField", "custom value"); + installationObj.set('customField', 'custom value'); return installationObj.save(null, { useMasterKey: true }); }) .then(updateResult => { expect(updateResult).not.toBeUndefined(); - expect(updateResult.get("customField")).toEqual("custom value"); + expect(updateResult.get('customField')).toEqual('custom value'); done(); }) .catch(error => { console.log(error); - fail("failed"); + fail('failed'); done(); }); }); - it("should properly handle installation save #2780", done => { - const installId = "12345678-abcd-abcd-abcd-123456789abc"; - const device = "android"; + it('should properly handle installation save #2780', done => { + const installId = '12345678-abcd-abcd-abcd-123456789abc'; + const device = 'android'; const input = { installationId: installId, deviceType: device, }; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(() => { const query = new Parse.Query(Parse.Installation); - query.equalTo("installationId", installId); + query.equalTo('installationId', installId); query .first({ useMasterKey: true }) .then(installation => { return installation.save( { - key: "value", + key: 'value', }, { useMasterKey: true } ); @@ -1345,38 +1345,38 @@ describe("Installations", () => { }); }); - it("should properly reject updating installationId", done => { - const installId = "12345678-abcd-abcd-abcd-123456789abc"; - const device = "android"; + it('should properly reject updating installationId', done => { + const installId = '12345678-abcd-abcd-abcd-123456789abc'; + const device = 'android'; const input = { installationId: installId, deviceType: device, }; rest - .create(config, auth.nobody(config), "_Installation", input) + .create(config, auth.nobody(config), '_Installation', input) .then(() => { const query = new Parse.Query(Parse.Installation); - query.equalTo("installationId", installId); + query.equalTo('installationId', installId); query .first({ useMasterKey: true }) .then(installation => { return installation.save( { - key: "value", - installationId: "22222222-abcd-abcd-abcd-123456789abc", + key: 'value', + installationId: '22222222-abcd-abcd-abcd-123456789abc', }, { useMasterKey: true } ); }) .then( () => { - fail("should not succeed"); + fail('should not succeed'); done(); }, err => { expect(err.code).toBe(136); expect(err.message).toBe( - "installationId may not be changed in this operation" + 'installationId may not be changed in this operation' ); done(); } @@ -1384,57 +1384,57 @@ describe("Installations", () => { }); }); - it_id("e581faea-c1b4-4c64-af8c-52287ce6cd06")(it)( - "can use push with beforeSave", + it_id('e581faea-c1b4-4c64-af8c-52287ce6cd06')(it)( + 'can use push with beforeSave', async () => { const input = { deviceToken: - "11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306", - deviceType: "ios", + '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306', + deviceType: 'ios', }; - await rest.create(config, auth.nobody(config), "_Installation", input); + await rest.create(config, auth.nobody(config), '_Installation', input); const functions = { beforeSave() {}, afterSave() {}, }; - spyOn(functions, "beforeSave").and.callThrough(); - spyOn(functions, "afterSave").and.callThrough(); + spyOn(functions, 'beforeSave').and.callThrough(); + spyOn(functions, 'afterSave').and.callThrough(); Parse.Cloud.beforeSave(Parse.Installation, functions.beforeSave); Parse.Cloud.afterSave(Parse.Installation, functions.afterSave); await Parse.Push.send({ where: { - deviceType: "ios", + deviceType: 'ios', }, data: { - badge: "increment", - alert: "Hello world!", + badge: 'increment', + alert: 'Hello world!', }, }); await Parse.Push.send({ where: { - deviceType: "ios", + deviceType: 'ios', }, data: { - badge: "increment", - alert: "Hello world!", + badge: 'increment', + alert: 'Hello world!', }, }); await Parse.Push.send({ where: { - deviceType: "ios", + deviceType: 'ios', }, data: { - badge: "increment", - alert: "Hello world!", + badge: 'increment', + alert: 'Hello world!', }, }); await new Promise(resolve => setTimeout(resolve, 1000)); const installation = await new Parse.Query(Parse.Installation).first({ useMasterKey: true, }); - expect(installation.get("badge")).toEqual(3); + expect(installation.get('badge')).toEqual(3); expect(functions.beforeSave).not.toHaveBeenCalled(); expect(functions.afterSave).not.toHaveBeenCalled(); } diff --git a/spec/ParseLiveQuery.spec.js b/spec/ParseLiveQuery.spec.js index e1709f1245..f5ab808781 100644 --- a/spec/ParseLiveQuery.spec.js +++ b/spec/ParseLiveQuery.spec.js @@ -1,22 +1,22 @@ -"use strict"; -const http = require("http"); -const Auth = require("../lib/Auth"); +'use strict'; +const http = require('http'); +const Auth = require('../lib/Auth'); const UserController = - require("../lib/Controllers/UserController").UserController; -const Config = require("../lib/Config"); -const ParseServer = require("../lib/index").ParseServer; -const triggers = require("../lib/triggers"); + require('../lib/Controllers/UserController').UserController; +const Config = require('../lib/Config'); +const ParseServer = require('../lib/index').ParseServer; +const triggers = require('../lib/triggers'); const { resolvingPromise, sleep, getConnectionsCount, -} = require("../lib/TestUtils"); -const request = require("../lib/request"); +} = require('../lib/TestUtils'); +const request = require('../lib/request'); const validatorFail = () => { - throw "you are not authorized"; + throw 'you are not authorized'; }; -describe("ParseLiveQuery", function () { +describe('ParseLiveQuery', function () { beforeEach(() => { Parse.CoreManager.getLiveQueryController().setDefaultLiveQueryClient(null); }); @@ -25,13 +25,13 @@ describe("ParseLiveQuery", function () { await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); await client.close(); }); - it("access user on onLiveQueryEvent disconnect", async done => { + it('access user on onLiveQueryEvent disconnect', async done => { const requestedUser = new Parse.User(); - requestedUser.setUsername("username"); - requestedUser.setPassword("password"); + requestedUser.setUsername('username'); + requestedUser.setPassword('password'); Parse.Cloud.onLiveQueryEvent(req => { const { event, sessionToken } = req; - if (event === "ws_disconnect") { + if (event === 'ws_disconnect') { Parse.Cloud._removeAllHooks(); expect(sessionToken).toBeDefined(); expect(sessionToken).toBe(requestedUser.getSessionToken()); @@ -46,25 +46,25 @@ describe("ParseLiveQuery", function () { await client.close(); }); - it("can subscribe to query", async done => { + it('can subscribe to query', async done => { const object = new TestObject(); await object.save(); const query = new Parse.Query(TestObject); - query.equalTo("objectId", object.id); + query.equalTo('objectId', object.id); const subscription = await query.subscribe(); - subscription.on("update", object => { - expect(object.get("foo")).toBe("bar"); + subscription.on('update', object => { + expect(object.get('foo')).toBe('bar'); done(); }); - object.set({ foo: "bar" }); + object.set({ foo: 'bar' }); await object.save(); }); - it("can use patterns in className", async done => { + it('can use patterns in className', async done => { await reconfigureServer({ liveQuery: { - classNames: ["Test.*"], + classNames: ['Test.*'], }, startLiveQueryServer: true, verbose: false, @@ -74,135 +74,135 @@ describe("ParseLiveQuery", function () { await object.save(); const query = new Parse.Query(TestObject); - query.equalTo("objectId", object.id); + query.equalTo('objectId', object.id); const subscription = await query.subscribe(); - subscription.on("update", object => { - expect(object.get("foo")).toBe("bar"); + subscription.on('update', object => { + expect(object.get('foo')).toBe('bar'); done(); }); - object.set({ foo: "bar" }); + object.set({ foo: 'bar' }); await object.save(); }); - it("expect afterEvent create", async done => { + it('expect afterEvent create', async done => { await reconfigureServer({ liveQuery: { - classNames: ["TestObject"], + classNames: ['TestObject'], }, startLiveQueryServer: true, verbose: false, silent: true, }); - Parse.Cloud.afterLiveQueryEvent("TestObject", req => { - expect(req.event).toBe("create"); + Parse.Cloud.afterLiveQueryEvent('TestObject', req => { + expect(req.event).toBe('create'); expect(req.user).toBeUndefined(); - expect(req.object.get("foo")).toBe("bar"); + expect(req.object.get('foo')).toBe('bar'); }); const query = new Parse.Query(TestObject); const subscription = await query.subscribe(); - subscription.on("create", object => { - expect(object.get("foo")).toBe("bar"); + subscription.on('create', object => { + expect(object.get('foo')).toBe('bar'); done(); }); const object = new TestObject(); - object.set("foo", "bar"); + object.set('foo', 'bar'); await object.save(); }); - it("expect afterEvent payload", async done => { + it('expect afterEvent payload', async done => { const object = new TestObject(); await object.save(); - Parse.Cloud.afterLiveQueryEvent("TestObject", req => { - expect(req.event).toBe("update"); + Parse.Cloud.afterLiveQueryEvent('TestObject', req => { + expect(req.event).toBe('update'); expect(req.user).toBeUndefined(); - expect(req.object.get("foo")).toBe("bar"); - expect(req.original.get("foo")).toBeUndefined(); + expect(req.object.get('foo')).toBe('bar'); + expect(req.original.get('foo')).toBeUndefined(); done(); }); const query = new Parse.Query(TestObject); - query.equalTo("objectId", object.id); + query.equalTo('objectId', object.id); await query.subscribe(); - object.set({ foo: "bar" }); + object.set({ foo: 'bar' }); await object.save(); }); - it("expect afterEvent enter", async done => { - Parse.Cloud.afterLiveQueryEvent("TestObject", req => { - expect(req.event).toBe("enter"); + it('expect afterEvent enter', async done => { + Parse.Cloud.afterLiveQueryEvent('TestObject', req => { + expect(req.event).toBe('enter'); expect(req.user).toBeUndefined(); - expect(req.object.get("foo")).toBe("bar"); - expect(req.original.get("foo")).toBeUndefined(); + expect(req.object.get('foo')).toBe('bar'); + expect(req.original.get('foo')).toBeUndefined(); }); const object = new TestObject(); await object.save(); const query = new Parse.Query(TestObject); - query.equalTo("foo", "bar"); + query.equalTo('foo', 'bar'); const subscription = await query.subscribe(); - subscription.on("enter", object => { - expect(object.get("foo")).toBe("bar"); + subscription.on('enter', object => { + expect(object.get('foo')).toBe('bar'); done(); }); - object.set("foo", "bar"); + object.set('foo', 'bar'); await object.save(); }); - it("expect afterEvent leave", async done => { - Parse.Cloud.afterLiveQueryEvent("TestObject", req => { - expect(req.event).toBe("leave"); + it('expect afterEvent leave', async done => { + Parse.Cloud.afterLiveQueryEvent('TestObject', req => { + expect(req.event).toBe('leave'); expect(req.user).toBeUndefined(); - expect(req.object.get("foo")).toBeUndefined(); - expect(req.original.get("foo")).toBe("bar"); + expect(req.object.get('foo')).toBeUndefined(); + expect(req.original.get('foo')).toBe('bar'); }); const object = new TestObject(); - object.set("foo", "bar"); + object.set('foo', 'bar'); await object.save(); const query = new Parse.Query(TestObject); - query.equalTo("foo", "bar"); + query.equalTo('foo', 'bar'); const subscription = await query.subscribe(); - subscription.on("leave", object => { - expect(object.get("foo")).toBeUndefined(); + subscription.on('leave', object => { + expect(object.get('foo')).toBeUndefined(); done(); }); - object.unset("foo"); + object.unset('foo'); await object.save(); }); - it("expect afterEvent delete", async done => { - Parse.Cloud.afterLiveQueryEvent("TestObject", req => { - expect(req.event).toBe("delete"); + it('expect afterEvent delete', async done => { + Parse.Cloud.afterLiveQueryEvent('TestObject', req => { + expect(req.event).toBe('delete'); expect(req.user).toBeUndefined(); - req.object.set("foo", "bar"); + req.object.set('foo', 'bar'); }); const object = new TestObject(); await object.save(); const query = new Parse.Query(TestObject); - query.equalTo("objectId", object.id); + query.equalTo('objectId', object.id); const subscription = await query.subscribe(); - subscription.on("delete", object => { - expect(object.get("foo")).toBe("bar"); + subscription.on('delete', object => { + expect(object.get('foo')).toBe('bar'); done(); }); await object.destroy(); }); - it("can handle afterEvent modification", async done => { + it('can handle afterEvent modification', async done => { await reconfigureServer({ liveQuery: { - classNames: ["TestObject"], + classNames: ['TestObject'], }, startLiveQueryServer: true, verbose: false, @@ -211,30 +211,30 @@ describe("ParseLiveQuery", function () { const object = new TestObject(); await object.save(); - Parse.Cloud.afterLiveQueryEvent("TestObject", req => { + Parse.Cloud.afterLiveQueryEvent('TestObject', req => { const current = req.object; - current.set("foo", "yolo"); + current.set('foo', 'yolo'); const original = req.original; - original.set("yolo", "foo"); + original.set('yolo', 'foo'); }); const query = new Parse.Query(TestObject); - query.equalTo("objectId", object.id); + query.equalTo('objectId', object.id); const subscription = await query.subscribe(); - subscription.on("update", (object, original) => { - expect(object.get("foo")).toBe("yolo"); - expect(original.get("yolo")).toBe("foo"); + subscription.on('update', (object, original) => { + expect(object.get('foo')).toBe('yolo'); + expect(original.get('yolo')).toBe('foo'); done(); }); - object.set({ foo: "bar" }); + object.set({ foo: 'bar' }); await object.save(); }); - it("can return different object in afterEvent", async done => { + it('can return different object in afterEvent', async done => { await reconfigureServer({ liveQuery: { - classNames: ["TestObject"], + classNames: ['TestObject'], }, startLiveQueryServer: true, verbose: false, @@ -243,26 +243,26 @@ describe("ParseLiveQuery", function () { const object = new TestObject(); await object.save(); - Parse.Cloud.afterLiveQueryEvent("TestObject", req => { - const object = new Parse.Object("Yolo"); + Parse.Cloud.afterLiveQueryEvent('TestObject', req => { + const object = new Parse.Object('Yolo'); req.object = object; }); const query = new Parse.Query(TestObject); - query.equalTo("objectId", object.id); + query.equalTo('objectId', object.id); const subscription = await query.subscribe(); - subscription.on("update", object => { - expect(object.className).toBe("Yolo"); + subscription.on('update', object => { + expect(object.className).toBe('Yolo'); done(); }); - object.set({ foo: "bar" }); + object.set({ foo: 'bar' }); await object.save(); }); - it("can handle afterEvent throw", async done => { + it('can handle afterEvent throw', async done => { await reconfigureServer({ liveQuery: { - classNames: ["TestObject"], + classNames: ['TestObject'], }, startLiveQueryServer: true, verbose: false, @@ -272,28 +272,28 @@ describe("ParseLiveQuery", function () { const object = new TestObject(); await object.save(); - Parse.Cloud.afterLiveQueryEvent("TestObject", () => { - throw "Throw error from LQ afterEvent."; + Parse.Cloud.afterLiveQueryEvent('TestObject', () => { + throw 'Throw error from LQ afterEvent.'; }); const query = new Parse.Query(TestObject); - query.equalTo("objectId", object.id); + query.equalTo('objectId', object.id); const subscription = await query.subscribe(); - subscription.on("update", () => { - fail("update should not have been called."); + subscription.on('update', () => { + fail('update should not have been called.'); }); - subscription.on("error", e => { - expect(e).toBe("Throw error from LQ afterEvent."); + subscription.on('error', e => { + expect(e).toBe('Throw error from LQ afterEvent.'); done(); }); - object.set({ foo: "bar" }); + object.set({ foo: 'bar' }); await object.save(); }); - it("can log on afterLiveQueryEvent throw", async () => { + it('can log on afterLiveQueryEvent throw', async () => { await reconfigureServer({ liveQuery: { - classNames: ["TestObject"], + classNames: ['TestObject'], }, startLiveQueryServer: true, verbose: false, @@ -303,11 +303,11 @@ describe("ParseLiveQuery", function () { const object = new TestObject(); await object.save(); - const logger = require("../lib/logger").logger; - spyOn(logger, "error").and.callFake(() => {}); + const logger = require('../lib/logger').logger; + spyOn(logger, 'error').and.callFake(() => {}); let session = undefined; - Parse.Cloud.afterLiveQueryEvent("TestObject", ({ sessionToken }) => { + Parse.Cloud.afterLiveQueryEvent('TestObject', ({ sessionToken }) => { session = sessionToken; /* eslint-disable no-undef */ foo.bar(); @@ -315,90 +315,90 @@ describe("ParseLiveQuery", function () { }); const query = new Parse.Query(TestObject); - query.equalTo("objectId", object.id); + query.equalTo('objectId', object.id); const subscription = await query.subscribe(); - object.set({ foo: "bar" }); + object.set({ foo: 'bar' }); await object.save(); - await new Promise(resolve => subscription.on("error", resolve)); + await new Promise(resolve => subscription.on('error', resolve)); expect(logger.error).toHaveBeenCalledWith( `Failed running afterLiveQueryEvent on class TestObject for event update with session ${session} with:\n Error: {"message":"foo is not defined","code":141}` ); }); - it("can handle afterEvent sendEvent to false", async () => { + it('can handle afterEvent sendEvent to false', async () => { const object = new TestObject(); await object.save(); const promise = resolvingPromise(); - Parse.Cloud.afterLiveQueryEvent("TestObject", req => { + Parse.Cloud.afterLiveQueryEvent('TestObject', req => { const current = req.object; const original = req.original; - if (current.get("foo") != original.get("foo")) { + if (current.get('foo') != original.get('foo')) { req.sendEvent = false; } promise.resolve(); }); const query = new Parse.Query(TestObject); - query.equalTo("objectId", object.id); + query.equalTo('objectId', object.id); const subscription = await query.subscribe(); - subscription.on("update", () => { - fail("update should not have been called."); + subscription.on('update', () => { + fail('update should not have been called.'); }); - subscription.on("error", () => { - fail("error should not have been called."); + subscription.on('error', () => { + fail('error should not have been called.'); }); - object.set({ foo: "bar" }); + object.set({ foo: 'bar' }); await object.save(); await promise; }); - it("can handle live query with fields", async () => { + it('can handle live query with fields', async () => { await reconfigureServer({ liveQuery: { - classNames: ["Test"], + classNames: ['Test'], }, startLiveQueryServer: true, }); - const query = new Parse.Query("Test"); - query.watch("yolo"); + const query = new Parse.Query('Test'); + query.watch('yolo'); const subscription = await query.subscribe(); const spy = { create(obj) { - if (!obj.get("yolo")) { - fail("create should not have been called"); + if (!obj.get('yolo')) { + fail('create should not have been called'); } }, update(object, original) { - if (object.get("yolo") === original.get("yolo")) { - fail("create should not have been called"); + if (object.get('yolo') === original.get('yolo')) { + fail('create should not have been called'); } }, }; - const createSpy = spyOn(spy, "create").and.callThrough(); - const updateSpy = spyOn(spy, "update").and.callThrough(); - subscription.on("create", spy.create); - subscription.on("update", spy.update); - const obj = new Parse.Object("Test"); - obj.set("foo", "bar"); + const createSpy = spyOn(spy, 'create').and.callThrough(); + const updateSpy = spyOn(spy, 'update').and.callThrough(); + subscription.on('create', spy.create); + subscription.on('update', spy.update); + const obj = new Parse.Object('Test'); + obj.set('foo', 'bar'); await obj.save(); - obj.set("foo", "xyz"); - obj.set("yolo", "xyz"); + obj.set('foo', 'xyz'); + obj.set('yolo', 'xyz'); await obj.save(); - const obj2 = new Parse.Object("Test"); - obj2.set("foo", "bar"); - obj2.set("yolo", "bar"); + const obj2 = new Parse.Object('Test'); + obj2.set('foo', 'bar'); + obj2.set('yolo', 'bar'); await obj2.save(); - obj2.set("foo", "bart"); + obj2.set('foo', 'bart'); await obj2.save(); expect(createSpy).toHaveBeenCalledTimes(1); expect(updateSpy).toHaveBeenCalledTimes(1); }); - it("can handle afterEvent set pointers", async done => { + it('can handle afterEvent set pointers', async done => { await reconfigureServer({ liveQuery: { - classNames: ["TestObject"], + classNames: ['TestObject'], }, startLiveQueryServer: true, verbose: false, @@ -408,35 +408,35 @@ describe("ParseLiveQuery", function () { const object = new TestObject(); await object.save(); - const secondObject = new Parse.Object("Test2"); - secondObject.set("foo", "bar"); + const secondObject = new Parse.Object('Test2'); + secondObject.set('foo', 'bar'); await secondObject.save(); - Parse.Cloud.afterLiveQueryEvent("TestObject", async ({ object }) => { - const query = new Parse.Query("Test2"); + Parse.Cloud.afterLiveQueryEvent('TestObject', async ({ object }) => { + const query = new Parse.Query('Test2'); const obj = await query.first(); - object.set("obj", obj); + object.set('obj', obj); }); const query = new Parse.Query(TestObject); - query.equalTo("objectId", object.id); + query.equalTo('objectId', object.id); const subscription = await query.subscribe(); - subscription.on("update", object => { - expect(object.get("obj")).toBeDefined(); - expect(object.get("obj").get("foo")).toBe("bar"); + subscription.on('update', object => { + expect(object.get('obj')).toBeDefined(); + expect(object.get('obj').get('foo')).toBe('bar'); done(); }); - subscription.on("error", () => { - fail("error should not have been called."); + subscription.on('error', () => { + fail('error should not have been called.'); }); - object.set({ foo: "bar" }); + object.set({ foo: 'bar' }); await object.save(); }); - it("can handle async afterEvent modification", async done => { + it('can handle async afterEvent modification', async done => { await reconfigureServer({ liveQuery: { - classNames: ["TestObject"], + classNames: ['TestObject'], }, startLiveQueryServer: true, verbose: false, @@ -444,31 +444,31 @@ describe("ParseLiveQuery", function () { }); const parent = new TestObject(); const child = new TestObject(); - child.set("bar", "foo"); + child.set('bar', 'foo'); await Parse.Object.saveAll([parent, child]); - Parse.Cloud.afterLiveQueryEvent("TestObject", async req => { + Parse.Cloud.afterLiveQueryEvent('TestObject', async req => { const current = req.object; - const pointer = current.get("child"); + const pointer = current.get('child'); await pointer.fetch(); }); const query = new Parse.Query(TestObject); - query.equalTo("objectId", parent.id); + query.equalTo('objectId', parent.id); const subscription = await query.subscribe(); - subscription.on("update", object => { - expect(object.get("child")).toBeDefined(); - expect(object.get("child").get("bar")).toBe("foo"); + subscription.on('update', object => { + expect(object.get('child')).toBeDefined(); + expect(object.get('child').get('bar')).toBe('foo'); done(); }); - parent.set("child", child); + parent.set('child', child); await parent.save(); }); - it("can handle beforeConnect / beforeSubscribe hooks", async done => { + it('can handle beforeConnect / beforeSubscribe hooks', async done => { await reconfigureServer({ liveQuery: { - classNames: ["TestObject"], + classNames: ['TestObject'], }, startLiveQueryServer: true, }); @@ -476,13 +476,13 @@ describe("ParseLiveQuery", function () { await object.save(); const hooks = { beforeSubscribe(req) { - expect(req.op).toBe("subscribe"); + expect(req.op).toBe('subscribe'); expect(req.requestId).toBe(1); expect(req.query).toBeDefined(); expect(req.user).toBeUndefined(); }, beforeConnect(req) { - expect(req.event).toBe("connect"); + expect(req.event).toBe('connect'); expect(req.clients).toBe(0); expect(req.subscriptions).toBe(0); expect(req.useMasterKey).toBe(false); @@ -491,27 +491,27 @@ describe("ParseLiveQuery", function () { expect(req.client).toBeDefined(); }, }; - spyOn(hooks, "beforeSubscribe").and.callThrough(); - spyOn(hooks, "beforeConnect").and.callThrough(); - Parse.Cloud.beforeSubscribe("TestObject", hooks.beforeSubscribe); + spyOn(hooks, 'beforeSubscribe').and.callThrough(); + spyOn(hooks, 'beforeConnect').and.callThrough(); + Parse.Cloud.beforeSubscribe('TestObject', hooks.beforeSubscribe); Parse.Cloud.beforeConnect(hooks.beforeConnect); const query = new Parse.Query(TestObject); - query.equalTo("objectId", object.id); + query.equalTo('objectId', object.id); const subscription = await query.subscribe(); - subscription.on("update", object => { - expect(object.get("foo")).toBe("bar"); + subscription.on('update', object => { + expect(object.get('foo')).toBe('bar'); expect(hooks.beforeConnect).toHaveBeenCalled(); expect(hooks.beforeSubscribe).toHaveBeenCalled(); done(); }); - object.set({ foo: "bar" }); + object.set({ foo: 'bar' }); await object.save(); }); - it("can handle beforeConnect validation function", async () => { + it('can handle beforeConnect validation function', async () => { await reconfigureServer({ liveQuery: { - classNames: ["TestObject"], + classNames: ['TestObject'], }, startLiveQueryServer: true, }); @@ -520,16 +520,16 @@ describe("ParseLiveQuery", function () { await object.save(); Parse.Cloud.beforeConnect(() => {}, validatorFail); const query = new Parse.Query(TestObject); - query.equalTo("objectId", object.id); + query.equalTo('objectId', object.id); await expectAsync(query.subscribe()).toBeRejectedWith( - new Parse.Error(Parse.Error.VALIDATION_ERROR, "you are not authorized") + new Parse.Error(Parse.Error.VALIDATION_ERROR, 'you are not authorized') ); }); - it("can handle beforeSubscribe validation function", async () => { + it('can handle beforeSubscribe validation function', async () => { await reconfigureServer({ liveQuery: { - classNames: ["TestObject"], + classNames: ['TestObject'], }, startLiveQueryServer: true, }); @@ -538,39 +538,39 @@ describe("ParseLiveQuery", function () { Parse.Cloud.beforeSubscribe(TestObject, () => {}, validatorFail); const query = new Parse.Query(TestObject); - query.equalTo("objectId", object.id); + query.equalTo('objectId', object.id); await expectAsync(query.subscribe()).toBeRejectedWith( - new Parse.Error(Parse.Error.VALIDATION_ERROR, "you are not authorized") + new Parse.Error(Parse.Error.VALIDATION_ERROR, 'you are not authorized') ); }); - it("can handle afterEvent validation function", async done => { + it('can handle afterEvent validation function', async done => { await reconfigureServer({ liveQuery: { - classNames: ["TestObject"], + classNames: ['TestObject'], }, startLiveQueryServer: true, verbose: false, silent: true, }); - Parse.Cloud.afterLiveQueryEvent("TestObject", () => {}, validatorFail); + Parse.Cloud.afterLiveQueryEvent('TestObject', () => {}, validatorFail); const query = new Parse.Query(TestObject); const subscription = await query.subscribe(); - subscription.on("error", error => { - expect(error).toBe("you are not authorized"); + subscription.on('error', error => { + expect(error).toBe('you are not authorized'); done(); }); const object = new TestObject(); - object.set("foo", "bar"); + object.set('foo', 'bar'); await object.save(); }); - it("can handle beforeConnect error", async () => { + it('can handle beforeConnect error', async () => { await reconfigureServer({ liveQuery: { - classNames: ["TestObject"], + classNames: ['TestObject'], }, startLiveQueryServer: true, }); @@ -578,25 +578,25 @@ describe("ParseLiveQuery", function () { await object.save(); Parse.Cloud.beforeConnect(() => { - throw new Error("You shall not pass!"); + throw new Error('You shall not pass!'); }); const query = new Parse.Query(TestObject); - query.equalTo("objectId", object.id); + query.equalTo('objectId', object.id); await expectAsync(query.subscribe()).toBeRejectedWith( - new Error("You shall not pass!") + new Error('You shall not pass!') ); }); - it("can log on beforeConnect throw", async () => { + it('can log on beforeConnect throw', async () => { await reconfigureServer({ liveQuery: { - classNames: ["TestObject"], + classNames: ['TestObject'], }, startLiveQueryServer: true, }); - const logger = require("../lib/logger").logger; - spyOn(logger, "error").and.callFake(() => {}); + const logger = require('../lib/logger').logger; + spyOn(logger, 'error').and.callFake(() => {}); let token = undefined; Parse.Cloud.beforeConnect(({ sessionToken }) => { token = sessionToken; @@ -605,17 +605,17 @@ describe("ParseLiveQuery", function () { /* eslint-enable no-undef */ }); await expectAsync(new Parse.Query(TestObject).subscribe()).toBeRejectedWith( - new Error("foo is not defined") + new Error('foo is not defined') ); expect(logger.error).toHaveBeenCalledWith( `Failed running beforeConnect for session ${token} with:\n Error: {"message":"foo is not defined","code":141}` ); }); - it("can handle beforeSubscribe error", async () => { + it('can handle beforeSubscribe error', async () => { await reconfigureServer({ liveQuery: { - classNames: ["TestObject"], + classNames: ['TestObject'], }, startLiveQueryServer: true, }); @@ -623,25 +623,25 @@ describe("ParseLiveQuery", function () { await object.save(); Parse.Cloud.beforeSubscribe(TestObject, () => { - throw new Error("You shall not subscribe!"); + throw new Error('You shall not subscribe!'); }); const query = new Parse.Query(TestObject); - query.equalTo("objectId", object.id); + query.equalTo('objectId', object.id); await expectAsync(query.subscribe()).toBeRejectedWith( - new Error("You shall not subscribe!") + new Error('You shall not subscribe!') ); }); - it("can log on beforeSubscribe error", async () => { + it('can log on beforeSubscribe error', async () => { await reconfigureServer({ liveQuery: { - classNames: ["TestObject"], + classNames: ['TestObject'], }, startLiveQueryServer: true, }); - const logger = require("../lib/logger").logger; - spyOn(logger, "error").and.callFake(() => {}); + const logger = require('../lib/logger').logger; + spyOn(logger, 'error').and.callFake(() => {}); Parse.Cloud.beforeSubscribe(TestObject, () => { /* eslint-disable no-undef */ @@ -651,7 +651,7 @@ describe("ParseLiveQuery", function () { const query = new Parse.Query(TestObject); await expectAsync(query.subscribe()).toBeRejectedWith( - new Error("foo is not defined") + new Error('foo is not defined') ); expect(logger.error).toHaveBeenCalledWith( @@ -659,47 +659,47 @@ describe("ParseLiveQuery", function () { ); }); - it("can handle mutate beforeSubscribe query", async done => { + it('can handle mutate beforeSubscribe query', async done => { await reconfigureServer({ liveQuery: { - classNames: ["TestObject"], + classNames: ['TestObject'], }, startLiveQueryServer: true, }); const hook = { beforeSubscribe(request) { - request.query.equalTo("yolo", "abc"); + request.query.equalTo('yolo', 'abc'); }, }; - spyOn(hook, "beforeSubscribe").and.callThrough(); - Parse.Cloud.beforeSubscribe("TestObject", hook.beforeSubscribe); + spyOn(hook, 'beforeSubscribe').and.callThrough(); + Parse.Cloud.beforeSubscribe('TestObject', hook.beforeSubscribe); const object = new TestObject(); await object.save(); - const query = new Parse.Query("TestObject"); - query.equalTo("objectId", object.id); + const query = new Parse.Query('TestObject'); + query.equalTo('objectId', object.id); const subscription = await query.subscribe(); - subscription.on("update", () => { - fail("beforeSubscribe should restrict subscription"); + subscription.on('update', () => { + fail('beforeSubscribe should restrict subscription'); }); - subscription.on("enter", object => { - if (object.get("yolo") === "abc") { + subscription.on('enter', object => { + if (object.get('yolo') === 'abc') { done(); } else { - fail("beforeSubscribe should restrict queries"); + fail('beforeSubscribe should restrict queries'); } }); - object.set({ yolo: "bar" }); + object.set({ yolo: 'bar' }); await object.save(); - object.set({ yolo: "abc" }); + object.set({ yolo: 'abc' }); await object.save(); expect(hook.beforeSubscribe).toHaveBeenCalled(); }); - it("can return a new beforeSubscribe query", async done => { + it('can return a new beforeSubscribe query', async done => { await reconfigureServer({ liveQuery: { - classNames: ["TestObject"], + classNames: ['TestObject'], }, startLiveQueryServer: true, verbose: false, @@ -707,62 +707,62 @@ describe("ParseLiveQuery", function () { }); Parse.Cloud.beforeSubscribe(TestObject, request => { const query = new Parse.Query(TestObject); - query.equalTo("foo", "yolo"); + query.equalTo('foo', 'yolo'); request.query = query; }); const query = new Parse.Query(TestObject); - query.equalTo("foo", "bar"); + query.equalTo('foo', 'bar'); const subscription = await query.subscribe(); - subscription.on("create", object => { - expect(object.get("foo")).toBe("yolo"); + subscription.on('create', object => { + expect(object.get('foo')).toBe('yolo'); done(); }); const object = new TestObject(); - object.set({ foo: "yolo" }); + object.set({ foo: 'yolo' }); await object.save(); }); - it("can handle select beforeSubscribe query", async done => { + it('can handle select beforeSubscribe query', async done => { Parse.Cloud.beforeSubscribe(TestObject, request => { const query = request.query; - query.select("yolo"); + query.select('yolo'); }); const object = new TestObject(); await object.save(); const query = new Parse.Query(TestObject); - query.equalTo("objectId", object.id); + query.equalTo('objectId', object.id); const subscription = await query.subscribe(); - subscription.on("update", object => { - expect(object.get("foo")).toBeUndefined(); - expect(object.get("yolo")).toBe("abc"); + subscription.on('update', object => { + expect(object.get('foo')).toBeUndefined(); + expect(object.get('yolo')).toBe('abc'); done(); }); - object.set({ foo: "bar", yolo: "abc" }); + object.set({ foo: 'bar', yolo: 'abc' }); await object.save(); }); - it("LiveQuery with ACL", async () => { + it('LiveQuery with ACL', async () => { await reconfigureServer({ liveQuery: { - classNames: ["Chat"], + classNames: ['Chat'], }, startLiveQueryServer: true, verbose: false, silent: true, }); const user = new Parse.User(); - user.setUsername("username"); - user.setPassword("password"); + user.setUsername('username'); + user.setPassword('password'); await user.signUp(); const calls = { beforeConnect(req) { - expect(req.event).toBe("connect"); + expect(req.event).toBe('connect'); expect(req.clients).toBe(0); expect(req.subscriptions).toBe(0); expect(req.useMasterKey).toBe(false); @@ -770,37 +770,37 @@ describe("ParseLiveQuery", function () { expect(req.client).toBeDefined(); }, beforeSubscribe(req) { - expect(req.op).toBe("subscribe"); + expect(req.op).toBe('subscribe'); expect(req.requestId).toBe(1); expect(req.query).toBeDefined(); expect(req.user).toBeDefined(); }, afterLiveQueryEvent(req) { expect(req.user).toBeDefined(); - expect(req.object.get("foo")).toBe("bar"); + expect(req.object.get('foo')).toBe('bar'); }, create(object) { - expect(object.get("foo")).toBe("bar"); + expect(object.get('foo')).toBe('bar'); }, delete(object) { - expect(object.get("foo")).toBe("bar"); + expect(object.get('foo')).toBe('bar'); }, }; for (const key in calls) { spyOn(calls, key).and.callThrough(); } Parse.Cloud.beforeConnect(calls.beforeConnect); - Parse.Cloud.beforeSubscribe("Chat", calls.beforeSubscribe); - Parse.Cloud.afterLiveQueryEvent("Chat", calls.afterLiveQueryEvent); + Parse.Cloud.beforeSubscribe('Chat', calls.beforeSubscribe); + Parse.Cloud.afterLiveQueryEvent('Chat', calls.afterLiveQueryEvent); - const chatQuery = new Parse.Query("Chat"); + const chatQuery = new Parse.Query('Chat'); const subscription = await chatQuery.subscribe(); - subscription.on("create", calls.create); - subscription.on("delete", calls.delete); - const object = new Parse.Object("Chat"); + subscription.on('create', calls.create); + subscription.on('delete', calls.delete); + const object = new Parse.Object('Chat'); const acl = new Parse.ACL(user); object.setACL(acl); - object.set({ foo: "bar" }); + object.set({ foo: 'bar' }); await object.save(); await object.destroy(); await sleep(200); @@ -809,44 +809,44 @@ describe("ParseLiveQuery", function () { } }); - it("LiveQuery should work with changing role", async () => { + it('LiveQuery should work with changing role', async () => { await reconfigureServer({ liveQuery: { - classNames: ["Chat"], + classNames: ['Chat'], }, startLiveQueryServer: true, }); const user = new Parse.User(); - user.setUsername("username"); - user.setPassword("password"); + user.setUsername('username'); + user.setPassword('password'); await user.signUp(); - const role = new Parse.Role("Test", new Parse.ACL(user)); + const role = new Parse.Role('Test', new Parse.ACL(user)); await role.save(); - const chatQuery = new Parse.Query("Chat"); + const chatQuery = new Parse.Query('Chat'); const subscription = await chatQuery.subscribe(); - subscription.on("create", () => { - fail("should not call create as user is not part of role."); + subscription.on('create', () => { + fail('should not call create as user is not part of role.'); }); - const object = new Parse.Object("Chat"); + const object = new Parse.Object('Chat'); const acl = new Parse.ACL(); acl.setRoleReadAccess(role, true); object.setACL(acl); - object.set({ foo: "bar" }); + object.set({ foo: 'bar' }); await object.save(null, { useMasterKey: true }); role.getUsers().add(user); await sleep(1000); await role.save(); await sleep(1000); - object.set("foo", "yolo"); + object.set('foo', 'yolo'); await Promise.all([ new Promise(resolve => { - subscription.on("update", obj => { - expect(obj.get("foo")).toBe("yolo"); + subscription.on('update', obj => { + expect(obj.get('foo')).toBe('yolo'); expect(obj.getACL().toJSON()).toEqual({ - "role:Test": { read: true }, + 'role:Test': { read: true }, }); resolve(); }); @@ -855,7 +855,7 @@ describe("ParseLiveQuery", function () { ]); }); - it("liveQuery on Session class", async done => { + it('liveQuery on Session class', async done => { await reconfigureServer({ liveQuery: { classNames: [Parse.Session] }, startLiveQueryServer: true, @@ -864,30 +864,30 @@ describe("ParseLiveQuery", function () { }); const user = new Parse.User(); - user.setUsername("username"); - user.setPassword("password"); + user.setUsername('username'); + user.setPassword('password'); await user.signUp(); const query = new Parse.Query(Parse.Session); const subscription = await query.subscribe(); - subscription.on("create", async obj => { - expect(obj.get("user").id).toBe(user.id); - expect(obj.get("createdWith")).toEqual({ - action: "login", - authProvider: "password", + subscription.on('create', async obj => { + expect(obj.get('user').id).toBe(user.id); + expect(obj.get('createdWith')).toEqual({ + action: 'login', + authProvider: 'password', }); - expect(obj.get("expiresAt")).toBeInstanceOf(Date); - expect(obj.get("installationId")).toBeDefined(); - expect(obj.get("createdAt")).toBeInstanceOf(Date); - expect(obj.get("updatedAt")).toBeInstanceOf(Date); + expect(obj.get('expiresAt')).toBeInstanceOf(Date); + expect(obj.get('installationId')).toBeDefined(); + expect(obj.get('createdAt')).toBeInstanceOf(Date); + expect(obj.get('updatedAt')).toBeInstanceOf(Date); done(); }); - await Parse.User.logIn("username", "password"); + await Parse.User.logIn('username', 'password'); }); - it("prevent liveQuery on Session class when not logged in", async () => { + it('prevent liveQuery on Session class when not logged in', async () => { await reconfigureServer({ liveQuery: { classNames: [Parse.Session], @@ -896,16 +896,16 @@ describe("ParseLiveQuery", function () { }); const query = new Parse.Query(Parse.Session); await expectAsync(query.subscribe()).toBeRejectedWith( - new Error("Invalid session token") + new Error('Invalid session token') ); }); - it_id("4ccc9508-ae6a-46ec-932a-9f5e49ab3b9e")(it)( - "handle invalid websocket payload length", + it_id('4ccc9508-ae6a-46ec-932a-9f5e49ab3b9e')(it)( + 'handle invalid websocket payload length', async done => { await reconfigureServer({ liveQuery: { - classNames: ["TestObject"], + classNames: ['TestObject'], }, startLiveQueryServer: true, verbose: false, @@ -916,7 +916,7 @@ describe("ParseLiveQuery", function () { await object.save(); const query = new Parse.Query(TestObject); - query.equalTo("objectId", object.id); + query.equalTo('objectId', object.id); const subscription = await query.subscribe(); // All control frames must have a payload length of 125 bytes or less. @@ -929,20 +929,20 @@ describe("ParseLiveQuery", function () { await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); client.socket._socket.write(Buffer.from([0x89, 0xfe])); - subscription.on("update", async object => { - expect(object.get("foo")).toBe("bar"); + subscription.on('update', async object => { + expect(object.get('foo')).toBe('bar'); done(); }); // Wait for Websocket timeout to reconnect setTimeout(async () => { - object.set({ foo: "bar" }); + object.set({ foo: 'bar' }); await object.save(); }, 1000); } ); - it_id("39a9191f-26dd-4e05-a379-297a67928de8")(it)( - "should execute live query update on email validation", + it_id('39a9191f-26dd-4e05-a379-297a67928de8')(it)( + 'should execute live query update on email validation', async done => { const emailAdapter = { sendVerificationEmail: () => {}, @@ -951,7 +951,7 @@ describe("ParseLiveQuery", function () { }; await reconfigureServer({ - maintenanceKey: "test2", + maintenanceKey: 'test2', liveQuery: { classNames: [Parse.User], }, @@ -959,24 +959,24 @@ describe("ParseLiveQuery", function () { verbose: false, silent: true, websocketTimeout: 100, - appName: "liveQueryEmailValidation", + appName: 'liveQueryEmailValidation', verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 20, // 0.5 second - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { const user = new Parse.User(); - user.set("password", "asdf"); - user.set("email", "asdf@example.com"); - user.set("username", "zxcv"); + user.set('password', 'asdf'); + user.set('email', 'asdf@example.com'); + user.set('username', 'zxcv'); user .signUp() .then(() => { - const config = Config.get("test"); + const config = Config.get('test'); return config.database.find( - "_User", + '_User', { - username: "zxcv", + username: 'zxcv', }, {}, Auth.maintenance(config) @@ -984,17 +984,17 @@ describe("ParseLiveQuery", function () { }) .then(async results => { const foundUser = results[0]; - const query = new Parse.Query("_User"); - query.equalTo("objectId", foundUser.objectId); + const query = new Parse.Query('_User'); + query.equalTo('objectId', foundUser.objectId); const subscription = await query.subscribe(); - subscription.on("update", async object => { + subscription.on('update', async object => { expect(object).toBeDefined(); - expect(object.get("emailVerified")).toBe(true); + expect(object.get('emailVerified')).toBe(true); done(); }); - const userController = new UserController(emailAdapter, "test", { + const userController = new UserController(emailAdapter, 'test', { verifyUserEmails: true, }); userController.verifyEmail(foundUser._email_verify_token); @@ -1003,10 +1003,10 @@ describe("ParseLiveQuery", function () { } ); - it("should not broadcast event to client with invalid session token - avisory GHSA-2xm2-xj2q-qgpj", async done => { + it('should not broadcast event to client with invalid session token - avisory GHSA-2xm2-xj2q-qgpj', async done => { await reconfigureServer({ liveQuery: { - classNames: ["TestObject"], + classNames: ['TestObject'], }, liveQueryServerOptions: { cacheTimeout: 100, @@ -1017,24 +1017,24 @@ describe("ParseLiveQuery", function () { cacheTTL: 100, }); const user = new Parse.User(); - user.setUsername("username"); - user.setPassword("password"); + user.setUsername('username'); + user.setPassword('password'); await user.signUp(); - const obj1 = new Parse.Object("TestObject"); + const obj1 = new Parse.Object('TestObject'); const obj1ACL = new Parse.ACL(); obj1ACL.setPublicReadAccess(false); obj1ACL.setReadAccess(user, true); obj1.setACL(obj1ACL); - const obj2 = new Parse.Object("TestObject"); + const obj2 = new Parse.Object('TestObject'); const obj2ACL = new Parse.ACL(); obj2ACL.setPublicReadAccess(false); obj2ACL.setReadAccess(user, true); obj2.setACL(obj2ACL); - const query = new Parse.Query("TestObject"); + const query = new Parse.Query('TestObject'); const subscription = await query.subscribe(); - subscription.on("create", obj => { + subscription.on('create', obj => { if (obj.id !== obj1.id) { - done.fail("should not fire"); + done.fail('should not fire'); } }); await obj1.save(); @@ -1045,33 +1045,33 @@ describe("ParseLiveQuery", function () { done(); }); - it("should strip out session token in LiveQuery", async () => { + it('should strip out session token in LiveQuery', async () => { await reconfigureServer({ - liveQuery: { classNames: ["_User"] }, + liveQuery: { classNames: ['_User'] }, startLiveQueryServer: true, verbose: false, silent: true, }); const user = new Parse.User(); - user.setUsername("username"); - user.setPassword("password"); - user.set("foo", "bar"); + user.setUsername('username'); + user.setPassword('password'); + user.set('foo', 'bar'); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); user.setACL(acl); const query = new Parse.Query(Parse.User); - query.equalTo("foo", "bar"); + query.equalTo('foo', 'bar'); const subscription = await query.subscribe(); - const events = ["create", "update", "enter", "leave", "delete"]; + const events = ['create', 'update', 'enter', 'leave', 'delete']; const response = (obj, prev) => { - expect(obj.get("sessionToken")).toBeUndefined(); + expect(obj.get('sessionToken')).toBeUndefined(); expect(obj.sessionToken).toBeUndefined(); expect(prev && prev.sessionToken).toBeUndefined(); if (prev && prev.get) { - expect(prev.get("sessionToken")).toBeUndefined(); + expect(prev.get('sessionToken')).toBeUndefined(); } }; const calls = {}; @@ -1081,11 +1081,11 @@ describe("ParseLiveQuery", function () { subscription.on(key, calls[key]); } await user.signUp(); - user.unset("foo"); + user.unset('foo'); await user.save(); - user.set("foo", "bar"); + user.set('foo', 'bar'); await user.save(); - user.set("yolo", "bar"); + user.set('yolo', 'bar'); await user.save(); await user.destroy(); await new Promise(resolve => setTimeout(resolve, 10)); @@ -1094,56 +1094,56 @@ describe("ParseLiveQuery", function () { } }); - it("should strip out protected fields", async () => { + it('should strip out protected fields', async () => { await reconfigureServer({ - liveQuery: { classNames: ["Test"] }, + liveQuery: { classNames: ['Test'] }, startLiveQueryServer: true, }); - const obj1 = new Parse.Object("Test"); - obj1.set("foo", "foo"); - obj1.set("bar", "bar"); - obj1.set("qux", "qux"); + const obj1 = new Parse.Object('Test'); + obj1.set('foo', 'foo'); + obj1.set('bar', 'bar'); + obj1.set('qux', 'qux'); await obj1.save(); const config = Config.get(Parse.applicationId); const schemaController = await config.database.loadSchema(); await schemaController.updateClass( - "Test", + 'Test', {}, { - get: { "*": true }, - find: { "*": true }, - update: { "*": true }, + get: { '*': true }, + find: { '*': true }, + update: { '*': true }, protectedFields: { - "*": ["foo"], + '*': ['foo'], }, } ); const object = await obj1.fetch(); - expect(object.get("foo")).toBe(undefined); - expect(object.get("bar")).toBeDefined(); - expect(object.get("qux")).toBeDefined(); + expect(object.get('foo')).toBe(undefined); + expect(object.get('bar')).toBeDefined(); + expect(object.get('qux')).toBeDefined(); - const subscription = await new Parse.Query("Test").subscribe(); + const subscription = await new Parse.Query('Test').subscribe(); await Promise.all([ new Promise(resolve => { - subscription.on("update", (obj, original) => { - expect(obj.get("foo")).toBe(undefined); - expect(obj.get("bar")).toBeDefined(); - expect(obj.get("qux")).toBeDefined(); - expect(original.get("foo")).toBe(undefined); - expect(original.get("bar")).toBeDefined(); - expect(original.get("qux")).toBeDefined(); + subscription.on('update', (obj, original) => { + expect(obj.get('foo')).toBe(undefined); + expect(obj.get('bar')).toBeDefined(); + expect(obj.get('qux')).toBeDefined(); + expect(original.get('foo')).toBe(undefined); + expect(original.get('bar')).toBeDefined(); + expect(original.get('qux')).toBeDefined(); resolve(); }); }), - obj1.save({ foo: "abc" }), + obj1.save({ foo: 'abc' }), ]); }); - it("can subscribe to query and return object with withinKilometers with last parameter on update", async done => { + it('can subscribe to query and return object with withinKilometers with last parameter on update', async done => { await reconfigureServer({ liveQuery: { - classNames: ["TestObject"], + classNames: ['TestObject'], }, startLiveQueryServer: true, verbose: false, @@ -1158,13 +1158,13 @@ describe("ParseLiveQuery", function () { const sorted = false; const query = new Parse.Query(TestObject); query.withinKilometers( - "location", + 'location', new Parse.GeoPoint({ latitude: 40.0, longitude: -30.0 }), 2, sorted ); const subscription = await query.subscribe(); - subscription.on("update", obj => { + subscription.on('update', obj => { expect(obj.id).toBe(object.id); done(); }); @@ -1177,42 +1177,42 @@ describe("ParseLiveQuery", function () { await object.save(); }); - it_id("2f95d8a9-7675-45ba-a4a6-e45cb7efb1fb")(it)( - "does shutdown liveQuery server", + it_id('2f95d8a9-7675-45ba-a4a6-e45cb7efb1fb')(it)( + 'does shutdown liveQuery server', async () => { - await reconfigureServer({ appId: "test_app_id" }); + await reconfigureServer({ appId: 'test_app_id' }); const config = { - appId: "hello_test", - masterKey: "world", + appId: 'hello_test', + masterKey: 'world', port: 1345, - mountPath: "/1", - serverURL: "http://localhost:1345/1", + mountPath: '/1', + serverURL: 'http://localhost:1345/1', liveQuery: { - classNames: ["Yolo"], + classNames: ['Yolo'], }, startLiveQueryServer: true, verbose: false, silent: true, }; - if (process.env.PARSE_SERVER_TEST_DB === "postgres") { + if (process.env.PARSE_SERVER_TEST_DB === 'postgres') { config.databaseAdapter = new databaseAdapter.constructor({ uri: databaseURI, - collectionPrefix: "test_", + collectionPrefix: 'test_', }); config.filesAdapter = defaultConfiguration.filesAdapter; } const server = await ParseServer.startApp(config); const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); - client.serverURL = "ws://localhost:1345/1"; - const query = await new Parse.Query("Yolo").subscribe(); + client.serverURL = 'ws://localhost:1345/1'; + const query = await new Parse.Query('Yolo').subscribe(); let liveQueryConnectionCount = await getConnectionsCount( server.liveQueryServer.server ); expect(liveQueryConnectionCount > 0).toBe(true); await Promise.all([ server.handleShutdown(), - new Promise(resolve => query.on("close", resolve)), + new Promise(resolve => query.on('close', resolve)), ]); await sleep(100); expect(server.liveQueryServer.server.address()).toBeNull(); @@ -1225,19 +1225,19 @@ describe("ParseLiveQuery", function () { } ); - it_id("45655b74-716f-4fa1-a058-67eb21f3c3db")(it)( - "does shutdown separate liveQuery server", + it_id('45655b74-716f-4fa1-a058-67eb21f3c3db')(it)( + 'does shutdown separate liveQuery server', async () => { - await reconfigureServer({ appId: "test_app_id" }); + await reconfigureServer({ appId: 'test_app_id' }); let close = false; const config = { - appId: "hello_test", - masterKey: "world", + appId: 'hello_test', + masterKey: 'world', port: 1345, - mountPath: "/1", - serverURL: "http://localhost:1345/1", + mountPath: '/1', + serverURL: 'http://localhost:1345/1', liveQuery: { - classNames: ["Yolo"], + classNames: ['Yolo'], }, startLiveQueryServer: true, verbose: false, @@ -1249,10 +1249,10 @@ describe("ParseLiveQuery", function () { close = true; }, }; - if (process.env.PARSE_SERVER_TEST_DB === "postgres") { + if (process.env.PARSE_SERVER_TEST_DB === 'postgres') { config.databaseAdapter = new databaseAdapter.constructor({ uri: databaseURI, - collectionPrefix: "test_", + collectionPrefix: 'test_', }); config.filesAdapter = defaultConfiguration.filesAdapter; } @@ -1263,22 +1263,22 @@ describe("ParseLiveQuery", function () { // Open a connection to the liveQuery server const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); - client.serverURL = "ws://localhost:1346/1"; - const query = await new Parse.Query("Yolo").subscribe(); + client.serverURL = 'ws://localhost:1346/1'; + const query = await new Parse.Query('Yolo').subscribe(); // Open a connection to the parse server const health = await request({ - method: "GET", + method: 'GET', url: `http://localhost:1345/1/health`, json: true, headers: { - "X-Parse-Application-Id": "hello_test", - "X-Parse-Master-Key": "world", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'hello_test', + 'X-Parse-Master-Key': 'world', + 'Content-Type': 'application/json', }, agent: new http.Agent({ keepAlive: true }), }).then(res => res.data); - expect(health.status).toBe("ok"); + expect(health.status).toBe('ok'); let parseConnectionCount = await getConnectionsCount(parseServer.server); let liveQueryConnectionCount = await getConnectionsCount( @@ -1289,7 +1289,7 @@ describe("ParseLiveQuery", function () { expect(liveQueryConnectionCount > 0).toBe(true); await Promise.all([ parseServer.handleShutdown(), - new Promise(resolve => query.on("close", resolve)), + new Promise(resolve => query.on('close', resolve)), ]); expect(close).toBe(true); await sleep(100); @@ -1305,16 +1305,16 @@ describe("ParseLiveQuery", function () { } ); - it("prevent afterSave trigger if not exists", async () => { + it('prevent afterSave trigger if not exists', async () => { await reconfigureServer({ liveQuery: { - classNames: ["TestObject"], + classNames: ['TestObject'], }, startLiveQueryServer: true, verbose: false, silent: true, }); - spyOn(triggers, "maybeRunTrigger").and.callThrough(); + spyOn(triggers, 'maybeRunTrigger').and.callThrough(); const object1 = new TestObject(); const object2 = new TestObject(); const object3 = new TestObject(); @@ -1326,10 +1326,10 @@ describe("ParseLiveQuery", function () { expect(object3.id).toBeDefined(); }); - it("triggers query event with constraint not equal to null", async () => { + it('triggers query event with constraint not equal to null', async () => { await reconfigureServer({ liveQuery: { - classNames: ["TestObject"], + classNames: ['TestObject'], }, startLiveQueryServer: true, verbose: false, @@ -1338,17 +1338,17 @@ describe("ParseLiveQuery", function () { const spy = { create(obj) { - expect(obj.attributes.foo).toEqual("bar"); + expect(obj.attributes.foo).toEqual('bar'); }, }; - const createSpy = spyOn(spy, "create"); + const createSpy = spyOn(spy, 'create'); const query = new Parse.Query(TestObject); - query.notEqualTo("foo", null); + query.notEqualTo('foo', null); const subscription = await query.subscribe(); - subscription.on("create", spy.create); + subscription.on('create', spy.create); const object1 = new TestObject(); - object1.set("foo", "bar"); + object1.set('foo', 'bar'); await object1.save(); await new Promise(resolve => setTimeout(resolve, 100)); diff --git a/spec/ParseLiveQueryRedis.spec.js b/spec/ParseLiveQueryRedis.spec.js index c2e44507f3..96d68f1464 100644 --- a/spec/ParseLiveQueryRedis.spec.js +++ b/spec/ParseLiveQueryRedis.spec.js @@ -1,34 +1,34 @@ -if (process.env.PARSE_SERVER_TEST_CACHE === "redis") { - describe("ParseLiveQuery redis", () => { +if (process.env.PARSE_SERVER_TEST_CACHE === 'redis') { + describe('ParseLiveQuery redis', () => { afterEach(async () => { const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); client.close(); }); - it("can connect", async () => { + it('can connect', async () => { await reconfigureServer({ - appId: "redis_live_query", + appId: 'redis_live_query', startLiveQueryServer: true, liveQuery: { - classNames: ["TestObject"], - redisURL: "redis://localhost:6379", + classNames: ['TestObject'], + redisURL: 'redis://localhost:6379', }, liveQueryServerOptions: { - redisURL: "redis://localhost:6379", + redisURL: 'redis://localhost:6379', }, }); - const subscription = await new Parse.Query("TestObject").subscribe(); + const subscription = await new Parse.Query('TestObject').subscribe(); const [object] = await Promise.all([ - new Parse.Object("TestObject").save(), + new Parse.Object('TestObject').save(), new Promise(resolve => - subscription.on("create", () => { + subscription.on('create', () => { resolve(); }) ), ]); await Promise.all([ new Promise(resolve => - subscription.on("delete", () => { + subscription.on('delete', () => { resolve(); }) ), @@ -36,16 +36,16 @@ if (process.env.PARSE_SERVER_TEST_CACHE === "redis") { ]); }); - it("can call connect twice", async () => { + it('can call connect twice', async () => { const server = await reconfigureServer({ - appId: "redis_live_query", + appId: 'redis_live_query', startLiveQueryServer: true, liveQuery: { - classNames: ["TestObject"], - redisURL: "redis://localhost:6379", + classNames: ['TestObject'], + redisURL: 'redis://localhost:6379', }, liveQueryServerOptions: { - redisURL: "redis://localhost:6379", + redisURL: 'redis://localhost:6379', }, }); expect( diff --git a/spec/ParseLiveQueryServer.spec.js b/spec/ParseLiveQueryServer.spec.js index 6dfa4e2dd4..59648250e3 100644 --- a/spec/ParseLiveQueryServer.spec.js +++ b/spec/ParseLiveQueryServer.spec.js @@ -1,110 +1,110 @@ -const Parse = require("parse/node"); +const Parse = require('parse/node'); const ParseLiveQueryServer = - require("../lib/LiveQuery/ParseLiveQueryServer").ParseLiveQueryServer; -const ParseServer = require("../lib/ParseServer").default; + require('../lib/LiveQuery/ParseLiveQueryServer').ParseLiveQueryServer; +const ParseServer = require('../lib/ParseServer').default; const LiveQueryController = - require("../lib/Controllers/LiveQueryController").LiveQueryController; -const auth = require("../lib/Auth"); + require('../lib/Controllers/LiveQueryController').LiveQueryController; +const auth = require('../lib/Auth'); // Global mock info -const queryHashValue = "hash"; -const testUserId = "userId"; -const testClassName = "TestObject"; +const queryHashValue = 'hash'; +const testUserId = 'userId'; +const testClassName = 'TestObject'; const timeout = () => jasmine.timeout(100); -describe("ParseLiveQueryServer", function () { +describe('ParseLiveQueryServer', function () { beforeEach(function (done) { // Mock ParseWebSocketServer - const mockParseWebSocketServer = jasmine.createSpy("ParseWebSocketServer"); + const mockParseWebSocketServer = jasmine.createSpy('ParseWebSocketServer'); jasmine.mockLibrary( - "../lib/LiveQuery/ParseWebSocketServer", - "ParseWebSocketServer", + '../lib/LiveQuery/ParseWebSocketServer', + 'ParseWebSocketServer', mockParseWebSocketServer ); // Mock Client const mockClient = function (id, socket, hasMasterKey) { - this.pushConnect = jasmine.createSpy("pushConnect"); - this.pushSubscribe = jasmine.createSpy("pushSubscribe"); - this.pushUnsubscribe = jasmine.createSpy("pushUnsubscribe"); - this.pushDelete = jasmine.createSpy("pushDelete"); - this.pushCreate = jasmine.createSpy("pushCreate"); - this.pushEnter = jasmine.createSpy("pushEnter"); - this.pushUpdate = jasmine.createSpy("pushUpdate"); - this.pushLeave = jasmine.createSpy("pushLeave"); - this.addSubscriptionInfo = jasmine.createSpy("addSubscriptionInfo"); - this.getSubscriptionInfo = jasmine.createSpy("getSubscriptionInfo"); - this.deleteSubscriptionInfo = jasmine.createSpy("deleteSubscriptionInfo"); + this.pushConnect = jasmine.createSpy('pushConnect'); + this.pushSubscribe = jasmine.createSpy('pushSubscribe'); + this.pushUnsubscribe = jasmine.createSpy('pushUnsubscribe'); + this.pushDelete = jasmine.createSpy('pushDelete'); + this.pushCreate = jasmine.createSpy('pushCreate'); + this.pushEnter = jasmine.createSpy('pushEnter'); + this.pushUpdate = jasmine.createSpy('pushUpdate'); + this.pushLeave = jasmine.createSpy('pushLeave'); + this.addSubscriptionInfo = jasmine.createSpy('addSubscriptionInfo'); + this.getSubscriptionInfo = jasmine.createSpy('getSubscriptionInfo'); + this.deleteSubscriptionInfo = jasmine.createSpy('deleteSubscriptionInfo'); this.hasMasterKey = hasMasterKey; }; - mockClient.pushError = jasmine.createSpy("pushError"); - jasmine.mockLibrary("../lib/LiveQuery/Client", "Client", mockClient); + mockClient.pushError = jasmine.createSpy('pushError'); + jasmine.mockLibrary('../lib/LiveQuery/Client', 'Client', mockClient); // Mock Subscription const mockSubscriotion = function () { - this.addClientSubscription = jasmine.createSpy("addClientSubscription"); + this.addClientSubscription = jasmine.createSpy('addClientSubscription'); this.deleteClientSubscription = jasmine.createSpy( - "deleteClientSubscription" + 'deleteClientSubscription' ); }; jasmine.mockLibrary( - "../lib/LiveQuery/Subscription", - "Subscription", + '../lib/LiveQuery/Subscription', + 'Subscription', mockSubscriotion ); // Mock queryHash const mockQueryHash = jasmine - .createSpy("matchesQuery") + .createSpy('matchesQuery') .and.returnValue(queryHashValue); jasmine.mockLibrary( - "../lib/LiveQuery/QueryTools", - "queryHash", + '../lib/LiveQuery/QueryTools', + 'queryHash', mockQueryHash ); // Mock matchesQuery const mockMatchesQuery = jasmine - .createSpy("matchesQuery") + .createSpy('matchesQuery') .and.returnValue(true); jasmine.mockLibrary( - "../lib/LiveQuery/QueryTools", - "matchesQuery", + '../lib/LiveQuery/QueryTools', + 'matchesQuery', mockMatchesQuery ); // Mock ParsePubSub const mockParsePubSub = { createPublisher: function () { return { - publish: jasmine.createSpy("publish"), - on: jasmine.createSpy("on"), + publish: jasmine.createSpy('publish'), + on: jasmine.createSpy('on'), }; }, createSubscriber: function () { return { - subscribe: jasmine.createSpy("subscribe"), - on: jasmine.createSpy("on"), + subscribe: jasmine.createSpy('subscribe'), + on: jasmine.createSpy('on'), }; }, }; jasmine.mockLibrary( - "../lib/LiveQuery/ParsePubSub", - "ParsePubSub", + '../lib/LiveQuery/ParsePubSub', + 'ParsePubSub', mockParsePubSub ); - spyOn(auth, "getAuthForSessionToken").and.callFake( + spyOn(auth, 'getAuthForSessionToken').and.callFake( ({ sessionToken, cacheController }) => { - if (typeof sessionToken === "undefined") { + if (typeof sessionToken === 'undefined') { return Promise.reject(); } if (sessionToken === null) { return Promise.reject(); } - if (sessionToken === "pleaseThrow") { + if (sessionToken === 'pleaseThrow') { return Promise.reject(); } - if (sessionToken === "invalid") { + if (sessionToken === 'invalid') { return Promise.reject( new Parse.Error( Parse.Error.INVALID_SESSION_TOKEN, - "invalid session token" + 'invalid session token' ) ); } @@ -116,7 +116,7 @@ describe("ParseLiveQueryServer", function () { done(); }); - it("can be initialized", function () { + it('can be initialized', function () { const httpServer = {}; const parseLiveQueryServer = new ParseLiveQueryServer(httpServer); @@ -125,7 +125,7 @@ describe("ParseLiveQueryServer", function () { expect(parseLiveQueryServer.subscriptions.size).toBe(0); }); - it("can be initialized from ParseServer", async () => { + it('can be initialized from ParseServer', async () => { const httpServer = {}; const parseLiveQueryServer = await ParseServer.createLiveQueryServer( httpServer, @@ -137,7 +137,7 @@ describe("ParseLiveQueryServer", function () { expect(parseLiveQueryServer.subscriptions.size).toBe(0); }); - it("can be initialized from ParseServer without httpServer", async () => { + it('can be initialized from ParseServer without httpServer', async () => { const parseLiveQueryServer = await ParseServer.createLiveQueryServer( undefined, { @@ -151,17 +151,17 @@ describe("ParseLiveQueryServer", function () { await new Promise(resolve => parseLiveQueryServer.server.close(resolve)); }); - describe_only_db("mongo")("initialization", () => { - beforeEach(() => reconfigureServer({ appId: "mongo_init_test" })); - it("can be initialized through ParseServer without liveQueryServerOptions", async () => { + describe_only_db('mongo')('initialization', () => { + beforeEach(() => reconfigureServer({ appId: 'mongo_init_test' })); + it('can be initialized through ParseServer without liveQueryServerOptions', async () => { const parseServer = await ParseServer.startApp({ - appId: "hello", - masterKey: "world", + appId: 'hello', + masterKey: 'world', port: 22345, - mountPath: "/1", - serverURL: "http://localhost:12345/1", + mountPath: '/1', + serverURL: 'http://localhost:12345/1', liveQuery: { - classNames: ["Yolo"], + classNames: ['Yolo'], }, startLiveQueryServer: true, }); @@ -170,15 +170,15 @@ describe("ParseLiveQueryServer", function () { await new Promise(resolve => parseServer.server.close(resolve)); }); - it("can be initialized through ParseServer with liveQueryServerOptions", async () => { + it('can be initialized through ParseServer with liveQueryServerOptions', async () => { const parseServer = await ParseServer.startApp({ - appId: "hello", - masterKey: "world", + appId: 'hello', + masterKey: 'world', port: 22346, - mountPath: "/1", - serverURL: "http://localhost:12345/1", + mountPath: '/1', + serverURL: 'http://localhost:12345/1', liveQuery: { - classNames: ["Yolo"], + classNames: ['Yolo'], }, liveQueryServerOptions: { port: 22347, @@ -190,9 +190,9 @@ describe("ParseLiveQueryServer", function () { }); }); - it("properly passes the CLP to afterSave/afterDelete hook", function (done) { + it('properly passes the CLP to afterSave/afterDelete hook', function (done) { function setPermissionsOnClass(className, permissions, doPut) { - const request = require("request"); + const request = require('request'); let op = request.post; if (doPut) { op = request.put; @@ -200,10 +200,10 @@ describe("ParseLiveQueryServer", function () { return new Promise((resolve, reject) => { op( { - url: Parse.serverURL + "/schemas/" + className, + url: Parse.serverURL + '/schemas/' + className, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Master-Key": Parse.masterKey, + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': Parse.masterKey, }, json: true, body: { @@ -227,22 +227,22 @@ describe("ParseLiveQueryServer", function () { let deleteSpy; reconfigureServer({ liveQuery: { - classNames: ["Yolo"], + classNames: ['Yolo'], }, }) .then(parseServer => { - saveSpy = spyOn(parseServer.config.liveQueryController, "onAfterSave"); + saveSpy = spyOn(parseServer.config.liveQueryController, 'onAfterSave'); deleteSpy = spyOn( parseServer.config.liveQueryController, - "onAfterDelete" + 'onAfterDelete' ); - return setPermissionsOnClass("Yolo", { - create: { "*": true }, - delete: { "*": true }, + return setPermissionsOnClass('Yolo', { + create: { '*': true }, + delete: { '*': true }, }); }) .then(() => { - const obj = new Parse.Object("Yolo"); + const obj = new Parse.Object('Yolo'); return obj.save(); }) .then(obj => { @@ -252,30 +252,30 @@ describe("ParseLiveQueryServer", function () { expect(saveSpy).toHaveBeenCalled(); const saveArgs = saveSpy.calls.mostRecent().args; expect(saveArgs.length).toBe(4); - expect(saveArgs[0]).toBe("Yolo"); + expect(saveArgs[0]).toBe('Yolo'); expect(saveArgs[3]).toEqual({ get: {}, count: {}, addField: {}, - create: { "*": true }, + create: { '*': true }, find: {}, update: {}, - delete: { "*": true }, + delete: { '*': true }, protectedFields: {}, }); expect(deleteSpy).toHaveBeenCalled(); const deleteArgs = deleteSpy.calls.mostRecent().args; expect(deleteArgs.length).toBe(4); - expect(deleteArgs[0]).toBe("Yolo"); + expect(deleteArgs[0]).toBe('Yolo'); expect(deleteArgs[3]).toEqual({ get: {}, count: {}, addField: {}, - create: { "*": true }, + create: { '*': true }, find: {}, update: {}, - delete: { "*": true }, + delete: { '*': true }, protectedFields: {}, }); done(); @@ -283,16 +283,16 @@ describe("ParseLiveQueryServer", function () { .catch(done.fail); }); - it("can handle connect command", async () => { + it('can handle connect command', async () => { const parseLiveQueryServer = new ParseLiveQueryServer({}); const parseWebSocket = { clientId: -1, }; parseLiveQueryServer._validateKeys = jasmine - .createSpy("validateKeys") + .createSpy('validateKeys') .and.returnValue(true); await parseLiveQueryServer._handleConnect(parseWebSocket, { - sessionToken: "token", + sessionToken: 'token', }); const clientKeys = parseLiveQueryServer.clients.keys(); @@ -305,62 +305,62 @@ describe("ParseLiveQueryServer", function () { expect(client.pushConnect).toHaveBeenCalled(); }); - it("basic beforeConnect rejection", async () => { + it('basic beforeConnect rejection', async () => { Parse.Cloud.beforeConnect(function () { - throw new Error("You shall not pass!"); + throw new Error('You shall not pass!'); }); const parseLiveQueryServer = new ParseLiveQueryServer({}); const parseWebSocket = { clientId: -1, }; await parseLiveQueryServer._handleConnect(parseWebSocket, { - sessionToken: "token", + sessionToken: 'token', }); expect(parseLiveQueryServer.clients.size).toBe(0); - const Client = require("../lib/LiveQuery/Client").Client; + const Client = require('../lib/LiveQuery/Client').Client; expect(Client.pushError).toHaveBeenCalled(); }); - it("basic beforeSubscribe rejection", async () => { - Parse.Cloud.beforeSubscribe("test", function () { - throw new Error("You shall not pass!"); + it('basic beforeSubscribe rejection', async () => { + Parse.Cloud.beforeSubscribe('test', function () { + throw new Error('You shall not pass!'); }); const parseLiveQueryServer = new ParseLiveQueryServer({}); const parseWebSocket = { clientId: -1, }; await parseLiveQueryServer._handleConnect(parseWebSocket, { - sessionToken: "token", + sessionToken: 'token', }); const query = { - className: "test", + className: 'test', where: { - key: "value", + key: 'value', }, - keys: ["test"], + keys: ['test'], }; const requestId = 2; const request = { query: query, requestId: requestId, - sessionToken: "sessionToken", + sessionToken: 'sessionToken', }; await parseLiveQueryServer._handleSubscribe(parseWebSocket, request); expect(parseLiveQueryServer.clients.size).toBe(1); - const Client = require("../lib/LiveQuery/Client").Client; + const Client = require('../lib/LiveQuery/Client').Client; expect(Client.pushError).toHaveBeenCalled(); }); - it("can handle subscribe command without clientId", async () => { + it('can handle subscribe command without clientId', async () => { const parseLiveQueryServer = new ParseLiveQueryServer({}); const incompleteParseConn = {}; await parseLiveQueryServer._handleSubscribe(incompleteParseConn, {}); - const Client = require("../lib/LiveQuery/Client").Client; + const Client = require('../lib/LiveQuery/Client').Client; expect(Client.pushError).toHaveBeenCalled(); }); - it("can handle subscribe command with new query", async () => { + it('can handle subscribe command with new query', async () => { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Add mock client const clientId = 1; @@ -370,17 +370,17 @@ describe("ParseLiveQueryServer", function () { clientId: clientId, }; const query = { - className: "test", + className: 'test', where: { - key: "value", + key: 'value', }, - keys: ["test"], + keys: ['test'], }; const requestId = 2; const request = { query: query, requestId: requestId, - sessionToken: "sessionToken", + sessionToken: 'sessionToken', }; await parseLiveQueryServer._handleSubscribe(parseWebSocket, request); @@ -390,10 +390,10 @@ describe("ParseLiveQueryServer", function () { expect(subscriptions.get(query.className)).not.toBeNull(); const classSubscriptions = subscriptions.get(query.className); expect(classSubscriptions.size).toBe(1); - expect(classSubscriptions.get("hash")).not.toBeNull(); + expect(classSubscriptions.get('hash')).not.toBeNull(); // TODO(check subscription constructor to verify we pass the right argument) // Make sure we add clientInfo to the subscription - const subscription = classSubscriptions.get("hash"); + const subscription = classSubscriptions.get('hash'); expect(subscription.addClientSubscription).toHaveBeenCalledWith( clientId, requestId @@ -407,7 +407,7 @@ describe("ParseLiveQueryServer", function () { expect(client.pushSubscribe).toHaveBeenCalledWith(requestId); }); - it("can handle subscribe command with existing query", async () => { + it('can handle subscribe command with existing query', async () => { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Add two mock clients const clientId = 1; @@ -420,11 +420,11 @@ describe("ParseLiveQueryServer", function () { }; const requestId = 2; const query = { - className: "test", + className: 'test', where: { - key: "value", + key: 'value', }, - keys: ["test"], + keys: ['test'], }; await addMockSubscription( parseLiveQueryServer, @@ -438,11 +438,11 @@ describe("ParseLiveQueryServer", function () { clientId: clientIdAgain, }; const queryAgain = { - className: "test", + className: 'test', where: { - key: "value", + key: 'value', }, - keys: ["testAgain"], + keys: ['testAgain'], }; const requestIdAgain = 1; await addMockSubscription( @@ -459,9 +459,9 @@ describe("ParseLiveQueryServer", function () { expect(subscriptions.get(query.className)).not.toBeNull(); const classSubscriptions = subscriptions.get(query.className); expect(classSubscriptions.size).toBe(1); - expect(classSubscriptions.get("hash")).not.toBeNull(); + expect(classSubscriptions.get('hash')).not.toBeNull(); // Make sure we add clientInfo to the subscription - const subscription = classSubscriptions.get("hash"); + const subscription = classSubscriptions.get('hash'); // Make sure client 2 info has been added let args = subscription.addClientSubscription.calls.mostRecent().args; expect(args).toEqual([clientIdAgain, requestIdAgain]); @@ -471,27 +471,27 @@ describe("ParseLiveQueryServer", function () { expect(args[1].keys).toBe(queryAgain.keys); }); - it("can handle unsubscribe command without clientId", function () { + it('can handle unsubscribe command without clientId', function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); const incompleteParseConn = {}; parseLiveQueryServer._handleUnsubscribe(incompleteParseConn, {}); - const Client = require("../lib/LiveQuery/Client").Client; + const Client = require('../lib/LiveQuery/Client').Client; expect(Client.pushError).toHaveBeenCalled(); }); - it("can handle unsubscribe command without not existed client", function () { + it('can handle unsubscribe command without not existed client', function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); const parseWebSocket = { clientId: 1, }; parseLiveQueryServer._handleUnsubscribe(parseWebSocket, {}); - const Client = require("../lib/LiveQuery/Client").Client; + const Client = require('../lib/LiveQuery/Client').Client; expect(Client.pushError).toHaveBeenCalled(); }); - it("can handle unsubscribe command without not existed query", async () => { + it('can handle unsubscribe command without not existed query', async () => { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Add mock client const clientId = 1; @@ -502,11 +502,11 @@ describe("ParseLiveQueryServer", function () { }; parseLiveQueryServer._handleUnsubscribe(parseWebSocket, {}); - const Client = require("../lib/LiveQuery/Client").Client; + const Client = require('../lib/LiveQuery/Client').Client; expect(Client.pushError).toHaveBeenCalled(); }); - it("can handle unsubscribe command", async () => { + it('can handle unsubscribe command', async () => { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Add mock client const clientId = 1; @@ -546,72 +546,72 @@ describe("ParseLiveQueryServer", function () { expect(subscriptions.size).toBe(0); }); - it("can set connect command message handler for a parseWebSocket", function () { + it('can set connect command message handler for a parseWebSocket', function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Register mock connect/subscribe/unsubscribe handler for the server - parseLiveQueryServer._handleConnect = jasmine.createSpy("_handleSubscribe"); + parseLiveQueryServer._handleConnect = jasmine.createSpy('_handleSubscribe'); // Make mock parseWebsocket - const EventEmitter = require("events"); + const EventEmitter = require('events'); const parseWebSocket = new EventEmitter(); // Register message handlers for the parseWebSocket parseLiveQueryServer._onConnect(parseWebSocket); // Check connect request const connectRequest = { - op: "connect", - applicationId: "1", - installationId: "1234", + op: 'connect', + applicationId: '1', + installationId: '1234', }; // Trigger message event - parseWebSocket.emit("message", connectRequest); + parseWebSocket.emit('message', connectRequest); // Make sure _handleConnect is called const args = parseLiveQueryServer._handleConnect.calls.mostRecent().args; expect(args[0]).toBe(parseWebSocket); }); - it("can set subscribe command message handler for a parseWebSocket", function () { + it('can set subscribe command message handler for a parseWebSocket', function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Register mock connect/subscribe/unsubscribe handler for the server parseLiveQueryServer._handleSubscribe = - jasmine.createSpy("_handleSubscribe"); + jasmine.createSpy('_handleSubscribe'); // Make mock parseWebsocket - const EventEmitter = require("events"); + const EventEmitter = require('events'); const parseWebSocket = new EventEmitter(); // Register message handlers for the parseWebSocket parseLiveQueryServer._onConnect(parseWebSocket); // Check subscribe request const subscribeRequest = JSON.stringify({ - op: "subscribe", + op: 'subscribe', requestId: 1, - query: { className: "Test", where: {} }, + query: { className: 'Test', where: {} }, }); // Trigger message event - parseWebSocket.emit("message", subscribeRequest); + parseWebSocket.emit('message', subscribeRequest); // Make sure _handleSubscribe is called const args = parseLiveQueryServer._handleSubscribe.calls.mostRecent().args; expect(args[0]).toBe(parseWebSocket); expect(JSON.stringify(args[1])).toBe(subscribeRequest); }); - it("can set unsubscribe command message handler for a parseWebSocket", function () { + it('can set unsubscribe command message handler for a parseWebSocket', function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Register mock connect/subscribe/unsubscribe handler for the server parseLiveQueryServer._handleUnsubscribe = - jasmine.createSpy("_handleSubscribe"); + jasmine.createSpy('_handleSubscribe'); // Make mock parseWebsocket - const EventEmitter = require("events"); + const EventEmitter = require('events'); const parseWebSocket = new EventEmitter(); // Register message handlers for the parseWebSocket parseLiveQueryServer._onConnect(parseWebSocket); // Check unsubscribe request const unsubscribeRequest = JSON.stringify({ - op: "unsubscribe", + op: 'unsubscribe', requestId: 1, }); // Trigger message event - parseWebSocket.emit("message", unsubscribeRequest); + parseWebSocket.emit('message', unsubscribeRequest); // Make sure _handleUnsubscribe is called const args = parseLiveQueryServer._handleUnsubscribe.calls.mostRecent().args; @@ -619,15 +619,15 @@ describe("ParseLiveQueryServer", function () { expect(JSON.stringify(args[1])).toBe(unsubscribeRequest); }); - it("can set update command message handler for a parseWebSocket", function () { + it('can set update command message handler for a parseWebSocket', function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Register mock connect/subscribe/unsubscribe handler for the server - spyOn(parseLiveQueryServer, "_handleUpdateSubscription").and.callThrough(); - spyOn(parseLiveQueryServer, "_handleUnsubscribe").and.callThrough(); - spyOn(parseLiveQueryServer, "_handleSubscribe").and.callThrough(); + spyOn(parseLiveQueryServer, '_handleUpdateSubscription').and.callThrough(); + spyOn(parseLiveQueryServer, '_handleUnsubscribe').and.callThrough(); + spyOn(parseLiveQueryServer, '_handleSubscribe').and.callThrough(); // Make mock parseWebsocket - const EventEmitter = require("events"); + const EventEmitter = require('events'); const parseWebSocket = new EventEmitter(); // Register message handlers for the parseWebSocket @@ -635,12 +635,12 @@ describe("ParseLiveQueryServer", function () { // Check updateRequest request const updateRequest = JSON.stringify({ - op: "update", + op: 'update', requestId: 1, - query: { className: "Test", where: {} }, + query: { className: 'Test', where: {} }, }); // Trigger message event - parseWebSocket.emit("message", updateRequest); + parseWebSocket.emit('message', updateRequest); // Make sure _handleUnsubscribe is called const args = parseLiveQueryServer._handleUpdateSubscription.calls.mostRecent().args; @@ -654,26 +654,26 @@ describe("ParseLiveQueryServer", function () { expect(parseLiveQueryServer._handleSubscribe).toHaveBeenCalled(); }); - it("can set missing command message handler for a parseWebSocket", function () { + it('can set missing command message handler for a parseWebSocket', function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make mock parseWebsocket - const EventEmitter = require("events"); + const EventEmitter = require('events'); const parseWebSocket = new EventEmitter(); // Register message handlers for the parseWebSocket parseLiveQueryServer._onConnect(parseWebSocket); // Check invalid request - const invalidRequest = "{}"; + const invalidRequest = '{}'; // Trigger message event - parseWebSocket.emit("message", invalidRequest); - const Client = require("../lib/LiveQuery/Client").Client; + parseWebSocket.emit('message', invalidRequest); + const Client = require('../lib/LiveQuery/Client').Client; expect(Client.pushError).toHaveBeenCalled(); }); - it("can set unknown command message handler for a parseWebSocket", function () { + it('can set unknown command message handler for a parseWebSocket', function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make mock parseWebsocket - const EventEmitter = require("events"); + const EventEmitter = require('events'); const parseWebSocket = new EventEmitter(); // Register message handlers for the parseWebSocket parseLiveQueryServer._onConnect(parseWebSocket); @@ -681,14 +681,14 @@ describe("ParseLiveQueryServer", function () { // Check unknown request const unknownRequest = '{"op":"unknown"}'; // Trigger message event - parseWebSocket.emit("message", unknownRequest); - const Client = require("../lib/LiveQuery/Client").Client; + parseWebSocket.emit('message', unknownRequest); + const Client = require('../lib/LiveQuery/Client').Client; expect(Client.pushError).toHaveBeenCalled(); }); - it("can set disconnect command message handler for a parseWebSocket which has not registered to the server", function () { + it('can set disconnect command message handler for a parseWebSocket which has not registered to the server', function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); - const EventEmitter = require("events"); + const EventEmitter = require('events'); const parseWebSocket = new EventEmitter(); parseWebSocket.clientId = 1; // Register message handlers for the parseWebSocket @@ -696,17 +696,17 @@ describe("ParseLiveQueryServer", function () { // Make sure we do not crash // Trigger disconnect event - parseWebSocket.emit("disconnect"); + parseWebSocket.emit('disconnect'); }); - it("can forward event to cloud code", function () { + it('can forward event to cloud code', function () { const cloudCodeHandler = { handler: () => {}, }; - const spy = spyOn(cloudCodeHandler, "handler").and.callThrough(); + const spy = spyOn(cloudCodeHandler, 'handler').and.callThrough(); Parse.Cloud.onLiveQueryEvent(cloudCodeHandler.handler); const parseLiveQueryServer = new ParseLiveQueryServer({}); - const EventEmitter = require("events"); + const EventEmitter = require('events'); const parseWebSocket = new EventEmitter(); parseWebSocket.clientId = 1; // Register message handlers for the parseWebSocket @@ -714,7 +714,7 @@ describe("ParseLiveQueryServer", function () { // Make sure we do not crash // Trigger disconnect event - parseWebSocket.emit("disconnect"); + parseWebSocket.emit('disconnect'); expect(spy).toHaveBeenCalled(); // call for ws_connect, another for ws_disconnect expect(spy.calls.count()).toBe(2); @@ -722,12 +722,12 @@ describe("ParseLiveQueryServer", function () { // TODO: Test server can set disconnect command message handler for a parseWebSocket - it("has no subscription and can handle object delete command", function () { + it('has no subscription and can handle object delete command', function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make deletedParseObject const parseObject = new Parse.Object(testClassName); parseObject._finishFetch({ - key: "value", + key: 'value', className: testClassName, }); // Make mock message @@ -738,12 +738,12 @@ describe("ParseLiveQueryServer", function () { parseLiveQueryServer._onAfterDelete(message, {}); }); - it("can handle object delete command which does not match any subscription", async () => { + it('can handle object delete command which does not match any subscription', async () => { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make deletedParseObject const parseObject = new Parse.Object(testClassName); parseObject._finishFetch({ - key: "value", + key: 'value', className: testClassName, }); // Make mock message @@ -771,12 +771,12 @@ describe("ParseLiveQueryServer", function () { expect(client.pushDelete).not.toHaveBeenCalled(); }); - it("can handle object delete command which matches some subscriptions", async done => { + it('can handle object delete command which matches some subscriptions', async done => { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make deletedParseObject const parseObject = new Parse.Object(testClassName); parseObject._finishFetch({ - key: "value", + key: 'value', className: testClassName, }); // Make mock message @@ -807,7 +807,7 @@ describe("ParseLiveQueryServer", function () { done(); }); - it("has no subscription and can handle object save command", async () => { + it('has no subscription and can handle object save command', async () => { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make mock request message const message = generateMockMessage(); @@ -815,17 +815,17 @@ describe("ParseLiveQueryServer", function () { parseLiveQueryServer._onAfterSave(message); }); - it("sends correct object for dates", async () => { - jasmine.restoreLibrary("../lib/LiveQuery/QueryTools", "matchesQuery"); + it('sends correct object for dates', async () => { + jasmine.restoreLibrary('../lib/LiveQuery/QueryTools', 'matchesQuery'); const parseLiveQueryServer = new ParseLiveQueryServer({}); const date = new Date(); const message = { currentParseObject: { - date: { __type: "Date", iso: date.toISOString() }, - __type: "Object", - key: "value", + date: { __type: 'Date', iso: date.toISOString() }, + __type: 'Object', + key: 'value', className: testClassName, }, }; @@ -850,15 +850,15 @@ describe("ParseLiveQueryServer", function () { expect(client.pushCreate).toHaveBeenCalledWith( requestId2, { - className: "TestObject", - key: "value", - date: { __type: "Date", iso: date.toISOString() }, + className: 'TestObject', + key: 'value', + date: { __type: 'Date', iso: date.toISOString() }, }, null ); }); - it("can handle object save command which does not match any subscription", async done => { + it('can handle object save command which does not match any subscription', async done => { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make mock request message const message = generateMockMessage(); @@ -889,7 +889,7 @@ describe("ParseLiveQueryServer", function () { done(); }); - it("can handle object enter command which matches some subscriptions", async done => { + it('can handle object enter command which matches some subscriptions', async done => { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make mock request message const message = generateMockMessage(true); @@ -926,7 +926,7 @@ describe("ParseLiveQueryServer", function () { done(); }); - it("can handle object update command which matches some subscriptions", async done => { + it('can handle object update command which matches some subscriptions', async done => { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make mock request message const message = generateMockMessage(true); @@ -959,7 +959,7 @@ describe("ParseLiveQueryServer", function () { done(); }); - it("can handle object leave command which matches some subscriptions", async done => { + it('can handle object leave command which matches some subscriptions', async done => { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make mock request message const message = generateMockMessage(true); @@ -996,10 +996,10 @@ describe("ParseLiveQueryServer", function () { done(); }); - it("sends correct events for object with multiple subscriptions", async done => { + it('sends correct events for object with multiple subscriptions', async done => { const parseLiveQueryServer = new ParseLiveQueryServer({}); - Parse.Cloud.afterLiveQueryEvent("TestObject", () => { + Parse.Cloud.afterLiveQueryEvent('TestObject', () => { // Simulate delay due to trigger, auth, etc. return jasmine.timeout(10); }); @@ -1009,15 +1009,15 @@ describe("ParseLiveQueryServer", function () { // Add mock client const clientId = 1; const client = addMockClient(parseLiveQueryServer, clientId); - client.sessionToken = "sessionToken"; + client.sessionToken = 'sessionToken'; // Mock queryHash for this special test const mockQueryHash = jasmine - .createSpy("matchesQuery") - .and.returnValue("hash1"); + .createSpy('matchesQuery') + .and.returnValue('hash1'); jasmine.mockLibrary( - "../lib/LiveQuery/QueryTools", - "queryHash", + '../lib/LiveQuery/QueryTools', + 'queryHash', mockQueryHash ); // Add mock subscription 1 @@ -1028,16 +1028,16 @@ describe("ParseLiveQueryServer", function () { requestId2, null, null, - "hash1" + 'hash1' ); // Mock queryHash for this special test const mockQueryHash2 = jasmine - .createSpy("matchesQuery") - .and.returnValue("hash2"); + .createSpy('matchesQuery') + .and.returnValue('hash2'); jasmine.mockLibrary( - "../lib/LiveQuery/QueryTools", - "queryHash", + '../lib/LiveQuery/QueryTools', + 'queryHash', mockQueryHash2 ); // Add mock subscription 2 @@ -1048,7 +1048,7 @@ describe("ParseLiveQueryServer", function () { requestId3, null, null, - "hash2" + 'hash2' ); // Mock _matchesSubscription to return matching // In order to mimic a leave, then enter, we need original match return true @@ -1075,23 +1075,23 @@ describe("ParseLiveQueryServer", function () { expect(client.pushEnter).toHaveBeenCalledTimes(1); expect(client.pushEnter).toHaveBeenCalledWith( requestId3, - { key: "value", className: "TestObject" }, - { key: "originalValue", className: "TestObject" } + { key: 'value', className: 'TestObject' }, + { key: 'originalValue', className: 'TestObject' } ); expect(client.pushUpdate).not.toHaveBeenCalled(); expect(client.pushDelete).not.toHaveBeenCalled(); expect(client.pushLeave).toHaveBeenCalledTimes(1); expect(client.pushLeave).toHaveBeenCalledWith( requestId2, - { key: "value", className: "TestObject" }, - { key: "originalValue", className: "TestObject" } + { key: 'value', className: 'TestObject' }, + { key: 'originalValue', className: 'TestObject' } ); done(); }); - it("can handle update command with original object", async done => { - jasmine.restoreLibrary("../lib/LiveQuery/Client", "Client"); - const Client = require("../lib/LiveQuery/Client").Client; + it('can handle update command with original object', async done => { + jasmine.restoreLibrary('../lib/LiveQuery/Client', 'Client'); + const Client = require('../lib/LiveQuery/Client').Client; const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make mock request message const message = generateMockMessage(true); @@ -1099,10 +1099,10 @@ describe("ParseLiveQueryServer", function () { const clientId = 1; const parseWebSocket = { clientId, - send: jasmine.createSpy("send"), + send: jasmine.createSpy('send'), }; const client = new Client(clientId, parseWebSocket); - spyOn(client, "pushUpdate").and.callThrough(); + spyOn(client, 'pushUpdate').and.callThrough(); parseLiveQueryServer.clients.set(clientId, client); // Add mock subscription @@ -1139,7 +1139,7 @@ describe("ParseLiveQueryServer", function () { done(); }); - it("can handle object create command which matches some subscriptions", async done => { + it('can handle object create command which matches some subscriptions', async done => { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make mock request message const message = generateMockMessage(); @@ -1172,9 +1172,9 @@ describe("ParseLiveQueryServer", function () { done(); }); - it("can handle create command with keys", async done => { - jasmine.restoreLibrary("../lib/LiveQuery/Client", "Client"); - const Client = require("../lib/LiveQuery/Client").Client; + it('can handle create command with keys', async done => { + jasmine.restoreLibrary('../lib/LiveQuery/Client', 'Client'); + const Client = require('../lib/LiveQuery/Client').Client; const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make mock request message const message = generateMockMessage(); @@ -1182,10 +1182,10 @@ describe("ParseLiveQueryServer", function () { const clientId = 1; const parseWebSocket = { clientId, - send: jasmine.createSpy("send"), + send: jasmine.createSpy('send'), }; const client = new Client(clientId, parseWebSocket); - spyOn(client, "pushCreate").and.callThrough(); + spyOn(client, 'pushCreate').and.callThrough(); parseLiveQueryServer.clients.set(clientId, client); // Add mock subscription @@ -1193,9 +1193,9 @@ describe("ParseLiveQueryServer", function () { const query = { className: testClassName, where: { - key: "value", + key: 'value', }, - keys: ["test"], + keys: ['test'], }; await addMockSubscription( parseLiveQueryServer, @@ -1228,9 +1228,9 @@ describe("ParseLiveQueryServer", function () { done(); }); - it("can handle create command with watch", async () => { - jasmine.restoreLibrary("../lib/LiveQuery/Client", "Client"); - const Client = require("../lib/LiveQuery/Client").Client; + it('can handle create command with watch', async () => { + jasmine.restoreLibrary('../lib/LiveQuery/Client', 'Client'); + const Client = require('../lib/LiveQuery/Client').Client; const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make mock request message const message = generateMockMessage(); @@ -1238,10 +1238,10 @@ describe("ParseLiveQueryServer", function () { const clientId = 1; const parseWebSocket = { clientId, - send: jasmine.createSpy("send"), + send: jasmine.createSpy('send'), }; const client = new Client(clientId, parseWebSocket); - spyOn(client, "pushCreate").and.callThrough(); + spyOn(client, 'pushCreate').and.callThrough(); parseLiveQueryServer.clients.set(clientId, client); // Add mock subscription @@ -1249,9 +1249,9 @@ describe("ParseLiveQueryServer", function () { const query = { className: testClassName, where: { - key: "value", + key: 'value', }, - watch: ["yolo"], + watch: ['yolo'], }; await addMockSubscription( parseLiveQueryServer, @@ -1278,7 +1278,7 @@ describe("ParseLiveQueryServer", function () { expect(client.pushCreate).not.toHaveBeenCalled(); - message.currentParseObject.set("yolo", "test"); + message.currentParseObject.set('yolo', 'test'); parseLiveQueryServer._onAfterSave(message); await timeout(); @@ -1289,11 +1289,11 @@ describe("ParseLiveQueryServer", function () { expect(toSend.original).toBeUndefined(); }); - it("can match subscription for null or undefined parse object", function () { + it('can match subscription for null or undefined parse object', function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make mock subscription const subscription = { - match: jasmine.createSpy("match"), + match: jasmine.createSpy('match'), }; expect(parseLiveQueryServer._matchesSubscription(null, subscription)).toBe( @@ -1306,7 +1306,7 @@ describe("ParseLiveQueryServer", function () { expect(subscription.match).not.toHaveBeenCalled(); }); - it("can match subscription", function () { + it('can match subscription', function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make mock subscription const subscription = { @@ -1317,26 +1317,26 @@ describe("ParseLiveQueryServer", function () { parseLiveQueryServer._matchesSubscription(parseObject, subscription) ).toBe(true); // Make sure matchesQuery is called - const matchesQuery = require("../lib/LiveQuery/QueryTools").matchesQuery; + const matchesQuery = require('../lib/LiveQuery/QueryTools').matchesQuery; expect(matchesQuery).toHaveBeenCalledWith(parseObject, subscription.query); }); - it("can inflate parse object", function () { + it('can inflate parse object', function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Make mock request const objectJSON = { - className: "testClassName", - createdAt: "2015-12-22T01:51:12.955Z", - key: "value", - objectId: "BfwxBCz6yW", - updatedAt: "2016-01-05T00:46:45.659Z", + className: 'testClassName', + createdAt: '2015-12-22T01:51:12.955Z', + key: 'value', + objectId: 'BfwxBCz6yW', + updatedAt: '2016-01-05T00:46:45.659Z', }; const originalObjectJSON = { - className: "testClassName", - createdAt: "2015-12-22T01:51:12.955Z", - key: "originalValue", - objectId: "BfwxBCz6yW", - updatedAt: "2016-01-05T00:46:45.659Z", + className: 'testClassName', + createdAt: '2015-12-22T01:51:12.955Z', + key: 'originalValue', + objectId: 'BfwxBCz6yW', + updatedAt: '2016-01-05T00:46:45.659Z', }; const message = { currentParseObject: objectJSON, @@ -1348,47 +1348,47 @@ describe("ParseLiveQueryServer", function () { // Verify object const object = message.currentParseObject; expect(object instanceof Parse.Object).toBeTruthy(); - expect(object.get("key")).toEqual("value"); - expect(object.className).toEqual("testClassName"); - expect(object.id).toBe("BfwxBCz6yW"); + expect(object.get('key')).toEqual('value'); + expect(object.className).toEqual('testClassName'); + expect(object.id).toBe('BfwxBCz6yW'); expect(object.createdAt).not.toBeUndefined(); expect(object.updatedAt).not.toBeUndefined(); // Verify original object const originalObject = message.originalParseObject; expect(originalObject instanceof Parse.Object).toBeTruthy(); - expect(originalObject.get("key")).toEqual("originalValue"); - expect(originalObject.className).toEqual("testClassName"); - expect(originalObject.id).toBe("BfwxBCz6yW"); + expect(originalObject.get('key')).toEqual('originalValue'); + expect(originalObject.className).toEqual('testClassName'); + expect(originalObject.id).toBe('BfwxBCz6yW'); expect(originalObject.createdAt).not.toBeUndefined(); expect(originalObject.updatedAt).not.toBeUndefined(); }); - it("can inflate user object", async () => { + it('can inflate user object', async () => { const parseLiveQueryServer = new ParseLiveQueryServer({}); const userJSON = { - username: "test", + username: 'test', ACL: {}, - createdAt: "2018-12-21T23:09:51.784Z", - sessionToken: "r:1234", - updatedAt: "2018-12-21T23:09:51.784Z", - objectId: "NhF2u9n72W", - __type: "Object", - className: "_User", - _hashed_password: "1234", - _email_verify_token: "1234", + createdAt: '2018-12-21T23:09:51.784Z', + sessionToken: 'r:1234', + updatedAt: '2018-12-21T23:09:51.784Z', + objectId: 'NhF2u9n72W', + __type: 'Object', + className: '_User', + _hashed_password: '1234', + _email_verify_token: '1234', }; const originalUserJSON = { - username: "test", + username: 'test', ACL: {}, - createdAt: "2018-12-21T23:09:51.784Z", - sessionToken: "r:1234", - updatedAt: "2018-12-21T23:09:51.784Z", - objectId: "NhF2u9n72W", - __type: "Object", - className: "_User", - _hashed_password: "12345", - _email_verify_token: "12345", + createdAt: '2018-12-21T23:09:51.784Z', + sessionToken: 'r:1234', + updatedAt: '2018-12-21T23:09:51.784Z', + objectId: 'NhF2u9n72W', + __type: 'Object', + className: '_User', + _hashed_password: '12345', + _email_verify_token: '12345', }; const message = { @@ -1399,24 +1399,24 @@ describe("ParseLiveQueryServer", function () { const object = message.currentParseObject; expect(object instanceof Parse.Object).toBeTruthy(); - expect(object.get("_hashed_password")).toBeUndefined(); - expect(object.get("_email_verify_token")).toBeUndefined(); - expect(object.className).toEqual("_User"); - expect(object.id).toBe("NhF2u9n72W"); + expect(object.get('_hashed_password')).toBeUndefined(); + expect(object.get('_email_verify_token')).toBeUndefined(); + expect(object.className).toEqual('_User'); + expect(object.id).toBe('NhF2u9n72W'); expect(object.createdAt).not.toBeUndefined(); expect(object.updatedAt).not.toBeUndefined(); const originalObject = message.originalParseObject; expect(originalObject instanceof Parse.Object).toBeTruthy(); - expect(originalObject.get("_hashed_password")).toBeUndefined(); - expect(originalObject.get("_email_verify_token")).toBeUndefined(); - expect(originalObject.className).toEqual("_User"); - expect(originalObject.id).toBe("NhF2u9n72W"); + expect(originalObject.get('_hashed_password')).toBeUndefined(); + expect(originalObject.get('_email_verify_token')).toBeUndefined(); + expect(originalObject.className).toEqual('_User'); + expect(originalObject.id).toBe('NhF2u9n72W'); expect(originalObject.createdAt).not.toBeUndefined(); expect(originalObject.updatedAt).not.toBeUndefined(); }); - it("can match undefined ACL", function (done) { + it('can match undefined ACL', function (done) { const parseLiveQueryServer = new ParseLiveQueryServer({}); const client = {}; const requestId = 0; @@ -1429,12 +1429,12 @@ describe("ParseLiveQueryServer", function () { }); }); - it("can match ACL with none exist requestId", function (done) { + it('can match ACL with none exist requestId', function (done) { const parseLiveQueryServer = new ParseLiveQueryServer({}); const acl = new Parse.ACL(); const client = { getSubscriptionInfo: jasmine - .createSpy("getSubscriptionInfo") + .createSpy('getSubscriptionInfo') .and.returnValue(undefined), }; const requestId = 0; @@ -1447,15 +1447,15 @@ describe("ParseLiveQueryServer", function () { }); }); - it("can match ACL with public read access", function (done) { + it('can match ACL with public read access', function (done) { const parseLiveQueryServer = new ParseLiveQueryServer({}); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); const client = { getSubscriptionInfo: jasmine - .createSpy("getSubscriptionInfo") + .createSpy('getSubscriptionInfo') .and.returnValue({ - sessionToken: "sessionToken", + sessionToken: 'sessionToken', }), }; const requestId = 0; @@ -1468,15 +1468,15 @@ describe("ParseLiveQueryServer", function () { }); }); - it("can match ACL with valid subscription sessionToken", function (done) { + it('can match ACL with valid subscription sessionToken', function (done) { const parseLiveQueryServer = new ParseLiveQueryServer({}); const acl = new Parse.ACL(); acl.setReadAccess(testUserId, true); const client = { getSubscriptionInfo: jasmine - .createSpy("getSubscriptionInfo") + .createSpy('getSubscriptionInfo') .and.returnValue({ - sessionToken: "sessionToken", + sessionToken: 'sessionToken', }), }; const requestId = 0; @@ -1489,15 +1489,15 @@ describe("ParseLiveQueryServer", function () { }); }); - it("can match ACL with valid client sessionToken", function (done) { + it('can match ACL with valid client sessionToken', function (done) { const parseLiveQueryServer = new ParseLiveQueryServer({}); const acl = new Parse.ACL(); acl.setReadAccess(testUserId, true); // Mock sessionTokenCache will return false when sessionToken is undefined const client = { - sessionToken: "sessionToken", + sessionToken: 'sessionToken', getSubscriptionInfo: jasmine - .createSpy("getSubscriptionInfo") + .createSpy('getSubscriptionInfo') .and.returnValue({ sessionToken: undefined, }), @@ -1512,7 +1512,7 @@ describe("ParseLiveQueryServer", function () { }); }); - it("can match ACL with invalid subscription and client sessionToken", function (done) { + it('can match ACL with invalid subscription and client sessionToken', function (done) { const parseLiveQueryServer = new ParseLiveQueryServer({}); const acl = new Parse.ACL(); acl.setReadAccess(testUserId, true); @@ -1520,7 +1520,7 @@ describe("ParseLiveQueryServer", function () { const client = { sessionToken: undefined, getSubscriptionInfo: jasmine - .createSpy("getSubscriptionInfo") + .createSpy('getSubscriptionInfo') .and.returnValue({ sessionToken: undefined, }), @@ -1535,7 +1535,7 @@ describe("ParseLiveQueryServer", function () { }); }); - it("can match ACL with subscription sessionToken checking error", function (done) { + it('can match ACL with subscription sessionToken checking error', function (done) { const parseLiveQueryServer = new ParseLiveQueryServer({}); const acl = new Parse.ACL(); acl.setReadAccess(testUserId, true); @@ -1543,7 +1543,7 @@ describe("ParseLiveQueryServer", function () { // the behaviour of our mock sessionTokenCache, not real sessionTokenCache const client = { getSubscriptionInfo: jasmine - .createSpy("getSubscriptionInfo") + .createSpy('getSubscriptionInfo') .and.returnValue({ sessionToken: null, }), @@ -1558,7 +1558,7 @@ describe("ParseLiveQueryServer", function () { }); }); - it("can match ACL with client sessionToken checking error", function (done) { + it('can match ACL with client sessionToken checking error', function (done) { const parseLiveQueryServer = new ParseLiveQueryServer({}); const acl = new Parse.ACL(); acl.setReadAccess(testUserId, true); @@ -1566,7 +1566,7 @@ describe("ParseLiveQueryServer", function () { const client = { sessionToken: null, getSubscriptionInfo: jasmine - .createSpy("getSubscriptionInfo") + .createSpy('getSubscriptionInfo') .and.returnValue({ sessionToken: null, }), @@ -1587,9 +1587,9 @@ describe("ParseLiveQueryServer", function () { acl.setPublicReadAccess(false); const client = { getSubscriptionInfo: jasmine - .createSpy("getSubscriptionInfo") + .createSpy('getSubscriptionInfo') .and.returnValue({ - sessionToken: "sessionToken", + sessionToken: 'sessionToken', }), }; const requestId = 0; @@ -1606,10 +1606,10 @@ describe("ParseLiveQueryServer", function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); const acl = new Parse.ACL(); acl.setPublicReadAccess(false); - acl.setRoleReadAccess("livequery", true); + acl.setRoleReadAccess('livequery', true); const client = { getSubscriptionInfo: jasmine - .createSpy("getSubscriptionInfo") + .createSpy('getSubscriptionInfo') .and.returnValue({}), }; const requestId = 0; @@ -1627,17 +1627,17 @@ describe("ParseLiveQueryServer", function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); const acl = new Parse.ACL(); acl.setPublicReadAccess(false); - acl.setRoleReadAccess("otherLiveQueryRead", true); + acl.setRoleReadAccess('otherLiveQueryRead', true); const client = { getSubscriptionInfo: jasmine - .createSpy("getSubscriptionInfo") + .createSpy('getSubscriptionInfo') .and.returnValue({ - sessionToken: "sessionToken", + sessionToken: 'sessionToken', }), }; const requestId = 0; - spyOn(Parse, "Query").and.callFake(function () { + spyOn(Parse, 'Query').and.callFake(function () { let shouldReturn = false; return { equalTo() { @@ -1655,10 +1655,10 @@ describe("ParseLiveQueryServer", function () { } //Return a role with the name "liveQueryRead" as that is what was set on the ACL const liveQueryRole = new Parse.Role( - "liveQueryRead", + 'liveQueryRead', new Parse.ACL() ); - liveQueryRole.id = "abcdef1234"; + liveQueryRole.id = 'abcdef1234'; return Promise.resolve([liveQueryRole]); }, }; @@ -1679,21 +1679,21 @@ describe("ParseLiveQueryServer", function () { }); }); - it("will match ACL with role based read access set to true", function (done) { + it('will match ACL with role based read access set to true', function (done) { const parseLiveQueryServer = new ParseLiveQueryServer({}); const acl = new Parse.ACL(); acl.setPublicReadAccess(false); - acl.setRoleReadAccess("liveQueryRead", true); + acl.setRoleReadAccess('liveQueryRead', true); const client = { getSubscriptionInfo: jasmine - .createSpy("getSubscriptionInfo") + .createSpy('getSubscriptionInfo') .and.returnValue({ - sessionToken: "sessionToken", + sessionToken: 'sessionToken', }), }; const requestId = 0; - spyOn(Parse, "Query").and.callFake(function () { + spyOn(Parse, 'Query').and.callFake(function () { let shouldReturn = false; return { equalTo() { @@ -1711,19 +1711,19 @@ describe("ParseLiveQueryServer", function () { } //Return a role with the name "liveQueryRead" as that is what was set on the ACL const liveQueryRole = new Parse.Role( - "liveQueryRead", + 'liveQueryRead', new Parse.ACL() ); - liveQueryRole.id = "abcdef1234"; + liveQueryRole.id = 'abcdef1234'; return Promise.resolve([liveQueryRole]); }, each(callback) { //Return a role with the name "liveQueryRead" as that is what was set on the ACL const liveQueryRole = new Parse.Role( - "liveQueryRead", + 'liveQueryRead', new Parse.ACL() ); - liveQueryRole.id = "abcdef1234"; + liveQueryRole.id = 'abcdef1234'; callback(liveQueryRole); return Promise.resolve(); }, @@ -1738,16 +1738,16 @@ describe("ParseLiveQueryServer", function () { }); }); - describe("class level permissions", () => { - it("matches CLP when find is closed", done => { + describe('class level permissions', () => { + it('matches CLP when find is closed', done => { const parseLiveQueryServer = new ParseLiveQueryServer({}); const acl = new Parse.ACL(); acl.setReadAccess(testUserId, true); // Mock sessionTokenCache will return false when sessionToken is undefined const client = { - sessionToken: "sessionToken", + sessionToken: 'sessionToken', getSubscriptionInfo: jasmine - .createSpy("getSubscriptionInfo") + .createSpy('getSubscriptionInfo') .and.returnValue({ sessionToken: undefined, }), @@ -1759,10 +1759,10 @@ describe("ParseLiveQueryServer", function () { { find: {}, }, - { className: "Yolo" }, + { className: 'Yolo' }, client, requestId, - "find" + 'find' ) .then(isMatched => { expect(isMatched).toBe(false); @@ -1770,15 +1770,15 @@ describe("ParseLiveQueryServer", function () { }); }); - it("matches CLP when find is open", done => { + it('matches CLP when find is open', done => { const parseLiveQueryServer = new ParseLiveQueryServer({}); const acl = new Parse.ACL(); acl.setReadAccess(testUserId, true); // Mock sessionTokenCache will return false when sessionToken is undefined const client = { - sessionToken: "sessionToken", + sessionToken: 'sessionToken', getSubscriptionInfo: jasmine - .createSpy("getSubscriptionInfo") + .createSpy('getSubscriptionInfo') .and.returnValue({ sessionToken: undefined, }), @@ -1788,12 +1788,12 @@ describe("ParseLiveQueryServer", function () { parseLiveQueryServer ._matchesCLP( { - find: { "*": true }, + find: { '*': true }, }, - { className: "Yolo" }, + { className: 'Yolo' }, client, requestId, - "find" + 'find' ) .then(isMatched => { expect(isMatched).toBe(true); @@ -1801,15 +1801,15 @@ describe("ParseLiveQueryServer", function () { }); }); - it("matches CLP when find is restricted to userIds", done => { + it('matches CLP when find is restricted to userIds', done => { const parseLiveQueryServer = new ParseLiveQueryServer({}); const acl = new Parse.ACL(); acl.setReadAccess(testUserId, true); // Mock sessionTokenCache will return false when sessionToken is undefined const client = { - sessionToken: "sessionToken", + sessionToken: 'sessionToken', getSubscriptionInfo: jasmine - .createSpy("getSubscriptionInfo") + .createSpy('getSubscriptionInfo') .and.returnValue({ sessionToken: undefined, }), @@ -1821,10 +1821,10 @@ describe("ParseLiveQueryServer", function () { { find: { userId: true }, }, - { className: "Yolo" }, + { className: 'Yolo' }, client, requestId, - "find" + 'find' ) .then(isMatched => { expect(isMatched).toBe(false); @@ -1833,17 +1833,17 @@ describe("ParseLiveQueryServer", function () { }); }); - it("can validate key when valid key is provided", function () { + it('can validate key when valid key is provided', function () { const parseLiveQueryServer = new ParseLiveQueryServer( {}, { keyPairs: { - clientKey: "test", + clientKey: 'test', }, } ); const request = { - clientKey: "test", + clientKey: 'test', }; expect( @@ -1851,17 +1851,17 @@ describe("ParseLiveQueryServer", function () { ).toBeTruthy(); }); - it("can validate key when invalid key is provided", function () { + it('can validate key when invalid key is provided', function () { const parseLiveQueryServer = new ParseLiveQueryServer( {}, { keyPairs: { - clientKey: "test", + clientKey: 'test', }, } ); const request = { - clientKey: "error", + clientKey: 'error', }; expect( @@ -1869,12 +1869,12 @@ describe("ParseLiveQueryServer", function () { ).not.toBeTruthy(); }); - it("can validate key when key is not provided", function () { + it('can validate key when key is not provided', function () { const parseLiveQueryServer = new ParseLiveQueryServer( {}, { keyPairs: { - clientKey: "test", + clientKey: 'test', }, } ); @@ -1885,7 +1885,7 @@ describe("ParseLiveQueryServer", function () { ).not.toBeTruthy(); }); - it("can validate key when validKerPairs is empty", function () { + it('can validate key when validKerPairs is empty', function () { const parseLiveQueryServer = new ParseLiveQueryServer({}, {}); const request = {}; @@ -1894,17 +1894,17 @@ describe("ParseLiveQueryServer", function () { ).toBeTruthy(); }); - it("can validate client has master key when valid", function () { + it('can validate client has master key when valid', function () { const parseLiveQueryServer = new ParseLiveQueryServer( {}, { keyPairs: { - masterKey: "test", + masterKey: 'test', }, } ); const request = { - masterKey: "test", + masterKey: 'test', }; expect( @@ -1917,12 +1917,12 @@ describe("ParseLiveQueryServer", function () { {}, { keyPairs: { - masterKey: "test", + masterKey: 'test', }, } ); const request = { - masterKey: "notValid", + masterKey: 'notValid', }; expect( @@ -1935,7 +1935,7 @@ describe("ParseLiveQueryServer", function () { {}, { keyPairs: { - masterKey: "test", + masterKey: 'test', }, } ); @@ -1948,7 +1948,7 @@ describe("ParseLiveQueryServer", function () { it("can validate client doesn't have master key when validKeyPairs is empty", function () { const parseLiveQueryServer = new ParseLiveQueryServer({}, {}); const request = { - masterKey: "test", + masterKey: 'test', }; expect( @@ -1956,13 +1956,13 @@ describe("ParseLiveQueryServer", function () { ).not.toBeTruthy(); }); - it("will match non-public ACL when client has master key", function (done) { + it('will match non-public ACL when client has master key', function (done) { const parseLiveQueryServer = new ParseLiveQueryServer({}); const acl = new Parse.ACL(); acl.setPublicReadAccess(false); const client = { getSubscriptionInfo: jasmine - .createSpy("getSubscriptionInfo") + .createSpy('getSubscriptionInfo') .and.returnValue({}), hasMasterKey: true, }; @@ -1982,7 +1982,7 @@ describe("ParseLiveQueryServer", function () { acl.setPublicReadAccess(false); const client = { getSubscriptionInfo: jasmine - .createSpy("getSubscriptionInfo") + .createSpy('getSubscriptionInfo') .and.returnValue({}), hasMasterKey: false, }; @@ -1996,54 +1996,54 @@ describe("ParseLiveQueryServer", function () { }); }); - it("should properly pull auth from cache", () => { + it('should properly pull auth from cache', () => { const parseLiveQueryServer = new ParseLiveQueryServer({}); - const promise = parseLiveQueryServer.getAuthForSessionToken("sessionToken"); + const promise = parseLiveQueryServer.getAuthForSessionToken('sessionToken'); const secondPromise = - parseLiveQueryServer.getAuthForSessionToken("sessionToken"); + parseLiveQueryServer.getAuthForSessionToken('sessionToken'); // should be in the cache - expect(parseLiveQueryServer.authCache.get("sessionToken")).toBe(promise); + expect(parseLiveQueryServer.authCache.get('sessionToken')).toBe(promise); // should be the same promise returned expect(promise).toBe(secondPromise); // the auth should be called only once expect(auth.getAuthForSessionToken.calls.count()).toBe(1); }); - it("should delete from cache throwing auth calls", async () => { + it('should delete from cache throwing auth calls', async () => { const parseLiveQueryServer = new ParseLiveQueryServer({}); - const promise = parseLiveQueryServer.getAuthForSessionToken("pleaseThrow"); - expect(parseLiveQueryServer.authCache.get("pleaseThrow")).toBe(promise); + const promise = parseLiveQueryServer.getAuthForSessionToken('pleaseThrow'); + expect(parseLiveQueryServer.authCache.get('pleaseThrow')).toBe(promise); // after the promise finishes, it should have removed it from the cache expect(await promise).toEqual({}); - expect(parseLiveQueryServer.authCache.get("pleaseThrow")).toBe(undefined); + expect(parseLiveQueryServer.authCache.get('pleaseThrow')).toBe(undefined); }); - it("should keep a cache of invalid sessions", async () => { + it('should keep a cache of invalid sessions', async () => { const parseLiveQueryServer = new ParseLiveQueryServer({}); - const promise = parseLiveQueryServer.getAuthForSessionToken("invalid"); - expect(parseLiveQueryServer.authCache.get("invalid")).toBe(promise); + const promise = parseLiveQueryServer.getAuthForSessionToken('invalid'); + expect(parseLiveQueryServer.authCache.get('invalid')).toBe(promise); // after the promise finishes, it should have removed it from the cache await promise; - const finalResult = await parseLiveQueryServer.authCache.get("invalid"); + const finalResult = await parseLiveQueryServer.authCache.get('invalid'); expect(finalResult.error).not.toBeUndefined(); - expect(parseLiveQueryServer.authCache.get("invalid")).not.toBe(undefined); + expect(parseLiveQueryServer.authCache.get('invalid')).not.toBe(undefined); }); afterEach(function () { jasmine.restoreLibrary( - "../lib/LiveQuery/ParseWebSocketServer", - "ParseWebSocketServer" + '../lib/LiveQuery/ParseWebSocketServer', + 'ParseWebSocketServer' ); - jasmine.restoreLibrary("../lib/LiveQuery/Client", "Client"); - jasmine.restoreLibrary("../lib/LiveQuery/Subscription", "Subscription"); - jasmine.restoreLibrary("../lib/LiveQuery/QueryTools", "queryHash"); - jasmine.restoreLibrary("../lib/LiveQuery/QueryTools", "matchesQuery"); - jasmine.restoreLibrary("../lib/LiveQuery/ParsePubSub", "ParsePubSub"); + jasmine.restoreLibrary('../lib/LiveQuery/Client', 'Client'); + jasmine.restoreLibrary('../lib/LiveQuery/Subscription', 'Subscription'); + jasmine.restoreLibrary('../lib/LiveQuery/QueryTools', 'queryHash'); + jasmine.restoreLibrary('../lib/LiveQuery/QueryTools', 'matchesQuery'); + jasmine.restoreLibrary('../lib/LiveQuery/ParsePubSub', 'ParsePubSub'); }); // Helper functions to add mock client and subscription to a liveQueryServer function addMockClient(parseLiveQueryServer, clientId) { - const Client = require("../lib/LiveQuery/Client").Client; + const Client = require('../lib/LiveQuery/Client').Client; const client = new Client(clientId, {}); parseLiveQueryServer.clients.set(clientId, client); return client; @@ -2059,7 +2059,7 @@ describe("ParseLiveQueryServer", function () { ) { // If parseWebSocket is null, we use the default one if (!parseWebSocket) { - const EventEmitter = require("events"); + const EventEmitter = require('events'); parseWebSocket = new EventEmitter(); } parseWebSocket.clientId = clientId; @@ -2068,15 +2068,15 @@ describe("ParseLiveQueryServer", function () { query = { className: testClassName, where: { - key: "value", + key: 'value', }, - keys: ["test"], + keys: ['test'], }; } const request = { query: query, requestId: requestId, - sessionToken: "sessionToken", + sessionToken: 'sessionToken', }; await parseLiveQueryServer._handleSubscribe(parseWebSocket, request); @@ -2105,7 +2105,7 @@ describe("ParseLiveQueryServer", function () { function generateMockMessage(hasOriginalParseObject) { const parseObject = new Parse.Object(testClassName); parseObject._finishFetch({ - key: "value", + key: 'value', className: testClassName, }); const message = { @@ -2114,7 +2114,7 @@ describe("ParseLiveQueryServer", function () { if (hasOriginalParseObject) { const originalParseObject = new Parse.Object(testClassName); originalParseObject._finishFetch({ - key: "originalValue", + key: 'originalValue', className: testClassName, }); message.originalParseObject = originalParseObject; @@ -2123,10 +2123,10 @@ describe("ParseLiveQueryServer", function () { } }); -describe("LiveQueryController", () => { - it("properly passes the CLP to afterSave/afterDelete hook", function (done) { +describe('LiveQueryController', () => { + it('properly passes the CLP to afterSave/afterDelete hook', function (done) { function setPermissionsOnClass(className, permissions, doPut) { - const request = require("request"); + const request = require('request'); let op = request.post; if (doPut) { op = request.put; @@ -2134,10 +2134,10 @@ describe("LiveQueryController", () => { return new Promise((resolve, reject) => { op( { - url: Parse.serverURL + "/schemas/" + className, + url: Parse.serverURL + '/schemas/' + className, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Master-Key": Parse.masterKey, + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': Parse.masterKey, }, json: true, body: { @@ -2161,25 +2161,25 @@ describe("LiveQueryController", () => { let deleteSpy; reconfigureServer({ liveQuery: { - classNames: ["Yolo"], + classNames: ['Yolo'], }, }) .then(parseServer => { saveSpy = spyOn( parseServer.config.liveQueryController, - "onAfterSave" + 'onAfterSave' ).and.callThrough(); deleteSpy = spyOn( parseServer.config.liveQueryController, - "onAfterDelete" + 'onAfterDelete' ).and.callThrough(); - return setPermissionsOnClass("Yolo", { - create: { "*": true }, - delete: { "*": true }, + return setPermissionsOnClass('Yolo', { + create: { '*': true }, + delete: { '*': true }, }); }) .then(() => { - const obj = new Parse.Object("Yolo"); + const obj = new Parse.Object('Yolo'); return obj.save(); }) .then(obj => { @@ -2189,30 +2189,30 @@ describe("LiveQueryController", () => { expect(saveSpy).toHaveBeenCalled(); const saveArgs = saveSpy.calls.mostRecent().args; expect(saveArgs.length).toBe(4); - expect(saveArgs[0]).toBe("Yolo"); + expect(saveArgs[0]).toBe('Yolo'); expect(saveArgs[3]).toEqual({ get: {}, count: {}, addField: {}, - create: { "*": true }, + create: { '*': true }, find: {}, update: {}, - delete: { "*": true }, + delete: { '*': true }, protectedFields: {}, }); expect(deleteSpy).toHaveBeenCalled(); const deleteArgs = deleteSpy.calls.mostRecent().args; expect(deleteArgs.length).toBe(4); - expect(deleteArgs[0]).toBe("Yolo"); + expect(deleteArgs[0]).toBe('Yolo'); expect(deleteArgs[3]).toEqual({ get: {}, count: {}, addField: {}, - create: { "*": true }, + create: { '*': true }, find: {}, update: {}, - delete: { "*": true }, + delete: { '*': true }, protectedFields: {}, }); done(); @@ -2220,12 +2220,12 @@ describe("LiveQueryController", () => { .catch(done.fail); }); - it("should properly pack message request on afterSave", () => { + it('should properly pack message request on afterSave', () => { const controller = new LiveQueryController({ - classNames: ["Yolo"], + classNames: ['Yolo'], }); - const spy = spyOn(controller.liveQueryPublisher, "onCloudCodeAfterSave"); - controller.onAfterSave("Yolo", { o: 1 }, { o: 2 }, { yolo: true }); + const spy = spyOn(controller.liveQueryPublisher, 'onCloudCodeAfterSave'); + controller.onAfterSave('Yolo', { o: 1 }, { o: 2 }, { yolo: true }); expect(spy).toHaveBeenCalled(); const args = spy.calls.mostRecent().args; expect(args.length).toBe(1); @@ -2236,12 +2236,12 @@ describe("LiveQueryController", () => { }); }); - it("should properly pack message request on afterDelete", () => { + it('should properly pack message request on afterDelete', () => { const controller = new LiveQueryController({ - classNames: ["Yolo"], + classNames: ['Yolo'], }); - const spy = spyOn(controller.liveQueryPublisher, "onCloudCodeAfterDelete"); - controller.onAfterDelete("Yolo", { o: 1 }, { o: 2 }, { yolo: true }); + const spy = spyOn(controller.liveQueryPublisher, 'onCloudCodeAfterDelete'); + controller.onAfterDelete('Yolo', { o: 1 }, { o: 2 }, { yolo: true }); expect(spy).toHaveBeenCalled(); const args = spy.calls.mostRecent().args; expect(args.length).toBe(1); @@ -2252,9 +2252,9 @@ describe("LiveQueryController", () => { }); }); - it("should properly pack message request", () => { + it('should properly pack message request', () => { const controller = new LiveQueryController({ - classNames: ["Yolo"], + classNames: ['Yolo'], }); expect(controller._makePublisherRequest({})).toEqual({ object: {}, diff --git a/spec/ParseObject.spec.js b/spec/ParseObject.spec.js index 8a7d6c59e2..e0e5091b1c 100644 --- a/spec/ParseObject.spec.js +++ b/spec/ParseObject.spec.js @@ -1,4 +1,4 @@ -"use strict"; +'use strict'; // This is a port of the test suite: // hungry/js/test/parse_object_test.js // @@ -12,55 +12,55 @@ // single-instance mode and we don't want these tests to run in // single-instance mode. -describe("Parse.Object testing", () => { - it("create", function (done) { - create({ test: "test" }, function (model) { - ok(model.id, "Should have an objectId set"); - equal(model.get("test"), "test", "Should have the right attribute"); +describe('Parse.Object testing', () => { + it('create', function (done) { + create({ test: 'test' }, function (model) { + ok(model.id, 'Should have an objectId set'); + equal(model.get('test'), 'test', 'Should have the right attribute'); done(); }); }); - it("update", function (done) { - create({ test: "test" }, function (model) { + it('update', function (done) { + create({ test: 'test' }, function (model) { const t2 = new TestObject({ objectId: model.id }); - t2.set("test", "changed"); + t2.set('test', 'changed'); t2.save().then(function (model) { - equal(model.get("test"), "changed", "Update should have succeeded"); + equal(model.get('test'), 'changed', 'Update should have succeeded'); done(); }); }); }); - it("save without null", function (done) { + it('save without null', function (done) { const object = new TestObject(); - object.set("favoritePony", "Rainbow Dash"); + object.set('favoritePony', 'Rainbow Dash'); object.save().then( function (objectAgain) { equal(objectAgain, object); done(); }, function (objectAgain, error) { - ok(null, "Error " + error.code + ": " + error.message); + ok(null, 'Error ' + error.code + ': ' + error.message); done(); } ); }); - it("save cycle", done => { - const a = new Parse.Object("TestObject"); - const b = new Parse.Object("TestObject"); - a.set("b", b); + it('save cycle', done => { + const a = new Parse.Object('TestObject'); + const b = new Parse.Object('TestObject'); + a.set('b', b); a.save() .then(function () { - b.set("a", a); + b.set('a', a); return b.save(); }) .then(function () { ok(a.id); ok(b.id); - strictEqual(a.get("b"), b); - strictEqual(b.get("a"), a); + strictEqual(a.get('b'), b); + strictEqual(b.get('a'), a); }) .then( function () { @@ -73,21 +73,21 @@ describe("Parse.Object testing", () => { ); }); - it("get", function (done) { - create({ test: "test" }, function (model) { + it('get', function (done) { + create({ test: 'test' }, function (model) { const t2 = new TestObject({ objectId: model.id }); t2.fetch().then(function (model2) { - equal(model2.get("test"), "test", "Update should have succeeded"); + equal(model2.get('test'), 'test', 'Update should have succeeded'); ok(model2.id); - equal(model2.id, model.id, "Ids should match"); + equal(model2.id, model.id, 'Ids should match'); done(); }); }); }); - it("delete", function (done) { + it('delete', function (done) { const t = new TestObject(); - t.set("test", "test"); + t.set('test', 'test'); t.save().then(function () { t.destroy().then(function () { const t2 = new TestObject({ objectId: t.id }); @@ -96,12 +96,12 @@ describe("Parse.Object testing", () => { }); }); - it("find", function (done) { + it('find', function (done) { const t = new TestObject(); - t.set("foo", "bar"); + t.set('foo', 'bar'); t.save().then(function () { const query = new Parse.Query(TestObject); - query.equalTo("foo", "bar"); + query.equalTo('foo', 'bar'); query.find().then(function (results) { equal(results.length, 1); done(); @@ -109,27 +109,27 @@ describe("Parse.Object testing", () => { }); }); - it("relational fields", function (done) { + it('relational fields', function (done) { const item = new Item(); - item.set("property", "x"); + item.set('property', 'x'); const container = new Container(); - container.set("item", item); + container.set('item', item); Parse.Object.saveAll([item, container]).then(function () { const query = new Parse.Query(Container); query.find().then(function (results) { equal(results.length, 1); const containerAgain = results[0]; - const itemAgain = containerAgain.get("item"); + const itemAgain = containerAgain.get('item'); itemAgain.fetch().then(function () { - equal(itemAgain.get("property"), "x"); + equal(itemAgain.get('property'), 'x'); done(); }); }); }); }); - it("save adds no data keys (other than createdAt and updatedAt)", function (done) { + it('save adds no data keys (other than createdAt and updatedAt)', function (done) { const object = new TestObject(); object.save().then(function () { const keys = Object.keys(object.attributes).sort(); @@ -138,35 +138,35 @@ describe("Parse.Object testing", () => { }); }); - it("recursive save", function (done) { + it('recursive save', function (done) { const item = new Item(); - item.set("property", "x"); + item.set('property', 'x'); const container = new Container(); - container.set("item", item); + container.set('item', item); container.save().then(function () { const query = new Parse.Query(Container); query.find().then(function (results) { equal(results.length, 1); const containerAgain = results[0]; - const itemAgain = containerAgain.get("item"); + const itemAgain = containerAgain.get('item'); itemAgain.fetch().then(function () { - equal(itemAgain.get("property"), "x"); + equal(itemAgain.get('property'), 'x'); done(); }); }); }); }); - it("fetch", function (done) { - const item = new Item({ foo: "bar" }); + it('fetch', function (done) { + const item = new Item({ foo: 'bar' }); item.save().then(function () { const itemAgain = new Item(); itemAgain.id = item.id; itemAgain.fetch().then(function () { - itemAgain.save({ foo: "baz" }).then(function () { + itemAgain.save({ foo: 'baz' }).then(function () { item.fetch().then(function () { - equal(item.get("foo"), itemAgain.get("foo")); + equal(item.get('foo'), itemAgain.get('foo')); done(); }); }); @@ -175,7 +175,7 @@ describe("Parse.Object testing", () => { }); it("createdAt doesn't change", function (done) { - const object = new TestObject({ foo: "bar" }); + const object = new TestObject({ foo: 'bar' }); object.save().then(function () { const objectAgain = new TestObject(); objectAgain.id = object.id; @@ -186,8 +186,8 @@ describe("Parse.Object testing", () => { }); }); - it("createdAt and updatedAt exposed", function (done) { - const object = new TestObject({ foo: "bar" }); + it('createdAt and updatedAt exposed', function (done) { + const object = new TestObject({ foo: 'bar' }); object.save().then(function () { notEqual(object.updatedAt, undefined); notEqual(object.createdAt, undefined); @@ -195,22 +195,22 @@ describe("Parse.Object testing", () => { }); }); - it("updatedAt gets updated", function (done) { - const object = new TestObject({ foo: "bar" }); + it('updatedAt gets updated', function (done) { + const object = new TestObject({ foo: 'bar' }); object.save().then(function () { - ok(object.updatedAt, "initial save should cause updatedAt to exist"); + ok(object.updatedAt, 'initial save should cause updatedAt to exist'); const firstUpdatedAt = object.updatedAt; - object.save({ foo: "baz" }).then(function () { - ok(object.updatedAt, "two saves should cause updatedAt to exist"); + object.save({ foo: 'baz' }).then(function () { + ok(object.updatedAt, 'two saves should cause updatedAt to exist'); notEqual(firstUpdatedAt, object.updatedAt); done(); }); }); }); - it("createdAt is reasonable", function (done) { + it('createdAt is reasonable', function (done) { const startTime = new Date(); - const object = new TestObject({ foo: "bar" }); + const object = new TestObject({ foo: 'bar' }); object.save().then(function () { const endTime = new Date(); const startDiff = Math.abs( @@ -225,34 +225,34 @@ describe("Parse.Object testing", () => { }); }); - it("can set null", function (done) { - const obj = new Parse.Object("TestObject"); - obj.set("foo", null); + it('can set null', function (done) { + const obj = new Parse.Object('TestObject'); + obj.set('foo', null); obj.save().then( function (obj) { - on_db("mongo", () => { - equal(obj.get("foo"), null); + on_db('mongo', () => { + equal(obj.get('foo'), null); }); - on_db("postgres", () => { - equal(obj.get("foo"), null); + on_db('postgres', () => { + equal(obj.get('foo'), null); }); done(); }, function () { - fail("should not fail"); + fail('should not fail'); done(); } ); }); - it("can set boolean", function (done) { - const obj = new Parse.Object("TestObject"); - obj.set("yes", true); - obj.set("no", false); + it('can set boolean', function (done) { + const obj = new Parse.Object('TestObject'); + obj.set('yes', true); + obj.set('no', false); obj.save().then( function (obj) { - equal(obj.get("yes"), true); - equal(obj.get("no"), false); + equal(obj.get('yes'), true); + equal(obj.get('no'), false); done(); }, function (obj, error) { @@ -262,9 +262,9 @@ describe("Parse.Object testing", () => { ); }); - it("cannot set invalid date", async function (done) { - const obj = new Parse.Object("TestObject"); - obj.set("when", new Date(Date.parse(null))); + it('cannot set invalid date', async function (done) { + const obj = new Parse.Object('TestObject'); + obj.set('when', new Date(Date.parse(null))); try { await obj.save(); } catch (e) { @@ -272,25 +272,25 @@ describe("Parse.Object testing", () => { done(); return; } - ok(false, "Saving an invalid date should throw"); + ok(false, 'Saving an invalid date should throw'); done(); }); - it("can set authData when not user class", async () => { - const obj = new Parse.Object("TestObject"); - obj.set("authData", "random"); + it('can set authData when not user class', async () => { + const obj = new Parse.Object('TestObject'); + obj.set('authData', 'random'); await obj.save(); - expect(obj.get("authData")).toBe("random"); - const query = new Parse.Query("TestObject"); + expect(obj.get('authData')).toBe('random'); + const query = new Parse.Query('TestObject'); const object = await query.get(obj.id, { useMasterKey: true }); - expect(object.get("authData")).toBe("random"); + expect(object.get('authData')).toBe('random'); }); - it("invalid class name", function (done) { - const item = new Parse.Object("Foo^bar"); + it('invalid class name', function (done) { + const item = new Parse.Object('Foo^bar'); item.save().then( function () { - ok(false, "The name should have been invalid."); + ok(false, 'The name should have been invalid.'); done(); }, function () { @@ -302,28 +302,28 @@ describe("Parse.Object testing", () => { ); }); - it("invalid key name", function (done) { - const item = new Parse.Object("Item"); - expect(() => item.set({ "foo^bar": "baz" })).toThrow( - new Parse.Error(Parse.Error.INVALID_KEY_NAME, "Invalid key name: foo^bar") + it('invalid key name', function (done) { + const item = new Parse.Object('Item'); + expect(() => item.set({ 'foo^bar': 'baz' })).toThrow( + new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'Invalid key name: foo^bar') ); - item.save({ "foo^bar": "baz" }).then(fail, () => done()); + item.save({ 'foo^bar': 'baz' }).then(fail, () => done()); }); - it("invalid __type", function (done) { - const item = new Parse.Object("Item"); + it('invalid __type', function (done) { + const item = new Parse.Object('Item'); const types = [ - "Pointer", - "File", - "Date", - "GeoPoint", - "Bytes", - "Polygon", - "Relation", + 'Pointer', + 'File', + 'Date', + 'GeoPoint', + 'Bytes', + 'Polygon', + 'Relation', ]; const tests = types.map(type => { - const test = new Parse.Object("Item"); - test.set("foo", { + const test = new Parse.Object('Item'); + test.set('foo', { __type: type, }); return test; @@ -341,229 +341,229 @@ describe("Parse.Object testing", () => { item .save({ foo: { - __type: "IvalidName", + __type: 'IvalidName', }, }) .then(fail, () => next(0)); }); - it("simple field deletion", function (done) { - const simple = new Parse.Object("SimpleObject"); + it('simple field deletion', function (done) { + const simple = new Parse.Object('SimpleObject'); simple .save({ - foo: "bar", + foo: 'bar', }) .then( function (simple) { - simple.unset("foo"); - ok(!simple.has("foo"), "foo should have been unset."); - ok(simple.dirty("foo"), "foo should be dirty."); - ok(simple.dirty(), "the whole object should be dirty."); + simple.unset('foo'); + ok(!simple.has('foo'), 'foo should have been unset.'); + ok(simple.dirty('foo'), 'foo should be dirty.'); + ok(simple.dirty(), 'the whole object should be dirty.'); simple.save().then( function (simple) { - ok(!simple.has("foo"), "foo should have been unset."); - ok(!simple.dirty("foo"), "the whole object was just saved."); - ok(!simple.dirty(), "the whole object was just saved."); + ok(!simple.has('foo'), 'foo should have been unset.'); + ok(!simple.dirty('foo'), 'the whole object was just saved.'); + ok(!simple.dirty(), 'the whole object was just saved.'); - const query = new Parse.Query("SimpleObject"); + const query = new Parse.Query('SimpleObject'); query.get(simple.id).then( function (simpleAgain) { - ok(!simpleAgain.has("foo"), "foo should have been removed."); + ok(!simpleAgain.has('foo'), 'foo should have been removed.'); done(); }, function (simpleAgain, error) { - ok(false, "Error " + error.code + ": " + error.message); + ok(false, 'Error ' + error.code + ': ' + error.message); done(); } ); }, function (simple, error) { - ok(false, "Error " + error.code + ": " + error.message); + ok(false, 'Error ' + error.code + ': ' + error.message); done(); } ); }, function (simple, error) { - ok(false, "Error " + error.code + ": " + error.message); + ok(false, 'Error ' + error.code + ': ' + error.message); done(); } ); }); - it("field deletion before first save", function (done) { - const simple = new Parse.Object("SimpleObject"); - simple.set("foo", "bar"); - simple.unset("foo"); + it('field deletion before first save', function (done) { + const simple = new Parse.Object('SimpleObject'); + simple.set('foo', 'bar'); + simple.unset('foo'); - ok(!simple.has("foo"), "foo should have been unset."); - ok(simple.dirty("foo"), "foo should be dirty."); - ok(simple.dirty(), "the whole object should be dirty."); + ok(!simple.has('foo'), 'foo should have been unset.'); + ok(simple.dirty('foo'), 'foo should be dirty.'); + ok(simple.dirty(), 'the whole object should be dirty.'); simple.save().then( function (simple) { - ok(!simple.has("foo"), "foo should have been unset."); - ok(!simple.dirty("foo"), "the whole object was just saved."); - ok(!simple.dirty(), "the whole object was just saved."); + ok(!simple.has('foo'), 'foo should have been unset.'); + ok(!simple.dirty('foo'), 'the whole object was just saved.'); + ok(!simple.dirty(), 'the whole object was just saved.'); - const query = new Parse.Query("SimpleObject"); + const query = new Parse.Query('SimpleObject'); query.get(simple.id).then( function (simpleAgain) { - ok(!simpleAgain.has("foo"), "foo should have been removed."); + ok(!simpleAgain.has('foo'), 'foo should have been removed.'); done(); }, function (simpleAgain, error) { - ok(false, "Error " + error.code + ": " + error.message); + ok(false, 'Error ' + error.code + ': ' + error.message); done(); } ); }, function (simple, error) { - ok(false, "Error " + error.code + ": " + error.message); + ok(false, 'Error ' + error.code + ': ' + error.message); done(); } ); }); - it("relation deletion", function (done) { - const simple = new Parse.Object("SimpleObject"); - const child = new Parse.Object("Child"); + it('relation deletion', function (done) { + const simple = new Parse.Object('SimpleObject'); + const child = new Parse.Object('Child'); simple .save({ child: child, }) .then( function (simple) { - simple.unset("child"); - ok(!simple.has("child"), "child should have been unset."); - ok(simple.dirty("child"), "child should be dirty."); - ok(simple.dirty(), "the whole object should be dirty."); + simple.unset('child'); + ok(!simple.has('child'), 'child should have been unset.'); + ok(simple.dirty('child'), 'child should be dirty.'); + ok(simple.dirty(), 'the whole object should be dirty.'); simple.save().then( function (simple) { - ok(!simple.has("child"), "child should have been unset."); - ok(!simple.dirty("child"), "the whole object was just saved."); - ok(!simple.dirty(), "the whole object was just saved."); + ok(!simple.has('child'), 'child should have been unset.'); + ok(!simple.dirty('child'), 'the whole object was just saved.'); + ok(!simple.dirty(), 'the whole object was just saved.'); - const query = new Parse.Query("SimpleObject"); + const query = new Parse.Query('SimpleObject'); query.get(simple.id).then( function (simpleAgain) { ok( - !simpleAgain.has("child"), - "child should have been removed." + !simpleAgain.has('child'), + 'child should have been removed.' ); done(); }, function (simpleAgain, error) { - ok(false, "Error " + error.code + ": " + error.message); + ok(false, 'Error ' + error.code + ': ' + error.message); done(); } ); }, function (simple, error) { - ok(false, "Error " + error.code + ": " + error.message); + ok(false, 'Error ' + error.code + ': ' + error.message); done(); } ); }, function (simple, error) { - ok(false, "Error " + error.code + ": " + error.message); + ok(false, 'Error ' + error.code + ': ' + error.message); done(); } ); }); - it("deleted keys get cleared", function (done) { - const simpleObject = new Parse.Object("SimpleObject"); - simpleObject.set("foo", "bar"); - simpleObject.unset("foo"); + it('deleted keys get cleared', function (done) { + const simpleObject = new Parse.Object('SimpleObject'); + simpleObject.set('foo', 'bar'); + simpleObject.unset('foo'); simpleObject.save().then(function (simpleObject) { - simpleObject.set("foo", "baz"); + simpleObject.set('foo', 'baz'); simpleObject.save().then(function (simpleObject) { - const query = new Parse.Query("SimpleObject"); + const query = new Parse.Query('SimpleObject'); query.get(simpleObject.id).then(function (simpleObjectAgain) { - equal(simpleObjectAgain.get("foo"), "baz"); + equal(simpleObjectAgain.get('foo'), 'baz'); done(); }, done.fail); }, done.fail); }, done.fail); }); - it("setting after deleting", function (done) { - const simpleObject = new Parse.Object("SimpleObject"); - simpleObject.set("foo", "bar"); + it('setting after deleting', function (done) { + const simpleObject = new Parse.Object('SimpleObject'); + simpleObject.set('foo', 'bar'); simpleObject.save().then( function (simpleObject) { - simpleObject.unset("foo"); - simpleObject.set("foo", "baz"); + simpleObject.unset('foo'); + simpleObject.set('foo', 'baz'); simpleObject.save().then( function (simpleObject) { - const query = new Parse.Query("SimpleObject"); + const query = new Parse.Query('SimpleObject'); query.get(simpleObject.id).then( function (simpleObjectAgain) { - equal(simpleObjectAgain.get("foo"), "baz"); + equal(simpleObjectAgain.get('foo'), 'baz'); done(); }, function (error) { - ok(false, "Error " + error.code + ": " + error.message); + ok(false, 'Error ' + error.code + ': ' + error.message); done(); } ); }, function (error) { - ok(false, "Error " + error.code + ": " + error.message); + ok(false, 'Error ' + error.code + ': ' + error.message); done(); } ); }, function (error) { - ok(false, "Error " + error.code + ": " + error.message); + ok(false, 'Error ' + error.code + ': ' + error.message); done(); } ); }); - it("increment", function (done) { - const simple = new Parse.Object("SimpleObject"); + it('increment', function (done) { + const simple = new Parse.Object('SimpleObject'); simple .save({ foo: 5, }) .then(function (simple) { - simple.increment("foo"); - equal(simple.get("foo"), 6); - ok(simple.dirty("foo"), "foo should be dirty."); - ok(simple.dirty(), "the whole object should be dirty."); + simple.increment('foo'); + equal(simple.get('foo'), 6); + ok(simple.dirty('foo'), 'foo should be dirty.'); + ok(simple.dirty(), 'the whole object should be dirty.'); simple.save().then(function (simple) { - equal(simple.get("foo"), 6); - ok(!simple.dirty("foo"), "the whole object was just saved."); - ok(!simple.dirty(), "the whole object was just saved."); + equal(simple.get('foo'), 6); + ok(!simple.dirty('foo'), 'the whole object was just saved.'); + ok(!simple.dirty(), 'the whole object was just saved.'); - const query = new Parse.Query("SimpleObject"); + const query = new Parse.Query('SimpleObject'); query.get(simple.id).then(function (simpleAgain) { - equal(simpleAgain.get("foo"), 6); + equal(simpleAgain.get('foo'), 6); done(); }); }); }); }); - it("addUnique", function (done) { - const x1 = new Parse.Object("X"); - x1.set("stuff", [1, 2]); + it('addUnique', function (done) { + const x1 = new Parse.Object('X'); + x1.set('stuff', [1, 2]); x1.save() .then(() => { const objectId = x1.id; - const x2 = new Parse.Object("X", { objectId: objectId }); - x2.addUnique("stuff", 2); - x2.addUnique("stuff", 4); - expect(x2.get("stuff")).toEqual([2, 4]); + const x2 = new Parse.Object('X', { objectId: objectId }); + x2.addUnique('stuff', 2); + x2.addUnique('stuff', 4); + expect(x2.get('stuff')).toEqual([2, 4]); return x2.save(); }) .then(() => { - const query = new Parse.Query("X"); + const query = new Parse.Query('X'); return query.get(x1.id); }) .then( x3 => { - const stuff = x3.get("stuff"); + const stuff = x3.get('stuff'); const expected = [1, 2, 4]; expect(stuff.length).toBe(expected.length); for (const i of stuff) { @@ -572,12 +572,12 @@ describe("Parse.Object testing", () => { done(); }, error => { - on_db("mongo", () => { + on_db('mongo', () => { jfail(error); }); - on_db("postgres", () => { + on_db('postgres', () => { expect(error.message).toEqual( - "Postgres does not support AddUnique operator." + 'Postgres does not support AddUnique operator.' ); }); done(); @@ -585,103 +585,103 @@ describe("Parse.Object testing", () => { ); }); - it_only_db("mongo")("can increment array nested fields", async () => { + it_only_db('mongo')('can increment array nested fields', async () => { const obj = new TestObject(); - obj.set("items", [ - { value: "a", count: 5 }, - { value: "b", count: 1 }, + obj.set('items', [ + { value: 'a', count: 5 }, + { value: 'b', count: 1 }, ]); await obj.save(); - obj.increment("items.0.count", 15); - obj.increment("items.1.count", 4); + obj.increment('items.0.count', 15); + obj.increment('items.1.count', 4); await obj.save(); - expect(obj.toJSON().items[0].value).toBe("a"); - expect(obj.toJSON().items[1].value).toBe("b"); + expect(obj.toJSON().items[0].value).toBe('a'); + expect(obj.toJSON().items[1].value).toBe('b'); expect(obj.toJSON().items[0].count).toBe(20); expect(obj.toJSON().items[1].count).toBe(5); const query = new Parse.Query(TestObject); const result = await query.get(obj.id); - expect(result.get("items")[0].value).toBe("a"); - expect(result.get("items")[1].value).toBe("b"); - expect(result.get("items")[0].count).toBe(20); - expect(result.get("items")[1].count).toBe(5); - expect(result.get("items")).toEqual(obj.get("items")); + expect(result.get('items')[0].value).toBe('a'); + expect(result.get('items')[1].value).toBe('b'); + expect(result.get('items')[0].count).toBe(20); + expect(result.get('items')[1].count).toBe(5); + expect(result.get('items')).toEqual(obj.get('items')); }); - it_only_db("mongo")( - "can increment array nested fields missing index", + it_only_db('mongo')( + 'can increment array nested fields missing index', async () => { const obj = new TestObject(); - obj.set("items", []); + obj.set('items', []); await obj.save(); - obj.increment("items.1.count", 15); + obj.increment('items.1.count', 15); await obj.save(); expect(obj.toJSON().items[0]).toBe(null); expect(obj.toJSON().items[1].count).toBe(15); const query = new Parse.Query(TestObject); const result = await query.get(obj.id); - expect(result.get("items")[0]).toBe(null); - expect(result.get("items")[1].count).toBe(15); - expect(result.get("items")).toEqual(obj.get("items")); + expect(result.get('items')[0]).toBe(null); + expect(result.get('items')[1].count).toBe(15); + expect(result.get('items')).toEqual(obj.get('items')); } ); - it_id("44097c6f-d0ca-4dc5-aa8a-3dd2d9ac645a")(it)( - "can query array nested fields", + it_id('44097c6f-d0ca-4dc5-aa8a-3dd2d9ac645a')(it)( + 'can query array nested fields', async () => { const objects = []; for (let i = 0; i < 10; i++) { const obj = new TestObject(); - obj.set("items", [i, { value: i }]); + obj.set('items', [i, { value: i }]); objects.push(obj); } await Parse.Object.saveAll(objects); let query = new Parse.Query(TestObject); - query.greaterThan("items.1.value", 5); + query.greaterThan('items.1.value', 5); let result = await query.find(); expect(result.length).toBe(4); query = new Parse.Query(TestObject); - query.lessThan("items.0", 3); + query.lessThan('items.0', 3); result = await query.find(); expect(result.length).toBe(3); query = new Parse.Query(TestObject); - query.equalTo("items.0", 5); + query.equalTo('items.0', 5); result = await query.find(); expect(result.length).toBe(1); query = new Parse.Query(TestObject); - query.notEqualTo("items.0", 5); + query.notEqualTo('items.0', 5); result = await query.find(); expect(result.length).toBe(9); } ); - it("addUnique with object", function (done) { - const x1 = new Parse.Object("X"); - x1.set("stuff", [1, { hello: "world" }, { foo: "bar" }]); + it('addUnique with object', function (done) { + const x1 = new Parse.Object('X'); + x1.set('stuff', [1, { hello: 'world' }, { foo: 'bar' }]); x1.save() .then(() => { const objectId = x1.id; - const x2 = new Parse.Object("X", { objectId: objectId }); - x2.addUnique("stuff", { hello: "world" }); - x2.addUnique("stuff", { bar: "baz" }); - expect(x2.get("stuff")).toEqual([{ hello: "world" }, { bar: "baz" }]); + const x2 = new Parse.Object('X', { objectId: objectId }); + x2.addUnique('stuff', { hello: 'world' }); + x2.addUnique('stuff', { bar: 'baz' }); + expect(x2.get('stuff')).toEqual([{ hello: 'world' }, { bar: 'baz' }]); return x2.save(); }) .then(() => { - const query = new Parse.Query("X"); + const query = new Parse.Query('X'); return query.get(x1.id); }) .then( x3 => { - const stuff = x3.get("stuff"); + const stuff = x3.get('stuff'); const target = [ 1, - { hello: "world" }, - { foo: "bar" }, - { bar: "baz" }, + { hello: 'world' }, + { foo: 'bar' }, + { bar: 'baz' }, ]; expect(stuff.length).toEqual(target.length); let found = 0; @@ -702,24 +702,24 @@ describe("Parse.Object testing", () => { ); }); - it("removes with object", function (done) { - const x1 = new Parse.Object("X"); - x1.set("stuff", [1, { hello: "world" }, { foo: "bar" }]); + it('removes with object', function (done) { + const x1 = new Parse.Object('X'); + x1.set('stuff', [1, { hello: 'world' }, { foo: 'bar' }]); x1.save() .then(() => { const objectId = x1.id; - const x2 = new Parse.Object("X", { objectId: objectId }); - x2.remove("stuff", { hello: "world" }); - expect(x2.get("stuff")).toEqual([]); + const x2 = new Parse.Object('X', { objectId: objectId }); + x2.remove('stuff', { hello: 'world' }); + expect(x2.get('stuff')).toEqual([]); return x2.save(); }) .then(() => { - const query = new Parse.Query("X"); + const query = new Parse.Query('X'); return query.get(x1.id); }) .then( x3 => { - expect(x3.get("stuff")).toEqual([1, { foo: "bar" }]); + expect(x3.get('stuff')).toEqual([1, { foo: 'bar' }]); done(); }, error => { @@ -729,40 +729,40 @@ describe("Parse.Object testing", () => { ); }); - it("dirty attributes", function (done) { - const object = new Parse.Object("TestObject"); - object.set("cat", "good"); - object.set("dog", "bad"); + it('dirty attributes', function (done) { + const object = new Parse.Object('TestObject'); + object.set('cat', 'good'); + object.set('dog', 'bad'); object.save().then( function (object) { ok(!object.dirty()); - ok(!object.dirty("cat")); - ok(!object.dirty("dog")); + ok(!object.dirty('cat')); + ok(!object.dirty('dog')); - object.set("dog", "okay"); + object.set('dog', 'okay'); ok(object.dirty()); - ok(!object.dirty("cat")); - ok(object.dirty("dog")); + ok(!object.dirty('cat')); + ok(object.dirty('dog')); done(); }, function () { - ok(false, "This should have saved."); + ok(false, 'This should have saved.'); done(); } ); }); - it("dirty keys", function (done) { - const object = new Parse.Object("TestObject"); - object.set("gogo", "good"); - object.set("sito", "sexy"); + it('dirty keys', function (done) { + const object = new Parse.Object('TestObject'); + object.set('gogo', 'good'); + object.set('sito', 'sexy'); ok(object.dirty()); let dirtyKeys = object.dirtyKeys(); equal(dirtyKeys.length, 2); - ok(arrayContains(dirtyKeys, "gogo")); - ok(arrayContains(dirtyKeys, "sito")); + ok(arrayContains(dirtyKeys, 'gogo')); + ok(arrayContains(dirtyKeys, 'sito')); object .save() @@ -770,51 +770,51 @@ describe("Parse.Object testing", () => { ok(!obj.dirty()); dirtyKeys = obj.dirtyKeys(); equal(dirtyKeys.length, 0); - ok(!arrayContains(dirtyKeys, "gogo")); - ok(!arrayContains(dirtyKeys, "sito")); + ok(!arrayContains(dirtyKeys, 'gogo')); + ok(!arrayContains(dirtyKeys, 'sito')); // try removing keys - obj.unset("sito"); + obj.unset('sito'); ok(obj.dirty()); dirtyKeys = obj.dirtyKeys(); equal(dirtyKeys.length, 1); - ok(!arrayContains(dirtyKeys, "gogo")); - ok(arrayContains(dirtyKeys, "sito")); + ok(!arrayContains(dirtyKeys, 'gogo')); + ok(arrayContains(dirtyKeys, 'sito')); return obj.save(); }) .then(function (obj) { ok(!obj.dirty()); - equal(obj.get("gogo"), "good"); - equal(obj.get("sito"), undefined); + equal(obj.get('gogo'), 'good'); + equal(obj.get('sito'), undefined); dirtyKeys = obj.dirtyKeys(); equal(dirtyKeys.length, 0); - ok(!arrayContains(dirtyKeys, "gogo")); - ok(!arrayContains(dirtyKeys, "sito")); + ok(!arrayContains(dirtyKeys, 'gogo')); + ok(!arrayContains(dirtyKeys, 'sito')); done(); }); }); - it("acl attribute", function (done) { - Parse.User.signUp("bob", "password").then(function (user) { - const TestObject = Parse.Object.extend("TestObject"); + it('acl attribute', function (done) { + Parse.User.signUp('bob', 'password').then(function (user) { + const TestObject = Parse.Object.extend('TestObject'); const obj = new TestObject({ ACL: new Parse.ACL(user), // ACLs cause things like validation to run }); - ok(obj.get("ACL") instanceof Parse.ACL); + ok(obj.get('ACL') instanceof Parse.ACL); obj.save().then(function (obj) { - ok(obj.get("ACL") instanceof Parse.ACL); + ok(obj.get('ACL') instanceof Parse.ACL); const query = new Parse.Query(TestObject); query.get(obj.id).then(function (obj) { - ok(obj.get("ACL") instanceof Parse.ACL); + ok(obj.get('ACL') instanceof Parse.ACL); const query = new Parse.Query(TestObject); query.find().then(function (results) { obj = results[0]; - ok(obj.get("ACL") instanceof Parse.ACL); + ok(obj.get('ACL') instanceof Parse.ACL); done(); }); @@ -823,14 +823,14 @@ describe("Parse.Object testing", () => { }); }); - it("cannot save object with invalid field", async () => { - const invalidFields = ["className", "length"]; + it('cannot save object with invalid field', async () => { + const invalidFields = ['className', 'length']; const promises = invalidFields.map(async field => { const obj = new TestObject(); - obj.set(field, "bar"); + obj.set(field, 'bar'); try { await obj.save(); - fail("should not succeed"); + fail('should not succeed'); } catch (e) { expect(e.message).toBe(`Invalid field name: ${field}.`); } @@ -838,252 +838,252 @@ describe("Parse.Object testing", () => { await Promise.all(promises); }); - it("old attribute unset then unset", function (done) { - const TestObject = Parse.Object.extend("TestObject"); + it('old attribute unset then unset', function (done) { + const TestObject = Parse.Object.extend('TestObject'); const obj = new TestObject(); - obj.set("x", 3); + obj.set('x', 3); obj.save().then(function () { - obj.unset("x"); - obj.unset("x"); + obj.unset('x'); + obj.unset('x'); obj.save().then(function () { - equal(obj.has("x"), false); - equal(obj.get("x"), undefined); + equal(obj.has('x'), false); + equal(obj.get('x'), undefined); const query = new Parse.Query(TestObject); query.get(obj.id).then(function (objAgain) { - equal(objAgain.has("x"), false); - equal(objAgain.get("x"), undefined); + equal(objAgain.has('x'), false); + equal(objAgain.get('x'), undefined); done(); }); }); }); }); - it("new attribute unset then unset", function (done) { - const TestObject = Parse.Object.extend("TestObject"); + it('new attribute unset then unset', function (done) { + const TestObject = Parse.Object.extend('TestObject'); const obj = new TestObject(); - obj.set("x", 5); - obj.unset("x"); - obj.unset("x"); + obj.set('x', 5); + obj.unset('x'); + obj.unset('x'); obj.save().then(function () { - equal(obj.has("x"), false); - equal(obj.get("x"), undefined); + equal(obj.has('x'), false); + equal(obj.get('x'), undefined); const query = new Parse.Query(TestObject); query.get(obj.id).then(function (objAgain) { - equal(objAgain.has("x"), false); - equal(objAgain.get("x"), undefined); + equal(objAgain.has('x'), false); + equal(objAgain.get('x'), undefined); done(); }); }); }); - it("unknown attribute unset then unset", function (done) { - const TestObject = Parse.Object.extend("TestObject"); + it('unknown attribute unset then unset', function (done) { + const TestObject = Parse.Object.extend('TestObject'); const obj = new TestObject(); - obj.unset("x"); - obj.unset("x"); + obj.unset('x'); + obj.unset('x'); obj.save().then(function () { - equal(obj.has("x"), false); - equal(obj.get("x"), undefined); + equal(obj.has('x'), false); + equal(obj.get('x'), undefined); const query = new Parse.Query(TestObject); query.get(obj.id).then(function (objAgain) { - equal(objAgain.has("x"), false); - equal(objAgain.get("x"), undefined); + equal(objAgain.has('x'), false); + equal(objAgain.get('x'), undefined); done(); }); }); }); - it("old attribute unset then clear", function (done) { - const TestObject = Parse.Object.extend("TestObject"); + it('old attribute unset then clear', function (done) { + const TestObject = Parse.Object.extend('TestObject'); const obj = new TestObject(); - obj.set("x", 3); + obj.set('x', 3); obj.save().then(function () { - obj.unset("x"); + obj.unset('x'); obj.clear(); obj.save().then(function () { - equal(obj.has("x"), false); - equal(obj.get("x"), undefined); + equal(obj.has('x'), false); + equal(obj.get('x'), undefined); const query = new Parse.Query(TestObject); query.get(obj.id).then(function (objAgain) { - equal(objAgain.has("x"), false); - equal(objAgain.get("x"), undefined); + equal(objAgain.has('x'), false); + equal(objAgain.get('x'), undefined); done(); }); }); }); }); - it("new attribute unset then clear", function (done) { - const TestObject = Parse.Object.extend("TestObject"); + it('new attribute unset then clear', function (done) { + const TestObject = Parse.Object.extend('TestObject'); const obj = new TestObject(); - obj.set("x", 5); - obj.unset("x"); + obj.set('x', 5); + obj.unset('x'); obj.clear(); obj.save().then(function () { - equal(obj.has("x"), false); - equal(obj.get("x"), undefined); + equal(obj.has('x'), false); + equal(obj.get('x'), undefined); const query = new Parse.Query(TestObject); query.get(obj.id).then(function (objAgain) { - equal(objAgain.has("x"), false); - equal(objAgain.get("x"), undefined); + equal(objAgain.has('x'), false); + equal(objAgain.get('x'), undefined); done(); }); }); }); - it("unknown attribute unset then clear", function (done) { - const TestObject = Parse.Object.extend("TestObject"); + it('unknown attribute unset then clear', function (done) { + const TestObject = Parse.Object.extend('TestObject'); const obj = new TestObject(); - obj.unset("x"); + obj.unset('x'); obj.clear(); obj.save().then(function () { - equal(obj.has("x"), false); - equal(obj.get("x"), undefined); + equal(obj.has('x'), false); + equal(obj.get('x'), undefined); const query = new Parse.Query(TestObject); query.get(obj.id).then(function (objAgain) { - equal(objAgain.has("x"), false); - equal(objAgain.get("x"), undefined); + equal(objAgain.has('x'), false); + equal(objAgain.get('x'), undefined); done(); }); }); }); - it("old attribute clear then unset", function (done) { - const TestObject = Parse.Object.extend("TestObject"); + it('old attribute clear then unset', function (done) { + const TestObject = Parse.Object.extend('TestObject'); const obj = new TestObject(); - obj.set("x", 3); + obj.set('x', 3); obj.save().then(function () { obj.clear(); - obj.unset("x"); + obj.unset('x'); obj.save().then(function () { - equal(obj.has("x"), false); - equal(obj.get("x"), undefined); + equal(obj.has('x'), false); + equal(obj.get('x'), undefined); const query = new Parse.Query(TestObject); query.get(obj.id).then(function (objAgain) { - equal(objAgain.has("x"), false); - equal(objAgain.get("x"), undefined); + equal(objAgain.has('x'), false); + equal(objAgain.get('x'), undefined); done(); }); }); }); }); - it("new attribute clear then unset", function (done) { - const TestObject = Parse.Object.extend("TestObject"); + it('new attribute clear then unset', function (done) { + const TestObject = Parse.Object.extend('TestObject'); const obj = new TestObject(); - obj.set("x", 5); + obj.set('x', 5); obj.clear(); - obj.unset("x"); + obj.unset('x'); obj.save().then(function () { - equal(obj.has("x"), false); - equal(obj.get("x"), undefined); + equal(obj.has('x'), false); + equal(obj.get('x'), undefined); const query = new Parse.Query(TestObject); query.get(obj.id).then(function (objAgain) { - equal(objAgain.has("x"), false); - equal(objAgain.get("x"), undefined); + equal(objAgain.has('x'), false); + equal(objAgain.get('x'), undefined); done(); }); }); }); - it("unknown attribute clear then unset", function (done) { - const TestObject = Parse.Object.extend("TestObject"); + it('unknown attribute clear then unset', function (done) { + const TestObject = Parse.Object.extend('TestObject'); const obj = new TestObject(); obj.clear(); - obj.unset("x"); + obj.unset('x'); obj.save().then(function () { - equal(obj.has("x"), false); - equal(obj.get("x"), undefined); + equal(obj.has('x'), false); + equal(obj.get('x'), undefined); const query = new Parse.Query(TestObject); query.get(obj.id).then(function (objAgain) { - equal(objAgain.has("x"), false); - equal(objAgain.get("x"), undefined); + equal(objAgain.has('x'), false); + equal(objAgain.get('x'), undefined); done(); }); }); }); - it("old attribute clear then clear", function (done) { - const TestObject = Parse.Object.extend("TestObject"); + it('old attribute clear then clear', function (done) { + const TestObject = Parse.Object.extend('TestObject'); const obj = new TestObject(); - obj.set("x", 3); + obj.set('x', 3); obj.save().then(function () { obj.clear(); obj.clear(); obj.save().then(function () { - equal(obj.has("x"), false); - equal(obj.get("x"), undefined); + equal(obj.has('x'), false); + equal(obj.get('x'), undefined); const query = new Parse.Query(TestObject); query.get(obj.id).then(function (objAgain) { - equal(objAgain.has("x"), false); - equal(objAgain.get("x"), undefined); + equal(objAgain.has('x'), false); + equal(objAgain.get('x'), undefined); done(); }); }); }); }); - it("new attribute clear then clear", function (done) { - const TestObject = Parse.Object.extend("TestObject"); + it('new attribute clear then clear', function (done) { + const TestObject = Parse.Object.extend('TestObject'); const obj = new TestObject(); - obj.set("x", 5); + obj.set('x', 5); obj.clear(); obj.clear(); obj.save().then(function () { - equal(obj.has("x"), false); - equal(obj.get("x"), undefined); + equal(obj.has('x'), false); + equal(obj.get('x'), undefined); const query = new Parse.Query(TestObject); query.get(obj.id).then(function (objAgain) { - equal(objAgain.has("x"), false); - equal(objAgain.get("x"), undefined); + equal(objAgain.has('x'), false); + equal(objAgain.get('x'), undefined); done(); }); }); }); - it("unknown attribute clear then clear", function (done) { - const TestObject = Parse.Object.extend("TestObject"); + it('unknown attribute clear then clear', function (done) { + const TestObject = Parse.Object.extend('TestObject'); const obj = new TestObject(); obj.clear(); obj.clear(); obj.save().then(function () { - equal(obj.has("x"), false); - equal(obj.get("x"), undefined); + equal(obj.has('x'), false); + equal(obj.get('x'), undefined); const query = new Parse.Query(TestObject); query.get(obj.id).then(function (objAgain) { - equal(objAgain.has("x"), false); - equal(objAgain.get("x"), undefined); + equal(objAgain.has('x'), false); + equal(objAgain.get('x'), undefined); done(); }); }); }); - it("saving children in an array", function (done) { - const Parent = Parse.Object.extend("Parent"); - const Child = Parse.Object.extend("Child"); + it('saving children in an array', function (done) { + const Parent = Parse.Object.extend('Parent'); + const Child = Parse.Object.extend('Child'); const child1 = new Child(); const child2 = new Child(); const parent = new Parent(); - child1.set("name", "jamie"); - child2.set("name", "cersei"); - parent.set("children", [child1, child2]); + child1.set('name', 'jamie'); + child2.set('name', 'cersei'); + parent.set('children', [child1, child2]); parent.save().then(function () { const query = new Parse.Query(Child); - query.ascending("name"); + query.ascending('name'); query.find().then(function (results) { equal(results.length, 2); - equal(results[0].get("name"), "cersei"); - equal(results[1].get("name"), "jamie"); + equal(results[0].get('name'), 'cersei'); + equal(results[1].get('name'), 'jamie'); done(); }); }, done.fail); }); - it("two saves at the same time", function (done) { - const object = new Parse.Object("TestObject"); + it('two saves at the same time', function (done) { + const object = new Parse.Object('TestObject'); let firstSave = true; const success = function () { @@ -1092,17 +1092,17 @@ describe("Parse.Object testing", () => { return; } - const query = new Parse.Query("TestObject"); + const query = new Parse.Query('TestObject'); query.find().then(function (results) { equal(results.length, 1); - equal(results[0].get("cat"), "meow"); - equal(results[0].get("dog"), "bark"); + equal(results[0].get('cat'), 'meow'); + equal(results[0].get('dog'), 'bark'); done(); }); }; - object.save({ cat: "meow" }).then(success, fail); - object.save({ dog: "bark" }).then(success, fail); + object.save({ cat: 'meow' }).then(success, fail); + object.save({ dog: 'bark' }).then(success, fail); }); // The schema-checking parts of this are working. @@ -1110,27 +1110,27 @@ describe("Parse.Object testing", () => { // typed field and saved okay, since that appears to be borked in // the client. // If this fails, it's probably a schema issue. - it("many saves after a failure", function (done) { + it('many saves after a failure', function (done) { // Make a class with a number in the schema. - const o1 = new Parse.Object("TestObject"); - o1.set("number", 1); + const o1 = new Parse.Object('TestObject'); + o1.set('number', 1); let object = null; o1.save() .then(() => { - object = new Parse.Object("TestObject"); - object.set("number", "two"); + object = new Parse.Object('TestObject'); + object.set('number', 'two'); return object.save(); }) .then(fail, error => { expect(error.code).toEqual(Parse.Error.INCORRECT_TYPE); - object.set("other", "foo"); + object.set('other', 'foo'); return object.save(); }) .then(fail, error => { expect(error.code).toEqual(Parse.Error.INCORRECT_TYPE); - object.set("other", "bar"); + object.set('other', 'bar'); return object.save(); }) .then(fail, error => { @@ -1140,35 +1140,35 @@ describe("Parse.Object testing", () => { }); }); - it("is not dirty after save", function (done) { - const obj = new Parse.Object("TestObject"); + it('is not dirty after save', function (done) { + const obj = new Parse.Object('TestObject'); obj.save().then(function () { - obj.set({ content: "x" }); + obj.set({ content: 'x' }); obj.fetch().then(function () { - equal(false, obj.dirty("content")); + equal(false, obj.dirty('content')); done(); }); }); }); - it("add with an object", function (done) { - const child = new Parse.Object("Person"); - const parent = new Parse.Object("Person"); + it('add with an object', function (done) { + const child = new Parse.Object('Person'); + const parent = new Parse.Object('Person'); Promise.resolve() .then(function () { return child.save(); }) .then(function () { - parent.add("children", child); + parent.add('children', child); return parent.save(); }) .then(function () { - const query = new Parse.Query("Person"); + const query = new Parse.Query('Person'); return query.get(parent.id); }) .then(function (parentAgain) { - equal(parentAgain.get("children")[0].id, child.id); + equal(parentAgain.get('children')[0].id, child.id); }) .then( function () { @@ -1181,8 +1181,8 @@ describe("Parse.Object testing", () => { ); }); - it("toJSON saved object", function (done) { - create({ foo: "bar" }, function (model) { + it('toJSON saved object', function (done) { + create({ foo: 'bar' }, function (model) { const objJSON = model.toJSON(); ok(objJSON.foo, "expected json to contain key 'foo'"); ok(objJSON.objectId, "expected json to contain key 'objectId'"); @@ -1192,36 +1192,36 @@ describe("Parse.Object testing", () => { }); }); - it("remove object from array", function (done) { + it('remove object from array', function (done) { const obj = new TestObject(); obj.save().then(function () { const container = new TestObject(); - container.add("array", obj); - equal(container.get("array").length, 1); + container.add('array', obj); + equal(container.get('array').length, 1); container.save(null).then(function () { const objAgain = new TestObject(); objAgain.id = obj.id; - container.remove("array", objAgain); - equal(container.get("array").length, 0); + container.remove('array', objAgain); + equal(container.get('array').length, 0); done(); }); }); }); - it("async methods", function (done) { + it('async methods', function (done) { const obj = new TestObject(); - obj.set("time", "adventure"); + obj.set('time', 'adventure'); obj .save() .then(function (obj) { - ok(obj.id, "objectId should not be null."); + ok(obj.id, 'objectId should not be null.'); const objAgain = new TestObject(); objAgain.id = obj.id; return objAgain.fetch(); }) .then(function (objAgain) { - equal(objAgain.get("time"), "adventure"); + equal(objAgain.get('time'), 'adventure'); return objAgain.destroy(); }) .then(function () { @@ -1236,11 +1236,11 @@ describe("Parse.Object testing", () => { }); }); - it("fail validation with promise", function (done) { - const PickyEater = Parse.Object.extend("PickyEater", { + it('fail validation with promise', function (done) { + const PickyEater = Parse.Object.extend('PickyEater', { validate: function (attrs) { - if (attrs.meal === "tomatoes") { - return "Ew. Tomatoes are gross."; + if (attrs.meal === 'tomatoes') { + return 'Ew. Tomatoes are gross.'; } return Parse.Object.prototype.validate.apply(this, arguments); }, @@ -1249,24 +1249,24 @@ describe("Parse.Object testing", () => { const bryan = new PickyEater(); bryan .save({ - meal: "burrito", + meal: 'burrito', }) .then( function () { return bryan.save({ - meal: "tomatoes", + meal: 'tomatoes', }); }, function () { - ok(false, "Save should have succeeded."); + ok(false, 'Save should have succeeded.'); } ) .then( function () { - ok(false, "Save should have failed."); + ok(false, 'Save should have failed.'); }, function (error) { - equal(error, "Ew. Tomatoes are gross."); + equal(error, 'Ew. Tomatoes are gross.'); done(); } ); @@ -1277,17 +1277,17 @@ describe("Parse.Object testing", () => { const r = restController.request; restController.request = function () { return r.apply(this, arguments).then(function (result) { - result.aDate = { __type: "Date", iso: "2014-06-24T06:06:06.452Z" }; + result.aDate = { __type: 'Date', iso: '2014-06-24T06:06:06.452Z' }; return result; }); }; - const obj = new Parse.Object("Thing"); + const obj = new Parse.Object('Thing'); obj .save() .then(function () { - ok(!obj.dirty(), "The object should not be dirty"); - ok(obj.get("aDate")); + ok(!obj.dirty(), 'The object should not be dirty'); + ok(obj.get('aDate')); }) .then(function () { restController.request = r; @@ -1300,21 +1300,21 @@ describe("Parse.Object testing", () => { const r = restController.request; restController.request = function () { return r.apply(restController, arguments).then(function (result) { - result.aDate = { __type: "Date", iso: "2014-06-24T06:06:06.452Z" }; + result.aDate = { __type: 'Date', iso: '2014-06-24T06:06:06.452Z' }; return result; }); }; const now = new Date(); - const obj = new Parse.Object("Thing"); + const obj = new Parse.Object('Thing'); const promise = obj.save(); - obj.set("aDate", now); + obj.set('aDate', now); promise .then(function () { - ok(obj.dirty(), "The object should be dirty"); - equal(now, obj.get("aDate")); + ok(obj.dirty(), 'The object should be dirty'); + equal(now, obj.get('aDate')); }) .then(function () { restController.request = r; @@ -1322,11 +1322,11 @@ describe("Parse.Object testing", () => { }); }); - it("bytes work", function (done) { + it('bytes work', function (done) { Promise.resolve() .then(function () { const obj = new TestObject(); - obj.set("bytes", { __type: "Bytes", base64: "ZnJveW8=" }); + obj.set('bytes', { __type: 'Bytes', base64: 'ZnJveW8=' }); return obj.save(); }) .then(function (obj) { @@ -1335,8 +1335,8 @@ describe("Parse.Object testing", () => { }) .then( function (obj) { - equal(obj.get("bytes").__type, "Bytes"); - equal(obj.get("bytes").base64, "ZnJveW8="); + equal(obj.get('bytes').__type, 'Bytes'); + equal(obj.get('bytes').base64, 'ZnJveW8='); done(); }, function (error) { @@ -1346,37 +1346,37 @@ describe("Parse.Object testing", () => { ); }); - it("destroyAll no objects", function (done) { + it('destroyAll no objects', function (done) { Parse.Object.destroyAll([]) .then(function (success) { - ok(success, "Should be able to destroy no objects"); + ok(success, 'Should be able to destroy no objects'); done(); }) .catch(done.fail); }); - it("destroyAll new objects only", function (done) { + it('destroyAll new objects only', function (done) { const objects = [new TestObject(), new TestObject()]; Parse.Object.destroyAll(objects) .then(function (success) { - ok(success, "Should be able to destroy only new objects"); + ok(success, 'Should be able to destroy only new objects'); done(); }) .catch(done.fail); }); - it("fetchAll", function (done) { + it('fetchAll', function (done) { const numItems = 11; const container = new Container(); const items = []; for (let i = 0; i < numItems; i++) { const item = new Item(); - item.set("x", i); + item.set('x', i); items.push(item); } Parse.Object.saveAll(items) .then(function () { - container.set("items", items); + container.set('items', items); return container.save(); }) .then(function () { @@ -1384,16 +1384,16 @@ describe("Parse.Object testing", () => { return query.get(container.id); }) .then(function (containerAgain) { - const itemsAgain = containerAgain.get("items"); + const itemsAgain = containerAgain.get('items'); if (!itemsAgain || !itemsAgain.forEach) { - fail("no itemsAgain retrieved", itemsAgain); + fail('no itemsAgain retrieved', itemsAgain); done(); return; } - equal(itemsAgain.length, numItems, "Should get the array back"); + equal(itemsAgain.length, numItems, 'Should get the array back'); itemsAgain.forEach(function (item, i) { const newValue = i * 2; - item.set("x", newValue); + item.set('x', newValue); }); return Parse.Object.saveAll(itemsAgain); }) @@ -1404,28 +1404,28 @@ describe("Parse.Object testing", () => { equal( fetchedItemsAgain.length, numItems, - "Number of items fetched should not change" + 'Number of items fetched should not change' ); fetchedItemsAgain.forEach(function (item, i) { - equal(item.get("x"), i * 2); + equal(item.get('x'), i * 2); }); done(); }); }); - it("fetchAll no objects", function (done) { + it('fetchAll no objects', function (done) { Parse.Object.fetchAll([]) .then(function (success) { - ok(Array.isArray(success), "Should be able to fetchAll no objects"); + ok(Array.isArray(success), 'Should be able to fetchAll no objects'); done(); }) .catch(done.fail); }); - it("fetchAll updates dates", function (done) { + it('fetchAll updates dates', function (done) { let updatedObject; const object = new TestObject(); - object.set("x", 7); + object.set('x', 7); object .save() .then(function () { @@ -1434,7 +1434,7 @@ describe("Parse.Object testing", () => { }) .then(function (results) { updatedObject = results[0]; - updatedObject.set("x", 11); + updatedObject.set('x', 11); return updatedObject.save(); }) .then(function () { @@ -1447,18 +1447,18 @@ describe("Parse.Object testing", () => { }); }); - xit("fetchAll backbone-style callbacks", function (done) { + xit('fetchAll backbone-style callbacks', function (done) { const numItems = 11; const container = new Container(); const items = []; for (let i = 0; i < numItems; i++) { const item = new Item(); - item.set("x", i); + item.set('x', i); items.push(item); } Parse.Object.saveAll(items) .then(function () { - container.set("items", items); + container.set('items', items); return container.save(); }) .then(function () { @@ -1466,16 +1466,16 @@ describe("Parse.Object testing", () => { return query.get(container.id); }) .then(function (containerAgain) { - const itemsAgain = containerAgain.get("items"); + const itemsAgain = containerAgain.get('items'); if (!itemsAgain || !itemsAgain.forEach) { - fail("no itemsAgain retrieved", itemsAgain); + fail('no itemsAgain retrieved', itemsAgain); done(); return; } - equal(itemsAgain.length, numItems, "Should get the array back"); + equal(itemsAgain.length, numItems, 'Should get the array back'); itemsAgain.forEach(function (item, i) { const newValue = i * 2; - item.set("x", newValue); + item.set('x', newValue); }); return Parse.Object.saveAll(itemsAgain); }) @@ -1485,25 +1485,25 @@ describe("Parse.Object testing", () => { equal( fetchedItemsAgain.length, numItems, - "Number of items fetched should not change" + 'Number of items fetched should not change' ); fetchedItemsAgain.forEach(function (item, i) { - equal(item.get("x"), i * 2); + equal(item.get('x'), i * 2); }); done(); }, function () { - ok(false, "Failed to fetchAll"); + ok(false, 'Failed to fetchAll'); done(); } ); }); }); - it("fetchAll error on multiple classes", function (done) { + it('fetchAll error on multiple classes', function (done) { const container = new Container(); - container.set("item", new Item()); - container.set("subcontainer", new Container()); + container.set('item', new Item()); + container.set('subcontainer', new Container()); return container .save() .then(function () { @@ -1511,8 +1511,8 @@ describe("Parse.Object testing", () => { return query.get(container.id); }) .then(function (containerAgain) { - const subContainerAgain = containerAgain.get("subcontainer"); - const itemAgain = containerAgain.get("item"); + const subContainerAgain = containerAgain.get('subcontainer'); + const itemAgain = containerAgain.get('item'); const multiClassArray = [subContainerAgain, itemAgain]; return Parse.Object.fetchAll(multiClassArray).catch(e => { expect(e.code).toBe(Parse.Error.INVALID_CLASS_NAME); @@ -1521,7 +1521,7 @@ describe("Parse.Object testing", () => { }); }); - it("fetchAll error on unsaved object", async function (done) { + it('fetchAll error on unsaved object', async function (done) { const unsavedObjectArray = [new TestObject()]; await Parse.Object.fetchAll(unsavedObjectArray).catch(e => { expect(e.code).toBe(Parse.Error.MISSING_OBJECT_ID); @@ -1529,12 +1529,12 @@ describe("Parse.Object testing", () => { }); }); - it("fetchAll error on deleted object", function (done) { + it('fetchAll error on deleted object', function (done) { const numItems = 11; const items = []; for (let i = 0; i < numItems; i++) { const item = new Item(); - item.set("x", i); + item.set('x', i); items.push(item); } Parse.Object.saveAll(items) @@ -1557,12 +1557,12 @@ describe("Parse.Object testing", () => { // TODO: Verify that with Sessions, this test is wrong... A fetch on // user should not bring down a session token. - xit("fetchAll User attributes get merged", function (done) { + xit('fetchAll User attributes get merged', function (done) { let sameUser; let user = new Parse.User(); - user.set("username", "asdf"); - user.set("password", "zxcv"); - user.set("foo", "bar"); + user.set('username', 'asdf'); + user.set('password', 'zxcv'); + user.set('foo', 'bar'); user .signUp() .then(function () { @@ -1573,14 +1573,14 @@ describe("Parse.Object testing", () => { .then(function (userAgain) { user = userAgain; sameUser = new Parse.User(); - sameUser.set("username", "asdf"); - sameUser.set("password", "zxcv"); + sameUser.set('username', 'asdf'); + sameUser.set('password', 'zxcv'); return sameUser.logIn(); }) .then(function () { - ok(!user.getSessionToken(), "user should not have a sessionToken"); - ok(sameUser.getSessionToken(), "sameUser should have a sessionToken"); - sameUser.set("baz", "qux"); + ok(!user.getSessionToken(), 'user should not have a sessionToken'); + ok(sameUser.getSessionToken(), 'sameUser should have a sessionToken'); + sameUser.set('baz', 'qux'); return sameUser.save(); }) .then(function () { @@ -1595,18 +1595,18 @@ describe("Parse.Object testing", () => { }); }); - it("fetchAllIfNeeded", function (done) { + it('fetchAllIfNeeded', function (done) { const numItems = 11; const container = new Container(); const items = []; for (let i = 0; i < numItems; i++) { const item = new Item(); - item.set("x", i); + item.set('x', i); items.push(item); } Parse.Object.saveAll(items) .then(function () { - container.set("items", items); + container.set('items', items); return container.save(); }) .then(function () { @@ -1614,14 +1614,14 @@ describe("Parse.Object testing", () => { return query.get(container.id); }) .then(function (containerAgain) { - const itemsAgain = containerAgain.get("items"); + const itemsAgain = containerAgain.get('items'); if (!itemsAgain || !itemsAgain.forEach) { - fail("no itemsAgain retrieved", itemsAgain); + fail('no itemsAgain retrieved', itemsAgain); done(); return; } itemsAgain.forEach(function (item, i) { - item.set("x", i * 2); + item.set('x', i * 2); }); return Parse.Object.saveAll(itemsAgain); }) @@ -1632,27 +1632,27 @@ describe("Parse.Object testing", () => { equal( fetchedItems.length, numItems, - "Number of items should not change" + 'Number of items should not change' ); fetchedItems.forEach(function (item, i) { - equal(item.get("x"), i); + equal(item.get('x'), i); }); done(); }); }); - xit("fetchAllIfNeeded backbone-style callbacks", function (done) { + xit('fetchAllIfNeeded backbone-style callbacks', function (done) { const numItems = 11; const container = new Container(); const items = []; for (let i = 0; i < numItems; i++) { const item = new Item(); - item.set("x", i); + item.set('x', i); items.push(item); } Parse.Object.saveAll(items) .then(function () { - container.set("items", items); + container.set('items', items); return container.save(); }) .then(function () { @@ -1660,49 +1660,49 @@ describe("Parse.Object testing", () => { return query.get(container.id); }) .then(function (containerAgain) { - const itemsAgain = containerAgain.get("items"); + const itemsAgain = containerAgain.get('items'); if (!itemsAgain || !itemsAgain.forEach) { - fail("no itemsAgain retrieved", itemsAgain); + fail('no itemsAgain retrieved', itemsAgain); done(); return; } itemsAgain.forEach(function (item, i) { - item.set("x", i * 2); + item.set('x', i * 2); }); return Parse.Object.saveAll(itemsAgain); }) .then(function () { - const items = container.get("items"); + const items = container.get('items'); return Parse.Object.fetchAllIfNeeded(items).then( function (fetchedItems) { equal( fetchedItems.length, numItems, - "Number of items should not change" + 'Number of items should not change' ); fetchedItems.forEach(function (item, j) { - equal(item.get("x"), j); + equal(item.get('x'), j); }); done(); }, function () { - ok(false, "Failed to fetchAll"); + ok(false, 'Failed to fetchAll'); done(); } ); }); }); - it("fetchAllIfNeeded no objects", function (done) { + it('fetchAllIfNeeded no objects', function (done) { Parse.Object.fetchAllIfNeeded([]) .then(function (success) { - ok(Array.isArray(success), "Should be able to fetchAll no objects"); + ok(Array.isArray(success), 'Should be able to fetchAll no objects'); done(); }) .catch(done.fail); }); - it("fetchAllIfNeeded unsaved object", async function (done) { + it('fetchAllIfNeeded unsaved object', async function (done) { const unsavedObjectArray = [new TestObject()]; await Parse.Object.fetchAllIfNeeded(unsavedObjectArray).catch(e => { expect(e.code).toBe(Parse.Error.MISSING_OBJECT_ID); @@ -1710,10 +1710,10 @@ describe("Parse.Object testing", () => { }); }); - it("fetchAllIfNeeded error on multiple classes", function (done) { + it('fetchAllIfNeeded error on multiple classes', function (done) { const container = new Container(); - container.set("item", new Item()); - container.set("subcontainer", new Container()); + container.set('item', new Item()); + container.set('subcontainer', new Container()); return container .save() .then(function () { @@ -1721,8 +1721,8 @@ describe("Parse.Object testing", () => { return query.get(container.id); }) .then(function (containerAgain) { - const subContainerAgain = containerAgain.get("subcontainer"); - const itemAgain = containerAgain.get("item"); + const subContainerAgain = containerAgain.get('subcontainer'); + const itemAgain = containerAgain.get('item'); const multiClassArray = [subContainerAgain, itemAgain]; return Parse.Object.fetchAllIfNeeded(multiClassArray).catch(e => { expect(e.code).toBe(Parse.Error.INVALID_CLASS_NAME); @@ -1731,52 +1731,52 @@ describe("Parse.Object testing", () => { }); }); - it("Objects with className User", function (done) { - equal(Parse.CoreManager.get("PERFORM_USER_REWRITE"), true); + it('Objects with className User', function (done) { + equal(Parse.CoreManager.get('PERFORM_USER_REWRITE'), true); const User1 = Parse.Object.extend({ - className: "User", + className: 'User', }); - equal(User1.className, "_User", "className is rewritten by default"); + equal(User1.className, '_User', 'className is rewritten by default'); Parse.User.allowCustomUserClass(true); - equal(Parse.CoreManager.get("PERFORM_USER_REWRITE"), false); + equal(Parse.CoreManager.get('PERFORM_USER_REWRITE'), false); const User2 = Parse.Object.extend({ - className: "User", + className: 'User', }); equal( User2.className, - "User", - "className is not rewritten when allowCustomUserClass(true)" + 'User', + 'className is not rewritten when allowCustomUserClass(true)' ); // Set back to default so as not to break other tests. Parse.User.allowCustomUserClass(false); equal( - Parse.CoreManager.get("PERFORM_USER_REWRITE"), + Parse.CoreManager.get('PERFORM_USER_REWRITE'), true, - "PERFORM_USER_REWRITE is reset" + 'PERFORM_USER_REWRITE is reset' ); const user = new User2(); - user.set("name", "Me"); + user.set('name', 'Me'); user.save({ height: 181 }).then(function (user) { - equal(user.get("name"), "Me"); - equal(user.get("height"), 181); + equal(user.get('name'), 'Me'); + equal(user.get('height'), 181); const query = new Parse.Query(User2); query.get(user.id).then(function (user) { - equal(user.className, "User"); - equal(user.get("name"), "Me"); - equal(user.get("height"), 181); + equal(user.className, 'User'); + equal(user.get('name'), 'Me'); + equal(user.get('height'), 181); done(); }); }); }); - it("create without data", function (done) { - const t1 = new TestObject({ test: "test" }); + it('create without data', function (done) { + const t1 = new TestObject({ test: 'test' }); t1.save() .then(function (t1) { const t2 = TestObject.createWithoutData(t1.id); @@ -1784,19 +1784,19 @@ describe("Parse.Object testing", () => { }) .then(function (t2) { equal( - t2.get("test"), - "test", - "Fetch should have grabbed " + "'test' property." + t2.get('test'), + 'test', + 'Fetch should have grabbed ' + "'test' property." ); const t3 = TestObject.createWithoutData(t2.id); - t3.set("test", "not test"); + t3.set('test', 'not test'); return t3.fetch(); }) .then( function (t3) { equal( - t3.get("test"), - "test", + t3.get('test'), + 'test', "Fetch should have grabbed server 'test' property." ); done(); @@ -1808,36 +1808,36 @@ describe("Parse.Object testing", () => { ); }); - it("remove from new field creates array key", done => { + it('remove from new field creates array key', done => { const obj = new TestObject(); - obj.remove("shouldBeArray", "foo"); + obj.remove('shouldBeArray', 'foo'); obj .save() .then(() => { - const query = new Parse.Query("TestObject"); + const query = new Parse.Query('TestObject'); return query.get(obj.id); }) .then(objAgain => { - const arr = objAgain.get("shouldBeArray"); - ok(Array.isArray(arr), "Should have created array key"); - ok(!arr || arr.length === 0, "Should have an empty array."); + const arr = objAgain.get('shouldBeArray'); + ok(Array.isArray(arr), 'Should have created array key'); + ok(!arr || arr.length === 0, 'Should have an empty array.'); done(); }); }); - it("increment with type conflict fails", done => { + it('increment with type conflict fails', done => { const obj = new TestObject(); - obj.set("astring", "foo"); + obj.set('astring', 'foo'); obj .save() .then(() => { const obj2 = new TestObject(); - obj2.increment("astring"); + obj2.increment('astring'); return obj2.save(); }) .then( () => { - fail("Should not have saved."); + fail('Should not have saved.'); done(); }, error => { @@ -1847,19 +1847,19 @@ describe("Parse.Object testing", () => { ); }); - it("increment with empty field solidifies type", done => { + it('increment with empty field solidifies type', done => { const obj = new TestObject(); - obj.increment("aninc"); + obj.increment('aninc'); obj .save() .then(() => { const obj2 = new TestObject(); - obj2.set("aninc", "foo"); + obj2.set('aninc', 'foo'); return obj2.save(); }) .then( () => { - fail("Should not have saved."); + fail('Should not have saved.'); done(); }, error => { @@ -1869,20 +1869,20 @@ describe("Parse.Object testing", () => { ); }); - it("increment update with type conflict fails", done => { + it('increment update with type conflict fails', done => { const obj = new TestObject(); - obj.set("someString", "foo"); + obj.set('someString', 'foo'); obj .save() .then(objAgain => { const obj2 = new TestObject(); obj2.id = objAgain.id; - obj2.increment("someString"); + obj2.increment('someString'); return obj2.save(); }) .then( () => { - fail("Should not have saved."); + fail('Should not have saved.'); done(); }, error => { @@ -1892,48 +1892,48 @@ describe("Parse.Object testing", () => { ); }); - it("dictionary fetched pointers do not lose data on fetch", done => { - const parent = new Parse.Object("Parent"); + it('dictionary fetched pointers do not lose data on fetch', done => { + const parent = new Parse.Object('Parent'); const dict = {}; for (let i = 0; i < 5; i++) { const proc = iter => { - const child = new Parse.Object("Child"); - child.set("name", "testname" + i); + const child = new Parse.Object('Child'); + child.set('name', 'testname' + i); dict[iter] = child; }; proc(i); } - parent.set("childDict", dict); + parent.set('childDict', dict); parent .save() .then(() => { return parent.fetch(); }) .then(parentAgain => { - const dictAgain = parentAgain.get("childDict"); + const dictAgain = parentAgain.get('childDict'); if (!dictAgain) { - fail("Should have been a dictionary."); + fail('Should have been a dictionary.'); return done(); } - expect(typeof dictAgain).toEqual("object"); - expect(typeof dictAgain["0"]).toEqual("object"); - expect(typeof dictAgain["1"]).toEqual("object"); - expect(typeof dictAgain["2"]).toEqual("object"); - expect(typeof dictAgain["3"]).toEqual("object"); - expect(typeof dictAgain["4"]).toEqual("object"); + expect(typeof dictAgain).toEqual('object'); + expect(typeof dictAgain['0']).toEqual('object'); + expect(typeof dictAgain['1']).toEqual('object'); + expect(typeof dictAgain['2']).toEqual('object'); + expect(typeof dictAgain['3']).toEqual('object'); + expect(typeof dictAgain['4']).toEqual('object'); done(); }); }); - it("should create nested keys with _", done => { - const object = new Parse.Object("AnObject"); - object.set("foo", { - _bar: "_", + it('should create nested keys with _', done => { + const object = new Parse.Object('AnObject'); + object.set('foo', { + _bar: '_', baz_bar: 1, __foo_bar: true, - _0: "underscore_zero", + _0: 'underscore_zero', _more: { - _nested: "key", + _nested: 'key', }, }); object @@ -1943,58 +1943,58 @@ describe("Parse.Object testing", () => { return res.fetch(); }) .then(res => { - const foo = res.get("foo"); - expect(foo["_bar"]).toEqual("_"); - expect(foo["baz_bar"]).toEqual(1); - expect(foo["__foo_bar"]).toBe(true); - expect(foo["_0"]).toEqual("underscore_zero"); - expect(foo["_more"]["_nested"]).toEqual("key"); + const foo = res.get('foo'); + expect(foo['_bar']).toEqual('_'); + expect(foo['baz_bar']).toEqual(1); + expect(foo['__foo_bar']).toBe(true); + expect(foo['_0']).toEqual('underscore_zero'); + expect(foo['_more']['_nested']).toEqual('key'); done(); }) .catch(err => { jfail(err); - fail("should not fail"); + fail('should not fail'); done(); }); }); - it("should have undefined includes when object is missing", done => { - const obj1 = new Parse.Object("AnObject"); - const obj2 = new Parse.Object("AnObject"); + it('should have undefined includes when object is missing', done => { + const obj1 = new Parse.Object('AnObject'); + const obj2 = new Parse.Object('AnObject'); Parse.Object.saveAll([obj1, obj2]) .then(() => { - obj1.set("obj", obj2); + obj1.set('obj', obj2); // Save the pointer, delete the pointee return obj1.save().then(() => { return obj2.destroy(); }); }) .then(() => { - const query = new Parse.Query("AnObject"); - query.include("obj"); + const query = new Parse.Query('AnObject'); + query.include('obj'); return query.find(); }) .then(res => { expect(res.length).toBe(1); if (res[0]) { - expect(res[0].get("obj")).toBe(undefined); + expect(res[0].get('obj')).toBe(undefined); } - const query = new Parse.Query("AnObject"); + const query = new Parse.Query('AnObject'); return query.find(); }) .then(res => { expect(res.length).toBe(1); if (res[0]) { - expect(res[0].get("obj")).not.toBe(undefined); - return res[0].get("obj").fetch(); + expect(res[0].get('obj')).not.toBe(undefined); + return res[0].get('obj').fetch(); } else { done(); } }) .then( () => { - fail("Should not fetch a deleted object"); + fail('Should not fetch a deleted object'); }, err => { expect(err.code).toBe(Parse.Error.OBJECT_NOT_FOUND); @@ -2003,27 +2003,27 @@ describe("Parse.Object testing", () => { ); }); - it("should have undefined includes when object is missing on deeper path", done => { - const obj1 = new Parse.Object("AnObject"); - const obj2 = new Parse.Object("AnObject"); - const obj3 = new Parse.Object("AnObject"); + it('should have undefined includes when object is missing on deeper path', done => { + const obj1 = new Parse.Object('AnObject'); + const obj2 = new Parse.Object('AnObject'); + const obj3 = new Parse.Object('AnObject'); Parse.Object.saveAll([obj1, obj2, obj3]) .then(() => { - obj1.set("obj", obj2); - obj2.set("obj", obj3); + obj1.set('obj', obj2); + obj2.set('obj', obj3); // Save the pointer, delete the pointee return Parse.Object.saveAll([obj1, obj2]).then(() => { return obj3.destroy(); }); }) .then(() => { - const query = new Parse.Query("AnObject"); - query.include("obj.obj"); + const query = new Parse.Query('AnObject'); + query.include('obj.obj'); return query.get(obj1.id); }) .then(res => { - expect(res.get("obj")).not.toBe(undefined); - expect(res.get("obj").get("obj")).toBe(undefined); + expect(res.get('obj')).not.toBe(undefined); + expect(res.get('obj').get('obj')).toBe(undefined); done(); }) .catch(err => { @@ -2032,12 +2032,12 @@ describe("Parse.Object testing", () => { }); }); - it("should handle includes on null arrays #2752", done => { - const obj1 = new Parse.Object("AnObject"); - const obj2 = new Parse.Object("AnotherObject"); - const obj3 = new Parse.Object("NestedObject"); + it('should handle includes on null arrays #2752', done => { + const obj1 = new Parse.Object('AnObject'); + const obj2 = new Parse.Object('AnotherObject'); + const obj3 = new Parse.Object('NestedObject'); obj3.set({ - foo: "bar", + foo: 'bar', }); obj2.set({ key: obj3, @@ -2045,22 +2045,22 @@ describe("Parse.Object testing", () => { Parse.Object.saveAll([obj1, obj2]) .then(() => { - obj1.set("objects", [null, null, obj2]); + obj1.set('objects', [null, null, obj2]); return obj1.save(); }) .then(() => { - const query = new Parse.Query("AnObject"); - query.include("objects.key"); + const query = new Parse.Query('AnObject'); + query.include('objects.key'); return query.find(); }) .then(res => { const obj = res[0]; - expect(obj.get("objects")).not.toBe(undefined); - const array = obj.get("objects"); + expect(obj.get('objects')).not.toBe(undefined); + const array = obj.get('objects'); expect(Array.isArray(array)).toBe(true); expect(array[0]).toBe(null); expect(array[1]).toBe(null); - expect(array[2].get("key").get("foo")).toEqual("bar"); + expect(array[2].get('key').get('foo')).toEqual('bar'); done(); }) .catch(err => { @@ -2069,9 +2069,9 @@ describe("Parse.Object testing", () => { }); }); - it("should handle select and include #2786", done => { - const score = new Parse.Object("GameScore"); - const player = new Parse.Object("Player"); + it('should handle select and include #2786', done => { + const score = new Parse.Object('GameScore'); + const player = new Parse.Object('Player'); score.set({ score: 1234, }); @@ -2079,23 +2079,23 @@ describe("Parse.Object testing", () => { score .save() .then(() => { - player.set("gameScore", score); - player.set("other", "value"); + player.set('gameScore', score); + player.set('other', 'value'); return player.save(); }) .then(() => { - const query = new Parse.Query("Player"); - query.include("gameScore"); - query.select("gameScore"); + const query = new Parse.Query('Player'); + query.include('gameScore'); + query.select('gameScore'); return query.find(); }) .then(res => { const obj = res[0]; - const gameScore = obj.get("gameScore"); - const other = obj.get("other"); + const gameScore = obj.get('gameScore'); + const other = obj.get('other'); expect(other).toBeUndefined(); expect(gameScore).not.toBeUndefined(); - expect(gameScore.get("score")).toBe(1234); + expect(gameScore.get('score')).toBe(1234); done(); }) .catch(err => { @@ -2104,9 +2104,9 @@ describe("Parse.Object testing", () => { }); }); - it("should include ACLs with select", done => { - const score = new Parse.Object("GameScore"); - const player = new Parse.Object("Player"); + it('should include ACLs with select', done => { + const score = new Parse.Object('GameScore'); + const player = new Parse.Object('Player'); score.set({ score: 1234, }); @@ -2117,24 +2117,24 @@ describe("Parse.Object testing", () => { score .save() .then(() => { - player.set("gameScore", score); - player.set("other", "value"); + player.set('gameScore', score); + player.set('other', 'value'); player.setACL(acl); return player.save(); }) .then(() => { - const query = new Parse.Query("Player"); - query.include("gameScore"); - query.select("gameScore"); + const query = new Parse.Query('Player'); + query.include('gameScore'); + query.select('gameScore'); return query.find(); }) .then(res => { const obj = res[0]; - const gameScore = obj.get("gameScore"); - const other = obj.get("other"); + const gameScore = obj.get('gameScore'); + const other = obj.get('other'); expect(other).toBeUndefined(); expect(gameScore).not.toBeUndefined(); - expect(gameScore.get("score")).toBe(1234); + expect(gameScore.get('score')).toBe(1234); expect(obj.getACL().getPublicReadAccess()).toBe(true); expect(obj.getACL().getPublicWriteAccess()).toBe(false); }) @@ -2142,88 +2142,88 @@ describe("Parse.Object testing", () => { .catch(done.fail); }); - it("Update object field should store exactly same sent object", async done => { + it('Update object field should store exactly same sent object', async done => { let object = new TestObject(); // Set initial data - object.set("jsonData", { a: "b" }); + object.set('jsonData', { a: 'b' }); object = await object.save(); - equal(object.get("jsonData"), { a: "b" }); + equal(object.get('jsonData'), { a: 'b' }); // Set empty JSON - object.set("jsonData", {}); + object.set('jsonData', {}); object = await object.save(); - equal(object.get("jsonData"), {}); + equal(object.get('jsonData'), {}); // Set new JSON data - object.unset("jsonData"); - object.set("jsonData", { c: "d" }); + object.unset('jsonData'); + object.set('jsonData', { c: 'd' }); object = await object.save(); - equal(object.get("jsonData"), { c: "d" }); + equal(object.get('jsonData'), { c: 'd' }); // Fetch object from server object = await object.fetch(); - equal(object.get("jsonData"), { c: "d" }); + equal(object.get('jsonData'), { c: 'd' }); done(); }); - it("isNew in cloud code", async () => { - Parse.Cloud.beforeSave("CloudCodeIsNew", req => { + it('isNew in cloud code', async () => { + Parse.Cloud.beforeSave('CloudCodeIsNew', req => { expect(req.object.isNew()).toBeTruthy(); expect(req.object.id).toBeUndefined(); }); - Parse.Cloud.afterSave("CloudCodeIsNew", req => { + Parse.Cloud.afterSave('CloudCodeIsNew', req => { expect(req.object.isNew()).toBeFalsy(); expect(req.object.id).toBeDefined(); }); - const object = new Parse.Object("CloudCodeIsNew"); + const object = new Parse.Object('CloudCodeIsNew'); await object.save(); }); - it("should not change the json field to array in afterSave", async () => { - Parse.Cloud.beforeSave("failingJSONTestCase", req => { - expect(req.object.get("jsonField")).toEqual({ 123: "test" }); + it('should not change the json field to array in afterSave', async () => { + Parse.Cloud.beforeSave('failingJSONTestCase', req => { + expect(req.object.get('jsonField')).toEqual({ 123: 'test' }); }); - Parse.Cloud.afterSave("failingJSONTestCase", req => { - expect(req.object.get("jsonField")).toEqual({ 123: "test" }); + Parse.Cloud.afterSave('failingJSONTestCase', req => { + expect(req.object.get('jsonField')).toEqual({ 123: 'test' }); }); - const object = new Parse.Object("failingJSONTestCase"); - object.set("jsonField", { 123: "test" }); + const object = new Parse.Object('failingJSONTestCase'); + object.set('jsonField', { 123: 'test' }); await object.save(); }); - it("returns correct field values", async () => { + it('returns correct field values', async () => { const values = [ - { field: "string", value: "string" }, - { field: "number", value: 1 }, - { field: "boolean", value: true }, - { field: "array", value: [0, 1, 2] }, - { field: "array", value: [1, 2, 3] }, - { field: "array", value: [{ 0: "a" }, 2, 3] }, - { field: "object", value: { key: "value" } }, - { field: "object", value: { key1: "value1", key2: "value2" } }, - { field: "object", value: { key1: 1, key2: 2 } }, - { field: "object", value: { "1x1": 1 } }, - { field: "object", value: { "1x1": 1, 2: 2 } }, - { field: "object", value: { 0: 0 } }, - { field: "object", value: { 1: 1 } }, - { field: "object", value: { 0: { 0: "a", 1: "b" } } }, - { field: "date", value: new Date() }, + { field: 'string', value: 'string' }, + { field: 'number', value: 1 }, + { field: 'boolean', value: true }, + { field: 'array', value: [0, 1, 2] }, + { field: 'array', value: [1, 2, 3] }, + { field: 'array', value: [{ 0: 'a' }, 2, 3] }, + { field: 'object', value: { key: 'value' } }, + { field: 'object', value: { key1: 'value1', key2: 'value2' } }, + { field: 'object', value: { key1: 1, key2: 2 } }, + { field: 'object', value: { '1x1': 1 } }, + { field: 'object', value: { '1x1': 1, 2: 2 } }, + { field: 'object', value: { 0: 0 } }, + { field: 'object', value: { 1: 1 } }, + { field: 'object', value: { 0: { 0: 'a', 1: 'b' } } }, + { field: 'date', value: new Date() }, { - field: "file", + field: 'file', value: Parse.File.fromJSON({ - __type: "File", - name: "name", - url: "http://localhost:8378/1/files/test/name", + __type: 'File', + name: 'name', + url: 'http://localhost:8378/1/files/test/name', }), }, - { field: "geoPoint", value: new Parse.GeoPoint(40, -30) }, - { field: "bytes", value: { __type: "Bytes", base64: "ZnJveW8=" } }, + { field: 'geoPoint', value: new Parse.GeoPoint(40, -30) }, + { field: 'bytes', value: { __type: 'Bytes', base64: 'ZnJveW8=' } }, ]; for (const value of values) { const object = new TestObject(); diff --git a/spec/ParsePolygon.spec.js b/spec/ParsePolygon.spec.js index a8bec05f1a..aa0293326d 100644 --- a/spec/ParsePolygon.spec.js +++ b/spec/ParsePolygon.spec.js @@ -1,14 +1,14 @@ -const TestObject = Parse.Object.extend("TestObject"); -const request = require("../lib/request"); -const TestUtils = require("../lib/TestUtils"); +const TestObject = Parse.Object.extend('TestObject'); +const request = require('../lib/request'); +const TestUtils = require('../lib/TestUtils'); const defaultHeaders = { - "X-Parse-Application-Id": "test", - "X-Parse-Rest-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Rest-API-Key': 'rest', + 'Content-Type': 'application/json', }; -describe("Parse.Polygon testing", () => { - it("polygon save open path", done => { +describe('Parse.Polygon testing', () => { + it('polygon save open path', done => { const coords = [ [0, 0], [0, 1], @@ -23,7 +23,7 @@ describe("Parse.Polygon testing", () => { [0, 0], ]; const obj = new TestObject(); - obj.set("polygon", new Parse.Polygon(coords)); + obj.set('polygon', new Parse.Polygon(coords)); return obj .save() .then(() => { @@ -31,14 +31,14 @@ describe("Parse.Polygon testing", () => { return query.get(obj.id); }) .then(result => { - const polygon = result.get("polygon"); + const polygon = result.get('polygon'); equal(polygon instanceof Parse.Polygon, true); equal(polygon.coordinates, closed); done(); }, done.fail); }); - it("polygon save closed path", done => { + it('polygon save closed path', done => { const coords = [ [0, 0], [0, 1], @@ -47,7 +47,7 @@ describe("Parse.Polygon testing", () => { [0, 0], ]; const obj = new TestObject(); - obj.set("polygon", new Parse.Polygon(coords)); + obj.set('polygon', new Parse.Polygon(coords)); return obj .save() .then(() => { @@ -55,15 +55,15 @@ describe("Parse.Polygon testing", () => { return query.get(obj.id); }) .then(result => { - const polygon = result.get("polygon"); + const polygon = result.get('polygon'); equal(polygon instanceof Parse.Polygon, true); equal(polygon.coordinates, coords); done(); }, done.fail); }); - it_id("3019353b-d5b3-4e53-bcb1-716418328bdd")(it)( - "polygon equalTo (open/closed) path", + it_id('3019353b-d5b3-4e53-bcb1-716418328bdd')(it)( + 'polygon equalTo (open/closed) path', done => { const openPoints = [ [0, 0], @@ -81,24 +81,24 @@ describe("Parse.Polygon testing", () => { const openPolygon = new Parse.Polygon(openPoints); const closedPolygon = new Parse.Polygon(closedPoints); const obj = new TestObject(); - obj.set("polygon", openPolygon); + obj.set('polygon', openPolygon); return obj .save() .then(() => { const query = new Parse.Query(TestObject); - query.equalTo("polygon", openPolygon); + query.equalTo('polygon', openPolygon); return query.find(); }) .then(results => { - const polygon = results[0].get("polygon"); + const polygon = results[0].get('polygon'); equal(polygon instanceof Parse.Polygon, true); equal(polygon.coordinates, closedPoints); const query = new Parse.Query(TestObject); - query.equalTo("polygon", closedPolygon); + query.equalTo('polygon', closedPolygon); return query.find(); }) .then(results => { - const polygon = results[0].get("polygon"); + const polygon = results[0].get('polygon'); equal(polygon instanceof Parse.Polygon, true); equal(polygon.coordinates, closedPoints); done(); @@ -106,7 +106,7 @@ describe("Parse.Polygon testing", () => { } ); - it("polygon update", done => { + it('polygon update', done => { const oldCoords = [ [0, 0], [0, 1], @@ -122,11 +122,11 @@ describe("Parse.Polygon testing", () => { ]; const newPolygon = new Parse.Polygon(newCoords); const obj = new TestObject(); - obj.set("polygon", oldPolygon); + obj.set('polygon', oldPolygon); return obj .save() .then(() => { - obj.set("polygon", newPolygon); + obj.set('polygon', newPolygon); return obj.save(); }) .then(() => { @@ -134,7 +134,7 @@ describe("Parse.Polygon testing", () => { return query.get(obj.id); }) .then(result => { - const polygon = result.get("polygon"); + const polygon = result.get('polygon'); newCoords.push(newCoords[0]); equal(polygon instanceof Parse.Polygon, true); equal(polygon.coordinates, newCoords); @@ -142,16 +142,16 @@ describe("Parse.Polygon testing", () => { }, done.fail); }); - it("polygon invalid value", done => { + it('polygon invalid value', done => { const coords = [ - ["foo", "bar"], + ['foo', 'bar'], [0, 1], [1, 0], [1, 1], [0, 0], ]; const obj = new TestObject(); - obj.set("polygon", { __type: "Polygon", coordinates: coords }); + obj.set('polygon', { __type: 'Polygon', coordinates: coords }); return obj .save() .then(() => { @@ -161,26 +161,26 @@ describe("Parse.Polygon testing", () => { .then(done.fail, () => done()); }); - it("polygon three points minimum", done => { + it('polygon three points minimum', done => { const coords = [[0, 0]]; const obj = new TestObject(); // use raw so we test the server validates properly - obj.set("polygon", { __type: "Polygon", coordinates: coords }); + obj.set('polygon', { __type: 'Polygon', coordinates: coords }); obj.save().then(done.fail, () => done()); }); - it("polygon three different points minimum", done => { + it('polygon three different points minimum', done => { const coords = [ [0, 0], [0, 1], [0, 0], ]; const obj = new TestObject(); - obj.set("polygon", new Parse.Polygon(coords)); + obj.set('polygon', new Parse.Polygon(coords)); obj.save().then(done.fail, () => done()); }); - it("polygon counterclockwise", done => { + it('polygon counterclockwise', done => { const coords = [ [1, 1], [0, 1], @@ -195,7 +195,7 @@ describe("Parse.Polygon testing", () => { [1, 1], ]; const obj = new TestObject(); - obj.set("polygon", new Parse.Polygon(coords)); + obj.set('polygon', new Parse.Polygon(coords)); obj .save() .then(() => { @@ -203,19 +203,19 @@ describe("Parse.Polygon testing", () => { return query.get(obj.id); }) .then(result => { - const polygon = result.get("polygon"); + const polygon = result.get('polygon'); equal(polygon instanceof Parse.Polygon, true); equal(polygon.coordinates, closed); done(); }, done.fail); }); - describe("with location", () => { - if (process.env.PARSE_SERVER_TEST_DB !== "postgres") { + describe('with location', () => { + if (process.env.PARSE_SERVER_TEST_DB !== 'postgres') { beforeEach(async () => await TestUtils.destroyAllDataPermanently()); } - it("polygonContain query", done => { + it('polygonContain query', done => { const points1 = [ [0, 0], [0, 1], @@ -246,18 +246,18 @@ describe("Parse.Polygon testing", () => { const where = { boundary: { $geoIntersects: { - $point: { __type: "GeoPoint", latitude: 0.5, longitude: 0.5 }, + $point: { __type: 'GeoPoint', latitude: 0.5, longitude: 0.5 }, }, }, }; return request({ - method: "POST", - url: Parse.serverURL + "/classes/TestObject", - body: { where, _method: "GET" }, + method: 'POST', + url: Parse.serverURL + '/classes/TestObject', + body: { where, _method: 'GET' }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Javascript-Key": Parse.javaScriptKey, - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, + 'Content-Type': 'application/json', }, }); }) @@ -267,7 +267,7 @@ describe("Parse.Polygon testing", () => { }, done.fail); }); - it("polygonContain query no reverse input (Regression test for #4608)", done => { + it('polygonContain query no reverse input (Regression test for #4608)', done => { const points1 = [ [0.25, 0], [0.25, 1.25], @@ -298,18 +298,18 @@ describe("Parse.Polygon testing", () => { const where = { boundary: { $geoIntersects: { - $point: { __type: "GeoPoint", latitude: 0.5, longitude: 1.0 }, + $point: { __type: 'GeoPoint', latitude: 0.5, longitude: 1.0 }, }, }, }; return request({ - method: "POST", - url: Parse.serverURL + "/classes/TestObject", - body: { where, _method: "GET" }, + method: 'POST', + url: Parse.serverURL + '/classes/TestObject', + body: { where, _method: 'GET' }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Javascript-Key": Parse.javaScriptKey, - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, + 'Content-Type': 'application/json', }, }); }) @@ -319,7 +319,7 @@ describe("Parse.Polygon testing", () => { }, done.fail); }); - it("polygonContain query real data (Regression test for #4608)", done => { + it('polygonContain query real data (Regression test for #4608)', done => { const detroit = [ [42.631655189280224, -83.78406753121705], [42.633047793854814, -83.75333640366955], @@ -336,7 +336,7 @@ describe("Parse.Polygon testing", () => { boundary: { $geoIntersects: { $point: { - __type: "GeoPoint", + __type: 'GeoPoint', latitude: 42.624599, longitude: -83.770162, }, @@ -344,13 +344,13 @@ describe("Parse.Polygon testing", () => { }, }; return request({ - method: "POST", - url: Parse.serverURL + "/classes/TestObject", - body: { where, _method: "GET" }, + method: 'POST', + url: Parse.serverURL + '/classes/TestObject', + body: { where, _method: 'GET' }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Javascript-Key": Parse.javaScriptKey, - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, + 'Content-Type': 'application/json', }, }); }) @@ -360,7 +360,7 @@ describe("Parse.Polygon testing", () => { }, done.fail); }); - it("polygonContain invalid input", done => { + it('polygonContain invalid input', done => { const points = [ [0, 0], [0, 1], @@ -375,24 +375,24 @@ describe("Parse.Polygon testing", () => { const where = { boundary: { $geoIntersects: { - $point: { __type: "GeoPoint", latitude: 181, longitude: 181 }, + $point: { __type: 'GeoPoint', latitude: 181, longitude: 181 }, }, }, }; return request({ - method: "POST", - url: Parse.serverURL + "/classes/TestObject", - body: { where, _method: "GET" }, + method: 'POST', + url: Parse.serverURL + '/classes/TestObject', + body: { where, _method: 'GET' }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Javascript-Key": Parse.javaScriptKey, + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, }, }); }) .then(done.fail, () => done()); }); - it("polygonContain invalid geoPoint", done => { + it('polygonContain invalid geoPoint', done => { const points = [ [0, 0], [0, 1], @@ -412,12 +412,12 @@ describe("Parse.Polygon testing", () => { }, }; return request({ - method: "POST", - url: Parse.serverURL + "/classes/TestObject", - body: { where, _method: "GET" }, + method: 'POST', + url: Parse.serverURL + '/classes/TestObject', + body: { where, _method: 'GET' }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Javascript-Key": Parse.javaScriptKey, + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, }, }); }) @@ -426,17 +426,17 @@ describe("Parse.Polygon testing", () => { }); }); -describe_only_db("mongo")("Parse.Polygon testing", () => { - const Config = require("../lib/Config"); +describe_only_db('mongo')('Parse.Polygon testing', () => { + const Config = require('../lib/Config'); let config; beforeEach(async () => { - if (process.env.PARSE_SERVER_TEST_DB !== "postgres") { + if (process.env.PARSE_SERVER_TEST_DB !== 'postgres') { await TestUtils.destroyAllDataPermanently(); } - config = Config.get("test"); + config = Config.get('test'); config.schemaCache.clear(); }); - it("support 2d and 2dsphere", done => { + it('support 2d and 2dsphere', done => { const coords = [ [0, 0], [0, 1], @@ -445,29 +445,29 @@ describe_only_db("mongo")("Parse.Polygon testing", () => { [0, 0], ]; // testings against REST API, use raw formats - const polygon = { __type: "Polygon", coordinates: coords }; - const location = { __type: "GeoPoint", latitude: 10, longitude: 10 }; + const polygon = { __type: 'Polygon', coordinates: coords }; + const location = { __type: 'GeoPoint', latitude: 10, longitude: 10 }; const databaseAdapter = config.database.adapter; return reconfigureServer({ - appId: "test", - restAPIKey: "rest", - publicServerURL: "http://localhost:8378/1", + appId: 'test', + restAPIKey: 'rest', + publicServerURL: 'http://localhost:8378/1', databaseAdapter, }) .then(() => { - return databaseAdapter.createIndex("TestObject", { location: "2d" }); + return databaseAdapter.createIndex('TestObject', { location: '2d' }); }) .then(() => { - return databaseAdapter.createIndex("TestObject", { - polygon: "2dsphere", + return databaseAdapter.createIndex('TestObject', { + polygon: '2dsphere', }); }) .then(() => { return request({ - method: "POST", - url: "http://localhost:8378/1/classes/TestObject", + method: 'POST', + url: 'http://localhost:8378/1/classes/TestObject', body: { - _method: "POST", + _method: 'POST', location, polygon, polygon2: polygon, @@ -477,9 +477,9 @@ describe_only_db("mongo")("Parse.Polygon testing", () => { }) .then(resp => { return request({ - method: "POST", + method: 'POST', url: `http://localhost:8378/1/classes/TestObject/${resp.data.objectId}`, - body: { _method: "GET" }, + body: { _method: 'GET' }, headers: defaultHeaders, }); }) @@ -487,21 +487,21 @@ describe_only_db("mongo")("Parse.Polygon testing", () => { equal(resp.data.location, location); equal(resp.data.polygon, polygon); equal(resp.data.polygon2, polygon); - return databaseAdapter.getIndexes("TestObject"); + return databaseAdapter.getIndexes('TestObject'); }) .then(indexes => { equal(indexes.length, 4); equal(indexes[0].key, { _id: 1 }); - equal(indexes[1].key, { location: "2d" }); - equal(indexes[2].key, { polygon: "2dsphere" }); - equal(indexes[3].key, { polygon2: "2dsphere" }); + equal(indexes[1].key, { location: '2d' }); + equal(indexes[2].key, { polygon: '2dsphere' }); + equal(indexes[3].key, { polygon2: '2dsphere' }); done(); }, done.fail); }); - it("polygon coordinates reverse input", done => { - const Config = require("../lib/Config"); - const config = Config.get("test"); + it('polygon coordinates reverse input', done => { + const Config = require('../lib/Config'); + const config = Config.get('test'); // When stored the first point should be the last point const input = [ @@ -520,11 +520,11 @@ describe_only_db("mongo")("Parse.Polygon testing", () => { ], ]; const obj = new TestObject(); - obj.set("polygon", new Parse.Polygon(input)); + obj.set('polygon', new Parse.Polygon(input)); obj .save() .then(() => { - return config.database.adapter._rawFind("TestObject", { _id: obj.id }); + return config.database.adapter._rawFind('TestObject', { _id: obj.id }); }) .then(results => { expect(results.length).toBe(1); @@ -533,7 +533,7 @@ describe_only_db("mongo")("Parse.Polygon testing", () => { }); }); - it("polygon loop is not valid", done => { + it('polygon loop is not valid', done => { const coords = [ [0, 0], [0, 1], @@ -541,7 +541,7 @@ describe_only_db("mongo")("Parse.Polygon testing", () => { [1, 1], ]; const obj = new TestObject(); - obj.set("polygon", new Parse.Polygon(coords)); + obj.set('polygon', new Parse.Polygon(coords)); obj.save().then(done.fail, () => done()); }); }); diff --git a/spec/ParsePubSub.spec.js b/spec/ParsePubSub.spec.js index 148ce226c3..323a72eda6 100644 --- a/spec/ParsePubSub.spec.js +++ b/spec/ParsePubSub.spec.js @@ -1,90 +1,90 @@ -const ParsePubSub = require("../lib/LiveQuery/ParsePubSub").ParsePubSub; +const ParsePubSub = require('../lib/LiveQuery/ParsePubSub').ParsePubSub; -describe("ParsePubSub", function () { +describe('ParsePubSub', function () { beforeEach(function (done) { // Mock RedisPubSub const mockRedisPubSub = { - createPublisher: jasmine.createSpy("createPublisherRedis"), - createSubscriber: jasmine.createSpy("createSubscriberRedis"), + createPublisher: jasmine.createSpy('createPublisherRedis'), + createSubscriber: jasmine.createSpy('createSubscriberRedis'), }; jasmine.mockLibrary( - "../lib/Adapters/PubSub/RedisPubSub", - "RedisPubSub", + '../lib/Adapters/PubSub/RedisPubSub', + 'RedisPubSub', mockRedisPubSub ); // Mock EventEmitterPubSub const mockEventEmitterPubSub = { - createPublisher: jasmine.createSpy("createPublisherEventEmitter"), - createSubscriber: jasmine.createSpy("createSubscriberEventEmitter"), + createPublisher: jasmine.createSpy('createPublisherEventEmitter'), + createSubscriber: jasmine.createSpy('createSubscriberEventEmitter'), }; jasmine.mockLibrary( - "../lib/Adapters/PubSub/EventEmitterPubSub", - "EventEmitterPubSub", + '../lib/Adapters/PubSub/EventEmitterPubSub', + 'EventEmitterPubSub', mockEventEmitterPubSub ); done(); }); - it("can create redis publisher", function () { + it('can create redis publisher', function () { ParsePubSub.createPublisher({ - redisURL: "redisURL", + redisURL: 'redisURL', redisOptions: { socket_keepalive: true }, }); const RedisPubSub = - require("../lib/Adapters/PubSub/RedisPubSub").RedisPubSub; + require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; const EventEmitterPubSub = - require("../lib/Adapters/PubSub/EventEmitterPubSub").EventEmitterPubSub; + require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub; expect(RedisPubSub.createPublisher).toHaveBeenCalledWith({ - redisURL: "redisURL", + redisURL: 'redisURL', redisOptions: { socket_keepalive: true }, }); expect(EventEmitterPubSub.createPublisher).not.toHaveBeenCalled(); }); - it("can create event emitter publisher", function () { + it('can create event emitter publisher', function () { ParsePubSub.createPublisher({}); const RedisPubSub = - require("../lib/Adapters/PubSub/RedisPubSub").RedisPubSub; + require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; const EventEmitterPubSub = - require("../lib/Adapters/PubSub/EventEmitterPubSub").EventEmitterPubSub; + require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub; expect(RedisPubSub.createPublisher).not.toHaveBeenCalled(); expect(EventEmitterPubSub.createPublisher).toHaveBeenCalled(); }); - it("can create redis subscriber", function () { + it('can create redis subscriber', function () { ParsePubSub.createSubscriber({ - redisURL: "redisURL", + redisURL: 'redisURL', redisOptions: { socket_keepalive: true }, }); const RedisPubSub = - require("../lib/Adapters/PubSub/RedisPubSub").RedisPubSub; + require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; const EventEmitterPubSub = - require("../lib/Adapters/PubSub/EventEmitterPubSub").EventEmitterPubSub; + require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub; expect(RedisPubSub.createSubscriber).toHaveBeenCalledWith({ - redisURL: "redisURL", + redisURL: 'redisURL', redisOptions: { socket_keepalive: true }, }); expect(EventEmitterPubSub.createSubscriber).not.toHaveBeenCalled(); }); - it("can create event emitter subscriber", function () { + it('can create event emitter subscriber', function () { ParsePubSub.createSubscriber({}); const RedisPubSub = - require("../lib/Adapters/PubSub/RedisPubSub").RedisPubSub; + require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; const EventEmitterPubSub = - require("../lib/Adapters/PubSub/EventEmitterPubSub").EventEmitterPubSub; + require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub; expect(RedisPubSub.createSubscriber).not.toHaveBeenCalled(); expect(EventEmitterPubSub.createSubscriber).toHaveBeenCalled(); }); - it("can create publisher/sub with custom adapter", function () { + it('can create publisher/sub with custom adapter', function () { const adapter = { - createPublisher: jasmine.createSpy("createPublisher"), - createSubscriber: jasmine.createSpy("createSubscriber"), + createPublisher: jasmine.createSpy('createPublisher'), + createSubscriber: jasmine.createSpy('createSubscriber'), }; ParsePubSub.createPublisher({ pubSubAdapter: adapter, @@ -97,19 +97,19 @@ describe("ParsePubSub", function () { expect(adapter.createSubscriber).toHaveBeenCalled(); const RedisPubSub = - require("../lib/Adapters/PubSub/RedisPubSub").RedisPubSub; + require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; const EventEmitterPubSub = - require("../lib/Adapters/PubSub/EventEmitterPubSub").EventEmitterPubSub; + require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub; expect(RedisPubSub.createSubscriber).not.toHaveBeenCalled(); expect(EventEmitterPubSub.createSubscriber).not.toHaveBeenCalled(); expect(RedisPubSub.createPublisher).not.toHaveBeenCalled(); expect(EventEmitterPubSub.createPublisher).not.toHaveBeenCalled(); }); - it("can create publisher/sub with custom function adapter", function () { + it('can create publisher/sub with custom function adapter', function () { const adapter = { - createPublisher: jasmine.createSpy("createPublisher"), - createSubscriber: jasmine.createSpy("createSubscriber"), + createPublisher: jasmine.createSpy('createPublisher'), + createSubscriber: jasmine.createSpy('createSubscriber'), }; ParsePubSub.createPublisher({ pubSubAdapter: function () { @@ -126,9 +126,9 @@ describe("ParsePubSub", function () { expect(adapter.createSubscriber).toHaveBeenCalled(); const RedisPubSub = - require("../lib/Adapters/PubSub/RedisPubSub").RedisPubSub; + require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; const EventEmitterPubSub = - require("../lib/Adapters/PubSub/EventEmitterPubSub").EventEmitterPubSub; + require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub; expect(RedisPubSub.createSubscriber).not.toHaveBeenCalled(); expect(EventEmitterPubSub.createSubscriber).not.toHaveBeenCalled(); expect(RedisPubSub.createPublisher).not.toHaveBeenCalled(); @@ -136,10 +136,10 @@ describe("ParsePubSub", function () { }); afterEach(function () { - jasmine.restoreLibrary("../lib/Adapters/PubSub/RedisPubSub", "RedisPubSub"); + jasmine.restoreLibrary('../lib/Adapters/PubSub/RedisPubSub', 'RedisPubSub'); jasmine.restoreLibrary( - "../lib/Adapters/PubSub/EventEmitterPubSub", - "EventEmitterPubSub" + '../lib/Adapters/PubSub/EventEmitterPubSub', + 'EventEmitterPubSub' ); }); }); diff --git a/spec/ParseQuery.Aggregate.spec.js b/spec/ParseQuery.Aggregate.spec.js index a977519967..d49ba726a7 100644 --- a/spec/ParseQuery.Aggregate.spec.js +++ b/spec/ParseQuery.Aggregate.spec.js @@ -1,13 +1,13 @@ -"use strict"; -const Parse = require("parse/node"); -const request = require("../lib/request"); -const Config = require("../lib/Config"); +'use strict'; +const Parse = require('parse/node'); +const request = require('../lib/request'); +const Config = require('../lib/Config'); const masterKeyHeaders = { - "X-Parse-Application-Id": "test", - "X-Parse-Rest-API-Key": "test", - "X-Parse-Master-Key": "test", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Rest-API-Key': 'test', + 'X-Parse-Master-Key': 'test', + 'Content-Type': 'application/json', }; const masterKeyOptions = { @@ -16,37 +16,37 @@ const masterKeyOptions = { }; const PointerObject = Parse.Object.extend({ - className: "PointerObject", + className: 'PointerObject', }); const loadTestData = () => { const data1 = { score: 10, - name: "foo", - sender: { group: "A" }, + name: 'foo', + sender: { group: 'A' }, views: 900, - size: ["S", "M"], + size: ['S', 'M'], }; const data2 = { score: 10, - name: "foo", - sender: { group: "A" }, + name: 'foo', + sender: { group: 'A' }, views: 800, - size: ["M", "L"], + size: ['M', 'L'], }; const data3 = { score: 10, - name: "bar", - sender: { group: "B" }, + name: 'bar', + sender: { group: 'B' }, views: 700, - size: ["S"], + size: ['S'], }; const data4 = { score: 20, - name: "dpl", - sender: { group: "B" }, + name: 'dpl', + sender: { group: 'B' }, views: 700, - size: ["S"], + size: ['S'], }; const obj1 = new TestObject(data1); const obj2 = new TestObject(data2); @@ -68,50 +68,50 @@ const get = function (url, options) { }); }; -describe("Parse.Query Aggregate testing", () => { +describe('Parse.Query Aggregate testing', () => { beforeEach(async () => { await loadTestData(); }); - it("should only query aggregate with master key", done => { - Parse._request("GET", `aggregate/someClass`, {}).then( + it('should only query aggregate with master key', done => { + Parse._request('GET', `aggregate/someClass`, {}).then( () => {}, error => { - expect(error.message).toEqual("unauthorized: master key is required"); + expect(error.message).toEqual('unauthorized: master key is required'); done(); } ); }); - it("invalid query group _id required", done => { + it('invalid query group _id required', done => { const options = Object.assign({}, masterKeyOptions, { body: { $group: {}, }, }); - get(Parse.serverURL + "/aggregate/TestObject", options).catch(error => { + get(Parse.serverURL + '/aggregate/TestObject', options).catch(error => { expect(error.error.code).toEqual(Parse.Error.INVALID_QUERY); done(); }); }); - it_id("add7050f-65d5-4a13-b526-5bd1ee09c7f1")(it)("group by field", done => { + it_id('add7050f-65d5-4a13-b526-5bd1ee09c7f1')(it)('group by field', done => { const options = Object.assign({}, masterKeyOptions, { body: { - $group: { _id: "$name" }, + $group: { _id: '$name' }, }, }); - get(Parse.serverURL + "/aggregate/TestObject", options) + get(Parse.serverURL + '/aggregate/TestObject', options) .then(resp => { expect(resp.results.length).toBe(3); expect( - Object.prototype.hasOwnProperty.call(resp.results[0], "objectId") + Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId') ).toBe(true); expect( - Object.prototype.hasOwnProperty.call(resp.results[1], "objectId") + Object.prototype.hasOwnProperty.call(resp.results[1], 'objectId') ).toBe(true); expect( - Object.prototype.hasOwnProperty.call(resp.results[2], "objectId") + Object.prototype.hasOwnProperty.call(resp.results[2], 'objectId') ).toBe(true); expect(resp.results[0].objectId).not.toBe(undefined); expect(resp.results[1].objectId).not.toBe(undefined); @@ -121,29 +121,29 @@ describe("Parse.Query Aggregate testing", () => { .catch(done.fail); }); - it_id("0ab0d776-e45d-419a-9b35-3d11933b77d1")(it)( - "group by pipeline operator", + it_id('0ab0d776-e45d-419a-9b35-3d11933b77d1')(it)( + 'group by pipeline operator', async () => { const options = Object.assign({}, masterKeyOptions, { body: { pipeline: { - $group: { _id: "$name" }, + $group: { _id: '$name' }, }, }, }); const resp = await get( - Parse.serverURL + "/aggregate/TestObject", + Parse.serverURL + '/aggregate/TestObject', options ); expect(resp.results.length).toBe(3); expect( - Object.prototype.hasOwnProperty.call(resp.results[0], "objectId") + Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId') ).toBe(true); expect( - Object.prototype.hasOwnProperty.call(resp.results[1], "objectId") + Object.prototype.hasOwnProperty.call(resp.results[1], 'objectId') ).toBe(true); expect( - Object.prototype.hasOwnProperty.call(resp.results[2], "objectId") + Object.prototype.hasOwnProperty.call(resp.results[2], 'objectId') ).toBe(true); expect(resp.results[0].objectId).not.toBe(undefined); expect(resp.results[1].objectId).not.toBe(undefined); @@ -151,8 +151,8 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("b6b42145-7eb4-47aa-ada6-8c1444420e07")(it)( - "group by empty object", + it_id('b6b42145-7eb4-47aa-ada6-8c1444420e07')(it)( + 'group by empty object', done => { const obj = new TestObject(); const pipeline = [ @@ -173,13 +173,13 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("0f5f6869-e675-41b9-9ad2-52b201124fb0")(it)( - "group by empty string", + it_id('0f5f6869-e675-41b9-9ad2-52b201124fb0')(it)( + 'group by empty string', done => { const obj = new TestObject(); const pipeline = [ { - $group: { _id: "" }, + $group: { _id: '' }, }, ]; obj @@ -195,8 +195,8 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("b9c4f1b4-47f4-4ff4-88fb-586711f57e4a")(it)( - "group by empty array", + it_id('b9c4f1b4-47f4-4ff4-88fb-586711f57e4a')(it)( + 'group by empty array', done => { const obj = new TestObject(); const pipeline = [ @@ -217,8 +217,8 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("bf5ee3e5-986c-4994-9c8d-79310283f602")(it)( - "group by multiple columns ", + it_id('bf5ee3e5-986c-4994-9c8d-79310283f602')(it)( + 'group by multiple columns ', done => { const obj1 = new TestObject(); const obj2 = new TestObject(); @@ -227,8 +227,8 @@ describe("Parse.Query Aggregate testing", () => { { $group: { _id: { - score: "$score", - views: "$views", + score: '$score', + views: '$views', }, count: { $sum: 1 }, }, @@ -246,8 +246,8 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("3e652c61-78e1-4541-83ac-51ad1def9874")(it)( - "group by date object", + it_id('3e652c61-78e1-4541-83ac-51ad1def9874')(it)( + 'group by date object', done => { const obj1 = new TestObject(); const obj2 = new TestObject(); @@ -256,9 +256,9 @@ describe("Parse.Query Aggregate testing", () => { { $group: { _id: { - day: { $dayOfMonth: "$_updated_at" }, - month: { $month: "$_created_at" }, - year: { $year: "$_created_at" }, + day: { $dayOfMonth: '$_updated_at' }, + month: { $month: '$_created_at' }, + year: { $year: '$_created_at' }, }, count: { $sum: 1 }, }, @@ -281,8 +281,8 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("5d3a0f73-1f49-46f3-9be5-caf1eaefec79")(it)( - "group by date object transform", + it_id('5d3a0f73-1f49-46f3-9be5-caf1eaefec79')(it)( + 'group by date object transform', done => { const obj1 = new TestObject(); const obj2 = new TestObject(); @@ -291,9 +291,9 @@ describe("Parse.Query Aggregate testing", () => { { $group: { _id: { - day: { $dayOfMonth: "$updatedAt" }, - month: { $month: "$createdAt" }, - year: { $year: "$createdAt" }, + day: { $dayOfMonth: '$updatedAt' }, + month: { $month: '$createdAt' }, + year: { $year: '$createdAt' }, }, count: { $sum: 1 }, }, @@ -316,20 +316,20 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("1f9b10f7-dc0e-467f-b506-a303b9c36258")(it)("group by number", done => { + it_id('1f9b10f7-dc0e-467f-b506-a303b9c36258')(it)('group by number', done => { const options = Object.assign({}, masterKeyOptions, { body: { - $group: { _id: "$score" }, + $group: { _id: '$score' }, }, }); - get(Parse.serverURL + "/aggregate/TestObject", options) + get(Parse.serverURL + '/aggregate/TestObject', options) .then(resp => { expect(resp.results.length).toBe(2); expect( - Object.prototype.hasOwnProperty.call(resp.results[0], "objectId") + Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId') ).toBe(true); expect( - Object.prototype.hasOwnProperty.call(resp.results[1], "objectId") + Object.prototype.hasOwnProperty.call(resp.results[1], 'objectId') ).toBe(true); expect( resp.results.sort((a, b) => (a.objectId > b.objectId ? 1 : -1)) @@ -339,16 +339,16 @@ describe("Parse.Query Aggregate testing", () => { .catch(done.fail); }); - it_id("c7695018-03de-49e4-8a72-d4d956f70deb")(it_exclude_dbs(["postgres"]))( - "group and multiply transform", + it_id('c7695018-03de-49e4-8a72-d4d956f70deb')(it_exclude_dbs(['postgres']))( + 'group and multiply transform', done => { - const obj1 = new TestObject({ name: "item a", quantity: 2, price: 10 }); - const obj2 = new TestObject({ name: "item b", quantity: 5, price: 5 }); + const obj1 = new TestObject({ name: 'item a', quantity: 2, price: 10 }); + const obj2 = new TestObject({ name: 'item b', quantity: 5, price: 5 }); const pipeline = [ { $group: { _id: null, - total: { $sum: { $multiply: ["$quantity", "$price"] } }, + total: { $sum: { $multiply: ['$quantity', '$price'] } }, }, }, ]; @@ -365,11 +365,11 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("2d278175-7594-4b29-bef4-04c778b7a42f")(it_exclude_dbs(["postgres"]))( - "project and multiply transform", + it_id('2d278175-7594-4b29-bef4-04c778b7a42f')(it_exclude_dbs(['postgres']))( + 'project and multiply transform', done => { - const obj1 = new TestObject({ name: "item a", quantity: 2, price: 10 }); - const obj2 = new TestObject({ name: "item b", quantity: 5, price: 5 }); + const obj1 = new TestObject({ name: 'item a', quantity: 2, price: 10 }); + const obj2 = new TestObject({ name: 'item b', quantity: 5, price: 5 }); const pipeline = [ { $match: { quantity: { $exists: true } }, @@ -377,7 +377,7 @@ describe("Parse.Query Aggregate testing", () => { { $project: { name: 1, - total: { $multiply: ["$quantity", "$price"] }, + total: { $multiply: ['$quantity', '$price'] }, }, }, ]; @@ -388,7 +388,7 @@ describe("Parse.Query Aggregate testing", () => { }) .then(results => { expect(results.length).toEqual(2); - if (results[0].name === "item a") { + if (results[0].name === 'item a') { expect(results[0].total).toEqual(20); expect(results[1].total).toEqual(25); } else { @@ -400,11 +400,11 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("9c9d9318-3a9e-4c2a-8a09-d3aa52c7505b")(it_exclude_dbs(["postgres"]))( - "project without objectId transform", + it_id('9c9d9318-3a9e-4c2a-8a09-d3aa52c7505b')(it_exclude_dbs(['postgres']))( + 'project without objectId transform', done => { - const obj1 = new TestObject({ name: "item a", quantity: 2, price: 10 }); - const obj2 = new TestObject({ name: "item b", quantity: 5, price: 5 }); + const obj1 = new TestObject({ name: 'item a', quantity: 2, price: 10 }); + const obj2 = new TestObject({ name: 'item b', quantity: 5, price: 5 }); const pipeline = [ { $match: { quantity: { $exists: true } }, @@ -412,7 +412,7 @@ describe("Parse.Query Aggregate testing", () => { { $project: { _id: 0, - total: { $multiply: ["$quantity", "$price"] }, + total: { $multiply: ['$quantity', '$price'] }, }, }, { @@ -435,8 +435,8 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("f92c82ac-1993-4758-b718-45689dfc4154")(it_exclude_dbs(["postgres"]))( - "project updatedAt only transform", + it_id('f92c82ac-1993-4758-b718-45689dfc4154')(it_exclude_dbs(['postgres']))( + 'project updatedAt only transform', done => { const pipeline = [ { @@ -449,10 +449,10 @@ describe("Parse.Query Aggregate testing", () => { for (let i = 0; i < results.length; i++) { const item = results[i]; expect( - Object.prototype.hasOwnProperty.call(item, "updatedAt") + Object.prototype.hasOwnProperty.call(item, 'updatedAt') ).toEqual(true); expect( - Object.prototype.hasOwnProperty.call(item, "objectId") + Object.prototype.hasOwnProperty.call(item, 'objectId') ).toEqual(false); } done(); @@ -460,8 +460,8 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("99566b1d-778d-4444-9deb-c398108e659d")(it_exclude_dbs(["postgres"]))( - "can group by any date field (it does not work if you have dirty data)", + it_id('99566b1d-778d-4444-9deb-c398108e659d')(it_exclude_dbs(['postgres']))( + 'can group by any date field (it does not work if you have dirty data)', done => { // rows in your collection with non date data in the field that is supposed to be a date const obj1 = new TestObject({ dateField2019: new Date(1990, 11, 1) }); @@ -476,9 +476,9 @@ describe("Parse.Query Aggregate testing", () => { { $group: { _id: { - day: { $dayOfMonth: "$dateField2019" }, - month: { $month: "$dateField2019" }, - year: { $year: "$dateField2019" }, + day: { $dayOfMonth: '$dateField2019' }, + month: { $month: '$dateField2019' }, + year: { $year: '$dateField2019' }, }, count: { $sum: 1 }, }, @@ -499,8 +499,8 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_only_db("postgres")( - "can group by any date field (it does not work if you have dirty data)", // rows in your collection with non date data in the field that is supposed to be a date + it_only_db('postgres')( + 'can group by any date field (it does not work if you have dirty data)', // rows in your collection with non date data in the field that is supposed to be a date done => { const obj1 = new TestObject({ dateField2019: new Date(1990, 11, 1) }); const obj2 = new TestObject({ dateField2019: new Date(1990, 5, 1) }); @@ -509,9 +509,9 @@ describe("Parse.Query Aggregate testing", () => { { $group: { _id: { - day: { $dayOfMonth: "$dateField2019" }, - month: { $month: "$dateField2019" }, - year: { $year: "$dateField2019" }, + day: { $dayOfMonth: '$dateField2019' }, + month: { $month: '$dateField2019' }, + year: { $year: '$dateField2019' }, }, count: { $sum: 1 }, }, @@ -532,15 +532,15 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("bf3c2704-b721-4b1b-92fa-e1b129ae4aff")(it)( - "group by pointer", + it_id('bf3c2704-b721-4b1b-92fa-e1b129ae4aff')(it)( + 'group by pointer', done => { const pointer1 = new TestObject(); const pointer2 = new TestObject(); const obj1 = new TestObject({ pointer: pointer1 }); const obj2 = new TestObject({ pointer: pointer2 }); const obj3 = new TestObject({ pointer: pointer1 }); - const pipeline = [{ $group: { _id: "$pointer" } }]; + const pipeline = [{ $group: { _id: '$pointer' } }]; Parse.Object.saveAll([pointer1, pointer2, obj1, obj2, obj3]) .then(() => { const query = new Parse.Query(TestObject); @@ -562,16 +562,16 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("9ee9e8c0-a590-4af9-97a9-4b8e5080ffae")(it)("group sum query", done => { + it_id('9ee9e8c0-a590-4af9-97a9-4b8e5080ffae')(it)('group sum query', done => { const options = Object.assign({}, masterKeyOptions, { body: { - $group: { _id: null, total: { $sum: "$score" } }, + $group: { _id: null, total: { $sum: '$score' } }, }, }); - get(Parse.serverURL + "/aggregate/TestObject", options) + get(Parse.serverURL + '/aggregate/TestObject', options) .then(resp => { expect( - Object.prototype.hasOwnProperty.call(resp.results[0], "objectId") + Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId') ).toBe(true); expect(resp.results[0].objectId).toBe(null); expect(resp.results[0].total).toBe(50); @@ -580,18 +580,18 @@ describe("Parse.Query Aggregate testing", () => { .catch(done.fail); }); - it_id("39133cd6-5bdf-4917-b672-a9d7a9157b6f")(it)( - "group count query", + it_id('39133cd6-5bdf-4917-b672-a9d7a9157b6f')(it)( + 'group count query', done => { const options = Object.assign({}, masterKeyOptions, { body: { $group: { _id: null, total: { $sum: 1 } }, }, }); - get(Parse.serverURL + "/aggregate/TestObject", options) + get(Parse.serverURL + '/aggregate/TestObject', options) .then(resp => { expect( - Object.prototype.hasOwnProperty.call(resp.results[0], "objectId") + Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId') ).toBe(true); expect(resp.results[0].objectId).toBe(null); expect(resp.results[0].total).toBe(4); @@ -601,16 +601,16 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("48685ff3-066f-4353-82e7-87f39d812ff7")(it)("group min query", done => { + it_id('48685ff3-066f-4353-82e7-87f39d812ff7')(it)('group min query', done => { const options = Object.assign({}, masterKeyOptions, { body: { - $group: { _id: null, minScore: { $min: "$score" } }, + $group: { _id: null, minScore: { $min: '$score' } }, }, }); - get(Parse.serverURL + "/aggregate/TestObject", options) + get(Parse.serverURL + '/aggregate/TestObject', options) .then(resp => { expect( - Object.prototype.hasOwnProperty.call(resp.results[0], "objectId") + Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId') ).toBe(true); expect(resp.results[0].objectId).toBe(null); expect(resp.results[0].minScore).toBe(10); @@ -619,16 +619,16 @@ describe("Parse.Query Aggregate testing", () => { .catch(done.fail); }); - it_id("581efea6-6525-4e10-96d9-76d32c73e7a9")(it)("group max query", done => { + it_id('581efea6-6525-4e10-96d9-76d32c73e7a9')(it)('group max query', done => { const options = Object.assign({}, masterKeyOptions, { body: { - $group: { _id: null, maxScore: { $max: "$score" } }, + $group: { _id: null, maxScore: { $max: '$score' } }, }, }); - get(Parse.serverURL + "/aggregate/TestObject", options) + get(Parse.serverURL + '/aggregate/TestObject', options) .then(resp => { expect( - Object.prototype.hasOwnProperty.call(resp.results[0], "objectId") + Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId') ).toBe(true); expect(resp.results[0].objectId).toBe(null); expect(resp.results[0].maxScore).toBe(20); @@ -637,16 +637,16 @@ describe("Parse.Query Aggregate testing", () => { .catch(done.fail); }); - it_id("5f880de2-b97f-43d1-89b7-ad903a4be4e2")(it)("group avg query", done => { + it_id('5f880de2-b97f-43d1-89b7-ad903a4be4e2')(it)('group avg query', done => { const options = Object.assign({}, masterKeyOptions, { body: { - $group: { _id: null, avgScore: { $avg: "$score" } }, + $group: { _id: null, avgScore: { $avg: '$score' } }, }, }); - get(Parse.serverURL + "/aggregate/TestObject", options) + get(Parse.serverURL + '/aggregate/TestObject', options) .then(resp => { expect( - Object.prototype.hasOwnProperty.call(resp.results[0], "objectId") + Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId') ).toBe(true); expect(resp.results[0].objectId).toBe(null); expect(resp.results[0].avgScore).toBe(12.5); @@ -655,13 +655,13 @@ describe("Parse.Query Aggregate testing", () => { .catch(done.fail); }); - it_id("58e7a1a0-fae1-4993-b336-7bcbd5b7c786")(it)("limit query", done => { + it_id('58e7a1a0-fae1-4993-b336-7bcbd5b7c786')(it)('limit query', done => { const options = Object.assign({}, masterKeyOptions, { body: { $limit: 2, }, }); - get(Parse.serverURL + "/aggregate/TestObject", options) + get(Parse.serverURL + '/aggregate/TestObject', options) .then(resp => { expect(resp.results.length).toBe(2); done(); @@ -669,55 +669,55 @@ describe("Parse.Query Aggregate testing", () => { .catch(done.fail); }); - it_id("c892a3d2-8ae8-4b88-bf2b-3c958e1cacd8")(it)( - "sort ascending query", + it_id('c892a3d2-8ae8-4b88-bf2b-3c958e1cacd8')(it)( + 'sort ascending query', done => { const options = Object.assign({}, masterKeyOptions, { body: { $sort: { name: 1 }, }, }); - get(Parse.serverURL + "/aggregate/TestObject", options) + get(Parse.serverURL + '/aggregate/TestObject', options) .then(resp => { expect(resp.results.length).toBe(4); - expect(resp.results[0].name).toBe("bar"); - expect(resp.results[1].name).toBe("dpl"); - expect(resp.results[2].name).toBe("foo"); - expect(resp.results[3].name).toBe("foo"); + expect(resp.results[0].name).toBe('bar'); + expect(resp.results[1].name).toBe('dpl'); + expect(resp.results[2].name).toBe('foo'); + expect(resp.results[3].name).toBe('foo'); done(); }) .catch(done.fail); } ); - it_id("79d4bc2e-8b69-42ec-8526-20d17e968ab3")(it)( - "sort decending query", + it_id('79d4bc2e-8b69-42ec-8526-20d17e968ab3')(it)( + 'sort decending query', done => { const options = Object.assign({}, masterKeyOptions, { body: { $sort: { name: -1 }, }, }); - get(Parse.serverURL + "/aggregate/TestObject", options) + get(Parse.serverURL + '/aggregate/TestObject', options) .then(resp => { expect(resp.results.length).toBe(4); - expect(resp.results[0].name).toBe("foo"); - expect(resp.results[1].name).toBe("foo"); - expect(resp.results[2].name).toBe("dpl"); - expect(resp.results[3].name).toBe("bar"); + expect(resp.results[0].name).toBe('foo'); + expect(resp.results[1].name).toBe('foo'); + expect(resp.results[2].name).toBe('dpl'); + expect(resp.results[3].name).toBe('bar'); done(); }) .catch(done.fail); } ); - it_id("b3d97d48-bd6b-444d-be64-cc1fd4738266")(it)("skip query", done => { + it_id('b3d97d48-bd6b-444d-be64-cc1fd4738266')(it)('skip query', done => { const options = Object.assign({}, masterKeyOptions, { body: { $skip: 2, }, }); - get(Parse.serverURL + "/aggregate/TestObject", options) + get(Parse.serverURL + '/aggregate/TestObject', options) .then(resp => { expect(resp.results.length).toBe(2); done(); @@ -725,8 +725,8 @@ describe("Parse.Query Aggregate testing", () => { .catch(done.fail); }); - it_id("4a7daee3-5ba1-4c8b-b406-1846a73a64c8")(it)( - "match comparison date query", + it_id('4a7daee3-5ba1-4c8b-b406-1846a73a64c8')(it)( + 'match comparison date query', done => { const today = new Date(); const yesterday = new Date(); @@ -749,19 +749,19 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("d98c8c20-6dac-4d74-8228-85a1ae46a7d0")(it)( - "should aggregate with Date object (directAccess)", + it_id('d98c8c20-6dac-4d74-8228-85a1ae46a7d0')(it)( + 'should aggregate with Date object (directAccess)', async () => { - const rest = require("../lib/rest"); - const auth = require("../lib/Auth"); - const TestObject = Parse.Object.extend("TestObject"); + const rest = require('../lib/rest'); + const auth = require('../lib/Auth'); + const TestObject = Parse.Object.extend('TestObject'); const date = new Date(); await new TestObject({ date: date }).save(null, { useMasterKey: true }); const config = Config.get(Parse.applicationId); const resp = await rest.find( config, auth.master(config), - "TestObject", + 'TestObject', {}, { pipeline: [{ $match: { date: { $lte: new Date() } } }] } ); @@ -769,15 +769,15 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("3d73d23a-fce1-4ac0-972a-50f6a550f348")(it)( - "match comparison query", + it_id('3d73d23a-fce1-4ac0-972a-50f6a550f348')(it)( + 'match comparison query', done => { const options = Object.assign({}, masterKeyOptions, { body: { $match: { score: { $gt: 15 } }, }, }); - get(Parse.serverURL + "/aggregate/TestObject", options) + get(Parse.serverURL + '/aggregate/TestObject', options) .then(resp => { expect(resp.results.length).toBe(1); expect(resp.results[0].score).toBe(20); @@ -787,15 +787,15 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("11772059-6c93-41ac-8dfe-e55b6c97e16f")(it)( - "match multiple comparison query", + it_id('11772059-6c93-41ac-8dfe-e55b6c97e16f')(it)( + 'match multiple comparison query', done => { const options = Object.assign({}, masterKeyOptions, { body: { $match: { score: { $gt: 5, $lt: 15 } }, }, }); - get(Parse.serverURL + "/aggregate/TestObject", options) + get(Parse.serverURL + '/aggregate/TestObject', options) .then(resp => { expect(resp.results.length).toBe(3); expect(resp.results[0].score).toBe(10); @@ -807,8 +807,8 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("ca2efb04-8f73-40ca-a5fc-79d0032bc398")(it)( - "match complex comparison query", + it_id('ca2efb04-8f73-40ca-a5fc-79d0032bc398')(it)( + 'match complex comparison query', done => { const options = Object.assign({}, masterKeyOptions, { body: { @@ -818,7 +818,7 @@ describe("Parse.Query Aggregate testing", () => { }, }, }); - get(Parse.serverURL + "/aggregate/TestObject", options) + get(Parse.serverURL + '/aggregate/TestObject', options) .then(resp => { expect(resp.results.length).toBe(1); expect(resp.results[0].score).toBe(10); @@ -829,15 +829,15 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("5ef9dcbe-fe54-4db2-b8fb-58c87c6ff072")(it)( - "match comparison and equality query", + it_id('5ef9dcbe-fe54-4db2-b8fb-58c87c6ff072')(it)( + 'match comparison and equality query', done => { const options = Object.assign({}, masterKeyOptions, { body: { $match: { score: { $gt: 5, $lt: 15 }, views: 900 }, }, }); - get(Parse.serverURL + "/aggregate/TestObject", options) + get(Parse.serverURL + '/aggregate/TestObject', options) .then(resp => { expect(resp.results.length).toBe(1); expect(resp.results[0].score).toBe(10); @@ -848,7 +848,7 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("c910a6af-58df-46aa-bbf8-da014a04cdcd")(it)("match $or query", done => { + it_id('c910a6af-58df-46aa-bbf8-da014a04cdcd')(it)('match $or query', done => { const options = Object.assign({}, masterKeyOptions, { body: { $match: { @@ -859,7 +859,7 @@ describe("Parse.Query Aggregate testing", () => { }, }, }); - get(Parse.serverURL + "/aggregate/TestObject", options) + get(Parse.serverURL + '/aggregate/TestObject', options) .then(resp => { expect(resp.results.length).toBe(2); // Match score { $gt: 15, $lt: 25 } @@ -874,8 +874,8 @@ describe("Parse.Query Aggregate testing", () => { .catch(done.fail); }); - it_id("0f768dc2-0675-4e45-a763-5ca9c895fa5f")(it)( - "match objectId query", + it_id('0f768dc2-0675-4e45-a763-5ca9c895fa5f')(it)( + 'match objectId query', done => { const obj1 = new TestObject(); const obj2 = new TestObject(); @@ -893,14 +893,14 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("27349e04-0d9d-453f-ad85-1a811631582d")(it)( - "match field query", + it_id('27349e04-0d9d-453f-ad85-1a811631582d')(it)( + 'match field query', done => { - const obj1 = new TestObject({ name: "TestObject1" }); - const obj2 = new TestObject({ name: "TestObject2" }); + const obj1 = new TestObject({ name: 'TestObject1' }); + const obj2 = new TestObject({ name: 'TestObject2' }); Parse.Object.saveAll([obj1, obj2]) .then(() => { - const pipeline = [{ $match: { name: "TestObject1" } }]; + const pipeline = [{ $match: { name: 'TestObject1' } }]; const query = new Parse.Query(TestObject); return query.aggregate(pipeline); }) @@ -912,8 +912,8 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("9222e025-d450-4699-8d5b-c5cf9a64fb24")(it)( - "match pointer query", + it_id('9222e025-d450-4699-8d5b-c5cf9a64fb24')(it)( + 'match pointer query', done => { const pointer1 = new PointerObject(); const pointer2 = new PointerObject(); @@ -942,8 +942,8 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("3a1e2cdc-52c7-4060-bc90-b06d557d85ce")(it_exclude_dbs(["postgres"]))( - "match exists query", + it_id('3a1e2cdc-52c7-4060-bc90-b06d557d85ce')(it_exclude_dbs(['postgres']))( + 'match exists query', done => { const pipeline = [{ $match: { score: { $exists: true } } }]; const query = new Parse.Query(TestObject); @@ -954,8 +954,8 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("0adea3f4-73f7-4b48-a7dd-c764ceb947ec")(it)( - "match date query - createdAt", + it_id('0adea3f4-73f7-4b48-a7dd-c764ceb947ec')(it)( + 'match date query - createdAt', done => { const obj1 = new TestObject(); const obj2 = new TestObject(); @@ -980,8 +980,8 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("cdc0eecb-f547-4881-84cc-c06fb46a636a")(it)( - "match date query - updatedAt", + it_id('cdc0eecb-f547-4881-84cc-c06fb46a636a')(it)( + 'match date query - updatedAt', done => { const obj1 = new TestObject(); const obj2 = new TestObject(); @@ -1006,8 +1006,8 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("621fe00a-1127-4341-a8e1-fc579b7ed8bd")(it)( - "match date query - empty", + it_id('621fe00a-1127-4341-a8e1-fc579b7ed8bd')(it)( + 'match date query - empty', done => { const obj1 = new TestObject(); const obj2 = new TestObject(); @@ -1031,8 +1031,8 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("802ffc99-861b-4b72-90a6-0c666a2e3fd8")(it_exclude_dbs(["postgres"]))( - "match pointer with operator query", + it_id('802ffc99-861b-4b72-90a6-0c666a2e3fd8')(it_exclude_dbs(['postgres']))( + 'match pointer with operator query', done => { const pointer = new PointerObject(); @@ -1061,35 +1061,35 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("28090280-7c3e-47f8-8bf6-bebf8566a36c")(it_exclude_dbs(["postgres"]))( - "match null values", + it_id('28090280-7c3e-47f8-8bf6-bebf8566a36c')(it_exclude_dbs(['postgres']))( + 'match null values', async () => { - const obj1 = new Parse.Object("MyCollection"); - obj1.set("language", "en"); - obj1.set("otherField", 1); - const obj2 = new Parse.Object("MyCollection"); - obj2.set("language", "en"); - obj2.set("otherField", 2); - const obj3 = new Parse.Object("MyCollection"); - obj3.set("language", null); - obj3.set("otherField", 3); - const obj4 = new Parse.Object("MyCollection"); - obj4.set("language", null); - obj4.set("otherField", 4); - const obj5 = new Parse.Object("MyCollection"); - obj5.set("language", "pt"); - obj5.set("otherField", 5); - const obj6 = new Parse.Object("MyCollection"); - obj6.set("language", "pt"); - obj6.set("otherField", 6); + const obj1 = new Parse.Object('MyCollection'); + obj1.set('language', 'en'); + obj1.set('otherField', 1); + const obj2 = new Parse.Object('MyCollection'); + obj2.set('language', 'en'); + obj2.set('otherField', 2); + const obj3 = new Parse.Object('MyCollection'); + obj3.set('language', null); + obj3.set('otherField', 3); + const obj4 = new Parse.Object('MyCollection'); + obj4.set('language', null); + obj4.set('otherField', 4); + const obj5 = new Parse.Object('MyCollection'); + obj5.set('language', 'pt'); + obj5.set('otherField', 5); + const obj6 = new Parse.Object('MyCollection'); + obj6.set('language', 'pt'); + obj6.set('otherField', 6); await Parse.Object.saveAll([obj1, obj2, obj3, obj4, obj5, obj6]); expect( ( - await new Parse.Query("MyCollection").aggregate([ + await new Parse.Query('MyCollection').aggregate([ { $match: { - language: { $in: [null, "en"] }, + language: { $in: [null, 'en'] }, }, }, ]) @@ -1100,10 +1100,10 @@ describe("Parse.Query Aggregate testing", () => { expect( ( - await new Parse.Query("MyCollection").aggregate([ + await new Parse.Query('MyCollection').aggregate([ { $match: { - $or: [{ language: "en" }, { language: null }], + $or: [{ language: 'en' }, { language: null }], }, }, ]) @@ -1114,13 +1114,13 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("df63d1f5-7c37-4ed9-8bc5-20d82f29f509")(it)("project query", done => { + it_id('df63d1f5-7c37-4ed9-8bc5-20d82f29f509')(it)('project query', done => { const options = Object.assign({}, masterKeyOptions, { body: { $project: { name: 1 }, }, }); - get(Parse.serverURL + "/aggregate/TestObject", options) + get(Parse.serverURL + '/aggregate/TestObject', options) .then(resp => { resp.results.forEach(result => { expect(result.objectId).not.toBe(undefined); @@ -1134,15 +1134,15 @@ describe("Parse.Query Aggregate testing", () => { .catch(done.fail); }); - it_id("69224bbb-8ea0-4ab4-af23-398b6432f668")(it)( - "multiple project query", + it_id('69224bbb-8ea0-4ab4-af23-398b6432f668')(it)( + 'multiple project query', done => { const options = Object.assign({}, masterKeyOptions, { body: { $project: { name: 1, score: 1, sender: 1 }, }, }); - get(Parse.serverURL + "/aggregate/TestObject", options) + get(Parse.serverURL + '/aggregate/TestObject', options) .then(resp => { resp.results.forEach(result => { expect(result.objectId).not.toBe(undefined); @@ -1157,11 +1157,11 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("97ce4c7c-8d9f-4ffd-9352-394bc9867bab")(it)( - "project pointer query", + it_id('97ce4c7c-8d9f-4ffd-9352-394bc9867bab')(it)( + 'project pointer query', done => { const pointer = new PointerObject(); - const obj = new TestObject({ pointer, name: "hello" }); + const obj = new TestObject({ pointer, name: 'hello' }); obj .save() @@ -1175,7 +1175,7 @@ describe("Parse.Query Aggregate testing", () => { }) .then(results => { expect(results.length).toEqual(1); - expect(results[0].name).toEqual("hello"); + expect(results[0].name).toEqual('hello'); expect(results[0].createdAt).not.toBe(undefined); expect(results[0].pointer.objectId).toEqual(pointer.id); done(); @@ -1183,21 +1183,21 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("3940aac3-ac49-4279-8083-af9096de636f")(it)( - "project with group query", + it_id('3940aac3-ac49-4279-8083-af9096de636f')(it)( + 'project with group query', done => { const options = Object.assign({}, masterKeyOptions, { body: { $project: { score: 1 }, - $group: { _id: "$score", score: { $sum: "$score" } }, + $group: { _id: '$score', score: { $sum: '$score' } }, }, }); - get(Parse.serverURL + "/aggregate/TestObject", options) + get(Parse.serverURL + '/aggregate/TestObject', options) .then(resp => { expect(resp.results.length).toBe(2); resp.results.forEach(result => { expect( - Object.prototype.hasOwnProperty.call(result, "objectId") + Object.prototype.hasOwnProperty.call(result, 'objectId') ).toBe(true); expect(result.name).toBe(undefined); expect(result.sender).toBe(undefined); @@ -1216,13 +1216,13 @@ describe("Parse.Query Aggregate testing", () => { } ); - it("class does not exist return empty", done => { + it('class does not exist return empty', done => { const options = Object.assign({}, masterKeyOptions, { body: { - $group: { _id: null, total: { $sum: "$score" } }, + $group: { _id: null, total: { $sum: '$score' } }, }, }); - get(Parse.serverURL + "/aggregate/UnknownClass", options) + get(Parse.serverURL + '/aggregate/UnknownClass', options) .then(resp => { expect(resp.results.length).toBe(0); done(); @@ -1230,13 +1230,13 @@ describe("Parse.Query Aggregate testing", () => { .catch(done.fail); }); - it("field does not exist return empty", done => { + it('field does not exist return empty', done => { const options = Object.assign({}, masterKeyOptions, { body: { - $group: { _id: null, total: { $sum: "$unknownfield" } }, + $group: { _id: null, total: { $sum: '$unknownfield' } }, }, }); - get(Parse.serverURL + "/aggregate/UnknownClass", options) + get(Parse.serverURL + '/aggregate/UnknownClass', options) .then(resp => { expect(resp.results.length).toBe(0); done(); @@ -1244,11 +1244,11 @@ describe("Parse.Query Aggregate testing", () => { .catch(done.fail); }); - it_id("985e7a66-d4f5-4f72-bd54-ee44670e0ab0")(it)("distinct query", done => { + it_id('985e7a66-d4f5-4f72-bd54-ee44670e0ab0')(it)('distinct query', done => { const options = Object.assign({}, masterKeyOptions, { - body: { distinct: "score" }, + body: { distinct: 'score' }, }); - get(Parse.serverURL + "/aggregate/TestObject", options) + get(Parse.serverURL + '/aggregate/TestObject', options) .then(resp => { expect(resp.results.length).toBe(2); expect(resp.results.includes(10)).toBe(true); @@ -1258,18 +1258,18 @@ describe("Parse.Query Aggregate testing", () => { .catch(done.fail); }); - it_id("ef157f86-c456-4a4c-8dac-81910bd0f716")(it)( - "distinct query with where", + it_id('ef157f86-c456-4a4c-8dac-81910bd0f716')(it)( + 'distinct query with where', done => { const options = Object.assign({}, masterKeyOptions, { body: { - distinct: "score", + distinct: 'score', $where: { - name: "bar", + name: 'bar', }, }, }); - get(Parse.serverURL + "/aggregate/TestObject", options) + get(Parse.serverURL + '/aggregate/TestObject', options) .then(resp => { expect(resp.results[0]).toBe(10); done(); @@ -1278,16 +1278,16 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("7f5275cc-2c34-42bc-8a09-43378419c326")(it)( - "distinct query with where string", + it_id('7f5275cc-2c34-42bc-8a09-43378419c326')(it)( + 'distinct query with where string', done => { const options = Object.assign({}, masterKeyOptions, { body: { - distinct: "score", - $where: JSON.stringify({ name: "bar" }), + distinct: 'score', + $where: JSON.stringify({ name: 'bar' }), }, }); - get(Parse.serverURL + "/aggregate/TestObject", options) + get(Parse.serverURL + '/aggregate/TestObject', options) .then(resp => { expect(resp.results[0]).toBe(10); done(); @@ -1296,22 +1296,22 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("383b7248-e457-4373-8d5c-f9359384347e")(it)("distinct nested", done => { + it_id('383b7248-e457-4373-8d5c-f9359384347e')(it)('distinct nested', done => { const options = Object.assign({}, masterKeyOptions, { - body: { distinct: "sender.group" }, + body: { distinct: 'sender.group' }, }); - get(Parse.serverURL + "/aggregate/TestObject", options) + get(Parse.serverURL + '/aggregate/TestObject', options) .then(resp => { expect(resp.results.length).toBe(2); - expect(resp.results.includes("A")).toBe(true); - expect(resp.results.includes("B")).toBe(true); + expect(resp.results.includes('A')).toBe(true); + expect(resp.results.includes('B')).toBe(true); done(); }) .catch(done.fail); }); - it_id("20f14464-adb7-428c-ac7a-5a91a1952a64")(it)( - "distinct pointer", + it_id('20f14464-adb7-428c-ac7a-5a91a1952a64')(it)( + 'distinct pointer', done => { const pointer1 = new PointerObject(); const pointer2 = new PointerObject(); @@ -1321,7 +1321,7 @@ describe("Parse.Query Aggregate testing", () => { Parse.Object.saveAll([pointer1, pointer2, obj1, obj2, obj3]) .then(() => { const query = new Parse.Query(TestObject); - return query.distinct("pointer"); + return query.distinct('pointer'); }) .then(results => { expect(results.length).toEqual(2); @@ -1336,13 +1336,13 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("91e6cb94-2837-44b7-b057-0c4965057caa")(it)( - "distinct class does not exist return empty", + it_id('91e6cb94-2837-44b7-b057-0c4965057caa')(it)( + 'distinct class does not exist return empty', done => { const options = Object.assign({}, masterKeyOptions, { - body: { distinct: "unknown" }, + body: { distinct: 'unknown' }, }); - get(Parse.serverURL + "/aggregate/UnknownClass", options) + get(Parse.serverURL + '/aggregate/UnknownClass', options) .then(resp => { expect(resp.results.length).toBe(0); done(); @@ -1351,17 +1351,17 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("bd15daaf-8dc7-458c-81e2-170026f4a8a7")(it)( - "distinct field does not exist return empty", + it_id('bd15daaf-8dc7-458c-81e2-170026f4a8a7')(it)( + 'distinct field does not exist return empty', done => { const options = Object.assign({}, masterKeyOptions, { - body: { distinct: "unknown" }, + body: { distinct: 'unknown' }, }); const obj = new TestObject(); obj .save() .then(() => { - return get(Parse.serverURL + "/aggregate/TestObject", options); + return get(Parse.serverURL + '/aggregate/TestObject', options); }) .then(resp => { expect(resp.results.length).toBe(0); @@ -1371,94 +1371,94 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("21988fce-8326-425f-82f0-cd444ca3671b")(it)("distinct array", done => { + it_id('21988fce-8326-425f-82f0-cd444ca3671b')(it)('distinct array', done => { const options = Object.assign({}, masterKeyOptions, { - body: { distinct: "size" }, + body: { distinct: 'size' }, }); - get(Parse.serverURL + "/aggregate/TestObject", options) + get(Parse.serverURL + '/aggregate/TestObject', options) .then(resp => { expect(resp.results.length).toBe(3); - expect(resp.results.includes("S")).toBe(true); - expect(resp.results.includes("M")).toBe(true); - expect(resp.results.includes("L")).toBe(true); + expect(resp.results.includes('S')).toBe(true); + expect(resp.results.includes('M')).toBe(true); + expect(resp.results.includes('L')).toBe(true); done(); }) .catch(done.fail); }); - it_id("633fde06-c4af-474b-9841-3ccabc24dd4f")(it)( - "distinct objectId", + it_id('633fde06-c4af-474b-9841-3ccabc24dd4f')(it)( + 'distinct objectId', async () => { const query = new Parse.Query(TestObject); - const results = await query.distinct("objectId"); + const results = await query.distinct('objectId'); expect(results.length).toBe(4); } ); - it_id("8f9706f4-2703-42f1-b524-f2f7e72bbfe7")(it)( - "distinct createdAt", + it_id('8f9706f4-2703-42f1-b524-f2f7e72bbfe7')(it)( + 'distinct createdAt', async () => { const object1 = new TestObject({ createdAt_test: true }); await object1.save(); const object2 = new TestObject({ createdAt_test: true }); await object2.save(); const query = new Parse.Query(TestObject); - query.equalTo("createdAt_test", true); - const results = await query.distinct("createdAt"); + query.equalTo('createdAt_test', true); + const results = await query.distinct('createdAt'); expect(results.length).toBe(2); } ); - it_id("3562e600-8ce5-4d6d-96df-8ff969e81421")(it)( - "distinct updatedAt", + it_id('3562e600-8ce5-4d6d-96df-8ff969e81421')(it)( + 'distinct updatedAt', async () => { const object1 = new TestObject({ updatedAt_test: true }); await object1.save(); const object2 = new TestObject(); await object2.save(); - object2.set("updatedAt_test", true); + object2.set('updatedAt_test', true); await object2.save(); const query = new Parse.Query(TestObject); - query.equalTo("updatedAt_test", true); - const results = await query.distinct("updatedAt"); + query.equalTo('updatedAt_test', true); + const results = await query.distinct('updatedAt'); expect(results.length).toBe(2); } ); - it_id("5012cfb1-b0aa-429d-a94f-d32d8aa0b7f9")(it)( - "distinct null field", + it_id('5012cfb1-b0aa-429d-a94f-d32d8aa0b7f9')(it)( + 'distinct null field', done => { const options = Object.assign({}, masterKeyOptions, { - body: { distinct: "distinctField" }, + body: { distinct: 'distinctField' }, }); const user1 = new Parse.User(); - user1.setUsername("distinct_1"); - user1.setPassword("password"); - user1.set("distinctField", "one"); + user1.setUsername('distinct_1'); + user1.setPassword('password'); + user1.set('distinctField', 'one'); const user2 = new Parse.User(); - user2.setUsername("distinct_2"); - user2.setPassword("password"); - user2.set("distinctField", null); + user2.setUsername('distinct_2'); + user2.setPassword('password'); + user2.set('distinctField', null); user1 .signUp() .then(() => { return user2.signUp(); }) .then(() => { - return get(Parse.serverURL + "/aggregate/_User", options); + return get(Parse.serverURL + '/aggregate/_User', options); }) .then(resp => { expect(resp.results.length).toEqual(1); - expect(resp.results).toEqual(["one"]); + expect(resp.results).toEqual(['one']); done(); }) .catch(done.fail); } ); - it_id("d9c19419-e99d-4d9f-b7f3-418e49ee47dd")(it)( - "does not return sensitive hidden properties", + it_id('d9c19419-e99d-4d9f-b7f3-418e49ee47dd')(it)( + 'does not return sensitive hidden properties', done => { const options = Object.assign({}, masterKeyOptions, { body: { @@ -1470,17 +1470,17 @@ describe("Parse.Query Aggregate testing", () => { }, }); - const username = "leaky_user"; + const username = 'leaky_user'; const score = 10; const user = new Parse.User(); user.setUsername(username); - user.setPassword("password"); - user.set("score", score); + user.setPassword('password'); + user.set('score', score); user .signUp() .then(function () { - return get(Parse.serverURL + "/aggregate/_User", options); + return get(Parse.serverURL + '/aggregate/_User', options); }) .then(function (resp) { expect(resp.results.length).toBe(1); @@ -1509,54 +1509,54 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_id("0a23e791-e9b5-457a-9bf9-9c5ecf406f42")(it_exclude_dbs(["postgres"]))( - "aggregate allow multiple of same stage", + it_id('0a23e791-e9b5-457a-9bf9-9c5ecf406f42')(it_exclude_dbs(['postgres']))( + 'aggregate allow multiple of same stage', async done => { await reconfigureServer({ silent: false }); 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 obj1 = new TestObject({ pointer: pointer1, name: 'Hello' }); + const obj2 = new TestObject({ pointer: pointer2, name: 'Hello' }); + const obj3 = new TestObject({ pointer: pointer3, name: 'World' }); const options = Object.assign({}, masterKeyOptions, { body: { pipeline: [ { - $match: { name: "Hello" }, + $match: { name: 'Hello' }, }, { // Transform className$objectId to objectId and store in new field tempPointer $project: { - tempPointer: { $substr: ["$_p_pointer", 11, -1] }, // Remove TestObject$ + tempPointer: { $substr: ['$_p_pointer', 11, -1] }, // Remove TestObject$ }, }, { // Left Join, replace objectId stored in tempPointer with an actual object $lookup: { - from: "test_TestObject", - localField: "tempPointer", - foreignField: "_id", - as: "tempPointer", + from: 'test_TestObject', + localField: 'tempPointer', + foreignField: '_id', + as: 'tempPointer', }, }, { // lookup returns an array, Deconstructs an array field to objects $unwind: { - path: "$tempPointer", + path: '$tempPointer', }, }, { - $match: { "tempPointer.value": 2 }, + $match: { 'tempPointer.value': 2 }, }, ], }, }); Parse.Object.saveAll([pointer1, pointer2, pointer3, obj1, obj2, obj3]) .then(() => { - return get(Parse.serverURL + "/aggregate/TestObject", options); + return get(Parse.serverURL + '/aggregate/TestObject', options); }) .then(resp => { expect(resp.results.length).toEqual(1); @@ -1566,22 +1566,22 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_only_db("mongo")("aggregate geoNear with location query", async () => { + it_only_db('mongo')('aggregate geoNear with location query', async () => { // Create geo index which is required for `geoNear` query const database = Config.get(Parse.applicationId).database; - const schema = await new Parse.Schema("GeoObject").save(); + const schema = await new Parse.Schema('GeoObject').save(); await database.adapter.ensureIndex( - "GeoObject", + 'GeoObject', schema, - ["location"], + ['location'], undefined, false, { - indexType: "2dsphere", + indexType: '2dsphere', } ); // Create objects - const GeoObject = Parse.Object.extend("GeoObject"); + const GeoObject = Parse.Object.extend('GeoObject'); const obj1 = new GeoObject({ value: 1, location: new Parse.GeoPoint(1, 1), @@ -1603,12 +1603,12 @@ describe("Parse.Query Aggregate testing", () => { { $geoNear: { near: { - type: "Point", + type: 'Point', coordinates: [1, 1], }, - key: "location", + key: 'location', spherical: true, - distanceField: "dist", + distanceField: 'dist', query: { date: { $gte: new Date(2), @@ -1626,22 +1626,22 @@ describe("Parse.Query Aggregate testing", () => { await database.adapter.deleteAllClasses(false); }); - it_only_db("mongo")("aggregate geoNear with near GeoJSON point", async () => { + it_only_db('mongo')('aggregate geoNear with near GeoJSON point', async () => { // Create geo index which is required for `geoNear` query const database = Config.get(Parse.applicationId).database; - const schema = await new Parse.Schema("GeoObject").save(); + const schema = await new Parse.Schema('GeoObject').save(); await database.adapter.ensureIndex( - "GeoObject", + 'GeoObject', schema, - ["location"], + ['location'], undefined, false, { - indexType: "2dsphere", + indexType: '2dsphere', } ); // Create objects - const GeoObject = Parse.Object.extend("GeoObject"); + const GeoObject = Parse.Object.extend('GeoObject'); const obj1 = new GeoObject({ value: 1, location: new Parse.GeoPoint(1, 1), @@ -1663,12 +1663,12 @@ describe("Parse.Query Aggregate testing", () => { { $geoNear: { near: { - type: "Point", + type: 'Point', coordinates: [1, 1], }, - key: "location", + key: 'location', spherical: true, - distanceField: "dist", + distanceField: 'dist', }, }, ]; @@ -1679,24 +1679,24 @@ describe("Parse.Query Aggregate testing", () => { await database.adapter.deleteAllClasses(false); }); - it_only_db("mongo")( - "aggregate geoNear with near legacy coordinate pair", + it_only_db('mongo')( + 'aggregate geoNear with near legacy coordinate pair', async () => { // Create geo index which is required for `geoNear` query const database = Config.get(Parse.applicationId).database; - const schema = await new Parse.Schema("GeoObject").save(); + const schema = await new Parse.Schema('GeoObject').save(); await database.adapter.ensureIndex( - "GeoObject", + 'GeoObject', schema, - ["location"], + ['location'], undefined, false, { - indexType: "2dsphere", + indexType: '2dsphere', } ); // Create objects - const GeoObject = Parse.Object.extend("GeoObject"); + const GeoObject = Parse.Object.extend('GeoObject'); const obj1 = new GeoObject({ value: 1, location: new Parse.GeoPoint(1, 1), @@ -1718,9 +1718,9 @@ describe("Parse.Query Aggregate testing", () => { { $geoNear: { near: [1, 1], - key: "location", + key: 'location', spherical: true, - distanceField: "dist", + distanceField: 'dist', }, }, ]; @@ -1732,14 +1732,14 @@ describe("Parse.Query Aggregate testing", () => { } ); - it_only_db("mongo")("aggregate handle mongodb errors", async () => { + it_only_db('mongo')('aggregate handle mongodb errors', async () => { const pipeline = [ { $search: { - index: "default", + index: 'default', text: { - path: ["name"], - query: "foo", + path: ['name'], + query: 'foo', }, }, }, diff --git a/spec/ParseQuery.Comment.spec.js b/spec/ParseQuery.Comment.spec.js index 111e06ea39..3b3fd07ce2 100644 --- a/spec/ParseQuery.Comment.spec.js +++ b/spec/ParseQuery.Comment.spec.js @@ -1,17 +1,17 @@ -"use strict"; +'use strict'; -const Config = require("../lib/Config"); -const { MongoClient } = require("mongodb"); -const databaseURI = "mongodb://localhost:27017/"; -const request = require("../lib/request"); +const Config = require('../lib/Config'); +const { MongoClient } = require('mongodb'); +const databaseURI = 'mongodb://localhost:27017/'; +const request = require('../lib/request'); let config, client, database; const masterKeyHeaders = { - "X-Parse-Application-Id": "test", - "X-Parse-Rest-API-Key": "rest", - "X-Parse-Master-Key": "test", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Rest-API-Key': 'rest', + 'X-Parse-Master-Key': 'test', + 'Content-Type': 'application/json', }; const masterKeyOptions = { @@ -20,11 +20,11 @@ const masterKeyOptions = { }; const profileLevel = 2; -describe_only_db("mongo")("Parse.Query with comment testing", () => { +describe_only_db('mongo')('Parse.Query with comment testing', () => { beforeAll(async () => { - config = Config.get("test"); + config = Config.get('test'); client = await MongoClient.connect(databaseURI); - database = client.db("parseServerMongoAdapterTestDatabase"); + database = client.db('parseServerMongoAdapterTestDatabase'); let profiler = await database.command({ profile: 0 }); expect(profiler.was).toEqual(0); // console.log(`Disabling profiler : ${profiler.was}`); @@ -45,13 +45,13 @@ describe_only_db("mongo")("Parse.Query with comment testing", () => { await client.close(); }); - it("send comment with query through REST", async () => { - const comment = "Hello Parse"; + it('send comment with query through REST', async () => { + const comment = 'Hello Parse'; const object = new TestObject(); - object.set("name", "object"); + object.set('name', 'object'); await object.save(); const options = Object.assign({}, masterKeyOptions, { - url: Parse.serverURL + "/classes/TestObject", + url: Parse.serverURL + '/classes/TestObject', qs: { explain: true, comment: comment, @@ -59,61 +59,61 @@ describe_only_db("mongo")("Parse.Query with comment testing", () => { }); await request(options); const result = await database - .collection("system.profile") + .collection('system.profile') .findOne({}, { sort: { ts: -1 } }); expect(result.command.explain.comment).toBe(comment); }); - it("send comment with query", async () => { - const comment = "Hello Parse"; + it('send comment with query', async () => { + const comment = 'Hello Parse'; const object = new TestObject(); - object.set("name", "object"); + object.set('name', 'object'); await object.save(); const collection = - await config.database.adapter._adaptiveCollection("TestObject"); - await collection._rawFind({ name: "object" }, { comment: comment }); + await config.database.adapter._adaptiveCollection('TestObject'); + await collection._rawFind({ name: 'object' }, { comment: comment }); const result = await database - .collection("system.profile") + .collection('system.profile') .findOne({}, { sort: { ts: -1 } }); expect(result.command.comment).toBe(comment); }); - it("send a comment with a count query", async () => { - const comment = "Hello Parse"; + it('send a comment with a count query', async () => { + const comment = 'Hello Parse'; const object = new TestObject(); - object.set("name", "object"); + object.set('name', 'object'); await object.save(); const object2 = new TestObject(); - object2.set("name", "object"); + object2.set('name', 'object'); await object2.save(); const collection = - await config.database.adapter._adaptiveCollection("TestObject"); + await config.database.adapter._adaptiveCollection('TestObject'); const countResult = await collection.count( - { name: "object" }, + { name: 'object' }, { comment: comment } ); expect(countResult).toEqual(2); const result = await database - .collection("system.profile") + .collection('system.profile') .findOne({}, { sort: { ts: -1 } }); expect(result.command.comment).toBe(comment); }); - it("attach a comment to an aggregation", async () => { - const comment = "Hello Parse"; + it('attach a comment to an aggregation', async () => { + const comment = 'Hello Parse'; const object = new TestObject(); - object.set("name", "object"); + object.set('name', 'object'); await object.save(); const collection = - await config.database.adapter._adaptiveCollection("TestObject"); - await collection.aggregate([{ $group: { _id: "$name" } }], { + await config.database.adapter._adaptiveCollection('TestObject'); + await collection.aggregate([{ $group: { _id: '$name' } }], { explain: true, comment: comment, }); const result = await database - .collection("system.profile") + .collection('system.profile') .findOne({}, { sort: { ts: -1 } }); expect(result.command.explain.comment).toBe(comment); }); diff --git a/spec/ParseQuery.FullTextSearch.spec.js b/spec/ParseQuery.FullTextSearch.spec.js index 4443e1d500..edbbccc7f2 100644 --- a/spec/ParseQuery.FullTextSearch.spec.js +++ b/spec/ParseQuery.FullTextSearch.spec.js @@ -1,99 +1,99 @@ -"use strict"; +'use strict'; -const Config = require("../lib/Config"); -const Parse = require("parse/node"); -const request = require("../lib/request"); +const Config = require('../lib/Config'); +const Parse = require('parse/node'); +const request = require('../lib/request'); const fullTextHelper = async () => { const subjects = [ - "coffee", - "Coffee Shopping", - "Baking a cake", - "baking", - "Café Con Leche", - "Сырники", - "coffee and cream", - "Cafe con Leche", + 'coffee', + 'Coffee Shopping', + 'Baking a cake', + 'baking', + 'Café Con Leche', + 'Сырники', + 'coffee and cream', + 'Cafe con Leche', ]; await Parse.Object.saveAll( subjects.map(subject => - new Parse.Object("TestObject").set({ subject, comment: subject }) + new Parse.Object('TestObject').set({ subject, comment: subject }) ) ); }; -describe("Parse.Query Full Text Search testing", () => { - it_id("77ba6779-6584-4e09-8e7e-31f89e741d6a")(it)( - "fullTextSearch: $search", +describe('Parse.Query Full Text Search testing', () => { + it_id('77ba6779-6584-4e09-8e7e-31f89e741d6a')(it)( + 'fullTextSearch: $search', async () => { await fullTextHelper(); - const query = new Parse.Query("TestObject"); - query.fullText("subject", "coffee"); + const query = new Parse.Query('TestObject'); + query.fullText('subject', 'coffee'); const results = await query.find(); expect(results.length).toBe(3); } ); - it_id("d1992ea6-6d92-4bfa-a487-2a49fbcf8f0d")(it)( - "fullTextSearch: $search, sort", + it_id('d1992ea6-6d92-4bfa-a487-2a49fbcf8f0d')(it)( + 'fullTextSearch: $search, sort', async () => { await fullTextHelper(); - const query = new Parse.Query("TestObject"); - query.fullText("subject", "coffee"); - query.select("$score"); - query.ascending("$score"); + const query = new Parse.Query('TestObject'); + query.fullText('subject', 'coffee'); + query.select('$score'); + query.ascending('$score'); const results = await query.find(); expect(results.length).toBe(3); - expect(results[0].get("score")); - expect(results[1].get("score")); - expect(results[2].get("score")); + expect(results[0].get('score')); + expect(results[1].get('score')); + expect(results[2].get('score')); } ); - it_id("07172595-50de-4be2-984a-d3136bebb22e")(it)( - "fulltext descending by $score", + it_id('07172595-50de-4be2-984a-d3136bebb22e')(it)( + 'fulltext descending by $score', async () => { await fullTextHelper(); - const query = new Parse.Query("TestObject"); - query.fullText("subject", "coffee"); - query.descending("$score"); - query.select("$score"); + const query = new Parse.Query('TestObject'); + query.fullText('subject', 'coffee'); + query.descending('$score'); + query.select('$score'); const [first, second, third] = await query.find(); expect(first).toBeDefined(); expect(second).toBeDefined(); expect(third).toBeDefined(); - expect(first.get("score")); - expect(second.get("score")); - expect(third.get("score")); - expect(first.get("score") >= second.get("score")).toBeTrue(); - expect(second.get("score") >= third.get("score")).toBeTrue(); + expect(first.get('score')); + expect(second.get('score')); + expect(third.get('score')); + expect(first.get('score') >= second.get('score')).toBeTrue(); + expect(second.get('score') >= third.get('score')).toBeTrue(); } ); - it_id("8e821973-3fae-4e7c-8152-766228a18cdd")(it)( - "fullTextSearch: $language", + it_id('8e821973-3fae-4e7c-8152-766228a18cdd')(it)( + 'fullTextSearch: $language', async () => { await fullTextHelper(); - const query = new Parse.Query("TestObject"); - query.fullText("subject", "leche", { language: "spanish" }); + const query = new Parse.Query('TestObject'); + query.fullText('subject', 'leche', { language: 'spanish' }); const resp = await query.find(); expect(resp.length).toBe(2); } ); - it_id("7d3da216-9582-40ee-a2fe-8316feaf5c0c")(it)( - "fullTextSearch: $diacriticSensitive", + it_id('7d3da216-9582-40ee-a2fe-8316feaf5c0c')(it)( + 'fullTextSearch: $diacriticSensitive', async () => { await fullTextHelper(); - const query = new Parse.Query("TestObject"); - query.fullText("subject", "CAFÉ", { diacriticSensitive: true }); + const query = new Parse.Query('TestObject'); + query.fullText('subject', 'CAFÉ', { diacriticSensitive: true }); const resp = await query.find(); expect(resp.length).toBe(1); } ); - it_id("dade10c8-2b9c-4f43-bb3f-a13bbd82ac22")(it)( - "fullTextSearch: $search, invalid input", + it_id('dade10c8-2b9c-4f43-bb3f-a13bbd82ac22')(it)( + 'fullTextSearch: $search, invalid input', async () => { await fullTextHelper(); const invalidQuery = async () => { @@ -106,13 +106,13 @@ describe("Parse.Query Full Text Search testing", () => { }; try { await request({ - method: "POST", - url: "http://localhost:8378/1/classes/TestObject", - body: { where, _method: "GET" }, + method: 'POST', + url: 'http://localhost:8378/1/classes/TestObject', + body: { where, _method: 'GET' }, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, }); } catch (e) { @@ -122,120 +122,120 @@ describe("Parse.Query Full Text Search testing", () => { await expectAsync(invalidQuery()).toBeRejectedWith( new Parse.Error( Parse.Error.INVALID_JSON, - "bad $text: $search, should be object" + 'bad $text: $search, should be object' ) ); } ); - it_id("ff7c6b1c-4712-4847-bb76-f4e1f641f7b5")(it)( - "fullTextSearch: $language, invalid input", + it_id('ff7c6b1c-4712-4847-bb76-f4e1f641f7b5')(it)( + 'fullTextSearch: $language, invalid input', async () => { await fullTextHelper(); - const query = new Parse.Query("TestObject"); - query.fullText("subject", "leche", { language: true }); + const query = new Parse.Query('TestObject'); + query.fullText('subject', 'leche', { language: true }); await expectAsync(query.find()).toBeRejectedWith( new Parse.Error( Parse.Error.INVALID_JSON, - "bad $text: $language, should be string" + 'bad $text: $language, should be string' ) ); } ); - it_id("de262dbc-ec75-4ec6-9217-fbb90146c272")(it)( - "fullTextSearch: $caseSensitive, invalid input", + it_id('de262dbc-ec75-4ec6-9217-fbb90146c272')(it)( + 'fullTextSearch: $caseSensitive, invalid input', async () => { await fullTextHelper(); - const query = new Parse.Query("TestObject"); - query.fullText("subject", "leche", { caseSensitive: "string" }); + const query = new Parse.Query('TestObject'); + query.fullText('subject', 'leche', { caseSensitive: 'string' }); await expectAsync(query.find()).toBeRejectedWith( new Parse.Error( Parse.Error.INVALID_JSON, - "bad $text: $caseSensitive, should be boolean" + 'bad $text: $caseSensitive, should be boolean' ) ); } ); - it_id("b7b7b3a9-8d6c-4f98-a0ff-0113593d06d4")(it)( - "fullTextSearch: $diacriticSensitive, invalid input", + it_id('b7b7b3a9-8d6c-4f98-a0ff-0113593d06d4')(it)( + 'fullTextSearch: $diacriticSensitive, invalid input', async () => { await fullTextHelper(); - const query = new Parse.Query("TestObject"); - query.fullText("subject", "leche", { diacriticSensitive: "string" }); + const query = new Parse.Query('TestObject'); + query.fullText('subject', 'leche', { diacriticSensitive: 'string' }); await expectAsync(query.find()).toBeRejectedWith( new Parse.Error( Parse.Error.INVALID_JSON, - "bad $text: $diacriticSensitive, should be boolean" + 'bad $text: $diacriticSensitive, should be boolean' ) ); } ); }); -describe_only_db("mongo")( - "[mongodb] Parse.Query Full Text Search testing", +describe_only_db('mongo')( + '[mongodb] Parse.Query Full Text Search testing', () => { - it("fullTextSearch: does not create text index if compound index exist", async () => { + it('fullTextSearch: does not create text index if compound index exist', async () => { await fullTextHelper(); - await databaseAdapter.dropAllIndexes("TestObject"); - let indexes = await databaseAdapter.getIndexes("TestObject"); + await databaseAdapter.dropAllIndexes('TestObject'); + let indexes = await databaseAdapter.getIndexes('TestObject'); expect(indexes.length).toEqual(1); - await databaseAdapter.createIndex("TestObject", { - subject: "text", - comment: "text", + await databaseAdapter.createIndex('TestObject', { + subject: 'text', + comment: 'text', }); - indexes = await databaseAdapter.getIndexes("TestObject"); - const query = new Parse.Query("TestObject"); - query.fullText("subject", "coffee"); - query.select("$score"); - query.ascending("$score"); + indexes = await databaseAdapter.getIndexes('TestObject'); + const query = new Parse.Query('TestObject'); + query.fullText('subject', 'coffee'); + query.select('$score'); + query.ascending('$score'); const results = await query.find(); expect(results.length).toBe(3); - expect(results[0].get("score")); - expect(results[1].get("score")); - expect(results[2].get("score")); + expect(results[0].get('score')); + expect(results[1].get('score')); + expect(results[2].get('score')); - indexes = await databaseAdapter.getIndexes("TestObject"); + indexes = await databaseAdapter.getIndexes('TestObject'); expect(indexes.length).toEqual(2); - const schemas = await new Parse.Schema("TestObject").get(); + const schemas = await new Parse.Schema('TestObject').get(); expect(schemas.indexes._id_).toBeDefined(); expect(schemas.indexes._id_._id).toEqual(1); expect(schemas.indexes.subject_text_comment_text).toBeDefined(); - expect(schemas.indexes.subject_text_comment_text.subject).toEqual("text"); - expect(schemas.indexes.subject_text_comment_text.comment).toEqual("text"); + expect(schemas.indexes.subject_text_comment_text.subject).toEqual('text'); + expect(schemas.indexes.subject_text_comment_text.comment).toEqual('text'); }); - it("fullTextSearch: does not create text index if schema compound index exist", done => { + it('fullTextSearch: does not create text index if schema compound index exist', done => { fullTextHelper() .then(() => { - return databaseAdapter.dropAllIndexes("TestObject"); + return databaseAdapter.dropAllIndexes('TestObject'); }) .then(() => { - return databaseAdapter.getIndexes("TestObject"); + return databaseAdapter.getIndexes('TestObject'); }) .then(indexes => { expect(indexes.length).toEqual(1); return request({ - method: "PUT", - url: "http://localhost:8378/1/schemas/TestObject", + method: 'PUT', + url: 'http://localhost:8378/1/schemas/TestObject', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "X-Parse-Master-Key": "test", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Master-Key': 'test', + 'Content-Type': 'application/json', }, body: { indexes: { - text_test: { subject: "text", comment: "text" }, + text_test: { subject: 'text', comment: 'text' }, }, }, }); }) .then(() => { - return databaseAdapter.getIndexes("TestObject"); + return databaseAdapter.getIndexes('TestObject'); }) .then(indexes => { expect(indexes.length).toEqual(2); @@ -243,90 +243,90 @@ describe_only_db("mongo")( subject: { $text: { $search: { - $term: "coffee", + $term: 'coffee', }, }, }, }; return request({ - method: "POST", - url: "http://localhost:8378/1/classes/TestObject", - body: { where, _method: "GET" }, + method: 'POST', + url: 'http://localhost:8378/1/classes/TestObject', + body: { where, _method: 'GET' }, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, }); }) .then(resp => { expect(resp.data.results.length).toEqual(3); - return databaseAdapter.getIndexes("TestObject"); + return databaseAdapter.getIndexes('TestObject'); }) .then(indexes => { expect(indexes.length).toEqual(2); request({ - url: "http://localhost:8378/1/schemas/TestObject", + url: 'http://localhost:8378/1/schemas/TestObject', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Master-Key": "test", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Master-Key': 'test', + 'Content-Type': 'application/json', }, }).then(response => { const body = response.data; expect(body.indexes._id_).toBeDefined(); expect(body.indexes._id_._id).toEqual(1); expect(body.indexes.text_test).toBeDefined(); - expect(body.indexes.text_test.subject).toEqual("text"); - expect(body.indexes.text_test.comment).toEqual("text"); + expect(body.indexes.text_test.subject).toEqual('text'); + expect(body.indexes.text_test.comment).toEqual('text'); done(); }); }) .catch(done.fail); }); - it("fullTextSearch: $diacriticSensitive - false", async () => { + it('fullTextSearch: $diacriticSensitive - false', async () => { await fullTextHelper(); - const query = new Parse.Query("TestObject"); - query.fullText("subject", "CAFÉ", { diacriticSensitive: false }); + const query = new Parse.Query('TestObject'); + query.fullText('subject', 'CAFÉ', { diacriticSensitive: false }); const resp = await query.find(); expect(resp.length).toBe(2); }); - it("fullTextSearch: $caseSensitive", async () => { + it('fullTextSearch: $caseSensitive', async () => { await fullTextHelper(); - const query = new Parse.Query("TestObject"); - query.fullText("subject", "Coffee", { caseSensitive: true }); + const query = new Parse.Query('TestObject'); + query.fullText('subject', 'Coffee', { caseSensitive: true }); const results = await query.find(); expect(results.length).toBe(1); }); } ); -describe_only_db("postgres")( - "[postgres] Parse.Query Full Text Search testing", +describe_only_db('postgres')( + '[postgres] Parse.Query Full Text Search testing', () => { - it("fullTextSearch: $diacriticSensitive - false", done => { + it('fullTextSearch: $diacriticSensitive - false', done => { fullTextHelper() .then(() => { const where = { subject: { $text: { $search: { - $term: "CAFÉ", + $term: 'CAFÉ', $diacriticSensitive: false, }, }, }, }; return request({ - method: "POST", - url: "http://localhost:8378/1/classes/TestObject", - body: { where, _method: "GET" }, + method: 'POST', + url: 'http://localhost:8378/1/classes/TestObject', + body: { where, _method: 'GET' }, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, }); }) @@ -342,27 +342,27 @@ describe_only_db("postgres")( }); }); - it("fullTextSearch: $caseSensitive", done => { + it('fullTextSearch: $caseSensitive', done => { fullTextHelper() .then(() => { const where = { subject: { $text: { $search: { - $term: "Coffee", + $term: 'Coffee', $caseSensitive: true, }, }, }, }; return request({ - method: "POST", - url: "http://localhost:8378/1/classes/TestObject", - body: { where, _method: "GET" }, + method: 'POST', + url: 'http://localhost:8378/1/classes/TestObject', + body: { where, _method: 'GET' }, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, }); }) diff --git a/spec/ParseQuery.hint.spec.js b/spec/ParseQuery.hint.spec.js index d349c730e3..3137299977 100644 --- a/spec/ParseQuery.hint.spec.js +++ b/spec/ParseQuery.hint.spec.js @@ -1,16 +1,16 @@ -"use strict"; +'use strict'; -const Config = require("../lib/Config"); -const TestUtils = require("../lib/TestUtils"); -const request = require("../lib/request"); +const Config = require('../lib/Config'); +const TestUtils = require('../lib/TestUtils'); +const request = require('../lib/request'); let config; const masterKeyHeaders = { - "X-Parse-Application-Id": "test", - "X-Parse-Rest-API-Key": "rest", - "X-Parse-Master-Key": "test", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Rest-API-Key': 'rest', + 'X-Parse-Master-Key': 'test', + 'Content-Type': 'application/json', }; const masterKeyOptions = { @@ -18,199 +18,199 @@ const masterKeyOptions = { json: true, }; -describe_only_db("mongo")("Parse.Query hint", () => { +describe_only_db('mongo')('Parse.Query hint', () => { beforeEach(() => { - config = Config.get("test"); + config = Config.get('test'); }); afterEach(async () => { await TestUtils.destroyAllDataPermanently(false); }); - it_only_mongodb_version("<5.1 || >=6 <8")( - "query find with hint string", + it_only_mongodb_version('<5.1 || >=6 <8')( + 'query find with hint string', async () => { const object = new TestObject(); await object.save(); const collection = - await config.database.adapter._adaptiveCollection("TestObject"); + await config.database.adapter._adaptiveCollection('TestObject'); let explain = await collection._rawFind( { _id: object.id }, { explain: true } ); - expect(explain.queryPlanner.winningPlan.stage).toBe("IDHACK"); + expect(explain.queryPlanner.winningPlan.stage).toBe('IDHACK'); explain = await collection._rawFind( { _id: object.id }, - { hint: "_id_", explain: true } + { hint: '_id_', explain: true } ); - expect(explain.queryPlanner.winningPlan.stage).toBe("FETCH"); + expect(explain.queryPlanner.winningPlan.stage).toBe('FETCH'); expect(explain.queryPlanner.winningPlan.inputStage.indexName).toBe( - "_id_" + '_id_' ); } ); - it_only_mongodb_version(">=8")("query find with hint string", async () => { + it_only_mongodb_version('>=8')('query find with hint string', async () => { const object = new TestObject(); await object.save(); const collection = - await config.database.adapter._adaptiveCollection("TestObject"); + await config.database.adapter._adaptiveCollection('TestObject'); let explain = await collection._rawFind( { _id: object.id }, { explain: true } ); - expect(explain.queryPlanner.winningPlan.stage).toBe("EXPRESS_IXSCAN"); + expect(explain.queryPlanner.winningPlan.stage).toBe('EXPRESS_IXSCAN'); explain = await collection._rawFind( { _id: object.id }, - { hint: "_id_", explain: true } + { hint: '_id_', explain: true } ); - expect(explain.queryPlanner.winningPlan.stage).toBe("FETCH"); - expect(explain.queryPlanner.winningPlan.inputStage.indexName).toBe("_id_"); + expect(explain.queryPlanner.winningPlan.stage).toBe('FETCH'); + expect(explain.queryPlanner.winningPlan.inputStage.indexName).toBe('_id_'); }); - it_only_mongodb_version("<5.1 || >=6 <8")( - "query find with hint object", + it_only_mongodb_version('<5.1 || >=6 <8')( + 'query find with hint object', async () => { const object = new TestObject(); await object.save(); const collection = - await config.database.adapter._adaptiveCollection("TestObject"); + await config.database.adapter._adaptiveCollection('TestObject'); let explain = await collection._rawFind( { _id: object.id }, { explain: true } ); - expect(explain.queryPlanner.winningPlan.stage).toBe("IDHACK"); + expect(explain.queryPlanner.winningPlan.stage).toBe('IDHACK'); explain = await collection._rawFind( { _id: object.id }, { hint: { _id: 1 }, explain: true } ); - expect(explain.queryPlanner.winningPlan.stage).toBe("FETCH"); + expect(explain.queryPlanner.winningPlan.stage).toBe('FETCH'); expect(explain.queryPlanner.winningPlan.inputStage.keyPattern).toEqual({ _id: 1, }); } ); - it_only_mongodb_version(">=8")("query find with hint object", async () => { + it_only_mongodb_version('>=8')('query find with hint object', async () => { const object = new TestObject(); await object.save(); const collection = - await config.database.adapter._adaptiveCollection("TestObject"); + await config.database.adapter._adaptiveCollection('TestObject'); let explain = await collection._rawFind( { _id: object.id }, { explain: true } ); - expect(explain.queryPlanner.winningPlan.stage).toBe("EXPRESS_IXSCAN"); + expect(explain.queryPlanner.winningPlan.stage).toBe('EXPRESS_IXSCAN'); explain = await collection._rawFind( { _id: object.id }, { hint: { _id: 1 }, explain: true } ); - expect(explain.queryPlanner.winningPlan.stage).toBe("FETCH"); + expect(explain.queryPlanner.winningPlan.stage).toBe('FETCH'); expect(explain.queryPlanner.winningPlan.inputStage.keyPattern).toEqual({ _id: 1, }); }); - it_only_mongodb_version("<7")( - "query aggregate with hint string", + it_only_mongodb_version('<7')( + 'query aggregate with hint string', async () => { - const object = new TestObject({ foo: "bar" }); + const object = new TestObject({ foo: 'bar' }); await object.save(); const collection = - await config.database.adapter._adaptiveCollection("TestObject"); - let result = await collection.aggregate([{ $group: { _id: "$foo" } }], { + await config.database.adapter._adaptiveCollection('TestObject'); + let result = await collection.aggregate([{ $group: { _id: '$foo' } }], { explain: true, }); let queryPlanner = result[0].stages[0].$cursor.queryPlanner; - expect(queryPlanner.winningPlan.stage).toBe("PROJECTION_SIMPLE"); - expect(queryPlanner.winningPlan.inputStage.stage).toBe("COLLSCAN"); + expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); + expect(queryPlanner.winningPlan.inputStage.stage).toBe('COLLSCAN'); expect(queryPlanner.winningPlan.inputStage.inputStage).toBeUndefined(); - result = await collection.aggregate([{ $group: { _id: "$foo" } }], { - hint: "_id_", + result = await collection.aggregate([{ $group: { _id: '$foo' } }], { + hint: '_id_', explain: true, }); queryPlanner = result[0].stages[0].$cursor.queryPlanner; - expect(queryPlanner.winningPlan.stage).toBe("PROJECTION_SIMPLE"); - expect(queryPlanner.winningPlan.inputStage.stage).toBe("FETCH"); + expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); + expect(queryPlanner.winningPlan.inputStage.stage).toBe('FETCH'); expect(queryPlanner.winningPlan.inputStage.inputStage.stage).toBe( - "IXSCAN" + 'IXSCAN' ); expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe( - "_id_" + '_id_' ); } ); - it_only_mongodb_version(">=7")( - "query aggregate with hint string", + it_only_mongodb_version('>=7')( + 'query aggregate with hint string', async () => { - const object = new TestObject({ foo: "bar" }); + const object = new TestObject({ foo: 'bar' }); await object.save(); const collection = - await config.database.adapter._adaptiveCollection("TestObject"); - let result = await collection.aggregate([{ $group: { _id: "$foo" } }], { + await config.database.adapter._adaptiveCollection('TestObject'); + let result = await collection.aggregate([{ $group: { _id: '$foo' } }], { explain: true, }); let queryPlanner = result[0].queryPlanner; - expect(queryPlanner.winningPlan.queryPlan.stage).toBe("GROUP"); + expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe( - "COLLSCAN" + 'COLLSCAN' ); expect( queryPlanner.winningPlan.queryPlan.inputStage.inputStage ).toBeUndefined(); - result = await collection.aggregate([{ $group: { _id: "$foo" } }], { - hint: "_id_", + result = await collection.aggregate([{ $group: { _id: '$foo' } }], { + hint: '_id_', explain: true, }); queryPlanner = result[0].queryPlanner; - expect(queryPlanner.winningPlan.queryPlan.stage).toBe("GROUP"); - expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe("FETCH"); + expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('FETCH'); expect( queryPlanner.winningPlan.queryPlan.inputStage.inputStage.stage - ).toBe("IXSCAN"); + ).toBe('IXSCAN'); expect( queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName - ).toBe("_id_"); + ).toBe('_id_'); } ); - it_only_mongodb_version("<7")( - "query aggregate with hint object", + it_only_mongodb_version('<7')( + 'query aggregate with hint object', async () => { - const object = new TestObject({ foo: "bar" }); + const object = new TestObject({ foo: 'bar' }); await object.save(); const collection = - await config.database.adapter._adaptiveCollection("TestObject"); - let result = await collection.aggregate([{ $group: { _id: "$foo" } }], { + await config.database.adapter._adaptiveCollection('TestObject'); + let result = await collection.aggregate([{ $group: { _id: '$foo' } }], { explain: true, }); let queryPlanner = result[0].stages[0].$cursor.queryPlanner; - expect(queryPlanner.winningPlan.stage).toBe("PROJECTION_SIMPLE"); - expect(queryPlanner.winningPlan.inputStage.stage).toBe("COLLSCAN"); + expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); + expect(queryPlanner.winningPlan.inputStage.stage).toBe('COLLSCAN'); expect(queryPlanner.winningPlan.inputStage.inputStage).toBeUndefined(); - result = await collection.aggregate([{ $group: { _id: "$foo" } }], { + result = await collection.aggregate([{ $group: { _id: '$foo' } }], { hint: { _id: 1 }, explain: true, }); queryPlanner = result[0].stages[0].$cursor.queryPlanner; - expect(queryPlanner.winningPlan.stage).toBe("PROJECTION_SIMPLE"); - expect(queryPlanner.winningPlan.inputStage.stage).toBe("FETCH"); + expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); + expect(queryPlanner.winningPlan.inputStage.stage).toBe('FETCH'); expect(queryPlanner.winningPlan.inputStage.inputStage.stage).toBe( - "IXSCAN" + 'IXSCAN' ); expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe( - "_id_" + '_id_' ); expect(queryPlanner.winningPlan.inputStage.inputStage.keyPattern).toEqual( { _id: 1 } @@ -218,52 +218,52 @@ describe_only_db("mongo")("Parse.Query hint", () => { } ); - it_only_mongodb_version(">=7")( - "query aggregate with hint object", + it_only_mongodb_version('>=7')( + 'query aggregate with hint object', async () => { - const object = new TestObject({ foo: "bar" }); + const object = new TestObject({ foo: 'bar' }); await object.save(); const collection = - await config.database.adapter._adaptiveCollection("TestObject"); - let result = await collection.aggregate([{ $group: { _id: "$foo" } }], { + await config.database.adapter._adaptiveCollection('TestObject'); + let result = await collection.aggregate([{ $group: { _id: '$foo' } }], { explain: true, }); let queryPlanner = result[0].queryPlanner; - expect(queryPlanner.winningPlan.queryPlan.stage).toBe("GROUP"); + expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe( - "COLLSCAN" + 'COLLSCAN' ); expect( queryPlanner.winningPlan.queryPlan.inputStage.inputStage ).toBeUndefined(); - result = await collection.aggregate([{ $group: { _id: "$foo" } }], { + result = await collection.aggregate([{ $group: { _id: '$foo' } }], { hint: { _id: 1 }, explain: true, }); queryPlanner = result[0].queryPlanner; - expect(queryPlanner.winningPlan.queryPlan.stage).toBe("GROUP"); - expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe("FETCH"); + expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('FETCH'); expect( queryPlanner.winningPlan.queryPlan.inputStage.inputStage.stage - ).toBe("IXSCAN"); + ).toBe('IXSCAN'); expect( queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName - ).toBe("_id_"); + ).toBe('_id_'); expect( queryPlanner.winningPlan.queryPlan.inputStage.inputStage.keyPattern ).toEqual({ _id: 1 }); } ); - it_only_mongodb_version("<5.1 || >=6")( - "query find with hint (rest)", + it_only_mongodb_version('<5.1 || >=6')( + 'query find with hint (rest)', async () => { const object = new TestObject(); await object.save(); let options = Object.assign({}, masterKeyOptions, { - url: Parse.serverURL + "/classes/TestObject", + url: Parse.serverURL + '/classes/TestObject', qs: { explain: true, }, @@ -271,60 +271,60 @@ describe_only_db("mongo")("Parse.Query hint", () => { let response = await request(options); let explain = response.data.results; expect(explain.queryPlanner.winningPlan.inputStage.stage).toBe( - "COLLSCAN" + 'COLLSCAN' ); options = Object.assign({}, masterKeyOptions, { - url: Parse.serverURL + "/classes/TestObject", + url: Parse.serverURL + '/classes/TestObject', qs: { explain: true, - hint: "_id_", + hint: '_id_', }, }); response = await request(options); explain = response.data.results; expect( explain.queryPlanner.winningPlan.inputStage.inputStage.indexName - ).toBe("_id_"); + ).toBe('_id_'); } ); - it_only_mongodb_version("<7")( - "query aggregate with hint (rest)", + it_only_mongodb_version('<7')( + 'query aggregate with hint (rest)', async () => { - const object = new TestObject({ foo: "bar" }); + const object = new TestObject({ foo: 'bar' }); await object.save(); let options = Object.assign({}, masterKeyOptions, { - url: Parse.serverURL + "/aggregate/TestObject", + url: Parse.serverURL + '/aggregate/TestObject', qs: { explain: true, - $group: JSON.stringify({ _id: "$foo" }), + $group: JSON.stringify({ _id: '$foo' }), }, }); let response = await request(options); let queryPlanner = response.data.results[0].stages[0].$cursor.queryPlanner; - expect(queryPlanner.winningPlan.stage).toBe("PROJECTION_SIMPLE"); - expect(queryPlanner.winningPlan.inputStage.stage).toBe("COLLSCAN"); + expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); + expect(queryPlanner.winningPlan.inputStage.stage).toBe('COLLSCAN'); expect(queryPlanner.winningPlan.inputStage.inputStage).toBeUndefined(); options = Object.assign({}, masterKeyOptions, { - url: Parse.serverURL + "/aggregate/TestObject", + url: Parse.serverURL + '/aggregate/TestObject', qs: { explain: true, - hint: "_id_", - $group: JSON.stringify({ _id: "$foo" }), + hint: '_id_', + $group: JSON.stringify({ _id: '$foo' }), }, }); response = await request(options); queryPlanner = response.data.results[0].stages[0].$cursor.queryPlanner; - expect(queryPlanner.winningPlan.stage).toBe("PROJECTION_SIMPLE"); - expect(queryPlanner.winningPlan.inputStage.stage).toBe("FETCH"); + expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); + expect(queryPlanner.winningPlan.inputStage.stage).toBe('FETCH'); expect(queryPlanner.winningPlan.inputStage.inputStage.stage).toBe( - "IXSCAN" + 'IXSCAN' ); expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe( - "_id_" + '_id_' ); expect(queryPlanner.winningPlan.inputStage.inputStage.keyPattern).toEqual( { _id: 1 } @@ -332,46 +332,46 @@ describe_only_db("mongo")("Parse.Query hint", () => { } ); - it_only_mongodb_version(">=7")( - "query aggregate with hint (rest)", + it_only_mongodb_version('>=7')( + 'query aggregate with hint (rest)', async () => { - const object = new TestObject({ foo: "bar" }); + const object = new TestObject({ foo: 'bar' }); await object.save(); let options = Object.assign({}, masterKeyOptions, { - url: Parse.serverURL + "/aggregate/TestObject", + url: Parse.serverURL + '/aggregate/TestObject', qs: { explain: true, - $group: JSON.stringify({ _id: "$foo" }), + $group: JSON.stringify({ _id: '$foo' }), }, }); let response = await request(options); let queryPlanner = response.data.results[0].queryPlanner; - expect(queryPlanner.winningPlan.queryPlan.stage).toBe("GROUP"); + expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe( - "COLLSCAN" + 'COLLSCAN' ); expect( queryPlanner.winningPlan.queryPlan.inputStage.inputStage ).toBeUndefined(); options = Object.assign({}, masterKeyOptions, { - url: Parse.serverURL + "/aggregate/TestObject", + url: Parse.serverURL + '/aggregate/TestObject', qs: { explain: true, - hint: "_id_", - $group: JSON.stringify({ _id: "$foo" }), + hint: '_id_', + $group: JSON.stringify({ _id: '$foo' }), }, }); response = await request(options); queryPlanner = response.data.results[0].queryPlanner; - expect(queryPlanner.winningPlan.queryPlan.stage).toBe("GROUP"); - expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe("FETCH"); + expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('FETCH'); expect( queryPlanner.winningPlan.queryPlan.inputStage.inputStage.stage - ).toBe("IXSCAN"); + ).toBe('IXSCAN'); expect( queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName - ).toBe("_id_"); + ).toBe('_id_'); expect( queryPlanner.winningPlan.queryPlan.inputStage.inputStage.keyPattern ).toEqual({ _id: 1 }); diff --git a/spec/ParseQuery.spec.js b/spec/ParseQuery.spec.js index 0799718ae8..da1d50e026 100644 --- a/spec/ParseQuery.spec.js +++ b/spec/ParseQuery.spec.js @@ -2,19 +2,19 @@ // hungry/js/test/parse_query_test.js // // Some new tests are added. -"use strict"; +'use strict'; -const Parse = require("parse/node"); -const request = require("../lib/request"); +const Parse = require('parse/node'); +const request = require('../lib/request'); const ParseServerRESTController = - require("../lib/ParseServerRESTController").ParseServerRESTController; -const ParseServer = require("../lib/ParseServer").default; + require('../lib/ParseServerRESTController').ParseServerRESTController; +const ParseServer = require('../lib/ParseServer').default; const masterKeyHeaders = { - "X-Parse-Application-Id": "test", - "X-Parse-Rest-API-Key": "test", - "X-Parse-Master-Key": "test", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Rest-API-Key': 'test', + 'X-Parse-Master-Key': 'test', + 'Content-Type': 'application/json', }; const masterKeyOptions = { @@ -22,61 +22,61 @@ const masterKeyOptions = { }; const BoxedNumber = Parse.Object.extend({ - className: "BoxedNumber", + className: 'BoxedNumber', }); -describe("Parse.Query testing", () => { - it("basic query", function (done) { - const baz = new TestObject({ foo: "baz" }); - const qux = new TestObject({ foo: "qux" }); +describe('Parse.Query testing', () => { + it('basic query', function (done) { + const baz = new TestObject({ foo: 'baz' }); + const qux = new TestObject({ foo: 'qux' }); Parse.Object.saveAll([baz, qux]).then(function () { const query = new Parse.Query(TestObject); - query.equalTo("foo", "baz"); + query.equalTo('foo', 'baz'); query.find().then(function (results) { equal(results.length, 1); - equal(results[0].get("foo"), "baz"); + equal(results[0].get('foo'), 'baz'); done(); }); }); }); - it_only_db("mongo")("gracefully handles invalid explain values", async () => { + it_only_db('mongo')('gracefully handles invalid explain values', async () => { // Note that anything that is not truthy (like 0) does not cause an exception, as they get swallowed up by ClassesRouter::optionsFromBody - const values = [1, "yolo", { a: 1 }, [1, 2, 3]]; + const values = [1, 'yolo', { a: 1 }, [1, 2, 3]]; for (const value of values) { try { await request({ - method: "GET", + method: 'GET', url: `http://localhost:8378/1/classes/_User?explain=${value}`, json: true, headers: masterKeyHeaders, }); - fail("request did not throw"); + fail('request did not throw'); } catch (e) { // Expect that Parse Server did not crash - expect(e.code).not.toEqual("ECONNRESET"); + expect(e.code).not.toEqual('ECONNRESET'); // Expect that Parse Server validates the explain value and does not crash; // see https://jira.mongodb.org/browse/NODE-3463 equal(e.data.code, Parse.Error.INVALID_QUERY); - equal(e.data.error, "Invalid value for explain"); + equal(e.data.error, 'Invalid value for explain'); } // get queries (of the form '/classes/:className/:objectId' cannot have the explain key, see ClassesRouter.js) // so it is enough that we test find queries } }); - it_only_db("mongo")("supports valid explain values", async () => { + it_only_db('mongo')('supports valid explain values', async () => { const values = [ false, true, - "queryPlanner", - "executionStats", - "allPlansExecution", + 'queryPlanner', + 'executionStats', + 'allPlansExecution', // 'queryPlannerExtended' is excluded as it only applies to MongoDB Data Lake which is currently not available in our CI environment ]; for (const value of values) { const response = await request({ - method: "GET", + method: 'GET', url: `http://localhost:8378/1/classes/_User?explain=${value}`, json: true, headers: masterKeyHeaders, @@ -88,16 +88,16 @@ describe("Parse.Query testing", () => { } }); - it("searching for null", function (done) { + it('searching for null', function (done) { const baz = new TestObject({ foo: null }); - const qux = new TestObject({ foo: "qux" }); + const qux = new TestObject({ foo: 'qux' }); const qux2 = new TestObject({}); Parse.Object.saveAll([baz, qux, qux2]).then(function () { const query = new Parse.Query(TestObject); - query.equalTo("foo", null); + query.equalTo('foo', null); query.find().then(function (results) { equal(results.length, 2); - qux.set("foo", null); + qux.set('foo', null); qux.save().then(function () { query.find().then(function (results) { equal(results.length, 3); @@ -108,16 +108,16 @@ describe("Parse.Query testing", () => { }); }); - it("searching for not null", function (done) { + it('searching for not null', function (done) { const baz = new TestObject({ foo: null }); - const qux = new TestObject({ foo: "qux" }); + const qux = new TestObject({ foo: 'qux' }); const qux2 = new TestObject({}); Parse.Object.saveAll([baz, qux, qux2]).then(function () { const query = new Parse.Query(TestObject); - query.notEqualTo("foo", null); + query.notEqualTo('foo', null); query.find().then(function (results) { equal(results.length, 1); - qux.set("foo", null); + qux.set('foo', null); qux.save().then(function () { query.find().then(function (results) { equal(results.length, 0); @@ -128,20 +128,20 @@ describe("Parse.Query testing", () => { }); }); - it("notEqualTo with Relation is working", function (done) { + it('notEqualTo with Relation is working', function (done) { const user = new Parse.User(); - user.setPassword("asdf"); - user.setUsername("zxcv"); + user.setPassword('asdf'); + user.setUsername('zxcv'); const user1 = new Parse.User(); - user1.setPassword("asdf"); - user1.setUsername("qwerty"); + user1.setPassword('asdf'); + user1.setUsername('qwerty'); const user2 = new Parse.User(); - user2.setPassword("asdf"); - user2.setUsername("asdf"); + user2.setPassword('asdf'); + user2.setUsername('asdf'); - const Cake = Parse.Object.extend("Cake"); + const Cake = Parse.Object.extend('Cake'); const cake1 = new Cake(); const cake2 = new Cake(); const cake3 = new Cake(); @@ -155,38 +155,38 @@ describe("Parse.Query testing", () => { return user2.signUp(); }) .then(function () { - const relLike1 = cake1.relation("liker"); + const relLike1 = cake1.relation('liker'); relLike1.add([user, user1]); - const relDislike1 = cake1.relation("hater"); + const relDislike1 = cake1.relation('hater'); relDislike1.add(user2); return cake1.save(); }) .then(function () { - const rellike2 = cake2.relation("liker"); + const rellike2 = cake2.relation('liker'); rellike2.add([user, user1]); - const relDislike2 = cake2.relation("hater"); + const relDislike2 = cake2.relation('hater'); relDislike2.add(user2); - const relSomething = cake2.relation("something"); + const relSomething = cake2.relation('something'); relSomething.add(user); return cake2.save(); }) .then(function () { - const rellike3 = cake3.relation("liker"); + const rellike3 = cake3.relation('liker'); rellike3.add(user); - const relDislike3 = cake3.relation("hater"); + const relDislike3 = cake3.relation('hater'); relDislike3.add([user1, user2]); return cake3.save(); }) .then(function () { const query = new Parse.Query(Cake); // User2 likes nothing so we should receive 0 - query.equalTo("liker", user2); + query.equalTo('liker', user2); return query.find().then(function (results) { equal(results.length, 0); }); @@ -194,7 +194,7 @@ describe("Parse.Query testing", () => { .then(function () { const query = new Parse.Query(Cake); // User1 likes two of three cakes - query.equalTo("liker", user1); + query.equalTo('liker', user1); return query.find().then(function (results) { // It should return 2 -> cake 1 and cake 2 equal(results.length, 2); @@ -203,7 +203,7 @@ describe("Parse.Query testing", () => { .then(function () { const query = new Parse.Query(Cake); // We want to know which cake the user1 is not appreciating -> cake3 - query.notEqualTo("liker", user1); + query.notEqualTo('liker', user1); return query.find().then(function (results) { // Should return 1 -> the cake 3 equal(results.length, 1); @@ -212,7 +212,7 @@ describe("Parse.Query testing", () => { .then(function () { const query = new Parse.Query(Cake); // User2 is a hater of everything so we should receive 0 - query.notEqualTo("hater", user2); + query.notEqualTo('hater', user2); return query.find().then(function (results) { equal(results.length, 0); }); @@ -220,7 +220,7 @@ describe("Parse.Query testing", () => { .then(function () { const query = new Parse.Query(Cake); // Only cake3 is liked by user - query.notContainedIn("liker", [user1]); + query.notContainedIn('liker', [user1]); return query.find().then(function (results) { equal(results.length, 1); }); @@ -228,9 +228,9 @@ describe("Parse.Query testing", () => { .then(function () { const query = new Parse.Query(Cake); // All the users - query.containedIn("liker", [user, user1, user2]); + query.containedIn('liker', [user, user1, user2]); // Exclude user 1 - query.notEqualTo("liker", user1); + query.notEqualTo('liker', user1); // Only cake3 is liked only by user1 return query.find().then(function (results) { equal(results.length, 1); @@ -241,9 +241,9 @@ describe("Parse.Query testing", () => { .then(function () { const query = new Parse.Query(Cake); // Exclude user1 - query.notEqualTo("liker", user1); + query.notEqualTo('liker', user1); // Only cake1 - query.equalTo("objectId", cake1.id); + query.equalTo('objectId', cake1.id); // user1 likes cake1 so this should return no results return query.find().then(function (results) { equal(results.length, 0); @@ -251,8 +251,8 @@ describe("Parse.Query testing", () => { }) .then(function () { const query = new Parse.Query(Cake); - query.notEqualTo("hater", user2); - query.notEqualTo("liker", user2); + query.notEqualTo('hater', user2); + query.notEqualTo('liker', user2); // user2 doesn't like any cake so this should be 0 return query.find().then(function (results) { equal(results.length, 0); @@ -260,8 +260,8 @@ describe("Parse.Query testing", () => { }) .then(function () { const query = new Parse.Query(Cake); - query.equalTo("hater", user); - query.equalTo("liker", user); + query.equalTo('hater', user); + query.equalTo('liker', user); // user doesn't hate any cake so this should be 0 return query.find().then(function (results) { equal(results.length, 0); @@ -269,8 +269,8 @@ describe("Parse.Query testing", () => { }) .then(function () { const query = new Parse.Query(Cake); - query.equalTo("hater", null); - query.equalTo("liker", null); + query.equalTo('hater', null); + query.equalTo('liker', null); // user doesn't hate any cake so this should be 0 return query.find().then(function (results) { equal(results.length, 0); @@ -278,7 +278,7 @@ describe("Parse.Query testing", () => { }) .then(function () { const query = new Parse.Query(Cake); - query.equalTo("something", null); + query.equalTo('something', null); // user doesn't hate any cake so this should be 0 return query.find().then(function (results) { equal(results.length, 0); @@ -293,43 +293,43 @@ describe("Parse.Query testing", () => { }); }); - it("query notContainedIn on empty array", async () => { + it('query notContainedIn on empty array', async () => { const object = new TestObject(); - object.set("value", 100); + object.set('value', 100); await object.save(); const query = new Parse.Query(TestObject); - query.notContainedIn("value", []); + query.notContainedIn('value', []); const results = await query.find(); equal(results.length, 1); }); - it("query containedIn on empty array", async () => { + it('query containedIn on empty array', async () => { const object = new TestObject(); - object.set("value", 100); + object.set('value', 100); await object.save(); const query = new Parse.Query(TestObject); - query.containedIn("value", []); + query.containedIn('value', []); const results = await query.find(); equal(results.length, 0); }); - it("query without limit respects default limit", async () => { + it('query without limit respects default limit', async () => { await reconfigureServer({ defaultLimit: 1 }); - const obj1 = new TestObject({ foo: "baz" }); - const obj2 = new TestObject({ foo: "qux" }); + const obj1 = new TestObject({ foo: 'baz' }); + const obj2 = new TestObject({ foo: 'qux' }); await Parse.Object.saveAll([obj1, obj2]); const query = new Parse.Query(TestObject); const result = await query.find(); expect(result.length).toBe(1); }); - it("query with limit", async () => { - const obj1 = new TestObject({ foo: "baz" }); - const obj2 = new TestObject({ foo: "qux" }); + it('query with limit', async () => { + const obj1 = new TestObject({ foo: 'baz' }); + const obj2 = new TestObject({ foo: 'qux' }); await Parse.Object.saveAll([obj1, obj2]); const query = new Parse.Query(TestObject); query.limit(1); @@ -337,10 +337,10 @@ describe("Parse.Query testing", () => { expect(result.length).toBe(1); }); - it("query with limit overrides default limit", async () => { + it('query with limit overrides default limit', async () => { await reconfigureServer({ defaultLimit: 2 }); - const obj1 = new TestObject({ foo: "baz" }); - const obj2 = new TestObject({ foo: "qux" }); + const obj1 = new TestObject({ foo: 'baz' }); + const obj2 = new TestObject({ foo: 'qux' }); await Parse.Object.saveAll([obj1, obj2]); const query = new Parse.Query(TestObject); query.limit(1); @@ -348,10 +348,10 @@ describe("Parse.Query testing", () => { expect(result.length).toBe(1); }); - it("query with limit equal to maxlimit", async () => { + it('query with limit equal to maxlimit', async () => { await reconfigureServer({ maxLimit: 1 }); - const obj1 = new TestObject({ foo: "baz" }); - const obj2 = new TestObject({ foo: "qux" }); + const obj1 = new TestObject({ foo: 'baz' }); + const obj2 = new TestObject({ foo: 'qux' }); await Parse.Object.saveAll([obj1, obj2]); const query = new Parse.Query(TestObject); query.limit(1); @@ -359,10 +359,10 @@ describe("Parse.Query testing", () => { expect(result.length).toBe(1); }); - it("query with limit exceeding maxlimit", async () => { + it('query with limit exceeding maxlimit', async () => { await reconfigureServer({ maxLimit: 1 }); - const obj1 = new TestObject({ foo: "baz" }); - const obj2 = new TestObject({ foo: "qux" }); + const obj1 = new TestObject({ foo: 'baz' }); + const obj2 = new TestObject({ foo: 'qux' }); await Parse.Object.saveAll([obj1, obj2]); const query = new Parse.Query(TestObject); query.limit(2); @@ -370,12 +370,12 @@ describe("Parse.Query testing", () => { expect(result.length).toBe(1); }); - it("containedIn object array queries", function (done) { + it('containedIn object array queries', function (done) { const messageList = []; for (let i = 0; i < 4; ++i) { const message = new TestObject({}); if (i > 0) { - message.set("prior", messageList[i - 1]); + message.set('prior', messageList[i - 1]); } messageList.push(message); } @@ -389,7 +389,7 @@ describe("Parse.Query testing", () => { inList.push(messageList[2]); const query = new Parse.Query(TestObject); - query.containedIn("prior", inList); + query.containedIn('prior', inList); query.find().then( function (results) { equal(results.length, 2); @@ -408,16 +408,16 @@ describe("Parse.Query testing", () => { ); }); - it("containedIn null array", done => { - const emails = ["contact@xyz.com", "contact@zyx.com", null]; + it('containedIn null array', done => { + const emails = ['contact@xyz.com', 'contact@zyx.com', null]; const user = new Parse.User(); user.setUsername(emails[0]); - user.setPassword("asdf"); + user.setPassword('asdf'); user .signUp() .then(() => { const query = new Parse.Query(Parse.User); - query.containedIn("username", emails); + query.containedIn('username', emails); return query.find({ useMasterKey: true }); }) .then(results => { @@ -426,35 +426,35 @@ describe("Parse.Query testing", () => { }, done.fail); }); - it("nested equalTo string with single quote", async () => { + it('nested equalTo string with single quote', async () => { const obj = new TestObject({ nested: { foo: "single'quote" } }); await obj.save(); const query = new Parse.Query(TestObject); - query.equalTo("nested.foo", "single'quote"); + query.equalTo('nested.foo', "single'quote"); const result = await query.get(obj.id); - equal(result.get("nested").foo, "single'quote"); + equal(result.get('nested').foo, "single'quote"); }); - it("nested containedIn string with single quote", async () => { + it('nested containedIn string with single quote', async () => { const obj = new TestObject({ nested: { foo: ["single'quote"] } }); await obj.save(); const query = new Parse.Query(TestObject); - query.containedIn("nested.foo", ["single'quote"]); + query.containedIn('nested.foo', ["single'quote"]); const result = await query.get(obj.id); - equal(result.get("nested").foo[0], "single'quote"); + equal(result.get('nested').foo[0], "single'quote"); }); - it("nested containedIn string", done => { - const sender1 = { group: ["A", "B"] }; - const sender2 = { group: ["A", "C"] }; - const sender3 = { group: ["B", "C"] }; + it('nested containedIn string', done => { + const sender1 = { group: ['A', 'B'] }; + const sender2 = { group: ['A', 'C'] }; + const sender3 = { group: ['B', 'C'] }; const obj1 = new TestObject({ sender: sender1 }); const obj2 = new TestObject({ sender: sender2 }); const obj3 = new TestObject({ sender: sender3 }); Parse.Object.saveAll([obj1, obj2, obj3]) .then(() => { const query = new Parse.Query(TestObject); - query.containedIn("sender.group", ["A"]); + query.containedIn('sender.group', ['A']); return query.find(); }) .then(results => { @@ -463,7 +463,7 @@ describe("Parse.Query testing", () => { }, done.fail); }); - it("nested containedIn number", done => { + it('nested containedIn number', done => { const sender1 = { group: [1, 2] }; const sender2 = { group: [1, 3] }; const sender3 = { group: [2, 3] }; @@ -473,7 +473,7 @@ describe("Parse.Query testing", () => { Parse.Object.saveAll([obj1, obj2, obj3]) .then(() => { const query = new Parse.Query(TestObject); - query.containedIn("sender.group", [1]); + query.containedIn('sender.group', [1]); return query.find(); }) .then(results => { @@ -482,8 +482,8 @@ describe("Parse.Query testing", () => { }, done.fail); }); - it("containsAll number array queries", function (done) { - const NumberSet = Parse.Object.extend({ className: "NumberSet" }); + it('containsAll number array queries', function (done) { + const NumberSet = Parse.Object.extend({ className: 'NumberSet' }); const objectsList = []; objectsList.push(new NumberSet({ numbers: [1, 2, 3, 4, 5] })); @@ -492,7 +492,7 @@ describe("Parse.Query testing", () => { Parse.Object.saveAll(objectsList) .then(function () { const query = new Parse.Query(NumberSet); - query.containsAll("numbers", [1, 2, 3]); + query.containsAll('numbers', [1, 2, 3]); query.find().then( function (results) { equal(results.length, 1); @@ -510,17 +510,17 @@ describe("Parse.Query testing", () => { }); }); - it("containsAll string array queries", function (done) { - const StringSet = Parse.Object.extend({ className: "StringSet" }); + it('containsAll string array queries', function (done) { + const StringSet = Parse.Object.extend({ className: 'StringSet' }); const objectsList = []; - objectsList.push(new StringSet({ strings: ["a", "b", "c", "d", "e"] })); - objectsList.push(new StringSet({ strings: ["a", "c", "d", "e"] })); + objectsList.push(new StringSet({ strings: ['a', 'b', 'c', 'd', 'e'] })); + objectsList.push(new StringSet({ strings: ['a', 'c', 'd', 'e'] })); Parse.Object.saveAll(objectsList) .then(function () { const query = new Parse.Query(StringSet); - query.containsAll("strings", ["a", "b", "c"]); + query.containsAll('strings', ['a', 'b', 'c']); query.find().then(function (results) { equal(results.length, 1); done(); @@ -532,16 +532,16 @@ describe("Parse.Query testing", () => { }); }); - it("containsAll date array queries", function (done) { - const DateSet = Parse.Object.extend({ className: "DateSet" }); + it('containsAll date array queries', function (done) { + const DateSet = Parse.Object.extend({ className: 'DateSet' }); function parseDate(iso8601) { const regexp = new RegExp( - "^([0-9]{1,4})-([0-9]{1,2})-([0-9]{1,2})" + - "T" + - "([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2})" + - "(.([0-9]+))?" + - "Z$" + '^([0-9]{1,4})-([0-9]{1,2})-([0-9]{1,2})' + + 'T' + + '([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2})' + + '(.([0-9]+))?' + + 'Z$' ); const match = regexp.exec(iso8601); if (!match) { @@ -561,7 +561,7 @@ describe("Parse.Query testing", () => { const makeDates = function (stringArray) { return stringArray.map(function (dateStr) { - return parseDate(dateStr + "T00:00:00Z"); + return parseDate(dateStr + 'T00:00:00Z'); }); }; @@ -569,24 +569,24 @@ describe("Parse.Query testing", () => { objectsList.push( new DateSet({ dates: makeDates([ - "2013-02-01", - "2013-02-02", - "2013-02-03", - "2013-02-04", + '2013-02-01', + '2013-02-02', + '2013-02-03', + '2013-02-04', ]), }) ); objectsList.push( new DateSet({ - dates: makeDates(["2013-02-01", "2013-02-03", "2013-02-04"]), + dates: makeDates(['2013-02-01', '2013-02-03', '2013-02-04']), }) ); Parse.Object.saveAll(objectsList).then(function () { const query = new Parse.Query(DateSet); query.containsAll( - "dates", - makeDates(["2013-02-01", "2013-02-02", "2013-02-03"]) + 'dates', + makeDates(['2013-02-01', '2013-02-02', '2013-02-03']) ); query.find().then( function (results) { @@ -601,10 +601,10 @@ describe("Parse.Query testing", () => { }); }); - it_id("25bb35a6-e953-4d6d-a31c-66324d5ae076")(it)( - "containsAll object array queries", + it_id('25bb35a6-e953-4d6d-a31c-66324d5ae076')(it)( + 'containsAll object array queries', function (done) { - const MessageSet = Parse.Object.extend({ className: "MessageSet" }); + const MessageSet = Parse.Object.extend({ className: 'MessageSet' }); const messageList = []; for (let i = 0; i < 4; ++i) { @@ -629,7 +629,7 @@ describe("Parse.Query testing", () => { inList.push(messageList[2]); const query = new Parse.Query(MessageSet); - query.containsAll("messages", inList); + query.containsAll('messages', inList); query.find().then(function (results) { equal(results.length, 1); done(); @@ -639,13 +639,13 @@ describe("Parse.Query testing", () => { } ); - it("containsAllStartingWith should match all strings that starts with string", done => { - const object = new Parse.Object("Object"); - object.set("strings", ["the", "brown", "lazy", "fox", "jumps"]); - const object2 = new Parse.Object("Object"); - object2.set("strings", ["the", "brown", "fox", "jumps"]); - const object3 = new Parse.Object("Object"); - object3.set("strings", ["over", "the", "lazy", "dog"]); + it('containsAllStartingWith should match all strings that starts with string', done => { + const object = new Parse.Object('Object'); + object.set('strings', ['the', 'brown', 'lazy', 'fox', 'jumps']); + const object2 = new Parse.Object('Object'); + object2.set('strings', ['the', 'brown', 'fox', 'jumps']); + const object3 = new Parse.Object('Object'); + object3.set('strings', ['over', 'the', 'lazy', 'dog']); const objectList = [object, object2, object3]; @@ -653,22 +653,22 @@ describe("Parse.Query testing", () => { equal(objectList.length, results.length); return request({ - url: Parse.serverURL + "/classes/Object", + url: Parse.serverURL + '/classes/Object', qs: { where: JSON.stringify({ strings: { $all: [ - { $regex: "^\\Qthe\\E" }, - { $regex: "^\\Qfox\\E" }, - { $regex: "^\\Qlazy\\E" }, + { $regex: '^\\Qthe\\E' }, + { $regex: '^\\Qfox\\E' }, + { $regex: '^\\Qlazy\\E' }, ], }, }), }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Javascript-Key": Parse.javaScriptKey, - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, + 'Content-Type': 'application/json', }, }) .then(function (response) { @@ -677,18 +677,18 @@ describe("Parse.Query testing", () => { arrayContains(results.results, object); return request({ - url: Parse.serverURL + "/classes/Object", + url: Parse.serverURL + '/classes/Object', qs: { where: JSON.stringify({ strings: { - $all: [{ $regex: "^\\Qthe\\E" }, { $regex: "^\\Qlazy\\E" }], + $all: [{ $regex: '^\\Qthe\\E' }, { $regex: '^\\Qlazy\\E' }], }, }), }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Javascript-Key": Parse.javaScriptKey, - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, + 'Content-Type': 'application/json', }, }); }) @@ -699,18 +699,18 @@ describe("Parse.Query testing", () => { arrayContains(results.results, object3); return request({ - url: Parse.serverURL + "/classes/Object", + url: Parse.serverURL + '/classes/Object', qs: { where: JSON.stringify({ strings: { - $all: [{ $regex: "^\\Qhe\\E" }, { $regex: "^\\Qlazy\\E" }], + $all: [{ $regex: '^\\Qhe\\E' }, { $regex: '^\\Qlazy\\E' }], }, }), }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Javascript-Key": Parse.javaScriptKey, - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, + 'Content-Type': 'application/json', }, }); }) @@ -723,11 +723,11 @@ describe("Parse.Query testing", () => { }); }); - it_id("3ea6ae04-bcc2-453d-8817-4c64d059c2f6")(it)( - "containsAllStartingWith values must be all of type starting with regex", + it_id('3ea6ae04-bcc2-453d-8817-4c64d059c2f6')(it)( + 'containsAllStartingWith values must be all of type starting with regex', done => { - const object = new Parse.Object("Object"); - object.set("strings", ["the", "brown", "lazy", "fox", "jumps"]); + const object = new Parse.Object('Object'); + object.set('strings', ['the', 'brown', 'lazy', 'fox', 'jumps']); object .save() @@ -735,23 +735,23 @@ describe("Parse.Query testing", () => { equal(object.isNew(), false); return request({ - url: Parse.serverURL + "/classes/Object", + url: Parse.serverURL + '/classes/Object', qs: { where: JSON.stringify({ strings: { $all: [ - { $regex: "^\\Qthe\\E" }, - { $regex: "^\\Qlazy\\E" }, - { $regex: "^\\Qfox\\E" }, + { $regex: '^\\Qthe\\E' }, + { $regex: '^\\Qlazy\\E' }, + { $regex: '^\\Qfox\\E' }, { $unknown: /unknown/ }, ], }, }), }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Javascript-Key": Parse.javaScriptKey, - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, + 'Content-Type': 'application/json', }, }); }) @@ -761,9 +761,9 @@ describe("Parse.Query testing", () => { } ); - it("containsAllStartingWith empty array values should return empty results", done => { - const object = new Parse.Object("Object"); - object.set("strings", ["the", "brown", "lazy", "fox", "jumps"]); + it('containsAllStartingWith empty array values should return empty results', done => { + const object = new Parse.Object('Object'); + object.set('strings', ['the', 'brown', 'lazy', 'fox', 'jumps']); object .save() @@ -771,7 +771,7 @@ describe("Parse.Query testing", () => { equal(object.isNew(), false); return request({ - url: Parse.serverURL + "/classes/Object", + url: Parse.serverURL + '/classes/Object', qs: { where: JSON.stringify({ strings: { @@ -780,9 +780,9 @@ describe("Parse.Query testing", () => { }), }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Javascript-Key": Parse.javaScriptKey, - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, + 'Content-Type': 'application/json', }, }); }) @@ -796,9 +796,9 @@ describe("Parse.Query testing", () => { ); }); - it("containsAllStartingWith single empty value returns empty results", done => { - const object = new Parse.Object("Object"); - object.set("strings", ["the", "brown", "lazy", "fox", "jumps"]); + it('containsAllStartingWith single empty value returns empty results', done => { + const object = new Parse.Object('Object'); + object.set('strings', ['the', 'brown', 'lazy', 'fox', 'jumps']); object .save() @@ -806,7 +806,7 @@ describe("Parse.Query testing", () => { equal(object.isNew(), false); return request({ - url: Parse.serverURL + "/classes/Object", + url: Parse.serverURL + '/classes/Object', qs: { where: JSON.stringify({ strings: { @@ -815,9 +815,9 @@ describe("Parse.Query testing", () => { }), }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Javascript-Key": Parse.javaScriptKey, - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, + 'Content-Type': 'application/json', }, }); }) @@ -831,13 +831,13 @@ describe("Parse.Query testing", () => { ); }); - it("containsAllStartingWith single regex value should return corresponding matching results", done => { - const object = new Parse.Object("Object"); - object.set("strings", ["the", "brown", "lazy", "fox", "jumps"]); - const object2 = new Parse.Object("Object"); - object2.set("strings", ["the", "brown", "fox", "jumps"]); - const object3 = new Parse.Object("Object"); - object3.set("strings", ["over", "the", "lazy", "dog"]); + it('containsAllStartingWith single regex value should return corresponding matching results', done => { + const object = new Parse.Object('Object'); + object.set('strings', ['the', 'brown', 'lazy', 'fox', 'jumps']); + const object2 = new Parse.Object('Object'); + object2.set('strings', ['the', 'brown', 'fox', 'jumps']); + const object3 = new Parse.Object('Object'); + object3.set('strings', ['over', 'the', 'lazy', 'dog']); const objectList = [object, object2, object3]; @@ -846,18 +846,18 @@ describe("Parse.Query testing", () => { equal(objectList.length, results.length); return request({ - url: Parse.serverURL + "/classes/Object", + url: Parse.serverURL + '/classes/Object', qs: { where: JSON.stringify({ strings: { - $all: [{ $regex: "^\\Qlazy\\E" }], + $all: [{ $regex: '^\\Qlazy\\E' }], }, }), }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Javascript-Key": Parse.javaScriptKey, - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, + 'Content-Type': 'application/json', }, }); }) @@ -871,9 +871,9 @@ describe("Parse.Query testing", () => { ); }); - it("containsAllStartingWith single invalid regex returns empty results", done => { - const object = new Parse.Object("Object"); - object.set("strings", ["the", "brown", "lazy", "fox", "jumps"]); + it('containsAllStartingWith single invalid regex returns empty results', done => { + const object = new Parse.Object('Object'); + object.set('strings', ['the', 'brown', 'lazy', 'fox', 'jumps']); object .save() @@ -881,17 +881,17 @@ describe("Parse.Query testing", () => { equal(object.isNew(), false); return request({ - url: Parse.serverURL + "/classes/Object", + url: Parse.serverURL + '/classes/Object', qs: { where: JSON.stringify({ strings: { - $all: [{ $unknown: "^\\Qlazy\\E" }], + $all: [{ $unknown: '^\\Qlazy\\E' }], }, }), }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Javascript-Key": Parse.javaScriptKey, + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, }, }); }) @@ -905,30 +905,30 @@ describe("Parse.Query testing", () => { ); }); - it_id("01a15195-dde2-4368-b996-d746a4ede3a1")(it)( - "containedBy pointer array", + it_id('01a15195-dde2-4368-b996-d746a4ede3a1')(it)( + 'containedBy pointer array', done => { const objects = Array.from(Array(10).keys()).map(idx => { - const obj = new Parse.Object("Object"); - obj.set("key", idx); + const obj = new Parse.Object('Object'); + obj.set('key', idx); return obj; }); - const parent = new Parse.Object("Parent"); - const parent2 = new Parse.Object("Parent"); - const parent3 = new Parse.Object("Parent"); + const parent = new Parse.Object('Parent'); + const parent2 = new Parse.Object('Parent'); + const parent3 = new Parse.Object('Parent'); Parse.Object.saveAll(objects) .then(() => { // [0, 1, 2] - parent.set("objects", objects.slice(0, 3)); + parent.set('objects', objects.slice(0, 3)); const shift = objects.shift(); // [2, 0] - parent2.set("objects", [objects[1], shift]); + parent2.set('objects', [objects[1], shift]); // [1, 2, 3, 4] - parent3.set("objects", objects.slice(1, 4)); + parent3.set('objects', objects.slice(1, 4)); return Parse.Object.saveAll([parent, parent2, parent3]); }) @@ -938,7 +938,7 @@ describe("Parse.Query testing", () => { // Return all Parent where all parent.objects are contained in objects return request({ - url: Parse.serverURL + "/classes/Parent", + url: Parse.serverURL + '/classes/Parent', qs: { where: JSON.stringify({ objects: { @@ -947,9 +947,9 @@ describe("Parse.Query testing", () => { }), }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Javascript-Key": Parse.javaScriptKey, - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, + 'Content-Type': 'application/json', }, }); }) @@ -963,7 +963,7 @@ describe("Parse.Query testing", () => { } ); - it("containedBy number array", done => { + it('containedBy number array', done => { const options = Object.assign({}, masterKeyOptions, { qs: { where: JSON.stringify({ @@ -978,7 +978,7 @@ describe("Parse.Query testing", () => { .then(() => { return request( Object.assign( - { url: Parse.serverURL + "/classes/TestObject" }, + { url: Parse.serverURL + '/classes/TestObject' }, options ) ); @@ -992,7 +992,7 @@ describe("Parse.Query testing", () => { }); }); - it("containedBy empty array", done => { + it('containedBy empty array', done => { const options = Object.assign({}, masterKeyOptions, { qs: { where: JSON.stringify({ numbers: { $containedBy: [] } }), @@ -1005,7 +1005,7 @@ describe("Parse.Query testing", () => { .then(() => { return request( Object.assign( - { url: Parse.serverURL + "/classes/TestObject" }, + { url: Parse.serverURL + '/classes/TestObject' }, options ) ); @@ -1017,7 +1017,7 @@ describe("Parse.Query testing", () => { }); }); - it("containedBy invalid query", done => { + it('containedBy invalid query', done => { const options = Object.assign({}, masterKeyOptions, { qs: { where: JSON.stringify({ objects: { $containedBy: 1234 } }), @@ -1029,7 +1029,7 @@ describe("Parse.Query testing", () => { .then(() => { return request( Object.assign( - { url: Parse.serverURL + "/classes/TestObject" }, + { url: Parse.serverURL + '/classes/TestObject' }, options ) ); @@ -1037,12 +1037,12 @@ describe("Parse.Query testing", () => { .then(done.fail) .catch(response => { equal(response.data.code, Parse.Error.INVALID_JSON); - equal(response.data.error, "bad $containedBy: should be an array"); + equal(response.data.error, 'bad $containedBy: should be an array'); done(); }); }); - it("equalTo queries", function (done) { + it('equalTo queries', function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; @@ -1050,7 +1050,7 @@ describe("Parse.Query testing", () => { [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) ).then(function () { const query = new Parse.Query(BoxedNumber); - query.equalTo("number", 3); + query.equalTo('number', 3); query.find().then(function (results) { equal(results.length, 1); done(); @@ -1058,7 +1058,7 @@ describe("Parse.Query testing", () => { }); }); - it("equalTo undefined", function (done) { + it('equalTo undefined', function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; @@ -1066,7 +1066,7 @@ describe("Parse.Query testing", () => { [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) ).then(function () { const query = new Parse.Query(BoxedNumber); - query.equalTo("number", undefined); + query.equalTo('number', undefined); query.find().then(function (results) { equal(results.length, 0); done(); @@ -1074,7 +1074,7 @@ describe("Parse.Query testing", () => { }); }); - it("lessThan queries", function (done) { + it('lessThan queries', function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; @@ -1082,7 +1082,7 @@ describe("Parse.Query testing", () => { [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) ).then(function () { const query = new Parse.Query(BoxedNumber); - query.lessThan("number", 7); + query.lessThan('number', 7); query.find().then(function (results) { equal(results.length, 7); done(); @@ -1090,7 +1090,7 @@ describe("Parse.Query testing", () => { }); }); - it("lessThanOrEqualTo queries", function (done) { + it('lessThanOrEqualTo queries', function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; @@ -1098,7 +1098,7 @@ describe("Parse.Query testing", () => { [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) ).then(function () { const query = new Parse.Query(BoxedNumber); - query.lessThanOrEqualTo("number", 7); + query.lessThanOrEqualTo('number', 7); query.find().then(function (results) { equal(results.length, 8); done(); @@ -1106,7 +1106,7 @@ describe("Parse.Query testing", () => { }); }); - it("lessThan zero queries", done => { + it('lessThan zero queries', done => { const makeBoxedNumber = i => { return new BoxedNumber({ number: i }); }; @@ -1115,7 +1115,7 @@ describe("Parse.Query testing", () => { Parse.Object.saveAll(boxedNumbers) .then(() => { const query = new Parse.Query(BoxedNumber); - query.lessThan("number", 0); + query.lessThan('number', 0); return query.find(); }) .then(results => { @@ -1124,7 +1124,7 @@ describe("Parse.Query testing", () => { }); }); - it("lessThanOrEqualTo zero queries", done => { + it('lessThanOrEqualTo zero queries', done => { const makeBoxedNumber = i => { return new BoxedNumber({ number: i }); }; @@ -1133,7 +1133,7 @@ describe("Parse.Query testing", () => { Parse.Object.saveAll(boxedNumbers) .then(() => { const query = new Parse.Query(BoxedNumber); - query.lessThanOrEqualTo("number", 0); + query.lessThanOrEqualTo('number', 0); return query.find(); }) .then(results => { @@ -1142,7 +1142,7 @@ describe("Parse.Query testing", () => { }); }); - it("greaterThan queries", function (done) { + it('greaterThan queries', function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; @@ -1150,7 +1150,7 @@ describe("Parse.Query testing", () => { [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) ).then(function () { const query = new Parse.Query(BoxedNumber); - query.greaterThan("number", 7); + query.greaterThan('number', 7); query.find().then(function (results) { equal(results.length, 2); done(); @@ -1158,7 +1158,7 @@ describe("Parse.Query testing", () => { }); }); - it("greaterThanOrEqualTo queries", function (done) { + it('greaterThanOrEqualTo queries', function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; @@ -1166,7 +1166,7 @@ describe("Parse.Query testing", () => { [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) ).then(function () { const query = new Parse.Query(BoxedNumber); - query.greaterThanOrEqualTo("number", 7); + query.greaterThanOrEqualTo('number', 7); query.find().then(function (results) { equal(results.length, 3); done(); @@ -1174,7 +1174,7 @@ describe("Parse.Query testing", () => { }); }); - it("greaterThan zero queries", done => { + it('greaterThan zero queries', done => { const makeBoxedNumber = i => { return new BoxedNumber({ number: i }); }; @@ -1183,7 +1183,7 @@ describe("Parse.Query testing", () => { Parse.Object.saveAll(boxedNumbers) .then(() => { const query = new Parse.Query(BoxedNumber); - query.greaterThan("number", 0); + query.greaterThan('number', 0); return query.find(); }) .then(results => { @@ -1192,7 +1192,7 @@ describe("Parse.Query testing", () => { }); }); - it("greaterThanOrEqualTo zero queries", done => { + it('greaterThanOrEqualTo zero queries', done => { const makeBoxedNumber = i => { return new BoxedNumber({ number: i }); }; @@ -1201,7 +1201,7 @@ describe("Parse.Query testing", () => { Parse.Object.saveAll(boxedNumbers) .then(() => { const query = new Parse.Query(BoxedNumber); - query.greaterThanOrEqualTo("number", 0); + query.greaterThanOrEqualTo('number', 0); return query.find(); }) .then(results => { @@ -1210,7 +1210,7 @@ describe("Parse.Query testing", () => { }); }); - it("lessThanOrEqualTo greaterThanOrEqualTo queries", function (done) { + it('lessThanOrEqualTo greaterThanOrEqualTo queries', function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; @@ -1218,8 +1218,8 @@ describe("Parse.Query testing", () => { [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) ).then(function () { const query = new Parse.Query(BoxedNumber); - query.lessThanOrEqualTo("number", 7); - query.greaterThanOrEqualTo("number", 7); + query.lessThanOrEqualTo('number', 7); + query.greaterThanOrEqualTo('number', 7); query.find().then(function (results) { equal(results.length, 1); done(); @@ -1227,7 +1227,7 @@ describe("Parse.Query testing", () => { }); }); - it("lessThan greaterThan queries", function (done) { + it('lessThan greaterThan queries', function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; @@ -1235,8 +1235,8 @@ describe("Parse.Query testing", () => { [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) ).then(function () { const query = new Parse.Query(BoxedNumber); - query.lessThan("number", 9); - query.greaterThan("number", 3); + query.lessThan('number', 9); + query.greaterThan('number', 3); query.find().then(function (results) { equal(results.length, 5); done(); @@ -1244,7 +1244,7 @@ describe("Parse.Query testing", () => { }); }); - it("notEqualTo queries", function (done) { + it('notEqualTo queries', function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; @@ -1252,7 +1252,7 @@ describe("Parse.Query testing", () => { [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) ).then(function () { const query = new Parse.Query(BoxedNumber); - query.notEqualTo("number", 5); + query.notEqualTo('number', 5); query.find().then(function (results) { equal(results.length, 9); done(); @@ -1260,7 +1260,7 @@ describe("Parse.Query testing", () => { }); }); - it("notEqualTo zero queries", done => { + it('notEqualTo zero queries', done => { const makeBoxedNumber = i => { return new BoxedNumber({ number: i }); }; @@ -1269,7 +1269,7 @@ describe("Parse.Query testing", () => { Parse.Object.saveAll(boxedNumbers) .then(() => { const query = new Parse.Query(BoxedNumber); - query.notEqualTo("number", 0); + query.notEqualTo('number', 0); return query.find(); }) .then(results => { @@ -1278,7 +1278,7 @@ describe("Parse.Query testing", () => { }); }); - it("equalTo zero queries", done => { + it('equalTo zero queries', done => { const makeBoxedNumber = i => { return new BoxedNumber({ number: i }); }; @@ -1287,7 +1287,7 @@ describe("Parse.Query testing", () => { Parse.Object.saveAll(boxedNumbers) .then(() => { const query = new Parse.Query(BoxedNumber); - query.equalTo("number", 0); + query.equalTo('number', 0); return query.find(); }) .then(results => { @@ -1296,7 +1296,7 @@ describe("Parse.Query testing", () => { }); }); - it("number equalTo boolean queries", done => { + it('number equalTo boolean queries', done => { const makeBoxedNumber = i => { return new BoxedNumber({ number: i }); }; @@ -1305,7 +1305,7 @@ describe("Parse.Query testing", () => { Parse.Object.saveAll(boxedNumbers) .then(() => { const query = new Parse.Query(BoxedNumber); - query.equalTo("number", false); + query.equalTo('number', false); return query.find(); }) .then(results => { @@ -1314,13 +1314,13 @@ describe("Parse.Query testing", () => { }); }); - it("equalTo false queries", done => { + it('equalTo false queries', done => { const obj1 = new TestObject({ field: false }); const obj2 = new TestObject({ field: true }); Parse.Object.saveAll([obj1, obj2]) .then(() => { const query = new Parse.Query(TestObject); - query.equalTo("field", false); + query.equalTo('field', false); return query.find(); }) .then(results => { @@ -1329,7 +1329,7 @@ describe("Parse.Query testing", () => { }); }); - it("where $eq false queries (rest)", done => { + it('where $eq false queries (rest)', done => { const options = Object.assign({}, masterKeyOptions, { qs: { where: JSON.stringify({ field: { $eq: false } }), @@ -1339,7 +1339,7 @@ describe("Parse.Query testing", () => { const obj2 = new TestObject({ field: true }); Parse.Object.saveAll([obj1, obj2]).then(() => { request( - Object.assign({ url: Parse.serverURL + "/classes/TestObject" }, options) + Object.assign({ url: Parse.serverURL + '/classes/TestObject' }, options) ).then(resp => { equal(resp.data.results.length, 1); done(); @@ -1347,7 +1347,7 @@ describe("Parse.Query testing", () => { }); }); - it("where $eq null queries (rest)", done => { + it('where $eq null queries (rest)', done => { const options = Object.assign({}, masterKeyOptions, { qs: { where: JSON.stringify({ field: { $eq: null } }), @@ -1357,7 +1357,7 @@ describe("Parse.Query testing", () => { const obj2 = new TestObject({ field: null }); Parse.Object.saveAll([obj1, obj2]).then(() => { return request( - Object.assign({ url: Parse.serverURL + "/classes/TestObject" }, options) + Object.assign({ url: Parse.serverURL + '/classes/TestObject' }, options) ).then(resp => { equal(resp.data.results.length, 1); done(); @@ -1365,7 +1365,7 @@ describe("Parse.Query testing", () => { }); }); - it("containedIn queries", function (done) { + it('containedIn queries', function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; @@ -1373,7 +1373,7 @@ describe("Parse.Query testing", () => { [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) ).then(function () { const query = new Parse.Query(BoxedNumber); - query.containedIn("number", [3, 5, 7, 9, 11]); + query.containedIn('number', [3, 5, 7, 9, 11]); query.find().then(function (results) { equal(results.length, 4); done(); @@ -1381,7 +1381,7 @@ describe("Parse.Query testing", () => { }); }); - it("containedIn false queries", done => { + it('containedIn false queries', done => { const makeBoxedNumber = i => { return new BoxedNumber({ number: i }); }; @@ -1390,18 +1390,18 @@ describe("Parse.Query testing", () => { Parse.Object.saveAll(boxedNumbers) .then(() => { const query = new Parse.Query(BoxedNumber); - query.containedIn("number", false); + query.containedIn('number', false); return query.find(); }) .then(done.fail) .catch(error => { equal(error.code, Parse.Error.INVALID_JSON); - equal(error.message, "bad $in value"); + equal(error.message, 'bad $in value'); done(); }); }); - it("notContainedIn false queries", done => { + it('notContainedIn false queries', done => { const makeBoxedNumber = i => { return new BoxedNumber({ number: i }); }; @@ -1410,18 +1410,18 @@ describe("Parse.Query testing", () => { Parse.Object.saveAll(boxedNumbers) .then(() => { const query = new Parse.Query(BoxedNumber); - query.notContainedIn("number", false); + query.notContainedIn('number', false); return query.find(); }) .then(done.fail) .catch(error => { equal(error.code, Parse.Error.INVALID_JSON); - equal(error.message, "bad $nin value"); + equal(error.message, 'bad $nin value'); done(); }); }); - it("notContainedIn queries", function (done) { + it('notContainedIn queries', function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; @@ -1429,7 +1429,7 @@ describe("Parse.Query testing", () => { [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) ).then(function () { const query = new Parse.Query(BoxedNumber); - query.notContainedIn("number", [3, 5, 7, 9, 11]); + query.notContainedIn('number', [3, 5, 7, 9, 11]); query.find().then(function (results) { equal(results.length, 6); done(); @@ -1437,7 +1437,7 @@ describe("Parse.Query testing", () => { }); }); - it("objectId containedIn queries", function (done) { + it('objectId containedIn queries', function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; @@ -1445,27 +1445,27 @@ describe("Parse.Query testing", () => { [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) ).then(function (list) { const query = new Parse.Query(BoxedNumber); - query.containedIn("objectId", [ + query.containedIn('objectId', [ list[2].id, list[3].id, list[0].id, - "NONSENSE", + 'NONSENSE', ]); - query.ascending("number"); + query.ascending('number'); query.find().then(function (results) { if (results.length != 3) { - fail("expected 3 results"); + fail('expected 3 results'); } else { - equal(results[0].get("number"), 0); - equal(results[1].get("number"), 2); - equal(results[2].get("number"), 3); + equal(results[0].get('number'), 0); + equal(results[1].get('number'), 2); + equal(results[2].get('number'), 3); } done(); }); }); }); - it("objectId equalTo queries", function (done) { + it('objectId equalTo queries', function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; @@ -1473,20 +1473,20 @@ describe("Parse.Query testing", () => { [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) ).then(function (list) { const query = new Parse.Query(BoxedNumber); - query.equalTo("objectId", list[4].id); + query.equalTo('objectId', list[4].id); query.find().then(function (results) { if (results.length != 1) { - fail("expected 1 result"); + fail('expected 1 result'); done(); } else { - equal(results[0].get("number"), 4); + equal(results[0].get('number'), 4); } done(); }); }); }); - it("find no elements", function (done) { + it('find no elements', function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; @@ -1494,7 +1494,7 @@ describe("Parse.Query testing", () => { [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) ).then(function () { const query = new Parse.Query(BoxedNumber); - query.equalTo("number", 17); + query.equalTo('number', 17); query.find().then(function (results) { equal(results.length, 0); done(); @@ -1502,9 +1502,9 @@ describe("Parse.Query testing", () => { }); }); - it("find with error", function (done) { + it('find with error', function (done) { const query = new Parse.Query(BoxedNumber); - query.equalTo("$foo", "bar"); + query.equalTo('$foo', 'bar'); query .find() .then(done.fail) @@ -1512,8 +1512,8 @@ describe("Parse.Query testing", () => { .then(done); }); - it("get", function (done) { - Parse.Object.saveAll([new TestObject({ foo: "bar" })]).then( + it('get', function (done) { + Parse.Object.saveAll([new TestObject({ foo: 'bar' })]).then( function (items) { ok(items[0]); const objectId = items[0].id; @@ -1521,7 +1521,7 @@ describe("Parse.Query testing", () => { query.get(objectId).then(function (result) { ok(result); equal(result.id, objectId); - equal(result.get("foo"), "bar"); + equal(result.get('foo'), 'bar'); ok(result.createdAt instanceof Date); ok(result.updatedAt instanceof Date); done(); @@ -1530,8 +1530,8 @@ describe("Parse.Query testing", () => { ); }); - it("get undefined", function (done) { - Parse.Object.saveAll([new TestObject({ foo: "bar" })]).then( + it('get undefined', function (done) { + Parse.Object.saveAll([new TestObject({ foo: 'bar' })]).then( function (items) { ok(items[0]); const query = new Parse.Query(TestObject); @@ -1540,14 +1540,14 @@ describe("Parse.Query testing", () => { ); }); - it("get error", function (done) { - Parse.Object.saveAll([new TestObject({ foo: "bar" })]).then( + it('get error', function (done) { + Parse.Object.saveAll([new TestObject({ foo: 'bar' })]).then( function (items) { ok(items[0]); const query = new Parse.Query(TestObject); - query.get("InvalidObjectID").then( + query.get('InvalidObjectID').then( function () { - ok(false, "The get should have failed."); + ok(false, 'The get should have failed.'); done(); }, function (error) { @@ -1559,21 +1559,21 @@ describe("Parse.Query testing", () => { ); }); - it("first", function (done) { - Parse.Object.saveAll([new TestObject({ foo: "bar" })]).then(function () { + it('first', function (done) { + Parse.Object.saveAll([new TestObject({ foo: 'bar' })]).then(function () { const query = new Parse.Query(TestObject); - query.equalTo("foo", "bar"); + query.equalTo('foo', 'bar'); query.first().then(function (result) { - equal(result.get("foo"), "bar"); + equal(result.get('foo'), 'bar'); done(); }); }); }); - it("first no result", function (done) { - Parse.Object.saveAll([new TestObject({ foo: "bar" })]).then(function () { + it('first no result', function (done) { + Parse.Object.saveAll([new TestObject({ foo: 'bar' })]).then(function () { const query = new Parse.Query(TestObject); - query.equalTo("foo", "baz"); + query.equalTo('foo', 'baz'); query.first().then(function (result) { equal(result, undefined); done(); @@ -1581,23 +1581,23 @@ describe("Parse.Query testing", () => { }); }); - it("first with two results", function (done) { + it('first with two results', function (done) { Parse.Object.saveAll([ - new TestObject({ foo: "bar" }), - new TestObject({ foo: "bar" }), + new TestObject({ foo: 'bar' }), + new TestObject({ foo: 'bar' }), ]).then(function () { const query = new Parse.Query(TestObject); - query.equalTo("foo", "bar"); + query.equalTo('foo', 'bar'); query.first().then(function (result) { - equal(result.get("foo"), "bar"); + equal(result.get('foo'), 'bar'); done(); }); }); }); - it("first with error", function (done) { + it('first with error', function (done) { const query = new Parse.Query(BoxedNumber); - query.equalTo("$foo", "bar"); + query.equalTo('$foo', 'bar'); query .first() .then(done.fail) @@ -1606,10 +1606,10 @@ describe("Parse.Query testing", () => { }); const Container = Parse.Object.extend({ - className: "Container", + className: 'Container', }); - it("notEqualTo object", function (done) { + it('notEqualTo object', function (done) { const item1 = new TestObject(); const item2 = new TestObject(); const container1 = new Container({ item: item1 }); @@ -1617,7 +1617,7 @@ describe("Parse.Query testing", () => { Parse.Object.saveAll([item1, item2, container1, container2]).then( function () { const query = new Parse.Query(Container); - query.notEqualTo("item", item1); + query.notEqualTo('item', item1); query.find().then(function (results) { equal(results.length, 1); done(); @@ -1626,7 +1626,7 @@ describe("Parse.Query testing", () => { ); }); - it("skip", function (done) { + it('skip', function (done) { Parse.Object.saveAll([new TestObject(), new TestObject()]).then( function () { const query = new Parse.Query(TestObject); @@ -1663,7 +1663,7 @@ describe("Parse.Query testing", () => { ); }); - it("count", function (done) { + it('count', function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; @@ -1671,7 +1671,7 @@ describe("Parse.Query testing", () => { [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) ).then(function () { const query = new Parse.Query(BoxedNumber); - query.greaterThan("number", 1); + query.greaterThan('number', 1); query.count().then(function (count) { equal(count, 8); done(); @@ -1679,90 +1679,90 @@ describe("Parse.Query testing", () => { }); }); - it("order by ascending number", function (done) { + it('order by ascending number', function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; Parse.Object.saveAll([3, 1, 2].map(makeBoxedNumber)).then(function () { const query = new Parse.Query(BoxedNumber); - query.ascending("number"); + query.ascending('number'); query.find().then(function (results) { equal(results.length, 3); - equal(results[0].get("number"), 1); - equal(results[1].get("number"), 2); - equal(results[2].get("number"), 3); + equal(results[0].get('number'), 1); + equal(results[1].get('number'), 2); + equal(results[2].get('number'), 3); done(); }); }); }); - it("order by descending number", function (done) { + it('order by descending number', function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; Parse.Object.saveAll([3, 1, 2].map(makeBoxedNumber)).then(function () { const query = new Parse.Query(BoxedNumber); - query.descending("number"); + query.descending('number'); query.find().then(function (results) { equal(results.length, 3); - equal(results[0].get("number"), 3); - equal(results[1].get("number"), 2); - equal(results[2].get("number"), 1); + equal(results[0].get('number'), 3); + equal(results[1].get('number'), 2); + equal(results[2].get('number'), 1); done(); }); }); }); - it("can order on an object string field", function (done) { + it('can order on an object string field', function (done) { const testSet = [ - { sortField: { value: "Z" } }, - { sortField: { value: "A" } }, - { sortField: { value: "M" } }, + { sortField: { value: 'Z' } }, + { sortField: { value: 'A' } }, + { sortField: { value: 'M' } }, ]; - const objects = testSet.map(e => new Parse.Object("Test", e)); + const objects = testSet.map(e => new Parse.Object('Test', e)); Parse.Object.saveAll(objects) .then(() => - new Parse.Query("Test").addDescending("sortField.value").first() + new Parse.Query('Test').addDescending('sortField.value').first() ) .then(result => { - expect(result.get("sortField").value).toBe("Z"); - return new Parse.Query("Test").addAscending("sortField.value").first(); + expect(result.get('sortField').value).toBe('Z'); + return new Parse.Query('Test').addAscending('sortField.value').first(); }) .then(result => { - expect(result.get("sortField").value).toBe("A"); + expect(result.get('sortField').value).toBe('A'); done(); }) .catch(done.fail); }); - it("can order on an object string field (level 2)", function (done) { + it('can order on an object string field (level 2)', function (done) { const testSet = [ - { sortField: { value: { field: "Z" } } }, - { sortField: { value: { field: "A" } } }, - { sortField: { value: { field: "M" } } }, + { sortField: { value: { field: 'Z' } } }, + { sortField: { value: { field: 'A' } } }, + { sortField: { value: { field: 'M' } } }, ]; - const objects = testSet.map(e => new Parse.Object("Test", e)); + const objects = testSet.map(e => new Parse.Object('Test', e)); Parse.Object.saveAll(objects) .then(() => - new Parse.Query("Test").addDescending("sortField.value.field").first() + new Parse.Query('Test').addDescending('sortField.value.field').first() ) .then(result => { - expect(result.get("sortField").value.field).toBe("Z"); - return new Parse.Query("Test") - .addAscending("sortField.value.field") + expect(result.get('sortField').value.field).toBe('Z'); + return new Parse.Query('Test') + .addAscending('sortField.value.field') .first(); }) .then(result => { - expect(result.get("sortField").value.field).toBe("A"); + expect(result.get('sortField').value.field).toBe('A'); done(); }) .catch(done.fail); }); - it_id("65c8238d-cf02-49d0-a919-8a17f5a58280")(it)( - "can order on an object number field", + it_id('65c8238d-cf02-49d0-a919-8a17f5a58280')(it)( + 'can order on an object number field', function (done) { const testSet = [ { sortField: { value: 10 } }, @@ -1770,27 +1770,27 @@ describe("Parse.Query testing", () => { { sortField: { value: 5 } }, ]; - const objects = testSet.map(e => new Parse.Object("Test", e)); + const objects = testSet.map(e => new Parse.Object('Test', e)); Parse.Object.saveAll(objects) .then(() => - new Parse.Query("Test").addDescending("sortField.value").first() + new Parse.Query('Test').addDescending('sortField.value').first() ) .then(result => { - expect(result.get("sortField").value).toBe(10); - return new Parse.Query("Test") - .addAscending("sortField.value") + expect(result.get('sortField').value).toBe(10); + return new Parse.Query('Test') + .addAscending('sortField.value') .first(); }) .then(result => { - expect(result.get("sortField").value).toBe(1); + expect(result.get('sortField').value).toBe(1); done(); }) .catch(done.fail); } ); - it_id("d8f0bead-b931-4d66-8b0c-28c5705e463c")(it)( - "can order on an object number field (level 2)", + it_id('d8f0bead-b931-4d66-8b0c-28c5705e463c')(it)( + 'can order on an object number field (level 2)', function (done) { const testSet = [ { sortField: { value: { field: 10 } } }, @@ -1798,60 +1798,60 @@ describe("Parse.Query testing", () => { { sortField: { value: { field: 5 } } }, ]; - const objects = testSet.map(e => new Parse.Object("Test", e)); + const objects = testSet.map(e => new Parse.Object('Test', e)); Parse.Object.saveAll(objects) .then(() => - new Parse.Query("Test").addDescending("sortField.value.field").first() + new Parse.Query('Test').addDescending('sortField.value.field').first() ) .then(result => { - expect(result.get("sortField").value.field).toBe(10); - return new Parse.Query("Test") - .addAscending("sortField.value.field") + expect(result.get('sortField').value.field).toBe(10); + return new Parse.Query('Test') + .addAscending('sortField.value.field') .first(); }) .then(result => { - expect(result.get("sortField").value.field).toBe(1); + expect(result.get('sortField').value.field).toBe(1); done(); }) .catch(done.fail); } ); - it("order by ascending number then descending string", function (done) { - const strings = ["a", "b", "c", "d"]; + it('order by ascending number then descending string', function (done) { + const strings = ['a', 'b', 'c', 'd']; const makeBoxedNumber = function (num, i) { return new BoxedNumber({ number: num, string: strings[i] }); }; Parse.Object.saveAll([3, 1, 3, 2].map(makeBoxedNumber)).then(function () { const query = new Parse.Query(BoxedNumber); - query.ascending("number").addDescending("string"); + query.ascending('number').addDescending('string'); query.find().then(function (results) { equal(results.length, 4); - equal(results[0].get("number"), 1); - equal(results[0].get("string"), "b"); - equal(results[1].get("number"), 2); - equal(results[1].get("string"), "d"); - equal(results[2].get("number"), 3); - equal(results[2].get("string"), "c"); - equal(results[3].get("number"), 3); - equal(results[3].get("string"), "a"); + equal(results[0].get('number'), 1); + equal(results[0].get('string'), 'b'); + equal(results[1].get('number'), 2); + equal(results[1].get('string'), 'd'); + equal(results[2].get('number'), 3); + equal(results[2].get('string'), 'c'); + equal(results[3].get('number'), 3); + equal(results[3].get('string'), 'a'); done(); }); }); }); - it("order by non-existing string", async () => { - const strings = ["a", "b", "c", "d"]; + it('order by non-existing string', async () => { + const strings = ['a', 'b', 'c', 'd']; const makeBoxedNumber = function (num, i) { return new BoxedNumber({ number: num, string: strings[i] }); }; await Parse.Object.saveAll([3, 1, 3, 2].map(makeBoxedNumber)); - const results = await new Parse.Query(BoxedNumber).ascending("foo").find(); + const results = await new Parse.Query(BoxedNumber).ascending('foo').find(); expect(results.length).toBe(4); }); - it("order by descending number then ascending string", function (done) { - const strings = ["a", "b", "c", "d"]; + it('order by descending number then ascending string', function (done) { + const strings = ['a', 'b', 'c', 'd']; const makeBoxedNumber = function (num, i) { return new BoxedNumber({ number: num, string: strings[i] }); }; @@ -1860,20 +1860,20 @@ describe("Parse.Query testing", () => { Parse.Object.saveAll(objects) .then(() => { const query = new Parse.Query(BoxedNumber); - query.descending("number").addAscending("string"); + query.descending('number').addAscending('string'); return query.find(); }) .then( results => { equal(results.length, 4); - equal(results[0].get("number"), 3); - equal(results[0].get("string"), "a"); - equal(results[1].get("number"), 3); - equal(results[1].get("string"), "c"); - equal(results[2].get("number"), 2); - equal(results[2].get("string"), "d"); - equal(results[3].get("number"), 1); - equal(results[3].get("string"), "b"); + equal(results[0].get('number'), 3); + equal(results[0].get('string'), 'a'); + equal(results[1].get('number'), 3); + equal(results[1].get('string'), 'c'); + equal(results[2].get('number'), 2); + equal(results[2].get('string'), 'd'); + equal(results[3].get('number'), 1); + equal(results[3].get('string'), 'b'); done(); }, err => { @@ -1883,48 +1883,48 @@ describe("Parse.Query testing", () => { ); }); - it("order by descending number and string", function (done) { - const strings = ["a", "b", "c", "d"]; + it('order by descending number and string', function (done) { + const strings = ['a', 'b', 'c', 'd']; const makeBoxedNumber = function (num, i) { return new BoxedNumber({ number: num, string: strings[i] }); }; Parse.Object.saveAll([3, 1, 3, 2].map(makeBoxedNumber)).then(function () { const query = new Parse.Query(BoxedNumber); - query.descending("number,string"); + query.descending('number,string'); query.find().then(function (results) { equal(results.length, 4); - equal(results[0].get("number"), 3); - equal(results[0].get("string"), "c"); - equal(results[1].get("number"), 3); - equal(results[1].get("string"), "a"); - equal(results[2].get("number"), 2); - equal(results[2].get("string"), "d"); - equal(results[3].get("number"), 1); - equal(results[3].get("string"), "b"); + equal(results[0].get('number'), 3); + equal(results[0].get('string'), 'c'); + equal(results[1].get('number'), 3); + equal(results[1].get('string'), 'a'); + equal(results[2].get('number'), 2); + equal(results[2].get('string'), 'd'); + equal(results[3].get('number'), 1); + equal(results[3].get('string'), 'b'); done(); }); }); }); - it("order by descending number and string, with space", function (done) { - const strings = ["a", "b", "c", "d"]; + it('order by descending number and string, with space', function (done) { + const strings = ['a', 'b', 'c', 'd']; const makeBoxedNumber = function (num, i) { return new BoxedNumber({ number: num, string: strings[i] }); }; Parse.Object.saveAll([3, 1, 3, 2].map(makeBoxedNumber)).then( function () { const query = new Parse.Query(BoxedNumber); - query.descending("number, string"); + query.descending('number, string'); query.find().then(function (results) { equal(results.length, 4); - equal(results[0].get("number"), 3); - equal(results[0].get("string"), "c"); - equal(results[1].get("number"), 3); - equal(results[1].get("string"), "a"); - equal(results[2].get("number"), 2); - equal(results[2].get("string"), "d"); - equal(results[3].get("number"), 1); - equal(results[3].get("string"), "b"); + equal(results[0].get('number'), 3); + equal(results[0].get('string'), 'c'); + equal(results[1].get('number'), 3); + equal(results[1].get('string'), 'a'); + equal(results[2].get('number'), 2); + equal(results[2].get('string'), 'd'); + equal(results[3].get('number'), 1); + equal(results[3].get('string'), 'b'); done(); }); }, @@ -1935,47 +1935,47 @@ describe("Parse.Query testing", () => { ); }); - it("order by descending number and string, with array arg", function (done) { - const strings = ["a", "b", "c", "d"]; + it('order by descending number and string, with array arg', function (done) { + const strings = ['a', 'b', 'c', 'd']; const makeBoxedNumber = function (num, i) { return new BoxedNumber({ number: num, string: strings[i] }); }; Parse.Object.saveAll([3, 1, 3, 2].map(makeBoxedNumber)).then(function () { const query = new Parse.Query(BoxedNumber); - query.descending(["number", "string"]); + query.descending(['number', 'string']); query.find().then(function (results) { equal(results.length, 4); - equal(results[0].get("number"), 3); - equal(results[0].get("string"), "c"); - equal(results[1].get("number"), 3); - equal(results[1].get("string"), "a"); - equal(results[2].get("number"), 2); - equal(results[2].get("string"), "d"); - equal(results[3].get("number"), 1); - equal(results[3].get("string"), "b"); + equal(results[0].get('number'), 3); + equal(results[0].get('string'), 'c'); + equal(results[1].get('number'), 3); + equal(results[1].get('string'), 'a'); + equal(results[2].get('number'), 2); + equal(results[2].get('string'), 'd'); + equal(results[3].get('number'), 1); + equal(results[3].get('string'), 'b'); done(); }); }); }); - it("order by descending number and string, with multiple args", function (done) { - const strings = ["a", "b", "c", "d"]; + it('order by descending number and string, with multiple args', function (done) { + const strings = ['a', 'b', 'c', 'd']; const makeBoxedNumber = function (num, i) { return new BoxedNumber({ number: num, string: strings[i] }); }; Parse.Object.saveAll([3, 1, 3, 2].map(makeBoxedNumber)).then(function () { const query = new Parse.Query(BoxedNumber); - query.descending("number", "string"); + query.descending('number', 'string'); query.find().then(function (results) { equal(results.length, 4); - equal(results[0].get("number"), 3); - equal(results[0].get("string"), "c"); - equal(results[1].get("number"), 3); - equal(results[1].get("string"), "a"); - equal(results[2].get("number"), 2); - equal(results[2].get("string"), "d"); - equal(results[3].get("number"), 1); - equal(results[3].get("string"), "b"); + equal(results[0].get('number'), 3); + equal(results[0].get('string'), 'c'); + equal(results[1].get('number'), 3); + equal(results[1].get('string'), 'a'); + equal(results[2].get('number'), 2); + equal(results[2].get('string'), 'd'); + equal(results[3].get('number'), 1); + equal(results[3].get('string'), 'b'); done(); }); }); @@ -1987,7 +1987,7 @@ describe("Parse.Query testing", () => { }; Parse.Object.saveAll([3, 1, 2].map(makeBoxedNumber)).then(function () { const query = new Parse.Query(BoxedNumber); - query.ascending("_password"); + query.ascending('_password'); query .find() .then(done.fail) @@ -1996,7 +1996,7 @@ describe("Parse.Query testing", () => { }); }); - it("order by _created_at", function (done) { + it('order by _created_at', function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; @@ -2011,18 +2011,18 @@ describe("Parse.Query testing", () => { }) .then(function () { const query = new Parse.Query(BoxedNumber); - query.ascending("_created_at"); + query.ascending('_created_at'); query.find().then(function (results) { equal(results.length, 3); - equal(results[0].get("number"), 3); - equal(results[1].get("number"), 1); - equal(results[2].get("number"), 2); + equal(results[0].get('number'), 3); + equal(results[1].get('number'), 1); + equal(results[2].get('number'), 2); done(); }, done.fail); }); }); - it("order by createdAt", function (done) { + it('order by createdAt', function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; @@ -2037,18 +2037,18 @@ describe("Parse.Query testing", () => { }) .then(function () { const query = new Parse.Query(BoxedNumber); - query.descending("createdAt"); + query.descending('createdAt'); query.find().then(function (results) { equal(results.length, 3); - equal(results[0].get("number"), 2); - equal(results[1].get("number"), 1); - equal(results[2].get("number"), 3); + equal(results[0].get('number'), 2); + equal(results[1].get('number'), 1); + equal(results[2].get('number'), 3); done(); }); }); }); - it("order by _updated_at", function (done) { + it('order by _updated_at', function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; @@ -2062,22 +2062,22 @@ describe("Parse.Query testing", () => { return numbers[2].save(); }) .then(function () { - numbers[1].set("number", 4); + numbers[1].set('number', 4); numbers[1].save().then(function () { const query = new Parse.Query(BoxedNumber); - query.ascending("_updated_at"); + query.ascending('_updated_at'); query.find().then(function (results) { equal(results.length, 3); - equal(results[0].get("number"), 3); - equal(results[1].get("number"), 2); - equal(results[2].get("number"), 4); + equal(results[0].get('number'), 3); + equal(results[1].get('number'), 2); + equal(results[2].get('number'), 4); done(); }); }); }); }); - it("order by updatedAt", function (done) { + it('order by updatedAt', function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; @@ -2091,15 +2091,15 @@ describe("Parse.Query testing", () => { return numbers[2].save(); }) .then(function () { - numbers[1].set("number", 4); + numbers[1].set('number', 4); numbers[1].save().then(function () { const query = new Parse.Query(BoxedNumber); - query.descending("_updated_at"); + query.descending('_updated_at'); query.find().then(function (results) { equal(results.length, 3); - equal(results[0].get("number"), 4); - equal(results[1].get("number"), 2); - equal(results[2].get("number"), 3); + equal(results[0].get('number'), 4); + equal(results[1].get('number'), 2); + equal(results[2].get('number'), 3); done(); }); }); @@ -2110,7 +2110,7 @@ describe("Parse.Query testing", () => { function makeTimeObject(start, i) { const time = new Date(); time.setSeconds(start.getSeconds() + i); - const item = new TestObject({ name: "item" + i, time: time }); + const item = new TestObject({ name: 'item' + i, time: time }); return item.save(); } @@ -2133,22 +2133,22 @@ describe("Parse.Query testing", () => { }); } - it("time equality", function (done) { + it('time equality', function (done) { makeThreeTimeObjects().then(function (list) { const query = new Parse.Query(TestObject); - query.equalTo("time", list[1].get("time")); + query.equalTo('time', list[1].get('time')); query.find().then(function (results) { equal(results.length, 1); - equal(results[0].get("name"), "item2"); + equal(results[0].get('name'), 'item2'); done(); }); }); }); - it("time lessThan", function (done) { + it('time lessThan', function (done) { makeThreeTimeObjects().then(function (list) { const query = new Parse.Query(TestObject); - query.lessThan("time", list[2].get("time")); + query.lessThan('time', list[2].get('time')); query.find().then(function (results) { equal(results.length, 2); done(); @@ -2157,10 +2157,10 @@ describe("Parse.Query testing", () => { }); // This test requires Date objects to be consistently stored as a Date. - it("time createdAt", function (done) { + it('time createdAt', function (done) { makeThreeTimeObjects().then(function (list) { const query = new Parse.Query(TestObject); - query.greaterThanOrEqualTo("createdAt", list[0].createdAt); + query.greaterThanOrEqualTo('createdAt', list[0].createdAt); query.find().then(function (results) { equal(results.length, 3); done(); @@ -2168,14 +2168,14 @@ describe("Parse.Query testing", () => { }); }); - it("matches string", function (done) { + it('matches string', function (done) { const thing1 = new TestObject(); - thing1.set("myString", "football"); + thing1.set('myString', 'football'); const thing2 = new TestObject(); - thing2.set("myString", "soccer"); + thing2.set('myString', 'soccer'); Parse.Object.saveAll([thing1, thing2]).then(function () { const query = new Parse.Query(TestObject); - query.matches("myString", "^fo*\\wb[^o]l+$"); + query.matches('myString', '^fo*\\wb[^o]l+$'); query.find().then(function (results) { equal(results.length, 1); done(); @@ -2183,14 +2183,14 @@ describe("Parse.Query testing", () => { }); }); - it("matches regex", function (done) { + it('matches regex', function (done) { const thing1 = new TestObject(); - thing1.set("myString", "football"); + thing1.set('myString', 'football'); const thing2 = new TestObject(); - thing2.set("myString", "soccer"); + thing2.set('myString', 'soccer'); Parse.Object.saveAll([thing1, thing2]).then(function () { const query = new Parse.Query(TestObject); - query.matches("myString", /^fo*\wb[^o]l+$/); + query.matches('myString', /^fo*\wb[^o]l+$/); query.find().then(function (results) { equal(results.length, 1); done(); @@ -2198,19 +2198,19 @@ describe("Parse.Query testing", () => { }); }); - it("case insensitive regex success", function (done) { + it('case insensitive regex success', function (done) { const thing = new TestObject(); - thing.set("myString", "football"); + thing.set('myString', 'football'); Parse.Object.saveAll([thing]).then(function () { const query = new Parse.Query(TestObject); - query.matches("myString", "FootBall", "i"); + query.matches('myString', 'FootBall', 'i'); query.find().then(done); }); }); - it("regexes with invalid options fail", function (done) { + it('regexes with invalid options fail', function (done) { const query = new Parse.Query(TestObject); - query.matches("myString", "FootBall", "some invalid option"); + query.matches('myString', 'FootBall', 'some invalid option'); query .find() .then(done.fail) @@ -2218,19 +2218,19 @@ describe("Parse.Query testing", () => { .then(done); }); - it_id("823852f6-1de5-45ba-a2b9-ed952fcc6012")(it)( - "Use a regex that requires all modifiers", + it_id('823852f6-1de5-45ba-a2b9-ed952fcc6012')(it)( + 'Use a regex that requires all modifiers', function (done) { const thing = new TestObject(); - thing.set("myString", "PArSe\nCom"); + thing.set('myString', 'PArSe\nCom'); Parse.Object.saveAll([thing]).then(function () { const query = new Parse.Query(TestObject); query.matches( - "myString", + 'myString', "parse # First fragment. We'll write this in one case but match insensitively\n" + - ".com # Second fragment. This can be separated by any character, including newline;" + - "however, this comment must end with a newline to recognize it as a comment\n", - "mixs" + '.com # Second fragment. This can be separated by any character, including newline;' + + 'however, this comment must end with a newline to recognize it as a comment\n', + 'mixs' ); query.find().then( function (results) { @@ -2246,12 +2246,12 @@ describe("Parse.Query testing", () => { } ); - it("Regular expression constructor includes modifiers inline", function (done) { + it('Regular expression constructor includes modifiers inline', function (done) { const thing = new TestObject(); - thing.set("myString", "\n\nbuffer\n\nparse.COM"); + thing.set('myString', '\n\nbuffer\n\nparse.COM'); Parse.Object.saveAll([thing]).then(function () { const query = new Parse.Query(TestObject); - query.matches("myString", /parse\.com/im); + query.matches('myString', /parse\.com/im); query.find().then(function (results) { equal(results.length, 1); done(); @@ -2263,15 +2263,15 @@ describe("Parse.Query testing", () => { "\\E' !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTU" + "VWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'"; - it("contains", function (done) { + it('contains', function (done) { Parse.Object.saveAll([ - new TestObject({ myString: "zax" + someAscii + "qub" }), - new TestObject({ myString: "start" + someAscii }), - new TestObject({ myString: someAscii + "end" }), + new TestObject({ myString: 'zax' + someAscii + 'qub' }), + new TestObject({ myString: 'start' + someAscii }), + new TestObject({ myString: someAscii + 'end' }), new TestObject({ myString: someAscii }), ]).then(function () { const query = new Parse.Query(TestObject); - query.contains("myString", someAscii); + query.contains('myString', someAscii); query.find().then(function (results) { equal(results.length, 4); done(); @@ -2279,17 +2279,17 @@ describe("Parse.Query testing", () => { }); }); - it("nested contains", done => { - const sender1 = { group: ["A", "B"] }; - const sender2 = { group: ["A", "C"] }; - const sender3 = { group: ["B", "C"] }; + it('nested contains', done => { + const sender1 = { group: ['A', 'B'] }; + const sender2 = { group: ['A', 'C'] }; + const sender3 = { group: ['B', 'C'] }; const obj1 = new TestObject({ sender: sender1 }); const obj2 = new TestObject({ sender: sender2 }); const obj3 = new TestObject({ sender: sender3 }); Parse.Object.saveAll([obj1, obj2, obj3]) .then(() => { const query = new Parse.Query(TestObject); - query.contains("sender.group", "A"); + query.contains('sender.group', 'A'); return query.find(); }) .then(results => { @@ -2298,15 +2298,15 @@ describe("Parse.Query testing", () => { }, done.fail); }); - it("startsWith", function (done) { + it('startsWith', function (done) { Parse.Object.saveAll([ - new TestObject({ myString: "zax" + someAscii + "qub" }), - new TestObject({ myString: "start" + someAscii }), - new TestObject({ myString: someAscii + "end" }), + new TestObject({ myString: 'zax' + someAscii + 'qub' }), + new TestObject({ myString: 'start' + someAscii }), + new TestObject({ myString: someAscii + 'end' }), new TestObject({ myString: someAscii }), ]).then(function () { const query = new Parse.Query(TestObject); - query.startsWith("myString", someAscii); + query.startsWith('myString', someAscii); query.find().then(function (results) { equal(results.length, 2); done(); @@ -2314,15 +2314,15 @@ describe("Parse.Query testing", () => { }); }); - it("endsWith", function (done) { + it('endsWith', function (done) { Parse.Object.saveAll([ - new TestObject({ myString: "zax" + someAscii + "qub" }), - new TestObject({ myString: "start" + someAscii }), - new TestObject({ myString: someAscii + "end" }), + new TestObject({ myString: 'zax' + someAscii + 'qub' }), + new TestObject({ myString: 'start' + someAscii }), + new TestObject({ myString: someAscii + 'end' }), new TestObject({ myString: someAscii }), ]).then(function () { const query = new Parse.Query(TestObject); - query.endsWith("myString", someAscii); + query.endsWith('myString', someAscii); query.find().then(function (results) { equal(results.length, 2); done(); @@ -2330,102 +2330,102 @@ describe("Parse.Query testing", () => { }); }); - it("exists", function (done) { + it('exists', function (done) { const objects = []; for (const i of [0, 1, 2, 3, 4, 5, 6, 7, 8]) { const item = new TestObject(); if (i % 2 === 0) { - item.set("x", i + 1); + item.set('x', i + 1); } else { - item.set("y", i + 1); + item.set('y', i + 1); } objects.push(item); } Parse.Object.saveAll(objects).then(function () { const query = new Parse.Query(TestObject); - query.exists("x"); + query.exists('x'); query.find().then(function (results) { equal(results.length, 5); for (const result of results) { - ok(result.get("x")); + ok(result.get('x')); } done(); }); }); }); - it("doesNotExist", function (done) { + it('doesNotExist', function (done) { const objects = []; for (const i of [0, 1, 2, 3, 4, 5, 6, 7, 8]) { const item = new TestObject(); if (i % 2 === 0) { - item.set("x", i + 1); + item.set('x', i + 1); } else { - item.set("y", i + 1); + item.set('y', i + 1); } objects.push(item); } Parse.Object.saveAll(objects).then(function () { const query = new Parse.Query(TestObject); - query.doesNotExist("x"); + query.doesNotExist('x'); query.find().then(function (results) { equal(results.length, 4); for (const result of results) { - ok(result.get("y")); + ok(result.get('y')); } done(); }); }); }); - it("exists relation", function (done) { + it('exists relation', function (done) { const objects = []; for (const i of [0, 1, 2, 3, 4, 5, 6, 7, 8]) { const container = new Container(); if (i % 2 === 0) { const item = new TestObject(); - item.set("x", i); - container.set("x", item); + item.set('x', i); + container.set('x', item); objects.push(item); } else { - container.set("y", i); + container.set('y', i); } objects.push(container); } Parse.Object.saveAll(objects).then(function () { const query = new Parse.Query(Container); - query.exists("x"); + query.exists('x'); query.find().then(function (results) { equal(results.length, 5); for (const result of results) { - ok(result.get("x")); + ok(result.get('x')); } done(); }); }); }); - it("doesNotExist relation", function (done) { + it('doesNotExist relation', function (done) { const objects = []; for (const i of [0, 1, 2, 3, 4, 5, 6, 7]) { const container = new Container(); if (i % 2 === 0) { const item = new TestObject(); - item.set("x", i); - container.set("x", item); + item.set('x', i); + container.set('x', item); objects.push(item); } else { - container.set("y", i); + container.set('y', i); } objects.push(container); } Parse.Object.saveAll(objects).then(function () { const query = new Parse.Query(Container); - query.doesNotExist("x"); + query.doesNotExist('x'); query.find().then(function (results) { equal(results.length, 4); for (const result of results) { - ok(result.get("y")); + ok(result.get('y')); } done(); }); @@ -2435,8 +2435,8 @@ describe("Parse.Query testing", () => { it("don't include by default", function (done) { const child = new TestObject(); const parent = new Container(); - child.set("foo", "bar"); - parent.set("child", child); + child.set('foo', 'bar'); + parent.set('child', child); Parse.Object.saveAll([child, parent]).then(function () { child._clearServerData(); const query = new Parse.Query(Container); @@ -2444,64 +2444,64 @@ describe("Parse.Query testing", () => { equal(results.length, 1); const parentAgain = results[0]; const goodURL = Parse.serverURL; - Parse.serverURL = "YAAAAAAAAARRRRRGGGGGGGGG"; - const childAgain = parentAgain.get("child"); + Parse.serverURL = 'YAAAAAAAAARRRRRGGGGGGGGG'; + const childAgain = parentAgain.get('child'); ok(childAgain); - equal(childAgain.get("foo"), undefined); + equal(childAgain.get('foo'), undefined); Parse.serverURL = goodURL; done(); }); }); }); - it("include relation", function (done) { + it('include relation', function (done) { const child = new TestObject(); const parent = new Container(); - child.set("foo", "bar"); - parent.set("child", child); + child.set('foo', 'bar'); + parent.set('child', child); Parse.Object.saveAll([child, parent]).then(function () { const query = new Parse.Query(Container); - query.include("child"); + query.include('child'); query.find().then(function (results) { equal(results.length, 1); const parentAgain = results[0]; const goodURL = Parse.serverURL; - Parse.serverURL = "YAAAAAAAAARRRRRGGGGGGGGG"; - const childAgain = parentAgain.get("child"); + Parse.serverURL = 'YAAAAAAAAARRRRRGGGGGGGGG'; + const childAgain = parentAgain.get('child'); ok(childAgain); - equal(childAgain.get("foo"), "bar"); + equal(childAgain.get('foo'), 'bar'); Parse.serverURL = goodURL; done(); }); }); }); - it("include relation array", function (done) { + it('include relation array', function (done) { const child = new TestObject(); const parent = new Container(); - child.set("foo", "bar"); - parent.set("child", child); + child.set('foo', 'bar'); + parent.set('child', child); Parse.Object.saveAll([child, parent]).then(function () { const query = new Parse.Query(Container); - query.include(["child"]); + query.include(['child']); query.find().then(function (results) { equal(results.length, 1); const parentAgain = results[0]; const goodURL = Parse.serverURL; - Parse.serverURL = "YAAAAAAAAARRRRRGGGGGGGGG"; - const childAgain = parentAgain.get("child"); + Parse.serverURL = 'YAAAAAAAAARRRRRGGGGGGGGG'; + const childAgain = parentAgain.get('child'); ok(childAgain); - equal(childAgain.get("foo"), "bar"); + equal(childAgain.get('foo'), 'bar'); Parse.serverURL = goodURL; done(); }); }); }); - it("nested include", function (done) { - const Child = Parse.Object.extend("Child"); - const Parent = Parse.Object.extend("Parent"); - const Grandparent = Parse.Object.extend("Grandparent"); + it('nested include', function (done) { + const Child = Parse.Object.extend('Child'); + const Parent = Parse.Object.extend('Parent'); + const Grandparent = Parse.Object.extend('Grandparent'); const objects = []; for (let i = 0; i < 5; ++i) { const grandparent = new Grandparent({ @@ -2518,12 +2518,12 @@ describe("Parse.Query testing", () => { Parse.Object.saveAll(objects).then(function () { const query = new Parse.Query(Grandparent); - query.include(["parent.child"]); + query.include(['parent.child']); query.find().then(function (results) { equal(results.length, 5); for (const object of results) { - equal(object.get("z"), object.get("parent").get("y")); - equal(object.get("z"), object.get("parent").get("child").get("x")); + equal(object.get('z'), object.get('parent').get('y')); + equal(object.get('z'), object.get('parent').get('child').get('x')); } done(); }); @@ -2531,23 +2531,23 @@ describe("Parse.Query testing", () => { }); it("include doesn't make dirty wrong", function (done) { - const Parent = Parse.Object.extend("ParentObject"); - const Child = Parse.Object.extend("ChildObject"); + const Parent = Parse.Object.extend('ParentObject'); + const Child = Parse.Object.extend('ChildObject'); const parent = new Parent(); const child = new Child(); - child.set("foo", "bar"); - parent.set("child", child); + child.set('foo', 'bar'); + parent.set('child', child); Parse.Object.saveAll([child, parent]).then(function () { const query = new Parse.Query(Parent); - query.include("child"); + query.include('child'); query.find().then(function (results) { equal(results.length, 1); const parentAgain = results[0]; - const childAgain = parentAgain.get("child"); + const childAgain = parentAgain.get('child'); equal(childAgain.id, child.id); equal(parentAgain.id, parent.id); - equal(childAgain.get("foo"), "bar"); + equal(childAgain.get('foo'), 'bar'); equal(false, parentAgain.dirty()); equal(false, childAgain.dirty()); done(); @@ -2555,215 +2555,215 @@ describe("Parse.Query testing", () => { }); }); - it("properly includes array", done => { + it('properly includes array', done => { const objects = []; let total = 0; while (objects.length != 5) { - const object = new Parse.Object("AnObject"); - object.set("key", objects.length); + const object = new Parse.Object('AnObject'); + object.set('key', objects.length); total += objects.length; objects.push(object); } Parse.Object.saveAll(objects) .then(() => { - const object = new Parse.Object("AContainer"); - object.set("objects", objects); + const object = new Parse.Object('AContainer'); + object.set('objects', objects); return object.save(); }) .then(() => { - const query = new Parse.Query("AContainer"); - query.include("objects"); + const query = new Parse.Query('AContainer'); + query.include('objects'); return query.find(); }) .then( results => { expect(results.length).toBe(1); const res = results[0]; - const objects = res.get("objects"); + const objects = res.get('objects'); expect(objects.length).toBe(5); objects.forEach(object => { - total -= object.get("key"); + total -= object.get('key'); }); expect(total).toBe(0); done(); }, () => { - fail("should not fail"); + fail('should not fail'); done(); } ); }); - it("properly includes array of mixed objects", done => { + it('properly includes array of mixed objects', done => { const objects = []; let total = 0; while (objects.length != 5) { - const object = new Parse.Object("AnObject"); - object.set("key", objects.length); + const object = new Parse.Object('AnObject'); + object.set('key', objects.length); total += objects.length; objects.push(object); } while (objects.length != 10) { - const object = new Parse.Object("AnotherObject"); - object.set("key", objects.length); + const object = new Parse.Object('AnotherObject'); + object.set('key', objects.length); total += objects.length; objects.push(object); } Parse.Object.saveAll(objects) .then(() => { - const object = new Parse.Object("AContainer"); - object.set("objects", objects); + const object = new Parse.Object('AContainer'); + object.set('objects', objects); return object.save(); }) .then(() => { - const query = new Parse.Query("AContainer"); - query.include("objects"); + const query = new Parse.Query('AContainer'); + query.include('objects'); return query.find(); }) .then( results => { expect(results.length).toBe(1); const res = results[0]; - const objects = res.get("objects"); + const objects = res.get('objects'); expect(objects.length).toBe(10); objects.forEach(object => { - total -= object.get("key"); + total -= object.get('key'); }); expect(total).toBe(0); done(); }, e => { - fail("should not fail"); + fail('should not fail'); fail(JSON.stringify(e)); done(); } ); }); - it("properly nested array of mixed objects with bad ids", done => { + it('properly nested array of mixed objects with bad ids', done => { const objects = []; let total = 0; while (objects.length != 5) { - const object = new Parse.Object("AnObject"); - object.set("key", objects.length); + const object = new Parse.Object('AnObject'); + object.set('key', objects.length); objects.push(object); } while (objects.length != 10) { - const object = new Parse.Object("AnotherObject"); - object.set("key", objects.length); + const object = new Parse.Object('AnotherObject'); + object.set('key', objects.length); objects.push(object); } Parse.Object.saveAll(objects) .then(() => { - const object = new Parse.Object("AContainer"); + const object = new Parse.Object('AContainer'); for (let i = 0; i < objects.length; i++) { if (i % 2 == 0) { - objects[i].id = "randomThing"; + objects[i].id = 'randomThing'; } else { - total += objects[i].get("key"); + total += objects[i].get('key'); } } - object.set("objects", objects); + object.set('objects', objects); return object.save(); }) .then(() => { - const query = new Parse.Query("AContainer"); - query.include("objects"); + const query = new Parse.Query('AContainer'); + query.include('objects'); return query.find(); }) .then( results => { expect(results.length).toBe(1); const res = results[0]; - const objects = res.get("objects"); + const objects = res.get('objects'); expect(objects.length).toBe(5); objects.forEach(object => { - total -= object.get("key"); + total -= object.get('key'); }); expect(total).toBe(0); done(); }, err => { jfail(err); - fail("should not fail"); + fail('should not fail'); done(); } ); }); - it("properly fetches nested pointers", done => { - const color = new Parse.Object("Color"); - color.set("hex", "#133733"); - const circle = new Parse.Object("Circle"); - circle.set("radius", 1337); + it('properly fetches nested pointers', done => { + const color = new Parse.Object('Color'); + color.set('hex', '#133733'); + const circle = new Parse.Object('Circle'); + circle.set('radius', 1337); Parse.Object.saveAll([color, circle]) .then(() => { - circle.set("color", color); - const badCircle = new Parse.Object("Circle"); - badCircle.id = "badId"; - const complexFigure = new Parse.Object("ComplexFigure"); - complexFigure.set("consistsOf", [circle, badCircle]); + circle.set('color', color); + const badCircle = new Parse.Object('Circle'); + badCircle.id = 'badId'; + const complexFigure = new Parse.Object('ComplexFigure'); + complexFigure.set('consistsOf', [circle, badCircle]); return complexFigure.save(); }) .then(() => { - const q = new Parse.Query("ComplexFigure"); - q.include("consistsOf.color"); + const q = new Parse.Query('ComplexFigure'); + q.include('consistsOf.color'); return q.find(); }) .then( results => { expect(results.length).toBe(1); const figure = results[0]; - expect(figure.get("consistsOf").length).toBe(1); - expect(figure.get("consistsOf")[0].get("color").get("hex")).toBe( - "#133733" + expect(figure.get('consistsOf').length).toBe(1); + expect(figure.get('consistsOf')[0].get('color').get('hex')).toBe( + '#133733' ); done(); }, () => { - fail("should not fail"); + fail('should not fail'); done(); } ); }); - it("result object creation uses current extension", function (done) { - const ParentObject = Parse.Object.extend({ className: "ParentObject" }); + it('result object creation uses current extension', function (done) { + const ParentObject = Parse.Object.extend({ className: 'ParentObject' }); // Add a foo() method to ChildObject. - let ChildObject = Parse.Object.extend("ChildObject", { + let ChildObject = Parse.Object.extend('ChildObject', { foo: function () { - return "foo"; + return 'foo'; }, }); const parent = new ParentObject(); const child = new ChildObject(); - parent.set("child", child); + parent.set('child', child); Parse.Object.saveAll([child, parent]).then(function () { // Add a bar() method to ChildObject. - ChildObject = Parse.Object.extend("ChildObject", { + ChildObject = Parse.Object.extend('ChildObject', { bar: function () { - return "bar"; + return 'bar'; }, }); const query = new Parse.Query(ParentObject); - query.include("child"); + query.include('child'); query.find().then(function (results) { equal(results.length, 1); const parentAgain = results[0]; - const childAgain = parentAgain.get("child"); - equal(childAgain.foo(), "foo"); - equal(childAgain.bar(), "bar"); + const childAgain = parentAgain.get('child'); + equal(childAgain.foo(), 'foo'); + equal(childAgain.bar(), 'bar'); done(); }); }); }); - it("matches query", function (done) { - const ParentObject = Parse.Object.extend("ParentObject"); - const ChildObject = Parse.Object.extend("ChildObject"); + it('matches query', function (done) { + const ParentObject = Parse.Object.extend('ParentObject'); + const ChildObject = Parse.Object.extend('ChildObject'); const objects = []; for (let i = 0; i < 10; ++i) { objects.push( @@ -2775,21 +2775,21 @@ describe("Parse.Query testing", () => { } Parse.Object.saveAll(objects).then(function () { const subQuery = new Parse.Query(ChildObject); - subQuery.greaterThan("x", 5); + subQuery.greaterThan('x', 5); const query = new Parse.Query(ParentObject); - query.matchesQuery("child", subQuery); + query.matchesQuery('child', subQuery); query.find().then(function (results) { equal(results.length, 4); for (const object of results) { - ok(object.get("x") > 15); + ok(object.get('x') > 15); } const query = new Parse.Query(ParentObject); - query.doesNotMatchQuery("child", subQuery); + query.doesNotMatchQuery('child', subQuery); query.find().then(function (results) { equal(results.length, 6); for (const object of results) { - ok(object.get("x") >= 10); - ok(object.get("x") <= 15); + ok(object.get('x') >= 10); + ok(object.get('x') <= 15); done(); } }); @@ -2797,49 +2797,49 @@ describe("Parse.Query testing", () => { }); }); - it("select query", function (done) { - const RestaurantObject = Parse.Object.extend("Restaurant"); - const PersonObject = Parse.Object.extend("Person"); + it('select query', function (done) { + const RestaurantObject = Parse.Object.extend('Restaurant'); + const PersonObject = Parse.Object.extend('Person'); const objects = [ - new RestaurantObject({ ratings: 5, location: "Djibouti" }), - new RestaurantObject({ ratings: 3, location: "Ouagadougou" }), - new PersonObject({ name: "Bob", hometown: "Djibouti" }), - new PersonObject({ name: "Tom", hometown: "Ouagadougou" }), - new PersonObject({ name: "Billy", hometown: "Detroit" }), + new RestaurantObject({ ratings: 5, location: 'Djibouti' }), + new RestaurantObject({ ratings: 3, location: 'Ouagadougou' }), + new PersonObject({ name: 'Bob', hometown: 'Djibouti' }), + new PersonObject({ name: 'Tom', hometown: 'Ouagadougou' }), + new PersonObject({ name: 'Billy', hometown: 'Detroit' }), ]; Parse.Object.saveAll(objects).then(function () { const query = new Parse.Query(RestaurantObject); - query.greaterThan("ratings", 4); + query.greaterThan('ratings', 4); const mainQuery = new Parse.Query(PersonObject); - mainQuery.matchesKeyInQuery("hometown", "location", query); + mainQuery.matchesKeyInQuery('hometown', 'location', query); mainQuery.find().then(function (results) { equal(results.length, 1); - equal(results[0].get("name"), "Bob"); + equal(results[0].get('name'), 'Bob'); done(); }); }); }); - it("$select inside $or", done => { - const Restaurant = Parse.Object.extend("Restaurant"); - const Person = Parse.Object.extend("Person"); + it('$select inside $or', done => { + const Restaurant = Parse.Object.extend('Restaurant'); + const Person = Parse.Object.extend('Person'); const objects = [ - new Restaurant({ ratings: 5, location: "Djibouti" }), - new Restaurant({ ratings: 3, location: "Ouagadougou" }), - new Person({ name: "Bob", hometown: "Djibouti" }), - new Person({ name: "Tom", hometown: "Ouagadougou" }), - new Person({ name: "Billy", hometown: "Detroit" }), + new Restaurant({ ratings: 5, location: 'Djibouti' }), + new Restaurant({ ratings: 3, location: 'Ouagadougou' }), + new Person({ name: 'Bob', hometown: 'Djibouti' }), + new Person({ name: 'Tom', hometown: 'Ouagadougou' }), + new Person({ name: 'Billy', hometown: 'Detroit' }), ]; Parse.Object.saveAll(objects) .then(() => { const subquery = new Parse.Query(Restaurant); - subquery.greaterThan("ratings", 4); + subquery.greaterThan('ratings', 4); const query1 = new Parse.Query(Person); - query1.matchesKeyInQuery("hometown", "location", subquery); + query1.matchesKeyInQuery('hometown', 'location', subquery); const query2 = new Parse.Query(Person); - query2.equalTo("name", "Tom"); + query2.equalTo('name', 'Tom'); const query = Parse.Query.or(query1, query2); return query.find(); }) @@ -2855,7 +2855,7 @@ describe("Parse.Query testing", () => { ); }); - it("$nor valid query", done => { + it('$nor valid query', done => { const objects = Array.from(Array(10).keys()).map(rating => { return new TestObject({ rating: rating }); }); @@ -2877,7 +2877,7 @@ describe("Parse.Query testing", () => { .then(() => { return request( Object.assign( - { url: Parse.serverURL + "/classes/TestObject" }, + { url: Parse.serverURL + '/classes/TestObject' }, options ) ); @@ -2894,7 +2894,7 @@ describe("Parse.Query testing", () => { }); }); - it("$nor invalid query - empty array", done => { + it('$nor invalid query - empty array', done => { const options = Object.assign({}, masterKeyOptions, { qs: { where: JSON.stringify({ $nor: [] }), @@ -2906,7 +2906,7 @@ describe("Parse.Query testing", () => { .then(() => { return request( Object.assign( - { url: Parse.serverURL + "/classes/TestObject" }, + { url: Parse.serverURL + '/classes/TestObject' }, options ) ); @@ -2918,7 +2918,7 @@ describe("Parse.Query testing", () => { }); }); - it("$nor invalid query - wrong type", done => { + it('$nor invalid query - wrong type', done => { const options = Object.assign({}, masterKeyOptions, { qs: { where: JSON.stringify({ $nor: 1337 }), @@ -2930,7 +2930,7 @@ describe("Parse.Query testing", () => { .then(() => { return request( Object.assign( - { url: Parse.serverURL + "/classes/TestObject" }, + { url: Parse.serverURL + '/classes/TestObject' }, options ) ); @@ -2942,73 +2942,73 @@ describe("Parse.Query testing", () => { }); }); - it("dontSelect query", function (done) { - const RestaurantObject = Parse.Object.extend("Restaurant"); - const PersonObject = Parse.Object.extend("Person"); + it('dontSelect query', function (done) { + const RestaurantObject = Parse.Object.extend('Restaurant'); + const PersonObject = Parse.Object.extend('Person'); const objects = [ - new RestaurantObject({ ratings: 5, location: "Djibouti" }), - new RestaurantObject({ ratings: 3, location: "Ouagadougou" }), - new PersonObject({ name: "Bob", hometown: "Djibouti" }), - new PersonObject({ name: "Tom", hometown: "Ouagadougou" }), - new PersonObject({ name: "Billy", hometown: "Djibouti" }), + new RestaurantObject({ ratings: 5, location: 'Djibouti' }), + new RestaurantObject({ ratings: 3, location: 'Ouagadougou' }), + new PersonObject({ name: 'Bob', hometown: 'Djibouti' }), + new PersonObject({ name: 'Tom', hometown: 'Ouagadougou' }), + new PersonObject({ name: 'Billy', hometown: 'Djibouti' }), ]; Parse.Object.saveAll(objects).then(function () { const query = new Parse.Query(RestaurantObject); - query.greaterThan("ratings", 4); + query.greaterThan('ratings', 4); const mainQuery = new Parse.Query(PersonObject); - mainQuery.doesNotMatchKeyInQuery("hometown", "location", query); + mainQuery.doesNotMatchKeyInQuery('hometown', 'location', query); mainQuery.find().then(function (results) { equal(results.length, 1); - equal(results[0].get("name"), "Tom"); + equal(results[0].get('name'), 'Tom'); done(); }); }); }); - it("dontSelect query without conditions", function (done) { - const RestaurantObject = Parse.Object.extend("Restaurant"); - const PersonObject = Parse.Object.extend("Person"); + it('dontSelect query without conditions', function (done) { + const RestaurantObject = Parse.Object.extend('Restaurant'); + const PersonObject = Parse.Object.extend('Person'); const objects = [ - new RestaurantObject({ location: "Djibouti" }), - new RestaurantObject({ location: "Ouagadougou" }), - new PersonObject({ name: "Bob", hometown: "Djibouti" }), - new PersonObject({ name: "Tom", hometown: "Yoloblahblahblah" }), - new PersonObject({ name: "Billy", hometown: "Ouagadougou" }), + new RestaurantObject({ location: 'Djibouti' }), + new RestaurantObject({ location: 'Ouagadougou' }), + new PersonObject({ name: 'Bob', hometown: 'Djibouti' }), + new PersonObject({ name: 'Tom', hometown: 'Yoloblahblahblah' }), + new PersonObject({ name: 'Billy', hometown: 'Ouagadougou' }), ]; Parse.Object.saveAll(objects).then(function () { const query = new Parse.Query(RestaurantObject); const mainQuery = new Parse.Query(PersonObject); - mainQuery.doesNotMatchKeyInQuery("hometown", "location", query); + mainQuery.doesNotMatchKeyInQuery('hometown', 'location', query); mainQuery.find().then(results => { equal(results.length, 1); - equal(results[0].get("name"), "Tom"); + equal(results[0].get('name'), 'Tom'); done(); }); }); }); - it("equalTo on same column as $dontSelect should not break $dontSelect functionality (#3678)", function (done) { - const AuthorObject = Parse.Object.extend("Author"); - const BlockedObject = Parse.Object.extend("Blocked"); - const PostObject = Parse.Object.extend("Post"); + it('equalTo on same column as $dontSelect should not break $dontSelect functionality (#3678)', function (done) { + const AuthorObject = Parse.Object.extend('Author'); + const BlockedObject = Parse.Object.extend('Blocked'); + const PostObject = Parse.Object.extend('Post'); let postAuthor = null; let requestUser = null; - return new AuthorObject({ name: "Julius" }) + return new AuthorObject({ name: 'Julius' }) .save() .then(user => { postAuthor = user; - return new AuthorObject({ name: "Bob" }).save(); + return new AuthorObject({ name: 'Bob' }).save(); }) .then(user => { requestUser = user; const objects = [ - new PostObject({ author: postAuthor, title: "Lorem ipsum" }), - new PostObject({ author: requestUser, title: "Kafka" }), - new PostObject({ author: requestUser, title: "Brown fox" }), + new PostObject({ author: postAuthor, title: 'Lorem ipsum' }), + new PostObject({ author: requestUser, title: 'Kafka' }), + new PostObject({ author: requestUser, title: 'Brown fox' }), new BlockedObject({ blockedBy: postAuthor, blockedUser: requestUser, @@ -3018,11 +3018,11 @@ describe("Parse.Query testing", () => { }) .then(() => { const banListQuery = new Parse.Query(BlockedObject); - banListQuery.equalTo("blockedUser", requestUser); + banListQuery.equalTo('blockedUser', requestUser); return new Parse.Query(PostObject) - .equalTo("author", postAuthor) - .doesNotMatchKeyInQuery("author", "blockedBy", banListQuery) + .equalTo('author', postAuthor) + .doesNotMatchKeyInQuery('author', 'blockedBy', banListQuery) .find() .then(r => { expect(r.length).toEqual(0); @@ -3031,44 +3031,44 @@ describe("Parse.Query testing", () => { }); }); - it("multiple dontSelect query", function (done) { - const RestaurantObject = Parse.Object.extend("Restaurant"); - const PersonObject = Parse.Object.extend("Person"); + it('multiple dontSelect query', function (done) { + const RestaurantObject = Parse.Object.extend('Restaurant'); + const PersonObject = Parse.Object.extend('Person'); const objects = [ - new RestaurantObject({ ratings: 7, location: "Djibouti2" }), - new RestaurantObject({ ratings: 5, location: "Djibouti" }), - new RestaurantObject({ ratings: 3, location: "Ouagadougou" }), - new PersonObject({ name: "Bob2", hometown: "Djibouti2" }), - new PersonObject({ name: "Bob", hometown: "Djibouti" }), - new PersonObject({ name: "Tom", hometown: "Ouagadougou" }), + new RestaurantObject({ ratings: 7, location: 'Djibouti2' }), + new RestaurantObject({ ratings: 5, location: 'Djibouti' }), + new RestaurantObject({ ratings: 3, location: 'Ouagadougou' }), + new PersonObject({ name: 'Bob2', hometown: 'Djibouti2' }), + new PersonObject({ name: 'Bob', hometown: 'Djibouti' }), + new PersonObject({ name: 'Tom', hometown: 'Ouagadougou' }), ]; Parse.Object.saveAll(objects).then(function () { const query = new Parse.Query(RestaurantObject); - query.greaterThan("ratings", 6); + query.greaterThan('ratings', 6); const query2 = new Parse.Query(RestaurantObject); - query2.lessThan("ratings", 4); + query2.lessThan('ratings', 4); const subQuery = new Parse.Query(PersonObject); - subQuery.matchesKeyInQuery("hometown", "location", query); + subQuery.matchesKeyInQuery('hometown', 'location', query); const subQuery2 = new Parse.Query(PersonObject); - subQuery2.matchesKeyInQuery("hometown", "location", query2); + subQuery2.matchesKeyInQuery('hometown', 'location', query2); const mainQuery = new Parse.Query(PersonObject); mainQuery.doesNotMatchKeyInQuery( - "objectId", - "objectId", + 'objectId', + 'objectId', Parse.Query.or(subQuery, subQuery2) ); mainQuery.find().then(function (results) { equal(results.length, 1); - equal(results[0].get("name"), "Bob"); + equal(results[0].get('name'), 'Bob'); done(); }); }); }); - it("include user", function (done) { - Parse.User.signUp("bob", "password", { age: 21 }).then(function (user) { - const TestObject = Parse.Object.extend("TestObject"); + it('include user', function (done) { + Parse.User.signUp('bob', 'password', { age: 21 }).then(function (user) { + const TestObject = Parse.Object.extend('TestObject'); const obj = new TestObject(); obj .save({ @@ -3076,33 +3076,33 @@ describe("Parse.Query testing", () => { }) .then(function (obj) { const query = new Parse.Query(TestObject); - query.include("owner"); + query.include('owner'); query.get(obj.id).then(function (objAgain) { equal(objAgain.id, obj.id); - ok(objAgain.get("owner") instanceof Parse.User); - equal(objAgain.get("owner").get("age"), 21); + ok(objAgain.get('owner') instanceof Parse.User); + equal(objAgain.get('owner').get('age'), 21); done(); }, done.fail); }, done.fail); }, done.fail); }); - it("or queries", function (done) { + it('or queries', function (done) { const objects = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(function (x) { - const object = new Parse.Object("BoxedNumber"); - object.set("x", x); + const object = new Parse.Object('BoxedNumber'); + object.set('x', x); return object; }); Parse.Object.saveAll(objects).then(function () { - const query1 = new Parse.Query("BoxedNumber"); - query1.lessThan("x", 2); - const query2 = new Parse.Query("BoxedNumber"); - query2.greaterThan("x", 5); + const query1 = new Parse.Query('BoxedNumber'); + query1.lessThan('x', 2); + const query2 = new Parse.Query('BoxedNumber'); + query2.greaterThan('x', 5); const orQuery = Parse.Query.or(query1, query2); orQuery.find().then(function (results) { equal(results.length, 6); for (const number of results) { - ok(number.get("x") < 2 || number.get("x") > 5); + ok(number.get('x') < 2 || number.get('x') > 5); } done(); }); @@ -3110,23 +3110,23 @@ describe("Parse.Query testing", () => { }); // This relies on matchesQuery aka the $inQuery operator - it("or complex queries", function (done) { + it('or complex queries', function (done) { const objects = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(function (x) { - const child = new Parse.Object("Child"); - child.set("x", x); - const parent = new Parse.Object("Parent"); - parent.set("child", child); - parent.set("y", x); + const child = new Parse.Object('Child'); + child.set('x', x); + const parent = new Parse.Object('Parent'); + parent.set('child', child); + parent.set('y', x); return parent; }); Parse.Object.saveAll(objects).then(function () { - const subQuery = new Parse.Query("Child"); - subQuery.equalTo("x", 4); - const query1 = new Parse.Query("Parent"); - query1.matchesQuery("child", subQuery); - const query2 = new Parse.Query("Parent"); - query2.lessThan("y", 2); + const subQuery = new Parse.Query('Child'); + subQuery.equalTo('x', 4); + const query1 = new Parse.Query('Parent'); + query1.matchesQuery('child', subQuery); + const query2 = new Parse.Query('Parent'); + query2.lessThan('y', 2); const orQuery = Parse.Query.or(query1, query2); orQuery.find().then(function (results) { equal(results.length, 3); @@ -3135,33 +3135,33 @@ describe("Parse.Query testing", () => { }); }); - it("async methods", function (done) { + it('async methods', function (done) { const saves = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(function (x) { - const obj = new Parse.Object("TestObject"); - obj.set("x", x + 1); + const obj = new Parse.Object('TestObject'); + obj.set('x', x + 1); return obj; }); Parse.Object.saveAll(saves) .then(function () { - const query = new Parse.Query("TestObject"); - query.ascending("x"); + const query = new Parse.Query('TestObject'); + query.ascending('x'); return query.first(); }) .then(function (obj) { - equal(obj.get("x"), 1); - const query = new Parse.Query("TestObject"); - query.descending("x"); + equal(obj.get('x'), 1); + const query = new Parse.Query('TestObject'); + query.descending('x'); return query.find(); }) .then(function (results) { equal(results.length, 10); - const query = new Parse.Query("TestObject"); + const query = new Parse.Query('TestObject'); return query.get(results[0].id); }) .then(function (obj1) { - equal(obj1.get("x"), 10); - const query = new Parse.Query("TestObject"); + equal(obj1.get('x'), 10); + const query = new Parse.Query('TestObject'); return query.count(); }) .then(function (count) { @@ -3172,25 +3172,25 @@ describe("Parse.Query testing", () => { }); }); - it("query.each", function (done) { + it('query.each', function (done) { const TOTAL = 50; const COUNT = 25; const items = range(TOTAL).map(function (x) { const obj = new TestObject(); - obj.set("x", x); + obj.set('x', x); return obj; }); Parse.Object.saveAll(items).then(function () { const query = new Parse.Query(TestObject); - query.lessThan("x", COUNT); + query.lessThan('x', COUNT); const seen = []; query .each( function (obj) { - seen[obj.get("x")] = (seen[obj.get("x")] || 0) + 1; + seen[obj.get('x')] = (seen[obj.get('x')] || 0) + 1; }, { batchSize: 10, @@ -3199,14 +3199,14 @@ describe("Parse.Query testing", () => { .then(function () { equal(seen.length, COUNT); for (let i = 0; i < COUNT; i++) { - equal(seen[i], 1, "Should have seen object number " + i); + equal(seen[i], 1, 'Should have seen object number ' + i); } done(); }, done.fail); }); }); - it("query.each async", function (done) { + it('query.each async', function (done) { const TOTAL = 50; const COUNT = 25; @@ -3214,7 +3214,7 @@ describe("Parse.Query testing", () => { const items = range(TOTAL).map(function (x) { const obj = new TestObject(); - obj.set("x", x); + obj.set('x', x); return obj; }); @@ -3223,12 +3223,12 @@ describe("Parse.Query testing", () => { Parse.Object.saveAll(items) .then(function () { const query = new Parse.Query(TestObject); - query.lessThan("x", COUNT); + query.lessThan('x', COUNT); return query.each( function (obj) { return new Promise(resolve => { process.nextTick(function () { - seen[obj.get("x")] = (seen[obj.get("x")] || 0) + 1; + seen[obj.get('x')] = (seen[obj.get('x')] || 0) + 1; resolve(); }); }); @@ -3241,19 +3241,19 @@ describe("Parse.Query testing", () => { .then(function () { equal(seen.length, COUNT); for (let i = 0; i < COUNT; i++) { - equal(seen[i], 1, "Should have seen object number " + i); + equal(seen[i], 1, 'Should have seen object number ' + i); } done(); }); }); - it("query.each fails with order", function (done) { + it('query.each fails with order', function (done) { const TOTAL = 50; const COUNT = 25; const items = range(TOTAL).map(function (x) { const obj = new TestObject(); - obj.set("x", x); + obj.set('x', x); return obj; }); @@ -3262,15 +3262,15 @@ describe("Parse.Query testing", () => { Parse.Object.saveAll(items) .then(function () { const query = new Parse.Query(TestObject); - query.lessThan("x", COUNT); - query.ascending("x"); + query.lessThan('x', COUNT); + query.ascending('x'); return query.each(function (obj) { - seen[obj.get("x")] = (seen[obj.get("x")] || 0) + 1; + seen[obj.get('x')] = (seen[obj.get('x')] || 0) + 1; }); }) .then( function () { - ok(false, "This should have failed."); + ok(false, 'This should have failed.'); done(); }, function () { @@ -3279,13 +3279,13 @@ describe("Parse.Query testing", () => { ); }); - it("query.each fails with skip", function (done) { + it('query.each fails with skip', function (done) { const TOTAL = 50; const COUNT = 25; const items = range(TOTAL).map(function (x) { const obj = new TestObject(); - obj.set("x", x); + obj.set('x', x); return obj; }); @@ -3294,15 +3294,15 @@ describe("Parse.Query testing", () => { Parse.Object.saveAll(items) .then(function () { const query = new Parse.Query(TestObject); - query.lessThan("x", COUNT); + query.lessThan('x', COUNT); query.skip(5); return query.each(function (obj) { - seen[obj.get("x")] = (seen[obj.get("x")] || 0) + 1; + seen[obj.get('x')] = (seen[obj.get('x')] || 0) + 1; }); }) .then( function () { - ok(false, "This should have failed."); + ok(false, 'This should have failed.'); done(); }, function () { @@ -3311,7 +3311,7 @@ describe("Parse.Query testing", () => { ); }); - it("query.each fails with limit", function (done) { + it('query.each fails with limit', function (done) { const TOTAL = 50; const COUNT = 25; @@ -3319,7 +3319,7 @@ describe("Parse.Query testing", () => { const items = range(TOTAL).map(function (x) { const obj = new TestObject(); - obj.set("x", x); + obj.set('x', x); return obj; }); @@ -3328,15 +3328,15 @@ describe("Parse.Query testing", () => { Parse.Object.saveAll(items) .then(function () { const query = new Parse.Query(TestObject); - query.lessThan("x", COUNT); + query.lessThan('x', COUNT); query.limit(5); return query.each(function (obj) { - seen[obj.get("x")] = (seen[obj.get("x")] || 0) + 1; + seen[obj.get('x')] = (seen[obj.get('x')] || 0) + 1; }); }) .then( function () { - ok(false, "This should have failed."); + ok(false, 'This should have failed.'); done(); }, function () { @@ -3345,54 +3345,54 @@ describe("Parse.Query testing", () => { ); }); - it("select keys query JS SDK", async () => { - const obj = new TestObject({ foo: "baz", bar: 1, qux: 2 }); + it('select keys query JS SDK', async () => { + const obj = new TestObject({ foo: 'baz', bar: 1, qux: 2 }); await obj.save(); obj._clearServerData(); const query1 = new Parse.Query(TestObject); - query1.select("foo"); + query1.select('foo'); const result1 = await query1.first(); - ok(result1.id, "expected object id to be set"); - ok(result1.createdAt, "expected object createdAt to be set"); - ok(result1.updatedAt, "expected object updatedAt to be set"); - ok(!result1.dirty(), "expected result not to be dirty"); - strictEqual(result1.get("foo"), "baz"); + ok(result1.id, 'expected object id to be set'); + ok(result1.createdAt, 'expected object createdAt to be set'); + ok(result1.updatedAt, 'expected object updatedAt to be set'); + ok(!result1.dirty(), 'expected result not to be dirty'); + strictEqual(result1.get('foo'), 'baz'); strictEqual( - result1.get("bar"), + result1.get('bar'), undefined, "expected 'bar' field to be unset" ); strictEqual( - result1.get("qux"), + result1.get('qux'), undefined, "expected 'qux' field to be unset" ); const result2 = await result1.fetch(); - strictEqual(result2.get("foo"), "baz"); - strictEqual(result2.get("bar"), 1); - strictEqual(result2.get("qux"), 2); + strictEqual(result2.get('foo'), 'baz'); + strictEqual(result2.get('bar'), 1); + strictEqual(result2.get('qux'), 2); obj._clearServerData(); const query2 = new Parse.Query(TestObject); query2.select(); const result3 = await query2.first(); - ok(result3.id, "expected object id to be set"); - ok(result3.createdAt, "expected object createdAt to be set"); - ok(result3.updatedAt, "expected object updatedAt to be set"); - ok(!result3.dirty(), "expected result not to be dirty"); + ok(result3.id, 'expected object id to be set'); + ok(result3.createdAt, 'expected object createdAt to be set'); + ok(result3.updatedAt, 'expected object updatedAt to be set'); + ok(!result3.dirty(), 'expected result not to be dirty'); strictEqual( - result3.get("foo"), + result3.get('foo'), undefined, "expected 'foo' field to be unset" ); strictEqual( - result3.get("bar"), + result3.get('bar'), undefined, "expected 'bar' field to be unset" ); strictEqual( - result3.get("qux"), + result3.get('qux'), undefined, "expected 'qux' field to be unset" ); @@ -3401,585 +3401,585 @@ describe("Parse.Query testing", () => { const query3 = new Parse.Query(TestObject); query3.select([]); const result4 = await query3.first(); - ok(result4.id, "expected object id to be set"); - ok(result4.createdAt, "expected object createdAt to be set"); - ok(result4.updatedAt, "expected object updatedAt to be set"); - ok(!result4.dirty(), "expected result not to be dirty"); + ok(result4.id, 'expected object id to be set'); + ok(result4.createdAt, 'expected object createdAt to be set'); + ok(result4.updatedAt, 'expected object updatedAt to be set'); + ok(!result4.dirty(), 'expected result not to be dirty'); strictEqual( - result4.get("foo"), + result4.get('foo'), undefined, "expected 'foo' field to be unset" ); strictEqual( - result4.get("bar"), + result4.get('bar'), undefined, "expected 'bar' field to be unset" ); strictEqual( - result4.get("qux"), + result4.get('qux'), undefined, "expected 'qux' field to be unset" ); obj._clearServerData(); const query4 = new Parse.Query(TestObject); - query4.select(["foo"]); + query4.select(['foo']); const result5 = await query4.first(); - ok(result5.id, "expected object id to be set"); - ok(result5.createdAt, "expected object createdAt to be set"); - ok(result5.updatedAt, "expected object updatedAt to be set"); - ok(!result5.dirty(), "expected result not to be dirty"); - strictEqual(result5.get("foo"), "baz"); + ok(result5.id, 'expected object id to be set'); + ok(result5.createdAt, 'expected object createdAt to be set'); + ok(result5.updatedAt, 'expected object updatedAt to be set'); + ok(!result5.dirty(), 'expected result not to be dirty'); + strictEqual(result5.get('foo'), 'baz'); strictEqual( - result5.get("bar"), + result5.get('bar'), undefined, "expected 'bar' field to be unset" ); strictEqual( - result5.get("qux"), + result5.get('qux'), undefined, "expected 'qux' field to be unset" ); obj._clearServerData(); const query5 = new Parse.Query(TestObject); - query5.select(["foo", "bar"]); + query5.select(['foo', 'bar']); const result6 = await query5.first(); - ok(result6.id, "expected object id to be set"); - ok(!result6.dirty(), "expected result not to be dirty"); - strictEqual(result6.get("foo"), "baz"); - strictEqual(result6.get("bar"), 1); + ok(result6.id, 'expected object id to be set'); + ok(!result6.dirty(), 'expected result not to be dirty'); + strictEqual(result6.get('foo'), 'baz'); + strictEqual(result6.get('bar'), 1); strictEqual( - result6.get("qux"), + result6.get('qux'), undefined, "expected 'qux' field to be unset" ); obj._clearServerData(); const query6 = new Parse.Query(TestObject); - query6.select(["foo", "bar", "qux"]); + query6.select(['foo', 'bar', 'qux']); const result7 = await query6.first(); - ok(result7.id, "expected object id to be set"); - ok(!result7.dirty(), "expected result not to be dirty"); - strictEqual(result7.get("foo"), "baz"); - strictEqual(result7.get("bar"), 1); - strictEqual(result7.get("qux"), 2); + ok(result7.id, 'expected object id to be set'); + ok(!result7.dirty(), 'expected result not to be dirty'); + strictEqual(result7.get('foo'), 'baz'); + strictEqual(result7.get('bar'), 1); + strictEqual(result7.get('qux'), 2); obj._clearServerData(); const query7 = new Parse.Query(TestObject); - query7.select("foo", "bar"); + query7.select('foo', 'bar'); const result8 = await query7.first(); - ok(result8.id, "expected object id to be set"); - ok(!result8.dirty(), "expected result not to be dirty"); - strictEqual(result8.get("foo"), "baz"); - strictEqual(result8.get("bar"), 1); + ok(result8.id, 'expected object id to be set'); + ok(!result8.dirty(), 'expected result not to be dirty'); + strictEqual(result8.get('foo'), 'baz'); + strictEqual(result8.get('bar'), 1); strictEqual( - result8.get("qux"), + result8.get('qux'), undefined, "expected 'qux' field to be unset" ); obj._clearServerData(); const query8 = new Parse.Query(TestObject); - query8.select("foo", "bar", "qux"); + query8.select('foo', 'bar', 'qux'); const result9 = await query8.first(); - ok(result9.id, "expected object id to be set"); - ok(!result9.dirty(), "expected result not to be dirty"); - strictEqual(result9.get("foo"), "baz"); - strictEqual(result9.get("bar"), 1); - strictEqual(result9.get("qux"), 2); + ok(result9.id, 'expected object id to be set'); + ok(!result9.dirty(), 'expected result not to be dirty'); + strictEqual(result9.get('foo'), 'baz'); + strictEqual(result9.get('bar'), 1); + strictEqual(result9.get('qux'), 2); }); - it("select keys (arrays)", async () => { - const obj = new TestObject({ foo: "baz", bar: 1, hello: "world" }); + it('select keys (arrays)', async () => { + const obj = new TestObject({ foo: 'baz', bar: 1, hello: 'world' }); await obj.save(); const response = await request({ - url: Parse.serverURL + "/classes/TestObject", + url: Parse.serverURL + '/classes/TestObject', qs: { - keys: "hello", + keys: 'hello', where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); expect(response.data.results[0].foo).toBeUndefined(); expect(response.data.results[0].bar).toBeUndefined(); - expect(response.data.results[0].hello).toBe("world"); + expect(response.data.results[0].hello).toBe('world'); const response2 = await request({ - url: Parse.serverURL + "/classes/TestObject", + url: Parse.serverURL + '/classes/TestObject', qs: { - keys: ["foo", "hello"], + keys: ['foo', 'hello'], where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); - expect(response2.data.results[0].foo).toBe("baz"); + expect(response2.data.results[0].foo).toBe('baz'); expect(response2.data.results[0].bar).toBeUndefined(); - expect(response2.data.results[0].hello).toBe("world"); + expect(response2.data.results[0].hello).toBe('world'); const response3 = await request({ - url: Parse.serverURL + "/classes/TestObject", + url: Parse.serverURL + '/classes/TestObject', qs: { - keys: ["foo", "bar", "hello"], + keys: ['foo', 'bar', 'hello'], where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); - expect(response3.data.results[0].foo).toBe("baz"); + expect(response3.data.results[0].foo).toBe('baz'); expect(response3.data.results[0].bar).toBe(1); - expect(response3.data.results[0].hello).toBe("world"); + expect(response3.data.results[0].hello).toBe('world'); const response4 = await request({ - url: Parse.serverURL + "/classes/TestObject", + url: Parse.serverURL + '/classes/TestObject', qs: { - keys: [""], + keys: [''], where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); - ok(response4.data.results[0].objectId, "expected objectId to be set"); + ok(response4.data.results[0].objectId, 'expected objectId to be set'); ok( response4.data.results[0].createdAt, - "expected object createdAt to be set" + 'expected object createdAt to be set' ); ok( response4.data.results[0].updatedAt, - "expected object updatedAt to be set" + 'expected object updatedAt to be set' ); expect(response4.data.results[0].foo).toBeUndefined(); expect(response4.data.results[0].bar).toBeUndefined(); expect(response4.data.results[0].hello).toBeUndefined(); const response5 = await request({ - url: Parse.serverURL + "/classes/TestObject", + url: Parse.serverURL + '/classes/TestObject', qs: { keys: [], where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); - ok(response5.data.results[0].objectId, "expected objectId to be set"); + ok(response5.data.results[0].objectId, 'expected objectId to be set'); ok( response5.data.results[0].createdAt, - "expected object createdAt to be set" + 'expected object createdAt to be set' ); ok( response5.data.results[0].updatedAt, - "expected object updatedAt to be set" + 'expected object updatedAt to be set' ); - expect(response5.data.results[0].foo).toBe("baz"); + expect(response5.data.results[0].foo).toBe('baz'); expect(response5.data.results[0].bar).toBe(1); - expect(response5.data.results[0].hello).toBe("world"); + expect(response5.data.results[0].hello).toBe('world'); }); - it("select keys (strings)", async () => { - const obj = new TestObject({ foo: "baz", bar: 1, hello: "world" }); + it('select keys (strings)', async () => { + const obj = new TestObject({ foo: 'baz', bar: 1, hello: 'world' }); await obj.save(); const response = await request({ - url: Parse.serverURL + "/classes/TestObject", + url: Parse.serverURL + '/classes/TestObject', qs: { - keys: "", + keys: '', where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); - ok(response.data.results[0].objectId, "expected objectId to be set"); + ok(response.data.results[0].objectId, 'expected objectId to be set'); ok( response.data.results[0].createdAt, - "expected object createdAt to be set" + 'expected object createdAt to be set' ); ok( response.data.results[0].updatedAt, - "expected object updatedAt to be set" + 'expected object updatedAt to be set' ); expect(response.data.results[0].foo).toBeUndefined(); expect(response.data.results[0].bar).toBeUndefined(); expect(response.data.results[0].hello).toBeUndefined(); const response2 = await request({ - url: Parse.serverURL + "/classes/TestObject", + url: Parse.serverURL + '/classes/TestObject', qs: { keys: '["foo", "hello"]', where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); - ok(response2.data.results[0].objectId, "expected objectId to be set"); + ok(response2.data.results[0].objectId, 'expected objectId to be set'); ok( response2.data.results[0].createdAt, - "expected object createdAt to be set" + 'expected object createdAt to be set' ); ok( response2.data.results[0].updatedAt, - "expected object updatedAt to be set" + 'expected object updatedAt to be set' ); - expect(response2.data.results[0].foo).toBe("baz"); + expect(response2.data.results[0].foo).toBe('baz'); expect(response2.data.results[0].bar).toBeUndefined(); - expect(response2.data.results[0].hello).toBe("world"); + expect(response2.data.results[0].hello).toBe('world'); const response3 = await request({ - url: Parse.serverURL + "/classes/TestObject", + url: Parse.serverURL + '/classes/TestObject', qs: { keys: '["foo", "bar", "hello"]', where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); - ok(response3.data.results[0].objectId, "expected objectId to be set"); + ok(response3.data.results[0].objectId, 'expected objectId to be set'); ok( response3.data.results[0].createdAt, - "expected object createdAt to be set" + 'expected object createdAt to be set' ); ok( response3.data.results[0].updatedAt, - "expected object updatedAt to be set" + 'expected object updatedAt to be set' ); - expect(response3.data.results[0].foo).toBe("baz"); + expect(response3.data.results[0].foo).toBe('baz'); expect(response3.data.results[0].bar).toBe(1); - expect(response3.data.results[0].hello).toBe("world"); + expect(response3.data.results[0].hello).toBe('world'); }); - it("exclude keys query JS SDK", async () => { - const obj = new TestObject({ foo: "baz", bar: 1, qux: 2 }); + it('exclude keys query JS SDK', async () => { + const obj = new TestObject({ foo: 'baz', bar: 1, qux: 2 }); await obj.save(); obj._clearServerData(); const query1 = new Parse.Query(TestObject); - query1.exclude("foo"); + query1.exclude('foo'); const result1 = await query1.first(); - ok(result1.id, "expected object id to be set"); - ok(result1.createdAt, "expected object createdAt to be set"); - ok(result1.updatedAt, "expected object updatedAt to be set"); - ok(!result1.dirty(), "expected result not to be dirty"); + ok(result1.id, 'expected object id to be set'); + ok(result1.createdAt, 'expected object createdAt to be set'); + ok(result1.updatedAt, 'expected object updatedAt to be set'); + ok(!result1.dirty(), 'expected result not to be dirty'); strictEqual( - result1.get("foo"), + result1.get('foo'), undefined, "expected 'bar' field to be unset" ); - strictEqual(result1.get("bar"), 1); - strictEqual(result1.get("qux"), 2); + strictEqual(result1.get('bar'), 1); + strictEqual(result1.get('qux'), 2); const result2 = await result1.fetch(); - strictEqual(result2.get("foo"), "baz"); - strictEqual(result2.get("bar"), 1); - strictEqual(result2.get("qux"), 2); + strictEqual(result2.get('foo'), 'baz'); + strictEqual(result2.get('bar'), 1); + strictEqual(result2.get('qux'), 2); obj._clearServerData(); const query2 = new Parse.Query(TestObject); query2.exclude(); const result3 = await query2.first(); - ok(result3.id, "expected object id to be set"); - ok(result3.createdAt, "expected object createdAt to be set"); - ok(result3.updatedAt, "expected object updatedAt to be set"); - ok(!result3.dirty(), "expected result not to be dirty"); - strictEqual(result3.get("foo"), "baz"); - strictEqual(result3.get("bar"), 1); - strictEqual(result3.get("qux"), 2); + ok(result3.id, 'expected object id to be set'); + ok(result3.createdAt, 'expected object createdAt to be set'); + ok(result3.updatedAt, 'expected object updatedAt to be set'); + ok(!result3.dirty(), 'expected result not to be dirty'); + strictEqual(result3.get('foo'), 'baz'); + strictEqual(result3.get('bar'), 1); + strictEqual(result3.get('qux'), 2); obj._clearServerData(); const query3 = new Parse.Query(TestObject); query3.exclude([]); const result4 = await query3.first(); - ok(result4.id, "expected object id to be set"); - ok(result4.createdAt, "expected object createdAt to be set"); - ok(result4.updatedAt, "expected object updatedAt to be set"); - ok(!result4.dirty(), "expected result not to be dirty"); - strictEqual(result4.get("foo"), "baz"); - strictEqual(result4.get("bar"), 1); - strictEqual(result4.get("qux"), 2); + ok(result4.id, 'expected object id to be set'); + ok(result4.createdAt, 'expected object createdAt to be set'); + ok(result4.updatedAt, 'expected object updatedAt to be set'); + ok(!result4.dirty(), 'expected result not to be dirty'); + strictEqual(result4.get('foo'), 'baz'); + strictEqual(result4.get('bar'), 1); + strictEqual(result4.get('qux'), 2); obj._clearServerData(); const query4 = new Parse.Query(TestObject); - query4.exclude(["foo"]); + query4.exclude(['foo']); const result5 = await query4.first(); - ok(result5.id, "expected object id to be set"); - ok(result5.createdAt, "expected object createdAt to be set"); - ok(result5.updatedAt, "expected object updatedAt to be set"); - ok(!result5.dirty(), "expected result not to be dirty"); + ok(result5.id, 'expected object id to be set'); + ok(result5.createdAt, 'expected object createdAt to be set'); + ok(result5.updatedAt, 'expected object updatedAt to be set'); + ok(!result5.dirty(), 'expected result not to be dirty'); strictEqual( - result5.get("foo"), + result5.get('foo'), undefined, "expected 'bar' field to be unset" ); - strictEqual(result5.get("bar"), 1); - strictEqual(result5.get("qux"), 2); + strictEqual(result5.get('bar'), 1); + strictEqual(result5.get('qux'), 2); obj._clearServerData(); const query5 = new Parse.Query(TestObject); - query5.exclude(["foo", "bar"]); + query5.exclude(['foo', 'bar']); const result6 = await query5.first(); - ok(result6.id, "expected object id to be set"); - ok(!result6.dirty(), "expected result not to be dirty"); + ok(result6.id, 'expected object id to be set'); + ok(!result6.dirty(), 'expected result not to be dirty'); strictEqual( - result6.get("foo"), + result6.get('foo'), undefined, "expected 'bar' field to be unset" ); strictEqual( - result6.get("bar"), + result6.get('bar'), undefined, "expected 'bar' field to be unset" ); - strictEqual(result6.get("qux"), 2); + strictEqual(result6.get('qux'), 2); obj._clearServerData(); const query6 = new Parse.Query(TestObject); - query6.exclude(["foo", "bar", "qux"]); + query6.exclude(['foo', 'bar', 'qux']); const result7 = await query6.first(); - ok(result7.id, "expected object id to be set"); - ok(!result7.dirty(), "expected result not to be dirty"); + ok(result7.id, 'expected object id to be set'); + ok(!result7.dirty(), 'expected result not to be dirty'); strictEqual( - result7.get("foo"), + result7.get('foo'), undefined, "expected 'bar' field to be unset" ); strictEqual( - result7.get("bar"), + result7.get('bar'), undefined, "expected 'bar' field to be unset" ); strictEqual( - result7.get("qux"), + result7.get('qux'), undefined, "expected 'bar' field to be unset" ); obj._clearServerData(); const query7 = new Parse.Query(TestObject); - query7.exclude("foo"); + query7.exclude('foo'); const result8 = await query7.first(); - ok(result8.id, "expected object id to be set"); - ok(!result8.dirty(), "expected result not to be dirty"); + ok(result8.id, 'expected object id to be set'); + ok(!result8.dirty(), 'expected result not to be dirty'); strictEqual( - result8.get("foo"), + result8.get('foo'), undefined, "expected 'bar' field to be unset" ); - strictEqual(result8.get("bar"), 1); - strictEqual(result8.get("qux"), 2); + strictEqual(result8.get('bar'), 1); + strictEqual(result8.get('qux'), 2); obj._clearServerData(); const query8 = new Parse.Query(TestObject); - query8.exclude("foo", "bar"); + query8.exclude('foo', 'bar'); const result9 = await query8.first(); - ok(result9.id, "expected object id to be set"); - ok(!result9.dirty(), "expected result not to be dirty"); + ok(result9.id, 'expected object id to be set'); + ok(!result9.dirty(), 'expected result not to be dirty'); strictEqual( - result9.get("foo"), + result9.get('foo'), undefined, "expected 'bar' field to be unset" ); strictEqual( - result9.get("bar"), + result9.get('bar'), undefined, "expected 'bar' field to be unset" ); - strictEqual(result9.get("qux"), 2); + strictEqual(result9.get('qux'), 2); obj._clearServerData(); const query9 = new Parse.Query(TestObject); - query9.exclude("foo", "bar", "qux"); + query9.exclude('foo', 'bar', 'qux'); const result10 = await query9.first(); - ok(result10.id, "expected object id to be set"); - ok(!result10.dirty(), "expected result not to be dirty"); + ok(result10.id, 'expected object id to be set'); + ok(!result10.dirty(), 'expected result not to be dirty'); strictEqual( - result10.get("foo"), + result10.get('foo'), undefined, "expected 'bar' field to be unset" ); strictEqual( - result10.get("bar"), + result10.get('bar'), undefined, "expected 'bar' field to be unset" ); strictEqual( - result10.get("qux"), + result10.get('qux'), undefined, "expected 'bar' field to be unset" ); }); - it("exclude keys (arrays)", async () => { - const obj = new TestObject({ foo: "baz", hello: "world" }); + it('exclude keys (arrays)', async () => { + const obj = new TestObject({ foo: 'baz', hello: 'world' }); await obj.save(); const response = await request({ - url: Parse.serverURL + "/classes/TestObject", + url: Parse.serverURL + '/classes/TestObject', qs: { - excludeKeys: ["foo"], + excludeKeys: ['foo'], where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); - ok(response.data.results[0].objectId, "expected objectId to be set"); + ok(response.data.results[0].objectId, 'expected objectId to be set'); ok( response.data.results[0].createdAt, - "expected object createdAt to be set" + 'expected object createdAt to be set' ); ok( response.data.results[0].updatedAt, - "expected object updatedAt to be set" + 'expected object updatedAt to be set' ); expect(response.data.results[0].foo).toBeUndefined(); - expect(response.data.results[0].hello).toBe("world"); + expect(response.data.results[0].hello).toBe('world'); const response2 = await request({ - url: Parse.serverURL + "/classes/TestObject", + url: Parse.serverURL + '/classes/TestObject', qs: { - excludeKeys: ["foo", "hello"], + excludeKeys: ['foo', 'hello'], where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); - ok(response2.data.results[0].objectId, "expected objectId to be set"); + ok(response2.data.results[0].objectId, 'expected objectId to be set'); ok( response2.data.results[0].createdAt, - "expected object createdAt to be set" + 'expected object createdAt to be set' ); ok( response2.data.results[0].updatedAt, - "expected object updatedAt to be set" + 'expected object updatedAt to be set' ); expect(response2.data.results[0].foo).toBeUndefined(); expect(response2.data.results[0].hello).toBeUndefined(); const response3 = await request({ - url: Parse.serverURL + "/classes/TestObject", + url: Parse.serverURL + '/classes/TestObject', qs: { excludeKeys: [], where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); - ok(response3.data.results[0].objectId, "expected objectId to be set"); + ok(response3.data.results[0].objectId, 'expected objectId to be set'); ok( response3.data.results[0].createdAt, - "expected object createdAt to be set" + 'expected object createdAt to be set' ); ok( response3.data.results[0].updatedAt, - "expected object updatedAt to be set" + 'expected object updatedAt to be set' ); - expect(response3.data.results[0].foo).toBe("baz"); - expect(response3.data.results[0].hello).toBe("world"); + expect(response3.data.results[0].foo).toBe('baz'); + expect(response3.data.results[0].hello).toBe('world'); const response4 = await request({ - url: Parse.serverURL + "/classes/TestObject", + url: Parse.serverURL + '/classes/TestObject', qs: { - excludeKeys: [""], + excludeKeys: [''], where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); - ok(response4.data.results[0].objectId, "expected objectId to be set"); + ok(response4.data.results[0].objectId, 'expected objectId to be set'); ok( response4.data.results[0].createdAt, - "expected object createdAt to be set" + 'expected object createdAt to be set' ); ok( response4.data.results[0].updatedAt, - "expected object updatedAt to be set" + 'expected object updatedAt to be set' ); - expect(response4.data.results[0].foo).toBe("baz"); - expect(response4.data.results[0].hello).toBe("world"); + expect(response4.data.results[0].foo).toBe('baz'); + expect(response4.data.results[0].hello).toBe('world'); }); - it("exclude keys (strings)", async () => { - const obj = new TestObject({ foo: "baz", hello: "world" }); + it('exclude keys (strings)', async () => { + const obj = new TestObject({ foo: 'baz', hello: 'world' }); await obj.save(); const response = await request({ - url: Parse.serverURL + "/classes/TestObject", + url: Parse.serverURL + '/classes/TestObject', qs: { - excludeKeys: "foo", + excludeKeys: 'foo', where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); - ok(response.data.results[0].objectId, "expected objectId to be set"); + ok(response.data.results[0].objectId, 'expected objectId to be set'); ok( response.data.results[0].createdAt, - "expected object createdAt to be set" + 'expected object createdAt to be set' ); ok( response.data.results[0].updatedAt, - "expected object updatedAt to be set" + 'expected object updatedAt to be set' ); expect(response.data.results[0].foo).toBeUndefined(); - expect(response.data.results[0].hello).toBe("world"); + expect(response.data.results[0].hello).toBe('world'); const response2 = await request({ - url: Parse.serverURL + "/classes/TestObject", + url: Parse.serverURL + '/classes/TestObject', qs: { - excludeKeys: "", + excludeKeys: '', where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); - ok(response2.data.results[0].objectId, "expected objectId to be set"); + ok(response2.data.results[0].objectId, 'expected objectId to be set'); ok( response2.data.results[0].createdAt, - "expected object createdAt to be set" + 'expected object createdAt to be set' ); ok( response2.data.results[0].updatedAt, - "expected object updatedAt to be set" + 'expected object updatedAt to be set' ); - expect(response2.data.results[0].foo).toBe("baz"); - expect(response2.data.results[0].hello).toBe("world"); + expect(response2.data.results[0].foo).toBe('baz'); + expect(response2.data.results[0].hello).toBe('world'); const response3 = await request({ - url: Parse.serverURL + "/classes/TestObject", + url: Parse.serverURL + '/classes/TestObject', qs: { excludeKeys: '["hello"]', where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); - ok(response3.data.results[0].objectId, "expected objectId to be set"); + ok(response3.data.results[0].objectId, 'expected objectId to be set'); ok( response3.data.results[0].createdAt, - "expected object createdAt to be set" + 'expected object createdAt to be set' ); ok( response3.data.results[0].updatedAt, - "expected object updatedAt to be set" + 'expected object updatedAt to be set' ); - expect(response3.data.results[0].foo).toBe("baz"); + expect(response3.data.results[0].foo).toBe('baz'); expect(response3.data.results[0].hello).toBeUndefined(); const response4 = await request({ - url: Parse.serverURL + "/classes/TestObject", + url: Parse.serverURL + '/classes/TestObject', qs: { excludeKeys: '["foo", "hello"]', where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); - ok(response4.data.results[0].objectId, "expected objectId to be set"); + ok(response4.data.results[0].objectId, 'expected objectId to be set'); ok( response4.data.results[0].createdAt, - "expected object createdAt to be set" + 'expected object createdAt to be set' ); ok( response4.data.results[0].updatedAt, - "expected object updatedAt to be set" + 'expected object updatedAt to be set' ); expect(response4.data.results[0].foo).toBeUndefined(); expect(response4.data.results[0].hello).toBeUndefined(); }); - it("exclude keys with select same key", async () => { - const obj = new TestObject({ foo: "baz", hello: "world" }); + it('exclude keys with select same key', async () => { + const obj = new TestObject({ foo: 'baz', hello: 'world' }); await obj.save(); const response = await request({ - url: Parse.serverURL + "/classes/TestObject", + url: Parse.serverURL + '/classes/TestObject', qs: { - keys: "foo", - excludeKeys: "foo", + keys: 'foo', + excludeKeys: 'foo', where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, @@ -3988,106 +3988,106 @@ describe("Parse.Query testing", () => { expect(response.data.results[0].hello).toBeUndefined(); }); - it("exclude keys with select different key", async () => { - const obj = new TestObject({ foo: "baz", hello: "world" }); + it('exclude keys with select different key', async () => { + const obj = new TestObject({ foo: 'baz', hello: 'world' }); await obj.save(); const response = await request({ - url: Parse.serverURL + "/classes/TestObject", + url: Parse.serverURL + '/classes/TestObject', qs: { - keys: "foo,hello", - excludeKeys: "foo", + keys: 'foo,hello', + excludeKeys: 'foo', where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); expect(response.data.results[0].foo).toBeUndefined(); - expect(response.data.results[0].hello).toBe("world"); + expect(response.data.results[0].hello).toBe('world'); }); - it("exclude keys with include same key", async () => { + it('exclude keys with include same key', async () => { const pointer = new TestObject(); await pointer.save(); - const obj = new TestObject({ child: pointer, hello: "world" }); + const obj = new TestObject({ child: pointer, hello: 'world' }); await obj.save(); const response = await request({ - url: Parse.serverURL + "/classes/TestObject", + url: Parse.serverURL + '/classes/TestObject', qs: { - include: "child", - excludeKeys: "child", + include: 'child', + excludeKeys: 'child', where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); expect(response.data.results[0].child).toBeUndefined(); - expect(response.data.results[0].hello).toBe("world"); + expect(response.data.results[0].hello).toBe('world'); }); - it("exclude keys with include different key", async () => { + it('exclude keys with include different key', async () => { const pointer = new TestObject(); await pointer.save(); const obj = new TestObject({ child1: pointer, child2: pointer, - hello: "world", + hello: 'world', }); await obj.save(); const response = await request({ - url: Parse.serverURL + "/classes/TestObject", + url: Parse.serverURL + '/classes/TestObject', qs: { - include: "child1,child2", - excludeKeys: "child1", + include: 'child1,child2', + excludeKeys: 'child1', where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); expect(response.data.results[0].child1).toBeUndefined(); expect(response.data.results[0].child2.objectId).toEqual(pointer.id); - expect(response.data.results[0].hello).toBe("world"); + expect(response.data.results[0].hello).toBe('world'); }); - it("exclude keys with includeAll", async () => { + it('exclude keys with includeAll', async () => { const pointer = new TestObject(); await pointer.save(); const obj = new TestObject({ child1: pointer, child2: pointer, - hello: "world", + hello: 'world', }); await obj.save(); const response = await request({ - url: Parse.serverURL + "/classes/TestObject", + url: Parse.serverURL + '/classes/TestObject', qs: { includeAll: true, - excludeKeys: "child1", + excludeKeys: 'child1', where: JSON.stringify({ objectId: obj.id }), }, headers: masterKeyHeaders, }); expect(response.data.results[0].child).toBeUndefined(); expect(response.data.results[0].child2.objectId).toEqual(pointer.id); - expect(response.data.results[0].hello).toBe("world"); + expect(response.data.results[0].hello).toBe('world'); }); - it("select keys with each query", function (done) { - const obj = new TestObject({ foo: "baz", bar: 1 }); + it('select keys with each query', function (done) { + const obj = new TestObject({ foo: 'baz', bar: 1 }); obj.save().then(function () { obj._clearServerData(); const query = new Parse.Query(TestObject); - query.select("foo"); + query.select('foo'); query .each(function (result) { - ok(result.id, "expected object id to be set"); - ok(result.createdAt, "expected object createdAt to be set"); - ok(result.updatedAt, "expected object updatedAt to be set"); - ok(!result.dirty(), "expected result not to be dirty"); - strictEqual(result.get("foo"), "baz"); + ok(result.id, 'expected object id to be set'); + ok(result.createdAt, 'expected object createdAt to be set'); + ok(result.updatedAt, 'expected object updatedAt to be set'); + ok(!result.dirty(), 'expected result not to be dirty'); + strictEqual(result.get('foo'), 'baz'); strictEqual( - result.get("bar"), + result.get('bar'), undefined, 'expected "bar" field to be unset' ); @@ -4104,21 +4104,21 @@ describe("Parse.Query testing", () => { }); }); - it_id("56b09b92-c756-4bae-8c32-1c32b5b4c397")(it)( - "notEqual with array of pointers", + it_id('56b09b92-c756-4bae-8c32-1c32b5b4c397')(it)( + 'notEqual with array of pointers', done => { const children = []; const parents = []; const promises = []; for (let i = 0; i < 2; i++) { const proc = iter => { - const child = new Parse.Object("Child"); + const child = new Parse.Object('Child'); children.push(child); - const parent = new Parse.Object("Parent"); + const parent = new Parse.Object('Parent'); parents.push(parent); promises.push( child.save().then(() => { - parents[iter].set("child", [children[iter]]); + parents[iter].set('child', [children[iter]]); return parents[iter].save(); }) ); @@ -4127,8 +4127,8 @@ describe("Parse.Query testing", () => { } Promise.all(promises) .then(() => { - const query = new Parse.Query("Parent"); - query.notEqualTo("child", children[0]); + const query = new Parse.Query('Parent'); + query.notEqualTo('child', children[0]); return query.find(); }) .then(results => { @@ -4143,37 +4143,37 @@ describe("Parse.Query testing", () => { ); // PG don't support creating a null column - it_exclude_dbs(["postgres"])("querying for null value", done => { - const obj = new Parse.Object("TestObject"); - obj.set("aNull", null); + it_exclude_dbs(['postgres'])('querying for null value', done => { + const obj = new Parse.Object('TestObject'); + obj.set('aNull', null); obj .save() .then(() => { - const query = new Parse.Query("TestObject"); - query.equalTo("aNull", null); + const query = new Parse.Query('TestObject'); + query.equalTo('aNull', null); return query.find(); }) .then(results => { expect(results.length).toEqual(1); - expect(results[0].get("aNull")).toEqual(null); + expect(results[0].get('aNull')).toEqual(null); done(); }); }); - it("query within dictionary", done => { + it('query within dictionary', done => { const promises = []; for (let i = 0; i < 2; i++) { const proc = iter => { - const obj = new Parse.Object("TestObject"); - obj.set("aDict", { x: iter + 1, y: iter + 2 }); + const obj = new Parse.Object('TestObject'); + obj.set('aDict', { x: iter + 1, y: iter + 2 }); promises.push(obj.save()); }; proc(i); } Promise.all(promises) .then(() => { - const query = new Parse.Query("TestObject"); - query.equalTo("aDict.x", 1); + const query = new Parse.Query('TestObject'); + query.equalTo('aDict.x', 1); return query.find(); }) .then( @@ -4187,52 +4187,52 @@ describe("Parse.Query testing", () => { ); }); - it("supports include on the wrong key type (#2262)", function (done) { - const childObject = new Parse.Object("TestChildObject"); - childObject.set("hello", "world"); + it('supports include on the wrong key type (#2262)', function (done) { + const childObject = new Parse.Object('TestChildObject'); + childObject.set('hello', 'world'); childObject .save() .then(() => { - const obj = new Parse.Object("TestObject"); - obj.set("foo", "bar"); - obj.set("child", childObject); + const obj = new Parse.Object('TestObject'); + obj.set('foo', 'bar'); + obj.set('child', childObject); return obj.save(); }) .then(() => { - const q = new Parse.Query("TestObject"); - q.include("child"); - q.include("child.parent"); - q.include("createdAt"); - q.include("createdAt.createdAt"); + const q = new Parse.Query('TestObject'); + q.include('child'); + q.include('child.parent'); + q.include('createdAt'); + q.include('createdAt.createdAt'); return q.find(); }) .then( objs => { expect(objs.length).toBe(1); - expect(objs[0].get("child").get("hello")).toEqual("world"); + expect(objs[0].get('child').get('hello')).toEqual('world'); expect(objs[0].createdAt instanceof Date).toBe(true); done(); }, () => { - fail("should not fail"); + fail('should not fail'); done(); } ); }); - it("query match on array with single object", done => { + it('query match on array with single object', done => { const target = { - __type: "Pointer", - className: "TestObject", - objectId: "abc123", + __type: 'Pointer', + className: 'TestObject', + objectId: 'abc123', }; - const obj = new Parse.Object("TestObject"); - obj.set("someObjs", [target]); + const obj = new Parse.Object('TestObject'); + obj.set('someObjs', [target]); obj .save() .then(() => { - const query = new Parse.Query("TestObject"); - query.equalTo("someObjs", target); + const query = new Parse.Query('TestObject'); + query.equalTo('someObjs', target); return query.find(); }) .then( @@ -4246,24 +4246,24 @@ describe("Parse.Query testing", () => { ); }); - it("query match on array with multiple objects", done => { + it('query match on array with multiple objects', done => { const target1 = { - __type: "Pointer", - className: "TestObject", - objectId: "abc", + __type: 'Pointer', + className: 'TestObject', + objectId: 'abc', }; const target2 = { - __type: "Pointer", - className: "TestObject", - objectId: "123", + __type: 'Pointer', + className: 'TestObject', + objectId: '123', }; - const obj = new Parse.Object("TestObject"); - obj.set("someObjs", [target1, target2]); + const obj = new Parse.Object('TestObject'); + obj.set('someObjs', [target1, target2]); obj .save() .then(() => { - const query = new Parse.Query("TestObject"); - query.equalTo("someObjs", target1); + const query = new Parse.Query('TestObject'); + query.equalTo('someObjs', target1); return query.find(); }) .then( @@ -4277,21 +4277,21 @@ describe("Parse.Query testing", () => { ); }); - it("query should not match on array when searching for null", done => { + it('query should not match on array when searching for null', done => { const target = { - __type: "Pointer", - className: "TestObject", - objectId: "123", + __type: 'Pointer', + className: 'TestObject', + objectId: '123', }; - const obj = new Parse.Object("TestObject"); - obj.set("someKey", "someValue"); - obj.set("someObjs", [target]); + const obj = new Parse.Object('TestObject'); + obj.set('someKey', 'someValue'); + obj.set('someObjs', [target]); obj .save() .then(() => { - const query = new Parse.Query("TestObject"); - query.equalTo("someKey", "someValue"); - query.equalTo("someObjs", null); + const query = new Parse.Query('TestObject'); + query.equalTo('someKey', 'someValue'); + query.equalTo('someObjs', null); return query.find(); }) .then( @@ -4306,62 +4306,62 @@ describe("Parse.Query testing", () => { }); // #371 - it("should properly interpret a query v1", done => { - const query = new Parse.Query("C1"); - const auxQuery = new Parse.Query("C1"); - query.matchesKeyInQuery("A1", "A2", auxQuery); - query.include("A3"); - query.include("A2"); + it('should properly interpret a query v1', done => { + const query = new Parse.Query('C1'); + const auxQuery = new Parse.Query('C1'); + query.matchesKeyInQuery('A1', 'A2', auxQuery); + query.include('A3'); + query.include('A2'); query.find().then( () => { done(); }, err => { jfail(err); - fail("should not failt"); + fail('should not failt'); done(); } ); }); - it_id("7079f0ef-47b3-4a1e-aac0-32654dadaa27")(it)( - "should properly interpret a query v2", + it_id('7079f0ef-47b3-4a1e-aac0-32654dadaa27')(it)( + 'should properly interpret a query v2', done => { const user = new Parse.User(); - user.set("username", "foo"); - user.set("password", "bar"); + user.set('username', 'foo'); + user.set('password', 'bar'); return user .save() .then(user => { - const objIdQuery = new Parse.Query("_User").equalTo( - "objectId", + const objIdQuery = new Parse.Query('_User').equalTo( + 'objectId', user.id ); - const blockedUserQuery = user.relation("blockedUsers").query(); + const blockedUserQuery = user.relation('blockedUsers').query(); const aResponseQuery = new Parse.Query( - "MatchRelationshipActivityResponse" + 'MatchRelationshipActivityResponse' ); - aResponseQuery.equalTo("userA", user); - aResponseQuery.equalTo("userAResponse", 1); + aResponseQuery.equalTo('userA', user); + aResponseQuery.equalTo('userAResponse', 1); const bResponseQuery = new Parse.Query( - "MatchRelationshipActivityResponse" + 'MatchRelationshipActivityResponse' ); - bResponseQuery.equalTo("userB", user); - bResponseQuery.equalTo("userBResponse", 1); + bResponseQuery.equalTo('userB', user); + bResponseQuery.equalTo('userBResponse', 1); const matchOr = Parse.Query.or(aResponseQuery, bResponseQuery); - const matchRelationshipA = new Parse.Query("_User"); + const matchRelationshipA = new Parse.Query('_User'); matchRelationshipA.matchesKeyInQuery( - "objectId", - "userAObjectId", + 'objectId', + 'userAObjectId', matchOr ); - const matchRelationshipB = new Parse.Query("_User"); + const matchRelationshipB = new Parse.Query('_User'); matchRelationshipB.matchesKeyInQuery( - "objectId", - "userBObjectId", + 'objectId', + 'userBObjectId', matchOr ); @@ -4371,8 +4371,8 @@ describe("Parse.Query testing", () => { matchRelationshipA, matchRelationshipB ); - const query = new Parse.Query("_User"); - query.doesNotMatchQuery("objectId", orQuery); + const query = new Parse.Query('_User'); + query.doesNotMatchQuery('objectId', orQuery); return query.find(); }) .then( @@ -4381,17 +4381,17 @@ describe("Parse.Query testing", () => { }, err => { jfail(err); - fail("should not fail"); + fail('should not fail'); done(); } ); } ); - it("should match a key in an array (#3195)", function (done) { - const AuthorObject = Parse.Object.extend("Author"); - const GroupObject = Parse.Object.extend("Group"); - const PostObject = Parse.Object.extend("Post"); + it('should match a key in an array (#3195)', function (done) { + const AuthorObject = Parse.Object.extend('Author'); + const GroupObject = Parse.Object.extend('Group'); + const PostObject = Parse.Object.extend('Post'); return new AuthorObject() .save() @@ -4409,7 +4409,7 @@ describe("Parse.Query testing", () => { .then(results => { const p = results[0]; return new Parse.Query(PostObject) - .matchesKeyInQuery("author", "members", new Parse.Query(GroupObject)) + .matchesKeyInQuery('author', 'members', new Parse.Query(GroupObject)) .find() .then(r => { expect(r.length).toEqual(1); @@ -4421,36 +4421,36 @@ describe("Parse.Query testing", () => { }); }); - it_id("d95818c0-9e3c-41e6-be20-e7bafb59eefb")(it)( - "should find objects with array of pointers", + it_id('d95818c0-9e3c-41e6-be20-e7bafb59eefb')(it)( + 'should find objects with array of pointers', done => { const objects = []; while (objects.length != 5) { - const object = new Parse.Object("ContainedObject"); - object.set("index", objects.length); + const object = new Parse.Object('ContainedObject'); + object.set('index', objects.length); objects.push(object); } Parse.Object.saveAll(objects) .then(objects => { - const container = new Parse.Object("Container"); + const container = new Parse.Object('Container'); const pointers = objects.map(obj => { return { - __type: "Pointer", - className: "ContainedObject", + __type: 'Pointer', + className: 'ContainedObject', objectId: obj.id, }; }); - container.set("objects", pointers); - const container2 = new Parse.Object("Container"); - container2.set("objects", pointers.slice(2, 3)); + container.set('objects', pointers); + const container2 = new Parse.Object('Container'); + container2.set('objects', pointers.slice(2, 3)); return Parse.Object.saveAll([container, container2]); }) .then(() => { - const inQuery = new Parse.Query("ContainedObject"); - inQuery.greaterThanOrEqualTo("index", 1); - const query = new Parse.Query("Container"); - query.matchesQuery("objects", inQuery); + const inQuery = new Parse.Query('ContainedObject'); + inQuery.greaterThanOrEqualTo('index', 1); + const query = new Parse.Query('Container'); + query.matchesQuery('objects', inQuery); return query.find(); }) .then(results => { @@ -4461,27 +4461,27 @@ describe("Parse.Query testing", () => { }) .catch(err => { jfail(err); - fail("should not fail"); + fail('should not fail'); done(); }); } ); - it("query with two OR subqueries (regression test #1259)", done => { - const relatedObject = new Parse.Object("Class2"); + it('query with two OR subqueries (regression test #1259)', done => { + const relatedObject = new Parse.Object('Class2'); relatedObject .save() .then(relatedObject => { - const anObject = new Parse.Object("Class1"); - const relation = anObject.relation("relation"); + const anObject = new Parse.Object('Class1'); + const relation = anObject.relation('relation'); relation.add(relatedObject); return anObject.save(); }) .then(anObject => { - const q1 = anObject.relation("relation").query(); - q1.doesNotExist("nonExistantKey1"); - const q2 = anObject.relation("relation").query(); - q2.doesNotExist("nonExistantKey2"); + const q1 = anObject.relation('relation').query(); + q1.doesNotExist('nonExistantKey1'); + const q2 = anObject.relation('relation').query(); + q2.doesNotExist('nonExistantKey2'); Parse.Query.or(q1, q2) .find() .then(results => { @@ -4494,8 +4494,8 @@ describe("Parse.Query testing", () => { }); }); - it("objectId containedIn with multiple large array", done => { - const obj = new Parse.Object("MyClass"); + it('objectId containedIn with multiple large array', done => { + const obj = new Parse.Object('MyClass'); obj .save() .then(obj => { @@ -4504,9 +4504,9 @@ describe("Parse.Query testing", () => { longListOfStrings.push(i.toString()); } longListOfStrings.push(obj.id); - const q = new Parse.Query("MyClass"); - q.containedIn("objectId", longListOfStrings); - q.containedIn("objectId", longListOfStrings); + const q = new Parse.Query('MyClass'); + q.containedIn('objectId', longListOfStrings); + q.containedIn('objectId', longListOfStrings); return q.find(); }) .then(results => { @@ -4515,25 +4515,25 @@ describe("Parse.Query testing", () => { }); }); - it("containedIn with pointers should work with string array", done => { - const obj = new Parse.Object("MyClass"); - const child = new Parse.Object("Child"); + it('containedIn with pointers should work with string array', done => { + const obj = new Parse.Object('MyClass'); + const child = new Parse.Object('Child'); child .save() .then(() => { - obj.set("child", child); + obj.set('child', child); return obj.save(); }) .then(() => { const objs = []; for (let i = 0; i < 10; i++) { - objs.push(new Parse.Object("MyClass")); + objs.push(new Parse.Object('MyClass')); } return Parse.Object.saveAll(objs); }) .then(() => { - const query = new Parse.Query("MyClass"); - query.containedIn("child", [child.id]); + const query = new Parse.Query('MyClass'); + query.containedIn('child', [child.id]); return query.find(); }) .then(results => { @@ -4543,12 +4543,12 @@ describe("Parse.Query testing", () => { .catch(done.fail); }); - it("containedIn with pointers should work with string array, with many objects", done => { + it('containedIn with pointers should work with string array, with many objects', done => { const objs = []; const children = []; for (let i = 0; i < 10; i++) { - const obj = new Parse.Object("MyClass"); - const child = new Parse.Object("Child"); + const obj = new Parse.Object('MyClass'); + const child = new Parse.Object('Child'); objs.push(obj); children.push(child); } @@ -4556,17 +4556,17 @@ describe("Parse.Query testing", () => { .then(() => { return Parse.Object.saveAll( objs.map((obj, i) => { - obj.set("child", children[i]); + obj.set('child', children[i]); return obj; }) ); }) .then(() => { - const query = new Parse.Query("MyClass"); + const query = new Parse.Query('MyClass'); const subset = children.slice(0, 5).map(child => { return child.id; }); - query.containedIn("child", subset); + query.containedIn('child', subset); return query.find(); }) .then(results => { @@ -4576,197 +4576,197 @@ describe("Parse.Query testing", () => { .catch(done.fail); }); - it("include for specific object", function (done) { - const child = new Parse.Object("Child"); - const parent = new Parse.Object("Parent"); - child.set("foo", "bar"); - parent.set("child", child); + it('include for specific object', function (done) { + const child = new Parse.Object('Child'); + const parent = new Parse.Object('Parent'); + child.set('foo', 'bar'); + parent.set('child', child); Parse.Object.saveAll([child, parent]).then(function (response) { const savedParent = response[1]; - const parentQuery = new Parse.Query("Parent"); - parentQuery.include("child"); + const parentQuery = new Parse.Query('Parent'); + parentQuery.include('child'); parentQuery.get(savedParent.id).then(function (parentObj) { - const childPointer = parentObj.get("child"); + const childPointer = parentObj.get('child'); ok(childPointer); - equal(childPointer.get("foo"), "bar"); + equal(childPointer.get('foo'), 'bar'); done(); }); }); }); - it("select keys for specific object", function (done) { - const Foobar = new Parse.Object("Foobar"); - Foobar.set("foo", "bar"); - Foobar.set("fizz", "buzz"); + it('select keys for specific object', function (done) { + const Foobar = new Parse.Object('Foobar'); + Foobar.set('foo', 'bar'); + Foobar.set('fizz', 'buzz'); Foobar.save().then(function (savedFoobar) { - const foobarQuery = new Parse.Query("Foobar"); - foobarQuery.select("fizz"); + const foobarQuery = new Parse.Query('Foobar'); + foobarQuery.select('fizz'); foobarQuery.get(savedFoobar.id).then(function (foobarObj) { - equal(foobarObj.get("fizz"), "buzz"); - equal(foobarObj.get("foo"), undefined); + equal(foobarObj.get('fizz'), 'buzz'); + equal(foobarObj.get('foo'), undefined); done(); }); }); }); - it("select nested keys (issue #1567)", function (done) { - const Foobar = new Parse.Object("Foobar"); - const BarBaz = new Parse.Object("Barbaz"); - BarBaz.set("key", "value"); - BarBaz.set("otherKey", "value"); + it('select nested keys (issue #1567)', function (done) { + const Foobar = new Parse.Object('Foobar'); + const BarBaz = new Parse.Object('Barbaz'); + BarBaz.set('key', 'value'); + BarBaz.set('otherKey', 'value'); BarBaz.save() .then(() => { - Foobar.set("foo", "bar"); - Foobar.set("fizz", "buzz"); - Foobar.set("barBaz", BarBaz); + Foobar.set('foo', 'bar'); + Foobar.set('fizz', 'buzz'); + Foobar.set('barBaz', BarBaz); return Foobar.save(); }) .then(function (savedFoobar) { - const foobarQuery = new Parse.Query("Foobar"); - foobarQuery.select(["fizz", "barBaz.key"]); + const foobarQuery = new Parse.Query('Foobar'); + foobarQuery.select(['fizz', 'barBaz.key']); foobarQuery.get(savedFoobar.id).then(function (foobarObj) { - equal(foobarObj.get("fizz"), "buzz"); - equal(foobarObj.get("foo"), undefined); - if (foobarObj.has("barBaz")) { - equal(foobarObj.get("barBaz").get("key"), "value"); - equal(foobarObj.get("barBaz").get("otherKey"), undefined); + equal(foobarObj.get('fizz'), 'buzz'); + equal(foobarObj.get('foo'), undefined); + if (foobarObj.has('barBaz')) { + equal(foobarObj.get('barBaz').get('key'), 'value'); + equal(foobarObj.get('barBaz').get('otherKey'), undefined); } else { - fail("barBaz should be set"); + fail('barBaz should be set'); } done(); }); }); }); - it("select nested keys 2 level (issue #1567)", function (done) { - const Foobar = new Parse.Object("Foobar"); - const BarBaz = new Parse.Object("Barbaz"); - const Bazoo = new Parse.Object("Bazoo"); + it('select nested keys 2 level (issue #1567)', function (done) { + const Foobar = new Parse.Object('Foobar'); + const BarBaz = new Parse.Object('Barbaz'); + const Bazoo = new Parse.Object('Bazoo'); - Bazoo.set("some", "thing"); - Bazoo.set("otherSome", "value"); + Bazoo.set('some', 'thing'); + Bazoo.set('otherSome', 'value'); Bazoo.save() .then(() => { - BarBaz.set("key", "value"); - BarBaz.set("otherKey", "value"); - BarBaz.set("bazoo", Bazoo); + BarBaz.set('key', 'value'); + BarBaz.set('otherKey', 'value'); + BarBaz.set('bazoo', Bazoo); return BarBaz.save(); }) .then(() => { - Foobar.set("foo", "bar"); - Foobar.set("fizz", "buzz"); - Foobar.set("barBaz", BarBaz); + Foobar.set('foo', 'bar'); + Foobar.set('fizz', 'buzz'); + Foobar.set('barBaz', BarBaz); return Foobar.save(); }) .then(function (savedFoobar) { - const foobarQuery = new Parse.Query("Foobar"); - foobarQuery.select(["fizz", "barBaz.key", "barBaz.bazoo.some"]); + const foobarQuery = new Parse.Query('Foobar'); + foobarQuery.select(['fizz', 'barBaz.key', 'barBaz.bazoo.some']); foobarQuery.get(savedFoobar.id).then(function (foobarObj) { - equal(foobarObj.get("fizz"), "buzz"); - equal(foobarObj.get("foo"), undefined); - if (foobarObj.has("barBaz")) { - equal(foobarObj.get("barBaz").get("key"), "value"); - equal(foobarObj.get("barBaz").get("otherKey"), undefined); - equal(foobarObj.get("barBaz").get("bazoo").get("some"), "thing"); + equal(foobarObj.get('fizz'), 'buzz'); + equal(foobarObj.get('foo'), undefined); + if (foobarObj.has('barBaz')) { + equal(foobarObj.get('barBaz').get('key'), 'value'); + equal(foobarObj.get('barBaz').get('otherKey'), undefined); + equal(foobarObj.get('barBaz').get('bazoo').get('some'), 'thing'); equal( - foobarObj.get("barBaz").get("bazoo").get("otherSome"), + foobarObj.get('barBaz').get('bazoo').get('otherSome'), undefined ); } else { - fail("barBaz should be set"); + fail('barBaz should be set'); } done(); }); }); }); - it("exclude nested keys", async () => { - const Foobar = new Parse.Object("Foobar"); - const BarBaz = new Parse.Object("Barbaz"); - BarBaz.set("key", "value"); - BarBaz.set("otherKey", "value"); + it('exclude nested keys', async () => { + const Foobar = new Parse.Object('Foobar'); + const BarBaz = new Parse.Object('Barbaz'); + BarBaz.set('key', 'value'); + BarBaz.set('otherKey', 'value'); await BarBaz.save(); - Foobar.set("foo", "bar"); - Foobar.set("fizz", "buzz"); - Foobar.set("barBaz", BarBaz); + Foobar.set('foo', 'bar'); + Foobar.set('fizz', 'buzz'); + Foobar.set('barBaz', BarBaz); const savedFoobar = await Foobar.save(); - const foobarQuery = new Parse.Query("Foobar"); - foobarQuery.exclude(["foo", "barBaz.otherKey"]); + const foobarQuery = new Parse.Query('Foobar'); + foobarQuery.exclude(['foo', 'barBaz.otherKey']); const foobarObj = await foobarQuery.get(savedFoobar.id); - equal(foobarObj.get("fizz"), "buzz"); - equal(foobarObj.get("foo"), undefined); - if (foobarObj.has("barBaz")) { - equal(foobarObj.get("barBaz").get("key"), "value"); - equal(foobarObj.get("barBaz").get("otherKey"), undefined); + equal(foobarObj.get('fizz'), 'buzz'); + equal(foobarObj.get('foo'), undefined); + if (foobarObj.has('barBaz')) { + equal(foobarObj.get('barBaz').get('key'), 'value'); + equal(foobarObj.get('barBaz').get('otherKey'), undefined); } else { - fail("barBaz should be set"); + fail('barBaz should be set'); } }); - it("exclude nested keys 2 level", async () => { - const Foobar = new Parse.Object("Foobar"); - const BarBaz = new Parse.Object("Barbaz"); - const Bazoo = new Parse.Object("Bazoo"); + it('exclude nested keys 2 level', async () => { + const Foobar = new Parse.Object('Foobar'); + const BarBaz = new Parse.Object('Barbaz'); + const Bazoo = new Parse.Object('Bazoo'); - Bazoo.set("some", "thing"); - Bazoo.set("otherSome", "value"); + Bazoo.set('some', 'thing'); + Bazoo.set('otherSome', 'value'); await Bazoo.save(); - BarBaz.set("key", "value"); - BarBaz.set("otherKey", "value"); - BarBaz.set("bazoo", Bazoo); + BarBaz.set('key', 'value'); + BarBaz.set('otherKey', 'value'); + BarBaz.set('bazoo', Bazoo); await BarBaz.save(); - Foobar.set("foo", "bar"); - Foobar.set("fizz", "buzz"); - Foobar.set("barBaz", BarBaz); + Foobar.set('foo', 'bar'); + Foobar.set('fizz', 'buzz'); + Foobar.set('barBaz', BarBaz); const savedFoobar = await Foobar.save(); - const foobarQuery = new Parse.Query("Foobar"); - foobarQuery.exclude(["foo", "barBaz.otherKey", "barBaz.bazoo.otherSome"]); + const foobarQuery = new Parse.Query('Foobar'); + foobarQuery.exclude(['foo', 'barBaz.otherKey', 'barBaz.bazoo.otherSome']); const foobarObj = await foobarQuery.get(savedFoobar.id); - equal(foobarObj.get("fizz"), "buzz"); - equal(foobarObj.get("foo"), undefined); - if (foobarObj.has("barBaz")) { - equal(foobarObj.get("barBaz").get("key"), "value"); - equal(foobarObj.get("barBaz").get("otherKey"), undefined); - equal(foobarObj.get("barBaz").get("bazoo").get("some"), "thing"); - equal(foobarObj.get("barBaz").get("bazoo").get("otherSome"), undefined); + equal(foobarObj.get('fizz'), 'buzz'); + equal(foobarObj.get('foo'), undefined); + if (foobarObj.has('barBaz')) { + equal(foobarObj.get('barBaz').get('key'), 'value'); + equal(foobarObj.get('barBaz').get('otherKey'), undefined); + equal(foobarObj.get('barBaz').get('bazoo').get('some'), 'thing'); + equal(foobarObj.get('barBaz').get('bazoo').get('otherSome'), undefined); } else { - fail("barBaz should be set"); + fail('barBaz should be set'); } }); - it("include with *", async () => { - const child1 = new TestObject({ foo: "bar", name: "ac" }); - const child2 = new TestObject({ foo: "baz", name: "flo" }); - const child3 = new TestObject({ foo: "bad", name: "mo" }); + it('include with *', async () => { + const child1 = new TestObject({ foo: 'bar', name: 'ac' }); + const child2 = new TestObject({ foo: 'baz', name: 'flo' }); + const child3 = new TestObject({ foo: 'bad', name: 'mo' }); const parent = new Container({ child1, child2, child3 }); await Parse.Object.saveAll([parent, child1, child2, child3]); const options = Object.assign({}, masterKeyOptions, { qs: { where: JSON.stringify({ objectId: parent.id }), - include: "*", + include: '*', }, }); const resp = await request( - Object.assign({ url: Parse.serverURL + "/classes/Container" }, options) + Object.assign({ url: Parse.serverURL + '/classes/Container' }, options) ); const result = resp.data.results[0]; - equal(result.child1.foo, "bar"); - equal(result.child2.foo, "baz"); - equal(result.child3.foo, "bad"); - equal(result.child1.name, "ac"); - equal(result.child2.name, "flo"); - equal(result.child3.name, "mo"); + equal(result.child1.foo, 'bar'); + equal(result.child2.foo, 'baz'); + equal(result.child3.foo, 'bad'); + equal(result.child1.name, 'ac'); + equal(result.child2.name, 'flo'); + equal(result.child3.name, 'mo'); }); it('include with ["*"]', async () => { - const child1 = new TestObject({ foo: "bar", name: "ac" }); - const child2 = new TestObject({ foo: "baz", name: "flo" }); - const child3 = new TestObject({ foo: "bad", name: "mo" }); + const child1 = new TestObject({ foo: 'bar', name: 'ac' }); + const child2 = new TestObject({ foo: 'baz', name: 'flo' }); + const child3 = new TestObject({ foo: 'bad', name: 'mo' }); const parent = new Container({ child1, child2, child3 }); await Parse.Object.saveAll([parent, child1, child2, child3]); const options = Object.assign({}, masterKeyOptions, { @@ -4776,45 +4776,45 @@ describe("Parse.Query testing", () => { }, }); const resp = await request( - Object.assign({ url: Parse.serverURL + "/classes/Container" }, options) + Object.assign({ url: Parse.serverURL + '/classes/Container' }, options) ); const result = resp.data.results[0]; - equal(result.child1.foo, "bar"); - equal(result.child2.foo, "baz"); - equal(result.child3.foo, "bad"); - equal(result.child1.name, "ac"); - equal(result.child2.name, "flo"); - equal(result.child3.name, "mo"); - }); - - it("include with * overrides", async () => { - const child1 = new TestObject({ foo: "bar", name: "ac" }); - const child2 = new TestObject({ foo: "baz", name: "flo" }); - const child3 = new TestObject({ foo: "bad", name: "mo" }); + equal(result.child1.foo, 'bar'); + equal(result.child2.foo, 'baz'); + equal(result.child3.foo, 'bad'); + equal(result.child1.name, 'ac'); + equal(result.child2.name, 'flo'); + equal(result.child3.name, 'mo'); + }); + + it('include with * overrides', async () => { + const child1 = new TestObject({ foo: 'bar', name: 'ac' }); + const child2 = new TestObject({ foo: 'baz', name: 'flo' }); + const child3 = new TestObject({ foo: 'bad', name: 'mo' }); const parent = new Container({ child1, child2, child3 }); await Parse.Object.saveAll([parent, child1, child2, child3]); const options = Object.assign({}, masterKeyOptions, { qs: { where: JSON.stringify({ objectId: parent.id }), - include: "child2,*", + include: 'child2,*', }, }); const resp = await request( - Object.assign({ url: Parse.serverURL + "/classes/Container" }, options) + Object.assign({ url: Parse.serverURL + '/classes/Container' }, options) ); const result = resp.data.results[0]; - equal(result.child1.foo, "bar"); - equal(result.child2.foo, "baz"); - equal(result.child3.foo, "bad"); - equal(result.child1.name, "ac"); - equal(result.child2.name, "flo"); - equal(result.child3.name, "mo"); + equal(result.child1.foo, 'bar'); + equal(result.child2.foo, 'baz'); + equal(result.child3.foo, 'bad'); + equal(result.child1.name, 'ac'); + equal(result.child2.name, 'flo'); + equal(result.child3.name, 'mo'); }); it('include with ["*"] overrides', async () => { - const child1 = new TestObject({ foo: "bar", name: "ac" }); - const child2 = new TestObject({ foo: "baz", name: "flo" }); - const child3 = new TestObject({ foo: "bad", name: "mo" }); + const child1 = new TestObject({ foo: 'bar', name: 'ac' }); + const child2 = new TestObject({ foo: 'baz', name: 'flo' }); + const child3 = new TestObject({ foo: 'bad', name: 'mo' }); const parent = new Container({ child1, child2, child3 }); await Parse.Object.saveAll([parent, child1, child2, child3]); const options = Object.assign({}, masterKeyOptions, { @@ -4824,21 +4824,21 @@ describe("Parse.Query testing", () => { }, }); const resp = await request( - Object.assign({ url: Parse.serverURL + "/classes/Container" }, options) + Object.assign({ url: Parse.serverURL + '/classes/Container' }, options) ); const result = resp.data.results[0]; - equal(result.child1.foo, "bar"); - equal(result.child2.foo, "baz"); - equal(result.child3.foo, "bad"); - equal(result.child1.name, "ac"); - equal(result.child2.name, "flo"); - equal(result.child3.name, "mo"); - }); - - it("includeAll", done => { - const child1 = new TestObject({ foo: "bar", name: "ac" }); - const child2 = new TestObject({ foo: "baz", name: "flo" }); - const child3 = new TestObject({ foo: "bad", name: "mo" }); + equal(result.child1.foo, 'bar'); + equal(result.child2.foo, 'baz'); + equal(result.child3.foo, 'bad'); + equal(result.child1.name, 'ac'); + equal(result.child2.name, 'flo'); + equal(result.child3.name, 'mo'); + }); + + it('includeAll', done => { + const child1 = new TestObject({ foo: 'bar', name: 'ac' }); + const child2 = new TestObject({ foo: 'baz', name: 'flo' }); + const child3 = new TestObject({ foo: 'bad', name: 'mo' }); const parent = new Container({ child1, child2, child3 }); Parse.Object.saveAll([parent, child1, child2, child3]) .then(() => { @@ -4850,144 +4850,144 @@ describe("Parse.Query testing", () => { }); return request( Object.assign( - { url: Parse.serverURL + "/classes/Container" }, + { url: Parse.serverURL + '/classes/Container' }, options ) ); }) .then(resp => { const result = resp.data.results[0]; - equal(result.child1.foo, "bar"); - equal(result.child2.foo, "baz"); - equal(result.child3.foo, "bad"); - equal(result.child1.name, "ac"); - equal(result.child2.name, "flo"); - equal(result.child3.name, "mo"); + equal(result.child1.foo, 'bar'); + equal(result.child2.foo, 'baz'); + equal(result.child3.foo, 'bad'); + equal(result.child1.name, 'ac'); + equal(result.child2.name, 'flo'); + equal(result.child3.name, 'mo'); done(); }); }); - it("include pointer and pointer array", function (done) { + it('include pointer and pointer array', function (done) { const child = new TestObject(); const child2 = new TestObject(); - child.set("foo", "bar"); - child2.set("hello", "world"); + child.set('foo', 'bar'); + child2.set('hello', 'world'); Parse.Object.saveAll([child, child2]).then(function () { const parent = new Container(); - parent.set("child", child.toPointer()); - parent.set("child2", [child2.toPointer()]); + parent.set('child', child.toPointer()); + parent.set('child2', [child2.toPointer()]); parent.save().then(function () { const query = new Parse.Query(Container); - query.include(["child", "child2"]); + query.include(['child', 'child2']); query.find().then(function (results) { equal(results.length, 1); const parentAgain = results[0]; - const childAgain = parentAgain.get("child"); + const childAgain = parentAgain.get('child'); ok(childAgain); - equal(childAgain.get("foo"), "bar"); - const child2Again = parentAgain.get("child2"); + equal(childAgain.get('foo'), 'bar'); + const child2Again = parentAgain.get('child2'); equal(child2Again.length, 1); ok(child2Again); - equal(child2Again[0].get("hello"), "world"); + equal(child2Again[0].get('hello'), 'world'); done(); }); }); }); }); - it("include pointer and pointer array (keys switched)", function (done) { + it('include pointer and pointer array (keys switched)', function (done) { const child = new TestObject(); const child2 = new TestObject(); - child.set("foo", "bar"); - child2.set("hello", "world"); + child.set('foo', 'bar'); + child2.set('hello', 'world'); Parse.Object.saveAll([child, child2]).then(function () { const parent = new Container(); - parent.set("child", child.toPointer()); - parent.set("child2", [child2.toPointer()]); + parent.set('child', child.toPointer()); + parent.set('child2', [child2.toPointer()]); parent.save().then(function () { const query = new Parse.Query(Container); - query.include(["child2", "child"]); + query.include(['child2', 'child']); query.find().then(function (results) { equal(results.length, 1); const parentAgain = results[0]; - const childAgain = parentAgain.get("child"); + const childAgain = parentAgain.get('child'); ok(childAgain); - equal(childAgain.get("foo"), "bar"); - const child2Again = parentAgain.get("child2"); + equal(childAgain.get('foo'), 'bar'); + const child2Again = parentAgain.get('child2'); equal(child2Again.length, 1); ok(child2Again); - equal(child2Again[0].get("hello"), "world"); + equal(child2Again[0].get('hello'), 'world'); done(); }); }); }); }); - it("includeAll pointer and pointer array", function (done) { + it('includeAll pointer and pointer array', function (done) { const child = new TestObject(); const child2 = new TestObject(); - child.set("foo", "bar"); - child2.set("hello", "world"); + child.set('foo', 'bar'); + child2.set('hello', 'world'); Parse.Object.saveAll([child, child2]).then(function () { const parent = new Container(); - parent.set("child", child.toPointer()); - parent.set("child2", [child2.toPointer()]); + parent.set('child', child.toPointer()); + parent.set('child2', [child2.toPointer()]); parent.save().then(function () { const query = new Parse.Query(Container); query.includeAll(); query.find().then(function (results) { equal(results.length, 1); const parentAgain = results[0]; - const childAgain = parentAgain.get("child"); + const childAgain = parentAgain.get('child'); ok(childAgain); - equal(childAgain.get("foo"), "bar"); - const child2Again = parentAgain.get("child2"); + equal(childAgain.get('foo'), 'bar'); + const child2Again = parentAgain.get('child2'); equal(child2Again.length, 1); ok(child2Again); - equal(child2Again[0].get("hello"), "world"); + equal(child2Again[0].get('hello'), 'world'); done(); }); }); }); }); - it("select nested keys 2 level includeAll", done => { - const Foobar = new Parse.Object("Foobar"); - const BarBaz = new Parse.Object("Barbaz"); - const Bazoo = new Parse.Object("Bazoo"); - const Tang = new Parse.Object("Tang"); + it('select nested keys 2 level includeAll', done => { + const Foobar = new Parse.Object('Foobar'); + const BarBaz = new Parse.Object('Barbaz'); + const Bazoo = new Parse.Object('Bazoo'); + const Tang = new Parse.Object('Tang'); - Bazoo.set("some", "thing"); - Bazoo.set("otherSome", "value"); + Bazoo.set('some', 'thing'); + Bazoo.set('otherSome', 'value'); Bazoo.save() .then(() => { - BarBaz.set("key", "value"); - BarBaz.set("otherKey", "value"); - BarBaz.set("bazoo", Bazoo); + BarBaz.set('key', 'value'); + BarBaz.set('otherKey', 'value'); + BarBaz.set('bazoo', Bazoo); return BarBaz.save(); }) .then(() => { - Tang.set("clan", "wu"); + Tang.set('clan', 'wu'); return Tang.save(); }) .then(() => { - Foobar.set("foo", "bar"); - Foobar.set("fizz", "buzz"); - Foobar.set("barBaz", BarBaz); - Foobar.set("group", Tang); + Foobar.set('foo', 'bar'); + Foobar.set('fizz', 'buzz'); + Foobar.set('barBaz', BarBaz); + Foobar.set('group', Tang); return Foobar.save(); }) .then(savedFoobar => { const options = Object.assign( { - url: Parse.serverURL + "/classes/Foobar", + url: Parse.serverURL + '/classes/Foobar', }, masterKeyOptions, { qs: { where: JSON.stringify({ objectId: savedFoobar.id }), includeAll: true, - keys: "fizz,barBaz.key,barBaz.bazoo.some", + keys: 'fizz,barBaz.key,barBaz.bazoo.some', }, } ); @@ -4995,80 +4995,80 @@ describe("Parse.Query testing", () => { }) .then(resp => { const result = resp.data.results[0]; - equal(result.group.clan, "wu"); + equal(result.group.clan, 'wu'); equal(result.foo, undefined); - equal(result.fizz, "buzz"); - equal(result.barBaz.key, "value"); + equal(result.fizz, 'buzz'); + equal(result.barBaz.key, 'value'); equal(result.barBaz.otherKey, undefined); - equal(result.barBaz.bazoo.some, "thing"); + equal(result.barBaz.bazoo.some, 'thing'); equal(result.barBaz.bazoo.otherSome, undefined); done(); }) .catch(done.fail); }); - it("select nested keys 2 level without include (issue #3185)", function (done) { - const Foobar = new Parse.Object("Foobar"); - const BarBaz = new Parse.Object("Barbaz"); - const Bazoo = new Parse.Object("Bazoo"); + it('select nested keys 2 level without include (issue #3185)', function (done) { + const Foobar = new Parse.Object('Foobar'); + const BarBaz = new Parse.Object('Barbaz'); + const Bazoo = new Parse.Object('Bazoo'); - Bazoo.set("some", "thing"); - Bazoo.set("otherSome", "value"); + Bazoo.set('some', 'thing'); + Bazoo.set('otherSome', 'value'); Bazoo.save() .then(() => { - BarBaz.set("key", "value"); - BarBaz.set("otherKey", "value"); - BarBaz.set("bazoo", Bazoo); + BarBaz.set('key', 'value'); + BarBaz.set('otherKey', 'value'); + BarBaz.set('bazoo', Bazoo); return BarBaz.save(); }) .then(() => { - Foobar.set("foo", "bar"); - Foobar.set("fizz", "buzz"); - Foobar.set("barBaz", BarBaz); + Foobar.set('foo', 'bar'); + Foobar.set('fizz', 'buzz'); + Foobar.set('barBaz', BarBaz); return Foobar.save(); }) .then(function (savedFoobar) { - const foobarQuery = new Parse.Query("Foobar"); - foobarQuery.select(["fizz", "barBaz.key", "barBaz.bazoo.some"]); + const foobarQuery = new Parse.Query('Foobar'); + foobarQuery.select(['fizz', 'barBaz.key', 'barBaz.bazoo.some']); return foobarQuery.get(savedFoobar.id); }) .then(foobarObj => { - equal(foobarObj.get("fizz"), "buzz"); - equal(foobarObj.get("foo"), undefined); - if (foobarObj.has("barBaz")) { - equal(foobarObj.get("barBaz").get("key"), "value"); - equal(foobarObj.get("barBaz").get("otherKey"), undefined); - if (foobarObj.get("barBaz").has("bazoo")) { - equal(foobarObj.get("barBaz").get("bazoo").get("some"), "thing"); + equal(foobarObj.get('fizz'), 'buzz'); + equal(foobarObj.get('foo'), undefined); + if (foobarObj.has('barBaz')) { + equal(foobarObj.get('barBaz').get('key'), 'value'); + equal(foobarObj.get('barBaz').get('otherKey'), undefined); + if (foobarObj.get('barBaz').has('bazoo')) { + equal(foobarObj.get('barBaz').get('bazoo').get('some'), 'thing'); equal( - foobarObj.get("barBaz").get("bazoo").get("otherSome"), + foobarObj.get('barBaz').get('bazoo').get('otherSome'), undefined ); } else { - fail("bazoo should be set"); + fail('bazoo should be set'); } } else { - fail("barBaz should be set"); + fail('barBaz should be set'); } done(); }); }); - it("properly handles nested ors", function (done) { + it('properly handles nested ors', function (done) { const objects = []; while (objects.length != 4) { - const obj = new Parse.Object("Object"); - obj.set("x", objects.length); + const obj = new Parse.Object('Object'); + obj.set('x', objects.length); objects.push(obj); } Parse.Object.saveAll(objects) .then(() => { - const q0 = new Parse.Query("Object"); - q0.equalTo("x", 0); - const q1 = new Parse.Query("Object"); - q1.equalTo("x", 1); - const q2 = new Parse.Query("Object"); - q2.equalTo("x", 2); + const q0 = new Parse.Query('Object'); + q0.equalTo('x', 0); + const q1 = new Parse.Query('Object'); + q1.equalTo('x', 1); + const q2 = new Parse.Query('Object'); + q2.equalTo('x', 2); const or01 = Parse.Query.or(q0, q1); return Parse.Query.or(or01, q2).find(); }) @@ -5077,39 +5077,39 @@ describe("Parse.Query testing", () => { done(); }) .catch(error => { - fail("should not fail"); + fail('should not fail'); jfail(error); done(); }); }); - it("should not depend on parameter order #3169", function (done) { - const score1 = new Parse.Object("Score", { scoreId: "1" }); - const score2 = new Parse.Object("Score", { scoreId: "2" }); - const game1 = new Parse.Object("Game", { gameId: "1" }); - const game2 = new Parse.Object("Game", { gameId: "2" }); + it('should not depend on parameter order #3169', function (done) { + const score1 = new Parse.Object('Score', { scoreId: '1' }); + const score2 = new Parse.Object('Score', { scoreId: '2' }); + const game1 = new Parse.Object('Game', { gameId: '1' }); + const game2 = new Parse.Object('Game', { gameId: '2' }); Parse.Object.saveAll([score1, score2, game1, game2]) .then(() => { - game1.set("score", [score1]); - game2.set("score", [score2]); + game1.set('score', [score1]); + game2.set('score', [score2]); return Parse.Object.saveAll([game1, game2]); }) .then(() => { const where = { score: { objectId: score1.id, - className: "Score", - __type: "Pointer", + className: 'Score', + __type: 'Pointer', }, }; return request({ - method: "POST", - url: Parse.serverURL + "/classes/Game", - body: { where, _method: "GET" }, + method: 'POST', + url: Parse.serverURL + '/classes/Game', + body: { where, _method: 'GET' }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Javascript-Key": Parse.javaScriptKey, - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, + 'Content-Type': 'application/json', }, }); }) @@ -5123,278 +5123,278 @@ describe("Parse.Query testing", () => { ); }); - it("should not interfere with has when using select on field with undefined value #3999", done => { - const obj1 = new Parse.Object("TestObject"); - const obj2 = new Parse.Object("OtherObject"); - obj2.set("otherField", 1); - obj1.set("testPointerField", obj2); - obj1.set("shouldBe", true); - const obj3 = new Parse.Object("TestObject"); - obj3.set("shouldBe", false); + it('should not interfere with has when using select on field with undefined value #3999', done => { + const obj1 = new Parse.Object('TestObject'); + const obj2 = new Parse.Object('OtherObject'); + obj2.set('otherField', 1); + obj1.set('testPointerField', obj2); + obj1.set('shouldBe', true); + const obj3 = new Parse.Object('TestObject'); + obj3.set('shouldBe', false); Parse.Object.saveAll([obj1, obj3]) .then(() => { - const query = new Parse.Query("TestObject"); - query.include("testPointerField"); + const query = new Parse.Query('TestObject'); + query.include('testPointerField'); query.select([ - "testPointerField", - "testPointerField.otherField", - "shouldBe", + 'testPointerField', + 'testPointerField.otherField', + 'shouldBe', ]); return query.find(); }) .then(results => { results.forEach(result => { - equal(result.has("testPointerField"), result.get("shouldBe")); + equal(result.has('testPointerField'), result.get('shouldBe')); }); done(); }) .catch(done.fail); }); - it("should handle relative times correctly", async () => { + it('should handle relative times correctly', async () => { const now = Date.now(); - const obj1 = new Parse.Object("MyCustomObject", { - name: "obj1", + const obj1 = new Parse.Object('MyCustomObject', { + name: 'obj1', ttl: new Date(now + 2 * 24 * 60 * 60 * 1000), // 2 days from now }); - const obj2 = new Parse.Object("MyCustomObject", { - name: "obj2", + const obj2 = new Parse.Object('MyCustomObject', { + name: 'obj2', ttl: new Date(now - 2 * 24 * 60 * 60 * 1000), // 2 days ago }); await Parse.Object.saveAll([obj1, obj2]); - const q1 = new Parse.Query("MyCustomObject"); - q1.greaterThan("ttl", { $relativeTime: "in 1 day" }); + const q1 = new Parse.Query('MyCustomObject'); + q1.greaterThan('ttl', { $relativeTime: 'in 1 day' }); const results1 = await q1.find({ useMasterKey: true }); expect(results1.length).toBe(1); - const q2 = new Parse.Query("MyCustomObject"); - q2.greaterThan("ttl", { $relativeTime: "1 day ago" }); + const q2 = new Parse.Query('MyCustomObject'); + q2.greaterThan('ttl', { $relativeTime: '1 day ago' }); const results2 = await q2.find({ useMasterKey: true }); expect(results2.length).toBe(1); - const q3 = new Parse.Query("MyCustomObject"); - q3.lessThan("ttl", { $relativeTime: "5 days ago" }); + const q3 = new Parse.Query('MyCustomObject'); + q3.lessThan('ttl', { $relativeTime: '5 days ago' }); const results3 = await q3.find({ useMasterKey: true }); expect(results3.length).toBe(0); - const q4 = new Parse.Query("MyCustomObject"); - q4.greaterThan("ttl", { $relativeTime: "3 days ago" }); + const q4 = new Parse.Query('MyCustomObject'); + q4.greaterThan('ttl', { $relativeTime: '3 days ago' }); const results4 = await q4.find({ useMasterKey: true }); expect(results4.length).toBe(2); - const q5 = new Parse.Query("MyCustomObject"); - q5.greaterThan("ttl", { $relativeTime: "now" }); + const q5 = new Parse.Query('MyCustomObject'); + q5.greaterThan('ttl', { $relativeTime: 'now' }); const results5 = await q5.find({ useMasterKey: true }); expect(results5.length).toBe(1); - const q6 = new Parse.Query("MyCustomObject"); - q6.greaterThan("ttl", { $relativeTime: "now" }); - q6.lessThan("ttl", { $relativeTime: "in 1 day" }); + const q6 = new Parse.Query('MyCustomObject'); + q6.greaterThan('ttl', { $relativeTime: 'now' }); + q6.lessThan('ttl', { $relativeTime: 'in 1 day' }); const results6 = await q6.find({ useMasterKey: true }); expect(results6.length).toBe(0); - const q7 = new Parse.Query("MyCustomObject"); - q7.greaterThan("ttl", { $relativeTime: "1 year 3 weeks ago" }); + const q7 = new Parse.Query('MyCustomObject'); + q7.greaterThan('ttl', { $relativeTime: '1 year 3 weeks ago' }); const results7 = await q7.find({ useMasterKey: true }); expect(results7.length).toBe(2); }); - it("should error on invalid relative time", async () => { - const obj1 = new Parse.Object("MyCustomObject", { - name: "obj1", + it('should error on invalid relative time', async () => { + const obj1 = new Parse.Object('MyCustomObject', { + name: 'obj1', ttl: new Date(Date.now() + 2 * 24 * 60 * 60 * 1000), // 2 days from now }); await obj1.save({ useMasterKey: true }); - const q = new Parse.Query("MyCustomObject"); - q.greaterThan("ttl", { $relativeTime: "-12 bananas ago" }); + const q = new Parse.Query('MyCustomObject'); + q.greaterThan('ttl', { $relativeTime: '-12 bananas ago' }); try { await q.find({ useMasterKey: true }); - fail("Should have thrown error"); + fail('Should have thrown error'); } catch (error) { expect(error.code).toBe(Parse.Error.INVALID_JSON); } }); - it("should error when using $relativeTime on non-Date field", async () => { - const obj1 = new Parse.Object("MyCustomObject", { - name: "obj1", - nonDateField: "abcd", + it('should error when using $relativeTime on non-Date field', async () => { + const obj1 = new Parse.Object('MyCustomObject', { + name: 'obj1', + nonDateField: 'abcd', ttl: new Date(Date.now() + 2 * 24 * 60 * 60 * 1000), // 2 days from now }); await obj1.save({ useMasterKey: true }); - const q = new Parse.Query("MyCustomObject"); - q.greaterThan("nonDateField", { $relativeTime: "1 day ago" }); + const q = new Parse.Query('MyCustomObject'); + q.greaterThan('nonDateField', { $relativeTime: '1 day ago' }); try { await q.find({ useMasterKey: true }); - fail("Should have thrown error"); + fail('Should have thrown error'); } catch (error) { expect(error.code).toBe(Parse.Error.INVALID_JSON); } }); - it("should match complex structure with dot notation when using matchesKeyInQuery", function (done) { - const group1 = new Parse.Object("Group", { - name: "Group #1", + it('should match complex structure with dot notation when using matchesKeyInQuery', function (done) { + const group1 = new Parse.Object('Group', { + name: 'Group #1', }); - const group2 = new Parse.Object("Group", { - name: "Group #2", + const group2 = new Parse.Object('Group', { + name: 'Group #2', }); Parse.Object.saveAll([group1, group2]) .then(() => { - const role1 = new Parse.Object("Role", { - name: "Role #1", - type: "x", + const role1 = new Parse.Object('Role', { + name: 'Role #1', + type: 'x', belongsTo: group1, }); - const role2 = new Parse.Object("Role", { - name: "Role #2", - type: "y", + const role2 = new Parse.Object('Role', { + name: 'Role #2', + type: 'y', belongsTo: group1, }); return Parse.Object.saveAll([role1, role2]); }) .then(() => { - const rolesOfTypeX = new Parse.Query("Role"); - rolesOfTypeX.equalTo("type", "x"); + const rolesOfTypeX = new Parse.Query('Role'); + rolesOfTypeX.equalTo('type', 'x'); - const groupsWithRoleX = new Parse.Query("Group"); + const groupsWithRoleX = new Parse.Query('Group'); groupsWithRoleX.matchesKeyInQuery( - "objectId", - "belongsTo.objectId", + 'objectId', + 'belongsTo.objectId', rolesOfTypeX ); groupsWithRoleX.find().then(function (results) { equal(results.length, 1); - equal(results[0].get("name"), group1.get("name")); + equal(results[0].get('name'), group1.get('name')); done(); }); }); }); - it("should match complex structure with dot notation when using doesNotMatchKeyInQuery", function (done) { - const group1 = new Parse.Object("Group", { - name: "Group #1", + it('should match complex structure with dot notation when using doesNotMatchKeyInQuery', function (done) { + const group1 = new Parse.Object('Group', { + name: 'Group #1', }); - const group2 = new Parse.Object("Group", { - name: "Group #2", + const group2 = new Parse.Object('Group', { + name: 'Group #2', }); Parse.Object.saveAll([group1, group2]) .then(() => { - const role1 = new Parse.Object("Role", { - name: "Role #1", - type: "x", + const role1 = new Parse.Object('Role', { + name: 'Role #1', + type: 'x', belongsTo: group1, }); - const role2 = new Parse.Object("Role", { - name: "Role #2", - type: "y", + const role2 = new Parse.Object('Role', { + name: 'Role #2', + type: 'y', belongsTo: group1, }); return Parse.Object.saveAll([role1, role2]); }) .then(() => { - const rolesOfTypeX = new Parse.Query("Role"); - rolesOfTypeX.equalTo("type", "x"); + const rolesOfTypeX = new Parse.Query('Role'); + rolesOfTypeX.equalTo('type', 'x'); - const groupsWithRoleX = new Parse.Query("Group"); + const groupsWithRoleX = new Parse.Query('Group'); groupsWithRoleX.doesNotMatchKeyInQuery( - "objectId", - "belongsTo.objectId", + 'objectId', + 'belongsTo.objectId', rolesOfTypeX ); groupsWithRoleX.find().then(function (results) { equal(results.length, 1); - equal(results[0].get("name"), group2.get("name")); + equal(results[0].get('name'), group2.get('name')); done(); }); }); }); - it("should not throw error with undefined dot notation when using matchesKeyInQuery", async () => { - const group = new Parse.Object("Group", { name: "Group #1" }); + it('should not throw error with undefined dot notation when using matchesKeyInQuery', async () => { + const group = new Parse.Object('Group', { name: 'Group #1' }); await group.save(); - const role1 = new Parse.Object("Role", { - name: "Role #1", - type: "x", + const role1 = new Parse.Object('Role', { + name: 'Role #1', + type: 'x', belongsTo: group, }); - const role2 = new Parse.Object("Role", { - name: "Role #2", - type: "y", + const role2 = new Parse.Object('Role', { + name: 'Role #2', + type: 'y', belongsTo: undefined, }); await Parse.Object.saveAll([role1, role2]); - const rolesOfTypeX = new Parse.Query("Role"); - rolesOfTypeX.equalTo("type", "x"); + const rolesOfTypeX = new Parse.Query('Role'); + rolesOfTypeX.equalTo('type', 'x'); - const groupsWithRoleX = new Parse.Query("Group"); + const groupsWithRoleX = new Parse.Query('Group'); groupsWithRoleX.matchesKeyInQuery( - "objectId", - "belongsTo.objectId", + 'objectId', + 'belongsTo.objectId', rolesOfTypeX ); const results = await groupsWithRoleX.find(); equal(results.length, 1); - equal(results[0].get("name"), group.get("name")); + equal(results[0].get('name'), group.get('name')); }); - it("should not throw error with undefined dot notation when using doesNotMatchKeyInQuery", async () => { - const group1 = new Parse.Object("Group", { name: "Group #1" }); - const group2 = new Parse.Object("Group", { name: "Group #2" }); + it('should not throw error with undefined dot notation when using doesNotMatchKeyInQuery', async () => { + const group1 = new Parse.Object('Group', { name: 'Group #1' }); + const group2 = new Parse.Object('Group', { name: 'Group #2' }); await Parse.Object.saveAll([group1, group2]); - const role1 = new Parse.Object("Role", { - name: "Role #1", - type: "x", + const role1 = new Parse.Object('Role', { + name: 'Role #1', + type: 'x', belongsTo: group1, }); - const role2 = new Parse.Object("Role", { - name: "Role #2", - type: "y", + const role2 = new Parse.Object('Role', { + name: 'Role #2', + type: 'y', belongsTo: undefined, }); await Parse.Object.saveAll([role1, role2]); - const rolesOfTypeX = new Parse.Query("Role"); - rolesOfTypeX.equalTo("type", "x"); + const rolesOfTypeX = new Parse.Query('Role'); + rolesOfTypeX.equalTo('type', 'x'); - const groupsWithRoleX = new Parse.Query("Group"); + const groupsWithRoleX = new Parse.Query('Group'); groupsWithRoleX.doesNotMatchKeyInQuery( - "objectId", - "belongsTo.objectId", + 'objectId', + 'belongsTo.objectId', rolesOfTypeX ); const results = await groupsWithRoleX.find(); equal(results.length, 1); - equal(results[0].get("name"), group2.get("name")); + equal(results[0].get('name'), group2.get('name')); }); - it_id("8886b994-fbb8-487d-a863-43bbd2b24b73")(it)( - "withJSON supports geoWithin.centerSphere", + it_id('8886b994-fbb8-487d-a863-43bbd2b24b73')(it)( + 'withJSON supports geoWithin.centerSphere', done => { const inbound = new Parse.GeoPoint(1.5, 1.5); const onbound = new Parse.GeoPoint(10, 10); const outbound = new Parse.GeoPoint(20, 20); - const obj1 = new Parse.Object("TestObject", { location: inbound }); - const obj2 = new Parse.Object("TestObject", { location: onbound }); - const obj3 = new Parse.Object("TestObject", { location: outbound }); + const obj1 = new Parse.Object('TestObject', { location: inbound }); + const obj2 = new Parse.Object('TestObject', { location: onbound }); + const obj3 = new Parse.Object('TestObject', { location: outbound }); const center = new Parse.GeoPoint(0, 0); const distanceInKilometers = 1569 + 1; // 1569km is the approximate distance between {0, 0} and {10, 10}. Parse.Object.saveAll([obj1, obj2, obj3]) @@ -5432,7 +5432,7 @@ describe("Parse.Query testing", () => { } ); - it("withJSON with geoWithin.centerSphere fails without parameters", done => { + it('withJSON with geoWithin.centerSphere fails without parameters', done => { const q = new Parse.Query(TestObject); const jsonQ = q.toJSON(); jsonQ.where.location = { @@ -5447,12 +5447,12 @@ describe("Parse.Query testing", () => { .then(done); }); - it("withJSON with geoWithin.centerSphere fails with invalid distance", done => { + it('withJSON with geoWithin.centerSphere fails with invalid distance', done => { const q = new Parse.Query(TestObject); const jsonQ = q.toJSON(); jsonQ.where.location = { $geoWithin: { - $centerSphere: [[0, 0], "invalid_distance"], + $centerSphere: [[0, 0], 'invalid_distance'], }, }; q.withJSON(jsonQ); @@ -5462,7 +5462,7 @@ describe("Parse.Query testing", () => { .then(done); }); - it("withJSON with geoWithin.centerSphere fails with invalid coordinate", done => { + it('withJSON with geoWithin.centerSphere fails with invalid coordinate', done => { const q = new Parse.Query(TestObject); const jsonQ = q.toJSON(); jsonQ.where.location = { @@ -5476,7 +5476,7 @@ describe("Parse.Query testing", () => { .catch(() => done()); }); - it("withJSON with geoWithin.centerSphere fails with invalid geo point", done => { + it('withJSON with geoWithin.centerSphere fails with invalid geo point', done => { const q = new Parse.Query(TestObject); const jsonQ = q.toJSON(); jsonQ.where.location = { @@ -5490,172 +5490,172 @@ describe("Parse.Query testing", () => { .catch(() => done()); }); - it_id("02d4e7e6-859a-4ab6-878d-135ccc77040e")(it)( - "can add new config to existing config", + it_id('02d4e7e6-859a-4ab6-878d-135ccc77040e')(it)( + 'can add new config to existing config', async () => { await request({ - method: "PUT", - url: "http://localhost:8378/1/config", + method: 'PUT', + url: 'http://localhost:8378/1/config', json: true, body: { params: { - files: [{ __type: "File", name: "name", url: "http://url" }], + files: [{ __type: 'File', name: 'name', url: 'http://url' }], }, }, headers: masterKeyHeaders, }); await request({ - method: "PUT", - url: "http://localhost:8378/1/config", + method: 'PUT', + url: 'http://localhost:8378/1/config', json: true, body: { - params: { newConfig: "good" }, + params: { newConfig: 'good' }, }, headers: masterKeyHeaders, }); const result = await Parse.Config.get(); - equal(result.get("files")[0].toJSON(), { - __type: "File", - name: "name", - url: "http://url", + equal(result.get('files')[0].toJSON(), { + __type: 'File', + name: 'name', + url: 'http://url', }); - equal(result.get("newConfig"), "good"); + equal(result.get('newConfig'), 'good'); } ); - it("can set object type key", async () => { + it('can set object type key', async () => { const data = { bar: true, baz: 100 }; const object = new TestObject(); - object.set("objectField", data); + object.set('objectField', data); await object.save(); const query = new Parse.Query(TestObject); let result = await query.get(object.id); - equal(result.get("objectField"), data); + equal(result.get('objectField'), data); - object.set("objectField.baz", 50, { ignoreValidation: true }); + object.set('objectField.baz', 50, { ignoreValidation: true }); await object.save(); result = await query.get(object.id); - equal(result.get("objectField"), { bar: true, baz: 50 }); + equal(result.get('objectField'), { bar: true, baz: 50 }); }); - it("can update numeric array", async () => { + it('can update numeric array', async () => { const data1 = [0, 1.1, 1, -2, 3]; const data2 = [0, 1.1, 1, -2, 3, 4]; const obj1 = new TestObject(); - obj1.set("array", data1); + obj1.set('array', data1); await obj1.save(); - equal(obj1.get("array"), data1); + equal(obj1.get('array'), data1); const query = new Parse.Query(TestObject); - query.equalTo("objectId", obj1.id); + query.equalTo('objectId', obj1.id); const result = await query.first(); - equal(result.get("array"), data1); + equal(result.get('array'), data1); - result.set("array", data2); - equal(result.get("array"), data2); + result.set('array', data2); + equal(result.get('array'), data2); await result.save(); - equal(result.get("array"), data2); + equal(result.get('array'), data2); const results = await query.find(); - equal(results[0].get("array"), data2); + equal(results[0].get('array'), data2); }); - it("can update mixed array", async () => { - const data1 = [0, 1.1, "hello world", { foo: "bar" }]; - const data2 = [0, 1, { foo: "bar" }, [], [1, 2, "bar"]]; + it('can update mixed array', async () => { + const data1 = [0, 1.1, 'hello world', { foo: 'bar' }]; + const data2 = [0, 1, { foo: 'bar' }, [], [1, 2, 'bar']]; const obj1 = new TestObject(); - obj1.set("array", data1); + obj1.set('array', data1); await obj1.save(); - equal(obj1.get("array"), data1); + equal(obj1.get('array'), data1); const query = new Parse.Query(TestObject); - query.equalTo("objectId", obj1.id); + query.equalTo('objectId', obj1.id); const result = await query.first(); - equal(result.get("array"), data1); + equal(result.get('array'), data1); - result.set("array", data2); - equal(result.get("array"), data2); + result.set('array', data2); + equal(result.get('array'), data2); await result.save(); - equal(result.get("array"), data2); + equal(result.get('array'), data2); const results = await query.find(); - equal(results[0].get("array"), data2); + equal(results[0].get('array'), data2); }); - it("can query regex with unicode", async () => { + it('can query regex with unicode', async () => { const object = new TestObject(); - object.set("field", "autoöo"); + object.set('field', 'autoöo'); await object.save(); const query = new Parse.Query(TestObject); - query.contains("field", "autoöo"); + query.contains('field', 'autoöo'); const results = await query.find(); expect(results.length).toBe(1); - expect(results[0].get("field")).toBe("autoöo"); + expect(results[0].get('field')).toBe('autoöo'); }); - it("can update mixed array more than 100 elements", async () => { - const array = [0, 1.1, "hello world", { foo: "bar" }, null]; + it('can update mixed array more than 100 elements', async () => { + const array = [0, 1.1, 'hello world', { foo: 'bar' }, null]; const obj = new TestObject({ array }); await obj.save(); const query = new Parse.Query(TestObject); const result = await query.get(obj.id); - equal(result.get("array").length, 5); + equal(result.get('array').length, 5); for (let i = 0; i < 100; i += 1) { array.push(i); } - obj.set("array", array); + obj.set('array', array); await obj.save(); const results = await query.find(); - equal(results[0].get("array").length, 105); + equal(results[0].get('array').length, 105); }); - xit("todo: exclude keys with select key (sdk query get)", async done => { + xit('todo: exclude keys with select key (sdk query get)', async done => { // there is some problem with js sdk caching - const obj = new TestObject({ foo: "baz", hello: "world" }); + const obj = new TestObject({ foo: 'baz', hello: 'world' }); await obj.save(); - const query = new Parse.Query("TestObject"); + const query = new Parse.Query('TestObject'); query.withJSON({ - keys: "hello", - excludeKeys: "hello", + keys: 'hello', + excludeKeys: 'hello', }); const object = await query.get(obj.id); - expect(object.get("foo")).toBeUndefined(); - expect(object.get("hello")).toBeUndefined(); + expect(object.get('foo')).toBeUndefined(); + expect(object.get('hello')).toBeUndefined(); done(); }); - it_only_db("mongo")("can use explain on User class", async () => { + it_only_db('mongo')('can use explain on User class', async () => { // Create user const user = new Parse.User(); - user.set("username", "foo"); - user.set("password", "bar"); + user.set('username', 'foo'); + user.set('password', 'bar'); await user.save(); // Query for user with explain - const query = new Parse.Query("_User"); - query.equalTo("objectId", user.id); + const query = new Parse.Query('_User'); + query.equalTo('objectId', user.id); query.explain(); const result = await query.find(); // Validate expect(result.executionStats).not.toBeUndefined(); }); - it("should query with distinct within eachBatch and direct access enabled", async () => { + it('should query with distinct within eachBatch and direct access enabled', async () => { await reconfigureServer({ directAccess: true, }); @@ -5668,34 +5668,34 @@ describe("Parse.Query testing", () => { ); const user = new Parse.User(); - user.set("username", "foo"); - user.set("password", "bar"); + user.set('username', 'foo'); + user.set('password', 'bar'); await user.save(); - const score = new Parse.Object("Score"); - score.set("player", user); - score.set("score", 1); + const score = new Parse.Object('Score'); + score.set('player', user); + score.set('score', 1); await score.save(); - await new Parse.Query("_User").equalTo("objectId", user.id).eachBatch( + await new Parse.Query('_User').equalTo('objectId', user.id).eachBatch( async ([user]) => { - const score = await new Parse.Query("Score") - .equalTo("player", user) - .distinct("score", { useMasterKey: true }); + const score = await new Parse.Query('Score') + .equalTo('player', user) + .distinct('score', { useMasterKey: true }); expect(score).toEqual([1]); }, { useMasterKey: true } ); }); - describe_only_db("mongo")("query nested keys", () => { - it("queries nested key using equalTo", async () => { - const child = new Parse.Object("Child"); - child.set("key", "value"); + describe_only_db('mongo')('query nested keys', () => { + it('queries nested key using equalTo', async () => { + const child = new Parse.Object('Child'); + child.set('key', 'value'); await child.save(); - const parent = new Parse.Object("Parent"); - parent.set("some", { + const parent = new Parse.Object('Parent'); + parent.set('some', { nested: { key: { child, @@ -5704,20 +5704,20 @@ describe("Parse.Query testing", () => { }); await parent.save(); - const query1 = await new Parse.Query("Parent") - .equalTo("some.nested.key.child", child) + const query1 = await new Parse.Query('Parent') + .equalTo('some.nested.key.child', child) .find(); expect(query1.length).toEqual(1); }); - it("queries nested key using containedIn", async () => { - const child = new Parse.Object("Child"); - child.set("key", "value"); + it('queries nested key using containedIn', async () => { + const child = new Parse.Object('Child'); + child.set('key', 'value'); await child.save(); - const parent = new Parse.Object("Parent"); - parent.set("some", { + const parent = new Parse.Object('Parent'); + parent.set('some', { nested: { key: { child, @@ -5726,20 +5726,20 @@ describe("Parse.Query testing", () => { }); await parent.save(); - const query1 = await new Parse.Query("Parent") - .containedIn("some.nested.key.child", [child]) + const query1 = await new Parse.Query('Parent') + .containedIn('some.nested.key.child', [child]) .find(); expect(query1.length).toEqual(1); }); - it("queries nested key using matchesQuery", async () => { - const child = new Parse.Object("Child"); - child.set("key", "value"); + it('queries nested key using matchesQuery', async () => { + const child = new Parse.Object('Child'); + child.set('key', 'value'); await child.save(); - const parent = new Parse.Object("Parent"); - parent.set("some", { + const parent = new Parse.Object('Parent'); + parent.set('some', { nested: { key: { child, @@ -5748,10 +5748,10 @@ describe("Parse.Query testing", () => { }); await parent.save(); - const query1 = await new Parse.Query("Parent") + const query1 = await new Parse.Query('Parent') .matchesQuery( - "some.nested.key.child", - new Parse.Query("Child").equalTo("key", "value") + 'some.nested.key.child', + new Parse.Query('Child').equalTo('key', 'value') ) .find(); diff --git a/spec/ParseRelation.spec.js b/spec/ParseRelation.spec.js index b7a62f6e20..93ae42225a 100644 --- a/spec/ParseRelation.spec.js +++ b/spec/ParseRelation.spec.js @@ -1,17 +1,17 @@ -"use strict"; +'use strict'; // This is a port of the test suite: // hungry/js/test/parse_relation_test.js -const ChildObject = Parse.Object.extend({ className: "ChildObject" }); -const ParentObject = Parse.Object.extend({ className: "ParentObject" }); +const ChildObject = Parse.Object.extend({ className: 'ChildObject' }); +const ParentObject = Parse.Object.extend({ className: 'ParentObject' }); -describe("Parse.Relation testing", () => { - it("simple add and remove relation", done => { +describe('Parse.Relation testing', () => { + it('simple add and remove relation', done => { const child = new ChildObject(); - child.set("x", 2); + child.set('x', 2); const parent = new ParentObject(); - parent.set("x", 4); - const relation = parent.relation("child"); + parent.set('x', 4); + const relation = parent.relation('child'); child .save() @@ -28,9 +28,9 @@ describe("Parse.Relation testing", () => { return relation.query().find(); }) .then(list => { - equal(list.length, 1, "Should have gotten one element back"); - equal(list[0].id, child.id, "Should have gotten the right value"); - ok(!parent.dirty("child"), "The relation should not be dirty"); + equal(list.length, 1, 'Should have gotten one element back'); + equal(list[0].id, child.id, 'Should have gotten the right value'); + ok(!parent.dirty('child'), 'The relation should not be dirty'); relation.remove(child); return parent.save(); @@ -39,59 +39,59 @@ describe("Parse.Relation testing", () => { return relation.query().find(); }) .then(list => { - equal(list.length, 0, "Delete should have worked"); - ok(!parent.dirty("child"), "The relation should not be dirty"); + equal(list.length, 0, 'Delete should have worked'); + ok(!parent.dirty('child'), 'The relation should not be dirty'); done(); }); }); - it("query relation without schema", async () => { - const ChildObject = Parse.Object.extend("ChildObject"); + it('query relation without schema', async () => { + const ChildObject = Parse.Object.extend('ChildObject'); const childObjects = []; for (let i = 0; i < 10; i++) { childObjects.push(new ChildObject({ x: i })); } await Parse.Object.saveAll(childObjects); - const ParentObject = Parse.Object.extend("ParentObject"); + const ParentObject = Parse.Object.extend('ParentObject'); const parent = new ParentObject(); - parent.set("x", 4); - let relation = parent.relation("child"); + parent.set('x', 4); + let relation = parent.relation('child'); relation.add(childObjects[0]); await parent.save(); const parentAgain = new ParentObject(); parentAgain.id = parent.id; - relation = parentAgain.relation("child"); + relation = parentAgain.relation('child'); const list = await relation.query().find(); - equal(list.length, 1, "Should have gotten one element back"); - equal(list[0].id, childObjects[0].id, "Should have gotten the right value"); + equal(list.length, 1, 'Should have gotten one element back'); + equal(list[0].id, childObjects[0].id, 'Should have gotten the right value'); }); - it("relations are constructed right from query", async () => { - const ChildObject = Parse.Object.extend("ChildObject"); + it('relations are constructed right from query', async () => { + const ChildObject = Parse.Object.extend('ChildObject'); const childObjects = []; for (let i = 0; i < 10; i++) { childObjects.push(new ChildObject({ x: i })); } await Parse.Object.saveAll(childObjects); - const ParentObject = Parse.Object.extend("ParentObject"); + const ParentObject = Parse.Object.extend('ParentObject'); const parent = new ParentObject(); - parent.set("x", 4); - const relation = parent.relation("child"); + parent.set('x', 4); + const relation = parent.relation('child'); relation.add(childObjects[0]); await parent.save(); const query = new Parse.Query(ParentObject); const object = await query.get(parent.id); - const relationAgain = object.relation("child"); + const relationAgain = object.relation('child'); const list = await relationAgain.query().find(); - equal(list.length, 1, "Should have gotten one element back"); - equal(list[0].id, childObjects[0].id, "Should have gotten the right value"); - ok(!parent.dirty("child"), "The relation should not be dirty"); + equal(list.length, 1, 'Should have gotten one element back'); + equal(list[0].id, childObjects[0].id, 'Should have gotten the right value'); + ok(!parent.dirty('child'), 'The relation should not be dirty'); }); - it("compound add and remove relation", done => { - const ChildObject = Parse.Object.extend("ChildObject"); + it('compound add and remove relation', done => { + const ChildObject = Parse.Object.extend('ChildObject'); const childObjects = []; for (let i = 0; i < 10; i++) { childObjects.push(new ChildObject({ x: i })); @@ -102,10 +102,10 @@ describe("Parse.Relation testing", () => { Parse.Object.saveAll(childObjects) .then(function () { - const ParentObject = Parse.Object.extend("ParentObject"); + const ParentObject = Parse.Object.extend('ParentObject'); parent = new ParentObject(); - parent.set("x", 4); - relation = parent.relation("child"); + parent.set('x', 4); + relation = parent.relation('child'); relation.add(childObjects[0]); relation.add(childObjects[1]); relation.remove(childObjects[0]); @@ -116,8 +116,8 @@ describe("Parse.Relation testing", () => { return relation.query().find(); }) .then(function (list) { - equal(list.length, 2, "Should have gotten two elements back"); - ok(!parent.dirty("child"), "The relation should not be dirty"); + equal(list.length, 2, 'Should have gotten two elements back'); + ok(!parent.dirty('child'), 'The relation should not be dirty'); relation.remove(childObjects[1]); relation.remove(childObjects[2]); relation.add(childObjects[1]); @@ -129,8 +129,8 @@ describe("Parse.Relation testing", () => { }) .then( function (list) { - equal(list.length, 2, "Deletes and then adds should have worked"); - ok(!parent.dirty("child"), "The relation should not be dirty"); + equal(list.length, 2, 'Deletes and then adds should have worked'); + ok(!parent.dirty('child'), 'The relation should not be dirty'); done(); }, function (err) { @@ -140,8 +140,8 @@ describe("Parse.Relation testing", () => { ); }); - it("related at ordering optimizations", done => { - const ChildObject = Parse.Object.extend("ChildObject"); + it('related at ordering optimizations', done => { + const ChildObject = Parse.Object.extend('ChildObject'); const childObjects = []; for (let i = 0; i < 10; i++) { childObjects.push(new ChildObject({ x: i })); @@ -152,16 +152,16 @@ describe("Parse.Relation testing", () => { Parse.Object.saveAll(childObjects) .then(function () { - const ParentObject = Parse.Object.extend("ParentObject"); + const ParentObject = Parse.Object.extend('ParentObject'); parent = new ParentObject(); - parent.set("x", 4); - relation = parent.relation("child"); + parent.set('x', 4); + relation = parent.relation('child'); relation.add(childObjects); return parent.save(); }) .then(function () { const query = relation.query(); - query.descending("createdAt"); + query.descending('createdAt'); query.skip(1); query.limit(3); return query.find(); @@ -172,52 +172,52 @@ describe("Parse.Relation testing", () => { .then(done, done.fail); }); - it("queries with relations", async () => { - const ChildObject = Parse.Object.extend("ChildObject"); + it('queries with relations', async () => { + const ChildObject = Parse.Object.extend('ChildObject'); const childObjects = []; for (let i = 0; i < 10; i++) { childObjects.push(new ChildObject({ x: i })); } await Parse.Object.saveAll(childObjects); - const ParentObject = Parse.Object.extend("ParentObject"); + const ParentObject = Parse.Object.extend('ParentObject'); const parent = new ParentObject(); - parent.set("x", 4); - const relation = parent.relation("child"); + parent.set('x', 4); + const relation = parent.relation('child'); relation.add(childObjects[0]); relation.add(childObjects[1]); relation.add(childObjects[2]); await parent.save(); const query = relation.query(); - query.equalTo("x", 2); + query.equalTo('x', 2); const list = await query.find(); - equal(list.length, 1, "There should only be one element"); - ok(list[0] instanceof ChildObject, "Should be of type ChildObject"); + equal(list.length, 1, 'There should only be one element'); + ok(list[0] instanceof ChildObject, 'Should be of type ChildObject'); equal( list[0].id, childObjects[2].id, - "We should have gotten back the right result" + 'We should have gotten back the right result' ); }); - it("queries on relation fields", async () => { - const ChildObject = Parse.Object.extend("ChildObject"); + it('queries on relation fields', async () => { + const ChildObject = Parse.Object.extend('ChildObject'); const childObjects = []; for (let i = 0; i < 10; i++) { childObjects.push(new ChildObject({ x: i })); } await Parse.Object.saveAll(childObjects); - const ParentObject = Parse.Object.extend("ParentObject"); + const ParentObject = Parse.Object.extend('ParentObject'); const parent = new ParentObject(); - parent.set("x", 4); - const relation = parent.relation("child"); + parent.set('x', 4); + const relation = parent.relation('child'); relation.add(childObjects[0]); relation.add(childObjects[1]); relation.add(childObjects[2]); const parent2 = new ParentObject(); - parent2.set("x", 3); - const relation2 = parent2.relation("child"); + parent2.set('x', 3); + const relation2 = parent2.relation('child'); relation2.add(childObjects[4]); relation2.add(childObjects[5]); relation2.add(childObjects[6]); @@ -229,13 +229,13 @@ describe("Parse.Relation testing", () => { const objects = []; objects.push(childObjects[4]); objects.push(childObjects[9]); - const list = await query.containedIn("child", objects).find(); - equal(list.length, 1, "There should be only one result"); - equal(list[0].id, parent2.id, "Should have gotten back the right result"); + const list = await query.containedIn('child', objects).find(); + equal(list.length, 1, 'There should be only one result'); + equal(list[0].id, parent2.id, 'Should have gotten back the right result'); }); - it("queries on relation fields with multiple containedIn (regression test for #1271)", done => { - const ChildObject = Parse.Object.extend("ChildObject"); + it('queries on relation fields with multiple containedIn (regression test for #1271)', done => { + const ChildObject = Parse.Object.extend('ChildObject'); const childObjects = []; for (let i = 0; i < 10; i++) { childObjects.push(new ChildObject({ x: i })); @@ -243,21 +243,21 @@ describe("Parse.Relation testing", () => { Parse.Object.saveAll(childObjects) .then(() => { - const ParentObject = Parse.Object.extend("ParentObject"); + const ParentObject = Parse.Object.extend('ParentObject'); const parent = new ParentObject(); - parent.set("x", 4); - const parent1Children = parent.relation("child"); + parent.set('x', 4); + const parent1Children = parent.relation('child'); parent1Children.add(childObjects[0]); parent1Children.add(childObjects[1]); parent1Children.add(childObjects[2]); const parent2 = new ParentObject(); - parent2.set("x", 3); - const parent2Children = parent2.relation("child"); + parent2.set('x', 3); + const parent2Children = parent2.relation('child'); parent2Children.add(childObjects[4]); parent2Children.add(childObjects[5]); parent2Children.add(childObjects[6]); - const parent2OtherChildren = parent2.relation("otherChild"); + const parent2OtherChildren = parent2.relation('otherChild'); parent2OtherChildren.add(childObjects[0]); parent2OtherChildren.add(childObjects[1]); parent2OtherChildren.add(childObjects[2]); @@ -266,8 +266,8 @@ describe("Parse.Relation testing", () => { }) .then(() => { const objectsWithChild0InBothChildren = new Parse.Query(ParentObject); - objectsWithChild0InBothChildren.containedIn("child", [childObjects[0]]); - objectsWithChild0InBothChildren.containedIn("otherChild", [ + objectsWithChild0InBothChildren.containedIn('child', [childObjects[0]]); + objectsWithChild0InBothChildren.containedIn('otherChild', [ childObjects[0], ]); return objectsWithChild0InBothChildren.find(); @@ -278,8 +278,8 @@ describe("Parse.Relation testing", () => { }) .then(() => { const objectsWithChild4andOtherChild1 = new Parse.Query(ParentObject); - objectsWithChild4andOtherChild1.containedIn("child", [childObjects[4]]); - objectsWithChild4andOtherChild1.containedIn("otherChild", [ + objectsWithChild4andOtherChild1.containedIn('child', [childObjects[4]]); + objectsWithChild4andOtherChild1.containedIn('otherChild', [ childObjects[1], ]); return objectsWithChild4andOtherChild1.find(); @@ -291,8 +291,8 @@ describe("Parse.Relation testing", () => { }); }); - it("query on pointer and relation fields with equal", done => { - const ChildObject = Parse.Object.extend("ChildObject"); + it('query on pointer and relation fields with equal', done => { + const ChildObject = Parse.Object.extend('ChildObject'); const childObjects = []; for (let i = 0; i < 10; i++) { childObjects.push(new ChildObject({ x: i })); @@ -300,17 +300,17 @@ describe("Parse.Relation testing", () => { Parse.Object.saveAll(childObjects) .then(() => { - const ParentObject = Parse.Object.extend("ParentObject"); + const ParentObject = Parse.Object.extend('ParentObject'); const parent = new ParentObject(); - parent.set("x", 4); - const relation = parent.relation("toChilds"); + parent.set('x', 4); + const relation = parent.relation('toChilds'); relation.add(childObjects[0]); relation.add(childObjects[1]); relation.add(childObjects[2]); const parent2 = new ParentObject(); - parent2.set("x", 3); - parent2.set("toChild", childObjects[2]); + parent2.set('x', 3); + parent2.set('toChild', childObjects[2]); const parents = []; parents.push(parent); @@ -319,11 +319,11 @@ describe("Parse.Relation testing", () => { return Parse.Object.saveAll(parents).then(() => { const query = new Parse.Query(ParentObject); - query.equalTo("objectId", parent.id); - query.equalTo("toChilds", childObjects[2]); + query.equalTo('objectId', parent.id); + query.equalTo('toChilds', childObjects[2]); return query.find().then(list => { - equal(list.length, 1, "There should be 1 result"); + equal(list.length, 1, 'There should be 1 result'); done(); }); }); @@ -334,25 +334,25 @@ describe("Parse.Relation testing", () => { }); }); - it("query on pointer and relation fields with equal bis", done => { - const ChildObject = Parse.Object.extend("ChildObject"); + it('query on pointer and relation fields with equal bis', done => { + const ChildObject = Parse.Object.extend('ChildObject'); const childObjects = []; for (let i = 0; i < 10; i++) { childObjects.push(new ChildObject({ x: i })); } Parse.Object.saveAll(childObjects).then(() => { - const ParentObject = Parse.Object.extend("ParentObject"); + const ParentObject = Parse.Object.extend('ParentObject'); const parent = new ParentObject(); - parent.set("x", 4); - const relation = parent.relation("toChilds"); + parent.set('x', 4); + const relation = parent.relation('toChilds'); relation.add(childObjects[0]); relation.add(childObjects[1]); relation.add(childObjects[2]); const parent2 = new ParentObject(); - parent2.set("x", 3); - parent2.relation("toChilds").add(childObjects[2]); + parent2.set('x', 3); + parent2.relation('toChilds').add(childObjects[2]); const parents = []; parents.push(parent); @@ -361,38 +361,38 @@ describe("Parse.Relation testing", () => { return Parse.Object.saveAll(parents).then(() => { const query = new Parse.Query(ParentObject); - query.equalTo("objectId", parent2.id); + query.equalTo('objectId', parent2.id); // childObjects[2] is in 2 relations // before the fix, that woul yield 2 results - query.equalTo("toChilds", childObjects[2]); + query.equalTo('toChilds', childObjects[2]); return query.find().then(list => { - equal(list.length, 1, "There should be 1 result"); + equal(list.length, 1, 'There should be 1 result'); done(); }); }); }); }); - it("or queries on pointer and relation fields", done => { - const ChildObject = Parse.Object.extend("ChildObject"); + it('or queries on pointer and relation fields', done => { + const ChildObject = Parse.Object.extend('ChildObject'); const childObjects = []; for (let i = 0; i < 10; i++) { childObjects.push(new ChildObject({ x: i })); } Parse.Object.saveAll(childObjects).then(() => { - const ParentObject = Parse.Object.extend("ParentObject"); + const ParentObject = Parse.Object.extend('ParentObject'); const parent = new ParentObject(); - parent.set("x", 4); - const relation = parent.relation("toChilds"); + parent.set('x', 4); + const relation = parent.relation('toChilds'); relation.add(childObjects[0]); relation.add(childObjects[1]); relation.add(childObjects[2]); const parent2 = new ParentObject(); - parent2.set("x", 3); - parent2.set("toChild", childObjects[2]); + parent2.set('x', 3); + parent2.set('toChild', childObjects[2]); const parents = []; parents.push(parent); @@ -401,9 +401,9 @@ describe("Parse.Relation testing", () => { return Parse.Object.saveAll(parents).then(() => { const query1 = new Parse.Query(ParentObject); - query1.containedIn("toChilds", [childObjects[2]]); + query1.containedIn('toChilds', [childObjects[2]]); const query2 = new Parse.Query(ParentObject); - query2.equalTo("toChild", childObjects[2]); + query2.equalTo('toChild', childObjects[2]); const query = Parse.Query.or(query1, query2); return query.find().then(list => { const objectIds = list.map(function (item) { @@ -411,31 +411,31 @@ describe("Parse.Relation testing", () => { }); expect(objectIds.indexOf(parent.id)).not.toBe(-1); expect(objectIds.indexOf(parent2.id)).not.toBe(-1); - equal(list.length, 2, "There should be 2 results"); + equal(list.length, 2, 'There should be 2 results'); done(); }); }); }); }); - it("or queries with base constraint on relation field", async () => { - const ChildObject = Parse.Object.extend("ChildObject"); + it('or queries with base constraint on relation field', async () => { + const ChildObject = Parse.Object.extend('ChildObject'); const childObjects = []; for (let i = 0; i < 10; i++) { childObjects.push(new ChildObject({ x: i })); } await Parse.Object.saveAll(childObjects); - const ParentObject = Parse.Object.extend("ParentObject"); + const ParentObject = Parse.Object.extend('ParentObject'); const parent = new ParentObject(); - parent.set("x", 4); - const relation = parent.relation("toChilds"); + parent.set('x', 4); + const relation = parent.relation('toChilds'); relation.add(childObjects[0]); relation.add(childObjects[1]); relation.add(childObjects[2]); const parent2 = new ParentObject(); - parent2.set("x", 3); - const relation2 = parent2.relation("toChilds"); + parent2.set('x', 3); + const relation2 = parent2.relation('toChilds'); relation2.add(childObjects[0]); relation2.add(childObjects[1]); relation2.add(childObjects[2]); @@ -447,30 +447,30 @@ describe("Parse.Relation testing", () => { await Parse.Object.saveAll(parents); const query1 = new Parse.Query(ParentObject); - query1.equalTo("x", 4); + query1.equalTo('x', 4); const query2 = new Parse.Query(ParentObject); - query2.equalTo("x", 3); + query2.equalTo('x', 3); const query = Parse.Query.or(query1, query2); - query.equalTo("toChilds", childObjects[2]); + query.equalTo('toChilds', childObjects[2]); const list = await query.find(); const objectIds = list.map(item => item.id); expect(objectIds.indexOf(parent.id)).not.toBe(-1); expect(objectIds.indexOf(parent2.id)).not.toBe(-1); - equal(list.length, 2, "There should be 2 results"); + equal(list.length, 2, 'There should be 2 results'); }); - it("Get query on relation using un-fetched parent object", done => { + it('Get query on relation using un-fetched parent object', done => { // Setup data model - const Wheel = Parse.Object.extend("Wheel"); - const Car = Parse.Object.extend("Car"); + const Wheel = Parse.Object.extend('Wheel'); + const Car = Parse.Object.extend('Car'); const origWheel = new Wheel(); origWheel .save() .then(function () { const car = new Car(); - const relation = car.relation("wheels"); + const relation = car.relation('wheels'); relation.add(origWheel); return car.save(); }) @@ -479,7 +479,7 @@ describe("Parse.Relation testing", () => { // Create an un-fetched shell car object const unfetchedCar = new Car(); unfetchedCar.id = car.id; - const relation = unfetchedCar.relation("wheels"); + const relation = unfetchedCar.relation('wheels'); const query = relation.query(); // Parent object is un-fetched, so this will call /1/classes/Car instead @@ -488,7 +488,7 @@ describe("Parse.Relation testing", () => { }) .then(function (wheel) { // Make sure this is Wheel and not Car. - strictEqual(wheel.className, "Wheel"); + strictEqual(wheel.className, 'Wheel'); strictEqual(wheel.id, origWheel.id); }) .then( @@ -496,22 +496,22 @@ describe("Parse.Relation testing", () => { done(); }, function (err) { - ok(false, "unexpected error: " + JSON.stringify(err)); + ok(false, 'unexpected error: ' + JSON.stringify(err)); done(); } ); }); - it("Find query on relation using un-fetched parent object", done => { + it('Find query on relation using un-fetched parent object', done => { // Setup data model - const Wheel = Parse.Object.extend("Wheel"); - const Car = Parse.Object.extend("Car"); + const Wheel = Parse.Object.extend('Wheel'); + const Car = Parse.Object.extend('Car'); const origWheel = new Wheel(); origWheel .save() .then(function () { const car = new Car(); - const relation = car.relation("wheels"); + const relation = car.relation('wheels'); relation.add(origWheel); return car.save(); }) @@ -520,7 +520,7 @@ describe("Parse.Relation testing", () => { // Create an un-fetched shell car object const unfetchedCar = new Car(); unfetchedCar.id = car.id; - const relation = unfetchedCar.relation("wheels"); + const relation = unfetchedCar.relation('wheels'); const query = relation.query(); // Parent object is un-fetched, so this will call /1/classes/Car instead @@ -530,7 +530,7 @@ describe("Parse.Relation testing", () => { .then(function (results) { // Make sure this is Wheel and not Car. const wheel = results[0]; - strictEqual(wheel.className, "Wheel"); + strictEqual(wheel.className, 'Wheel'); strictEqual(wheel.id, origWheel.id); }) .then( @@ -538,28 +538,28 @@ describe("Parse.Relation testing", () => { done(); }, function (err) { - ok(false, "unexpected error: " + JSON.stringify(err)); + ok(false, 'unexpected error: ' + JSON.stringify(err)); done(); } ); }); - it("Find objects with a related object using equalTo", done => { + it('Find objects with a related object using equalTo', done => { // Setup the objects - const Card = Parse.Object.extend("Card"); - const House = Parse.Object.extend("House"); + const Card = Parse.Object.extend('Card'); + const House = Parse.Object.extend('House'); const card = new Card(); card .save() .then(() => { const house = new House(); - const relation = house.relation("cards"); + const relation = house.relation('cards'); relation.add(card); return house.save(); }) .then(() => { - const query = new Parse.Query("House"); - query.equalTo("cards", card); + const query = new Parse.Query('House'); + query.equalTo('cards', card); return query.find(); }) .then(results => { @@ -568,76 +568,76 @@ describe("Parse.Relation testing", () => { }); }); - it("should properly get related objects with unfetched queries", done => { + it('should properly get related objects with unfetched queries', done => { const objects = []; const owners = []; const allObjects = []; // Build 10 Objects and 10 owners while (objects.length != 10) { - const object = new Parse.Object("AnObject"); + const object = new Parse.Object('AnObject'); object.set({ index: objects.length, even: objects.length % 2 == 0, }); objects.push(object); - const owner = new Parse.Object("AnOwner"); + const owner = new Parse.Object('AnOwner'); owners.push(owner); allObjects.push(object); allObjects.push(owner); } - const anotherOwner = new Parse.Object("AnotherOwner"); + const anotherOwner = new Parse.Object('AnotherOwner'); return Parse.Object.saveAll(allObjects.concat([anotherOwner])) .then(() => { // put all the AnObject into the anotherOwner relationKey - anotherOwner.relation("relationKey").add(objects); + anotherOwner.relation('relationKey').add(objects); // Set each object[i] into owner[i]; owners.forEach((owner, i) => { - owner.set("key", objects[i]); + owner.set('key', objects[i]); }); return Parse.Object.saveAll(owners.concat([anotherOwner])); }) .then(() => { // Query on the relation of another owner - const object = new Parse.Object("AnotherOwner"); + const object = new Parse.Object('AnotherOwner'); object.id = anotherOwner.id; - const relationQuery = object.relation("relationKey").query(); + const relationQuery = object.relation('relationKey').query(); // Just get the even ones - relationQuery.equalTo("even", true); + relationQuery.equalTo('even', true); // Make the query on anOwner - const query = new Parse.Query("AnOwner"); + const query = new Parse.Query('AnOwner'); // where key match the relation query. - query.matchesQuery("key", relationQuery); - query.include("key"); + query.matchesQuery('key', relationQuery); + query.include('key'); return query.find(); }) .then(results => { expect(results.length).toBe(5); results.forEach(result => { - expect(result.get("key").get("even")).toBe(true); + expect(result.get('key').get('even')).toBe(true); }); return Promise.resolve(); }) .then(() => { // Query on the relation of another owner - const object = new Parse.Object("AnotherOwner"); + const object = new Parse.Object('AnotherOwner'); object.id = anotherOwner.id; - const relationQuery = object.relation("relationKey").query(); + const relationQuery = object.relation('relationKey').query(); // Just get the even ones - relationQuery.equalTo("even", true); + relationQuery.equalTo('even', true); // Make the query on anOwner - const query = new Parse.Query("AnOwner"); + const query = new Parse.Query('AnOwner'); // where key match the relation query. - query.doesNotMatchQuery("key", relationQuery); - query.include("key"); + query.doesNotMatchQuery('key', relationQuery); + query.include('key'); return query.find(); }) .then( results => { expect(results.length).toBe(5); results.forEach(result => { - expect(result.get("key").get("even")).toBe(false); + expect(result.get('key').get('even')).toBe(false); }); done(); }, @@ -648,39 +648,39 @@ describe("Parse.Relation testing", () => { ); }); - it("select query", function (done) { - const RestaurantObject = Parse.Object.extend("Restaurant"); - const PersonObject = Parse.Object.extend("Person"); - const OwnerObject = Parse.Object.extend("Owner"); + it('select query', function (done) { + const RestaurantObject = Parse.Object.extend('Restaurant'); + const PersonObject = Parse.Object.extend('Person'); + const OwnerObject = Parse.Object.extend('Owner'); const restaurants = [ - new RestaurantObject({ ratings: 5, location: "Djibouti" }), - new RestaurantObject({ ratings: 3, location: "Ouagadougou" }), + new RestaurantObject({ ratings: 5, location: 'Djibouti' }), + new RestaurantObject({ ratings: 3, location: 'Ouagadougou' }), ]; const persons = [ - new PersonObject({ name: "Bob", hometown: "Djibouti" }), - new PersonObject({ name: "Tom", hometown: "Ouagadougou" }), - new PersonObject({ name: "Billy", hometown: "Detroit" }), + new PersonObject({ name: 'Bob', hometown: 'Djibouti' }), + new PersonObject({ name: 'Tom', hometown: 'Ouagadougou' }), + new PersonObject({ name: 'Billy', hometown: 'Detroit' }), ]; - const owner = new OwnerObject({ name: "Joe" }); + const owner = new OwnerObject({ name: 'Joe' }); const allObjects = [owner].concat(restaurants).concat(persons); expect(allObjects.length).toEqual(6); Parse.Object.saveAll([owner].concat(restaurants).concat(persons)) .then(function () { - owner.relation("restaurants").add(restaurants); + owner.relation('restaurants').add(restaurants); return owner.save(); }) .then( async () => { const unfetchedOwner = new OwnerObject(); unfetchedOwner.id = owner.id; - const query = unfetchedOwner.relation("restaurants").query(); - query.greaterThan("ratings", 4); + const query = unfetchedOwner.relation('restaurants').query(); + query.greaterThan('ratings', 4); const mainQuery = new Parse.Query(PersonObject); - mainQuery.matchesKeyInQuery("hometown", "location", query); + mainQuery.matchesKeyInQuery('hometown', 'location', query); const results = await mainQuery.find(); equal(results.length, 1); if (results.length > 0) { - equal(results[0].get("name"), "Bob"); + equal(results[0].get('name'), 'Bob'); } done(); }, @@ -691,41 +691,41 @@ describe("Parse.Relation testing", () => { ); }); - it("dontSelect query", function (done) { - const RestaurantObject = Parse.Object.extend("Restaurant"); - const PersonObject = Parse.Object.extend("Person"); - const OwnerObject = Parse.Object.extend("Owner"); + it('dontSelect query', function (done) { + const RestaurantObject = Parse.Object.extend('Restaurant'); + const PersonObject = Parse.Object.extend('Person'); + const OwnerObject = Parse.Object.extend('Owner'); const restaurants = [ - new RestaurantObject({ ratings: 5, location: "Djibouti" }), - new RestaurantObject({ ratings: 3, location: "Ouagadougou" }), + new RestaurantObject({ ratings: 5, location: 'Djibouti' }), + new RestaurantObject({ ratings: 3, location: 'Ouagadougou' }), ]; const persons = [ - new PersonObject({ name: "Bob", hometown: "Djibouti" }), - new PersonObject({ name: "Tom", hometown: "Ouagadougou" }), - new PersonObject({ name: "Billy", hometown: "Detroit" }), + new PersonObject({ name: 'Bob', hometown: 'Djibouti' }), + new PersonObject({ name: 'Tom', hometown: 'Ouagadougou' }), + new PersonObject({ name: 'Billy', hometown: 'Detroit' }), ]; - const owner = new OwnerObject({ name: "Joe" }); + const owner = new OwnerObject({ name: 'Joe' }); const allObjects = [owner].concat(restaurants).concat(persons); expect(allObjects.length).toEqual(6); Parse.Object.saveAll([owner].concat(restaurants).concat(persons)) .then(function () { - owner.relation("restaurants").add(restaurants); + owner.relation('restaurants').add(restaurants); return owner.save(); }) .then( async () => { const unfetchedOwner = new OwnerObject(); unfetchedOwner.id = owner.id; - const query = unfetchedOwner.relation("restaurants").query(); - query.greaterThan("ratings", 4); + const query = unfetchedOwner.relation('restaurants').query(); + query.greaterThan('ratings', 4); const mainQuery = new Parse.Query(PersonObject); - mainQuery.doesNotMatchKeyInQuery("hometown", "location", query); - mainQuery.ascending("name"); + mainQuery.doesNotMatchKeyInQuery('hometown', 'location', query); + mainQuery.ascending('name'); const results = await mainQuery.find(); equal(results.length, 2); if (results.length > 0) { - equal(results[0].get("name"), "Billy"); - equal(results[1].get("name"), "Tom"); + equal(results[0].get('name'), 'Billy'); + equal(results[1].get('name'), 'Tom'); } done(); }, @@ -736,23 +736,23 @@ describe("Parse.Relation testing", () => { ); }); - it("relations are not bidirectional (regression test for #871)", done => { - const PersonObject = Parse.Object.extend("Person"); + it('relations are not bidirectional (regression test for #871)', done => { + const PersonObject = Parse.Object.extend('Person'); const p1 = new PersonObject(); const p2 = new PersonObject(); Parse.Object.saveAll([p1, p2]).then(results => { const p1 = results[0]; const p2 = results[1]; - const relation = p1.relation("relation"); + const relation = p1.relation('relation'); relation.add(p2); p1.save().then(() => { const query = new Parse.Query(PersonObject); - query.equalTo("relation", p1); + query.equalTo('relation', p1); query.find().then(results => { expect(results.length).toEqual(0); const query = new Parse.Query(PersonObject); - query.equalTo("relation", p2); + query.equalTo('relation', p2); query.find().then(results => { expect(results.length).toEqual(1); expect(results[0].objectId).toEqual(p1.objectId); @@ -763,32 +763,32 @@ describe("Parse.Relation testing", () => { }); }); - it("can query roles in Cloud Code (regession test #1489)", done => { - Parse.Cloud.define("isAdmin", request => { + it('can query roles in Cloud Code (regession test #1489)', done => { + Parse.Cloud.define('isAdmin', request => { const query = new Parse.Query(Parse.Role); - query.equalTo("name", "admin"); + query.equalTo('name', 'admin'); return query.first({ useMasterKey: true }).then( role => { - const relation = new Parse.Relation(role, "users"); + const relation = new Parse.Relation(role, 'users'); const admins = relation.query(); - admins.equalTo("username", request.user.get("username")); + admins.equalTo('username', request.user.get('username')); admins.first({ useMasterKey: true }).then( user => { if (user) { done(); } else { - fail("Should have found admin user, found nothing instead"); + fail('Should have found admin user, found nothing instead'); done(); } }, () => { - fail("User not admin"); + fail('User not admin'); done(); } ); }, error => { - fail("Should have found admin user, errored instead"); + fail('Should have found admin user, errored instead'); fail(error); done(); } @@ -796,107 +796,107 @@ describe("Parse.Relation testing", () => { }); const adminUser = new Parse.User(); - adminUser.set("username", "name"); - adminUser.set("password", "pass"); + adminUser.set('username', 'name'); + adminUser.set('password', 'pass'); adminUser.signUp().then( adminUser => { const adminACL = new Parse.ACL(); adminACL.setPublicReadAccess(true); // Create admin role - const adminRole = new Parse.Role("admin", adminACL); + const adminRole = new Parse.Role('admin', adminACL); adminRole.getUsers().add(adminUser); adminRole.save().then( () => { - Parse.Cloud.run("isAdmin"); + Parse.Cloud.run('isAdmin'); }, error => { - fail("failed to save role"); + fail('failed to save role'); fail(error); done(); } ); }, error => { - fail("failed to sign up"); + fail('failed to sign up'); fail(error); done(); } ); }); - it("can be saved without error", done => { - const obj1 = new Parse.Object("PPAP"); + it('can be saved without error', done => { + const obj1 = new Parse.Object('PPAP'); obj1.save().then( () => { - const newRelation = obj1.relation("aRelation"); + const newRelation = obj1.relation('aRelation'); newRelation.add(obj1); obj1.save().then( () => { - const relation = obj1.get("aRelation"); - obj1.set("aRelation", relation); + const relation = obj1.get('aRelation'); + obj1.set('aRelation', relation); obj1.save().then( () => { done(); }, error => { - fail("failed to save ParseRelation object"); + fail('failed to save ParseRelation object'); fail(error); done(); } ); }, error => { - fail("failed to create relation field"); + fail('failed to create relation field'); fail(error); done(); } ); }, error => { - fail("failed to save obj"); + fail('failed to save obj'); fail(error); done(); } ); }); - it("ensures beforeFind on relation doesnt side effect", done => { - const parent = new Parse.Object("Parent"); - const child = new Parse.Object("Child"); + it('ensures beforeFind on relation doesnt side effect', done => { + const parent = new Parse.Object('Parent'); + const child = new Parse.Object('Child'); child .save() .then(() => { - parent.relation("children").add(child); + parent.relation('children').add(child); return parent.save(); }) .then(() => { // We need to use a new reference otherwise the JS SDK remembers the className for a relation // After saves or finds - const otherParent = new Parse.Object("Parent"); + const otherParent = new Parse.Object('Parent'); otherParent.id = parent.id; - return otherParent.relation("children").query().find(); + return otherParent.relation('children').query().find(); }) .then(children => { // Without an after find all is good, all results have been redirected with proper className - children.forEach(child => expect(child.className).toBe("Child")); + children.forEach(child => expect(child.className).toBe('Child')); // Setup the afterFind - Parse.Cloud.afterFind("Child", req => { + Parse.Cloud.afterFind('Child', req => { return Promise.resolve( req.objects.map(child => { - child.set("afterFound", true); + child.set('afterFound', true); return child; }) ); }); - const otherParent = new Parse.Object("Parent"); + const otherParent = new Parse.Object('Parent'); otherParent.id = parent.id; - return otherParent.relation("children").query().find(); + return otherParent.relation('children').query().find(); }) .then(children => { children.forEach(child => { - expect(child.className).toBe("Child"); - expect(child.get("afterFound")).toBe(true); + expect(child.className).toBe('Child'); + expect(child.get('afterFound')).toBe(true); }); }) .then(done) diff --git a/spec/ParseRole.spec.js b/spec/ParseRole.spec.js index cd616a767d..30b6d083dc 100644 --- a/spec/ParseRole.spec.js +++ b/spec/ParseRole.spec.js @@ -1,32 +1,32 @@ -"use strict"; +'use strict'; // Roles are not accessible without the master key, so they are not intended // for use by clients. We can manually test them using the master key. -const RestQuery = require("../lib/RestQuery"); -const Auth = require("../lib/Auth").Auth; -const Config = require("../lib/Config"); +const RestQuery = require('../lib/RestQuery'); +const Auth = require('../lib/Auth').Auth; +const Config = require('../lib/Config'); function testLoadRoles(config, done) { - const rolesNames = ["FooRole", "BarRole", "BazRole"]; + const rolesNames = ['FooRole', 'BarRole', 'BazRole']; const roleIds = {}; createTestUser() .then(user => { // Put the user on the 1st role return createRole(rolesNames[0], null, user) .then(aRole => { - roleIds[aRole.get("name")] = aRole.id; + roleIds[aRole.get('name')] = aRole.id; // set the 1st role as a sibling of the second // user will should have 2 role now return createRole(rolesNames[1], aRole, null); }) .then(anotherRole => { - roleIds[anotherRole.get("name")] = anotherRole.id; + roleIds[anotherRole.get('name')] = anotherRole.id; // set this role as a sibling of the last // the user should now have 3 roles return createRole(rolesNames[2], anotherRole, null); }) .then(lastRole => { - roleIds[lastRole.get("name")] = lastRole.id; + roleIds[lastRole.get('name')] = lastRole.id; const auth = new Auth({ config, isMaster: true, user: user }); return auth._loadRoles(); }); @@ -35,12 +35,12 @@ function testLoadRoles(config, done) { roles => { expect(roles.length).toEqual(3); rolesNames.forEach(name => { - expect(roles.indexOf("role:" + name)).not.toBe(-1); + expect(roles.indexOf('role:' + name)).not.toBe(-1); }); done(); }, function () { - fail("should succeed"); + fail('should succeed'); done(); } ); @@ -49,17 +49,17 @@ function testLoadRoles(config, done) { const createRole = function (name, sibling, user) { const role = new Parse.Role(name, new Parse.ACL()); if (user) { - const users = role.relation("users"); + const users = role.relation('users'); users.add(user); } if (sibling) { - role.relation("roles").add(sibling); + role.relation('roles').add(sibling); } return role.save({}, { useMasterKey: true }); }; -describe("Parse Role testing", () => { - it("Do a bunch of basic role testing", done => { +describe('Parse Role testing', () => { + it('Do a bunch of basic role testing', done => { let user; let role; @@ -69,20 +69,20 @@ describe("Parse Role testing", () => { const acl = new Parse.ACL(); acl.setPublicReadAccess(true); acl.setPublicWriteAccess(false); - role = new Parse.Object("_Role"); - role.set("name", "Foos"); + role = new Parse.Object('_Role'); + role.set('name', 'Foos'); role.setACL(acl); - const users = role.relation("users"); + const users = role.relation('users'); users.add(user); return role.save({}, { useMasterKey: true }); }) .then(() => { - const query = new Parse.Query("_Role"); + const query = new Parse.Query('_Role'); return query.find({ useMasterKey: true }); }) .then(x => { expect(x.length).toEqual(1); - const relation = x[0].relation("users").query(); + const relation = x[0].relation('users').query(); return relation.first({ useMasterKey: true }); }) .then(x => { @@ -90,34 +90,34 @@ describe("Parse Role testing", () => { // Here we've got a valid role and a user assigned. // Lets create an object only the role can read/write and test // the different scenarios. - const obj = new Parse.Object("TestObject"); + const obj = new Parse.Object('TestObject'); const acl = new Parse.ACL(); acl.setPublicReadAccess(false); acl.setPublicWriteAccess(false); - acl.setRoleReadAccess("Foos", true); - acl.setRoleWriteAccess("Foos", true); + acl.setRoleReadAccess('Foos', true); + acl.setRoleWriteAccess('Foos', true); obj.setACL(acl); return obj.save(); }) .then(() => { - const query = new Parse.Query("TestObject"); + const query = new Parse.Query('TestObject'); return query.find({ sessionToken: user.getSessionToken() }); }) .then(x => { expect(x.length).toEqual(1); const objAgain = x[0]; - objAgain.set("foo", "bar"); + objAgain.set('foo', 'bar'); // This should succeed: return objAgain.save({}, { sessionToken: user.getSessionToken() }); }) .then(x => { - x.set("foo", "baz"); + x.set('foo', 'baz'); // This should fail: - return x.save({}, { sessionToken: "" }); + return x.save({}, { sessionToken: '' }); }) .then( () => { - fail("Should not have been able to save."); + fail('Should not have been able to save.'); }, e => { expect(e.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); @@ -126,11 +126,11 @@ describe("Parse Role testing", () => { ); }); - it_id("b03abe32-e8e4-4666-9b81-9c804aa53400")(it)( - "should not recursively load the same role multiple times", + it_id('b03abe32-e8e4-4666-9b81-9c804aa53400')(it)( + 'should not recursively load the same role multiple times', done => { - const rootRole = "RootRole"; - const roleNames = ["FooRole", "BarRole", "BazRole"]; + const rootRole = 'RootRole'; + const roleNames = ['FooRole', 'BarRole', 'BazRole']; const allRoles = [rootRole].concat(roleNames); const roleObjs = {}; @@ -146,7 +146,7 @@ describe("Parse Role testing", () => { const restExecute = spyOn( RestQuery._UnsafeRestQuery.prototype, - "execute" + 'execute' ).and.callThrough(); let user, auth, getAllRolesSpy; @@ -160,11 +160,11 @@ describe("Parse Role testing", () => { roles.forEach(function (role, i) { // Add all roles to the RootRole if (role.id !== rootRoleObj.id) { - role.relation("roles").add(rootRoleObj); + role.relation('roles').add(rootRoleObj); } // Add all "roleNames" roles to the previous role if (i > 0) { - role.relation("roles").add(roles[i - 1]); + role.relation('roles').add(roles[i - 1]); } }); @@ -172,13 +172,13 @@ describe("Parse Role testing", () => { }) .then(() => { auth = new Auth({ - config: Config.get("test"), + config: Config.get('test'), isMaster: true, user: user, }); getAllRolesSpy = spyOn( auth, - "_getAllRolesNamesForRoleIds" + '_getAllRolesNamesForRoleIds' ).and.callThrough(); return auth._loadRoles(); @@ -187,7 +187,7 @@ describe("Parse Role testing", () => { expect(roles.length).toEqual(4); allRoles.forEach(function (name) { - expect(roles.indexOf("role:" + name)).not.toBe(-1); + expect(roles.indexOf('role:' + name)).not.toBe(-1); }); // 1 Query for the initial setup @@ -200,32 +200,32 @@ describe("Parse Role testing", () => { done(); }) .catch(() => { - fail("should succeed"); + fail('should succeed'); done(); }); } ); - it("should recursively load roles", done => { - testLoadRoles(Config.get("test"), done); + it('should recursively load roles', done => { + testLoadRoles(Config.get('test'), done); }); - it("should recursively load roles without config", done => { + it('should recursively load roles without config', done => { testLoadRoles(undefined, done); }); - it("_Role object should not save without name.", done => { + it('_Role object should not save without name.', done => { const role = new Parse.Role(); role.save(null, { useMasterKey: true }).then( () => { - fail("_Role object should not save without name."); + fail('_Role object should not save without name.'); }, error => { expect(error.code).toEqual(111); - role.set("name", "testRole"); + role.set('name', 'testRole'); role.save(null, { useMasterKey: true }).then( () => { - fail("_Role object should not save without ACL."); + fail('_Role object should not save without ACL.'); }, error2 => { expect(error2.code).toEqual(111); @@ -236,9 +236,9 @@ describe("Parse Role testing", () => { ); }); - it("Different _Role objects cannot have the same name.", async done => { + it('Different _Role objects cannot have the same name.', async done => { await reconfigureServer(); - const roleName = "MyRole"; + const roleName = 'MyRole'; let aUser; createTestUser() .then(user => { @@ -251,7 +251,7 @@ describe("Parse Role testing", () => { }) .then( () => { - fail("_Role cannot have the same name as another role"); + fail('_Role cannot have the same name as another role'); done(); }, error => { @@ -261,13 +261,13 @@ describe("Parse Role testing", () => { ); }); - it("Should properly resolve roles", done => { - const admin = new Parse.Role("Admin", new Parse.ACL()); - const moderator = new Parse.Role("Moderator", new Parse.ACL()); - const superModerator = new Parse.Role("SuperModerator", new Parse.ACL()); - const contentManager = new Parse.Role("ContentManager", new Parse.ACL()); + it('Should properly resolve roles', done => { + const admin = new Parse.Role('Admin', new Parse.ACL()); + const moderator = new Parse.Role('Moderator', new Parse.ACL()); + const superModerator = new Parse.Role('SuperModerator', new Parse.ACL()); + const contentManager = new Parse.Role('ContentManager', new Parse.ACL()); const superContentManager = new Parse.Role( - "SuperContentManager", + 'SuperContentManager', new Parse.ACL() ); Parse.Object.saveAll( @@ -292,7 +292,7 @@ describe("Parse Role testing", () => { ); }) .then(() => { - const auth = new Auth({ config: Config.get("test"), isMaster: true }); + const auth = new Auth({ config: Config.get('test'), isMaster: true }); // For each role, fetch their sibling, what they inherit // return with result and roleId for later comparison const promises = [admin, moderator, contentManager, superModerator].map( @@ -300,7 +300,7 @@ describe("Parse Role testing", () => { return auth._getAllRolesNamesForRoleIds([role.id]).then(result => { return Promise.resolve({ id: role.id, - name: role.get("name"), + name: role.get('name'), roleNames: result, }); }); @@ -315,18 +315,18 @@ describe("Parse Role testing", () => { const roleNames = result.roleNames; if (id == admin.id) { expect(roleNames.length).toBe(2); - expect(roleNames.indexOf("Moderator")).not.toBe(-1); - expect(roleNames.indexOf("ContentManager")).not.toBe(-1); + expect(roleNames.indexOf('Moderator')).not.toBe(-1); + expect(roleNames.indexOf('ContentManager')).not.toBe(-1); } else if (id == moderator.id) { expect(roleNames.length).toBe(1); - expect(roleNames.indexOf("ContentManager")).toBe(0); + expect(roleNames.indexOf('ContentManager')).toBe(0); } else if (id == contentManager.id) { expect(roleNames.length).toBe(0); } else if (id == superModerator.id) { expect(roleNames.length).toBe(3); - expect(roleNames.indexOf("Moderator")).not.toBe(-1); - expect(roleNames.indexOf("ContentManager")).not.toBe(-1); - expect(roleNames.indexOf("SuperContentManager")).not.toBe(-1); + expect(roleNames.indexOf('Moderator')).not.toBe(-1); + expect(roleNames.indexOf('ContentManager')).not.toBe(-1); + expect(roleNames.indexOf('SuperContentManager')).not.toBe(-1); } }); done(); @@ -336,31 +336,31 @@ describe("Parse Role testing", () => { }); }); - it("can create role and query empty users", done => { + it('can create role and query empty users', done => { const roleACL = new Parse.ACL(); roleACL.setPublicReadAccess(true); - const role = new Parse.Role("subscribers", roleACL); + const role = new Parse.Role('subscribers', roleACL); role.save({}, { useMasterKey: true }).then( () => { - const query = role.relation("users").query(); + const query = role.relation('users').query(); query.find({ useMasterKey: true }).then( () => { done(); }, () => { - fail("should not have errors"); + fail('should not have errors'); done(); } ); }, () => { - fail("should not have errored"); + fail('should not have errored'); } ); }); // Based on various scenarios described in issues #827 and #683, - it("should properly handle role permissions on objects", done => { + it('should properly handle role permissions on objects', done => { let user, user2, user3; let role, role2, role3; let obj, obj2; @@ -373,94 +373,94 @@ describe("Parse Role testing", () => { .then(x => { user = x; user2 = new Parse.User(); - return user2.save({ username: "user2", password: "omgbbq" }); + return user2.save({ username: 'user2', password: 'omgbbq' }); }) .then(() => { user3 = new Parse.User(); - return user3.save({ username: "user3", password: "omgbbq" }); + return user3.save({ username: 'user3', password: 'omgbbq' }); }) .then(() => { - role = new Parse.Role("Admin", prACL); + role = new Parse.Role('Admin', prACL); role.getUsers().add(user); return role.save({}, { useMasterKey: true }); }) .then(() => { adminACL = new Parse.ACL(); - adminACL.setRoleReadAccess("Admin", true); - adminACL.setRoleWriteAccess("Admin", true); + adminACL.setRoleReadAccess('Admin', true); + adminACL.setRoleWriteAccess('Admin', true); - role2 = new Parse.Role("Super", prACL); + role2 = new Parse.Role('Super', prACL); role2.getUsers().add(user2); return role2.save({}, { useMasterKey: true }); }) .then(() => { superACL = new Parse.ACL(); - superACL.setRoleReadAccess("Super", true); - superACL.setRoleWriteAccess("Super", true); + superACL.setRoleReadAccess('Super', true); + superACL.setRoleWriteAccess('Super', true); role.getRoles().add(role2); return role.save({}, { useMasterKey: true }); }) .then(() => { - role3 = new Parse.Role("Customer", prACL); + role3 = new Parse.Role('Customer', prACL); role3.getUsers().add(user3); role3.getRoles().add(role); return role3.save({}, { useMasterKey: true }); }) .then(() => { customerACL = new Parse.ACL(); - customerACL.setRoleReadAccess("Customer", true); - customerACL.setRoleWriteAccess("Customer", true); + customerACL.setRoleReadAccess('Customer', true); + customerACL.setRoleWriteAccess('Customer', true); - const query = new Parse.Query("_Role"); + const query = new Parse.Query('_Role'); return query.find({ useMasterKey: true }); }) .then(x => { expect(x.length).toEqual(3); - obj = new Parse.Object("TestObjectRoles"); - obj.set("ACL", customerACL); + obj = new Parse.Object('TestObjectRoles'); + obj.set('ACL', customerACL); return obj.save(null, { useMasterKey: true }); }) .then(() => { // Above, the Admin role was added to the Customer role. // An object secured by the Customer ACL should be able to be edited by the Admin user. - obj.set("changedByAdmin", true); + obj.set('changedByAdmin', true); return obj.save(null, { sessionToken: user.getSessionToken() }); }) .then( () => { - obj2 = new Parse.Object("TestObjectRoles"); - obj2.set("ACL", adminACL); + obj2 = new Parse.Object('TestObjectRoles'); + obj2.set('ACL', adminACL); return obj2.save(null, { useMasterKey: true }); }, () => { - fail("Admin user should have been able to save."); + fail('Admin user should have been able to save.'); done(); } ) .then(() => { // An object secured by the Admin ACL should not be able to be edited by a Customer role user. - obj2.set("changedByCustomer", true); + obj2.set('changedByCustomer', true); return obj2.save(null, { sessionToken: user3.getSessionToken() }); }) .then( () => { - fail("Customer user should not have been able to save."); + fail('Customer user should not have been able to save.'); done(); }, e => { if (e) { expect(e.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); } else { - fail("should return an error"); + fail('should return an error'); } done(); } ); }); - it("should add multiple users to a role and remove users", done => { + it('should add multiple users to a role and remove users', done => { let user, user2, user3; let role; let obj; @@ -473,15 +473,15 @@ describe("Parse Role testing", () => { .then(x => { user = x; user2 = new Parse.User(); - return user2.save({ username: "user2", password: "omgbbq" }); + return user2.save({ username: 'user2', password: 'omgbbq' }); }) .then(() => { user3 = new Parse.User(); - return user3.save({ username: "user3", password: "omgbbq" }); + return user3.save({ username: 'user3', password: 'omgbbq' }); }) .then(() => { - role = new Parse.Role("sharedRole", prACL); - const users = role.relation("users"); + role = new Parse.Role('sharedRole', prACL); + const users = role.relation('users'); users.add(user); users.add(user2); users.add(user3); @@ -489,60 +489,60 @@ describe("Parse Role testing", () => { }) .then(() => { // query for saved role and get 3 users - const query = new Parse.Query("_Role"); - query.equalTo("name", "sharedRole"); + const query = new Parse.Query('_Role'); + query.equalTo('name', 'sharedRole'); return query.find({ useMasterKey: true }); }) .then(role => { expect(role.length).toEqual(1); - const users = role[0].relation("users").query(); + const users = role[0].relation('users').query(); return users.find({ useMasterKey: true }); }) .then(users => { expect(users.length).toEqual(3); - obj = new Parse.Object("TestObjectRoles"); - obj.set("ACL", prACL); + obj = new Parse.Object('TestObjectRoles'); + obj.set('ACL', prACL); return obj.save(null, { useMasterKey: true }); }) .then(() => { // Above, the Admin role was added to the Customer role. // An object secured by the Customer ACL should be able to be edited by the Admin user. - obj.set("changedByUsers", true); + obj.set('changedByUsers', true); return obj.save(null, { sessionToken: user.getSessionToken() }); }) .then(() => { // query for saved role and get 3 users - const query = new Parse.Query("_Role"); - query.equalTo("name", "sharedRole"); + const query = new Parse.Query('_Role'); + query.equalTo('name', 'sharedRole'); return query.find({ useMasterKey: true }); }) .then(role => { expect(role.length).toEqual(1); - const users = role[0].relation("users"); + const users = role[0].relation('users'); users.remove(user); users.remove(user3); return role[0].save({}, { useMasterKey: true }); }) .then(role => { - const users = role.relation("users").query(); + const users = role.relation('users').query(); return users.find({ useMasterKey: true }); }) .then(users => { expect(users.length).toEqual(1); - expect(users[0].get("username")).toEqual("user2"); + expect(users[0].get('username')).toEqual('user2'); done(); }); }); - it("should be secure (#3835)", done => { + it('should be secure (#3835)', done => { const acl = new Parse.ACL(); acl.getPublicReadAccess(true); - const role = new Parse.Role("admin", acl); + const role = new Parse.Role('admin', acl); role .save() .then(() => { const user = new Parse.User(); - return user.signUp({ username: "hello", password: "world" }); + return user.signUp({ username: 'hello', password: 'world' }); }) .then(user => { role.getUsers().add(user); @@ -559,19 +559,19 @@ describe("Parse Role testing", () => { .catch(done.fail); }); - it("should match when matching in users relation", done => { + it('should match when matching in users relation', done => { const user = new Parse.User(); - user.save({ username: "admin", password: "admin" }).then(user => { + user.save({ username: 'admin', password: 'admin' }).then(user => { const aCL = new Parse.ACL(); aCL.setPublicReadAccess(true); aCL.setPublicWriteAccess(true); - const role = new Parse.Role("admin", aCL); - const users = role.relation("users"); + const role = new Parse.Role('admin', aCL); + const users = role.relation('users'); users.add(user); role.save({}, { useMasterKey: true }).then(() => { const query = new Parse.Query(Parse.Role); - query.equalTo("name", "admin"); - query.equalTo("users", user); + query.equalTo('name', 'admin'); + query.equalTo('users', user); query.find().then(function (roles) { expect(roles.length).toEqual(1); done(); @@ -580,23 +580,23 @@ describe("Parse Role testing", () => { }); }); - it("should not match any entry when not matching in users relation", done => { + it('should not match any entry when not matching in users relation', done => { const user = new Parse.User(); - user.save({ username: "admin", password: "admin" }).then(user => { + user.save({ username: 'admin', password: 'admin' }).then(user => { const aCL = new Parse.ACL(); aCL.setPublicReadAccess(true); aCL.setPublicWriteAccess(true); - const role = new Parse.Role("admin", aCL); - const users = role.relation("users"); + const role = new Parse.Role('admin', aCL); + const users = role.relation('users'); users.add(user); role.save({}, { useMasterKey: true }).then(() => { const otherUser = new Parse.User(); otherUser - .save({ username: "otherUser", password: "otherUser" }) + .save({ username: 'otherUser', password: 'otherUser' }) .then(otherUser => { const query = new Parse.Query(Parse.Role); - query.equalTo("name", "admin"); - query.equalTo("users", otherUser); + query.equalTo('name', 'admin'); + query.equalTo('users', otherUser); query.find().then(function (roles) { expect(roles.length).toEqual(0); done(); @@ -606,19 +606,19 @@ describe("Parse Role testing", () => { }); }); - it("should not match any entry when searching for null in users relation", done => { + it('should not match any entry when searching for null in users relation', done => { const user = new Parse.User(); - user.save({ username: "admin", password: "admin" }).then(user => { + user.save({ username: 'admin', password: 'admin' }).then(user => { const aCL = new Parse.ACL(); aCL.setPublicReadAccess(true); aCL.setPublicWriteAccess(true); - const role = new Parse.Role("admin", aCL); - const users = role.relation("users"); + const role = new Parse.Role('admin', aCL); + const users = role.relation('users'); users.add(user); role.save({}, { useMasterKey: true }).then(() => { const query = new Parse.Query(Parse.Role); - query.equalTo("name", "admin"); - query.equalTo("users", null); + query.equalTo('name', 'admin'); + query.equalTo('users', null); query.find().then(function (roles) { expect(roles.length).toEqual(0); done(); diff --git a/spec/ParseServer.spec.js b/spec/ParseServer.spec.js index 87a0e16c2a..f2c4b5e0e2 100644 --- a/spec/ParseServer.spec.js +++ b/spec/ParseServer.spec.js @@ -1,18 +1,18 @@ -"use strict"; +'use strict'; /* Tests for ParseServer.js */ -const express = require("express"); -const ParseServer = require("../lib/ParseServer").default; -const path = require("path"); -const { spawn } = require("child_process"); +const express = require('express'); +const ParseServer = require('../lib/ParseServer').default; +const path = require('path'); +const { spawn } = require('child_process'); -describe("Server Url Checks", () => { +describe('Server Url Checks', () => { let server; beforeEach(done => { if (!server) { const app = express(); - app.get("/health", function (req, res) { + app.get('/health', function (req, res) { res.json({ - status: "ok", + status: 'ok', }); }); server = app.listen(13376, undefined, done); @@ -22,19 +22,19 @@ describe("Server Url Checks", () => { }); afterAll(done => { - Parse.serverURL = "http://localhost:8378/1"; + Parse.serverURL = 'http://localhost:8378/1'; server.close(done); }); - it("validate good server url", async () => { - Parse.serverURL = "http://localhost:13376"; + it('validate good server url', async () => { + Parse.serverURL = 'http://localhost:13376'; const response = await ParseServer.verifyServerUrl(); expect(response).toBeTrue(); }); - it("mark bad server url", async () => { - spyOn(console, "warn").and.callFake(() => {}); - Parse.serverURL = "notavalidurl"; + it('mark bad server url', async () => { + spyOn(console, 'warn').and.callFake(() => {}); + Parse.serverURL = 'notavalidurl'; const response = await ParseServer.verifyServerUrl(); expect(response).not.toBeTrue(); expect(console.warn).toHaveBeenCalledWith( @@ -42,22 +42,22 @@ describe("Server Url Checks", () => { ); }); - it("does not have unhandled promise rejection in the case of load error", done => { + it('does not have unhandled promise rejection in the case of load error', done => { const parseServerProcess = spawn( - path.resolve(__dirname, "./support/FailingServer.js") + path.resolve(__dirname, './support/FailingServer.js') ); let stdout; let stderr; - parseServerProcess.stdout.on("data", data => { + parseServerProcess.stdout.on('data', data => { stdout = data.toString(); }); - parseServerProcess.stderr.on("data", data => { + parseServerProcess.stderr.on('data', data => { stderr = data.toString(); }); - parseServerProcess.on("close", async code => { + parseServerProcess.on('close', async code => { expect(code).toEqual(1); - expect(stdout).not.toContain("UnhandledPromiseRejectionWarning"); - expect(stderr).toContain("MongoServerSelectionError"); + expect(stdout).not.toContain('UnhandledPromiseRejectionWarning'); + expect(stderr).toContain('MongoServerSelectionError'); await reconfigureServer(); done(); }); diff --git a/spec/ParseServerRESTController.spec.js b/spec/ParseServerRESTController.spec.js index b4baa51ea5..c51a976e85 100644 --- a/spec/ParseServerRESTController.spec.js +++ b/spec/ParseServerRESTController.spec.js @@ -1,66 +1,66 @@ const ParseServerRESTController = - require("../lib/ParseServerRESTController").ParseServerRESTController; -const ParseServer = require("../lib/ParseServer").default; -const Parse = require("parse/node").Parse; + require('../lib/ParseServerRESTController').ParseServerRESTController; +const ParseServer = require('../lib/ParseServer').default; +const Parse = require('parse/node').Parse; let RESTController; -describe("ParseServerRESTController", () => { +describe('ParseServerRESTController', () => { let createSpy; beforeEach(() => { RESTController = ParseServerRESTController( Parse.applicationId, ParseServer.promiseRouter({ appId: Parse.applicationId }) ); - createSpy = spyOn(databaseAdapter, "createObject").and.callThrough(); + createSpy = spyOn(databaseAdapter, 'createObject').and.callThrough(); }); - it("should handle a get request", async () => { - const res = await RESTController.request("GET", "/classes/MyObject"); + it('should handle a get request', async () => { + const res = await RESTController.request('GET', '/classes/MyObject'); expect(res.results.length).toBe(0); }); - it("should handle a get request with full serverURL mount path", async () => { - const res = await RESTController.request("GET", "/1/classes/MyObject"); + it('should handle a get request with full serverURL mount path', async () => { + const res = await RESTController.request('GET', '/1/classes/MyObject'); expect(res.results.length).toBe(0); }); - it("should handle a POST batch without transaction", async () => { - const res = await RESTController.request("POST", "batch", { + it('should handle a POST batch without transaction', async () => { + const res = await RESTController.request('POST', 'batch', { requests: [ { - method: "GET", - path: "/classes/MyObject", + method: 'GET', + path: '/classes/MyObject', }, { - method: "POST", - path: "/classes/MyObject", - body: { key: "value" }, + method: 'POST', + path: '/classes/MyObject', + body: { key: 'value' }, }, { - method: "GET", - path: "/classes/MyObject", + method: 'GET', + path: '/classes/MyObject', }, ], }); expect(res.length).toBe(3); }); - it("should handle a POST batch with transaction=false", async () => { - const res = await RESTController.request("POST", "batch", { + it('should handle a POST batch with transaction=false', async () => { + const res = await RESTController.request('POST', 'batch', { requests: [ { - method: "GET", - path: "/classes/MyObject", + method: 'GET', + path: '/classes/MyObject', }, { - method: "POST", - path: "/classes/MyObject", - body: { key: "value" }, + method: 'POST', + path: '/classes/MyObject', + body: { key: 'value' }, }, { - method: "GET", - path: "/classes/MyObject", + method: 'GET', + path: '/classes/MyObject', }, ], transaction: false, @@ -68,11 +68,11 @@ describe("ParseServerRESTController", () => { expect(res.length).toBe(3); }); - it("should handle response status", async () => { + it('should handle response status', async () => { const router = ParseServer.promiseRouter({ appId: Parse.applicationId }); - spyOn(router, "tryRouteRequest").and.callThrough(); + spyOn(router, 'tryRouteRequest').and.callThrough(); RESTController = ParseServerRESTController(Parse.applicationId, router); - const resp = await RESTController.request("POST", "/classes/MyObject"); + const resp = await RESTController.request('POST', '/classes/MyObject'); const { status, response, location } = await router.tryRouteRequest.calls.all()[0].returnValue; @@ -83,22 +83,22 @@ describe("ParseServerRESTController", () => { ); }); - it("should handle response status in batch", async () => { + it('should handle response status in batch', async () => { const router = ParseServer.promiseRouter({ appId: Parse.applicationId }); - spyOn(router, "tryRouteRequest").and.callThrough(); + spyOn(router, 'tryRouteRequest').and.callThrough(); RESTController = ParseServerRESTController(Parse.applicationId, router); const resp = await RESTController.request( - "POST", - "batch", + 'POST', + 'batch', { requests: [ { - method: "POST", - path: "/classes/MyObject", + method: 'POST', + path: '/classes/MyObject', }, { - method: "POST", - path: "/classes/MyObject", + method: 'POST', + path: '/classes/MyObject', }, ], }, @@ -114,45 +114,45 @@ describe("ParseServerRESTController", () => { expect(router.tryRouteRequest.calls.all().length).toBe(2); }); - it("properly handle existed", async done => { + it('properly handle existed', async done => { const restController = Parse.CoreManager.getRESTController(); Parse.CoreManager.setRESTController(RESTController); - Parse.Cloud.define("handleStatus", async () => { - const obj = new Parse.Object("TestObject"); + Parse.Cloud.define('handleStatus', async () => { + const obj = new Parse.Object('TestObject'); expect(obj.existed()).toBe(false); await obj.save(); expect(obj.existed()).toBe(false); - const query = new Parse.Query("TestObject"); + const query = new Parse.Query('TestObject'); const result = await query.get(obj.id); expect(result.existed()).toBe(true); Parse.CoreManager.setRESTController(restController); done(); }); - await Parse.Cloud.run("handleStatus"); + await Parse.Cloud.run('handleStatus'); }); if ( - process.env.MONGODB_TOPOLOGY === "replicaset" || - process.env.PARSE_SERVER_TEST_DB === "postgres" + process.env.MONGODB_TOPOLOGY === 'replicaset' || + process.env.PARSE_SERVER_TEST_DB === 'postgres' ) { - describe("transactions", () => { - it("should handle a batch request with transaction = true", async () => { - const myObject = new Parse.Object("MyObject"); // This is important because transaction only works on pre-existing collections + describe('transactions', () => { + it('should handle a batch request with transaction = true', async () => { + const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections await myObject.save(); await myObject.destroy(); createSpy.calls.reset(); - const response = await RESTController.request("POST", "batch", { + const response = await RESTController.request('POST', 'batch', { requests: [ { - method: "POST", - path: "/1/classes/MyObject", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject", - body: { key: "value2" }, + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value2' }, }, ], transaction: true, @@ -162,7 +162,7 @@ describe("ParseServerRESTController", () => { expect(response[0].success.createdAt).toBeDefined(); expect(response[1].success.objectId).toBeDefined(); expect(response[1].success.createdAt).toBeDefined(); - const query = new Parse.Query("MyObject"); + const query = new Parse.Query('MyObject'); const results = await query.find(); expect(createSpy.calls.count()).toBe(2); for (let i = 0; i + 1 < createSpy.calls.length; i = i + 2) { @@ -170,109 +170,109 @@ describe("ParseServerRESTController", () => { createSpy.calls.argsFor(i + 1)[3] ); } - expect(results.map(result => result.get("key")).sort()).toEqual([ - "value1", - "value2", + expect(results.map(result => result.get('key')).sort()).toEqual([ + 'value1', + 'value2', ]); }); - it("should not save anything when one operation fails in a transaction", async () => { - const myObject = new Parse.Object("MyObject"); // This is important because transaction only works on pre-existing collections - await myObject.save({ key: "stringField" }); + it('should not save anything when one operation fails in a transaction', async () => { + const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections + await myObject.save({ key: 'stringField' }); await myObject.destroy(); createSpy.calls.reset(); try { // Saving a number to a string field should fail - await RESTController.request("POST", "batch", { + await RESTController.request('POST', 'batch', { requests: [ { - method: "POST", - path: "/1/classes/MyObject", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject", + method: 'POST', + path: '/1/classes/MyObject', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject", + method: 'POST', + path: '/1/classes/MyObject', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject", + method: 'POST', + path: '/1/classes/MyObject', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject", + method: 'POST', + path: '/1/classes/MyObject', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject", + method: 'POST', + path: '/1/classes/MyObject', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject", + method: 'POST', + path: '/1/classes/MyObject', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject", + method: 'POST', + path: '/1/classes/MyObject', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject", + method: 'POST', + path: '/1/classes/MyObject', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject", + method: 'POST', + path: '/1/classes/MyObject', body: { key: 10 }, }, ], @@ -281,142 +281,142 @@ describe("ParseServerRESTController", () => { fail(); } catch (error) { expect(error).toBeDefined(); - const query = new Parse.Query("MyObject"); + const query = new Parse.Query('MyObject'); const results = await query.find(); expect(results.length).toBe(0); } }); - it("should generate separate session for each call", async () => { - const myObject = new Parse.Object("MyObject"); // This is important because transaction only works on pre-existing collections - await myObject.save({ key: "stringField" }); + it('should generate separate session for each call', async () => { + const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections + await myObject.save({ key: 'stringField' }); await myObject.destroy(); - const myObject2 = new Parse.Object("MyObject2"); // This is important because transaction only works on pre-existing collections - await myObject2.save({ key: "stringField" }); + const myObject2 = new Parse.Object('MyObject2'); // This is important because transaction only works on pre-existing collections + await myObject2.save({ key: 'stringField' }); await myObject2.destroy(); createSpy.calls.reset(); let myObjectCalls = 0; - Parse.Cloud.beforeSave("MyObject", async () => { + Parse.Cloud.beforeSave('MyObject', async () => { myObjectCalls++; if (myObjectCalls === 2) { try { // Saving a number to a string field should fail - await RESTController.request("POST", "batch", { + await RESTController.request('POST', 'batch', { requests: [ { - method: "POST", - path: "/1/classes/MyObject2", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject2", + method: 'POST', + path: '/1/classes/MyObject2', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject2", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject2", + method: 'POST', + path: '/1/classes/MyObject2', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject2", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject2", + method: 'POST', + path: '/1/classes/MyObject2', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject2", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject2", + method: 'POST', + path: '/1/classes/MyObject2', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject2", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject2", + method: 'POST', + path: '/1/classes/MyObject2', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject2", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject2", + method: 'POST', + path: '/1/classes/MyObject2', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject2", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject2", + method: 'POST', + path: '/1/classes/MyObject2', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject2", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject2", + method: 'POST', + path: '/1/classes/MyObject2', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject2", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject2", + method: 'POST', + path: '/1/classes/MyObject2', body: { key: 10 }, }, ], transaction: true, }); - fail("should fail"); + fail('should fail'); } catch (e) { expect(e).toBeDefined(); } } }); - const response = await RESTController.request("POST", "batch", { + const response = await RESTController.request('POST', 'batch', { requests: [ { - method: "POST", - path: "/1/classes/MyObject", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject", - body: { key: "value2" }, + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value2' }, }, ], transaction: true, @@ -428,37 +428,37 @@ describe("ParseServerRESTController", () => { expect(response[1].success.objectId).toBeDefined(); expect(response[1].success.createdAt).toBeDefined(); - await RESTController.request("POST", "batch", { + await RESTController.request('POST', 'batch', { requests: [ { - method: "POST", - path: "/1/classes/MyObject3", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject3', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject3", - body: { key: "value2" }, + method: 'POST', + path: '/1/classes/MyObject3', + body: { key: 'value2' }, }, ], }); - const query = new Parse.Query("MyObject"); + const query = new Parse.Query('MyObject'); const results = await query.find(); - expect(results.map(result => result.get("key")).sort()).toEqual([ - "value1", - "value2", + expect(results.map(result => result.get('key')).sort()).toEqual([ + 'value1', + 'value2', ]); - const query2 = new Parse.Query("MyObject2"); + const query2 = new Parse.Query('MyObject2'); const results2 = await query2.find(); expect(results2.length).toEqual(0); - const query3 = new Parse.Query("MyObject3"); + const query3 = new Parse.Query('MyObject3'); const results3 = await query3.find(); - expect(results3.map(result => result.get("key")).sort()).toEqual([ - "value1", - "value2", + expect(results3.map(result => result.get('key')).sort()).toEqual([ + 'value1', + 'value2', ]); expect(createSpy.calls.count() >= 13).toEqual(true); @@ -470,7 +470,7 @@ describe("ParseServerRESTController", () => { for (let i = 0; i < createSpy.calls.count(); i++) { const args = createSpy.calls.argsFor(i); switch (args[0]) { - case "MyObject": + case 'MyObject': myObjectDBCalls++; if (!transactionalSession || (myObjectDBCalls - 1) % 2 === 0) { transactionalSession = args[3]; @@ -481,7 +481,7 @@ describe("ParseServerRESTController", () => { expect(transactionalSession2).not.toBe(args[3]); } break; - case "MyObject2": + case 'MyObject2': myObject2DBCalls++; if (!transactionalSession2 || (myObject2DBCalls - 1) % 9 === 0) { transactionalSession2 = args[3]; @@ -492,7 +492,7 @@ describe("ParseServerRESTController", () => { expect(transactionalSession).not.toBe(args[3]); } break; - case "MyObject3": + case 'MyObject3': myObject3DBCalls++; expect(args[3]).toEqual(null); break; @@ -508,46 +508,46 @@ describe("ParseServerRESTController", () => { }); } - it("should handle a POST request", async () => { - await RESTController.request("POST", "/classes/MyObject", { key: "value" }); - const res = await RESTController.request("GET", "/classes/MyObject"); + it('should handle a POST request', async () => { + await RESTController.request('POST', '/classes/MyObject', { key: 'value' }); + const res = await RESTController.request('GET', '/classes/MyObject'); expect(res.results.length).toBe(1); - expect(res.results[0].key).toEqual("value"); + expect(res.results[0].key).toEqual('value'); }); - it("should handle a POST request with context", async () => { - Parse.Cloud.beforeSave("MyObject", req => { - expect(req.context.a).toEqual("a"); + it('should handle a POST request with context', async () => { + Parse.Cloud.beforeSave('MyObject', req => { + expect(req.context.a).toEqual('a'); }); - Parse.Cloud.afterSave("MyObject", req => { - expect(req.context.a).toEqual("a"); + Parse.Cloud.afterSave('MyObject', req => { + expect(req.context.a).toEqual('a'); }); await RESTController.request( - "POST", - "/classes/MyObject", - { key: "value" }, - { context: { a: "a" } } + 'POST', + '/classes/MyObject', + { key: 'value' }, + { context: { a: 'a' } } ); }); - it("ensures sessionTokens are properly handled", async () => { - const user = await Parse.User.signUp("user", "pass"); + it('ensures sessionTokens are properly handled', async () => { + const user = await Parse.User.signUp('user', 'pass'); const sessionToken = user.getSessionToken(); - const res = await RESTController.request("GET", "/users/me", undefined, { + const res = await RESTController.request('GET', '/users/me', undefined, { sessionToken, }); // Result is in JSON format expect(res.objectId).toEqual(user.id); }); - it("ensures masterKey is properly handled", async () => { - const user = await Parse.User.signUp("user", "pass"); + it('ensures masterKey is properly handled', async () => { + const user = await Parse.User.signUp('user', 'pass'); const userId = user.id; await Parse.User.logOut(); const res = await RESTController.request( - "GET", - "/classes/_User", + 'GET', + '/classes/_User', undefined, { useMasterKey: true, @@ -557,81 +557,81 @@ describe("ParseServerRESTController", () => { expect(res.results[0].objectId).toEqual(userId); }); - it("ensures no user is created when passing an empty username", async () => { + it('ensures no user is created when passing an empty username', async () => { try { - await RESTController.request("POST", "/classes/_User", { - username: "", - password: "world", + await RESTController.request('POST', '/classes/_User', { + username: '', + password: 'world', }); fail( - "Success callback should not be called when passing an empty username." + 'Success callback should not be called when passing an empty username.' ); } catch (err) { expect(err.code).toBe(Parse.Error.USERNAME_MISSING); - expect(err.message).toBe("bad or missing username"); + expect(err.message).toBe('bad or missing username'); } }); - it("ensures no user is created when passing an empty password", async () => { + it('ensures no user is created when passing an empty password', async () => { try { - await RESTController.request("POST", "/classes/_User", { - username: "hello", - password: "", + await RESTController.request('POST', '/classes/_User', { + username: 'hello', + password: '', }); fail( - "Success callback should not be called when passing an empty password." + 'Success callback should not be called when passing an empty password.' ); } catch (err) { expect(err.code).toBe(Parse.Error.PASSWORD_MISSING); - expect(err.message).toBe("password is required"); + expect(err.message).toBe('password is required'); } }); - it("ensures no session token is created on creating users", async () => { - const user = await RESTController.request("POST", "/classes/_User", { - username: "hello", - password: "world", + it('ensures no session token is created on creating users', async () => { + const user = await RESTController.request('POST', '/classes/_User', { + username: 'hello', + password: 'world', }); expect(user.sessionToken).toBeUndefined(); - const query = new Parse.Query("_Session"); + const query = new Parse.Query('_Session'); const sessions = await query.find({ useMasterKey: true }); expect(sessions.length).toBe(0); }); - it("ensures a session token is created when passing installationId != cloud", async () => { + it('ensures a session token is created when passing installationId != cloud', async () => { const user = await RESTController.request( - "POST", - "/classes/_User", - { username: "hello", password: "world" }, - { installationId: "my-installation" } + 'POST', + '/classes/_User', + { username: 'hello', password: 'world' }, + { installationId: 'my-installation' } ); expect(user.sessionToken).not.toBeUndefined(); - const query = new Parse.Query("_Session"); + const query = new Parse.Query('_Session'); const sessions = await query.find({ useMasterKey: true }); expect(sessions.length).toBe(1); - expect(sessions[0].get("installationId")).toBe("my-installation"); + expect(sessions[0].get('installationId')).toBe('my-installation'); }); - it("ensures logIn is saved with installationId", async () => { - const installationId = "installation123"; + it('ensures logIn is saved with installationId', async () => { + const installationId = 'installation123'; const user = await RESTController.request( - "POST", - "/classes/_User", - { username: "hello", password: "world" }, + 'POST', + '/classes/_User', + { username: 'hello', password: 'world' }, { installationId } ); expect(user.sessionToken).not.toBeUndefined(); - const query = new Parse.Query("_Session"); + const query = new Parse.Query('_Session'); let sessions = await query.find({ useMasterKey: true }); expect(sessions.length).toBe(1); - expect(sessions[0].get("installationId")).toBe(installationId); - expect(sessions[0].get("sessionToken")).toBe(user.sessionToken); + expect(sessions[0].get('installationId')).toBe(installationId); + expect(sessions[0].get('sessionToken')).toBe(user.sessionToken); const loggedUser = await RESTController.request( - "POST", - "/login", - { username: "hello", password: "world" }, + 'POST', + '/login', + { username: 'hello', password: 'world' }, { installationId } ); expect(loggedUser.sessionToken).not.toBeUndefined(); @@ -639,55 +639,55 @@ describe("ParseServerRESTController", () => { // Should clean up old sessions with this installationId expect(sessions.length).toBe(1); - expect(sessions[0].get("installationId")).toBe(installationId); - expect(sessions[0].get("sessionToken")).toBe(loggedUser.sessionToken); + expect(sessions[0].get('installationId')).toBe(installationId); + expect(sessions[0].get('sessionToken')).toBe(loggedUser.sessionToken); }); - it("returns a statusId when running jobs", async () => { - Parse.Cloud.job("CloudJob", () => { - return "Cloud job completed"; + it('returns a statusId when running jobs', async () => { + Parse.Cloud.job('CloudJob', () => { + return 'Cloud job completed'; }); const res = await RESTController.request( - "POST", - "/jobs/CloudJob", + 'POST', + '/jobs/CloudJob', {}, { useMasterKey: true, returnStatus: true } ); - const jobStatusId = res._headers["X-Parse-Job-Status-Id"]; + const jobStatusId = res._headers['X-Parse-Job-Status-Id']; expect(jobStatusId).toBeDefined(); const result = await Parse.Cloud.getJobStatus(jobStatusId); expect(result.id).toBe(jobStatusId); }); - it("returns a statusId when running push notifications", async () => { + it('returns a statusId when running push notifications', async () => { const payload = { - data: { alert: "We return status!" }, - where: { deviceType: "ios" }, + data: { alert: 'We return status!' }, + where: { deviceType: 'ios' }, }; - const res = await RESTController.request("POST", "/push", payload, { + const res = await RESTController.request('POST', '/push', payload, { useMasterKey: true, returnStatus: true, }); - const pushStatusId = res._headers["X-Parse-Push-Status-Id"]; + const pushStatusId = res._headers['X-Parse-Push-Status-Id']; expect(pushStatusId).toBeDefined(); const result = await Parse.Push.getPushStatus(pushStatusId); expect(result.id).toBe(pushStatusId); }); - it("returns a statusId when running batch push notifications", async () => { + it('returns a statusId when running batch push notifications', async () => { const payload = { - data: { alert: "We return status!" }, - where: { deviceType: "ios" }, + data: { alert: 'We return status!' }, + where: { deviceType: 'ios' }, }; const res = await RESTController.request( - "POST", - "batch", + 'POST', + 'batch', { requests: [ { - method: "POST", - path: "/push", + method: 'POST', + path: '/push', body: payload, }, ], @@ -697,7 +697,7 @@ describe("ParseServerRESTController", () => { returnStatus: true, } ); - const pushStatusId = res[0]._headers["X-Parse-Push-Status-Id"]; + const pushStatusId = res[0]._headers['X-Parse-Push-Status-Id']; expect(pushStatusId).toBeDefined(); const result = await Parse.Push.getPushStatus(pushStatusId); diff --git a/spec/ParseSession.spec.js b/spec/ParseSession.spec.js index 43bb0bb79a..45c31106c4 100644 --- a/spec/ParseSession.spec.js +++ b/spec/ParseSession.spec.js @@ -2,8 +2,8 @@ // Tests behavior of Parse Sessions // -"use strict"; -const request = require("../lib/request"); +'use strict'; +const request = require('../lib/request'); function setupTestUsers() { const acl = new Parse.ACL(); @@ -12,13 +12,13 @@ function setupTestUsers() { const user2 = new Parse.User(); const user3 = new Parse.User(); - user1.set("username", "testuser_1"); - user2.set("username", "testuser_2"); - user3.set("username", "testuser_3"); + user1.set('username', 'testuser_1'); + user2.set('username', 'testuser_2'); + user3.set('username', 'testuser_3'); - user1.set("password", "password"); - user2.set("password", "password"); - user3.set("password", "password"); + user1.set('password', 'password'); + user2.set('password', 'password'); + user3.set('password', 'password'); user1.setACL(acl); user2.setACL(acl); @@ -34,24 +34,24 @@ function setupTestUsers() { }); } -describe("Parse.Session", () => { +describe('Parse.Session', () => { // multiple sessions with masterKey + sessionToken - it("should retain original sessionTokens with masterKey & sessionToken set", done => { + it('should retain original sessionTokens with masterKey & sessionToken set', done => { setupTestUsers() .then(user => { const query = new Parse.Query(Parse.Session); return query.find({ useMasterKey: true, - sessionToken: user.get("sessionToken"), + sessionToken: user.get('sessionToken'), }); }) .then(results => { const foundKeys = []; expect(results.length).toBe(3); for (const key in results) { - const sessionToken = results[key].get("sessionToken"); + const sessionToken = results[key].get('sessionToken'); if (foundKeys[sessionToken]) { - fail("Duplicate session token present in response"); + fail('Duplicate session token present in response'); break; } foundKeys[sessionToken] = 1; @@ -64,11 +64,11 @@ describe("Parse.Session", () => { }); // single session returned, with just one sessionToken - it("should retain original sessionTokens with just sessionToken set", done => { + it('should retain original sessionTokens with just sessionToken set', done => { let knownSessionToken; setupTestUsers() .then(user => { - knownSessionToken = user.get("sessionToken"); + knownSessionToken = user.get('sessionToken'); const query = new Parse.Query(Parse.Session); return query.find({ sessionToken: knownSessionToken, @@ -76,7 +76,7 @@ describe("Parse.Session", () => { }) .then(results => { expect(results.length).toBe(1); - const sessionToken = results[0].get("sessionToken"); + const sessionToken = results[0].get('sessionToken'); expect(sessionToken).toBe(knownSessionToken); done(); }) @@ -86,22 +86,22 @@ describe("Parse.Session", () => { }); // multiple users with masterKey + sessionToken - it("token on users should retain original sessionTokens with masterKey & sessionToken set", done => { + it('token on users should retain original sessionTokens with masterKey & sessionToken set', done => { setupTestUsers() .then(user => { const query = new Parse.Query(Parse.User); return query.find({ useMasterKey: true, - sessionToken: user.get("sessionToken"), + sessionToken: user.get('sessionToken'), }); }) .then(results => { const foundKeys = []; expect(results.length).toBe(3); for (const key in results) { - const sessionToken = results[key].get("sessionToken"); + const sessionToken = results[key].get('sessionToken'); if (foundKeys[sessionToken] && sessionToken !== undefined) { - fail("Duplicate session token present in response"); + fail('Duplicate session token present in response'); break; } foundKeys[sessionToken] = 1; @@ -114,11 +114,11 @@ describe("Parse.Session", () => { }); // multiple users with just sessionToken - it("token on users should retain original sessionTokens with just sessionToken set", done => { + it('token on users should retain original sessionTokens with just sessionToken set', done => { let knownSessionToken; setupTestUsers() .then(user => { - knownSessionToken = user.get("sessionToken"); + knownSessionToken = user.get('sessionToken'); const query = new Parse.Query(Parse.User); return query.find({ sessionToken: knownSessionToken, @@ -128,9 +128,9 @@ describe("Parse.Session", () => { const foundKeys = []; expect(results.length).toBe(3); for (const key in results) { - const sessionToken = results[key].get("sessionToken"); + const sessionToken = results[key].get('sessionToken'); if (foundKeys[sessionToken] && sessionToken !== undefined) { - fail("Duplicate session token present in response"); + fail('Duplicate session token present in response'); break; } foundKeys[sessionToken] = 1; @@ -143,32 +143,32 @@ describe("Parse.Session", () => { }); }); - it("cannot edit session with known ID", async () => { + it('cannot edit session with known ID', async () => { await setupTestUsers(); const [first, second] = await new Parse.Query(Parse.Session).find({ useMasterKey: true, }); const headers = { - "X-Parse-Application-Id": "test", - "X-Parse-Rest-API-Key": "rest", - "X-Parse-Session-Token": second.get("sessionToken"), - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Rest-API-Key': 'rest', + 'X-Parse-Session-Token': second.get('sessionToken'), + 'Content-Type': 'application/json', }; - const firstUser = first.get("user").id; - const secondUser = second.get("user").id; + const firstUser = first.get('user').id; + const secondUser = second.get('user').id; const e = await request({ - method: "PUT", + method: 'PUT', headers, url: `http://localhost:8378/1/sessions/${first.id}`, body: JSON.stringify({ - foo: "bar", - user: { __type: "Pointer", className: "_User", objectId: secondUser }, + foo: 'bar', + user: { __type: 'Pointer', className: '_User', objectId: secondUser }, }), }).catch(e => e.data); expect(e.code).toBe(Parse.Error.OBJECT_NOT_FOUND); - expect(e.error).toBe("Object not found."); + expect(e.error).toBe('Object not found.'); await Parse.Object.fetchAll([first, second], { useMasterKey: true }); - expect(first.get("user").id).toBe(firstUser); - expect(second.get("user").id).toBe(secondUser); + expect(first.get('user').id).toBe(firstUser); + expect(second.get('user').id).toBe(secondUser); }); }); diff --git a/spec/ParseUser.spec.js b/spec/ParseUser.spec.js index d25af0eb82..e62b3ff734 100644 --- a/spec/ParseUser.spec.js +++ b/spec/ParseUser.spec.js @@ -5,39 +5,39 @@ // Tests that involve revocable sessions. // Tests that involve sending password reset emails. -"use strict"; +'use strict'; const MongoStorageAdapter = - require("../lib/Adapters/Storage/Mongo/MongoStorageAdapter").default; -const request = require("../lib/request"); -const passwordCrypto = require("../lib/password"); -const Config = require("../lib/Config"); -const cryptoUtils = require("../lib/cryptoUtils"); - -describe("allowExpiredAuthDataToken option", () => { - it("should accept true value", async () => { + require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter').default; +const request = require('../lib/request'); +const passwordCrypto = require('../lib/password'); +const Config = require('../lib/Config'); +const cryptoUtils = require('../lib/cryptoUtils'); + +describe('allowExpiredAuthDataToken option', () => { + it('should accept true value', async () => { await reconfigureServer({ allowExpiredAuthDataToken: true }); expect(Config.get(Parse.applicationId).allowExpiredAuthDataToken).toBe( true ); }); - it("should accept false value", async () => { + it('should accept false value', async () => { await reconfigureServer({ allowExpiredAuthDataToken: false }); expect(Config.get(Parse.applicationId).allowExpiredAuthDataToken).toBe( false ); }); - it("should default false", async () => { + it('should default false', async () => { await reconfigureServer({}); expect(Config.get(Parse.applicationId).allowExpiredAuthDataToken).toBe( false ); }); - it("should enforce boolean values", async () => { - const options = [[], "a", "", 0, 1, {}, "true", "false"]; + it('should enforce boolean values', async () => { + const options = [[], 'a', '', 0, 1, {}, 'true', 'false']; for (const option of options) { await expectAsync( reconfigureServer({ allowExpiredAuthDataToken: option }) @@ -46,25 +46,25 @@ describe("allowExpiredAuthDataToken option", () => { }); }); -describe("Parse.User testing", () => { - it("user sign up class method", async done => { - const user = await Parse.User.signUp("asdf", "zxcv"); +describe('Parse.User testing', () => { + it('user sign up class method', async done => { + const user = await Parse.User.signUp('asdf', 'zxcv'); ok(user.getSessionToken()); done(); }); - it("user sign up instance method", async () => { + it('user sign up instance method', async () => { const user = new Parse.User(); - user.setPassword("asdf"); - user.setUsername("zxcv"); + user.setPassword('asdf'); + user.setUsername('zxcv'); await user.signUp(); ok(user.getSessionToken()); }); - it("user login wrong username", async done => { - await Parse.User.signUp("asdf", "zxcv"); + it('user login wrong username', async done => { + await Parse.User.signUp('asdf', 'zxcv'); try { - await Parse.User.logIn("non_existent_user", "asdf3"); + await Parse.User.logIn('non_existent_user', 'asdf3'); done.fail(); } catch (e) { expect(e.code).toBe(Parse.Error.OBJECT_NOT_FOUND); @@ -72,10 +72,10 @@ describe("Parse.User testing", () => { } }); - it("user login wrong password", async done => { - await Parse.User.signUp("asdf", "zxcv"); + it('user login wrong password', async done => { + await Parse.User.signUp('asdf', 'zxcv'); try { - await Parse.User.logIn("asdf", "asdfWrong"); + await Parse.User.logIn('asdf', 'asdfWrong'); done.fail(); } catch (e) { expect(e.code).toBe(Parse.Error.OBJECT_NOT_FOUND); @@ -83,9 +83,9 @@ describe("Parse.User testing", () => { } }); - it("user login with context", async () => { + it('user login with context', async () => { let hit = 0; - const context = { foo: "bar" }; + const context = { foo: 'bar' }; Parse.Cloud.beforeLogin(req => { expect(req.context).toEqual(context); hit++; @@ -94,39 +94,39 @@ describe("Parse.User testing", () => { expect(req.context).toEqual(context); hit++; }); - await Parse.User.signUp("asdf", "zxcv"); + await Parse.User.signUp('asdf', 'zxcv'); await request({ - method: "POST", - url: "http://localhost:8378/1/login", + method: 'POST', + url: 'http://localhost:8378/1/login', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", - "X-Parse-Cloud-Context": JSON.stringify(context), - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Cloud-Context': JSON.stringify(context), + 'Content-Type': 'application/json', }, body: { - _method: "GET", - username: "asdf", - password: "zxcv", + _method: 'GET', + username: 'asdf', + password: 'zxcv', }, }); expect(hit).toBe(2); }); - it("user login with non-string username with REST API", async done => { - await Parse.User.signUp("asdf", "zxcv"); + it('user login with non-string username with REST API', async done => { + await Parse.User.signUp('asdf', 'zxcv'); request({ - method: "POST", - url: "http://localhost:8378/1/login", + method: 'POST', + url: 'http://localhost:8378/1/login', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, body: { - _method: "GET", - username: { $regex: "^asd" }, - password: "zxcv", + _method: 'GET', + username: { $regex: '^asd' }, + password: 'zxcv', }, }) .then(res => { @@ -142,20 +142,20 @@ describe("Parse.User testing", () => { }); }); - it("user login with non-string username with REST API (again)", async done => { - await Parse.User.signUp("asdf", "zxcv"); + it('user login with non-string username with REST API (again)', async done => { + await Parse.User.signUp('asdf', 'zxcv'); request({ - method: "POST", - url: "http://localhost:8378/1/login", + method: 'POST', + url: 'http://localhost:8378/1/login', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, body: { - _method: "GET", - username: "asdf", - password: { $regex: "^zx" }, + _method: 'GET', + username: 'asdf', + password: { $regex: '^zx' }, }, }) .then(res => { @@ -171,22 +171,22 @@ describe("Parse.User testing", () => { }); }); - it("user login using POST with REST API", async done => { - await Parse.User.signUp("some_user", "some_password"); + it('user login using POST with REST API', async done => { + await Parse.User.signUp('some_user', 'some_password'); request({ - method: "POST", - url: "http://localhost:8378/1/login", + method: 'POST', + url: 'http://localhost:8378/1/login', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', }, body: { - username: "some_user", - password: "some_password", + username: 'some_user', + password: 'some_password', }, }) .then(res => { - expect(res.data.username).toBe("some_user"); + expect(res.data.username).toBe('some_user'); done(); }) .catch(err => { @@ -195,10 +195,10 @@ describe("Parse.User testing", () => { }); }); - it("user login", async done => { - await Parse.User.signUp("asdf", "zxcv"); - const user = await Parse.User.logIn("asdf", "zxcv"); - equal(user.get("username"), "asdf"); + it('user login', async done => { + await Parse.User.signUp('asdf', 'zxcv'); + const user = await Parse.User.logIn('asdf', 'zxcv'); + equal(user.get('username'), 'asdf'); const ACL = user.getACL(); expect(ACL.getReadAccess(user)).toBe(true); expect(ACL.getWriteAccess(user)).toBe(true); @@ -208,25 +208,25 @@ describe("Parse.User testing", () => { expect(Object.keys(perms).length).toBe(1); expect(perms[user.id].read).toBe(true); expect(perms[user.id].write).toBe(true); - expect(perms["*"]).toBeUndefined(); + expect(perms['*']).toBeUndefined(); done(); }); - it("should respect ACL without locking user out", done => { + it('should respect ACL without locking user out', done => { const user = new Parse.User(); const ACL = new Parse.ACL(); ACL.setPublicReadAccess(false); ACL.setPublicWriteAccess(false); - user.setUsername("asdf"); - user.setPassword("zxcv"); + user.setUsername('asdf'); + user.setPassword('zxcv'); user.setACL(ACL); user .signUp() .then(() => { - return Parse.User.logIn("asdf", "zxcv"); + return Parse.User.logIn('asdf', 'zxcv'); }) .then(user => { - equal(user.get("username"), "asdf"); + equal(user.get('username'), 'asdf'); const ACL = user.getACL(); expect(ACL.getReadAccess(user)).toBe(true); expect(ACL.getWriteAccess(user)).toBe(true); @@ -236,7 +236,7 @@ describe("Parse.User testing", () => { expect(Object.keys(perms).length).toBe(1); expect(perms[user.id].read).toBe(true); expect(perms[user.id].write).toBe(true); - expect(perms["*"]).toBeUndefined(); + expect(perms['*']).toBeUndefined(); // Try to lock out user const newACL = new Parse.ACL(); newACL.setReadAccess(user.id, false); @@ -245,10 +245,10 @@ describe("Parse.User testing", () => { return user.save(); }) .then(() => { - return Parse.User.logIn("asdf", "zxcv"); + return Parse.User.logIn('asdf', 'zxcv'); }) .then(user => { - equal(user.get("username"), "asdf"); + equal(user.get('username'), 'asdf'); const ACL = user.getACL(); expect(ACL.getReadAccess(user)).toBe(true); expect(ACL.getWriteAccess(user)).toBe(true); @@ -258,30 +258,30 @@ describe("Parse.User testing", () => { expect(Object.keys(perms).length).toBe(1); expect(perms[user.id].read).toBe(true); expect(perms[user.id].write).toBe(true); - expect(perms["*"]).toBeUndefined(); + expect(perms['*']).toBeUndefined(); done(); }) .catch(() => { - fail("Should not fail"); + fail('Should not fail'); done(); }); }); - it("should let masterKey lockout user", done => { + it('should let masterKey lockout user', done => { const user = new Parse.User(); const ACL = new Parse.ACL(); ACL.setPublicReadAccess(false); ACL.setPublicWriteAccess(false); - user.setUsername("asdf"); - user.setPassword("zxcv"); + user.setUsername('asdf'); + user.setPassword('zxcv'); user.setACL(ACL); user .signUp() .then(() => { - return Parse.User.logIn("asdf", "zxcv"); + return Parse.User.logIn('asdf', 'zxcv'); }) .then(user => { - equal(user.get("username"), "asdf"); + equal(user.get('username'), 'asdf'); // Lock the user down const ACL = new Parse.ACL(); user.setACL(ACL); @@ -289,22 +289,22 @@ describe("Parse.User testing", () => { }) .then(() => { expect(user.getACL().getPublicReadAccess()).toBe(false); - return Parse.User.logIn("asdf", "zxcv"); + return Parse.User.logIn('asdf', 'zxcv'); }) .then(done.fail) .catch(err => { - expect(err.message).toBe("Invalid username/password."); + expect(err.message).toBe('Invalid username/password.'); expect(err.code).toBe(Parse.Error.OBJECT_NOT_FOUND); done(); }); }); - it_only_db("mongo")("should let legacy users without ACL login", async () => { + it_only_db('mongo')('should let legacy users without ACL login', async () => { await reconfigureServer(); const databaseURI = - "mongodb://localhost:27017/parseServerMongoAdapterTestDatabase"; + 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase'; const adapter = new MongoStorageAdapter({ - collectionPrefix: "test_", + collectionPrefix: 'test_', uri: databaseURI, }); await adapter.connect(); @@ -314,45 +314,45 @@ describe("Parse.User testing", () => { const user = new Parse.User(); await user.signUp({ - username: "newUser", - password: "password", + username: 'newUser', + password: 'password', }); - const collection = await adapter._adaptiveCollection("_User"); + const collection = await adapter._adaptiveCollection('_User'); await collection.insertOne({ // the hashed password is 'password' hashed _hashed_password: - "$2b$10$mJ2ca2UbCM9hlojYHZxkQe8pyEXe5YMg0nMdvP4AJBeqlTEZJ6/Uu", - _session_token: "xxx", - email: "xxx@a.b", - username: "oldUser", + '$2b$10$mJ2ca2UbCM9hlojYHZxkQe8pyEXe5YMg0nMdvP4AJBeqlTEZJ6/Uu', + _session_token: 'xxx', + email: 'xxx@a.b', + username: 'oldUser', emailVerified: true, - _email_verify_token: "yyy", + _email_verify_token: 'yyy', }); // get the 2 users const users = await collection.find(); expect(users.length).toBe(2); - const aUser = await Parse.User.logIn("oldUser", "password"); + const aUser = await Parse.User.logIn('oldUser', 'password'); expect(aUser).not.toBeUndefined(); - const newUser = await Parse.User.logIn("newUser", "password"); + const newUser = await Parse.User.logIn('newUser', 'password'); expect(newUser).not.toBeUndefined(); }); - it("should be let masterKey lock user out with authData", async () => { + it('should be let masterKey lock user out with authData', async () => { const response = await request({ - method: "POST", - url: "http://localhost:8378/1/classes/_User", + method: 'POST', + url: 'http://localhost:8378/1/classes/_User', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, body: { - key: "value", - authData: { anonymous: { id: "00000000-0000-0000-0000-000000000001" } }, + key: 'value', + authData: { anonymous: { id: '00000000-0000-0000-0000-000000000001' } }, }, }); const body = response.data; @@ -367,17 +367,17 @@ describe("Parse.User testing", () => { await user.save(null, { useMasterKey: true }); // update the user const options = { - method: "POST", + method: 'POST', url: `http://localhost:8378/1/classes/_User/`, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, body: { - key: "otherValue", + key: 'otherValue', authData: { - anonymous: { id: "00000000-0000-0000-0000-000000000001" }, + anonymous: { id: '00000000-0000-0000-0000-000000000001' }, }, }, }; @@ -385,18 +385,18 @@ describe("Parse.User testing", () => { expect(res.data.objectId).not.toEqual(objectId); }); - it("user login with files", done => { - const file = new Parse.File("yolo.txt", [1, 2, 3], "text/plain"); + it('user login with files', done => { + const file = new Parse.File('yolo.txt', [1, 2, 3], 'text/plain'); file .save() .then(file => { - return Parse.User.signUp("asdf", "zxcv", { file: file }); + return Parse.User.signUp('asdf', 'zxcv', { file: file }); }) .then(() => { - return Parse.User.logIn("asdf", "zxcv"); + return Parse.User.logIn('asdf', 'zxcv'); }) .then(user => { - const fileAgain = user.get("file"); + const fileAgain = user.get('file'); ok(fileAgain.name()); ok(fileAgain.url()); done(); @@ -407,14 +407,14 @@ describe("Parse.User testing", () => { }); }); - it("become sends token back", done => { + it('become sends token back', done => { let user = null; let sessionToken = null; - Parse.User.signUp("Jason", "Parse", { code: "red" }) + Parse.User.signUp('Jason', 'Parse', { code: 'red' }) .then(newUser => { user = newUser; - expect(user.get("code"), "red"); + expect(user.get('code'), 'red'); sessionToken = newUser.getSessionToken(); expect(sessionToken).toBeDefined(); @@ -423,8 +423,8 @@ describe("Parse.User testing", () => { }) .then(newUser => { expect(newUser.id).toEqual(user.id); - expect(newUser.get("username"), "Jason"); - expect(newUser.get("code"), "red"); + expect(newUser.get('username'), 'Jason'); + expect(newUser.get('code'), 'red'); expect(newUser.getSessionToken()).toEqual(sessionToken); }) .then( @@ -438,13 +438,13 @@ describe("Parse.User testing", () => { ); }); - it("become", done => { + it('become', done => { let user = null; let sessionToken = null; Promise.resolve() .then(function () { - return Parse.User.signUp("Jason", "Parse", { code: "red" }); + return Parse.User.signUp('Jason', 'Parse', { code: 'red' }); }) .then(function (newUser) { equal(Parse.User.current(), newUser); @@ -465,15 +465,15 @@ describe("Parse.User testing", () => { ok(newUser); equal(newUser.id, user.id); - equal(newUser.get("username"), "Jason"); - equal(newUser.get("code"), "red"); + equal(newUser.get('username'), 'Jason'); + equal(newUser.get('code'), 'red'); return Parse.User.logOut(); }) .then(() => { ok(!Parse.User.current()); - return Parse.User.become("somegarbage"); + return Parse.User.become('somegarbage'); }) .then( function () { @@ -500,7 +500,7 @@ describe("Parse.User testing", () => { ); }); - it("should not call beforeLogin with become", async done => { + it('should not call beforeLogin with become', async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); @@ -509,19 +509,19 @@ describe("Parse.User testing", () => { hit++; }); - await Parse.User._logInWith("facebook"); + await Parse.User._logInWith('facebook'); const sessionToken = Parse.User.current().getSessionToken(); await Parse.User.become(sessionToken); expect(hit).toBe(0); done(); }); - it("cannot save non-authed user", async done => { + it('cannot save non-authed user', async done => { let user = new Parse.User(); user.set({ - password: "asdf", - email: "asdf@example.com", - username: "zxcv", + password: 'asdf', + email: 'asdf@example.com', + username: 'zxcv', }); let userAgain = await user.signUp(); equal(userAgain, user); @@ -529,34 +529,34 @@ describe("Parse.User testing", () => { const userNotAuthed = await query.get(user.id); user = new Parse.User(); user.set({ - username: "hacker", - password: "password", + username: 'hacker', + password: 'password', }); userAgain = await user.signUp(); equal(userAgain, user); - userNotAuthed.set("username", "changed"); + userNotAuthed.set('username', 'changed'); userNotAuthed.save().then(fail, err => { expect(err.code).toEqual(Parse.Error.SESSION_MISSING); done(); }); }); - it("cannot delete non-authed user", async done => { + it('cannot delete non-authed user', async done => { let user = new Parse.User(); await user.signUp({ - password: "asdf", - email: "asdf@example.com", - username: "zxcv", + password: 'asdf', + email: 'asdf@example.com', + username: 'zxcv', }); const query = new Parse.Query(Parse.User); const userNotAuthed = await query.get(user.id); user = new Parse.User(); const userAgain = await user.signUp({ - username: "hacker", - password: "password", + username: 'hacker', + password: 'password', }); equal(userAgain, user); - userNotAuthed.set("username", "changed"); + userNotAuthed.set('username', 'changed'); try { await userNotAuthed.destroy(); done.fail(); @@ -566,22 +566,22 @@ describe("Parse.User testing", () => { } }); - it("cannot saveAll with non-authed user", async done => { + it('cannot saveAll with non-authed user', async done => { let user = new Parse.User(); await user.signUp({ - password: "asdf", - email: "asdf@example.com", - username: "zxcv", + password: 'asdf', + email: 'asdf@example.com', + username: 'zxcv', }); const query = new Parse.Query(Parse.User); const userNotAuthed = await query.get(user.id); user = new Parse.User(); await user.signUp({ - username: "hacker", - password: "password", + username: 'hacker', + password: 'password', }); const userNotAuthedNotChanged = await query.get(user.id); - userNotAuthed.set("username", "changed"); + userNotAuthed.set('username', 'changed'); const object = new TestObject(); await object.save({ user: userNotAuthedNotChanged, @@ -590,9 +590,9 @@ describe("Parse.User testing", () => { await item1.save({ number: 0, }); - item1.set("number", 1); + item1.set('number', 1); const item2 = new TestObject(); - item2.set("number", 2); + item2.set('number', 2); try { await Parse.Object.saveAll([item1, item2, userNotAuthed]); done.fail(); @@ -602,11 +602,11 @@ describe("Parse.User testing", () => { } }); - it("never locks himself up", async () => { + it('never locks himself up', async () => { const user = new Parse.User(); await user.signUp({ - username: "username", - password: "password", + username: 'username', + password: 'password', }); user.setACL(new Parse.ACL()); await user.save(); @@ -617,11 +617,11 @@ describe("Parse.User testing", () => { publicReadACL.setPublicReadAccess(true); // Create an administrator role with a single admin user - const role = new Parse.Role("admin", publicReadACL); + const role = new Parse.Role('admin', publicReadACL); const admin = new Parse.User(); await admin.signUp({ - username: "admin", - password: "admin", + username: 'admin', + password: 'admin', }); role.getUsers().add(admin); await role.save(null, { useMasterKey: true }); @@ -636,18 +636,18 @@ describe("Parse.User testing", () => { // Try to update from admin... should all work fine await user.save( - { key: "fromAdmin" }, + { key: 'fromAdmin' }, { sessionToken: admin.getSessionToken() } ); await user.fetch(); - expect(user.toJSON().key).toEqual("fromAdmin"); + expect(user.toJSON().key).toEqual('fromAdmin'); // Try to save when logged out (public) let failed = false; try { // Ensure no session token is sent await Parse.User.logOut(); - await user.save({ key: "fromPublic" }); + await user.save({ key: 'fromPublic' }); } catch (e) { failed = true; expect(e.code).toBe(Parse.Error.SESSION_MISSING); @@ -658,11 +658,11 @@ describe("Parse.User testing", () => { failed = false; const anyUser = new Parse.User(); await anyUser.signUp({ - username: "randomUser", - password: "password", + username: 'randomUser', + password: 'password', }); try { - await user.save({ key: "fromAnyUser" }); + await user.save({ key: 'fromAnyUser' }); } catch (e) { failed = true; expect(e.code).toBe(Parse.Error.SESSION_MISSING); @@ -670,11 +670,11 @@ describe("Parse.User testing", () => { expect({ failed }).toEqual({ failed: true }); }); - it("current user", done => { + it('current user', done => { const user = new Parse.User(); - user.set("password", "asdf"); - user.set("email", "asdf@example.com"); - user.set("username", "zxcv"); + user.set('password', 'asdf'); + user.set('email', 'asdf@example.com'); + user.set('username', 'zxcv'); user .signUp() .then(() => { @@ -695,18 +695,18 @@ describe("Parse.User testing", () => { }); }); - it("user.isCurrent", done => { + it('user.isCurrent', done => { const user1 = new Parse.User(); const user2 = new Parse.User(); const user3 = new Parse.User(); - user1.set("username", "a"); - user2.set("username", "b"); - user3.set("username", "c"); + user1.set('username', 'a'); + user2.set('username', 'b'); + user3.set('username', 'c'); - user1.set("password", "password"); - user2.set("password", "password"); - user3.set("password", "password"); + user1.set('password', 'password'); + user2.set('password', 'password'); + user3.set('password', 'password'); user1 .signUp() @@ -726,19 +726,19 @@ describe("Parse.User testing", () => { equal(user1.isCurrent(), false); equal(user2.isCurrent(), false); equal(user3.isCurrent(), true); - return Parse.User.logIn("a", "password"); + return Parse.User.logIn('a', 'password'); }) .then(() => { equal(user1.isCurrent(), true); equal(user2.isCurrent(), false); equal(user3.isCurrent(), false); - return Parse.User.logIn("b", "password"); + return Parse.User.logIn('b', 'password'); }) .then(() => { equal(user1.isCurrent(), false); equal(user2.isCurrent(), true); equal(user3.isCurrent(), false); - return Parse.User.logIn("b", "password"); + return Parse.User.logIn('b', 'password'); }) .then(() => { equal(user1.isCurrent(), false); @@ -752,32 +752,32 @@ describe("Parse.User testing", () => { }); }); - it("user associations", async done => { + it('user associations', async done => { const child = new TestObject(); await child.save(); const user = new Parse.User(); - user.set("password", "asdf"); - user.set("email", "asdf@example.com"); - user.set("username", "zxcv"); - user.set("child", child); + user.set('password', 'asdf'); + user.set('email', 'asdf@example.com'); + user.set('username', 'zxcv'); + user.set('child', child); await user.signUp(); const object = new TestObject(); - object.set("user", user); + object.set('user', user); await object.save(); const query = new Parse.Query(TestObject); const objectAgain = await query.get(object.id); - const userAgain = objectAgain.get("user"); + const userAgain = objectAgain.get('user'); await userAgain.fetch(); equal(user.id, userAgain.id); - equal(userAgain.get("child").id, child.id); + equal(userAgain.get('child').id, child.id); done(); }); - it("user queries", async done => { + it('user queries', async done => { const user = new Parse.User(); - user.set("password", "asdf"); - user.set("email", "asdf@example.com"); - user.set("username", "zxcv"); + user.set('password', 'asdf'); + user.set('email', 'asdf@example.com'); + user.set('username', 'zxcv'); await user.signUp(); const query = new Parse.Query(Parse.User); const userAgain = await query.get(user.id); @@ -785,7 +785,7 @@ describe("Parse.User testing", () => { const users = await query.find(); equal(users.length, 1); equal(users[0].id, user.id); - ok(userAgain.get("email"), "asdf@example.com"); + ok(userAgain.get('email'), 'asdf@example.com'); done(); }); @@ -802,30 +802,30 @@ describe("Parse.User testing", () => { return promise.then(optionsOrCallback); } - it("contained in user array queries", async done => { + it('contained in user array queries', async done => { const USERS = 4; const MESSAGES = 5; // Make a list of users. const userList = range(USERS).map(function (i) { const user = new Parse.User(); - user.set("password", "user_num_" + i); - user.set("email", "user_num_" + i + "@example.com"); - user.set("username", "xinglblog_num_" + i); + user.set('password', 'user_num_' + i); + user.set('email', 'user_num_' + i + '@example.com'); + user.set('username', 'xinglblog_num_' + i); return user; }); signUpAll(userList, async function (users) { // Make a list of messages. if (!users || users.length != USERS) { - fail("signupAll failed"); + fail('signupAll failed'); done(); return; } const messageList = range(MESSAGES).map(function (i) { const message = new TestObject(); - message.set("to", users[(i + 1) % USERS]); - message.set("from", users[i % USERS]); + message.set('to', users[(i + 1) % USERS]); + message.set('from', users[i % USERS]); return message; }); @@ -835,7 +835,7 @@ describe("Parse.User testing", () => { // Assemble an "in" list. const inList = [users[0], users[3], users[3]]; // Intentional dupe const query = new Parse.Query(TestObject); - query.containedIn("from", inList); + query.containedIn('from', inList); const results = await query.find(); equal(results.length, 3); done(); @@ -845,27 +845,27 @@ describe("Parse.User testing", () => { it("saving a user signs them up but doesn't log them in", async done => { const user = new Parse.User(); await user.save({ - password: "asdf", - email: "asdf@example.com", - username: "zxcv", + password: 'asdf', + email: 'asdf@example.com', + username: 'zxcv', }); equal(Parse.User.current(), null); done(); }); - it("user updates", async done => { + it('user updates', async done => { const user = new Parse.User(); await user.signUp({ - password: "asdf", - email: "asdf@example.com", - username: "zxcv", + password: 'asdf', + email: 'asdf@example.com', + username: 'zxcv', }); - user.set("username", "test"); + user.set('username', 'test'); await user.save(); equal(Object.keys(user.attributes).length, 5); - ok(user.attributes["username"]); - ok(user.attributes["email"]); + ok(user.attributes['username']); + ok(user.attributes['email']); await user.destroy(); const query = new Parse.Query(Parse.User); try { @@ -878,14 +878,14 @@ describe("Parse.User testing", () => { } }); - it("count users", async done => { + it('count users', async done => { const james = new Parse.User(); - james.set("username", "james"); - james.set("password", "mypass"); + james.set('username', 'james'); + james.set('password', 'mypass'); await james.signUp(); const kevin = new Parse.User(); - kevin.set("username", "kevin"); - kevin.set("password", "mypass"); + kevin.set('username', 'kevin'); + kevin.set('password', 'mypass'); await kevin.signUp(); const query = new Parse.Query(Parse.User); const count = await query.find({ useMasterKey: true }); @@ -893,86 +893,86 @@ describe("Parse.User testing", () => { done(); }); - it("user sign up with container class", async done => { - await Parse.User.signUp("ilya", "mypass", { array: ["hello"] }); + it('user sign up with container class', async done => { + await Parse.User.signUp('ilya', 'mypass', { array: ['hello'] }); done(); }); - it("user modified while saving", async done => { + it('user modified while saving', async done => { Parse.Object.disableSingleInstance(); await reconfigureServer(); const user = new Parse.User(); - user.set("username", "alice"); - user.set("password", "password"); + user.set('username', 'alice'); + user.set('password', 'password'); user.signUp().then(function (userAgain) { - equal(userAgain.get("username"), "bob"); - ok(userAgain.dirty("username")); + equal(userAgain.get('username'), 'bob'); + ok(userAgain.dirty('username')); const query = new Parse.Query(Parse.User); query.get(user.id).then(freshUser => { equal(freshUser.id, user.id); - equal(freshUser.get("username"), "alice"); + equal(freshUser.get('username'), 'alice'); done(); }); }); // Jump a frame so the signup call is properly sent // This is due to the fact that now, we use real promises process.nextTick(() => { - ok(user.set("username", "bob")); + ok(user.set('username', 'bob')); }); }); - it("user modified while saving with unsaved child", done => { + it('user modified while saving with unsaved child', done => { Parse.Object.disableSingleInstance(); const user = new Parse.User(); - user.set("username", "alice"); - user.set("password", "password"); - user.set("child", new TestObject()); + user.set('username', 'alice'); + user.set('password', 'password'); + user.set('child', new TestObject()); user.signUp().then(userAgain => { - equal(userAgain.get("username"), "bob"); + equal(userAgain.get('username'), 'bob'); // Should be dirty, but it depends on batch support. // ok(userAgain.dirty("username")); const query = new Parse.Query(Parse.User); query.get(user.id).then(freshUser => { equal(freshUser.id, user.id); // Should be alice, but it depends on batch support. - equal(freshUser.get("username"), "bob"); + equal(freshUser.get('username'), 'bob'); done(); }); }); - ok(user.set("username", "bob")); + ok(user.set('username', 'bob')); }); - it("user loaded from localStorage from signup", async done => { - const alice = await Parse.User.signUp("alice", "password"); - ok(alice.id, "Alice should have an objectId"); - ok(alice.getSessionToken(), "Alice should have a session token"); - equal(alice.get("password"), undefined, "Alice should not have a password"); + it('user loaded from localStorage from signup', async done => { + const alice = await Parse.User.signUp('alice', 'password'); + ok(alice.id, 'Alice should have an objectId'); + ok(alice.getSessionToken(), 'Alice should have a session token'); + equal(alice.get('password'), undefined, 'Alice should not have a password'); // Simulate the environment getting reset. Parse.User._currentUser = null; Parse.User._currentUserMatchesDisk = false; const aliceAgain = Parse.User.current(); - equal(aliceAgain.get("username"), "alice"); - equal(aliceAgain.id, alice.id, "currentUser should have objectId"); - ok(aliceAgain.getSessionToken(), "currentUser should have a sessionToken"); + equal(aliceAgain.get('username'), 'alice'); + equal(aliceAgain.id, alice.id, 'currentUser should have objectId'); + ok(aliceAgain.getSessionToken(), 'currentUser should have a sessionToken'); equal( - alice.get("password"), + alice.get('password'), undefined, - "currentUser should not have password" + 'currentUser should not have password' ); done(); }); - it("user loaded from localStorage from login", done => { + it('user loaded from localStorage from login', done => { let id; - Parse.User.signUp("alice", "password") + Parse.User.signUp('alice', 'password') .then(alice => { id = alice.id; return Parse.User.logOut(); }) .then(() => { - return Parse.User.logIn("alice", "password"); + return Parse.User.logIn('alice', 'password'); }) .then(() => { // Force the current user to read from disk @@ -981,29 +981,29 @@ describe("Parse.User testing", () => { const userFromDisk = Parse.User.current(); equal( - userFromDisk.get("password"), + userFromDisk.get('password'), undefined, - "password should not be in attributes" + 'password should not be in attributes' ); - equal(userFromDisk.id, id, "id should be set"); + equal(userFromDisk.id, id, 'id should be set'); ok( userFromDisk.getSessionToken(), - "currentUser should have a sessionToken" + 'currentUser should have a sessionToken' ); done(); }); }); - it("saving user after browser refresh", done => { + it('saving user after browser refresh', done => { let id; - Parse.User.signUp("alice", "password", null) + Parse.User.signUp('alice', 'password', null) .then(function (alice) { id = alice.id; return Parse.User.logOut(); }) .then(() => { - return Parse.User.logIn("alice", "password"); + return Parse.User.logIn('alice', 'password'); }) .then(function () { // Simulate browser refresh by force-reloading user from localStorage @@ -1019,35 +1019,35 @@ describe("Parse.User testing", () => { equal( userInMemory.getUsername(), - "alice", - "saving user should not remove existing fields" + 'alice', + 'saving user should not remove existing fields' ); equal( - userInMemory.get("some_field"), + userInMemory.get('some_field'), 1, - "saving user should save specified field" + 'saving user should save specified field' ); equal( - userInMemory.get("password"), + userInMemory.get('password'), undefined, - "password should not be in attributes after saving user" + 'password should not be in attributes after saving user' ); equal( - userInMemory.get("objectId"), + userInMemory.get('objectId'), undefined, - "objectId should not be in attributes after saving user" + 'objectId should not be in attributes after saving user' ); equal( - userInMemory.get("_id"), + userInMemory.get('_id'), undefined, - "_id should not be in attributes after saving user" + '_id should not be in attributes after saving user' ); - equal(userInMemory.id, id, "id should be set"); + equal(userInMemory.id, id, 'id should be set'); expect(userInMemory.updatedAt instanceof Date).toBe(true); @@ -1055,7 +1055,7 @@ describe("Parse.User testing", () => { ok( userInMemory.getSessionToken(), - "user should have a sessionToken after saving" + 'user should have a sessionToken after saving' ); // Force the current user to read from localStorage, and check again @@ -1065,35 +1065,35 @@ describe("Parse.User testing", () => { equal( userFromDisk.getUsername(), - "alice", - "userFromDisk should have previously existing fields" + 'alice', + 'userFromDisk should have previously existing fields' ); equal( - userFromDisk.get("some_field"), + userFromDisk.get('some_field'), 1, - "userFromDisk should have saved field" + 'userFromDisk should have saved field' ); equal( - userFromDisk.get("password"), + userFromDisk.get('password'), undefined, - "password should not be in attributes of userFromDisk" + 'password should not be in attributes of userFromDisk' ); equal( - userFromDisk.get("objectId"), + userFromDisk.get('objectId'), undefined, - "objectId should not be in attributes of userFromDisk" + 'objectId should not be in attributes of userFromDisk' ); equal( - userFromDisk.get("_id"), + userFromDisk.get('_id'), undefined, - "_id should not be in attributes of userFromDisk" + '_id should not be in attributes of userFromDisk' ); - equal(userFromDisk.id, id, "id should be set on userFromDisk"); + equal(userFromDisk.id, id, 'id should be set on userFromDisk'); ok(userFromDisk.updatedAt instanceof Date); @@ -1101,7 +1101,7 @@ describe("Parse.User testing", () => { ok( userFromDisk.getSessionToken(), - "userFromDisk should have a sessionToken" + 'userFromDisk should have a sessionToken' ); done(); @@ -1113,9 +1113,9 @@ describe("Parse.User testing", () => { ); }); - it("user with missing username", async done => { + it('user with missing username', async done => { const user = new Parse.User(); - user.set("password", "foo"); + user.set('password', 'foo'); try { await user.signUp(); done.fail(); @@ -1125,9 +1125,9 @@ describe("Parse.User testing", () => { } }); - it("user with missing password", async done => { + it('user with missing password', async done => { const user = new Parse.User(); - user.set("username", "foo"); + user.set('username', 'foo'); try { await user.signUp(); done.fail(); @@ -1137,56 +1137,56 @@ describe("Parse.User testing", () => { } }); - it("user stupid subclassing", async done => { - const SuperUser = Parse.Object.extend("User"); + it('user stupid subclassing', async done => { + const SuperUser = Parse.Object.extend('User'); const user = new SuperUser(); - user.set("username", "bob"); - user.set("password", "welcome"); - ok(user instanceof Parse.User, "Subclassing User should have worked"); + user.set('username', 'bob'); + user.set('password', 'welcome'); + ok(user instanceof Parse.User, 'Subclassing User should have worked'); await user.signUp(); done(); }); - it("user signup class method uses subclassing", async done => { + it('user signup class method uses subclassing', async done => { const SuperUser = Parse.User.extend({ secret: function () { return 1337; }, }); - const user = await Parse.User.signUp("bob", "welcome"); - ok(user instanceof SuperUser, "Subclassing User should have worked"); + const user = await Parse.User.signUp('bob', 'welcome'); + ok(user instanceof SuperUser, 'Subclassing User should have worked'); equal(user.secret(), 1337); done(); }); - it("user on disk gets updated after save", async done => { + it('user on disk gets updated after save', async done => { Parse.User.extend({ isSuper: function () { return true; }, }); - const user = await Parse.User.signUp("bob", "welcome"); - await user.save("secret", 1337); + const user = await Parse.User.signUp('bob', 'welcome'); + await user.save('secret', 1337); delete Parse.User._currentUser; delete Parse.User._currentUserMatchesDisk; const userFromDisk = Parse.User.current(); - equal(userFromDisk.get("secret"), 1337); - ok(userFromDisk.isSuper(), "The subclass should have been used"); + equal(userFromDisk.get('secret'), 1337); + ok(userFromDisk.isSuper(), 'The subclass should have been used'); done(); }); it("current user isn't dirty", async done => { - const user = await Parse.User.signUp("andrew", "oppa", { - style: "gangnam", + const user = await Parse.User.signUp('andrew', 'oppa', { + style: 'gangnam', }); - ok(!user.dirty("style"), "The user just signed up."); + ok(!user.dirty('style'), 'The user just signed up.'); Parse.User._currentUser = null; Parse.User._currentUserMatchesDisk = false; const userAgain = Parse.User.current(); - ok(!userAgain.dirty("style"), "The user was just read from disk."); + ok(!userAgain.dirty('style'), 'The user was just read from disk.'); done(); }); @@ -1205,7 +1205,7 @@ describe("Parse.User testing", () => { authenticate: function (options) { if (this.shouldError) { - options.error(this, "An error occurred"); + options.error(this, 'An error occurred'); } else if (this.shouldCancel) { options.error(this, null); } else { @@ -1225,7 +1225,7 @@ describe("Parse.User testing", () => { return true; }, getAuthType() { - return "facebook"; + return 'facebook'; }, deauthenticate: function () { this.loggedOut = true; @@ -1237,14 +1237,14 @@ describe("Parse.User testing", () => { // Note that this mocks out client-side Facebook action rather than // server-side. const getMockFacebookProvider = function () { - return getMockFacebookProviderWithIdToken("8675309", "jenny"); + return getMockFacebookProviderWithIdToken('8675309', 'jenny'); }; const getMockMyOauthProvider = function () { return { authData: { - id: "12345", - access_token: "12345", + id: '12345', + access_token: '12345', expiration_date: new Date().toJSON(), }, shouldError: false, @@ -1255,7 +1255,7 @@ describe("Parse.User testing", () => { authenticate(options) { if (this.shouldError) { - options.error(this, "An error occurred"); + options.error(this, 'An error occurred'); } else if (this.shouldCancel) { options.error(this, null); } else { @@ -1275,7 +1275,7 @@ describe("Parse.User testing", () => { return true; }, getAuthType() { - return "myoauth"; + return 'myoauth'; }, deauthenticate() { this.loggedOut = true; @@ -1290,142 +1290,142 @@ describe("Parse.User testing", () => { }, }); - it("log in with provider", async done => { + it('log in with provider', async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - const model = await Parse.User._logInWith("facebook"); - ok(model instanceof Parse.User, "Model should be a Parse.User"); + const model = await Parse.User._logInWith('facebook'); + ok(model instanceof Parse.User, 'Model should be a Parse.User'); strictEqual(Parse.User.current(), model); - ok(model.extended(), "Should have used subclass."); + ok(model.extended(), 'Should have used subclass.'); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); strictEqual( provider.authData.expiration_date, provider.synchronizedExpiration ); - ok(model._isLinked("facebook"), "User should be linked to facebook"); + ok(model._isLinked('facebook'), 'User should be linked to facebook'); done(); }); - it("can not set authdata to null", async () => { + it('can not set authdata to null', async () => { try { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - const user = await Parse.User._logInWith("facebook"); - user.set("authData", null); + const user = await Parse.User._logInWith('facebook'); + user.set('authData', null); await user.save(); fail(); } catch (e) { - expect(e.message).toBe("This authentication method is unsupported."); + expect(e.message).toBe('This authentication method is unsupported.'); } }); - it("ignore setting authdata to undefined", async () => { + it('ignore setting authdata to undefined', async () => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - const user = await Parse.User._logInWith("facebook"); - user.set("authData", undefined); + const user = await Parse.User._logInWith('facebook'); + user.set('authData', undefined); await user.save(); - let authData = user.get("authData"); + let authData = user.get('authData'); expect(authData).toBe(undefined); await user.fetch(); - authData = user.get("authData"); + authData = user.get('authData'); expect(authData.facebook.id).toBeDefined(); }); - it("user authData should be available in cloudcode (#2342)", async done => { - Parse.Cloud.define("checkLogin", req => { + it('user authData should be available in cloudcode (#2342)', async done => { + Parse.Cloud.define('checkLogin', req => { expect(req.user).not.toBeUndefined(); expect(Parse.FacebookUtils.isLinked(req.user)).toBe(true); - return "ok"; + return 'ok'; }); const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - const model = await Parse.User._logInWith("facebook"); - ok(model instanceof Parse.User, "Model should be a Parse.User"); + const model = await Parse.User._logInWith('facebook'); + ok(model instanceof Parse.User, 'Model should be a Parse.User'); strictEqual(Parse.User.current(), model); - ok(model.extended(), "Should have used subclass."); + ok(model.extended(), 'Should have used subclass.'); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); strictEqual( provider.authData.expiration_date, provider.synchronizedExpiration ); - ok(model._isLinked("facebook"), "User should be linked to facebook"); + ok(model._isLinked('facebook'), 'User should be linked to facebook'); - Parse.Cloud.run("checkLogin").then(done, done); + Parse.Cloud.run('checkLogin').then(done, done); }); - it("log in with provider and update token", async done => { + it('log in with provider and update token', async done => { const provider = getMockFacebookProvider(); const secondProvider = getMockFacebookProviderWithIdToken( - "8675309", - "jenny_valid_token" + '8675309', + 'jenny_valid_token' ); Parse.User._registerAuthenticationProvider(provider); - await Parse.User._logInWith("facebook"); + await Parse.User._logInWith('facebook'); Parse.User._registerAuthenticationProvider(secondProvider); await Parse.User.logOut(); - await Parse.User._logInWith("facebook"); - expect(secondProvider.synchronizedAuthToken).toEqual("jenny_valid_token"); + await Parse.User._logInWith('facebook'); + expect(secondProvider.synchronizedAuthToken).toEqual('jenny_valid_token'); // Make sure we can login with the new token again await Parse.User.logOut(); - await Parse.User._logInWith("facebook"); + await Parse.User._logInWith('facebook'); done(); }); - it("returns authData when authed and logged in with provider (regression test for #1498)", async done => { + it('returns authData when authed and logged in with provider (regression test for #1498)', async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - const user = await Parse.User._logInWith("facebook"); + const user = await Parse.User._logInWith('facebook'); const userQuery = new Parse.Query(Parse.User); userQuery.get(user.id).then(user => { - expect(user.get("authData")).not.toBeUndefined(); + expect(user.get('authData')).not.toBeUndefined(); done(); }); }); - it("only creates a single session for an installation / user pair (#2885)", async done => { + it('only creates a single session for an installation / user pair (#2885)', async done => { Parse.Object.disableSingleInstance(); const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - await Parse.User.logInWith("facebook"); - await Parse.User.logInWith("facebook"); - const user = await Parse.User.logInWith("facebook"); + await Parse.User.logInWith('facebook'); + await Parse.User.logInWith('facebook'); + const user = await Parse.User.logInWith('facebook'); const sessionToken = user.getSessionToken(); - const query = new Parse.Query("_Session"); + const query = new Parse.Query('_Session'); return query .find({ useMasterKey: true }) .then(results => { expect(results.length).toBe(1); - expect(results[0].get("sessionToken")).toBe(sessionToken); - expect(results[0].get("createdWith")).toEqual({ - action: "login", - authProvider: "facebook", + expect(results[0].get('sessionToken')).toBe(sessionToken); + expect(results[0].get('createdWith')).toEqual({ + action: 'login', + authProvider: 'facebook', }); done(); }) .catch(done.fail); }); - it("log in with provider with files", done => { + it('log in with provider with files', done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - const file = new Parse.File("yolo.txt", [1, 2, 3], "text/plain"); + const file = new Parse.File('yolo.txt', [1, 2, 3], 'text/plain'); file .save() .then(file => { const user = new Parse.User(); - user.set("file", file); - return user._linkWith("facebook", {}); + user.set('file', file); + return user._linkWith('facebook', {}); }) .then(user => { - expect(user._isLinked("facebook")).toBeTruthy(); - return Parse.User._logInWith("facebook", {}); + expect(user._isLinked('facebook')).toBeTruthy(); + return Parse.User._logInWith('facebook', {}); }) .then(user => { - const fileAgain = user.get("file"); + const fileAgain = user.get('file'); expect(fileAgain.name()).toMatch(/yolo.txt$/); expect(fileAgain.url()).toMatch(/yolo.txt$/); }) @@ -1435,78 +1435,78 @@ describe("Parse.User testing", () => { .catch(done.fail); }); - it("log in with provider twice", async done => { + it('log in with provider twice', async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - const model = await Parse.User._logInWith("facebook"); - ok(model instanceof Parse.User, "Model should be a Parse.User"); + const model = await Parse.User._logInWith('facebook'); + ok(model instanceof Parse.User, 'Model should be a Parse.User'); strictEqual(Parse.User.current(), model); - ok(model.extended(), "Should have used the subclass."); + ok(model.extended(), 'Should have used the subclass.'); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); strictEqual( provider.authData.expiration_date, provider.synchronizedExpiration ); - ok(model._isLinked("facebook"), "User should be linked to facebook"); + ok(model._isLinked('facebook'), 'User should be linked to facebook'); Parse.User.logOut().then(async () => { ok(provider.loggedOut); provider.loggedOut = false; - const innerModel = await Parse.User._logInWith("facebook"); - ok(innerModel instanceof Parse.User, "Model should be a Parse.User"); + const innerModel = await Parse.User._logInWith('facebook'); + ok(innerModel instanceof Parse.User, 'Model should be a Parse.User'); ok( innerModel === Parse.User.current(), - "Returned model should be the current user" + 'Returned model should be the current user' ); ok(provider.authData.id === provider.synchronizedUserId); ok(provider.authData.access_token === provider.synchronizedAuthToken); - ok(innerModel._isLinked("facebook"), "User should be linked to facebook"); - ok(innerModel.existed(), "User should not be newly-created"); + ok(innerModel._isLinked('facebook'), 'User should be linked to facebook'); + ok(innerModel.existed(), 'User should not be newly-created'); done(); }, done.fail); }); - it("log in with provider failed", async done => { + it('log in with provider failed', async done => { const provider = getMockFacebookProvider(); provider.shouldError = true; Parse.User._registerAuthenticationProvider(provider); try { - await Parse.User._logInWith("facebook"); + await Parse.User._logInWith('facebook'); done.fail(); } catch (error) { - ok(error, "Error should be non-null"); + ok(error, 'Error should be non-null'); done(); } }); - it("log in with provider cancelled", async done => { + it('log in with provider cancelled', async done => { const provider = getMockFacebookProvider(); provider.shouldCancel = true; Parse.User._registerAuthenticationProvider(provider); try { - await Parse.User._logInWith("facebook"); + await Parse.User._logInWith('facebook'); done.fail(); } catch (error) { - ok(error === null, "Error should be null"); + ok(error === null, 'Error should be null'); done(); } }); - it("login with provider should not call beforeSave trigger", async done => { + it('login with provider should not call beforeSave trigger', async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - await Parse.User._logInWith("facebook"); + await Parse.User._logInWith('facebook'); Parse.User.logOut().then(async () => { Parse.Cloud.beforeSave(Parse.User, function (req, res) { res.error("Before save shouldn't be called on login"); }); - await Parse.User._logInWith("facebook"); + await Parse.User._logInWith('facebook'); done(); }); }); - it("signup with provider should not call beforeLogin trigger", async done => { + it('signup with provider should not call beforeLogin trigger', async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); @@ -1515,30 +1515,30 @@ describe("Parse.User testing", () => { hit++; }); - await Parse.User._logInWith("facebook"); + await Parse.User._logInWith('facebook'); expect(hit).toBe(0); done(); }); - it("login with provider should call beforeLogin trigger", async done => { + it('login with provider should call beforeLogin trigger', async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); let hit = 0; Parse.Cloud.beforeLogin(req => { hit++; - expect(req.object.get("authData")).toBeDefined(); - expect(req.object.get("name")).toBe("tupac shakur"); + expect(req.object.get('authData')).toBeDefined(); + expect(req.object.get('name')).toBe('tupac shakur'); }); - await Parse.User._logInWith("facebook"); - await Parse.User.current().save({ name: "tupac shakur" }); + await Parse.User._logInWith('facebook'); + await Parse.User.current().save({ name: 'tupac shakur' }); await Parse.User.logOut(); - await Parse.User._logInWith("facebook"); + await Parse.User._logInWith('facebook'); expect(hit).toBe(1); done(); }); - it("incorrect login with provider should not call beforeLogin trigger", async done => { + it('incorrect login with provider should not call beforeLogin trigger', async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); @@ -1546,11 +1546,11 @@ describe("Parse.User testing", () => { Parse.Cloud.beforeLogin(() => { hit++; }); - await Parse.User._logInWith("facebook"); + await Parse.User._logInWith('facebook'); await Parse.User.logOut(); provider.shouldError = true; try { - await Parse.User._logInWith("facebook"); + await Parse.User._logInWith('facebook'); } catch (e) { expect(e).toBeDefined(); } @@ -1558,89 +1558,89 @@ describe("Parse.User testing", () => { done(); }); - it("login with provider should be blockable by beforeLogin", async done => { + it('login with provider should be blockable by beforeLogin', async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); let hit = 0; Parse.Cloud.beforeLogin(req => { hit++; - if (req.object.get("isBanned")) { - throw new Error("banned account"); + if (req.object.get('isBanned')) { + throw new Error('banned account'); } }); - await Parse.User._logInWith("facebook"); + await Parse.User._logInWith('facebook'); await Parse.User.current().save({ isBanned: true }); await Parse.User.logOut(); try { - await Parse.User._logInWith("facebook"); - throw new Error("should not have continued login."); + await Parse.User._logInWith('facebook'); + throw new Error('should not have continued login.'); } catch (e) { - expect(e.message).toBe("banned account"); + expect(e.message).toBe('banned account'); } expect(hit).toBe(1); done(); }); - it("login with provider should be blockable by beforeLogin even when the user has a attached file", async done => { + it('login with provider should be blockable by beforeLogin even when the user has a attached file', async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); let hit = 0; Parse.Cloud.beforeLogin(req => { hit++; - if (req.object.get("isBanned")) { - throw new Error("banned account"); + if (req.object.get('isBanned')) { + throw new Error('banned account'); } }); - const user = await Parse.User._logInWith("facebook"); - const base64 = "aHR0cHM6Ly9naXRodWIuY29tL2t2bmt1YW5n"; - const file = new Parse.File("myfile.txt", { base64 }); + const user = await Parse.User._logInWith('facebook'); + const base64 = 'aHR0cHM6Ly9naXRodWIuY29tL2t2bmt1YW5n'; + const file = new Parse.File('myfile.txt', { base64 }); await file.save(); await user.save({ isBanned: true, file }); await Parse.User.logOut(); try { - await Parse.User._logInWith("facebook"); - throw new Error("should not have continued login."); + await Parse.User._logInWith('facebook'); + throw new Error('should not have continued login.'); } catch (e) { - expect(e.message).toBe("banned account"); + expect(e.message).toBe('banned account'); } expect(hit).toBe(1); done(); }); - it("logout with provider should call afterLogout trigger", async done => { + it('logout with provider should call afterLogout trigger', async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); let userId; Parse.Cloud.afterLogout(req => { - expect(req.object.className).toEqual("_Session"); + expect(req.object.className).toEqual('_Session'); expect(req.object.id).toBeDefined(); - const user = req.object.get("user"); + const user = req.object.get('user'); expect(user).toBeDefined(); userId = user.id; }); - const user = await Parse.User._logInWith("facebook"); + const user = await Parse.User._logInWith('facebook'); await Parse.User.logOut(); expect(user.id).toBe(userId); done(); }); - it("link with provider", async done => { + it('link with provider', async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); const user = new Parse.User(); - user.set("username", "testLinkWithProvider"); - user.set("password", "mypass"); + user.set('username', 'testLinkWithProvider'); + user.set('password', 'mypass'); await user.signUp(); - const model = await user._linkWith("facebook"); - ok(model instanceof Parse.User, "Model should be a Parse.User"); + const model = await user._linkWith('facebook'); + ok(model instanceof Parse.User, 'Model should be a Parse.User'); strictEqual(Parse.User.current(), model); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); @@ -1648,21 +1648,21 @@ describe("Parse.User testing", () => { provider.authData.expiration_date, provider.synchronizedExpiration ); - ok(model._isLinked("facebook"), "User should be linked"); + ok(model._isLinked('facebook'), 'User should be linked'); done(); }); // What this means is, only one Parse User can be linked to a // particular Facebook account. - it("link with provider for already linked user", async done => { + it('link with provider for already linked user', async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); const user = new Parse.User(); - user.set("username", "testLinkWithProviderToAlreadyLinkedUser"); - user.set("password", "mypass"); + user.set('username', 'testLinkWithProviderToAlreadyLinkedUser'); + user.set('password', 'mypass'); await user.signUp(); - const model = await user._linkWith("facebook"); - ok(model instanceof Parse.User, "Model should be a Parse.User"); + const model = await user._linkWith('facebook'); + ok(model instanceof Parse.User, 'Model should be a Parse.User'); strictEqual(Parse.User.current(), model); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); @@ -1670,13 +1670,13 @@ describe("Parse.User testing", () => { provider.authData.expiration_date, provider.synchronizedExpiration ); - ok(model._isLinked("facebook"), "User should be linked."); + ok(model._isLinked('facebook'), 'User should be linked.'); const user2 = new Parse.User(); - user2.set("username", "testLinkWithProviderToAlreadyLinkedUser2"); - user2.set("password", "mypass"); + user2.set('username', 'testLinkWithProviderToAlreadyLinkedUser2'); + user2.set('password', 'mypass'); await user2.signUp(); try { - await user2._linkWith("facebook"); + await user2._linkWith('facebook'); done.fail(); } catch (error) { expect(error.code).toEqual(Parse.Error.ACCOUNT_ALREADY_LINKED); @@ -1684,31 +1684,31 @@ describe("Parse.User testing", () => { } }); - it("link with provider should return sessionToken", async () => { + it('link with provider should return sessionToken', async () => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); const user = new Parse.User(); - user.set("username", "testLinkWithProvider"); - user.set("password", "mypass"); + user.set('username', 'testLinkWithProvider'); + user.set('password', 'mypass'); await user.signUp(); const query = new Parse.Query(Parse.User); const u2 = await query.get(user.id); - const model = await u2._linkWith("facebook", {}, { useMasterKey: true }); + const model = await u2._linkWith('facebook', {}, { useMasterKey: true }); expect(u2.getSessionToken()).toBeDefined(); expect(model.getSessionToken()).toBeDefined(); expect(u2.getSessionToken()).toBe(model.getSessionToken()); }); - it("link with provider via sessionToken should not create new sessionToken (Regression #5799)", async () => { + it('link with provider via sessionToken should not create new sessionToken (Regression #5799)', async () => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); const user = new Parse.User(); - user.set("username", "testLinkWithProviderNoOverride"); - user.set("password", "mypass"); + user.set('username', 'testLinkWithProviderNoOverride'); + user.set('password', 'mypass'); await user.signUp(); const sessionToken = user.getSessionToken(); - await user._linkWith("facebook", {}, { sessionToken }); + await user._linkWith('facebook', {}, { sessionToken }); expect(sessionToken).toBe(user.getSessionToken()); expect(user._isLinked(provider)).toBe(true); @@ -1719,197 +1719,197 @@ describe("Parse.User testing", () => { expect(sessionToken).toBe(become.getSessionToken()); }); - it("link with provider failed", async done => { + it('link with provider failed', async done => { const provider = getMockFacebookProvider(); provider.shouldError = true; Parse.User._registerAuthenticationProvider(provider); const user = new Parse.User(); - user.set("username", "testLinkWithProvider"); - user.set("password", "mypass"); + user.set('username', 'testLinkWithProvider'); + user.set('password', 'mypass'); await user.signUp(); try { - await user._linkWith("facebook"); + await user._linkWith('facebook'); done.fail(); } catch (error) { - ok(error, "Linking should fail"); - ok(!user._isLinked("facebook"), "User should not be linked to facebook"); + ok(error, 'Linking should fail'); + ok(!user._isLinked('facebook'), 'User should not be linked to facebook'); done(); } }); - it("link with provider cancelled", async done => { + it('link with provider cancelled', async done => { const provider = getMockFacebookProvider(); provider.shouldCancel = true; Parse.User._registerAuthenticationProvider(provider); const user = new Parse.User(); - user.set("username", "testLinkWithProvider"); - user.set("password", "mypass"); + user.set('username', 'testLinkWithProvider'); + user.set('password', 'mypass'); await user.signUp(); try { - await user._linkWith("facebook"); + await user._linkWith('facebook'); done.fail(); } catch (error) { - ok(!error, "Linking should be cancelled"); - ok(!user._isLinked("facebook"), "User should not be linked to facebook"); + ok(!error, 'Linking should be cancelled'); + ok(!user._isLinked('facebook'), 'User should not be linked to facebook'); done(); } }); - it("unlink with provider", async done => { + it('unlink with provider', async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - const model = await Parse.User._logInWith("facebook"); - ok(model instanceof Parse.User, "Model should be a Parse.User."); + const model = await Parse.User._logInWith('facebook'); + ok(model instanceof Parse.User, 'Model should be a Parse.User.'); strictEqual(Parse.User.current(), model); - ok(model.extended(), "Should have used the subclass."); + ok(model.extended(), 'Should have used the subclass.'); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); strictEqual( provider.authData.expiration_date, provider.synchronizedExpiration ); - ok(model._isLinked("facebook"), "User should be linked to facebook."); - await model._unlinkFrom("facebook"); - ok(!model._isLinked("facebook"), "User should not be linked."); - ok(!provider.synchronizedUserId, "User id should be cleared."); - ok(!provider.synchronizedAuthToken, "Auth token should be cleared."); - ok(!provider.synchronizedExpiration, "Expiration should be cleared."); + ok(model._isLinked('facebook'), 'User should be linked to facebook.'); + await model._unlinkFrom('facebook'); + ok(!model._isLinked('facebook'), 'User should not be linked.'); + ok(!provider.synchronizedUserId, 'User id should be cleared.'); + ok(!provider.synchronizedAuthToken, 'Auth token should be cleared.'); + ok(!provider.synchronizedExpiration, 'Expiration should be cleared.'); done(); }); - it("unlink and link", async done => { + it('unlink and link', async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - const model = await Parse.User._logInWith("facebook"); - ok(model instanceof Parse.User, "Model should be a Parse.User"); + const model = await Parse.User._logInWith('facebook'); + ok(model instanceof Parse.User, 'Model should be a Parse.User'); strictEqual(Parse.User.current(), model); - ok(model.extended(), "Should have used the subclass."); + ok(model.extended(), 'Should have used the subclass.'); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); strictEqual( provider.authData.expiration_date, provider.synchronizedExpiration ); - ok(model._isLinked("facebook"), "User should be linked to facebook"); - - await model._unlinkFrom("facebook"); - ok(!model._isLinked("facebook"), "User should not be linked to facebook"); - ok(!provider.synchronizedUserId, "User id should be cleared"); - ok(!provider.synchronizedAuthToken, "Auth token should be cleared"); - ok(!provider.synchronizedExpiration, "Expiration should be cleared"); - - await model._linkWith("facebook"); - ok(provider.synchronizedUserId, "User id should have a value"); - ok(provider.synchronizedAuthToken, "Auth token should have a value"); - ok(provider.synchronizedExpiration, "Expiration should have a value"); - ok(model._isLinked("facebook"), "User should be linked to facebook"); + ok(model._isLinked('facebook'), 'User should be linked to facebook'); + + await model._unlinkFrom('facebook'); + ok(!model._isLinked('facebook'), 'User should not be linked to facebook'); + ok(!provider.synchronizedUserId, 'User id should be cleared'); + ok(!provider.synchronizedAuthToken, 'Auth token should be cleared'); + ok(!provider.synchronizedExpiration, 'Expiration should be cleared'); + + await model._linkWith('facebook'); + ok(provider.synchronizedUserId, 'User id should have a value'); + ok(provider.synchronizedAuthToken, 'Auth token should have a value'); + ok(provider.synchronizedExpiration, 'Expiration should have a value'); + ok(model._isLinked('facebook'), 'User should be linked to facebook'); done(); }); - it("link multiple providers", async done => { + it('link multiple providers', async done => { const provider = getMockFacebookProvider(); const mockProvider = getMockMyOauthProvider(); Parse.User._registerAuthenticationProvider(provider); - const model = await Parse.User._logInWith("facebook"); - ok(model instanceof Parse.User, "Model should be a Parse.User"); + const model = await Parse.User._logInWith('facebook'); + ok(model instanceof Parse.User, 'Model should be a Parse.User'); strictEqual(Parse.User.current(), model); - ok(model.extended(), "Should have used the subclass."); + ok(model.extended(), 'Should have used the subclass.'); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); strictEqual( provider.authData.expiration_date, provider.synchronizedExpiration ); - ok(model._isLinked("facebook"), "User should be linked to facebook"); + ok(model._isLinked('facebook'), 'User should be linked to facebook'); Parse.User._registerAuthenticationProvider(mockProvider); const objectId = model.id; - await model._linkWith("myoauth"); + await model._linkWith('myoauth'); expect(model.id).toEqual(objectId); - ok(model._isLinked("facebook"), "User should be linked to facebook"); - ok(model._isLinked("myoauth"), "User should be linked to myoauth"); + ok(model._isLinked('facebook'), 'User should be linked to facebook'); + ok(model._isLinked('myoauth'), 'User should be linked to myoauth'); done(); }); - it("link multiple providers and updates token", async done => { + it('link multiple providers and updates token', async done => { const provider = getMockFacebookProvider(); const secondProvider = getMockFacebookProviderWithIdToken( - "8675309", - "jenny_valid_token" + '8675309', + 'jenny_valid_token' ); const mockProvider = getMockMyOauthProvider(); Parse.User._registerAuthenticationProvider(provider); - const model = await Parse.User._logInWith("facebook"); + const model = await Parse.User._logInWith('facebook'); Parse.User._registerAuthenticationProvider(mockProvider); const objectId = model.id; - await model._linkWith("myoauth"); + await model._linkWith('myoauth'); Parse.User._registerAuthenticationProvider(secondProvider); await Parse.User.logOut(); - await Parse.User._logInWith("facebook"); + await Parse.User._logInWith('facebook'); await Parse.User.logOut(); - const user = await Parse.User._logInWith("myoauth"); + const user = await Parse.User._logInWith('myoauth'); expect(user.id).toBe(objectId); done(); }); - it("link multiple providers and update token", async done => { + it('link multiple providers and update token', async done => { const provider = getMockFacebookProvider(); const mockProvider = getMockMyOauthProvider(); Parse.User._registerAuthenticationProvider(provider); - const model = await Parse.User._logInWith("facebook"); - ok(model instanceof Parse.User, "Model should be a Parse.User"); + const model = await Parse.User._logInWith('facebook'); + ok(model instanceof Parse.User, 'Model should be a Parse.User'); strictEqual(Parse.User.current(), model); - ok(model.extended(), "Should have used the subclass."); + ok(model.extended(), 'Should have used the subclass.'); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); strictEqual( provider.authData.expiration_date, provider.synchronizedExpiration ); - ok(model._isLinked("facebook"), "User should be linked to facebook"); + ok(model._isLinked('facebook'), 'User should be linked to facebook'); Parse.User._registerAuthenticationProvider(mockProvider); const objectId = model.id; - await model._linkWith("myoauth"); + await model._linkWith('myoauth'); expect(model.id).toEqual(objectId); - ok(model._isLinked("facebook"), "User should be linked to facebook"); - ok(model._isLinked("myoauth"), "User should be linked to myoauth"); - await model._linkWith("facebook"); - ok(model._isLinked("facebook"), "User should be linked to facebook"); - ok(model._isLinked("myoauth"), "User should be linked to myoauth"); + ok(model._isLinked('facebook'), 'User should be linked to facebook'); + ok(model._isLinked('myoauth'), 'User should be linked to myoauth'); + await model._linkWith('facebook'); + ok(model._isLinked('facebook'), 'User should be linked to facebook'); + ok(model._isLinked('myoauth'), 'User should be linked to myoauth'); done(); }); - it("should fail linking with existing", async done => { + it('should fail linking with existing', async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - await Parse.User._logInWith("facebook"); + await Parse.User._logInWith('facebook'); await Parse.User.logOut(); const user = new Parse.User(); - user.setUsername("user"); - user.setPassword("password"); + user.setUsername('user'); + user.setPassword('password'); await user.signUp(); // try to link here try { - await user._linkWith("facebook"); + await user._linkWith('facebook'); done.fail(); } catch (e) { done(); } }); - it("should fail linking with existing through REST", async done => { + it('should fail linking with existing through REST', async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - const model = await Parse.User._logInWith("facebook"); + const model = await Parse.User._logInWith('facebook'); const userId = model.id; Parse.User.logOut().then(() => { request({ - method: "POST", - url: Parse.serverURL + "/classes/_User", + method: 'POST', + url: Parse.serverURL + '/classes/_User', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, body: { authData: { facebook: provider.authData } }, }).then(response => { @@ -1918,18 +1918,18 @@ describe("Parse.User testing", () => { expect(userId).not.toBeUndefined(); expect(body.objectId).toEqual(userId); expect(response.headers.location).toEqual( - Parse.serverURL + "/users/" + userId + Parse.serverURL + '/users/' + userId ); done(); }); }); }); - it("should not allow login with expired authData token since allowExpiredAuthDataToken is set to false by default", async () => { + it('should not allow login with expired authData token since allowExpiredAuthDataToken is set to false by default', async () => { const provider = { authData: { - id: "12345", - access_token: "token", + id: '12345', + access_token: 'token', }, restoreAuthentication: function () { return true; @@ -1941,26 +1941,26 @@ describe("Parse.User testing", () => { options.success(this, provider.authData); }, getAuthType: function () { - return "shortLivedAuth"; + return 'shortLivedAuth'; }, }; - defaultConfiguration.auth.shortLivedAuth.setValidAccessToken("token"); + defaultConfiguration.auth.shortLivedAuth.setValidAccessToken('token'); Parse.User._registerAuthenticationProvider(provider); - await Parse.User._logInWith("shortLivedAuth", {}); + await Parse.User._logInWith('shortLivedAuth', {}); // Simulate a remotely expired token (like a short lived one) // In this case, we want success as it was valid once. // If the client needs an updated token, do lock the user out - defaultConfiguration.auth.shortLivedAuth.setValidAccessToken("otherToken"); + defaultConfiguration.auth.shortLivedAuth.setValidAccessToken('otherToken'); await expectAsync( - Parse.User._logInWith("shortLivedAuth", {}) + Parse.User._logInWith('shortLivedAuth', {}) ).toBeRejected(); }); - it("should allow PUT request with stale auth Data", done => { + it('should allow PUT request with stale auth Data', done => { const provider = { authData: { - id: "12345", - access_token: "token", + id: '12345', + access_token: 'token', }, restoreAuthentication: function () { return true; @@ -1972,35 +1972,35 @@ describe("Parse.User testing", () => { options.success(this, provider.authData); }, getAuthType: function () { - return "shortLivedAuth"; + return 'shortLivedAuth'; }, }; - defaultConfiguration.auth.shortLivedAuth.setValidAccessToken("token"); + defaultConfiguration.auth.shortLivedAuth.setValidAccessToken('token'); Parse.User._registerAuthenticationProvider(provider); - Parse.User._logInWith("shortLivedAuth", {}) + Parse.User._logInWith('shortLivedAuth', {}) .then(() => { // Simulate a remotely expired token (like a short lived one) // In this case, we want success as it was valid once. // If the client needs an updated one, do lock the user out defaultConfiguration.auth.shortLivedAuth.setValidAccessToken( - "otherToken" + 'otherToken' ); return request({ - method: "PUT", - url: Parse.serverURL + "/users/" + Parse.User.current().id, + method: 'PUT', + url: Parse.serverURL + '/users/' + Parse.User.current().id, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Javascript-Key": Parse.javaScriptKey, - "X-Parse-Session-Token": Parse.User.current().getSessionToken(), - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, + 'X-Parse-Session-Token': Parse.User.current().getSessionToken(), + 'Content-Type': 'application/json', }, body: { - key: "value", // update a key + key: 'value', // update a key authData: { // pass the original auth data shortLivedAuth: { - id: "12345", - access_token: "token", + id: '12345', + access_token: 'token', }, }, }, @@ -2016,74 +2016,74 @@ describe("Parse.User testing", () => { ); }); - it("should properly error when password is missing", async done => { + it('should properly error when password is missing', async done => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - const user = await Parse.User._logInWith("facebook"); - user.set("username", "myUser"); - user.set("email", "foo@example.com"); + const user = await Parse.User._logInWith('facebook'); + user.set('username', 'myUser'); + user.set('email', 'foo@example.com'); user .save() .then(() => { return Parse.User.logOut(); }) .then(() => { - return Parse.User.logIn("myUser", "password"); + return Parse.User.logIn('myUser', 'password'); }) .then( () => { - fail("should not succeed"); + fail('should not succeed'); done(); }, err => { expect(err.code).toBe(Parse.Error.OBJECT_NOT_FOUND); - expect(err.message).toEqual("Invalid username/password."); + expect(err.message).toEqual('Invalid username/password.'); done(); } ); }); - it("should have authData in beforeSave and afterSave", async done => { - Parse.Cloud.beforeSave("_User", request => { - const authData = request.object.get("authData"); + it('should have authData in beforeSave and afterSave', async done => { + Parse.Cloud.beforeSave('_User', request => { + const authData = request.object.get('authData'); expect(authData).not.toBeUndefined(); if (authData) { - expect(authData.facebook.id).toEqual("8675309"); - expect(authData.facebook.access_token).toEqual("jenny"); + expect(authData.facebook.id).toEqual('8675309'); + expect(authData.facebook.access_token).toEqual('jenny'); } else { - fail("authData should be set"); + fail('authData should be set'); } }); - Parse.Cloud.afterSave("_User", request => { - const authData = request.object.get("authData"); + Parse.Cloud.afterSave('_User', request => { + const authData = request.object.get('authData'); expect(authData).not.toBeUndefined(); if (authData) { - expect(authData.facebook.id).toEqual("8675309"); - expect(authData.facebook.access_token).toEqual("jenny"); + expect(authData.facebook.id).toEqual('8675309'); + expect(authData.facebook.access_token).toEqual('jenny'); } else { - fail("authData should be set"); + fail('authData should be set'); } }); const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - await Parse.User._logInWith("facebook"); + await Parse.User._logInWith('facebook'); done(); }); - it("set password then change password", done => { - Parse.User.signUp("bob", "barker") + it('set password then change password', done => { + Parse.User.signUp('bob', 'barker') .then(bob => { - bob.setPassword("meower"); + bob.setPassword('meower'); return bob.save(); }) .then(() => { - return Parse.User.logIn("bob", "meower"); + return Parse.User.logIn('bob', 'meower'); }) .then( bob => { - expect(bob.getUsername()).toEqual("bob"); + expect(bob.getUsername()).toEqual('bob'); done(); }, e => { @@ -2093,29 +2093,29 @@ describe("Parse.User testing", () => { ); }); - it("authenticated check", async done => { + it('authenticated check', async done => { const user = new Parse.User(); - user.set("username", "darkhelmet"); - user.set("password", "onetwothreefour"); + user.set('username', 'darkhelmet'); + user.set('password', 'onetwothreefour'); ok(!user.authenticated()); await user.signUp(null); ok(user.authenticated()); done(); }); - it("log in with explicit facebook auth data", async done => { + it('log in with explicit facebook auth data', async done => { await Parse.FacebookUtils.logIn({ - id: "8675309", - access_token: "jenny", + id: '8675309', + access_token: 'jenny', expiration_date: new Date().toJSON(), }); done(); }); - it("log in async with explicit facebook auth data", done => { + it('log in async with explicit facebook auth data', done => { Parse.FacebookUtils.logIn({ - id: "8675309", - access_token: "jenny", + id: '8675309', + access_token: 'jenny', expiration_date: new Date().toJSON(), }).then( function () { @@ -2128,11 +2128,11 @@ describe("Parse.User testing", () => { ); }); - it("link with explicit facebook auth data", async done => { - const user = await Parse.User.signUp("mask", "open sesame"); + it('link with explicit facebook auth data', async done => { + const user = await Parse.User.signUp('mask', 'open sesame'); Parse.FacebookUtils.link(user, { - id: "8675309", - access_token: "jenny", + id: '8675309', + access_token: 'jenny', expiration_date: new Date().toJSON(), }).then(done, error => { jfail(error); @@ -2140,11 +2140,11 @@ describe("Parse.User testing", () => { }); }); - it("link async with explicit facebook auth data", async done => { - const user = await Parse.User.signUp("mask", "open sesame"); + it('link async with explicit facebook auth data', async done => { + const user = await Parse.User.signUp('mask', 'open sesame'); Parse.FacebookUtils.link(user, { - id: "8675309", - access_token: "jenny", + id: '8675309', + access_token: 'jenny', expiration_date: new Date().toJSON(), }).then( function () { @@ -2157,56 +2157,56 @@ describe("Parse.User testing", () => { ); }); - it("async methods", done => { - const data = { foo: "bar" }; + it('async methods', done => { + const data = { foo: 'bar' }; - Parse.User.signUp("finn", "human", data) + Parse.User.signUp('finn', 'human', data) .then(function (user) { equal(Parse.User.current(), user); - equal(user.get("foo"), "bar"); + equal(user.get('foo'), 'bar'); return Parse.User.logOut(); }) .then(function () { - return Parse.User.logIn("finn", "human"); + return Parse.User.logIn('finn', 'human'); }) .then(function (user) { equal(user, Parse.User.current()); - equal(user.get("foo"), "bar"); + equal(user.get('foo'), 'bar'); return Parse.User.logOut(); }) .then(function () { const user = new Parse.User(); - user.set("username", "jake"); - user.set("password", "dog"); - user.set("foo", "baz"); + user.set('username', 'jake'); + user.set('password', 'dog'); + user.set('foo', 'baz'); return user.signUp(); }) .then(function (user) { equal(user, Parse.User.current()); - equal(user.get("foo"), "baz"); + equal(user.get('foo'), 'baz'); user = new Parse.User(); - user.set("username", "jake"); - user.set("password", "dog"); + user.set('username', 'jake'); + user.set('password', 'dog'); return user.logIn(); }) .then(function (user) { equal(user, Parse.User.current()); - equal(user.get("foo"), "baz"); + equal(user.get('foo'), 'baz'); const userAgain = new Parse.User(); userAgain.id = user.id; return userAgain.fetch(); }) .then(function (userAgain) { - equal(userAgain.get("foo"), "baz"); + equal(userAgain.get('foo'), 'baz'); done(); }); }); it("querying for users doesn't get session tokens", done => { const user = new Parse.User(); - user.set("username", "finn"); - user.set("password", "human"); - user.set("foo", "bar"); + user.set('username', 'finn'); + user.set('password', 'human'); + user.set('foo', 'bar'); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); user.setACL(acl); @@ -2217,9 +2217,9 @@ describe("Parse.User testing", () => { }) .then(() => { const user = new Parse.User(); - user.set("username", "jake"); - user.set("password", "dog"); - user.set("foo", "baz"); + user.set('username', 'jake'); + user.set('password', 'dog'); + user.set('foo', 'baz'); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); user.setACL(acl); @@ -2239,7 +2239,7 @@ describe("Parse.User testing", () => { expect(user.getSessionToken()).toBeUndefined(); ok( !user.getSessionToken(), - "user should not have a session token." + 'user should not have a session token.' ); }); done(); @@ -2251,21 +2251,21 @@ describe("Parse.User testing", () => { ); }); - it("querying for users only gets the expected fields", done => { + it('querying for users only gets the expected fields', done => { const user = new Parse.User(); - user.setUsername("finn"); - user.setPassword("human"); - user.set("foo", "bar"); + user.setUsername('finn'); + user.setPassword('human'); + user.set('foo', 'bar'); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); user.setACL(acl); user.signUp().then(() => { request({ headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }, - url: "http://localhost:8378/1/users", + url: 'http://localhost:8378/1/users', }).then(response => { const b = response.data; expect(b.results.length).toEqual(1); @@ -2278,9 +2278,9 @@ describe("Parse.User testing", () => { it("retrieve user data from fetch, make sure the session token hasn't changed", done => { const user = new Parse.User(); - user.setPassword("asdf"); - user.setUsername("zxcv"); - let currentSessionToken = ""; + user.setPassword('asdf'); + user.setUsername('zxcv'); + let currentSessionToken = ''; Promise.resolve() .then(function () { return user.signUp(); @@ -2301,14 +2301,14 @@ describe("Parse.User testing", () => { ); }); - it("user save should fail with invalid email", done => { + it('user save should fail with invalid email', done => { const user = new Parse.User(); - user.set("username", "teste"); - user.set("password", "test"); - user.set("email", "invalid"); + user.set('username', 'teste'); + user.set('password', 'test'); + user.set('email', 'invalid'); user.signUp().then( () => { - fail("Should not have been able to save."); + fail('Should not have been able to save.'); done(); }, error => { @@ -2318,23 +2318,23 @@ describe("Parse.User testing", () => { ); }); - it("user signup should error if email taken", done => { + it('user signup should error if email taken', done => { const user = new Parse.User(); - user.set("username", "test1"); - user.set("password", "test"); - user.set("email", "test@test.com"); + user.set('username', 'test1'); + user.set('password', 'test'); + user.set('email', 'test@test.com'); user .signUp() .then(() => { const user2 = new Parse.User(); - user2.set("username", "test2"); - user2.set("password", "test"); - user2.set("email", "test@test.com"); + user2.set('username', 'test2'); + user2.set('password', 'test'); + user2.set('email', 'test@test.com'); return user2.signUp(); }) .then( () => { - fail("Should not have been able to sign up."); + fail('Should not have been able to sign up.'); done(); }, () => { @@ -2343,136 +2343,136 @@ describe("Parse.User testing", () => { ); }); - describe("case insensitive signup not allowed", () => { - it_id("464eddc2-7a46-413d-888e-b43b040f1511")(it)( - "signup should fail with duplicate case insensitive username with basic setter", + describe('case insensitive signup not allowed', () => { + it_id('464eddc2-7a46-413d-888e-b43b040f1511')(it)( + 'signup should fail with duplicate case insensitive username with basic setter', async () => { const user = new Parse.User(); - user.set("username", "test1"); - user.set("password", "test"); + user.set('username', 'test1'); + user.set('password', 'test'); await user.signUp(); const user2 = new Parse.User(); - user2.set("username", "Test1"); - user2.set("password", "test"); + user2.set('username', 'Test1'); + user2.set('password', 'test'); await expectAsync(user2.signUp()).toBeRejectedWith( new Parse.Error( Parse.Error.USERNAME_TAKEN, - "Account already exists for this username." + 'Account already exists for this username.' ) ); } ); - it_id("1cef005b-d5f0-4699-af0c-bb0af27d2437")(it)( - "signup should fail with duplicate case insensitive username with field specific setter", + it_id('1cef005b-d5f0-4699-af0c-bb0af27d2437')(it)( + 'signup should fail with duplicate case insensitive username with field specific setter', async () => { const user = new Parse.User(); - user.setUsername("test1"); - user.setPassword("test"); + user.setUsername('test1'); + user.setPassword('test'); await user.signUp(); const user2 = new Parse.User(); - user2.setUsername("Test1"); - user2.setPassword("test"); + user2.setUsername('Test1'); + user2.setPassword('test'); await expectAsync(user2.signUp()).toBeRejectedWith( new Parse.Error( Parse.Error.USERNAME_TAKEN, - "Account already exists for this username." + 'Account already exists for this username.' ) ); } ); - it_id("12735529-98d1-42c0-b437-3b47fe78ddde")(it)( - "signup should fail with duplicate case insensitive email", + it_id('12735529-98d1-42c0-b437-3b47fe78ddde')(it)( + 'signup should fail with duplicate case insensitive email', async () => { const user = new Parse.User(); - user.setUsername("test1"); - user.setPassword("test"); - user.setEmail("test@example.com"); + user.setUsername('test1'); + user.setPassword('test'); + user.setEmail('test@example.com'); await user.signUp(); const user2 = new Parse.User(); - user2.setUsername("test2"); - user2.setPassword("test"); - user2.setEmail("Test@Example.Com"); + user2.setUsername('test2'); + user2.setPassword('test'); + user2.setEmail('Test@Example.Com'); await expectAsync(user2.signUp()).toBeRejectedWith( new Parse.Error( Parse.Error.EMAIL_TAKEN, - "Account already exists for this email address." + 'Account already exists for this email address.' ) ); } ); - it_id("66e51d52-2420-4b62-8a0d-c7e1b384763e")(it)( - "edit should fail with duplicate case insensitive email", + it_id('66e51d52-2420-4b62-8a0d-c7e1b384763e')(it)( + 'edit should fail with duplicate case insensitive email', async () => { const user = new Parse.User(); - user.setUsername("test1"); - user.setPassword("test"); - user.setEmail("test@example.com"); + user.setUsername('test1'); + user.setPassword('test'); + user.setEmail('test@example.com'); await user.signUp(); const user2 = new Parse.User(); - user2.setUsername("test2"); - user2.setPassword("test"); - user2.setEmail("Foo@Example.Com"); + user2.setUsername('test2'); + user2.setPassword('test'); + user2.setEmail('Foo@Example.Com'); await user2.signUp(); - user2.setEmail("Test@Example.Com"); + user2.setEmail('Test@Example.Com'); await expectAsync(user2.save()).toBeRejectedWith( new Parse.Error( Parse.Error.EMAIL_TAKEN, - "Account already exists for this email address." + 'Account already exists for this email address.' ) ); } ); - describe("anonymous users", () => { - it("should not fail on case insensitive matches", async () => { - spyOn(cryptoUtils, "randomString").and.returnValue("abcdefghijklmnop"); + describe('anonymous users', () => { + it('should not fail on case insensitive matches', async () => { + spyOn(cryptoUtils, 'randomString').and.returnValue('abcdefghijklmnop'); const logIn = id => - Parse.User.logInWith("anonymous", { authData: { id } }); - const user1 = await logIn("test1"); - const username1 = user1.get("username"); + Parse.User.logInWith('anonymous', { authData: { id } }); + const user1 = await logIn('test1'); + const username1 = user1.get('username'); - cryptoUtils.randomString.and.returnValue("ABCDEFGHIJKLMNOp"); - const user2 = await logIn("test2"); - const username2 = user2.get("username"); + cryptoUtils.randomString.and.returnValue('ABCDEFGHIJKLMNOp'); + const user2 = await logIn('test2'); + const username2 = user2.get('username'); expect(username1).not.toBeUndefined(); expect(username2).not.toBeUndefined(); - expect(username1.toLowerCase()).toBe("abcdefghijklmnop"); - expect(username2.toLowerCase()).toBe("abcdefghijklmnop"); + expect(username1.toLowerCase()).toBe('abcdefghijklmnop'); + expect(username2.toLowerCase()).toBe('abcdefghijklmnop'); expect(username2).not.toBe(username1); expect(username2.toLowerCase()).toBe(username1.toLowerCase()); // this is redundant :). }); }); }); - it("user cannot update email to existing user", done => { + it('user cannot update email to existing user', done => { const user = new Parse.User(); - user.set("username", "test1"); - user.set("password", "test"); - user.set("email", "test@test.com"); + user.set('username', 'test1'); + user.set('password', 'test'); + user.set('email', 'test@test.com'); user .signUp() .then(() => { const user2 = new Parse.User(); - user2.set("username", "test2"); - user2.set("password", "test"); + user2.set('username', 'test2'); + user2.set('password', 'test'); return user2.signUp(); }) .then(user2 => { - user2.set("email", "test@test.com"); + user2.set('email', 'test@test.com'); return user2.save(); }) .then( () => { - fail("Should not have been able to sign up."); + fail('Should not have been able to sign up.'); done(); }, () => { @@ -2481,19 +2481,19 @@ describe("Parse.User testing", () => { ); }); - it("unset user email", done => { + it('unset user email', done => { const user = new Parse.User(); - user.set("username", "test"); - user.set("password", "test"); - user.set("email", "test@test.com"); + user.set('username', 'test'); + user.set('password', 'test'); + user.set('email', 'test@test.com'); user .signUp() .then(() => { - user.unset("email"); + user.unset('email'); return user.save(); }) .then(() => { - return Parse.User.logIn("test", "test"); + return Parse.User.logIn('test', 'test'); }) .then(user => { expect(user.getEmail()).toBeUndefined(); @@ -2501,115 +2501,115 @@ describe("Parse.User testing", () => { }); }); - it("create session from user", done => { + it('create session from user', done => { Promise.resolve() .then(() => { - return Parse.User.signUp("finn", "human", { foo: "bar" }); + return Parse.User.signUp('finn', 'human', { foo: 'bar' }); }) .then(user => { request({ - method: "POST", + method: 'POST', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Session-Token": user.getSessionToken(), - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Session-Token': user.getSessionToken(), + 'X-Parse-REST-API-Key': 'rest', }, - url: "http://localhost:8378/1/sessions", + url: 'http://localhost:8378/1/sessions', }).then(response => { const b = response.data; - expect(typeof b.sessionToken).toEqual("string"); - expect(typeof b.createdWith).toEqual("object"); - expect(b.createdWith.action).toEqual("create"); - expect(typeof b.user).toEqual("object"); + expect(typeof b.sessionToken).toEqual('string'); + expect(typeof b.createdWith).toEqual('object'); + expect(b.createdWith.action).toEqual('create'); + expect(typeof b.user).toEqual('object'); expect(b.user.objectId).toEqual(user.id); done(); }); }); }); - it("user get session from token on signup", async () => { - const user = await Parse.User.signUp("finn", "human", { foo: "bar" }); + it('user get session from token on signup', async () => { + const user = await Parse.User.signUp('finn', 'human', { foo: 'bar' }); const response = await request({ headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Session-Token": user.getSessionToken(), - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Session-Token': user.getSessionToken(), + 'X-Parse-REST-API-Key': 'rest', }, - url: "http://localhost:8378/1/sessions/me", + url: 'http://localhost:8378/1/sessions/me', }); const data = response.data; - expect(typeof data.sessionToken).toEqual("string"); - expect(typeof data.createdWith).toEqual("object"); - expect(data.createdWith.action).toEqual("signup"); - expect(data.createdWith.authProvider).toEqual("password"); - expect(typeof data.user).toEqual("object"); + expect(typeof data.sessionToken).toEqual('string'); + expect(typeof data.createdWith).toEqual('object'); + expect(data.createdWith.action).toEqual('signup'); + expect(data.createdWith.authProvider).toEqual('password'); + expect(typeof data.user).toEqual('object'); expect(data.user.objectId).toEqual(user.id); }); - it("user get session from token on username/password login", async () => { - await Parse.User.signUp("finn", "human", { foo: "bar" }); + it('user get session from token on username/password login', async () => { + await Parse.User.signUp('finn', 'human', { foo: 'bar' }); await Parse.User.logOut(); - const user = await Parse.User.logIn("finn", "human"); + const user = await Parse.User.logIn('finn', 'human'); const response = await request({ headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Session-Token": user.getSessionToken(), - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Session-Token': user.getSessionToken(), + 'X-Parse-REST-API-Key': 'rest', }, - url: "http://localhost:8378/1/sessions/me", + url: 'http://localhost:8378/1/sessions/me', }); const data = response.data; - expect(typeof data.sessionToken).toEqual("string"); - expect(typeof data.createdWith).toEqual("object"); - expect(data.createdWith.action).toEqual("login"); - expect(data.createdWith.authProvider).toEqual("password"); - expect(typeof data.user).toEqual("object"); + expect(typeof data.sessionToken).toEqual('string'); + expect(typeof data.createdWith).toEqual('object'); + expect(data.createdWith.action).toEqual('login'); + expect(data.createdWith.authProvider).toEqual('password'); + expect(typeof data.user).toEqual('object'); expect(data.user.objectId).toEqual(user.id); }); - it("user get session from token on anonymous login", async () => { + it('user get session from token on anonymous login', async () => { const user = await Parse.AnonymousUtils.logIn(); const response = await request({ headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Session-Token": user.getSessionToken(), - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Session-Token': user.getSessionToken(), + 'X-Parse-REST-API-Key': 'rest', }, - url: "http://localhost:8378/1/sessions/me", + url: 'http://localhost:8378/1/sessions/me', }); const data = response.data; - expect(typeof data.sessionToken).toEqual("string"); - expect(typeof data.createdWith).toEqual("object"); - expect(data.createdWith.action).toEqual("login"); - expect(data.createdWith.authProvider).toEqual("anonymous"); - expect(typeof data.user).toEqual("object"); + expect(typeof data.sessionToken).toEqual('string'); + expect(typeof data.createdWith).toEqual('object'); + expect(data.createdWith.action).toEqual('login'); + expect(data.createdWith.authProvider).toEqual('anonymous'); + expect(typeof data.user).toEqual('object'); expect(data.user.objectId).toEqual(user.id); }); - it("user update session with other field", done => { + it('user update session with other field', done => { Promise.resolve() .then(() => { - return Parse.User.signUp("finn", "human", { foo: "bar" }); + return Parse.User.signUp('finn', 'human', { foo: 'bar' }); }) .then(user => { request({ headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Session-Token": user.getSessionToken(), - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Session-Token': user.getSessionToken(), + 'X-Parse-REST-API-Key': 'rest', }, - url: "http://localhost:8378/1/sessions/me", + url: 'http://localhost:8378/1/sessions/me', }).then(response => { const b = response.data; request({ - method: "PUT", + method: 'PUT', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Session-Token": user.getSessionToken(), - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Session-Token': user.getSessionToken(), + 'X-Parse-REST-API-Key': 'rest', }, - url: "http://localhost:8378/1/sessions/" + b.objectId, - body: JSON.stringify({ foo: "bar" }), + url: 'http://localhost:8378/1/sessions/' + b.objectId, + body: JSON.stringify({ foo: 'bar' }), }).then(() => { done(); }); @@ -2617,45 +2617,45 @@ describe("Parse.User testing", () => { }); }); - it("cannot update session if invalid or no session token", done => { + it('cannot update session if invalid or no session token', done => { Promise.resolve() .then(() => { - return Parse.User.signUp("finn", "human", { foo: "bar" }); + return Parse.User.signUp('finn', 'human', { foo: 'bar' }); }) .then(user => { request({ headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Session-Token": user.getSessionToken(), - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Session-Token': user.getSessionToken(), + 'X-Parse-REST-API-Key': 'rest', }, - url: "http://localhost:8378/1/sessions/me", + url: 'http://localhost:8378/1/sessions/me', }).then(response => { const b = response.data; request({ - method: "PUT", + method: 'PUT', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Session-Token": "foo", - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Session-Token': 'foo', + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, - url: "http://localhost:8378/1/sessions/" + b.objectId, - body: JSON.stringify({ foo: "bar" }), + url: 'http://localhost:8378/1/sessions/' + b.objectId, + body: JSON.stringify({ foo: 'bar' }), }).then(fail, response => { const b = response.data; - expect(b.error).toBe("Invalid session token"); + expect(b.error).toBe('Invalid session token'); request({ - method: "PUT", + method: 'PUT', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }, - url: "http://localhost:8378/1/sessions/" + b.objectId, - body: JSON.stringify({ foo: "bar" }), + url: 'http://localhost:8378/1/sessions/' + b.objectId, + body: JSON.stringify({ foo: 'bar' }), }).then(fail, response => { const b = response.data; - expect(b.error).toBe("Session token required."); + expect(b.error).toBe('Session token required.'); done(); }); }); @@ -2663,48 +2663,48 @@ describe("Parse.User testing", () => { }); }); - it("get session only for current user", done => { + it('get session only for current user', done => { Promise.resolve() .then(() => { - return Parse.User.signUp("test1", "test", { foo: "bar" }); + return Parse.User.signUp('test1', 'test', { foo: 'bar' }); }) .then(() => { - return Parse.User.signUp("test2", "test", { foo: "bar" }); + return Parse.User.signUp('test2', 'test', { foo: 'bar' }); }) .then(user => { request({ headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Session-Token": user.getSessionToken(), - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Session-Token': user.getSessionToken(), + 'X-Parse-REST-API-Key': 'rest', }, - url: "http://localhost:8378/1/sessions", + url: 'http://localhost:8378/1/sessions', }).then(response => { const b = response.data; expect(b.results.length).toEqual(1); - expect(typeof b.results[0].user).toEqual("object"); + expect(typeof b.results[0].user).toEqual('object'); expect(b.results[0].user.objectId).toEqual(user.id); done(); }); }); }); - it("delete session by object", done => { + it('delete session by object', done => { Promise.resolve() .then(() => { - return Parse.User.signUp("test1", "test", { foo: "bar" }); + return Parse.User.signUp('test1', 'test', { foo: 'bar' }); }) .then(() => { - return Parse.User.signUp("test2", "test", { foo: "bar" }); + return Parse.User.signUp('test2', 'test', { foo: 'bar' }); }) .then(user => { request({ headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Session-Token": user.getSessionToken(), - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Session-Token': user.getSessionToken(), + 'X-Parse-REST-API-Key': 'rest', }, - url: "http://localhost:8378/1/sessions", + url: 'http://localhost:8378/1/sessions', }).then(response => { const b = response.data; let objId; @@ -2717,25 +2717,25 @@ describe("Parse.User testing", () => { return; } request({ - method: "DELETE", + method: 'DELETE', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Session-Token": user.getSessionToken(), - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Session-Token': user.getSessionToken(), + 'X-Parse-REST-API-Key': 'rest', }, - url: "http://localhost:8378/1/sessions/" + objId, + url: 'http://localhost:8378/1/sessions/' + objId, }).then(() => { request({ headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Session-Token": user.getSessionToken(), - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Session-Token': user.getSessionToken(), + 'X-Parse-REST-API-Key': 'rest', }, - url: "http://localhost:8378/1/sessions", + url: 'http://localhost:8378/1/sessions', }).then(fail, response => { const b = response.data; expect(b.code).toEqual(209); - expect(b.error).toBe("Invalid session token"); + expect(b.error).toBe('Invalid session token'); done(); }); }); @@ -2743,70 +2743,70 @@ describe("Parse.User testing", () => { }); }); - it("cannot delete session if no sessionToken", done => { + it('cannot delete session if no sessionToken', done => { Promise.resolve() .then(() => { - return Parse.User.signUp("test1", "test", { foo: "bar" }); + return Parse.User.signUp('test1', 'test', { foo: 'bar' }); }) .then(() => { - return Parse.User.signUp("test2", "test", { foo: "bar" }); + return Parse.User.signUp('test2', 'test', { foo: 'bar' }); }) .then(user => { request({ headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Session-Token": user.getSessionToken(), - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Session-Token': user.getSessionToken(), + 'X-Parse-REST-API-Key': 'rest', }, - url: "http://localhost:8378/1/sessions", + url: 'http://localhost:8378/1/sessions', }).then(response => { const b = response.data; expect(b.results.length).toEqual(1); const objId = b.results[0].objectId; request({ - method: "DELETE", + method: 'DELETE', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }, - url: "http://localhost:8378/1/sessions/" + objId, + url: 'http://localhost:8378/1/sessions/' + objId, }).then(fail, response => { const b = response.data; expect(b.code).toEqual(209); - expect(b.error).toBe("Invalid session token"); + expect(b.error).toBe('Invalid session token'); done(); }); }); }); }); - it("password format matches hosted parse", done => { + it('password format matches hosted parse', done => { const hashed = - "$2a$10$8/wZJyEuiEaobBBqzTG.jeY.XSFJd0rzaN//ososvEI4yLqI.4aie"; - passwordCrypto.compare("test", hashed).then( + '$2a$10$8/wZJyEuiEaobBBqzTG.jeY.XSFJd0rzaN//ososvEI4yLqI.4aie'; + passwordCrypto.compare('test', hashed).then( pass => { expect(pass).toBe(true); done(); }, () => { - fail("Password format did not match."); + fail('Password format did not match.'); done(); } ); }); - it("changing password clears sessions", done => { + it('changing password clears sessions', done => { let sessionToken = null; Promise.resolve() .then(function () { - return Parse.User.signUp("fosco", "parse"); + return Parse.User.signUp('fosco', 'parse'); }) .then(function (newUser) { equal(Parse.User.current(), newUser); sessionToken = newUser.getSessionToken(); ok(sessionToken); - newUser.set("password", "facebook"); + newUser.set('password', 'facebook'); return newUser.save(); }) .then(function () { @@ -2814,28 +2814,28 @@ describe("Parse.User testing", () => { }) .then( function () { - fail("Session should have been invalidated"); + fail('Session should have been invalidated'); done(); }, function (err) { expect(err.code).toBe(Parse.Error.INVALID_SESSION_TOKEN); - expect(err.message).toBe("Invalid session token"); + expect(err.message).toBe('Invalid session token'); done(); } ); }); - it("test parse user become", done => { + it('test parse user become', done => { let sessionToken = null; Promise.resolve() .then(function () { - return Parse.User.signUp("flessard", "folo", { foo: 1 }); + return Parse.User.signUp('flessard', 'folo', { foo: 1 }); }) .then(function (newUser) { equal(Parse.User.current(), newUser); sessionToken = newUser.getSessionToken(); ok(sessionToken); - newUser.set("foo", 2); + newUser.set('foo', 2); return newUser.save(); }) .then(function () { @@ -2843,23 +2843,23 @@ describe("Parse.User testing", () => { }) .then( function (newUser) { - equal(newUser.get("foo"), 2); + equal(newUser.get('foo'), 2); done(); }, function () { - fail("The session should still be valid"); + fail('The session should still be valid'); done(); } ); }); - it("ensure logout works", done => { + it('ensure logout works', done => { let user = null; let sessionToken = null; Promise.resolve() .then(function () { - return Parse.User.signUp("log", "out"); + return Parse.User.signUp('log', 'out'); }) .then(newUser => { user = newUser; @@ -2867,12 +2867,12 @@ describe("Parse.User testing", () => { return Parse.User.logOut(); }) .then(() => { - user.set("foo", "bar"); + user.set('foo', 'bar'); return user.save(null, { sessionToken: sessionToken }); }) .then( () => { - fail("Save should have failed."); + fail('Save should have failed.'); done(); }, e => { @@ -2882,137 +2882,137 @@ describe("Parse.User testing", () => { ); }); - it("support user/password signup with empty authData block", done => { + it('support user/password signup with empty authData block', done => { // The android SDK can send an empty authData object along with username and password. - Parse.User.signUp("artof", "thedeal", { authData: {} }).then( + Parse.User.signUp('artof', 'thedeal', { authData: {} }).then( () => { done(); }, () => { - fail("Signup should have succeeded."); + fail('Signup should have succeeded.'); done(); } ); }); - it("session expiresAt correct format", async done => { - await Parse.User.signUp("asdf", "zxcv"); + it('session expiresAt correct format', async done => { + await Parse.User.signUp('asdf', 'zxcv'); request({ - url: "http://localhost:8378/1/classes/_Session", + url: 'http://localhost:8378/1/classes/_Session', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Master-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Master-Key': 'test', }, }).then(response => { const body = response.data; - expect(body.results[0].expiresAt.__type).toEqual("Date"); + expect(body.results[0].expiresAt.__type).toEqual('Date'); done(); }); }); - it("Invalid session tokens are rejected", async done => { - await Parse.User.signUp("asdf", "zxcv"); + it('Invalid session tokens are rejected', async done => { + await Parse.User.signUp('asdf', 'zxcv'); request({ - url: "http://localhost:8378/1/classes/AClass", + url: 'http://localhost:8378/1/classes/AClass', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Rest-API-Key": "rest", - "X-Parse-Session-Token": "text", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Rest-API-Key': 'rest', + 'X-Parse-Session-Token': 'text', }, }).then(fail, response => { const body = response.data; expect(body.code).toBe(209); - expect(body.error).toBe("Invalid session token"); + expect(body.error).toBe('Invalid session token'); done(); }); }); - it_exclude_dbs(["postgres"])( - "should cleanup null authData keys (regression test for #935)", + it_exclude_dbs(['postgres'])( + 'should cleanup null authData keys (regression test for #935)', done => { const database = Config.get(Parse.applicationId).database; database .create( - "_User", + '_User', { - username: "user", + username: 'user', _hashed_password: - "$2a$10$8/wZJyEuiEaobBBqzTG.jeY.XSFJd0rzaN//ososvEI4yLqI.4aie", + '$2a$10$8/wZJyEuiEaobBBqzTG.jeY.XSFJd0rzaN//ososvEI4yLqI.4aie', _auth_data_facebook: null, }, {} ) .then(() => { return request({ - url: "http://localhost:8378/1/login?username=user&password=test", + url: 'http://localhost:8378/1/login?username=user&password=test', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Master-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Master-Key': 'test', }, }).then(res => res.data); }) .then(user => { const authData = user.authData; - expect(user.username).toEqual("user"); + expect(user.username).toEqual('user'); expect(authData).toBeUndefined(); done(); }) .catch(() => { - fail("this should not fail"); + fail('this should not fail'); done(); }); } ); - it_exclude_dbs(["postgres"])("should not serve null authData keys", done => { + it_exclude_dbs(['postgres'])('should not serve null authData keys', done => { const database = Config.get(Parse.applicationId).database; database .create( - "_User", + '_User', { - username: "user", + username: 'user', _hashed_password: - "$2a$10$8/wZJyEuiEaobBBqzTG.jeY.XSFJd0rzaN//ososvEI4yLqI.4aie", + '$2a$10$8/wZJyEuiEaobBBqzTG.jeY.XSFJd0rzaN//ososvEI4yLqI.4aie', _auth_data_facebook: null, }, {} ) .then(() => { return new Parse.Query(Parse.User) - .equalTo("username", "user") + .equalTo('username', 'user') .first({ useMasterKey: true }); }) .then(user => { - const authData = user.get("authData"); - expect(user.get("username")).toEqual("user"); + const authData = user.get('authData'); + expect(user.get('username')).toEqual('user'); expect(authData).toBeUndefined(); done(); }) .catch(() => { - fail("this should not fail"); + fail('this should not fail'); done(); }); }); - it("should cleanup null authData keys ParseUser update (regression test for #1198, #2252)", done => { - Parse.Cloud.beforeSave("_User", req => { - req.object.set("foo", "bar"); + it('should cleanup null authData keys ParseUser update (regression test for #1198, #2252)', done => { + Parse.Cloud.beforeSave('_User', req => { + req.object.set('foo', 'bar'); }); let originalSessionToken; let originalUserId; // Simulate anonymous user save request({ - method: "POST", - url: "http://localhost:8378/1/classes/_User", + method: 'POST', + url: 'http://localhost:8378/1/classes/_User', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, body: { authData: { - anonymous: { id: "00000000-0000-0000-0000-000000000001" }, + anonymous: { id: '00000000-0000-0000-0000-000000000001' }, }, }, }) @@ -3022,53 +3022,53 @@ describe("Parse.User testing", () => { originalUserId = user.objectId; // Simulate registration return request({ - method: "PUT", - url: "http://localhost:8378/1/classes/_User/" + user.objectId, + method: 'PUT', + url: 'http://localhost:8378/1/classes/_User/' + user.objectId, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Session-Token": user.sessionToken, - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Session-Token': user.sessionToken, + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, body: { authData: { anonymous: null }, - username: "user", - password: "password", + username: 'user', + password: 'password', }, }).then(response => { return response.data; }); }) .then(user => { - expect(typeof user).toEqual("object"); + expect(typeof user).toEqual('object'); expect(user.authData).toBeUndefined(); expect(user.sessionToken).not.toBeUndefined(); // Session token should have changed expect(user.sessionToken).not.toEqual(originalSessionToken); // test that the sessionToken is valid return request({ - url: "http://localhost:8378/1/users/me", + url: 'http://localhost:8378/1/users/me', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Session-Token": user.sessionToken, - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Session-Token': user.sessionToken, + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, }).then(response => { const body = response.data; - expect(body.username).toEqual("user"); + expect(body.username).toEqual('user'); expect(body.objectId).toEqual(originalUserId); done(); }); }) .catch(err => { - fail("no request should fail: " + JSON.stringify(err)); + fail('no request should fail: ' + JSON.stringify(err)); done(); }); }); - it_id("1be98368-19ac-4c77-8531-762a114f43fb")(it)( - "should send email when upgrading from anon", + it_id('1be98368-19ac-4c77-8531-762a114f43fb')(it)( + 'should send email when upgrading from anon', async done => { await reconfigureServer(); let emailCalled = false; @@ -3082,42 +3082,42 @@ describe("Parse.User testing", () => { sendMail: () => Promise.resolve(), }; await reconfigureServer({ - appName: "unused", + appName: 'unused', verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); // Simulate anonymous user save return request({ - method: "POST", - url: "http://localhost:8378/1/classes/_User", + method: 'POST', + url: 'http://localhost:8378/1/classes/_User', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, body: { authData: { - anonymous: { id: "00000000-0000-0000-0000-000000000001" }, + anonymous: { id: '00000000-0000-0000-0000-000000000001' }, }, }, }) .then(response => { const user = response.data; return request({ - method: "PUT", - url: "http://localhost:8378/1/classes/_User/" + user.objectId, + method: 'PUT', + url: 'http://localhost:8378/1/classes/_User/' + user.objectId, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Session-Token": user.sessionToken, - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Session-Token': user.sessionToken, + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, body: { authData: { anonymous: null }, - username: "user", - email: "user@email.com", - password: "password", + username: 'user', + email: 'user@email.com', + password: 'password', }, }); }) @@ -3125,19 +3125,19 @@ describe("Parse.User testing", () => { .then(() => { expect(emailCalled).toBe(true); expect(emailOptions).not.toBeUndefined(); - expect(emailOptions.user.get("email")).toEqual("user@email.com"); + expect(emailOptions.user.get('email')).toEqual('user@email.com'); done(); }) .catch(err => { jfail(err); - fail("no request should fail: " + JSON.stringify(err)); + fail('no request should fail: ' + JSON.stringify(err)); done(); }); } ); - it_id("bf668670-39fa-44d3-a9a9-cad52f36d272")(it)( - "should not send email when email is not a string", + it_id('bf668670-39fa-44d3-a9a9-cad52f36d272')(it)( + 'should not send email when email is not a string', async done => { let emailCalled = false; let emailOptions; @@ -3150,31 +3150,31 @@ describe("Parse.User testing", () => { sendMail: () => Promise.resolve(), }; await reconfigureServer({ - appName: "unused", + appName: 'unused', verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); const user = new Parse.User(); - user.set("username", "asdf@jkl.com"); - user.set("password", "zxcv"); - user.set("email", "asdf@jkl.com"); + user.set('username', 'asdf@jkl.com'); + user.set('password', 'zxcv'); + user.set('email', 'asdf@jkl.com'); await user.signUp(); request({ - method: "POST", - url: "http://localhost:8378/1/requestPasswordReset", + method: 'POST', + url: 'http://localhost:8378/1/requestPasswordReset', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Session-Token": user.sessionToken, - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Session-Token': user.sessionToken, + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, body: { - email: { $regex: "^asd" }, + email: { $regex: '^asd' }, }, }) .then(res => { - fail("no request should succeed: " + JSON.stringify(res)); + fail('no request should succeed: ' + JSON.stringify(res)); done(); }) .catch(err => { @@ -3189,20 +3189,20 @@ describe("Parse.User testing", () => { } ); - it("should aftersave with full object", done => { + it('should aftersave with full object', done => { let hit = 0; - Parse.Cloud.afterSave("_User", (req, res) => { + Parse.Cloud.afterSave('_User', (req, res) => { hit++; - expect(req.object.get("username")).toEqual("User"); + expect(req.object.get('username')).toEqual('User'); res.success(); }); const user = new Parse.User(); - user.setUsername("User"); - user.setPassword("pass"); + user.setUsername('User'); + user.setPassword('pass'); user .signUp() .then(() => { - user.set("hello", "world"); + user.set('hello', 'world'); return user.save(); }) .then(() => { @@ -3211,44 +3211,44 @@ describe("Parse.User testing", () => { }); }); - it("changes to a user should update the cache", done => { - Parse.Cloud.define("testUpdatedUser", req => { - expect(req.user.get("han")).toEqual("solo"); + it('changes to a user should update the cache', done => { + Parse.Cloud.define('testUpdatedUser', req => { + expect(req.user.get('han')).toEqual('solo'); return {}; }); const user = new Parse.User(); - user.setUsername("harrison"); - user.setPassword("ford"); + user.setUsername('harrison'); + user.setPassword('ford'); user .signUp() .then(() => { - user.set("han", "solo"); + user.set('han', 'solo'); return user.save(); }) .then(() => { - return Parse.Cloud.run("testUpdatedUser"); + return Parse.Cloud.run('testUpdatedUser'); }) .then( () => { done(); }, () => { - fail("Should not have failed."); + fail('Should not have failed.'); done(); } ); }); - it("should fail to become user with expired token", done => { + it('should fail to become user with expired token', done => { let token; - Parse.User.signUp("auser", "somepass", null) + Parse.User.signUp('auser', 'somepass', null) .then(() => request({ - method: "GET", - url: "http://localhost:8378/1/classes/_Session", + method: 'GET', + url: 'http://localhost:8378/1/classes/_Session', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Master-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Master-Key': 'test', }, }) ) @@ -3258,84 +3258,84 @@ describe("Parse.User testing", () => { const expiresAt = new Date(new Date().setYear(2015)); token = body.results[0].sessionToken; return request({ - method: "PUT", - url: "http://localhost:8378/1/classes/_Session/" + id, + method: 'PUT', + url: 'http://localhost:8378/1/classes/_Session/' + id, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Master-Key": "test", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Master-Key': 'test', + 'Content-Type': 'application/json', }, body: { - expiresAt: { __type: "Date", iso: expiresAt.toISOString() }, + expiresAt: { __type: 'Date', iso: expiresAt.toISOString() }, }, }); }) .then(() => Parse.User.become(token)) .then( () => { - fail("Should not have succeded"); + fail('Should not have succeded'); done(); }, error => { expect(error.code).toEqual(209); - expect(error.message).toEqual("Session token is expired."); + expect(error.message).toEqual('Session token is expired.'); done(); } ) .catch(done.fail); }); - it("should return current session with expired expiration date", async () => { - await Parse.User.signUp("buser", "somepass", null); + it('should return current session with expired expiration date', async () => { + await Parse.User.signUp('buser', 'somepass', null); const response = await request({ - method: "GET", - url: "http://localhost:8378/1/classes/_Session", + method: 'GET', + url: 'http://localhost:8378/1/classes/_Session', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Master-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Master-Key': 'test', }, }); const body = response.data; const id = body.results[0].objectId; const expiresAt = new Date(new Date().setYear(2015)); await request({ - method: "PUT", - url: "http://localhost:8378/1/classes/_Session/" + id, + method: 'PUT', + url: 'http://localhost:8378/1/classes/_Session/' + id, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Master-Key": "test", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Master-Key': 'test', + 'Content-Type': 'application/json', }, body: { - expiresAt: { __type: "Date", iso: expiresAt.toISOString() }, + expiresAt: { __type: 'Date', iso: expiresAt.toISOString() }, }, }); const session = await Parse.Session.current(); - expect(session.get("expiresAt")).toEqual(expiresAt); + expect(session.get('expiresAt')).toEqual(expiresAt); }); - it("should not create extraneous session tokens", done => { + it('should not create extraneous session tokens', done => { const config = Config.get(Parse.applicationId); config.database .loadSchema() .then(s => { // Lock down the _User class for creation - return s.addClassIfNotExists("_User", {}, { create: {} }); + return s.addClassIfNotExists('_User', {}, { create: {} }); }) .then(() => { const user = new Parse.User(); - return user.save({ username: "user", password: "pass" }); + return user.save({ username: 'user', password: 'pass' }); }) .then( () => { - fail("should not be able to save the user"); + fail('should not be able to save the user'); }, () => { return Promise.resolve(); } ) .then(() => { - const q = new Parse.Query("_Session"); + const q = new Parse.Query('_Session'); return q.find({ useMasterKey: true }); }) .then( @@ -3345,55 +3345,55 @@ describe("Parse.User testing", () => { done(); }, () => { - fail("should not fail"); + fail('should not fail'); done(); } ); }); - it("should not overwrite username when unlinking facebook user (regression test for #1532)", async done => { + it('should not overwrite username when unlinking facebook user (regression test for #1532)', async done => { Parse.Object.disableSingleInstance(); const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); let user = new Parse.User(); - user.set("username", "testLinkWithProvider"); - user.set("password", "mypass"); + user.set('username', 'testLinkWithProvider'); + user.set('password', 'mypass'); await user.signUp(); - await user._linkWith("facebook"); - expect(user.get("username")).toEqual("testLinkWithProvider"); + await user._linkWith('facebook'); + expect(user.get('username')).toEqual('testLinkWithProvider'); expect(Parse.FacebookUtils.isLinked(user)).toBeTruthy(); - await user._unlinkFrom("facebook"); + await user._unlinkFrom('facebook'); user = await user.fetch(); - expect(user.get("username")).toEqual("testLinkWithProvider"); + expect(user.get('username')).toEqual('testLinkWithProvider'); expect(Parse.FacebookUtils.isLinked(user)).toBeFalsy(); done(); }); it('should revoke sessions when converting anonymous user to "normal" user', done => { request({ - method: "POST", - url: "http://localhost:8378/1/classes/_User", + method: 'POST', + url: 'http://localhost:8378/1/classes/_User', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, body: { authData: { - anonymous: { id: "00000000-0000-0000-0000-000000000001" }, + anonymous: { id: '00000000-0000-0000-0000-000000000001' }, }, }, }).then(response => { const body = response.data; Parse.User.become(body.sessionToken).then(user => { - const obj = new Parse.Object("TestObject"); + const obj = new Parse.Object('TestObject'); obj.setACL(new Parse.ACL(user)); return obj .save() .then(() => { // Change password, revoking session - user.set("username", "no longer anonymous"); - user.set("password", "password"); + user.set('username', 'no longer anonymous'); + user.set('password', 'password'); return user.save(); }) .then(() => { @@ -3405,40 +3405,40 @@ describe("Parse.User testing", () => { done(); }) .catch(() => { - fail("should not fail"); + fail('should not fail'); done(); }); }); }); }); - it("should not revoke session tokens if the server is configures to not revoke session tokens", done => { + it('should not revoke session tokens if the server is configures to not revoke session tokens', done => { reconfigureServer({ revokeSessionOnPasswordReset: false }).then(() => { request({ - method: "POST", - url: "http://localhost:8378/1/classes/_User", + method: 'POST', + url: 'http://localhost:8378/1/classes/_User', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, body: { authData: { - anonymous: { id: "00000000-0000-0000-0000-000000000001" }, + anonymous: { id: '00000000-0000-0000-0000-000000000001' }, }, }, }).then(response => { const body = response.data; Parse.User.become(body.sessionToken).then(user => { - const obj = new Parse.Object("TestObject"); + const obj = new Parse.Object('TestObject'); obj.setACL(new Parse.ACL(user)); return ( obj .save() .then(() => { // Change password, revoking session - user.set("username", "no longer anonymous"); - user.set("password", "password"); + user.set('username', 'no longer anonymous'); + user.set('password', 'password'); return user.save(); }) .then(() => obj.fetch()) @@ -3450,16 +3450,16 @@ describe("Parse.User testing", () => { }); }); - it("should not fail querying non existing relations", done => { + it('should not fail querying non existing relations', done => { const user = new Parse.User(); user.set({ - username: "hello", - password: "world", + username: 'hello', + password: 'world', }); user .signUp() .then(() => { - return Parse.User.current().relation("relation").query().find(); + return Parse.User.current().relation('relation').query().find(); }) .then(res => { expect(res.length).toBe(0); @@ -3471,7 +3471,7 @@ describe("Parse.User testing", () => { }); }); - it("should not allow updates to emailVerified", done => { + it('should not allow updates to emailVerified', done => { const emailAdapter = { sendVerificationEmail: () => {}, sendPasswordResetEmail: () => Promise.resolve(), @@ -3480,25 +3480,25 @@ describe("Parse.User testing", () => { const user = new Parse.User(); user.set({ - username: "hello", - password: "world", - email: "test@email.com", + username: 'hello', + password: 'world', + email: 'test@email.com', }); reconfigureServer({ - appName: "unused", + appName: 'unused', verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }) .then(() => { return user.signUp(); }) .then(() => { - return Parse.User.current().set("emailVerified", true).save(); + return Parse.User.current().set('emailVerified', true).save(); }) .then(() => { - fail("Should not be able to update emailVerified"); + fail('Should not be able to update emailVerified'); done(); }) .catch(err => { @@ -3509,7 +3509,7 @@ describe("Parse.User testing", () => { }); }); - it("should not retrieve hidden fields on GET users/me (#3432)", done => { + it('should not retrieve hidden fields on GET users/me (#3432)', done => { const emailAdapter = { sendVerificationEmail: () => {}, sendPasswordResetEmail: () => Promise.resolve(), @@ -3518,28 +3518,28 @@ describe("Parse.User testing", () => { const user = new Parse.User(); user.set({ - username: "hello", - password: "world", - email: "test@email.com", + username: 'hello', + password: 'world', + email: 'test@email.com', }); reconfigureServer({ - appName: "unused", + appName: 'unused', verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }) .then(() => { return user.signUp(); }) .then(() => request({ - method: "GET", - url: "http://localhost:8378/1/users/me", + method: 'GET', + url: 'http://localhost:8378/1/users/me', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Session-Token": Parse.User.current().getSessionToken(), - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Session-Token': Parse.User.current().getSessionToken(), + 'X-Parse-REST-API-Key': 'rest', }, }) ) @@ -3552,7 +3552,7 @@ describe("Parse.User testing", () => { .catch(done.fail); }); - it("should not retrieve hidden fields on GET users/id (#3432)", done => { + it('should not retrieve hidden fields on GET users/id (#3432)', done => { const emailAdapter = { sendVerificationEmail: () => {}, sendPasswordResetEmail: () => Promise.resolve(), @@ -3561,30 +3561,30 @@ describe("Parse.User testing", () => { const user = new Parse.User(); user.set({ - username: "hello", - password: "world", - email: "test@email.com", + username: 'hello', + password: 'world', + email: 'test@email.com', }); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); user.setACL(acl); reconfigureServer({ - appName: "unused", + appName: 'unused', verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }) .then(() => { return user.signUp(); }) .then(() => request({ - method: "GET", - url: "http://localhost:8378/1/users/" + Parse.User.current().id, + method: 'GET', + url: 'http://localhost:8378/1/users/' + Parse.User.current().id, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', }, }) ) @@ -3600,7 +3600,7 @@ describe("Parse.User testing", () => { }); }); - it("should not retrieve hidden fields on login (#3432)", done => { + it('should not retrieve hidden fields on login (#3432)', done => { const emailAdapter = { sendVerificationEmail: () => {}, sendPasswordResetEmail: () => Promise.resolve(), @@ -3609,26 +3609,26 @@ describe("Parse.User testing", () => { const user = new Parse.User(); user.set({ - username: "hello", - password: "world", - email: "test@email.com", + username: 'hello', + password: 'world', + email: 'test@email.com', }); reconfigureServer({ - appName: "unused", + appName: 'unused', verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }) .then(() => { return user.signUp(); }) .then(() => request({ - url: "http://localhost:8378/1/login?email=test@email.com&username=hello&password=world", + url: 'http://localhost:8378/1/login?email=test@email.com&username=hello&password=world', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', }, }) ) @@ -3644,7 +3644,7 @@ describe("Parse.User testing", () => { }); }); - it("should not allow updates to hidden fields", async () => { + it('should not allow updates to hidden fields', async () => { const emailAdapter = { sendVerificationEmail: () => {}, sendPasswordResetEmail: () => Promise.resolve(), @@ -3652,27 +3652,27 @@ describe("Parse.User testing", () => { }; const user = new Parse.User(); user.set({ - username: "hello", - password: "world", - email: "test@email.com", + username: 'hello', + password: 'world', + email: 'test@email.com', }); await reconfigureServer({ - appName: "unused", + appName: 'unused', verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); await user.signUp(); - user.set("_email_verify_token", "bad", { ignoreValidation: true }); + user.set('_email_verify_token', 'bad', { ignoreValidation: true }); await expectAsync(user.save()).toBeRejectedWith( new Parse.Error( Parse.Error.INVALID_KEY_NAME, - "Invalid field name: _email_verify_token." + 'Invalid field name: _email_verify_token.' ) ); }); - it("should allow updates to fields with maintenanceKey", async () => { + it('should allow updates to fields with maintenanceKey', async () => { const emailAdapter = { sendVerificationEmail: () => {}, sendPasswordResetEmail: () => Promise.resolve(), @@ -3680,13 +3680,13 @@ describe("Parse.User testing", () => { }; const user = new Parse.User(); user.set({ - username: "hello", - password: "world", - email: "test@example.com", + username: 'hello', + password: 'world', + email: 'test@example.com', }); await reconfigureServer({ - appName: "unused", - maintenanceKey: "test2", + appName: 'unused', + maintenanceKey: 'test2', verifyUserEmails: true, emailVerifyTokenValidityDuration: 5, accountLockout: { @@ -3694,67 +3694,67 @@ describe("Parse.User testing", () => { threshold: 1, }, emailAdapter: emailAdapter, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); await user.signUp(); for (let i = 0; i < 2; i++) { try { - await Parse.User.logIn(user.getEmail(), "abc"); + await Parse.User.logIn(user.getEmail(), 'abc'); } catch (e) { expect(e.code).toBe(Parse.Error.OBJECT_NOT_FOUND); expect( - e.message === "Invalid username/password." || + e.message === 'Invalid username/password.' || e.message === - "Your account is locked due to multiple failed login attempts. Please try again after 1 minute(s)" + 'Your account is locked due to multiple failed login attempts. Please try again after 1 minute(s)' ).toBeTrue(); } } await Parse.User.requestPasswordReset(user.getEmail()); const headers = { - "X-Parse-Application-Id": "test", - "X-Parse-Rest-API-Key": "rest", - "X-Parse-Maintenance-Key": "test2", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Rest-API-Key': 'rest', + 'X-Parse-Maintenance-Key': 'test2', + 'Content-Type': 'application/json', }; const userMaster = await request({ - method: "GET", + method: 'GET', url: `http://localhost:8378/1/classes/_User`, json: true, headers, }).then(res => res.data.results[0]); expect(Object.keys(userMaster).sort()).toEqual( [ - "ACL", - "_account_lockout_expires_at", - "_email_verify_token", - "_email_verify_token_expires_at", - "_failed_login_count", - "_perishable_token", - "createdAt", - "email", - "emailVerified", - "objectId", - "updatedAt", - "username", + 'ACL', + '_account_lockout_expires_at', + '_email_verify_token', + '_email_verify_token_expires_at', + '_failed_login_count', + '_perishable_token', + 'createdAt', + 'email', + 'emailVerified', + 'objectId', + 'updatedAt', + 'username', ].sort() ); const toSet = { _account_lockout_expires_at: new Date(), - _email_verify_token: "abc", + _email_verify_token: 'abc', _email_verify_token_expires_at: new Date(), _failed_login_count: 0, _perishable_token_expires_at: new Date(), - _perishable_token: "abc", + _perishable_token: 'abc', }; await request({ - method: "PUT", + method: 'PUT', headers, - url: Parse.serverURL + "/users/" + userMaster.objectId, + url: Parse.serverURL + '/users/' + userMaster.objectId, json: true, body: toSet, }).then(res => res.data); const update = await request({ - method: "GET", + method: 'GET', url: `http://localhost:8378/1/classes/_User`, json: true, headers, @@ -3771,17 +3771,17 @@ describe("Parse.User testing", () => { } }); - it("should revoke sessions when setting paswword with masterKey (#3289)", done => { + it('should revoke sessions when setting paswword with masterKey (#3289)', done => { let user; - Parse.User.signUp("username", "password") + Parse.User.signUp('username', 'password') .then(newUser => { user = newUser; - user.set("password", "newPassword"); + user.set('password', 'newPassword'); return user.save(null, { useMasterKey: true }); }) .then(() => { - const query = new Parse.Query("_Session"); - query.equalTo("user", user); + const query = new Parse.Query('_Session'); + query.equalTo('user', user); return query.find({ useMasterKey: true }); }) .then(results => { @@ -3790,9 +3790,9 @@ describe("Parse.User testing", () => { }, done.fail); }); - xit("should not send a verification email if the user signed up using oauth", done => { + xit('should not send a verification email if the user signed up using oauth', done => { pending( - "this test fails. See: https://github.com/parse-community/parse-server/issues/5097" + 'this test fails. See: https://github.com/parse-community/parse-server/issues/5097' ); let emailCalledCount = 0; const emailAdapter = { @@ -3804,19 +3804,19 @@ describe("Parse.User testing", () => { sendMail: () => Promise.resolve(), }; reconfigureServer({ - appName: "unused", + appName: 'unused', verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); const user = new Parse.User(); - user.set("email", "email1@host.com"); + user.set('email', 'email1@host.com'); Parse.FacebookUtils.link(user, { - id: "8675309", - access_token: "jenny", + id: '8675309', + access_token: 'jenny', expiration_date: new Date().toJSON(), }).then(user => { - user.set("email", "email2@host.com"); + user.set('email', 'email2@host.com'); user.save().then(() => { expect(emailCalledCount).toBe(0); done(); @@ -3824,7 +3824,7 @@ describe("Parse.User testing", () => { }); }); - it("should be able to update user with authData passed", done => { + it('should be able to update user with authData passed', done => { let objectId; let sessionToken; @@ -3832,24 +3832,24 @@ describe("Parse.User testing", () => { return request({ url: `http://localhost:8378/1/classes/_User/${objectId}`, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", - "X-Parse-Session-Token": sessionToken, + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Session-Token': sessionToken, }, }).then(response => block(response.data)); } request({ - method: "POST", - url: "http://localhost:8378/1/classes/_User", + method: 'POST', + url: 'http://localhost:8378/1/classes/_User', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, body: { - key: "value", - authData: { anonymous: { id: "00000000-0000-0000-0000-000000000001" } }, + key: 'value', + authData: { anonymous: { id: '00000000-0000-0000-0000-000000000001' } }, }, }) .then(response => { @@ -3860,24 +3860,24 @@ describe("Parse.User testing", () => { expect(objectId).toBeDefined(); return validate(user => { // validate that keys are set on creation - expect(user.key).toBe("value"); + expect(user.key).toBe('value'); }); }) .then(() => { // update the user const options = { - method: "PUT", + method: 'PUT', url: `http://localhost:8378/1/classes/_User/${objectId}`, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", - "X-Parse-Session-Token": sessionToken, - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Session-Token': sessionToken, + 'Content-Type': 'application/json', }, body: { - key: "otherValue", + key: 'otherValue', authData: { - anonymous: { id: "00000000-0000-0000-0000-000000000001" }, + anonymous: { id: '00000000-0000-0000-0000-000000000001' }, }, }, }; @@ -3886,7 +3886,7 @@ describe("Parse.User testing", () => { .then(() => { return validate(user => { // validate that keys are set on update - expect(user.key).toBe("otherValue"); + expect(user.key).toBe('otherValue'); }); }) .then(() => { @@ -3896,22 +3896,22 @@ describe("Parse.User testing", () => { .catch(done.fail); }); - it("can login with email", done => { + it('can login with email', done => { const user = new Parse.User(); user .save({ - username: "yolo", - password: "yolopass", - email: "yo@lo.com", + username: 'yolo', + password: 'yolopass', + email: 'yo@lo.com', }) .then(() => { const options = { url: `http://localhost:8378/1/login`, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', }, - qs: { email: "yo@lo.com", password: "yolopass" }, + qs: { email: 'yo@lo.com', password: 'yolopass' }, }; return request(options); }) @@ -3919,24 +3919,24 @@ describe("Parse.User testing", () => { .catch(done.fail); }); - it("cannot login with email and invalid password", done => { + it('cannot login with email and invalid password', done => { const user = new Parse.User(); user .save({ - username: "yolo", - password: "yolopass", - email: "yo@lo.com", + username: 'yolo', + password: 'yolopass', + email: 'yo@lo.com', }) .then(() => { const options = { - method: "POST", + method: 'POST', url: `http://localhost:8378/1/login`, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, - body: { email: "yo@lo.com", password: "yolopass2" }, + body: { email: 'yo@lo.com', password: 'yolopass2' }, }; return request(options); }) @@ -3944,20 +3944,20 @@ describe("Parse.User testing", () => { .catch(() => done()); }); - it("can login with email through query string", done => { + it('can login with email through query string', done => { const user = new Parse.User(); user .save({ - username: "yolo", - password: "yolopass", - email: "yo@lo.com", + username: 'yolo', + password: 'yolopass', + email: 'yo@lo.com', }) .then(() => { const options = { url: `http://localhost:8378/1/login?email=yo@lo.com&password=yolopass`, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', }, }; return request(options); @@ -3966,20 +3966,20 @@ describe("Parse.User testing", () => { .catch(done.fail); }); - it("can login when both email and username are passed", done => { + it('can login when both email and username are passed', done => { const user = new Parse.User(); user .save({ - username: "yolo", - password: "yolopass", - email: "yo@lo.com", + username: 'yolo', + password: 'yolopass', + email: 'yo@lo.com', }) .then(() => { const options = { url: `http://localhost:8378/1/login?email=yo@lo.com&username=yolo&password=yolopass`, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', }, }; return request(options); @@ -3992,23 +3992,23 @@ describe("Parse.User testing", () => { const user = new Parse.User(); user .save({ - username: "yolo", - password: "yolopass", - email: "yo@lo.com", + username: 'yolo', + password: 'yolopass', + email: 'yo@lo.com', }) .then(() => { const options = { url: `http://localhost:8378/1/login?email=yo@lo.com&username=yolo2&password=yolopass`, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', }, }; return request(options); }) .then(done.fail) .catch(err => { - expect(err.data.error).toEqual("Invalid username/password."); + expect(err.data.error).toEqual('Invalid username/password.'); done(); }); }); @@ -4017,150 +4017,150 @@ describe("Parse.User testing", () => { const user = new Parse.User(); user .save({ - username: "yolo", - password: "yolopass", - email: "yo@lo.com", + username: 'yolo', + password: 'yolopass', + email: 'yo@lo.com', }) .then(() => { const options = { url: `http://localhost:8378/1/login?email=yo@lo2.com&username=yolo&password=yolopass`, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', }, }; return request(options); }) .then(done.fail) .catch(err => { - expect(err.data.error).toEqual("Invalid username/password."); + expect(err.data.error).toEqual('Invalid username/password.'); done(); }); }); - it("fails to login when email and username are not provided", done => { + it('fails to login when email and username are not provided', done => { const user = new Parse.User(); user .save({ - username: "yolo", - password: "yolopass", - email: "yo@lo.com", + username: 'yolo', + password: 'yolopass', + email: 'yo@lo.com', }) .then(() => { const options = { url: `http://localhost:8378/1/login?password=yolopass`, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', }, }; return request(options); }) .then(done.fail) .catch(err => { - expect(err.data.error).toEqual("username/email is required."); + expect(err.data.error).toEqual('username/email is required.'); done(); }); }); - it("allows login when providing email as username", done => { + it('allows login when providing email as username', done => { const user = new Parse.User(); user .save({ - username: "yolo", - password: "yolopass", - email: "yo@lo.com", + username: 'yolo', + password: 'yolopass', + email: 'yo@lo.com', }) .then(() => { - return Parse.User.logIn("yo@lo.com", "yolopass"); + return Parse.User.logIn('yo@lo.com', 'yolopass'); }) .then(user => { - expect(user.get("username")).toBe("yolo"); + expect(user.get('username')).toBe('yolo'); }) .then(done) .catch(done.fail); }); - it("handles properly when 2 users share username / email pairs", done => { + it('handles properly when 2 users share username / email pairs', done => { const user = new Parse.User({ - username: "yo@loname.com", - password: "yolopass", - email: "yo@lo.com", + username: 'yo@loname.com', + password: 'yolopass', + email: 'yo@lo.com', }); const user2 = new Parse.User({ - username: "yo@lo.com", - email: "yo@loname.com", - password: "yolopass2", // different passwords + username: 'yo@lo.com', + email: 'yo@loname.com', + password: 'yolopass2', // different passwords }); Parse.Object.saveAll([user, user2]) .then(() => { - return Parse.User.logIn("yo@loname.com", "yolopass"); + return Parse.User.logIn('yo@loname.com', 'yolopass'); }) .then(user => { // the username takes precedence over the email, // so we get the user with username as passed in - expect(user.get("username")).toBe("yo@loname.com"); + expect(user.get('username')).toBe('yo@loname.com'); }) .then(done) .catch(done.fail); }); - it("handles properly when 2 users share username / email pairs, counterpart", done => { + it('handles properly when 2 users share username / email pairs, counterpart', done => { const user = new Parse.User({ - username: "yo@loname.com", - password: "yolopass", - email: "yo@lo.com", + username: 'yo@loname.com', + password: 'yolopass', + email: 'yo@lo.com', }); const user2 = new Parse.User({ - username: "yo@lo.com", - email: "yo@loname.com", - password: "yolopass2", // different passwords + username: 'yo@lo.com', + email: 'yo@loname.com', + password: 'yolopass2', // different passwords }); Parse.Object.saveAll([user, user2]) .then(() => { - return Parse.User.logIn("yo@loname.com", "yolopass2"); + return Parse.User.logIn('yo@loname.com', 'yolopass2'); }) .then(done.fail) .catch(err => { - expect(err.message).toEqual("Invalid username/password."); + expect(err.message).toEqual('Invalid username/password.'); done(); }); }); - it("fails to login when password is not provided", done => { + it('fails to login when password is not provided', done => { const user = new Parse.User(); user .save({ - username: "yolo", - password: "yolopass", - email: "yo@lo.com", + username: 'yolo', + password: 'yolopass', + email: 'yo@lo.com', }) .then(() => { const options = { url: `http://localhost:8378/1/login?username=yolo`, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', }, }; return request(options); }) .then(done.fail) .catch(err => { - expect(err.data.error).toEqual("password is required."); + expect(err.data.error).toEqual('password is required.'); done(); }); }); - it("does not duplicate session when logging in multiple times #3451", done => { + it('does not duplicate session when logging in multiple times #3451', done => { const user = new Parse.User(); user .signUp({ - username: "yolo", - password: "yolo", - email: "yo@lo.com", + username: 'yolo', + password: 'yolo', + email: 'yo@lo.com', }) .then(() => { const token = user.getSessionToken(); @@ -4168,7 +4168,7 @@ describe("Parse.User testing", () => { let count = 0; while (count < 5) { promise = promise.then(() => { - return Parse.User.logIn("yolo", "yolo").then(res => { + return Parse.User.logIn('yolo', 'yolo').then(res => { // ensure a new session token is generated at each login expect(res.getSessionToken()).not.toBe(token); }); @@ -4184,7 +4184,7 @@ describe("Parse.User testing", () => { }); }) .then(() => { - const query = new Parse.Query("_Session"); + const query = new Parse.Query('_Session'); return query.find({ useMasterKey: true }); }) .then(results => { @@ -4194,38 +4194,38 @@ describe("Parse.User testing", () => { .then(done, done.fail); }); - it("should throw OBJECT_NOT_FOUND instead of SESSION_MISSING when using masterKey", async () => { + it('should throw OBJECT_NOT_FOUND instead of SESSION_MISSING when using masterKey', async () => { await reconfigureServer(); // create a fake user (just so we simulate an object not found) - const non_existent_user = Parse.User.createWithoutData("fake_id"); + const non_existent_user = Parse.User.createWithoutData('fake_id'); try { await non_existent_user.destroy({ useMasterKey: true }); - throw ""; + throw ''; } catch (e) { expect(e.code).toBe(Parse.Error.OBJECT_NOT_FOUND); } try { await non_existent_user.save({}, { useMasterKey: true }); - throw ""; + throw ''; } catch (e) { expect(e.code).toBe(Parse.Error.OBJECT_NOT_FOUND); } try { await non_existent_user.save(); - throw ""; + throw ''; } catch (e) { expect(e.code).toBe(Parse.Error.SESSION_MISSING); } try { await non_existent_user.destroy(); - throw ""; + throw ''; } catch (e) { expect(e.code).toBe(Parse.Error.SESSION_MISSING); } }); - it("should throw when enforcePrivateUsers is invalid", async () => { - const options = [[], "a", 0, {}]; + it('should throw when enforcePrivateUsers is invalid', async () => { + const options = [[], 'a', 0, {}]; for (const option of options) { await expectAsync( reconfigureServer({ enforcePrivateUsers: option }) @@ -4233,11 +4233,11 @@ describe("Parse.User testing", () => { } }); - it("user login with enforcePrivateUsers", async done => { + it('user login with enforcePrivateUsers', async done => { await reconfigureServer({ enforcePrivateUsers: true }); - await Parse.User.signUp("asdf", "zxcv"); - const user = await Parse.User.logIn("asdf", "zxcv"); - equal(user.get("username"), "asdf"); + await Parse.User.signUp('asdf', 'zxcv'); + const user = await Parse.User.logIn('asdf', 'zxcv'); + equal(user.get('username'), 'asdf'); const ACL = user.getACL(); expect(ACL.getReadAccess(user)).toBe(true); expect(ACL.getWriteAccess(user)).toBe(true); @@ -4247,38 +4247,38 @@ describe("Parse.User testing", () => { expect(Object.keys(perms).length).toBe(1); expect(perms[user.id].read).toBe(true); expect(perms[user.id].write).toBe(true); - expect(perms["*"]).toBeUndefined(); + expect(perms['*']).toBeUndefined(); done(); }); - describe("issue #4897", () => { - it_only_db("mongo")( - "should be able to login with a legacy user (no ACL)", + describe('issue #4897', () => { + it_only_db('mongo')( + 'should be able to login with a legacy user (no ACL)', async () => { // This issue is a side effect of the locked users and legacy users which don't have ACL's // In this scenario, a legacy user wasn't be able to login as there's no ACL on it await reconfigureServer(); const database = Config.get(Parse.applicationId).database; - const collection = await database.adapter._adaptiveCollection("_User"); + const collection = await database.adapter._adaptiveCollection('_User'); await collection.insertOne({ - _id: "ABCDEF1234", - name: "", - email: "", - username: "", - _hashed_password: "", + _id: 'ABCDEF1234', + name: '', + email: '', + username: '', + _hashed_password: '', _auth_data_facebook: { - id: "8675309", - access_token: "jenny", + id: '8675309', + access_token: 'jenny', }, - sessionToken: "", + sessionToken: '', }); const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); - const model = await Parse.User._logInWith("facebook", {}); - expect(model.id).toBe("ABCDEF1234"); - ok(model instanceof Parse.User, "Model should be a Parse.User"); + const model = await Parse.User._logInWith('facebook', {}); + expect(model.id).toBe('ABCDEF1234'); + ok(model instanceof Parse.User, 'Model should be a Parse.User'); strictEqual(Parse.User.current(), model); - ok(model.extended(), "Should have used subclass."); + ok(model.extended(), 'Should have used subclass.'); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual( provider.authData.access_token, @@ -4288,17 +4288,17 @@ describe("Parse.User testing", () => { provider.authData.expiration_date, provider.synchronizedExpiration ); - ok(model._isLinked("facebook"), "User should be linked to facebook"); + ok(model._isLinked('facebook'), 'User should be linked to facebook'); } ); }); - it("should strip out authdata in LiveQuery", async () => { + it('should strip out authdata in LiveQuery', async () => { const provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); await reconfigureServer({ - liveQuery: { classNames: ["_User"] }, + liveQuery: { classNames: ['_User'] }, startLiveQueryServer: true, verbose: false, silent: true, @@ -4311,16 +4311,16 @@ describe("Parse.User testing", () => { }); const query = new Parse.Query(Parse.User); - query.doesNotExist("foo"); + query.doesNotExist('foo'); const subscription = await query.subscribe(); - const events = ["create", "update", "enter", "leave", "delete"]; + const events = ['create', 'update', 'enter', 'leave', 'delete']; const response = (obj, prev) => { - expect(obj.get("authData")).toBeUndefined(); + expect(obj.get('authData')).toBeUndefined(); expect(obj.authData).toBeUndefined(); expect(prev && prev.authData).toBeUndefined(); if (prev && prev.get) { - expect(prev.get("authData")).toBeUndefined(); + expect(prev.get('authData')).toBeUndefined(); } }; const calls = {}; @@ -4329,12 +4329,12 @@ describe("Parse.User testing", () => { spyOn(calls, key).and.callThrough(); subscription.on(key, calls[key]); } - const user = await Parse.User._logInWith("facebook"); - user.set("foo", "bar"); + const user = await Parse.User._logInWith('facebook'); + user.set('foo', 'bar'); await user.save(); - user.unset("foo"); + user.unset('foo'); await user.save(); - user.set("yolo", "bar"); + user.set('yolo', 'bar'); await user.save(); await user.destroy(); await new Promise(resolve => setTimeout(resolve, 10)); @@ -4349,27 +4349,27 @@ describe("Parse.User testing", () => { }); }); -describe("Security Advisory GHSA-8w3j-g983-8jh5", function () { - it_only_db("mongo")( - "should validate credentials first and check if account already linked afterwards ()", +describe('Security Advisory GHSA-8w3j-g983-8jh5', function () { + it_only_db('mongo')( + 'should validate credentials first and check if account already linked afterwards ()', async done => { await reconfigureServer(); // Add User to Database with authData const database = Config.get(Parse.applicationId).database; - const collection = await database.adapter._adaptiveCollection("_User"); + const collection = await database.adapter._adaptiveCollection('_User'); await collection.insertOne({ - _id: "ABCDEF1234", - name: "", - email: "", - username: "", - _hashed_password: "", + _id: 'ABCDEF1234', + name: '', + email: '', + username: '', + _hashed_password: '', _auth_data_custom: { - id: "linkedID", // Already linked userid + id: 'linkedID', // Already linked userid }, - sessionToken: "", + sessionToken: '', }); const provider = { - getAuthType: () => "custom", + getAuthType: () => 'custom', restoreAuthentication: () => true, }; // AuthProvider checks if password is 'password' Parse.User._registerAuthenticationProvider(provider); @@ -4378,7 +4378,7 @@ describe("Security Advisory GHSA-8w3j-g983-8jh5", function () { try { const user = await Parse.AnonymousUtils.logIn(); await user._linkWith(provider.getAuthType(), { - authData: { id: "linkedID", password: "wrong" }, + authData: { id: 'linkedID', password: 'wrong' }, }); } catch (error) { // This should throw Parse.Error.SESSION_MISSING and not Parse.Error.ACCOUNT_ALREADY_LINKED @@ -4390,48 +4390,48 @@ describe("Security Advisory GHSA-8w3j-g983-8jh5", function () { done(); } ); - it_only_db("mongo")("should ignore authData field", async () => { + it_only_db('mongo')('should ignore authData field', async () => { // Add User to Database with authData await reconfigureServer(); const database = Config.get(Parse.applicationId).database; - const collection = await database.adapter._adaptiveCollection("_User"); + const collection = await database.adapter._adaptiveCollection('_User'); await collection.insertOne({ - _id: "1234ABCDEF", - name: "", - email: "", - username: "", - _hashed_password: "", + _id: '1234ABCDEF', + name: '', + email: '', + username: '', + _hashed_password: '', _auth_data_custom: { - id: "linkedID", + id: 'linkedID', }, - sessionToken: "", + sessionToken: '', authData: null, // should ignore }); const provider = { - getAuthType: () => "custom", + getAuthType: () => 'custom', restoreAuthentication: () => true, }; Parse.User._registerAuthenticationProvider(provider); const query = new Parse.Query(Parse.User); - const user = await query.get("1234ABCDEF", { useMasterKey: true }); - expect(user.get("authData")).toEqual({ custom: { id: "linkedID" } }); + const user = await query.get('1234ABCDEF', { useMasterKey: true }); + expect(user.get('authData')).toEqual({ custom: { id: 'linkedID' } }); }); }); -describe("login as other user", () => { - it("allows creating a session for another user with the master key", async done => { - await Parse.User.signUp("some_user", "some_password"); +describe('login as other user', () => { + it('allows creating a session for another user with the master key', async done => { + await Parse.User.signUp('some_user', 'some_password'); const userId = Parse.User.current().id; await Parse.User.logOut(); try { const response = await request({ - method: "POST", - url: "http://localhost:8378/1/loginAs", + method: 'POST', + url: 'http://localhost:8378/1/loginAs', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", - "X-Parse-Master-Key": "test", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Master-Key': 'test', }, body: { userId, @@ -4453,26 +4453,26 @@ describe("login as other user", () => { done(); }); - it("rejects creating a session for another user if the user does not exist", async done => { + it('rejects creating a session for another user if the user does not exist', async done => { try { await request({ - method: "POST", - url: "http://localhost:8378/1/loginAs", + method: 'POST', + url: 'http://localhost:8378/1/loginAs', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", - "X-Parse-Master-Key": "test", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Master-Key': 'test', }, body: { - userId: "bogus-user", + userId: 'bogus-user', }, }); - fail("Request should fail without a valid user ID"); + fail('Request should fail without a valid user ID'); done(); } catch (err) { expect(err.data.code).toBe(Parse.Error.OBJECT_NOT_FOUND); - expect(err.data.error).toBe("user not found"); + expect(err.data.error).toBe('user not found'); } const sessionsQuery = new Parse.Query(Parse.Session); @@ -4484,30 +4484,30 @@ describe("login as other user", () => { done(); }); - it("rejects creating a session for another user with invalid parameters", async done => { - const invalidUserIds = [undefined, null, ""]; + it('rejects creating a session for another user with invalid parameters', async done => { + const invalidUserIds = [undefined, null, '']; for (const invalidUserId of invalidUserIds) { try { await request({ - method: "POST", - url: "http://localhost:8378/1/loginAs", + method: 'POST', + url: 'http://localhost:8378/1/loginAs', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", - "X-Parse-Master-Key": "test", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Master-Key': 'test', }, body: { userId: invalidUserId, }, }); - fail("Request should fail without a valid user ID"); + fail('Request should fail without a valid user ID'); done(); } catch (err) { expect(err.data.code).toBe(Parse.Error.INVALID_VALUE); expect(err.data.error).toBe( - "userId must not be empty, null, or undefined" + 'userId must not be empty, null, or undefined' ); } @@ -4521,29 +4521,29 @@ describe("login as other user", () => { done(); }); - it("rejects creating a session for another user without the master key", async done => { - await Parse.User.signUp("some_user", "some_password"); + it('rejects creating a session for another user without the master key', async done => { + await Parse.User.signUp('some_user', 'some_password'); const userId = Parse.User.current().id; await Parse.User.logOut(); try { await request({ - method: "POST", - url: "http://localhost:8378/1/loginAs", + method: 'POST', + url: 'http://localhost:8378/1/loginAs', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', }, body: { userId, }, }); - fail("Request should fail without the master key"); + fail('Request should fail without the master key'); done(); } catch (err) { expect(err.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN); - expect(err.data.error).toBe("master key is required"); + expect(err.data.error).toBe('master key is required'); } const sessionsQuery = new Parse.Query(Parse.Session); @@ -4556,9 +4556,9 @@ describe("login as other user", () => { }); }); -describe("allowClientClassCreation option", () => { - it("should enforce boolean values", async () => { - const options = [[], "a", "", 0, 1, {}, "true", "false"]; +describe('allowClientClassCreation option', () => { + it('should enforce boolean values', async () => { + const options = [[], 'a', '', 0, 1, {}, 'true', 'false']; for (const option of options) { await expectAsync( reconfigureServer({ allowClientClassCreation: option }) @@ -4566,19 +4566,19 @@ describe("allowClientClassCreation option", () => { } }); - it("should accept true value", async () => { + it('should accept true value', async () => { await reconfigureServer({ allowClientClassCreation: true }); expect(Config.get(Parse.applicationId).allowClientClassCreation).toBe(true); }); - it("should accept false value", async () => { + it('should accept false value', async () => { await reconfigureServer({ allowClientClassCreation: false }); expect(Config.get(Parse.applicationId).allowClientClassCreation).toBe( false ); }); - it("should default false", async () => { + it('should default false', async () => { // remove predefined allowClientClassCreation:true on global defaultConfiguration delete defaultConfiguration.allowClientClassCreation; await reconfigureServer(defaultConfiguration); diff --git a/spec/ParseWebSocket.spec.js b/spec/ParseWebSocket.spec.js index 915591c183..b8b25b7b97 100644 --- a/spec/ParseWebSocket.spec.js +++ b/spec/ParseWebSocket.spec.js @@ -1,43 +1,43 @@ const ParseWebSocket = - require("../lib/LiveQuery/ParseWebSocketServer").ParseWebSocket; + require('../lib/LiveQuery/ParseWebSocketServer').ParseWebSocket; -describe("ParseWebSocket", function () { - it("can be initialized", function () { +describe('ParseWebSocket', function () { + it('can be initialized', function () { const ws = {}; const parseWebSocket = new ParseWebSocket(ws); expect(parseWebSocket.ws).toBe(ws); }); - it("can handle disconnect event", function (done) { + it('can handle disconnect event', function (done) { const ws = { onclose: () => {}, }; const parseWebSocket = new ParseWebSocket(ws); - parseWebSocket.on("disconnect", () => { + parseWebSocket.on('disconnect', () => { done(); }); ws.onclose(); }); - it("can handle message event", function (done) { + it('can handle message event', function (done) { const ws = { onmessage: () => {}, }; const parseWebSocket = new ParseWebSocket(ws); - parseWebSocket.on("message", () => { + parseWebSocket.on('message', () => { done(); }); ws.onmessage(); }); - it("can send a message", function () { + it('can send a message', function () { const ws = { - send: jasmine.createSpy("send"), + send: jasmine.createSpy('send'), }; const parseWebSocket = new ParseWebSocket(ws); - parseWebSocket.send("message"); + parseWebSocket.send('message'); - expect(parseWebSocket.ws.send).toHaveBeenCalledWith("message"); + expect(parseWebSocket.ws.send).toHaveBeenCalledWith('message'); }); }); diff --git a/spec/ParseWebSocketServer.spec.js b/spec/ParseWebSocketServer.spec.js index 13ae4c5b62..89ed1ccb53 100644 --- a/spec/ParseWebSocketServer.spec.js +++ b/spec/ParseWebSocketServer.spec.js @@ -1,22 +1,22 @@ const { ParseWebSocketServer, -} = require("../lib/LiveQuery/ParseWebSocketServer"); -const EventEmitter = require("events"); +} = require('../lib/LiveQuery/ParseWebSocketServer'); +const EventEmitter = require('events'); -describe("ParseWebSocketServer", function () { +describe('ParseWebSocketServer', function () { beforeEach(function (done) { // Mock ws server const mockServer = function () { return new EventEmitter(); }; - jasmine.mockLibrary("ws", "Server", mockServer); + jasmine.mockLibrary('ws', 'Server', mockServer); done(); }); - it("can handle connect event when ws is open", function (done) { - const onConnectCallback = jasmine.createSpy("onConnectCallback"); - const http = require("http"); + it('can handle connect event when ws is open', function (done) { + const onConnectCallback = jasmine.createSpy('onConnectCallback'); + const http = require('http'); const server = http.createServer(); const parseWebSocketServer = new ParseWebSocketServer( server, @@ -28,7 +28,7 @@ describe("ParseWebSocketServer", function () { const ws = new EventEmitter(); ws.readyState = 0; ws.OPEN = 0; - ws.ping = jasmine.createSpy("ping"); + ws.ping = jasmine.createSpy('ping'); ws.terminate = () => {}; parseWebSocketServer.onConnection(ws); @@ -43,9 +43,9 @@ describe("ParseWebSocketServer", function () { }, 10); }); - it("can handle error event", async () => { - jasmine.restoreLibrary("ws", "Server"); - const WebSocketServer = require("ws").Server; + it('can handle error event', async () => { + jasmine.restoreLibrary('ws', 'Server'); + const WebSocketServer = require('ws').Server; let wssError; class WSSAdapter { constructor(options) { @@ -56,9 +56,9 @@ describe("ParseWebSocketServer", function () { onError() {} start() { const wss = new WebSocketServer({ server: this.options.server }); - wss.on("listening", this.onListen); - wss.on("connection", this.onConnection); - wss.on("error", error => { + wss.on('listening', this.onListen); + wss.on('connection', this.onConnection); + wss.on('error', error => { wssError = error; this.onError(error); }); @@ -68,7 +68,7 @@ describe("ParseWebSocketServer", function () { const server = await reconfigureServer({ liveQuery: { - classNames: ["TestObject"], + classNames: ['TestObject'], }, liveQueryServerOptions: { wssAdapter: WSSAdapter, @@ -78,13 +78,13 @@ describe("ParseWebSocketServer", function () { silent: true, }); const wssAdapter = server.liveQueryServer.parseWebSocketServer.server; - wssAdapter.wss.emit("error", "Invalid Packet"); - expect(wssError).toBe("Invalid Packet"); + wssAdapter.wss.emit('error', 'Invalid Packet'); + expect(wssError).toBe('Invalid Packet'); }); - it("can handle ping/pong", async () => { - const onConnectCallback = jasmine.createSpy("onConnectCallback"); - const http = require("http"); + it('can handle ping/pong', async () => { + const onConnectCallback = jasmine.createSpy('onConnectCallback'); + const http = require('http'); const server = http.createServer(); const parseWebSocketServer = new ParseWebSocketServer( server, @@ -97,8 +97,8 @@ describe("ParseWebSocketServer", function () { const ws = new EventEmitter(); ws.readyState = 0; ws.OPEN = 0; - ws.ping = jasmine.createSpy("ping"); - ws.terminate = jasmine.createSpy("terminate"); + ws.ping = jasmine.createSpy('ping'); + ws.terminate = jasmine.createSpy('terminate'); parseWebSocketServer.onConnection(ws); @@ -107,7 +107,7 @@ describe("ParseWebSocketServer", function () { await new Promise(resolve => setTimeout(resolve, 10)); expect(ws.ping).toHaveBeenCalled(); expect(ws.waitingForPong).toBe(true); - ws.emit("pong"); + ws.emit('pong'); expect(ws.waitingForPong).toBe(false); await new Promise(resolve => setTimeout(resolve, 10)); expect(ws.waitingForPong).toBe(true); @@ -115,9 +115,9 @@ describe("ParseWebSocketServer", function () { server.close(); }); - it("closes interrupted connection", async () => { - const onConnectCallback = jasmine.createSpy("onConnectCallback"); - const http = require("http"); + it('closes interrupted connection', async () => { + const onConnectCallback = jasmine.createSpy('onConnectCallback'); + const http = require('http'); const server = http.createServer(); const parseWebSocketServer = new ParseWebSocketServer( server, @@ -129,8 +129,8 @@ describe("ParseWebSocketServer", function () { const ws = new EventEmitter(); ws.readyState = 0; ws.OPEN = 0; - ws.ping = jasmine.createSpy("ping"); - ws.terminate = jasmine.createSpy("terminate"); + ws.ping = jasmine.createSpy('ping'); + ws.terminate = jasmine.createSpy('terminate'); parseWebSocketServer.onConnection(ws); @@ -146,6 +146,6 @@ describe("ParseWebSocketServer", function () { }); afterEach(function () { - jasmine.restoreLibrary("ws", "Server"); + jasmine.restoreLibrary('ws', 'Server'); }); }); diff --git a/spec/PasswordPolicy.spec.js b/spec/PasswordPolicy.spec.js index b420fc5911..1c15b8191a 100644 --- a/spec/PasswordPolicy.spec.js +++ b/spec/PasswordPolicy.spec.js @@ -1,10 +1,10 @@ -"use strict"; +'use strict'; -const request = require("../lib/request"); +const request = require('../lib/request'); -describe("Password Policy: ", () => { - it_id("b400a867-9f05-496f-af79-933aa588dde5")(it)( - "should show the invalid link page if the user clicks on the password reset link after the token expires", +describe('Password Policy: ', () => { + it_id('b400a867-9f05-496f-af79-933aa588dde5')(it)( + 'should show the invalid link page if the user clicks on the password reset link after the token expires', done => { const user = new Parse.User(); let sendEmailOptions; @@ -16,23 +16,23 @@ describe("Password Policy: ", () => { sendMail: () => {}, }; reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', emailAdapter: emailAdapter, passwordPolicy: { resetTokenValidityDuration: 0.5, // 0.5 second }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }) .then(() => { - user.setUsername("testResetTokenValidity"); - user.setPassword("original"); - user.set("email", "user@parse.com"); + user.setUsername('testResetTokenValidity'); + user.setPassword('original'); + user.set('email', 'user@parse.com'); return user.signUp(); }) .then(() => { - Parse.User.requestPasswordReset("user@parse.com").catch(err => { + Parse.User.requestPasswordReset('user@parse.com').catch(err => { jfail(err); - fail("Reset password request should not fail"); + fail('Reset password request should not fail'); done(); }); }) @@ -50,7 +50,7 @@ describe("Password Policy: ", () => { .then(response => { expect(response.status).toEqual(302); expect(response.text).toEqual( - "Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html" + 'Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html' ); done(); }) @@ -66,7 +66,7 @@ describe("Password Policy: ", () => { } ); - it("should show the reset password page if the user clicks on the password reset link before the token expires", done => { + it('should show the reset password page if the user clicks on the password reset link before the token expires', done => { const user = new Parse.User(); let sendEmailOptions; const emailAdapter = { @@ -77,23 +77,23 @@ describe("Password Policy: ", () => { sendMail: () => {}, }; reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', emailAdapter: emailAdapter, passwordPolicy: { resetTokenValidityDuration: 5, // 5 seconds }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }) .then(() => { - user.setUsername("testResetTokenValidity"); - user.setPassword("original"); - user.set("email", "user@parse.com"); + user.setUsername('testResetTokenValidity'); + user.setPassword('original'); + user.set('email', 'user@parse.com'); return user.signUp(); }) .then(() => { - Parse.User.requestPasswordReset("user@parse.com").catch(err => { + Parse.User.requestPasswordReset('user@parse.com').catch(err => { jfail(err); - fail("Reset password request should not fail"); + fail('Reset password request should not fail'); done(); }); }) @@ -126,7 +126,7 @@ describe("Password Policy: ", () => { }); }); - it("should not keep reset token by default", async done => { + it('should not keep reset token by default', async done => { const sendEmailOptions = []; const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), @@ -136,26 +136,26 @@ describe("Password Policy: ", () => { sendMail: () => {}, }; await reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', emailAdapter: emailAdapter, passwordPolicy: { resetTokenValidityDuration: 5 * 60, // 5 minutes }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); const user = new Parse.User(); - user.setUsername("testResetTokenValidity"); - user.setPassword("original"); - user.set("email", "user@example.com"); + user.setUsername('testResetTokenValidity'); + user.setPassword('original'); + user.set('email', 'user@example.com'); await user.signUp(); - await Parse.User.requestPasswordReset("user@example.com"); - await Parse.User.requestPasswordReset("user@example.com"); + await Parse.User.requestPasswordReset('user@example.com'); + await Parse.User.requestPasswordReset('user@example.com'); expect(sendEmailOptions[0].link).not.toBe(sendEmailOptions[1].link); done(); }); - it_id("7d98e1f2-ae89-4038-9ea7-5254854ea42e")(it)( - "should keep reset token with resetTokenReuseIfValid", + it_id('7d98e1f2-ae89-4038-9ea7-5254854ea42e')(it)( + 'should keep reset token with resetTokenReuseIfValid', async done => { const sendEmailOptions = []; const emailAdapter = { @@ -166,27 +166,27 @@ describe("Password Policy: ", () => { sendMail: () => {}, }; await reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', emailAdapter: emailAdapter, passwordPolicy: { resetTokenValidityDuration: 5 * 60, // 5 minutes resetTokenReuseIfValid: true, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); const user = new Parse.User(); - user.setUsername("testResetTokenValidity"); - user.setPassword("original"); - user.set("email", "user@example.com"); + user.setUsername('testResetTokenValidity'); + user.setPassword('original'); + user.set('email', 'user@example.com'); await user.signUp(); - await Parse.User.requestPasswordReset("user@example.com"); - await Parse.User.requestPasswordReset("user@example.com"); + await Parse.User.requestPasswordReset('user@example.com'); + await Parse.User.requestPasswordReset('user@example.com'); expect(sendEmailOptions[0].link).toBe(sendEmailOptions[1].link); done(); } ); - it("should throw with invalid resetTokenReuseIfValid", async done => { + it('should throw with invalid resetTokenReuseIfValid', async done => { const sendEmailOptions = []; const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), @@ -197,43 +197,43 @@ describe("Password Policy: ", () => { }; try { await reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', emailAdapter: emailAdapter, passwordPolicy: { resetTokenValidityDuration: 5 * 60, // 5 minutes resetTokenReuseIfValid: [], }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); - fail("should have thrown."); + fail('should have thrown.'); } catch (e) { - expect(e).toBe("resetTokenReuseIfValid must be a boolean value"); + expect(e).toBe('resetTokenReuseIfValid must be a boolean value'); } try { await reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', emailAdapter: emailAdapter, passwordPolicy: { resetTokenReuseIfValid: true, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); - fail("should have thrown."); + fail('should have thrown.'); } catch (e) { expect(e).toBe( - "You cannot use resetTokenReuseIfValid without resetTokenValidityDuration" + 'You cannot use resetTokenReuseIfValid without resetTokenValidityDuration' ); } done(); }); - it("should fail if passwordPolicy.resetTokenValidityDuration is not a number", done => { + it('should fail if passwordPolicy.resetTokenValidityDuration is not a number', done => { reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', passwordPolicy: { - resetTokenValidityDuration: "not a number", + resetTokenValidityDuration: 'not a number', }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }) .then(() => { fail( @@ -243,89 +243,89 @@ describe("Password Policy: ", () => { }) .catch(err => { expect(err).toEqual( - "passwordPolicy.resetTokenValidityDuration must be a positive number" + 'passwordPolicy.resetTokenValidityDuration must be a positive number' ); done(); }); }); - it("should fail if passwordPolicy.resetTokenValidityDuration is zero or a negative number", done => { + it('should fail if passwordPolicy.resetTokenValidityDuration is zero or a negative number', done => { reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', passwordPolicy: { resetTokenValidityDuration: 0, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }) .then(() => { - fail("resetTokenValidityDuration negative number test failed"); + fail('resetTokenValidityDuration negative number test failed'); done(); }) .catch(err => { expect(err).toEqual( - "passwordPolicy.resetTokenValidityDuration must be a positive number" + 'passwordPolicy.resetTokenValidityDuration must be a positive number' ); done(); }); }); - it("should fail if passwordPolicy.validatorPattern setting is invalid type", done => { + it('should fail if passwordPolicy.validatorPattern setting is invalid type', done => { reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', passwordPolicy: { validatorPattern: 1234, // number is not a valid setting }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }) .then(() => { - fail("passwordPolicy.validatorPattern type test failed"); + fail('passwordPolicy.validatorPattern type test failed'); done(); }) .catch(err => { expect(err).toEqual( - "passwordPolicy.validatorPattern must be a regex string or RegExp object." + 'passwordPolicy.validatorPattern must be a regex string or RegExp object.' ); done(); }); }); - it("should fail if passwordPolicy.validatorCallback setting is invalid type", done => { + it('should fail if passwordPolicy.validatorCallback setting is invalid type', done => { reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', passwordPolicy: { - validatorCallback: "abc", // string is not a valid setting + validatorCallback: 'abc', // string is not a valid setting }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }) .then(() => { - fail("passwordPolicy.validatorCallback type test failed"); + fail('passwordPolicy.validatorCallback type test failed'); done(); }) .catch(err => { expect(err).toEqual( - "passwordPolicy.validatorCallback must be a function." + 'passwordPolicy.validatorCallback must be a function.' ); done(); }); }); - it("signup should fail if password does not conform to the policy enforced using validatorPattern", done => { + it('signup should fail if password does not conform to the policy enforced using validatorPattern', done => { const user = new Parse.User(); reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', passwordPolicy: { validatorPattern: /[0-9]+/, // password should contain at least one digit }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { - user.setUsername("user1"); - user.setPassword("nodigit"); - user.set("email", "user1@parse.com"); + user.setUsername('user1'); + user.setPassword('nodigit'); + user.set('email', 'user1@parse.com'); user .signUp() .then(() => { fail( - "Should have failed as password does not conform to the policy." + 'Should have failed as password does not conform to the policy.' ); done(); }) @@ -336,23 +336,23 @@ describe("Password Policy: ", () => { }); }); - it("signup should fail if password does not conform to the policy enforced using validatorPattern string", done => { + it('signup should fail if password does not conform to the policy enforced using validatorPattern string', done => { const user = new Parse.User(); reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', passwordPolicy: { - validatorPattern: "^.{8,}", // password should contain at least 8 char + validatorPattern: '^.{8,}', // password should contain at least 8 char }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { - user.setUsername("user1"); - user.setPassword("less"); - user.set("email", "user1@parse.com"); + user.setUsername('user1'); + user.setPassword('less'); + user.set('email', 'user1@parse.com'); user .signUp() .then(() => { fail( - "Should have failed as password does not conform to the policy." + 'Should have failed as password does not conform to the policy.' ); done(); }) @@ -363,138 +363,138 @@ describe("Password Policy: ", () => { }); }); - it("signup should fail if password is empty", done => { + it('signup should fail if password is empty', done => { const user = new Parse.User(); reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', passwordPolicy: { - validatorPattern: "^.{8,}", // password should contain at least 8 char + validatorPattern: '^.{8,}', // password should contain at least 8 char }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { - user.setUsername("user1"); - user.setPassword(""); - user.set("email", "user1@parse.com"); + user.setUsername('user1'); + user.setPassword(''); + user.set('email', 'user1@parse.com'); user .signUp() .then(() => { fail( - "Should have failed as password does not conform to the policy." + 'Should have failed as password does not conform to the policy.' ); done(); }) .catch(error => { expect(error.message).toEqual( - "Cannot sign up user with an empty password." + 'Cannot sign up user with an empty password.' ); done(); }); }); }); - it("signup should succeed if password conforms to the policy enforced using validatorPattern", done => { + it('signup should succeed if password conforms to the policy enforced using validatorPattern', done => { const user = new Parse.User(); reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', passwordPolicy: { validatorPattern: /[0-9]+/, // password should contain at least one digit }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { - user.setUsername("user1"); - user.setPassword("1digit"); - user.set("email", "user1@parse.com"); + user.setUsername('user1'); + user.setPassword('1digit'); + user.set('email', 'user1@parse.com'); user .signUp() .then(() => { Parse.User.logOut() .then(() => { - Parse.User.logIn("user1", "1digit") + Parse.User.logIn('user1', '1digit') .then(function () { done(); }) .catch(err => { jfail(err); - fail("Should be able to login"); + fail('Should be able to login'); done(); }); }) .catch(error => { jfail(error); - fail("logout should have succeeded"); + fail('logout should have succeeded'); done(); }); }) .catch(error => { jfail(error); fail( - "Signup should have succeeded as password conforms to the policy." + 'Signup should have succeeded as password conforms to the policy.' ); done(); }); }); }); - it("signup should succeed if password conforms to the policy enforced using validatorPattern string", done => { + it('signup should succeed if password conforms to the policy enforced using validatorPattern string', done => { const user = new Parse.User(); reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', passwordPolicy: { - validatorPattern: "[!@#$]+", // password should contain at least one special char + validatorPattern: '[!@#$]+', // password should contain at least one special char }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { - user.setUsername("user1"); - user.setPassword("p@sswrod"); - user.set("email", "user1@parse.com"); + user.setUsername('user1'); + user.setPassword('p@sswrod'); + user.set('email', 'user1@parse.com'); user .signUp() .then(() => { Parse.User.logOut() .then(() => { - Parse.User.logIn("user1", "p@sswrod") + Parse.User.logIn('user1', 'p@sswrod') .then(function () { done(); }) .catch(err => { jfail(err); - fail("Should be able to login"); + fail('Should be able to login'); done(); }); }) .catch(error => { jfail(error); - fail("logout should have succeeded"); + fail('logout should have succeeded'); done(); }); }) .catch(error => { jfail(error); fail( - "Signup should have succeeded as password conforms to the policy." + 'Signup should have succeeded as password conforms to the policy.' ); done(); }); }); }); - it("signup should fail if password does not conform to the policy enforced using validatorCallback", done => { + it('signup should fail if password does not conform to the policy enforced using validatorCallback', done => { const user = new Parse.User(); reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', passwordPolicy: { validatorCallback: () => false, // just fail }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { - user.setUsername("user1"); - user.setPassword("any"); - user.set("email", "user1@parse.com"); + user.setUsername('user1'); + user.setPassword('any'); + user.set('email', 'user1@parse.com'); user .signUp() .then(() => { fail( - "Should have failed as password does not conform to the policy." + 'Should have failed as password does not conform to the policy.' ); done(); }) @@ -505,65 +505,65 @@ describe("Password Policy: ", () => { }); }); - it("signup should succeed if password conforms to the policy enforced using validatorCallback", done => { + it('signup should succeed if password conforms to the policy enforced using validatorCallback', done => { const user = new Parse.User(); reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', passwordPolicy: { validatorCallback: () => true, // never fail }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { - user.setUsername("user1"); - user.setPassword("oneUpper"); - user.set("email", "user1@parse.com"); + user.setUsername('user1'); + user.setPassword('oneUpper'); + user.set('email', 'user1@parse.com'); user .signUp() .then(() => { Parse.User.logOut() .then(() => { - Parse.User.logIn("user1", "oneUpper") + Parse.User.logIn('user1', 'oneUpper') .then(function () { done(); }) .catch(err => { jfail(err); - fail("Should be able to login"); + fail('Should be able to login'); done(); }); }) .catch(error => { jfail(error); - fail("Logout should have succeeded"); + fail('Logout should have succeeded'); done(); }); }) .catch(error => { jfail(error); - fail("Should have succeeded as password conforms to the policy."); + fail('Should have succeeded as password conforms to the policy.'); done(); }); }); }); - it("signup should fail if password does not match validatorPattern but succeeds validatorCallback", done => { + it('signup should fail if password does not match validatorPattern but succeeds validatorCallback', done => { const user = new Parse.User(); reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', passwordPolicy: { validatorPattern: /[A-Z]+/, // password should contain at least one UPPER case letter validatorCallback: () => true, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { - user.setUsername("user1"); - user.setPassword("all lower"); - user.set("email", "user1@parse.com"); + user.setUsername('user1'); + user.setPassword('all lower'); + user.set('email', 'user1@parse.com'); user .signUp() .then(() => { fail( - "Should have failed as password does not conform to the policy." + 'Should have failed as password does not conform to the policy.' ); done(); }) @@ -574,24 +574,24 @@ describe("Password Policy: ", () => { }); }); - it("signup should fail if password matches validatorPattern but fails validatorCallback", done => { + it('signup should fail if password matches validatorPattern but fails validatorCallback', done => { const user = new Parse.User(); reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', passwordPolicy: { validatorPattern: /[A-Z]+/, // password should contain at least one UPPER case letter validatorCallback: () => false, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { - user.setUsername("user1"); - user.setPassword("oneUpper"); - user.set("email", "user1@parse.com"); + user.setUsername('user1'); + user.setPassword('oneUpper'); + user.set('email', 'user1@parse.com'); user .signUp() .then(() => { fail( - "Should have failed as password does not conform to the policy." + 'Should have failed as password does not conform to the policy.' ); done(); }) @@ -602,49 +602,49 @@ describe("Password Policy: ", () => { }); }); - it("signup should succeed if password conforms to both validatorPattern and validatorCallback", done => { + it('signup should succeed if password conforms to both validatorPattern and validatorCallback', done => { const user = new Parse.User(); reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', passwordPolicy: { validatorPattern: /[A-Z]+/, // password should contain at least one digit validatorCallback: () => true, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { - user.setUsername("user1"); - user.setPassword("oneUpper"); - user.set("email", "user1@parse.com"); + user.setUsername('user1'); + user.setPassword('oneUpper'); + user.set('email', 'user1@parse.com'); user .signUp() .then(() => { Parse.User.logOut() .then(() => { - Parse.User.logIn("user1", "oneUpper") + Parse.User.logIn('user1', 'oneUpper') .then(function () { done(); }) .catch(err => { jfail(err); - fail("Should be able to login"); + fail('Should be able to login'); done(); }); }) .catch(error => { jfail(error); - fail("logout should have succeeded"); + fail('logout should have succeeded'); done(); }); }) .catch(error => { jfail(error); - fail("Should have succeeded as password conforms to the policy."); + fail('Should have succeeded as password conforms to the policy.'); done(); }); }); }); - it("should reset password if new password conforms to password policy", done => { + it('should reset password if new password conforms to password policy', done => { const user = new Parse.User(); const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), @@ -661,18 +661,18 @@ describe("Password Policy: ", () => { /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; const match = response.text.match(re); if (!match) { - fail("should have a token"); + fail('should have a token'); done(); return; } const token = match[1]; request({ - method: "POST", - url: "http://localhost:8378/1/apps/test/request_password_reset", + method: 'POST', + url: 'http://localhost:8378/1/apps/test/request_password_reset', body: `new_password=has2init&token=${token}`, headers: { - "Content-Type": "application/x-www-form-urlencoded", + 'Content-Type': 'application/x-www-form-urlencoded', }, followRedirects: false, simple: false, @@ -681,63 +681,63 @@ describe("Password Policy: ", () => { .then(response => { expect(response.status).toEqual(302); expect(response.text).toEqual( - "Found. Redirecting to http://localhost:8378/1/apps/password_reset_success.html" + 'Found. Redirecting to http://localhost:8378/1/apps/password_reset_success.html' ); - Parse.User.logIn("user1", "has2init") + Parse.User.logIn('user1', 'has2init') .then(function () { done(); }) .catch(err => { jfail(err); - fail("should login with new password"); + fail('should login with new password'); done(); }); }) .catch(error => { jfail(error); - fail("Failed to POST request password reset"); + fail('Failed to POST request password reset'); done(); }); }) .catch(error => { jfail(error); - fail("Failed to get the reset link"); + fail('Failed to get the reset link'); done(); }); }, sendMail: () => {}, }; reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', verifyUserEmails: false, emailAdapter: emailAdapter, passwordPolicy: { validatorPattern: /[0-9]+/, // password should contain at least one digit }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { - user.setUsername("user1"); - user.setPassword("has 1 digit"); - user.set("email", "user1@parse.com"); + user.setUsername('user1'); + user.setPassword('has 1 digit'); + user.set('email', 'user1@parse.com'); user .signUp() .then(() => { - Parse.User.requestPasswordReset("user1@parse.com").catch(err => { + Parse.User.requestPasswordReset('user1@parse.com').catch(err => { jfail(err); - fail("Reset password request should not fail"); + fail('Reset password request should not fail'); done(); }); }) .catch(error => { jfail(error); - fail("signUp should not fail"); + fail('signUp should not fail'); done(); }); }); }); - it("should fail to reset password if the new password does not conform to password policy", done => { + it('should fail to reset password if the new password does not conform to password policy', done => { const user = new Parse.User(); const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), @@ -754,18 +754,18 @@ describe("Password Policy: ", () => { /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; const match = response.text.match(re); if (!match) { - fail("should have a token"); + fail('should have a token'); done(); return; } const token = match[1]; request({ - method: "POST", - url: "http://localhost:8378/1/apps/test/request_password_reset", + method: 'POST', + url: 'http://localhost:8378/1/apps/test/request_password_reset', body: `new_password=hasnodigit&token=${token}`, headers: { - "Content-Type": "application/x-www-form-urlencoded", + 'Content-Type': 'application/x-www-form-urlencoded', }, followRedirects: false, simple: false, @@ -777,158 +777,158 @@ describe("Password Policy: ", () => { `Found. Redirecting to http://localhost:8378/1/apps/choose_password?token=${token}&id=test&error=Password%20should%20contain%20at%20least%20one%20digit.&app=passwordPolicy` ); - Parse.User.logIn("user1", "has 1 digit") + Parse.User.logIn('user1', 'has 1 digit') .then(function () { done(); }) .catch(err => { jfail(err); - fail("should login with old password"); + fail('should login with old password'); done(); }); }) .catch(error => { jfail(error); - fail("Failed to POST request password reset"); + fail('Failed to POST request password reset'); done(); }); }) .catch(error => { jfail(error); - fail("Failed to get the reset link"); + fail('Failed to get the reset link'); done(); }); }, sendMail: () => {}, }; reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', verifyUserEmails: false, emailAdapter: emailAdapter, passwordPolicy: { validatorPattern: /[0-9]+/, // password should contain at least one digit - validationError: "Password should contain at least one digit.", + validationError: 'Password should contain at least one digit.', }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { - user.setUsername("user1"); - user.setPassword("has 1 digit"); - user.set("email", "user1@parse.com"); + user.setUsername('user1'); + user.setPassword('has 1 digit'); + user.set('email', 'user1@parse.com'); user .signUp() .then(() => { - Parse.User.requestPasswordReset("user1@parse.com").catch(err => { + Parse.User.requestPasswordReset('user1@parse.com').catch(err => { jfail(err); - fail("Reset password request should not fail"); + fail('Reset password request should not fail'); done(); }); }) .catch(error => { jfail(error); - fail("signUp should not fail"); + fail('signUp should not fail'); done(); }); }); }); - it("should fail if passwordPolicy.doNotAllowUsername is not a boolean value", done => { + it('should fail if passwordPolicy.doNotAllowUsername is not a boolean value', done => { reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', passwordPolicy: { - doNotAllowUsername: "no", + doNotAllowUsername: 'no', }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }) .then(() => { - fail("passwordPolicy.doNotAllowUsername type test failed"); + fail('passwordPolicy.doNotAllowUsername type test failed'); done(); }) .catch(err => { expect(err).toEqual( - "passwordPolicy.doNotAllowUsername must be a boolean value." + 'passwordPolicy.doNotAllowUsername must be a boolean value.' ); done(); }); }); - it("signup should fail if password contains the username and is not allowed by policy", done => { + it('signup should fail if password contains the username and is not allowed by policy', done => { const user = new Parse.User(); reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', passwordPolicy: { validatorPattern: /[0-9]+/, doNotAllowUsername: true, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { - user.setUsername("user1"); - user.setPassword("@user11"); - user.set("email", "user1@parse.com"); + user.setUsername('user1'); + user.setPassword('@user11'); + user.set('email', 'user1@parse.com'); user .signUp() .then(() => { - fail("Should have failed as password contains username."); + fail('Should have failed as password contains username.'); done(); }) .catch(error => { expect(error.code).toEqual(142); expect(error.message).toEqual( - "Password cannot contain your username." + 'Password cannot contain your username.' ); done(); }); }); }); - it("signup should succeed if password does not contain the username and is not allowed by policy", done => { + it('signup should succeed if password does not contain the username and is not allowed by policy', done => { const user = new Parse.User(); reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', passwordPolicy: { doNotAllowUsername: true, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { - user.setUsername("user1"); - user.setPassword("r@nd0m"); - user.set("email", "user1@parse.com"); + user.setUsername('user1'); + user.setPassword('r@nd0m'); + user.set('email', 'user1@parse.com'); user .signUp() .then(() => { done(); }) .catch(() => { - fail("Should have succeeded as password does not contain username."); + fail('Should have succeeded as password does not contain username.'); done(); }); }); }); - it("signup should succeed if password contains the username and it is allowed by policy", done => { + it('signup should succeed if password contains the username and it is allowed by policy', done => { const user = new Parse.User(); reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', passwordPolicy: { validatorPattern: /[0-9]+/, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { - user.setUsername("user1"); - user.setPassword("user1"); - user.set("email", "user1@parse.com"); + user.setUsername('user1'); + user.setPassword('user1'); + user.set('email', 'user1@parse.com'); user .signUp() .then(() => { done(); }) .catch(() => { - fail("Should have succeeded as policy allows username in password."); + fail('Should have succeeded as policy allows username in password.'); done(); }); }); }); - it("should fail to reset password if the new password contains username and not allowed by password policy", done => { + it('should fail to reset password if the new password contains username and not allowed by password policy', done => { const user = new Parse.User(); const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), @@ -945,18 +945,18 @@ describe("Password Policy: ", () => { /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; const match = response.text.match(re); if (!match) { - fail("should have a token"); + fail('should have a token'); done(); return; } const token = match[1]; request({ - method: "POST", - url: "http://localhost:8378/1/apps/test/request_password_reset", + method: 'POST', + url: 'http://localhost:8378/1/apps/test/request_password_reset', body: `new_password=xuser12&token=${token}`, headers: { - "Content-Type": "application/x-www-form-urlencoded", + 'Content-Type': 'application/x-www-form-urlencoded', }, followRedirects: false, simple: false, @@ -968,60 +968,60 @@ describe("Password Policy: ", () => { `Found. Redirecting to http://localhost:8378/1/apps/choose_password?token=${token}&id=test&error=Password%20cannot%20contain%20your%20username.&app=passwordPolicy` ); - Parse.User.logIn("user1", "r@nd0m") + Parse.User.logIn('user1', 'r@nd0m') .then(function () { done(); }) .catch(err => { jfail(err); - fail("should login with old password"); + fail('should login with old password'); done(); }); }) .catch(error => { jfail(error); - fail("Failed to POST request password reset"); + fail('Failed to POST request password reset'); done(); }); }) .catch(error => { jfail(error); - fail("Failed to get the reset link"); + fail('Failed to get the reset link'); done(); }); }, sendMail: () => {}, }; reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', verifyUserEmails: false, emailAdapter: emailAdapter, passwordPolicy: { doNotAllowUsername: true, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { - user.setUsername("user1"); - user.setPassword("r@nd0m"); - user.set("email", "user1@parse.com"); + user.setUsername('user1'); + user.setPassword('r@nd0m'); + user.set('email', 'user1@parse.com'); user .signUp() .then(() => { - Parse.User.requestPasswordReset("user1@parse.com").catch(err => { + Parse.User.requestPasswordReset('user1@parse.com').catch(err => { jfail(err); - fail("Reset password request should not fail"); + fail('Reset password request should not fail'); done(); }); }) .catch(error => { jfail(error); - fail("signUp should not fail"); + fail('signUp should not fail'); done(); }); }); }); - it("Should return error when password violates Password Policy and reset through ajax", async done => { + it('Should return error when password violates Password Policy and reset through ajax', async done => { const user = new Parse.User(); const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), @@ -1037,19 +1037,19 @@ describe("Password Policy: ", () => { /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; const match = response.text.match(re); if (!match) { - fail("should have a token"); + fail('should have a token'); return; } const token = match[1]; try { await request({ - method: "POST", - url: "http://localhost:8378/1/apps/test/request_password_reset", + method: 'POST', + url: 'http://localhost:8378/1/apps/test/request_password_reset', body: `new_password=xuser12&token=${token}`, headers: { - "Content-Type": "application/x-www-form-urlencoded", - "X-Requested-With": "XMLHttpRequest", + 'Content-Type': 'application/x-www-form-urlencoded', + 'X-Requested-With': 'XMLHttpRequest', }, followRedirects: false, }); @@ -1059,29 +1059,29 @@ describe("Password Policy: ", () => { '{"code":-1,"error":"Password cannot contain your username."}' ); } - await Parse.User.logIn("user1", "r@nd0m"); + await Parse.User.logIn('user1', 'r@nd0m'); done(); }, sendMail: () => {}, }; await reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', verifyUserEmails: false, emailAdapter: emailAdapter, passwordPolicy: { doNotAllowUsername: true, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); - user.setUsername("user1"); - user.setPassword("r@nd0m"); - user.set("email", "user1@parse.com"); + user.setUsername('user1'); + user.setPassword('r@nd0m'); + user.set('email', 'user1@parse.com'); await user.signUp(); - await Parse.User.requestPasswordReset("user1@parse.com"); + await Parse.User.requestPasswordReset('user1@parse.com'); }); - it("should reset password even if the new password contains user name while the policy allows", done => { + it('should reset password even if the new password contains user name while the policy allows', done => { const user = new Parse.User(); const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), @@ -1098,18 +1098,18 @@ describe("Password Policy: ", () => { /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; const match = response.text.match(re); if (!match) { - fail("should have a token"); + fail('should have a token'); done(); return; } const token = match[1]; request({ - method: "POST", - url: "http://localhost:8378/1/apps/test/request_password_reset", + method: 'POST', + url: 'http://localhost:8378/1/apps/test/request_password_reset', body: `new_password=uuser11&token=${token}`, headers: { - "Content-Type": "application/x-www-form-urlencoded", + 'Content-Type': 'application/x-www-form-urlencoded', }, followRedirects: false, simple: false, @@ -1118,67 +1118,67 @@ describe("Password Policy: ", () => { .then(response => { expect(response.status).toEqual(302); expect(response.text).toEqual( - "Found. Redirecting to http://localhost:8378/1/apps/password_reset_success.html" + 'Found. Redirecting to http://localhost:8378/1/apps/password_reset_success.html' ); - Parse.User.logIn("user1", "uuser11") + Parse.User.logIn('user1', 'uuser11') .then(function () { done(); }) .catch(err => { jfail(err); - fail("should login with new password"); + fail('should login with new password'); done(); }); }) .catch(error => { jfail(error); - fail("Failed to POST request password reset"); + fail('Failed to POST request password reset'); }); }) .catch(error => { jfail(error); - fail("Failed to get the reset link"); + fail('Failed to get the reset link'); }); }, sendMail: () => {}, }; reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', verifyUserEmails: false, emailAdapter: emailAdapter, passwordPolicy: { validatorPattern: /[0-9]+/, doNotAllowUsername: false, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }) .then(() => { - user.setUsername("user1"); - user.setPassword("has 1 digit"); - user.set("email", "user1@parse.com"); + user.setUsername('user1'); + user.setPassword('has 1 digit'); + user.set('email', 'user1@parse.com'); user.signUp().then(() => { - Parse.User.requestPasswordReset("user1@parse.com").catch(err => { + Parse.User.requestPasswordReset('user1@parse.com').catch(err => { jfail(err); - fail("Reset password request should not fail"); + fail('Reset password request should not fail'); done(); }); }); }) .catch(error => { jfail(error); - fail("signUp should not fail"); + fail('signUp should not fail'); done(); }); }); - it("should fail if passwordPolicy.maxPasswordAge is not a number", done => { + it('should fail if passwordPolicy.maxPasswordAge is not a number', done => { reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', passwordPolicy: { - maxPasswordAge: "not a number", + maxPasswordAge: 'not a number', }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }) .then(() => { fail('passwordPolicy.maxPasswordAge "not a number" test failed'); @@ -1186,96 +1186,96 @@ describe("Password Policy: ", () => { }) .catch(err => { expect(err).toEqual( - "passwordPolicy.maxPasswordAge must be a positive number" + 'passwordPolicy.maxPasswordAge must be a positive number' ); done(); }); }); - it("should fail if passwordPolicy.maxPasswordAge is a negative number", done => { + it('should fail if passwordPolicy.maxPasswordAge is a negative number', done => { reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', passwordPolicy: { maxPasswordAge: -100, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }) .then(() => { - fail("passwordPolicy.maxPasswordAge negative number test failed"); + fail('passwordPolicy.maxPasswordAge negative number test failed'); done(); }) .catch(err => { expect(err).toEqual( - "passwordPolicy.maxPasswordAge must be a positive number" + 'passwordPolicy.maxPasswordAge must be a positive number' ); done(); }); }); - it_id("d7d0a93e-efe6-48c0-b622-0f7fb570ccc1")(it)( - "should succeed if logged in before password expires", + it_id('d7d0a93e-efe6-48c0-b622-0f7fb570ccc1')(it)( + 'should succeed if logged in before password expires', done => { const user = new Parse.User(); reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', passwordPolicy: { maxPasswordAge: 1, // 1 day }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { - user.setUsername("user1"); - user.setPassword("user1"); - user.set("email", "user1@parse.com"); + user.setUsername('user1'); + user.setPassword('user1'); + user.set('email', 'user1@parse.com'); user .signUp() .then(() => { - Parse.User.logIn("user1", "user1") + Parse.User.logIn('user1', 'user1') .then(() => { done(); }) .catch(error => { jfail(error); - fail("Login should have succeeded before password expiry."); + fail('Login should have succeeded before password expiry.'); done(); }); }) .catch(error => { jfail(error); - fail("Signup failed."); + fail('Signup failed.'); done(); }); }); } ); - it_id("22428408-8763-445d-9833-2b2d92008f62")(it)( - "should fail if logged in after password expires", + it_id('22428408-8763-445d-9833-2b2d92008f62')(it)( + 'should fail if logged in after password expires', done => { const user = new Parse.User(); reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', passwordPolicy: { maxPasswordAge: 0.5 / (24 * 60 * 60), // 0.5 sec }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { - user.setUsername("user1"); - user.setPassword("user1"); - user.set("email", "user1@parse.com"); + user.setUsername('user1'); + user.setPassword('user1'); + user.set('email', 'user1@parse.com'); user .signUp() .then(() => { // wait for a bit more than the validity duration set setTimeout(() => { - Parse.User.logIn("user1", "user1") + Parse.User.logIn('user1', 'user1') .then(() => { - fail("logIn should have failed"); + fail('logIn should have failed'); done(); }) .catch(error => { expect(error.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); expect(error.message).toEqual( - "Your password has expired. Please reset your password." + 'Your password has expired. Please reset your password.' ); done(); }); @@ -1283,45 +1283,45 @@ describe("Password Policy: ", () => { }) .catch(error => { jfail(error); - fail("Signup failed."); + fail('Signup failed.'); done(); }); }); } ); - it_id("cc97a109-e35f-4f94-b942-3a6134921cdd")(it)( - "should apply password expiry policy to existing user upon first login after policy is enabled", + it_id('cc97a109-e35f-4f94-b942-3a6134921cdd')(it)( + 'should apply password expiry policy to existing user upon first login after policy is enabled', done => { const user = new Parse.User(); reconfigureServer({ - appName: "passwordPolicy", - publicServerURL: "http://localhost:8378/1", + appName: 'passwordPolicy', + publicServerURL: 'http://localhost:8378/1', }).then(() => { - user.setUsername("user1"); - user.setPassword("user1"); - user.set("email", "user1@parse.com"); + user.setUsername('user1'); + user.setPassword('user1'); + user.set('email', 'user1@parse.com'); user .signUp() .then(() => { Parse.User.logOut() .then(() => { reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', passwordPolicy: { maxPasswordAge: 0.5 / (24 * 60 * 60), // 0.5 sec }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { - Parse.User.logIn("user1", "user1") + Parse.User.logIn('user1', 'user1') .then(() => { Parse.User.logOut() .then(() => { // wait for a bit more than the validity duration set setTimeout(() => { - Parse.User.logIn("user1", "user1") + Parse.User.logIn('user1', 'user1') .then(() => { - fail("logIn should have failed"); + fail('logIn should have failed'); done(); }) .catch(error => { @@ -1329,7 +1329,7 @@ describe("Password Policy: ", () => { Parse.Error.OBJECT_NOT_FOUND ); expect(error.message).toEqual( - "Your password has expired. Please reset your password." + 'Your password has expired. Please reset your password.' ); done(); }); @@ -1337,34 +1337,34 @@ describe("Password Policy: ", () => { }) .catch(error => { jfail(error); - fail("logout should have succeeded"); + fail('logout should have succeeded'); done(); }); }) .catch(error => { jfail(error); - fail("Login failed."); + fail('Login failed.'); done(); }); }); }) .catch(error => { jfail(error); - fail("logout should have succeeded"); + fail('logout should have succeeded'); done(); }); }) .catch(error => { jfail(error); - fail("Signup failed."); + fail('Signup failed.'); done(); }); }); } ); - it_id("d1e6ab9d-c091-4fea-b952-08b7484bfc89")(it)( - "should reset password timestamp when password is reset", + it_id('d1e6ab9d-c091-4fea-b952-08b7484bfc89')(it)( + 'should reset password timestamp when password is reset', done => { const user = new Parse.User(); const emailAdapter = { @@ -1382,18 +1382,18 @@ describe("Password Policy: ", () => { /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; const match = response.text.match(re); if (!match) { - fail("should have a token"); + fail('should have a token'); done(); return; } const token = match[1]; request({ - method: "POST", - url: "http://localhost:8378/1/apps/test/request_password_reset", + method: 'POST', + url: 'http://localhost:8378/1/apps/test/request_password_reset', body: `new_password=uuser11&token=${token}`, headers: { - "Content-Type": "application/x-www-form-urlencoded", + 'Content-Type': 'application/x-www-form-urlencoded', }, followRedirects: false, simple: false, @@ -1402,61 +1402,61 @@ describe("Password Policy: ", () => { .then(response => { expect(response.status).toEqual(302); expect(response.text).toEqual( - "Found. Redirecting to http://localhost:8378/1/apps/password_reset_success.html" + 'Found. Redirecting to http://localhost:8378/1/apps/password_reset_success.html' ); - Parse.User.logIn("user1", "uuser11") + Parse.User.logIn('user1', 'uuser11') .then(function () { done(); }) .catch(err => { jfail(err); - fail("should login with new password"); + fail('should login with new password'); done(); }); }) .catch(error => { jfail(error); - fail("Failed to POST request password reset"); + fail('Failed to POST request password reset'); }); }) .catch(error => { jfail(error); - fail("Failed to get the reset link"); + fail('Failed to get the reset link'); }); }, sendMail: () => {}, }; reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', emailAdapter: emailAdapter, passwordPolicy: { maxPasswordAge: 0.5 / (24 * 60 * 60), // 0.5 sec }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { - user.setUsername("user1"); - user.setPassword("user1"); - user.set("email", "user1@parse.com"); + user.setUsername('user1'); + user.setPassword('user1'); + user.set('email', 'user1@parse.com'); user .signUp() .then(() => { // wait for a bit more than the validity duration set setTimeout(() => { - Parse.User.logIn("user1", "user1") + Parse.User.logIn('user1', 'user1') .then(() => { - fail("logIn should have failed"); + fail('logIn should have failed'); done(); }) .catch(error => { expect(error.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); expect(error.message).toEqual( - "Your password has expired. Please reset your password." + 'Your password has expired. Please reset your password.' ); - Parse.User.requestPasswordReset("user1@parse.com").catch( + Parse.User.requestPasswordReset('user1@parse.com').catch( err => { jfail(err); - fail("Reset password request should not fail"); + fail('Reset password request should not fail'); done(); } ); @@ -1465,20 +1465,20 @@ describe("Password Policy: ", () => { }) .catch(error => { jfail(error); - fail("Signup failed."); + fail('Signup failed.'); done(); }); }); } ); - it("should fail if passwordPolicy.maxPasswordHistory is not a number", done => { + it('should fail if passwordPolicy.maxPasswordHistory is not a number', done => { reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', passwordPolicy: { - maxPasswordHistory: "not a number", + maxPasswordHistory: 'not a number', }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }) .then(() => { fail('passwordPolicy.maxPasswordHistory "not a number" test failed'); @@ -1486,53 +1486,53 @@ describe("Password Policy: ", () => { }) .catch(err => { expect(err).toEqual( - "passwordPolicy.maxPasswordHistory must be an integer ranging 0 - 20" + 'passwordPolicy.maxPasswordHistory must be an integer ranging 0 - 20' ); done(); }); }); - it("should fail if passwordPolicy.maxPasswordHistory is a negative number", done => { + it('should fail if passwordPolicy.maxPasswordHistory is a negative number', done => { reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', passwordPolicy: { maxPasswordHistory: -10, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }) .then(() => { - fail("passwordPolicy.maxPasswordHistory negative number test failed"); + fail('passwordPolicy.maxPasswordHistory negative number test failed'); done(); }) .catch(err => { expect(err).toEqual( - "passwordPolicy.maxPasswordHistory must be an integer ranging 0 - 20" + 'passwordPolicy.maxPasswordHistory must be an integer ranging 0 - 20' ); done(); }); }); - it("should fail if passwordPolicy.maxPasswordHistory is greater than 20", done => { + it('should fail if passwordPolicy.maxPasswordHistory is greater than 20', done => { reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', passwordPolicy: { maxPasswordHistory: 21, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }) .then(() => { - fail("passwordPolicy.maxPasswordHistory negative number test failed"); + fail('passwordPolicy.maxPasswordHistory negative number test failed'); done(); }) .catch(err => { expect(err).toEqual( - "passwordPolicy.maxPasswordHistory must be an integer ranging 0 - 20" + 'passwordPolicy.maxPasswordHistory must be an integer ranging 0 - 20' ); done(); }); }); - it("should fail to reset if the new password is same as the last password", done => { + it('should fail to reset if the new password is same as the last password', done => { const user = new Parse.User(); const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), @@ -1547,18 +1547,18 @@ describe("Password Policy: ", () => { /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; const match = response.text.match(re); if (!match) { - fail("should have a token"); - return Promise.reject("Invalid password link"); + fail('should have a token'); + return Promise.reject('Invalid password link'); } return Promise.resolve(match[1]); // token }) .then(token => { return request({ - method: "POST", - url: "http://localhost:8378/1/apps/test/request_password_reset", + method: 'POST', + url: 'http://localhost:8378/1/apps/test/request_password_reset', body: `new_password=user1&token=${token}`, headers: { - "Content-Type": "application/x-www-form-urlencoded", + 'Content-Type': 'application/x-www-form-urlencoded', }, followRedirects: false, simple: false, @@ -1579,70 +1579,70 @@ describe("Password Policy: ", () => { }) .catch(error => { fail(error); - fail("Repeat password test failed"); + fail('Repeat password test failed'); done(); }); }, sendMail: () => {}, }; reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', verifyUserEmails: false, emailAdapter: emailAdapter, passwordPolicy: { maxPasswordHistory: 1, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { - user.setUsername("user1"); - user.setPassword("user1"); - user.set("email", "user1@parse.com"); + user.setUsername('user1'); + user.setPassword('user1'); + user.set('email', 'user1@parse.com'); user .signUp() .then(() => { return Parse.User.logOut(); }) .then(() => { - return Parse.User.requestPasswordReset("user1@parse.com"); + return Parse.User.requestPasswordReset('user1@parse.com'); }) .catch(error => { jfail(error); - fail("SignUp or reset request failed"); + fail('SignUp or reset request failed'); done(); }); }); }); - it("should fail if the new password is same as the previous one", done => { + it('should fail if the new password is same as the previous one', done => { const user = new Parse.User(); reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', verifyUserEmails: false, passwordPolicy: { maxPasswordHistory: 5, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { - user.setUsername("user1"); - user.setPassword("user1"); - user.set("email", "user1@parse.com"); + user.setUsername('user1'); + user.setPassword('user1'); + user.set('email', 'user1@parse.com'); user .signUp() .then(() => { // try to set the same password as the previous one - user.setPassword("user1"); + user.setPassword('user1'); return user.save(); }) .then(() => { fail( - "should have failed because the new password is same as the old" + 'should have failed because the new password is same as the old' ); done(); }) .catch(error => { expect(error.message).toEqual( - "New password should not be the same as last 5 passwords." + 'New password should not be the same as last 5 passwords.' ); expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); done(); @@ -1650,53 +1650,53 @@ describe("Password Policy: ", () => { }); }); - it("should fail if the new password is same as the 5th oldest one and policy does not allow the previous 5", done => { + it('should fail if the new password is same as the 5th oldest one and policy does not allow the previous 5', done => { const user = new Parse.User(); reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', verifyUserEmails: false, passwordPolicy: { maxPasswordHistory: 5, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { - user.setUsername("user1"); - user.setPassword("user1"); - user.set("email", "user1@parse.com"); + user.setUsername('user1'); + user.setPassword('user1'); + user.set('email', 'user1@parse.com'); user .signUp() .then(() => { // build history - user.setPassword("user2"); + user.setPassword('user2'); return user.save(); }) .then(() => { - user.setPassword("user3"); + user.setPassword('user3'); return user.save(); }) .then(() => { - user.setPassword("user4"); + user.setPassword('user4'); return user.save(); }) .then(() => { - user.setPassword("user5"); + user.setPassword('user5'); return user.save(); }) .then(() => { // set the same password as the initial one - user.setPassword("user1"); + user.setPassword('user1'); return user.save(); }) .then(() => { fail( - "should have failed because the new password is same as the old" + 'should have failed because the new password is same as the old' ); done(); }) .catch(error => { expect(error.message).toEqual( - "New password should not be the same as last 5 passwords." + 'New password should not be the same as last 5 passwords.' ); expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); done(); @@ -1704,46 +1704,46 @@ describe("Password Policy: ", () => { }); }); - it("should succeed if the new password is same as the 6th oldest one and policy does not allow only previous 5", done => { + it('should succeed if the new password is same as the 6th oldest one and policy does not allow only previous 5', done => { const user = new Parse.User(); reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', verifyUserEmails: false, passwordPolicy: { maxPasswordHistory: 5, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { - user.setUsername("user1"); - user.setPassword("user1"); - user.set("email", "user1@parse.com"); + user.setUsername('user1'); + user.setPassword('user1'); + user.set('email', 'user1@parse.com'); user .signUp() .then(() => { // build history - user.setPassword("user2"); + user.setPassword('user2'); return user.save(); }) .then(() => { - user.setPassword("user3"); + user.setPassword('user3'); return user.save(); }) .then(() => { - user.setPassword("user4"); + user.setPassword('user4'); return user.save(); }) .then(() => { - user.setPassword("user5"); + user.setPassword('user5'); return user.save(); }) .then(() => { - user.setPassword("user6"); // this pushes initial password out of history + user.setPassword('user6'); // this pushes initial password out of history return user.save(); }) .then(() => { // set the same password as the initial one - user.setPassword("user1"); + user.setPassword('user1'); return user.save(); }) .then(() => { @@ -1751,56 +1751,56 @@ describe("Password Policy: ", () => { }) .catch(() => { fail( - "should have succeeded because the new password is not in history" + 'should have succeeded because the new password is not in history' ); done(); }); }); }); - it("should not infinitely loop if maxPasswordHistory is 1 (#4918)", async () => { + it('should not infinitely loop if maxPasswordHistory is 1 (#4918)', async () => { const headers = { - "X-Parse-Application-Id": "test", - "X-Parse-Rest-API-Key": "test", - "X-Parse-Maintenance-Key": "test2", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Rest-API-Key': 'test', + 'X-Parse-Maintenance-Key': 'test2', + 'Content-Type': 'application/json', }; const user = new Parse.User(); const query = new Parse.Query(Parse.User); await reconfigureServer({ - appName: "passwordPolicy", + appName: 'passwordPolicy', verifyUserEmails: false, - maintenanceKey: "test2", + maintenanceKey: 'test2', passwordPolicy: { maxPasswordHistory: 1, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); - user.setUsername("user1"); - user.setPassword("user1"); - user.set("email", "user1@parse.com"); + user.setUsername('user1'); + user.setPassword('user1'); + user.set('email', 'user1@parse.com'); await user.signUp(); - user.setPassword("user2"); + user.setPassword('user2'); await user.save(); const user1 = await query.get(user.id, { useMasterKey: true }); - expect(user1.get("_password_history")).toBeUndefined(); + expect(user1.get('_password_history')).toBeUndefined(); const result1 = await request({ - method: "GET", + method: 'GET', url: `http://localhost:8378/1/classes/_User/${user.id}`, json: true, headers, }).then(res => res.data); expect(result1._password_history.length).toBe(1); - user.setPassword("user3"); + user.setPassword('user3'); await user.save(); const result2 = await request({ - method: "GET", + method: 'GET', url: `http://localhost:8378/1/classes/_User/${user.id}`, json: true, headers, diff --git a/spec/PointerPermissions.spec.js b/spec/PointerPermissions.spec.js index 473603d966..bc99a357a7 100644 --- a/spec/PointerPermissions.spec.js +++ b/spec/PointerPermissions.spec.js @@ -1,47 +1,47 @@ -"use strict"; -const Config = require("../lib/Config"); +'use strict'; +const Config = require('../lib/Config'); -describe("Pointer Permissions", () => { +describe('Pointer Permissions', () => { beforeEach(() => { Config.get(Parse.applicationId).schemaCache.clear(); }); - describe("using single user-pointers", () => { - it("should work with find", done => { + describe('using single user-pointers', () => { + it('should work with find', done => { const config = Config.get(Parse.applicationId); const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: "user1", - password: "password", + username: 'user1', + password: 'password', }); user2.set({ - username: "user2", - password: "password", + username: 'user2', + password: 'password', }); - const obj = new Parse.Object("AnObject"); - const obj2 = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); + const obj2 = new Parse.Object('AnObject'); Parse.Object.saveAll([user, user2]) .then(() => { - obj.set("owner", user); - obj2.set("owner", user2); + obj.set('owner', user); + obj2.set('owner', user2); return Parse.Object.saveAll([obj, obj2]); }) .then(() => { return config.database.loadSchema().then(schema => { return schema.updateClass( - "AnObject", + 'AnObject', {}, - { readUserFields: ["owner"] } + { readUserFields: ['owner'] } ); }); }) .then(() => { - return Parse.User.logIn("user1", "password"); + return Parse.User.logIn('user1', 'password'); }) .then(() => { - const q = new Parse.Query("AnObject"); + const q = new Parse.Query('AnObject'); return q.find(); }) .then(res => { @@ -55,51 +55,51 @@ describe("Pointer Permissions", () => { }); }); - it("should work with write", done => { + it('should work with write', done => { const config = Config.get(Parse.applicationId); const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: "user1", - password: "password", + username: 'user1', + password: 'password', }); user2.set({ - username: "user2", - password: "password", + username: 'user2', + password: 'password', }); - const obj = new Parse.Object("AnObject"); - const obj2 = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); + const obj2 = new Parse.Object('AnObject'); Parse.Object.saveAll([user, user2]) .then(() => { - obj.set("owner", user); - obj.set("reader", user2); - obj2.set("owner", user2); - obj2.set("reader", user); + obj.set('owner', user); + obj.set('reader', user2); + obj2.set('owner', user2); + obj2.set('reader', user); return Parse.Object.saveAll([obj, obj2]); }) .then(() => { return config.database.loadSchema().then(schema => { return schema.updateClass( - "AnObject", + 'AnObject', {}, { - writeUserFields: ["owner"], - readUserFields: ["reader", "owner"], + writeUserFields: ['owner'], + readUserFields: ['reader', 'owner'], } ); }); }) .then(() => { - return Parse.User.logIn("user1", "password"); + return Parse.User.logIn('user1', 'password'); }) .then(() => { - obj2.set("hello", "world"); + obj2.set('hello', 'world'); return obj2.save(); }) .then( () => { - fail("User should not be able to update obj2"); + fail('User should not be able to update obj2'); }, err => { // User 1 should not be able to update obj2 @@ -108,25 +108,25 @@ describe("Pointer Permissions", () => { } ) .then(() => { - obj.set("hello", "world"); + obj.set('hello', 'world'); return obj.save(); }) .then( () => { - return Parse.User.logIn("user2", "password"); + return Parse.User.logIn('user2', 'password'); }, () => { - fail("User should be able to update"); + fail('User should be able to update'); return Promise.resolve(); } ) .then( () => { - const q = new Parse.Query("AnObject"); + const q = new Parse.Query('AnObject'); return q.find(); }, () => { - fail("should login with user 2"); + fail('should login with user 2'); } ) .then( @@ -134,7 +134,7 @@ describe("Pointer Permissions", () => { expect(res.length).toBe(2); res.forEach(result => { if (result.id == obj.id) { - expect(result.get("hello")).toBe("world"); + expect(result.get('hello')).toBe('world'); } else { expect(result.id).toBe(obj2.id); } @@ -142,26 +142,26 @@ describe("Pointer Permissions", () => { done(); }, () => { - fail("failed"); + fail('failed'); done(); } ); }); - it("should let a proper user find", done => { + it('should let a proper user find', done => { const config = Config.get(Parse.applicationId); const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: "user1", - password: "password", + username: 'user1', + password: 'password', }); user2.set({ - username: "user2", - password: "password", + username: 'user2', + password: 'password', }); - const obj = new Parse.Object("AnObject"); - const obj2 = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); + const obj2 = new Parse.Object('AnObject'); user .signUp() .then(() => { @@ -171,52 +171,52 @@ describe("Pointer Permissions", () => { Parse.User.logOut(); }) .then(() => { - obj.set("owner", user); + obj.set('owner', user); return Parse.Object.saveAll([obj, obj2]); }) .then(() => { return config.database.loadSchema().then(schema => { return schema.updateClass( - "AnObject", + 'AnObject', {}, - { find: {}, get: {}, readUserFields: ["owner"] } + { find: {}, get: {}, readUserFields: ['owner'] } ); }); }) .then(() => { - const q = new Parse.Query("AnObject"); + const q = new Parse.Query('AnObject'); return q.find(); }) .then(res => { expect(res.length).toBe(0); }) .then(() => { - return Parse.User.logIn("user2", "password"); + return Parse.User.logIn('user2', 'password'); }) .then(() => { - const q = new Parse.Query("AnObject"); + const q = new Parse.Query('AnObject'); return q.find(); }) .then(res => { expect(res.length).toBe(0); - const q = new Parse.Query("AnObject"); + const q = new Parse.Query('AnObject'); return q.get(obj.id); }) .then( () => { - fail("User 2 should not get the obj1 object"); + fail('User 2 should not get the obj1 object'); }, err => { expect(err.code).toBe(Parse.Error.OBJECT_NOT_FOUND); - expect(err.message).toBe("Object not found."); + expect(err.message).toBe('Object not found.'); return Promise.resolve(); } ) .then(() => { - return Parse.User.logIn("user1", "password"); + return Parse.User.logIn('user1', 'password'); }) .then(() => { - const q = new Parse.Query("AnObject"); + const q = new Parse.Query('AnObject'); return q.find(); }) .then(res => { @@ -225,27 +225,27 @@ describe("Pointer Permissions", () => { }) .catch(err => { jfail(err); - fail("should not fail"); + fail('should not fail'); done(); }); }); - it_id("f38c35e7-d804-4d32-986d-2579e25d2461")(it)( - "should query on pointer permission enabled column", + it_id('f38c35e7-d804-4d32-986d-2579e25d2461')(it)( + 'should query on pointer permission enabled column', done => { const config = Config.get(Parse.applicationId); const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: "user1", - password: "password", + username: 'user1', + password: 'password', }); user2.set({ - username: "user2", - password: "password", + username: 'user2', + password: 'password', }); - const obj = new Parse.Object("AnObject"); - const obj2 = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); + const obj2 = new Parse.Object('AnObject'); user .signUp() .then(() => { @@ -255,24 +255,24 @@ describe("Pointer Permissions", () => { Parse.User.logOut(); }) .then(() => { - obj.set("owner", user); + obj.set('owner', user); return Parse.Object.saveAll([obj, obj2]); }) .then(() => { return config.database.loadSchema().then(schema => { return schema.updateClass( - "AnObject", + 'AnObject', {}, - { find: {}, get: {}, readUserFields: ["owner"] } + { find: {}, get: {}, readUserFields: ['owner'] } ); }); }) .then(() => { - return Parse.User.logIn("user1", "password"); + return Parse.User.logIn('user1', 'password'); }) .then(() => { - const q = new Parse.Query("AnObject"); - q.equalTo("owner", user2); + const q = new Parse.Query('AnObject'); + q.equalTo('owner', user2); return q.find(); }) .then(res => { @@ -281,45 +281,45 @@ describe("Pointer Permissions", () => { }) .catch(err => { jfail(err); - fail("should not fail"); + fail('should not fail'); done(); }); } ); - it("should not allow creating objects", done => { + it('should not allow creating objects', done => { const config = Config.get(Parse.applicationId); const user = new Parse.User(); user.set({ - username: "user1", - password: "password", + username: 'user1', + password: 'password', }); - const obj = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); user .save() .then(() => { return config.database.loadSchema().then(schema => { return schema.addClassIfNotExists( - "AnObject", - { owner: { type: "Pointer", targetClass: "_User" } }, + 'AnObject', + { owner: { type: 'Pointer', targetClass: '_User' } }, { create: {}, - writeUserFields: ["owner"], - readUserFields: ["owner"], + writeUserFields: ['owner'], + readUserFields: ['owner'], } ); }); }) .then(() => { - return Parse.User.logIn("user1", "password"); + return Parse.User.logIn('user1', 'password'); }) .then(() => { - obj.set("owner", user); + obj.set('owner', user); return obj.save(); }) .then( () => { - fail("should not succeed"); + fail('should not succeed'); done(); }, err => { @@ -329,69 +329,69 @@ describe("Pointer Permissions", () => { ); }); - it("should handle multiple writeUserFields", done => { + it('should handle multiple writeUserFields', done => { const config = Config.get(Parse.applicationId); const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: "user1", - password: "password", + username: 'user1', + password: 'password', }); user2.set({ - username: "user2", - password: "password", + username: 'user2', + password: 'password', }); - const obj = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); Parse.Object.saveAll([user, user2]) .then(() => { - obj.set("owner", user); - obj.set("otherOwner", user2); + obj.set('owner', user); + obj.set('otherOwner', user2); return obj.save(); }) .then(() => config.database.loadSchema()) .then(schema => schema.updateClass( - "AnObject", + 'AnObject', {}, - { find: { "*": true }, writeUserFields: ["owner", "otherOwner"] } + { find: { '*': true }, writeUserFields: ['owner', 'otherOwner'] } ) ) - .then(() => Parse.User.logIn("user1", "password")) - .then(() => obj.save({ hello: "fromUser1" })) - .then(() => Parse.User.logIn("user2", "password")) - .then(() => obj.save({ hello: "fromUser2" })) + .then(() => Parse.User.logIn('user1', 'password')) + .then(() => obj.save({ hello: 'fromUser1' })) + .then(() => Parse.User.logIn('user2', 'password')) + .then(() => obj.save({ hello: 'fromUser2' })) .then(() => Parse.User.logOut()) .then(() => { - const q = new Parse.Query("AnObject"); + const q = new Parse.Query('AnObject'); return q.first(); }) .then(result => { - expect(result.get("hello")).toBe("fromUser2"); + expect(result.get('hello')).toBe('fromUser2'); done(); }) .catch(() => { - fail("should not fail"); + fail('should not fail'); done(); }); }); - it("should prevent creating pointer permission on missing field", done => { + it('should prevent creating pointer permission on missing field', done => { const config = Config.get(Parse.applicationId); config.database .loadSchema() .then(schema => { return schema.addClassIfNotExists( - "AnObject", + 'AnObject', {}, { create: {}, - writeUserFields: ["owner"], - readUserFields: ["owner"], + writeUserFields: ['owner'], + readUserFields: ['owner'], } ); }) .then(() => { - fail("should not succeed"); + fail('should not succeed'); }) .catch(err => { expect(err.code).toBe(107); @@ -402,23 +402,23 @@ describe("Pointer Permissions", () => { }); }); - it("should prevent creating pointer permission on bad field (of wrong type)", done => { + it('should prevent creating pointer permission on bad field (of wrong type)', done => { const config = Config.get(Parse.applicationId); config.database .loadSchema() .then(schema => { return schema.addClassIfNotExists( - "AnObject", - { owner: { type: "String" } }, + 'AnObject', + { owner: { type: 'String' } }, { create: {}, - writeUserFields: ["owner"], - readUserFields: ["owner"], + writeUserFields: ['owner'], + readUserFields: ['owner'], } ); }) .then(() => { - fail("should not succeed"); + fail('should not succeed'); }) .catch(err => { expect(err.code).toBe(107); @@ -429,23 +429,23 @@ describe("Pointer Permissions", () => { }); }); - it("should prevent creating pointer permission on bad field (non-user pointer)", done => { + it('should prevent creating pointer permission on bad field (non-user pointer)', done => { const config = Config.get(Parse.applicationId); config.database .loadSchema() .then(schema => { return schema.addClassIfNotExists( - "AnObject", - { owner: { type: "Pointer", targetClass: "_Session" } }, + 'AnObject', + { owner: { type: 'Pointer', targetClass: '_Session' } }, { create: {}, - writeUserFields: ["owner"], - readUserFields: ["owner"], + writeUserFields: ['owner'], + readUserFields: ['owner'], } ); }) .then(() => { - fail("should not succeed"); + fail('should not succeed'); }) .catch(err => { expect(err.code).toBe(107); @@ -456,10 +456,10 @@ describe("Pointer Permissions", () => { }); }); - it("should prevent creating pointer permission on bad field (non-existing)", done => { + it('should prevent creating pointer permission on bad field (non-existing)', done => { const config = Config.get(Parse.applicationId); - const object = new Parse.Object("AnObject"); - object.set("owner", "value"); + const object = new Parse.Object('AnObject'); + object.set('owner', 'value'); object .save() .then(() => { @@ -467,17 +467,17 @@ describe("Pointer Permissions", () => { }) .then(schema => { return schema.updateClass( - "AnObject", + 'AnObject', {}, { create: {}, - writeUserFields: ["owner"], - readUserFields: ["owner"], + writeUserFields: ['owner'], + readUserFields: ['owner'], } ); }) .then(() => { - fail("should not succeed"); + fail('should not succeed'); }) .catch(err => { expect(err.code).toBe(107); @@ -488,7 +488,7 @@ describe("Pointer Permissions", () => { }); }); - it("tests CLP / Pointer Perms / ACL write (PP Locked)", done => { + it('tests CLP / Pointer Perms / ACL write (PP Locked)', done => { /* tests: CLP: update closed ({}) @@ -501,43 +501,43 @@ describe("Pointer Permissions", () => { const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: "user1", - password: "password", + username: 'user1', + password: 'password', }); user2.set({ - username: "user2", - password: "password", + username: 'user2', + password: 'password', }); - const obj = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); Parse.Object.saveAll([user, user2]) .then(() => { const ACL = new Parse.ACL(); ACL.setReadAccess(user, true); ACL.setWriteAccess(user, true); obj.setACL(ACL); - obj.set("owner", user2); + obj.set('owner', user2); return obj.save(); }) .then(() => { return config.database.loadSchema().then(schema => { // Lock the update, and let only owner write return schema.updateClass( - "AnObject", + 'AnObject', {}, - { update: {}, writeUserFields: ["owner"] } + { update: {}, writeUserFields: ['owner'] } ); }); }) .then(() => { - return Parse.User.logIn("user1", "password"); + return Parse.User.logIn('user1', 'password'); }) .then(() => { // user1 has ACL read/write but should be blocked by PP - return obj.save({ key: "value" }); + return obj.save({ key: 'value' }); }) .then( () => { - fail("Should not succeed saving"); + fail('Should not succeed saving'); done(); }, err => { @@ -547,7 +547,7 @@ describe("Pointer Permissions", () => { ); }); - it("tests CLP / Pointer Perms / ACL write (ACL Locked)", done => { + it('tests CLP / Pointer Perms / ACL write (ACL Locked)', done => { /* tests: CLP: update closed ({}) @@ -558,43 +558,43 @@ describe("Pointer Permissions", () => { const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: "user1", - password: "password", + username: 'user1', + password: 'password', }); user2.set({ - username: "user2", - password: "password", + username: 'user2', + password: 'password', }); - const obj = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); Parse.Object.saveAll([user, user2]) .then(() => { const ACL = new Parse.ACL(); ACL.setReadAccess(user, true); ACL.setWriteAccess(user, true); obj.setACL(ACL); - obj.set("owner", user2); + obj.set('owner', user2); return obj.save(); }) .then(() => { return config.database.loadSchema().then(schema => { // Lock the update, and let only owner write return schema.updateClass( - "AnObject", + 'AnObject', {}, - { update: {}, writeUserFields: ["owner"] } + { update: {}, writeUserFields: ['owner'] } ); }); }) .then(() => { - return Parse.User.logIn("user2", "password"); + return Parse.User.logIn('user2', 'password'); }) .then(() => { // user1 has ACL read/write but should be blocked by ACL - return obj.save({ key: "value" }); + return obj.save({ key: 'value' }); }) .then( () => { - fail("Should not succeed saving"); + fail('Should not succeed saving'); done(); }, err => { @@ -604,7 +604,7 @@ describe("Pointer Permissions", () => { ); }); - it("tests CLP / Pointer Perms / ACL write (ACL/PP OK)", done => { + it('tests CLP / Pointer Perms / ACL write (ACL/PP OK)', done => { /* tests: CLP: update closed ({}) @@ -615,53 +615,53 @@ describe("Pointer Permissions", () => { const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: "user1", - password: "password", + username: 'user1', + password: 'password', }); user2.set({ - username: "user2", - password: "password", + username: 'user2', + password: 'password', }); - const obj = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); Parse.Object.saveAll([user, user2]) .then(() => { const ACL = new Parse.ACL(); ACL.setWriteAccess(user, true); ACL.setWriteAccess(user2, true); obj.setACL(ACL); - obj.set("owner", user2); + obj.set('owner', user2); return obj.save(); }) .then(() => { return config.database.loadSchema().then(schema => { // Lock the update, and let only owner write return schema.updateClass( - "AnObject", + 'AnObject', {}, - { update: {}, writeUserFields: ["owner"] } + { update: {}, writeUserFields: ['owner'] } ); }); }) .then(() => { - return Parse.User.logIn("user2", "password"); + return Parse.User.logIn('user2', 'password'); }) .then(() => { // user1 has ACL read/write but should be blocked by ACL - return obj.save({ key: "value" }); + return obj.save({ key: 'value' }); }) .then( objAgain => { - expect(objAgain.get("key")).toBe("value"); + expect(objAgain.get('key')).toBe('value'); done(); }, () => { - fail("Should not fail saving"); + fail('Should not fail saving'); done(); } ); }); - it("tests CLP / Pointer Perms / ACL read (PP locked)", done => { + it('tests CLP / Pointer Perms / ACL read (PP locked)', done => { /* tests: CLP: find/get open ({}) @@ -674,35 +674,35 @@ describe("Pointer Permissions", () => { const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: "user1", - password: "password", + username: 'user1', + password: 'password', }); user2.set({ - username: "user2", - password: "password", + username: 'user2', + password: 'password', }); - const obj = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); Parse.Object.saveAll([user, user2]) .then(() => { const ACL = new Parse.ACL(); ACL.setReadAccess(user, true); ACL.setWriteAccess(user, true); obj.setACL(ACL); - obj.set("owner", user2); + obj.set('owner', user2); return obj.save(); }) .then(() => { return config.database.loadSchema().then(schema => { // Lock the update, and let only owner write return schema.updateClass( - "AnObject", + 'AnObject', {}, - { find: {}, get: {}, readUserFields: ["owner"] } + { find: {}, get: {}, readUserFields: ['owner'] } ); }); }) .then(() => { - return Parse.User.logIn("user1", "password"); + return Parse.User.logIn('user1', 'password'); }) .then(() => { // user1 has ACL read/write but should be block @@ -710,7 +710,7 @@ describe("Pointer Permissions", () => { }) .then( () => { - fail("Should not succeed saving"); + fail('Should not succeed saving'); done(); }, err => { @@ -720,7 +720,7 @@ describe("Pointer Permissions", () => { ); }); - it("tests CLP / Pointer Perms / ACL read (PP/ACL OK)", done => { + it('tests CLP / Pointer Perms / ACL read (PP/ACL OK)', done => { /* tests: CLP: find/get open ({"*": true}) @@ -731,14 +731,14 @@ describe("Pointer Permissions", () => { const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: "user1", - password: "password", + username: 'user1', + password: 'password', }); user2.set({ - username: "user2", - password: "password", + username: 'user2', + password: 'password', }); - const obj = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); Parse.Object.saveAll([user, user2]) .then(() => { const ACL = new Parse.ACL(); @@ -747,25 +747,25 @@ describe("Pointer Permissions", () => { ACL.setReadAccess(user2, true); ACL.setWriteAccess(user2, true); obj.setACL(ACL); - obj.set("owner", user2); + obj.set('owner', user2); return obj.save(); }) .then(() => { return config.database.loadSchema().then(schema => { // Lock the update, and let only owner write return schema.updateClass( - "AnObject", + 'AnObject', {}, { - find: { "*": true }, - get: { "*": true }, - readUserFields: ["owner"], + find: { '*': true }, + get: { '*': true }, + readUserFields: ['owner'], } ); }); }) .then(() => { - return Parse.User.logIn("user2", "password"); + return Parse.User.logIn('user2', 'password'); }) .then(() => { // user1 has ACL read/write but should be block @@ -777,13 +777,13 @@ describe("Pointer Permissions", () => { done(); }, () => { - fail("Should not fail fetching"); + fail('Should not fail fetching'); done(); } ); }); - it("tests CLP / Pointer Perms / ACL read (ACL locked)", done => { + it('tests CLP / Pointer Perms / ACL read (ACL locked)', done => { /* tests: CLP: find/get open ({"*": true}) @@ -794,39 +794,39 @@ describe("Pointer Permissions", () => { const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: "user1", - password: "password", + username: 'user1', + password: 'password', }); user2.set({ - username: "user2", - password: "password", + username: 'user2', + password: 'password', }); - const obj = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); Parse.Object.saveAll([user, user2]) .then(() => { const ACL = new Parse.ACL(); ACL.setReadAccess(user, true); ACL.setWriteAccess(user, true); obj.setACL(ACL); - obj.set("owner", user2); + obj.set('owner', user2); return obj.save(); }) .then(() => { return config.database.loadSchema().then(schema => { // Lock the update, and let only owner write return schema.updateClass( - "AnObject", + 'AnObject', {}, { - find: { "*": true }, - get: { "*": true }, - readUserFields: ["owner"], + find: { '*': true }, + get: { '*': true }, + readUserFields: ['owner'], } ); }); }) .then(() => { - return Parse.User.logIn("user2", "password"); + return Parse.User.logIn('user2', 'password'); }) .then(() => { // user2 has ACL read/write but should be block by ACL @@ -834,7 +834,7 @@ describe("Pointer Permissions", () => { }) .then( () => { - fail("Should not succeed saving"); + fail('Should not succeed saving'); done(); }, err => { @@ -844,24 +844,24 @@ describe("Pointer Permissions", () => { ); }); - it("should let master key find objects", done => { + it('should let master key find objects', done => { const config = Config.get(Parse.applicationId); - const object = new Parse.Object("AnObject"); - object.set("hello", "world"); + const object = new Parse.Object('AnObject'); + object.set('hello', 'world'); return object .save() .then(() => { return config.database.loadSchema().then(schema => { // Lock the update, and let only owner write return schema.updateClass( - "AnObject", - { owner: { type: "Pointer", targetClass: "_User" } }, - { find: {}, get: {}, readUserFields: ["owner"] } + 'AnObject', + { owner: { type: 'Pointer', targetClass: '_User' } }, + { find: {}, get: {}, readUserFields: ['owner'] } ); }); }) .then(() => { - const q = new Parse.Query("AnObject"); + const q = new Parse.Query('AnObject'); return q.find(); }) .then( @@ -872,7 +872,7 @@ describe("Pointer Permissions", () => { } ) .then(() => { - const q = new Parse.Query("AnObject"); + const q = new Parse.Query('AnObject'); return q.find({ useMasterKey: true }); }) .then( @@ -881,30 +881,30 @@ describe("Pointer Permissions", () => { done(); }, () => { - fail("master key should find the object"); + fail('master key should find the object'); done(); } ); }); - it("should let master key get objects", done => { + it('should let master key get objects', done => { const config = Config.get(Parse.applicationId); - const object = new Parse.Object("AnObject"); - object.set("hello", "world"); + const object = new Parse.Object('AnObject'); + object.set('hello', 'world'); return object .save() .then(() => { return config.database.loadSchema().then(schema => { // Lock the update, and let only owner write return schema.updateClass( - "AnObject", - { owner: { type: "Pointer", targetClass: "_User" } }, - { find: {}, get: {}, readUserFields: ["owner"] } + 'AnObject', + { owner: { type: 'Pointer', targetClass: '_User' } }, + { find: {}, get: {}, readUserFields: ['owner'] } ); }); }) .then(() => { - const q = new Parse.Query("AnObject"); + const q = new Parse.Query('AnObject'); return q.get(object.id); }) .then( @@ -915,7 +915,7 @@ describe("Pointer Permissions", () => { } ) .then(() => { - const q = new Parse.Query("AnObject"); + const q = new Parse.Query('AnObject'); return q.get(object.id, { useMasterKey: true }); }) .then( @@ -925,30 +925,30 @@ describe("Pointer Permissions", () => { done(); }, () => { - fail("master key should find the object"); + fail('master key should find the object'); done(); } ); }); - it("should let master key update objects", done => { + it('should let master key update objects', done => { const config = Config.get(Parse.applicationId); - const object = new Parse.Object("AnObject"); - object.set("hello", "world"); + const object = new Parse.Object('AnObject'); + object.set('hello', 'world'); return object .save() .then(() => { return config.database.loadSchema().then(schema => { // Lock the update, and let only owner write return schema.updateClass( - "AnObject", - { owner: { type: "Pointer", targetClass: "_User" } }, - { update: {}, writeUserFields: ["owner"] } + 'AnObject', + { owner: { type: 'Pointer', targetClass: '_User' } }, + { update: {}, writeUserFields: ['owner'] } ); }); }) .then(() => { - return object.save({ hello: "bar" }); + return object.save({ hello: 'bar' }); }) .then( () => {}, @@ -958,33 +958,33 @@ describe("Pointer Permissions", () => { } ) .then(() => { - return object.save({ hello: "baz" }, { useMasterKey: true }); + return object.save({ hello: 'baz' }, { useMasterKey: true }); }) .then( objectAgain => { - expect(objectAgain.get("hello")).toBe("baz"); + expect(objectAgain.get('hello')).toBe('baz'); done(); }, () => { - fail("master key should save the object"); + fail('master key should save the object'); done(); } ); }); - it("should let master key delete objects", done => { + it('should let master key delete objects', done => { const config = Config.get(Parse.applicationId); - const object = new Parse.Object("AnObject"); - object.set("hello", "world"); + const object = new Parse.Object('AnObject'); + object.set('hello', 'world'); return object .save() .then(() => { return config.database.loadSchema().then(schema => { // Lock the update, and let only owner write return schema.updateClass( - "AnObject", - { owner: { type: "Pointer", targetClass: "_User" } }, - { delete: {}, writeUserFields: ["owner"] } + 'AnObject', + { owner: { type: 'Pointer', targetClass: '_User' } }, + { delete: {}, writeUserFields: ['owner'] } ); }); }) @@ -1008,22 +1008,22 @@ describe("Pointer Permissions", () => { done(); }, () => { - fail("master key should destroy the object"); + fail('master key should destroy the object'); done(); } ); }); - it("should fail with invalid pointer perms (not array)", done => { + it('should fail with invalid pointer perms (not array)', done => { const config = Config.get(Parse.applicationId); config.database .loadSchema() .then(schema => { // Lock the update, and let only owner write return schema.addClassIfNotExists( - "AnObject", - { owner: { type: "Pointer", targetClass: "_User" } }, - { delete: {}, writeUserFields: "owner" } + 'AnObject', + { owner: { type: 'Pointer', targetClass: '_User' } }, + { delete: {}, writeUserFields: 'owner' } ); }) .catch(err => { @@ -1032,16 +1032,16 @@ describe("Pointer Permissions", () => { }); }); - it("should fail with invalid pointer perms (non-existing field)", done => { + it('should fail with invalid pointer perms (non-existing field)', done => { const config = Config.get(Parse.applicationId); config.database .loadSchema() .then(schema => { // Lock the update, and let only owner write return schema.addClassIfNotExists( - "AnObject", - { owner: { type: "Pointer", targetClass: "_User" } }, - { delete: {}, writeUserFields: ["owner", "invalid"] } + 'AnObject', + { owner: { type: 'Pointer', targetClass: '_User' } }, + { delete: {}, writeUserFields: ['owner', 'invalid'] } ); }) .catch(err => { @@ -1051,35 +1051,35 @@ describe("Pointer Permissions", () => { }); }); - describe("using arrays of user-pointers", () => { - it("should work with find", async done => { + describe('using arrays of user-pointers', () => { + it('should work with find', async done => { const config = Config.get(Parse.applicationId); const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: "user1", - password: "password", + username: 'user1', + password: 'password', }); user2.set({ - username: "user2", - password: "password", + username: 'user2', + password: 'password', }); - const obj = new Parse.Object("AnObject"); - const obj2 = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); + const obj2 = new Parse.Object('AnObject'); await Parse.Object.saveAll([user, user2]); - obj.set("owners", [user]); - obj2.set("owners", [user2]); + obj.set('owners', [user]); + obj2.set('owners', [user2]); await Parse.Object.saveAll([obj, obj2]); const schema = await config.database.loadSchema(); - await schema.updateClass("AnObject", {}, { readUserFields: ["owners"] }); + await schema.updateClass('AnObject', {}, { readUserFields: ['owners'] }); - await Parse.User.logIn("user1", "password"); + await Parse.User.logIn('user1', 'password'); try { - const q = new Parse.Query("AnObject"); + const q = new Parse.Query('AnObject'); const res = await q.find(); expect(res.length).toBe(1); expect(res[0].id).toBe(obj.id); @@ -1089,279 +1089,279 @@ describe("Pointer Permissions", () => { } }); - it_id("1bbb9ed6-5558-4ce5-a238-b1a2015d273f")(it)( - "should work with write", + it_id('1bbb9ed6-5558-4ce5-a238-b1a2015d273f')(it)( + 'should work with write', async done => { const config = Config.get(Parse.applicationId); const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: "user1", - password: "password", + username: 'user1', + password: 'password', }); user2.set({ - username: "user2", - password: "password", + username: 'user2', + password: 'password', }); - const obj = new Parse.Object("AnObject"); - const obj2 = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); + const obj2 = new Parse.Object('AnObject'); await Parse.Object.saveAll([user, user2]); - obj.set("owner", user); - obj.set("readers", [user2]); - obj2.set("owner", user2); - obj2.set("readers", [user]); + obj.set('owner', user); + obj.set('readers', [user2]); + obj2.set('owner', user2); + obj2.set('readers', [user]); await Parse.Object.saveAll([obj, obj2]); const schema = await config.database.loadSchema(); await schema.updateClass( - "AnObject", + 'AnObject', {}, { - writeUserFields: ["owner"], - readUserFields: ["readers", "owner"], + writeUserFields: ['owner'], + readUserFields: ['readers', 'owner'], } ); - await Parse.User.logIn("user1", "password"); + await Parse.User.logIn('user1', 'password'); - obj2.set("hello", "world"); + obj2.set('hello', 'world'); try { await obj2.save(); - done.fail("User should not be able to update obj2"); + done.fail('User should not be able to update obj2'); } catch (err) { // User 1 should not be able to update obj2 expect(err.code).toBe(Parse.Error.OBJECT_NOT_FOUND); } - obj.set("hello", "world"); + obj.set('hello', 'world'); try { await obj.save(); } catch (err) { - done.fail("User should be able to update"); + done.fail('User should be able to update'); } - await Parse.User.logIn("user2", "password"); + await Parse.User.logIn('user2', 'password'); try { - const q = new Parse.Query("AnObject"); + const q = new Parse.Query('AnObject'); const res = await q.find(); expect(res.length).toBe(2); res.forEach(result => { if (result.id == obj.id) { - expect(result.get("hello")).toBe("world"); + expect(result.get('hello')).toBe('world'); } else { expect(result.id).toBe(obj2.id); } }); done(); } catch (err) { - done.fail("failed"); + done.fail('failed'); } } ); - it("should let a proper user find", async done => { + it('should let a proper user find', async done => { const config = Config.get(Parse.applicationId); const user = new Parse.User(); const user2 = new Parse.User(); const user3 = new Parse.User(); user.set({ - username: "user1", - password: "password", + username: 'user1', + password: 'password', }); user2.set({ - username: "user2", - password: "password", + username: 'user2', + password: 'password', }); user3.set({ - username: "user3", - password: "password", + username: 'user3', + password: 'password', }); - const obj = new Parse.Object("AnObject"); - const obj2 = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); + const obj2 = new Parse.Object('AnObject'); await user.signUp(); await user2.signUp(); await user3.signUp(); await Parse.User.logOut(); - obj.set("owners", [user, user2]); + obj.set('owners', [user, user2]); await Parse.Object.saveAll([obj, obj2]); const schema = await config.database.loadSchema(); await schema.updateClass( - "AnObject", + 'AnObject', {}, - { find: {}, get: {}, readUserFields: ["owners"] } + { find: {}, get: {}, readUserFields: ['owners'] } ); - let q = new Parse.Query("AnObject"); + let q = new Parse.Query('AnObject'); let result = await q.find(); expect(result.length).toBe(0); - Parse.User.logIn("user3", "password"); - q = new Parse.Query("AnObject"); + Parse.User.logIn('user3', 'password'); + q = new Parse.Query('AnObject'); result = await q.find(); expect(result.length).toBe(0); - q = new Parse.Query("AnObject"); + q = new Parse.Query('AnObject'); try { await q.get(obj.id); - done.fail("User 3 should not get the obj1 object"); + done.fail('User 3 should not get the obj1 object'); } catch (err) { expect(err.code).toBe(Parse.Error.OBJECT_NOT_FOUND); - expect(err.message).toBe("Object not found."); + expect(err.message).toBe('Object not found.'); } - for (const owner of ["user1", "user2"]) { - await Parse.User.logIn(owner, "password"); + for (const owner of ['user1', 'user2']) { + await Parse.User.logIn(owner, 'password'); try { - const q = new Parse.Query("AnObject"); + const q = new Parse.Query('AnObject'); result = await q.find(); expect(result.length).toBe(1); } catch (err) { - done.fail("should not fail"); + done.fail('should not fail'); } } done(); }); - it_id("8a7d188c-b75c-4eac-90b6-9b0b11f873ae")(it)( - "should query on pointer permission enabled column", + it_id('8a7d188c-b75c-4eac-90b6-9b0b11f873ae')(it)( + 'should query on pointer permission enabled column', async done => { const config = Config.get(Parse.applicationId); const user = new Parse.User(); const user2 = new Parse.User(); const user3 = new Parse.User(); user.set({ - username: "user1", - password: "password", + username: 'user1', + password: 'password', }); user2.set({ - username: "user2", - password: "password", + username: 'user2', + password: 'password', }); user3.set({ - username: "user3", - password: "password", + username: 'user3', + password: 'password', }); - const obj = new Parse.Object("AnObject"); - const obj2 = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); + const obj2 = new Parse.Object('AnObject'); await user.signUp(); await user2.signUp(); await user3.signUp(); await Parse.User.logOut(); - obj.set("owners", [user, user2]); + obj.set('owners', [user, user2]); await Parse.Object.saveAll([obj, obj2]); const schema = await config.database.loadSchema(); await schema.updateClass( - "AnObject", + 'AnObject', {}, - { find: {}, get: {}, readUserFields: ["owners"] } + { find: {}, get: {}, readUserFields: ['owners'] } ); - for (const owner of ["user1", "user2"]) { - await Parse.User.logIn(owner, "password"); + for (const owner of ['user1', 'user2']) { + await Parse.User.logIn(owner, 'password'); try { - const q = new Parse.Query("AnObject"); - q.equalTo("owners", user3); + const q = new Parse.Query('AnObject'); + q.equalTo('owners', user3); const result = await q.find(); expect(result.length).toBe(0); } catch (err) { - done.fail("should not fail"); + done.fail('should not fail'); } } done(); } ); - it("should not query using arrays on pointer permission enabled column", async done => { + it('should not query using arrays on pointer permission enabled column', async done => { const config = Config.get(Parse.applicationId); const user = new Parse.User(); const user2 = new Parse.User(); const user3 = new Parse.User(); user.set({ - username: "user1", - password: "password", + username: 'user1', + password: 'password', }); user2.set({ - username: "user2", - password: "password", + username: 'user2', + password: 'password', }); user3.set({ - username: "user3", - password: "password", + username: 'user3', + password: 'password', }); - const obj = new Parse.Object("AnObject"); - const obj2 = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); + const obj2 = new Parse.Object('AnObject'); await user.signUp(); await user2.signUp(); await user3.signUp(); await Parse.User.logOut(); - obj.set("owners", [user, user2]); + obj.set('owners', [user, user2]); await Parse.Object.saveAll([obj, obj2]); const schema = await config.database.loadSchema(); await schema.updateClass( - "AnObject", + 'AnObject', {}, - { find: {}, get: {}, readUserFields: ["owners"] } + { find: {}, get: {}, readUserFields: ['owners'] } ); - for (const owner of ["user1", "user2"]) { + for (const owner of ['user1', 'user2']) { try { - await Parse.User.logIn(owner, "password"); + await Parse.User.logIn(owner, 'password'); // Since querying for arrays is not supported this should throw an error - const q = new Parse.Query("AnObject"); - q.equalTo("owners", [user3]); + const q = new Parse.Query('AnObject'); + q.equalTo('owners', [user3]); await q.find(); - done.fail("should fail"); + done.fail('should fail'); // eslint-disable-next-line no-empty } catch (error) {} } done(); }); - it("should not allow creating objects", async done => { + it('should not allow creating objects', async done => { const config = Config.get(Parse.applicationId); const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: "user1", - password: "password", + username: 'user1', + password: 'password', }); user2.set({ - username: "user2", - password: "password", + username: 'user2', + password: 'password', }); - const obj = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); await Parse.Object.saveAll([user, user2]); const schema = await config.database.loadSchema(); await schema.addClassIfNotExists( - "AnObject", - { owners: { type: "Array" } }, + 'AnObject', + { owners: { type: 'Array' } }, { create: {}, - writeUserFields: ["owners"], - readUserFields: ["owners"], + writeUserFields: ['owners'], + readUserFields: ['owners'], } ); - for (const owner of ["user1", "user2"]) { - await Parse.User.logIn(owner, "password"); + for (const owner of ['user1', 'user2']) { + await Parse.User.logIn(owner, 'password'); try { - obj.set("owners", [user, user2]); + obj.set('owners', [user, user2]); await obj.save(); - done.fail("should not succeed"); + done.fail('should not succeed'); } catch (err) { expect(err.code).toBe(119); } @@ -1369,62 +1369,62 @@ describe("Pointer Permissions", () => { done(); }); - it("should handle multiple writeUserFields", async done => { + it('should handle multiple writeUserFields', async done => { const config = Config.get(Parse.applicationId); const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: "user1", - password: "password", + username: 'user1', + password: 'password', }); user2.set({ - username: "user2", - password: "password", + username: 'user2', + password: 'password', }); - const obj = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); await Parse.Object.saveAll([user, user2]); - obj.set("owners", [user]); - obj.set("otherOwners", [user2]); + obj.set('owners', [user]); + obj.set('otherOwners', [user2]); await obj.save(); const schema = await config.database.loadSchema(); await schema.updateClass( - "AnObject", + 'AnObject', {}, - { find: { "*": true }, writeUserFields: ["owners", "otherOwners"] } + { find: { '*': true }, writeUserFields: ['owners', 'otherOwners'] } ); - await Parse.User.logIn("user1", "password"); - await obj.save({ hello: "fromUser1" }); - await Parse.User.logIn("user2", "password"); - await obj.save({ hello: "fromUser2" }); + await Parse.User.logIn('user1', 'password'); + await obj.save({ hello: 'fromUser1' }); + await Parse.User.logIn('user2', 'password'); + await obj.save({ hello: 'fromUser2' }); await Parse.User.logOut(); try { - const q = new Parse.Query("AnObject"); + const q = new Parse.Query('AnObject'); const result = await q.first(); - expect(result.get("hello")).toBe("fromUser2"); + expect(result.get('hello')).toBe('fromUser2'); done(); } catch (err) { - done.fail("should not fail"); + done.fail('should not fail'); } }); - it("should prevent creating pointer permission on missing field", async done => { + it('should prevent creating pointer permission on missing field', async done => { const config = Config.get(Parse.applicationId); const schema = await config.database.loadSchema(); try { await schema.addClassIfNotExists( - "AnObject", + 'AnObject', {}, { create: {}, - writeUserFields: ["owners"], - readUserFields: ["owners"], + writeUserFields: ['owners'], + readUserFields: ['owners'], } ); - done.fail("should not succeed"); + done.fail('should not succeed'); } catch (err) { expect(err.code).toBe(107); expect(err.message).toBe( @@ -1434,20 +1434,20 @@ describe("Pointer Permissions", () => { } }); - it("should prevent creating pointer permission on bad field (of wrong type)", async done => { + it('should prevent creating pointer permission on bad field (of wrong type)', async done => { const config = Config.get(Parse.applicationId); const schema = await config.database.loadSchema(); try { await schema.addClassIfNotExists( - "AnObject", - { owners: { type: "String" } }, + 'AnObject', + { owners: { type: 'String' } }, { create: {}, - writeUserFields: ["owners"], - readUserFields: ["owners"], + writeUserFields: ['owners'], + readUserFields: ['owners'], } ); - done.fail("should not succeed"); + done.fail('should not succeed'); } catch (err) { expect(err.code).toBe(107); expect(err.message).toBe( @@ -1457,24 +1457,24 @@ describe("Pointer Permissions", () => { } }); - it("should prevent creating pointer permission on bad field (non-existing)", async done => { + it('should prevent creating pointer permission on bad field (non-existing)', async done => { const config = Config.get(Parse.applicationId); - const object = new Parse.Object("AnObject"); - object.set("owners", "value"); + const object = new Parse.Object('AnObject'); + object.set('owners', 'value'); await object.save(); const schema = await config.database.loadSchema(); try { await schema.updateClass( - "AnObject", + 'AnObject', {}, { create: {}, - writeUserFields: ["owners"], - readUserFields: ["owners"], + writeUserFields: ['owners'], + readUserFields: ['owners'], } ); - done.fail("should not succeed"); + done.fail('should not succeed'); } catch (err) { expect(err.code).toBe(107); expect(err.message).toBe( @@ -1484,34 +1484,34 @@ describe("Pointer Permissions", () => { } }); - it("should work with arrays containing valid & invalid elements", async done => { + it('should work with arrays containing valid & invalid elements', async done => { /* Since there is no way to check the validity of objects in arrays before querying invalid elements in arrays should be ignored. */ const config = Config.get(Parse.applicationId); const user = new Parse.User(); const user2 = new Parse.User(); user.set({ - username: "user1", - password: "password", + username: 'user1', + password: 'password', }); user2.set({ - username: "user2", - password: "password", + username: 'user2', + password: 'password', }); - const obj = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); await Parse.Object.saveAll([user, user2]); - obj.set("owners", [user, "", -1, true, [], { invalid: -1 }]); + obj.set('owners', [user, '', -1, true, [], { invalid: -1 }]); await Parse.Object.saveAll([obj]); const schema = await config.database.loadSchema(); - await schema.updateClass("AnObject", {}, { readUserFields: ["owners"] }); + await schema.updateClass('AnObject', {}, { readUserFields: ['owners'] }); - await Parse.User.logIn("user1", "password"); + await Parse.User.logIn('user1', 'password'); try { - const q = new Parse.Query("AnObject"); + const q = new Parse.Query('AnObject'); const res = await q.find(); expect(res.length).toBe(1); expect(res[0].id).toBe(obj.id); @@ -1520,10 +1520,10 @@ describe("Pointer Permissions", () => { } await Parse.User.logOut(); - await Parse.User.logIn("user2", "password"); + await Parse.User.logIn('user2', 'password'); try { - const q = new Parse.Query("AnObject"); + const q = new Parse.Query('AnObject'); const res = await q.find(); expect(res.length).toBe(0); done(); @@ -1532,7 +1532,7 @@ describe("Pointer Permissions", () => { } }); - it("tests CLP / Pointer Perms / ACL write (PP Locked)", async done => { + it('tests CLP / Pointer Perms / ACL write (PP Locked)', async done => { /* tests: CLP: update closed ({}) @@ -1546,18 +1546,18 @@ describe("Pointer Permissions", () => { const user2 = new Parse.User(); const user3 = new Parse.User(); user.set({ - username: "user1", - password: "password", + username: 'user1', + password: 'password', }); user2.set({ - username: "user2", - password: "password", + username: 'user2', + password: 'password', }); user3.set({ - username: "user3", - password: "password", + username: 'user3', + password: 'password', }); - const obj = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); await Parse.Object.saveAll([user, user2, user3]); @@ -1565,29 +1565,29 @@ describe("Pointer Permissions", () => { ACL.setReadAccess(user, true); ACL.setWriteAccess(user, true); obj.setACL(ACL); - obj.set("owners", [user2, user3]); + obj.set('owners', [user2, user3]); await obj.save(); const schema = await config.database.loadSchema(); // Lock the update, and let only owners write await schema.updateClass( - "AnObject", + 'AnObject', {}, - { update: {}, writeUserFields: ["owners"] } + { update: {}, writeUserFields: ['owners'] } ); - await Parse.User.logIn("user1", "password"); + await Parse.User.logIn('user1', 'password'); try { // user1 has ACL read/write but should be blocked by PP - await obj.save({ key: "value" }); - done.fail("Should not succeed saving"); + await obj.save({ key: 'value' }); + done.fail('Should not succeed saving'); } catch (err) { expect(err.code).toBe(Parse.Error.OBJECT_NOT_FOUND); done(); } }); - it("tests CLP / Pointer Perms / ACL write (ACL Locked)", async done => { + it('tests CLP / Pointer Perms / ACL write (ACL Locked)', async done => { /* tests: CLP: update closed ({}) @@ -1599,18 +1599,18 @@ describe("Pointer Permissions", () => { const user2 = new Parse.User(); const user3 = new Parse.User(); user.set({ - username: "user1", - password: "password", + username: 'user1', + password: 'password', }); user2.set({ - username: "user2", - password: "password", + username: 'user2', + password: 'password', }); user3.set({ - username: "user3", - password: "password", + username: 'user3', + password: 'password', }); - const obj = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); await Parse.Object.saveAll([user, user2, user3]); @@ -1618,22 +1618,22 @@ describe("Pointer Permissions", () => { ACL.setReadAccess(user, true); ACL.setWriteAccess(user, true); obj.setACL(ACL); - obj.set("owners", [user2, user3]); + obj.set('owners', [user2, user3]); await obj.save(); const schema = await config.database.loadSchema(); // Lock the update, and let only owners write await schema.updateClass( - "AnObject", + 'AnObject', {}, - { update: {}, writeUserFields: ["owners"] } + { update: {}, writeUserFields: ['owners'] } ); - for (const owner of ["user2", "user3"]) { - await Parse.User.logIn(owner, "password"); + for (const owner of ['user2', 'user3']) { + await Parse.User.logIn(owner, 'password'); try { - await obj.save({ key: "value" }); - done.fail("Should not succeed saving"); + await obj.save({ key: 'value' }); + done.fail('Should not succeed saving'); } catch (err) { expect(err.code).toBe(Parse.Error.OBJECT_NOT_FOUND); } @@ -1641,7 +1641,7 @@ describe("Pointer Permissions", () => { done(); }); - it("tests CLP / Pointer Perms / ACL write (ACL/PP OK)", async done => { + it('tests CLP / Pointer Perms / ACL write (ACL/PP OK)', async done => { /* tests: CLP: update closed ({}) @@ -1653,18 +1653,18 @@ describe("Pointer Permissions", () => { const user2 = new Parse.User(); const user3 = new Parse.User(); user.set({ - username: "user1", - password: "password", + username: 'user1', + password: 'password', }); user2.set({ - username: "user2", - password: "password", + username: 'user2', + password: 'password', }); user3.set({ - username: "user3", - password: "password", + username: 'user3', + password: 'password', }); - const obj = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); await Parse.Object.saveAll([user, user2, user3]); const ACL = new Parse.ACL(); @@ -1672,30 +1672,30 @@ describe("Pointer Permissions", () => { ACL.setWriteAccess(user2, true); ACL.setWriteAccess(user3, true); obj.setACL(ACL); - obj.set("owners", [user2, user3]); + obj.set('owners', [user2, user3]); await obj.save(); const schema = await config.database.loadSchema(); // Lock the update, and let only owners write await schema.updateClass( - "AnObject", + 'AnObject', {}, - { update: {}, writeUserFields: ["owners"] } + { update: {}, writeUserFields: ['owners'] } ); - for (const owner of ["user2", "user3"]) { - await Parse.User.logIn(owner, "password"); + for (const owner of ['user2', 'user3']) { + await Parse.User.logIn(owner, 'password'); try { - const objectAgain = await obj.save({ key: "value" }); - expect(objectAgain.get("key")).toBe("value"); + const objectAgain = await obj.save({ key: 'value' }); + expect(objectAgain.get('key')).toBe('value'); } catch (err) { - done.fail("Should not fail saving"); + done.fail('Should not fail saving'); } } done(); }); - it("tests CLP / Pointer Perms / ACL read (PP locked)", async done => { + it('tests CLP / Pointer Perms / ACL read (PP locked)', async done => { /* tests: CLP: find/get open ({}) @@ -1709,18 +1709,18 @@ describe("Pointer Permissions", () => { const user2 = new Parse.User(); const user3 = new Parse.User(); user.set({ - username: "user1", - password: "password", + username: 'user1', + password: 'password', }); user2.set({ - username: "user2", - password: "password", + username: 'user2', + password: 'password', }); user3.set({ - username: "user3", - password: "password", + username: 'user3', + password: 'password', }); - const obj = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); await Parse.Object.saveAll([user, user2, user3]); @@ -1728,22 +1728,22 @@ describe("Pointer Permissions", () => { ACL.setReadAccess(user, true); ACL.setWriteAccess(user, true); obj.setACL(ACL); - obj.set("owners", [user2, user3]); + obj.set('owners', [user2, user3]); await obj.save(); const schema = await config.database.loadSchema(); // Lock reading, and let only owners read await schema.updateClass( - "AnObject", + 'AnObject', {}, - { find: {}, get: {}, readUserFields: ["owners"] } + { find: {}, get: {}, readUserFields: ['owners'] } ); - await Parse.User.logIn("user1", "password"); + await Parse.User.logIn('user1', 'password'); try { // user1 has ACL read/write but should be blocked await obj.fetch(); - done.fail("Should not succeed fetching"); + done.fail('Should not succeed fetching'); } catch (err) { expect(err.code).toBe(Parse.Error.OBJECT_NOT_FOUND); done(); @@ -1751,7 +1751,7 @@ describe("Pointer Permissions", () => { done(); }); - it("tests CLP / Pointer Perms / ACL read (PP/ACL OK)", async done => { + it('tests CLP / Pointer Perms / ACL read (PP/ACL OK)', async done => { /* tests: CLP: find/get open ({"*": true}) @@ -1763,18 +1763,18 @@ describe("Pointer Permissions", () => { const user2 = new Parse.User(); const user3 = new Parse.User(); user.set({ - username: "user1", - password: "password", + username: 'user1', + password: 'password', }); user2.set({ - username: "user2", - password: "password", + username: 'user2', + password: 'password', }); user3.set({ - username: "user3", - password: "password", + username: 'user3', + password: 'password', }); - const obj = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); await Parse.Object.saveAll([user, user2, user3]); @@ -1786,34 +1786,34 @@ describe("Pointer Permissions", () => { ACL.setReadAccess(user3, true); ACL.setWriteAccess(user3, true); obj.setACL(ACL); - obj.set("owners", [user2, user3]); + obj.set('owners', [user2, user3]); await obj.save(); const schema = await config.database.loadSchema(); // Allow public and owners read await schema.updateClass( - "AnObject", + 'AnObject', {}, { - find: { "*": true }, - get: { "*": true }, - readUserFields: ["owners"], + find: { '*': true }, + get: { '*': true }, + readUserFields: ['owners'], } ); - for (const owner of ["user2", "user3"]) { - await Parse.User.logIn(owner, "password"); + for (const owner of ['user2', 'user3']) { + await Parse.User.logIn(owner, 'password'); try { const objectAgain = await obj.fetch(); expect(objectAgain.id).toBe(obj.id); } catch (err) { - done.fail("Should not fail fetching"); + done.fail('Should not fail fetching'); } } done(); }); - it("tests CLP / Pointer Perms / ACL read (ACL locked)", async done => { + it('tests CLP / Pointer Perms / ACL read (ACL locked)', async done => { /* tests: CLP: find/get open ({"*": true}) @@ -1825,44 +1825,44 @@ describe("Pointer Permissions", () => { const user2 = new Parse.User(); const user3 = new Parse.User(); user.set({ - username: "user1", - password: "password", + username: 'user1', + password: 'password', }); user2.set({ - username: "user2", - password: "password", + username: 'user2', + password: 'password', }); user3.set({ - username: "user3", - password: "password", + username: 'user3', + password: 'password', }); - const obj = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); await Parse.Object.saveAll([user, user2, user3]); const ACL = new Parse.ACL(); ACL.setReadAccess(user, true); ACL.setWriteAccess(user, true); obj.setACL(ACL); - obj.set("owners", [user2, user3]); + obj.set('owners', [user2, user3]); await obj.save(); const schema = await config.database.loadSchema(); // Allow public and owners read await schema.updateClass( - "AnObject", + 'AnObject', {}, { - find: { "*": true }, - get: { "*": true }, - readUserFields: ["owners"], + find: { '*': true }, + get: { '*': true }, + readUserFields: ['owners'], } ); - for (const owner of ["user2", "user3"]) { - await Parse.User.logIn(owner, "password"); + for (const owner of ['user2', 'user3']) { + await Parse.User.logIn(owner, 'password'); try { await obj.fetch(); - done.fail("Should not succeed fetching"); + done.fail('Should not succeed fetching'); } catch (err) { expect(err.code).toBe(Parse.Error.OBJECT_NOT_FOUND); } @@ -1870,21 +1870,21 @@ describe("Pointer Permissions", () => { done(); }); - it("should let master key find objects", async done => { + it('should let master key find objects', async done => { const config = Config.get(Parse.applicationId); - const object = new Parse.Object("AnObject"); - object.set("hello", "world"); + const object = new Parse.Object('AnObject'); + object.set('hello', 'world'); await object.save(); const schema = await config.database.loadSchema(); // Lock the find/get, and let only owners read await schema.updateClass( - "AnObject", - { owners: { type: "Array" } }, - { find: {}, get: {}, readUserFields: ["owners"] } + 'AnObject', + { owners: { type: 'Array' } }, + { find: {}, get: {}, readUserFields: ['owners'] } ); - const q = new Parse.Query("AnObject"); + const q = new Parse.Query('AnObject'); const objects = await q.find(); expect(objects.length).toBe(0); @@ -1893,25 +1893,25 @@ describe("Pointer Permissions", () => { expect(objects.length).toBe(1); done(); } catch (err) { - done.fail("master key should find the object"); + done.fail('master key should find the object'); } }); - it("should let master key get objects", async done => { + it('should let master key get objects', async done => { const config = Config.get(Parse.applicationId); - const object = new Parse.Object("AnObject"); - object.set("hello", "world"); + const object = new Parse.Object('AnObject'); + object.set('hello', 'world'); await object.save(); const schema = await config.database.loadSchema(); // Lock the find/get, and let only owners read await schema.updateClass( - "AnObject", - { owners: { type: "Array" } }, - { find: {}, get: {}, readUserFields: ["owners"] } + 'AnObject', + { owners: { type: 'Array' } }, + { find: {}, get: {}, readUserFields: ['owners'] } ); - const q = new Parse.Query("AnObject"); + const q = new Parse.Query('AnObject'); try { await q.get(object.id); done.fail(); @@ -1925,26 +1925,26 @@ describe("Pointer Permissions", () => { expect(objectAgain.id).toBe(object.id); done(); } catch (err) { - done.fail("master key should get the object"); + done.fail('master key should get the object'); } }); - it("should let master key update objects", async done => { + it('should let master key update objects', async done => { const config = Config.get(Parse.applicationId); - const object = new Parse.Object("AnObject"); - object.set("hello", "world"); + const object = new Parse.Object('AnObject'); + object.set('hello', 'world'); await object.save(); const schema = await config.database.loadSchema(); // Lock the update, and let only owners write await schema.updateClass( - "AnObject", - { owners: { type: "Array" } }, - { update: {}, writeUserFields: ["owners"] } + 'AnObject', + { owners: { type: 'Array' } }, + { update: {}, writeUserFields: ['owners'] } ); try { - await object.save({ hello: "bar" }); + await object.save({ hello: 'bar' }); done.fail(); } catch (err) { expect(err.code).toBe(Parse.Error.OBJECT_NOT_FOUND); @@ -1952,29 +1952,29 @@ describe("Pointer Permissions", () => { try { const objectAgain = await object.save( - { hello: "baz" }, + { hello: 'baz' }, { useMasterKey: true } ); - expect(objectAgain.get("hello")).toBe("baz"); + expect(objectAgain.get('hello')).toBe('baz'); done(); } catch (err) { - done.fail("master key should save the object"); + done.fail('master key should save the object'); } }); - it("should let master key delete objects", async done => { + it('should let master key delete objects', async done => { const config = Config.get(Parse.applicationId); - const object = new Parse.Object("AnObject"); - object.set("hello", "world"); + const object = new Parse.Object('AnObject'); + object.set('hello', 'world'); await object.save(); const schema = await config.database.loadSchema(); // Lock the delete, and let only owners write await schema.updateClass( - "AnObject", - { owners: { type: "Array" } }, - { delete: {}, writeUserFields: ["owners"] } + 'AnObject', + { owners: { type: 'Array' } }, + { delete: {}, writeUserFields: ['owners'] } ); try { @@ -1987,19 +1987,19 @@ describe("Pointer Permissions", () => { await object.destroy({ useMasterKey: true }); done(); } catch (err) { - done.fail("master key should destroy the object"); + done.fail('master key should destroy the object'); } }); - it("should fail with invalid pointer perms (not array)", async done => { + it('should fail with invalid pointer perms (not array)', async done => { const config = Config.get(Parse.applicationId); const schema = await config.database.loadSchema(); try { // Lock the delete, and let only owners write await schema.addClassIfNotExists( - "AnObject", - { owners: { type: "Array" } }, - { delete: {}, writeUserFields: "owners" } + 'AnObject', + { owners: { type: 'Array' } }, + { delete: {}, writeUserFields: 'owners' } ); } catch (err) { expect(err.code).toBe(Parse.Error.INVALID_JSON); @@ -2007,15 +2007,15 @@ describe("Pointer Permissions", () => { } }); - it("should fail with invalid pointer perms (non-existing field)", async done => { + it('should fail with invalid pointer perms (non-existing field)', async done => { const config = Config.get(Parse.applicationId); const schema = await config.database.loadSchema(); try { // Lock the delete, and let only owners write await schema.addClassIfNotExists( - "AnObject", - { owners: { type: "Array" } }, - { delete: {}, writeUserFields: ["owners", "invalid"] } + 'AnObject', + { owners: { type: 'Array' } }, + { delete: {}, writeUserFields: ['owners', 'invalid'] } ); } catch (err) { expect(err.code).toBe(Parse.Error.INVALID_JSON); @@ -2024,8 +2024,8 @@ describe("Pointer Permissions", () => { }); }); - describe("Granular ", () => { - const className = "AnObject"; + describe('Granular ', () => { + const className = 'AnObject'; const actionGet = id => new Parse.Query(className).get(id); const actionFind = () => new Parse.Query(className).find(); @@ -2034,17 +2034,17 @@ describe("Pointer Permissions", () => { const actionUpdate = obj => obj.save({ revision: 2 }); const actionDelete = obj => obj.destroy(); const actionAddFieldOnCreate = () => - new Parse.Object(className, { ["extra" + Date.now()]: "field" }).save(); + new Parse.Object(className, { ['extra' + Date.now()]: 'field' }).save(); const actionAddFieldOnUpdate = obj => - obj.save({ ["another" + Date.now()]: "field" }); + obj.save({ ['another' + Date.now()]: 'field' }); const OBJECT_NOT_FOUND = new Parse.Error( Parse.Error.OBJECT_NOT_FOUND, - "Object not found." + 'Object not found.' ); - const PERMISSION_DENIED = jasmine.stringMatching("Permission denied"); + const PERMISSION_DENIED = jasmine.stringMatching('Permission denied'); - async function createUser(username, password = "password") { + async function createUser(username, password = 'password') { const user = new Parse.User({ username: username + Date.now(), password, @@ -2056,7 +2056,7 @@ describe("Pointer Permissions", () => { } async function logIn(userObject) { - return await Parse.User.logIn(userObject.getUsername(), "password"); + return await Parse.User.logIn(userObject.getUsername(), 'password'); } async function updateCLP(clp) { @@ -2066,7 +2066,7 @@ describe("Pointer Permissions", () => { await schemaController.updateClass(className, {}, clp); } - describe("on single-pointer fields", () => { + describe('on single-pointer fields', () => { /** owns: **obj1** */ let user1; @@ -2083,8 +2083,8 @@ describe("Pointer Permissions", () => { await Config.get(Parse.applicationId).schemaCache.clear(); [user1, user2] = await Promise.all([ - createUser("user1"), - createUser("user2"), + createUser('user1'), + createUser('user2'), ]); obj1 = new Parse.Object(className, { @@ -2106,11 +2106,11 @@ describe("Pointer Permissions", () => { await initialize(); }); - describe("get action", () => { - it("should be allowed", async done => { + describe('get action', () => { + it('should be allowed', async done => { await updateCLP({ get: { - pointerFields: ["owner"], + pointerFields: ['owner'], }, }); @@ -2121,12 +2121,12 @@ describe("Pointer Permissions", () => { done(); }); - it_id("9ba681d5-59f5-4996-b36d-6647d23e6a44")(it)( - "should fail for user not listed", + it_id('9ba681d5-59f5-4996-b36d-6647d23e6a44')(it)( + 'should fail for user not listed', async done => { await updateCLP({ get: { - pointerFields: ["owner"], + pointerFields: ['owner'], }, }); @@ -2139,10 +2139,10 @@ describe("Pointer Permissions", () => { } ); - it("should not allow other actions", async done => { + it('should not allow other actions', async done => { await updateCLP({ get: { - pointerFields: ["owner"], + pointerFields: ['owner'], }, }); @@ -2164,11 +2164,11 @@ describe("Pointer Permissions", () => { }); }); - describe("find action", () => { - it("should be allowed", async done => { + describe('find action', () => { + it('should be allowed', async done => { await updateCLP({ find: { - pointerFields: ["owner"], + pointerFields: ['owner'], }, }); @@ -2178,10 +2178,10 @@ describe("Pointer Permissions", () => { done(); }); - it("should be limited to objects where user is listed in field", async done => { + it('should be limited to objects where user is listed in field', async done => { await updateCLP({ find: { - pointerFields: ["owner"], + pointerFields: ['owner'], }, }); @@ -2192,10 +2192,10 @@ describe("Pointer Permissions", () => { done(); }); - it("should not allow other actions", async done => { + it('should not allow other actions', async done => { await updateCLP({ find: { - pointerFields: ["owner"], + pointerFields: ['owner'], }, }); @@ -2217,11 +2217,11 @@ describe("Pointer Permissions", () => { }); }); - describe("count action", () => { - it("should be allowed", async done => { + describe('count action', () => { + it('should be allowed', async done => { await updateCLP({ count: { - pointerFields: ["owner"], + pointerFields: ['owner'], }, }); @@ -2232,14 +2232,14 @@ describe("Pointer Permissions", () => { done(); }); - it("should be limited to objects where user is listed in field", async done => { + it('should be limited to objects where user is listed in field', async done => { await updateCLP({ count: { - pointerFields: ["owner"], + pointerFields: ['owner'], }, }); - const user3 = await createUser("user3"); + const user3 = await createUser('user3'); await logIn(user3); const p = await actionCount(); @@ -2248,10 +2248,10 @@ describe("Pointer Permissions", () => { done(); }); - it("should not allow other actions", async done => { + it('should not allow other actions', async done => { await updateCLP({ count: { - pointerFields: ["owner"], + pointerFields: ['owner'], }, }); @@ -2273,11 +2273,11 @@ describe("Pointer Permissions", () => { }); }); - describe("update action", () => { - it("should be allowed", async done => { + describe('update action', () => { + it('should be allowed', async done => { await updateCLP({ update: { - pointerFields: ["owner"], + pointerFields: ['owner'], }, }); @@ -2286,12 +2286,12 @@ describe("Pointer Permissions", () => { done(); }); - it_id("bcdb158d-c0b6-45e3-84ab-a3636f7cb470")(it)( - "should fail for user not listed", + it_id('bcdb158d-c0b6-45e3-84ab-a3636f7cb470')(it)( + 'should fail for user not listed', async done => { await updateCLP({ update: { - pointerFields: ["owner"], + pointerFields: ['owner'], }, }); @@ -2304,10 +2304,10 @@ describe("Pointer Permissions", () => { } ); - it("should not allow other actions", async done => { + it('should not allow other actions', async done => { await updateCLP({ update: { - pointerFields: ["owner"], + pointerFields: ['owner'], }, }); @@ -2329,11 +2329,11 @@ describe("Pointer Permissions", () => { }); }); - describe("delete action", () => { - it("should be allowed", async done => { + describe('delete action', () => { + it('should be allowed', async done => { await updateCLP({ delete: { - pointerFields: ["owner"], + pointerFields: ['owner'], }, }); @@ -2343,12 +2343,12 @@ describe("Pointer Permissions", () => { done(); }); - it_id("70aa3853-6e26-4c38-a927-2ddb24ced7d4")(it)( - "should fail for user not listed", + it_id('70aa3853-6e26-4c38-a927-2ddb24ced7d4')(it)( + 'should fail for user not listed', async done => { await updateCLP({ delete: { - pointerFields: ["owner"], + pointerFields: ['owner'], }, }); @@ -2361,10 +2361,10 @@ describe("Pointer Permissions", () => { } ); - it("should not allow other actions", async done => { + it('should not allow other actions', async done => { await updateCLP({ delete: { - pointerFields: ["owner"], + pointerFields: ['owner'], }, }); @@ -2386,13 +2386,13 @@ describe("Pointer Permissions", () => { }); }); - describe("create action", () => { + describe('create action', () => { // For Pointer permissions create is different from other operations // since there's no object holding the pointer before created - it("should be denied (writelock) when no other permissions on class", async done => { + it('should be denied (writelock) when no other permissions on class', async done => { await updateCLP({ create: { - pointerFields: ["owner"], + pointerFields: ['owner'], }, }); @@ -2402,15 +2402,15 @@ describe("Pointer Permissions", () => { }); }); - describe("addField action", () => { - xit("should have no effect when creating object (and allowed by explicit userid permission)", async done => { + describe('addField action', () => { + xit('should have no effect when creating object (and allowed by explicit userid permission)', async done => { await updateCLP({ create: { - "*": true, + '*': true, }, addField: { [user1.id]: true, - pointerFields: ["owner"], + pointerFields: ['owner'], }, }); @@ -2420,13 +2420,13 @@ describe("Pointer Permissions", () => { done(); }); - xit("should be denied when creating object (and no explicit permission)", async done => { + xit('should be denied when creating object (and no explicit permission)', async done => { await updateCLP({ create: { - "*": true, + '*': true, }, addField: { - pointerFields: ["owner"], + pointerFields: ['owner'], }, }); @@ -2434,7 +2434,7 @@ describe("Pointer Permissions", () => { const newObject = new Parse.Object(className, { owner: user1, - extra: "field", + extra: 'field', }); await expectAsync(newObject.save()).toBeRejectedWith( PERMISSION_DENIED @@ -2442,13 +2442,13 @@ describe("Pointer Permissions", () => { done(); }); - it("should be allowed when updating object", async done => { + it('should be allowed when updating object', async done => { await updateCLP({ update: { - "*": true, + '*': true, }, addField: { - pointerFields: ["owner"], + pointerFields: ['owner'], }, }); @@ -2459,13 +2459,13 @@ describe("Pointer Permissions", () => { done(); }); - it("should be denied when updating object for user without addField permission", async done => { + it('should be denied when updating object for user without addField permission', async done => { await updateCLP({ update: { - "*": true, + '*': true, }, addField: { - pointerFields: ["owner"], + pointerFields: ['owner'], }, }); @@ -2480,7 +2480,7 @@ describe("Pointer Permissions", () => { }); }); - describe("on array of pointers", () => { + describe('on array of pointers', () => { /** * owns: **obj1** * @@ -2527,9 +2527,9 @@ describe("Pointer Permissions", () => { await Config.get(Parse.applicationId).schemaCache.clear(); [user1, user2, user3] = await Promise.all([ - createUser("user1"), - createUser("user2"), - createUser("user3"), + createUser('user1'), + createUser('user2'), + createUser('user3'), ]); obj1 = new Parse.Object(className); @@ -2570,11 +2570,11 @@ describe("Pointer Permissions", () => { await initialize(); }); - describe("get action", () => { - it("should be allowed (1 user in array)", async done => { + describe('get action', () => { + it('should be allowed (1 user in array)', async done => { await updateCLP({ get: { - pointerFields: ["owners"], + pointerFields: ['owners'], }, }); @@ -2585,10 +2585,10 @@ describe("Pointer Permissions", () => { done(); }); - it("should be allowed (multiple users in array)", async done => { + it('should be allowed (multiple users in array)', async done => { await updateCLP({ get: { - pointerFields: ["moderators"], + pointerFields: ['moderators'], }, }); @@ -2599,12 +2599,12 @@ describe("Pointer Permissions", () => { done(); }); - it_id("84a42339-c7b5-4735-a431-57b46535b073")(it)( - "should fail for user not listed", + it_id('84a42339-c7b5-4735-a431-57b46535b073')(it)( + 'should fail for user not listed', async done => { await updateCLP({ get: { - pointerFields: ["moderators"], + pointerFields: ['moderators'], }, }); @@ -2617,10 +2617,10 @@ describe("Pointer Permissions", () => { } ); - it("should not allow other actions", async done => { + it('should not allow other actions', async done => { await updateCLP({ get: { - pointerFields: ["owners"], + pointerFields: ['owners'], }, }); @@ -2643,11 +2643,11 @@ describe("Pointer Permissions", () => { }); }); - describe("find action", () => { - it("should be allowed (1 user in array)", async done => { + describe('find action', () => { + it('should be allowed (1 user in array)', async done => { await updateCLP({ find: { - pointerFields: ["owners"], + pointerFields: ['owners'], }, }); @@ -2658,10 +2658,10 @@ describe("Pointer Permissions", () => { done(); }); - it("should be allowed (multiple users in array)", async done => { + it('should be allowed (multiple users in array)', async done => { await updateCLP({ find: { - pointerFields: ["moderators"], + pointerFields: ['moderators'], }, }); @@ -2672,10 +2672,10 @@ describe("Pointer Permissions", () => { done(); }); - it("should be limited to objects where user is listed in field", async done => { + it('should be limited to objects where user is listed in field', async done => { await updateCLP({ find: { - pointerFields: ["moderators"], + pointerFields: ['moderators'], }, }); @@ -2686,10 +2686,10 @@ describe("Pointer Permissions", () => { done(); }); - it("should not allow other actions", async done => { + it('should not allow other actions', async done => { await updateCLP({ find: { - pointerFields: ["moderators"], + pointerFields: ['moderators'], }, }); @@ -2712,16 +2712,16 @@ describe("Pointer Permissions", () => { }); }); - describe("count action", () => { + describe('count action', () => { beforeEach(async () => { await updateCLP({ count: { - pointerFields: ["moderators"], + pointerFields: ['moderators'], }, }); }); - it("should be allowed", async done => { + it('should be allowed', async done => { await logIn(user1); const count = await actionCount(); @@ -2729,7 +2729,7 @@ describe("Pointer Permissions", () => { done(); }); - it("should be limited to objects where user is listed in field", async done => { + it('should be limited to objects where user is listed in field', async done => { await logIn(user2); const count = await actionCount(); @@ -2738,7 +2738,7 @@ describe("Pointer Permissions", () => { done(); }); - it("should not allow other actions", async done => { + it('should not allow other actions', async done => { await logIn(user1); await Promise.all( @@ -2758,11 +2758,11 @@ describe("Pointer Permissions", () => { }); }); - describe("update action", () => { - it("should be allowed (1 user in array)", async done => { + describe('update action', () => { + it('should be allowed (1 user in array)', async done => { await updateCLP({ update: { - pointerFields: ["owners"], + pointerFields: ['owners'], }, }); @@ -2772,12 +2772,12 @@ describe("Pointer Permissions", () => { done(); }); - it_id("2b19234a-a471-48b4-bd1a-27bd286d066f")(it)( - "should be allowed (multiple users in array)", + it_id('2b19234a-a471-48b4-bd1a-27bd286d066f')(it)( + 'should be allowed (multiple users in array)', async done => { await updateCLP({ update: { - pointerFields: ["moderators"], + pointerFields: ['moderators'], }, }); @@ -2788,12 +2788,12 @@ describe("Pointer Permissions", () => { } ); - it_id("1abb9f4a-fb24-48c7-8025-3001d6cf8737")(it)( - "should fail for user not listed", + it_id('1abb9f4a-fb24-48c7-8025-3001d6cf8737')(it)( + 'should fail for user not listed', async done => { await updateCLP({ update: { - pointerFields: ["moderators"], + pointerFields: ['moderators'], }, }); @@ -2806,10 +2806,10 @@ describe("Pointer Permissions", () => { } ); - it("should not allow other actions", async done => { + it('should not allow other actions', async done => { await updateCLP({ update: { - pointerFields: ["moderators"], + pointerFields: ['moderators'], }, }); @@ -2832,11 +2832,11 @@ describe("Pointer Permissions", () => { }); }); - describe("delete action", () => { - it("should be allowed (1 user in array)", async done => { + describe('delete action', () => { + it('should be allowed (1 user in array)', async done => { await updateCLP({ delete: { - pointerFields: ["owners"], + pointerFields: ['owners'], }, }); @@ -2846,10 +2846,10 @@ describe("Pointer Permissions", () => { done(); }); - it("should be allowed (multiple users in array)", async done => { + it('should be allowed (multiple users in array)', async done => { await updateCLP({ delete: { - pointerFields: ["moderators"], + pointerFields: ['moderators'], }, }); @@ -2859,12 +2859,12 @@ describe("Pointer Permissions", () => { done(); }); - it_id("3175a0e3-e51e-4b84-a2e6-50bbcc582123")(it)( - "should fail for user not listed", + it_id('3175a0e3-e51e-4b84-a2e6-50bbcc582123')(it)( + 'should fail for user not listed', async done => { await updateCLP({ delete: { - pointerFields: ["owners"], + pointerFields: ['owners'], }, }); @@ -2877,10 +2877,10 @@ describe("Pointer Permissions", () => { } ); - it("should not allow other actions", async done => { + it('should not allow other actions', async done => { await updateCLP({ delete: { - pointerFields: ["moderators"], + pointerFields: ['moderators'], }, }); @@ -2903,13 +2903,13 @@ describe("Pointer Permissions", () => { }); }); - describe("create action", () => { + describe('create action', () => { /* For Pointer permissions 'create' is different from other operations since there's no object holding the pointer before created */ - it("should be denied (writelock) when no other permissions on class", async done => { + it('should be denied (writelock) when no other permissions on class', async done => { await updateCLP({ create: { - pointerFields: ["moderators"], + pointerFields: ['moderators'], }, }); @@ -2919,15 +2919,15 @@ describe("Pointer Permissions", () => { }); }); - describe("addField action", () => { - it("should have no effect on create (allowed by explicit userid)", async done => { + describe('addField action', () => { + it('should have no effect on create (allowed by explicit userid)', async done => { await updateCLP({ create: { - "*": true, + '*': true, }, addField: { [user1.id]: true, - pointerFields: ["moderators"], + pointerFields: ['moderators'], }, }); @@ -2937,13 +2937,13 @@ describe("Pointer Permissions", () => { done(); }); - it("should be denied when creating object (and no explicit permission)", async done => { + it('should be denied when creating object (and no explicit permission)', async done => { await updateCLP({ create: { - "*": true, + '*': true, }, addField: { - pointerFields: ["moderators"], + pointerFields: ['moderators'], }, }); @@ -2951,7 +2951,7 @@ describe("Pointer Permissions", () => { const newObject = new Parse.Object(className, { moderators: user1, - extra: "field", + extra: 'field', }); await expectAsync(newObject.save()).toBeRejectedWith( PERMISSION_DENIED @@ -2959,13 +2959,13 @@ describe("Pointer Permissions", () => { done(); }); - it("should be allowed when updating object", async done => { + it('should be allowed when updating object', async done => { await updateCLP({ update: { - "*": true, + '*': true, }, addField: { - pointerFields: ["moderators"], + pointerFields: ['moderators'], }, }); @@ -2976,15 +2976,15 @@ describe("Pointer Permissions", () => { done(); }); - it_id("51e896e9-73b3-404f-b5ff-bdb99005a9f7")(it)( - "should be restricted when updating object without addField permission", + it_id('51e896e9-73b3-404f-b5ff-bdb99005a9f7')(it)( + 'should be restricted when updating object without addField permission', async done => { await updateCLP({ update: { - "*": true, + '*': true, }, addField: { - pointerFields: ["moderators"], + pointerFields: ['moderators'], }, }); @@ -3000,7 +3000,7 @@ describe("Pointer Permissions", () => { }); }); - describe("combined with grouped", () => { + describe('combined with grouped', () => { /** * owns: **obj1** * @@ -3029,8 +3029,8 @@ describe("Pointer Permissions", () => { await Config.get(Parse.applicationId).schemaCache.clear(); [user1, user2] = await Promise.all([ - createUser("user1"), - createUser("user2"), + createUser('user1'), + createUser('user2'), ]); // User1 owns object1 @@ -3056,14 +3056,14 @@ describe("Pointer Permissions", () => { await initialize(); }); - it_id("b43db366-8cce-4a11-9cf2-eeee9603d40b")(it)( - "should not limit the scope of grouped read permissions", + it_id('b43db366-8cce-4a11-9cf2-eeee9603d40b')(it)( + 'should not limit the scope of grouped read permissions', async done => { await updateCLP({ get: { - pointerFields: ["owner"], + pointerFields: ['owner'], }, - readUserFields: ["moderators"], + readUserFields: ['moderators'], }); await logIn(user2); @@ -3080,14 +3080,14 @@ describe("Pointer Permissions", () => { } ); - it_id("bbb1686d-0e2a-4365-8b64-b5faa3e7b9cf")(it)( - "should not limit the scope of grouped write permissions", + it_id('bbb1686d-0e2a-4365-8b64-b5faa3e7b9cf')(it)( + 'should not limit the scope of grouped write permissions', async done => { await updateCLP({ update: { - pointerFields: ["owner"], + pointerFields: ['owner'], }, - writeUserFields: ["moderators"], + writeUserFields: ['moderators'], }); await logIn(user2); @@ -3101,12 +3101,12 @@ describe("Pointer Permissions", () => { } ); - it("should not inherit scope of grouped read permissions from another field", async done => { + it('should not inherit scope of grouped read permissions from another field', async done => { await updateCLP({ get: { - pointerFields: ["owner"], + pointerFields: ['owner'], }, - readUserFields: ["moderators"], + readUserFields: ['moderators'], }); await logIn(user1); @@ -3120,12 +3120,12 @@ describe("Pointer Permissions", () => { done(); }); - it("should not inherit scope of grouped write permissions from another field", async done => { + it('should not inherit scope of grouped write permissions from another field', async done => { await updateCLP({ update: { - pointerFields: ["moderators"], + pointerFields: ['moderators'], }, - writeUserFields: ["owner"], + writeUserFields: ['owner'], }); await logIn(user1); @@ -3138,7 +3138,7 @@ describe("Pointer Permissions", () => { }); }); - describe("using pointer-fields and queries with keys projection", () => { + describe('using pointer-fields and queries with keys projection', () => { let user1; /** * owner: user1 @@ -3153,14 +3153,14 @@ describe("Pointer Permissions", () => { async function initialize() { await Config.get(Parse.applicationId).schemaCache.clear(); - user1 = await createUser("user1"); + user1 = await createUser('user1'); user1 = await logIn(user1); obj = new Parse.Object(className); - obj.set("owner", user1); - obj.set("field", "field"); - obj.set("test", "test"); + obj.set('owner', user1); + obj.set('field', 'field'); + obj.set('test', 'test'); await Parse.Object.saveAll([obj], { useMasterKey: true }); @@ -3171,19 +3171,19 @@ describe("Pointer Permissions", () => { await initialize(); }); - it("should be enforced regardless of pointer-field being included in keys (select)", async done => { + it('should be enforced regardless of pointer-field being included in keys (select)', async done => { await updateCLP({ - get: { "*": true }, - find: { pointerFields: ["owner"] }, - update: { pointerFields: ["owner"] }, + get: { '*': true }, + find: { pointerFields: ['owner'] }, + update: { pointerFields: ['owner'] }, }); - const query = new Parse.Query("AnObject"); - query.select("field", "test"); + const query = new Parse.Query('AnObject'); + query.select('field', 'test'); const [object] = await query.find({ objectId: obj.id }); - expect(object.get("field")).toBe("field"); - expect(object.get("test")).toBe("test"); + expect(object.get('field')).toBe('field'); + expect(object.get('test')).toBe('test'); done(); }); }); diff --git a/spec/PostgresConfigParser.spec.js b/spec/PostgresConfigParser.spec.js index d9fceb5a97..58a2af87bd 100644 --- a/spec/PostgresConfigParser.spec.js +++ b/spec/PostgresConfigParser.spec.js @@ -1,14 +1,14 @@ -const parser = require("../lib/Adapters/Storage/Postgres/PostgresConfigParser"); -const fs = require("fs"); +const parser = require('../lib/Adapters/Storage/Postgres/PostgresConfigParser'); +const fs = require('fs'); const queryParamTests = { - "a=1&b=2": { a: "1", b: "2" }, - "a=abcd%20efgh&b=abcd%3Defgh": { a: "abcd efgh", b: "abcd=efgh" }, - "a=1&b&c=true": { a: "1", b: "", c: "true" }, + 'a=1&b=2': { a: '1', b: '2' }, + 'a=abcd%20efgh&b=abcd%3Defgh': { a: 'abcd efgh', b: 'abcd=efgh' }, + 'a=1&b&c=true': { a: '1', b: '', c: 'true' }, }; -describe("PostgresConfigParser.parseQueryParams", () => { - it("creates a map from a query string", () => { +describe('PostgresConfigParser.parseQueryParams', () => { + it('creates a map from a query string', () => { for (const key in queryParamTests) { const result = parser.parseQueryParams(key); @@ -23,16 +23,16 @@ describe("PostgresConfigParser.parseQueryParams", () => { }); }); -const baseURI = "postgres://username:password@localhost:5432/db-name"; -const testfile = fs.readFileSync("./Dockerfile").toString(); +const baseURI = 'postgres://username:password@localhost:5432/db-name'; +const testfile = fs.readFileSync('./Dockerfile').toString(); const dbOptionsTest = {}; dbOptionsTest[ `${baseURI}?ssl=true&binary=true&application_name=app_name&fallback_application_name=f_app_name&poolSize=12` ] = { ssl: true, binary: true, - application_name: "app_name", - fallback_application_name: "f_app_name", + application_name: 'app_name', + fallback_application_name: 'f_app_name', max: 12, }; dbOptionsTest[`${baseURI}?ssl=&binary=aa`] = { @@ -46,7 +46,7 @@ dbOptionsTest[ pfx: testfile, cert: testfile, key: testfile, - passphrase: "word", + passphrase: 'word', secureOptions: 20, }, binary: false, @@ -69,8 +69,8 @@ dbOptionsTest[ keepAlive: true, }; -describe("PostgresConfigParser.getDatabaseOptionsFromURI", () => { - it("creates a db options map from a query string", () => { +describe('PostgresConfigParser.getDatabaseOptionsFromURI', () => { + it('creates a db options map from a query string', () => { for (const key in dbOptionsTest) { const result = parser.getDatabaseOptionsFromURI(key); @@ -82,20 +82,20 @@ describe("PostgresConfigParser.getDatabaseOptionsFromURI", () => { } }); - it("sets the poolSize to 10 if the it is not a number", () => { + it('sets the poolSize to 10 if the it is not a number', () => { const result = parser.getDatabaseOptionsFromURI(`${baseURI}?poolSize=sdf`); expect(result.max).toEqual(10); }); - it("sets the max to 10 if the it is not a number", () => { + it('sets the max to 10 if the it is not a number', () => { const result = parser.getDatabaseOptionsFromURI(`${baseURI}?&max=sdf`); expect(result.poolSize).toBeUndefined(); expect(result.max).toEqual(10); }); - it("max should take precedence over poolSize", () => { + it('max should take precedence over poolSize', () => { const result = parser.getDatabaseOptionsFromURI( `${baseURI}?poolSize=20&max=12` ); diff --git a/spec/PostgresInitOptions.spec.js b/spec/PostgresInitOptions.spec.js index f0bf554bcb..2af94e9fc3 100644 --- a/spec/PostgresInitOptions.spec.js +++ b/spec/PostgresInitOptions.spec.js @@ -1,33 +1,33 @@ -const Parse = require("parse/node").Parse; +const Parse = require('parse/node').Parse; const PostgresStorageAdapter = - require("../lib/Adapters/Storage/Postgres/PostgresStorageAdapter").default; + require('../lib/Adapters/Storage/Postgres/PostgresStorageAdapter').default; const postgresURI = process.env.PARSE_SERVER_TEST_DATABASE_URI || - "postgres://localhost:5432/parse_server_postgres_adapter_test_database"; + 'postgres://localhost:5432/parse_server_postgres_adapter_test_database'; //public schema const databaseOptions1 = { initOptions: { - schema: "public", + schema: 'public', }, }; //not exists schema const databaseOptions2 = { initOptions: { - schema: "not_exists_schema", + schema: 'not_exists_schema', }, }; const GameScore = Parse.Object.extend({ - className: "GameScore", + className: 'GameScore', }); -describe_only_db("postgres")("Postgres database init options", () => { - it("should create server with public schema databaseOptions", async () => { +describe_only_db('postgres')('Postgres database init options', () => { + it('should create server with public schema databaseOptions', async () => { const adapter = new PostgresStorageAdapter({ uri: postgresURI, - collectionPrefix: "test_", + collectionPrefix: 'test_', databaseOptions: databaseOptions1, }); await reconfigureServer({ @@ -35,18 +35,18 @@ describe_only_db("postgres")("Postgres database init options", () => { }); const score = new GameScore({ score: 1337, - playerName: "Sean Plott", + playerName: 'Sean Plott', cheatMode: false, }); await score.save(); }); - it("should create server using postgresql uri with public schema databaseOptions", async () => { + it('should create server using postgresql uri with public schema databaseOptions', async () => { const postgresURI2 = new URL(postgresURI); - postgresURI2.protocol = "postgresql:"; + postgresURI2.protocol = 'postgresql:'; const adapter = new PostgresStorageAdapter({ uri: postgresURI2.toString(), - collectionPrefix: "test_", + collectionPrefix: 'test_', databaseOptions: databaseOptions1, }); await reconfigureServer({ @@ -54,23 +54,23 @@ describe_only_db("postgres")("Postgres database init options", () => { }); const score = new GameScore({ score: 1337, - playerName: "Sean Plott", + playerName: 'Sean Plott', cheatMode: false, }); await score.save(); }); - it("should fail to create server if schema databaseOptions does not exist", async () => { + it('should fail to create server if schema databaseOptions does not exist', async () => { const adapter = new PostgresStorageAdapter({ uri: postgresURI, - collectionPrefix: "test_", + collectionPrefix: 'test_', databaseOptions: databaseOptions2, }); try { await reconfigureServer({ databaseAdapter: adapter, }); - fail("Should have thrown error"); + fail('Should have thrown error'); } catch (error) { expect(error).toBeDefined(); } diff --git a/spec/PostgresStorageAdapter.spec.js b/spec/PostgresStorageAdapter.spec.js index 431865c824..71c2d60110 100644 --- a/spec/PostgresStorageAdapter.spec.js +++ b/spec/PostgresStorageAdapter.spec.js @@ -1,41 +1,41 @@ const PostgresStorageAdapter = - require("../lib/Adapters/Storage/Postgres/PostgresStorageAdapter").default; + require('../lib/Adapters/Storage/Postgres/PostgresStorageAdapter').default; const databaseURI = process.env.PARSE_SERVER_TEST_DATABASE_URI || - "postgres://localhost:5432/parse_server_postgres_adapter_test_database"; -const Config = require("../lib/Config"); + 'postgres://localhost:5432/parse_server_postgres_adapter_test_database'; +const Config = require('../lib/Config'); const getColumns = (client, className) => { return client.map( - "SELECT column_name FROM information_schema.columns WHERE table_name = $", + 'SELECT column_name FROM information_schema.columns WHERE table_name = $', { className }, a => a.column_name ); }; const dropTable = (client, className) => { - return client.none("DROP TABLE IF EXISTS $", { className }); + return client.none('DROP TABLE IF EXISTS $', { className }); }; -describe_only_db("postgres")("PostgresStorageAdapter", () => { +describe_only_db('postgres')('PostgresStorageAdapter', () => { let adapter; beforeEach(async () => { - const config = Config.get("test"); + const config = Config.get('test'); adapter = config.database.adapter; }); - it("schemaUpgrade, upgrade the database schema when schema changes", async done => { + it('schemaUpgrade, upgrade the database schema when schema changes', async done => { await adapter.deleteAllClasses(); - const config = Config.get("test"); + const config = Config.get('test'); config.schemaCache.clear(); await adapter.performInitialization({ VolatileClassesSchemas: [] }); const client = adapter._client; - const className = "_PushStatus"; + const className = '_PushStatus'; const schema = { fields: { - pushTime: { type: "String" }, - source: { type: "String" }, - query: { type: "String" }, + pushTime: { type: 'String' }, + source: { type: 'String' }, + query: { type: 'String' }, }, }; @@ -43,34 +43,34 @@ describe_only_db("postgres")("PostgresStorageAdapter", () => { .createTable(className, schema) .then(() => getColumns(client, className)) .then(columns => { - expect(columns).toContain("pushTime"); - expect(columns).toContain("source"); - expect(columns).toContain("query"); - expect(columns).not.toContain("expiration_interval"); + expect(columns).toContain('pushTime'); + expect(columns).toContain('source'); + expect(columns).toContain('query'); + expect(columns).not.toContain('expiration_interval'); - schema.fields.expiration_interval = { type: "Number" }; + schema.fields.expiration_interval = { type: 'Number' }; return adapter.schemaUpgrade(className, schema); }) .then(() => getColumns(client, className)) .then(async columns => { - expect(columns).toContain("pushTime"); - expect(columns).toContain("source"); - expect(columns).toContain("query"); - expect(columns).toContain("expiration_interval"); + expect(columns).toContain('pushTime'); + expect(columns).toContain('source'); + expect(columns).toContain('query'); + expect(columns).toContain('expiration_interval'); await reconfigureServer(); done(); }) .catch(error => done.fail(error)); }); - it("schemaUpgrade, maintain correct schema", done => { + it('schemaUpgrade, maintain correct schema', done => { const client = adapter._client; - const className = "Table"; + const className = 'Table'; const schema = { fields: { - columnA: { type: "String" }, - columnB: { type: "String" }, - columnC: { type: "String" }, + columnA: { type: 'String' }, + columnB: { type: 'String' }, + columnC: { type: 'String' }, }, }; @@ -78,27 +78,27 @@ describe_only_db("postgres")("PostgresStorageAdapter", () => { .createTable(className, schema) .then(() => getColumns(client, className)) .then(columns => { - expect(columns).toContain("columnA"); - expect(columns).toContain("columnB"); - expect(columns).toContain("columnC"); + expect(columns).toContain('columnA'); + expect(columns).toContain('columnB'); + expect(columns).toContain('columnC'); return adapter.schemaUpgrade(className, schema); }) .then(() => getColumns(client, className)) .then(columns => { expect(columns.length).toEqual(3); - expect(columns).toContain("columnA"); - expect(columns).toContain("columnB"); - expect(columns).toContain("columnC"); + expect(columns).toContain('columnA'); + expect(columns).toContain('columnB'); + expect(columns).toContain('columnC'); done(); }) .catch(error => done.fail(error)); }); - it("Create a table without columns and upgrade with columns", done => { + it('Create a table without columns and upgrade with columns', done => { const client = adapter._client; - const className = "EmptyTable"; + const className = 'EmptyTable'; dropTable(client, className) .then(() => adapter.createTable(className, {})) .then(() => getColumns(client, className)) @@ -107,8 +107,8 @@ describe_only_db("postgres")("PostgresStorageAdapter", () => { const newSchema = { fields: { - columnA: { type: "String" }, - columnB: { type: "String" }, + columnA: { type: 'String' }, + columnB: { type: 'String' }, }, }; @@ -117,58 +117,58 @@ describe_only_db("postgres")("PostgresStorageAdapter", () => { .then(() => getColumns(client, className)) .then(columns => { expect(columns.length).toEqual(2); - expect(columns).toContain("columnA"); - expect(columns).toContain("columnB"); + expect(columns).toContain('columnA'); + expect(columns).toContain('columnB'); done(); }) .catch(done); }); - it("getClass if exists", async () => { + it('getClass if exists', async () => { const schema = { fields: { - array: { type: "Array" }, - object: { type: "Object" }, - date: { type: "Date" }, + array: { type: 'Array' }, + object: { type: 'Object' }, + date: { type: 'Date' }, }, }; - await adapter.createClass("MyClass", schema); - const myClassSchema = await adapter.getClass("MyClass"); + await adapter.createClass('MyClass', schema); + const myClassSchema = await adapter.getClass('MyClass'); expect(myClassSchema).toBeDefined(); }); - it("getClass if not exists", async () => { + it('getClass if not exists', async () => { const schema = { fields: { - array: { type: "Array" }, - object: { type: "Object" }, - date: { type: "Date" }, + array: { type: 'Array' }, + object: { type: 'Object' }, + date: { type: 'Date' }, }, }; - await adapter.createClass("MyClass", schema); - await expectAsync(adapter.getClass("UnknownClass")).toBeRejectedWith( + await adapter.createClass('MyClass', schema); + await expectAsync(adapter.getClass('UnknownClass')).toBeRejectedWith( undefined ); }); - it("$relativeTime should error on $eq", async () => { - const tableName = "_User"; + it('$relativeTime should error on $eq', async () => { + const tableName = '_User'; const schema = { fields: { - objectId: { type: "String" }, - username: { type: "String" }, - email: { type: "String" }, - emailVerified: { type: "Boolean" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - authData: { type: "Object" }, + objectId: { type: 'String' }, + username: { type: 'String' }, + email: { type: 'String' }, + emailVerified: { type: 'Boolean' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + authData: { type: 'Object' }, }, }; const client = adapter._client; await adapter.createTable(tableName, schema); await client.none( - "INSERT INTO $1:name ($2:name, $3:name) VALUES ($4, $5)", - [tableName, "objectId", "username", "Bugs", "Bunny"] + 'INSERT INTO $1:name ($2:name, $3:name) VALUES ($4, $5)', + [tableName, 'objectId', 'username', 'Bugs', 'Bunny'] ); const database = Config.get(Parse.applicationId).database; await database.loadSchema({ clearCache: true }); @@ -178,37 +178,37 @@ describe_only_db("postgres")("PostgresStorageAdapter", () => { { createdAt: { $eq: { - $relativeTime: "12 days ago", + $relativeTime: '12 days ago', }, }, }, {} ); - fail("Should have thrown error"); + fail('Should have thrown error'); } catch (error) { expect(error.code).toBe(Parse.Error.INVALID_JSON); } await dropTable(client, tableName); }); - it("$relativeTime should error on $ne", async () => { - const tableName = "_User"; + it('$relativeTime should error on $ne', async () => { + const tableName = '_User'; const schema = { fields: { - objectId: { type: "String" }, - username: { type: "String" }, - email: { type: "String" }, - emailVerified: { type: "Boolean" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - authData: { type: "Object" }, + objectId: { type: 'String' }, + username: { type: 'String' }, + email: { type: 'String' }, + emailVerified: { type: 'Boolean' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + authData: { type: 'Object' }, }, }; const client = adapter._client; await adapter.createTable(tableName, schema); await client.none( - "INSERT INTO $1:name ($2:name, $3:name) VALUES ($4, $5)", - [tableName, "objectId", "username", "Bugs", "Bunny"] + 'INSERT INTO $1:name ($2:name, $3:name) VALUES ($4, $5)', + [tableName, 'objectId', 'username', 'Bugs', 'Bunny'] ); const database = Config.get(Parse.applicationId).database; await database.loadSchema({ clearCache: true }); @@ -218,37 +218,37 @@ describe_only_db("postgres")("PostgresStorageAdapter", () => { { createdAt: { $ne: { - $relativeTime: "12 days ago", + $relativeTime: '12 days ago', }, }, }, {} ); - fail("Should have thrown error"); + fail('Should have thrown error'); } catch (error) { expect(error.code).toBe(Parse.Error.INVALID_JSON); } await dropTable(client, tableName); }); - it("$relativeTime should error on $exists", async () => { - const tableName = "_User"; + it('$relativeTime should error on $exists', async () => { + const tableName = '_User'; const schema = { fields: { - objectId: { type: "String" }, - username: { type: "String" }, - email: { type: "String" }, - emailVerified: { type: "Boolean" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - authData: { type: "Object" }, + objectId: { type: 'String' }, + username: { type: 'String' }, + email: { type: 'String' }, + emailVerified: { type: 'Boolean' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + authData: { type: 'Object' }, }, }; const client = adapter._client; await adapter.createTable(tableName, schema); await client.none( - "INSERT INTO $1:name ($2:name, $3:name) VALUES ($4, $5)", - [tableName, "objectId", "username", "Bugs", "Bunny"] + 'INSERT INTO $1:name ($2:name, $3:name) VALUES ($4, $5)', + [tableName, 'objectId', 'username', 'Bugs', 'Bunny'] ); const database = Config.get(Parse.applicationId).database; await database.loadSchema({ clearCache: true }); @@ -258,127 +258,127 @@ describe_only_db("postgres")("PostgresStorageAdapter", () => { { createdAt: { $exists: { - $relativeTime: "12 days ago", + $relativeTime: '12 days ago', }, }, }, {} ); - fail("Should have thrown error"); + fail('Should have thrown error'); } catch (error) { expect(error.code).toBe(Parse.Error.INVALID_JSON); } await dropTable(client, tableName); }); - it("should use index for caseInsensitive query using Postgres", async () => { - const tableName = "_User"; + it('should use index for caseInsensitive query using Postgres', async () => { + const tableName = '_User'; const schema = { fields: { - objectId: { type: "String" }, - username: { type: "String" }, - email: { type: "String" }, - emailVerified: { type: "Boolean" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - authData: { type: "Object" }, + objectId: { type: 'String' }, + username: { type: 'String' }, + email: { type: 'String' }, + emailVerified: { type: 'Boolean' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + authData: { type: 'Object' }, }, }; const client = adapter._client; await adapter.createTable(tableName, schema); await client.none( - "INSERT INTO $1:name ($2:name, $3:name) VALUES ($4, $5)", - [tableName, "objectId", "username", "Bugs", "Bunny"] + 'INSERT INTO $1:name ($2:name, $3:name) VALUES ($4, $5)', + [tableName, 'objectId', 'username', 'Bugs', 'Bunny'] ); //Postgres won't take advantage of the index until it has a lot of records because sequential is faster for small db's await client.none( - "INSERT INTO $1:name ($2:name, $3:name) SELECT gen_random_uuid(), gen_random_uuid() FROM generate_series(1,5000)", - [tableName, "objectId", "username"] + 'INSERT INTO $1:name ($2:name, $3:name) SELECT gen_random_uuid(), gen_random_uuid() FROM generate_series(1,5000)', + [tableName, 'objectId', 'username'] ); - const caseInsensitiveData = "bugs"; + const caseInsensitiveData = 'bugs'; const originalQuery = - "SELECT * FROM $1:name WHERE lower($2:name)=lower($3)"; + 'SELECT * FROM $1:name WHERE lower($2:name)=lower($3)'; const analyzedExplainQuery = adapter.createExplainableQuery( originalQuery, true ); const preIndexPlan = await client.one(analyzedExplainQuery, [ tableName, - "objectId", + 'objectId', caseInsensitiveData, ]); - preIndexPlan["QUERY PLAN"].forEach(element => { + preIndexPlan['QUERY PLAN'].forEach(element => { //Make sure search returned with only 1 result - expect(element.Plan["Actual Rows"]).toBe(1); - expect(element.Plan["Node Type"]).toBe("Seq Scan"); + expect(element.Plan['Actual Rows']).toBe(1); + expect(element.Plan['Node Type']).toBe('Seq Scan'); }); - const indexName = "test_case_insensitive_column"; - await adapter.ensureIndex(tableName, schema, ["objectId"], indexName, true); + const indexName = 'test_case_insensitive_column'; + await adapter.ensureIndex(tableName, schema, ['objectId'], indexName, true); const postIndexPlan = await client.one(analyzedExplainQuery, [ tableName, - "objectId", + 'objectId', caseInsensitiveData, ]); - postIndexPlan["QUERY PLAN"].forEach(element => { + postIndexPlan['QUERY PLAN'].forEach(element => { //Make sure search returned with only 1 result - expect(element.Plan["Actual Rows"]).toBe(1); + expect(element.Plan['Actual Rows']).toBe(1); //Should not be a sequential scan - expect(element.Plan["Node Type"]).not.toContain("Seq Scan"); + expect(element.Plan['Node Type']).not.toContain('Seq Scan'); //Should be using the index created for this element.Plan.Plans.forEach(innerElement => { - expect(innerElement["Index Name"]).toBe(indexName); + expect(innerElement['Index Name']).toBe(indexName); }); }); //These are the same query so should be the same size - for (let i = 0; i < preIndexPlan["QUERY PLAN"].length; i++) { + for (let i = 0; i < preIndexPlan['QUERY PLAN'].length; i++) { //Sequential should take more time to execute than indexed - expect(preIndexPlan["QUERY PLAN"][i]["Execution Time"]).toBeGreaterThan( - postIndexPlan["QUERY PLAN"][i]["Execution Time"] + expect(preIndexPlan['QUERY PLAN'][i]['Execution Time']).toBeGreaterThan( + postIndexPlan['QUERY PLAN'][i]['Execution Time'] ); } //Test explaining without analyzing const basicExplainQuery = adapter.createExplainableQuery(originalQuery); const explained = await client.one(basicExplainQuery, [ tableName, - "objectId", + 'objectId', caseInsensitiveData, ]); - explained["QUERY PLAN"].forEach(element => { + explained['QUERY PLAN'].forEach(element => { //Check that basic query plans isn't a sequential scan - expect(element.Plan["Node Type"]).not.toContain("Seq Scan"); + expect(element.Plan['Node Type']).not.toContain('Seq Scan'); //Basic query plans shouldn't have an execution time - expect(element["Execution Time"]).toBeUndefined(); + expect(element['Execution Time']).toBeUndefined(); }); await dropTable(client, tableName); }); - it("should use index for caseInsensitive query with user", async () => { + it('should use index for caseInsensitive query with user', async () => { await adapter.deleteAllClasses(); - const config = Config.get("test"); + const config = Config.get('test'); config.schemaCache.clear(); await adapter.performInitialization({ VolatileClassesSchemas: [] }); const database = Config.get(Parse.applicationId).database; await database.loadSchema({ clearCache: true }); - const tableName = "_User"; + const tableName = '_User'; const user = new Parse.User(); - user.set("username", "Elmer"); - user.set("password", "Fudd"); + user.set('username', 'Elmer'); + user.set('password', 'Fudd'); await user.signUp(); //Postgres won't take advantage of the index until it has a lot of records because sequential is faster for small db's const client = adapter._client; await client.none( - "INSERT INTO $1:name ($2:name, $3:name) SELECT gen_random_uuid(), gen_random_uuid() FROM generate_series(1,5000)", - [tableName, "objectId", "username"] + 'INSERT INTO $1:name ($2:name, $3:name) SELECT gen_random_uuid(), gen_random_uuid() FROM generate_series(1,5000)', + [tableName, 'objectId', 'username'] ); - const caseInsensitiveData = "elmer"; - const fieldToSearch = "username"; + const caseInsensitiveData = 'elmer'; + const fieldToSearch = 'username'; //Check using find method for Parse const preIndexPlan = await database.find( tableName, @@ -387,16 +387,16 @@ describe_only_db("postgres")("PostgresStorageAdapter", () => { ); preIndexPlan.forEach(element => { - element["QUERY PLAN"].forEach(innerElement => { + element['QUERY PLAN'].forEach(innerElement => { //Check that basic query plans isn't a sequential scan, be careful as find uses "any" to query - expect(innerElement.Plan["Node Type"]).toBe("Seq Scan"); + expect(innerElement.Plan['Node Type']).toBe('Seq Scan'); //Basic query plans shouldn't have an execution time - expect(innerElement["Execution Time"]).toBeUndefined(); + expect(innerElement['Execution Time']).toBeUndefined(); }); }); - const indexName = "test_case_insensitive_column"; - const schema = await new Parse.Schema("_User").get(); + const indexName = 'test_case_insensitive_column'; + const schema = await new Parse.Schema('_User').get(); await adapter.ensureIndex( tableName, schema, @@ -413,43 +413,43 @@ describe_only_db("postgres")("PostgresStorageAdapter", () => { ); postIndexPlan.forEach(element => { - element["QUERY PLAN"].forEach(innerElement => { + element['QUERY PLAN'].forEach(innerElement => { //Check that basic query plans isn't a sequential scan - expect(innerElement.Plan["Node Type"]).not.toContain("Seq Scan"); + expect(innerElement.Plan['Node Type']).not.toContain('Seq Scan'); //Basic query plans shouldn't have an execution time - expect(innerElement["Execution Time"]).toBeUndefined(); + expect(innerElement['Execution Time']).toBeUndefined(); }); }); }); - it("should use index for caseInsensitive query using default indexname", async () => { + it('should use index for caseInsensitive query using default indexname', async () => { await adapter.deleteAllClasses(); - const config = Config.get("test"); + const config = Config.get('test'); config.schemaCache.clear(); await adapter.performInitialization({ VolatileClassesSchemas: [] }); const database = Config.get(Parse.applicationId).database; await database.loadSchema({ clearCache: true }); - const tableName = "_User"; + const tableName = '_User'; const user = new Parse.User(); - user.set("username", "Tweety"); - user.set("password", "Bird"); + user.set('username', 'Tweety'); + user.set('password', 'Bird'); await user.signUp(); - const fieldToSearch = "username"; + const fieldToSearch = 'username'; //Create index before data is inserted - const schema = await new Parse.Schema("_User").get(); + const schema = await new Parse.Schema('_User').get(); await adapter.ensureIndex(tableName, schema, [fieldToSearch], null, true); //Postgres won't take advantage of the index until it has a lot of records because sequential is faster for small db's const client = adapter._client; await client.none( - "INSERT INTO $1:name ($2:name, $3:name) SELECT gen_random_uuid(), gen_random_uuid() FROM generate_series(1,5000)", - [tableName, "objectId", "username"] + 'INSERT INTO $1:name ($2:name, $3:name) SELECT gen_random_uuid(), gen_random_uuid() FROM generate_series(1,5000)', + [tableName, 'objectId', 'username'] ); - const caseInsensitiveData = "tweeTy"; + const caseInsensitiveData = 'tweeTy'; //Check using find method for Parse const indexPlan = await database.find( tableName, @@ -457,22 +457,22 @@ describe_only_db("postgres")("PostgresStorageAdapter", () => { { caseInsensitive: true, explain: true } ); indexPlan.forEach(element => { - element["QUERY PLAN"].forEach(innerElement => { - expect(innerElement.Plan["Node Type"]).not.toContain("Seq Scan"); - expect(innerElement.Plan["Index Name"]).toContain("parse_default"); + element['QUERY PLAN'].forEach(innerElement => { + expect(innerElement.Plan['Node Type']).not.toContain('Seq Scan'); + expect(innerElement.Plan['Index Name']).toContain('parse_default'); }); }); }); - it("should allow multiple unique indexes for same field name and different class", async () => { - const firstTableName = "Test1"; + it('should allow multiple unique indexes for same field name and different class', async () => { + const firstTableName = 'Test1'; const firstTableSchema = new Parse.Schema(firstTableName); - const uniqueField = "uuid"; + const uniqueField = 'uuid'; firstTableSchema.addString(uniqueField); await firstTableSchema.save(); await firstTableSchema.get(); - const secondTableName = "Test2"; + const secondTableName = 'Test2'; const secondTableSchema = new Parse.Schema(secondTableName); secondTableSchema.addString(uniqueField); await secondTableSchema.save(); @@ -491,45 +491,45 @@ describe_only_db("postgres")("PostgresStorageAdapter", () => { //Postgres won't take advantage of the index until it has a lot of records because sequential is faster for small db's const client = adapter._client; await client.none( - "INSERT INTO $1:name ($2:name, $3:name) SELECT gen_random_uuid(), gen_random_uuid() FROM generate_series(1,5000)", - [firstTableName, "objectId", uniqueField] + 'INSERT INTO $1:name ($2:name, $3:name) SELECT gen_random_uuid(), gen_random_uuid() FROM generate_series(1,5000)', + [firstTableName, 'objectId', uniqueField] ); await client.none( - "INSERT INTO $1:name ($2:name, $3:name) SELECT gen_random_uuid(), gen_random_uuid() FROM generate_series(1,5000)", - [secondTableName, "objectId", uniqueField] + 'INSERT INTO $1:name ($2:name, $3:name) SELECT gen_random_uuid(), gen_random_uuid() FROM generate_series(1,5000)', + [secondTableName, 'objectId', uniqueField] ); //Check using find method for Parse const indexPlan = await database.find( firstTableName, - { uuid: "1234" }, + { uuid: '1234' }, { caseInsensitive: false, explain: true } ); indexPlan.forEach(element => { - element["QUERY PLAN"].forEach(innerElement => { - expect(innerElement.Plan["Node Type"]).not.toContain("Seq Scan"); - expect(innerElement.Plan["Index Name"]).toContain(uniqueField); + element['QUERY PLAN'].forEach(innerElement => { + expect(innerElement.Plan['Node Type']).not.toContain('Seq Scan'); + expect(innerElement.Plan['Index Name']).toContain(uniqueField); }); }); const indexPlan2 = await database.find( secondTableName, - { uuid: "1234" }, + { uuid: '1234' }, { caseInsensitive: false, explain: true } ); indexPlan2.forEach(element => { - element["QUERY PLAN"].forEach(innerElement => { - expect(innerElement.Plan["Node Type"]).not.toContain("Seq Scan"); - expect(innerElement.Plan["Index Name"]).toContain(uniqueField); + element['QUERY PLAN'].forEach(innerElement => { + expect(innerElement.Plan['Node Type']).not.toContain('Seq Scan'); + expect(innerElement.Plan['Index Name']).toContain(uniqueField); }); }); }); - it("should watch _SCHEMA changes", async () => { + it('should watch _SCHEMA changes', async () => { const enableSchemaHooks = true; await reconfigureServer({ databaseAdapter: undefined, databaseURI, - collectionPrefix: "", + collectionPrefix: '', databaseOptions: { enableSchemaHooks, }, @@ -537,25 +537,25 @@ describe_only_db("postgres")("PostgresStorageAdapter", () => { const { database } = Config.get(Parse.applicationId); const { adapter } = database; expect(adapter.enableSchemaHooks).toBe(enableSchemaHooks); - spyOn(adapter, "_onchange"); + spyOn(adapter, '_onchange'); enableSchemaHooks; const otherInstance = new PostgresStorageAdapter({ uri: databaseURI, - collectionPrefix: "", + collectionPrefix: '', databaseOptions: { enableSchemaHooks }, }); expect(otherInstance.enableSchemaHooks).toBe(enableSchemaHooks); otherInstance._listenToSchema(); - await otherInstance.createClass("Stuff", { - className: "Stuff", + await otherInstance.createClass('Stuff', { + className: 'Stuff', fields: { - objectId: { type: "String" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - _rperm: { type: "Array" }, - _wperm: { type: "Array" }, + objectId: { type: 'String' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + _rperm: { type: 'Array' }, + _wperm: { type: 'Array' }, }, classLevelPermissions: undefined, }); @@ -563,32 +563,32 @@ describe_only_db("postgres")("PostgresStorageAdapter", () => { expect(adapter._onchange).toHaveBeenCalled(); }); - it("Idempotency class should have function", async () => { + it('Idempotency class should have function', async () => { await reconfigureServer(); - const adapter = Config.get("test").database.adapter; + const adapter = Config.get('test').database.adapter; const client = adapter._client; const qs = "SELECT format('%I.%I(%s)', ns.nspname, p.proname, oidvectortypes(p.proargtypes)) FROM pg_proc p INNER JOIN pg_namespace ns ON (p.pronamespace = ns.oid) WHERE p.proname = 'idempotency_delete_expired_records'"; const foundFunction = await client.one(qs); expect(foundFunction.format).toBe( - "public.idempotency_delete_expired_records()" + 'public.idempotency_delete_expired_records()' ); await adapter.deleteIdempotencyFunction(); await client.none(qs); }); }); -describe_only_db("postgres")("PostgresStorageAdapter shutdown", () => { - it("handleShutdown, close connection", () => { +describe_only_db('postgres')('PostgresStorageAdapter shutdown', () => { + it('handleShutdown, close connection', () => { const adapter = new PostgresStorageAdapter({ uri: databaseURI }); expect(adapter._client.$pool.ending).toEqual(false); adapter.handleShutdown(); expect(adapter._client.$pool.ending).toEqual(true); }); - it("handleShutdown, close connection of postgresql uri", () => { + it('handleShutdown, close connection of postgresql uri', () => { const databaseURI2 = new URL(databaseURI); - databaseURI2.protocol = "postgresql:"; + databaseURI2.protocol = 'postgresql:'; const adapter = new PostgresStorageAdapter({ uri: databaseURI2.toString(), }); diff --git a/spec/PromiseRouter.spec.js b/spec/PromiseRouter.spec.js index b2d7e8c399..51a4ce21a1 100644 --- a/spec/PromiseRouter.spec.js +++ b/spec/PromiseRouter.spec.js @@ -1,30 +1,30 @@ -const PromiseRouter = require("../lib/PromiseRouter").default; +const PromiseRouter = require('../lib/PromiseRouter').default; -describe("PromiseRouter", () => { - it("should properly handle rejects", done => { +describe('PromiseRouter', () => { + it('should properly handle rejects', done => { const router = new PromiseRouter(); router.route( - "GET", - "/dummy", + 'GET', + '/dummy', () => { return Promise.reject({ - error: "an error", + error: 'an error', code: -1, }); }, () => { - fail("this should not be called"); + fail('this should not be called'); } ); router.routes[0].handler({}).then( result => { jfail(result); - fail("this should not be called"); + fail('this should not be called'); done(); }, error => { - expect(error.error).toEqual("an error"); + expect(error.error).toEqual('an error'); expect(error.code).toEqual(-1); done(); } diff --git a/spec/ProtectedFields.spec.js b/spec/ProtectedFields.spec.js index db5e8218ef..db51184935 100644 --- a/spec/ProtectedFields.spec.js +++ b/spec/ProtectedFields.spec.js @@ -1,784 +1,784 @@ -const Config = require("../lib/Config"); -const Parse = require("parse/node"); -const request = require("../lib/request"); +const Config = require('../lib/Config'); +const Parse = require('parse/node'); +const request = require('../lib/request'); const { className, createRole, createUser, logIn, updateCLP, -} = require("./support/dev"); +} = require('./support/dev'); -describe("ProtectedFields", function () { - it("should handle and empty protectedFields", async function () { +describe('ProtectedFields', function () { + it('should handle and empty protectedFields', async function () { const protectedFields = {}; await reconfigureServer({ protectedFields }); const user = new Parse.User(); - user.setUsername("Alice"); - user.setPassword("sekrit"); - user.set("email", "alice@aol.com"); - user.set("favoriteColor", "yellow"); + user.setUsername('Alice'); + user.setPassword('sekrit'); + user.set('email', 'alice@aol.com'); + user.set('favoriteColor', 'yellow'); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); user.setACL(acl); await user.save(); const fetched = await new Parse.Query(Parse.User).get(user.id); - expect(fetched.has("email")).toBeFalsy(); - expect(fetched.has("favoriteColor")).toBeTruthy(); + expect(fetched.has('email')).toBeFalsy(); + expect(fetched.has('favoriteColor')).toBeTruthy(); }); - describe("interaction with legacy userSensitiveFields", function () { - it("should fall back on sensitive fields if protected fields are not configured", async function () { - const userSensitiveFields = ["phoneNumber", "timeZone"]; + describe('interaction with legacy userSensitiveFields', function () { + it('should fall back on sensitive fields if protected fields are not configured', async function () { + const userSensitiveFields = ['phoneNumber', 'timeZone']; - const protectedFields = { _User: { "*": ["email"] } }; + const protectedFields = { _User: { '*': ['email'] } }; await reconfigureServer({ userSensitiveFields, protectedFields }); const user = new Parse.User(); - user.setUsername("Alice"); - user.setPassword("sekrit"); - user.set("email", "alice@aol.com"); - user.set("phoneNumber", 8675309); - user.set("timeZone", "America/Los_Angeles"); - user.set("favoriteColor", "yellow"); - user.set("favoriteFood", "pizza"); + user.setUsername('Alice'); + user.setPassword('sekrit'); + user.set('email', 'alice@aol.com'); + user.set('phoneNumber', 8675309); + user.set('timeZone', 'America/Los_Angeles'); + user.set('favoriteColor', 'yellow'); + user.set('favoriteFood', 'pizza'); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); user.setACL(acl); await user.save(); const fetched = await new Parse.Query(Parse.User).get(user.id); - expect(fetched.has("email")).toBeFalsy(); - expect(fetched.has("phoneNumber")).toBeFalsy(); - expect(fetched.has("favoriteColor")).toBeTruthy(); + expect(fetched.has('email')).toBeFalsy(); + expect(fetched.has('phoneNumber')).toBeFalsy(); + expect(fetched.has('favoriteColor')).toBeTruthy(); }); - it("should merge protected and sensitive for extra safety", async function () { - const userSensitiveFields = ["phoneNumber", "timeZone"]; + it('should merge protected and sensitive for extra safety', async function () { + const userSensitiveFields = ['phoneNumber', 'timeZone']; - const protectedFields = { _User: { "*": ["email", "favoriteFood"] } }; + const protectedFields = { _User: { '*': ['email', 'favoriteFood'] } }; await reconfigureServer({ userSensitiveFields, protectedFields }); const user = new Parse.User(); - user.setUsername("Alice"); - user.setPassword("sekrit"); - user.set("email", "alice@aol.com"); - user.set("phoneNumber", 8675309); - user.set("timeZone", "America/Los_Angeles"); - user.set("favoriteColor", "yellow"); - user.set("favoriteFood", "pizza"); + user.setUsername('Alice'); + user.setPassword('sekrit'); + user.set('email', 'alice@aol.com'); + user.set('phoneNumber', 8675309); + user.set('timeZone', 'America/Los_Angeles'); + user.set('favoriteColor', 'yellow'); + user.set('favoriteFood', 'pizza'); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); user.setACL(acl); await user.save(); const fetched = await new Parse.Query(Parse.User).get(user.id); - expect(fetched.has("email")).toBeFalsy(); - expect(fetched.has("phoneNumber")).toBeFalsy(); - expect(fetched.has("favoriteFood")).toBeFalsy(); - expect(fetched.has("favoriteColor")).toBeTruthy(); + expect(fetched.has('email')).toBeFalsy(); + expect(fetched.has('phoneNumber')).toBeFalsy(); + expect(fetched.has('favoriteFood')).toBeFalsy(); + expect(fetched.has('favoriteColor')).toBeTruthy(); }); }); - describe("non user class", function () { - it("should hide fields in a non user class", async function () { + describe('non user class', function () { + it('should hide fields in a non user class', async function () { const protectedFields = { - ClassA: { "*": ["foo"] }, - ClassB: { "*": ["bar"] }, + ClassA: { '*': ['foo'] }, + ClassB: { '*': ['bar'] }, }; await reconfigureServer({ protectedFields }); - const objA = await new Parse.Object("ClassA") - .set("foo", "zzz") - .set("bar", "yyy") + const objA = await new Parse.Object('ClassA') + .set('foo', 'zzz') + .set('bar', 'yyy') .save(); - const objB = await new Parse.Object("ClassB") - .set("foo", "zzz") - .set("bar", "yyy") + const objB = await new Parse.Object('ClassB') + .set('foo', 'zzz') + .set('bar', 'yyy') .save(); const [fetchedA, fetchedB] = await Promise.all([ - new Parse.Query("ClassA").get(objA.id), - new Parse.Query("ClassB").get(objB.id), + new Parse.Query('ClassA').get(objA.id), + new Parse.Query('ClassB').get(objB.id), ]); - expect(fetchedA.has("foo")).toBeFalsy(); - expect(fetchedA.has("bar")).toBeTruthy(); + expect(fetchedA.has('foo')).toBeFalsy(); + expect(fetchedA.has('bar')).toBeTruthy(); - expect(fetchedB.has("foo")).toBeTruthy(); - expect(fetchedB.has("bar")).toBeFalsy(); + expect(fetchedB.has('foo')).toBeTruthy(); + expect(fetchedB.has('bar')).toBeFalsy(); }); - it("should hide fields in non user class and non standard user field at same time", async function () { + it('should hide fields in non user class and non standard user field at same time', async function () { const protectedFields = { - _User: { "*": ["phoneNumber"] }, - ClassA: { "*": ["foo"] }, - ClassB: { "*": ["bar"] }, + _User: { '*': ['phoneNumber'] }, + ClassA: { '*': ['foo'] }, + ClassB: { '*': ['bar'] }, }; await reconfigureServer({ protectedFields }); const user = new Parse.User(); - user.setUsername("Alice"); - user.setPassword("sekrit"); - user.set("email", "alice@aol.com"); - user.set("phoneNumber", 8675309); - user.set("timeZone", "America/Los_Angeles"); - user.set("favoriteColor", "yellow"); - user.set("favoriteFood", "pizza"); + user.setUsername('Alice'); + user.setPassword('sekrit'); + user.set('email', 'alice@aol.com'); + user.set('phoneNumber', 8675309); + user.set('timeZone', 'America/Los_Angeles'); + user.set('favoriteColor', 'yellow'); + user.set('favoriteFood', 'pizza'); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); user.setACL(acl); await user.save(); - const objA = await new Parse.Object("ClassA") - .set("foo", "zzz") - .set("bar", "yyy") + const objA = await new Parse.Object('ClassA') + .set('foo', 'zzz') + .set('bar', 'yyy') .save(); - const objB = await new Parse.Object("ClassB") - .set("foo", "zzz") - .set("bar", "yyy") + const objB = await new Parse.Object('ClassB') + .set('foo', 'zzz') + .set('bar', 'yyy') .save(); const [fetchedUser, fetchedA, fetchedB] = await Promise.all([ new Parse.Query(Parse.User).get(user.id), - new Parse.Query("ClassA").get(objA.id), - new Parse.Query("ClassB").get(objB.id), + new Parse.Query('ClassA').get(objA.id), + new Parse.Query('ClassB').get(objB.id), ]); - expect(fetchedA.has("foo")).toBeFalsy(); - expect(fetchedA.has("bar")).toBeTruthy(); + expect(fetchedA.has('foo')).toBeFalsy(); + expect(fetchedA.has('bar')).toBeTruthy(); - expect(fetchedB.has("foo")).toBeTruthy(); - expect(fetchedB.has("bar")).toBeFalsy(); + expect(fetchedB.has('foo')).toBeTruthy(); + expect(fetchedB.has('bar')).toBeFalsy(); - expect(fetchedUser.has("email")).toBeFalsy(); - expect(fetchedUser.has("phoneNumber")).toBeFalsy(); - expect(fetchedUser.has("favoriteColor")).toBeTruthy(); + expect(fetchedUser.has('email')).toBeFalsy(); + expect(fetchedUser.has('phoneNumber')).toBeFalsy(); + expect(fetchedUser.has('favoriteColor')).toBeTruthy(); }); }); - describe("using the pointer-permission variant", () => { + describe('using the pointer-permission variant', () => { let user1, user2; beforeEach(async () => { Config.get(Parse.applicationId).schemaCache.clear(); - user1 = await Parse.User.signUp("user1", "password"); - user2 = await Parse.User.signUp("user2", "password"); + user1 = await Parse.User.signUp('user1', 'password'); + user2 = await Parse.User.signUp('user2', 'password'); await Parse.User.logOut(); }); - describe("and get/fetch", () => { - it("should allow access using single user pointer-permissions", async done => { + describe('and get/fetch', () => { + it('should allow access using single user pointer-permissions', async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); - obj.set("owner", user1); - obj.set("test", "test"); + obj.set('owner', user1); + obj.set('test', 'test'); await obj.save(); const schema = await config.database.loadSchema(); await schema.updateClass( - "AnObject", + 'AnObject', {}, { - get: { "*": true }, - find: { "*": true }, - protectedFields: { "*": ["owner"], "userField:owner": [] }, + get: { '*': true }, + find: { '*': true }, + protectedFields: { '*': ['owner'], 'userField:owner': [] }, } ); - await Parse.User.logIn("user1", "password"); + await Parse.User.logIn('user1', 'password'); const objectAgain = await obj.fetch(); - expect(objectAgain.get("owner").id).toBe(user1.id); - expect(objectAgain.get("test")).toBe("test"); + expect(objectAgain.get('owner').id).toBe(user1.id); + expect(objectAgain.get('test')).toBe('test'); done(); }); - it("should deny access to other users using single user pointer-permissions", async done => { + it('should deny access to other users using single user pointer-permissions', async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); - obj.set("owner", user1); - obj.set("test", "test"); + obj.set('owner', user1); + obj.set('test', 'test'); await obj.save(); const schema = await config.database.loadSchema(); await schema.updateClass( - "AnObject", + 'AnObject', {}, { - get: { "*": true }, - find: { "*": true }, - protectedFields: { "*": ["owner"], "userField:owner": [] }, + get: { '*': true }, + find: { '*': true }, + protectedFields: { '*': ['owner'], 'userField:owner': [] }, } ); - await Parse.User.logIn("user2", "password"); + await Parse.User.logIn('user2', 'password'); const objectAgain = await obj.fetch(); - expect(objectAgain.get("owner")).toBe(undefined); - expect(objectAgain.get("test")).toBe("test"); + expect(objectAgain.get('owner')).toBe(undefined); + expect(objectAgain.get('test')).toBe('test'); done(); }); - it("should deny access to public using single user pointer-permissions", async done => { + it('should deny access to public using single user pointer-permissions', async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); - obj.set("owner", user1); - obj.set("test", "test"); + obj.set('owner', user1); + obj.set('test', 'test'); await obj.save(); const schema = await config.database.loadSchema(); await schema.updateClass( - "AnObject", + 'AnObject', {}, { - get: { "*": true }, - find: { "*": true }, - protectedFields: { "*": ["owner"], "userField:owner": [] }, + get: { '*': true }, + find: { '*': true }, + protectedFields: { '*': ['owner'], 'userField:owner': [] }, } ); const objectAgain = await obj.fetch(); - expect(objectAgain.get("owner")).toBe(undefined); - expect(objectAgain.get("test")).toBe("test"); + expect(objectAgain.get('owner')).toBe(undefined); + expect(objectAgain.get('test')).toBe('test'); done(); }); - it("should allow access using user array pointer-permissions", async done => { + it('should allow access using user array pointer-permissions', async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); - obj.set("owners", [user1, user2]); - obj.set("test", "test"); + obj.set('owners', [user1, user2]); + obj.set('test', 'test'); await obj.save(); const schema = await config.database.loadSchema(); await schema.updateClass( - "AnObject", + 'AnObject', {}, { - get: { "*": true }, - find: { "*": true }, - protectedFields: { "*": ["owners"], "userField:owners": [] }, + get: { '*': true }, + find: { '*': true }, + protectedFields: { '*': ['owners'], 'userField:owners': [] }, } ); - await Parse.User.logIn("user1", "password"); + await Parse.User.logIn('user1', 'password'); let objectAgain = await obj.fetch(); - expect(objectAgain.get("owners")[0].id).toBe(user1.id); - expect(objectAgain.get("test")).toBe("test"); - await Parse.User.logIn("user2", "password"); + expect(objectAgain.get('owners')[0].id).toBe(user1.id); + expect(objectAgain.get('test')).toBe('test'); + await Parse.User.logIn('user2', 'password'); objectAgain = await obj.fetch(); - expect(objectAgain.get("owners")[1].id).toBe(user2.id); - expect(objectAgain.get("test")).toBe("test"); + expect(objectAgain.get('owners')[1].id).toBe(user2.id); + expect(objectAgain.get('test')).toBe('test'); done(); }); - it("should deny access to other users using user array pointer-permissions", async done => { + it('should deny access to other users using user array pointer-permissions', async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); - obj.set("owners", [user1]); - obj.set("test", "test"); + obj.set('owners', [user1]); + obj.set('test', 'test'); await obj.save(); const schema = await config.database.loadSchema(); await schema.updateClass( - "AnObject", + 'AnObject', {}, { - get: { "*": true }, - find: { "*": true }, - protectedFields: { "*": ["owners"], "userField:owners": [] }, + get: { '*': true }, + find: { '*': true }, + protectedFields: { '*': ['owners'], 'userField:owners': [] }, } ); - await Parse.User.logIn("user2", "password"); + await Parse.User.logIn('user2', 'password'); const objectAgain = await obj.fetch(); - expect(objectAgain.get("owners")).toBe(undefined); - expect(objectAgain.get("test")).toBe("test"); + expect(objectAgain.get('owners')).toBe(undefined); + expect(objectAgain.get('test')).toBe('test'); done(); }); - it("should deny access to public using user array pointer-permissions", async done => { + it('should deny access to public using user array pointer-permissions', async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); - obj.set("owners", [user1, user2]); - obj.set("test", "test"); + obj.set('owners', [user1, user2]); + obj.set('test', 'test'); await obj.save(); const schema = await config.database.loadSchema(); await schema.updateClass( - "AnObject", + 'AnObject', {}, { - get: { "*": true }, - find: { "*": true }, - protectedFields: { "*": ["owners"], "userField:owners": [] }, + get: { '*': true }, + find: { '*': true }, + protectedFields: { '*': ['owners'], 'userField:owners': [] }, } ); const objectAgain = await obj.fetch(); - expect(objectAgain.get("owners")).toBe(undefined); - expect(objectAgain.get("test")).toBe("test"); + expect(objectAgain.get('owners')).toBe(undefined); + expect(objectAgain.get('test')).toBe('test'); done(); }); - it("should intersect protected fields when using multiple pointer-permission fields", async done => { + it('should intersect protected fields when using multiple pointer-permission fields', async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); - obj.set("owners", [user1]); - obj.set("owner", user1); - obj.set("test", "test"); + obj.set('owners', [user1]); + obj.set('owner', user1); + obj.set('test', 'test'); await obj.save(); const schema = await config.database.loadSchema(); await schema.updateClass( - "AnObject", + 'AnObject', {}, { - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, protectedFields: { - "*": ["owners", "owner", "test"], - "userField:owners": ["owners", "owner"], - "userField:owner": ["owner"], + '*': ['owners', 'owner', 'test'], + 'userField:owners': ['owners', 'owner'], + 'userField:owner': ['owner'], }, } ); // Check if protectFields from pointer-permissions got combined - await Parse.User.logIn("user1", "password"); + await Parse.User.logIn('user1', 'password'); const objectAgain = await obj.fetch(); - expect(objectAgain.get("owners").length).toBe(1); - expect(objectAgain.get("owner")).toBe(undefined); - expect(objectAgain.get("test")).toBe("test"); + expect(objectAgain.get('owners').length).toBe(1); + expect(objectAgain.get('owner')).toBe(undefined); + expect(objectAgain.get('test')).toBe('test'); done(); }); - it("should ignore pointer-permission fields not present in object", async done => { + it('should ignore pointer-permission fields not present in object', async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); - obj.set("owners", [user1]); - obj.set("owner", user1); - obj.set("test", "test"); + obj.set('owners', [user1]); + obj.set('owner', user1); + obj.set('test', 'test'); await obj.save(); const schema = await config.database.loadSchema(); await schema.updateClass( - "AnObject", + 'AnObject', {}, { - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, protectedFields: { - "*": [], - "userField:idontexist": ["owner"], - "userField:idontexist2": ["owners"], + '*': [], + 'userField:idontexist': ['owner'], + 'userField:idontexist2': ['owners'], }, } ); - await Parse.User.logIn("user1", "password"); + await Parse.User.logIn('user1', 'password'); const objectAgain = await obj.fetch(); - expect(objectAgain.get("owners")).not.toBe(undefined); - expect(objectAgain.get("owner")).not.toBe(undefined); - expect(objectAgain.get("test")).toBe("test"); + expect(objectAgain.get('owners')).not.toBe(undefined); + expect(objectAgain.get('owner')).not.toBe(undefined); + expect(objectAgain.get('test')).toBe('test'); done(); }); }); - describe("and find", () => { - it("should allow access using single user pointer-permissions", async done => { + describe('and find', () => { + it('should allow access using single user pointer-permissions', async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object("AnObject"); - const obj2 = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); + const obj2 = new Parse.Object('AnObject'); - obj.set("owner", user1); - obj.set("test", "test"); - obj2.set("owner", user1); - obj2.set("test", "test2"); + obj.set('owner', user1); + obj.set('test', 'test'); + obj2.set('owner', user1); + obj2.set('test', 'test2'); await Parse.Object.saveAll([obj, obj2]); const schema = await config.database.loadSchema(); await schema.updateClass( - "AnObject", + 'AnObject', {}, { - get: { "*": true }, - find: { "*": true }, - protectedFields: { "*": ["owner"], "userField:owner": [] }, + get: { '*': true }, + find: { '*': true }, + protectedFields: { '*': ['owner'], 'userField:owner': [] }, } ); - await Parse.User.logIn("user1", "password"); + await Parse.User.logIn('user1', 'password'); - const q = new Parse.Query("AnObject"); + const q = new Parse.Query('AnObject'); const results = await q.find(); // sort for checking in correct order - results.sort((a, b) => a.get("test").localeCompare(b.get("test"))); + results.sort((a, b) => a.get('test').localeCompare(b.get('test'))); expect(results.length).toBe(2); - expect(results[0].get("owner").id).toBe(user1.id); - expect(results[0].get("test")).toBe("test"); - expect(results[1].get("owner").id).toBe(user1.id); - expect(results[1].get("test")).toBe("test2"); + expect(results[0].get('owner').id).toBe(user1.id); + expect(results[0].get('test')).toBe('test'); + expect(results[1].get('owner').id).toBe(user1.id); + expect(results[1].get('test')).toBe('test2'); done(); }); - it("should deny access to other users using single user pointer-permissions", async done => { + it('should deny access to other users using single user pointer-permissions', async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object("AnObject"); - const obj2 = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); + const obj2 = new Parse.Object('AnObject'); - obj.set("owner", user1); - obj.set("test", "test"); - obj2.set("owner", user1); - obj2.set("test", "test2"); + obj.set('owner', user1); + obj.set('test', 'test'); + obj2.set('owner', user1); + obj2.set('test', 'test2'); await Parse.Object.saveAll([obj, obj2]); const schema = await config.database.loadSchema(); await schema.updateClass( - "AnObject", + 'AnObject', {}, { - get: { "*": true }, - find: { "*": true }, - protectedFields: { "*": ["owner"], "userField:owner": [] }, + get: { '*': true }, + find: { '*': true }, + protectedFields: { '*': ['owner'], 'userField:owner': [] }, } ); - await Parse.User.logIn("user2", "password"); - const q = new Parse.Query("AnObject"); + await Parse.User.logIn('user2', 'password'); + const q = new Parse.Query('AnObject'); const results = await q.find(); // sort for checking in correct order - results.sort((a, b) => a.get("test").localeCompare(b.get("test"))); + results.sort((a, b) => a.get('test').localeCompare(b.get('test'))); expect(results.length).toBe(2); - expect(results[0].get("owner")).toBe(undefined); - expect(results[0].get("test")).toBe("test"); - expect(results[1].get("owner")).toBe(undefined); - expect(results[1].get("test")).toBe("test2"); + expect(results[0].get('owner')).toBe(undefined); + expect(results[0].get('test')).toBe('test'); + expect(results[1].get('owner')).toBe(undefined); + expect(results[1].get('test')).toBe('test2'); done(); }); - it("should deny access to public using single user pointer-permissions", async done => { + it('should deny access to public using single user pointer-permissions', async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object("AnObject"); - const obj2 = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); + const obj2 = new Parse.Object('AnObject'); - obj.set("owner", user1); - obj.set("test", "test"); - obj2.set("owner", user1); - obj2.set("test", "test2"); + obj.set('owner', user1); + obj.set('test', 'test'); + obj2.set('owner', user1); + obj2.set('test', 'test2'); await Parse.Object.saveAll([obj, obj2]); const schema = await config.database.loadSchema(); await schema.updateClass( - "AnObject", + 'AnObject', {}, { - get: { "*": true }, - find: { "*": true }, - protectedFields: { "*": ["owner"], "userField:owner": [] }, + get: { '*': true }, + find: { '*': true }, + protectedFields: { '*': ['owner'], 'userField:owner': [] }, } ); - const q = new Parse.Query("AnObject"); + const q = new Parse.Query('AnObject'); const results = await q.find(); // sort for checking in correct order - results.sort((a, b) => a.get("test").localeCompare(b.get("test"))); + results.sort((a, b) => a.get('test').localeCompare(b.get('test'))); expect(results.length).toBe(2); - expect(results[0].get("owner")).toBe(undefined); - expect(results[0].get("test")).toBe("test"); - expect(results[1].get("owner")).toBe(undefined); - expect(results[1].get("test")).toBe("test2"); + expect(results[0].get('owner')).toBe(undefined); + expect(results[0].get('test')).toBe('test'); + expect(results[1].get('owner')).toBe(undefined); + expect(results[1].get('test')).toBe('test2'); done(); }); - it("should allow access using user array pointer-permissions", async done => { + it('should allow access using user array pointer-permissions', async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object("AnObject"); - const obj2 = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); + const obj2 = new Parse.Object('AnObject'); - obj.set("owners", [user1, user2]); - obj.set("test", "test"); - obj2.set("owners", [user1, user2]); - obj2.set("test", "test2"); + obj.set('owners', [user1, user2]); + obj.set('test', 'test'); + obj2.set('owners', [user1, user2]); + obj2.set('test', 'test2'); await Parse.Object.saveAll([obj, obj2]); const schema = await config.database.loadSchema(); await schema.updateClass( - "AnObject", + 'AnObject', {}, { - get: { "*": true }, - find: { "*": true }, - protectedFields: { "*": ["owners"], "userField:owners": [] }, + get: { '*': true }, + find: { '*': true }, + protectedFields: { '*': ['owners'], 'userField:owners': [] }, } ); - const q = new Parse.Query("AnObject"); + const q = new Parse.Query('AnObject'); let results; - await Parse.User.logIn("user1", "password"); + await Parse.User.logIn('user1', 'password'); results = await q.find(); // sort for checking in correct order - results.sort((a, b) => a.get("test").localeCompare(b.get("test"))); + results.sort((a, b) => a.get('test').localeCompare(b.get('test'))); expect(results.length).toBe(2); - expect(results[0].get("owners")[0].id).toBe(user1.id); - expect(results[0].get("test")).toBe("test"); - expect(results[1].get("owners")[0].id).toBe(user1.id); - expect(results[1].get("test")).toBe("test2"); + expect(results[0].get('owners')[0].id).toBe(user1.id); + expect(results[0].get('test')).toBe('test'); + expect(results[1].get('owners')[0].id).toBe(user1.id); + expect(results[1].get('test')).toBe('test2'); - await Parse.User.logIn("user2", "password"); + await Parse.User.logIn('user2', 'password'); results = await q.find(); // sort for checking in correct order - results.sort((a, b) => a.get("test").localeCompare(b.get("test"))); + results.sort((a, b) => a.get('test').localeCompare(b.get('test'))); expect(results.length).toBe(2); - expect(results[0].get("owners")[1].id).toBe(user2.id); - expect(results[0].get("test")).toBe("test"); - expect(results[1].get("owners")[1].id).toBe(user2.id); - expect(results[1].get("test")).toBe("test2"); + expect(results[0].get('owners')[1].id).toBe(user2.id); + expect(results[0].get('test')).toBe('test'); + expect(results[1].get('owners')[1].id).toBe(user2.id); + expect(results[1].get('test')).toBe('test2'); done(); }); - it("should deny access to other users using user array pointer-permissions", async done => { + it('should deny access to other users using user array pointer-permissions', async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object("AnObject"); - const obj2 = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); + const obj2 = new Parse.Object('AnObject'); - obj.set("owners", [user1]); - obj.set("test", "test"); - obj2.set("owners", [user1]); - obj2.set("test", "test2"); + obj.set('owners', [user1]); + obj.set('test', 'test'); + obj2.set('owners', [user1]); + obj2.set('test', 'test2'); await Parse.Object.saveAll([obj, obj2]); const schema = await config.database.loadSchema(); await schema.updateClass( - "AnObject", + 'AnObject', {}, { - get: { "*": true }, - find: { "*": true }, - protectedFields: { "*": ["owners"], "userField:owners": [] }, + get: { '*': true }, + find: { '*': true }, + protectedFields: { '*': ['owners'], 'userField:owners': [] }, } ); - await Parse.User.logIn("user2", "password"); - const q = new Parse.Query("AnObject"); + await Parse.User.logIn('user2', 'password'); + const q = new Parse.Query('AnObject'); const results = await q.find(); // sort for checking in correct order - results.sort((a, b) => a.get("test").localeCompare(b.get("test"))); + results.sort((a, b) => a.get('test').localeCompare(b.get('test'))); expect(results.length).toBe(2); - expect(results[0].get("owners")).toBe(undefined); - expect(results[0].get("test")).toBe("test"); - expect(results[1].get("owners")).toBe(undefined); - expect(results[1].get("test")).toBe("test2"); + expect(results[0].get('owners')).toBe(undefined); + expect(results[0].get('test')).toBe('test'); + expect(results[1].get('owners')).toBe(undefined); + expect(results[1].get('test')).toBe('test2'); done(); }); - it("should deny access to public using user array pointer-permissions", async done => { + it('should deny access to public using user array pointer-permissions', async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object("AnObject"); - const obj2 = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); + const obj2 = new Parse.Object('AnObject'); - obj.set("owners", [user1, user2]); - obj.set("test", "test"); - obj2.set("owners", [user1, user2]); - obj2.set("test", "test2"); + obj.set('owners', [user1, user2]); + obj.set('test', 'test'); + obj2.set('owners', [user1, user2]); + obj2.set('test', 'test2'); await Parse.Object.saveAll([obj, obj2]); const schema = await config.database.loadSchema(); await schema.updateClass( - "AnObject", + 'AnObject', {}, { - get: { "*": true }, - find: { "*": true }, - protectedFields: { "*": ["owners"], "userField:owners": [] }, + get: { '*': true }, + find: { '*': true }, + protectedFields: { '*': ['owners'], 'userField:owners': [] }, } ); - const q = new Parse.Query("AnObject"); + const q = new Parse.Query('AnObject'); const results = await q.find(); // sort for checking in correct order - results.sort((a, b) => a.get("test").localeCompare(b.get("test"))); + results.sort((a, b) => a.get('test').localeCompare(b.get('test'))); expect(results.length).toBe(2); - expect(results[0].get("owners")).toBe(undefined); - expect(results[0].get("test")).toBe("test"); - expect(results[1].get("owners")).toBe(undefined); - expect(results[1].get("test")).toBe("test2"); + expect(results[0].get('owners')).toBe(undefined); + expect(results[0].get('test')).toBe('test'); + expect(results[1].get('owners')).toBe(undefined); + expect(results[1].get('test')).toBe('test2'); done(); }); - it("should intersect protected fields when using multiple pointer-permission fields", async done => { + it('should intersect protected fields when using multiple pointer-permission fields', async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object("AnObject"); - const obj2 = new Parse.Object("AnObject"); - - obj.set("owners", [user1]); - obj.set("owner", user1); - obj.set("test", "test"); - obj2.set("owners", [user1]); - obj2.set("test", "test2"); + const obj = new Parse.Object('AnObject'); + const obj2 = new Parse.Object('AnObject'); + + obj.set('owners', [user1]); + obj.set('owner', user1); + obj.set('test', 'test'); + obj2.set('owners', [user1]); + obj2.set('test', 'test2'); await Parse.Object.saveAll([obj, obj2]); const schema = await config.database.loadSchema(); await schema.updateClass( - "AnObject", + 'AnObject', {}, { - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, protectedFields: { - "*": ["owners", "owner", "test"], - "userField:owners": ["owners", "owner"], - "userField:owner": ["owner"], + '*': ['owners', 'owner', 'test'], + 'userField:owners': ['owners', 'owner'], + 'userField:owner': ['owner'], }, } ); // Check if protectFields from pointer-permissions got combined - await Parse.User.logIn("user1", "password"); + await Parse.User.logIn('user1', 'password'); - const q = new Parse.Query("AnObject"); + const q = new Parse.Query('AnObject'); const results = await q.find(); // sort for checking in correct order - results.sort((a, b) => a.get("test").localeCompare(b.get("test"))); + results.sort((a, b) => a.get('test').localeCompare(b.get('test'))); expect(results.length).toBe(2); - expect(results[0].get("owners").length).toBe(1); - expect(results[0].get("owner")).toBe(undefined); - expect(results[0].get("test")).toBe("test"); - expect(results[1].get("owners")).toBe(undefined); - expect(results[1].get("owner")).toBe(undefined); - expect(results[1].get("test")).toBe("test2"); + expect(results[0].get('owners').length).toBe(1); + expect(results[0].get('owner')).toBe(undefined); + expect(results[0].get('test')).toBe('test'); + expect(results[1].get('owners')).toBe(undefined); + expect(results[1].get('owner')).toBe(undefined); + expect(results[1].get('test')).toBe('test2'); done(); }); - it("should ignore pointer-permission fields not present in object", async done => { + it('should ignore pointer-permission fields not present in object', async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object("AnObject"); - const obj2 = new Parse.Object("AnObject"); - - obj.set("owners", [user1]); - obj.set("owner", user1); - obj.set("test", "test"); - obj2.set("owners", [user1]); - obj2.set("owner", user1); - obj2.set("test", "test2"); + const obj = new Parse.Object('AnObject'); + const obj2 = new Parse.Object('AnObject'); + + obj.set('owners', [user1]); + obj.set('owner', user1); + obj.set('test', 'test'); + obj2.set('owners', [user1]); + obj2.set('owner', user1); + obj2.set('test', 'test2'); await Parse.Object.saveAll([obj, obj2]); const schema = await config.database.loadSchema(); await schema.updateClass( - "AnObject", + 'AnObject', {}, { - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, protectedFields: { - "*": [], - "userField:idontexist": ["owner"], - "userField:idontexist2": ["owners"], + '*': [], + 'userField:idontexist': ['owner'], + 'userField:idontexist2': ['owners'], }, } ); - await Parse.User.logIn("user1", "password"); + await Parse.User.logIn('user1', 'password'); - const q = new Parse.Query("AnObject"); + const q = new Parse.Query('AnObject'); const results = await q.find(); // sort for checking in correct order - results.sort((a, b) => a.get("test").localeCompare(b.get("test"))); + results.sort((a, b) => a.get('test').localeCompare(b.get('test'))); expect(results.length).toBe(2); - expect(results[0].get("owners")).not.toBe(undefined); - expect(results[0].get("owner")).not.toBe(undefined); - expect(results[0].get("test")).toBe("test"); - expect(results[1].get("owners")).not.toBe(undefined); - expect(results[1].get("owner")).not.toBe(undefined); - expect(results[1].get("test")).toBe("test2"); + expect(results[0].get('owners')).not.toBe(undefined); + expect(results[0].get('owner')).not.toBe(undefined); + expect(results[0].get('test')).toBe('test'); + expect(results[1].get('owners')).not.toBe(undefined); + expect(results[1].get('owner')).not.toBe(undefined); + expect(results[1].get('test')).toBe('test2'); done(); }); - it("should filter only fields from objects not owned by the user", async done => { + it('should filter only fields from objects not owned by the user', async done => { const config = Config.get(Parse.applicationId); - const obj = new Parse.Object("AnObject"); - const obj2 = new Parse.Object("AnObject"); - const obj3 = new Parse.Object("AnObject"); - - obj.set("owner", user1); - obj.set("test", "test"); - obj2.set("owner", user2); - obj2.set("test", "test2"); - obj3.set("owner", user2); - obj3.set("test", "test3"); + const obj = new Parse.Object('AnObject'); + const obj2 = new Parse.Object('AnObject'); + const obj3 = new Parse.Object('AnObject'); + + obj.set('owner', user1); + obj.set('test', 'test'); + obj2.set('owner', user2); + obj2.set('test', 'test2'); + obj3.set('owner', user2); + obj3.set('test', 'test3'); await Parse.Object.saveAll([obj, obj2, obj3]); const schema = await config.database.loadSchema(); await schema.updateClass( - "AnObject", + 'AnObject', {}, { - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, protectedFields: { - "*": ["owner"], - "userField:owner": [], + '*': ['owner'], + 'userField:owner': [], }, } ); - const q = new Parse.Query("AnObject"); + const q = new Parse.Query('AnObject'); let results; - await Parse.User.logIn("user1", "password"); + await Parse.User.logIn('user1', 'password'); results = await q.find(); // sort for checking in correct order - results.sort((a, b) => a.get("test").localeCompare(b.get("test"))); + results.sort((a, b) => a.get('test').localeCompare(b.get('test'))); expect(results.length).toBe(3); - expect(results[0].get("owner")).not.toBe(undefined); - expect(results[0].get("test")).toBe("test"); - expect(results[1].get("owner")).toBe(undefined); - expect(results[1].get("test")).toBe("test2"); - expect(results[2].get("owner")).toBe(undefined); - expect(results[2].get("test")).toBe("test3"); + expect(results[0].get('owner')).not.toBe(undefined); + expect(results[0].get('test')).toBe('test'); + expect(results[1].get('owner')).toBe(undefined); + expect(results[1].get('test')).toBe('test2'); + expect(results[2].get('owner')).toBe(undefined); + expect(results[2].get('test')).toBe('test3'); - await Parse.User.logIn("user2", "password"); + await Parse.User.logIn('user2', 'password'); results = await q.find(); // sort for checking in correct order - results.sort((a, b) => a.get("test").localeCompare(b.get("test"))); + results.sort((a, b) => a.get('test').localeCompare(b.get('test'))); expect(results.length).toBe(3); - expect(results[0].get("owner")).toBe(undefined); - expect(results[0].get("test")).toBe("test"); - expect(results[1].get("owner")).not.toBe(undefined); - expect(results[1].get("test")).toBe("test2"); - expect(results[2].get("owner")).not.toBe(undefined); - expect(results[2].get("test")).toBe("test3"); + expect(results[0].get('owner')).toBe(undefined); + expect(results[0].get('test')).toBe('test'); + expect(results[1].get('owner')).not.toBe(undefined); + expect(results[1].get('test')).toBe('test2'); + expect(results[2].get('owner')).not.toBe(undefined); + expect(results[2].get('test')).toBe('test3'); done(); }); }); }); - describe("schema setup", () => { + describe('schema setup', () => { let object; async function initialize() { @@ -786,8 +786,8 @@ describe("ProtectedFields", function () { object = new Parse.Object(className); - object.set("revision", 0); - object.set("test", "test"); + object.set('revision', 0); + object.set('test', 'test'); await object.save(null, { useMasterKey: true }); } @@ -796,9 +796,9 @@ describe("ProtectedFields", function () { await initialize(); }); - it("should fail setting non-existing protected field", async done => { - const field = "non-existing"; - const entity = "*"; + it('should fail setting non-existing protected field', async done => { + const field = 'non-existing'; + const entity = '*'; await expectAsync( updateCLP({ @@ -815,23 +815,23 @@ describe("ProtectedFields", function () { done(); }); - it("should allow setting authenticated", async () => { + it('should allow setting authenticated', async () => { await expectAsync( updateCLP({ protectedFields: { - authenticated: ["test"], + authenticated: ['test'], }, }) ).toBeResolved(); }); - it("should not allow protecting default fields", async () => { - const defaultFields = ["objectId", "createdAt", "updatedAt", "ACL"]; + it('should not allow protecting default fields', async () => { + const defaultFields = ['objectId', 'createdAt', 'updatedAt', 'ACL']; for (const field of defaultFields) { await expectAsync( updateCLP({ protectedFields: { - "*": [field], + '*': [field], }, }) ).toBeRejectedWith( @@ -844,7 +844,7 @@ describe("ProtectedFields", function () { }); }); - describe("targeting public access", () => { + describe('targeting public access', () => { let obj1; async function initialize() { @@ -852,9 +852,9 @@ describe("ProtectedFields", function () { obj1 = new Parse.Object(className); - obj1.set("foo", "foo"); - obj1.set("bar", "bar"); - obj1.set("qux", "qux"); + obj1.set('foo', 'foo'); + obj1.set('bar', 'bar'); + obj1.set('qux', 'qux'); await obj1.save(null, { useMasterKey: true, @@ -865,59 +865,59 @@ describe("ProtectedFields", function () { await initialize(); }); - it("should hide field", async done => { + it('should hide field', async done => { await updateCLP({ - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, protectedFields: { - "*": ["foo"], + '*': ['foo'], }, }); // unauthenticated const object = await obj1.fetch(); - expect(object.get("foo")).toBe(undefined); - expect(object.get("bar")).toBeDefined(); - expect(object.get("qux")).toBeDefined(); + expect(object.get('foo')).toBe(undefined); + expect(object.get('bar')).toBeDefined(); + expect(object.get('qux')).toBeDefined(); done(); }); - it("should hide mutiple fields", async done => { + it('should hide mutiple fields', async done => { await updateCLP({ - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, protectedFields: { - "*": ["foo", "bar"], + '*': ['foo', 'bar'], }, }); // unauthenticated const object = await obj1.fetch(); - expect(object.get("foo")).toBe(undefined); - expect(object.get("bar")).toBe(undefined); - expect(object.get("qux")).toBeDefined(); + expect(object.get('foo')).toBe(undefined); + expect(object.get('bar')).toBe(undefined); + expect(object.get('qux')).toBeDefined(); done(); }); - it("should not hide any fields when set as empty array", async done => { + it('should not hide any fields when set as empty array', async done => { await updateCLP({ - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, protectedFields: { - "*": [], + '*': [], }, }); // unauthenticated const object = await obj1.fetch(); - expect(object.get("foo")).toBeDefined(); - expect(object.get("bar")).toBeDefined(); - expect(object.get("qux")).toBeDefined(); + expect(object.get('foo')).toBeDefined(); + expect(object.get('bar')).toBeDefined(); + expect(object.get('qux')).toBeDefined(); expect(object.id).toBeDefined(); expect(object.createdAt).toBeDefined(); expect(object.updatedAt).toBeDefined(); @@ -927,7 +927,7 @@ describe("ProtectedFields", function () { }); }); - describe("targeting authenticated", () => { + describe('targeting authenticated', () => { /** * is **owner** of: _obj1_ * @@ -962,20 +962,20 @@ describe("ProtectedFields", function () { await Parse.User.logOut(); [user1, user2] = await Promise.all([ - createUser("user1"), - createUser("user2"), + createUser('user1'), + createUser('user2'), ]); obj1 = new Parse.Object(className); obj2 = new Parse.Object(className); - obj1.set("owner", user1); - obj1.set("testers", [user1, user2]); - obj1.set("test", "test"); + obj1.set('owner', user1); + obj1.set('testers', [user1, user2]); + obj1.set('test', 'test'); - obj2.set("owner", user2); - obj2.set("testers", [user1]); - obj2.set("test", "test"); + obj2.set('owner', user2); + obj2.set('testers', [user1]); + obj2.set('test', 'test'); await Parse.Object.saveAll([obj1, obj2], { useMasterKey: true, @@ -986,10 +986,10 @@ describe("ProtectedFields", function () { await initialize(); }); - it("should not hide any fields when set as empty array", async done => { + it('should not hide any fields when set as empty array', async done => { await updateCLP({ - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, protectedFields: { authenticated: [], }, @@ -1000,9 +1000,9 @@ describe("ProtectedFields", function () { const object = await obj1.fetch(); - expect(object.get("owner")).toBeDefined(); - expect(object.get("testers")).toBeDefined(); - expect(object.get("test")).toBeDefined(); + expect(object.get('owner')).toBeDefined(); + expect(object.get('testers')).toBeDefined(); + expect(object.get('test')).toBeDefined(); expect(object.id).toBeDefined(); expect(object.createdAt).toBeDefined(); expect(object.updatedAt).toBeDefined(); @@ -1011,36 +1011,36 @@ describe("ProtectedFields", function () { done(); }); - it("should hide fields for authenticated users only (* not set)", async done => { + it('should hide fields for authenticated users only (* not set)', async done => { await updateCLP({ - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, protectedFields: { - authenticated: ["test"], + authenticated: ['test'], }, }); // not authenticated const objectNonAuth = await obj1.fetch(); - expect(objectNonAuth.get("test")).toBeDefined(); + expect(objectNonAuth.get('test')).toBeDefined(); // authenticated await logIn(user1); const object = await obj1.fetch(); - expect(object.get("test")).toBe(undefined); + expect(object.get('test')).toBe(undefined); done(); }); - it("should intersect public and auth for authenticated user", async done => { + it('should intersect public and auth for authenticated user', async done => { await updateCLP({ - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, protectedFields: { - "*": ["owner", "testers"], - authenticated: ["testers"], + '*': ['owner', 'testers'], + authenticated: ['testers'], }, }); @@ -1050,25 +1050,25 @@ describe("ProtectedFields", function () { // ( {A,B} intersect {B} ) == {B} - expect(objectAuth.get("testers")).not.toBeDefined( - "Should not be visible - protected for * and authenticated" + expect(objectAuth.get('testers')).not.toBeDefined( + 'Should not be visible - protected for * and authenticated' ); - expect(objectAuth.get("test")).toBeDefined( - "Should be visible - not protected for everyone (* and authenticated)" + expect(objectAuth.get('test')).toBeDefined( + 'Should be visible - not protected for everyone (* and authenticated)' ); - expect(objectAuth.get("owner")).toBeDefined( - "Should be visible - not protected for authenticated" + expect(objectAuth.get('owner')).toBeDefined( + 'Should be visible - not protected for authenticated' ); done(); }); - it("should have higher prio than public for logged in users (intersect)", async done => { + it('should have higher prio than public for logged in users (intersect)', async done => { await updateCLP({ - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, protectedFields: { - "*": ["test"], + '*': ['test'], authenticated: [], }, }); @@ -1076,33 +1076,33 @@ describe("ProtectedFields", function () { await logIn(user1); const object = await obj1.fetch(); - expect(object.get("test")).toBe("test"); + expect(object.get('test')).toBe('test'); done(); }); - it("should have no effect on unauthenticated users (public not set)", async done => { + it('should have no effect on unauthenticated users (public not set)', async done => { await updateCLP({ - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, protectedFields: { - authenticated: ["test"], + authenticated: ['test'], }, }); // unauthenticated, protected const objectNonAuth = await obj1.fetch(); - expect(objectNonAuth.get("test")).toBe("test"); + expect(objectNonAuth.get('test')).toBe('test'); done(); }); - it("should protect multiple fields for authenticated users", async done => { + it('should protect multiple fields for authenticated users', async done => { await updateCLP({ - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, protectedFields: { - authenticated: ["test", "owner"], + authenticated: ['test', 'owner'], }, }); @@ -1110,23 +1110,23 @@ describe("ProtectedFields", function () { await logIn(user1); const object = await obj1.fetch(); - expect(object.get("test")).toBe(undefined); - expect(object.get("owner")).toBe(undefined); + expect(object.get('test')).toBe(undefined); + expect(object.get('owner')).toBe(undefined); done(); }); - it("should not be affected by rules not applicable to user (smoke)", async done => { + it('should not be affected by rules not applicable to user (smoke)', async done => { const role = await createRole({ users: user1 }); - const roleName = role.get("name"); + const roleName = role.get('name'); await updateCLP({ - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, protectedFields: { - authenticated: ["owner", "testers"], - [`role:${roleName}`]: ["test"], - "userField:owner": [], + authenticated: ['owner', 'testers'], + [`role:${roleName}`]: ['test'], + 'userField:owner': [], [user1.id]: [], }, }); @@ -1135,15 +1135,15 @@ describe("ProtectedFields", function () { await logIn(user2); const objectNotOwned = await obj1.fetch(); - expect(objectNotOwned.get("owner")).toBe(undefined); - expect(objectNotOwned.get("testers")).toBe(undefined); - expect(objectNotOwned.get("test")).toBeDefined(); + expect(objectNotOwned.get('owner')).toBe(undefined); + expect(objectNotOwned.get('testers')).toBe(undefined); + expect(objectNotOwned.get('test')).toBeDefined(); done(); }); }); - describe("targeting roles", () => { + describe('targeting roles', () => { let user1, user2; /** @@ -1164,20 +1164,20 @@ describe("ProtectedFields", function () { await Config.get(Parse.applicationId).schemaCache.clear(); [user1, user2] = await Promise.all([ - createUser("user1"), - createUser("user2"), + createUser('user1'), + createUser('user2'), ]); obj1 = new Parse.Object(className); obj2 = new Parse.Object(className); - obj1.set("owner", user1); - obj1.set("testers", [user1, user2]); - obj1.set("test", "test"); + obj1.set('owner', user1); + obj1.set('testers', [user1, user2]); + obj1.set('test', 'test'); - obj2.set("owner", user2); - obj2.set("testers", [user1]); - obj2.set("test", "test"); + obj2.set('owner', user2); + obj2.set('testers', [user1]); + obj2.set('test', 'test'); await Parse.Object.saveAll([obj1, obj2], { useMasterKey: true, @@ -1188,39 +1188,39 @@ describe("ProtectedFields", function () { await initialize(); }); - it("should hide field when user belongs to a role", async done => { + it('should hide field when user belongs to a role', async done => { const role = await createRole({ users: user1 }); - const roleName = role.get("name"); + const roleName = role.get('name'); await updateCLP({ protectedFields: { - [`role:${roleName}`]: ["test"], + [`role:${roleName}`]: ['test'], }, - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, }); // user has role await logIn(user1); const object = await obj1.fetch(); - expect(object.get("test")).toBe(undefined); // field protected - expect(object.get("owner")).toBeDefined(); - expect(object.get("testers")).toBeDefined(); + expect(object.get('test')).toBe(undefined); // field protected + expect(object.get('owner')).toBeDefined(); + expect(object.get('testers')).toBeDefined(); done(); }); - it("should not hide any fields when set as empty array", async done => { + it('should not hide any fields when set as empty array', async done => { const role = await createRole({ users: user1 }); - const roleName = role.get("name"); + const roleName = role.get('name'); await updateCLP({ protectedFields: { [`role:${roleName}`]: [], }, - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, }); // user has role @@ -1228,9 +1228,9 @@ describe("ProtectedFields", function () { const object = await obj1.fetch(); - expect(object.get("owner")).toBeDefined(); - expect(object.get("testers")).toBeDefined(); - expect(object.get("test")).toBeDefined(); + expect(object.get('owner')).toBeDefined(); + expect(object.get('testers')).toBeDefined(); + expect(object.get('test')).toBeDefined(); expect(object.id).toBeDefined(); expect(object.createdAt).toBeDefined(); expect(object.updatedAt).toBeDefined(); @@ -1239,15 +1239,15 @@ describe("ProtectedFields", function () { done(); }); - it("should hide multiple fields when user belongs to a role", async done => { + it('should hide multiple fields when user belongs to a role', async done => { const role = await createRole({ users: user1 }); - const roleName = role.get("name"); + const roleName = role.get('name'); await updateCLP({ - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, protectedFields: { - [`role:${roleName}`]: ["test", "owner"], + [`role:${roleName}`]: ['test', 'owner'], }, }); @@ -1256,28 +1256,28 @@ describe("ProtectedFields", function () { const object = await obj1.fetch(); - expect(object.get("test")).toBe( + expect(object.get('test')).toBe( undefined, - "Field should not be visible - protected by role" + 'Field should not be visible - protected by role' ); - expect(object.get("owner")).toBe( + expect(object.get('owner')).toBe( undefined, - "Field should not be visible - protected by role" + 'Field should not be visible - protected by role' ); - expect(object.get("testers")).toBeDefined(); + expect(object.get('testers')).toBeDefined(); done(); }); - it("should not protect when user does not belong to a role", async done => { + it('should not protect when user does not belong to a role', async done => { const role = await createRole({ users: user1 }); - const roleName = role.get("name"); + const roleName = role.get('name'); await updateCLP({ - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, protectedFields: { - [`role:${roleName}`]: ["test", "owner"], + [`role:${roleName}`]: ['test', 'owner'], }, }); @@ -1285,26 +1285,26 @@ describe("ProtectedFields", function () { await logIn(user2); const object = await obj1.fetch(); - expect(object.get("test")).toBeDefined(); - expect(object.get("owner")).toBeDefined(); - expect(object.get("testers")).toBeDefined(); + expect(object.get('test')).toBeDefined(); + expect(object.get('owner')).toBeDefined(); + expect(object.get('testers')).toBeDefined(); done(); }); - it("should intersect protected fields when user belongs to multiple roles", async done => { + it('should intersect protected fields when user belongs to multiple roles', async done => { const role1 = await createRole({ users: user1 }); const role2 = await createRole({ users: user1 }); - const role1name = role1.get("name"); - const role2name = role2.get("name"); + const role1name = role1.get('name'); + const role2name = role2.get('name'); await updateCLP({ - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, protectedFields: { - [`role:${role1name}`]: ["owner"], - [`role:${role2name}`]: ["test", "owner"], + [`role:${role1name}`]: ['owner'], + [`role:${role2name}`]: ['test', 'owner'], }, }); @@ -1313,49 +1313,49 @@ describe("ProtectedFields", function () { const object = await obj1.fetch(); // "owner" is a result of intersection - expect(object.get("owner")).toBe( + expect(object.get('owner')).toBe( undefined, - "Must not be visible - protected for all roles the user belongs to" + 'Must not be visible - protected for all roles the user belongs to' ); - expect(object.get("test")).toBeDefined( - "Has to be visible - is not protected for users with role1" + expect(object.get('test')).toBeDefined( + 'Has to be visible - is not protected for users with role1' ); done(); }); - it("should intersect protected fields when user belongs to multiple roles hierarchy", async done => { + it('should intersect protected fields when user belongs to multiple roles hierarchy', async done => { const admin = await createRole({ users: user1, - roleName: "admin", + roleName: 'admin', }); const moder = await createRole({ users: [user1, user2], - roleName: "moder", + roleName: 'moder', }); const tester = await createRole({ - roleName: "tester", + roleName: 'tester', }); // admin supersets moder role - moder.relation("roles").add(admin); + moder.relation('roles').add(admin); await moder.save(null, { useMasterKey: true }); - tester.relation("roles").add(moder); + tester.relation('roles').add(moder); await tester.save(null, { useMasterKey: true }); - const roleAdmin = `role:${admin.get("name")}`; - const roleModer = `role:${moder.get("name")}`; - const roleTester = `role:${tester.get("name")}`; + const roleAdmin = `role:${admin.get('name')}`; + const roleModer = `role:${moder.get('name')}`; + const roleTester = `role:${tester.get('name')}`; await updateCLP({ - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, protectedFields: { [roleAdmin]: [], - [roleModer]: ["owner"], - [roleTester]: ["test", "owner"], + [roleModer]: ['owner'], + [roleTester]: ['test', 'owner'], }, }); @@ -1364,11 +1364,11 @@ describe("ProtectedFields", function () { const object = await obj1.fetch(); // being admin makes all fields visible - expect(object.get("test")).toBeDefined( - "Should be visible - admin role explicitly removes protection for all fields ( [] )" + expect(object.get('test')).toBeDefined( + 'Should be visible - admin role explicitly removes protection for all fields ( [] )' ); - expect(object.get("owner")).toBeDefined( - "Should be visible - admin role explicitly removes protection for all fields ( [] )" + expect(object.get('owner')).toBeDefined( + 'Should be visible - admin role explicitly removes protection for all fields ( [] )' ); // user2 has moder & tester role, moder includes tester. @@ -1376,26 +1376,26 @@ describe("ProtectedFields", function () { const objectAgain = await obj1.fetch(); // being moder allows "test" field - expect(objectAgain.get("owner")).toBe( + expect(objectAgain.get('owner')).toBe( undefined, '"owner" should not be visible - protected for each role user belongs to' ); - expect(objectAgain.get("test")).toBeDefined( + expect(objectAgain.get('test')).toBeDefined( 'Should be visible - moder role does not protect "test" field' ); done(); }); - it("should be able to clear protected fields for role (protected for authenticated)", async done => { + it('should be able to clear protected fields for role (protected for authenticated)', async done => { const role = await createRole({ users: user1 }); - const roleName = role.get("name"); + const roleName = role.get('name'); await updateCLP({ - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, protectedFields: { - authenticated: ["test"], + authenticated: ['test'], [`role:${roleName}`]: [], }, }); @@ -1403,21 +1403,21 @@ describe("ProtectedFields", function () { // user has role, test field visible await logIn(user1); const object = await obj1.fetch(); - expect(object.get("test")).toBe("test"); + expect(object.get('test')).toBe('test'); done(); }); - it("should determine protectedFields as intersection of field sets for public and role", async done => { + it('should determine protectedFields as intersection of field sets for public and role', async done => { const role = await createRole({ users: user1 }); - const roleName = role.get("name"); + const roleName = role.get('name'); await updateCLP({ - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, protectedFields: { - "*": ["test", "owner"], - [`role:${roleName}`]: ["owner", "testers"], + '*': ['test', 'owner'], + [`role:${roleName}`]: ['owner', 'testers'], }, }); @@ -1425,22 +1425,22 @@ describe("ProtectedFields", function () { await logIn(user1); const object = await obj1.fetch(); - expect(object.get("test")).toBeDefined( + expect(object.get('test')).toBeDefined( 'Should be visible - "test" is not protected for role user belongs to' ); - expect(object.get("testers")).toBeDefined( + expect(object.get('testers')).toBeDefined( 'Should be visible - "testers" is allowed for everyone (*)' ); - expect(object.get("owner")).toBe( + expect(object.get('owner')).toBe( undefined, 'Should not be visible - "test" is not allowed for both public(*) and role' ); done(); }); - it("should be determined as an intersection of protecedFields for authenticated and role", async done => { + it('should be determined as an intersection of protecedFields for authenticated and role', async done => { const role = await createRole({ users: user1 }); - const roleName = role.get("name"); + const roleName = role.get('name'); // this is an example of misunderstood configuration. // If you allow (== do not restrict) some field for broader audience @@ -1448,11 +1448,11 @@ describe("ProtectedFields", function () { // it's not possible to narrow by protecting field for a role. // You'd have to protect it for 'authenticated' as well. await updateCLP({ - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, protectedFields: { - authenticated: ["test"], - [`role:${roleName}`]: ["owner"], + authenticated: ['test'], + [`role:${roleName}`]: ['owner'], }, }); @@ -1461,26 +1461,26 @@ describe("ProtectedFields", function () { const object = await obj1.fetch(); // - expect(object.get("test")).toBeDefined( + expect(object.get('test')).toBeDefined( "Being both auhenticated and having a role leads to clearing protection on 'test' (by role rules)" ); - expect(object.get("owner")).toBeDefined( + expect(object.get('owner')).toBeDefined( 'All authenticated users allowed to see "owner"' ); - expect(object.get("testers")).toBeDefined(); + expect(object.get('testers')).toBeDefined(); done(); }); - it("should not hide fields when user does not belong to a role protectedFields set for", async done => { + it('should not hide fields when user does not belong to a role protectedFields set for', async done => { const role = await createRole({ users: user2 }); - const roleName = role.get("name"); + const roleName = role.get('name'); await updateCLP({ - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, protectedFields: { - [`role:${roleName}`]: ["test"], + [`role:${roleName}`]: ['test'], }, }); @@ -1490,15 +1490,15 @@ describe("ProtectedFields", function () { await logIn(user1); const object = await obj1.fetch(); - expect(object.get("test")).toBeDefined( - "Field should be visible - user belongs to a role that has no protectedFields set" + expect(object.get('test')).toBeDefined( + 'Field should be visible - user belongs to a role that has no protectedFields set' ); done(); }); }); - describe("using pointer-fields and queries with keys projection", () => { + describe('using pointer-fields and queries with keys projection', () => { /* * Pointer variant ("userField:column") relies on User ids * returned after query executed (hides fields before sending it to client) @@ -1523,23 +1523,23 @@ describe("ProtectedFields", function () { async function initialize() { await Config.get(Parse.applicationId).schemaCache.clear(); - user1 = await createUser("user1"); + user1 = await createUser('user1'); user1 = await logIn(user1); // await user1.fetch(); obj = new Parse.Object(className); - obj.set("owner", user1); - obj.set("field", "field"); - obj.set("test", "test"); + obj.set('owner', user1); + obj.set('field', 'field'); + obj.set('test', 'test'); await Parse.Object.saveAll([obj], { useMasterKey: true }); headers = { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Rest-API-Key": "rest", - "Content-Type": "application/json", - "X-Parse-Session-Token": user1.getSessionToken(), + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Rest-API-Key': 'rest', + 'Content-Type': 'application/json', + 'X-Parse-Session-Token': user1.getSessionToken(), }; } @@ -1547,53 +1547,53 @@ describe("ProtectedFields", function () { await initialize(); }); - it("should be enforced regardless of pointer-field being included in keys (select)", async done => { + it('should be enforced regardless of pointer-field being included in keys (select)', async done => { await updateCLP({ - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, protectedFields: { - "*": ["field", "test"], - "userField:owner": [], + '*': ['field', 'test'], + 'userField:owner': [], }, }); - const query = new Parse.Query("AnObject"); - query.select("field", "test"); + const query = new Parse.Query('AnObject'); + query.select('field', 'test'); const object = await query.get(obj.id); - expect(object.get("field")).toBe("field"); - expect(object.get("test")).toBe("test"); + expect(object.get('field')).toBe('field'); + expect(object.get('test')).toBe('test'); done(); }); - it("should protect fields for query where pointer field is not included via keys (REST GET)", async done => { + it('should protect fields for query where pointer field is not included via keys (REST GET)', async done => { const obj = new Parse.Object(className); - obj.set("owner", user1); - obj.set("field", "field"); - obj.set("test", "test"); + obj.set('owner', user1); + obj.set('field', 'field'); + obj.set('test', 'test'); await Parse.Object.saveAll([obj], { useMasterKey: true }); await updateCLP({ - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, protectedFields: { - "*": ["field", "test"], - "userField:owner": ["test"], + '*': ['field', 'test'], + 'userField:owner': ['test'], }, }); const { data: object } = await request({ url: `${Parse.serverURL}/classes/${className}/${obj.id}`, qs: { - keys: "field,test", + keys: 'field,test', }, headers: headers, }); expect(object.field).toBe( - "field", + 'field', 'Should BE in response - not protected by "userField:owner"' ); expect(object.test).toBe( @@ -1607,30 +1607,30 @@ describe("ProtectedFields", function () { done(); }); - it("should protect fields for query where pointer field is not included via keys (REST FIND)", async done => { + it('should protect fields for query where pointer field is not included via keys (REST FIND)', async done => { const obj = new Parse.Object(className); - obj.set("owner", user1); - obj.set("field", "field"); - obj.set("test", "test"); + obj.set('owner', user1); + obj.set('field', 'field'); + obj.set('test', 'test'); await Parse.Object.saveAll([obj], { useMasterKey: true }); await obj.fetch(); await updateCLP({ - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, protectedFields: { - "*": ["field", "test"], - "userField:owner": ["test"], + '*': ['field', 'test'], + 'userField:owner': ['test'], }, }); const { data } = await request({ url: `${Parse.serverURL}/classes/${className}`, qs: { - keys: "field,test", + keys: 'field,test', where: JSON.stringify({ objectId: obj.id }), }, headers, @@ -1639,7 +1639,7 @@ describe("ProtectedFields", function () { const object = data.results[0]; expect(object.field).toBe( - "field", + 'field', 'Should be in response - not protected by "userField:owner"' ); expect(object.test).toBe( @@ -1653,52 +1653,52 @@ describe("ProtectedFields", function () { done(); }); - it("should protect fields for query where pointer field is in excludeKeys (REST GET)", async done => { + it('should protect fields for query where pointer field is in excludeKeys (REST GET)', async done => { await updateCLP({ - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, protectedFields: { - "*": ["field", "test"], - "userField:owner": ["test"], + '*': ['field', 'test'], + 'userField:owner': ['test'], }, }); const { data: object } = await request({ qs: { - excludeKeys: "owner", + excludeKeys: 'owner', }, headers, url: `${Parse.serverURL}/classes/${className}/${obj.id}`, }); expect(object.field).toBe( - "field", + 'field', 'Should be in response - not protected by "userField:owner"' ); - expect(object["test"]).toBe( + expect(object['test']).toBe( undefined, 'Should not be in response - protected by "userField:owner"' ); - expect(object["owner"]).toBe( + expect(object['owner']).toBe( undefined, 'Should not be in response - not included in "keys"' ); done(); }); - it("should protect fields for query where pointer field is in excludedKeys (REST FIND)", async done => { + it('should protect fields for query where pointer field is in excludedKeys (REST FIND)', async done => { await updateCLP({ protectedFields: { - "*": ["field", "test"], - "userField:owner": ["test"], + '*': ['field', 'test'], + 'userField:owner': ['test'], }, - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, }); const { data } = await request({ qs: { - excludeKeys: "owner", + excludeKeys: 'owner', where: JSON.stringify({ objectId: obj.id }), }, headers, @@ -1708,7 +1708,7 @@ describe("ProtectedFields", function () { const object = data.results[0]; expect(object.field).toBe( - "field", + 'field', 'Should be in response - not protected by "userField:owner"' ); expect(object.test).toBe( @@ -1722,25 +1722,25 @@ describe("ProtectedFields", function () { done(); }); - xit("todo: should be enforced regardless of pointer-field being excluded", async done => { + xit('todo: should be enforced regardless of pointer-field being excluded', async done => { await updateCLP({ - get: { "*": true }, - find: { "*": true }, + get: { '*': true }, + find: { '*': true }, protectedFields: { - "*": ["field", "test"], - "userField:owner": [], + '*': ['field', 'test'], + 'userField:owner': [], }, }); - const query = new Parse.Query("AnObject"); + const query = new Parse.Query('AnObject'); /* TODO: this has some caching problems on JS-SDK (2.11.) side */ // query.exclude('owner') const object = await query.get(obj.id); - expect(object.get("field")).toBe("field"); - expect(object.get("test")).toBe("test"); - expect(object.get("owner")).toBe(undefined); + expect(object.get('field')).toBe('field'); + expect(object.get('test')).toBe('test'); + expect(object.get('owner')).toBe(undefined); done(); }); }); diff --git a/spec/PublicAPI.spec.js b/spec/PublicAPI.spec.js index 4de3e3c10f..4ee8bbd863 100644 --- a/spec/PublicAPI.spec.js +++ b/spec/PublicAPI.spec.js @@ -1,4 +1,4 @@ -const req = require("../lib/request"); +const req = require('../lib/request'); const request = function (url, callback) { return req({ @@ -9,20 +9,20 @@ const request = function (url, callback) { ); }; -describe("public API", () => { - it("should return missing token error on ajax request without token provided", async () => { +describe('public API', () => { + it('should return missing token error on ajax request without token provided', async () => { await reconfigureServer({ - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); try { await req({ - method: "POST", - url: "http://localhost:8378/1/apps/test/request_password_reset", + method: 'POST', + url: 'http://localhost:8378/1/apps/test/request_password_reset', body: `new_password=user1&token=`, headers: { - "Content-Type": "application/x-www-form-urlencoded", - "X-Requested-With": "XMLHttpRequest", + 'Content-Type': 'application/x-www-form-urlencoded', + 'X-Requested-With': 'XMLHttpRequest', }, followRedirects: false, }); @@ -32,19 +32,19 @@ describe("public API", () => { } }); - it("should return missing password error on ajax request without password provided", async () => { + it('should return missing password error on ajax request without password provided', async () => { await reconfigureServer({ - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); try { await req({ - method: "POST", - url: "http://localhost:8378/1/apps/test/request_password_reset", + method: 'POST', + url: 'http://localhost:8378/1/apps/test/request_password_reset', body: `new_password=&token=132414`, headers: { - "Content-Type": "application/x-www-form-urlencoded", - "X-Requested-With": "XMLHttpRequest", + 'Content-Type': 'application/x-www-form-urlencoded', + 'X-Requested-With': 'XMLHttpRequest', }, followRedirects: false, }); @@ -54,9 +54,9 @@ describe("public API", () => { } }); - it("should get invalid_link.html", done => { + it('should get invalid_link.html', done => { request( - "http://localhost:8378/1/apps/invalid_link.html", + 'http://localhost:8378/1/apps/invalid_link.html', (err, httpResponse) => { expect(httpResponse.status).toBe(200); done(); @@ -64,13 +64,13 @@ describe("public API", () => { ); }); - it("should get choose_password", done => { + it('should get choose_password', done => { reconfigureServer({ - appName: "unused", - publicServerURL: "http://localhost:8378/1", + appName: 'unused', + publicServerURL: 'http://localhost:8378/1', }).then(() => { request( - "http://localhost:8378/1/apps/choose_password?id=test", + 'http://localhost:8378/1/apps/choose_password?id=test', (err, httpResponse) => { expect(httpResponse.status).toBe(200); done(); @@ -79,9 +79,9 @@ describe("public API", () => { }); }); - it("should get verify_email_success.html", done => { + it('should get verify_email_success.html', done => { request( - "http://localhost:8378/1/apps/verify_email_success.html", + 'http://localhost:8378/1/apps/verify_email_success.html', (err, httpResponse) => { expect(httpResponse.status).toBe(200); done(); @@ -89,9 +89,9 @@ describe("public API", () => { ); }); - it("should get password_reset_success.html", done => { + it('should get password_reset_success.html', done => { request( - "http://localhost:8378/1/apps/password_reset_success.html", + 'http://localhost:8378/1/apps/password_reset_success.html', (err, httpResponse) => { expect(httpResponse.status).toBe(200); done(); @@ -100,13 +100,13 @@ describe("public API", () => { }); }); -describe("public API without publicServerURL", () => { +describe('public API without publicServerURL', () => { beforeEach(async () => { - await reconfigureServer({ appName: "unused" }); + await reconfigureServer({ appName: 'unused' }); }); - it("should get 404 on verify_email", done => { + it('should get 404 on verify_email', done => { request( - "http://localhost:8378/1/apps/test/verify_email", + 'http://localhost:8378/1/apps/test/verify_email', (err, httpResponse) => { expect(httpResponse.status).toBe(404); done(); @@ -114,9 +114,9 @@ describe("public API without publicServerURL", () => { ); }); - it("should get 404 choose_password", done => { + it('should get 404 choose_password', done => { request( - "http://localhost:8378/1/apps/choose_password?id=test", + 'http://localhost:8378/1/apps/choose_password?id=test', (err, httpResponse) => { expect(httpResponse.status).toBe(404); done(); @@ -124,9 +124,9 @@ describe("public API without publicServerURL", () => { ); }); - it("should get 404 on request_password_reset", done => { + it('should get 404 on request_password_reset', done => { request( - "http://localhost:8378/1/apps/test/request_password_reset", + 'http://localhost:8378/1/apps/test/request_password_reset', (err, httpResponse) => { expect(httpResponse.status).toBe(404); done(); @@ -135,14 +135,14 @@ describe("public API without publicServerURL", () => { }); }); -describe("public API supplied with invalid application id", () => { +describe('public API supplied with invalid application id', () => { beforeEach(async () => { - await reconfigureServer({ appName: "unused" }); + await reconfigureServer({ appName: 'unused' }); }); - it("should get 403 on verify_email", done => { + it('should get 403 on verify_email', done => { request( - "http://localhost:8378/1/apps/invalid/verify_email", + 'http://localhost:8378/1/apps/invalid/verify_email', (err, httpResponse) => { expect(httpResponse.status).toBe(403); done(); @@ -150,9 +150,9 @@ describe("public API supplied with invalid application id", () => { ); }); - it("should get 403 choose_password", done => { + it('should get 403 choose_password', done => { request( - "http://localhost:8378/1/apps/choose_password?id=invalid", + 'http://localhost:8378/1/apps/choose_password?id=invalid', (err, httpResponse) => { expect(httpResponse.status).toBe(403); done(); @@ -160,9 +160,9 @@ describe("public API supplied with invalid application id", () => { ); }); - it("should get 403 on get of request_password_reset", done => { + it('should get 403 on get of request_password_reset', done => { request( - "http://localhost:8378/1/apps/invalid/request_password_reset", + 'http://localhost:8378/1/apps/invalid/request_password_reset', (err, httpResponse) => { expect(httpResponse.status).toBe(403); done(); @@ -170,19 +170,19 @@ describe("public API supplied with invalid application id", () => { ); }); - it("should get 403 on post of request_password_reset", done => { + it('should get 403 on post of request_password_reset', done => { req({ - url: "http://localhost:8378/1/apps/invalid/request_password_reset", - method: "POST", + url: 'http://localhost:8378/1/apps/invalid/request_password_reset', + method: 'POST', }).then(done.fail, httpResponse => { expect(httpResponse.status).toBe(403); done(); }); }); - it("should get 403 on resendVerificationEmail", done => { + it('should get 403 on resendVerificationEmail', done => { request( - "http://localhost:8378/1/apps/invalid/resend_verification_email", + 'http://localhost:8378/1/apps/invalid/resend_verification_email', (err, httpResponse) => { expect(httpResponse.status).toBe(403); done(); diff --git a/spec/PurchaseValidation.spec.js b/spec/PurchaseValidation.spec.js index 2bc1d16881..6350cabdf0 100644 --- a/spec/PurchaseValidation.spec.js +++ b/spec/PurchaseValidation.spec.js @@ -1,74 +1,74 @@ -const request = require("../lib/request"); +const request = require('../lib/request'); function createProduct() { const file = new Parse.File( - "name", + 'name', { - base64: new Buffer("download_file", "utf-8").toString("base64"), + base64: new Buffer('download_file', 'utf-8').toString('base64'), }, - "text" + 'text' ); return file.save().then(function () { - const product = new Parse.Object("_Product"); + const product = new Parse.Object('_Product'); product.set({ download: file, icon: file, - title: "a product", - subtitle: "a product", + title: 'a product', + subtitle: 'a product', order: 1, - productIdentifier: "a-product", + productIdentifier: 'a-product', }); return product.save(); }); } -describe("test validate_receipt endpoint", () => { +describe('test validate_receipt endpoint', () => { beforeEach(async () => { await createProduct(); }); - it("should bypass appstore validation", async () => { + it('should bypass appstore validation', async () => { const httpResponse = await request({ headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, - method: "POST", - url: "http://localhost:8378/1/validate_purchase", + method: 'POST', + url: 'http://localhost:8378/1/validate_purchase', body: { - productIdentifier: "a-product", + productIdentifier: 'a-product', receipt: { - __type: "Bytes", - base64: new Buffer("receipt", "utf-8").toString("base64"), + __type: 'Bytes', + base64: new Buffer('receipt', 'utf-8').toString('base64'), }, bypassAppStoreValidation: true, }, }); const body = httpResponse.data; - if (typeof body != "object") { - fail("Body is not an object"); + if (typeof body != 'object') { + fail('Body is not an object'); } else { - expect(body.__type).toEqual("File"); + expect(body.__type).toEqual('File'); const url = body.url; const otherResponse = await request({ url: url, }); - expect(otherResponse.text).toBe("download_file"); + expect(otherResponse.text).toBe('download_file'); } }); - it("should fail for missing receipt", async () => { + it('should fail for missing receipt', async () => { const response = await request({ headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, - url: "http://localhost:8378/1/validate_purchase", - method: "POST", + url: 'http://localhost:8378/1/validate_purchase', + method: 'POST', body: { - productIdentifier: "a-product", + productIdentifier: 'a-product', bypassAppStoreValidation: true, }, }).then(fail, res => res); @@ -76,19 +76,19 @@ describe("test validate_receipt endpoint", () => { expect(body.code).toEqual(Parse.Error.INVALID_JSON); }); - it("should fail for missing product identifier", async () => { + it('should fail for missing product identifier', async () => { const response = await request({ headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, - url: "http://localhost:8378/1/validate_purchase", - method: "POST", + url: 'http://localhost:8378/1/validate_purchase', + method: 'POST', body: { receipt: { - __type: "Bytes", - base64: new Buffer("receipt", "utf-8").toString("base64"), + __type: 'Bytes', + base64: new Buffer('receipt', 'utf-8').toString('base64'), }, bypassAppStoreValidation: true, }, @@ -97,66 +97,66 @@ describe("test validate_receipt endpoint", () => { expect(body.code).toEqual(Parse.Error.INVALID_JSON); }); - it("should bypass appstore validation and not find product", async () => { + it('should bypass appstore validation and not find product', async () => { const response = await request({ headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, - url: "http://localhost:8378/1/validate_purchase", - method: "POST", + url: 'http://localhost:8378/1/validate_purchase', + method: 'POST', body: { - productIdentifier: "another-product", + productIdentifier: 'another-product', receipt: { - __type: "Bytes", - base64: new Buffer("receipt", "utf8").toString("base64"), + __type: 'Bytes', + base64: new Buffer('receipt', 'utf8').toString('base64'), }, bypassAppStoreValidation: true, }, }).catch(error => error); const body = response.data; - if (typeof body != "object") { - fail("Body is not an object"); + if (typeof body != 'object') { + fail('Body is not an object'); } else { expect(body.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); - expect(body.error).toEqual("Object not found."); + expect(body.error).toEqual('Object not found.'); } }); - it("should fail at appstore validation", async () => { + it('should fail at appstore validation', async () => { const response = await request({ headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, - url: "http://localhost:8378/1/validate_purchase", - method: "POST", + url: 'http://localhost:8378/1/validate_purchase', + method: 'POST', body: { - productIdentifier: "a-product", + productIdentifier: 'a-product', receipt: { - __type: "Bytes", - base64: new Buffer("receipt", "utf-8").toString("base64"), + __type: 'Bytes', + base64: new Buffer('receipt', 'utf-8').toString('base64'), }, }, }); const body = response.data; - if (typeof body != "object") { - fail("Body is not an object"); + if (typeof body != 'object') { + fail('Body is not an object'); } else { expect(body.status).toBe(21002); expect(body.error).toBe( - "The data in the receipt-data property was malformed or missing." + 'The data in the receipt-data property was malformed or missing.' ); } }); - it("should not create a _Product", done => { - const product = new Parse.Object("_Product"); + it('should not create a _Product', done => { + const product = new Parse.Object('_Product'); product.save().then( function () { - fail("Should not be able to save"); + fail('Should not be able to save'); done(); }, function (err) { @@ -166,22 +166,22 @@ describe("test validate_receipt endpoint", () => { ); }); - it("should be able to update a _Product", done => { - const query = new Parse.Query("_Product"); + it('should be able to update a _Product', done => { + const query = new Parse.Query('_Product'); query .first() .then(function (product) { if (!product) { - return Promise.reject(new Error("Product should be found")); + return Promise.reject(new Error('Product should be found')); } - product.set("title", "a new title"); + product.set('title', 'a new title'); return product.save(); }) .then(function (productAgain) { - expect(productAgain.get("downloadName")).toEqual( - productAgain.get("download").name() + expect(productAgain.get('downloadName')).toEqual( + productAgain.get('download').name() ); - expect(productAgain.get("title")).toEqual("a new title"); + expect(productAgain.get('title')).toEqual('a new title'); done(); }) .catch(function (err) { @@ -190,24 +190,24 @@ describe("test validate_receipt endpoint", () => { }); }); - it("should not be able to remove a require key in a _Product", done => { - const query = new Parse.Query("_Product"); + it('should not be able to remove a require key in a _Product', done => { + const query = new Parse.Query('_Product'); query .first() .then(function (product) { if (!product) { - return Promise.reject(new Error("Product should be found")); + return Promise.reject(new Error('Product should be found')); } - product.unset("title"); + product.unset('title'); return product.save(); }) .then(function () { - fail("Should not succeed"); + fail('Should not succeed'); done(); }) .catch(function (err) { expect(err.code).toEqual(Parse.Error.INCORRECT_TYPE); - expect(err.message).toEqual("title is required."); + expect(err.message).toEqual('title is required.'); done(); }); }); diff --git a/spec/PushController.spec.js b/spec/PushController.spec.js index c6b858bcaf..9b82298c16 100644 --- a/spec/PushController.spec.js +++ b/spec/PushController.spec.js @@ -1,9 +1,9 @@ -"use strict"; +'use strict'; const PushController = - require("../lib/Controllers/PushController").PushController; -const StatusHandler = require("../lib/StatusHandler"); -const Config = require("../lib/Config"); -const validatePushType = require("../lib/Push/utils").validatePushType; + require('../lib/Controllers/PushController').PushController; +const StatusHandler = require('../lib/StatusHandler'); +const Config = require('../lib/Config'); +const validatePushType = require('../lib/Push/utils').validatePushType; const successfulTransmissions = function (body, installations) { const promises = installations.map(device => { @@ -19,7 +19,7 @@ const successfulTransmissions = function (body, installations) { const successfulIOS = function (body, installations) { const promises = installations.map(device => { return Promise.resolve({ - transmitted: device.deviceType == "ios", + transmitted: device.deviceType == 'ios', device: device, }); }); @@ -28,10 +28,10 @@ const successfulIOS = function (body, installations) { }; const pushCompleted = async pushId => { - const query = new Parse.Query("_PushStatus"); - query.equalTo("objectId", pushId); + const query = new Parse.Query('_PushStatus'); + query.equalTo('objectId', pushId); let result = await query.first({ useMasterKey: true }); - while (!(result && result.get("status") === "succeeded")) { + while (!(result && result.get('status') === 'succeeded')) { await jasmine.timeout(); result = await query.first({ useMasterKey: true }); } @@ -46,11 +46,11 @@ const sendPush = (body, where, config, auth, now) => { }); }; -describe("PushController", () => { - it("can validate device type when no device type is set", done => { +describe('PushController', () => { + it('can validate device type when no device type is set', done => { // Make query condition const where = {}; - const validPushTypes = ["ios", "android"]; + const validPushTypes = ['ios', 'android']; expect(function () { validatePushType(where, validPushTypes); @@ -58,12 +58,12 @@ describe("PushController", () => { done(); }); - it("can validate device type when single valid device type is set", done => { + it('can validate device type when single valid device type is set', done => { // Make query condition const where = { - deviceType: "ios", + deviceType: 'ios', }; - const validPushTypes = ["ios", "android"]; + const validPushTypes = ['ios', 'android']; expect(function () { validatePushType(where, validPushTypes); @@ -71,14 +71,14 @@ describe("PushController", () => { done(); }); - it("can validate device type when multiple valid device types are set", done => { + it('can validate device type when multiple valid device types are set', done => { // Make query condition const where = { deviceType: { - $in: ["android", "ios"], + $in: ['android', 'ios'], }, }; - const validPushTypes = ["ios", "android"]; + const validPushTypes = ['ios', 'android']; expect(function () { validatePushType(where, validPushTypes); @@ -86,12 +86,12 @@ describe("PushController", () => { done(); }); - it("can throw on validateDeviceType when single invalid device type is set", done => { + it('can throw on validateDeviceType when single invalid device type is set', done => { // Make query condition const where = { - deviceType: "osx", + deviceType: 'osx', }; - const validPushTypes = ["ios", "android"]; + const validPushTypes = ['ios', 'android']; expect(function () { validatePushType(where, validPushTypes); @@ -99,9 +99,9 @@ describe("PushController", () => { done(); }); - it("can get expiration time in string format", done => { + it('can get expiration time in string format', done => { // Make mock request - const timeStr = "2015-03-19T22:05:08Z"; + const timeStr = '2015-03-19T22:05:08Z'; const body = { expiration_time: timeStr, }; @@ -111,7 +111,7 @@ describe("PushController", () => { done(); }); - it("can get expiration time in number format", done => { + it('can get expiration time in number format', done => { // Make mock request const timeNumber = 1426802708; const body = { @@ -123,10 +123,10 @@ describe("PushController", () => { done(); }); - it("can throw on getExpirationTime in invalid format", done => { + it('can throw on getExpirationTime in invalid format', done => { // Make mock request const body = { - expiration_time: "abcd", + expiration_time: 'abcd', }; expect(function () { @@ -135,9 +135,9 @@ describe("PushController", () => { done(); }); - it("can get push time in string format", done => { + it('can get push time in string format', done => { // Make mock request - const timeStr = "2015-03-19T22:05:08Z"; + const timeStr = '2015-03-19T22:05:08Z'; const body = { push_time: timeStr, }; @@ -147,7 +147,7 @@ describe("PushController", () => { done(); }); - it("can get push time in number format", done => { + it('can get push time in number format', done => { // Make mock request const timeNumber = 1426802708; const body = { @@ -159,10 +159,10 @@ describe("PushController", () => { done(); }); - it("can throw on getPushTime in invalid format", done => { + it('can throw on getPushTime in invalid format', done => { // Make mock request const body = { - push_time: "abcd", + push_time: 'abcd', }; expect(function () { @@ -171,8 +171,8 @@ describe("PushController", () => { done(); }); - it_id("01e3e1b8-fad2-4249-b664-5a3efaab8cb1")(it)( - "properly increment badges", + it_id('01e3e1b8-fad2-4249-b664-5a3efaab8cb1')(it)( + 'properly increment badges', async () => { const pushAdapter = { send: function (body, installations) { @@ -184,7 +184,7 @@ describe("PushController", () => { return successfulTransmissions(body, installations); }, getValidPushTypes: function () { - return ["ios", "android"]; + return ['ios', 'android']; }, }; await reconfigureServer({ @@ -192,34 +192,34 @@ describe("PushController", () => { }); const payload = { data: { - alert: "Hello World!", - badge: "Increment", + alert: 'Hello World!', + badge: 'Increment', }, }; const installations = []; while (installations.length != 10) { - const installation = new Parse.Object("_Installation"); + const installation = new Parse.Object('_Installation'); installation.set( - "installationId", - "installation_" + installations.length + 'installationId', + 'installation_' + installations.length ); - installation.set("deviceToken", "device_token_" + installations.length); - installation.set("badge", installations.length); - installation.set("originalBadge", installations.length); - installation.set("deviceType", "ios"); + installation.set('deviceToken', 'device_token_' + installations.length); + installation.set('badge', installations.length); + installation.set('originalBadge', installations.length); + installation.set('deviceType', 'ios'); installations.push(installation); } while (installations.length != 15) { - const installation = new Parse.Object("_Installation"); + const installation = new Parse.Object('_Installation'); installation.set( - "installationId", - "installation_" + installations.length + 'installationId', + 'installation_' + installations.length ); - installation.set("deviceToken", "device_token_" + installations.length); - installation.set("badge", installations.length); - installation.set("originalBadge", installations.length); - installation.set("deviceType", "android"); + installation.set('deviceToken', 'device_token_' + installations.length); + installation.set('badge', installations.length); + installation.set('originalBadge', installations.length); + installation.set('deviceType', 'android'); installations.push(installation); } const config = Config.get(Parse.applicationId); @@ -232,23 +232,23 @@ describe("PushController", () => { // Check we actually sent 15 pushes. const pushStatus = await Parse.Push.getPushStatus(pushStatusId); - expect(pushStatus.get("numSent")).toBe(15); + expect(pushStatus.get('numSent')).toBe(15); // Check that the installations were actually updated. - const query = new Parse.Query("_Installation"); + const query = new Parse.Query('_Installation'); const results = await query.find({ useMasterKey: true }); expect(results.length).toBe(15); for (let i = 0; i < 15; i++) { const installation = results[i]; - expect(installation.get("badge")).toBe( - parseInt(installation.get("originalBadge")) + 1 + expect(installation.get('badge')).toBe( + parseInt(installation.get('originalBadge')) + 1 ); } } ); - it_id("14afcedf-e65d-41cd-981e-07f32df84c14")(it)( - "properly increment badges by more than 1", + it_id('14afcedf-e65d-41cd-981e-07f32df84c14')(it)( + 'properly increment badges by more than 1', async () => { const pushAdapter = { send: function (body, installations) { @@ -260,7 +260,7 @@ describe("PushController", () => { return successfulTransmissions(body, installations); }, getValidPushTypes: function () { - return ["ios", "android"]; + return ['ios', 'android']; }, }; await reconfigureServer({ @@ -268,34 +268,34 @@ describe("PushController", () => { }); const payload = { data: { - alert: "Hello World!", - badge: { __op: "Increment", amount: 3 }, + alert: 'Hello World!', + badge: { __op: 'Increment', amount: 3 }, }, }; const installations = []; while (installations.length != 10) { - const installation = new Parse.Object("_Installation"); + const installation = new Parse.Object('_Installation'); installation.set( - "installationId", - "installation_" + installations.length + 'installationId', + 'installation_' + installations.length ); - installation.set("deviceToken", "device_token_" + installations.length); - installation.set("badge", installations.length); - installation.set("originalBadge", installations.length); - installation.set("deviceType", "ios"); + installation.set('deviceToken', 'device_token_' + installations.length); + installation.set('badge', installations.length); + installation.set('originalBadge', installations.length); + installation.set('deviceType', 'ios'); installations.push(installation); } while (installations.length != 15) { - const installation = new Parse.Object("_Installation"); + const installation = new Parse.Object('_Installation'); installation.set( - "installationId", - "installation_" + installations.length + 'installationId', + 'installation_' + installations.length ); - installation.set("deviceToken", "device_token_" + installations.length); - installation.set("badge", installations.length); - installation.set("originalBadge", installations.length); - installation.set("deviceType", "android"); + installation.set('deviceToken', 'device_token_' + installations.length); + installation.set('badge', installations.length); + installation.set('originalBadge', installations.length); + installation.set('deviceType', 'android'); installations.push(installation); } const config = Config.get(Parse.applicationId); @@ -306,22 +306,22 @@ describe("PushController", () => { const pushStatusId = await sendPush(payload, {}, config, auth); await pushCompleted(pushStatusId); const pushStatus = await Parse.Push.getPushStatus(pushStatusId); - expect(pushStatus.get("numSent")).toBe(15); + expect(pushStatus.get('numSent')).toBe(15); // Check that the installations were actually updated. - const query = new Parse.Query("_Installation"); + const query = new Parse.Query('_Installation'); const results = await query.find({ useMasterKey: true }); expect(results.length).toBe(15); for (let i = 0; i < 15; i++) { const installation = results[i]; - expect(installation.get("badge")).toBe( - parseInt(installation.get("originalBadge")) + 3 + expect(installation.get('badge')).toBe( + parseInt(installation.get('originalBadge')) + 3 ); } } ); - it_id("758dd579-aa91-4010-9033-8d48d3463644")(it)( - "properly set badges to 1", + it_id('758dd579-aa91-4010-9033-8d48d3463644')(it)( + 'properly set badges to 1', async () => { const pushAdapter = { send: function (body, installations) { @@ -333,7 +333,7 @@ describe("PushController", () => { return successfulTransmissions(body, installations); }, getValidPushTypes: function () { - return ["ios"]; + return ['ios']; }, }; await reconfigureServer({ @@ -341,21 +341,21 @@ describe("PushController", () => { }); const payload = { data: { - alert: "Hello World!", + alert: 'Hello World!', badge: 1, }, }; const installations = []; while (installations.length != 10) { - const installation = new Parse.Object("_Installation"); + const installation = new Parse.Object('_Installation'); installation.set( - "installationId", - "installation_" + installations.length + 'installationId', + 'installation_' + installations.length ); - installation.set("deviceToken", "device_token_" + installations.length); - installation.set("badge", installations.length); - installation.set("originalBadge", installations.length); - installation.set("deviceType", "ios"); + installation.set('deviceToken', 'device_token_' + installations.length); + installation.set('badge', installations.length); + installation.set('originalBadge', installations.length); + installation.set('deviceType', 'ios'); installations.push(installation); } @@ -367,39 +367,39 @@ describe("PushController", () => { const pushStatusId = await sendPush(payload, {}, config, auth); await pushCompleted(pushStatusId); const pushStatus = await Parse.Push.getPushStatus(pushStatusId); - expect(pushStatus.get("numSent")).toBe(10); + expect(pushStatus.get('numSent')).toBe(10); // Check that the installations were actually updated. - const query = new Parse.Query("_Installation"); + const query = new Parse.Query('_Installation'); const results = await query.find({ useMasterKey: true }); expect(results.length).toBe(10); for (let i = 0; i < 10; i++) { const installation = results[i]; - expect(installation.get("badge")).toBe(1); + expect(installation.get('badge')).toBe(1); } } ); - it_id("75c39ae3-06ac-4354-b321-931e81c5a927")(it)( - "properly set badges to 1 with complex query #2903 #3022", + it_id('75c39ae3-06ac-4354-b321-931e81c5a927')(it)( + 'properly set badges to 1 with complex query #2903 #3022', async () => { const payload = { data: { - alert: "Hello World!", + alert: 'Hello World!', badge: 1, }, }; const installations = []; while (installations.length != 10) { - const installation = new Parse.Object("_Installation"); + const installation = new Parse.Object('_Installation'); installation.set( - "installationId", - "installation_" + installations.length + 'installationId', + 'installation_' + installations.length ); - installation.set("deviceToken", "device_token_" + installations.length); - installation.set("badge", installations.length); - installation.set("originalBadge", installations.length); - installation.set("deviceType", "ios"); + installation.set('deviceToken', 'device_token_' + installations.length); + installation.set('badge', installations.length); + installation.set('originalBadge', installations.length); + installation.set('deviceType', 'ios'); installations.push(installation); } let matchedInstallationsCount = 0; @@ -414,7 +414,7 @@ describe("PushController", () => { return successfulTransmissions(body, installations); }, getValidPushTypes: function () { - return ["ios"]; + return ['ios']; }, }; await reconfigureServer({ @@ -435,47 +435,47 @@ describe("PushController", () => { await pushCompleted(pushStatusId); expect(matchedInstallationsCount).toBe(5); const query = new Parse.Query(Parse.Installation); - query.equalTo("badge", 1); + query.equalTo('badge', 1); const results = await query.find({ useMasterKey: true }); expect(results.length).toBe(5); } ); - it_id("667f31c0-b458-4f61-ab57-668c04e3cc0b")(it)( - "properly creates _PushStatus", + it_id('667f31c0-b458-4f61-ab57-668c04e3cc0b')(it)( + 'properly creates _PushStatus', async () => { const pushStatusAfterSave = { handler: function () {}, }; - const spy = spyOn(pushStatusAfterSave, "handler").and.callThrough(); - Parse.Cloud.afterSave("_PushStatus", pushStatusAfterSave.handler); + const spy = spyOn(pushStatusAfterSave, 'handler').and.callThrough(); + Parse.Cloud.afterSave('_PushStatus', pushStatusAfterSave.handler); const installations = []; while (installations.length != 10) { - const installation = new Parse.Object("_Installation"); + const installation = new Parse.Object('_Installation'); installation.set( - "installationId", - "installation_" + installations.length + 'installationId', + 'installation_' + installations.length ); - installation.set("deviceToken", "device_token_" + installations.length); - installation.set("badge", installations.length); - installation.set("originalBadge", installations.length); - installation.set("deviceType", "ios"); + installation.set('deviceToken', 'device_token_' + installations.length); + installation.set('badge', installations.length); + installation.set('originalBadge', installations.length); + installation.set('deviceType', 'ios'); installations.push(installation); } while (installations.length != 15) { - const installation = new Parse.Object("_Installation"); + const installation = new Parse.Object('_Installation'); installation.set( - "installationId", - "installation_" + installations.length + 'installationId', + 'installation_' + installations.length ); - installation.set("deviceToken", "device_token_" + installations.length); - installation.set("deviceType", "android"); + installation.set('deviceToken', 'device_token_' + installations.length); + installation.set('deviceType', 'android'); installations.push(installation); } const payload = { data: { - alert: "Hello World!", + alert: 'Hello World!', badge: 1, }, }; @@ -485,7 +485,7 @@ describe("PushController", () => { return successfulIOS(body, installations); }, getValidPushTypes: function () { - return ["ios"]; + return ['ios']; }, }; await reconfigureServer({ @@ -502,22 +502,22 @@ describe("PushController", () => { expect(result.createdAt instanceof Date).toBe(true); expect(result.updatedAt instanceof Date).toBe(true); expect(result.id.length).toBe(10); - expect(result.get("source")).toEqual("rest"); - expect(result.get("query")).toEqual(JSON.stringify({})); - expect(typeof result.get("payload")).toEqual("string"); - expect(JSON.parse(result.get("payload"))).toEqual(payload.data); - expect(result.get("status")).toEqual("succeeded"); - expect(result.get("numSent")).toEqual(10); - expect(result.get("sentPerType")).toEqual({ + expect(result.get('source')).toEqual('rest'); + expect(result.get('query')).toEqual(JSON.stringify({})); + expect(typeof result.get('payload')).toEqual('string'); + expect(JSON.parse(result.get('payload'))).toEqual(payload.data); + expect(result.get('status')).toEqual('succeeded'); + expect(result.get('numSent')).toEqual(10); + expect(result.get('sentPerType')).toEqual({ ios: 10, // 10 ios }); - expect(result.get("numFailed")).toEqual(5); - expect(result.get("failedPerType")).toEqual({ + expect(result.get('numFailed')).toEqual(5); + expect(result.get('failedPerType')).toEqual({ android: 5, // android }); try { // Try to get it without masterKey - const query = new Parse.Query("_PushStatus"); + const query = new Parse.Query('_PushStatus'); await query.find(); fail(); } catch (error) { @@ -538,25 +538,25 @@ describe("PushController", () => { const object = call.args[0].object; expect(object instanceof Parse.Object).toBe(true); const pushStatus = getPushStatus(index); - if (pushStatus.get("status") === "pending") { + if (pushStatus.get('status') === 'pending') { pendingCount += 1; } - if (pushStatus.get("status") === "running") { + if (pushStatus.get('status') === 'running') { runningCount += 1; } - if (pushStatus.get("status") === "succeeded") { + if (pushStatus.get('status') === 'succeeded') { succeedCount += 1; } if ( - pushStatus.get("status") === "running" && - pushStatus.get("numSent") > 0 + pushStatus.get('status') === 'running' && + pushStatus.get('numSent') > 0 ) { - expect(pushStatus.get("numSent")).toBe(10); - expect(pushStatus.get("numFailed")).toBe(5); - expect(pushStatus.get("failedPerType")).toEqual({ + expect(pushStatus.get('numSent')).toBe(10); + expect(pushStatus.get('numFailed')).toBe(5); + expect(pushStatus.get('failedPerType')).toEqual({ android: 5, }); - expect(pushStatus.get("sentPerType")).toEqual({ + expect(pushStatus.get('sentPerType')).toEqual({ ios: 10, }); } @@ -567,23 +567,23 @@ describe("PushController", () => { } ); - it_id("30e0591a-56de-4720-8c60-7d72291b532a")(it)( - "properly creates _PushStatus without serverURL", + it_id('30e0591a-56de-4720-8c60-7d72291b532a')(it)( + 'properly creates _PushStatus without serverURL', async () => { const pushStatusAfterSave = { handler: function () {}, }; - Parse.Cloud.afterSave("_PushStatus", pushStatusAfterSave.handler); - const installation = new Parse.Object("_Installation"); - installation.set("installationId", "installation"); - installation.set("deviceToken", "device_token"); - installation.set("badge", 0); - installation.set("originalBadge", 0); - installation.set("deviceType", "ios"); + Parse.Cloud.afterSave('_PushStatus', pushStatusAfterSave.handler); + const installation = new Parse.Object('_Installation'); + installation.set('installationId', 'installation'); + installation.set('deviceToken', 'device_token'); + installation.set('badge', 0); + installation.set('originalBadge', 0); + installation.set('deviceType', 'ios'); const payload = { data: { - alert: "Hello World!", + alert: 'Hello World!', badge: 1, }, }; @@ -593,12 +593,12 @@ describe("PushController", () => { return successfulIOS(body, installations); }, getValidPushTypes: function () { - return ["ios"]; + return ['ios']; }, }; await installation.save(); await reconfigureServer({ - serverURL: "http://localhost:8378/", // server with borked URL + serverURL: 'http://localhost:8378/', // server with borked URL push: { adapter: pushAdapter }, }); const config = Config.get(Parse.applicationId); @@ -608,14 +608,14 @@ describe("PushController", () => { const pushStatusId = await sendPush(payload, {}, config, auth); // it is enqueued so it can take time await jasmine.timeout(1000); - Parse.serverURL = "http://localhost:8378/1"; // GOOD url + Parse.serverURL = 'http://localhost:8378/1'; // GOOD url const result = await Parse.Push.getPushStatus(pushStatusId); expect(result).toBeDefined(); await pushCompleted(pushStatusId); } ); - it("should properly report failures in _PushStatus", async () => { + it('should properly report failures in _PushStatus', async () => { const pushAdapter = { send: function (body, installations) { return installations.map(installation => { @@ -625,7 +625,7 @@ describe("PushController", () => { }); }, getValidPushTypes: function () { - return ["ios"]; + return ['ios']; }, }; await reconfigureServer({ @@ -634,12 +634,12 @@ describe("PushController", () => { // $ins is invalid query const where = { channels: { - $ins: ["Giants", "Mets"], + $ins: ['Giants', 'Mets'], }, }; const payload = { data: { - alert: "Hello World!", + alert: 'Hello World!', badge: 1, }, }; @@ -652,24 +652,24 @@ describe("PushController", () => { await pushController.sendPush(payload, where, config, auth); fail(); } catch (e) { - const query = new Parse.Query("_PushStatus"); + const query = new Parse.Query('_PushStatus'); let results = await query.find({ useMasterKey: true }); while (results.length === 0) { results = await query.find({ useMasterKey: true }); } expect(results.length).toBe(1); const pushStatus = results[0]; - expect(pushStatus.get("status")).toBe("failed"); + expect(pushStatus.get('status')).toBe('failed'); } }); - it_id("53551fc3-b975-4774-92e6-7e5f3c05e105")(it)( - "should support full RESTQuery for increment", + it_id('53551fc3-b975-4774-92e6-7e5f3c05e105')(it)( + 'should support full RESTQuery for increment', async () => { const payload = { data: { - alert: "Hello World!", - badge: "Increment", + alert: 'Hello World!', + badge: 'Increment', }, }; @@ -678,7 +678,7 @@ describe("PushController", () => { return successfulTransmissions(body, installations); }, getValidPushTypes: function () { - return ["ios"]; + return ['ios']; }, }; await reconfigureServer({ @@ -691,35 +691,35 @@ describe("PushController", () => { const where = { deviceToken: { - $in: ["device_token_0", "device_token_1", "device_token_2"], + $in: ['device_token_0', 'device_token_1', 'device_token_2'], }, }; const installations = []; while (installations.length != 5) { - const installation = new Parse.Object("_Installation"); + const installation = new Parse.Object('_Installation'); installation.set( - "installationId", - "installation_" + installations.length + 'installationId', + 'installation_' + installations.length ); - installation.set("deviceToken", "device_token_" + installations.length); - installation.set("badge", installations.length); - installation.set("originalBadge", installations.length); - installation.set("deviceType", "ios"); + installation.set('deviceToken', 'device_token_' + installations.length); + installation.set('badge', installations.length); + installation.set('originalBadge', installations.length); + installation.set('deviceType', 'ios'); installations.push(installation); } await Parse.Object.saveAll(installations); const pushStatusId = await sendPush(payload, where, config, auth); await pushCompleted(pushStatusId); const pushStatus = await Parse.Push.getPushStatus(pushStatusId); - expect(pushStatus.get("numSent")).toBe(3); + expect(pushStatus.get('numSent')).toBe(3); } ); - it("should support object type for alert", async () => { + it('should support object type for alert', async () => { const payload = { data: { alert: { - "loc-key": "hello_world", + 'loc-key': 'hello_world', }, }, }; @@ -729,7 +729,7 @@ describe("PushController", () => { return successfulTransmissions(body, installations); }, getValidPushTypes: function () { - return ["ios"]; + return ['ios']; }, }; await reconfigureServer({ @@ -740,34 +740,34 @@ describe("PushController", () => { isMaster: true, }; const where = { - deviceType: "ios", + deviceType: 'ios', }; const installations = []; while (installations.length != 5) { - const installation = new Parse.Object("_Installation"); + const installation = new Parse.Object('_Installation'); installation.set( - "installationId", - "installation_" + installations.length + 'installationId', + 'installation_' + installations.length ); - installation.set("deviceToken", "device_token_" + installations.length); - installation.set("badge", installations.length); - installation.set("originalBadge", installations.length); - installation.set("deviceType", "ios"); + installation.set('deviceToken', 'device_token_' + installations.length); + installation.set('badge', installations.length); + installation.set('originalBadge', installations.length); + installation.set('deviceType', 'ios'); installations.push(installation); } await Parse.Object.saveAll(installations); const pushStatusId = await sendPush(payload, where, config, auth); await pushCompleted(pushStatusId); const pushStatus = await Parse.Push.getPushStatus(pushStatusId); - expect(pushStatus.get("numSent")).toBe(5); + expect(pushStatus.get('numSent')).toBe(5); }); - it("should flatten", () => { + it('should flatten', () => { const res = StatusHandler.flatten([1, [2], [[3, 4], 5], [[[6]]]]); expect(res).toEqual([1, 2, 3, 4, 5, 6]); }); - it("properly transforms push time", () => { + it('properly transforms push time', () => { expect(PushController.getPushTime()).toBe(undefined); expect( PushController.getPushTime({ @@ -776,13 +776,13 @@ describe("PushController", () => { ).toEqual(new Date(1000 * 1000)); expect( PushController.getPushTime({ - push_time: "2017-01-01", + push_time: '2017-01-01', }).date - ).toEqual(new Date("2017-01-01")); + ).toEqual(new Date('2017-01-01')); expect(() => { PushController.getPushTime({ - push_time: "gibberish-time", + push_time: 'gibberish-time', }); }).toThrow(); expect(() => { @@ -793,37 +793,37 @@ describe("PushController", () => { expect( PushController.getPushTime({ - push_time: "2017-09-06T13:42:48.369Z", + push_time: '2017-09-06T13:42:48.369Z', }) ).toEqual({ - date: new Date("2017-09-06T13:42:48.369Z"), + date: new Date('2017-09-06T13:42:48.369Z'), isLocalTime: false, }); expect( PushController.getPushTime({ - push_time: "2007-04-05T12:30-02:00", + push_time: '2007-04-05T12:30-02:00', }) ).toEqual({ - date: new Date("2007-04-05T12:30-02:00"), + date: new Date('2007-04-05T12:30-02:00'), isLocalTime: false, }); expect( PushController.getPushTime({ - push_time: "2007-04-05T12:30", + push_time: '2007-04-05T12:30', }) ).toEqual({ - date: new Date("2007-04-05T12:30"), + date: new Date('2007-04-05T12:30'), isLocalTime: true, }); }); - it("should not schedule push when not configured", async () => { + it('should not schedule push when not configured', async () => { const pushAdapter = { send: function (body, installations) { return successfulTransmissions(body, installations); }, getValidPushTypes: function () { - return ["ios"]; + return ['ios']; }, }; await reconfigureServer({ @@ -836,35 +836,35 @@ describe("PushController", () => { const pushController = new PushController(); const payload = { data: { - alert: "hello", + alert: 'hello', }, push_time: new Date().getTime(), }; const installations = []; while (installations.length != 10) { - const installation = new Parse.Object("_Installation"); + const installation = new Parse.Object('_Installation'); installation.set( - "installationId", - "installation_" + installations.length + 'installationId', + 'installation_' + installations.length ); - installation.set("deviceToken", "device_token_" + installations.length); - installation.set("badge", installations.length); - installation.set("originalBadge", installations.length); - installation.set("deviceType", "ios"); + installation.set('deviceToken', 'device_token_' + installations.length); + installation.set('badge', installations.length); + installation.set('originalBadge', installations.length); + installation.set('deviceType', 'ios'); installations.push(installation); } await Parse.Object.saveAll(installations); await pushController.sendPush(payload, {}, config, auth); await jasmine.timeout(1000); - const query = new Parse.Query("_PushStatus"); + const query = new Parse.Query('_PushStatus'); const results = await query.find({ useMasterKey: true }); expect(results.length).toBe(1); const pushStatus = results[0]; - expect(pushStatus.get("status")).not.toBe("scheduled"); + expect(pushStatus.get('status')).not.toBe('scheduled'); }); - it("should schedule push when configured", async () => { + it('should schedule push when configured', async () => { const auth = { isMaster: true, }; @@ -884,27 +884,27 @@ describe("PushController", () => { return Promise.all(promises); }, getValidPushTypes: function () { - return ["ios"]; + return ['ios']; }, }; const pushController = new PushController(); const payload = { data: { - alert: "hello", + alert: 'hello', }, push_time: new Date().getTime() / 1000, }; const installations = []; while (installations.length != 10) { - const installation = new Parse.Object("_Installation"); + const installation = new Parse.Object('_Installation'); installation.set( - "installationId", - "installation_" + installations.length + 'installationId', + 'installation_' + installations.length ); - installation.set("deviceToken", "device_token_" + installations.length); - installation.set("badge", installations.length); - installation.set("originalBadge", installations.length); - installation.set("deviceType", "ios"); + installation.set('deviceToken', 'device_token_' + installations.length); + installation.set('badge', installations.length); + installation.set('originalBadge', installations.length); + installation.set('deviceType', 'ios'); installations.push(installation); } await reconfigureServer({ @@ -915,14 +915,14 @@ describe("PushController", () => { await Parse.Object.saveAll(installations); await pushController.sendPush(payload, {}, config, auth); await jasmine.timeout(1000); - const query = new Parse.Query("_PushStatus"); + const query = new Parse.Query('_PushStatus'); const results = await query.find({ useMasterKey: true }); expect(results.length).toBe(1); const pushStatus = results[0]; - expect(pushStatus.get("status")).toBe("scheduled"); + expect(pushStatus.get('status')).toBe('scheduled'); }); - it("should not enqueue push when device token is not set", async () => { + it('should not enqueue push when device token is not set', async () => { const auth = { isMaster: true, }; @@ -942,37 +942,37 @@ describe("PushController", () => { return Promise.all(promises); }, getValidPushTypes: function () { - return ["ios"]; + return ['ios']; }, }; const payload = { data: { - alert: "hello", + alert: 'hello', }, push_time: new Date().getTime() / 1000, }; const installations = []; while (installations.length != 5) { - const installation = new Parse.Object("_Installation"); + const installation = new Parse.Object('_Installation'); installation.set( - "installationId", - "installation_" + installations.length + 'installationId', + 'installation_' + installations.length ); - installation.set("deviceToken", "device_token_" + installations.length); - installation.set("badge", installations.length); - installation.set("originalBadge", installations.length); - installation.set("deviceType", "ios"); + installation.set('deviceToken', 'device_token_' + installations.length); + installation.set('badge', installations.length); + installation.set('originalBadge', installations.length); + installation.set('deviceType', 'ios'); installations.push(installation); } while (installations.length != 15) { - const installation = new Parse.Object("_Installation"); + const installation = new Parse.Object('_Installation'); installation.set( - "installationId", - "installation_" + installations.length + 'installationId', + 'installation_' + installations.length ); - installation.set("badge", installations.length); - installation.set("originalBadge", installations.length); - installation.set("deviceType", "ios"); + installation.set('badge', installations.length); + installation.set('originalBadge', installations.length); + installation.set('deviceType', 'ios'); installations.push(installation); } await reconfigureServer({ @@ -983,11 +983,11 @@ describe("PushController", () => { const pushStatusId = await sendPush(payload, {}, config, auth); await pushCompleted(pushStatusId); const pushStatus = await Parse.Push.getPushStatus(pushStatusId); - expect(pushStatus.get("numSent")).toBe(5); - expect(pushStatus.get("status")).toBe("succeeded"); + expect(pushStatus.get('numSent')).toBe(5); + expect(pushStatus.get('status')).toBe('succeeded'); }); - it("should not mark the _PushStatus as failed when audience has no deviceToken", async () => { + it('should not mark the _PushStatus as failed when audience has no deviceToken', async () => { const auth = { isMaster: true, }; @@ -1007,25 +1007,25 @@ describe("PushController", () => { return Promise.all(promises); }, getValidPushTypes: function () { - return ["ios"]; + return ['ios']; }, }; const payload = { data: { - alert: "hello", + alert: 'hello', }, push_time: new Date().getTime() / 1000, }; const installations = []; while (installations.length != 5) { - const installation = new Parse.Object("_Installation"); + const installation = new Parse.Object('_Installation'); installation.set( - "installationId", - "installation_" + installations.length + 'installationId', + 'installation_' + installations.length ); - installation.set("badge", installations.length); - installation.set("originalBadge", installations.length); - installation.set("deviceType", "ios"); + installation.set('badge', installations.length); + installation.set('originalBadge', installations.length); + installation.set('deviceType', 'ios'); installations.push(installation); } await reconfigureServer({ @@ -1036,15 +1036,15 @@ describe("PushController", () => { const pushStatusId = await sendPush(payload, {}, config, auth); await pushCompleted(pushStatusId); const pushStatus = await Parse.Push.getPushStatus(pushStatusId); - expect(pushStatus.get("status")).toBe("succeeded"); + expect(pushStatus.get('status')).toBe('succeeded'); }); - it("should support localized payload data", async () => { + it('should support localized payload data', async () => { const payload = { data: { - alert: "Hello!", - "alert-fr": "Bonjour", - "alert-es": "Ola", + alert: 'Hello!', + 'alert-fr': 'Bonjour', + 'alert-es': 'Ola', }, }; const pushAdapter = { @@ -1052,10 +1052,10 @@ describe("PushController", () => { return successfulTransmissions(body, installations); }, getValidPushTypes: function () { - return ["ios"]; + return ['ios']; }, }; - spyOn(pushAdapter, "send").and.callThrough(); + spyOn(pushAdapter, 'send').and.callThrough(); await reconfigureServer({ push: { adapter: pushAdapter }, }); @@ -1064,24 +1064,24 @@ describe("PushController", () => { isMaster: true, }; const where = { - deviceType: "ios", + deviceType: 'ios', }; const installations = []; while (installations.length != 5) { - const installation = new Parse.Object("_Installation"); + const installation = new Parse.Object('_Installation'); installation.set( - "installationId", - "installation_" + installations.length + 'installationId', + 'installation_' + installations.length ); - installation.set("deviceToken", "device_token_" + installations.length); - installation.set("badge", installations.length); - installation.set("originalBadge", installations.length); - installation.set("deviceType", "ios"); + installation.set('deviceToken', 'device_token_' + installations.length); + installation.set('badge', installations.length); + installation.set('originalBadge', installations.length); + installation.set('deviceType', 'ios'); installations.push(installation); } - installations[0].set("localeIdentifier", "fr-CA"); - installations[1].set("localeIdentifier", "fr-FR"); - installations[2].set("localeIdentifier", "en-US"); + installations[0].set('localeIdentifier', 'fr-CA'); + installations[1].set('localeIdentifier', 'fr-FR'); + installations[2].set('localeIdentifier', 'en-US'); await Parse.Object.saveAll(installations); const pushStatusId = await sendPush(payload, where, config, auth); @@ -1090,30 +1090,30 @@ describe("PushController", () => { expect(pushAdapter.send.calls.count()).toBe(2); const firstCall = pushAdapter.send.calls.first(); expect(firstCall.args[0].data).toEqual({ - alert: "Hello!", + alert: 'Hello!', }); expect(firstCall.args[1].length).toBe(3); // 3 installations const lastCall = pushAdapter.send.calls.mostRecent(); expect(lastCall.args[0].data).toEqual({ - alert: "Bonjour", + alert: 'Bonjour', }); expect(lastCall.args[1].length).toBe(2); // 2 installations // No installation is in es so only 1 call for fr, and another for default }); - it_id("ef2e5569-50c3-40c2-ab49-175cdbd5f024")(it)( - "should update audiences", + it_id('ef2e5569-50c3-40c2-ab49-175cdbd5f024')(it)( + 'should update audiences', async () => { const pushAdapter = { send: function (body, installations) { return successfulTransmissions(body, installations); }, getValidPushTypes: function () { - return ["ios"]; + return ['ios']; }, }; - spyOn(pushAdapter, "send").and.callThrough(); + spyOn(pushAdapter, 'send').and.callThrough(); await reconfigureServer({ push: { adapter: pushAdapter }, }); @@ -1125,44 +1125,44 @@ describe("PushController", () => { const now = new Date(); let timesUsed = 0; const where = { - deviceType: "ios", + deviceType: 'ios', }; const installations = []; while (installations.length != 5) { - const installation = new Parse.Object("_Installation"); + const installation = new Parse.Object('_Installation'); installation.set( - "installationId", - "installation_" + installations.length + 'installationId', + 'installation_' + installations.length ); - installation.set("deviceToken", "device_token_" + installations.length); - installation.set("badge", installations.length); - installation.set("originalBadge", installations.length); - installation.set("deviceType", "ios"); + installation.set('deviceToken', 'device_token_' + installations.length); + installation.set('badge', installations.length); + installation.set('originalBadge', installations.length); + installation.set('deviceType', 'ios'); installations.push(installation); } await Parse.Object.saveAll(installations); // Create an audience - const query = new Parse.Query("_Audience"); - query.descending("createdAt"); - query.equalTo("query", JSON.stringify(where)); + const query = new Parse.Query('_Audience'); + query.descending('createdAt'); + query.equalTo('query', JSON.stringify(where)); const parseResults = results => { if (results.length > 0) { audienceId = results[0].id; - timesUsed = results[0].get("timesUsed"); + timesUsed = results[0].get('timesUsed'); if (!isFinite(timesUsed)) { timesUsed = 0; } } }; - const audience = new Parse.Object("_Audience"); - audience.set("name", "testAudience"); - audience.set("query", JSON.stringify(where)); + const audience = new Parse.Object('_Audience'); + audience.set('name', 'testAudience'); + audience.set('query', JSON.stringify(where)); await Parse.Object.saveAll(audience); await query.find({ useMasterKey: true }).then(parseResults); const body = { - data: { alert: "hello" }, + data: { alert: 'hello' }, audience_id: audienceId, }; const pushStatusId = await sendPush(body, where, config, auth); @@ -1170,66 +1170,66 @@ describe("PushController", () => { expect(pushAdapter.send.calls.count()).toBe(1); const firstCall = pushAdapter.send.calls.first(); expect(firstCall.args[0].data).toEqual({ - alert: "hello", + alert: 'hello', }); expect(firstCall.args[1].length).toBe(5); // Get the audience we used above. - const audienceQuery = new Parse.Query("_Audience"); - audienceQuery.equalTo("objectId", audienceId); + const audienceQuery = new Parse.Query('_Audience'); + audienceQuery.equalTo('objectId', audienceId); const results = await audienceQuery.find({ useMasterKey: true }); - expect(results[0].get("query")).toBe(JSON.stringify(where)); - expect(results[0].get("timesUsed")).toBe(timesUsed + 1); - expect(results[0].get("lastUsed")).not.toBeLessThan(now); + expect(results[0].get('query')).toBe(JSON.stringify(where)); + expect(results[0].get('timesUsed')).toBe(timesUsed + 1); + expect(results[0].get('lastUsed')).not.toBeLessThan(now); } ); - describe("pushTimeHasTimezoneComponent", () => { - it("should be accurate", () => { + describe('pushTimeHasTimezoneComponent', () => { + it('should be accurate', () => { expect( - PushController.pushTimeHasTimezoneComponent("2017-09-06T17:14:01.048Z") - ).toBe(true, "UTC time"); + PushController.pushTimeHasTimezoneComponent('2017-09-06T17:14:01.048Z') + ).toBe(true, 'UTC time'); expect( - PushController.pushTimeHasTimezoneComponent("2007-04-05T12:30-02:00") - ).toBe(true, "Timezone offset"); + PushController.pushTimeHasTimezoneComponent('2007-04-05T12:30-02:00') + ).toBe(true, 'Timezone offset'); expect( PushController.pushTimeHasTimezoneComponent( - "2007-04-05T12:30:00.000Z-02:00" + '2007-04-05T12:30:00.000Z-02:00' ) - ).toBe(true, "Seconds + Milliseconds + Timezone offset"); + ).toBe(true, 'Seconds + Milliseconds + Timezone offset'); expect( - PushController.pushTimeHasTimezoneComponent("2017-09-06T17:14:01.048") - ).toBe(false, "No timezone"); - expect(PushController.pushTimeHasTimezoneComponent("2017-09-06")).toBe( + PushController.pushTimeHasTimezoneComponent('2017-09-06T17:14:01.048') + ).toBe(false, 'No timezone'); + expect(PushController.pushTimeHasTimezoneComponent('2017-09-06')).toBe( false, - "YY-MM-DD" + 'YY-MM-DD' ); }); }); - describe("formatPushTime", () => { - it("should format as ISO string", () => { + describe('formatPushTime', () => { + it('should format as ISO string', () => { expect( PushController.formatPushTime({ - date: new Date("2017-09-06T17:14:01.048Z"), + date: new Date('2017-09-06T17:14:01.048Z'), isLocalTime: false, }) - ).toBe("2017-09-06T17:14:01.048Z", "UTC time"); + ).toBe('2017-09-06T17:14:01.048Z', 'UTC time'); expect( PushController.formatPushTime({ - date: new Date("2007-04-05T12:30-02:00"), + date: new Date('2007-04-05T12:30-02:00'), isLocalTime: false, }) - ).toBe("2007-04-05T14:30:00.000Z", "Timezone offset"); + ).toBe('2007-04-05T14:30:00.000Z', 'Timezone offset'); - const noTimezone = new Date("2017-09-06T17:14:01.048"); + const noTimezone = new Date('2017-09-06T17:14:01.048'); let expectedHour = 17 + noTimezone.getTimezoneOffset() / 60; - let day = "06"; + let day = '06'; if (expectedHour >= 24) { expectedHour = expectedHour - 24; - day = "07"; + day = '07'; } expect( PushController.formatPushTime({ @@ -1237,40 +1237,40 @@ describe("PushController", () => { isLocalTime: true, }) ).toBe( - `2017-09-${day}T${expectedHour.toString().padStart(2, "0")}:14:01.048`, - "No timezone" + `2017-09-${day}T${expectedHour.toString().padStart(2, '0')}:14:01.048`, + 'No timezone' ); expect( PushController.formatPushTime({ - date: new Date("2017-09-06"), + date: new Date('2017-09-06'), isLocalTime: true, }) - ).toBe("2017-09-06T00:00:00.000", "YY-MM-DD"); + ).toBe('2017-09-06T00:00:00.000', 'YY-MM-DD'); }); }); - describe("Scheduling pushes in local time", () => { - it("should preserve the push time", async () => { + describe('Scheduling pushes in local time', () => { + it('should preserve the push time', async () => { const auth = { isMaster: true }; const pushAdapter = { send(body, installations) { return successfulTransmissions(body, installations); }, getValidPushTypes() { - return ["ios"]; + return ['ios']; }, }; - const pushTime = "2017-09-06T17:14:01.048"; + const pushTime = '2017-09-06T17:14:01.048'; let expectedHour = 17 + new Date(pushTime).getTimezoneOffset() / 60; - let day = "06"; + let day = '06'; if (expectedHour >= 24) { expectedHour = expectedHour - 24; - day = "07"; + day = '07'; } const payload = { data: { - alert: "Hello World!", - badge: "Increment", + alert: 'Hello World!', + badge: 'Increment', }, push_time: pushTime, }; @@ -1281,14 +1281,14 @@ describe("PushController", () => { const config = Config.get(Parse.applicationId); const pushStatusId = await sendPush(payload, {}, config, auth); const pushStatus = await Parse.Push.getPushStatus(pushStatusId); - expect(pushStatus.get("status")).toBe("scheduled"); - expect(pushStatus.get("pushTime")).toBe( - `2017-09-${day}T${expectedHour.toString().padStart(2, "0")}:14:01.048` + expect(pushStatus.get('status')).toBe('scheduled'); + expect(pushStatus.get('pushTime')).toBe( + `2017-09-${day}T${expectedHour.toString().padStart(2, '0')}:14:01.048` ); }); }); - describe("With expiration defined", () => { + describe('With expiration defined', () => { const auth = { isMaster: true }; const pushController = new PushController(); @@ -1301,7 +1301,7 @@ describe("PushController", () => { return successfulTransmissions(body, installations); }, getValidPushTypes() { - return ["ios"]; + return ['ios']; }, }; @@ -1312,11 +1312,11 @@ describe("PushController", () => { config = Config.get(Parse.applicationId); }); - it("should throw if both expiration_time and expiration_interval are set", () => { + it('should throw if both expiration_time and expiration_interval are set', () => { expect(() => pushController.sendPush( { - expiration_time: "2017-09-25T13:21:20.841Z", + expiration_time: '2017-09-25T13:21:20.841Z', expiration_interval: 1000, }, {}, @@ -1326,7 +1326,7 @@ describe("PushController", () => { ).toThrow(); }); - it("should throw on invalid expiration_interval", () => { + it('should throw on invalid expiration_interval', () => { expect(() => pushController.sendPush( { @@ -1340,7 +1340,7 @@ describe("PushController", () => { expect(() => pushController.sendPush( { - expiration_interval: "", + expiration_interval: '', }, {}, config, @@ -1359,12 +1359,12 @@ describe("PushController", () => { ).toThrow(); }); - describe("For immediate pushes", () => { - it("should transform the expiration_interval into an absolute time", async () => { - const now = new Date("2017-09-25T13:30:10.452Z"); + describe('For immediate pushes', () => { + it('should transform the expiration_interval into an absolute time', async () => { + const now = new Date('2017-09-25T13:30:10.452Z'); const payload = { data: { - alert: "immediate push", + alert: 'immediate push', }, expiration_interval: 20 * 60, // twenty minutes }; @@ -1379,15 +1379,15 @@ describe("PushController", () => { now ); const pushStatus = await Parse.Push.getPushStatus(pushStatusId); - expect(pushStatus.get("expiry")).toBeDefined("expiry must be set"); - expect(pushStatus.get("expiry")).toEqual( - new Date("2017-09-25T13:50:10.452Z").valueOf() + expect(pushStatus.get('expiry')).toBeDefined('expiry must be set'); + expect(pushStatus.get('expiry')).toEqual( + new Date('2017-09-25T13:50:10.452Z').valueOf() ); - expect(pushStatus.get("expiration_interval")).toBeDefined( - "expiration_interval must be defined" + expect(pushStatus.get('expiration_interval')).toBeDefined( + 'expiration_interval must be defined' ); - expect(pushStatus.get("expiration_interval")).toBe(20 * 60); + expect(pushStatus.get('expiration_interval')).toBe(20 * 60); }); }); }); diff --git a/spec/PushQueue.spec.js b/spec/PushQueue.spec.js index b88f1637af..8cf42e81a7 100644 --- a/spec/PushQueue.spec.js +++ b/spec/PushQueue.spec.js @@ -1,14 +1,14 @@ -const Config = require("../lib/Config"); -const { PushQueue } = require("../lib/Push/PushQueue"); +const Config = require('../lib/Config'); +const { PushQueue } = require('../lib/Push/PushQueue'); -describe("PushQueue", () => { - describe("With a defined channel", () => { - it("should be propagated to the PushWorker and PushQueue", done => { +describe('PushQueue', () => { + describe('With a defined channel', () => { + it('should be propagated to the PushWorker and PushQueue', done => { reconfigureServer({ push: { queueOptions: { disablePushWorker: false, - channel: "my-specific-channel", + channel: 'my-specific-channel', }, adapter: { send() { @@ -23,20 +23,20 @@ describe("PushQueue", () => { .then(() => { const config = Config.get(Parse.applicationId); expect(config.pushWorker.channel).toEqual( - "my-specific-channel", - "pushWorker.channel" + 'my-specific-channel', + 'pushWorker.channel' ); expect(config.pushControllerQueue.channel).toEqual( - "my-specific-channel", - "pushWorker.channel" + 'my-specific-channel', + 'pushWorker.channel' ); }) .then(done, done.fail); }); }); - describe("Default channel", () => { - it("should be prefixed with the applicationId", done => { + describe('Default channel', () => { + it('should be prefixed with the applicationId', done => { reconfigureServer({ push: { queueOptions: { @@ -55,11 +55,11 @@ describe("PushQueue", () => { .then(() => { const config = Config.get(Parse.applicationId); expect(PushQueue.defaultPushChannel()).toEqual( - "test-parse-server-push" + 'test-parse-server-push' ); - expect(config.pushWorker.channel).toEqual("test-parse-server-push"); + expect(config.pushWorker.channel).toEqual('test-parse-server-push'); expect(config.pushControllerQueue.channel).toEqual( - "test-parse-server-push" + 'test-parse-server-push' ); }) .then(done, done.fail); diff --git a/spec/PushRouter.spec.js b/spec/PushRouter.spec.js index 94e847b809..99ff17d243 100644 --- a/spec/PushRouter.spec.js +++ b/spec/PushRouter.spec.js @@ -1,25 +1,25 @@ -const PushRouter = require("../lib/Routers/PushRouter").PushRouter; -const request = require("../lib/request"); +const PushRouter = require('../lib/Routers/PushRouter').PushRouter; +const request = require('../lib/request'); -describe("PushRouter", () => { - it("can get query condition when channels is set", done => { +describe('PushRouter', () => { + it('can get query condition when channels is set', done => { // Make mock request const request = { body: { - channels: ["Giants", "Mets"], + channels: ['Giants', 'Mets'], }, }; const where = PushRouter.getQueryCondition(request); expect(where).toEqual({ channels: { - $in: ["Giants", "Mets"], + $in: ['Giants', 'Mets'], }, }); done(); }); - it("can get query condition when where is set", done => { + it('can get query condition when where is set', done => { // Make mock request const request = { body: { @@ -36,7 +36,7 @@ describe("PushRouter", () => { done(); }); - it("can get query condition when nothing is set", done => { + it('can get query condition when nothing is set', done => { // Make mock request const request = { body: {}, @@ -48,12 +48,12 @@ describe("PushRouter", () => { done(); }); - it("can throw on getQueryCondition when channels and where are set", done => { + it('can throw on getQueryCondition when channels and where are set', done => { // Make mock request const request = { body: { channels: { - $in: ["Giants", "Mets"], + $in: ['Giants', 'Mets'], }, where: { injuryReports: true, @@ -67,23 +67,23 @@ describe("PushRouter", () => { done(); }); - it("sends a push through REST", done => { + it('sends a push through REST', done => { request({ - method: "POST", - url: Parse.serverURL + "/push", + method: 'POST', + url: Parse.serverURL + '/push', body: { channels: { - $in: ["Giants", "Mets"], + $in: ['Giants', 'Mets'], }, }, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Master-Key": Parse.masterKey, - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': Parse.masterKey, + 'Content-Type': 'application/json', }, }).then(res => { - expect(res.headers["x-parse-push-status-id"]).not.toBe(undefined); - expect(res.headers["x-parse-push-status-id"].length).toBe(10); + expect(res.headers['x-parse-push-status-id']).not.toBe(undefined); + expect(res.headers['x-parse-push-status-id'].length).toBe(10); expect(res.data.result).toBe(true); done(); }); diff --git a/spec/PushWorker.spec.js b/spec/PushWorker.spec.js index 358e88666b..12a0ece5df 100644 --- a/spec/PushWorker.spec.js +++ b/spec/PushWorker.spec.js @@ -1,11 +1,11 @@ -const PushWorker = require("../lib").PushWorker; -const PushUtils = require("../lib/Push/utils"); -const Config = require("../lib/Config"); -const { pushStatusHandler } = require("../lib/StatusHandler"); -const rest = require("../lib/rest"); +const PushWorker = require('../lib').PushWorker; +const PushUtils = require('../lib/Push/utils'); +const Config = require('../lib/Config'); +const { pushStatusHandler } = require('../lib/StatusHandler'); +const rest = require('../lib/rest'); -describe("PushWorker", () => { - it("should run with small batch", done => { +describe('PushWorker', () => { + it('should run with small batch', done => { const batchSize = 3; let sendCount = 0; reconfigureServer({ @@ -17,7 +17,7 @@ describe("PushWorker", () => { }, }) .then(() => { - expect(Config.get("test").pushWorker).toBeUndefined(); + expect(Config.get('test').pushWorker).toBeUndefined(); new PushWorker({ send: (body, installations) => { expect(installations.length <= batchSize).toBe(true); @@ -25,22 +25,22 @@ describe("PushWorker", () => { return Promise.resolve(); }, getValidPushTypes: function () { - return ["ios", "android"]; + return ['ios', 'android']; }, }); const installations = []; while (installations.length != 10) { - const installation = new Parse.Object("_Installation"); + const installation = new Parse.Object('_Installation'); installation.set( - "installationId", - "installation_" + installations.length + 'installationId', + 'installation_' + installations.length ); installation.set( - "deviceToken", - "device_token_" + installations.length + 'deviceToken', + 'device_token_' + installations.length ); - installation.set("badge", 1); - installation.set("deviceType", "ios"); + installation.set('badge', 1); + installation.set('deviceType', 'ios'); installations.push(installation); } return Parse.Object.saveAll(installations); @@ -49,10 +49,10 @@ describe("PushWorker", () => { return Parse.Push.send( { where: { - deviceType: "ios", + deviceType: 'ios', }, data: { - alert: "Hello world!", + alert: 'Hello world!', }, }, { useMasterKey: true } @@ -72,113 +72,113 @@ describe("PushWorker", () => { }); }); - describe("localized push", () => { - it("should return locales", () => { + describe('localized push', () => { + it('should return locales', () => { const locales = PushUtils.getLocalesFromPush({ data: { - "alert-fr": "french", - alert: "Yo!", - "alert-en-US": "English", + 'alert-fr': 'french', + alert: 'Yo!', + 'alert-en-US': 'English', }, }); - expect(locales).toEqual(["fr", "en-US"]); + expect(locales).toEqual(['fr', 'en-US']); }); - it("should return and empty array if no locale is set", () => { + it('should return and empty array if no locale is set', () => { const locales = PushUtils.getLocalesFromPush({ data: { - alert: "Yo!", + alert: 'Yo!', }, }); expect(locales).toEqual([]); }); - it("should deduplicate locales", () => { + it('should deduplicate locales', () => { const locales = PushUtils.getLocalesFromPush({ data: { - alert: "Yo!", - "alert-fr": "french", - "title-fr": "french", + alert: 'Yo!', + 'alert-fr': 'french', + 'title-fr': 'french', }, }); - expect(locales).toEqual(["fr"]); + expect(locales).toEqual(['fr']); }); - it("should handle empty body data", () => { + it('should handle empty body data', () => { expect(PushUtils.getLocalesFromPush({})).toEqual([]); }); - it("transforms body appropriately", () => { + it('transforms body appropriately', () => { const cleanBody = PushUtils.transformPushBodyForLocale( { data: { - alert: "Yo!", - "alert-fr": "frenchy!", - "alert-en": "english", + alert: 'Yo!', + 'alert-fr': 'frenchy!', + 'alert-en': 'english', }, }, - "fr" + 'fr' ); expect(cleanBody).toEqual({ data: { - alert: "frenchy!", + alert: 'frenchy!', }, }); }); - it("transforms body appropriately with title locale", () => { + it('transforms body appropriately with title locale', () => { const cleanBody = PushUtils.transformPushBodyForLocale( { data: { - alert: "Yo!", - "alert-fr": "frenchy!", - "alert-en": "english", - "title-fr": "french title", + alert: 'Yo!', + 'alert-fr': 'frenchy!', + 'alert-en': 'english', + 'title-fr': 'french title', }, }, - "fr" + 'fr' ); expect(cleanBody).toEqual({ data: { - alert: "frenchy!", - title: "french title", + alert: 'frenchy!', + title: 'french title', }, }); }); - it("maps body on all provided locales", () => { + it('maps body on all provided locales', () => { const bodies = PushUtils.bodiesPerLocales( { data: { - alert: "Yo!", - "alert-fr": "frenchy!", - "alert-en": "english", - "title-fr": "french title", + alert: 'Yo!', + 'alert-fr': 'frenchy!', + 'alert-en': 'english', + 'title-fr': 'french title', }, }, - ["fr", "en"] + ['fr', 'en'] ); expect(bodies).toEqual({ fr: { data: { - alert: "frenchy!", - title: "french title", + alert: 'frenchy!', + title: 'french title', }, }, en: { data: { - alert: "english", + alert: 'english', }, }, default: { data: { - alert: "Yo!", + alert: 'Yo!', }, }, }); }); - it("should properly handle default cases", () => { + it('should properly handle default cases', () => { expect(PushUtils.transformPushBodyForLocale({})).toEqual({}); expect(PushUtils.stripLocalesFromBody({})).toEqual({}); expect(PushUtils.bodiesPerLocales({ where: {} })).toEqual({ @@ -188,11 +188,11 @@ describe("PushWorker", () => { }); }); - describe("pushStatus", () => { - it("should remove invalid installations", done => { - const config = Config.get("test"); + describe('pushStatus', () => { + it('should remove invalid installations', done => { + const config = Config.get('test'); const handler = pushStatusHandler(config); - const spy = spyOn(config.database, "update").and.callFake(() => { + const spy = spyOn(config.database, 'update').and.callFake(() => { return Promise.resolve({}); }); const toAwait = handler.trackSent( @@ -201,64 +201,64 @@ describe("PushWorker", () => { transmitted: false, device: { deviceToken: 1, - deviceType: "ios", + deviceType: 'ios', }, - response: { error: "Unregistered" }, + response: { error: 'Unregistered' }, }, { transmitted: true, device: { deviceToken: 10, - deviceType: "ios", + deviceType: 'ios', }, }, { transmitted: false, device: { deviceToken: 2, - deviceType: "ios", + deviceType: 'ios', }, - response: { error: "NotRegistered" }, + response: { error: 'NotRegistered' }, }, { transmitted: false, device: { deviceToken: 3, - deviceType: "ios", + deviceType: 'ios', }, - response: { error: "InvalidRegistration" }, + response: { error: 'InvalidRegistration' }, }, { transmitted: true, device: { deviceToken: 11, - deviceType: "ios", + deviceType: 'ios', }, }, { transmitted: false, device: { deviceToken: 4, - deviceType: "ios", + deviceType: 'ios', }, - response: { error: "InvalidRegistration" }, + response: { error: 'InvalidRegistration' }, }, { transmitted: false, device: { deviceToken: 5, - deviceType: "ios", + deviceType: 'ios', }, - response: { error: "InvalidRegistration" }, + response: { error: 'InvalidRegistration' }, }, { // should not be deleted transmitted: false, device: { deviceToken: Parse.Error.OBJECT_NOT_FOUND, - deviceType: "ios", + deviceType: 'ios', }, - response: { error: "invalid error..." }, + response: { error: 'invalid error...' }, }, ], undefined, @@ -267,22 +267,22 @@ describe("PushWorker", () => { expect(spy).toHaveBeenCalled(); expect(spy.calls.count()).toBe(1); const lastCall = spy.calls.mostRecent(); - expect(lastCall.args[0]).toBe("_Installation"); + expect(lastCall.args[0]).toBe('_Installation'); expect(lastCall.args[1]).toEqual({ deviceToken: { $in: [1, 2, 3, 4, 5] }, }); expect(lastCall.args[2]).toEqual({ - deviceToken: { __op: "Delete" }, + deviceToken: { __op: 'Delete' }, }); toAwait.then(done).catch(done); }); - it_id("764d28ab-241b-4b96-8ce9-e03541850e3f")(it)( - "tracks push status per UTC offsets", + it_id('764d28ab-241b-4b96-8ce9-e03541850e3f')(it)( + 'tracks push status per UTC offsets', done => { - const config = Config.get("test"); + const config = Config.get('test'); const handler = pushStatusHandler(config); - const spy = spyOn(rest, "update").and.callThrough(); + const spy = spyOn(rest, 'update').and.callThrough(); const UTCOffset = 1; handler .setInitial() @@ -293,14 +293,14 @@ describe("PushWorker", () => { transmitted: false, device: { deviceToken: 1, - deviceType: "ios", + deviceType: 'ios', }, }, { transmitted: true, device: { deviceToken: 1, - deviceType: "ios", + deviceType: 'ios', }, }, ], @@ -312,50 +312,50 @@ describe("PushWorker", () => { const lastCall = spy.calls.mostRecent(); expect(lastCall.args[2]).toBe(`_PushStatus`); expect(lastCall.args[4]).toEqual({ - numSent: { __op: "Increment", amount: 1 }, - numFailed: { __op: "Increment", amount: 1 }, - "sentPerType.ios": { __op: "Increment", amount: 1 }, - "failedPerType.ios": { __op: "Increment", amount: 1 }, + numSent: { __op: 'Increment', amount: 1 }, + numFailed: { __op: 'Increment', amount: 1 }, + 'sentPerType.ios': { __op: 'Increment', amount: 1 }, + 'failedPerType.ios': { __op: 'Increment', amount: 1 }, [`sentPerUTCOffset.${UTCOffset}`]: { - __op: "Increment", + __op: 'Increment', amount: 1, }, [`failedPerUTCOffset.${UTCOffset}`]: { - __op: "Increment", + __op: 'Increment', amount: 1, }, - count: { __op: "Increment", amount: -1 }, - status: "running", + count: { __op: 'Increment', amount: -1 }, + status: 'running', }); - const query = new Parse.Query("_PushStatus"); + const query = new Parse.Query('_PushStatus'); return query.get(handler.objectId, { useMasterKey: true }); }) .then(pushStatus => { - const sentPerUTCOffset = pushStatus.get("sentPerUTCOffset"); - expect(sentPerUTCOffset["1"]).toBe(1); - const failedPerUTCOffset = pushStatus.get("failedPerUTCOffset"); - expect(failedPerUTCOffset["1"]).toBe(1); + const sentPerUTCOffset = pushStatus.get('sentPerUTCOffset'); + expect(sentPerUTCOffset['1']).toBe(1); + const failedPerUTCOffset = pushStatus.get('failedPerUTCOffset'); + expect(failedPerUTCOffset['1']).toBe(1); return handler.trackSent( [ { transmitted: false, device: { deviceToken: 1, - deviceType: "ios", + deviceType: 'ios', }, }, { transmitted: true, device: { deviceToken: 1, - deviceType: "ios", + deviceType: 'ios', }, }, { transmitted: true, device: { deviceToken: 1, - deviceType: "ios", + deviceType: 'ios', }, }, ], @@ -363,24 +363,24 @@ describe("PushWorker", () => { ); }) .then(() => { - const query = new Parse.Query("_PushStatus"); + const query = new Parse.Query('_PushStatus'); return query.get(handler.objectId, { useMasterKey: true }); }) .then(pushStatus => { - const sentPerUTCOffset = pushStatus.get("sentPerUTCOffset"); - expect(sentPerUTCOffset["1"]).toBe(3); - const failedPerUTCOffset = pushStatus.get("failedPerUTCOffset"); - expect(failedPerUTCOffset["1"]).toBe(2); + const sentPerUTCOffset = pushStatus.get('sentPerUTCOffset'); + expect(sentPerUTCOffset['1']).toBe(3); + const failedPerUTCOffset = pushStatus.get('failedPerUTCOffset'); + expect(failedPerUTCOffset['1']).toBe(2); }) .then(done) .catch(done.fail); } ); - it("tracks push status per UTC offsets with negative offsets", done => { - const config = Config.get("test"); + it('tracks push status per UTC offsets with negative offsets', done => { + const config = Config.get('test'); const handler = pushStatusHandler(config); - const spy = spyOn(rest, "update").and.callThrough(); + const spy = spyOn(rest, 'update').and.callThrough(); const UTCOffset = -6; handler .setInitial() @@ -391,17 +391,17 @@ describe("PushWorker", () => { transmitted: false, device: { deviceToken: 1, - deviceType: "ios", + deviceType: 'ios', }, - response: { error: "Unregistered" }, + response: { error: 'Unregistered' }, }, { transmitted: true, device: { deviceToken: 1, - deviceType: "ios", + deviceType: 'ios', }, - response: { error: "Unregistered" }, + response: { error: 'Unregistered' }, }, ], UTCOffset @@ -410,19 +410,19 @@ describe("PushWorker", () => { .then(() => { expect(spy).toHaveBeenCalled(); const lastCall = spy.calls.mostRecent(); - expect(lastCall.args[2]).toBe("_PushStatus"); + expect(lastCall.args[2]).toBe('_PushStatus'); expect(lastCall.args[4]).toEqual({ - numSent: { __op: "Increment", amount: 1 }, - numFailed: { __op: "Increment", amount: 1 }, - "sentPerType.ios": { __op: "Increment", amount: 1 }, - "failedPerType.ios": { __op: "Increment", amount: 1 }, - [`sentPerUTCOffset.${UTCOffset}`]: { __op: "Increment", amount: 1 }, + numSent: { __op: 'Increment', amount: 1 }, + numFailed: { __op: 'Increment', amount: 1 }, + 'sentPerType.ios': { __op: 'Increment', amount: 1 }, + 'failedPerType.ios': { __op: 'Increment', amount: 1 }, + [`sentPerUTCOffset.${UTCOffset}`]: { __op: 'Increment', amount: 1 }, [`failedPerUTCOffset.${UTCOffset}`]: { - __op: "Increment", + __op: 'Increment', amount: 1, }, - count: { __op: "Increment", amount: -1 }, - status: "running", + count: { __op: 'Increment', amount: -1 }, + status: 'running', }); done(); }); diff --git a/spec/QueryTools.spec.js b/spec/QueryTools.spec.js index 51bb63e15e..8dbda98b0a 100644 --- a/spec/QueryTools.spec.js +++ b/spec/QueryTools.spec.js @@ -1,140 +1,140 @@ -const Parse = require("parse/node"); +const Parse = require('parse/node'); -const Id = require("../lib/LiveQuery/Id"); -const QueryTools = require("../lib/LiveQuery/QueryTools"); +const Id = require('../lib/LiveQuery/Id'); +const QueryTools = require('../lib/LiveQuery/QueryTools'); const queryHash = QueryTools.queryHash; const matchesQuery = QueryTools.matchesQuery; -const Item = Parse.Object.extend("Item"); +const Item = Parse.Object.extend('Item'); -describe("queryHash", function () { - it("should always hash a query to the same string", function () { +describe('queryHash', function () { + it('should always hash a query to the same string', function () { const q = new Parse.Query(Item); - q.equalTo("field", "value"); - q.exists("name"); - q.ascending("createdAt"); + q.equalTo('field', 'value'); + q.exists('name'); + q.ascending('createdAt'); q.limit(10); const firstHash = queryHash(q); const secondHash = queryHash(q); expect(firstHash).toBe(secondHash); }); - it("should return equivalent hashes for equivalent queries", function () { + it('should return equivalent hashes for equivalent queries', function () { let q1 = new Parse.Query(Item); - q1.equalTo("field", "value"); - q1.exists("name"); - q1.lessThan("age", 30); - q1.greaterThan("age", 3); - q1.ascending("createdAt"); - q1.include(["name", "age"]); + q1.equalTo('field', 'value'); + q1.exists('name'); + q1.lessThan('age', 30); + q1.greaterThan('age', 3); + q1.ascending('createdAt'); + q1.include(['name', 'age']); q1.limit(10); let q2 = new Parse.Query(Item); q2.limit(10); - q2.greaterThan("age", 3); - q2.lessThan("age", 30); - q2.include(["name", "age"]); - q2.ascending("createdAt"); - q2.exists("name"); - q2.equalTo("field", "value"); + q2.greaterThan('age', 3); + q2.lessThan('age', 30); + q2.include(['name', 'age']); + q2.ascending('createdAt'); + q2.exists('name'); + q2.equalTo('field', 'value'); let firstHash = queryHash(q1); let secondHash = queryHash(q2); expect(firstHash).toBe(secondHash); - q1.containedIn("fruit", ["apple", "banana", "cherry"]); + q1.containedIn('fruit', ['apple', 'banana', 'cherry']); firstHash = queryHash(q1); expect(firstHash).not.toBe(secondHash); - q2.containedIn("fruit", ["banana", "cherry", "apple"]); + q2.containedIn('fruit', ['banana', 'cherry', 'apple']); secondHash = queryHash(q2); expect(secondHash).toBe(firstHash); - q1.containedIn("fruit", ["coconut"]); + q1.containedIn('fruit', ['coconut']); firstHash = queryHash(q1); expect(firstHash).not.toBe(secondHash); q1 = new Parse.Query(Item); - q1.equalTo("field", "value"); - q1.lessThan("age", 30); - q1.exists("name"); + q1.equalTo('field', 'value'); + q1.lessThan('age', 30); + q1.exists('name'); q2 = new Parse.Query(Item); - q2.equalTo("name", "person"); - q2.equalTo("field", "other"); + q2.equalTo('name', 'person'); + q2.equalTo('field', 'other'); firstHash = queryHash(Parse.Query.or(q1, q2)); secondHash = queryHash(Parse.Query.or(q2, q1)); expect(firstHash).toBe(secondHash); }); - it("should not let fields of different types appear similar", function () { + it('should not let fields of different types appear similar', function () { let q1 = new Parse.Query(Item); - q1.lessThan("age", 30); + q1.lessThan('age', 30); const q2 = new Parse.Query(Item); - q2.equalTo("age", "{$lt:30}"); + q2.equalTo('age', '{$lt:30}'); expect(queryHash(q1)).not.toBe(queryHash(q2)); q1 = new Parse.Query(Item); - q1.equalTo("age", 15); + q1.equalTo('age', 15); - q2.equalTo("age", "15"); + q2.equalTo('age', '15'); expect(queryHash(q1)).not.toBe(queryHash(q2)); }); }); -describe("matchesQuery", function () { - it("matches blanket queries", function () { +describe('matchesQuery', function () { + it('matches blanket queries', function () { const obj = { - id: new Id("Klass", "O1"), + id: new Id('Klass', 'O1'), value: 12, }; - const q = new Parse.Query("Klass"); + const q = new Parse.Query('Klass'); expect(matchesQuery(obj, q)).toBe(true); - obj.id = new Id("Other", "O1"); + obj.id = new Id('Other', 'O1'); expect(matchesQuery(obj, q)).toBe(false); }); - it("matches existence queries", function () { + it('matches existence queries', function () { const obj = { - id: new Id("Item", "O1"), + id: new Id('Item', 'O1'), count: 15, }; - const q = new Parse.Query("Item"); - q.exists("count"); + const q = new Parse.Query('Item'); + q.exists('count'); expect(matchesQuery(obj, q)).toBe(true); - q.exists("name"); + q.exists('name'); expect(matchesQuery(obj, q)).toBe(false); }); - it("matches queries with doesNotExist constraint", function () { + it('matches queries with doesNotExist constraint', function () { const obj = { - id: new Id("Item", "O1"), + id: new Id('Item', 'O1'), count: 15, }; - let q = new Parse.Query("Item"); - q.doesNotExist("name"); + let q = new Parse.Query('Item'); + q.doesNotExist('name'); expect(matchesQuery(obj, q)).toBe(true); - q = new Parse.Query("Item"); - q.doesNotExist("count"); + q = new Parse.Query('Item'); + q.doesNotExist('count'); expect(matchesQuery(obj, q)).toBe(false); }); - it("matches queries with eq constraint", function () { + it('matches queries with eq constraint', function () { const obj = { - objectId: "Person2", + objectId: 'Person2', score: 12, - name: "Tom", + name: 'Tom', }; const q1 = { objectId: { - $eq: "Person2", + $eq: 'Person2', }, }; @@ -146,7 +146,7 @@ describe("matchesQuery", function () { const q3 = { name: { - $eq: "Tom", + $eq: 'Tom', }, }; expect(matchesQuery(obj, q1)).toBe(true); @@ -154,51 +154,51 @@ describe("matchesQuery", function () { expect(matchesQuery(obj, q3)).toBe(true); }); - it("matches on equality queries", function () { + it('matches on equality queries', function () { const day = new Date(); const location = new Parse.GeoPoint({ latitude: 37.484815, longitude: -122.148377, }); const obj = { - id: new Id("Person", "O1"), + id: new Id('Person', 'O1'), score: 12, - name: "Bill", + name: 'Bill', birthday: day, lastLocation: location, }; - let q = new Parse.Query("Person"); - q.equalTo("score", 12); + let q = new Parse.Query('Person'); + q.equalTo('score', 12); expect(matchesQuery(obj, q)).toBe(true); - q = new Parse.Query("Person"); - q.equalTo("name", "Bill"); + q = new Parse.Query('Person'); + q.equalTo('name', 'Bill'); expect(matchesQuery(obj, q)).toBe(true); - q.equalTo("name", "Jeff"); + q.equalTo('name', 'Jeff'); expect(matchesQuery(obj, q)).toBe(false); - q = new Parse.Query("Person"); - q.containedIn("name", ["Adam", "Ben", "Charles"]); + q = new Parse.Query('Person'); + q.containedIn('name', ['Adam', 'Ben', 'Charles']); expect(matchesQuery(obj, q)).toBe(false); - q.containedIn("name", ["Adam", "Bill", "Charles"]); + q.containedIn('name', ['Adam', 'Bill', 'Charles']); expect(matchesQuery(obj, q)).toBe(true); - q = new Parse.Query("Person"); - q.notContainedIn("name", ["Adam", "Bill", "Charles"]); + q = new Parse.Query('Person'); + q.notContainedIn('name', ['Adam', 'Bill', 'Charles']); expect(matchesQuery(obj, q)).toBe(false); - q.notContainedIn("name", ["Adam", "Ben", "Charles"]); + q.notContainedIn('name', ['Adam', 'Ben', 'Charles']); expect(matchesQuery(obj, q)).toBe(true); - q = new Parse.Query("Person"); - q.equalTo("birthday", day); + q = new Parse.Query('Person'); + q.equalTo('birthday', day); expect(matchesQuery(obj, q)).toBe(true); - q.equalTo("birthday", new Date(1990, 1)); + q.equalTo('birthday', new Date(1990, 1)); expect(matchesQuery(obj, q)).toBe(false); - q = new Parse.Query("Person"); + q = new Parse.Query('Person'); q.equalTo( - "lastLocation", + 'lastLocation', new Parse.GeoPoint({ latitude: 37.484815, longitude: -122.148377, @@ -206,7 +206,7 @@ describe("matchesQuery", function () { ); expect(matchesQuery(obj, q)).toBe(true); q.equalTo( - "lastLocation", + 'lastLocation', new Parse.GeoPoint({ latitude: 37.4848, longitude: -122.1483, @@ -215,156 +215,156 @@ describe("matchesQuery", function () { expect(matchesQuery(obj, q)).toBe(false); q.equalTo( - "lastLocation", + 'lastLocation', new Parse.GeoPoint({ latitude: 37.484815, longitude: -122.148377, }) ); - q.equalTo("score", 12); - q.equalTo("name", "Bill"); - q.equalTo("birthday", day); + q.equalTo('score', 12); + q.equalTo('name', 'Bill'); + q.equalTo('birthday', day); expect(matchesQuery(obj, q)).toBe(true); - q.equalTo("name", "bill"); + q.equalTo('name', 'bill'); expect(matchesQuery(obj, q)).toBe(false); let img = { - id: new Id("Image", "I1"), - tags: ["nofilter", "latergram", "tbt"], + id: new Id('Image', 'I1'), + tags: ['nofilter', 'latergram', 'tbt'], }; - q = new Parse.Query("Image"); - q.equalTo("tags", "selfie"); + q = new Parse.Query('Image'); + q.equalTo('tags', 'selfie'); expect(matchesQuery(img, q)).toBe(false); - q.equalTo("tags", "tbt"); + q.equalTo('tags', 'tbt'); expect(matchesQuery(img, q)).toBe(true); - const q2 = new Parse.Query("Image"); - q2.containsAll("tags", ["latergram", "nofilter"]); + const q2 = new Parse.Query('Image'); + q2.containsAll('tags', ['latergram', 'nofilter']); expect(matchesQuery(img, q2)).toBe(true); - q2.containsAll("tags", ["latergram", "selfie"]); + q2.containsAll('tags', ['latergram', 'selfie']); expect(matchesQuery(img, q2)).toBe(false); const u = new Parse.User(); - u.id = "U2"; - q = new Parse.Query("Image"); - q.equalTo("owner", u); + u.id = 'U2'; + q = new Parse.Query('Image'); + q.equalTo('owner', u); img = { - className: "Image", - objectId: "I1", + className: 'Image', + objectId: 'I1', owner: { - className: "_User", - objectId: "U2", + className: '_User', + objectId: 'U2', }, }; expect(matchesQuery(img, q)).toBe(true); - img.owner.objectId = "U3"; + img.owner.objectId = 'U3'; expect(matchesQuery(img, q)).toBe(false); // pointers in arrays - q = new Parse.Query("Image"); - q.equalTo("owners", u); + q = new Parse.Query('Image'); + q.equalTo('owners', u); img = { - className: "Image", - objectId: "I1", + className: 'Image', + objectId: 'I1', owners: [ { - className: "_User", - objectId: "U2", + className: '_User', + objectId: 'U2', }, ], }; expect(matchesQuery(img, q)).toBe(true); - img.owners[0].objectId = "U3"; + img.owners[0].objectId = 'U3'; expect(matchesQuery(img, q)).toBe(false); }); - it("matches on inequalities", function () { + it('matches on inequalities', function () { const player = { - id: new Id("Person", "O1"), + id: new Id('Person', 'O1'), score: 12, - name: "Bill", + name: 'Bill', birthday: new Date(1980, 2, 4), }; - let q = new Parse.Query("Person"); - q.lessThan("score", 15); + let q = new Parse.Query('Person'); + q.lessThan('score', 15); expect(matchesQuery(player, q)).toBe(true); - q.lessThan("score", 10); + q.lessThan('score', 10); expect(matchesQuery(player, q)).toBe(false); - q = new Parse.Query("Person"); - q.lessThanOrEqualTo("score", 15); + q = new Parse.Query('Person'); + q.lessThanOrEqualTo('score', 15); expect(matchesQuery(player, q)).toBe(true); - q.lessThanOrEqualTo("score", 12); + q.lessThanOrEqualTo('score', 12); expect(matchesQuery(player, q)).toBe(true); - q.lessThanOrEqualTo("score", 10); + q.lessThanOrEqualTo('score', 10); expect(matchesQuery(player, q)).toBe(false); - q = new Parse.Query("Person"); - q.greaterThan("score", 15); + q = new Parse.Query('Person'); + q.greaterThan('score', 15); expect(matchesQuery(player, q)).toBe(false); - q.greaterThan("score", 10); + q.greaterThan('score', 10); expect(matchesQuery(player, q)).toBe(true); - q = new Parse.Query("Person"); - q.greaterThanOrEqualTo("score", 15); + q = new Parse.Query('Person'); + q.greaterThanOrEqualTo('score', 15); expect(matchesQuery(player, q)).toBe(false); - q.greaterThanOrEqualTo("score", 12); + q.greaterThanOrEqualTo('score', 12); expect(matchesQuery(player, q)).toBe(true); - q.greaterThanOrEqualTo("score", 10); + q.greaterThanOrEqualTo('score', 10); expect(matchesQuery(player, q)).toBe(true); - q = new Parse.Query("Person"); - q.notEqualTo("score", 12); + q = new Parse.Query('Person'); + q.notEqualTo('score', 12); expect(matchesQuery(player, q)).toBe(false); - q.notEqualTo("score", 40); + q.notEqualTo('score', 40); expect(matchesQuery(player, q)).toBe(true); }); - it("matches an $or query", function () { + it('matches an $or query', function () { const player = { - id: new Id("Player", "P1"), - name: "Player 1", + id: new Id('Player', 'P1'), + name: 'Player 1', score: 12, }; - const q = new Parse.Query("Player"); - q.equalTo("name", "Player 1"); - const q2 = new Parse.Query("Player"); - q2.equalTo("name", "Player 2"); + const q = new Parse.Query('Player'); + q.equalTo('name', 'Player 1'); + const q2 = new Parse.Query('Player'); + q2.equalTo('name', 'Player 2'); const orQuery = Parse.Query.or(q, q2); expect(matchesQuery(player, q)).toBe(true); expect(matchesQuery(player, q2)).toBe(false); expect(matchesQuery(player, orQuery)).toBe(true); }); - it("does not match $all query when value is missing", () => { + it('does not match $all query when value is missing', () => { const player = { - id: new Id("Player", "P1"), - name: "Player 1", + id: new Id('Player', 'P1'), + name: 'Player 1', score: 12, }; const q = { missing: { $all: [1, 2, 3] } }; expect(matchesQuery(player, q)).toBe(false); }); - it("matches an $and query", () => { + it('matches an $and query', () => { const player = { - id: new Id("Player", "P1"), - name: "Player 1", + id: new Id('Player', 'P1'), + name: 'Player 1', score: 12, }; - const q = new Parse.Query("Player"); - q.equalTo("name", "Player 1"); - const q2 = new Parse.Query("Player"); - q2.equalTo("score", 12); - const q3 = new Parse.Query("Player"); - q3.equalTo("score", 100); + const q = new Parse.Query('Player'); + q.equalTo('name', 'Player 1'); + const q2 = new Parse.Query('Player'); + q2.equalTo('score', 12); + const q3 = new Parse.Query('Player'); + q3.equalTo('score', 100); const andQuery1 = Parse.Query.and(q, q2); const andQuery2 = Parse.Query.and(q, q3); expect(matchesQuery(player, q)).toBe(true); @@ -373,19 +373,19 @@ describe("matchesQuery", function () { expect(matchesQuery(player, andQuery2)).toBe(false); }); - it("matches an $nor query", () => { + it('matches an $nor query', () => { const player = { - id: new Id("Player", "P1"), - name: "Player 1", + id: new Id('Player', 'P1'), + name: 'Player 1', score: 12, }; - const q = new Parse.Query("Player"); - q.equalTo("name", "Player 1"); - const q2 = new Parse.Query("Player"); - q2.equalTo("name", "Player 2"); - const q3 = new Parse.Query("Player"); - q3.equalTo("name", "Player 3"); + const q = new Parse.Query('Player'); + q.equalTo('name', 'Player 1'); + const q2 = new Parse.Query('Player'); + q2.equalTo('name', 'Player 2'); + const q3 = new Parse.Query('Player'); + q3.equalTo('name', 'Player 3'); const norQuery1 = Parse.Query.nor(q, q2); const norQuery2 = Parse.Query.nor(q2, q3); @@ -396,109 +396,109 @@ describe("matchesQuery", function () { expect(matchesQuery(player, norQuery2)).toBe(true); }); - it("matches $regex queries", function () { + it('matches $regex queries', function () { const player = { - id: new Id("Player", "P1"), - name: "Player 1", + id: new Id('Player', 'P1'), + name: 'Player 1', score: 12, }; - let q = new Parse.Query("Player"); - q.startsWith("name", "Play"); + let q = new Parse.Query('Player'); + q.startsWith('name', 'Play'); expect(matchesQuery(player, q)).toBe(true); - q.startsWith("name", "Ploy"); + q.startsWith('name', 'Ploy'); expect(matchesQuery(player, q)).toBe(false); - q = new Parse.Query("Player"); - q.endsWith("name", " 1"); + q = new Parse.Query('Player'); + q.endsWith('name', ' 1'); expect(matchesQuery(player, q)).toBe(true); - q.endsWith("name", " 2"); + q.endsWith('name', ' 2'); expect(matchesQuery(player, q)).toBe(false); // Check that special characters are escaped - player.name = "Android-7"; - q = new Parse.Query("Player"); - q.contains("name", "d-7"); + player.name = 'Android-7'; + q = new Parse.Query('Player'); + q.contains('name', 'd-7'); expect(matchesQuery(player, q)).toBe(true); - q = new Parse.Query("Player"); - q.matches("name", /A.d/); + q = new Parse.Query('Player'); + q.matches('name', /A.d/); expect(matchesQuery(player, q)).toBe(true); - q.matches("name", /A[^n]d/); + q.matches('name', /A[^n]d/); expect(matchesQuery(player, q)).toBe(false); // Check that the string \\E is returned to normal - player.name = "Slash \\E"; - q = new Parse.Query("Player"); - q.endsWith("name", "h \\E"); + player.name = 'Slash \\E'; + q = new Parse.Query('Player'); + q.endsWith('name', 'h \\E'); expect(matchesQuery(player, q)).toBe(true); - q.endsWith("name", "h \\Ee"); + q.endsWith('name', 'h \\Ee'); expect(matchesQuery(player, q)).toBe(false); - player.name = "Slash \\Q and more"; - q = new Parse.Query("Player"); - q.contains("name", "h \\Q and"); + player.name = 'Slash \\Q and more'; + q = new Parse.Query('Player'); + q.contains('name', 'h \\Q and'); expect(matchesQuery(player, q)).toBe(true); - q.contains("name", "h \\Q or"); + q.contains('name', 'h \\Q or'); expect(matchesQuery(player, q)).toBe(false); }); - it("matches $nearSphere queries", function () { - let q = new Parse.Query("Checkin"); - q.near("location", new Parse.GeoPoint(20, 20)); + it('matches $nearSphere queries', function () { + let q = new Parse.Query('Checkin'); + q.near('location', new Parse.GeoPoint(20, 20)); // With no max distance, any GeoPoint is 'near' const pt = { - id: new Id("Checkin", "C1"), + id: new Id('Checkin', 'C1'), location: new Parse.GeoPoint(40, 40), }; const ptUndefined = { - id: new Id("Checkin", "C1"), + id: new Id('Checkin', 'C1'), }; const ptNull = { - id: new Id("Checkin", "C1"), + id: new Id('Checkin', 'C1'), location: null, }; expect(matchesQuery(pt, q)).toBe(true); expect(matchesQuery(ptUndefined, q)).toBe(false); expect(matchesQuery(ptNull, q)).toBe(false); - q = new Parse.Query("Checkin"); + q = new Parse.Query('Checkin'); pt.location = new Parse.GeoPoint(40, 40); - q.withinRadians("location", new Parse.GeoPoint(30, 30), 0.3); + q.withinRadians('location', new Parse.GeoPoint(30, 30), 0.3); expect(matchesQuery(pt, q)).toBe(true); - q.withinRadians("location", new Parse.GeoPoint(30, 30), 0.2); + q.withinRadians('location', new Parse.GeoPoint(30, 30), 0.2); expect(matchesQuery(pt, q)).toBe(false); }); - it("matches $within queries", function () { + it('matches $within queries', function () { const caltrainStation = { - id: new Id("Checkin", "C1"), + id: new Id('Checkin', 'C1'), location: new Parse.GeoPoint(37.776346, -122.394218), - name: "Caltrain", + name: 'Caltrain', }; const santaClara = { - id: new Id("Checkin", "C2"), + id: new Id('Checkin', 'C2'), location: new Parse.GeoPoint(37.325635, -121.945753), - name: "Santa Clara", + name: 'Santa Clara', }; const noLocation = { - id: new Id("Checkin", "C2"), - name: "Santa Clara", + id: new Id('Checkin', 'C2'), + name: 'Santa Clara', }; const nullLocation = { - id: new Id("Checkin", "C2"), + id: new Id('Checkin', 'C2'), location: null, - name: "Santa Clara", + name: 'Santa Clara', }; - let q = new Parse.Query("Checkin").withinGeoBox( - "location", + let q = new Parse.Query('Checkin').withinGeoBox( + 'location', new Parse.GeoPoint(37.708813, -122.526398), new Parse.GeoPoint(37.822802, -122.373962) ); @@ -508,8 +508,8 @@ describe("matchesQuery", function () { expect(matchesQuery(noLocation, q)).toBe(false); expect(matchesQuery(nullLocation, q)).toBe(false); // Invalid rectangles - q = new Parse.Query("Checkin").withinGeoBox( - "location", + q = new Parse.Query('Checkin').withinGeoBox( + 'location', new Parse.GeoPoint(37.822802, -122.373962), new Parse.GeoPoint(37.708813, -122.526398) ); @@ -517,8 +517,8 @@ describe("matchesQuery", function () { expect(matchesQuery(caltrainStation, q)).toBe(false); expect(matchesQuery(santaClara, q)).toBe(false); - q = new Parse.Query("Checkin").withinGeoBox( - "location", + q = new Parse.Query('Checkin').withinGeoBox( + 'location', new Parse.GeoPoint(37.708813, -122.373962), new Parse.GeoPoint(37.822802, -122.526398) ); @@ -527,254 +527,254 @@ describe("matchesQuery", function () { expect(matchesQuery(santaClara, q)).toBe(false); }); - it("matches on subobjects with dot notation", function () { + it('matches on subobjects with dot notation', function () { const message = { - id: new Id("Message", "O1"), - text: "content", - status: { x: "read", y: "delivered" }, + id: new Id('Message', 'O1'), + text: 'content', + status: { x: 'read', y: 'delivered' }, }; - let q = new Parse.Query("Message"); - q.equalTo("status.x", "read"); + let q = new Parse.Query('Message'); + q.equalTo('status.x', 'read'); expect(matchesQuery(message, q)).toBe(true); - q = new Parse.Query("Message"); - q.equalTo("status.z", "read"); + q = new Parse.Query('Message'); + q.equalTo('status.z', 'read'); expect(matchesQuery(message, q)).toBe(false); - q = new Parse.Query("Message"); - q.equalTo("status.x", "delivered"); + q = new Parse.Query('Message'); + q.equalTo('status.x', 'delivered'); expect(matchesQuery(message, q)).toBe(false); - q = new Parse.Query("Message"); - q.notEqualTo("status.x", "read"); + q = new Parse.Query('Message'); + q.notEqualTo('status.x', 'read'); expect(matchesQuery(message, q)).toBe(false); - q = new Parse.Query("Message"); - q.notEqualTo("status.z", "read"); + q = new Parse.Query('Message'); + q.notEqualTo('status.z', 'read'); expect(matchesQuery(message, q)).toBe(true); - q = new Parse.Query("Message"); - q.notEqualTo("status.x", "delivered"); + q = new Parse.Query('Message'); + q.notEqualTo('status.x', 'delivered'); expect(matchesQuery(message, q)).toBe(true); - q = new Parse.Query("Message"); - q.exists("status.x"); + q = new Parse.Query('Message'); + q.exists('status.x'); expect(matchesQuery(message, q)).toBe(true); - q = new Parse.Query("Message"); - q.exists("status.z"); + q = new Parse.Query('Message'); + q.exists('status.z'); expect(matchesQuery(message, q)).toBe(false); - q = new Parse.Query("Message"); - q.exists("nonexistent.x"); + q = new Parse.Query('Message'); + q.exists('nonexistent.x'); expect(matchesQuery(message, q)).toBe(false); - q = new Parse.Query("Message"); - q.doesNotExist("status.x"); + q = new Parse.Query('Message'); + q.doesNotExist('status.x'); expect(matchesQuery(message, q)).toBe(false); - q = new Parse.Query("Message"); - q.doesNotExist("status.z"); + q = new Parse.Query('Message'); + q.doesNotExist('status.z'); expect(matchesQuery(message, q)).toBe(true); - q = new Parse.Query("Message"); - q.doesNotExist("nonexistent.z"); + q = new Parse.Query('Message'); + q.doesNotExist('nonexistent.z'); expect(matchesQuery(message, q)).toBe(true); - q = new Parse.Query("Message"); - q.equalTo("status.x", "read"); - q.doesNotExist("status.y"); + q = new Parse.Query('Message'); + q.equalTo('status.x', 'read'); + q.doesNotExist('status.y'); expect(matchesQuery(message, q)).toBe(false); }); function pointer(className, objectId) { - return { __type: "Pointer", className, objectId }; + return { __type: 'Pointer', className, objectId }; } - it("should support containedIn with pointers", () => { + it('should support containedIn with pointers', () => { const message = { - id: new Id("Message", "O1"), - profile: pointer("Profile", "abc"), + id: new Id('Message', 'O1'), + profile: pointer('Profile', 'abc'), }; - let q = new Parse.Query("Message"); - q.containedIn("profile", [ - Parse.Object.fromJSON({ className: "Profile", objectId: "abc" }), - Parse.Object.fromJSON({ className: "Profile", objectId: "def" }), + let q = new Parse.Query('Message'); + q.containedIn('profile', [ + Parse.Object.fromJSON({ className: 'Profile', objectId: 'abc' }), + Parse.Object.fromJSON({ className: 'Profile', objectId: 'def' }), ]); expect(matchesQuery(message, q)).toBe(true); - q = new Parse.Query("Message"); - q.containedIn("profile", [ - Parse.Object.fromJSON({ className: "Profile", objectId: "ghi" }), - Parse.Object.fromJSON({ className: "Profile", objectId: "def" }), + q = new Parse.Query('Message'); + q.containedIn('profile', [ + Parse.Object.fromJSON({ className: 'Profile', objectId: 'ghi' }), + Parse.Object.fromJSON({ className: 'Profile', objectId: 'def' }), ]); expect(matchesQuery(message, q)).toBe(false); }); - it("should support containedIn with array of pointers", () => { + it('should support containedIn with array of pointers', () => { const message = { - id: new Id("Message", "O2"), - profiles: [pointer("Profile", "yeahaw"), pointer("Profile", "yes")], + id: new Id('Message', 'O2'), + profiles: [pointer('Profile', 'yeahaw'), pointer('Profile', 'yes')], }; - let q = new Parse.Query("Message"); - q.containedIn("profiles", [ - Parse.Object.fromJSON({ className: "Profile", objectId: "no" }), - Parse.Object.fromJSON({ className: "Profile", objectId: "yes" }), + let q = new Parse.Query('Message'); + q.containedIn('profiles', [ + Parse.Object.fromJSON({ className: 'Profile', objectId: 'no' }), + Parse.Object.fromJSON({ className: 'Profile', objectId: 'yes' }), ]); expect(matchesQuery(message, q)).toBe(true); - q = new Parse.Query("Message"); - q.containedIn("profiles", [ - Parse.Object.fromJSON({ className: "Profile", objectId: "no" }), - Parse.Object.fromJSON({ className: "Profile", objectId: "nope" }), + q = new Parse.Query('Message'); + q.containedIn('profiles', [ + Parse.Object.fromJSON({ className: 'Profile', objectId: 'no' }), + Parse.Object.fromJSON({ className: 'Profile', objectId: 'nope' }), ]); expect(matchesQuery(message, q)).toBe(false); }); - it("should support notContainedIn with pointers", () => { + it('should support notContainedIn with pointers', () => { let message = { - id: new Id("Message", "O1"), - profile: pointer("Profile", "abc"), + id: new Id('Message', 'O1'), + profile: pointer('Profile', 'abc'), }; - let q = new Parse.Query("Message"); - q.notContainedIn("profile", [ - Parse.Object.fromJSON({ className: "Profile", objectId: "def" }), - Parse.Object.fromJSON({ className: "Profile", objectId: "ghi" }), + let q = new Parse.Query('Message'); + q.notContainedIn('profile', [ + Parse.Object.fromJSON({ className: 'Profile', objectId: 'def' }), + Parse.Object.fromJSON({ className: 'Profile', objectId: 'ghi' }), ]); expect(matchesQuery(message, q)).toBe(true); message = { - id: new Id("Message", "O1"), - profile: pointer("Profile", "def"), + id: new Id('Message', 'O1'), + profile: pointer('Profile', 'def'), }; - q = new Parse.Query("Message"); - q.notContainedIn("profile", [ - Parse.Object.fromJSON({ className: "Profile", objectId: "ghi" }), - Parse.Object.fromJSON({ className: "Profile", objectId: "def" }), + q = new Parse.Query('Message'); + q.notContainedIn('profile', [ + Parse.Object.fromJSON({ className: 'Profile', objectId: 'ghi' }), + Parse.Object.fromJSON({ className: 'Profile', objectId: 'def' }), ]); expect(matchesQuery(message, q)).toBe(false); }); - it("should support containedIn queries with [objectId]", () => { + it('should support containedIn queries with [objectId]', () => { let message = { - id: new Id("Message", "O1"), - profile: pointer("Profile", "abc"), + id: new Id('Message', 'O1'), + profile: pointer('Profile', 'abc'), }; - let q = new Parse.Query("Message"); - q.containedIn("profile", ["abc", "def"]); + let q = new Parse.Query('Message'); + q.containedIn('profile', ['abc', 'def']); expect(matchesQuery(message, q)).toBe(true); message = { - id: new Id("Message", "O1"), - profile: pointer("Profile", "ghi"), + id: new Id('Message', 'O1'), + profile: pointer('Profile', 'ghi'), }; - q = new Parse.Query("Message"); - q.containedIn("profile", ["abc", "def"]); + q = new Parse.Query('Message'); + q.containedIn('profile', ['abc', 'def']); expect(matchesQuery(message, q)).toBe(false); }); - it("should support notContainedIn queries with [objectId]", () => { + it('should support notContainedIn queries with [objectId]', () => { let message = { - id: new Id("Message", "O1"), - profile: pointer("Profile", "ghi"), + id: new Id('Message', 'O1'), + profile: pointer('Profile', 'ghi'), }; - let q = new Parse.Query("Message"); - q.notContainedIn("profile", ["abc", "def"]); + let q = new Parse.Query('Message'); + q.notContainedIn('profile', ['abc', 'def']); expect(matchesQuery(message, q)).toBe(true); message = { - id: new Id("Message", "O1"), - profile: pointer("Profile", "ghi"), + id: new Id('Message', 'O1'), + profile: pointer('Profile', 'ghi'), }; - q = new Parse.Query("Message"); - q.notContainedIn("profile", ["abc", "def", "ghi"]); + q = new Parse.Query('Message'); + q.notContainedIn('profile', ['abc', 'def', 'ghi']); expect(matchesQuery(message, q)).toBe(false); }); - it("matches on Date", () => { + it('matches on Date', () => { // given const now = new Date(); const obj = { - id: new Id("Person", "01"), + id: new Id('Person', '01'), dateObject: now, dateJSON: { - __type: "Date", + __type: 'Date', iso: now.toISOString(), }, }; // when, then: Equal - let q = new Parse.Query("Person"); - q.equalTo("dateObject", now); - q.equalTo("dateJSON", now); + let q = new Parse.Query('Person'); + q.equalTo('dateObject', now); + q.equalTo('dateJSON', now); expect(matchesQuery(Object.assign({}, obj), q)).toBe(true); // when, then: lessThan const future = Date(now.getTime() + 1000); - q = new Parse.Query("Person"); - q.lessThan("dateObject", future); - q.lessThan("dateJSON", future); + q = new Parse.Query('Person'); + q.lessThan('dateObject', future); + q.lessThan('dateJSON', future); expect(matchesQuery(Object.assign({}, obj), q)).toBe(true); // when, then: lessThanOrEqualTo - q = new Parse.Query("Person"); - q.lessThanOrEqualTo("dateObject", now); - q.lessThanOrEqualTo("dateJSON", now); + q = new Parse.Query('Person'); + q.lessThanOrEqualTo('dateObject', now); + q.lessThanOrEqualTo('dateJSON', now); expect(matchesQuery(Object.assign({}, obj), q)).toBe(true); // when, then: greaterThan const past = Date(now.getTime() - 1000); - q = new Parse.Query("Person"); - q.greaterThan("dateObject", past); - q.greaterThan("dateJSON", past); + q = new Parse.Query('Person'); + q.greaterThan('dateObject', past); + q.greaterThan('dateJSON', past); expect(matchesQuery(Object.assign({}, obj), q)).toBe(true); // when, then: greaterThanOrEqualTo - q = new Parse.Query("Person"); - q.greaterThanOrEqualTo("dateObject", now); - q.greaterThanOrEqualTo("dateJSON", now); + q = new Parse.Query('Person'); + q.greaterThanOrEqualTo('dateObject', now); + q.greaterThanOrEqualTo('dateJSON', now); expect(matchesQuery(Object.assign({}, obj), q)).toBe(true); }); - it("should support containedBy query", () => { + it('should support containedBy query', () => { const obj1 = { - id: new Id("Numbers", "N1"), + id: new Id('Numbers', 'N1'), numbers: [0, 1, 2], }; const obj2 = { - id: new Id("Numbers", "N2"), + id: new Id('Numbers', 'N2'), numbers: [2, 0], }; const obj3 = { - id: new Id("Numbers", "N3"), + id: new Id('Numbers', 'N3'), numbers: [1, 2, 3, 4], }; - const q = new Parse.Query("Numbers"); - q.containedBy("numbers", [1, 2, 3, 4, 5]); + const q = new Parse.Query('Numbers'); + q.containedBy('numbers', [1, 2, 3, 4, 5]); expect(matchesQuery(obj1, q)).toBe(false); expect(matchesQuery(obj2, q)).toBe(false); expect(matchesQuery(obj3, q)).toBe(true); }); - it("should support withinPolygon query", () => { + it('should support withinPolygon query', () => { const sacramento = { - id: new Id("Location", "L1"), + id: new Id('Location', 'L1'), location: new Parse.GeoPoint(38.52, -121.5), - name: "Sacramento", + name: 'Sacramento', }; const honolulu = { - id: new Id("Location", "L2"), + id: new Id('Location', 'L2'), location: new Parse.GeoPoint(21.35, -157.93), - name: "Honolulu", + name: 'Honolulu', }; const sf = { - id: new Id("Location", "L3"), + id: new Id('Location', 'L3'), location: new Parse.GeoPoint(37.75, -122.68), - name: "San Francisco", + name: 'San Francisco', }; const points = [ @@ -783,15 +783,15 @@ describe("matchesQuery", function () { new Parse.GeoPoint(37.68, -122.9), new Parse.GeoPoint(37.68, -122.33), ]; - const q = new Parse.Query("Location"); - q.withinPolygon("location", points); + const q = new Parse.Query('Location'); + q.withinPolygon('location', points); expect(matchesQuery(sacramento, q)).toBe(false); expect(matchesQuery(honolulu, q)).toBe(false); expect(matchesQuery(sf, q)).toBe(true); }); - it("should support polygonContains query", () => { + it('should support polygonContains query', () => { const p1 = [ [0, 0], [0, 1], @@ -813,21 +813,21 @@ describe("matchesQuery", function () { ]; const obj1 = { - id: new Id("Bounds", "B1"), + id: new Id('Bounds', 'B1'), polygon: new Parse.Polygon(p1), }; const obj2 = { - id: new Id("Bounds", "B2"), + id: new Id('Bounds', 'B2'), polygon: new Parse.Polygon(p2), }; const obj3 = { - id: new Id("Bounds", "B3"), + id: new Id('Bounds', 'B3'), polygon: new Parse.Polygon(p3), }; const point = new Parse.GeoPoint(0.5, 0.5); - const q = new Parse.Query("Bounds"); - q.polygonContains("polygon", point); + const q = new Parse.Query('Bounds'); + q.polygonContains('polygon', point); expect(matchesQuery(obj1, q)).toBe(true); expect(matchesQuery(obj2, q)).toBe(true); diff --git a/spec/RateLimit.spec.js b/spec/RateLimit.spec.js index 0ebf01e9d4..aa62b6655d 100644 --- a/spec/RateLimit.spec.js +++ b/spec/RateLimit.spec.js @@ -1,483 +1,483 @@ const RedisCacheAdapter = - require("../lib/Adapters/Cache/RedisCacheAdapter").default; -describe("rate limit", () => { - it("can limit cloud functions", async () => { - Parse.Cloud.define("test", () => "Abc"); + require('../lib/Adapters/Cache/RedisCacheAdapter').default; +describe('rate limit', () => { + it('can limit cloud functions', async () => { + Parse.Cloud.define('test', () => 'Abc'); await reconfigureServer({ rateLimit: [ { - requestPath: "/functions/*", + requestPath: '/functions/*', requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: "Too many requests", + errorResponseMessage: 'Too many requests', includeInternalRequests: true, }, ], }); - const response1 = await Parse.Cloud.run("test"); - expect(response1).toBe("Abc"); - await expectAsync(Parse.Cloud.run("test")).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") + const response1 = await Parse.Cloud.run('test'); + expect(response1).toBe('Abc'); + await expectAsync(Parse.Cloud.run('test')).toBeRejectedWith( + new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') ); }); - it("can limit cloud functions with user session token", async () => { - await Parse.User.signUp("myUser", "password"); - Parse.Cloud.define("test", () => "Abc"); + it('can limit cloud functions with user session token', async () => { + await Parse.User.signUp('myUser', 'password'); + Parse.Cloud.define('test', () => 'Abc'); await reconfigureServer({ rateLimit: [ { - requestPath: "/functions/*", + requestPath: '/functions/*', requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: "Too many requests", + errorResponseMessage: 'Too many requests', includeInternalRequests: true, }, ], }); - const response1 = await Parse.Cloud.run("test"); - expect(response1).toBe("Abc"); - await expectAsync(Parse.Cloud.run("test")).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") + const response1 = await Parse.Cloud.run('test'); + expect(response1).toBe('Abc'); + await expectAsync(Parse.Cloud.run('test')).toBeRejectedWith( + new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') ); }); - it("can add global limit", async () => { - Parse.Cloud.define("test", () => "Abc"); + it('can add global limit', async () => { + Parse.Cloud.define('test', () => 'Abc'); await reconfigureServer({ rateLimit: { - requestPath: "*", + requestPath: '*', requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: "Too many requests", + errorResponseMessage: 'Too many requests', includeInternalRequests: true, }, }); - const response1 = await Parse.Cloud.run("test"); - expect(response1).toBe("Abc"); - await expectAsync(Parse.Cloud.run("test")).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") + const response1 = await Parse.Cloud.run('test'); + expect(response1).toBe('Abc'); + await expectAsync(Parse.Cloud.run('test')).toBeRejectedWith( + new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') ); - await expectAsync(new Parse.Object("Test").save()).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") + await expectAsync(new Parse.Object('Test').save()).toBeRejectedWith( + new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') ); }); - it("can limit cloud with validator", async () => { - Parse.Cloud.define("test", () => "Abc", { + it('can limit cloud with validator', async () => { + Parse.Cloud.define('test', () => 'Abc', { rateLimit: { requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: "Too many requests", + errorResponseMessage: 'Too many requests', includeInternalRequests: true, }, }); - const response1 = await Parse.Cloud.run("test"); - expect(response1).toBe("Abc"); - await expectAsync(Parse.Cloud.run("test")).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") + const response1 = await Parse.Cloud.run('test'); + expect(response1).toBe('Abc'); + await expectAsync(Parse.Cloud.run('test')).toBeRejectedWith( + new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') ); }); - it("can skip with masterKey", async () => { - Parse.Cloud.define("test", () => "Abc"); + it('can skip with masterKey', async () => { + Parse.Cloud.define('test', () => 'Abc'); await reconfigureServer({ rateLimit: [ { - requestPath: "/functions/*", + requestPath: '/functions/*', requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: "Too many requests", + errorResponseMessage: 'Too many requests', includeInternalRequests: true, }, ], }); - const response1 = await Parse.Cloud.run("test", null, { + const response1 = await Parse.Cloud.run('test', null, { useMasterKey: true, }); - expect(response1).toBe("Abc"); - const response2 = await Parse.Cloud.run("test", null, { + expect(response1).toBe('Abc'); + const response2 = await Parse.Cloud.run('test', null, { useMasterKey: true, }); - expect(response2).toBe("Abc"); + expect(response2).toBe('Abc'); }); - it("should run with masterKey", async () => { - Parse.Cloud.define("test", () => "Abc"); + it('should run with masterKey', async () => { + Parse.Cloud.define('test', () => 'Abc'); await reconfigureServer({ rateLimit: [ { - requestPath: "/functions/*", + requestPath: '/functions/*', requestTimeWindow: 10000, requestCount: 1, includeMasterKey: true, - errorResponseMessage: "Too many requests", + errorResponseMessage: 'Too many requests', includeInternalRequests: true, }, ], }); - const response1 = await Parse.Cloud.run("test", null, { + const response1 = await Parse.Cloud.run('test', null, { useMasterKey: true, }); - expect(response1).toBe("Abc"); - await expectAsync(Parse.Cloud.run("test")).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") + expect(response1).toBe('Abc'); + await expectAsync(Parse.Cloud.run('test')).toBeRejectedWith( + new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') ); }); - it("can limit saving objects", async () => { + it('can limit saving objects', async () => { await reconfigureServer({ rateLimit: [ { - requestPath: "/classes/*", + requestPath: '/classes/*', requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: "Too many requests", + errorResponseMessage: 'Too many requests', includeInternalRequests: true, }, ], }); - const obj = new Parse.Object("Test"); + const obj = new Parse.Object('Test'); await obj.save(); await expectAsync(obj.save()).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") + new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') ); }); - it("can set method to post", async () => { + it('can set method to post', async () => { await reconfigureServer({ rateLimit: [ { - requestPath: "/classes/*", + requestPath: '/classes/*', requestTimeWindow: 10000, requestCount: 1, - requestMethods: "POST", - errorResponseMessage: "Too many requests", + requestMethods: 'POST', + errorResponseMessage: 'Too many requests', includeInternalRequests: true, }, ], }); - const obj = new Parse.Object("Test"); + const obj = new Parse.Object('Test'); await obj.save(); await obj.save(); - const obj2 = new Parse.Object("Test"); + const obj2 = new Parse.Object('Test'); await expectAsync(obj2.save()).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") + new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') ); }); - it("can use a validator for post", async () => { - Parse.Cloud.beforeSave("Test", () => {}, { + it('can use a validator for post', async () => { + Parse.Cloud.beforeSave('Test', () => {}, { rateLimit: { requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: "Too many requests", + errorResponseMessage: 'Too many requests', includeInternalRequests: true, }, }); - const obj = new Parse.Object("Test"); + const obj = new Parse.Object('Test'); await obj.save(); await expectAsync(obj.save()).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") + new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') ); }); - it("can use a validator for file", async () => { + it('can use a validator for file', async () => { Parse.Cloud.beforeSave(Parse.File, () => {}, { rateLimit: { requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: "Too many requests", + errorResponseMessage: 'Too many requests', includeInternalRequests: true, }, }); - const file = new Parse.File("yolo.txt", [1, 2, 3], "text/plain"); + const file = new Parse.File('yolo.txt', [1, 2, 3], 'text/plain'); await file.save(); - const file2 = new Parse.File("yolo.txt", [1, 2, 3], "text/plain"); + const file2 = new Parse.File('yolo.txt', [1, 2, 3], 'text/plain'); await expectAsync(file2.save()).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") + new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') ); }); - it("can set method to get", async () => { + it('can set method to get', async () => { await reconfigureServer({ rateLimit: [ { - requestPath: "/classes/Test", + requestPath: '/classes/Test', requestTimeWindow: 10000, requestCount: 1, - requestMethods: "GET", - errorResponseMessage: "Too many requests", + requestMethods: 'GET', + errorResponseMessage: 'Too many requests', includeInternalRequests: true, }, ], }); - const obj = new Parse.Object("Test"); + const obj = new Parse.Object('Test'); await obj.save(); await obj.save(); - await new Parse.Query("Test").first(); - await expectAsync(new Parse.Query("Test").first()).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") + await new Parse.Query('Test').first(); + await expectAsync(new Parse.Query('Test').first()).toBeRejectedWith( + new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') ); }); - it("can use a validator", async () => { + it('can use a validator', async () => { await reconfigureServer({ silent: false }); - Parse.Cloud.beforeFind("TestObject", () => {}, { + Parse.Cloud.beforeFind('TestObject', () => {}, { rateLimit: { requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: "Too many requests", + errorResponseMessage: 'Too many requests', includeInternalRequests: true, }, }); - const obj = new Parse.Object("TestObject"); + const obj = new Parse.Object('TestObject'); await obj.save(); await obj.save(); - await new Parse.Query("TestObject").first(); - await expectAsync(new Parse.Query("TestObject").first()).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") + await new Parse.Query('TestObject').first(); + await expectAsync(new Parse.Query('TestObject').first()).toBeRejectedWith( + new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') ); await expectAsync( - new Parse.Query("TestObject").get("abc") + new Parse.Query('TestObject').get('abc') ).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") + new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') ); }); - it("can set method to delete", async () => { + it('can set method to delete', async () => { await reconfigureServer({ rateLimit: [ { - requestPath: "/classes/Test/*", + requestPath: '/classes/Test/*', requestTimeWindow: 10000, requestCount: 1, - requestMethods: "DELETE", - errorResponseMessage: "Too many requests", + requestMethods: 'DELETE', + errorResponseMessage: 'Too many requests', includeInternalRequests: true, }, ], }); - const obj = new Parse.Object("Test"); + const obj = new Parse.Object('Test'); await obj.save(); await obj.destroy(); await expectAsync(obj.destroy()).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") + new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') ); }); - it("can set beforeDelete", async () => { - const obj = new Parse.Object("TestDelete"); + it('can set beforeDelete', async () => { + const obj = new Parse.Object('TestDelete'); await obj.save(); - Parse.Cloud.beforeDelete("TestDelete", () => {}, { + Parse.Cloud.beforeDelete('TestDelete', () => {}, { rateLimit: { requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: "Too many requests", + errorResponseMessage: 'Too many requests', includeInternalRequests: true, }, }); await obj.destroy(); await expectAsync(obj.destroy()).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") + new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') ); }); - it("can set beforeLogin", async () => { + it('can set beforeLogin', async () => { Parse.Cloud.beforeLogin(() => {}, { rateLimit: { requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: "Too many requests", + errorResponseMessage: 'Too many requests', includeInternalRequests: true, }, }); - await Parse.User.signUp("myUser", "password"); - await Parse.User.logIn("myUser", "password"); - await expectAsync(Parse.User.logIn("myUser", "password")).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") + await Parse.User.signUp('myUser', 'password'); + await Parse.User.logIn('myUser', 'password'); + await expectAsync(Parse.User.logIn('myUser', 'password')).toBeRejectedWith( + new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') ); }); - it("can define limits via rateLimit and define", async () => { + it('can define limits via rateLimit and define', async () => { await reconfigureServer({ rateLimit: [ { - requestPath: "/functions/*", + requestPath: '/functions/*', requestTimeWindow: 10000, requestCount: 100, - errorResponseMessage: "Too many requests", + errorResponseMessage: 'Too many requests', includeInternalRequests: true, }, ], }); - Parse.Cloud.define("test", () => "Abc", { + Parse.Cloud.define('test', () => 'Abc', { rateLimit: { requestTimeWindow: 10000, requestCount: 1, includeInternalRequests: true, }, }); - const response1 = await Parse.Cloud.run("test"); - expect(response1).toBe("Abc"); - await expectAsync(Parse.Cloud.run("test")).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests.") + const response1 = await Parse.Cloud.run('test'); + expect(response1).toBe('Abc'); + await expectAsync(Parse.Cloud.run('test')).toBeRejectedWith( + new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests.') ); }); - it("does not limit internal calls", async () => { + it('does not limit internal calls', async () => { await reconfigureServer({ rateLimit: [ { - requestPath: "/functions/*", + requestPath: '/functions/*', requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: "Too many requests", + errorResponseMessage: 'Too many requests', }, ], }); - Parse.Cloud.define("test1", () => "Abc"); - Parse.Cloud.define("test2", async () => { - await Parse.Cloud.run("test1"); - await Parse.Cloud.run("test1"); + Parse.Cloud.define('test1', () => 'Abc'); + Parse.Cloud.define('test2', async () => { + await Parse.Cloud.run('test1'); + await Parse.Cloud.run('test1'); }); - await Parse.Cloud.run("test2"); + await Parse.Cloud.run('test2'); }); - describe("zone", () => { - const middlewares = require("../lib/middlewares"); - it("can use global zone", async () => { + describe('zone', () => { + const middlewares = require('../lib/middlewares'); + it('can use global zone', async () => { await reconfigureServer({ rateLimit: { - requestPath: "*", + requestPath: '*', requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: "Too many requests", + errorResponseMessage: 'Too many requests', includeInternalRequests: true, zone: Parse.Server.RateLimitZone.global, }, }); const fakeReq = { - originalUrl: "http://example.com/parse/", - url: "http://example.com/", + originalUrl: 'http://example.com/parse/', + url: 'http://example.com/', body: { - _ApplicationId: "test", + _ApplicationId: 'test', }, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }, get: key => { return fakeReq.headers[key]; }, }; - fakeReq.ip = "127.0.0.1"; - let fakeRes = jasmine.createSpyObj("fakeRes", [ - "end", - "status", - "setHeader", - "json", + fakeReq.ip = '127.0.0.1'; + let fakeRes = jasmine.createSpyObj('fakeRes', [ + 'end', + 'status', + 'setHeader', + 'json', ]); await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve) ); - fakeReq.ip = "127.0.0.2"; - fakeRes = jasmine.createSpyObj("fakeRes", ["end", "status", "setHeader"]); + fakeReq.ip = '127.0.0.2'; + fakeRes = jasmine.createSpyObj('fakeRes', ['end', 'status', 'setHeader']); let resolvingPromise; const promise = new Promise(resolve => { resolvingPromise = resolve; }); - fakeRes.json = jasmine.createSpy("json").and.callFake(resolvingPromise); + fakeRes.json = jasmine.createSpy('json').and.callFake(resolvingPromise); middlewares.handleParseHeaders(fakeReq, fakeRes, () => { - throw "Should not call next"; + throw 'Should not call next'; }); await promise; expect(fakeRes.status).toHaveBeenCalledWith(429); expect(fakeRes.json).toHaveBeenCalledWith({ code: Parse.Error.CONNECTION_FAILED, - error: "Too many requests", + error: 'Too many requests', }); }); - it("can use session zone", async () => { + it('can use session zone', async () => { await reconfigureServer({ rateLimit: { - requestPath: "/functions/*", + requestPath: '/functions/*', requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: "Too many requests", + errorResponseMessage: 'Too many requests', includeInternalRequests: true, zone: Parse.Server.RateLimitZone.session, }, }); - Parse.Cloud.define("test", () => "Abc"); - await Parse.User.signUp("username", "password"); - await Parse.Cloud.run("test"); - await expectAsync(Parse.Cloud.run("test")).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") + Parse.Cloud.define('test', () => 'Abc'); + await Parse.User.signUp('username', 'password'); + await Parse.Cloud.run('test'); + await expectAsync(Parse.Cloud.run('test')).toBeRejectedWith( + new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') ); - await Parse.User.logIn("username", "password"); - await Parse.Cloud.run("test"); + await Parse.User.logIn('username', 'password'); + await Parse.Cloud.run('test'); }); - it("can use user zone", async () => { + it('can use user zone', async () => { await reconfigureServer({ rateLimit: { - requestPath: "/functions/*", + requestPath: '/functions/*', requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: "Too many requests", + errorResponseMessage: 'Too many requests', includeInternalRequests: true, zone: Parse.Server.RateLimitZone.user, }, }); - Parse.Cloud.define("test", () => "Abc"); - await Parse.User.signUp("username", "password"); - await Parse.Cloud.run("test"); - await expectAsync(Parse.Cloud.run("test")).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") + Parse.Cloud.define('test', () => 'Abc'); + await Parse.User.signUp('username', 'password'); + await Parse.Cloud.run('test'); + await expectAsync(Parse.Cloud.run('test')).toBeRejectedWith( + new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') ); - await Parse.User.logIn("username", "password"); - await expectAsync(Parse.Cloud.run("test")).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") + await Parse.User.logIn('username', 'password'); + await expectAsync(Parse.Cloud.run('test')).toBeRejectedWith( + new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') ); }); }); - it("can validate rateLimit", async () => { - const Config = require("../lib/Config"); + it('can validate rateLimit', async () => { + const Config = require('../lib/Config'); const validateRateLimit = ({ rateLimit }) => Config.validateRateLimit(rateLimit); expect(() => validateRateLimit({ - rateLimit: "a", + rateLimit: 'a', requestTimeWindow: 1000, requestCount: 3, }) - ).toThrow("rateLimit must be an array or object"); - expect(() => validateRateLimit({ rateLimit: ["a"] })).toThrow( - "rateLimit must be an array of objects" + ).toThrow('rateLimit must be an array or object'); + expect(() => validateRateLimit({ rateLimit: ['a'] })).toThrow( + 'rateLimit must be an array of objects' ); expect(() => validateRateLimit({ rateLimit: [{ requestPath: [] }] }) - ).toThrow("rateLimit.requestPath must be a string"); + ).toThrow('rateLimit.requestPath must be a string'); expect(() => validateRateLimit({ - rateLimit: [{ requestTimeWindow: [], requestPath: "a" }], + rateLimit: [{ requestTimeWindow: [], requestPath: 'a' }], }) - ).toThrow("rateLimit.requestTimeWindow must be a number"); + ).toThrow('rateLimit.requestTimeWindow must be a number'); expect(() => validateRateLimit({ rateLimit: [ { - requestPath: "a", + requestPath: 'a', requestTimeWindow: 1000, requestCount: 3, - zone: "abc", + zone: 'abc', }, ], }) - ).toThrow("rateLimit.zone must be one of global, session, user, or ip"); + ).toThrow('rateLimit.zone must be one of global, session, user, or ip'); expect(() => validateRateLimit({ rateLimit: [ @@ -485,18 +485,18 @@ describe("rate limit", () => { includeInternalRequests: [], requestTimeWindow: 1000, requestCount: 3, - requestPath: "a", + requestPath: 'a', }, ], }) - ).toThrow("rateLimit.includeInternalRequests must be a boolean"); + ).toThrow('rateLimit.includeInternalRequests must be a boolean'); expect(() => validateRateLimit({ rateLimit: [ - { requestCount: [], requestTimeWindow: 1000, requestPath: "a" }, + { requestCount: [], requestTimeWindow: 1000, requestPath: 'a' }, ], }) - ).toThrow("rateLimit.requestCount must be a number"); + ).toThrow('rateLimit.requestCount must be a number'); expect(() => validateRateLimit({ rateLimit: [ @@ -504,65 +504,65 @@ describe("rate limit", () => { errorResponseMessage: [], requestTimeWindow: 1000, requestCount: 3, - requestPath: "a", + requestPath: 'a', }, ], }) - ).toThrow("rateLimit.errorResponseMessage must be a string"); + ).toThrow('rateLimit.errorResponseMessage must be a string'); expect(() => validateRateLimit({ - rateLimit: [{ requestCount: 3, requestPath: "abc" }], + rateLimit: [{ requestCount: 3, requestPath: 'abc' }], }) - ).toThrow("rateLimit.requestTimeWindow must be defined"); + ).toThrow('rateLimit.requestTimeWindow must be defined'); expect(() => validateRateLimit({ - rateLimit: [{ requestTimeWindow: 3, requestPath: "abc" }], + rateLimit: [{ requestTimeWindow: 3, requestPath: 'abc' }], }) - ).toThrow("rateLimit.requestCount must be defined"); + ).toThrow('rateLimit.requestCount must be defined'); expect(() => validateRateLimit({ - rateLimit: [{ requestTimeWindow: 3, requestCount: "abc" }], + rateLimit: [{ requestTimeWindow: 3, requestCount: 'abc' }], }) - ).toThrow("rateLimit.requestPath must be defined"); + ).toThrow('rateLimit.requestPath must be defined'); await expectAsync( reconfigureServer({ rateLimit: [ { requestTimeWindow: 3, requestCount: 1, - path: "abc", - requestPath: "a", + path: 'abc', + requestPath: 'a', }, ], }) ).toBeRejectedWith(`Invalid rate limit option "path"`); }); describe_only(() => { - return process.env.PARSE_SERVER_TEST_CACHE === "redis"; - })("with RedisCache", function () { - it("does work with cache", async () => { + return process.env.PARSE_SERVER_TEST_CACHE === 'redis'; + })('with RedisCache', function () { + it('does work with cache', async () => { await reconfigureServer({ rateLimit: [ { - requestPath: "/classes/*", + requestPath: '/classes/*', requestTimeWindow: 10000, requestCount: 1, - errorResponseMessage: "Too many requests", + errorResponseMessage: 'Too many requests', includeInternalRequests: true, - redisUrl: "redis://localhost:6379", + redisUrl: 'redis://localhost:6379', }, ], }); - const obj = new Parse.Object("Test"); + const obj = new Parse.Object('Test'); await obj.save(); await expectAsync(obj.save()).toBeRejectedWith( - new Parse.Error(Parse.Error.CONNECTION_FAILED, "Too many requests") + new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') ); const cache = new RedisCacheAdapter(); await cache.connect(); - const value = await cache.get("rl:127.0.0.1"); + const value = await cache.get('rl:127.0.0.1'); expect(value).toEqual(2); - const ttl = await cache.client.ttl("rl:127.0.0.1"); + const ttl = await cache.client.ttl('rl:127.0.0.1'); expect(ttl).toEqual(10); }); }); diff --git a/spec/ReadPreferenceOption.spec.js b/spec/ReadPreferenceOption.spec.js index f4eae62596..7cc662b771 100644 --- a/spec/ReadPreferenceOption.spec.js +++ b/spec/ReadPreferenceOption.spec.js @@ -1,8 +1,8 @@ -"use strict"; +'use strict'; -const Parse = require("parse/node"); -const { ReadPreference, Collection } = require("mongodb"); -const request = require("../lib/request"); +const Parse = require('parse/node'); +const { ReadPreference, Collection } = require('mongodb'); +const request = require('../lib/request'); function waitForReplication() { return new Promise(function (resolve) { @@ -10,26 +10,26 @@ function waitForReplication() { }); } -describe_only_db("mongo")("Read preference option", () => { - it("should find in primary by default", done => { - const obj0 = new Parse.Object("MyObject"); - obj0.set("boolKey", false); - const obj1 = new Parse.Object("MyObject"); - obj1.set("boolKey", true); +describe_only_db('mongo')('Read preference option', () => { + it('should find in primary by default', done => { + const obj0 = new Parse.Object('MyObject'); + obj0.set('boolKey', false); + const obj1 = new Parse.Object('MyObject'); + obj1.set('boolKey', true); Parse.Object.saveAll([obj0, obj1]) .then(() => { - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); - const query = new Parse.Query("MyObject"); - query.equalTo("boolKey", false); + const query = new Parse.Query('MyObject'); + query.equalTo('boolKey', false); return query.find().then(results => { expect(results.length).toBe(1); - expect(results[0].get("boolKey")).toBe(false); + expect(results[0].get('boolKey')).toBe(false); let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { myObjectReadPreference = true; expect(call.object.s.readPreference.mode).toBe( ReadPreference.PRIMARY @@ -45,12 +45,12 @@ describe_only_db("mongo")("Read preference option", () => { .catch(done.fail); }); - xit("should preserve the read preference set (#4831)", async () => { + xit('should preserve the read preference set (#4831)', async () => { const { MongoStorageAdapter, - } = require("../lib/Adapters/Storage/Mongo/MongoStorageAdapter"); + } = require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter'); const adapterOptions = { - uri: "mongodb://localhost:27017/parseServerMongoAdapterTestDatabase", + uri: 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase', mongoOptions: { readPreference: ReadPreference.NEAREST, }, @@ -59,24 +59,24 @@ describe_only_db("mongo")("Read preference option", () => { databaseAdapter: new MongoStorageAdapter(adapterOptions), }); - const obj0 = new Parse.Object("MyObject"); - obj0.set("boolKey", false); - const obj1 = new Parse.Object("MyObject"); - obj1.set("boolKey", true); + const obj0 = new Parse.Object('MyObject'); + obj0.set('boolKey', false); + const obj1 = new Parse.Object('MyObject'); + obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); - const query = new Parse.Query("MyObject"); - query.equalTo("boolKey", false); + const query = new Parse.Query('MyObject'); + query.equalTo('boolKey', false); const results = await query.find(); expect(results.length).toBe(1); - expect(results[0].get("boolKey")).toBe(false); + expect(results[0].get('boolKey')).toBe(false); let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { myObjectReadPreference = true; expect(call.args[1].readPreference).toBe(ReadPreference.NEAREST); } @@ -85,30 +85,30 @@ describe_only_db("mongo")("Read preference option", () => { expect(myObjectReadPreference).toBe(true); }); - it("should change read preference in the beforeFind trigger", async () => { - const obj0 = new Parse.Object("MyObject"); - obj0.set("boolKey", false); - const obj1 = new Parse.Object("MyObject"); - obj1.set("boolKey", true); + it('should change read preference in the beforeFind trigger', async () => { + const obj0 = new Parse.Object('MyObject'); + obj0.set('boolKey', false); + const obj1 = new Parse.Object('MyObject'); + obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); - Parse.Cloud.beforeFind("MyObject", req => { - req.readPreference = "SECONDARY"; + Parse.Cloud.beforeFind('MyObject', req => { + req.readPreference = 'SECONDARY'; }); await waitForReplication(); - const query = new Parse.Query("MyObject"); - query.equalTo("boolKey", false); + const query = new Parse.Query('MyObject'); + query.equalTo('boolKey', false); const results = await query.find(); expect(results.length).toBe(1); - expect(results[0].get("boolKey")).toBe(false); + expect(results[0].get('boolKey')).toBe(false); let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -116,31 +116,31 @@ describe_only_db("mongo")("Read preference option", () => { expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY); }); - it("should check read preference as case insensitive", async () => { - const obj0 = new Parse.Object("MyObject"); - obj0.set("boolKey", false); - const obj1 = new Parse.Object("MyObject"); - obj1.set("boolKey", true); + it('should check read preference as case insensitive', async () => { + const obj0 = new Parse.Object('MyObject'); + obj0.set('boolKey', false); + const obj1 = new Parse.Object('MyObject'); + obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); - Parse.Cloud.beforeFind("MyObject", req => { - req.readPreference = "sEcOnDarY"; + Parse.Cloud.beforeFind('MyObject', req => { + req.readPreference = 'sEcOnDarY'; }); await waitForReplication(); - const query = new Parse.Query("MyObject"); - query.equalTo("boolKey", false); + const query = new Parse.Query('MyObject'); + query.equalTo('boolKey', false); const results = await query.find(); expect(results.length).toBe(1); - expect(results[0].get("boolKey")).toBe(false); + expect(results[0].get('boolKey')).toBe(false); let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -148,31 +148,31 @@ describe_only_db("mongo")("Read preference option", () => { expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY); }); - it("should change read preference in the beforeFind trigger even changing query", async () => { - const obj0 = new Parse.Object("MyObject"); - obj0.set("boolKey", false); - const obj1 = new Parse.Object("MyObject"); - obj1.set("boolKey", true); + it('should change read preference in the beforeFind trigger even changing query', async () => { + const obj0 = new Parse.Object('MyObject'); + obj0.set('boolKey', false); + const obj1 = new Parse.Object('MyObject'); + obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); - Parse.Cloud.beforeFind("MyObject", req => { - req.query.equalTo("boolKey", true); - req.readPreference = "SECONDARY"; + Parse.Cloud.beforeFind('MyObject', req => { + req.query.equalTo('boolKey', true); + req.readPreference = 'SECONDARY'; }); await waitForReplication(); - const query = new Parse.Query("MyObject"); - query.equalTo("boolKey", false); + const query = new Parse.Query('MyObject'); + query.equalTo('boolKey', false); const results = await query.find(); expect(results.length).toBe(1); - expect(results[0].get("boolKey")).toBe(true); + expect(results[0].get('boolKey')).toBe(true); let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -180,35 +180,35 @@ describe_only_db("mongo")("Read preference option", () => { expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY); }); - it("should change read preference in the beforeFind trigger even returning query", async () => { - const obj0 = new Parse.Object("MyObject"); - obj0.set("boolKey", false); - const obj1 = new Parse.Object("MyObject"); - obj1.set("boolKey", true); + it('should change read preference in the beforeFind trigger even returning query', async () => { + const obj0 = new Parse.Object('MyObject'); + obj0.set('boolKey', false); + const obj1 = new Parse.Object('MyObject'); + obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); - Parse.Cloud.beforeFind("MyObject", req => { - req.readPreference = "SECONDARY"; + Parse.Cloud.beforeFind('MyObject', req => { + req.readPreference = 'SECONDARY'; - const otherQuery = new Parse.Query("MyObject"); - otherQuery.equalTo("boolKey", true); + const otherQuery = new Parse.Query('MyObject'); + otherQuery.equalTo('boolKey', true); return otherQuery; }); await waitForReplication(); - const query = new Parse.Query("MyObject"); - query.equalTo("boolKey", false); + const query = new Parse.Query('MyObject'); + query.equalTo('boolKey', false); const results = await query.find(); expect(results.length).toBe(1); - expect(results[0].get("boolKey")).toBe(true); + expect(results[0].get('boolKey')).toBe(true); let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -216,34 +216,34 @@ describe_only_db("mongo")("Read preference option", () => { expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY); }); - it("should change read preference in the beforeFind trigger even returning promise", async () => { - const obj0 = new Parse.Object("MyObject"); - obj0.set("boolKey", false); - const obj1 = new Parse.Object("MyObject"); - obj1.set("boolKey", true); + it('should change read preference in the beforeFind trigger even returning promise', async () => { + const obj0 = new Parse.Object('MyObject'); + obj0.set('boolKey', false); + const obj1 = new Parse.Object('MyObject'); + obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); - Parse.Cloud.beforeFind("MyObject", req => { - req.readPreference = "SECONDARY"; + Parse.Cloud.beforeFind('MyObject', req => { + req.readPreference = 'SECONDARY'; - const otherQuery = new Parse.Query("MyObject"); - otherQuery.equalTo("boolKey", true); + const otherQuery = new Parse.Query('MyObject'); + otherQuery.equalTo('boolKey', true); return Promise.resolve(otherQuery); }); await waitForReplication(); - const query = new Parse.Query("MyObject"); - query.equalTo("boolKey", false); + const query = new Parse.Query('MyObject'); + query.equalTo('boolKey', false); const results = await query.find(); expect(results.length).toBe(1); - expect(results[0].get("boolKey")).toBe(true); + expect(results[0].get('boolKey')).toBe(true); let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -251,30 +251,30 @@ describe_only_db("mongo")("Read preference option", () => { expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY); }); - it("should change read preference to PRIMARY_PREFERRED", async () => { - const obj0 = new Parse.Object("MyObject"); - obj0.set("boolKey", false); - const obj1 = new Parse.Object("MyObject"); - obj1.set("boolKey", true); + it('should change read preference to PRIMARY_PREFERRED', async () => { + const obj0 = new Parse.Object('MyObject'); + obj0.set('boolKey', false); + const obj1 = new Parse.Object('MyObject'); + obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); - Parse.Cloud.beforeFind("MyObject", req => { - req.readPreference = "PRIMARY_PREFERRED"; + Parse.Cloud.beforeFind('MyObject', req => { + req.readPreference = 'PRIMARY_PREFERRED'; }); await waitForReplication(); - const query = new Parse.Query("MyObject"); - query.equalTo("boolKey", false); + const query = new Parse.Query('MyObject'); + query.equalTo('boolKey', false); const results = await query.find(); expect(results.length).toBe(1); - expect(results[0].get("boolKey")).toBe(false); + expect(results[0].get('boolKey')).toBe(false); let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -282,30 +282,30 @@ describe_only_db("mongo")("Read preference option", () => { expect(myObjectReadPreference).toEqual(ReadPreference.PRIMARY_PREFERRED); }); - it("should change read preference to SECONDARY_PREFERRED", async () => { - const obj0 = new Parse.Object("MyObject"); - obj0.set("boolKey", false); - const obj1 = new Parse.Object("MyObject"); - obj1.set("boolKey", true); + it('should change read preference to SECONDARY_PREFERRED', async () => { + const obj0 = new Parse.Object('MyObject'); + obj0.set('boolKey', false); + const obj1 = new Parse.Object('MyObject'); + obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); - Parse.Cloud.beforeFind("MyObject", req => { - req.readPreference = "SECONDARY_PREFERRED"; + Parse.Cloud.beforeFind('MyObject', req => { + req.readPreference = 'SECONDARY_PREFERRED'; }); await waitForReplication(); - const query = new Parse.Query("MyObject"); - query.equalTo("boolKey", false); + const query = new Parse.Query('MyObject'); + query.equalTo('boolKey', false); const results = await query.find(); expect(results.length).toBe(1); - expect(results[0].get("boolKey")).toBe(false); + expect(results[0].get('boolKey')).toBe(false); let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -313,30 +313,30 @@ describe_only_db("mongo")("Read preference option", () => { expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY_PREFERRED); }); - it("should change read preference to NEAREST", async () => { - const obj0 = new Parse.Object("MyObject"); - obj0.set("boolKey", false); - const obj1 = new Parse.Object("MyObject"); - obj1.set("boolKey", true); + it('should change read preference to NEAREST', async () => { + const obj0 = new Parse.Object('MyObject'); + obj0.set('boolKey', false); + const obj1 = new Parse.Object('MyObject'); + obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); - Parse.Cloud.beforeFind("MyObject", req => { - req.readPreference = "NEAREST"; + Parse.Cloud.beforeFind('MyObject', req => { + req.readPreference = 'NEAREST'; }); await waitForReplication(); - const query = new Parse.Query("MyObject"); - query.equalTo("boolKey", false); + const query = new Parse.Query('MyObject'); + query.equalTo('boolKey', false); const results = await query.find(); expect(results.length).toBe(1); - expect(results[0].get("boolKey")).toBe(false); + expect(results[0].get('boolKey')).toBe(false); let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -344,28 +344,28 @@ describe_only_db("mongo")("Read preference option", () => { expect(myObjectReadPreference).toEqual(ReadPreference.NEAREST); }); - it("should change read preference for GET", async () => { - const obj0 = new Parse.Object("MyObject"); - obj0.set("boolKey", false); - const obj1 = new Parse.Object("MyObject"); - obj1.set("boolKey", true); + it('should change read preference for GET', async () => { + const obj0 = new Parse.Object('MyObject'); + obj0.set('boolKey', false); + const obj1 = new Parse.Object('MyObject'); + obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); - Parse.Cloud.beforeFind("MyObject", req => { - req.readPreference = "SECONDARY"; + Parse.Cloud.beforeFind('MyObject', req => { + req.readPreference = 'SECONDARY'; }); await waitForReplication(); - const query = new Parse.Query("MyObject"); + const query = new Parse.Query('MyObject'); const result = await query.get(obj0.id); - expect(result.get("boolKey")).toBe(false); + expect(result.get('boolKey')).toBe(false); let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -373,26 +373,26 @@ describe_only_db("mongo")("Read preference option", () => { expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY); }); - it("should change read preference for GET using API", async () => { - const obj0 = new Parse.Object("MyObject"); - obj0.set("boolKey", false); - const obj1 = new Parse.Object("MyObject"); - obj1.set("boolKey", true); + it('should change read preference for GET using API', async () => { + const obj0 = new Parse.Object('MyObject'); + obj0.set('boolKey', false); + const obj1 = new Parse.Object('MyObject'); + obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); - Parse.Cloud.beforeFind("MyObject", req => { - req.readPreference = "SECONDARY"; + Parse.Cloud.beforeFind('MyObject', req => { + req.readPreference = 'SECONDARY'; }); await waitForReplication(); const response = await request({ - method: "GET", - url: "http://localhost:8378/1/classes/MyObject/" + obj0.id, + method: 'GET', + url: 'http://localhost:8378/1/classes/MyObject/' + obj0.id, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }, json: true, }); @@ -401,7 +401,7 @@ describe_only_db("mongo")("Read preference option", () => { let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -409,25 +409,25 @@ describe_only_db("mongo")("Read preference option", () => { expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY); }); - it("should change read preference for GET directly from API", async () => { - const obj0 = new Parse.Object("MyObject"); - obj0.set("boolKey", false); - const obj1 = new Parse.Object("MyObject"); - obj1.set("boolKey", true); + it('should change read preference for GET directly from API', async () => { + const obj0 = new Parse.Object('MyObject'); + obj0.set('boolKey', false); + const obj1 = new Parse.Object('MyObject'); + obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); await waitForReplication(); const response = await request({ - method: "GET", + method: 'GET', url: - "http://localhost:8378/1/classes/MyObject/" + + 'http://localhost:8378/1/classes/MyObject/' + obj0.id + - "?readPreference=SECONDARY", + '?readPreference=SECONDARY', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }, json: true, }); @@ -435,7 +435,7 @@ describe_only_db("mongo")("Read preference option", () => { let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -443,29 +443,29 @@ describe_only_db("mongo")("Read preference option", () => { expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY); }); - it("should change read preference for GET using API through the beforeFind overriding API option", async () => { - const obj0 = new Parse.Object("MyObject"); - obj0.set("boolKey", false); - const obj1 = new Parse.Object("MyObject"); - obj1.set("boolKey", true); + it('should change read preference for GET using API through the beforeFind overriding API option', async () => { + const obj0 = new Parse.Object('MyObject'); + obj0.set('boolKey', false); + const obj1 = new Parse.Object('MyObject'); + obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); - Parse.Cloud.beforeFind("MyObject", req => { - req.readPreference = "SECONDARY_PREFERRED"; + Parse.Cloud.beforeFind('MyObject', req => { + req.readPreference = 'SECONDARY_PREFERRED'; }); await waitForReplication(); const response = await request({ - method: "GET", + method: 'GET', url: - "http://localhost:8378/1/classes/MyObject/" + + 'http://localhost:8378/1/classes/MyObject/' + obj0.id + - "?readPreference=SECONDARY", + '?readPreference=SECONDARY', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }, json: true, }); @@ -473,7 +473,7 @@ describe_only_db("mongo")("Read preference option", () => { let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -481,26 +481,26 @@ describe_only_db("mongo")("Read preference option", () => { expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY_PREFERRED); }); - it("should change read preference for FIND using API through beforeFind trigger", async () => { - const obj0 = new Parse.Object("MyObject"); - obj0.set("boolKey", false); - const obj1 = new Parse.Object("MyObject"); - obj1.set("boolKey", true); + it('should change read preference for FIND using API through beforeFind trigger', async () => { + const obj0 = new Parse.Object('MyObject'); + obj0.set('boolKey', false); + const obj1 = new Parse.Object('MyObject'); + obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); - Parse.Cloud.beforeFind("MyObject", req => { - req.readPreference = "SECONDARY"; + Parse.Cloud.beforeFind('MyObject', req => { + req.readPreference = 'SECONDARY'; }); await waitForReplication(); const response = await request({ - method: "GET", - url: "http://localhost:8378/1/classes/MyObject/", + method: 'GET', + url: 'http://localhost:8378/1/classes/MyObject/', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }, json: true, }); @@ -508,7 +508,7 @@ describe_only_db("mongo")("Read preference option", () => { let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -516,22 +516,22 @@ describe_only_db("mongo")("Read preference option", () => { expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY); }); - it("should change read preference for FIND directly from API", async () => { - const obj0 = new Parse.Object("MyObject"); - obj0.set("boolKey", false); - const obj1 = new Parse.Object("MyObject"); - obj1.set("boolKey", true); + it('should change read preference for FIND directly from API', async () => { + const obj0 = new Parse.Object('MyObject'); + obj0.set('boolKey', false); + const obj1 = new Parse.Object('MyObject'); + obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); await waitForReplication(); const response = await request({ - method: "GET", - url: "http://localhost:8378/1/classes/MyObject?readPreference=SECONDARY", + method: 'GET', + url: 'http://localhost:8378/1/classes/MyObject?readPreference=SECONDARY', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }, json: true, }); @@ -539,7 +539,7 @@ describe_only_db("mongo")("Read preference option", () => { let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -547,26 +547,26 @@ describe_only_db("mongo")("Read preference option", () => { expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY); }); - it("should change read preference for FIND using API through the beforeFind overriding API option", async () => { - const obj0 = new Parse.Object("MyObject"); - obj0.set("boolKey", false); - const obj1 = new Parse.Object("MyObject"); - obj1.set("boolKey", true); + it('should change read preference for FIND using API through the beforeFind overriding API option', async () => { + const obj0 = new Parse.Object('MyObject'); + obj0.set('boolKey', false); + const obj1 = new Parse.Object('MyObject'); + obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); - Parse.Cloud.beforeFind("MyObject", req => { - req.readPreference = "SECONDARY_PREFERRED"; + Parse.Cloud.beforeFind('MyObject', req => { + req.readPreference = 'SECONDARY_PREFERRED'; }); await waitForReplication(); const response = await request({ - method: "GET", - url: "http://localhost:8378/1/classes/MyObject/?readPreference=SECONDARY", + method: 'GET', + url: 'http://localhost:8378/1/classes/MyObject/?readPreference=SECONDARY', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }, json: true, }); @@ -574,7 +574,7 @@ describe_only_db("mongo")("Read preference option", () => { let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -582,21 +582,21 @@ describe_only_db("mongo")("Read preference option", () => { expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY_PREFERRED); }); - xit("should change read preference for count", done => { - const obj0 = new Parse.Object("MyObject"); - obj0.set("boolKey", false); - const obj1 = new Parse.Object("MyObject"); - obj1.set("boolKey", true); + xit('should change read preference for count', done => { + const obj0 = new Parse.Object('MyObject'); + obj0.set('boolKey', false); + const obj1 = new Parse.Object('MyObject'); + obj1.set('boolKey', true); Parse.Object.saveAll([obj0, obj1]).then(() => { - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); - Parse.Cloud.beforeFind("MyObject", req => { - req.readPreference = "SECONDARY"; + Parse.Cloud.beforeFind('MyObject', req => { + req.readPreference = 'SECONDARY'; }); - const query = new Parse.Query("MyObject"); - query.equalTo("boolKey", false); + const query = new Parse.Query('MyObject'); + query.equalTo('boolKey', false); query .count() @@ -605,7 +605,7 @@ describe_only_db("mongo")("Read preference option", () => { let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); @@ -618,117 +618,117 @@ describe_only_db("mongo")("Read preference option", () => { }); }); - it("should change read preference for `aggregate` using `beforeFind`", async () => { + it('should change read preference for `aggregate` using `beforeFind`', async () => { // Save objects - const obj0 = new Parse.Object("MyObject"); - obj0.set("boolKey", false); - const obj1 = new Parse.Object("MyObject"); - obj1.set("boolKey", true); + const obj0 = new Parse.Object('MyObject'); + obj0.set('boolKey', false); + const obj1 = new Parse.Object('MyObject'); + obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); // Add trigger - Parse.Cloud.beforeFind("MyObject", req => { - req.readPreference = "SECONDARY"; + Parse.Cloud.beforeFind('MyObject', req => { + req.readPreference = 'SECONDARY'; }); await waitForReplication(); // Spy on DB adapter - spyOn(Collection.prototype, "aggregate").and.callThrough(); + spyOn(Collection.prototype, 'aggregate').and.callThrough(); // Query - const query = new Parse.Query("MyObject"); + const query = new Parse.Query('MyObject'); const results = await query.aggregate([{ $match: { boolKey: false } }]); // Validate expect(results.length).toBe(1); let readPreference = null; Collection.prototype.aggregate.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf("MyObject") > -1) { + if (call.object.s.namespace.collection.indexOf('MyObject') > -1) { readPreference = call.args[1].readPreference; } }); expect(readPreference).toEqual(ReadPreference.SECONDARY); }); - it("should change read preference for `find` using query option", async () => { + it('should change read preference for `find` using query option', async () => { // Save objects - const obj0 = new Parse.Object("MyObject"); - obj0.set("boolKey", false); - const obj1 = new Parse.Object("MyObject"); - obj1.set("boolKey", true); + const obj0 = new Parse.Object('MyObject'); + obj0.set('boolKey', false); + const obj1 = new Parse.Object('MyObject'); + obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); await waitForReplication(); // Spy on DB adapter - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); // Query - const query = new Parse.Query("MyObject"); - query.equalTo("boolKey", false); - query.readPreference("SECONDARY"); + const query = new Parse.Query('MyObject'); + query.equalTo('boolKey', false); + query.readPreference('SECONDARY'); const results = await query.find(); // Validate expect(results.length).toBe(1); let myObjectReadPreference = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf("MyObject") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { myObjectReadPreference = call.args[1].readPreference; } }); expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY); }); - it("should change read preference for `aggregate` using query option", async () => { + it('should change read preference for `aggregate` using query option', async () => { // Save objects - const obj0 = new Parse.Object("MyObject"); - obj0.set("boolKey", false); - const obj1 = new Parse.Object("MyObject"); - obj1.set("boolKey", true); + const obj0 = new Parse.Object('MyObject'); + obj0.set('boolKey', false); + const obj1 = new Parse.Object('MyObject'); + obj1.set('boolKey', true); await Parse.Object.saveAll([obj0, obj1]); await waitForReplication(); // Spy on DB adapter - spyOn(Collection.prototype, "aggregate").and.callThrough(); + spyOn(Collection.prototype, 'aggregate').and.callThrough(); // Query - const query = new Parse.Query("MyObject"); - query.readPreference("SECONDARY"); + const query = new Parse.Query('MyObject'); + query.readPreference('SECONDARY'); const results = await query.aggregate([{ $match: { boolKey: false } }]); // Validate expect(results.length).toBe(1); let readPreference = null; Collection.prototype.aggregate.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf("MyObject") > -1) { + if (call.object.s.namespace.collection.indexOf('MyObject') > -1) { readPreference = call.args[1].readPreference; } }); expect(readPreference).toEqual(ReadPreference.SECONDARY); }); - it("should find includes in same replica of readPreference by default", async () => { - const obj0 = new Parse.Object("MyObject0"); - obj0.set("boolKey", false); - const obj1 = new Parse.Object("MyObject1"); - obj1.set("boolKey", true); - obj1.set("myObject0", obj0); - const obj2 = new Parse.Object("MyObject2"); - obj2.set("boolKey", false); - obj2.set("myObject1", obj1); + it('should find includes in same replica of readPreference by default', async () => { + const obj0 = new Parse.Object('MyObject0'); + obj0.set('boolKey', false); + const obj1 = new Parse.Object('MyObject1'); + obj1.set('boolKey', true); + obj1.set('myObject0', obj0); + const obj2 = new Parse.Object('MyObject2'); + obj2.set('boolKey', false); + obj2.set('myObject1', obj1); await Parse.Object.saveAll([obj0, obj1, obj2]); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); - Parse.Cloud.beforeFind("MyObject2", req => { - req.readPreference = "SECONDARY"; + Parse.Cloud.beforeFind('MyObject2', req => { + req.readPreference = 'SECONDARY'; }); await waitForReplication(); - const query = new Parse.Query("MyObject2"); - query.equalTo("boolKey", false); - query.include("myObject1"); - query.include("myObject1.myObject0"); + const query = new Parse.Query('MyObject2'); + query.equalTo('boolKey', false); + query.include('myObject1'); + query.include('myObject1.myObject0'); const results = await query.find(); expect(results.length).toBe(1); const firstResult = results[0]; - expect(firstResult.get("boolKey")).toBe(false); - expect(firstResult.get("myObject1").get("boolKey")).toBe(true); - expect(firstResult.get("myObject1").get("myObject0").get("boolKey")).toBe( + expect(firstResult.get('boolKey')).toBe(false); + expect(firstResult.get('myObject1').get('boolKey')).toBe(true); + expect(firstResult.get('myObject1').get('myObject0').get('boolKey')).toBe( false ); @@ -736,13 +736,13 @@ describe_only_db("mongo")("Read preference option", () => { let myObjectReadPreference1 = null; let myObjectReadPreference2 = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf("MyObject0") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) { myObjectReadPreference0 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf("MyObject1") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) { myObjectReadPreference1 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf("MyObject2") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) { myObjectReadPreference2 = call.args[1].readPreference; } }); @@ -752,36 +752,36 @@ describe_only_db("mongo")("Read preference option", () => { expect(myObjectReadPreference2).toEqual(ReadPreference.SECONDARY); }); - it("should change includes read preference", async () => { - const obj0 = new Parse.Object("MyObject0"); - obj0.set("boolKey", false); - const obj1 = new Parse.Object("MyObject1"); - obj1.set("boolKey", true); - obj1.set("myObject0", obj0); - const obj2 = new Parse.Object("MyObject2"); - obj2.set("boolKey", false); - obj2.set("myObject1", obj1); + it('should change includes read preference', async () => { + const obj0 = new Parse.Object('MyObject0'); + obj0.set('boolKey', false); + const obj1 = new Parse.Object('MyObject1'); + obj1.set('boolKey', true); + obj1.set('myObject0', obj0); + const obj2 = new Parse.Object('MyObject2'); + obj2.set('boolKey', false); + obj2.set('myObject1', obj1); await Parse.Object.saveAll([obj0, obj1, obj2]); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); - Parse.Cloud.beforeFind("MyObject2", req => { - req.readPreference = "SECONDARY_PREFERRED"; - req.includeReadPreference = "SECONDARY"; + Parse.Cloud.beforeFind('MyObject2', req => { + req.readPreference = 'SECONDARY_PREFERRED'; + req.includeReadPreference = 'SECONDARY'; }); await waitForReplication(); - const query = new Parse.Query("MyObject2"); - query.equalTo("boolKey", false); - query.include("myObject1"); - query.include("myObject1.myObject0"); + const query = new Parse.Query('MyObject2'); + query.equalTo('boolKey', false); + query.include('myObject1'); + query.include('myObject1.myObject0'); const results = await query.find(); expect(results.length).toBe(1); const firstResult = results[0]; - expect(firstResult.get("boolKey")).toBe(false); - expect(firstResult.get("myObject1").get("boolKey")).toBe(true); - expect(firstResult.get("myObject1").get("myObject0").get("boolKey")).toBe( + expect(firstResult.get('boolKey')).toBe(false); + expect(firstResult.get('myObject1').get('boolKey')).toBe(true); + expect(firstResult.get('myObject1').get('myObject0').get('boolKey')).toBe( false ); @@ -789,13 +789,13 @@ describe_only_db("mongo")("Read preference option", () => { let myObjectReadPreference1 = null; let myObjectReadPreference2 = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf("MyObject0") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) { myObjectReadPreference0 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf("MyObject1") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) { myObjectReadPreference1 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf("MyObject2") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) { myObjectReadPreference2 = call.args[1].readPreference; } }); @@ -805,31 +805,31 @@ describe_only_db("mongo")("Read preference option", () => { expect(myObjectReadPreference2).toEqual(ReadPreference.SECONDARY_PREFERRED); }); - it("should change includes read preference when finding through API", async () => { - const obj0 = new Parse.Object("MyObject0"); - obj0.set("boolKey", false); - const obj1 = new Parse.Object("MyObject1"); - obj1.set("boolKey", true); - obj1.set("myObject0", obj0); - const obj2 = new Parse.Object("MyObject2"); - obj2.set("boolKey", false); - obj2.set("myObject1", obj1); + it('should change includes read preference when finding through API', async () => { + const obj0 = new Parse.Object('MyObject0'); + obj0.set('boolKey', false); + const obj1 = new Parse.Object('MyObject1'); + obj1.set('boolKey', true); + obj1.set('myObject0', obj0); + const obj2 = new Parse.Object('MyObject2'); + obj2.set('boolKey', false); + obj2.set('myObject1', obj1); await Parse.Object.saveAll([obj0, obj1, obj2]); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); await waitForReplication(); const response = await request({ - method: "GET", + method: 'GET', url: - "http://localhost:8378/1/classes/MyObject2/" + + 'http://localhost:8378/1/classes/MyObject2/' + obj2.id + - "?include=" + - JSON.stringify(["myObject1", "myObject1.myObject0"]) + - "&readPreference=SECONDARY_PREFERRED&includeReadPreference=SECONDARY", + '?include=' + + JSON.stringify(['myObject1', 'myObject1.myObject0']) + + '&readPreference=SECONDARY_PREFERRED&includeReadPreference=SECONDARY', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }, json: true, }); @@ -842,13 +842,13 @@ describe_only_db("mongo")("Read preference option", () => { let myObjectReadPreference1 = null; let myObjectReadPreference2 = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf("MyObject0") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) { myObjectReadPreference0 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf("MyObject1") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) { myObjectReadPreference1 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf("MyObject2") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) { myObjectReadPreference2 = call.args[1].readPreference; } }); @@ -858,31 +858,31 @@ describe_only_db("mongo")("Read preference option", () => { expect(myObjectReadPreference2).toEqual(ReadPreference.SECONDARY_PREFERRED); }); - it("should change includes read preference when getting through API", async () => { - const obj0 = new Parse.Object("MyObject0"); - obj0.set("boolKey", false); - const obj1 = new Parse.Object("MyObject1"); - obj1.set("boolKey", true); - obj1.set("myObject0", obj0); - const obj2 = new Parse.Object("MyObject2"); - obj2.set("boolKey", false); - obj2.set("myObject1", obj1); + it('should change includes read preference when getting through API', async () => { + const obj0 = new Parse.Object('MyObject0'); + obj0.set('boolKey', false); + const obj1 = new Parse.Object('MyObject1'); + obj1.set('boolKey', true); + obj1.set('myObject0', obj0); + const obj2 = new Parse.Object('MyObject2'); + obj2.set('boolKey', false); + obj2.set('myObject1', obj1); await Parse.Object.saveAll([obj0, obj1, obj2]); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); await waitForReplication(); const response = await request({ - method: "GET", + method: 'GET', url: - "http://localhost:8378/1/classes/MyObject2?where=" + + 'http://localhost:8378/1/classes/MyObject2?where=' + JSON.stringify({ boolKey: false }) + - "&include=" + - JSON.stringify(["myObject1", "myObject1.myObject0"]) + - "&readPreference=SECONDARY_PREFERRED&includeReadPreference=SECONDARY", + '&include=' + + JSON.stringify(['myObject1', 'myObject1.myObject0']) + + '&readPreference=SECONDARY_PREFERRED&includeReadPreference=SECONDARY', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }, json: true, }); @@ -896,13 +896,13 @@ describe_only_db("mongo")("Read preference option", () => { let myObjectReadPreference1 = null; let myObjectReadPreference2 = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf("MyObject0") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) { myObjectReadPreference0 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf("MyObject1") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) { myObjectReadPreference1 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf("MyObject2") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) { myObjectReadPreference2 = call.args[1].readPreference; } }); @@ -912,48 +912,48 @@ describe_only_db("mongo")("Read preference option", () => { expect(myObjectReadPreference2).toEqual(ReadPreference.SECONDARY_PREFERRED); }); - it("should find subqueries in same replica of readPreference by default", async () => { - const obj0 = new Parse.Object("MyObject0"); - obj0.set("boolKey", false); - const obj1 = new Parse.Object("MyObject1"); - obj1.set("boolKey", true); - obj1.set("myObject0", obj0); - const obj2 = new Parse.Object("MyObject2"); - obj2.set("boolKey", false); - obj2.set("myObject1", obj1); + it('should find subqueries in same replica of readPreference by default', async () => { + const obj0 = new Parse.Object('MyObject0'); + obj0.set('boolKey', false); + const obj1 = new Parse.Object('MyObject1'); + obj1.set('boolKey', true); + obj1.set('myObject0', obj0); + const obj2 = new Parse.Object('MyObject2'); + obj2.set('boolKey', false); + obj2.set('myObject1', obj1); await Parse.Object.saveAll([obj0, obj1, obj2]); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); - Parse.Cloud.beforeFind("MyObject2", req => { - req.readPreference = "SECONDARY"; + Parse.Cloud.beforeFind('MyObject2', req => { + req.readPreference = 'SECONDARY'; }); await waitForReplication(); - const query0 = new Parse.Query("MyObject0"); - query0.equalTo("boolKey", false); + const query0 = new Parse.Query('MyObject0'); + query0.equalTo('boolKey', false); - const query1 = new Parse.Query("MyObject1"); - query1.matchesQuery("myObject0", query0); + const query1 = new Parse.Query('MyObject1'); + query1.matchesQuery('myObject0', query0); - const query2 = new Parse.Query("MyObject2"); - query2.matchesQuery("myObject1", query1); + const query2 = new Parse.Query('MyObject2'); + query2.matchesQuery('myObject1', query1); const results = await query2.find(); expect(results.length).toBe(1); - expect(results[0].get("boolKey")).toBe(false); + expect(results[0].get('boolKey')).toBe(false); let myObjectReadPreference0 = null; let myObjectReadPreference1 = null; let myObjectReadPreference2 = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf("MyObject0") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) { myObjectReadPreference0 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf("MyObject1") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) { myObjectReadPreference1 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf("MyObject2") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) { myObjectReadPreference2 = call.args[1].readPreference; } }); @@ -963,49 +963,49 @@ describe_only_db("mongo")("Read preference option", () => { expect(myObjectReadPreference2).toEqual(ReadPreference.SECONDARY); }); - it("should change subqueries read preference when using matchesQuery", async () => { - const obj0 = new Parse.Object("MyObject0"); - obj0.set("boolKey", false); - const obj1 = new Parse.Object("MyObject1"); - obj1.set("boolKey", true); - obj1.set("myObject0", obj0); - const obj2 = new Parse.Object("MyObject2"); - obj2.set("boolKey", false); - obj2.set("myObject1", obj1); + it('should change subqueries read preference when using matchesQuery', async () => { + const obj0 = new Parse.Object('MyObject0'); + obj0.set('boolKey', false); + const obj1 = new Parse.Object('MyObject1'); + obj1.set('boolKey', true); + obj1.set('myObject0', obj0); + const obj2 = new Parse.Object('MyObject2'); + obj2.set('boolKey', false); + obj2.set('myObject1', obj1); await Parse.Object.saveAll([obj0, obj1, obj2]); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); - Parse.Cloud.beforeFind("MyObject2", req => { - req.readPreference = "SECONDARY_PREFERRED"; - req.subqueryReadPreference = "SECONDARY"; + Parse.Cloud.beforeFind('MyObject2', req => { + req.readPreference = 'SECONDARY_PREFERRED'; + req.subqueryReadPreference = 'SECONDARY'; }); await waitForReplication(); - const query0 = new Parse.Query("MyObject0"); - query0.equalTo("boolKey", false); + const query0 = new Parse.Query('MyObject0'); + query0.equalTo('boolKey', false); - const query1 = new Parse.Query("MyObject1"); - query1.matchesQuery("myObject0", query0); + const query1 = new Parse.Query('MyObject1'); + query1.matchesQuery('myObject0', query0); - const query2 = new Parse.Query("MyObject2"); - query2.matchesQuery("myObject1", query1); + const query2 = new Parse.Query('MyObject2'); + query2.matchesQuery('myObject1', query1); const results = await query2.find(); expect(results.length).toBe(1); - expect(results[0].get("boolKey")).toBe(false); + expect(results[0].get('boolKey')).toBe(false); let myObjectReadPreference0 = null; let myObjectReadPreference1 = null; let myObjectReadPreference2 = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf("MyObject0") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) { myObjectReadPreference0 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf("MyObject1") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) { myObjectReadPreference1 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf("MyObject2") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) { myObjectReadPreference2 = call.args[1].readPreference; } }); @@ -1015,49 +1015,49 @@ describe_only_db("mongo")("Read preference option", () => { expect(myObjectReadPreference2).toEqual(ReadPreference.SECONDARY_PREFERRED); }); - it("should change subqueries read preference when using doesNotMatchQuery", async () => { - const obj0 = new Parse.Object("MyObject0"); - obj0.set("boolKey", false); - const obj1 = new Parse.Object("MyObject1"); - obj1.set("boolKey", true); - obj1.set("myObject0", obj0); - const obj2 = new Parse.Object("MyObject2"); - obj2.set("boolKey", false); - obj2.set("myObject1", obj1); + it('should change subqueries read preference when using doesNotMatchQuery', async () => { + const obj0 = new Parse.Object('MyObject0'); + obj0.set('boolKey', false); + const obj1 = new Parse.Object('MyObject1'); + obj1.set('boolKey', true); + obj1.set('myObject0', obj0); + const obj2 = new Parse.Object('MyObject2'); + obj2.set('boolKey', false); + obj2.set('myObject1', obj1); await Parse.Object.saveAll([obj0, obj1, obj2]); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); - Parse.Cloud.beforeFind("MyObject2", req => { - req.readPreference = "SECONDARY_PREFERRED"; - req.subqueryReadPreference = "SECONDARY"; + Parse.Cloud.beforeFind('MyObject2', req => { + req.readPreference = 'SECONDARY_PREFERRED'; + req.subqueryReadPreference = 'SECONDARY'; }); await waitForReplication(); - const query0 = new Parse.Query("MyObject0"); - query0.equalTo("boolKey", false); + const query0 = new Parse.Query('MyObject0'); + query0.equalTo('boolKey', false); - const query1 = new Parse.Query("MyObject1"); - query1.doesNotMatchQuery("myObject0", query0); + const query1 = new Parse.Query('MyObject1'); + query1.doesNotMatchQuery('myObject0', query0); - const query2 = new Parse.Query("MyObject2"); - query2.doesNotMatchQuery("myObject1", query1); + const query2 = new Parse.Query('MyObject2'); + query2.doesNotMatchQuery('myObject1', query1); const results = await query2.find(); expect(results.length).toBe(1); - expect(results[0].get("boolKey")).toBe(false); + expect(results[0].get('boolKey')).toBe(false); let myObjectReadPreference0 = null; let myObjectReadPreference1 = null; let myObjectReadPreference2 = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf("MyObject0") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) { myObjectReadPreference0 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf("MyObject1") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) { myObjectReadPreference1 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf("MyObject2") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) { myObjectReadPreference2 = call.args[1].readPreference; } }); @@ -1067,50 +1067,50 @@ describe_only_db("mongo")("Read preference option", () => { expect(myObjectReadPreference2).toEqual(ReadPreference.SECONDARY_PREFERRED); }); - it("should change subqueries read preference when using matchesKeyInQuery and doesNotMatchKeyInQuery", async () => { - const obj0 = new Parse.Object("MyObject0"); - obj0.set("boolKey", false); - const obj1 = new Parse.Object("MyObject1"); - obj1.set("boolKey", true); - obj1.set("myObject0", obj0); - const obj2 = new Parse.Object("MyObject2"); - obj2.set("boolKey", false); - obj2.set("myObject1", obj1); + it('should change subqueries read preference when using matchesKeyInQuery and doesNotMatchKeyInQuery', async () => { + const obj0 = new Parse.Object('MyObject0'); + obj0.set('boolKey', false); + const obj1 = new Parse.Object('MyObject1'); + obj1.set('boolKey', true); + obj1.set('myObject0', obj0); + const obj2 = new Parse.Object('MyObject2'); + obj2.set('boolKey', false); + obj2.set('myObject1', obj1); await Parse.Object.saveAll([obj0, obj1, obj2]); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); - Parse.Cloud.beforeFind("MyObject2", req => { - req.readPreference = "SECONDARY_PREFERRED"; - req.subqueryReadPreference = "SECONDARY"; + Parse.Cloud.beforeFind('MyObject2', req => { + req.readPreference = 'SECONDARY_PREFERRED'; + req.subqueryReadPreference = 'SECONDARY'; }); await waitForReplication(); - const query0 = new Parse.Query("MyObject0"); - query0.equalTo("boolKey", false); + const query0 = new Parse.Query('MyObject0'); + query0.equalTo('boolKey', false); - const query1 = new Parse.Query("MyObject1"); - query1.equalTo("boolKey", true); + const query1 = new Parse.Query('MyObject1'); + query1.equalTo('boolKey', true); - const query2 = new Parse.Query("MyObject2"); - query2.matchesKeyInQuery("boolKey", "boolKey", query0); - query2.doesNotMatchKeyInQuery("boolKey", "boolKey", query1); + const query2 = new Parse.Query('MyObject2'); + query2.matchesKeyInQuery('boolKey', 'boolKey', query0); + query2.doesNotMatchKeyInQuery('boolKey', 'boolKey', query1); const results = await query2.find(); expect(results.length).toBe(1); - expect(results[0].get("boolKey")).toBe(false); + expect(results[0].get('boolKey')).toBe(false); let myObjectReadPreference0 = null; let myObjectReadPreference1 = null; let myObjectReadPreference2 = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf("MyObject0") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) { myObjectReadPreference0 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf("MyObject1") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) { myObjectReadPreference1 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf("MyObject2") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) { myObjectReadPreference2 = call.args[1].readPreference; } }); @@ -1120,48 +1120,48 @@ describe_only_db("mongo")("Read preference option", () => { expect(myObjectReadPreference2).toEqual(ReadPreference.SECONDARY_PREFERRED); }); - it("should change subqueries read preference when using matchesKeyInQuery and doesNotMatchKeyInQuery to find through API", async () => { - const obj0 = new Parse.Object("MyObject0"); - obj0.set("boolKey", false); - const obj1 = new Parse.Object("MyObject1"); - obj1.set("boolKey", true); - obj1.set("myObject0", obj0); - const obj2 = new Parse.Object("MyObject2"); - obj2.set("boolKey", false); - obj2.set("myObject1", obj1); + it('should change subqueries read preference when using matchesKeyInQuery and doesNotMatchKeyInQuery to find through API', async () => { + const obj0 = new Parse.Object('MyObject0'); + obj0.set('boolKey', false); + const obj1 = new Parse.Object('MyObject1'); + obj1.set('boolKey', true); + obj1.set('myObject0', obj0); + const obj2 = new Parse.Object('MyObject2'); + obj2.set('boolKey', false); + obj2.set('myObject1', obj1); await Parse.Object.saveAll([obj0, obj1, obj2]); - spyOn(Collection.prototype, "find").and.callThrough(); + spyOn(Collection.prototype, 'find').and.callThrough(); await waitForReplication(); const whereString = JSON.stringify({ boolKey: { $select: { query: { - className: "MyObject0", + className: 'MyObject0', where: { boolKey: false }, }, - key: "boolKey", + key: 'boolKey', }, $dontSelect: { query: { - className: "MyObject1", + className: 'MyObject1', where: { boolKey: true }, }, - key: "boolKey", + key: 'boolKey', }, }, }); const response = await request({ - method: "GET", + method: 'GET', url: - "http://localhost:8378/1/classes/MyObject2/?where=" + + 'http://localhost:8378/1/classes/MyObject2/?where=' + whereString + - "&readPreference=SECONDARY_PREFERRED&subqueryReadPreference=SECONDARY", + '&readPreference=SECONDARY_PREFERRED&subqueryReadPreference=SECONDARY', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }, json: true, }); @@ -1172,13 +1172,13 @@ describe_only_db("mongo")("Read preference option", () => { let myObjectReadPreference1 = null; let myObjectReadPreference2 = null; Collection.prototype.find.calls.all().forEach(call => { - if (call.object.s.namespace.collection.indexOf("MyObject0") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) { myObjectReadPreference0 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf("MyObject1") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) { myObjectReadPreference1 = call.args[1].readPreference; } - if (call.object.s.namespace.collection.indexOf("MyObject2") >= 0) { + if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) { myObjectReadPreference2 = call.args[1].readPreference; } }); diff --git a/spec/RedisCacheAdapter.spec.js b/spec/RedisCacheAdapter.spec.js index 691e733e72..cdabf34ea2 100644 --- a/spec/RedisCacheAdapter.spec.js +++ b/spec/RedisCacheAdapter.spec.js @@ -1,5 +1,5 @@ const RedisCacheAdapter = - require("../lib/Adapters/Cache/RedisCacheAdapter").default; + require('../lib/Adapters/Cache/RedisCacheAdapter').default; function wait(sleep) { return new Promise(function (resolve) { @@ -12,10 +12,10 @@ set PARSE_SERVER_TEST_CACHE='redis' and make sure a redis server is available on the default port */ describe_only(() => { - return process.env.PARSE_SERVER_TEST_CACHE === "redis"; -})("RedisCacheAdapter", function () { - const KEY = "hello"; - const VALUE = "world"; + return process.env.PARSE_SERVER_TEST_CACHE === 'redis'; +})('RedisCacheAdapter', function () { + const KEY = 'hello'; + const VALUE = 'world'; let cache; beforeEach(async () => { @@ -24,7 +24,7 @@ describe_only(() => { await cache.clear(); }); - it("should get/set/clear", async () => { + it('should get/set/clear', async () => { const cacheNaN = new RedisCacheAdapter({ ttl: NaN, }); @@ -38,7 +38,7 @@ describe_only(() => { await cacheNaN.clear(); }); - it("should expire after ttl", done => { + it('should expire after ttl', done => { cache .put(KEY, VALUE) .then(() => cache.get(KEY)) @@ -49,7 +49,7 @@ describe_only(() => { .then(done); }); - it("should not store value for ttl=0", done => { + it('should not store value for ttl=0', done => { cache .put(KEY, VALUE, 0) .then(() => cache.get(KEY)) @@ -57,7 +57,7 @@ describe_only(() => { .then(done); }); - it("should not expire when ttl=Infinity", done => { + it('should not expire when ttl=Infinity', done => { cache .put(KEY, VALUE, Infinity) .then(() => cache.get(KEY)) @@ -68,10 +68,10 @@ describe_only(() => { .then(done); }); - it("should fallback to default ttl", done => { + it('should fallback to default ttl', done => { let promise = Promise.resolve(); - [-100, null, undefined, "not number", true].forEach(ttl => { + [-100, null, undefined, 'not number', true].forEach(ttl => { promise = promise.then(() => cache .put(KEY, VALUE, ttl) @@ -86,7 +86,7 @@ describe_only(() => { promise.then(done); }); - it("should find un-expired records", done => { + it('should find un-expired records', done => { cache .put(KEY, VALUE) .then(() => cache.get(KEY)) @@ -97,7 +97,7 @@ describe_only(() => { .then(done); }); - it("handleShutdown, close connection", async () => { + it('handleShutdown, close connection', async () => { await cache.handleShutdown(); setTimeout(() => { expect(cache.client.isOpen).toBe(false); @@ -106,11 +106,11 @@ describe_only(() => { }); describe_only(() => { - return process.env.PARSE_SERVER_TEST_CACHE === "redis"; -})("RedisCacheAdapter/KeyPromiseQueue", function () { - const KEY1 = "key1"; - const KEY2 = "key2"; - const VALUE = "hello"; + return process.env.PARSE_SERVER_TEST_CACHE === 'redis'; +})('RedisCacheAdapter/KeyPromiseQueue', function () { + const KEY1 = 'key1'; + const KEY2 = 'key2'; + const VALUE = 'hello'; // number of chained ops on a single key function getQueueCountForKey(cache, key) { @@ -122,7 +122,7 @@ describe_only(() => { return Object.keys(cache.queue.queue).length; } - it("it should clear completed operations from queue", async done => { + it('it should clear completed operations from queue', async done => { const cache = new RedisCacheAdapter({ ttl: NaN }); await cache.connect(); @@ -145,7 +145,7 @@ describe_only(() => { promise.then(() => expect(getQueueCount(cache)).toEqual(0)).then(done); }); - it("it should count per key chained operations correctly", async done => { + it('it should count per key chained operations correctly', async done => { const cache = new RedisCacheAdapter({ ttl: NaN }); await cache.connect(); @@ -169,12 +169,12 @@ describe_only(() => { .then(done); }); - it("should start and connect cache adapter", async () => { + it('should start and connect cache adapter', async () => { const server = await reconfigureServer({ cacheAdapter: { - module: `${__dirname.replace("/spec", "")}/lib/Adapters/Cache/RedisCacheAdapter`, + module: `${__dirname.replace('/spec', '')}/lib/Adapters/Cache/RedisCacheAdapter`, options: { - url: "redis://127.0.0.1:6379/1", + url: 'redis://127.0.0.1:6379/1', }, }, }); diff --git a/spec/RedisPubSub.spec.js b/spec/RedisPubSub.spec.js index cece4b6b63..868e590740 100644 --- a/spec/RedisPubSub.spec.js +++ b/spec/RedisPubSub.spec.js @@ -1,45 +1,45 @@ -const RedisPubSub = require("../lib/Adapters/PubSub/RedisPubSub").RedisPubSub; +const RedisPubSub = require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; -describe("RedisPubSub", function () { +describe('RedisPubSub', function () { beforeEach(function (done) { // Mock redis - const createClient = jasmine.createSpy("createClient").and.returnValue({ - connect: jasmine.createSpy("connect").and.resolveTo(), - on: jasmine.createSpy("on"), + const createClient = jasmine.createSpy('createClient').and.returnValue({ + connect: jasmine.createSpy('connect').and.resolveTo(), + on: jasmine.createSpy('on'), }); - jasmine.mockLibrary("redis", "createClient", createClient); + jasmine.mockLibrary('redis', 'createClient', createClient); done(); }); - it("can create publisher", function () { + it('can create publisher', function () { RedisPubSub.createPublisher({ - redisURL: "redisAddress", + redisURL: 'redisAddress', redisOptions: { socket_keepalive: true }, }); - const redis = require("redis"); + const redis = require('redis'); expect(redis.createClient).toHaveBeenCalledWith({ - url: "redisAddress", + url: 'redisAddress', socket_keepalive: true, no_ready_check: true, }); }); - it("can create subscriber", function () { + it('can create subscriber', function () { RedisPubSub.createSubscriber({ - redisURL: "redisAddress", + redisURL: 'redisAddress', redisOptions: { socket_keepalive: true }, }); - const redis = require("redis"); + const redis = require('redis'); expect(redis.createClient).toHaveBeenCalledWith({ - url: "redisAddress", + url: 'redisAddress', socket_keepalive: true, no_ready_check: true, }); }); afterEach(function () { - jasmine.restoreLibrary("redis", "createClient"); + jasmine.restoreLibrary('redis', 'createClient'); }); }); diff --git a/spec/RegexVulnerabilities.spec.js b/spec/RegexVulnerabilities.spec.js index 7173fcd2b5..5cba845119 100644 --- a/spec/RegexVulnerabilities.spec.js +++ b/spec/RegexVulnerabilities.spec.js @@ -1,22 +1,22 @@ -const request = require("../lib/request"); +const request = require('../lib/request'); -const serverURL = "http://localhost:8378/1"; +const serverURL = 'http://localhost:8378/1'; const headers = { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }; const keys = { - _ApplicationId: "test", - _JavaScriptKey: "test", + _ApplicationId: 'test', + _JavaScriptKey: 'test', }; const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), sendPasswordResetEmail: () => Promise.resolve(), sendMail: () => {}, }; -const appName = "test"; -const publicServerURL = "http://localhost:8378/1"; +const appName = 'test'; +const publicServerURL = 'http://localhost:8378/1'; -describe("Regex Vulnerabilities", () => { +describe('Regex Vulnerabilities', () => { let objectId; let sessionToken; let partialSessionToken; @@ -24,7 +24,7 @@ describe("Regex Vulnerabilities", () => { beforeEach(async () => { await reconfigureServer({ - maintenanceKey: "test2", + maintenanceKey: 'test2', verifyUserEmails: true, emailAdapter, appName, @@ -33,14 +33,14 @@ describe("Regex Vulnerabilities", () => { const signUpResponse = await request({ url: `${serverURL}/users`, - method: "POST", + method: 'POST', headers, body: JSON.stringify({ ...keys, - _method: "POST", - username: "someemail@somedomain.com", - password: "somepassword", - email: "someemail@somedomain.com", + _method: 'POST', + username: 'someemail@somedomain.com', + password: 'somepassword', + email: 'someemail@somedomain.com', }), }); objectId = signUpResponse.data.objectId; @@ -48,37 +48,37 @@ describe("Regex Vulnerabilities", () => { partialSessionToken = sessionToken.slice(0, 3); }); - describe("on session token", () => { - it("should not work with regex", async () => { + describe('on session token', () => { + it('should not work with regex', async () => { try { await request({ url: `${serverURL}/users/me`, - method: "POST", + method: 'POST', headers, body: JSON.stringify({ ...keys, _SessionToken: { $regex: partialSessionToken, }, - _method: "GET", + _method: 'GET', }), }); - fail("should not work"); + fail('should not work'); } catch (e) { expect(e.data.code).toEqual(209); - expect(e.data.error).toEqual("Invalid session token"); + expect(e.data.error).toEqual('Invalid session token'); } }); - it("should work with plain token", async () => { + it('should work with plain token', async () => { const meResponse = await request({ url: `${serverURL}/users/me`, - method: "POST", + method: 'POST', headers, body: JSON.stringify({ ...keys, _SessionToken: sessionToken, - _method: "GET", + _method: 'GET', }), }); expect(meResponse.data.objectId).toEqual(objectId); @@ -86,69 +86,69 @@ describe("Regex Vulnerabilities", () => { }); }); - describe("on verify e-mail", () => { + describe('on verify e-mail', () => { beforeEach(async function () { const userQuery = new Parse.Query(Parse.User); user = await userQuery.get(objectId, { useMasterKey: true }); }); - it("should not work with regex", async () => { - expect(user.get("emailVerified")).toEqual(false); + it('should not work with regex', async () => { + expect(user.get('emailVerified')).toEqual(false); await request({ url: `${serverURL}/apps/test/verify_email?token[$regex]=`, - method: "GET", + method: 'GET', }); await user.fetch({ useMasterKey: true }); - expect(user.get("emailVerified")).toEqual(false); + expect(user.get('emailVerified')).toEqual(false); }); - it_id("92bbb86d-bcda-49fa-8d79-aa0501078044")(it)( - "should work with plain token", + it_id('92bbb86d-bcda-49fa-8d79-aa0501078044')(it)( + 'should work with plain token', async () => { - expect(user.get("emailVerified")).toEqual(false); + expect(user.get('emailVerified')).toEqual(false); const current = await request({ - method: "GET", + method: 'GET', url: `http://localhost:8378/1/classes/_User/${user.id}`, json: true, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Rest-API-Key": "test", - "X-Parse-Maintenance-Key": "test2", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Rest-API-Key': 'test', + 'X-Parse-Maintenance-Key': 'test2', + 'Content-Type': 'application/json', }, }).then(res => res.data); // It should work await request({ url: `${serverURL}/apps/test/verify_email?token=${current._email_verify_token}`, - method: "GET", + method: 'GET', }); await user.fetch({ useMasterKey: true }); - expect(user.get("emailVerified")).toEqual(true); + expect(user.get('emailVerified')).toEqual(true); } ); }); - describe("on password reset", () => { + describe('on password reset', () => { beforeEach(async () => { - user = await Parse.User.logIn("someemail@somedomain.com", "somepassword"); + user = await Parse.User.logIn('someemail@somedomain.com', 'somepassword'); }); - it("should not work with regex", async () => { + it('should not work with regex', async () => { expect(user.id).toEqual(objectId); await request({ url: `${serverURL}/requestPasswordReset`, - method: "POST", + method: 'POST', headers, body: JSON.stringify({ ...keys, - _method: "POST", - email: "someemail@somedomain.com", + _method: 'POST', + email: 'someemail@somedomain.com', }), }); await user.fetch({ useMasterKey: true }); const passwordResetResponse = await request({ url: `${serverURL}/apps/test/request_password_reset?token[$regex]=`, - method: "GET", + method: 'GET', }); expect(passwordResetResponse.status).toEqual(302); expect(passwordResetResponse.headers.location).toMatch( @@ -156,49 +156,49 @@ describe("Regex Vulnerabilities", () => { ); await request({ url: `${serverURL}/apps/test/request_password_reset`, - method: "POST", + method: 'POST', body: { - token: { $regex: "" }, - username: "someemail@somedomain.com", - new_password: "newpassword", + token: { $regex: '' }, + username: 'someemail@somedomain.com', + new_password: 'newpassword', }, }); try { - await Parse.User.logIn("someemail@somedomain.com", "newpassword"); - fail("should not work"); + await Parse.User.logIn('someemail@somedomain.com', 'newpassword'); + fail('should not work'); } catch (e) { expect(e.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); - expect(e.message).toEqual("Invalid username/password."); + expect(e.message).toEqual('Invalid username/password.'); } }); - it("should work with plain token", async () => { + it('should work with plain token', async () => { expect(user.id).toEqual(objectId); await request({ url: `${serverURL}/requestPasswordReset`, - method: "POST", + method: 'POST', headers, body: JSON.stringify({ ...keys, - _method: "POST", - email: "someemail@somedomain.com", + _method: 'POST', + email: 'someemail@somedomain.com', }), }); const current = await request({ - method: "GET", + method: 'GET', url: `http://localhost:8378/1/classes/_User/${user.id}`, json: true, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Rest-API-Key": "test", - "X-Parse-Maintenance-Key": "test2", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Rest-API-Key': 'test', + 'X-Parse-Maintenance-Key': 'test2', + 'Content-Type': 'application/json', }, }).then(res => res.data); const token = current._perishable_token; const passwordResetResponse = await request({ url: `${serverURL}/apps/test/request_password_reset?token=${token}`, - method: "GET", + method: 'GET', }); expect(passwordResetResponse.status).toEqual(302); expect(passwordResetResponse.headers.location).toMatch( @@ -206,16 +206,16 @@ describe("Regex Vulnerabilities", () => { ); await request({ url: `${serverURL}/apps/test/request_password_reset`, - method: "POST", + method: 'POST', body: { token, - username: "someemail@somedomain.com", - new_password: "newpassword", + username: 'someemail@somedomain.com', + new_password: 'newpassword', }, }); const userAgain = await Parse.User.logIn( - "someemail@somedomain.com", - "newpassword" + 'someemail@somedomain.com', + 'newpassword' ); expect(userAgain.id).toEqual(objectId); }); diff --git a/spec/RestQuery.spec.js b/spec/RestQuery.spec.js index 6e6156fce3..9949419857 100644 --- a/spec/RestQuery.spec.js +++ b/spec/RestQuery.spec.js @@ -1,28 +1,28 @@ -"use strict"; +'use strict'; // These tests check the "find" functionality of the REST API. -const auth = require("../lib/Auth"); -const Config = require("../lib/Config"); -const rest = require("../lib/rest"); -const RestQuery = require("../lib/RestQuery"); -const request = require("../lib/request"); +const auth = require('../lib/Auth'); +const Config = require('../lib/Config'); +const rest = require('../lib/rest'); +const RestQuery = require('../lib/RestQuery'); +const request = require('../lib/request'); -const querystring = require("querystring"); +const querystring = require('querystring'); let config; let database; const nobody = auth.nobody(config); -describe("rest query", () => { +describe('rest query', () => { beforeEach(() => { - config = Config.get("test"); + config = Config.get('test'); database = config.database; }); - it("basic query", done => { + it('basic query', done => { rest - .create(config, nobody, "TestObject", {}) + .create(config, nobody, 'TestObject', {}) .then(() => { - return rest.find(config, nobody, "TestObject", {}); + return rest.find(config, nobody, 'TestObject', {}); }) .then(response => { expect(response.results.length).toEqual(1); @@ -30,14 +30,14 @@ describe("rest query", () => { }); }); - it("query with limit", done => { + it('query with limit', done => { rest - .create(config, nobody, "TestObject", { foo: "baz" }) + .create(config, nobody, 'TestObject', { foo: 'baz' }) .then(() => { - return rest.create(config, nobody, "TestObject", { foo: "qux" }); + return rest.create(config, nobody, 'TestObject', { foo: 'qux' }); }) .then(() => { - return rest.find(config, nobody, "TestObject", {}, { limit: 1 }); + return rest.find(config, nobody, 'TestObject', {}, { limit: 1 }); }) .then(response => { expect(response.results.length).toEqual(1); @@ -47,22 +47,22 @@ describe("rest query", () => { }); const data = { - username: "blah", - password: "pass", - sessionToken: "abc123", + username: 'blah', + password: 'pass', + sessionToken: 'abc123', }; - it_exclude_dbs(["postgres"])( - "query for user w/ legacy credentials without masterKey has them stripped from results", + it_exclude_dbs(['postgres'])( + 'query for user w/ legacy credentials without masterKey has them stripped from results', done => { database - .create("_User", data) + .create('_User', data) .then(() => { - return rest.find(config, nobody, "_User"); + return rest.find(config, nobody, '_User'); }) .then(result => { const user = result.results[0]; - expect(user.username).toEqual("blah"); + expect(user.username).toEqual('blah'); expect(user.sessionToken).toBeUndefined(); expect(user.password).toBeUndefined(); done(); @@ -70,17 +70,17 @@ describe("rest query", () => { } ); - it_exclude_dbs(["postgres"])( - "query for user w/ legacy credentials with masterKey has them stripped from results", + it_exclude_dbs(['postgres'])( + 'query for user w/ legacy credentials with masterKey has them stripped from results', done => { database - .create("_User", data) + .create('_User', data) .then(() => { - return rest.find(config, { isMaster: true }, "_User"); + return rest.find(config, { isMaster: true }, '_User'); }) .then(result => { const user = result.results[0]; - expect(user.username).toEqual("blah"); + expect(user.username).toEqual('blah'); expect(user.sessionToken).toBeUndefined(); expect(user.password).toBeUndefined(); done(); @@ -89,59 +89,59 @@ describe("rest query", () => { ); // Created to test a scenario in AnyPic - it_exclude_dbs(["postgres"])("query with include", done => { + it_exclude_dbs(['postgres'])('query with include', done => { let photo = { - foo: "bar", + foo: 'bar', }; let user = { - username: "aUsername", - password: "aPassword", - ACL: { "*": { read: true } }, + username: 'aUsername', + password: 'aPassword', + ACL: { '*': { read: true } }, }; const activity = { - type: "comment", + type: 'comment', photo: { - __type: "Pointer", - className: "TestPhoto", - objectId: "", + __type: 'Pointer', + className: 'TestPhoto', + objectId: '', }, fromUser: { - __type: "Pointer", - className: "_User", - objectId: "", + __type: 'Pointer', + className: '_User', + objectId: '', }, }; const queryWhere = { photo: { - __type: "Pointer", - className: "TestPhoto", - objectId: "", + __type: 'Pointer', + className: 'TestPhoto', + objectId: '', }, - type: "comment", + type: 'comment', }; const queryOptions = { - include: "fromUser", - order: "createdAt", + include: 'fromUser', + order: 'createdAt', limit: 30, }; rest - .create(config, nobody, "TestPhoto", photo) + .create(config, nobody, 'TestPhoto', photo) .then(p => { photo = p; - return rest.create(config, nobody, "_User", user); + return rest.create(config, nobody, '_User', user); }) .then(u => { user = u.response; activity.photo.objectId = photo.objectId; activity.fromUser.objectId = user.objectId; - return rest.create(config, nobody, "TestActivity", activity); + return rest.create(config, nobody, 'TestActivity', activity); }) .then(() => { queryWhere.photo.objectId = photo.objectId; return rest.find( config, nobody, - "TestActivity", + 'TestActivity', queryWhere, queryOptions ); @@ -149,10 +149,10 @@ describe("rest query", () => { .then(response => { const results = response.results; expect(results.length).toEqual(1); - expect(typeof results[0].objectId).toEqual("string"); - expect(typeof results[0].photo).toEqual("object"); - expect(typeof results[0].fromUser).toEqual("object"); - expect(typeof results[0].fromUser.username).toEqual("string"); + expect(typeof results[0].objectId).toEqual('string'); + expect(typeof results[0].photo).toEqual('object'); + expect(typeof results[0].fromUser).toEqual('object'); + expect(typeof results[0].fromUser.username).toEqual('string'); done(); }) .catch(error => { @@ -160,60 +160,60 @@ describe("rest query", () => { }); }); - it("query non-existent class when disabled client class creation", done => { + it('query non-existent class when disabled client class creation', done => { const customConfig = Object.assign({}, config, { allowClientClassCreation: false, }); rest - .find(customConfig, auth.nobody(customConfig), "ClientClassCreation", {}) + .find(customConfig, auth.nobody(customConfig), 'ClientClassCreation', {}) .then( () => { - fail("Should throw an error"); + fail('Should throw an error'); done(); }, err => { expect(err.code).toEqual(Parse.Error.OPERATION_FORBIDDEN); expect(err.message).toEqual( - "This user is not allowed to access " + - "non-existent class: ClientClassCreation" + 'This user is not allowed to access ' + + 'non-existent class: ClientClassCreation' ); done(); } ); }); - it("query existent class when disabled client class creation", async () => { + it('query existent class when disabled client class creation', async () => { const customConfig = Object.assign({}, config, { allowClientClassCreation: false, }); const schema = await config.database.loadSchema(); const actualSchema = await schema.addClassIfNotExists( - "ClientClassCreation", + 'ClientClassCreation', {} ); - expect(actualSchema.className).toEqual("ClientClassCreation"); + expect(actualSchema.className).toEqual('ClientClassCreation'); await schema.reloadData({ clearCache: true }); // Should not throw const result = await rest.find( customConfig, auth.nobody(customConfig), - "ClientClassCreation", + 'ClientClassCreation', {} ); expect(result.results.length).toEqual(0); }); - it("query internal field", async () => { + it('query internal field', async () => { const internalFields = [ - "_email_verify_token", - "_perishable_token", - "_tombstone", - "_email_verify_token_expires_at", - "_failed_login_count", - "_account_lockout_expires_at", - "_password_changed_at", - "_password_history", + '_email_verify_token', + '_perishable_token', + '_tombstone', + '_email_verify_token_expires_at', + '_failed_login_count', + '_account_lockout_expires_at', + '_password_changed_at', + '_password_history', ]; await Promise.all([ ...internalFields.map(field => @@ -232,85 +232,85 @@ describe("rest query", () => { ]); }); - it("query protected field", async () => { + it('query protected field', async () => { const user = new Parse.User(); - user.setUsername("username1"); - user.setPassword("password"); + user.setUsername('username1'); + user.setPassword('password'); await user.signUp(); const config = Config.get(Parse.applicationId); - const obj = new Parse.Object("Test"); + const obj = new Parse.Object('Test'); - obj.set("owner", user); - obj.set("test", "test"); - obj.set("zip", 1234); + obj.set('owner', user); + obj.set('test', 'test'); + obj.set('zip', 1234); await obj.save(); const schema = await config.database.loadSchema(); await schema.updateClass( - "Test", + 'Test', {}, { - get: { "*": true }, - find: { "*": true }, - protectedFields: { [user.id]: ["zip"] }, + get: { '*': true }, + find: { '*': true }, + protectedFields: { [user.id]: ['zip'] }, } ); await Promise.all([ - new Parse.Query("Test").exists("test").find(), + new Parse.Query('Test').exists('test').find(), expectAsync( - new Parse.Query("Test").exists("zip").find() + new Parse.Query('Test').exists('zip').find() ).toBeRejectedWith( new Parse.Error( Parse.Error.OPERATION_FORBIDDEN, - "This user is not allowed to query zip on class Test" + 'This user is not allowed to query zip on class Test' ) ), ]); }); - it("query protected field with matchesQuery", async () => { + it('query protected field with matchesQuery', async () => { const user = new Parse.User(); - user.setUsername("username1"); - user.setPassword("password"); + user.setUsername('username1'); + user.setPassword('password'); await user.signUp(); - const test = new Parse.Object("TestObject", { user }); + const test = new Parse.Object('TestObject', { user }); await test.save(); const subQuery = new Parse.Query(Parse.User); - subQuery.exists("_perishable_token"); + subQuery.exists('_perishable_token'); await expectAsync( - new Parse.Query("TestObject").matchesQuery("user", subQuery).find() + new Parse.Query('TestObject').matchesQuery('user', subQuery).find() ).toBeRejectedWith( new Parse.Error( Parse.Error.INVALID_KEY_NAME, - "Invalid key name: _perishable_token" + 'Invalid key name: _perishable_token' ) ); }); - it("query with wrongly encoded parameter", done => { + it('query with wrongly encoded parameter', done => { rest - .create(config, nobody, "TestParameterEncode", { foo: "bar" }) + .create(config, nobody, 'TestParameterEncode', { foo: 'bar' }) .then(() => { - return rest.create(config, nobody, "TestParameterEncode", { - foo: "baz", + return rest.create(config, nobody, 'TestParameterEncode', { + foo: 'baz', }); }) .then(() => { const headers = { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; const p0 = request({ headers: headers, url: - "http://localhost:8378/1/classes/TestParameterEncode?" + + 'http://localhost:8378/1/classes/TestParameterEncode?' + querystring .stringify({ where: '{"foo":{"$ne": "baz"}}', limit: 1, }) - .replace("=", "%3D"), + .replace('=', '%3D'), }).then(fail, response => { const error = response.data; expect(error.code).toEqual(Parse.Error.INVALID_QUERY); @@ -319,12 +319,12 @@ describe("rest query", () => { const p1 = request({ headers: headers, url: - "http://localhost:8378/1/classes/TestParameterEncode?" + + 'http://localhost:8378/1/classes/TestParameterEncode?' + querystring .stringify({ limit: 1, }) - .replace("=", "%3D"), + .replace('=', '%3D'), }).then(fail, response => { const error = response.data; expect(error.code).toEqual(Parse.Error.INVALID_QUERY); @@ -334,19 +334,19 @@ describe("rest query", () => { .then(done) .catch(err => { jfail(err); - fail("should not fail"); + fail('should not fail'); done(); }); }); - it("query with limit = 0", done => { + it('query with limit = 0', done => { rest - .create(config, nobody, "TestObject", { foo: "baz" }) + .create(config, nobody, 'TestObject', { foo: 'baz' }) .then(() => { - return rest.create(config, nobody, "TestObject", { foo: "qux" }); + return rest.create(config, nobody, 'TestObject', { foo: 'qux' }); }) .then(() => { - return rest.find(config, nobody, "TestObject", {}, { limit: 0 }); + return rest.find(config, nobody, 'TestObject', {}, { limit: 0 }); }) .then(response => { expect(response.results.length).toEqual(0); @@ -354,17 +354,17 @@ describe("rest query", () => { }); }); - it("query with limit = 0 and count = 1", done => { + it('query with limit = 0 and count = 1', done => { rest - .create(config, nobody, "TestObject", { foo: "baz" }) + .create(config, nobody, 'TestObject', { foo: 'baz' }) .then(() => { - return rest.create(config, nobody, "TestObject", { foo: "qux" }); + return rest.create(config, nobody, 'TestObject', { foo: 'qux' }); }) .then(() => { return rest.find( config, nobody, - "TestObject", + 'TestObject', {}, { limit: 0, count: 1 } ); @@ -376,23 +376,23 @@ describe("rest query", () => { }); }); - it("makes sure null pointers are handed correctly #2189", done => { - const object = new Parse.Object("AnObject"); - const anotherObject = new Parse.Object("AnotherObject"); + it('makes sure null pointers are handed correctly #2189', done => { + const object = new Parse.Object('AnObject'); + const anotherObject = new Parse.Object('AnotherObject'); anotherObject .save() .then(() => { - object.set("values", [null, null, anotherObject]); + object.set('values', [null, null, anotherObject]); return object.save(); }) .then(() => { - const query = new Parse.Query("AnObject"); - query.include("values"); + const query = new Parse.Query('AnObject'); + query.include('values'); return query.first(); }) .then( result => { - const values = result.get("values"); + const values = result.get('values'); expect(values.length).toBe(3); let anotherObjectFound = false; let nullCounts = 0; @@ -416,31 +416,31 @@ describe("rest query", () => { }); }); -describe("RestQuery.each", () => { +describe('RestQuery.each', () => { beforeEach(() => { - config = Config.get("test"); + config = Config.get('test'); }); - it_id("3416c90b-ee2e-4bb5-9231-46cd181cd0a2")(it)( - "should run each", + it_id('3416c90b-ee2e-4bb5-9231-46cd181cd0a2')(it)( + 'should run each', async () => { const objects = []; while (objects.length != 10) { - objects.push(new Parse.Object("Object", { value: objects.length })); + objects.push(new Parse.Object('Object', { value: objects.length })); } - const config = Config.get("test"); + const config = Config.get('test'); await Parse.Object.saveAll(objects); const query = await RestQuery({ method: RestQuery.Method.find, config, auth: auth.master(config), - className: "Object", + className: 'Object', restWhere: { value: { $gt: 2 } }, restOptions: { limit: 2 }, }); - const spy = spyOn(query, "execute").and.callThrough(); + const spy = spyOn(query, 'execute').and.callThrough(); const classSpy = spyOn( RestQuery._UnsafeRestQuery.prototype, - "execute" + 'execute' ).and.callThrough(); const results = []; await query.each(result => { @@ -453,23 +453,23 @@ describe("RestQuery.each", () => { } ); - it_id("0fe22501-4b18-461e-b87d-82ceac4a496e")(it)( - "should work with query on relations", + it_id('0fe22501-4b18-461e-b87d-82ceac4a496e')(it)( + 'should work with query on relations', async () => { - const objectA = new Parse.Object("Letter", { value: "A" }); - const objectB = new Parse.Object("Letter", { value: "B" }); + const objectA = new Parse.Object('Letter', { value: 'A' }); + const objectB = new Parse.Object('Letter', { value: 'B' }); - const object1 = new Parse.Object("Number", { value: "1" }); - const object2 = new Parse.Object("Number", { value: "2" }); - const object3 = new Parse.Object("Number", { value: "3" }); - const object4 = new Parse.Object("Number", { value: "4" }); + const object1 = new Parse.Object('Number', { value: '1' }); + const object2 = new Parse.Object('Number', { value: '2' }); + const object3 = new Parse.Object('Number', { value: '3' }); + const object4 = new Parse.Object('Number', { value: '4' }); await Parse.Object.saveAll([object1, object2, object3, object4]); - objectA.relation("numbers").add(object1); - objectB.relation("numbers").add(object2); + objectA.relation('numbers').add(object1); + objectB.relation('numbers').add(object2); await Parse.Object.saveAll([objectA, objectB]); - const config = Config.get("test"); + const config = Config.get('test'); /** * Two queries needed since objectId are sorted and we can't know which one @@ -479,11 +479,11 @@ describe("RestQuery.each", () => { method: RestQuery.Method.get, config, auth: auth.master(config), - className: "Letter", + className: 'Letter', restWhere: { numbers: { - __type: "Pointer", - className: "Number", + __type: 'Pointer', + className: 'Number', objectId: object1.id, }, }, @@ -494,11 +494,11 @@ describe("RestQuery.each", () => { method: RestQuery.Method.get, config, auth: auth.master(config), - className: "Letter", + className: 'Letter', restWhere: { numbers: { - __type: "Pointer", - className: "Number", + __type: 'Pointer', + className: 'Number', objectId: object2.id, }, }, @@ -507,7 +507,7 @@ describe("RestQuery.each", () => { const classSpy = spyOn( RestQuery._UnsafeRestQuery.prototype, - "execute" + 'execute' ).and.callThrough(); const resultsOne = []; const resultsTwo = []; @@ -523,13 +523,13 @@ describe("RestQuery.each", () => { } ); - it("test afterSave response object is return", done => { - Parse.Cloud.beforeSave("TestObject2", function (req) { - req.object.set("tobeaddbefore", true); - req.object.set("tobeaddbeforeandremoveafter", true); + it('test afterSave response object is return', done => { + Parse.Cloud.beforeSave('TestObject2', function (req) { + req.object.set('tobeaddbefore', true); + req.object.set('tobeaddbeforeandremoveafter', true); }); - Parse.Cloud.afterSave("TestObject2", function (req) { + Parse.Cloud.afterSave('TestObject2', function (req) { const jsonObject = req.object.toJSON(); delete jsonObject.todelete; delete jsonObject.tobeaddbeforeandremoveafter; @@ -539,7 +539,7 @@ describe("RestQuery.each", () => { }); rest - .create(config, nobody, "TestObject2", { todelete: true, tokeep: true }) + .create(config, nobody, 'TestObject2', { todelete: true, tokeep: true }) .then(response => { expect(response.response.toadd).toBeTruthy(); expect(response.response.tokeep).toBeTruthy(); @@ -550,24 +550,24 @@ describe("RestQuery.each", () => { }); }); - it("test afterSave should not affect save response", async () => { - Parse.Cloud.beforeSave("TestObject2", ({ object }) => { - object.set("addedBeforeSave", true); + it('test afterSave should not affect save response', async () => { + Parse.Cloud.beforeSave('TestObject2', ({ object }) => { + object.set('addedBeforeSave', true); }); - Parse.Cloud.afterSave("TestObject2", ({ object }) => { - object.set("addedAfterSave", true); - object.unset("initialToRemove"); + Parse.Cloud.afterSave('TestObject2', ({ object }) => { + object.set('addedAfterSave', true); + object.unset('initialToRemove'); }); - const { response } = await rest.create(config, nobody, "TestObject2", { + const { response } = await rest.create(config, nobody, 'TestObject2', { initialSave: true, initialToRemove: true, }); expect(Object.keys(response).sort()).toEqual([ - "addedAfterSave", - "addedBeforeSave", - "createdAt", - "initialToRemove", - "objectId", + 'addedAfterSave', + 'addedBeforeSave', + 'createdAt', + 'initialToRemove', + 'objectId', ]); }); }); diff --git a/spec/RevocableSessionsUpgrade.spec.js b/spec/RevocableSessionsUpgrade.spec.js index bafd28f4e6..8820444ba4 100644 --- a/spec/RevocableSessionsUpgrade.spec.js +++ b/spec/RevocableSessionsUpgrade.spec.js @@ -1,48 +1,48 @@ -const Config = require("../lib/Config"); -const sessionToken = "legacySessionToken"; -const request = require("../lib/request"); -const Parse = require("parse/node"); +const Config = require('../lib/Config'); +const sessionToken = 'legacySessionToken'; +const request = require('../lib/request'); +const Parse = require('parse/node'); function createUser() { const config = Config.get(Parse.applicationId); const user = { - objectId: "1234567890", - username: "hello", - password: "pass", + objectId: '1234567890', + username: 'hello', + password: 'pass', _session_token: sessionToken, }; - return config.database.create("_User", user); + return config.database.create('_User', user); } -describe_only_db("mongo")("revocable sessions", () => { +describe_only_db('mongo')('revocable sessions', () => { beforeEach(async () => { // Create 1 user with the legacy await createUser(); }); - it("should upgrade legacy session token", done => { + it('should upgrade legacy session token', done => { const user = Parse.Object.fromJSON({ - className: "_User", - objectId: "1234567890", + className: '_User', + objectId: '1234567890', sessionToken: sessionToken, }); user ._upgradeToRevocableSession() .then(res => { - expect(res.getSessionToken().indexOf("r:")).toBe(0); + expect(res.getSessionToken().indexOf('r:')).toBe(0); const config = Config.get(Parse.applicationId); // use direct access to the DB to make sure we're not // getting the session token stripped return config.database .loadSchema() .then(schemaController => { - return schemaController.getOneSchema("_User", true); + return schemaController.getOneSchema('_User', true); }) .then(schema => { return config.database.adapter.find( - "_User", + '_User', schema, - { objectId: "1234567890" }, + { objectId: '1234567890' }, {} ); }) @@ -62,22 +62,22 @@ describe_only_db("mongo")("revocable sessions", () => { ); }); - it("should be able to become with revocable session token", done => { + it('should be able to become with revocable session token', done => { const user = Parse.Object.fromJSON({ - className: "_User", - objectId: "1234567890", + className: '_User', + objectId: '1234567890', sessionToken: sessionToken, }); user ._upgradeToRevocableSession() .then(res => { - expect(res.getSessionToken().indexOf("r:")).toBe(0); + expect(res.getSessionToken().indexOf('r:')).toBe(0); return Parse.User.logOut() .then(() => { return Parse.User.become(res.getSessionToken()); }) .then(user => { - expect(user.id).toEqual("1234567890"); + expect(user.id).toEqual('1234567890'); }); }) .then( @@ -91,25 +91,25 @@ describe_only_db("mongo")("revocable sessions", () => { ); }); - it("should not upgrade bad legacy session token", done => { + it('should not upgrade bad legacy session token', done => { request({ - method: "POST", - url: Parse.serverURL + "/upgradeToRevocableSession", + method: 'POST', + url: Parse.serverURL + '/upgradeToRevocableSession', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Rest-API-Key": "rest", - "X-Parse-Session-Token": "badSessionToken", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Rest-API-Key': 'rest', + 'X-Parse-Session-Token': 'badSessionToken', }, }) .then( () => { - fail("should not be able to upgrade a bad token"); + fail('should not be able to upgrade a bad token'); }, response => { expect(response.status).toBe(400); expect(response.data).not.toBeUndefined(); expect(response.data.code).toBe(Parse.Error.INVALID_SESSION_TOKEN); - expect(response.data.error).toEqual("invalid legacy session token"); + expect(response.data.error).toEqual('invalid legacy session token'); } ) .then(() => { @@ -117,24 +117,24 @@ describe_only_db("mongo")("revocable sessions", () => { }); }); - it("should not crash without session token #2720", done => { + it('should not crash without session token #2720', done => { request({ - method: "POST", - url: Parse.serverURL + "/upgradeToRevocableSession", + method: 'POST', + url: Parse.serverURL + '/upgradeToRevocableSession', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Rest-API-Key": "rest", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Rest-API-Key': 'rest', }, }) .then( () => { - fail("should not be able to upgrade a bad token"); + fail('should not be able to upgrade a bad token'); }, response => { expect(response.status).toBe(404); expect(response.data).not.toBeUndefined(); expect(response.data.code).toBe(Parse.Error.OBJECT_NOT_FOUND); - expect(response.data.error).toEqual("invalid session"); + expect(response.data.error).toEqual('invalid session'); } ) .then(() => { diff --git a/spec/Schema.spec.js b/spec/Schema.spec.js index adc6ba6353..ec1f5b70df 100644 --- a/spec/Schema.spec.js +++ b/spec/Schema.spec.js @@ -1,37 +1,37 @@ -"use strict"; +'use strict'; -const Config = require("../lib/Config"); -const SchemaController = require("../lib/Controllers/SchemaController"); -const dd = require("deep-diff"); +const Config = require('../lib/Config'); +const SchemaController = require('../lib/Controllers/SchemaController'); +const dd = require('deep-diff'); let config; const hasAllPODobject = () => { - const obj = new Parse.Object("HasAllPOD"); - obj.set("aNumber", 5); - obj.set("aString", "string"); - obj.set("aBool", true); - obj.set("aDate", new Date()); - obj.set("aObject", { k1: "value", k2: true, k3: 5 }); - obj.set("aArray", ["contents", true, 5]); - obj.set("aGeoPoint", new Parse.GeoPoint({ latitude: 0, longitude: 0 })); + const obj = new Parse.Object('HasAllPOD'); + obj.set('aNumber', 5); + obj.set('aString', 'string'); + obj.set('aBool', true); + obj.set('aDate', new Date()); + obj.set('aObject', { k1: 'value', k2: true, k3: 5 }); + obj.set('aArray', ['contents', true, 5]); + obj.set('aGeoPoint', new Parse.GeoPoint({ latitude: 0, longitude: 0 })); obj.set( - "aFile", - new Parse.File("f.txt", { base64: "V29ya2luZyBhdCBQYXJzZSBpcyBncmVhdCE=" }) + 'aFile', + new Parse.File('f.txt', { base64: 'V29ya2luZyBhdCBQYXJzZSBpcyBncmVhdCE=' }) ); return obj; }; -describe("SchemaController", () => { +describe('SchemaController', () => { beforeEach(() => { - config = Config.get("test"); + config = Config.get('test'); }); - it("can validate one object", done => { + it('can validate one object', done => { config.database .loadSchema() .then(schema => { - return schema.validateObject("TestObject", { a: 1, b: "yo", c: false }); + return schema.validateObject('TestObject', { a: 1, b: 'yo', c: false }); }) .then( () => { @@ -44,15 +44,15 @@ describe("SchemaController", () => { ); }); - it("can validate one object with dot notation", done => { + it('can validate one object with dot notation', done => { config.database .loadSchema() .then(schema => { - return schema.validateObject("TestObjectWithSubDoc", { + return schema.validateObject('TestObjectWithSubDoc', { x: false, - y: "YY", + y: 'YY', z: 1, - "aObject.k1": "newValue", + 'aObject.k1': 'newValue', }); }) .then( @@ -66,100 +66,100 @@ describe("SchemaController", () => { ); }); - it("can validate two objects in a row", done => { + it('can validate two objects in a row', done => { config.database .loadSchema() .then(schema => { - return schema.validateObject("Foo", { x: true, y: "yyy", z: 0 }); + return schema.validateObject('Foo', { x: true, y: 'yyy', z: 0 }); }) .then(schema => { - return schema.validateObject("Foo", { x: false, y: "YY", z: 1 }); + return schema.validateObject('Foo', { x: false, y: 'YY', z: 1 }); }) .then(() => { done(); }); }); - it("can validate Relation object", done => { + it('can validate Relation object', done => { config.database .loadSchema() .then(schema => { - return schema.validateObject("Stuff", { - aRelation: { __type: "Relation", className: "Stuff" }, + return schema.validateObject('Stuff', { + aRelation: { __type: 'Relation', className: 'Stuff' }, }); }) .then(schema => { return schema - .validateObject("Stuff", { - aRelation: { __type: "Pointer", className: "Stuff" }, + .validateObject('Stuff', { + aRelation: { __type: 'Pointer', className: 'Stuff' }, }) .then( () => { - done.fail("expected invalidity"); + done.fail('expected invalidity'); }, () => done() ); }, done.fail); }); - it("rejects inconsistent types", done => { + it('rejects inconsistent types', done => { config.database .loadSchema() .then(schema => { - return schema.validateObject("Stuff", { bacon: 7 }); + return schema.validateObject('Stuff', { bacon: 7 }); }) .then(schema => { - return schema.validateObject("Stuff", { bacon: "z" }); + return schema.validateObject('Stuff', { bacon: 'z' }); }) .then( () => { - fail("expected invalidity"); + fail('expected invalidity'); done(); }, () => done() ); }); - it("updates when new fields are added", done => { + it('updates when new fields are added', done => { config.database .loadSchema() .then(schema => { - return schema.validateObject("Stuff", { bacon: 7 }); + return schema.validateObject('Stuff', { bacon: 7 }); }) .then(schema => { - return schema.validateObject("Stuff", { sausage: 8 }); + return schema.validateObject('Stuff', { sausage: 8 }); }) .then(schema => { - return schema.validateObject("Stuff", { sausage: "ate" }); + return schema.validateObject('Stuff', { sausage: 'ate' }); }) .then( () => { - fail("expected invalidity"); + fail('expected invalidity'); done(); }, () => done() ); }); - it("class-level permissions test find", done => { + it('class-level permissions test find', done => { config.database .loadSchema() .then(schema => { // Just to create a valid class - return schema.validateObject("Stuff", { foo: "bar" }); + return schema.validateObject('Stuff', { foo: 'bar' }); }) .then(schema => { - return schema.setPermissions("Stuff", { + return schema.setPermissions('Stuff', { find: {}, }); }) .then(() => { - const query = new Parse.Query("Stuff"); + const query = new Parse.Query('Stuff'); return query.find(); }) .then( () => { - fail("Class permissions should have rejected this query."); + fail('Class permissions should have rejected this query.'); done(); }, () => { @@ -168,7 +168,7 @@ describe("SchemaController", () => { ); }); - it("class-level permissions test user", done => { + it('class-level permissions test user', done => { let user; createTestUser() .then(u => { @@ -177,17 +177,17 @@ describe("SchemaController", () => { }) .then(schema => { // Just to create a valid class - return schema.validateObject("Stuff", { foo: "bar" }); + return schema.validateObject('Stuff', { foo: 'bar' }); }) .then(schema => { const find = {}; find[user.id] = true; - return schema.setPermissions("Stuff", { + return schema.setPermissions('Stuff', { find: find, }); }) .then(() => { - const query = new Parse.Query("Stuff"); + const query = new Parse.Query('Stuff'); return query.find(); }) .then( @@ -195,53 +195,53 @@ describe("SchemaController", () => { done(); }, () => { - fail("Class permissions should have allowed this query."); + fail('Class permissions should have allowed this query.'); done(); } ); }); - it("class-level permissions test get", done => { + it('class-level permissions test get', done => { let obj; createTestUser().then(user => { return ( config.database .loadSchema() // Create a valid class - .then(schema => schema.validateObject("Stuff", { foo: "bar" })) + .then(schema => schema.validateObject('Stuff', { foo: 'bar' })) .then(schema => { const find = {}; const get = {}; get[user.id] = true; - return schema.setPermissions("Stuff", { - create: { "*": true }, + return schema.setPermissions('Stuff', { + create: { '*': true }, find: find, get: get, }); }) .then(() => { - obj = new Parse.Object("Stuff"); - obj.set("foo", "bar"); + obj = new Parse.Object('Stuff'); + obj.set('foo', 'bar'); return obj.save(); }) .then(o => { obj = o; - const query = new Parse.Query("Stuff"); + const query = new Parse.Query('Stuff'); return query.find(); }) .then( () => { - fail("Class permissions should have rejected this query."); + fail('Class permissions should have rejected this query.'); done(); }, () => { - const query = new Parse.Query("Stuff"); + const query = new Parse.Query('Stuff'); return query.get(obj.id).then( () => { done(); }, () => { - fail("Class permissions should have allowed this get query"); + fail('Class permissions should have allowed this get query'); done(); } ); @@ -251,43 +251,43 @@ describe("SchemaController", () => { }); }); - it("class-level permissions test count", done => { + it('class-level permissions test count', done => { let obj; return ( config.database .loadSchema() // Create a valid class - .then(schema => schema.validateObject("Stuff", { foo: "bar" })) + .then(schema => schema.validateObject('Stuff', { foo: 'bar' })) .then(schema => { const count = {}; - return schema.setPermissions("Stuff", { - create: { "*": true }, - find: { "*": true }, + return schema.setPermissions('Stuff', { + create: { '*': true }, + find: { '*': true }, count: count, }); }) .then(() => { - obj = new Parse.Object("Stuff"); - obj.set("foo", "bar"); + obj = new Parse.Object('Stuff'); + obj.set('foo', 'bar'); return obj.save(); }) .then(o => { obj = o; - const query = new Parse.Query("Stuff"); + const query = new Parse.Query('Stuff'); return query.find(); }) .then(results => { expect(results.length).toBe(1); - const query = new Parse.Query("Stuff"); + const query = new Parse.Query('Stuff'); return query.count(); }) .then( () => { - fail("Class permissions should have rejected this query."); + fail('Class permissions should have rejected this query.'); }, err => { expect(err.message).toEqual( - "Permission denied for action count on class Stuff." + 'Permission denied for action count on class Stuff.' ); done(); } @@ -295,92 +295,92 @@ describe("SchemaController", () => { ); }); - it("can add classes without needing an object", done => { + it('can add classes without needing an object', done => { config.database .loadSchema() .then(schema => - schema.addClassIfNotExists("NewClass", { - foo: { type: "String" }, + schema.addClassIfNotExists('NewClass', { + foo: { type: 'String' }, }) ) .then(actualSchema => { const expectedSchema = { - className: "NewClass", + className: 'NewClass', fields: { - objectId: { type: "String" }, - updatedAt: { type: "Date" }, - createdAt: { type: "Date" }, - ACL: { type: "ACL" }, - foo: { type: "String" }, + objectId: { type: 'String' }, + updatedAt: { type: 'Date' }, + createdAt: { type: 'Date' }, + ACL: { type: 'ACL' }, + foo: { type: 'String' }, }, classLevelPermissions: { ACL: { - "*": { + '*': { read: true, write: true, }, }, - find: { "*": true }, - get: { "*": true }, - count: { "*": true }, - create: { "*": true }, - update: { "*": true }, - delete: { "*": true }, - addField: { "*": true }, - protectedFields: { "*": [] }, + find: { '*': true }, + get: { '*': true }, + count: { '*': true }, + create: { '*': true }, + update: { '*': true }, + delete: { '*': true }, + addField: { '*': true }, + protectedFields: { '*': [] }, }, }; expect(dd(actualSchema, expectedSchema)).toEqual(undefined); done(); }) .catch(error => { - fail("Error creating class: " + JSON.stringify(error)); + fail('Error creating class: ' + JSON.stringify(error)); }); }); - it("can update classes without needing an object", done => { + it('can update classes without needing an object', done => { const levelPermissions = { ACL: { - "*": { + '*': { read: true, write: true, }, }, - find: { "*": true }, - get: { "*": true }, - count: { "*": true }, - create: { "*": true }, - update: { "*": true }, - delete: { "*": true }, - addField: { "*": true }, - protectedFields: { "*": [] }, + find: { '*': true }, + get: { '*': true }, + count: { '*': true }, + create: { '*': true }, + update: { '*': true }, + delete: { '*': true }, + addField: { '*': true }, + protectedFields: { '*': [] }, }; config.database.loadSchema().then(schema => { schema - .validateObject("NewClass", { foo: 2 }) + .validateObject('NewClass', { foo: 2 }) .then(() => schema.reloadData()) .then(() => schema.updateClass( - "NewClass", + 'NewClass', { - fooOne: { type: "Number" }, - fooTwo: { type: "Array" }, - fooThree: { type: "Date" }, - fooFour: { type: "Object" }, - fooFive: { type: "Relation", targetClass: "_User" }, - fooSix: { type: "String" }, - fooSeven: { type: "Object" }, - fooEight: { type: "String" }, - fooNine: { type: "String" }, - fooTeen: { type: "Number" }, - fooEleven: { type: "String" }, - fooTwelve: { type: "String" }, - fooThirteen: { type: "String" }, - fooFourteen: { type: "String" }, - fooFifteen: { type: "String" }, - fooSixteen: { type: "String" }, - fooEighteen: { type: "String" }, - fooNineteen: { type: "String" }, + fooOne: { type: 'Number' }, + fooTwo: { type: 'Array' }, + fooThree: { type: 'Date' }, + fooFour: { type: 'Object' }, + fooFive: { type: 'Relation', targetClass: '_User' }, + fooSix: { type: 'String' }, + fooSeven: { type: 'Object' }, + fooEight: { type: 'String' }, + fooNine: { type: 'String' }, + fooTeen: { type: 'Number' }, + fooEleven: { type: 'String' }, + fooTwelve: { type: 'String' }, + fooThirteen: { type: 'String' }, + fooFourteen: { type: 'String' }, + fooFifteen: { type: 'String' }, + fooSixteen: { type: 'String' }, + fooEighteen: { type: 'String' }, + fooNineteen: { type: 'String' }, }, levelPermissions, {}, @@ -389,31 +389,31 @@ describe("SchemaController", () => { ) .then(actualSchema => { const expectedSchema = { - className: "NewClass", + className: 'NewClass', fields: { - objectId: { type: "String" }, - updatedAt: { type: "Date" }, - createdAt: { type: "Date" }, - ACL: { type: "ACL" }, - foo: { type: "Number" }, - fooOne: { type: "Number" }, - fooTwo: { type: "Array" }, - fooThree: { type: "Date" }, - fooFour: { type: "Object" }, - fooFive: { type: "Relation", targetClass: "_User" }, - fooSix: { type: "String" }, - fooSeven: { type: "Object" }, - fooEight: { type: "String" }, - fooNine: { type: "String" }, - fooTeen: { type: "Number" }, - fooEleven: { type: "String" }, - fooTwelve: { type: "String" }, - fooThirteen: { type: "String" }, - fooFourteen: { type: "String" }, - fooFifteen: { type: "String" }, - fooSixteen: { type: "String" }, - fooEighteen: { type: "String" }, - fooNineteen: { type: "String" }, + objectId: { type: 'String' }, + updatedAt: { type: 'Date' }, + createdAt: { type: 'Date' }, + ACL: { type: 'ACL' }, + foo: { type: 'Number' }, + fooOne: { type: 'Number' }, + fooTwo: { type: 'Array' }, + fooThree: { type: 'Date' }, + fooFour: { type: 'Object' }, + fooFive: { type: 'Relation', targetClass: '_User' }, + fooSix: { type: 'String' }, + fooSeven: { type: 'Object' }, + fooEight: { type: 'String' }, + fooNine: { type: 'String' }, + fooTeen: { type: 'Number' }, + fooEleven: { type: 'String' }, + fooTwelve: { type: 'String' }, + fooThirteen: { type: 'String' }, + fooFourteen: { type: 'String' }, + fooFifteen: { type: 'String' }, + fooSixteen: { type: 'String' }, + fooEighteen: { type: 'String' }, + fooNineteen: { type: 'String' }, }, classLevelPermissions: { ...levelPermissions }, indexes: { @@ -427,29 +427,29 @@ describe("SchemaController", () => { .catch(error => { console.trace(error); done(); - fail("Error creating class: " + JSON.stringify(error)); + fail('Error creating class: ' + JSON.stringify(error)); }); }); }); - it("can update class level permission", done => { + it('can update class level permission', done => { const newLevelPermissions = { find: {}, - get: { "*": true }, + get: { '*': true }, count: {}, - create: { "*": true }, + create: { '*': true }, update: {}, - delete: { "*": true }, + delete: { '*': true }, addField: {}, - protectedFields: { "*": [] }, + protectedFields: { '*': [] }, }; config.database.loadSchema().then(schema => { schema - .validateObject("NewClass", { foo: 2 }) + .validateObject('NewClass', { foo: 2 }) .then(() => schema.reloadData()) .then(() => schema.updateClass( - "NewClass", + 'NewClass', {}, newLevelPermissions, {}, @@ -465,71 +465,71 @@ describe("SchemaController", () => { .catch(error => { console.trace(error); done(); - fail("Error creating class: " + JSON.stringify(error)); + fail('Error creating class: ' + JSON.stringify(error)); }); }); }); - it("will fail to create a class if that class was already created by an object", done => { + it('will fail to create a class if that class was already created by an object', done => { config.database.loadSchema().then(schema => { schema - .validateObject("NewClass", { foo: 7 }) + .validateObject('NewClass', { foo: 7 }) .then(() => schema.reloadData()) .then(() => - schema.addClassIfNotExists("NewClass", { - foo: { type: "String" }, + schema.addClassIfNotExists('NewClass', { + foo: { type: 'String' }, }) ) .catch(error => { expect(error.code).toEqual(Parse.Error.INVALID_CLASS_NAME); - expect(error.message).toEqual("Class NewClass already exists."); + expect(error.message).toEqual('Class NewClass already exists.'); done(); }); }); }); - it("will resolve class creation races appropriately", done => { + it('will resolve class creation races appropriately', done => { // If two callers race to create the same schema, the response to the // race loser should be the same as if they hadn't been racing. config.database.loadSchema().then(schema => { const p1 = schema - .addClassIfNotExists("NewClass", { - foo: { type: "String" }, + .addClassIfNotExists('NewClass', { + foo: { type: 'String' }, }) .then(validateSchema) .catch(validateError); const p2 = schema - .addClassIfNotExists("NewClass", { - foo: { type: "String" }, + .addClassIfNotExists('NewClass', { + foo: { type: 'String' }, }) .then(validateSchema) .catch(validateError); let schemaValidated = false; function validateSchema(actualSchema) { const expectedSchema = { - className: "NewClass", + className: 'NewClass', fields: { - objectId: { type: "String" }, - updatedAt: { type: "Date" }, - createdAt: { type: "Date" }, - ACL: { type: "ACL" }, - foo: { type: "String" }, + objectId: { type: 'String' }, + updatedAt: { type: 'Date' }, + createdAt: { type: 'Date' }, + ACL: { type: 'ACL' }, + foo: { type: 'String' }, }, classLevelPermissions: { ACL: { - "*": { + '*': { read: true, write: true, }, }, - find: { "*": true }, - get: { "*": true }, - count: { "*": true }, - create: { "*": true }, - update: { "*": true }, - delete: { "*": true }, - addField: { "*": true }, - protectedFields: { "*": [] }, + find: { '*': true }, + get: { '*': true }, + count: { '*': true }, + create: { '*': true }, + update: { '*': true }, + delete: { '*': true }, + addField: { '*': true }, + protectedFields: { '*': [] }, }, }; expect(dd(actualSchema, expectedSchema)).toEqual(undefined); @@ -538,7 +538,7 @@ describe("SchemaController", () => { let errorValidated = false; function validateError(error) { expect(error.code).toEqual(Parse.Error.INVALID_CLASS_NAME); - expect(error.message).toEqual("Class NewClass already exists."); + expect(error.message).toEqual('Class NewClass already exists.'); errorValidated = true; } Promise.all([p1, p2]).then(() => { @@ -549,209 +549,209 @@ describe("SchemaController", () => { }); }); - it("refuses to create classes with invalid names", done => { + it('refuses to create classes with invalid names', done => { config.database.loadSchema().then(schema => { schema - .addClassIfNotExists("_InvalidName", { foo: { type: "String" } }) + .addClassIfNotExists('_InvalidName', { foo: { type: 'String' } }) .catch(error => { expect(error.message).toEqual( - "Invalid classname: _InvalidName, classnames can only have alphanumeric characters and _, and must start with an alpha character " + 'Invalid classname: _InvalidName, classnames can only have alphanumeric characters and _, and must start with an alpha character ' ); done(); }); }); }); - it("refuses to add fields with invalid names", done => { + it('refuses to add fields with invalid names', done => { config.database .loadSchema() .then(schema => - schema.addClassIfNotExists("NewClass", { - "0InvalidName": { type: "String" }, + schema.addClassIfNotExists('NewClass', { + '0InvalidName': { type: 'String' }, }) ) .catch(error => { expect(error.code).toEqual(Parse.Error.INVALID_KEY_NAME); - expect(error.message).toEqual("invalid field name: 0InvalidName"); + expect(error.message).toEqual('invalid field name: 0InvalidName'); done(); }); }); - it("refuses to explicitly create the default fields for custom classes", done => { + it('refuses to explicitly create the default fields for custom classes', done => { config.database .loadSchema() .then(schema => - schema.addClassIfNotExists("NewClass", { objectId: { type: "String" } }) + schema.addClassIfNotExists('NewClass', { objectId: { type: 'String' } }) ) .catch(error => { expect(error.code).toEqual(136); - expect(error.message).toEqual("field objectId cannot be added"); + expect(error.message).toEqual('field objectId cannot be added'); done(); }); }); - it("refuses to explicitly create the default fields for non-custom classes", done => { + it('refuses to explicitly create the default fields for non-custom classes', done => { config.database .loadSchema() .then(schema => - schema.addClassIfNotExists("_Installation", { - localeIdentifier: { type: "String" }, + schema.addClassIfNotExists('_Installation', { + localeIdentifier: { type: 'String' }, }) ) .catch(error => { expect(error.code).toEqual(136); - expect(error.message).toEqual("field localeIdentifier cannot be added"); + expect(error.message).toEqual('field localeIdentifier cannot be added'); done(); }); }); - it("refuses to add fields with invalid types", done => { + it('refuses to add fields with invalid types', done => { config.database .loadSchema() .then(schema => - schema.addClassIfNotExists("NewClass", { + schema.addClassIfNotExists('NewClass', { foo: { type: 7 }, }) ) .catch(error => { expect(error.code).toEqual(Parse.Error.INVALID_JSON); - expect(error.message).toEqual("invalid JSON"); + expect(error.message).toEqual('invalid JSON'); done(); }); }); - it("refuses to add fields with invalid pointer types", done => { + it('refuses to add fields with invalid pointer types', done => { config.database .loadSchema() .then(schema => - schema.addClassIfNotExists("NewClass", { - foo: { type: "Pointer" }, + schema.addClassIfNotExists('NewClass', { + foo: { type: 'Pointer' }, }) ) .catch(error => { expect(error.code).toEqual(135); - expect(error.message).toEqual("type Pointer needs a class name"); + expect(error.message).toEqual('type Pointer needs a class name'); done(); }); }); - it("refuses to add fields with invalid pointer target", done => { + it('refuses to add fields with invalid pointer target', done => { config.database .loadSchema() .then(schema => - schema.addClassIfNotExists("NewClass", { - foo: { type: "Pointer", targetClass: 7 }, + schema.addClassIfNotExists('NewClass', { + foo: { type: 'Pointer', targetClass: 7 }, }) ) .catch(error => { expect(error.code).toEqual(Parse.Error.INVALID_JSON); - expect(error.message).toEqual("invalid JSON"); + expect(error.message).toEqual('invalid JSON'); done(); }); }); - it("refuses to add fields with invalid Relation type", done => { + it('refuses to add fields with invalid Relation type', done => { config.database .loadSchema() .then(schema => - schema.addClassIfNotExists("NewClass", { - foo: { type: "Relation", uselessKey: 7 }, + schema.addClassIfNotExists('NewClass', { + foo: { type: 'Relation', uselessKey: 7 }, }) ) .catch(error => { expect(error.code).toEqual(135); - expect(error.message).toEqual("type Relation needs a class name"); + expect(error.message).toEqual('type Relation needs a class name'); done(); }); }); - it("refuses to add fields with invalid relation target", done => { + it('refuses to add fields with invalid relation target', done => { config.database .loadSchema() .then(schema => - schema.addClassIfNotExists("NewClass", { - foo: { type: "Relation", targetClass: 7 }, + schema.addClassIfNotExists('NewClass', { + foo: { type: 'Relation', targetClass: 7 }, }) ) .catch(error => { expect(error.code).toEqual(Parse.Error.INVALID_JSON); - expect(error.message).toEqual("invalid JSON"); + expect(error.message).toEqual('invalid JSON'); done(); }); }); - it("refuses to add fields with uncreatable pointer target class", done => { + it('refuses to add fields with uncreatable pointer target class', done => { config.database .loadSchema() .then(schema => - schema.addClassIfNotExists("NewClass", { - foo: { type: "Pointer", targetClass: "not a valid class name" }, + schema.addClassIfNotExists('NewClass', { + foo: { type: 'Pointer', targetClass: 'not a valid class name' }, }) ) .catch(error => { expect(error.code).toEqual(Parse.Error.INVALID_CLASS_NAME); expect(error.message).toEqual( - "Invalid classname: not a valid class name, classnames can only have alphanumeric characters and _, and must start with an alpha character " + 'Invalid classname: not a valid class name, classnames can only have alphanumeric characters and _, and must start with an alpha character ' ); done(); }); }); - it("refuses to add fields with uncreatable relation target class", done => { + it('refuses to add fields with uncreatable relation target class', done => { config.database .loadSchema() .then(schema => - schema.addClassIfNotExists("NewClass", { - foo: { type: "Relation", targetClass: "not a valid class name" }, + schema.addClassIfNotExists('NewClass', { + foo: { type: 'Relation', targetClass: 'not a valid class name' }, }) ) .catch(error => { expect(error.code).toEqual(Parse.Error.INVALID_CLASS_NAME); expect(error.message).toEqual( - "Invalid classname: not a valid class name, classnames can only have alphanumeric characters and _, and must start with an alpha character " + 'Invalid classname: not a valid class name, classnames can only have alphanumeric characters and _, and must start with an alpha character ' ); done(); }); }); - it("refuses to add fields with unknown types", done => { + it('refuses to add fields with unknown types', done => { config.database .loadSchema() .then(schema => - schema.addClassIfNotExists("NewClass", { - foo: { type: "Unknown" }, + schema.addClassIfNotExists('NewClass', { + foo: { type: 'Unknown' }, }) ) .catch(error => { expect(error.code).toEqual(Parse.Error.INCORRECT_TYPE); - expect(error.message).toEqual("invalid field type: Unknown"); + expect(error.message).toEqual('invalid field type: Unknown'); done(); }); }); - it("refuses to add CLP with incorrect find", done => { + it('refuses to add CLP with incorrect find', done => { const levelPermissions = { ACL: { - "*": { + '*': { read: true, write: true, }, }, - find: { "*": false }, - get: { "*": true }, - create: { "*": true }, - update: { "*": true }, - delete: { "*": true }, - addField: { "*": true }, - protectedFields: { "*": ["email"] }, + find: { '*': false }, + get: { '*': true }, + create: { '*': true }, + update: { '*': true }, + delete: { '*': true }, + addField: { '*': true }, + protectedFields: { '*': ['email'] }, }; config.database.loadSchema().then(schema => { schema - .validateObject("NewClass", {}) + .validateObject('NewClass', {}) .then(() => schema.reloadData()) .then(() => schema.updateClass( - "NewClass", + 'NewClass', {}, levelPermissions, {}, @@ -766,29 +766,29 @@ describe("SchemaController", () => { }); }); - it("refuses to add CLP when incorrectly sending a string to protectedFields object value instead of an array", done => { + it('refuses to add CLP when incorrectly sending a string to protectedFields object value instead of an array', done => { const levelPermissions = { ACL: { - "*": { + '*': { read: true, write: true, }, }, - find: { "*": true }, - get: { "*": true }, - create: { "*": true }, - update: { "*": true }, - delete: { "*": true }, - addField: { "*": true }, - protectedFields: { "*": "email" }, + find: { '*': true }, + get: { '*': true }, + create: { '*': true }, + update: { '*': true }, + delete: { '*': true }, + addField: { '*': true }, + protectedFields: { '*': 'email' }, }; config.database.loadSchema().then(schema => { schema - .validateObject("NewClass", {}) + .validateObject('NewClass', {}) .then(() => schema.reloadData()) .then(() => schema.updateClass( - "NewClass", + 'NewClass', {}, levelPermissions, {}, @@ -803,67 +803,67 @@ describe("SchemaController", () => { }); }); - it("will create classes", done => { + it('will create classes', done => { config.database .loadSchema() .then(schema => - schema.addClassIfNotExists("NewClass", { - aNumber: { type: "Number" }, - aString: { type: "String" }, - aBool: { type: "Boolean" }, - aDate: { type: "Date" }, - aObject: { type: "Object" }, - aArray: { type: "Array" }, - aGeoPoint: { type: "GeoPoint" }, - aFile: { type: "File" }, + schema.addClassIfNotExists('NewClass', { + aNumber: { type: 'Number' }, + aString: { type: 'String' }, + aBool: { type: 'Boolean' }, + aDate: { type: 'Date' }, + aObject: { type: 'Object' }, + aArray: { type: 'Array' }, + aGeoPoint: { type: 'GeoPoint' }, + aFile: { type: 'File' }, aPointer: { - type: "Pointer", - targetClass: "ThisClassDoesNotExistYet", + type: 'Pointer', + targetClass: 'ThisClassDoesNotExistYet', }, - aRelation: { type: "Relation", targetClass: "NewClass" }, - aBytes: { type: "Bytes" }, - aPolygon: { type: "Polygon" }, + aRelation: { type: 'Relation', targetClass: 'NewClass' }, + aBytes: { type: 'Bytes' }, + aPolygon: { type: 'Polygon' }, }) ) .then(actualSchema => { const expectedSchema = { - className: "NewClass", + className: 'NewClass', fields: { - objectId: { type: "String" }, - updatedAt: { type: "Date" }, - createdAt: { type: "Date" }, - ACL: { type: "ACL" }, - aString: { type: "String" }, - aNumber: { type: "Number" }, - aBool: { type: "Boolean" }, - aDate: { type: "Date" }, - aObject: { type: "Object" }, - aArray: { type: "Array" }, - aGeoPoint: { type: "GeoPoint" }, - aFile: { type: "File" }, + objectId: { type: 'String' }, + updatedAt: { type: 'Date' }, + createdAt: { type: 'Date' }, + ACL: { type: 'ACL' }, + aString: { type: 'String' }, + aNumber: { type: 'Number' }, + aBool: { type: 'Boolean' }, + aDate: { type: 'Date' }, + aObject: { type: 'Object' }, + aArray: { type: 'Array' }, + aGeoPoint: { type: 'GeoPoint' }, + aFile: { type: 'File' }, aPointer: { - type: "Pointer", - targetClass: "ThisClassDoesNotExistYet", + type: 'Pointer', + targetClass: 'ThisClassDoesNotExistYet', }, - aRelation: { type: "Relation", targetClass: "NewClass" }, - aBytes: { type: "Bytes" }, - aPolygon: { type: "Polygon" }, + aRelation: { type: 'Relation', targetClass: 'NewClass' }, + aBytes: { type: 'Bytes' }, + aPolygon: { type: 'Polygon' }, }, classLevelPermissions: { ACL: { - "*": { + '*': { read: true, write: true, }, }, - find: { "*": true }, - get: { "*": true }, - count: { "*": true }, - create: { "*": true }, - update: { "*": true }, - delete: { "*": true }, - addField: { "*": true }, - protectedFields: { "*": [] }, + find: { '*': true }, + get: { '*': true }, + count: { '*': true }, + create: { '*': true }, + update: { '*': true }, + delete: { '*': true }, + addField: { '*': true }, + protectedFields: { '*': [] }, }, }; expect(dd(actualSchema, expectedSchema)).toEqual(undefined); @@ -871,52 +871,52 @@ describe("SchemaController", () => { }); }); - it("creates the default fields for non-custom classes", done => { + it('creates the default fields for non-custom classes', done => { config.database .loadSchema() .then(schema => - schema.addClassIfNotExists("_Installation", { - foo: { type: "Number" }, + schema.addClassIfNotExists('_Installation', { + foo: { type: 'Number' }, }) ) .then(actualSchema => { const expectedSchema = { - className: "_Installation", + className: '_Installation', fields: { - objectId: { type: "String" }, - updatedAt: { type: "Date" }, - createdAt: { type: "Date" }, - ACL: { type: "ACL" }, - foo: { type: "Number" }, - installationId: { type: "String" }, - deviceToken: { type: "String" }, - channels: { type: "Array" }, - deviceType: { type: "String" }, - pushType: { type: "String" }, - GCMSenderId: { type: "String" }, - timeZone: { type: "String" }, - localeIdentifier: { type: "String" }, - badge: { type: "Number" }, - appVersion: { type: "String" }, - appName: { type: "String" }, - appIdentifier: { type: "String" }, - parseVersion: { type: "String" }, + objectId: { type: 'String' }, + updatedAt: { type: 'Date' }, + createdAt: { type: 'Date' }, + ACL: { type: 'ACL' }, + foo: { type: 'Number' }, + installationId: { type: 'String' }, + deviceToken: { type: 'String' }, + channels: { type: 'Array' }, + deviceType: { type: 'String' }, + pushType: { type: 'String' }, + GCMSenderId: { type: 'String' }, + timeZone: { type: 'String' }, + localeIdentifier: { type: 'String' }, + badge: { type: 'Number' }, + appVersion: { type: 'String' }, + appName: { type: 'String' }, + appIdentifier: { type: 'String' }, + parseVersion: { type: 'String' }, }, classLevelPermissions: { ACL: { - "*": { + '*': { read: true, write: true, }, }, - find: { "*": true }, - get: { "*": true }, - count: { "*": true }, - create: { "*": true }, - update: { "*": true }, - delete: { "*": true }, - addField: { "*": true }, - protectedFields: { "*": [] }, + find: { '*': true }, + get: { '*': true }, + count: { '*': true }, + create: { '*': true }, + update: { '*': true }, + delete: { '*': true }, + addField: { '*': true }, + protectedFields: { '*': [] }, }, }; expect(dd(actualSchema, expectedSchema)).toEqual(undefined); @@ -924,39 +924,39 @@ describe("SchemaController", () => { }); }); - it("creates non-custom classes which include relation field", async done => { + it('creates non-custom classes which include relation field', async done => { await reconfigureServer(); config.database .loadSchema() //as `_Role` is always created by default, we only get it here - .then(schema => schema.getOneSchema("_Role")) + .then(schema => schema.getOneSchema('_Role')) .then(actualSchema => { const expectedSchema = { - className: "_Role", + className: '_Role', fields: { - objectId: { type: "String" }, - updatedAt: { type: "Date" }, - createdAt: { type: "Date" }, - ACL: { type: "ACL" }, - name: { type: "String" }, - users: { type: "Relation", targetClass: "_User" }, - roles: { type: "Relation", targetClass: "_Role" }, + objectId: { type: 'String' }, + updatedAt: { type: 'Date' }, + createdAt: { type: 'Date' }, + ACL: { type: 'ACL' }, + name: { type: 'String' }, + users: { type: 'Relation', targetClass: '_User' }, + roles: { type: 'Relation', targetClass: '_Role' }, }, classLevelPermissions: { ACL: { - "*": { + '*': { read: true, write: true, }, }, - find: { "*": true }, - get: { "*": true }, - count: { "*": true }, - create: { "*": true }, - update: { "*": true }, - delete: { "*": true }, - addField: { "*": true }, - protectedFields: { "*": [] }, + find: { '*': true }, + get: { '*': true }, + count: { '*': true }, + create: { '*': true }, + update: { '*': true }, + delete: { '*': true }, + addField: { '*': true }, + protectedFields: { '*': [] }, }, }; expect(dd(actualSchema, expectedSchema)).toEqual(undefined); @@ -964,39 +964,39 @@ describe("SchemaController", () => { }); }); - it("creates non-custom classes which include pointer field", done => { + it('creates non-custom classes which include pointer field', done => { config.database .loadSchema() - .then(schema => schema.addClassIfNotExists("_Session", {})) + .then(schema => schema.addClassIfNotExists('_Session', {})) .then(actualSchema => { const expectedSchema = { - className: "_Session", + className: '_Session', fields: { - objectId: { type: "String" }, - updatedAt: { type: "Date" }, - createdAt: { type: "Date" }, - user: { type: "Pointer", targetClass: "_User" }, - installationId: { type: "String" }, - sessionToken: { type: "String" }, - expiresAt: { type: "Date" }, - createdWith: { type: "Object" }, - ACL: { type: "ACL" }, + objectId: { type: 'String' }, + updatedAt: { type: 'Date' }, + createdAt: { type: 'Date' }, + user: { type: 'Pointer', targetClass: '_User' }, + installationId: { type: 'String' }, + sessionToken: { type: 'String' }, + expiresAt: { type: 'Date' }, + createdWith: { type: 'Object' }, + ACL: { type: 'ACL' }, }, classLevelPermissions: { ACL: { - "*": { + '*': { read: true, write: true, }, }, - find: { "*": true }, - get: { "*": true }, - count: { "*": true }, - create: { "*": true }, - update: { "*": true }, - delete: { "*": true }, - addField: { "*": true }, - protectedFields: { "*": [] }, + find: { '*': true }, + get: { '*': true }, + count: { '*': true }, + create: { '*': true }, + update: { '*': true }, + delete: { '*': true }, + addField: { '*': true }, + protectedFields: { '*': [] }, }, }; expect(dd(actualSchema, expectedSchema)).toEqual(undefined); @@ -1004,34 +1004,34 @@ describe("SchemaController", () => { }); }); - it("refuses to create two geopoints", done => { + it('refuses to create two geopoints', done => { config.database .loadSchema() .then(schema => - schema.addClassIfNotExists("NewClass", { - geo1: { type: "GeoPoint" }, - geo2: { type: "GeoPoint" }, + schema.addClassIfNotExists('NewClass', { + geo1: { type: 'GeoPoint' }, + geo2: { type: 'GeoPoint' }, }) ) .catch(error => { expect(error.code).toEqual(Parse.Error.INCORRECT_TYPE); expect(error.message).toEqual( - "currently, only one GeoPoint field may exist in an object. Adding geo2 when geo1 already exists." + 'currently, only one GeoPoint field may exist in an object. Adding geo2 when geo1 already exists.' ); done(); }); }); - it("can check if a class exists", done => { + it('can check if a class exists', done => { config.database .loadSchema() .then(schema => { return schema - .addClassIfNotExists("NewClass", {}) + .addClassIfNotExists('NewClass', {}) .then(() => schema.reloadData({ clearCache: true })) .then(() => { schema - .hasClass("NewClass") + .hasClass('NewClass') .then(hasClass => { expect(hasClass).toEqual(true); done(); @@ -1039,7 +1039,7 @@ describe("SchemaController", () => { .catch(fail); schema - .hasClass("NonexistantClass") + .hasClass('NonexistantClass') .then(hasClass => { expect(hasClass).toEqual(false); done(); @@ -1054,21 +1054,21 @@ describe("SchemaController", () => { .catch(() => fail("Couldn't load schema")); }); - it("refuses to delete fields from invalid class names", done => { + it('refuses to delete fields from invalid class names', done => { config.database .loadSchema() - .then(schema => schema.deleteField("fieldName", "invalid class name")) + .then(schema => schema.deleteField('fieldName', 'invalid class name')) .catch(error => { expect(error.code).toEqual(Parse.Error.INVALID_CLASS_NAME); done(); }); }); - it("refuses to delete invalid fields", done => { + it('refuses to delete invalid fields', done => { config.database .loadSchema() .then(schema => - schema.deleteField("invalid field name", "ValidClassName") + schema.deleteField('invalid field name', 'ValidClassName') ) .catch(error => { expect(error.code).toEqual(Parse.Error.INVALID_KEY_NAME); @@ -1076,81 +1076,81 @@ describe("SchemaController", () => { }); }); - it("refuses to delete the default fields", done => { + it('refuses to delete the default fields', done => { config.database .loadSchema() - .then(schema => schema.deleteField("installationId", "_Installation")) + .then(schema => schema.deleteField('installationId', '_Installation')) .catch(error => { expect(error.code).toEqual(136); - expect(error.message).toEqual("field installationId cannot be changed"); + expect(error.message).toEqual('field installationId cannot be changed'); done(); }); }); - it("refuses to delete fields from nonexistant classes", done => { + it('refuses to delete fields from nonexistant classes', done => { config.database .loadSchema() - .then(schema => schema.deleteField("field", "NoClass")) + .then(schema => schema.deleteField('field', 'NoClass')) .catch(error => { expect(error.code).toEqual(Parse.Error.INVALID_CLASS_NAME); - expect(error.message).toEqual("Class NoClass does not exist."); + expect(error.message).toEqual('Class NoClass does not exist.'); done(); }); }); - it("refuses to delete fields that dont exist", done => { + it('refuses to delete fields that dont exist', done => { hasAllPODobject() .save() .then(() => config.database.loadSchema()) - .then(schema => schema.deleteField("missingField", "HasAllPOD")) + .then(schema => schema.deleteField('missingField', 'HasAllPOD')) .catch(error => { expect(error.code).toEqual(255); expect(error.message).toEqual( - "Field missingField does not exist, cannot delete." + 'Field missingField does not exist, cannot delete.' ); done(); }); }); - it("drops related collection when deleting relation field", done => { + it('drops related collection when deleting relation field', done => { const obj1 = hasAllPODobject(); obj1 .save() .then(savedObj1 => { - const obj2 = new Parse.Object("HasPointersAndRelations"); - obj2.set("aPointer", savedObj1); - const relation = obj2.relation("aRelation"); + const obj2 = new Parse.Object('HasPointersAndRelations'); + obj2.set('aPointer', savedObj1); + const relation = obj2.relation('aRelation'); relation.add(obj1); return obj2.save(); }) .then(() => config.database.collectionExists( - "_Join:aRelation:HasPointersAndRelations" + '_Join:aRelation:HasPointersAndRelations' ) ) .then(exists => { if (!exists) { - fail("Relation collection " + "should exist after save."); + fail('Relation collection ' + 'should exist after save.'); } }) .then(() => config.database.loadSchema()) .then(schema => schema.deleteField( - "aRelation", - "HasPointersAndRelations", + 'aRelation', + 'HasPointersAndRelations', config.database ) ) .then(() => config.database.collectionExists( - "_Join:aRelation:HasPointersAndRelations" + '_Join:aRelation:HasPointersAndRelations' ) ) .then( exists => { if (exists) { fail( - "Relation collection should not exist after deleting relation field." + 'Relation collection should not exist after deleting relation field.' ); } done(); @@ -1162,47 +1162,47 @@ describe("SchemaController", () => { ); }); - it("can delete relation field when related _Join collection not exist", done => { + it('can delete relation field when related _Join collection not exist', done => { config.database.loadSchema().then(schema => { schema - .addClassIfNotExists("NewClass", { - relationField: { type: "Relation", targetClass: "_User" }, + .addClassIfNotExists('NewClass', { + relationField: { type: 'Relation', targetClass: '_User' }, }) .then(actualSchema => { const expectedSchema = { - className: "NewClass", + className: 'NewClass', fields: { - objectId: { type: "String" }, - updatedAt: { type: "Date" }, - createdAt: { type: "Date" }, - ACL: { type: "ACL" }, - relationField: { type: "Relation", targetClass: "_User" }, + objectId: { type: 'String' }, + updatedAt: { type: 'Date' }, + createdAt: { type: 'Date' }, + ACL: { type: 'ACL' }, + relationField: { type: 'Relation', targetClass: '_User' }, }, classLevelPermissions: { ACL: { - "*": { + '*': { read: true, write: true, }, }, - find: { "*": true }, - get: { "*": true }, - count: { "*": true }, - create: { "*": true }, - update: { "*": true }, - delete: { "*": true }, - addField: { "*": true }, - protectedFields: { "*": [] }, + find: { '*': true }, + get: { '*': true }, + count: { '*': true }, + create: { '*': true }, + update: { '*': true }, + delete: { '*': true }, + addField: { '*': true }, + protectedFields: { '*': [] }, }, }; expect(dd(actualSchema, expectedSchema)).toEqual(undefined); }) .then(() => - config.database.collectionExists("_Join:relationField:NewClass") + config.database.collectionExists('_Join:relationField:NewClass') ) .then(exist => { on_db( - "postgres", + 'postgres', () => { // We create the table when creating the column expect(exist).toEqual(true); @@ -1213,15 +1213,15 @@ describe("SchemaController", () => { ); }) .then(() => - schema.deleteField("relationField", "NewClass", config.database) + schema.deleteField('relationField', 'NewClass', config.database) ) .then(() => schema.reloadData()) .then(() => { const expectedSchema = { - objectId: { type: "String" }, - updatedAt: { type: "Date" }, - createdAt: { type: "Date" }, - ACL: { type: "ACL" }, + objectId: { type: 'String' }, + updatedAt: { type: 'Date' }, + createdAt: { type: 'Date' }, + ACL: { type: 'ACL' }, }; expect(dd(schema.schemaData.NewClass.fields, expectedSchema)).toEqual( undefined @@ -1232,30 +1232,30 @@ describe("SchemaController", () => { }); }); - it("can delete string fields and resave as number field", done => { + it('can delete string fields and resave as number field', done => { Parse.Object.disableSingleInstance(); const obj1 = hasAllPODobject(); const obj2 = hasAllPODobject(); Parse.Object.saveAll([obj1, obj2]) .then(() => config.database.loadSchema()) .then(schema => - schema.deleteField("aString", "HasAllPOD", config.database) + schema.deleteField('aString', 'HasAllPOD', config.database) ) - .then(() => new Parse.Query("HasAllPOD").get(obj1.id)) + .then(() => new Parse.Query('HasAllPOD').get(obj1.id)) .then(obj1Reloaded => { - expect(obj1Reloaded.get("aString")).toEqual(undefined); - obj1Reloaded.set("aString", ["not a string", "this time"]); + expect(obj1Reloaded.get('aString')).toEqual(undefined); + obj1Reloaded.set('aString', ['not a string', 'this time']); obj1Reloaded .save() .then(obj1reloadedAgain => { - expect(obj1reloadedAgain.get("aString")).toEqual([ - "not a string", - "this time", + expect(obj1reloadedAgain.get('aString')).toEqual([ + 'not a string', + 'this time', ]); - return new Parse.Query("HasAllPOD").get(obj2.id); + return new Parse.Query('HasAllPOD').get(obj2.id); }) .then(obj2reloaded => { - expect(obj2reloaded.get("aString")).toEqual(undefined); + expect(obj2reloaded.get('aString')).toEqual(undefined); done(); }); }) @@ -1265,200 +1265,200 @@ describe("SchemaController", () => { }); }); - it("can delete pointer fields and resave as string", done => { + it('can delete pointer fields and resave as string', done => { Parse.Object.disableSingleInstance(); - const obj1 = new Parse.Object("NewClass"); + const obj1 = new Parse.Object('NewClass'); obj1 .save() .then(() => { - obj1.set("aPointer", obj1); + obj1.set('aPointer', obj1); return obj1.save(); }) .then(obj1 => { - expect(obj1.get("aPointer").id).toEqual(obj1.id); + expect(obj1.get('aPointer').id).toEqual(obj1.id); }) .then(() => config.database.loadSchema()) .then(schema => - schema.deleteField("aPointer", "NewClass", config.database) + schema.deleteField('aPointer', 'NewClass', config.database) ) - .then(() => new Parse.Query("NewClass").get(obj1.id)) + .then(() => new Parse.Query('NewClass').get(obj1.id)) .then(obj1 => { - expect(obj1.get("aPointer")).toEqual(undefined); - obj1.set("aPointer", "Now a string"); + expect(obj1.get('aPointer')).toEqual(undefined); + obj1.set('aPointer', 'Now a string'); return obj1.save(); }) .then(obj1 => { - expect(obj1.get("aPointer")).toEqual("Now a string"); + expect(obj1.get('aPointer')).toEqual('Now a string'); done(); }); }); - it("can merge schemas", done => { + it('can merge schemas', done => { expect( SchemaController.buildMergedSchemaObject( { - _id: "SomeClass", - someType: { type: "Number" }, + _id: 'SomeClass', + someType: { type: 'Number' }, }, { - newType: { type: "Number" }, + newType: { type: 'Number' }, } ) ).toEqual({ - someType: { type: "Number" }, - newType: { type: "Number" }, + someType: { type: 'Number' }, + newType: { type: 'Number' }, }); done(); }); - it("can merge deletions", done => { + it('can merge deletions', done => { expect( SchemaController.buildMergedSchemaObject( { - _id: "SomeClass", - someType: { type: "Number" }, - outDatedType: { type: "String" }, + _id: 'SomeClass', + someType: { type: 'Number' }, + outDatedType: { type: 'String' }, }, { - newType: { type: "GeoPoint" }, - outDatedType: { __op: "Delete" }, + newType: { type: 'GeoPoint' }, + outDatedType: { __op: 'Delete' }, } ) ).toEqual({ - someType: { type: "Number" }, - newType: { type: "GeoPoint" }, + someType: { type: 'Number' }, + newType: { type: 'GeoPoint' }, }); done(); }); - it("ignore default field when merge with system class", done => { + it('ignore default field when merge with system class', done => { expect( SchemaController.buildMergedSchemaObject( { - _id: "_User", - username: { type: "String" }, - password: { type: "String" }, - email: { type: "String" }, - emailVerified: { type: "Boolean" }, + _id: '_User', + username: { type: 'String' }, + password: { type: 'String' }, + email: { type: 'String' }, + emailVerified: { type: 'Boolean' }, }, { - emailVerified: { type: "String" }, - customField: { type: "String" }, + emailVerified: { type: 'String' }, + customField: { type: 'String' }, } ) ).toEqual({ - customField: { type: "String" }, + customField: { type: 'String' }, }); done(); }); - it("yields a proper schema mismatch error (#2661)", done => { - const anObject = new Parse.Object("AnObject"); - const anotherObject = new Parse.Object("AnotherObject"); - const someObject = new Parse.Object("SomeObject"); + it('yields a proper schema mismatch error (#2661)', done => { + const anObject = new Parse.Object('AnObject'); + const anotherObject = new Parse.Object('AnotherObject'); + const someObject = new Parse.Object('SomeObject'); Parse.Object.saveAll([anObject, anotherObject, someObject]) .then(() => { - anObject.set("pointer", anotherObject); + anObject.set('pointer', anotherObject); return anObject.save(); }) .then(() => { - anObject.set("pointer", someObject); + anObject.set('pointer', someObject); return anObject.save(); }) .then( () => { - fail("shoud not save correctly"); + fail('shoud not save correctly'); done(); }, err => { expect(err instanceof Parse.Error).toBeTruthy(); expect(err.message).toEqual( - "schema mismatch for AnObject.pointer; expected Pointer but got Pointer" + 'schema mismatch for AnObject.pointer; expected Pointer but got Pointer' ); done(); } ); }); - it("yields a proper schema mismatch error bis (#2661)", done => { - const anObject = new Parse.Object("AnObject"); - const someObject = new Parse.Object("SomeObject"); + it('yields a proper schema mismatch error bis (#2661)', done => { + const anObject = new Parse.Object('AnObject'); + const someObject = new Parse.Object('SomeObject'); Parse.Object.saveAll([anObject, someObject]) .then(() => { - anObject.set("number", 1); + anObject.set('number', 1); return anObject.save(); }) .then(() => { - anObject.set("number", someObject); + anObject.set('number', someObject); return anObject.save(); }) .then( () => { - fail("shoud not save correctly"); + fail('shoud not save correctly'); done(); }, err => { expect(err instanceof Parse.Error).toBeTruthy(); expect(err.message).toEqual( - "schema mismatch for AnObject.number; expected Number but got Pointer" + 'schema mismatch for AnObject.number; expected Number but got Pointer' ); done(); } ); }); - it("yields a proper schema mismatch error ter (#2661)", done => { - const anObject = new Parse.Object("AnObject"); - const someObject = new Parse.Object("SomeObject"); + it('yields a proper schema mismatch error ter (#2661)', done => { + const anObject = new Parse.Object('AnObject'); + const someObject = new Parse.Object('SomeObject'); Parse.Object.saveAll([anObject, someObject]) .then(() => { - anObject.set("pointer", someObject); + anObject.set('pointer', someObject); return anObject.save(); }) .then(() => { - anObject.set("pointer", 1); + anObject.set('pointer', 1); return anObject.save(); }) .then( () => { - fail("shoud not save correctly"); + fail('shoud not save correctly'); done(); }, err => { expect(err instanceof Parse.Error).toBeTruthy(); expect(err.message).toEqual( - "schema mismatch for AnObject.pointer; expected Pointer but got Number" + 'schema mismatch for AnObject.pointer; expected Pointer but got Number' ); done(); } ); }); - it("properly handles volatile _Schemas", async done => { + it('properly handles volatile _Schemas', async done => { await reconfigureServer(); function validateSchemaStructure(schema) { - expect(Object.prototype.hasOwnProperty.call(schema, "className")).toBe( + expect(Object.prototype.hasOwnProperty.call(schema, 'className')).toBe( true ); - expect(Object.prototype.hasOwnProperty.call(schema, "fields")).toBe(true); + expect(Object.prototype.hasOwnProperty.call(schema, 'fields')).toBe(true); expect( - Object.prototype.hasOwnProperty.call(schema, "classLevelPermissions") + Object.prototype.hasOwnProperty.call(schema, 'classLevelPermissions') ).toBe(true); } function validateSchemaDataStructure(schemaData) { Object.keys(schemaData).forEach(className => { const schema = schemaData[className]; // Hooks has className... - if (className != "_Hooks") { + if (className != '_Hooks') { expect( - Object.prototype.hasOwnProperty.call(schema, "className") + Object.prototype.hasOwnProperty.call(schema, 'className') ).toBe(false); } - expect(Object.prototype.hasOwnProperty.call(schema, "fields")).toBe( + expect(Object.prototype.hasOwnProperty.call(schema, 'fields')).toBe( false ); expect( - Object.prototype.hasOwnProperty.call(schema, "classLevelPermissions") + Object.prototype.hasOwnProperty.call(schema, 'classLevelPermissions') ).toBe(false); }); } @@ -1467,12 +1467,12 @@ describe("SchemaController", () => { .loadSchema() .then(s => { schema = s; - return schema.getOneSchema("_User", false); + return schema.getOneSchema('_User', false); }) .then(userSchema => { validateSchemaStructure(userSchema); validateSchemaDataStructure(schema.schemaData); - return schema.getOneSchema("_PushStatus", true); + return schema.getOneSchema('_PushStatus', true); }) .then(pushStatusSchema => { validateSchemaStructure(pushStatusSchema); @@ -1482,85 +1482,85 @@ describe("SchemaController", () => { .catch(done.fail); }); - it("should not throw on null field types", async () => { + it('should not throw on null field types', async () => { const schema = await config.database.loadSchema(); const result = await schema.enforceFieldExists( - "NewClass", - "fieldName", + 'NewClass', + 'fieldName', null ); expect(result).toBeUndefined(); }); - it("ensureFields should throw when schema is not set", async () => { + it('ensureFields should throw when schema is not set', async () => { const schema = await config.database.loadSchema(); try { schema.ensureFields([ { - className: "NewClass", - fieldName: "fieldName", - type: "String", + className: 'NewClass', + fieldName: 'fieldName', + type: 'String', }, ]); } catch (e) { - expect(e.message).toBe("Could not add field fieldName"); + expect(e.message).toBe('Could not add field fieldName'); } }); }); -describe("Class Level Permissions for requiredAuth", () => { +describe('Class Level Permissions for requiredAuth', () => { beforeEach(() => { - config = Config.get("test"); + config = Config.get('test'); }); function createUser() { const user = new Parse.User(); - user.set("username", "hello"); - user.set("password", "world"); + user.set('username', 'hello'); + user.set('password', 'world'); return user.signUp(null); } - it("required auth test find", done => { + it('required auth test find', done => { config.database .loadSchema() .then(schema => { // Just to create a valid class - return schema.validateObject("Stuff", { foo: "bar" }); + return schema.validateObject('Stuff', { foo: 'bar' }); }) .then(schema => { - return schema.setPermissions("Stuff", { + return schema.setPermissions('Stuff', { find: { requiresAuthentication: true, }, }); }) .then(() => { - const query = new Parse.Query("Stuff"); + const query = new Parse.Query('Stuff'); return query.find(); }) .then( () => { - fail("Class permissions should have rejected this query."); + fail('Class permissions should have rejected this query.'); done(); }, e => { expect(e.message).toEqual( - "Permission denied, user needs to be authenticated." + 'Permission denied, user needs to be authenticated.' ); done(); } ); }); - it("required auth test find authenticated", done => { + it('required auth test find authenticated', done => { config.database .loadSchema() .then(schema => { // Just to create a valid class - return schema.validateObject("Stuff", { foo: "bar" }); + return schema.validateObject('Stuff', { foo: 'bar' }); }) .then(schema => { - return schema.setPermissions("Stuff", { + return schema.setPermissions('Stuff', { find: { requiresAuthentication: true, }, @@ -1570,7 +1570,7 @@ describe("Class Level Permissions for requiredAuth", () => { return createUser(); }) .then(() => { - const query = new Parse.Query("Stuff"); + const query = new Parse.Query('Stuff'); return query.find(); }) .then( @@ -1580,21 +1580,21 @@ describe("Class Level Permissions for requiredAuth", () => { }, e => { console.error(e); - fail("Should not have failed"); + fail('Should not have failed'); done(); } ); }); - it("required auth should allow create authenticated", done => { + it('required auth should allow create authenticated', done => { config.database .loadSchema() .then(schema => { // Just to create a valid class - return schema.validateObject("Stuff", { foo: "bar" }); + return schema.validateObject('Stuff', { foo: 'bar' }); }) .then(schema => { - return schema.setPermissions("Stuff", { + return schema.setPermissions('Stuff', { create: { requiresAuthentication: true, }, @@ -1604,8 +1604,8 @@ describe("Class Level Permissions for requiredAuth", () => { return createUser(); }) .then(() => { - const stuff = new Parse.Object("Stuff"); - stuff.set("foo", "bar"); + const stuff = new Parse.Object('Stuff'); + stuff.set('foo', 'bar'); return stuff.save(); }) .then( @@ -1614,54 +1614,54 @@ describe("Class Level Permissions for requiredAuth", () => { }, e => { console.error(e); - fail("Should not have failed"); + fail('Should not have failed'); done(); } ); }); - it("required auth should reject create when not authenticated", done => { + it('required auth should reject create when not authenticated', done => { config.database .loadSchema() .then(schema => { // Just to create a valid class - return schema.validateObject("Stuff", { foo: "bar" }); + return schema.validateObject('Stuff', { foo: 'bar' }); }) .then(schema => { - return schema.setPermissions("Stuff", { + return schema.setPermissions('Stuff', { create: { requiresAuthentication: true, }, }); }) .then(() => { - const stuff = new Parse.Object("Stuff"); - stuff.set("foo", "bar"); + const stuff = new Parse.Object('Stuff'); + stuff.set('foo', 'bar'); return stuff.save(); }) .then( () => { - fail("Class permissions should have rejected this query."); + fail('Class permissions should have rejected this query.'); done(); }, e => { expect(e.message).toEqual( - "Permission denied, user needs to be authenticated." + 'Permission denied, user needs to be authenticated.' ); done(); } ); }); - it("required auth test create/get/update/delete authenticated", done => { + it('required auth test create/get/update/delete authenticated', done => { config.database .loadSchema() .then(schema => { // Just to create a valid class - return schema.validateObject("Stuff", { foo: "bar" }); + return schema.validateObject('Stuff', { foo: 'bar' }); }) .then(schema => { - return schema.setPermissions("Stuff", { + return schema.setPermissions('Stuff', { create: { requiresAuthentication: true, }, @@ -1680,15 +1680,15 @@ describe("Class Level Permissions for requiredAuth", () => { return createUser(); }) .then(() => { - const stuff = new Parse.Object("Stuff"); - stuff.set("foo", "bar"); + const stuff = new Parse.Object('Stuff'); + stuff.set('foo', 'bar'); return stuff.save().then(() => { - const query = new Parse.Query("Stuff"); + const query = new Parse.Query('Stuff'); return query.get(stuff.id); }); }) .then(gotStuff => { - return gotStuff.save({ foo: "baz" }).then(() => { + return gotStuff.save({ foo: 'baz' }).then(() => { return gotStuff.destroy(); }); }) @@ -1698,141 +1698,141 @@ describe("Class Level Permissions for requiredAuth", () => { }, e => { console.error(e); - fail("Should not have failed"); + fail('Should not have failed'); done(); } ); }); - it("required auth test get not authenticated", done => { + it('required auth test get not authenticated', done => { config.database .loadSchema() .then(schema => { // Just to create a valid class - return schema.validateObject("Stuff", { foo: "bar" }); + return schema.validateObject('Stuff', { foo: 'bar' }); }) .then(schema => { - return schema.setPermissions("Stuff", { + return schema.setPermissions('Stuff', { get: { requiresAuthentication: true, }, create: { - "*": true, + '*': true, }, }); }) .then(() => { - const stuff = new Parse.Object("Stuff"); - stuff.set("foo", "bar"); + const stuff = new Parse.Object('Stuff'); + stuff.set('foo', 'bar'); return stuff.save().then(() => { - const query = new Parse.Query("Stuff"); + const query = new Parse.Query('Stuff'); return query.get(stuff.id); }); }) .then( () => { - fail("Should not succeed!"); + fail('Should not succeed!'); done(); }, e => { expect(e.message).toEqual( - "Permission denied, user needs to be authenticated." + 'Permission denied, user needs to be authenticated.' ); done(); } ); }); - it("required auth test find not authenticated", done => { + it('required auth test find not authenticated', done => { config.database .loadSchema() .then(schema => { // Just to create a valid class - return schema.validateObject("Stuff", { foo: "bar" }); + return schema.validateObject('Stuff', { foo: 'bar' }); }) .then(schema => { - return schema.setPermissions("Stuff", { + return schema.setPermissions('Stuff', { find: { requiresAuthentication: true, }, create: { - "*": true, + '*': true, }, get: { - "*": true, + '*': true, }, }); }) .then(() => { - const stuff = new Parse.Object("Stuff"); - stuff.set("foo", "bar"); + const stuff = new Parse.Object('Stuff'); + stuff.set('foo', 'bar'); return stuff.save().then(() => { - const query = new Parse.Query("Stuff"); + const query = new Parse.Query('Stuff'); return query.get(stuff.id); }); }) .then(result => { - expect(result.get("foo")).toEqual("bar"); - const query = new Parse.Query("Stuff"); + expect(result.get('foo')).toEqual('bar'); + const query = new Parse.Query('Stuff'); return query.find(); }) .then( () => { - fail("Should not succeed!"); + fail('Should not succeed!'); done(); }, e => { expect(e.message).toEqual( - "Permission denied, user needs to be authenticated." + 'Permission denied, user needs to be authenticated.' ); done(); } ); }); - it("required auth test create/get/update/delete with roles (#3753)", done => { + it('required auth test create/get/update/delete with roles (#3753)', done => { let user; config.database .loadSchema() .then(schema => { // Just to create a valid class - return schema.validateObject("Stuff", { foo: "bar" }); + return schema.validateObject('Stuff', { foo: 'bar' }); }) .then(schema => { - return schema.setPermissions("Stuff", { + return schema.setPermissions('Stuff', { find: { requiresAuthentication: true, - "role:admin": true, + 'role:admin': true, }, - create: { "role:admin": true }, - update: { "role:admin": true }, - delete: { "role:admin": true }, + create: { 'role:admin': true }, + update: { 'role:admin': true }, + delete: { 'role:admin': true }, get: { requiresAuthentication: true, - "role:admin": true, + 'role:admin': true, }, }); }) .then(() => { - const stuff = new Parse.Object("Stuff"); - stuff.set("foo", "bar"); + const stuff = new Parse.Object('Stuff'); + stuff.set('foo', 'bar'); return stuff .save(null, { useMasterKey: true }) .then(() => { - const query = new Parse.Query("Stuff"); + const query = new Parse.Query('Stuff'); return query .get(stuff.id) .then( () => { - done.fail("should not succeed"); + done.fail('should not succeed'); }, () => { - return new Parse.Query("Stuff").find(); + return new Parse.Query('Stuff').find(); } ) .then( () => { - done.fail("should not succeed"); + done.fail('should not succeed'); }, () => { return Promise.resolve(); @@ -1840,9 +1840,9 @@ describe("Class Level Permissions for requiredAuth", () => { ); }) .then(() => { - return Parse.User.signUp("user", "password").then(signedUpUser => { + return Parse.User.signUp('user', 'password').then(signedUpUser => { user = signedUpUser; - const query = new Parse.Query("Stuff"); + const query = new Parse.Query('Stuff'); return query.get(stuff.id, { sessionToken: user.getSessionToken(), }); @@ -1850,8 +1850,8 @@ describe("Class Level Permissions for requiredAuth", () => { }); }) .then(result => { - expect(result.get("foo")).toEqual("bar"); - const query = new Parse.Query("Stuff"); + expect(result.get('foo')).toEqual('bar'); + const query = new Parse.Query('Stuff'); return query.find({ sessionToken: user.getSessionToken() }); }) .then( diff --git a/spec/SchemaPerformance.spec.js b/spec/SchemaPerformance.spec.js index 3fa06b60fc..6d6f3571c0 100644 --- a/spec/SchemaPerformance.spec.js +++ b/spec/SchemaPerformance.spec.js @@ -1,47 +1,47 @@ -const Config = require("../lib/Config"); +const Config = require('../lib/Config'); -describe("Schema Performance", function () { +describe('Schema Performance', function () { let getAllSpy; let config; beforeEach(async () => { await reconfigureServer(); - config = Config.get("test"); - getAllSpy = spyOn(databaseAdapter, "getAllClasses").and.callThrough(); + config = Config.get('test'); + getAllSpy = spyOn(databaseAdapter, 'getAllClasses').and.callThrough(); }); - it("test new object", async () => { + it('test new object', async () => { const object = new TestObject(); - object.set("foo", "bar"); + object.set('foo', 'bar'); await object.save(); expect(getAllSpy.calls.count()).toBe(2); }); - it("test new object multiple fields", async () => { + it('test new object multiple fields', async () => { const container = new Container({ dateField: new Date(), arrayField: [], numberField: 1, - stringField: "hello", + stringField: 'hello', booleanField: true, }); await container.save(); expect(getAllSpy.calls.count()).toBe(2); }); - it("test update existing fields", async () => { + it('test update existing fields', async () => { const object = new TestObject(); - object.set("foo", "bar"); + object.set('foo', 'bar'); await object.save(); getAllSpy.calls.reset(); - object.set("foo", "barz"); + object.set('foo', 'barz'); await object.save(); expect(getAllSpy.calls.count()).toBe(0); }); - xit("test saveAll / destroyAll", async () => { + xit('test saveAll / destroyAll', async () => { // This test can be flaky due to the nature of /batch requests // Used for performance const object = new TestObject(); @@ -52,7 +52,7 @@ describe("Schema Performance", function () { const objects = []; for (let i = 0; i < 10; i++) { const object = new TestObject(); - object.set("number", i); + object.set('number', i); objects.push(object); } await Parse.Object.saveAll(objects); @@ -70,21 +70,21 @@ describe("Schema Performance", function () { expect(getAllSpy.calls.count()).toBe(0); }); - it("test add new field to existing object", async () => { + it('test add new field to existing object', async () => { const object = new TestObject(); - object.set("foo", "bar"); + object.set('foo', 'bar'); await object.save(); getAllSpy.calls.reset(); - object.set("new", "barz"); + object.set('new', 'barz'); await object.save(); expect(getAllSpy.calls.count()).toBe(1); }); - it("test add multiple fields to existing object", async () => { + it('test add multiple fields to existing object', async () => { const object = new TestObject(); - object.set("foo", "bar"); + object.set('foo', 'bar'); await object.save(); getAllSpy.calls.reset(); @@ -93,45 +93,45 @@ describe("Schema Performance", function () { dateField: new Date(), arrayField: [], numberField: 1, - stringField: "hello", + stringField: 'hello', booleanField: true, }); await object.save(); expect(getAllSpy.calls.count()).toBe(1); }); - it("test user", async () => { + it('test user', async () => { const user = new Parse.User(); - user.setUsername("testing"); - user.setPassword("testing"); + user.setUsername('testing'); + user.setPassword('testing'); await user.signUp(); expect(getAllSpy.calls.count()).toBe(1); }); - it("test query include", async () => { + it('test query include', async () => { const child = new TestObject(); await child.save(); const object = new TestObject(); - object.set("child", child); + object.set('child', child); await object.save(); getAllSpy.calls.reset(); const query = new Parse.Query(TestObject); - query.include("child"); + query.include('child'); await query.get(object.id); expect(getAllSpy.calls.count()).toBe(0); }); - it("query relation without schema", async () => { - const child = new Parse.Object("ChildObject"); + it('query relation without schema', async () => { + const child = new Parse.Object('ChildObject'); await child.save(); - const parent = new Parse.Object("ParentObject"); - const relation = parent.relation("child"); + const parent = new Parse.Object('ParentObject'); + const relation = parent.relation('child'); relation.add(child); await parent.save(); @@ -144,9 +144,9 @@ describe("Schema Performance", function () { expect(getAllSpy.calls.count()).toBe(0); }); - it("test delete object", async () => { + it('test delete object', async () => { const object = new TestObject(); - object.set("foo", "bar"); + object.set('foo', 'bar'); await object.save(); getAllSpy.calls.reset(); @@ -155,7 +155,7 @@ describe("Schema Performance", function () { expect(getAllSpy.calls.count()).toBe(0); }); - it("test schema update class", async () => { + it('test schema update class', async () => { const container = new Container(); await container.save(); @@ -166,41 +166,41 @@ describe("Schema Performance", function () { const levelPermissions = { ACL: { - "*": { + '*': { read: true, write: true, }, }, - find: { "*": true }, - get: { "*": true }, - create: { "*": true }, - update: { "*": true }, - delete: { "*": true }, - addField: { "*": true }, - protectedFields: { "*": [] }, + find: { '*': true }, + get: { '*': true }, + create: { '*': true }, + update: { '*': true }, + delete: { '*': true }, + addField: { '*': true }, + protectedFields: { '*': [] }, }; await schema.updateClass( - "Container", + 'Container', { - fooOne: { type: "Number" }, - fooTwo: { type: "Array" }, - fooThree: { type: "Date" }, - fooFour: { type: "Object" }, - fooFive: { type: "Relation", targetClass: "_User" }, - fooSix: { type: "String" }, - fooSeven: { type: "Object" }, - fooEight: { type: "String" }, - fooNine: { type: "String" }, - fooTeen: { type: "Number" }, - fooEleven: { type: "String" }, - fooTwelve: { type: "String" }, - fooThirteen: { type: "String" }, - fooFourteen: { type: "String" }, - fooFifteen: { type: "String" }, - fooSixteen: { type: "String" }, - fooEighteen: { type: "String" }, - fooNineteen: { type: "String" }, + fooOne: { type: 'Number' }, + fooTwo: { type: 'Array' }, + fooThree: { type: 'Date' }, + fooFour: { type: 'Object' }, + fooFive: { type: 'Relation', targetClass: '_User' }, + fooSix: { type: 'String' }, + fooSeven: { type: 'Object' }, + fooEight: { type: 'String' }, + fooNine: { type: 'String' }, + fooTeen: { type: 'Number' }, + fooEleven: { type: 'String' }, + fooTwelve: { type: 'String' }, + fooThirteen: { type: 'String' }, + fooFourteen: { type: 'String' }, + fooFifteen: { type: 'String' }, + fooSixteen: { type: 'String' }, + fooEighteen: { type: 'String' }, + fooNineteen: { type: 'String' }, }, levelPermissions, {}, @@ -209,13 +209,13 @@ describe("Schema Performance", function () { expect(getAllSpy.calls.count()).toBe(2); }); - it_id("9dd70965-b683-4cb8-b43a-44c1f4def9f4")(it)( - "does reload with schemaCacheTtl", + it_id('9dd70965-b683-4cb8-b43a-44c1f4def9f4')(it)( + 'does reload with schemaCacheTtl', async () => { const databaseURI = - process.env.PARSE_SERVER_TEST_DB === "postgres" + process.env.PARSE_SERVER_TEST_DB === 'postgres' ? process.env.PARSE_SERVER_TEST_DATABASE_URI - : "mongodb://localhost:27017/parseServerMongoAdapterTestDatabase"; + : 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase'; await reconfigureServer({ databaseAdapter: undefined, databaseURI, @@ -223,38 +223,38 @@ describe("Schema Performance", function () { databaseOptions: { schemaCacheTtl: 1000 }, }); const SchemaController = - require("../lib/Controllers/SchemaController").SchemaController; + require('../lib/Controllers/SchemaController').SchemaController; const spy = spyOn( SchemaController.prototype, - "reloadData" + 'reloadData' ).and.callThrough(); - Object.defineProperty(spy, "reloadCalls", { + Object.defineProperty(spy, 'reloadCalls', { get: () => spy.calls.all().filter(call => call.args[0].clearCache).length, }); const object = new TestObject(); - object.set("foo", "bar"); + object.set('foo', 'bar'); await object.save(); spy.calls.reset(); - object.set("foo", "bar"); + object.set('foo', 'bar'); await object.save(); expect(spy.reloadCalls).toBe(0); await new Promise(resolve => setTimeout(resolve, 1100)); - object.set("foo", "bar"); + object.set('foo', 'bar'); await object.save(); expect(spy.reloadCalls).toBe(1); } ); - it_id("b0ae21f2-c947-48ed-a0db-e8900d45a4c8")(it)( - "cannot set invalid databaseOptions", + it_id('b0ae21f2-c947-48ed-a0db-e8900d45a4c8')(it)( + 'cannot set invalid databaseOptions', async () => { const expectError = async (key, value, expected) => expectAsync( @@ -263,16 +263,16 @@ describe("Schema Performance", function () { databaseOptions: { [key]: value }, }) ).toBeRejectedWith(`databaseOptions.${key} must be a ${expected}`); - for (const databaseOptions of [[], 0, "string"]) { + for (const databaseOptions of [[], 0, 'string']) { await expectAsync( reconfigureServer({ databaseAdapter: undefined, databaseOptions }) ).toBeRejectedWith(`databaseOptions must be an object`); } - for (const value of [null, 0, "string", {}, []]) { - await expectError("enableSchemaHooks", value, "boolean"); + for (const value of [null, 0, 'string', {}, []]) { + await expectError('enableSchemaHooks', value, 'boolean'); } - for (const value of [null, false, "string", {}, []]) { - await expectError("schemaCacheTtl", value, "number"); + for (const value of [null, false, 'string', {}, []]) { + await expectError('schemaCacheTtl', value, 'number'); } } ); diff --git a/spec/SecurityCheck.spec.js b/spec/SecurityCheck.spec.js index f57f900724..9c70c86170 100644 --- a/spec/SecurityCheck.spec.js +++ b/spec/SecurityCheck.spec.js @@ -1,22 +1,22 @@ -"use strict"; +'use strict'; -const Utils = require("../lib/Utils"); -const Config = require("../lib/Config"); -const request = require("../lib/request"); -const Definitions = require("../lib/Options/Definitions"); -const { Check, CheckState } = require("../lib/Security/Check"); -const CheckGroup = require("../lib/Security/CheckGroup"); -const CheckRunner = require("../lib/Security/CheckRunner"); -const CheckGroups = require("../lib/Security/CheckGroups/CheckGroups"); +const Utils = require('../lib/Utils'); +const Config = require('../lib/Config'); +const request = require('../lib/request'); +const Definitions = require('../lib/Options/Definitions'); +const { Check, CheckState } = require('../lib/Security/Check'); +const CheckGroup = require('../lib/Security/CheckGroup'); +const CheckRunner = require('../lib/Security/CheckRunner'); +const CheckGroups = require('../lib/Security/CheckGroups/CheckGroups'); -describe("Security Check", () => { +describe('Security Check', () => { let Group; let groupName; let checkSuccess; let checkFail; let config; - const publicServerURL = "http://localhost:8378/1"; - const securityUrl = publicServerURL + "/security"; + const publicServerURL = 'http://localhost:8378/1'; + const securityUrl = publicServerURL + '/security'; async function reconfigureServerWithSecurityConfig(security) { config.security = security; @@ -29,8 +29,8 @@ describe("Security Check", () => { { url: securityUrl, headers: { - "X-Parse-Master-Key": Parse.masterKey, - "X-Parse-Application-Id": Parse.applicationId, + 'X-Parse-Master-Key': Parse.masterKey, + 'X-Parse-Application-Id': Parse.applicationId, }, followRedirects: false, }, @@ -39,23 +39,23 @@ describe("Security Check", () => { ).catch(e => e); beforeEach(async () => { - groupName = "Example Group Name"; + groupName = 'Example Group Name'; checkSuccess = new Check({ - group: "TestGroup", - title: "TestTitleSuccess", - warning: "TestWarning", - solution: "TestSolution", + group: 'TestGroup', + title: 'TestTitleSuccess', + warning: 'TestWarning', + solution: 'TestSolution', check: () => { return true; }, }); checkFail = new Check({ - group: "TestGroup", - title: "TestTitleFail", - warning: "TestWarning", - solution: "TestSolution", + group: 'TestGroup', + title: 'TestTitleFail', + warning: 'TestWarning', + solution: 'TestSolution', check: () => { - throw "Fail"; + throw 'Fail'; }, }); Group = class Group extends CheckGroup { @@ -67,8 +67,8 @@ describe("Security Check", () => { } }; config = { - appId: "test", - appName: "ExampleAppName", + appId: 'test', + appName: 'ExampleAppName', publicServerURL, security: { enableCheck: true, @@ -78,8 +78,8 @@ describe("Security Check", () => { await reconfigureServer(config); }); - describe("server options", () => { - it("uses default configuration when none is set", async () => { + describe('server options', () => { + it('uses default configuration when none is set', async () => { await reconfigureServerWithSecurityConfig({}); expect(Config.get(Parse.applicationId).security.enableCheck).toBe( Definitions.SecurityOptions.enableCheck.default @@ -89,17 +89,17 @@ describe("Security Check", () => { ); }); - it("throws on invalid configuration", async () => { + it('throws on invalid configuration', async () => { const options = [ [], - "a", + 'a', 0, true, - { enableCheck: "a" }, + { enableCheck: 'a' }, { enableCheck: 0 }, { enableCheck: {} }, { enableCheck: [] }, - { enableCheckLog: "a" }, + { enableCheckLog: 'a' }, { enableCheckLog: 0 }, { enableCheckLog: {} }, { enableCheckLog: [] }, @@ -112,9 +112,9 @@ describe("Security Check", () => { }); }); - describe("auto-run", () => { - it("runs security checks on server start if enabled", async () => { - const runnerSpy = spyOn(CheckRunner.prototype, "run").and.callThrough(); + describe('auto-run', () => { + it('runs security checks on server start if enabled', async () => { + const runnerSpy = spyOn(CheckRunner.prototype, 'run').and.callThrough(); await reconfigureServerWithSecurityConfig({ enableCheck: true, enableCheckLog: true, @@ -122,8 +122,8 @@ describe("Security Check", () => { expect(runnerSpy).toHaveBeenCalledTimes(1); }); - it("does not run security checks on server start if disabled", async () => { - const runnerSpy = spyOn(CheckRunner.prototype, "run").and.callThrough(); + it('does not run security checks on server start if disabled', async () => { + const runnerSpy = spyOn(CheckRunner.prototype, 'run').and.callThrough(); const configs = [ { enableCheck: true, enableCheckLog: false }, { enableCheck: false, enableCheckLog: false }, @@ -137,41 +137,41 @@ describe("Security Check", () => { }); }); - describe("security endpoint accessibility", () => { - it("responds with 403 without masterkey", async () => { + describe('security endpoint accessibility', () => { + it('responds with 403 without masterkey', async () => { const response = await securityRequest({ headers: {} }); expect(response.status).toBe(403); }); - it("responds with 409 with masterkey and security check disabled", async () => { + it('responds with 409 with masterkey and security check disabled', async () => { await reconfigureServerWithSecurityConfig({}); const response = await securityRequest(); expect(response.status).toBe(409); }); - it("responds with 200 with masterkey and security check enabled", async () => { + it('responds with 200 with masterkey and security check enabled', async () => { const response = await securityRequest(); expect(response.status).toBe(200); }); }); - describe("check", () => { + describe('check', () => { const initCheck = config => (() => new Check(config)).bind(null); - it("instantiates check with valid parameters", async () => { + it('instantiates check with valid parameters', async () => { const configs = [ { - group: "string", - title: "string", - warning: "string", - solution: "string", + group: 'string', + title: 'string', + warning: 'string', + solution: 'string', check: () => {}, }, { - group: "string", - title: "string", - warning: "string", - solution: "string", + group: 'string', + title: 'string', + warning: 'string', + solution: 'string', check: async () => {}, }, ]; @@ -180,13 +180,13 @@ describe("Security Check", () => { } }); - it("throws instantiating check with invalid parameters", async () => { + it('throws instantiating check with invalid parameters', async () => { const configDefinition = { group: [false, true, 0, 1, [], {}, () => {}], title: [false, true, 0, 1, [], {}, () => {}], warning: [false, true, 0, 1, [], {}, () => {}], solution: [false, true, 0, 1, [], {}, () => {}], - check: [false, true, 0, 1, [], {}, "string"], + check: [false, true, 0, 1, [], {}, 'string'], }; const configs = Utils.getObjectKeyPermutations(configDefinition); @@ -195,12 +195,12 @@ describe("Security Check", () => { } }); - it("sets correct states for check success", async () => { + it('sets correct states for check success', async () => { const check = new Check({ - group: "string", - title: "string", - warning: "string", - solution: "string", + group: 'string', + title: 'string', + warning: 'string', + solution: 'string', check: () => {}, }); expect(check._checkState == CheckState.none); @@ -208,14 +208,14 @@ describe("Security Check", () => { expect(check._checkState == CheckState.success); }); - it("sets correct states for check fail", async () => { + it('sets correct states for check fail', async () => { const check = new Check({ - group: "string", - title: "string", - warning: "string", - solution: "string", + group: 'string', + title: 'string', + warning: 'string', + solution: 'string', check: () => { - throw "error"; + throw 'error'; }, }); expect(check._checkState == CheckState.none); @@ -224,8 +224,8 @@ describe("Security Check", () => { }); }); - describe("check group", () => { - it("returns properties if subclassed correctly", async () => { + describe('check group', () => { + it('returns properties if subclassed correctly', async () => { const group = new Group(); expect(group.name()).toBe(groupName); expect(group.checks().length).toBe(2); @@ -233,10 +233,10 @@ describe("Security Check", () => { expect(group.checks()[1]).toEqual(checkFail); }); - it("throws if subclassed incorrectly", async () => { + it('throws if subclassed incorrectly', async () => { class InvalidGroup1 extends CheckGroup {} expect((() => new InvalidGroup1()).bind()).toThrow( - "Check group has no name." + 'Check group has no name.' ); class InvalidGroup2 extends CheckGroup { setName() { @@ -244,11 +244,11 @@ describe("Security Check", () => { } } expect((() => new InvalidGroup2()).bind()).toThrow( - "Check group has no checks." + 'Check group has no checks.' ); }); - it("runs checks", async () => { + it('runs checks', async () => { const group = new Group(); expect(group.checks()[0].checkState()).toBe(CheckState.none); expect(group.checks()[1].checkState()).toBe(CheckState.none); @@ -258,10 +258,10 @@ describe("Security Check", () => { }); }); - describe("check runner", () => { + describe('check runner', () => { const initRunner = config => (() => new CheckRunner(config)).bind(null); - it("instantiates runner with valid parameters", async () => { + it('instantiates runner with valid parameters', async () => { const configDefinition = { enableCheck: [false, true, undefined], enableCheckLog: [false, true, undefined], @@ -273,7 +273,7 @@ describe("Security Check", () => { } }); - it("throws instantiating runner with invalid parameters", async () => { + it('throws instantiating runner with invalid parameters', async () => { const configDefinition = { enableCheck: [0, 1, [], {}, () => {}], enableCheckLog: [0, 1, [], {}, () => {}], @@ -286,14 +286,14 @@ describe("Security Check", () => { } }); - it("instantiates runner with default parameters", async () => { + it('instantiates runner with default parameters', async () => { const runner = new CheckRunner(); expect(runner.enableCheck).toBeFalse(); expect(runner.enableCheckLog).toBeFalse(); expect(runner.checkGroups).toBe(CheckGroups); }); - it("runs all checks of all groups", async () => { + it('runs all checks of all groups', async () => { const checkGroups = [Group, Group]; const runner = new CheckRunner({ checkGroups }); const report = await runner.run(); @@ -303,28 +303,28 @@ describe("Security Check", () => { expect(report.report.groups[1].checks[1].state).toBe(CheckState.fail); }); - it("reports correct default syntax version 1.0.0", async () => { + it('reports correct default syntax version 1.0.0', async () => { const checkGroups = [Group]; const runner = new CheckRunner({ checkGroups, enableCheckLog: true }); const report = await runner.run(); expect(report).toEqual({ report: { - version: "1.0.0", - state: "fail", + version: '1.0.0', + state: 'fail', groups: [ { - name: "Example Group Name", - state: "fail", + name: 'Example Group Name', + state: 'fail', checks: [ { - title: "TestTitleSuccess", - state: "success", + title: 'TestTitleSuccess', + state: 'success', }, { - title: "TestTitleFail", - state: "fail", - warning: "TestWarning", - solution: "TestSolution", + title: 'TestTitleFail', + state: 'fail', + warning: 'TestWarning', + solution: 'TestSolution', }, ], }, @@ -333,9 +333,9 @@ describe("Security Check", () => { }); }); - it("logs report", async () => { - const logger = require("../lib/logger").logger; - const logSpy = spyOn(logger, "warn").and.callThrough(); + it('logs report', async () => { + const logger = require('../lib/logger').logger; + const logSpy = spyOn(logger, 'warn').and.callThrough(); const checkGroups = [Group]; const runner = new CheckRunner({ checkGroups, enableCheckLog: true }); const report = await runner.run(); @@ -349,14 +349,14 @@ describe("Security Check", () => { } }); - it("does update featuresRouter", async () => { + it('does update featuresRouter', async () => { let response = await request({ - url: "http://localhost:8378/1/serverInfo", + url: 'http://localhost:8378/1/serverInfo', json: true, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "X-Parse-Master-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Master-Key': 'test', }, }); expect(response.data.features.settings.securityCheck).toBeTrue(); @@ -366,12 +366,12 @@ describe("Security Check", () => { }, }); response = await request({ - url: "http://localhost:8378/1/serverInfo", + url: 'http://localhost:8378/1/serverInfo', json: true, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "X-Parse-Master-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Master-Key': 'test', }, }); expect(response.data.features.settings.securityCheck).toBeFalse(); diff --git a/spec/SecurityCheckGroups.spec.js b/spec/SecurityCheckGroups.spec.js index e2846fa0d0..41a4fa2e82 100644 --- a/spec/SecurityCheckGroups.spec.js +++ b/spec/SecurityCheckGroups.spec.js @@ -1,18 +1,18 @@ -"use strict"; +'use strict'; -const Config = require("../lib/Config"); -const { CheckState } = require("../lib/Security/Check"); -const CheckGroupServerConfig = require("../lib/Security/CheckGroups/CheckGroupServerConfig"); -const CheckGroupDatabase = require("../lib/Security/CheckGroups/CheckGroupDatabase"); +const Config = require('../lib/Config'); +const { CheckState } = require('../lib/Security/Check'); +const CheckGroupServerConfig = require('../lib/Security/CheckGroups/CheckGroupServerConfig'); +const CheckGroupDatabase = require('../lib/Security/CheckGroups/CheckGroupDatabase'); -describe("Security Check Groups", () => { +describe('Security Check Groups', () => { let config; beforeEach(async () => { config = { - appId: "test", - appName: "ExampleAppName", - publicServerURL: "http://localhost:8378/1", + appId: 'test', + appName: 'ExampleAppName', + publicServerURL: 'http://localhost:8378/1', security: { enableCheck: true, enableCheckLog: false, @@ -21,15 +21,15 @@ describe("Security Check Groups", () => { await reconfigureServer(config); }); - describe("CheckGroupServerConfig", () => { - it("is subclassed correctly", async () => { + describe('CheckGroupServerConfig', () => { + it('is subclassed correctly', async () => { const group = new CheckGroupServerConfig(); expect(group.name()).toBeDefined(); expect(group.checks().length).toBeGreaterThan(0); }); - it("checks succeed correctly", async () => { - config.masterKey = "aMoreSecur3Passwor7!"; + it('checks succeed correctly', async () => { + config.masterKey = 'aMoreSecur3Passwor7!'; config.security.enableCheckLog = false; config.allowClientClassCreation = false; config.enableInsecureAuthAdapters = false; @@ -43,8 +43,8 @@ describe("Security Check Groups", () => { expect(group.checks()[4].checkState()).toBe(CheckState.success); }); - it("checks fail correctly", async () => { - config.masterKey = "insecure"; + it('checks fail correctly', async () => { + config.masterKey = 'insecure'; config.security.enableCheckLog = true; config.allowClientClassCreation = true; await reconfigureServer(config); @@ -58,28 +58,28 @@ describe("Security Check Groups", () => { }); }); - describe("CheckGroupDatabase", () => { - it("is subclassed correctly", async () => { + describe('CheckGroupDatabase', () => { + it('is subclassed correctly', async () => { const group = new CheckGroupDatabase(); expect(group.name()).toBeDefined(); expect(group.checks().length).toBeGreaterThan(0); }); - it("checks succeed correctly", async () => { + it('checks succeed correctly', async () => { const config = Config.get(Parse.applicationId); const uri = config.database.adapter._uri; config.database.adapter._uri = - "protocol://user:aMoreSecur3Passwor7!@example.com"; + 'protocol://user:aMoreSecur3Passwor7!@example.com'; const group = new CheckGroupDatabase(); await group.run(); expect(group.checks()[0].checkState()).toBe(CheckState.success); config.database.adapter._uri = uri; }); - it("checks fail correctly", async () => { + it('checks fail correctly', async () => { const config = Config.get(Parse.applicationId); const uri = config.database.adapter._uri; - config.database.adapter._uri = "protocol://user:insecure@example.com"; + config.database.adapter._uri = 'protocol://user:insecure@example.com'; const group = new CheckGroupDatabase(); await group.run(); expect(group.checks()[0].checkState()).toBe(CheckState.fail); diff --git a/spec/SessionTokenCache.spec.js b/spec/SessionTokenCache.spec.js index fc86c56ee5..b61b5559be 100644 --- a/spec/SessionTokenCache.spec.js +++ b/spec/SessionTokenCache.spec.js @@ -1,15 +1,15 @@ const SessionTokenCache = - require("../lib/LiveQuery/SessionTokenCache").SessionTokenCache; + require('../lib/LiveQuery/SessionTokenCache').SessionTokenCache; -describe("SessionTokenCache", function () { +describe('SessionTokenCache', function () { beforeEach(function (done) { - const Parse = require("parse/node"); + const Parse = require('parse/node'); - spyOn(Parse, "Query").and.returnValue({ - first: jasmine.createSpy("first").and.returnValue( + spyOn(Parse, 'Query').and.returnValue({ + first: jasmine.createSpy('first').and.returnValue( Promise.resolve( - new Parse.Object("_Session", { - user: new Parse.User({ id: "userId" }), + new Parse.Object('_Session', { + user: new Parse.User({ id: 'userId' }), }) ) ), @@ -19,7 +19,7 @@ describe("SessionTokenCache", function () { done(); }); - it("can get undefined userId", function (done) { + it('can get undefined userId', function (done) { const sessionTokenCache = new SessionTokenCache(); sessionTokenCache.getUserId(undefined).then( @@ -31,10 +31,10 @@ describe("SessionTokenCache", function () { ); }); - it("can get existing userId", function (done) { + it('can get existing userId', function (done) { const sessionTokenCache = new SessionTokenCache(); - const sessionToken = "sessionToken"; - const userId = "userId"; + const sessionToken = 'sessionToken'; + const userId = 'userId'; sessionTokenCache.cache.set(sessionToken, userId); sessionTokenCache.getUserId(sessionToken).then(userIdFromCache => { @@ -43,11 +43,11 @@ describe("SessionTokenCache", function () { }); }); - it("can get new userId", function (done) { + it('can get new userId', function (done) { const sessionTokenCache = new SessionTokenCache(); - sessionTokenCache.getUserId("sessionToken").then(userIdFromCache => { - expect(userIdFromCache).toBe("userId"); + sessionTokenCache.getUserId('sessionToken').then(userIdFromCache => { + expect(userIdFromCache).toBe('userId'); expect(sessionTokenCache.cache.size).toBe(1); done(); }); diff --git a/spec/Subscription.spec.js b/spec/Subscription.spec.js index fef262afd4..854086f1c9 100644 --- a/spec/Subscription.spec.js +++ b/spec/Subscription.spec.js @@ -1,50 +1,50 @@ -const Subscription = require("../lib/LiveQuery/Subscription").Subscription; +const Subscription = require('../lib/LiveQuery/Subscription').Subscription; let logger; -describe("Subscription", function () { +describe('Subscription', function () { beforeEach(function () { - logger = require("../lib/logger").logger; - spyOn(logger, "error").and.callThrough(); + logger = require('../lib/logger').logger; + spyOn(logger, 'error').and.callThrough(); }); - it("can be initialized", function () { + it('can be initialized', function () { const subscription = new Subscription( - "className", - { key: "value" }, - "hash" + 'className', + { key: 'value' }, + 'hash' ); - expect(subscription.className).toBe("className"); - expect(subscription.query).toEqual({ key: "value" }); - expect(subscription.hash).toBe("hash"); + expect(subscription.className).toBe('className'); + expect(subscription.query).toEqual({ key: 'value' }); + expect(subscription.hash).toBe('hash'); expect(subscription.clientRequestIds.size).toBe(0); }); - it("can check it has subscribing clients", function () { + it('can check it has subscribing clients', function () { const subscription = new Subscription( - "className", - { key: "value" }, - "hash" + 'className', + { key: 'value' }, + 'hash' ); expect(subscription.hasSubscribingClient()).toBe(false); }); - it("can check it does not have subscribing clients", function () { + it('can check it does not have subscribing clients', function () { const subscription = new Subscription( - "className", - { key: "value" }, - "hash" + 'className', + { key: 'value' }, + 'hash' ); subscription.addClientSubscription(1, 1); expect(subscription.hasSubscribingClient()).toBe(true); }); - it("can add one request for one client", function () { + it('can add one request for one client', function () { const subscription = new Subscription( - "className", - { key: "value" }, - "hash" + 'className', + { key: 'value' }, + 'hash' ); subscription.addClientSubscription(1, 1); @@ -52,11 +52,11 @@ describe("Subscription", function () { expect(subscription.clientRequestIds.get(1)).toEqual([1]); }); - it("can add requests for one client", function () { + it('can add requests for one client', function () { const subscription = new Subscription( - "className", - { key: "value" }, - "hash" + 'className', + { key: 'value' }, + 'hash' ); subscription.addClientSubscription(1, 1); subscription.addClientSubscription(1, 2); @@ -65,11 +65,11 @@ describe("Subscription", function () { expect(subscription.clientRequestIds.get(1)).toEqual([1, 2]); }); - it("can add requests for clients", function () { + it('can add requests for clients', function () { const subscription = new Subscription( - "className", - { key: "value" }, - "hash" + 'className', + { key: 'value' }, + 'hash' ); subscription.addClientSubscription(1, 1); subscription.addClientSubscription(1, 2); @@ -81,22 +81,22 @@ describe("Subscription", function () { expect(subscription.clientRequestIds.get(2)).toEqual([2, 3]); }); - it("can delete requests for nonexistent client", function () { + it('can delete requests for nonexistent client', function () { const subscription = new Subscription( - "className", - { key: "value" }, - "hash" + 'className', + { key: 'value' }, + 'hash' ); subscription.deleteClientSubscription(1, 1); expect(logger.error).toHaveBeenCalled(); }); - it("can delete nonexistent request for one client", function () { + it('can delete nonexistent request for one client', function () { const subscription = new Subscription( - "className", - { key: "value" }, - "hash" + 'className', + { key: 'value' }, + 'hash' ); subscription.addClientSubscription(1, 1); subscription.deleteClientSubscription(1, 2); @@ -106,11 +106,11 @@ describe("Subscription", function () { expect(subscription.clientRequestIds.get(1)).toEqual([1]); }); - it("can delete some requests for one client", function () { + it('can delete some requests for one client', function () { const subscription = new Subscription( - "className", - { key: "value" }, - "hash" + 'className', + { key: 'value' }, + 'hash' ); subscription.addClientSubscription(1, 1); subscription.addClientSubscription(1, 2); @@ -121,11 +121,11 @@ describe("Subscription", function () { expect(subscription.clientRequestIds.get(1)).toEqual([1]); }); - it("can delete all requests for one client", function () { + it('can delete all requests for one client', function () { const subscription = new Subscription( - "className", - { key: "value" }, - "hash" + 'className', + { key: 'value' }, + 'hash' ); subscription.addClientSubscription(1, 1); subscription.addClientSubscription(1, 2); @@ -136,11 +136,11 @@ describe("Subscription", function () { expect(subscription.clientRequestIds.size).toBe(0); }); - it("can delete requests for multiple clients", function () { + it('can delete requests for multiple clients', function () { const subscription = new Subscription( - "className", - { key: "value" }, - "hash" + 'className', + { key: 'value' }, + 'hash' ); subscription.addClientSubscription(1, 1); subscription.addClientSubscription(1, 2); diff --git a/spec/Uniqueness.spec.js b/spec/Uniqueness.spec.js index 928bbe3bae..7f53ac54bb 100644 --- a/spec/Uniqueness.spec.js +++ b/spec/Uniqueness.spec.js @@ -1,31 +1,31 @@ -"use strict"; +'use strict'; -const Parse = require("parse/node"); -const Config = require("../lib/Config"); +const Parse = require('parse/node'); +const Config = require('../lib/Config'); -describe("Uniqueness", function () { - it("fail when create duplicate value in unique field", done => { - const obj = new Parse.Object("UniqueField"); - obj.set("unique", "value"); +describe('Uniqueness', function () { + it('fail when create duplicate value in unique field', done => { + const obj = new Parse.Object('UniqueField'); + obj.set('unique', 'value'); obj .save() .then(() => { expect(obj.id).not.toBeUndefined(); - const config = Config.get("test"); + const config = Config.get('test'); return config.database.adapter.ensureUniqueness( - "UniqueField", - { fields: { unique: { __type: "String" } } }, - ["unique"] + 'UniqueField', + { fields: { unique: { __type: 'String' } } }, + ['unique'] ); }) .then(() => { - const obj = new Parse.Object("UniqueField"); - obj.set("unique", "value"); + const obj = new Parse.Object('UniqueField'); + obj.set('unique', 'value'); return obj.save(); }) .then( () => { - fail("Saving duplicate field should have failed"); + fail('Saving duplicate field should have failed'); done(); }, error => { @@ -35,31 +35,31 @@ describe("Uniqueness", function () { ); }); - it("unique indexing works on pointer fields", done => { - const obj = new Parse.Object("UniquePointer"); + it('unique indexing works on pointer fields', done => { + const obj = new Parse.Object('UniquePointer'); obj - .save({ string: "who cares" }) + .save({ string: 'who cares' }) .then(() => obj.save({ ptr: obj })) .then(() => { - const config = Config.get("test"); + const config = Config.get('test'); return config.database.adapter.ensureUniqueness( - "UniquePointer", + 'UniquePointer', { fields: { - string: { __type: "String" }, - ptr: { __type: "Pointer", targetClass: "UniquePointer" }, + string: { __type: 'String' }, + ptr: { __type: 'Pointer', targetClass: 'UniquePointer' }, }, }, - ["ptr"] + ['ptr'] ); }) .then(() => { - const newObj = new Parse.Object("UniquePointer"); - newObj.set("ptr", obj); + const newObj = new Parse.Object('UniquePointer'); + newObj.set('ptr', obj); return newObj.save(); }) .then(() => { - fail("save should have failed due to duplicate value"); + fail('save should have failed due to duplicate value'); done(); }) .catch(error => { @@ -68,20 +68,20 @@ describe("Uniqueness", function () { }); }); - it_id("802650a9-a6db-447e-88d0-8aae99100088")(it)( - "fails when attempting to ensure uniqueness of fields that are not currently unique", + it_id('802650a9-a6db-447e-88d0-8aae99100088')(it)( + 'fails when attempting to ensure uniqueness of fields that are not currently unique', done => { - const o1 = new Parse.Object("UniqueFail"); - o1.set("key", "val"); - const o2 = new Parse.Object("UniqueFail"); - o2.set("key", "val"); + const o1 = new Parse.Object('UniqueFail'); + o1.set('key', 'val'); + const o2 = new Parse.Object('UniqueFail'); + o2.set('key', 'val'); Parse.Object.saveAll([o1, o2]) .then(() => { - const config = Config.get("test"); + const config = Config.get('test'); return config.database.adapter.ensureUniqueness( - "UniqueFail", - { fields: { key: { __type: "String" } } }, - ["key"] + 'UniqueFail', + { fields: { key: { __type: 'String' } } }, + ['key'] ); }) .catch(error => { @@ -91,36 +91,36 @@ describe("Uniqueness", function () { } ); - it_exclude_dbs(["postgres"])("can do compound uniqueness", done => { - const config = Config.get("test"); + it_exclude_dbs(['postgres'])('can do compound uniqueness', done => { + const config = Config.get('test'); config.database.adapter .ensureUniqueness( - "CompoundUnique", - { fields: { k1: { __type: "String" }, k2: { __type: "String" } } }, - ["k1", "k2"] + 'CompoundUnique', + { fields: { k1: { __type: 'String' }, k2: { __type: 'String' } } }, + ['k1', 'k2'] ) .then(() => { - const o1 = new Parse.Object("CompoundUnique"); - o1.set("k1", "v1"); - o1.set("k2", "v2"); + const o1 = new Parse.Object('CompoundUnique'); + o1.set('k1', 'v1'); + o1.set('k2', 'v2'); return o1.save(); }) .then(() => { - const o2 = new Parse.Object("CompoundUnique"); - o2.set("k1", "v1"); - o2.set("k2", "not a dupe"); + const o2 = new Parse.Object('CompoundUnique'); + o2.set('k1', 'v1'); + o2.set('k2', 'not a dupe'); return o2.save(); }) .then(() => { - const o3 = new Parse.Object("CompoundUnique"); - o3.set("k1", "not a dupe"); - o3.set("k2", "v2"); + const o3 = new Parse.Object('CompoundUnique'); + o3.set('k1', 'not a dupe'); + o3.set('k2', 'v2'); return o3.save(); }) .then(() => { - const o4 = new Parse.Object("CompoundUnique"); - o4.set("k1", "v1"); - o4.set("k2", "v2"); + const o4 = new Parse.Object('CompoundUnique'); + o4.set('k1', 'v1'); + o4.set('k2', 'v2'); return o4.save(); }) .catch(error => { diff --git a/spec/UserController.spec.js b/spec/UserController.spec.js index e61de3aaae..5eab88cccf 100644 --- a/spec/UserController.spec.js +++ b/spec/UserController.spec.js @@ -1,22 +1,22 @@ -const emailAdapter = require("./support/MockEmailAdapter"); -const Config = require("../lib/Config"); -const Auth = require("../lib/Auth"); -const { resolvingPromise } = require("../lib/TestUtils"); +const emailAdapter = require('./support/MockEmailAdapter'); +const Config = require('../lib/Config'); +const Auth = require('../lib/Auth'); +const { resolvingPromise } = require('../lib/TestUtils'); -describe("UserController", () => { - describe("sendVerificationEmail", () => { - describe("parseFrameURL not provided", () => { - it_id("61338330-eca7-4c33-8816-7ff05966f43b")(it)( - "uses publicServerURL", +describe('UserController', () => { + describe('sendVerificationEmail', () => { + describe('parseFrameURL not provided', () => { + it_id('61338330-eca7-4c33-8816-7ff05966f43b')(it)( + 'uses publicServerURL', async () => { await reconfigureServer({ - publicServerURL: "http://www.example.com", + publicServerURL: 'http://www.example.com', customPages: { parseFrameURL: undefined, }, verifyUserEmails: true, emailAdapter, - appName: "test", + appName: 'test', }); let emailOptions; @@ -26,17 +26,17 @@ describe("UserController", () => { sendPromise.resolve(); }; - const username = "verificationUser"; + const username = 'verificationUser'; const user = new Parse.User(); user.setUsername(username); - user.setPassword("pass"); - user.setEmail("verification@example.com"); + user.setPassword('pass'); + user.setEmail('verification@example.com'); await user.signUp(); await sendPromise; - const config = Config.get("test"); + const config = Config.get('test'); const rawUser = await config.database.find( - "_User", + '_User', { username }, {}, Auth.maintenance(config) @@ -53,18 +53,18 @@ describe("UserController", () => { ); }); - describe("parseFrameURL provided", () => { - it_id("673c2bb1-049e-4dda-b6be-88c866260036")(it)( - "uses parseFrameURL and includes the destination in the link parameter", + describe('parseFrameURL provided', () => { + it_id('673c2bb1-049e-4dda-b6be-88c866260036')(it)( + 'uses parseFrameURL and includes the destination in the link parameter', async () => { await reconfigureServer({ - publicServerURL: "http://www.example.com", + publicServerURL: 'http://www.example.com', customPages: { - parseFrameURL: "http://someother.example.com/handle-parse-iframe", + parseFrameURL: 'http://someother.example.com/handle-parse-iframe', }, verifyUserEmails: true, emailAdapter, - appName: "test", + appName: 'test', }); let emailOptions; @@ -74,17 +74,17 @@ describe("UserController", () => { sendPromise.resolve(); }; - const username = "verificationUser"; + const username = 'verificationUser'; const user = new Parse.User(); user.setUsername(username); - user.setPassword("pass"); - user.setEmail("verification@example.com"); + user.setPassword('pass'); + user.setEmail('verification@example.com'); await user.signUp(); await sendPromise; - const config = Config.get("test"); + const config = Config.get('test'); const rawUser = await config.database.find( - "_User", + '_User', { username }, {}, Auth.maintenance(config) diff --git a/spec/UserPII.spec.js b/spec/UserPII.spec.js index 9533e58f3f..6aee1a14e3 100644 --- a/spec/UserPII.spec.js +++ b/spec/UserPII.spec.js @@ -1,52 +1,52 @@ -"use strict"; +'use strict'; -const Parse = require("parse/node"); -const request = require("../lib/request"); +const Parse = require('parse/node'); +const request = require('../lib/request'); // const Config = require('../lib/Config'); -const EMAIL = "foo@bar.com"; -const ZIP = "10001"; -const SSN = "999-99-9999"; +const EMAIL = 'foo@bar.com'; +const ZIP = '10001'; +const SSN = '999-99-9999'; -describe("Personally Identifiable Information", () => { +describe('Personally Identifiable Information', () => { let user; beforeEach(async done => { await reconfigureServer(); - user = await Parse.User.signUp("tester", "abc"); - user = await Parse.User.logIn(user.get("username"), "abc"); + user = await Parse.User.signUp('tester', 'abc'); + user = await Parse.User.logIn(user.get('username'), 'abc'); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); await user - .set("email", EMAIL) - .set("zip", ZIP) - .set("ssn", SSN) + .set('email', EMAIL) + .set('zip', ZIP) + .set('ssn', SSN) .setACL(acl) .save(); done(); }); - it("should be able to get own PII via API with object", done => { + it('should be able to get own PII via API with object', done => { const userObj = new (Parse.Object.extend(Parse.User))(); userObj.id = user.id; return userObj .fetch() .then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(EMAIL); + expect(fetchedUser.get('email')).toBe(EMAIL); }) .then(done) .catch(done.fail); }); - it("should not be able to get PII via API with object", done => { + it('should not be able to get PII via API with object', done => { return Parse.User.logOut().then(() => { const userObj = new (Parse.Object.extend(Parse.User))(); userObj.id = user.id; userObj .fetch() .then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(undefined); + expect(fetchedUser.get('email')).toBe(undefined); done(); }) .catch(e => { @@ -57,90 +57,90 @@ describe("Personally Identifiable Information", () => { }); }); - it("should be able to get PII via API with object using master key", done => { + it('should be able to get PII via API with object using master key', done => { return Parse.User.logOut().then(() => { const userObj = new (Parse.Object.extend(Parse.User))(); userObj.id = user.id; userObj .fetch({ useMasterKey: true }) - .then(fetchedUser => expect(fetchedUser.get("email")).toBe(EMAIL)) + .then(fetchedUser => expect(fetchedUser.get('email')).toBe(EMAIL)) .then(done) .catch(done.fail); }); }); - it("should be able to get own PII via API with Find", done => { + it('should be able to get own PII via API with Find', done => { return new Parse.Query(Parse.User).first().then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(EMAIL); - expect(fetchedUser.get("zip")).toBe(ZIP); - expect(fetchedUser.get("ssn")).toBe(SSN); + expect(fetchedUser.get('email')).toBe(EMAIL); + expect(fetchedUser.get('zip')).toBe(ZIP); + expect(fetchedUser.get('ssn')).toBe(SSN); done(); }); }); - it("should not get PII via API with Find", done => { + it('should not get PII via API with Find', done => { return Parse.User.logOut().then(() => new Parse.Query(Parse.User).first().then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(undefined); - expect(fetchedUser.get("zip")).toBe(ZIP); - expect(fetchedUser.get("ssn")).toBe(SSN); + expect(fetchedUser.get('email')).toBe(undefined); + expect(fetchedUser.get('zip')).toBe(ZIP); + expect(fetchedUser.get('ssn')).toBe(SSN); done(); }) ); }); - it("should get PII via API with Find using master key", done => { + it('should get PII via API with Find using master key', done => { return Parse.User.logOut().then(() => new Parse.Query(Parse.User) .first({ useMasterKey: true }) .then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(EMAIL); - expect(fetchedUser.get("zip")).toBe(ZIP); - expect(fetchedUser.get("ssn")).toBe(SSN); + expect(fetchedUser.get('email')).toBe(EMAIL); + expect(fetchedUser.get('zip')).toBe(ZIP); + expect(fetchedUser.get('ssn')).toBe(SSN); done(); }) ); }); - it("should be able to get own PII via API with Get", done => { + it('should be able to get own PII via API with Get', done => { return new Parse.Query(Parse.User).get(user.id).then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(EMAIL); - expect(fetchedUser.get("zip")).toBe(ZIP); - expect(fetchedUser.get("ssn")).toBe(SSN); + expect(fetchedUser.get('email')).toBe(EMAIL); + expect(fetchedUser.get('zip')).toBe(ZIP); + expect(fetchedUser.get('ssn')).toBe(SSN); done(); }); }); - it("should not get PII via API with Get", done => { + it('should not get PII via API with Get', done => { return Parse.User.logOut().then(() => new Parse.Query(Parse.User).get(user.id).then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(undefined); - expect(fetchedUser.get("zip")).toBe(ZIP); - expect(fetchedUser.get("ssn")).toBe(SSN); + expect(fetchedUser.get('email')).toBe(undefined); + expect(fetchedUser.get('zip')).toBe(ZIP); + expect(fetchedUser.get('ssn')).toBe(SSN); done(); }) ); }); - it("should get PII via API with Get using master key", done => { + it('should get PII via API with Get using master key', done => { return Parse.User.logOut().then(() => new Parse.Query(Parse.User) .get(user.id, { useMasterKey: true }) .then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(EMAIL); - expect(fetchedUser.get("zip")).toBe(ZIP); - expect(fetchedUser.get("ssn")).toBe(SSN); + expect(fetchedUser.get('email')).toBe(EMAIL); + expect(fetchedUser.get('zip')).toBe(ZIP); + expect(fetchedUser.get('ssn')).toBe(SSN); done(); }) ); }); - it("should not get PII via REST", done => { + it('should not get PII via REST', done => { return request({ - url: "http://localhost:8378/1/classes/_User", + url: 'http://localhost:8378/1/classes/_User', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Javascript-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Javascript-Key': 'test', }, }) .then(response => { @@ -153,14 +153,14 @@ describe("Personally Identifiable Information", () => { .catch(done.fail); }); - it("should get PII via REST with self credentials", done => { + it('should get PII via REST with self credentials', done => { return request({ - url: "http://localhost:8378/1/classes/_User", + url: 'http://localhost:8378/1/classes/_User', json: true, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Javascript-Key": "test", - "X-Parse-Session-Token": user.getSessionToken(), + 'X-Parse-Application-Id': 'test', + 'X-Parse-Javascript-Key': 'test', + 'X-Parse-Session-Token': user.getSessionToken(), }, }) .then(response => { @@ -173,13 +173,13 @@ describe("Personally Identifiable Information", () => { .catch(done.fail); }); - it("should get PII via REST using master key", done => { + it('should get PII via REST using master key', done => { request({ - url: "http://localhost:8378/1/classes/_User", + url: 'http://localhost:8378/1/classes/_User', json: true, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Master-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Master-Key': 'test', }, }) .then(response => { @@ -192,12 +192,12 @@ describe("Personally Identifiable Information", () => { .catch(done.fail); }); - it("should not get PII via REST by ID", done => { + it('should not get PII via REST by ID', done => { request({ url: `http://localhost:8378/1/classes/_User/${user.id}`, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Javascript-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Javascript-Key': 'test', }, }) .then( @@ -211,14 +211,14 @@ describe("Personally Identifiable Information", () => { .then(() => done()); }); - it("should get PII via REST by ID with self credentials", done => { + it('should get PII via REST by ID with self credentials', done => { request({ url: `http://localhost:8378/1/classes/_User/${user.id}`, json: true, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Javascript-Key": "test", - "X-Parse-Session-Token": user.getSessionToken(), + 'X-Parse-Application-Id': 'test', + 'X-Parse-Javascript-Key': 'test', + 'X-Parse-Session-Token': user.getSessionToken(), }, }) .then(response => { @@ -231,14 +231,14 @@ describe("Personally Identifiable Information", () => { .catch(done.fail); }); - it("should get PII via REST by ID with master key", done => { + it('should get PII via REST by ID with master key', done => { request({ url: `http://localhost:8378/1/classes/_User/${user.id}`, json: true, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Javascript-Key": "test", - "X-Parse-Master-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Javascript-Key': 'test', + 'X-Parse-Master-Key': 'test', }, }) .then(response => { @@ -251,129 +251,129 @@ describe("Personally Identifiable Information", () => { .catch(done.fail); }); - describe("with deprecated configured sensitive fields", () => { + describe('with deprecated configured sensitive fields', () => { beforeEach(async () => { - await reconfigureServer({ userSensitiveFields: ["ssn", "zip"] }); + await reconfigureServer({ userSensitiveFields: ['ssn', 'zip'] }); }); - it("should be able to get own PII via API with object", done => { + it('should be able to get own PII via API with object', done => { const userObj = new (Parse.Object.extend(Parse.User))(); userObj.id = user.id; return userObj .fetch() .then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(EMAIL); - expect(fetchedUser.get("zip")).toBe(ZIP); - expect(fetchedUser.get("ssn")).toBe(SSN); + expect(fetchedUser.get('email')).toBe(EMAIL); + expect(fetchedUser.get('zip')).toBe(ZIP); + expect(fetchedUser.get('ssn')).toBe(SSN); done(); }) .catch(done.fail); }); - it("should not be able to get PII via API with object", done => { + it('should not be able to get PII via API with object', done => { Parse.User.logOut().then(() => { const userObj = new (Parse.Object.extend(Parse.User))(); userObj.id = user.id; userObj .fetch() .then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(undefined); - expect(fetchedUser.get("zip")).toBe(undefined); - expect(fetchedUser.get("ssn")).toBe(undefined); + expect(fetchedUser.get('email')).toBe(undefined); + expect(fetchedUser.get('zip')).toBe(undefined); + expect(fetchedUser.get('ssn')).toBe(undefined); }) .then(done) .catch(done.fail); }); }); - it("should be able to get PII via API with object using master key", done => { + it('should be able to get PII via API with object using master key', done => { Parse.User.logOut().then(() => { const userObj = new (Parse.Object.extend(Parse.User))(); userObj.id = user.id; userObj .fetch({ useMasterKey: true }) .then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(EMAIL); - expect(fetchedUser.get("zip")).toBe(ZIP); - expect(fetchedUser.get("ssn")).toBe(SSN); + expect(fetchedUser.get('email')).toBe(EMAIL); + expect(fetchedUser.get('zip')).toBe(ZIP); + expect(fetchedUser.get('ssn')).toBe(SSN); }, done.fail) .then(done) .catch(done.fail); }); }); - it("should be able to get own PII via API with Find", done => { + it('should be able to get own PII via API with Find', done => { new Parse.Query(Parse.User).first().then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(EMAIL); - expect(fetchedUser.get("zip")).toBe(ZIP); - expect(fetchedUser.get("ssn")).toBe(SSN); + expect(fetchedUser.get('email')).toBe(EMAIL); + expect(fetchedUser.get('zip')).toBe(ZIP); + expect(fetchedUser.get('ssn')).toBe(SSN); done(); }); }); - it("should not get PII via API with Find", done => { + it('should not get PII via API with Find', done => { Parse.User.logOut().then(() => new Parse.Query(Parse.User).first().then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(undefined); - expect(fetchedUser.get("zip")).toBe(undefined); - expect(fetchedUser.get("ssn")).toBe(undefined); + expect(fetchedUser.get('email')).toBe(undefined); + expect(fetchedUser.get('zip')).toBe(undefined); + expect(fetchedUser.get('ssn')).toBe(undefined); done(); }) ); }); - it("should get PII via API with Find using master key", done => { + it('should get PII via API with Find using master key', done => { Parse.User.logOut().then(() => new Parse.Query(Parse.User) .first({ useMasterKey: true }) .then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(EMAIL); - expect(fetchedUser.get("zip")).toBe(ZIP); - expect(fetchedUser.get("ssn")).toBe(SSN); + expect(fetchedUser.get('email')).toBe(EMAIL); + expect(fetchedUser.get('zip')).toBe(ZIP); + expect(fetchedUser.get('ssn')).toBe(SSN); done(); }) ); }); - it("should be able to get own PII via API with Get", done => { + it('should be able to get own PII via API with Get', done => { new Parse.Query(Parse.User).get(user.id).then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(EMAIL); - expect(fetchedUser.get("zip")).toBe(ZIP); - expect(fetchedUser.get("ssn")).toBe(SSN); + expect(fetchedUser.get('email')).toBe(EMAIL); + expect(fetchedUser.get('zip')).toBe(ZIP); + expect(fetchedUser.get('ssn')).toBe(SSN); done(); }); }); - it("should not get PII via API with Get", done => { + it('should not get PII via API with Get', done => { Parse.User.logOut().then(() => new Parse.Query(Parse.User).get(user.id).then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(undefined); - expect(fetchedUser.get("zip")).toBe(undefined); - expect(fetchedUser.get("ssn")).toBe(undefined); + expect(fetchedUser.get('email')).toBe(undefined); + expect(fetchedUser.get('zip')).toBe(undefined); + expect(fetchedUser.get('ssn')).toBe(undefined); done(); }) ); }); - it("should get PII via API with Get using master key", done => { + it('should get PII via API with Get using master key', done => { Parse.User.logOut().then(() => new Parse.Query(Parse.User) .get(user.id, { useMasterKey: true }) .then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(EMAIL); - expect(fetchedUser.get("zip")).toBe(ZIP); - expect(fetchedUser.get("ssn")).toBe(SSN); + expect(fetchedUser.get('email')).toBe(EMAIL); + expect(fetchedUser.get('zip')).toBe(ZIP); + expect(fetchedUser.get('ssn')).toBe(SSN); done(); }) ); }); - it("should not get PII via REST", done => { + it('should not get PII via REST', done => { request({ - url: "http://localhost:8378/1/classes/_User", + url: 'http://localhost:8378/1/classes/_User', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Javascript-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Javascript-Key': 'test', }, }) .then(response => { @@ -387,14 +387,14 @@ describe("Personally Identifiable Information", () => { .catch(done.fail); }); - it("should get PII via REST with self credentials", done => { + it('should get PII via REST with self credentials', done => { request({ - url: "http://localhost:8378/1/classes/_User", + url: 'http://localhost:8378/1/classes/_User', json: true, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Javascript-Key": "test", - "X-Parse-Session-Token": user.getSessionToken(), + 'X-Parse-Application-Id': 'test', + 'X-Parse-Javascript-Key': 'test', + 'X-Parse-Session-Token': user.getSessionToken(), }, }) .then(response => { @@ -408,13 +408,13 @@ describe("Personally Identifiable Information", () => { .catch(done.fail); }); - it("should get PII via REST using master key", done => { + it('should get PII via REST using master key', done => { request({ - url: "http://localhost:8378/1/classes/_User", + url: 'http://localhost:8378/1/classes/_User', json: true, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Master-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Master-Key': 'test', }, }) .then( @@ -431,13 +431,13 @@ describe("Personally Identifiable Information", () => { .catch(done.fail); }); - it("should not get PII via REST by ID", done => { + it('should not get PII via REST by ID', done => { request({ url: `http://localhost:8378/1/classes/_User/${user.id}`, json: true, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Javascript-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Javascript-Key': 'test', }, }) .then( @@ -452,14 +452,14 @@ describe("Personally Identifiable Information", () => { .catch(done.fail); }); - it("should get PII via REST by ID with self credentials", done => { + it('should get PII via REST by ID with self credentials', done => { request({ url: `http://localhost:8378/1/classes/_User/${user.id}`, json: true, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Javascript-Key": "test", - "X-Parse-Session-Token": user.getSessionToken(), + 'X-Parse-Application-Id': 'test', + 'X-Parse-Javascript-Key': 'test', + 'X-Parse-Session-Token': user.getSessionToken(), }, }) .then( @@ -474,13 +474,13 @@ describe("Personally Identifiable Information", () => { .catch(done.fail); }); - it("should get PII via REST by ID with master key", done => { + it('should get PII via REST by ID with master key', done => { request({ url: `http://localhost:8378/1/classes/_User/${user.id}`, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Javascript-Key": "test", - "X-Parse-Master-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Javascript-Key': 'test', + 'X-Parse-Master-Key': 'test', }, }) .then( @@ -497,19 +497,19 @@ describe("Personally Identifiable Information", () => { }); // Explicit ACL should be able to read sensitive information - describe("with privileged user no CLP", () => { + describe('with privileged user no CLP', () => { let adminUser; beforeEach(async done => { const adminRole = await new Parse.Role( - "Administrator", + 'Administrator', new Parse.ACL() ).save(null, { useMasterKey: true, }); const managementRole = new Parse.Role( - "managementOf_user" + user.id, + 'managementOf_user' + user.id, new Parse.ACL(user) ); managementRole.getRoles().add(adminRole); @@ -519,8 +519,8 @@ describe("Personally Identifiable Information", () => { userACL.setReadAccess(managementRole, true); await user.setACL(userACL).save(null, { useMasterKey: true }); - adminUser = await Parse.User.signUp("administrator", "secure"); - adminUser = await Parse.User.logIn(adminUser.get("username"), "secure"); + adminUser = await Parse.User.signUp('administrator', 'secure'); + adminUser = await Parse.User.logIn(adminUser.get('username'), 'secure'); await adminRole .getUsers() .add(adminUser) @@ -529,52 +529,52 @@ describe("Personally Identifiable Information", () => { done(); }); - it("privileged user should not be able to get user PII via API with object", done => { + it('privileged user should not be able to get user PII via API with object', done => { const userObj = new (Parse.Object.extend(Parse.User))(); userObj.id = user.id; userObj .fetch() .then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(undefined); + expect(fetchedUser.get('email')).toBe(undefined); }) .then(done) .catch(done.fail); }); - it("privileged user should not be able to get user PII via API with Find", done => { + it('privileged user should not be able to get user PII via API with Find', done => { new Parse.Query(Parse.User) - .equalTo("objectId", user.id) + .equalTo('objectId', user.id) .find() .then(fetchedUser => { fetchedUser = fetchedUser[0]; - expect(fetchedUser.get("email")).toBe(undefined); - expect(fetchedUser.get("zip")).toBe(undefined); - expect(fetchedUser.get("ssn")).toBe(undefined); + expect(fetchedUser.get('email')).toBe(undefined); + expect(fetchedUser.get('zip')).toBe(undefined); + expect(fetchedUser.get('ssn')).toBe(undefined); done(); }) .catch(done.fail); }); - it("privileged user should not be able to get user PII via API with Get", done => { + it('privileged user should not be able to get user PII via API with Get', done => { new Parse.Query(Parse.User) .get(user.id) .then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(undefined); - expect(fetchedUser.get("zip")).toBe(undefined); - expect(fetchedUser.get("ssn")).toBe(undefined); + expect(fetchedUser.get('email')).toBe(undefined); + expect(fetchedUser.get('zip')).toBe(undefined); + expect(fetchedUser.get('ssn')).toBe(undefined); done(); }) .catch(done.fail); }); - it("privileged user should not get user PII via REST by ID", done => { + it('privileged user should not get user PII via REST by ID', done => { request({ url: `http://localhost:8378/1/classes/_User/${user.id}`, json: true, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Javascript-Key": "test", - "X-Parse-Session-Token": adminUser.getSessionToken(), + 'X-Parse-Application-Id': 'test', + 'X-Parse-Javascript-Key': 'test', + 'X-Parse-Session-Token': adminUser.getSessionToken(), }, }) .then(response => { @@ -589,7 +589,7 @@ describe("Personally Identifiable Information", () => { }); // Public access ACL should always hide sensitive information - describe("with public read ACL", () => { + describe('with public read ACL', () => { beforeEach(async done => { const userACL = new Parse.ACL(); userACL.setPublicReadAccess(true); @@ -597,57 +597,57 @@ describe("Personally Identifiable Information", () => { done(); }); - it("should not be able to get user PII via API with object", done => { + it('should not be able to get user PII via API with object', done => { Parse.User.logOut().then(() => { const userObj = new (Parse.Object.extend(Parse.User))(); userObj.id = user.id; userObj .fetch() .then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(undefined); + expect(fetchedUser.get('email')).toBe(undefined); }) .then(done) .catch(done.fail); }); }); - it("should not be able to get user PII via API with Find", done => { + it('should not be able to get user PII via API with Find', done => { Parse.User.logOut().then(() => new Parse.Query(Parse.User) - .equalTo("objectId", user.id) + .equalTo('objectId', user.id) .find() .then(fetchedUser => { fetchedUser = fetchedUser[0]; - expect(fetchedUser.get("email")).toBe(undefined); - expect(fetchedUser.get("zip")).toBe(undefined); - expect(fetchedUser.get("ssn")).toBe(undefined); + expect(fetchedUser.get('email')).toBe(undefined); + expect(fetchedUser.get('zip')).toBe(undefined); + expect(fetchedUser.get('ssn')).toBe(undefined); done(); }) .catch(done.fail) ); }); - it("should not be able to get user PII via API with Get", done => { + it('should not be able to get user PII via API with Get', done => { Parse.User.logOut().then(() => new Parse.Query(Parse.User) .get(user.id) .then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(undefined); - expect(fetchedUser.get("zip")).toBe(undefined); - expect(fetchedUser.get("ssn")).toBe(undefined); + expect(fetchedUser.get('email')).toBe(undefined); + expect(fetchedUser.get('zip')).toBe(undefined); + expect(fetchedUser.get('ssn')).toBe(undefined); done(); }) .catch(done.fail) ); }); - it("should not get user PII via REST by ID", done => { + it('should not get user PII via REST by ID', done => { request({ url: `http://localhost:8378/1/classes/_User/${user.id}`, json: true, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Javascript-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Javascript-Key': 'test', }, }) .then(response => { @@ -661,49 +661,49 @@ describe("Personally Identifiable Information", () => { }); // Even with an authenticated user, Public read ACL should never expose sensitive data. - describe("with another authenticated user", () => { + describe('with another authenticated user', () => { let anotherUser; beforeEach(async done => { - return Parse.User.signUp("another", "abc") + return Parse.User.signUp('another', 'abc') .then(loggedInUser => (anotherUser = loggedInUser)) - .then(() => Parse.User.logIn(anotherUser.get("username"), "abc")) + .then(() => Parse.User.logIn(anotherUser.get('username'), 'abc')) .then(() => done()); }); - it("should not be able to get user PII via API with object", done => { + it('should not be able to get user PII via API with object', done => { const userObj = new (Parse.Object.extend(Parse.User))(); userObj.id = user.id; userObj .fetch() .then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(undefined); + expect(fetchedUser.get('email')).toBe(undefined); }) .then(done) .catch(done.fail); }); - it("should not be able to get user PII via API with Find", done => { + it('should not be able to get user PII via API with Find', done => { new Parse.Query(Parse.User) - .equalTo("objectId", user.id) + .equalTo('objectId', user.id) .find() .then(fetchedUser => { fetchedUser = fetchedUser[0]; - expect(fetchedUser.get("email")).toBe(undefined); - expect(fetchedUser.get("zip")).toBe(undefined); - expect(fetchedUser.get("ssn")).toBe(undefined); + expect(fetchedUser.get('email')).toBe(undefined); + expect(fetchedUser.get('zip')).toBe(undefined); + expect(fetchedUser.get('ssn')).toBe(undefined); done(); }) .catch(done.fail); }); - it("should not be able to get user PII via API with Get", done => { + it('should not be able to get user PII via API with Get', done => { new Parse.Query(Parse.User) .get(user.id) .then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(undefined); - expect(fetchedUser.get("zip")).toBe(undefined); - expect(fetchedUser.get("ssn")).toBe(undefined); + expect(fetchedUser.get('email')).toBe(undefined); + expect(fetchedUser.get('zip')).toBe(undefined); + expect(fetchedUser.get('ssn')).toBe(undefined); done(); }) .catch(done.fail); @@ -712,130 +712,130 @@ describe("Personally Identifiable Information", () => { }); }); - describe("with configured sensitive fields via CLP", () => { + describe('with configured sensitive fields via CLP', () => { beforeEach(async () => { await reconfigureServer({ protectedFields: { - _User: { "*": ["ssn", "zip"], "role:Administrator": [] }, + _User: { '*': ['ssn', 'zip'], 'role:Administrator': [] }, }, }); }); - it("should be able to get own PII via API with object", done => { + it('should be able to get own PII via API with object', done => { const userObj = new (Parse.Object.extend(Parse.User))(); userObj.id = user.id; userObj.fetch().then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(EMAIL); - expect(fetchedUser.get("zip")).toBe(ZIP); - expect(fetchedUser.get("ssn")).toBe(SSN); + expect(fetchedUser.get('email')).toBe(EMAIL); + expect(fetchedUser.get('zip')).toBe(ZIP); + expect(fetchedUser.get('ssn')).toBe(SSN); done(); }, done.fail); }); - it("should not be able to get PII via API with object", done => { + it('should not be able to get PII via API with object', done => { Parse.User.logOut().then(() => { const userObj = new (Parse.Object.extend(Parse.User))(); userObj.id = user.id; userObj .fetch() .then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(undefined); - expect(fetchedUser.get("zip")).toBe(undefined); - expect(fetchedUser.get("ssn")).toBe(undefined); + expect(fetchedUser.get('email')).toBe(undefined); + expect(fetchedUser.get('zip')).toBe(undefined); + expect(fetchedUser.get('ssn')).toBe(undefined); }) .then(done) .catch(done.fail); }); }); - it("should be able to get PII via API with object using master key", done => { + it('should be able to get PII via API with object using master key', done => { Parse.User.logOut().then(() => { const userObj = new (Parse.Object.extend(Parse.User))(); userObj.id = user.id; userObj .fetch({ useMasterKey: true }) .then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(EMAIL); - expect(fetchedUser.get("zip")).toBe(ZIP); - expect(fetchedUser.get("ssn")).toBe(SSN); + expect(fetchedUser.get('email')).toBe(EMAIL); + expect(fetchedUser.get('zip')).toBe(ZIP); + expect(fetchedUser.get('ssn')).toBe(SSN); }, done.fail) .then(done) .catch(done.fail); }); }); - it("should be able to get own PII via API with Find", done => { + it('should be able to get own PII via API with Find', done => { new Parse.Query(Parse.User).first().then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(EMAIL); - expect(fetchedUser.get("zip")).toBe(ZIP); - expect(fetchedUser.get("ssn")).toBe(SSN); + expect(fetchedUser.get('email')).toBe(EMAIL); + expect(fetchedUser.get('zip')).toBe(ZIP); + expect(fetchedUser.get('ssn')).toBe(SSN); done(); }); }); - it("should not get PII via API with Find", done => { + it('should not get PII via API with Find', done => { Parse.User.logOut().then(() => new Parse.Query(Parse.User).first().then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(undefined); - expect(fetchedUser.get("zip")).toBe(undefined); - expect(fetchedUser.get("ssn")).toBe(undefined); + expect(fetchedUser.get('email')).toBe(undefined); + expect(fetchedUser.get('zip')).toBe(undefined); + expect(fetchedUser.get('ssn')).toBe(undefined); done(); }) ); }); - it("should get PII via API with Find using master key", done => { + it('should get PII via API with Find using master key', done => { Parse.User.logOut().then(() => new Parse.Query(Parse.User) .first({ useMasterKey: true }) .then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(EMAIL); - expect(fetchedUser.get("zip")).toBe(ZIP); - expect(fetchedUser.get("ssn")).toBe(SSN); + expect(fetchedUser.get('email')).toBe(EMAIL); + expect(fetchedUser.get('zip')).toBe(ZIP); + expect(fetchedUser.get('ssn')).toBe(SSN); done(); }) ); }); - it("should be able to get own PII via API with Get", done => { + it('should be able to get own PII via API with Get', done => { new Parse.Query(Parse.User).get(user.id).then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(EMAIL); - expect(fetchedUser.get("zip")).toBe(ZIP); - expect(fetchedUser.get("ssn")).toBe(SSN); + expect(fetchedUser.get('email')).toBe(EMAIL); + expect(fetchedUser.get('zip')).toBe(ZIP); + expect(fetchedUser.get('ssn')).toBe(SSN); done(); }); }); - it("should not get PII via API with Get", done => { + it('should not get PII via API with Get', done => { Parse.User.logOut().then(() => new Parse.Query(Parse.User).get(user.id).then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(undefined); - expect(fetchedUser.get("zip")).toBe(undefined); - expect(fetchedUser.get("ssn")).toBe(undefined); + expect(fetchedUser.get('email')).toBe(undefined); + expect(fetchedUser.get('zip')).toBe(undefined); + expect(fetchedUser.get('ssn')).toBe(undefined); done(); }) ); }); - it("should get PII via API with Get using master key", done => { + it('should get PII via API with Get using master key', done => { Parse.User.logOut().then(() => new Parse.Query(Parse.User) .get(user.id, { useMasterKey: true }) .then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(EMAIL); - expect(fetchedUser.get("zip")).toBe(ZIP); - expect(fetchedUser.get("ssn")).toBe(SSN); + expect(fetchedUser.get('email')).toBe(EMAIL); + expect(fetchedUser.get('zip')).toBe(ZIP); + expect(fetchedUser.get('ssn')).toBe(SSN); done(); }) ); }); - it("should not get PII via REST", done => { + it('should not get PII via REST', done => { request({ - url: "http://localhost:8378/1/classes/_User", + url: 'http://localhost:8378/1/classes/_User', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Javascript-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Javascript-Key': 'test', }, }) .then(response => { @@ -849,14 +849,14 @@ describe("Personally Identifiable Information", () => { .catch(done.fail); }); - it("should get PII via REST with self credentials", done => { + it('should get PII via REST with self credentials', done => { request({ - url: "http://localhost:8378/1/classes/_User", + url: 'http://localhost:8378/1/classes/_User', json: true, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Javascript-Key": "test", - "X-Parse-Session-Token": user.getSessionToken(), + 'X-Parse-Application-Id': 'test', + 'X-Parse-Javascript-Key': 'test', + 'X-Parse-Session-Token': user.getSessionToken(), }, }) .then( @@ -873,13 +873,13 @@ describe("Personally Identifiable Information", () => { .catch(done.fail); }); - it("should get PII via REST using master key", done => { + it('should get PII via REST using master key', done => { request({ - url: "http://localhost:8378/1/classes/_User", + url: 'http://localhost:8378/1/classes/_User', json: true, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Master-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Master-Key': 'test', }, }) .then( @@ -896,13 +896,13 @@ describe("Personally Identifiable Information", () => { .catch(done.fail); }); - it("should not get PII via REST by ID", done => { + it('should not get PII via REST by ID', done => { request({ url: `http://localhost:8378/1/classes/_User/${user.id}`, json: true, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Javascript-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Javascript-Key': 'test', }, }) .then( @@ -917,14 +917,14 @@ describe("Personally Identifiable Information", () => { .catch(done.fail); }); - it("should get PII via REST by ID with self credentials", done => { + it('should get PII via REST by ID with self credentials', done => { request({ url: `http://localhost:8378/1/classes/_User/${user.id}`, json: true, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Javascript-Key": "test", - "X-Parse-Session-Token": user.getSessionToken(), + 'X-Parse-Application-Id': 'test', + 'X-Parse-Javascript-Key': 'test', + 'X-Parse-Session-Token': user.getSessionToken(), }, }) .then( @@ -939,13 +939,13 @@ describe("Personally Identifiable Information", () => { .catch(done.fail); }); - it("should get PII via REST by ID with master key", done => { + it('should get PII via REST by ID with master key', done => { request({ url: `http://localhost:8378/1/classes/_User/${user.id}`, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Javascript-Key": "test", - "X-Parse-Master-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Javascript-Key': 'test', + 'X-Parse-Master-Key': 'test', }, }) .then( @@ -962,19 +962,19 @@ describe("Personally Identifiable Information", () => { }); // Explicit ACL should be able to read sensitive information - describe("with privileged user CLP", () => { + describe('with privileged user CLP', () => { let adminUser; beforeEach(async done => { const adminRole = await new Parse.Role( - "Administrator", + 'Administrator', new Parse.ACL() ).save(null, { useMasterKey: true, }); const managementRole = new Parse.Role( - "managementOf_user" + user.id, + 'managementOf_user' + user.id, new Parse.ACL(user) ); managementRole.getRoles().add(adminRole); @@ -984,8 +984,8 @@ describe("Personally Identifiable Information", () => { userACL.setReadAccess(managementRole, true); await user.setACL(userACL).save(null, { useMasterKey: true }); - adminUser = await Parse.User.signUp("administrator", "secure"); - adminUser = await Parse.User.logIn(adminUser.get("username"), "secure"); + adminUser = await Parse.User.signUp('administrator', 'secure'); + adminUser = await Parse.User.logIn(adminUser.get('username'), 'secure'); await adminRole .getUsers() .add(adminUser) @@ -994,52 +994,52 @@ describe("Personally Identifiable Information", () => { done(); }); - it("privileged user should be able to get user PII via API with object", done => { + it('privileged user should be able to get user PII via API with object', done => { const userObj = new (Parse.Object.extend(Parse.User))(); userObj.id = user.id; userObj .fetch() .then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(EMAIL); + expect(fetchedUser.get('email')).toBe(EMAIL); }) .then(done) .catch(done.fail); }); - it("privileged user should be able to get user PII via API with Find", done => { + it('privileged user should be able to get user PII via API with Find', done => { new Parse.Query(Parse.User) - .equalTo("objectId", user.id) + .equalTo('objectId', user.id) .find() .then(fetchedUser => { fetchedUser = fetchedUser[0]; - expect(fetchedUser.get("email")).toBe(EMAIL); - expect(fetchedUser.get("zip")).toBe(ZIP); - expect(fetchedUser.get("ssn")).toBe(SSN); + expect(fetchedUser.get('email')).toBe(EMAIL); + expect(fetchedUser.get('zip')).toBe(ZIP); + expect(fetchedUser.get('ssn')).toBe(SSN); done(); }) .catch(done.fail); }); - it("privileged user should be able to get user PII via API with Get", done => { + it('privileged user should be able to get user PII via API with Get', done => { new Parse.Query(Parse.User) .get(user.id) .then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(EMAIL); - expect(fetchedUser.get("zip")).toBe(ZIP); - expect(fetchedUser.get("ssn")).toBe(SSN); + expect(fetchedUser.get('email')).toBe(EMAIL); + expect(fetchedUser.get('zip')).toBe(ZIP); + expect(fetchedUser.get('ssn')).toBe(SSN); done(); }) .catch(done.fail); }); - it("privileged user should get user PII via REST by ID", done => { + it('privileged user should get user PII via REST by ID', done => { request({ url: `http://localhost:8378/1/classes/_User/${user.id}`, json: true, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Javascript-Key": "test", - "X-Parse-Session-Token": adminUser.getSessionToken(), + 'X-Parse-Application-Id': 'test', + 'X-Parse-Javascript-Key': 'test', + 'X-Parse-Session-Token': adminUser.getSessionToken(), }, }) .then(response => { @@ -1054,7 +1054,7 @@ describe("Personally Identifiable Information", () => { }); // Public access ACL should always hide sensitive information - describe("with public read ACL", () => { + describe('with public read ACL', () => { beforeEach(async done => { const userACL = new Parse.ACL(); userACL.setPublicReadAccess(true); @@ -1062,57 +1062,57 @@ describe("Personally Identifiable Information", () => { done(); }); - it("should not be able to get user PII via API with object", done => { + it('should not be able to get user PII via API with object', done => { Parse.User.logOut().then(() => { const userObj = new (Parse.Object.extend(Parse.User))(); userObj.id = user.id; userObj .fetch() .then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(undefined); + expect(fetchedUser.get('email')).toBe(undefined); }) .then(done) .catch(done.fail); }); }); - it("should not be able to get user PII via API with Find", done => { + it('should not be able to get user PII via API with Find', done => { Parse.User.logOut().then(() => new Parse.Query(Parse.User) - .equalTo("objectId", user.id) + .equalTo('objectId', user.id) .find() .then(fetchedUser => { fetchedUser = fetchedUser[0]; - expect(fetchedUser.get("email")).toBe(undefined); - expect(fetchedUser.get("zip")).toBe(undefined); - expect(fetchedUser.get("ssn")).toBe(undefined); + expect(fetchedUser.get('email')).toBe(undefined); + expect(fetchedUser.get('zip')).toBe(undefined); + expect(fetchedUser.get('ssn')).toBe(undefined); done(); }) .catch(done.fail) ); }); - it("should not be able to get user PII via API with Get", done => { + it('should not be able to get user PII via API with Get', done => { Parse.User.logOut().then(() => new Parse.Query(Parse.User) .get(user.id) .then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(undefined); - expect(fetchedUser.get("zip")).toBe(undefined); - expect(fetchedUser.get("ssn")).toBe(undefined); + expect(fetchedUser.get('email')).toBe(undefined); + expect(fetchedUser.get('zip')).toBe(undefined); + expect(fetchedUser.get('ssn')).toBe(undefined); done(); }) .catch(done.fail) ); }); - it("should not get user PII via REST by ID", done => { + it('should not get user PII via REST by ID', done => { request({ url: `http://localhost:8378/1/classes/_User/${user.id}`, json: true, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Javascript-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Javascript-Key': 'test', }, }) .then(response => { @@ -1126,87 +1126,87 @@ describe("Personally Identifiable Information", () => { }); // Even with an authenticated user, Public read ACL should never expose sensitive data. - describe("with another authenticated user", () => { + describe('with another authenticated user', () => { let anotherUser; - const ANOTHER_EMAIL = "another@bar.com"; + const ANOTHER_EMAIL = 'another@bar.com'; beforeEach(async done => { - return Parse.User.signUp("another", "abc") + return Parse.User.signUp('another', 'abc') .then(loggedInUser => (anotherUser = loggedInUser)) - .then(() => Parse.User.logIn(anotherUser.get("username"), "abc")) + .then(() => Parse.User.logIn(anotherUser.get('username'), 'abc')) .then(() => anotherUser - .set("email", ANOTHER_EMAIL) - .set("zip", ZIP) - .set("ssn", SSN) + .set('email', ANOTHER_EMAIL) + .set('zip', ZIP) + .set('ssn', SSN) .save() ) .then(() => done()); }); - it("should not be able to get user PII via API with object", done => { + it('should not be able to get user PII via API with object', done => { const userObj = new (Parse.Object.extend(Parse.User))(); userObj.id = user.id; userObj .fetch() .then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(undefined); + expect(fetchedUser.get('email')).toBe(undefined); }) .then(done) .catch(done.fail); }); - it("should not be able to get user PII via API with Find", done => { + it('should not be able to get user PII via API with Find', done => { new Parse.Query(Parse.User) - .equalTo("objectId", user.id) + .equalTo('objectId', user.id) .find() .then(fetchedUser => { fetchedUser = fetchedUser[0]; - expect(fetchedUser.get("email")).toBe(undefined); - expect(fetchedUser.get("zip")).toBe(undefined); - expect(fetchedUser.get("ssn")).toBe(undefined); + expect(fetchedUser.get('email')).toBe(undefined); + expect(fetchedUser.get('zip')).toBe(undefined); + expect(fetchedUser.get('ssn')).toBe(undefined); done(); }) .catch(done.fail); }); - it("should not be able to get user PII via API with Find without constraints", done => { + it('should not be able to get user PII via API with Find without constraints', done => { new Parse.Query(Parse.User) .find() .then(fetchedUsers => { const notCurrentUser = fetchedUsers.find( u => u.id !== anotherUser.id ); - expect(notCurrentUser.get("email")).toBe(undefined); - expect(notCurrentUser.get("zip")).toBe(undefined); - expect(notCurrentUser.get("ssn")).toBe(undefined); + expect(notCurrentUser.get('email')).toBe(undefined); + expect(notCurrentUser.get('zip')).toBe(undefined); + expect(notCurrentUser.get('ssn')).toBe(undefined); done(); }) .catch(done.fail); }); - it("should be able to get own PII via API with Find without constraints", done => { + it('should be able to get own PII via API with Find without constraints', done => { new Parse.Query(Parse.User) .find() .then(fetchedUsers => { const currentUser = fetchedUsers.find( u => u.id === anotherUser.id ); - expect(currentUser.get("email")).toBe(ANOTHER_EMAIL); - expect(currentUser.get("zip")).toBe(ZIP); - expect(currentUser.get("ssn")).toBe(SSN); + expect(currentUser.get('email')).toBe(ANOTHER_EMAIL); + expect(currentUser.get('zip')).toBe(ZIP); + expect(currentUser.get('ssn')).toBe(SSN); done(); }) .catch(done.fail); }); - it("should not be able to get user PII via API with Get", done => { + it('should not be able to get user PII via API with Get', done => { new Parse.Query(Parse.User) .get(user.id) .then(fetchedUser => { - expect(fetchedUser.get("email")).toBe(undefined); - expect(fetchedUser.get("zip")).toBe(undefined); - expect(fetchedUser.get("ssn")).toBe(undefined); + expect(fetchedUser.get('email')).toBe(undefined); + expect(fetchedUser.get('zip')).toBe(undefined); + expect(fetchedUser.get('ssn')).toBe(undefined); done(); }) .catch(done.fail); diff --git a/spec/Utils.spec.js b/spec/Utils.spec.js index 83a1832423..fe4c8aef67 100644 --- a/spec/Utils.spec.js +++ b/spec/Utils.spec.js @@ -1,12 +1,12 @@ -const Utils = require("../src/Utils"); +const Utils = require('../src/Utils'); -describe("Utils", () => { - describe("encodeForUrl", () => { - it("should properly escape email with all special ASCII characters for use in URLs", async () => { +describe('Utils', () => { + describe('encodeForUrl', () => { + it('should properly escape email with all special ASCII characters for use in URLs', async () => { const values = [ { input: `!\"'),.:;<>?]^}`, - output: "%21%22%27%29%2C%2E%3A%3B%3C%3E%3F%5D%5E%7D", + output: '%21%22%27%29%2C%2E%3A%3B%3C%3E%3F%5D%5E%7D', }, ]; for (const value of values) { @@ -15,8 +15,8 @@ describe("Utils", () => { }); }); - describe("addNestedKeysToRoot", () => { - it("should move the nested keys to root of object", async () => { + describe('addNestedKeysToRoot', () => { + it('should move the nested keys to root of object', async () => { const obj = { a: 1, b: { @@ -25,7 +25,7 @@ describe("Utils", () => { }, e: 4, }; - Utils.addNestedKeysToRoot(obj, "b"); + Utils.addNestedKeysToRoot(obj, 'b'); expect(obj).toEqual({ a: 1, c: 2, @@ -34,25 +34,25 @@ describe("Utils", () => { }); }); - it("should not modify the object if the key does not exist", async () => { + it('should not modify the object if the key does not exist', async () => { const obj = { a: 1, e: 4, }; - Utils.addNestedKeysToRoot(obj, "b"); + Utils.addNestedKeysToRoot(obj, 'b'); expect(obj).toEqual({ a: 1, e: 4, }); }); - it("should not modify the object if the key is not an object", () => { + it('should not modify the object if the key is not an object', () => { const obj = { a: 1, b: 2, e: 4, }; - Utils.addNestedKeysToRoot(obj, "b"); + Utils.addNestedKeysToRoot(obj, 'b'); expect(obj).toEqual({ a: 1, b: 2, diff --git a/spec/ValidationAndPasswordsReset.spec.js b/spec/ValidationAndPasswordsReset.spec.js index 0bb52eb504..6df0437b5c 100644 --- a/spec/ValidationAndPasswordsReset.spec.js +++ b/spec/ValidationAndPasswordsReset.spec.js @@ -1,43 +1,43 @@ -"use strict"; +'use strict'; -const MockEmailAdapterWithOptions = require("./support/MockEmailAdapterWithOptions"); -const request = require("../lib/request"); -const Config = require("../lib/Config"); -const Auth = require("../lib/Auth"); +const MockEmailAdapterWithOptions = require('./support/MockEmailAdapterWithOptions'); +const request = require('../lib/request'); +const Config = require('../lib/Config'); +const Auth = require('../lib/Auth'); -describe("Custom Pages, Email Verification, Password Reset", () => { - it("should set the custom pages", done => { +describe('Custom Pages, Email Verification, Password Reset', () => { + it('should set the custom pages', done => { reconfigureServer({ - appName: "unused", + appName: 'unused', customPages: { - invalidLink: "myInvalidLink", - verifyEmailSuccess: "myVerifyEmailSuccess", - choosePassword: "myChoosePassword", - passwordResetSuccess: "myPasswordResetSuccess", - parseFrameURL: "http://example.com/handle-parse-iframe", + invalidLink: 'myInvalidLink', + verifyEmailSuccess: 'myVerifyEmailSuccess', + choosePassword: 'myChoosePassword', + passwordResetSuccess: 'myPasswordResetSuccess', + parseFrameURL: 'http://example.com/handle-parse-iframe', }, - publicServerURL: "https://my.public.server.com/1", + publicServerURL: 'https://my.public.server.com/1', }).then(() => { - const config = Config.get("test"); - expect(config.invalidLinkURL).toEqual("myInvalidLink"); - expect(config.verifyEmailSuccessURL).toEqual("myVerifyEmailSuccess"); - expect(config.choosePasswordURL).toEqual("myChoosePassword"); - expect(config.passwordResetSuccessURL).toEqual("myPasswordResetSuccess"); + const config = Config.get('test'); + expect(config.invalidLinkURL).toEqual('myInvalidLink'); + expect(config.verifyEmailSuccessURL).toEqual('myVerifyEmailSuccess'); + expect(config.choosePasswordURL).toEqual('myChoosePassword'); + expect(config.passwordResetSuccessURL).toEqual('myPasswordResetSuccess'); expect(config.parseFrameURL).toEqual( - "http://example.com/handle-parse-iframe" + 'http://example.com/handle-parse-iframe' ); expect(config.verifyEmailURL).toEqual( - "https://my.public.server.com/1/apps/test/verify_email" + 'https://my.public.server.com/1/apps/test/verify_email' ); expect(config.requestResetPasswordURL).toEqual( - "https://my.public.server.com/1/apps/test/request_password_reset" + 'https://my.public.server.com/1/apps/test/request_password_reset' ); done(); }); }); - it_id("5e558687-40f3-496c-9e4f-af6100bd1b2f")(it)( - "sends verification email if email verification is enabled", + it_id('5e558687-40f3-496c-9e4f-af6100bd1b2f')(it)( + 'sends verification email if email verification is enabled', done => { const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), @@ -45,81 +45,81 @@ describe("Custom Pages, Email Verification, Password Reset", () => { sendMail: () => Promise.resolve(), }; reconfigureServer({ - appName: "unused", + appName: 'unused', verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(async () => { - spyOn(emailAdapter, "sendVerificationEmail"); + spyOn(emailAdapter, 'sendVerificationEmail'); const user = new Parse.User(); - user.setPassword("asdf"); - user.setUsername("zxcv"); - user.setEmail("testIfEnabled@parse.com"); + user.setPassword('asdf'); + user.setUsername('zxcv'); + user.setEmail('testIfEnabled@parse.com'); await user.signUp(); await jasmine.timeout(); expect(emailAdapter.sendVerificationEmail).toHaveBeenCalled(); user.fetch().then(() => { - expect(user.get("emailVerified")).toEqual(false); + expect(user.get('emailVerified')).toEqual(false); done(); }); }); } ); - it("does not send verification email when verification is enabled and email is not set", done => { + it('does not send verification email when verification is enabled and email is not set', done => { const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), sendPasswordResetEmail: () => Promise.resolve(), sendMail: () => Promise.resolve(), }; reconfigureServer({ - appName: "unused", + appName: 'unused', verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(async () => { - spyOn(emailAdapter, "sendVerificationEmail"); + spyOn(emailAdapter, 'sendVerificationEmail'); const user = new Parse.User(); - user.setPassword("asdf"); - user.setUsername("zxcv"); + user.setPassword('asdf'); + user.setUsername('zxcv'); await user.signUp(); expect(emailAdapter.sendVerificationEmail).not.toHaveBeenCalled(); user.fetch().then(() => { - expect(user.get("emailVerified")).toEqual(undefined); + expect(user.get('emailVerified')).toEqual(undefined); done(); }); }); }); - it("does send a validation email when updating the email", done => { + it('does send a validation email when updating the email', done => { const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), sendPasswordResetEmail: () => Promise.resolve(), sendMail: () => Promise.resolve(), }; reconfigureServer({ - appName: "unused", + appName: 'unused', verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(async () => { - spyOn(emailAdapter, "sendVerificationEmail"); + spyOn(emailAdapter, 'sendVerificationEmail'); const user = new Parse.User(); - user.setPassword("asdf"); - user.setUsername("zxcv"); + user.setPassword('asdf'); + user.setUsername('zxcv'); await user.signUp(); expect(emailAdapter.sendVerificationEmail).not.toHaveBeenCalled(); user .fetch() .then(user => { - user.set("email", "testWhenUpdating@parse.com"); + user.set('email', 'testWhenUpdating@parse.com'); return user.save(); }) .then(user => { return user.fetch(); }) .then(() => { - expect(user.get("emailVerified")).toEqual(false); + expect(user.get('emailVerified')).toEqual(false); // Wait as on update email, we need to fetch the username setTimeout(function () { expect(emailAdapter.sendVerificationEmail).toHaveBeenCalled(); @@ -129,34 +129,34 @@ describe("Custom Pages, Email Verification, Password Reset", () => { }); }); - it("does send a validation email with valid verification link when updating the email", async done => { + it('does send a validation email with valid verification link when updating the email', async done => { const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), sendPasswordResetEmail: () => Promise.resolve(), sendMail: () => Promise.resolve(), }; await reconfigureServer({ - appName: "unused", + appName: 'unused', verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); - spyOn(emailAdapter, "sendVerificationEmail").and.callFake(options => { + spyOn(emailAdapter, 'sendVerificationEmail').and.callFake(options => { expect(options.link).not.toBeNull(); expect(options.link).not.toMatch(/token=undefined/); expect(options.link).not.toMatch(/username=undefined/); Promise.resolve(); }); const user = new Parse.User(); - user.setPassword("asdf"); - user.setUsername("zxcv"); + user.setPassword('asdf'); + user.setUsername('zxcv'); await user.signUp(); expect(emailAdapter.sendVerificationEmail).not.toHaveBeenCalled(); await user.fetch(); - user.set("email", "testValidLinkWhenUpdating@parse.com"); + user.set('email', 'testValidLinkWhenUpdating@parse.com'); await user.save(); await user.fetch(); - expect(user.get("emailVerified")).toEqual(false); + expect(user.get('emailVerified')).toEqual(false); // Wait as on update email, we need to fetch the username setTimeout(function () { expect(emailAdapter.sendVerificationEmail).toHaveBeenCalled(); @@ -164,20 +164,20 @@ describe("Custom Pages, Email Verification, Password Reset", () => { }, 200); }); - it_id("33d31119-c724-4f5d-83ec-f56815d23df3")(it)( - "does send with a simple adapter", + it_id('33d31119-c724-4f5d-83ec-f56815d23df3')(it)( + 'does send with a simple adapter', done => { let calls = 0; const emailAdapter = { sendMail: function (options) { - expect(options.to).toBe("testSendSimpleAdapter@parse.com"); + expect(options.to).toBe('testSendSimpleAdapter@parse.com'); if (calls == 0) { expect(options.subject).toEqual( - "Please verify your e-mail for My Cool App" + 'Please verify your e-mail for My Cool App' ); expect(options.text.match(/verify_email/)).not.toBe(null); } else if (calls == 1) { - expect(options.subject).toEqual("Password Reset for My Cool App"); + expect(options.subject).toEqual('Password Reset for My Cool App'); expect(options.text.match(/request_password_reset/)).not.toBe(null); } calls++; @@ -185,15 +185,15 @@ describe("Custom Pages, Email Verification, Password Reset", () => { }, }; reconfigureServer({ - appName: "My Cool App", + appName: 'My Cool App', verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(async () => { const user = new Parse.User(); - user.setPassword("asdf"); - user.setUsername("zxcv"); - user.set("email", "testSendSimpleAdapter@parse.com"); + user.setPassword('asdf'); + user.setUsername('zxcv'); + user.set('email', 'testSendSimpleAdapter@parse.com'); await user.signUp(); await jasmine.timeout(); expect(calls).toBe(1); @@ -204,9 +204,9 @@ describe("Custom Pages, Email Verification, Password Reset", () => { }) .then(() => { return Parse.User.requestPasswordReset( - "testSendSimpleAdapter@parse.com" + 'testSendSimpleAdapter@parse.com' ).catch(() => { - fail("Should not fail requesting a password"); + fail('Should not fail requesting a password'); done(); }); }) @@ -218,36 +218,36 @@ describe("Custom Pages, Email Verification, Password Reset", () => { } ); - it("prevents user from login if email is not verified but preventLoginWithUnverifiedEmail is set to true", done => { + it('prevents user from login if email is not verified but preventLoginWithUnverifiedEmail is set to true', done => { reconfigureServer({ - appName: "test", - publicServerURL: "http://localhost:1337/1", + appName: 'test', + publicServerURL: 'http://localhost:1337/1', verifyUserEmails: true, preventLoginWithUnverifiedEmail: true, emailAdapter: MockEmailAdapterWithOptions({ - fromAddress: "parse@example.com", - apiKey: "k", - domain: "d", + fromAddress: 'parse@example.com', + apiKey: 'k', + domain: 'd', }), }) .then(() => { const user = new Parse.User(); - user.setPassword("asdf"); - user.setUsername("zxcv"); - user.set("email", "testInvalidConfig@parse.com"); + user.setPassword('asdf'); + user.setUsername('zxcv'); + user.set('email', 'testInvalidConfig@parse.com'); user .signUp(null) .then(user => { expect(user.getSessionToken()).toBe(undefined); - return Parse.User.logIn("zxcv", "asdf"); + return Parse.User.logIn('zxcv', 'asdf'); }) .then( () => { - fail("login should have failed"); + fail('login should have failed'); done(); }, error => { - expect(error.message).toEqual("User email is not verified."); + expect(error.message).toEqual('User email is not verified.'); done(); } ); @@ -258,36 +258,36 @@ describe("Custom Pages, Email Verification, Password Reset", () => { }); }); - it("prevents user from signup and login if email is not verified and preventLoginWithUnverifiedEmail is set to function returning true", async () => { + it('prevents user from signup and login if email is not verified and preventLoginWithUnverifiedEmail is set to function returning true', async () => { await reconfigureServer({ - appName: "test", - publicServerURL: "http://localhost:1337/1", + appName: 'test', + publicServerURL: 'http://localhost:1337/1', verifyUserEmails: async () => true, preventLoginWithUnverifiedEmail: async () => true, preventSignupWithUnverifiedEmail: true, emailAdapter: MockEmailAdapterWithOptions({ - fromAddress: "parse@example.com", - apiKey: "k", - domain: "d", + fromAddress: 'parse@example.com', + apiKey: 'k', + domain: 'd', }), }); const user = new Parse.User(); - user.setPassword("asdf"); - user.setUsername("zxcv"); - user.set("email", "testInvalidConfig@parse.com"); + user.setPassword('asdf'); + user.setUsername('zxcv'); + user.set('email', 'testInvalidConfig@parse.com'); const signupRes = await user.signUp(null).catch(e => e); - expect(signupRes.message).toEqual("User email is not verified."); + expect(signupRes.message).toEqual('User email is not verified.'); - const loginRes = await Parse.User.logIn("zxcv", "asdf").catch(e => e); - expect(loginRes.message).toEqual("User email is not verified."); + const loginRes = await Parse.User.logIn('zxcv', 'asdf').catch(e => e); + expect(loginRes.message).toEqual('User email is not verified.'); }); - it("provides function arguments in verifyUserEmails on login", async () => { + it('provides function arguments in verifyUserEmails on login', async () => { const user = new Parse.User(); - user.setUsername("user"); - user.setPassword("pass"); - user.set("email", "test@example.com"); + user.setUsername('user'); + user.setPassword('pass'); + user.set('email', 'test@example.com'); await user.signUp(); const verifyUserEmails = { @@ -301,28 +301,28 @@ describe("Custom Pages, Email Verification, Password Reset", () => { }; const verifyUserEmailsSpy = spyOn( verifyUserEmails, - "method" + 'method' ).and.callThrough(); await reconfigureServer({ - appName: "test", - publicServerURL: "http://localhost:1337/1", + appName: 'test', + publicServerURL: 'http://localhost:1337/1', verifyUserEmails: verifyUserEmails.method, preventLoginWithUnverifiedEmail: verifyUserEmails.method, preventSignupWithUnverifiedEmail: true, emailAdapter: MockEmailAdapterWithOptions({ - fromAddress: "parse@example.com", - apiKey: "k", - domain: "d", + fromAddress: 'parse@example.com', + apiKey: 'k', + domain: 'd', }), }); - const res = await Parse.User.logIn("user", "pass").catch(e => e); + const res = await Parse.User.logIn('user', 'pass').catch(e => e); expect(res.code).toBe(205); expect(verifyUserEmailsSpy).toHaveBeenCalledTimes(2); }); - it_id("2a5d24be-2ca5-4385-b580-1423bd392e43")(it)( - "allows user to login only after user clicks on the link to confirm email address if preventLoginWithUnverifiedEmail is set to true", + it_id('2a5d24be-2ca5-4385-b580-1423bd392e43')(it)( + 'allows user to login only after user clicks on the link to confirm email address if preventLoginWithUnverifiedEmail is set to true', async () => { let sendEmailOptions; const emailAdapter = { @@ -333,16 +333,16 @@ describe("Custom Pages, Email Verification, Password Reset", () => { sendMail: () => {}, }; await reconfigureServer({ - appName: "emailing app", + appName: 'emailing app', verifyUserEmails: true, preventLoginWithUnverifiedEmail: true, emailAdapter: emailAdapter, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); let user = new Parse.User(); - user.setPassword("other-password"); - user.setUsername("user"); - user.set("email", "user@example.com"); + user.setPassword('other-password'); + user.setUsername('user'); + user.set('email', 'user@example.com'); await user.signUp(); await jasmine.timeout(); expect(sendEmailOptions).not.toBeUndefined(); @@ -352,44 +352,44 @@ describe("Custom Pages, Email Verification, Password Reset", () => { }); expect(response.status).toEqual(302); expect(response.text).toEqual( - "Found. Redirecting to http://localhost:8378/1/apps/verify_email_success.html" + 'Found. Redirecting to http://localhost:8378/1/apps/verify_email_success.html' ); user = await new Parse.Query(Parse.User).first({ useMasterKey: true }); - expect(user.get("emailVerified")).toEqual(true); - user = await Parse.User.logIn("user", "other-password"); - expect(typeof user).toBe("object"); - expect(user.get("emailVerified")).toBe(true); + expect(user.get('emailVerified')).toEqual(true); + user = await Parse.User.logIn('user', 'other-password'); + expect(typeof user).toBe('object'); + expect(user.get('emailVerified')).toBe(true); } ); - it("allows user to login if email is not verified but preventLoginWithUnverifiedEmail is set to false", done => { + it('allows user to login if email is not verified but preventLoginWithUnverifiedEmail is set to false', done => { reconfigureServer({ - appName: "test", - publicServerURL: "http://localhost:1337/1", + appName: 'test', + publicServerURL: 'http://localhost:1337/1', verifyUserEmails: true, preventLoginWithUnverifiedEmail: false, emailAdapter: MockEmailAdapterWithOptions({ - fromAddress: "parse@example.com", - apiKey: "k", - domain: "d", + fromAddress: 'parse@example.com', + apiKey: 'k', + domain: 'd', }), }) .then(() => { const user = new Parse.User(); - user.setPassword("asdf"); - user.setUsername("zxcv"); - user.set("email", "testInvalidConfig@parse.com"); + user.setPassword('asdf'); + user.setUsername('zxcv'); + user.set('email', 'testInvalidConfig@parse.com'); user .signUp(null) - .then(() => Parse.User.logIn("zxcv", "asdf")) + .then(() => Parse.User.logIn('zxcv', 'asdf')) .then( user => { - expect(typeof user).toBe("object"); - expect(user.get("emailVerified")).toBe(false); + expect(typeof user).toBe('object'); + expect(user.get('emailVerified')).toBe(false); done(); }, () => { - fail("login should have succeeded"); + fail('login should have succeeded'); done(); } ); @@ -400,8 +400,8 @@ describe("Custom Pages, Email Verification, Password Reset", () => { }); }); - it_id("a18a07af-0319-4f15-8237-28070c5948fa")(it)( - "does not allow signup with preventSignupWithUnverified", + it_id('a18a07af-0319-4f15-8237-28070c5948fa')(it)( + 'does not allow signup with preventSignupWithUnverified', async () => { let sendEmailOptions; const emailAdapter = { @@ -412,21 +412,21 @@ describe("Custom Pages, Email Verification, Password Reset", () => { sendMail: () => {}, }; await reconfigureServer({ - appName: "test", - publicServerURL: "http://localhost:1337/1", + appName: 'test', + publicServerURL: 'http://localhost:1337/1', verifyUserEmails: true, preventLoginWithUnverifiedEmail: true, preventSignupWithUnverifiedEmail: true, emailAdapter, }); const newUser = new Parse.User(); - newUser.setPassword("asdf"); - newUser.setUsername("zxcv"); - newUser.set("email", "test@example.com"); + newUser.setPassword('asdf'); + newUser.setUsername('zxcv'); + newUser.set('email', 'test@example.com'); await expectAsync(newUser.signUp()).toBeRejectedWith( new Parse.Error( Parse.Error.EMAIL_NOT_FOUND, - "User email is not verified." + 'User email is not verified.' ) ); const user = await new Parse.Query(Parse.User).first({ @@ -437,34 +437,34 @@ describe("Custom Pages, Email Verification, Password Reset", () => { } ); - it("fails if you include an emailAdapter, set a publicServerURL, but have no appName and send a password reset email", done => { + it('fails if you include an emailAdapter, set a publicServerURL, but have no appName and send a password reset email', done => { reconfigureServer({ appName: undefined, - publicServerURL: "http://localhost:1337/1", + publicServerURL: 'http://localhost:1337/1', emailAdapter: MockEmailAdapterWithOptions({ - fromAddress: "parse@example.com", - apiKey: "k", - domain: "d", + fromAddress: 'parse@example.com', + apiKey: 'k', + domain: 'd', }), }) .then(() => { const user = new Parse.User(); - user.setPassword("asdf"); - user.setUsername("zxcv"); - user.set("email", "testInvalidConfig@parse.com"); + user.setPassword('asdf'); + user.setUsername('zxcv'); + user.set('email', 'testInvalidConfig@parse.com'); user .signUp(null) .then(() => - Parse.User.requestPasswordReset("testInvalidConfig@parse.com") + Parse.User.requestPasswordReset('testInvalidConfig@parse.com') ) .then( () => { - fail("sending password reset email should not have succeeded"); + fail('sending password reset email should not have succeeded'); done(); }, error => { expect(error.message).toEqual( - "An appName, publicServerURL, and emailAdapter are required for password reset and email verification functionality." + 'An appName, publicServerURL, and emailAdapter are required for password reset and email verification functionality.' ); done(); } @@ -476,33 +476,33 @@ describe("Custom Pages, Email Verification, Password Reset", () => { }); }); - it("fails if you include an emailAdapter, have an appName, but have no publicServerURL and send a password reset email", done => { + it('fails if you include an emailAdapter, have an appName, but have no publicServerURL and send a password reset email', done => { reconfigureServer({ appName: undefined, emailAdapter: MockEmailAdapterWithOptions({ - fromAddress: "parse@example.com", - apiKey: "k", - domain: "d", + fromAddress: 'parse@example.com', + apiKey: 'k', + domain: 'd', }), }) .then(() => { const user = new Parse.User(); - user.setPassword("asdf"); - user.setUsername("zxcv"); - user.set("email", "testInvalidConfig@parse.com"); + user.setPassword('asdf'); + user.setUsername('zxcv'); + user.set('email', 'testInvalidConfig@parse.com'); user .signUp(null) .then(() => - Parse.User.requestPasswordReset("testInvalidConfig@parse.com") + Parse.User.requestPasswordReset('testInvalidConfig@parse.com') ) .then( () => { - fail("sending password reset email should not have succeeded"); + fail('sending password reset email should not have succeeded'); done(); }, error => { expect(error.message).toEqual( - "An appName, publicServerURL, and emailAdapter are required for password reset and email verification functionality." + 'An appName, publicServerURL, and emailAdapter are required for password reset and email verification functionality.' ); done(); } @@ -514,30 +514,30 @@ describe("Custom Pages, Email Verification, Password Reset", () => { }); }); - it("fails if you set a publicServerURL, have an appName, but no emailAdapter and send a password reset email", done => { + it('fails if you set a publicServerURL, have an appName, but no emailAdapter and send a password reset email', done => { reconfigureServer({ - appName: "unused", - publicServerURL: "http://localhost:1337/1", + appName: 'unused', + publicServerURL: 'http://localhost:1337/1', emailAdapter: undefined, }) .then(() => { const user = new Parse.User(); - user.setPassword("asdf"); - user.setUsername("zxcv"); - user.set("email", "testInvalidConfig@parse.com"); + user.setPassword('asdf'); + user.setUsername('zxcv'); + user.set('email', 'testInvalidConfig@parse.com'); user .signUp(null) .then(() => - Parse.User.requestPasswordReset("testInvalidConfig@parse.com") + Parse.User.requestPasswordReset('testInvalidConfig@parse.com') ) .then( () => { - fail("sending password reset email should not have succeeded"); + fail('sending password reset email should not have succeeded'); done(); }, error => { expect(error.message).toEqual( - "An appName, publicServerURL, and emailAdapter are required for password reset and email verification functionality." + 'An appName, publicServerURL, and emailAdapter are required for password reset and email verification functionality.' ); done(); } @@ -549,25 +549,25 @@ describe("Custom Pages, Email Verification, Password Reset", () => { }); }); - it("succeeds sending a password reset email if appName, publicServerURL, and email adapter are provided", done => { + it('succeeds sending a password reset email if appName, publicServerURL, and email adapter are provided', done => { reconfigureServer({ - appName: "coolapp", - publicServerURL: "http://localhost:1337/1", + appName: 'coolapp', + publicServerURL: 'http://localhost:1337/1', emailAdapter: MockEmailAdapterWithOptions({ - fromAddress: "parse@example.com", - apiKey: "k", - domain: "d", + fromAddress: 'parse@example.com', + apiKey: 'k', + domain: 'd', }), }) .then(() => { const user = new Parse.User(); - user.setPassword("asdf"); - user.setUsername("zxcv"); - user.set("email", "testInvalidConfig@parse.com"); + user.setPassword('asdf'); + user.setUsername('zxcv'); + user.set('email', 'testInvalidConfig@parse.com'); user .signUp(null) .then(() => - Parse.User.requestPasswordReset("testInvalidConfig@parse.com") + Parse.User.requestPasswordReset('testInvalidConfig@parse.com') ) .then( () => { @@ -584,13 +584,13 @@ describe("Custom Pages, Email Verification, Password Reset", () => { }); }); - it("succeeds sending a password reset username if appName, publicServerURL, and email adapter are provided", done => { + it('succeeds sending a password reset username if appName, publicServerURL, and email adapter are provided', done => { const adapter = MockEmailAdapterWithOptions({ - fromAddress: "parse@example.com", - apiKey: "k", - domain: "d", + fromAddress: 'parse@example.com', + apiKey: 'k', + domain: 'd', sendMail: function (options) { - expect(options.to).toEqual("testValidConfig@parse.com"); + expect(options.to).toEqual('testValidConfig@parse.com'); return Promise.resolve(); }, }); @@ -598,20 +598,20 @@ describe("Custom Pages, Email Verification, Password Reset", () => { // delete that handler to force using the default delete adapter.sendPasswordResetEmail; - spyOn(adapter, "sendMail").and.callThrough(); + spyOn(adapter, 'sendMail').and.callThrough(); reconfigureServer({ - appName: "coolapp", - publicServerURL: "http://localhost:1337/1", + appName: 'coolapp', + publicServerURL: 'http://localhost:1337/1', emailAdapter: adapter, }) .then(() => { const user = new Parse.User(); - user.setPassword("asdf"); - user.setUsername("testValidConfig@parse.com"); + user.setPassword('asdf'); + user.setUsername('testValidConfig@parse.com'); user .signUp(null) .then(() => - Parse.User.requestPasswordReset("testValidConfig@parse.com") + Parse.User.requestPasswordReset('testValidConfig@parse.com') ) .then( () => { @@ -629,53 +629,53 @@ describe("Custom Pages, Email Verification, Password Reset", () => { }); }); - it("does not send verification email if email verification is disabled", done => { + it('does not send verification email if email verification is disabled', done => { const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), sendPasswordResetEmail: () => Promise.resolve(), sendMail: () => Promise.resolve(), }; reconfigureServer({ - appName: "unused", - publicServerURL: "http://localhost:1337/1", + appName: 'unused', + publicServerURL: 'http://localhost:1337/1', verifyUserEmails: false, emailAdapter: emailAdapter, }).then(async () => { - spyOn(emailAdapter, "sendVerificationEmail"); + spyOn(emailAdapter, 'sendVerificationEmail'); const user = new Parse.User(); - user.setPassword("asdf"); - user.setUsername("zxcv"); + user.setPassword('asdf'); + user.setUsername('zxcv'); await user.signUp(); await user.fetch(); expect(emailAdapter.sendVerificationEmail.calls.count()).toEqual(0); - expect(user.get("emailVerified")).toEqual(undefined); + expect(user.get('emailVerified')).toEqual(undefined); done(); }); }); - it_id("45f550a2-a2b2-4b2b-b533-ccbf96139cc9")(it)( - "receives the app name and user in the adapter", + it_id('45f550a2-a2b2-4b2b-b533-ccbf96139cc9')(it)( + 'receives the app name and user in the adapter', done => { let emailSent = false; const emailAdapter = { sendVerificationEmail: options => { - expect(options.appName).toEqual("emailing app"); - expect(options.user.get("email")).toEqual("user@parse.com"); + expect(options.appName).toEqual('emailing app'); + expect(options.user.get('email')).toEqual('user@parse.com'); emailSent = true; }, sendPasswordResetEmail: () => Promise.resolve(), sendMail: () => {}, }; reconfigureServer({ - appName: "emailing app", + appName: 'emailing app', verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(async () => { const user = new Parse.User(); - user.setPassword("asdf"); - user.setUsername("zxcv"); - user.set("email", "user@parse.com"); + user.setPassword('asdf'); + user.setUsername('zxcv'); + user.set('email', 'user@parse.com'); await user.signUp(); await jasmine.timeout(); expect(emailSent).toBe(true); @@ -684,8 +684,8 @@ describe("Custom Pages, Email Verification, Password Reset", () => { } ); - it_id("ea37ef62-aad8-4a17-8dfe-35e5b2986f0f")(it)( - "when you click the link in the email it sets emailVerified to true and redirects you", + it_id('ea37ef62-aad8-4a17-8dfe-35e5b2986f0f')(it)( + 'when you click the link in the email it sets emailVerified to true and redirects you', done => { const user = new Parse.User(); let sendEmailOptions; @@ -697,15 +697,15 @@ describe("Custom Pages, Email Verification, Password Reset", () => { sendMail: () => {}, }; reconfigureServer({ - appName: "emailing app", + appName: 'emailing app', verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }) .then(() => { - user.setPassword("other-password"); - user.setUsername("user"); - user.set("email", "user@parse.com"); + user.setPassword('other-password'); + user.setUsername('user'); + user.set('email', 'user@parse.com'); return user.signUp(); }) .then(() => jasmine.timeout()) @@ -717,18 +717,18 @@ describe("Custom Pages, Email Verification, Password Reset", () => { }).then(response => { expect(response.status).toEqual(302); expect(response.text).toEqual( - "Found. Redirecting to http://localhost:8378/1/apps/verify_email_success.html" + 'Found. Redirecting to http://localhost:8378/1/apps/verify_email_success.html' ); user .fetch() .then( () => { - expect(user.get("emailVerified")).toEqual(true); + expect(user.get('emailVerified')).toEqual(true); done(); }, err => { jfail(err); - fail("this should not fail"); + fail('this should not fail'); done(); } ) @@ -741,96 +741,96 @@ describe("Custom Pages, Email Verification, Password Reset", () => { } ); - it("redirects you to invalid link if you try to verify email incorrectly", done => { + it('redirects you to invalid link if you try to verify email incorrectly', done => { reconfigureServer({ - appName: "emailing app", + appName: 'emailing app', verifyUserEmails: true, emailAdapter: { sendVerificationEmail: () => Promise.resolve(), sendPasswordResetEmail: () => Promise.resolve(), sendMail: () => {}, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { request({ - url: "http://localhost:8378/1/apps/test/verify_email", + url: 'http://localhost:8378/1/apps/test/verify_email', followRedirects: false, }).then(response => { expect(response.status).toEqual(302); expect(response.text).toEqual( - "Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html" + 'Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html' ); done(); }); }); }); - it("redirects you to invalid verification link page if you try to validate a nonexistant users email", done => { + it('redirects you to invalid verification link page if you try to validate a nonexistant users email', done => { reconfigureServer({ - appName: "emailing app", + appName: 'emailing app', verifyUserEmails: true, emailAdapter: { sendVerificationEmail: () => Promise.resolve(), sendPasswordResetEmail: () => Promise.resolve(), sendMail: () => {}, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { request({ - url: "http://localhost:8378/1/apps/test/verify_email?token=asdfasdf", + url: 'http://localhost:8378/1/apps/test/verify_email?token=asdfasdf', followRedirects: false, }).then(response => { expect(response.status).toEqual(302); expect(response.text).toEqual( - "Found. Redirecting to http://localhost:8378/1/apps/invalid_verification_link.html?appId=test&token=asdfasdf" + 'Found. Redirecting to http://localhost:8378/1/apps/invalid_verification_link.html?appId=test&token=asdfasdf' ); done(); }); }); }); - it("redirects you to link send fail page if you try to resend a link for a nonexistant user", done => { + it('redirects you to link send fail page if you try to resend a link for a nonexistant user', done => { reconfigureServer({ - appName: "emailing app", + appName: 'emailing app', verifyUserEmails: true, emailAdapter: { sendVerificationEmail: () => Promise.resolve(), sendPasswordResetEmail: () => Promise.resolve(), sendMail: () => {}, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { request({ - url: "http://localhost:8378/1/apps/test/resend_verification_email", - method: "POST", + url: 'http://localhost:8378/1/apps/test/resend_verification_email', + method: 'POST', followRedirects: false, body: { - username: "sadfasga", + username: 'sadfasga', }, }).then(response => { expect(response.status).toEqual(302); expect(response.text).toEqual( - "Found. Redirecting to http://localhost:8378/1/apps/link_send_fail.html" + 'Found. Redirecting to http://localhost:8378/1/apps/link_send_fail.html' ); done(); }); }); }); - it("does not update email verified if you use an invalid token", done => { + it('does not update email verified if you use an invalid token', done => { const user = new Parse.User(); const emailAdapter = { sendVerificationEmail: () => { request({ - url: "http://localhost:8378/1/apps/test/verify_email?token=invalid", + url: 'http://localhost:8378/1/apps/test/verify_email?token=invalid', followRedirects: false, }).then(response => { expect(response.status).toEqual(302); expect(response.text).toEqual( - "Found. Redirecting to http://localhost:8378/1/apps/invalid_verification_link.html?appId=test&token=invalid" + 'Found. Redirecting to http://localhost:8378/1/apps/invalid_verification_link.html?appId=test&token=invalid' ); user.fetch().then(() => { - expect(user.get("emailVerified")).toEqual(false); + expect(user.get('emailVerified')).toEqual(false); done(); }); }); @@ -839,25 +839,25 @@ describe("Custom Pages, Email Verification, Password Reset", () => { sendMail: () => {}, }; reconfigureServer({ - appName: "emailing app", + appName: 'emailing app', verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { - user.setPassword("asdf"); - user.setUsername("zxcv"); - user.set("email", "user@parse.com"); + user.setPassword('asdf'); + user.setUsername('zxcv'); + user.set('email', 'user@parse.com'); user.signUp(null, { success: () => {}, error: function () { - fail("Failed to save user"); + fail('Failed to save user'); done(); }, }); }); }); - it("should send a password reset link", done => { + it('should send a password reset link', done => { const user = new Parse.User(); const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), @@ -876,19 +876,19 @@ describe("Custom Pages, Email Verification, Password Reset", () => { sendMail: () => {}, }; reconfigureServer({ - appName: "emailing app", + appName: 'emailing app', verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { - user.setPassword("asdf"); - user.setUsername("zxcv+zxcv"); - user.set("email", "user@parse.com"); + user.setPassword('asdf'); + user.setUsername('zxcv+zxcv'); + user.set('email', 'user@parse.com'); user.signUp().then(() => { - Parse.User.requestPasswordReset("user@parse.com", { + Parse.User.requestPasswordReset('user@parse.com', { error: err => { jfail(err); - fail("Should not fail requesting a password"); + fail('Should not fail requesting a password'); done(); }, }); @@ -896,31 +896,31 @@ describe("Custom Pages, Email Verification, Password Reset", () => { }); }); - it("redirects you to invalid link if you try to request password for a nonexistant users email", done => { + it('redirects you to invalid link if you try to request password for a nonexistant users email', done => { reconfigureServer({ - appName: "emailing app", + appName: 'emailing app', verifyUserEmails: true, emailAdapter: { sendVerificationEmail: () => Promise.resolve(), sendPasswordResetEmail: () => Promise.resolve(), sendMail: () => {}, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { request({ - url: "http://localhost:8378/1/apps/test/request_password_reset?token=asdfasdf", + url: 'http://localhost:8378/1/apps/test/request_password_reset?token=asdfasdf', followRedirects: false, }).then(response => { expect(response.status).toEqual(302); expect(response.text).toEqual( - "Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html" + 'Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html' ); done(); }); }); }); - it("should programmatically reset password", done => { + it('should programmatically reset password', done => { const user = new Parse.User(); const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), @@ -934,46 +934,46 @@ describe("Custom Pages, Email Verification, Password Reset", () => { /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; const match = response.text.match(re); if (!match) { - fail("should have a token"); + fail('should have a token'); done(); return; } const token = match[1]; request({ - url: "http://localhost:8378/1/apps/test/request_password_reset", - method: "POST", - body: { new_password: "hello", token, username: "zxcv" }, + url: 'http://localhost:8378/1/apps/test/request_password_reset', + method: 'POST', + body: { new_password: 'hello', token, username: 'zxcv' }, headers: { - "Content-Type": "application/x-www-form-urlencoded", + 'Content-Type': 'application/x-www-form-urlencoded', }, followRedirects: false, }).then(response => { expect(response.status).toEqual(302); expect(response.text).toEqual( - "Found. Redirecting to http://localhost:8378/1/apps/password_reset_success.html" + 'Found. Redirecting to http://localhost:8378/1/apps/password_reset_success.html' ); - Parse.User.logIn("zxcv", "hello").then( + Parse.User.logIn('zxcv', 'hello').then( function () { - const config = Config.get("test"); + const config = Config.get('test'); config.database.adapter .find( - "_User", + '_User', { fields: {} }, - { username: "zxcv" }, + { username: 'zxcv' }, { limit: 1 } ) .then(results => { // _perishable_token should be unset after reset password expect(results.length).toEqual(1); - expect(results[0]["_perishable_token"]).toEqual(undefined); + expect(results[0]['_perishable_token']).toEqual(undefined); done(); }); }, err => { jfail(err); - fail("should login with new password"); + fail('should login with new password'); done(); } ); @@ -983,19 +983,19 @@ describe("Custom Pages, Email Verification, Password Reset", () => { sendMail: () => {}, }; reconfigureServer({ - appName: "emailing app", + appName: 'emailing app', verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { - user.setPassword("asdf"); - user.setUsername("zxcv"); - user.set("email", "user@parse.com"); + user.setPassword('asdf'); + user.setUsername('zxcv'); + user.set('email', 'user@parse.com'); user.signUp().then(() => { - Parse.User.requestPasswordReset("user@parse.com", { + Parse.User.requestPasswordReset('user@parse.com', { error: err => { jfail(err); - fail("Should not fail"); + fail('Should not fail'); done(); }, }); @@ -1003,7 +1003,7 @@ describe("Custom Pages, Email Verification, Password Reset", () => { }); }); - it("should redirect with username encoded on success page", done => { + it('should redirect with username encoded on success page', done => { const user = new Parse.User(); const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), @@ -1017,24 +1017,24 @@ describe("Custom Pages, Email Verification, Password Reset", () => { /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; const match = response.text.match(re); if (!match) { - fail("should have a token"); + fail('should have a token'); done(); return; } const token = match[1]; request({ - url: "http://localhost:8378/1/apps/test/request_password_reset", - method: "POST", - body: { new_password: "hello", token, username: "zxcv+1" }, + url: 'http://localhost:8378/1/apps/test/request_password_reset', + method: 'POST', + body: { new_password: 'hello', token, username: 'zxcv+1' }, headers: { - "Content-Type": "application/x-www-form-urlencoded", + 'Content-Type': 'application/x-www-form-urlencoded', }, followRedirects: false, }).then(response => { expect(response.status).toEqual(302); expect(response.text).toEqual( - "Found. Redirecting to http://localhost:8378/1/apps/password_reset_success.html" + 'Found. Redirecting to http://localhost:8378/1/apps/password_reset_success.html' ); done(); }); @@ -1043,19 +1043,19 @@ describe("Custom Pages, Email Verification, Password Reset", () => { sendMail: () => {}, }; reconfigureServer({ - appName: "emailing app", + appName: 'emailing app', verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(() => { - user.setPassword("asdf"); - user.setUsername("zxcv+1"); - user.set("email", "user@parse.com"); + user.setPassword('asdf'); + user.setUsername('zxcv+1'); + user.set('email', 'user@parse.com'); user.signUp().then(() => { - Parse.User.requestPasswordReset("user@parse.com", { + Parse.User.requestPasswordReset('user@parse.com', { error: err => { jfail(err); - fail("Should not fail"); + fail('Should not fail'); done(); }, }); @@ -1063,7 +1063,7 @@ describe("Custom Pages, Email Verification, Password Reset", () => { }); }); - it("should programmatically reset password on ajax request", async done => { + it('should programmatically reset password on ajax request', async done => { const user = new Parse.User(); const emailAdapter = { sendVerificationEmail: () => Promise.resolve(), @@ -1077,65 +1077,65 @@ describe("Custom Pages, Email Verification, Password Reset", () => { /http:\/\/localhost:8378\/1\/apps\/choose_password\?token=([a-zA-Z0-9]+)\&id=test\&/; const match = response.text.match(re); if (!match) { - fail("should have a token"); + fail('should have a token'); return; } const token = match[1]; const resetResponse = await request({ - url: "http://localhost:8378/1/apps/test/request_password_reset", - method: "POST", - body: { new_password: "hello", token, username: "zxcv" }, + url: 'http://localhost:8378/1/apps/test/request_password_reset', + method: 'POST', + body: { new_password: 'hello', token, username: 'zxcv' }, headers: { - "Content-Type": "application/x-www-form-urlencoded", - "X-Requested-With": "XMLHttpRequest", + 'Content-Type': 'application/x-www-form-urlencoded', + 'X-Requested-With': 'XMLHttpRequest', }, followRedirects: false, }); expect(resetResponse.status).toEqual(200); expect(resetResponse.text).toEqual('"Password successfully reset"'); - await Parse.User.logIn("zxcv", "hello"); - const config = Config.get("test"); + await Parse.User.logIn('zxcv', 'hello'); + const config = Config.get('test'); const results = await config.database.adapter.find( - "_User", + '_User', { fields: {} }, - { username: "zxcv" }, + { username: 'zxcv' }, { limit: 1 } ); // _perishable_token should be unset after reset password expect(results.length).toEqual(1); - expect(results[0]["_perishable_token"]).toEqual(undefined); + expect(results[0]['_perishable_token']).toEqual(undefined); done(); }, sendMail: () => {}, }; await reconfigureServer({ - appName: "emailing app", + appName: 'emailing app', verifyUserEmails: true, emailAdapter: emailAdapter, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); - user.setPassword("asdf"); - user.setUsername("zxcv"); - user.set("email", "user@parse.com"); + user.setPassword('asdf'); + user.setUsername('zxcv'); + user.set('email', 'user@parse.com'); await user.signUp(); - await Parse.User.requestPasswordReset("user@parse.com"); + await Parse.User.requestPasswordReset('user@parse.com'); }); - it("should return ajax failure error on ajax request with wrong data provided", async () => { + it('should return ajax failure error on ajax request with wrong data provided', async () => { await reconfigureServer({ - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }); try { await request({ - method: "POST", - url: "http://localhost:8378/1/apps/test/request_password_reset", + method: 'POST', + url: 'http://localhost:8378/1/apps/test/request_password_reset', body: `new_password=user1&token=12345`, headers: { - "Content-Type": "application/x-www-form-urlencoded", - "X-Requested-With": "XMLHttpRequest", + 'Content-Type': 'application/x-www-form-urlencoded', + 'X-Requested-With': 'XMLHttpRequest', }, followRedirects: false, }); @@ -1147,51 +1147,51 @@ describe("Custom Pages, Email Verification, Password Reset", () => { } }); - it("deletes password reset token on email address change", done => { + it('deletes password reset token on email address change', done => { reconfigureServer({ - appName: "coolapp", - publicServerURL: "http://localhost:1337/1", + appName: 'coolapp', + publicServerURL: 'http://localhost:1337/1', emailAdapter: MockEmailAdapterWithOptions({ - fromAddress: "parse@example.com", - apiKey: "k", - domain: "d", + fromAddress: 'parse@example.com', + apiKey: 'k', + domain: 'd', }), }) .then(() => { - const config = Config.get("test"); + const config = Config.get('test'); const user = new Parse.User(); - user.setPassword("asdf"); - user.setUsername("zxcv"); - user.set("email", "test@parse.com"); + user.setPassword('asdf'); + user.setUsername('zxcv'); + user.set('email', 'test@parse.com'); return user .signUp(null) - .then(() => Parse.User.requestPasswordReset("test@parse.com")) + .then(() => Parse.User.requestPasswordReset('test@parse.com')) .then(() => config.database.adapter.find( - "_User", + '_User', { fields: {} }, - { username: "zxcv" }, + { username: 'zxcv' }, { limit: 1 } ) ) .then(results => { // validate that there is a token expect(results.length).toEqual(1); - expect(results[0]["_perishable_token"]).not.toBeNull(); - user.set("email", "test2@parse.com"); + expect(results[0]['_perishable_token']).not.toBeNull(); + user.set('email', 'test2@parse.com'); return user.save(); }) .then(() => config.database.adapter.find( - "_User", + '_User', { fields: {} }, - { username: "zxcv" }, + { username: 'zxcv' }, { limit: 1 } ) ) .then(results => { expect(results.length).toEqual(1); - expect(results[0]["_perishable_token"]).toBeUndefined(); + expect(results[0]['_perishable_token']).toBeUndefined(); done(); }); }) @@ -1201,7 +1201,7 @@ describe("Custom Pages, Email Verification, Password Reset", () => { }); }); - it("can resend email using an expired reset password token", async () => { + it('can resend email using an expired reset password token', async () => { const user = new Parse.User(); const emailAdapter = { sendVerificationEmail: () => {}, @@ -1209,32 +1209,32 @@ describe("Custom Pages, Email Verification, Password Reset", () => { sendMail: () => {}, }; await reconfigureServer({ - appName: "emailVerifyToken", + appName: 'emailVerifyToken', verifyUserEmails: true, emailAdapter: emailAdapter, emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', passwordPolicy: { resetTokenValidityDuration: 5 * 60, // 5 minutes }, silent: false, }); - user.setUsername("test"); - user.setPassword("password"); - user.set("email", "user@example.com"); + user.setUsername('test'); + user.setPassword('password'); + user.set('email', 'user@example.com'); await user.signUp(); - await Parse.User.requestPasswordReset("user@example.com"); + await Parse.User.requestPasswordReset('user@example.com'); await Parse.Server.database.update( - "_User", + '_User', { objectId: user.id }, { - _perishable_token_expires_at: Parse._encode(new Date("2000")), + _perishable_token_expires_at: Parse._encode(new Date('2000')), } ); let obj = await Parse.Server.database.find( - "_User", + '_User', { objectId: user.id }, {}, Auth.maintenance(Parse.Server) @@ -1242,10 +1242,10 @@ describe("Custom Pages, Email Verification, Password Reset", () => { const token = obj[0]._perishable_token; const res = await request({ url: `http://localhost:8378/1/apps/test/request_password_reset`, - method: "POST", + method: 'POST', body: { token, - new_password: "newpassword", + new_password: 'newpassword', }, }); expect(res.text).toEqual( @@ -1254,19 +1254,19 @@ describe("Custom Pages, Email Verification, Password Reset", () => { await request({ url: `http://localhost:8378/1/requestPasswordReset`, - method: "POST", + method: 'POST', body: { token: token, }, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, }); obj = await Parse.Server.database.find( - "_User", + '_User', { objectId: user.id }, {}, Auth.maintenance(Parse.Server) @@ -1275,14 +1275,14 @@ describe("Custom Pages, Email Verification, Password Reset", () => { expect(obj._perishable_token).not.toBe(token); }); - it("should throw on an invalid reset password", async () => { + it('should throw on an invalid reset password', async () => { await reconfigureServer({ - appName: "coolapp", - publicServerURL: "http://localhost:1337/1", + appName: 'coolapp', + publicServerURL: 'http://localhost:1337/1', emailAdapter: MockEmailAdapterWithOptions({ - fromAddress: "parse@example.com", - apiKey: "k", - domain: "d", + fromAddress: 'parse@example.com', + apiKey: 'k', + domain: 'd', }), passwordPolicy: { resetPasswordSuccessOnInvalidEmail: false, @@ -1290,33 +1290,33 @@ describe("Custom Pages, Email Verification, Password Reset", () => { }); await expectAsync( - Parse.User.requestPasswordReset("test@example.com") + Parse.User.requestPasswordReset('test@example.com') ).toBeRejectedWith( new Parse.Error( Parse.Error.OBJECT_NOT_FOUND, - "A user with that email does not exist." + 'A user with that email does not exist.' ) ); }); - it("validate resetPasswordSuccessonInvalidEmail", async () => { - const invalidValues = [[], {}, 1, "string"]; + it('validate resetPasswordSuccessonInvalidEmail', async () => { + const invalidValues = [[], {}, 1, 'string']; for (const value of invalidValues) { await expectAsync( reconfigureServer({ - appName: "coolapp", - publicServerURL: "http://localhost:1337/1", + appName: 'coolapp', + publicServerURL: 'http://localhost:1337/1', emailAdapter: MockEmailAdapterWithOptions({ - fromAddress: "parse@example.com", - apiKey: "k", - domain: "d", + fromAddress: 'parse@example.com', + apiKey: 'k', + domain: 'd', }), passwordPolicy: { resetPasswordSuccessOnInvalidEmail: value, }, }) ).toBeRejectedWith( - "resetPasswordSuccessOnInvalidEmail must be a boolean value" + 'resetPasswordSuccessOnInvalidEmail must be a boolean value' ); } }); diff --git a/spec/VerifyUserPassword.spec.js b/spec/VerifyUserPassword.spec.js index 5aae756e70..659dedcba2 100644 --- a/spec/VerifyUserPassword.spec.js +++ b/spec/VerifyUserPassword.spec.js @@ -1,17 +1,17 @@ -"use strict"; +'use strict'; -const request = require("../lib/request"); -const MockEmailAdapterWithOptions = require("./support/MockEmailAdapterWithOptions"); +const request = require('../lib/request'); +const MockEmailAdapterWithOptions = require('./support/MockEmailAdapterWithOptions'); const verifyPassword = function (login, password, isEmail = false) { const body = !isEmail ? { username: login, password } : { email: login, password }; return request({ - url: Parse.serverURL + "/verifyPassword", + url: Parse.serverURL + '/verifyPassword', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', }, qs: body, }) @@ -28,13 +28,13 @@ const isAccountLockoutError = function ( return new Promise((resolve, reject) => { setTimeout(() => { Parse.User.logIn(username, password) - .then(() => reject("login should have failed")) + .then(() => reject('login should have failed')) .catch(err => { if ( err.message === - "Your account is locked due to multiple failed login attempts. Please try again after " + + 'Your account is locked due to multiple failed login attempts. Please try again after ' + duration + - " minute(s)" + ' minute(s)' ) { resolve(); } else { @@ -45,22 +45,22 @@ const isAccountLockoutError = function ( }); }; -describe("Verify User Password", () => { - it("fails to verify password when masterKey has locked out user", done => { +describe('Verify User Password', () => { + it('fails to verify password when masterKey has locked out user', done => { const user = new Parse.User(); const ACL = new Parse.ACL(); ACL.setPublicReadAccess(false); ACL.setPublicWriteAccess(false); - user.setUsername("testuser"); - user.setPassword("mypass"); + user.setUsername('testuser'); + user.setPassword('mypass'); user.setACL(ACL); user .signUp() .then(() => { - return Parse.User.logIn("testuser", "mypass"); + return Parse.User.logIn('testuser', 'mypass'); }) .then(user => { - equal(user.get("username"), "testuser"); + equal(user.get('username'), 'testuser'); // Lock the user down const ACL = new Parse.ACL(); user.setACL(ACL); @@ -69,14 +69,14 @@ describe("Verify User Password", () => { .then(() => { expect(user.getACL().getPublicReadAccess()).toBe(false); return request({ - url: Parse.serverURL + "/verifyPassword", + url: Parse.serverURL + '/verifyPassword', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', }, qs: { - username: "testuser", - password: "mypass", + username: 'testuser', + password: 'mypass', }, }); }) @@ -92,24 +92,24 @@ describe("Verify User Password", () => { done(); }); }); - it("fails to verify password when username is not provided in query string REST API", done => { + it('fails to verify password when username is not provided in query string REST API', done => { const user = new Parse.User(); user .save({ - username: "testuser", - password: "mypass", - email: "my@user.com", + username: 'testuser', + password: 'mypass', + email: 'my@user.com', }) .then(() => { return request({ - url: Parse.serverURL + "/verifyPassword", + url: Parse.serverURL + '/verifyPassword', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', }, qs: { - username: "", - password: "mypass", + username: '', + password: 'mypass', }, }); }) @@ -125,24 +125,24 @@ describe("Verify User Password", () => { done(); }); }); - it("fails to verify password when email is not provided in query string REST API", done => { + it('fails to verify password when email is not provided in query string REST API', done => { const user = new Parse.User(); user .save({ - username: "testuser", - password: "mypass", - email: "my@user.com", + username: 'testuser', + password: 'mypass', + email: 'my@user.com', }) .then(() => { return request({ - url: Parse.serverURL + "/verifyPassword", + url: Parse.serverURL + '/verifyPassword', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', }, qs: { - email: "", - password: "mypass", + email: '', + password: 'mypass', }, }); }) @@ -158,16 +158,16 @@ describe("Verify User Password", () => { done(); }); }); - it("fails to verify password when username is not provided with json payload REST API", done => { + it('fails to verify password when username is not provided with json payload REST API', done => { const user = new Parse.User(); user .save({ - username: "testuser", - password: "mypass", - email: "my@user.com", + username: 'testuser', + password: 'mypass', + email: 'my@user.com', }) .then(() => { - return verifyPassword("", "mypass"); + return verifyPassword('', 'mypass'); }) .then(res => { expect(res.status).toBe(400); @@ -181,16 +181,16 @@ describe("Verify User Password", () => { done(); }); }); - it("fails to verify password when email is not provided with json payload REST API", done => { + it('fails to verify password when email is not provided with json payload REST API', done => { const user = new Parse.User(); user .save({ - username: "testuser", - password: "mypass", - email: "my@user.com", + username: 'testuser', + password: 'mypass', + email: 'my@user.com', }) .then(() => { - return verifyPassword("", "mypass", true); + return verifyPassword('', 'mypass', true); }) .then(res => { expect(res.status).toBe(400); @@ -204,16 +204,16 @@ describe("Verify User Password", () => { done(); }); }); - it("fails to verify password when password is not provided with json payload REST API", done => { + it('fails to verify password when password is not provided with json payload REST API', done => { const user = new Parse.User(); user .save({ - username: "testuser", - password: "mypass", - email: "my@user.com", + username: 'testuser', + password: 'mypass', + email: 'my@user.com', }) .then(() => { - return verifyPassword("testuser", ""); + return verifyPassword('testuser', ''); }) .then(res => { expect(res.status).toBe(400); @@ -227,16 +227,16 @@ describe("Verify User Password", () => { done(); }); }); - it("fails to verify password when username matches but password does not match hash with json payload REST API", done => { + it('fails to verify password when username matches but password does not match hash with json payload REST API', done => { const user = new Parse.User(); user .save({ - username: "testuser", - password: "mypass", - email: "my@user.com", + username: 'testuser', + password: 'mypass', + email: 'my@user.com', }) .then(() => { - return verifyPassword("testuser", "wrong password"); + return verifyPassword('testuser', 'wrong password'); }) .then(res => { expect(res.status).toBe(404); @@ -250,16 +250,16 @@ describe("Verify User Password", () => { done(); }); }); - it("fails to verify password when email matches but password does not match hash with json payload REST API", done => { + it('fails to verify password when email matches but password does not match hash with json payload REST API', done => { const user = new Parse.User(); user .save({ - username: "testuser", - password: "mypass", - email: "my@user.com", + username: 'testuser', + password: 'mypass', + email: 'my@user.com', }) .then(() => { - return verifyPassword("my@user.com", "wrong password", true); + return verifyPassword('my@user.com', 'wrong password', true); }) .then(res => { expect(res.status).toBe(404); @@ -273,16 +273,16 @@ describe("Verify User Password", () => { done(); }); }); - it("fails to verify password when typeof username does not equal string REST API", done => { + it('fails to verify password when typeof username does not equal string REST API', done => { const user = new Parse.User(); user .save({ - username: "testuser", - password: "mypass", - email: "my@user.com", + username: 'testuser', + password: 'mypass', + email: 'my@user.com', }) .then(() => { - return verifyPassword(123, "mypass"); + return verifyPassword(123, 'mypass'); }) .then(res => { expect(res.status).toBe(404); @@ -296,16 +296,16 @@ describe("Verify User Password", () => { done(); }); }); - it("fails to verify password when typeof email does not equal string REST API", done => { + it('fails to verify password when typeof email does not equal string REST API', done => { const user = new Parse.User(); user .save({ - username: "testuser", - password: "mypass", - email: "my@user.com", + username: 'testuser', + password: 'mypass', + email: 'my@user.com', }) .then(() => { - return verifyPassword(123, "mypass", true); + return verifyPassword(123, 'mypass', true); }) .then(res => { expect(res.status).toBe(404); @@ -319,16 +319,16 @@ describe("Verify User Password", () => { done(); }); }); - it("fails to verify password when typeof password does not equal string REST API", done => { + it('fails to verify password when typeof password does not equal string REST API', done => { const user = new Parse.User(); user .save({ - username: "testuser", - password: "mypass", - email: "my@user.com", + username: 'testuser', + password: 'mypass', + email: 'my@user.com', }) .then(() => { - return verifyPassword("my@user.com", 123, true); + return verifyPassword('my@user.com', 123, true); }) .then(res => { expect(res.status).toBe(404); @@ -342,8 +342,8 @@ describe("Verify User Password", () => { done(); }); }); - it("fails to verify password when username cannot be found REST API", done => { - verifyPassword("mytestuser", "mypass") + it('fails to verify password when username cannot be found REST API', done => { + verifyPassword('mytestuser', 'mypass') .then(res => { expect(res.status).toBe(404); expect(res.text).toMatch( @@ -356,8 +356,8 @@ describe("Verify User Password", () => { done(); }); }); - it("fails to verify password when email cannot be found REST API", done => { - verifyPassword("my@user.com", "mypass", true) + it('fails to verify password when email cannot be found REST API', done => { + verifyPassword('my@user.com', 'mypass', true) .then(res => { expect(res.status).toBe(404); expect(res.text).toMatch( @@ -371,95 +371,95 @@ describe("Verify User Password", () => { }); }); - it("fails to verify password when preventLoginWithUnverifiedEmail is set to true REST API", async () => { + it('fails to verify password when preventLoginWithUnverifiedEmail is set to true REST API', async () => { await reconfigureServer({ - publicServerURL: "http://localhost:8378/", - appName: "emailVerify", + publicServerURL: 'http://localhost:8378/', + appName: 'emailVerify', verifyUserEmails: true, preventLoginWithUnverifiedEmail: true, emailAdapter: MockEmailAdapterWithOptions({ - fromAddress: "parse@example.com", - apiKey: "k", - domain: "d", + fromAddress: 'parse@example.com', + apiKey: 'k', + domain: 'd', }), }); const user = new Parse.User(); await user.save({ - username: "unverified-user", - password: "mypass", - email: "unverified-email@example.com", + username: 'unverified-user', + password: 'mypass', + email: 'unverified-email@example.com', }); const res = await verifyPassword( - "unverified-email@example.com", - "mypass", + 'unverified-email@example.com', + 'mypass', true ); expect(res.status).toBe(400); expect(res.data).toEqual({ code: Parse.Error.EMAIL_NOT_FOUND, - error: "User email is not verified.", + error: 'User email is not verified.', }); }); - it("verify password lock account if failed verify password attempts are above threshold", done => { + it('verify password lock account if failed verify password attempts are above threshold', done => { reconfigureServer({ - appName: "lockout threshold", + appName: 'lockout threshold', accountLockout: { duration: 1, threshold: 2, }, - publicServerURL: "http://localhost:8378/", + publicServerURL: 'http://localhost:8378/', }) .then(() => { const user = new Parse.User(); return user.save({ - username: "testuser", - password: "mypass", - email: "my@user.com", + username: 'testuser', + password: 'mypass', + email: 'my@user.com', }); }) .then(() => { - return verifyPassword("testuser", "wrong password"); + return verifyPassword('testuser', 'wrong password'); }) .then(() => { - return verifyPassword("testuser", "wrong password"); + return verifyPassword('testuser', 'wrong password'); }) .then(() => { - return verifyPassword("testuser", "wrong password"); + return verifyPassword('testuser', 'wrong password'); }) .then(() => { - return isAccountLockoutError("testuser", "wrong password", 1, 1); + return isAccountLockoutError('testuser', 'wrong password', 1, 1); }) .then(() => { done(); }) .catch(err => { fail( - "lock account after failed login attempts test failed: " + + 'lock account after failed login attempts test failed: ' + JSON.stringify(err) ); done(); }); }); - it("succeed in verifying password when username and email are provided and password matches hash with json payload REST API", done => { + it('succeed in verifying password when username and email are provided and password matches hash with json payload REST API', done => { const user = new Parse.User(); user .save({ - username: "testuser", - password: "mypass", - email: "my@user.com", + username: 'testuser', + password: 'mypass', + email: 'my@user.com', }) .then(() => { return request({ - url: Parse.serverURL + "/verifyPassword", + url: Parse.serverURL + '/verifyPassword', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', }, qs: { - username: "testuser", - email: "my@user.com", - password: "mypass", + username: 'testuser', + email: 'my@user.com', + password: 'mypass', }, json: true, }) @@ -468,12 +468,12 @@ describe("Verify User Password", () => { }) .then(response => { const res = response.data; - expect(typeof res).toBe("object"); - expect(typeof res["objectId"]).toEqual("string"); + expect(typeof res).toBe('object'); + expect(typeof res['objectId']).toEqual('string'); expect( - Object.prototype.hasOwnProperty.call(res, "sessionToken") + Object.prototype.hasOwnProperty.call(res, 'sessionToken') ).toEqual(false); - expect(Object.prototype.hasOwnProperty.call(res, "password")).toEqual( + expect(Object.prototype.hasOwnProperty.call(res, 'password')).toEqual( false ); done(); @@ -483,232 +483,232 @@ describe("Verify User Password", () => { done(); }); }); - it("succeed in verifying password when username and password matches hash with json payload REST API", done => { + it('succeed in verifying password when username and password matches hash with json payload REST API', done => { const user = new Parse.User(); user .save({ - username: "testuser", - password: "mypass", - email: "my@user.com", + username: 'testuser', + password: 'mypass', + email: 'my@user.com', }) .then(() => { - return verifyPassword("testuser", "mypass"); + return verifyPassword('testuser', 'mypass'); }) .then(response => { const res = response.data; - expect(typeof res).toBe("object"); - expect(typeof res["objectId"]).toEqual("string"); + expect(typeof res).toBe('object'); + expect(typeof res['objectId']).toEqual('string'); expect( - Object.prototype.hasOwnProperty.call(res, "sessionToken") + Object.prototype.hasOwnProperty.call(res, 'sessionToken') ).toEqual(false); - expect(Object.prototype.hasOwnProperty.call(res, "password")).toEqual( + expect(Object.prototype.hasOwnProperty.call(res, 'password')).toEqual( false ); done(); }); }); - it("succeed in verifying password when email and password matches hash with json payload REST API", done => { + it('succeed in verifying password when email and password matches hash with json payload REST API', done => { const user = new Parse.User(); user .save({ - username: "testuser", - password: "mypass", - email: "my@user.com", + username: 'testuser', + password: 'mypass', + email: 'my@user.com', }) .then(() => { - return verifyPassword("my@user.com", "mypass", true); + return verifyPassword('my@user.com', 'mypass', true); }) .then(response => { const res = response.data; - expect(typeof res).toBe("object"); - expect(typeof res["objectId"]).toEqual("string"); + expect(typeof res).toBe('object'); + expect(typeof res['objectId']).toEqual('string'); expect( - Object.prototype.hasOwnProperty.call(res, "sessionToken") + Object.prototype.hasOwnProperty.call(res, 'sessionToken') ).toEqual(false); - expect(Object.prototype.hasOwnProperty.call(res, "password")).toEqual( + expect(Object.prototype.hasOwnProperty.call(res, 'password')).toEqual( false ); done(); }); }); - it("succeed to verify password when username and password provided in query string REST API", done => { + it('succeed to verify password when username and password provided in query string REST API', done => { const user = new Parse.User(); user .save({ - username: "testuser", - password: "mypass", - email: "my@user.com", + username: 'testuser', + password: 'mypass', + email: 'my@user.com', }) .then(() => { return request({ - url: Parse.serverURL + "/verifyPassword", + url: Parse.serverURL + '/verifyPassword', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', }, qs: { - username: "testuser", - password: "mypass", + username: 'testuser', + password: 'mypass', }, }); }) .then(response => { const res = response.text; - expect(typeof res).toBe("string"); + expect(typeof res).toBe('string'); const body = JSON.parse(res); - expect(typeof body["objectId"]).toEqual("string"); + expect(typeof body['objectId']).toEqual('string'); expect( - Object.prototype.hasOwnProperty.call(body, "sessionToken") + Object.prototype.hasOwnProperty.call(body, 'sessionToken') ).toEqual(false); - expect(Object.prototype.hasOwnProperty.call(body, "password")).toEqual( + expect(Object.prototype.hasOwnProperty.call(body, 'password')).toEqual( false ); done(); }); }); - it("succeed to verify password when email and password provided in query string REST API", done => { + it('succeed to verify password when email and password provided in query string REST API', done => { const user = new Parse.User(); user .save({ - username: "testuser", - password: "mypass", - email: "my@user.com", + username: 'testuser', + password: 'mypass', + email: 'my@user.com', }) .then(() => { return request({ - url: Parse.serverURL + "/verifyPassword", + url: Parse.serverURL + '/verifyPassword', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', }, qs: { - email: "my@user.com", - password: "mypass", + email: 'my@user.com', + password: 'mypass', }, }); }) .then(response => { const res = response.text; - expect(typeof res).toBe("string"); + expect(typeof res).toBe('string'); const body = JSON.parse(res); - expect(typeof body["objectId"]).toEqual("string"); + expect(typeof body['objectId']).toEqual('string'); expect( - Object.prototype.hasOwnProperty.call(body, "sessionToken") + Object.prototype.hasOwnProperty.call(body, 'sessionToken') ).toEqual(false); - expect(Object.prototype.hasOwnProperty.call(body, "password")).toEqual( + expect(Object.prototype.hasOwnProperty.call(body, 'password')).toEqual( false ); done(); }); }); - it("succeed to verify password with username when user1 has username === user2 email REST API", done => { + it('succeed to verify password with username when user1 has username === user2 email REST API', done => { const user1 = new Parse.User(); user1 .save({ - username: "email@user.com", - password: "mypass1", - email: "1@user.com", + username: 'email@user.com', + password: 'mypass1', + email: '1@user.com', }) .then(() => { const user2 = new Parse.User(); return user2.save({ - username: "user2", - password: "mypass2", - email: "email@user.com", + username: 'user2', + password: 'mypass2', + email: 'email@user.com', }); }) .then(() => { - return verifyPassword("email@user.com", "mypass1"); + return verifyPassword('email@user.com', 'mypass1'); }) .then(response => { const res = response.data; - expect(typeof res).toBe("object"); - expect(typeof res["objectId"]).toEqual("string"); + expect(typeof res).toBe('object'); + expect(typeof res['objectId']).toEqual('string'); expect( - Object.prototype.hasOwnProperty.call(res, "sessionToken") + Object.prototype.hasOwnProperty.call(res, 'sessionToken') ).toEqual(false); - expect(Object.prototype.hasOwnProperty.call(res, "password")).toEqual( + expect(Object.prototype.hasOwnProperty.call(res, 'password')).toEqual( false ); done(); }); }); - it("verify password of user with unverified email with master key and ignoreEmailVerification=true", async () => { + it('verify password of user with unverified email with master key and ignoreEmailVerification=true', async () => { await reconfigureServer({ - publicServerURL: "http://localhost:8378/", - appName: "emailVerify", + publicServerURL: 'http://localhost:8378/', + appName: 'emailVerify', verifyUserEmails: true, preventLoginWithUnverifiedEmail: true, emailAdapter: MockEmailAdapterWithOptions({ - fromAddress: "parse@example.com", - apiKey: "k", - domain: "d", + fromAddress: 'parse@example.com', + apiKey: 'k', + domain: 'd', }), }); const user = new Parse.User(); - user.setUsername("user"); - user.setPassword("pass"); - user.setEmail("test@example.com"); + user.setUsername('user'); + user.setPassword('pass'); + user.setEmail('test@example.com'); await user.signUp(); const { data: res } = await request({ - method: "POST", - url: Parse.serverURL + "/verifyPassword", + method: 'POST', + url: Parse.serverURL + '/verifyPassword', headers: { - "X-Parse-Master-Key": Parse.masterKey, - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Master-Key': Parse.masterKey, + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, body: { - username: "user", - password: "pass", + username: 'user', + password: 'pass', ignoreEmailVerification: true, }, json: true, }); expect(res.objectId).toBe(user.id); - expect(Object.prototype.hasOwnProperty.call(res, "sessionToken")).toEqual( + expect(Object.prototype.hasOwnProperty.call(res, 'sessionToken')).toEqual( false ); - expect(Object.prototype.hasOwnProperty.call(res, "password")).toEqual( + expect(Object.prototype.hasOwnProperty.call(res, 'password')).toEqual( false ); }); - it("fails to verify password of user with unverified email with master key and ignoreEmailVerification=false", async () => { + it('fails to verify password of user with unverified email with master key and ignoreEmailVerification=false', async () => { await reconfigureServer({ - publicServerURL: "http://localhost:8378/", - appName: "emailVerify", + publicServerURL: 'http://localhost:8378/', + appName: 'emailVerify', verifyUserEmails: true, preventLoginWithUnverifiedEmail: true, emailAdapter: MockEmailAdapterWithOptions({ - fromAddress: "parse@example.com", - apiKey: "k", - domain: "d", + fromAddress: 'parse@example.com', + apiKey: 'k', + domain: 'd', }), }); const user = new Parse.User(); - user.setUsername("user"); - user.setPassword("pass"); - user.setEmail("test@example.com"); + user.setUsername('user'); + user.setPassword('pass'); + user.setEmail('test@example.com'); await user.signUp(); const res = await request({ - method: "POST", - url: Parse.serverURL + "/verifyPassword", + method: 'POST', + url: Parse.serverURL + '/verifyPassword', headers: { - "X-Parse-Master-Key": Parse.masterKey, - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Master-Key': Parse.masterKey, + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }, body: { - username: "user", - password: "pass", + username: 'user', + password: 'pass', ignoreEmailVerification: false, }, json: true, diff --git a/spec/WinstonLoggerAdapter.spec.js b/spec/WinstonLoggerAdapter.spec.js index 0ce41a18e5..f32ec35574 100644 --- a/spec/WinstonLoggerAdapter.spec.js +++ b/spec/WinstonLoggerAdapter.spec.js @@ -1,30 +1,30 @@ -"use strict"; +'use strict'; const WinstonLoggerAdapter = - require("../lib/Adapters/Logger/WinstonLoggerAdapter").WinstonLoggerAdapter; -const request = require("../lib/request"); + require('../lib/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter; +const request = require('../lib/request'); describe_only(() => { - return process.env.PARSE_SERVER_LOG_LEVEL !== "debug"; -})("info logs", () => { - it("Verify INFO logs", done => { + return process.env.PARSE_SERVER_LOG_LEVEL !== 'debug'; +})('info logs', () => { + it('Verify INFO logs', done => { const winstonLoggerAdapter = new WinstonLoggerAdapter(); - winstonLoggerAdapter.log("info", "testing info logs with 1234"); + winstonLoggerAdapter.log('info', 'testing info logs with 1234'); winstonLoggerAdapter.query( { from: new Date(Date.now() - 500), size: 100, - level: "info", - order: "desc", + level: 'info', + order: 'desc', }, results => { if (results.length == 0) { - fail("The adapter should return non-empty results"); + fail('The adapter should return non-empty results'); } else { const log = results.find( - x => x.message === "testing info logs with 1234" + x => x.message === 'testing info logs with 1234' ); - expect(log.level).toEqual("info"); + expect(log.level).toEqual('info'); } // Check the error log // Regression #2639 @@ -32,11 +32,11 @@ describe_only(() => { { from: new Date(Date.now() - 200), size: 100, - level: "error", + level: 'error', }, errors => { const log = errors.find( - x => x.message === "testing info logs with 1234" + x => x.message === 'testing info logs with 1234' ); expect(log).toBeUndefined(); done(); @@ -46,32 +46,32 @@ describe_only(() => { ); }); - it("info logs should interpolate string", async () => { + it('info logs should interpolate string', async () => { const winstonLoggerAdapter = new WinstonLoggerAdapter(); - winstonLoggerAdapter.log("info", "testing info logs with %s", "replace"); + winstonLoggerAdapter.log('info', 'testing info logs with %s', 'replace'); const results = await winstonLoggerAdapter.query({ from: new Date(Date.now() - 500), size: 100, - level: "info", - order: "desc", + level: 'info', + order: 'desc', }); expect(results.length > 0).toBeTruthy(); const log = results.find( - x => x.message === "testing info logs with replace" + x => x.message === 'testing info logs with replace' ); expect(log); }); - it("info logs should interpolate json", async () => { + it('info logs should interpolate json', async () => { const winstonLoggerAdapter = new WinstonLoggerAdapter(); - winstonLoggerAdapter.log("info", "testing info logs with %j", { - hello: "world", + winstonLoggerAdapter.log('info', 'testing info logs with %j', { + hello: 'world', }); const results = await winstonLoggerAdapter.query({ from: new Date(Date.now() - 500), size: 100, - level: "info", - order: "desc", + level: 'info', + order: 'desc', }); expect(results.length > 0).toBeTruthy(); const log = results.find( @@ -80,86 +80,86 @@ describe_only(() => { expect(log); }); - it("info logs should interpolate number", async () => { + it('info logs should interpolate number', async () => { const winstonLoggerAdapter = new WinstonLoggerAdapter(); - winstonLoggerAdapter.log("info", "testing info logs with %d", 123); + winstonLoggerAdapter.log('info', 'testing info logs with %d', 123); const results = await winstonLoggerAdapter.query({ from: new Date(Date.now() - 500), size: 100, - level: "info", - order: "desc", + level: 'info', + order: 'desc', }); expect(results.length > 0).toBeTruthy(); - const log = results.find(x => x.message === "testing info logs with 123"); + const log = results.find(x => x.message === 'testing info logs with 123'); expect(log); }); }); describe_only(() => { - return process.env.PARSE_SERVER_LOG_LEVEL !== "debug"; -})("error logs", () => { - it("Verify ERROR logs", done => { + return process.env.PARSE_SERVER_LOG_LEVEL !== 'debug'; +})('error logs', () => { + it('Verify ERROR logs', done => { const winstonLoggerAdapter = new WinstonLoggerAdapter(); - winstonLoggerAdapter.log("error", "testing error logs"); + winstonLoggerAdapter.log('error', 'testing error logs'); winstonLoggerAdapter.query( { from: new Date(Date.now() - 500), size: 100, - level: "error", + level: 'error', }, results => { if (results.length == 0) { - fail("The adapter should return non-empty results"); + fail('The adapter should return non-empty results'); done(); } else { - expect(results[0].message).toEqual("testing error logs"); + expect(results[0].message).toEqual('testing error logs'); done(); } } ); }); - it("Should filter on query", done => { + it('Should filter on query', done => { const winstonLoggerAdapter = new WinstonLoggerAdapter(); - winstonLoggerAdapter.log("error", "testing error logs"); + winstonLoggerAdapter.log('error', 'testing error logs'); winstonLoggerAdapter.query( { from: new Date(Date.now() - 500), size: 100, - level: "error", + level: 'error', }, results => { - expect(results.filter(e => e.level !== "error").length).toBe(0); + expect(results.filter(e => e.level !== 'error').length).toBe(0); done(); } ); }); - it("error logs should interpolate string", async () => { + it('error logs should interpolate string', async () => { const winstonLoggerAdapter = new WinstonLoggerAdapter(); - winstonLoggerAdapter.log("error", "testing error logs with %s", "replace"); + winstonLoggerAdapter.log('error', 'testing error logs with %s', 'replace'); const results = await winstonLoggerAdapter.query({ from: new Date(Date.now() - 500), size: 100, - level: "error", + level: 'error', }); expect(results.length > 0).toBeTruthy(); const log = results.find( - x => x.message === "testing error logs with replace" + x => x.message === 'testing error logs with replace' ); expect(log); }); - it("error logs should interpolate json", async () => { + it('error logs should interpolate json', async () => { const winstonLoggerAdapter = new WinstonLoggerAdapter(); - winstonLoggerAdapter.log("error", "testing error logs with %j", { - hello: "world", + winstonLoggerAdapter.log('error', 'testing error logs with %j', { + hello: 'world', }); const results = await winstonLoggerAdapter.query({ from: new Date(Date.now() - 500), size: 100, - level: "error", - order: "desc", + level: 'error', + order: 'desc', }); expect(results.length > 0).toBeTruthy(); const log = results.find( @@ -168,26 +168,26 @@ describe_only(() => { expect(log); }); - it("error logs should interpolate number", async () => { + it('error logs should interpolate number', async () => { const winstonLoggerAdapter = new WinstonLoggerAdapter(); - winstonLoggerAdapter.log("error", "testing error logs with %d", 123); + winstonLoggerAdapter.log('error', 'testing error logs with %d', 123); const results = await winstonLoggerAdapter.query({ from: new Date(Date.now() - 500), size: 100, - level: "error", - order: "desc", + level: 'error', + order: 'desc', }); expect(results.length > 0).toBeTruthy(); - const log = results.find(x => x.message === "testing error logs with 123"); + const log = results.find(x => x.message === 'testing error logs with 123'); expect(log); }); }); describe_only(() => { - return process.env.PARSE_SERVER_LOG_LEVEL !== "debug"; -})("verbose logs", () => { - it_id("9ca72994-d255-4c11-a5a2-693c99ee2cdb")(it)( - "mask sensitive information in _User class", + return process.env.PARSE_SERVER_LOG_LEVEL !== 'debug'; +})('verbose logs', () => { + it_id('9ca72994-d255-4c11-a5a2-693c99ee2cdb')(it)( + 'mask sensitive information in _User class', done => { reconfigureServer({ verbose: true }) .then(() => createTestUser()) @@ -196,7 +196,7 @@ describe_only(() => { return winstonLoggerAdapter.query({ from: new Date(Date.now() - 500), size: 100, - level: "verbose", + level: 'verbose', }); }) .then(results => { @@ -205,19 +205,19 @@ describe_only(() => { expect(logString.match(/moon-y/g)).toBe(null); const headers = { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; request({ headers: headers, - url: "http://localhost:8378/1/login?username=test&password=moon-y", + url: 'http://localhost:8378/1/login?username=test&password=moon-y', }).then(() => { const winstonLoggerAdapter = new WinstonLoggerAdapter(); return winstonLoggerAdapter .query({ from: new Date(Date.now() - 500), size: 100, - level: "verbose", + level: 'verbose', }) .then(results => { const logString = JSON.stringify(results); @@ -234,37 +234,37 @@ describe_only(() => { } ); - it("verbose logs should interpolate string", async () => { + it('verbose logs should interpolate string', async () => { await reconfigureServer({ verbose: true }); const winstonLoggerAdapter = new WinstonLoggerAdapter(); winstonLoggerAdapter.log( - "verbose", - "testing verbose logs with %s", - "replace" + 'verbose', + 'testing verbose logs with %s', + 'replace' ); const results = await winstonLoggerAdapter.query({ from: new Date(Date.now() - 500), size: 100, - level: "verbose", + level: 'verbose', }); expect(results.length > 0).toBeTruthy(); const log = results.find( - x => x.message === "testing verbose logs with replace" + x => x.message === 'testing verbose logs with replace' ); expect(log); }); - it("verbose logs should interpolate json", async () => { + it('verbose logs should interpolate json', async () => { await reconfigureServer({ verbose: true }); const winstonLoggerAdapter = new WinstonLoggerAdapter(); - winstonLoggerAdapter.log("verbose", "testing verbose logs with %j", { - hello: "world", + winstonLoggerAdapter.log('verbose', 'testing verbose logs with %j', { + hello: 'world', }); const results = await winstonLoggerAdapter.query({ from: new Date(Date.now() - 500), size: 100, - level: "verbose", - order: "desc", + level: 'verbose', + order: 'desc', }); expect(results.length > 0).toBeTruthy(); const log = results.find( @@ -273,29 +273,29 @@ describe_only(() => { expect(log); }); - it("verbose logs should interpolate number", async () => { + it('verbose logs should interpolate number', async () => { await reconfigureServer({ verbose: true }); const winstonLoggerAdapter = new WinstonLoggerAdapter(); - winstonLoggerAdapter.log("verbose", "testing verbose logs with %d", 123); + winstonLoggerAdapter.log('verbose', 'testing verbose logs with %d', 123); const results = await winstonLoggerAdapter.query({ from: new Date(Date.now() - 500), size: 100, - level: "verbose", - order: "desc", + level: 'verbose', + order: 'desc', }); expect(results.length > 0).toBeTruthy(); const log = results.find( - x => x.message === "testing verbose logs with 123" + x => x.message === 'testing verbose logs with 123' ); expect(log); }); - it("verbose logs should interpolate stdout", async () => { + it('verbose logs should interpolate stdout', async () => { await reconfigureServer({ verbose: true, silent: false, logsFolder: null }); - spyOn(process.stdout, "write"); + spyOn(process.stdout, 'write'); const winstonLoggerAdapter = new WinstonLoggerAdapter(); - winstonLoggerAdapter.log("verbose", "testing verbose logs with %j", { - hello: "world", + winstonLoggerAdapter.log('verbose', 'testing verbose logs with %j', { + hello: 'world', }); const firstLog = process.stdout.write.calls.first().args[0]; expect(firstLog).toBe( diff --git a/spec/batch.spec.js b/spec/batch.spec.js index 715c9e86a6..0380ce762d 100644 --- a/spec/batch.spec.js +++ b/spec/batch.spec.js @@ -1,158 +1,158 @@ -const batch = require("../lib/batch"); -const request = require("../lib/request"); +const batch = require('../lib/batch'); +const request = require('../lib/request'); -const originalURL = "/parse/batch"; -const serverURL = "http://localhost:1234/parse"; -const serverURL1 = "http://localhost:1234/1"; -const serverURLNaked = "http://localhost:1234/"; -const publicServerURL = "http://domain.com/parse"; -const publicServerURLNaked = "http://domain.com/"; -const publicServerURLLong = "https://domain.com/something/really/long"; +const originalURL = '/parse/batch'; +const serverURL = 'http://localhost:1234/parse'; +const serverURL1 = 'http://localhost:1234/1'; +const serverURLNaked = 'http://localhost:1234/'; +const publicServerURL = 'http://domain.com/parse'; +const publicServerURLNaked = 'http://domain.com/'; +const publicServerURLLong = 'https://domain.com/something/really/long'; const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "X-Parse-Installation-Id": "yolo", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Installation-Id': 'yolo', }; -describe("batch", () => { +describe('batch', () => { let createSpy; beforeEach(async () => { - createSpy = spyOn(databaseAdapter, "createObject").and.callThrough(); + createSpy = spyOn(databaseAdapter, 'createObject').and.callThrough(); }); - it("should return the proper url", () => { + it('should return the proper url', () => { const internalURL = batch.makeBatchRoutingPathFunction(originalURL)( - "/parse/classes/Object" + '/parse/classes/Object' ); - expect(internalURL).toEqual("/classes/Object"); + expect(internalURL).toEqual('/classes/Object'); }); - it("should return the proper url given a public url-only path", () => { - const originalURL = "/something/really/long/batch"; + it('should return the proper url given a public url-only path', () => { + const originalURL = '/something/really/long/batch'; const internalURL = batch.makeBatchRoutingPathFunction( originalURL, serverURL, publicServerURLLong - )("/parse/classes/Object"); - expect(internalURL).toEqual("/classes/Object"); + )('/parse/classes/Object'); + expect(internalURL).toEqual('/classes/Object'); }); - it("should return the proper url given a server url-only path", () => { - const originalURL = "/parse/batch"; + it('should return the proper url given a server url-only path', () => { + const originalURL = '/parse/batch'; const internalURL = batch.makeBatchRoutingPathFunction( originalURL, serverURL, publicServerURLLong - )("/parse/classes/Object"); - expect(internalURL).toEqual("/classes/Object"); + )('/parse/classes/Object'); + expect(internalURL).toEqual('/classes/Object'); }); - it("should return the proper url same public/local endpoint", () => { - const originalURL = "/parse/batch"; + it('should return the proper url same public/local endpoint', () => { + const originalURL = '/parse/batch'; const internalURL = batch.makeBatchRoutingPathFunction( originalURL, serverURL, publicServerURL - )("/parse/classes/Object"); + )('/parse/classes/Object'); - expect(internalURL).toEqual("/classes/Object"); + expect(internalURL).toEqual('/classes/Object'); }); - it("should return the proper url with different public/local mount", () => { - const originalURL = "/parse/batch"; + it('should return the proper url with different public/local mount', () => { + const originalURL = '/parse/batch'; const internalURL = batch.makeBatchRoutingPathFunction( originalURL, serverURL1, publicServerURL - )("/parse/classes/Object"); + )('/parse/classes/Object'); - expect(internalURL).toEqual("/classes/Object"); + expect(internalURL).toEqual('/classes/Object'); }); - it("should return the proper url with naked public", () => { - const originalURL = "/batch"; + it('should return the proper url with naked public', () => { + const originalURL = '/batch'; const internalURL = batch.makeBatchRoutingPathFunction( originalURL, serverURL, publicServerURLNaked - )("/classes/Object"); + )('/classes/Object'); - expect(internalURL).toEqual("/classes/Object"); + expect(internalURL).toEqual('/classes/Object'); }); - it("should return the proper url with naked local", () => { - const originalURL = "/parse/batch"; + it('should return the proper url with naked local', () => { + const originalURL = '/parse/batch'; const internalURL = batch.makeBatchRoutingPathFunction( originalURL, serverURLNaked, publicServerURL - )("/parse/classes/Object"); + )('/parse/classes/Object'); - expect(internalURL).toEqual("/classes/Object"); + expect(internalURL).toEqual('/classes/Object'); }); - it("should return the proper url with no url provided", () => { - const originalURL = "/parse/batch"; + it('should return the proper url with no url provided', () => { + const originalURL = '/parse/batch'; const internalURL = batch.makeBatchRoutingPathFunction( originalURL, undefined, publicServerURL - )("/parse/classes/Object"); + )('/parse/classes/Object'); - expect(internalURL).toEqual("/classes/Object"); + expect(internalURL).toEqual('/classes/Object'); }); - it("should return the proper url with no public url provided", () => { - const originalURL = "/parse/batch"; + it('should return the proper url with no public url provided', () => { + const originalURL = '/parse/batch'; const internalURL = batch.makeBatchRoutingPathFunction( originalURL, serverURLNaked, undefined - )("/parse/classes/Object"); + )('/parse/classes/Object'); - expect(internalURL).toEqual("/classes/Object"); + expect(internalURL).toEqual('/classes/Object'); }); - it("should return the proper url with bad url provided", () => { - const originalURL = "/parse/batch"; + it('should return the proper url with bad url provided', () => { + const originalURL = '/parse/batch'; const internalURL = batch.makeBatchRoutingPathFunction( originalURL, - "badurl.com", + 'badurl.com', publicServerURL - )("/parse/classes/Object"); + )('/parse/classes/Object'); - expect(internalURL).toEqual("/classes/Object"); + expect(internalURL).toEqual('/classes/Object'); }); - it("should return the proper url with bad public url provided", () => { - const originalURL = "/parse/batch"; + it('should return the proper url with bad public url provided', () => { + const originalURL = '/parse/batch'; const internalURL = batch.makeBatchRoutingPathFunction( originalURL, serverURLNaked, - "badurl.com" - )("/parse/classes/Object"); + 'badurl.com' + )('/parse/classes/Object'); - expect(internalURL).toEqual("/classes/Object"); + expect(internalURL).toEqual('/classes/Object'); }); - it("should handle a batch request without transaction", async () => { + it('should handle a batch request without transaction', async () => { const response = await request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/batch", + url: 'http://localhost:8378/1/batch', body: JSON.stringify({ requests: [ { - method: "POST", - path: "/1/classes/MyObject", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject", - body: { key: "value2" }, + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value2' }, }, ], }), @@ -162,33 +162,33 @@ describe("batch", () => { expect(response.data[0].success.createdAt).toBeDefined(); expect(response.data[1].success.objectId).toBeDefined(); expect(response.data[1].success.createdAt).toBeDefined(); - const query = new Parse.Query("MyObject"); + const query = new Parse.Query('MyObject'); const results = await query.find(); expect(createSpy.calls.count()).toBe(2); expect(createSpy.calls.argsFor(0)[3]).toEqual(null); expect(createSpy.calls.argsFor(1)[3]).toEqual(null); - expect(results.map(result => result.get("key")).sort()).toEqual([ - "value1", - "value2", + expect(results.map(result => result.get('key')).sort()).toEqual([ + 'value1', + 'value2', ]); }); - it("should handle a batch request with transaction = false", async () => { + it('should handle a batch request with transaction = false', async () => { const response = await request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/batch", + url: 'http://localhost:8378/1/batch', body: JSON.stringify({ requests: [ { - method: "POST", - path: "/1/classes/MyObject", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject", - body: { key: "value2" }, + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value2' }, }, ], transaction: false, @@ -200,42 +200,42 @@ describe("batch", () => { expect(response.data[1].success.objectId).toBeDefined(); expect(response.data[1].success.createdAt).toBeDefined(); - const query = new Parse.Query("MyObject"); + const query = new Parse.Query('MyObject'); const results = await query.find(); expect(createSpy.calls.count()).toBe(2); expect(createSpy.calls.argsFor(0)[3]).toEqual(null); expect(createSpy.calls.argsFor(1)[3]).toEqual(null); - expect(results.map(result => result.get("key")).sort()).toEqual([ - "value1", - "value2", + expect(results.map(result => result.get('key')).sort()).toEqual([ + 'value1', + 'value2', ]); }); if ( - process.env.MONGODB_TOPOLOGY === "replicaset" || - process.env.PARSE_SERVER_TEST_DB === "postgres" + process.env.MONGODB_TOPOLOGY === 'replicaset' || + process.env.PARSE_SERVER_TEST_DB === 'postgres' ) { - describe("transactions", () => { - it("should handle a batch request with transaction = true", async () => { - const myObject = new Parse.Object("MyObject"); // This is important because transaction only works on pre-existing collections + describe('transactions', () => { + it('should handle a batch request with transaction = true', async () => { + const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections await myObject.save(); await myObject.destroy(); createSpy.calls.reset(); const response = await request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/batch", + url: 'http://localhost:8378/1/batch', body: JSON.stringify({ requests: [ { - method: "POST", - path: "/1/classes/MyObject", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject", - body: { key: "value2" }, + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value2' }, }, ], transaction: true, @@ -246,7 +246,7 @@ describe("batch", () => { expect(response.data[0].success.createdAt).toBeDefined(); expect(response.data[1].success.objectId).toBeDefined(); expect(response.data[1].success.createdAt).toBeDefined(); - const query = new Parse.Query("MyObject"); + const query = new Parse.Query('MyObject'); const results = await query.find(); expect(createSpy.calls.count()).toBe(2); for (let i = 0; i + 1 < createSpy.calls.length; i = i + 2) { @@ -254,113 +254,113 @@ describe("batch", () => { createSpy.calls.argsFor(i + 1)[3] ); } - expect(results.map(result => result.get("key")).sort()).toEqual([ - "value1", - "value2", + expect(results.map(result => result.get('key')).sort()).toEqual([ + 'value1', + 'value2', ]); }); - it("should not save anything when one operation fails in a transaction", async () => { - const myObject = new Parse.Object("MyObject"); // This is important because transaction only works on pre-existing collections - await myObject.save({ key: "stringField" }); + it('should not save anything when one operation fails in a transaction', async () => { + const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections + await myObject.save({ key: 'stringField' }); await myObject.destroy(); createSpy.calls.reset(); try { // Saving a number to a string field should fail await request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/batch", + url: 'http://localhost:8378/1/batch', body: JSON.stringify({ requests: [ { - method: "POST", - path: "/1/classes/MyObject", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject", + method: 'POST', + path: '/1/classes/MyObject', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject", + method: 'POST', + path: '/1/classes/MyObject', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject", + method: 'POST', + path: '/1/classes/MyObject', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject", + method: 'POST', + path: '/1/classes/MyObject', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject", + method: 'POST', + path: '/1/classes/MyObject', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject", + method: 'POST', + path: '/1/classes/MyObject', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject", + method: 'POST', + path: '/1/classes/MyObject', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject", + method: 'POST', + path: '/1/classes/MyObject', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject", + method: 'POST', + path: '/1/classes/MyObject', body: { key: 10 }, }, ], @@ -370,129 +370,129 @@ describe("batch", () => { fail(); } catch (error) { expect(error).toBeDefined(); - const query = new Parse.Query("MyObject"); + const query = new Parse.Query('MyObject'); const results = await query.find(); expect(results.length).toBe(0); } }); - it("should generate separate session for each call", async () => { - const myObject = new Parse.Object("MyObject"); // This is important because transaction only works on pre-existing collections - await myObject.save({ key: "stringField" }); + it('should generate separate session for each call', async () => { + const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections + await myObject.save({ key: 'stringField' }); await myObject.destroy(); - const myObject2 = new Parse.Object("MyObject2"); // This is important because transaction only works on pre-existing collections - await myObject2.save({ key: "stringField" }); + const myObject2 = new Parse.Object('MyObject2'); // This is important because transaction only works on pre-existing collections + await myObject2.save({ key: 'stringField' }); await myObject2.destroy(); createSpy.calls.reset(); let myObjectCalls = 0; - Parse.Cloud.beforeSave("MyObject", async () => { + Parse.Cloud.beforeSave('MyObject', async () => { myObjectCalls++; if (myObjectCalls === 2) { try { // Saving a number to a string field should fail await request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/batch", + url: 'http://localhost:8378/1/batch', body: JSON.stringify({ requests: [ { - method: "POST", - path: "/1/classes/MyObject2", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject2", + method: 'POST', + path: '/1/classes/MyObject2', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject2", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject2", + method: 'POST', + path: '/1/classes/MyObject2', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject2", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject2", + method: 'POST', + path: '/1/classes/MyObject2', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject2", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject2", + method: 'POST', + path: '/1/classes/MyObject2', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject2", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject2", + method: 'POST', + path: '/1/classes/MyObject2', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject2", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject2", + method: 'POST', + path: '/1/classes/MyObject2', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject2", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject2", + method: 'POST', + path: '/1/classes/MyObject2', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject2", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject2", + method: 'POST', + path: '/1/classes/MyObject2', body: { key: 10 }, }, { - method: "POST", - path: "/1/classes/MyObject2", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject2', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject2", + method: 'POST', + path: '/1/classes/MyObject2', body: { key: 10 }, }, ], transaction: true, }), }); - fail("should fail"); + fail('should fail'); } catch (e) { expect(e).toBeDefined(); } @@ -500,20 +500,20 @@ describe("batch", () => { }); const response = await request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/batch", + url: 'http://localhost:8378/1/batch', body: JSON.stringify({ requests: [ { - method: "POST", - path: "/1/classes/MyObject", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject", - body: { key: "value2" }, + method: 'POST', + path: '/1/classes/MyObject', + body: { key: 'value2' }, }, ], transaction: true, @@ -527,41 +527,41 @@ describe("batch", () => { expect(response.data[1].success.createdAt).toBeDefined(); await request({ - method: "POST", + method: 'POST', headers: headers, - url: "http://localhost:8378/1/batch", + url: 'http://localhost:8378/1/batch', body: JSON.stringify({ requests: [ { - method: "POST", - path: "/1/classes/MyObject3", - body: { key: "value1" }, + method: 'POST', + path: '/1/classes/MyObject3', + body: { key: 'value1' }, }, { - method: "POST", - path: "/1/classes/MyObject3", - body: { key: "value2" }, + method: 'POST', + path: '/1/classes/MyObject3', + body: { key: 'value2' }, }, ], }), }); - const query = new Parse.Query("MyObject"); + const query = new Parse.Query('MyObject'); const results = await query.find(); - expect(results.map(result => result.get("key")).sort()).toEqual([ - "value1", - "value2", + expect(results.map(result => result.get('key')).sort()).toEqual([ + 'value1', + 'value2', ]); - const query2 = new Parse.Query("MyObject2"); + const query2 = new Parse.Query('MyObject2'); const results2 = await query2.find(); expect(results2.length).toEqual(0); - const query3 = new Parse.Query("MyObject3"); + const query3 = new Parse.Query('MyObject3'); const results3 = await query3.find(); - expect(results3.map(result => result.get("key")).sort()).toEqual([ - "value1", - "value2", + expect(results3.map(result => result.get('key')).sort()).toEqual([ + 'value1', + 'value2', ]); expect(createSpy.calls.count() >= 13).toEqual(true); @@ -573,7 +573,7 @@ describe("batch", () => { for (let i = 0; i < createSpy.calls.count(); i++) { const args = createSpy.calls.argsFor(i); switch (args[0]) { - case "MyObject": + case 'MyObject': myObjectDBCalls++; if (!transactionalSession || (myObjectDBCalls - 1) % 2 === 0) { transactionalSession = args[3]; @@ -584,7 +584,7 @@ describe("batch", () => { expect(transactionalSession2).not.toBe(args[3]); } break; - case "MyObject2": + case 'MyObject2': myObject2DBCalls++; if (!transactionalSession2 || (myObject2DBCalls - 1) % 9 === 0) { transactionalSession2 = args[3]; @@ -595,7 +595,7 @@ describe("batch", () => { expect(transactionalSession).not.toBe(args[3]); } break; - case "MyObject3": + case 'MyObject3': myObject3DBCalls++; expect(args[3]).toEqual(null); break; diff --git a/spec/cloud/cloudCodeAbsoluteFile.js b/spec/cloud/cloudCodeAbsoluteFile.js index 70e8b6a3ed..a62b4fcc24 100644 --- a/spec/cloud/cloudCodeAbsoluteFile.js +++ b/spec/cloud/cloudCodeAbsoluteFile.js @@ -1,3 +1,3 @@ -Parse.Cloud.define("cloudCodeInFile", () => { - return "It is possible to define cloud code in a file."; +Parse.Cloud.define('cloudCodeInFile', () => { + return 'It is possible to define cloud code in a file.'; }); diff --git a/spec/cloud/cloudCodeModuleFile.js b/spec/cloud/cloudCodeModuleFile.js index 70e8b6a3ed..a62b4fcc24 100644 --- a/spec/cloud/cloudCodeModuleFile.js +++ b/spec/cloud/cloudCodeModuleFile.js @@ -1,3 +1,3 @@ -Parse.Cloud.define("cloudCodeInFile", () => { - return "It is possible to define cloud code in a file."; +Parse.Cloud.define('cloudCodeInFile', () => { + return 'It is possible to define cloud code in a file.'; }); diff --git a/spec/cloud/cloudCodeRelativeFile.js b/spec/cloud/cloudCodeRelativeFile.js index 70e8b6a3ed..a62b4fcc24 100644 --- a/spec/cloud/cloudCodeRelativeFile.js +++ b/spec/cloud/cloudCodeRelativeFile.js @@ -1,3 +1,3 @@ -Parse.Cloud.define("cloudCodeInFile", () => { - return "It is possible to define cloud code in a file."; +Parse.Cloud.define('cloudCodeInFile', () => { + return 'It is possible to define cloud code in a file.'; }); diff --git a/spec/cryptoUtils.spec.js b/spec/cryptoUtils.spec.js index 7d222a9cda..a3b1c69718 100644 --- a/spec/cryptoUtils.spec.js +++ b/spec/cryptoUtils.spec.js @@ -1,4 +1,4 @@ -const cryptoUtils = require("../lib/cryptoUtils"); +const cryptoUtils = require('../lib/cryptoUtils'); function givesUniqueResults(fn, iterations) { const results = {}; @@ -12,80 +12,80 @@ function givesUniqueResults(fn, iterations) { return true; } -describe("randomString", () => { - it("returns a string", () => { - expect(typeof cryptoUtils.randomString(10)).toBe("string"); +describe('randomString', () => { + it('returns a string', () => { + expect(typeof cryptoUtils.randomString(10)).toBe('string'); }); - it("returns result of the given length", () => { + it('returns result of the given length', () => { expect(cryptoUtils.randomString(11).length).toBe(11); expect(cryptoUtils.randomString(25).length).toBe(25); }); - it("throws if requested length is zero", () => { + it('throws if requested length is zero', () => { expect(() => cryptoUtils.randomString(0)).toThrow(); }); - it("returns unique results", () => { + it('returns unique results', () => { expect(givesUniqueResults(() => cryptoUtils.randomString(10), 100)).toBe( true ); }); }); -describe("randomHexString", () => { - it("returns a string", () => { - expect(typeof cryptoUtils.randomHexString(10)).toBe("string"); +describe('randomHexString', () => { + it('returns a string', () => { + expect(typeof cryptoUtils.randomHexString(10)).toBe('string'); }); - it("returns result of the given length", () => { + it('returns result of the given length', () => { expect(cryptoUtils.randomHexString(10).length).toBe(10); expect(cryptoUtils.randomHexString(32).length).toBe(32); }); - it("throws if requested length is zero", () => { + it('throws if requested length is zero', () => { expect(() => cryptoUtils.randomHexString(0)).toThrow(); }); - it("throws if requested length is not even", () => { + it('throws if requested length is not even', () => { expect(() => cryptoUtils.randomHexString(11)).toThrow(); }); - it("returns unique results", () => { + it('returns unique results', () => { expect(givesUniqueResults(() => cryptoUtils.randomHexString(20), 100)).toBe( true ); }); }); -describe("newObjectId", () => { - it("returns a string", () => { - expect(typeof cryptoUtils.newObjectId()).toBe("string"); +describe('newObjectId', () => { + it('returns a string', () => { + expect(typeof cryptoUtils.newObjectId()).toBe('string'); }); - it("returns result with at least 10 characters", () => { + it('returns result with at least 10 characters', () => { expect(cryptoUtils.newObjectId().length).toBeGreaterThan(9); }); - it("returns result with required number of characters", () => { + it('returns result with required number of characters', () => { expect(cryptoUtils.newObjectId(42).length).toBe(42); }); - it("returns unique results", () => { + it('returns unique results', () => { expect(givesUniqueResults(() => cryptoUtils.newObjectId(), 100)).toBe(true); }); }); -describe("newToken", () => { - it("returns a string", () => { - expect(typeof cryptoUtils.newToken()).toBe("string"); +describe('newToken', () => { + it('returns a string', () => { + expect(typeof cryptoUtils.newToken()).toBe('string'); }); - it("returns result with at least 32 characters", () => { + it('returns result with at least 32 characters', () => { expect(cryptoUtils.newToken().length).toBeGreaterThan(31); }); - it("returns unique results", () => { + it('returns unique results', () => { expect(givesUniqueResults(() => cryptoUtils.newToken(), 100)).toBe(true); }); }); diff --git a/spec/defaultGraphQLTypes.spec.js b/spec/defaultGraphQLTypes.spec.js index a600fcfe21..e702ae0b31 100644 --- a/spec/defaultGraphQLTypes.spec.js +++ b/spec/defaultGraphQLTypes.spec.js @@ -1,4 +1,4 @@ -const { Kind } = require("graphql"); +const { Kind } = require('graphql'); const { TypeValidationError, parseStringValue, @@ -12,7 +12,7 @@ const { BYTES, DATE, FILE, -} = require("../lib/GraphQL/loaders/defaultGraphQLTypes"); +} = require('../lib/GraphQL/loaders/defaultGraphQLTypes'); function createValue(kind, value, values, fields) { return { @@ -32,178 +32,178 @@ function createObjectField(name, value) { }; } -describe("defaultGraphQLTypes", () => { - describe("TypeValidationError", () => { - it("should be an error with specific message", () => { +describe('defaultGraphQLTypes', () => { + describe('TypeValidationError', () => { + it('should be an error with specific message', () => { const typeValidationError = new TypeValidationError( - "somevalue", - "sometype" + 'somevalue', + 'sometype' ); expect(typeValidationError).toEqual(jasmine.any(Error)); expect(typeValidationError.message).toEqual( - "somevalue is not a valid sometype" + 'somevalue is not a valid sometype' ); }); }); - describe("parseStringValue", () => { - it("should return itself if a string", () => { - const myString = "myString"; + describe('parseStringValue', () => { + it('should return itself if a string', () => { + const myString = 'myString'; expect(parseStringValue(myString)).toBe(myString); }); - it("should fail if not a string", () => { + it('should fail if not a string', () => { expect(() => parseStringValue()).toThrow( - jasmine.stringMatching("is not a valid String") + jasmine.stringMatching('is not a valid String') ); expect(() => parseStringValue({})).toThrow( - jasmine.stringMatching("is not a valid String") + jasmine.stringMatching('is not a valid String') ); expect(() => parseStringValue([])).toThrow( - jasmine.stringMatching("is not a valid String") + jasmine.stringMatching('is not a valid String') ); expect(() => parseStringValue(123)).toThrow( - jasmine.stringMatching("is not a valid String") + jasmine.stringMatching('is not a valid String') ); }); }); - describe("parseIntValue", () => { - it("should parse to number if a string", () => { - const myString = "123"; + describe('parseIntValue', () => { + it('should parse to number if a string', () => { + const myString = '123'; expect(parseIntValue(myString)).toBe(123); }); - it("should fail if not a string", () => { + it('should fail if not a string', () => { expect(() => parseIntValue()).toThrow( - jasmine.stringMatching("is not a valid Int") + jasmine.stringMatching('is not a valid Int') ); expect(() => parseIntValue({})).toThrow( - jasmine.stringMatching("is not a valid Int") + jasmine.stringMatching('is not a valid Int') ); expect(() => parseIntValue([])).toThrow( - jasmine.stringMatching("is not a valid Int") + jasmine.stringMatching('is not a valid Int') ); expect(() => parseIntValue(123)).toThrow( - jasmine.stringMatching("is not a valid Int") + jasmine.stringMatching('is not a valid Int') ); }); - it("should fail if not an integer string", () => { - expect(() => parseIntValue("a123")).toThrow( - jasmine.stringMatching("is not a valid Int") + it('should fail if not an integer string', () => { + expect(() => parseIntValue('a123')).toThrow( + jasmine.stringMatching('is not a valid Int') ); - expect(() => parseIntValue("123.4")).toThrow( - jasmine.stringMatching("is not a valid Int") + expect(() => parseIntValue('123.4')).toThrow( + jasmine.stringMatching('is not a valid Int') ); }); }); - describe("parseFloatValue", () => { - it("should parse to number if a string", () => { - expect(parseFloatValue("123")).toBe(123); - expect(parseFloatValue("123.4")).toBe(123.4); + describe('parseFloatValue', () => { + it('should parse to number if a string', () => { + expect(parseFloatValue('123')).toBe(123); + expect(parseFloatValue('123.4')).toBe(123.4); }); - it("should fail if not a string", () => { + it('should fail if not a string', () => { expect(() => parseFloatValue()).toThrow( - jasmine.stringMatching("is not a valid Float") + jasmine.stringMatching('is not a valid Float') ); expect(() => parseFloatValue({})).toThrow( - jasmine.stringMatching("is not a valid Float") + jasmine.stringMatching('is not a valid Float') ); expect(() => parseFloatValue([])).toThrow( - jasmine.stringMatching("is not a valid Float") + jasmine.stringMatching('is not a valid Float') ); }); - it("should fail if not a float string", () => { - expect(() => parseIntValue("a123")).toThrow( - jasmine.stringMatching("is not a valid Int") + it('should fail if not a float string', () => { + expect(() => parseIntValue('a123')).toThrow( + jasmine.stringMatching('is not a valid Int') ); }); }); - describe("parseBooleanValue", () => { - it("should return itself if a boolean", () => { + describe('parseBooleanValue', () => { + it('should return itself if a boolean', () => { let myBoolean = true; expect(parseBooleanValue(myBoolean)).toBe(myBoolean); myBoolean = false; expect(parseBooleanValue(myBoolean)).toBe(myBoolean); }); - it("should fail if not a boolean", () => { + it('should fail if not a boolean', () => { expect(() => parseBooleanValue()).toThrow( - jasmine.stringMatching("is not a valid Boolean") + jasmine.stringMatching('is not a valid Boolean') ); expect(() => parseBooleanValue({})).toThrow( - jasmine.stringMatching("is not a valid Boolean") + jasmine.stringMatching('is not a valid Boolean') ); expect(() => parseBooleanValue([])).toThrow( - jasmine.stringMatching("is not a valid Boolean") + jasmine.stringMatching('is not a valid Boolean') ); expect(() => parseBooleanValue(123)).toThrow( - jasmine.stringMatching("is not a valid Boolean") + jasmine.stringMatching('is not a valid Boolean') ); - expect(() => parseBooleanValue("true")).toThrow( - jasmine.stringMatching("is not a valid Boolean") + expect(() => parseBooleanValue('true')).toThrow( + jasmine.stringMatching('is not a valid Boolean') ); }); }); - describe("parseDateValue", () => { - it("should parse to date if a string", () => { - const myDateString = "2019-05-09T23:12:00.000Z"; + describe('parseDateValue', () => { + it('should parse to date if a string', () => { + const myDateString = '2019-05-09T23:12:00.000Z'; const myDate = new Date(Date.UTC(2019, 4, 9, 23, 12, 0, 0)); expect(parseDateIsoValue(myDateString)).toEqual(myDate); }); - it("should fail if not a string", () => { + it('should fail if not a string', () => { expect(() => parseDateIsoValue()).toThrow( - jasmine.stringMatching("is not a valid Date") + jasmine.stringMatching('is not a valid Date') ); expect(() => parseDateIsoValue({})).toThrow( - jasmine.stringMatching("is not a valid Date") + jasmine.stringMatching('is not a valid Date') ); expect(() => parseDateIsoValue([])).toThrow( - jasmine.stringMatching("is not a valid Date") + jasmine.stringMatching('is not a valid Date') ); expect(() => parseDateIsoValue(123)).toThrow( - jasmine.stringMatching("is not a valid Date") + jasmine.stringMatching('is not a valid Date') ); }); - it("should fail if not a date string", () => { - expect(() => parseDateIsoValue("not a date")).toThrow( - jasmine.stringMatching("is not a valid Date") + it('should fail if not a date string', () => { + expect(() => parseDateIsoValue('not a date')).toThrow( + jasmine.stringMatching('is not a valid Date') ); }); }); - describe("parseValue", () => { - const someString = createValue(Kind.STRING, "somestring"); - const someInt = createValue(Kind.INT, "123"); - const someFloat = createValue(Kind.FLOAT, "123.4"); + describe('parseValue', () => { + const someString = createValue(Kind.STRING, 'somestring'); + const someInt = createValue(Kind.INT, '123'); + const someFloat = createValue(Kind.FLOAT, '123.4'); const someBoolean = createValue(Kind.BOOLEAN, true); const someOther = createValue(undefined, new Object()); const someObject = createValue(Kind.OBJECT, undefined, undefined, [ - createObjectField("someString", someString), - createObjectField("someInt", someInt), - createObjectField("someFloat", someFloat), - createObjectField("someBoolean", someBoolean), - createObjectField("someOther", someOther), + createObjectField('someString', someString), + createObjectField('someInt', someInt), + createObjectField('someFloat', someFloat), + createObjectField('someBoolean', someBoolean), + createObjectField('someOther', someOther), createObjectField( - "someList", + 'someList', createValue(Kind.LIST, undefined, [ createValue(Kind.OBJECT, undefined, undefined, [ - createObjectField("someString", someString), + createObjectField('someString', someString), ]), ]) ), createObjectField( - "someObject", + 'someObject', createValue(Kind.OBJECT, undefined, undefined, [ - createObjectField("someString", someString), + createObjectField('someString', someString), ]) ), ]); @@ -224,62 +224,62 @@ describe("defaultGraphQLTypes", () => { ]), ]); - it("should parse string", () => { - expect(parseValue(someString)).toEqual("somestring"); + it('should parse string', () => { + expect(parseValue(someString)).toEqual('somestring'); }); - it("should parse int", () => { + it('should parse int', () => { expect(parseValue(someInt)).toEqual(123); }); - it("should parse float", () => { + it('should parse float', () => { expect(parseValue(someFloat)).toEqual(123.4); }); - it("should parse boolean", () => { + it('should parse boolean', () => { expect(parseValue(someBoolean)).toEqual(true); }); - it("should parse list", () => { + it('should parse list', () => { expect(parseValue(someList)).toEqual([ - "somestring", + 'somestring', 123, 123.4, true, { - someString: "somestring", + someString: 'somestring', someInt: 123, someFloat: 123.4, someBoolean: true, someOther: {}, someList: [ { - someString: "somestring", + someString: 'somestring', }, ], someObject: { - someString: "somestring", + someString: 'somestring', }, }, {}, [ - "somestring", + 'somestring', 123, 123.4, true, { - someString: "somestring", + someString: 'somestring', someInt: 123, someFloat: 123.4, someBoolean: true, someOther: {}, someList: [ { - someString: "somestring", + someString: 'somestring', }, ], someObject: { - someString: "somestring", + someString: 'somestring', }, }, {}, @@ -287,427 +287,427 @@ describe("defaultGraphQLTypes", () => { ]); }); - it("should parse object", () => { + it('should parse object', () => { expect(parseValue(someObject)).toEqual({ - someString: "somestring", + someString: 'somestring', someInt: 123, someFloat: 123.4, someBoolean: true, someOther: {}, someList: [ { - someString: "somestring", + someString: 'somestring', }, ], someObject: { - someString: "somestring", + someString: 'somestring', }, }); }); - it("should return value otherwise", () => { + it('should return value otherwise', () => { expect(parseValue(someOther)).toEqual(new Object()); }); }); - describe("parseListValues", () => { - it("should parse to list if an array", () => { + describe('parseListValues', () => { + it('should parse to list if an array', () => { expect( parseListValues([ - { kind: Kind.STRING, value: "someString" }, - { kind: Kind.INT, value: "123" }, + { kind: Kind.STRING, value: 'someString' }, + { kind: Kind.INT, value: '123' }, ]) - ).toEqual(["someString", 123]); + ).toEqual(['someString', 123]); }); - it("should fail if not an array", () => { + it('should fail if not an array', () => { expect(() => parseListValues()).toThrow( - jasmine.stringMatching("is not a valid List") + jasmine.stringMatching('is not a valid List') ); expect(() => parseListValues({})).toThrow( - jasmine.stringMatching("is not a valid List") + jasmine.stringMatching('is not a valid List') ); - expect(() => parseListValues("some string")).toThrow( - jasmine.stringMatching("is not a valid List") + expect(() => parseListValues('some string')).toThrow( + jasmine.stringMatching('is not a valid List') ); expect(() => parseListValues(123)).toThrow( - jasmine.stringMatching("is not a valid List") + jasmine.stringMatching('is not a valid List') ); }); }); - describe("parseObjectFields", () => { - it("should parse to list if an array", () => { + describe('parseObjectFields', () => { + it('should parse to list if an array', () => { expect( parseObjectFields([ { - name: { value: "someString" }, - value: { kind: Kind.STRING, value: "someString" }, + name: { value: 'someString' }, + value: { kind: Kind.STRING, value: 'someString' }, }, { - name: { value: "someInt" }, - value: { kind: Kind.INT, value: "123" }, + name: { value: 'someInt' }, + value: { kind: Kind.INT, value: '123' }, }, ]) ).toEqual({ - someString: "someString", + someString: 'someString', someInt: 123, }); }); - it("should fail if not an array", () => { + it('should fail if not an array', () => { expect(() => parseObjectFields()).toThrow( - jasmine.stringMatching("is not a valid Object") + jasmine.stringMatching('is not a valid Object') ); expect(() => parseObjectFields({})).toThrow( - jasmine.stringMatching("is not a valid Object") + jasmine.stringMatching('is not a valid Object') ); - expect(() => parseObjectFields("some string")).toThrow( - jasmine.stringMatching("is not a valid Object") + expect(() => parseObjectFields('some string')).toThrow( + jasmine.stringMatching('is not a valid Object') ); expect(() => parseObjectFields(123)).toThrow( - jasmine.stringMatching("is not a valid Object") + jasmine.stringMatching('is not a valid Object') ); }); }); - describe("Date", () => { - describe("parse literal", () => { + describe('Date', () => { + describe('parse literal', () => { const { parseLiteral } = DATE; - it("should parse to date if string", () => { - const date = "2019-05-09T23:12:00.000Z"; + it('should parse to date if string', () => { + const date = '2019-05-09T23:12:00.000Z'; expect(parseLiteral(createValue(Kind.STRING, date))).toEqual({ - __type: "Date", + __type: 'Date', iso: new Date(date), }); }); - it("should parse to date if object", () => { - const date = "2019-05-09T23:12:00.000Z"; + it('should parse to date if object', () => { + const date = '2019-05-09T23:12:00.000Z'; expect( parseLiteral( createValue(Kind.OBJECT, undefined, undefined, [ - createObjectField("__type", { value: "Date" }), - createObjectField("iso", { value: date, kind: Kind.STRING }), + createObjectField('__type', { value: 'Date' }), + createObjectField('iso', { value: date, kind: Kind.STRING }), ]) ) ).toEqual({ - __type: "Date", + __type: 'Date', iso: new Date(date), }); }); - it("should fail if not an valid object or string", () => { + it('should fail if not an valid object or string', () => { expect(() => parseLiteral({})).toThrow( - jasmine.stringMatching("is not a valid Date") + jasmine.stringMatching('is not a valid Date') ); expect(() => parseLiteral( createValue(Kind.OBJECT, undefined, undefined, [ - createObjectField("__type", { value: "Foo" }), - createObjectField("iso", { value: "2019-05-09T23:12:00.000Z" }), + createObjectField('__type', { value: 'Foo' }), + createObjectField('iso', { value: '2019-05-09T23:12:00.000Z' }), ]) ) - ).toThrow(jasmine.stringMatching("is not a valid Date")); + ).toThrow(jasmine.stringMatching('is not a valid Date')); expect(() => parseLiteral([])).toThrow( - jasmine.stringMatching("is not a valid Date") + jasmine.stringMatching('is not a valid Date') ); expect(() => parseLiteral(123)).toThrow( - jasmine.stringMatching("is not a valid Date") + jasmine.stringMatching('is not a valid Date') ); }); }); - describe("parse value", () => { + describe('parse value', () => { const { parseValue } = DATE; - it("should parse string value", () => { - const date = "2019-05-09T23:12:00.000Z"; + it('should parse string value', () => { + const date = '2019-05-09T23:12:00.000Z'; expect(parseValue(date)).toEqual({ - __type: "Date", + __type: 'Date', iso: new Date(date), }); }); - it("should parse object value", () => { + it('should parse object value', () => { const input = { - __type: "Date", - iso: new Date("2019-05-09T23:12:00.000Z"), + __type: 'Date', + iso: new Date('2019-05-09T23:12:00.000Z'), }; expect(parseValue(input)).toEqual(input); }); - it("should fail if not an valid object or string", () => { + it('should fail if not an valid object or string', () => { expect(() => parseValue({})).toThrow( - jasmine.stringMatching("is not a valid Date") + jasmine.stringMatching('is not a valid Date') ); expect(() => parseValue({ - __type: "Foo", - iso: "2019-05-09T23:12:00.000Z", + __type: 'Foo', + iso: '2019-05-09T23:12:00.000Z', }) - ).toThrow(jasmine.stringMatching("is not a valid Date")); + ).toThrow(jasmine.stringMatching('is not a valid Date')); expect(() => parseValue({ - __type: "Date", - iso: "foo", + __type: 'Date', + iso: 'foo', }) - ).toThrow(jasmine.stringMatching("is not a valid Date")); + ).toThrow(jasmine.stringMatching('is not a valid Date')); expect(() => parseValue([])).toThrow( - jasmine.stringMatching("is not a valid Date") + jasmine.stringMatching('is not a valid Date') ); expect(() => parseValue(123)).toThrow( - jasmine.stringMatching("is not a valid Date") + jasmine.stringMatching('is not a valid Date') ); }); }); - describe("serialize date type", () => { + describe('serialize date type', () => { const { serialize } = DATE; - it("should do nothing if string", () => { - const str = "2019-05-09T23:12:00.000Z"; + it('should do nothing if string', () => { + const str = '2019-05-09T23:12:00.000Z'; expect(serialize(str)).toBe(str); }); - it("should serialize date", () => { + it('should serialize date', () => { const date = new Date(); expect(serialize(date)).toBe(date.toISOString()); }); - it("should return iso value if object", () => { - const iso = "2019-05-09T23:12:00.000Z"; + it('should return iso value if object', () => { + const iso = '2019-05-09T23:12:00.000Z'; const date = { - __type: "Date", + __type: 'Date', iso, }; expect(serialize(date)).toEqual(iso); }); - it("should fail if not an valid object or string", () => { + it('should fail if not an valid object or string', () => { expect(() => serialize({})).toThrow( - jasmine.stringMatching("is not a valid Date") + jasmine.stringMatching('is not a valid Date') ); expect(() => serialize({ - __type: "Foo", - iso: "2019-05-09T23:12:00.000Z", + __type: 'Foo', + iso: '2019-05-09T23:12:00.000Z', }) - ).toThrow(jasmine.stringMatching("is not a valid Date")); + ).toThrow(jasmine.stringMatching('is not a valid Date')); expect(() => serialize([])).toThrow( - jasmine.stringMatching("is not a valid Date") + jasmine.stringMatching('is not a valid Date') ); expect(() => serialize(123)).toThrow( - jasmine.stringMatching("is not a valid Date") + jasmine.stringMatching('is not a valid Date') ); }); }); }); - describe("Bytes", () => { - describe("parse literal", () => { + describe('Bytes', () => { + describe('parse literal', () => { const { parseLiteral } = BYTES; - it("should parse to bytes if string", () => { - expect(parseLiteral(createValue(Kind.STRING, "bytesContent"))).toEqual({ - __type: "Bytes", - base64: "bytesContent", + it('should parse to bytes if string', () => { + expect(parseLiteral(createValue(Kind.STRING, 'bytesContent'))).toEqual({ + __type: 'Bytes', + base64: 'bytesContent', }); }); - it("should parse to bytes if object", () => { + it('should parse to bytes if object', () => { expect( parseLiteral( createValue(Kind.OBJECT, undefined, undefined, [ - createObjectField("__type", { value: "Bytes" }), - createObjectField("base64", { value: "bytesContent" }), + createObjectField('__type', { value: 'Bytes' }), + createObjectField('base64', { value: 'bytesContent' }), ]) ) ).toEqual({ - __type: "Bytes", - base64: "bytesContent", + __type: 'Bytes', + base64: 'bytesContent', }); }); - it("should fail if not an valid object or string", () => { + it('should fail if not an valid object or string', () => { expect(() => parseLiteral({})).toThrow( - jasmine.stringMatching("is not a valid Bytes") + jasmine.stringMatching('is not a valid Bytes') ); expect(() => parseLiteral( createValue(Kind.OBJECT, undefined, undefined, [ - createObjectField("__type", { value: "Foo" }), - createObjectField("base64", { value: "bytesContent" }), + createObjectField('__type', { value: 'Foo' }), + createObjectField('base64', { value: 'bytesContent' }), ]) ) - ).toThrow(jasmine.stringMatching("is not a valid Bytes")); + ).toThrow(jasmine.stringMatching('is not a valid Bytes')); expect(() => parseLiteral([])).toThrow( - jasmine.stringMatching("is not a valid Bytes") + jasmine.stringMatching('is not a valid Bytes') ); expect(() => parseLiteral(123)).toThrow( - jasmine.stringMatching("is not a valid Bytes") + jasmine.stringMatching('is not a valid Bytes') ); }); }); - describe("parse value", () => { + describe('parse value', () => { const { parseValue } = BYTES; - it("should parse string value", () => { - expect(parseValue("bytesContent")).toEqual({ - __type: "Bytes", - base64: "bytesContent", + it('should parse string value', () => { + expect(parseValue('bytesContent')).toEqual({ + __type: 'Bytes', + base64: 'bytesContent', }); }); - it("should parse object value", () => { + it('should parse object value', () => { const input = { - __type: "Bytes", - base64: "bytesContent", + __type: 'Bytes', + base64: 'bytesContent', }; expect(parseValue(input)).toEqual(input); }); - it("should fail if not an valid object or string", () => { + it('should fail if not an valid object or string', () => { expect(() => parseValue({})).toThrow( - jasmine.stringMatching("is not a valid Bytes") + jasmine.stringMatching('is not a valid Bytes') ); expect(() => parseValue({ - __type: "Foo", - base64: "bytesContent", + __type: 'Foo', + base64: 'bytesContent', }) - ).toThrow(jasmine.stringMatching("is not a valid Bytes")); + ).toThrow(jasmine.stringMatching('is not a valid Bytes')); expect(() => parseValue([])).toThrow( - jasmine.stringMatching("is not a valid Bytes") + jasmine.stringMatching('is not a valid Bytes') ); expect(() => parseValue(123)).toThrow( - jasmine.stringMatching("is not a valid Bytes") + jasmine.stringMatching('is not a valid Bytes') ); }); }); - describe("serialize bytes type", () => { + describe('serialize bytes type', () => { const { serialize } = BYTES; - it("should do nothing if string", () => { - const str = "foo"; + it('should do nothing if string', () => { + const str = 'foo'; expect(serialize(str)).toBe(str); }); - it("should return base64 value if object", () => { - const base64Content = "bytesContent"; + it('should return base64 value if object', () => { + const base64Content = 'bytesContent'; const bytes = { - __type: "Bytes", + __type: 'Bytes', base64: base64Content, }; expect(serialize(bytes)).toEqual(base64Content); }); - it("should fail if not an valid object or string", () => { + it('should fail if not an valid object or string', () => { expect(() => serialize({})).toThrow( - jasmine.stringMatching("is not a valid Bytes") + jasmine.stringMatching('is not a valid Bytes') ); expect(() => serialize({ - __type: "Foo", - base64: "bytesContent", + __type: 'Foo', + base64: 'bytesContent', }) - ).toThrow(jasmine.stringMatching("is not a valid Bytes")); + ).toThrow(jasmine.stringMatching('is not a valid Bytes')); expect(() => serialize([])).toThrow( - jasmine.stringMatching("is not a valid Bytes") + jasmine.stringMatching('is not a valid Bytes') ); expect(() => serialize(123)).toThrow( - jasmine.stringMatching("is not a valid Bytes") + jasmine.stringMatching('is not a valid Bytes') ); }); }); }); - describe("File", () => { - describe("parse literal", () => { + describe('File', () => { + describe('parse literal', () => { const { parseLiteral } = FILE; - it("should parse to file if string", () => { - expect(parseLiteral(createValue(Kind.STRING, "parsefile"))).toEqual({ - __type: "File", - name: "parsefile", + it('should parse to file if string', () => { + expect(parseLiteral(createValue(Kind.STRING, 'parsefile'))).toEqual({ + __type: 'File', + name: 'parsefile', }); }); - it("should parse to file if object", () => { + it('should parse to file if object', () => { expect( parseLiteral( createValue(Kind.OBJECT, undefined, undefined, [ - createObjectField("__type", { value: "File" }), - createObjectField("name", { value: "parsefile" }), - createObjectField("url", { value: "myurl" }), + createObjectField('__type', { value: 'File' }), + createObjectField('name', { value: 'parsefile' }), + createObjectField('url', { value: 'myurl' }), ]) ) ).toEqual({ - __type: "File", - name: "parsefile", - url: "myurl", + __type: 'File', + name: 'parsefile', + url: 'myurl', }); }); - it("should fail if not an valid object or string", () => { + it('should fail if not an valid object or string', () => { expect(() => parseLiteral({})).toThrow( - jasmine.stringMatching("is not a valid File") + jasmine.stringMatching('is not a valid File') ); expect(() => parseLiteral( createValue(Kind.OBJECT, undefined, undefined, [ - createObjectField("__type", { value: "Foo" }), - createObjectField("name", { value: "parsefile" }), - createObjectField("url", { value: "myurl" }), + createObjectField('__type', { value: 'Foo' }), + createObjectField('name', { value: 'parsefile' }), + createObjectField('url', { value: 'myurl' }), ]) ) - ).toThrow(jasmine.stringMatching("is not a valid File")); + ).toThrow(jasmine.stringMatching('is not a valid File')); expect(() => parseLiteral([])).toThrow( - jasmine.stringMatching("is not a valid File") + jasmine.stringMatching('is not a valid File') ); expect(() => parseLiteral(123)).toThrow( - jasmine.stringMatching("is not a valid File") + jasmine.stringMatching('is not a valid File') ); }); }); - describe("serialize file type", () => { + describe('serialize file type', () => { const { serialize } = FILE; - it("should do nothing if string", () => { - const str = "foo"; + it('should do nothing if string', () => { + const str = 'foo'; expect(serialize(str)).toBe(str); }); - it("should return file name if object", () => { - const fileName = "parsefile"; + it('should return file name if object', () => { + const fileName = 'parsefile'; const file = { - __type: "File", + __type: 'File', name: fileName, - url: "myurl", + url: 'myurl', }; expect(serialize(file)).toEqual(fileName); }); - it("should fail if not an valid object or string", () => { + it('should fail if not an valid object or string', () => { expect(() => serialize({})).toThrow( - jasmine.stringMatching("is not a valid File") + jasmine.stringMatching('is not a valid File') ); expect(() => serialize({ - __type: "Foo", - name: "parsefile", - url: "myurl", + __type: 'Foo', + name: 'parsefile', + url: 'myurl', }) - ).toThrow(jasmine.stringMatching("is not a valid File")); + ).toThrow(jasmine.stringMatching('is not a valid File')); expect(() => serialize([])).toThrow( - jasmine.stringMatching("is not a valid File") + jasmine.stringMatching('is not a valid File') ); expect(() => serialize(123)).toThrow( - jasmine.stringMatching("is not a valid File") + jasmine.stringMatching('is not a valid File') ); }); }); diff --git a/spec/eslint.config.js b/spec/eslint.config.js index 2f8faa2aec..cf0dc6d846 100644 --- a/spec/eslint.config.js +++ b/spec/eslint.config.js @@ -1,57 +1,57 @@ -const js = require("@eslint/js"); -const globals = require("globals"); +const js = require('@eslint/js'); +const globals = require('globals'); module.exports = [ js.configs.recommended, { languageOptions: { - ecmaVersion: "latest", - sourceType: "module", + ecmaVersion: 'latest', + sourceType: 'module', globals: { ...globals.node, ...globals.jasmine, - mockFetch: "readonly", - Parse: "readonly", - reconfigureServer: "readonly", - createTestUser: "readonly", - jfail: "readonly", - ok: "readonly", - strictEqual: "readonly", - TestObject: "readonly", - Item: "readonly", - Container: "readonly", - equal: "readonly", - expectAsync: "readonly", - notEqual: "readonly", - it_id: "readonly", - fit_id: "readonly", - it_only_db: "readonly", - it_only_mongodb_version: "readonly", - it_only_postgres_version: "readonly", - it_only_node_version: "readonly", - fit_only_mongodb_version: "readonly", - fit_only_postgres_version: "readonly", - fit_only_node_version: "readonly", - it_exclude_dbs: "readonly", - fit_exclude_dbs: "readonly", - describe_only_db: "readonly", - fdescribe_only_db: "readonly", - describe_only: "readonly", - fdescribe_only: "readonly", - on_db: "readonly", - defaultConfiguration: "readonly", - range: "readonly", - jequal: "readonly", - create: "readonly", - arrayContains: "readonly", - databaseAdapter: "readonly", - databaseURI: "readonly", + mockFetch: 'readonly', + Parse: 'readonly', + reconfigureServer: 'readonly', + createTestUser: 'readonly', + jfail: 'readonly', + ok: 'readonly', + strictEqual: 'readonly', + TestObject: 'readonly', + Item: 'readonly', + Container: 'readonly', + equal: 'readonly', + expectAsync: 'readonly', + notEqual: 'readonly', + it_id: 'readonly', + fit_id: 'readonly', + it_only_db: 'readonly', + it_only_mongodb_version: 'readonly', + it_only_postgres_version: 'readonly', + it_only_node_version: 'readonly', + fit_only_mongodb_version: 'readonly', + fit_only_postgres_version: 'readonly', + fit_only_node_version: 'readonly', + it_exclude_dbs: 'readonly', + fit_exclude_dbs: 'readonly', + describe_only_db: 'readonly', + fdescribe_only_db: 'readonly', + describe_only: 'readonly', + fdescribe_only: 'readonly', + on_db: 'readonly', + defaultConfiguration: 'readonly', + range: 'readonly', + jequal: 'readonly', + create: 'readonly', + arrayContains: 'readonly', + databaseAdapter: 'readonly', + databaseURI: 'readonly', }, }, rules: { - "no-console": "off", - "no-var": "error", - "no-unused-vars": "off", - "no-useless-escape": "off", + 'no-console': 'off', + 'no-var': 'error', + 'no-unused-vars': 'off', + 'no-useless-escape': 'off', }, }, ]; diff --git a/spec/features.spec.js b/spec/features.spec.js index 435e3890a9..23b43c0b2e 100644 --- a/spec/features.spec.js +++ b/spec/features.spec.js @@ -1,16 +1,16 @@ -"use strict"; +'use strict'; -const request = require("../lib/request"); +const request = require('../lib/request'); -describe("features", () => { - it("should return the serverInfo", async () => { +describe('features', () => { + it('should return the serverInfo', async () => { const response = await request({ - url: "http://localhost:8378/1/serverInfo", + url: 'http://localhost:8378/1/serverInfo', json: true, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "X-Parse-Master-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Master-Key': 'test', }, }); const data = response.data; @@ -19,22 +19,22 @@ describe("features", () => { expect(data.parseServerVersion).toBeDefined(); }); - it("requires the master key to get features", async done => { + it('requires the master key to get features', async done => { try { await request({ - url: "http://localhost:8378/1/serverInfo", + url: 'http://localhost:8378/1/serverInfo', json: true, headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }, }); done.fail( - "The serverInfo request should be rejected without the master key" + 'The serverInfo request should be rejected without the master key' ); } catch (error) { expect(error.status).toEqual(403); - expect(error.data.error).toEqual("unauthorized: master key is required"); + expect(error.data.error).toEqual('unauthorized: master key is required'); done(); } }); diff --git a/spec/graphQLObjectsQueries.js b/spec/graphQLObjectsQueries.js index ccf682b8ad..40bdca8b6d 100644 --- a/spec/graphQLObjectsQueries.js +++ b/spec/graphQLObjectsQueries.js @@ -1,29 +1,29 @@ -const { offsetToCursor } = require("graphql-relay"); +const { offsetToCursor } = require('graphql-relay'); const { calculateSkipAndLimit, -} = require("../lib/GraphQL/helpers/objectsQueries"); +} = require('../lib/GraphQL/helpers/objectsQueries'); -describe("GraphQL objectsQueries", () => { - describe("calculateSkipAndLimit", () => { - it("should fail with invalid params", () => { +describe('GraphQL objectsQueries', () => { + describe('calculateSkipAndLimit', () => { + it('should fail with invalid params', () => { expect(() => calculateSkipAndLimit(-1)).toThrow( - jasmine.stringMatching("Skip should be a positive number") + jasmine.stringMatching('Skip should be a positive number') ); expect(() => calculateSkipAndLimit(1, -1)).toThrow( - jasmine.stringMatching("First should be a positive number") + jasmine.stringMatching('First should be a positive number') ); expect(() => calculateSkipAndLimit(1, 1, offsetToCursor(-1))).toThrow( - jasmine.stringMatching("After is not a valid curso") + jasmine.stringMatching('After is not a valid curso') ); expect(() => calculateSkipAndLimit(1, 1, offsetToCursor(1), -1)).toThrow( - jasmine.stringMatching("Last should be a positive number") + jasmine.stringMatching('Last should be a positive number') ); expect(() => calculateSkipAndLimit(1, 1, offsetToCursor(1), 1, offsetToCursor(-1)) - ).toThrow(jasmine.stringMatching("Before is not a valid curso")); + ).toThrow(jasmine.stringMatching('Before is not a valid curso')); }); - it("should work only with skip", () => { + it('should work only with skip', () => { expect(calculateSkipAndLimit(10)).toEqual({ skip: 10, limit: undefined, @@ -31,7 +31,7 @@ describe("GraphQL objectsQueries", () => { }); }); - it("should work only with after", () => { + it('should work only with after', () => { expect( calculateSkipAndLimit(undefined, undefined, offsetToCursor(9)) ).toEqual({ @@ -41,7 +41,7 @@ describe("GraphQL objectsQueries", () => { }); }); - it("should work with limit and after", () => { + it('should work with limit and after', () => { expect(calculateSkipAndLimit(10, undefined, offsetToCursor(9))).toEqual({ skip: 20, limit: undefined, @@ -49,7 +49,7 @@ describe("GraphQL objectsQueries", () => { }); }); - it("first alone should set the limit", () => { + it('first alone should set the limit', () => { expect(calculateSkipAndLimit(10, 30, offsetToCursor(9))).toEqual({ skip: 20, limit: 30, @@ -57,7 +57,7 @@ describe("GraphQL objectsQueries", () => { }); }); - it("if before cursor is less than skipped items, no objects will be returned", () => { + it('if before cursor is less than skipped items, no objects will be returned', () => { expect( calculateSkipAndLimit( 10, @@ -73,7 +73,7 @@ describe("GraphQL objectsQueries", () => { }); }); - it("if before cursor is greater than returned objects set by limit, nothing is changed", () => { + it('if before cursor is greater than returned objects set by limit, nothing is changed', () => { expect( calculateSkipAndLimit( 10, @@ -89,7 +89,7 @@ describe("GraphQL objectsQueries", () => { }); }); - it("if before cursor is less than returned objects set by limit, limit is adjusted", () => { + it('if before cursor is less than returned objects set by limit, limit is adjusted', () => { expect( calculateSkipAndLimit( 10, @@ -105,7 +105,7 @@ describe("GraphQL objectsQueries", () => { }); }); - it("last should work alone but requires pre count", () => { + it('last should work alone but requires pre count', () => { expect( calculateSkipAndLimit(undefined, undefined, undefined, 10) ).toEqual({ @@ -115,7 +115,7 @@ describe("GraphQL objectsQueries", () => { }); }); - it("last should be adjusted to max limit", () => { + it('last should be adjusted to max limit', () => { expect( calculateSkipAndLimit(undefined, undefined, undefined, 10, undefined, 5) ).toEqual({ @@ -125,7 +125,7 @@ describe("GraphQL objectsQueries", () => { }); }); - it("no objects will be returned if last is equal to 0", () => { + it('no objects will be returned if last is equal to 0', () => { expect(calculateSkipAndLimit(undefined, undefined, undefined, 0)).toEqual( { skip: undefined, @@ -135,7 +135,7 @@ describe("GraphQL objectsQueries", () => { ); }); - it("nothing changes if last is bigger than the calculared limit", () => { + it('nothing changes if last is bigger than the calculared limit', () => { expect( calculateSkipAndLimit(10, 30, offsetToCursor(9), 30, offsetToCursor(40)) ).toEqual({ @@ -145,7 +145,7 @@ describe("GraphQL objectsQueries", () => { }); }); - it("If last is small than limit, new limit is calculated", () => { + it('If last is small than limit, new limit is calculated', () => { expect( calculateSkipAndLimit(10, 30, offsetToCursor(9), 10, offsetToCursor(40)) ).toEqual({ diff --git a/spec/helper.js b/spec/helper.js index 0aec0a777f..2c1786481e 100644 --- a/spec/helper.js +++ b/spec/helper.js @@ -1,15 +1,15 @@ -"use strict"; -const dns = require("dns"); -const semver = require("semver"); -const Parse = require("parse/node"); -const CurrentSpecReporter = require("./support/CurrentSpecReporter.js"); -const { SpecReporter } = require("jasmine-spec-reporter"); -const SchemaCache = require("../lib/Adapters/Cache/SchemaCache").default; -const { sleep, Connections } = require("../lib/TestUtils"); +'use strict'; +const dns = require('dns'); +const semver = require('semver'); +const Parse = require('parse/node'); +const CurrentSpecReporter = require('./support/CurrentSpecReporter.js'); +const { SpecReporter } = require('jasmine-spec-reporter'); +const SchemaCache = require('../lib/Adapters/Cache/SchemaCache').default; +const { sleep, Connections } = require('../lib/TestUtils'); // Ensure localhost resolves to ipv4 address first on node v17+ if (dns.setDefaultResultOrder) { - dns.setDefaultResultOrder("ipv4first"); + dns.setDefaultResultOrder('ipv4first'); } // Sets up a Parse API server for testing. @@ -22,7 +22,7 @@ global.retryFlakyTests(); global.on_db = (db, callback, elseCallback) => { if (process.env.PARSE_SERVER_TEST_DB == db) { return callback(); - } else if (!process.env.PARSE_SERVER_TEST_DB && db == "mongo") { + } else if (!process.env.PARSE_SERVER_TEST_DB && db == 'mongo') { return callback(); } if (elseCallback) { @@ -31,52 +31,52 @@ global.on_db = (db, callback, elseCallback) => { }; if (global._babelPolyfill) { - console.error("We should not use polyfilled tests"); + console.error('We should not use polyfilled tests'); process.exit(1); } process.noDeprecation = true; -const cache = require("../lib/cache").default; -const defaults = require("../lib/defaults").default; -const ParseServer = require("../lib/index").ParseServer; -const loadAdapter = require("../lib/Adapters/AdapterLoader").loadAdapter; -const path = require("path"); -const TestUtils = require("../lib/TestUtils"); +const cache = require('../lib/cache').default; +const defaults = require('../lib/defaults').default; +const ParseServer = require('../lib/index').ParseServer; +const loadAdapter = require('../lib/Adapters/AdapterLoader').loadAdapter; +const path = require('path'); +const TestUtils = require('../lib/TestUtils'); const GridFSBucketAdapter = - require("../lib/Adapters/Files/GridFSBucketAdapter").GridFSBucketAdapter; -const FSAdapter = require("@parse/fs-files-adapter"); + require('../lib/Adapters/Files/GridFSBucketAdapter').GridFSBucketAdapter; +const FSAdapter = require('@parse/fs-files-adapter'); const PostgresStorageAdapter = - require("../lib/Adapters/Storage/Postgres/PostgresStorageAdapter").default; + require('../lib/Adapters/Storage/Postgres/PostgresStorageAdapter').default; const MongoStorageAdapter = - require("../lib/Adapters/Storage/Mongo/MongoStorageAdapter").default; + require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter').default; const RedisCacheAdapter = - require("../lib/Adapters/Cache/RedisCacheAdapter").default; -const RESTController = require("parse/lib/node/RESTController").default; + require('../lib/Adapters/Cache/RedisCacheAdapter').default; +const RESTController = require('parse/lib/node/RESTController').default; const { VolatileClassesSchemas, -} = require("../lib/Controllers/SchemaController"); +} = require('../lib/Controllers/SchemaController'); const mongoURI = - "mongodb://localhost:27017/parseServerMongoAdapterTestDatabase"; + 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase'; const postgresURI = - "postgres://localhost:5432/parse_server_postgres_adapter_test_database"; + 'postgres://localhost:5432/parse_server_postgres_adapter_test_database'; let databaseAdapter; let databaseURI; if (process.env.PARSE_SERVER_DATABASE_ADAPTER) { databaseAdapter = JSON.parse(process.env.PARSE_SERVER_DATABASE_ADAPTER); databaseAdapter = loadAdapter(databaseAdapter); -} else if (process.env.PARSE_SERVER_TEST_DB === "postgres") { +} else if (process.env.PARSE_SERVER_TEST_DB === 'postgres') { databaseURI = process.env.PARSE_SERVER_TEST_DATABASE_URI || postgresURI; databaseAdapter = new PostgresStorageAdapter({ uri: databaseURI, - collectionPrefix: "test_", + collectionPrefix: 'test_', }); } else { databaseURI = mongoURI; databaseAdapter = new MongoStorageAdapter({ uri: databaseURI, - collectionPrefix: "test_", + collectionPrefix: 'test_', }); } @@ -85,7 +85,7 @@ const serverURL = `http://localhost:${port}/1`; let filesAdapter; on_db( - "mongo", + 'mongo', () => { filesAdapter = new GridFSBucketAdapter(mongoURI); }, @@ -98,7 +98,7 @@ let logLevel; let silent = true; if (process.env.VERBOSE) { silent = false; - logLevel = "verbose"; + logLevel = 'verbose'; } if (process.env.PARSE_SERVER_LOG_LEVEL) { silent = false; @@ -109,22 +109,22 @@ const defaultConfiguration = { filesAdapter, serverURL, databaseAdapter, - appId: "test", - javascriptKey: "test", - dotNetKey: "windows", - clientKey: "client", - restAPIKey: "rest", - webhookKey: "hook", - masterKey: "test", - maintenanceKey: "testing", - readOnlyMasterKey: "read-only-test", - fileKey: "test", + appId: 'test', + javascriptKey: 'test', + dotNetKey: 'windows', + clientKey: 'client', + restAPIKey: 'rest', + webhookKey: 'hook', + masterKey: 'test', + maintenanceKey: 'testing', + readOnlyMasterKey: 'read-only-test', + fileKey: 'test', directAccess: true, silent, verbose: !silent, logLevel, liveQuery: { - classNames: ["TestObject"], + classNames: ['TestObject'], }, startLiveQueryServer: true, fileUpload: { @@ -134,8 +134,8 @@ const defaultConfiguration = { }, push: { android: { - senderId: "yolo", - apiKey: "yolo", + senderId: 'yolo', + apiKey: 'yolo', }, }, auth: { @@ -143,7 +143,7 @@ const defaultConfiguration = { custom: mockCustom(), facebook: mockFacebook(), myoauth: { - module: path.resolve(__dirname, "support/myoauth"), // relative path as it's run from src + module: path.resolve(__dirname, 'support/myoauth'), // relative path as it's run from src }, shortLivedAuth: mockShortLivedAuth(), }, @@ -153,11 +153,11 @@ const defaultConfiguration = { if (silent) { defaultConfiguration.logLevels = { - cloudFunctionSuccess: "silent", - cloudFunctionError: "silent", - triggerAfter: "silent", - triggerBeforeError: "silent", - triggerBeforeSuccess: "silent", + cloudFunctionSuccess: 'silent', + cloudFunctionError: 'silent', + triggerAfter: 'silent', + triggerBeforeError: 'silent', + triggerBeforeSuccess: 'silent', }; } @@ -185,14 +185,14 @@ const reconfigureServer = async (changedConfiguration = {}) => { didChangeConfiguration = Object.keys(changedConfiguration).length !== 0; databaseAdapter = new databaseAdapter.constructor({ uri: databaseURI, - collectionPrefix: "test_", + collectionPrefix: 'test_', }); defaultConfiguration.databaseAdapter = databaseAdapter; global.databaseAdapter = databaseAdapter; if (filesAdapter instanceof GridFSBucketAdapter) { defaultConfiguration.filesAdapter = new GridFSBucketAdapter(mongoURI); } - if (process.env.PARSE_SERVER_TEST_CACHE === "redis") { + if (process.env.PARSE_SERVER_TEST_CACHE === 'redis') { defaultConfiguration.cacheAdapter = new RedisCacheAdapter(); } const newConfiguration = Object.assign( @@ -200,16 +200,16 @@ const reconfigureServer = async (changedConfiguration = {}) => { defaultConfiguration, changedConfiguration, { - mountPath: "/1", + mountPath: '/1', port, } ); cache.clear(); parseServer = await ParseServer.startApp(newConfiguration); Parse.CoreManager.setRESTController(RESTController); - parseServer.expressApp.use("/1", err => { + parseServer.expressApp.use('/1', err => { console.error(err); - fail("should not call next"); + fail('should not call next'); }); openConnections.track(parseServer.server); if ( @@ -223,16 +223,16 @@ const reconfigureServer = async (changedConfiguration = {}) => { beforeAll(async () => { await reconfigureServer(); - Parse.initialize("test", "test", "test"); + Parse.initialize('test', 'test', 'test'); Parse.serverURL = serverURL; Parse.User.enableUnsafeCurrentUser(); - Parse.CoreManager.set("REQUEST_ATTEMPT_LIMIT", 1); + Parse.CoreManager.set('REQUEST_ATTEMPT_LIMIT', 1); }); global.afterEachFn = async () => { Parse.Cloud._removeAllHooks(); Parse.CoreManager.getLiveQueryController().setDefaultLiveQueryClient(); - defaults.protectedFields = { _User: { "*": ["email"] } }; + defaults.protectedFields = { _User: { '*': ['email'] } }; const allSchemas = await databaseAdapter.getAllClasses().catch(() => []); @@ -240,17 +240,17 @@ global.afterEachFn = async () => { const className = schema.className; expect(className).toEqual({ asymmetricMatch: className => { - if (!className.startsWith("_")) { + if (!className.startsWith('_')) { return true; } return [ - "_User", - "_Installation", - "_Role", - "_Session", - "_Product", - "_Audience", - "_Idempotency", + '_User', + '_Installation', + '_Role', + '_Session', + '_Product', + '_Audience', + '_Idempotency', ].includes(className); }, }); @@ -272,13 +272,13 @@ afterAll(() => { }); const TestObject = Parse.Object.extend({ - className: "TestObject", + className: 'TestObject', }); const Item = Parse.Object.extend({ - className: "Item", + className: 'Item', }); const Container = Parse.Object.extend({ - className: "Container", + className: 'Container', }); // Convenience method to create a new TestObject with a callback @@ -289,8 +289,8 @@ function create(options, callback) { function createTestUser() { const user = new Parse.User(); - user.set("username", "test"); - user.set("password", "moon-y"); + user.set('username', 'test'); + user.set('password', 'moon-y'); return user.signUp(); } @@ -315,19 +315,19 @@ function arrayContains(arr, item) { // Normalizes a JSON object. function normalize(obj) { - if (obj === null || typeof obj !== "object") { + if (obj === null || typeof obj !== 'object') { return JSON.stringify(obj); } if (obj instanceof Array) { - return "[" + obj.map(normalize).join(", ") + "]"; + return '[' + obj.map(normalize).join(', ') + ']'; } - let answer = "{"; + let answer = '{'; for (const key of Object.keys(obj).sort()) { - answer += key + ": "; + answer += key + ': '; answer += normalize(obj[key]); - answer += ", "; + answer += ', '; } - answer += "}"; + answer += '}'; return answer; } @@ -350,7 +350,7 @@ function mockCustomAuthenticator(id, password) { if (authData.id === id && authData.password.startsWith(password)) { return Promise.resolve(); } - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, "not validated"); + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'not validated'); }; custom.validateAppId = function () { return Promise.resolve(); @@ -359,7 +359,7 @@ function mockCustomAuthenticator(id, password) { } function mockCustom() { - return mockCustomAuthenticator("fastrde", "password"); + return mockCustomAuthenticator('fastrde', 'password'); } function mockFacebookAuthenticator(id, token) { @@ -382,7 +382,7 @@ function mockFacebookAuthenticator(id, token) { } function mockFacebook() { - return mockFacebookAuthenticator("8675309", "jenny"); + return mockFacebookAuthenticator('8675309', 'jenny'); } function mockShortLivedAuth() { @@ -395,7 +395,7 @@ function mockShortLivedAuth() { if (authData.access_token == accessToken) { return Promise.resolve(); } else { - return Promise.reject("Invalid access token"); + return Promise.reject('Invalid access token'); } }; auth.validateAppId = function () { @@ -406,9 +406,9 @@ function mockShortLivedAuth() { function mockFetch(mockResponses) { global.fetch = jasmine - .createSpy("fetch") + .createSpy('fetch') .and.callFake((url, options = {}) => { - options.method ||= "GET"; + options.method ||= 'GET'; const mockResponse = mockResponses.find( mock => mock.url === url && mock.method === options.method ); @@ -419,7 +419,7 @@ function mockFetch(mockResponses) { return Promise.resolve({ ok: false, - statusText: "Unknown URL or method", + statusText: 'Unknown URL or method', }); }); } @@ -461,12 +461,12 @@ global.it_exclude_dbs = excluded => { let testExclusionList = []; try { // Fetch test exclusion list - testExclusionList = require("./testExclusionList.json"); + testExclusionList = require('./testExclusionList.json'); console.log( `Using test exclusion list with ${testExclusionList.length} entries` ); } catch (error) { - if (error.code !== "MODULE_NOT_FOUND") { + if (error.code !== 'MODULE_NOT_FOUND') { throw error; } } @@ -488,7 +488,7 @@ global.it_id = id => { global.it_only_db = db => { if ( process.env.PARSE_SERVER_TEST_DB === db || - (!process.env.PARSE_SERVER_TEST_DB && db == "mongo") + (!process.env.PARSE_SERVER_TEST_DB && db == 'mongo') ) { return it; } else { @@ -498,7 +498,7 @@ global.it_only_db = db => { global.it_only_mongodb_version = version => { if (!semver.validRange(version)) { - throw new Error("Invalid version range"); + throw new Error('Invalid version range'); } const envVersion = process.env.MONGODB_VERSION; if (!envVersion || semver.satisfies(envVersion, version)) { @@ -510,7 +510,7 @@ global.it_only_mongodb_version = version => { global.it_only_postgres_version = version => { if (!semver.validRange(version)) { - throw new Error("Invalid version range"); + throw new Error('Invalid version range'); } const envVersion = process.env.POSTGRES_VERSION; if (!envVersion || semver.satisfies(envVersion, version)) { @@ -522,7 +522,7 @@ global.it_only_postgres_version = version => { global.it_only_node_version = version => { if (!semver.validRange(version)) { - throw new Error("Invalid version range"); + throw new Error('Invalid version range'); } const envVersion = process.version; if (!envVersion || semver.satisfies(envVersion, version)) { @@ -534,7 +534,7 @@ global.it_only_node_version = version => { global.fit_only_mongodb_version = version => { if (!semver.validRange(version)) { - throw new Error("Invalid version range"); + throw new Error('Invalid version range'); } const envVersion = process.env.MONGODB_VERSION; if (!envVersion || semver.satisfies(envVersion, version)) { @@ -546,7 +546,7 @@ global.fit_only_mongodb_version = version => { global.fit_only_postgres_version = version => { if (!semver.validRange(version)) { - throw new Error("Invalid version range"); + throw new Error('Invalid version range'); } const envVersion = process.env.POSTGRES_VERSION; if (!envVersion || semver.satisfies(envVersion, version)) { @@ -558,7 +558,7 @@ global.fit_only_postgres_version = version => { global.fit_only_node_version = version => { if (!semver.validRange(version)) { - throw new Error("Invalid version range"); + throw new Error('Invalid version range'); } const envVersion = process.version; if (!envVersion || semver.satisfies(envVersion, version)) { @@ -579,7 +579,7 @@ global.fit_exclude_dbs = excluded => { global.describe_only_db = db => { if (process.env.PARSE_SERVER_TEST_DB == db) { return describe; - } else if (!process.env.PARSE_SERVER_TEST_DB && db == "mongo") { + } else if (!process.env.PARSE_SERVER_TEST_DB && db == 'mongo') { return describe; } else { return xdescribe; @@ -589,7 +589,7 @@ global.describe_only_db = db => { global.fdescribe_only_db = db => { if (process.env.PARSE_SERVER_TEST_DB == db) { return fdescribe; - } else if (!process.env.PARSE_SERVER_TEST_DB && db == "mongo") { + } else if (!process.env.PARSE_SERVER_TEST_DB && db == 'mongo') { return fdescribe; } else { return xdescribe; @@ -624,7 +624,7 @@ jasmine.mockLibrary = function (library, name, mock) { jasmine.restoreLibrary = function (library, name) { if (!libraryCache[library] || !libraryCache[library][name]) { - throw "Can not find library " + library + " " + name; + throw 'Can not find library ' + library + ' ' + name; } require(library)[name] = libraryCache[library][name]; }; diff --git a/spec/index.spec.js b/spec/index.spec.js index 77f09d5cee..dcc141931a 100644 --- a/spec/index.spec.js +++ b/spec/index.spec.js @@ -1,46 +1,46 @@ -"use strict"; -const request = require("../lib/request"); -const parseServerPackage = require("../package.json"); -const MockEmailAdapterWithOptions = require("./support/MockEmailAdapterWithOptions"); -const ParseServer = require("../lib/index"); -const Config = require("../lib/Config"); -const express = require("express"); +'use strict'; +const request = require('../lib/request'); +const parseServerPackage = require('../package.json'); +const MockEmailAdapterWithOptions = require('./support/MockEmailAdapterWithOptions'); +const ParseServer = require('../lib/index'); +const Config = require('../lib/Config'); +const express = require('express'); const MongoStorageAdapter = - require("../lib/Adapters/Storage/Mongo/MongoStorageAdapter").default; + require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter').default; -describe("server", () => { - it("requires a master key and app id", done => { +describe('server', () => { + it('requires a master key and app id', done => { reconfigureServer({ appId: undefined }) .catch(error => { - expect(error).toEqual("You must provide an appId!"); + expect(error).toEqual('You must provide an appId!'); return reconfigureServer({ masterKey: undefined }); }) .catch(error => { - expect(error).toEqual("You must provide a masterKey!"); + expect(error).toEqual('You must provide a masterKey!'); return reconfigureServer({ serverURL: undefined }); }) .catch(error => { - expect(error).toEqual("You must provide a serverURL!"); + expect(error).toEqual('You must provide a serverURL!'); done(); }); }); - it("show warning if any reserved characters in appId", done => { - spyOn(console, "warn").and.callFake(() => {}); - reconfigureServer({ appId: "test!-^" }).then(() => { + it('show warning if any reserved characters in appId', done => { + spyOn(console, 'warn').and.callFake(() => {}); + reconfigureServer({ appId: 'test!-^' }).then(() => { expect(console.warn).toHaveBeenCalled(); return done(); }); }); - it("support http basic authentication with masterkey", done => { - reconfigureServer({ appId: "test" }).then(() => { + it('support http basic authentication with masterkey', done => { + reconfigureServer({ appId: 'test' }).then(() => { request({ - url: "http://localhost:8378/1/classes/TestObject", + url: 'http://localhost:8378/1/classes/TestObject', headers: { Authorization: - "Basic " + Buffer.from("test:" + "test").toString("base64"), + 'Basic ' + Buffer.from('test:' + 'test').toString('base64'), }, }).then(response => { expect(response.status).toEqual(200); @@ -49,14 +49,14 @@ describe("server", () => { }); }); - it("support http basic authentication with javascriptKey", done => { - reconfigureServer({ appId: "test" }).then(() => { + it('support http basic authentication with javascriptKey', done => { + reconfigureServer({ appId: 'test' }).then(() => { request({ - url: "http://localhost:8378/1/classes/TestObject", + url: 'http://localhost:8378/1/classes/TestObject', headers: { Authorization: - "Basic " + - Buffer.from("test:javascript-key=" + "test").toString("base64"), + 'Basic ' + + Buffer.from('test:javascript-key=' + 'test').toString('base64'), }, }).then(response => { expect(response.status).toEqual(200); @@ -65,104 +65,104 @@ describe("server", () => { }); }); - it("fails if database is unreachable", async () => { - spyOn(console, "error").and.callFake(() => {}); + it('fails if database is unreachable', async () => { + spyOn(console, 'error').and.callFake(() => {}); const server = new ParseServer.default({ ...defaultConfiguration, databaseAdapter: new MongoStorageAdapter({ - uri: "mongodb://fake:fake@localhost:43605/drew3", + uri: 'mongodb://fake:fake@localhost:43605/drew3', mongoOptions: { serverSelectionTimeoutMS: 2000, }, }), }); const error = await server.start().catch(e => e); - expect(`${error}`.includes("MongoServerSelectionError")).toBeTrue(); + expect(`${error}`.includes('MongoServerSelectionError')).toBeTrue(); await reconfigureServer(); }); - describe("mail adapter", () => { - it("can load email adapter via object", done => { + describe('mail adapter', () => { + it('can load email adapter via object', done => { reconfigureServer({ - appName: "unused", + appName: 'unused', verifyUserEmails: true, emailAdapter: MockEmailAdapterWithOptions({ - fromAddress: "parse@example.com", - apiKey: "k", - domain: "d", + fromAddress: 'parse@example.com', + apiKey: 'k', + domain: 'd', }), - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(done, fail); }); - it("can load email adapter via class", done => { + it('can load email adapter via class', done => { reconfigureServer({ - appName: "unused", + appName: 'unused', verifyUserEmails: true, emailAdapter: { class: MockEmailAdapterWithOptions, options: { - fromAddress: "parse@example.com", - apiKey: "k", - domain: "d", + fromAddress: 'parse@example.com', + apiKey: 'k', + domain: 'd', }, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }).then(done, fail); }); - it("can load email adapter via module name", async () => { + it('can load email adapter via module name', async () => { const options = { - appName: "unused", + appName: 'unused', verifyUserEmails: true, emailAdapter: { - module: "mock-mail-adapter", + module: 'mock-mail-adapter', options: {}, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }; await reconfigureServer(options); - const config = Config.get("test"); + const config = Config.get('test'); const mailAdapter = config.userController.adapter; expect(mailAdapter.sendMail).toBeDefined(); }); - it("can load email adapter via only module name", async () => { + it('can load email adapter via only module name', async () => { const options = { - appName: "unused", + appName: 'unused', verifyUserEmails: true, - emailAdapter: "mock-mail-adapter", - publicServerURL: "http://localhost:8378/1", + emailAdapter: 'mock-mail-adapter', + publicServerURL: 'http://localhost:8378/1', }; await reconfigureServer(options); - const config = Config.get("test"); + const config = Config.get('test'); const mailAdapter = config.userController.adapter; expect(mailAdapter.sendMail).toBeDefined(); }); - it("throws if you initialize email adapter incorrectly", async () => { + it('throws if you initialize email adapter incorrectly', async () => { const options = { - appName: "unused", + appName: 'unused', verifyUserEmails: true, emailAdapter: { - module: "mock-mail-adapter", + module: 'mock-mail-adapter', options: { throw: true }, }, - publicServerURL: "http://localhost:8378/1", + publicServerURL: 'http://localhost:8378/1', }; await expectAsync(reconfigureServer(options)).toBeRejected( - "MockMailAdapterConstructor" + 'MockMailAdapterConstructor' ); }); }); - it("can report the server version", async done => { + it('can report the server version', async done => { await reconfigureServer(); request({ - url: "http://localhost:8378/1/serverInfo", + url: 'http://localhost:8378/1/serverInfo', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Master-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Master-Key': 'test', }, }).then(response => { const body = response.data; @@ -171,17 +171,17 @@ describe("server", () => { }); }); - it("can properly sets the push support", async done => { + it('can properly sets the push support', async done => { await reconfigureServer(); // default config passes push options - const config = Config.get("test"); + const config = Config.get('test'); expect(config.hasPushSupport).toEqual(true); expect(config.hasPushScheduledSupport).toEqual(false); request({ - url: "http://localhost:8378/1/serverInfo", + url: 'http://localhost:8378/1/serverInfo', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Master-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Master-Key': 'test', }, json: true, }).then(response => { @@ -192,19 +192,19 @@ describe("server", () => { }); }); - it("can properly sets the push support when not configured", done => { + it('can properly sets the push support when not configured', done => { reconfigureServer({ push: undefined, // force no config }) .then(() => { - const config = Config.get("test"); + const config = Config.get('test'); expect(config.hasPushSupport).toEqual(false); expect(config.hasPushScheduledSupport).toEqual(false); request({ - url: "http://localhost:8378/1/serverInfo", + url: 'http://localhost:8378/1/serverInfo', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Master-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Master-Key': 'test', }, json: true, }).then(response => { @@ -217,7 +217,7 @@ describe("server", () => { .catch(done.fail); }); - it("can properly sets the push support ", done => { + it('can properly sets the push support ', done => { reconfigureServer({ push: { adapter: { @@ -227,14 +227,14 @@ describe("server", () => { }, }) .then(() => { - const config = Config.get("test"); + const config = Config.get('test'); expect(config.hasPushSupport).toEqual(true); expect(config.hasPushScheduledSupport).toEqual(false); request({ - url: "http://localhost:8378/1/serverInfo", + url: 'http://localhost:8378/1/serverInfo', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Master-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Master-Key': 'test', }, json: true, }).then(response => { @@ -247,7 +247,7 @@ describe("server", () => { .catch(done.fail); }); - it("can properly sets the push schedule support", done => { + it('can properly sets the push schedule support', done => { reconfigureServer({ push: { adapter: { @@ -258,14 +258,14 @@ describe("server", () => { scheduledPush: true, }) .then(() => { - const config = Config.get("test"); + const config = Config.get('test'); expect(config.hasPushSupport).toEqual(true); expect(config.hasPushScheduledSupport).toEqual(true); request({ - url: "http://localhost:8378/1/serverInfo", + url: 'http://localhost:8378/1/serverInfo', headers: { - "X-Parse-Application-Id": "test", - "X-Parse-Master-Key": "test", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Master-Key': 'test', }, json: true, }).then(response => { @@ -278,76 +278,76 @@ describe("server", () => { .catch(done.fail); }); - it("can respond 200 on path health", done => { + it('can respond 200 on path health', done => { request({ - url: "http://localhost:8378/1/health", + url: 'http://localhost:8378/1/health', }).then(response => { expect(response.status).toBe(200); done(); }); }); - it("can create a parse-server v1", async () => { - await reconfigureServer({ appId: "aTestApp" }); + it('can create a parse-server v1', async () => { + await reconfigureServer({ appId: 'aTestApp' }); const parseServer = new ParseServer.default( Object.assign({}, defaultConfiguration, { - appId: "aTestApp", - masterKey: "aTestMasterKey", - serverURL: "http://localhost:12666/parse", + appId: 'aTestApp', + masterKey: 'aTestMasterKey', + serverURL: 'http://localhost:12666/parse', }) ); await parseServer.start(); - expect(Parse.applicationId).toEqual("aTestApp"); + expect(Parse.applicationId).toEqual('aTestApp'); const app = express(); - app.use("/parse", parseServer.app); + app.use('/parse', parseServer.app); const server = app.listen(12666); - const obj = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); await obj.save(); - const query = await new Parse.Query("AnObject").first(); + const query = await new Parse.Query('AnObject').first(); expect(obj.id).toEqual(query.id); await new Promise(resolve => server.close(resolve)); }); - it("can create a parse-server v2", async () => { - await reconfigureServer({ appId: "anOtherTestApp" }); + it('can create a parse-server v2', async () => { + await reconfigureServer({ appId: 'anOtherTestApp' }); const parseServer = ParseServer.ParseServer( Object.assign({}, defaultConfiguration, { - appId: "anOtherTestApp", - masterKey: "anOtherTestMasterKey", - serverURL: "http://localhost:12667/parse", + appId: 'anOtherTestApp', + masterKey: 'anOtherTestMasterKey', + serverURL: 'http://localhost:12667/parse', }) ); - expect(Parse.applicationId).toEqual("anOtherTestApp"); + expect(Parse.applicationId).toEqual('anOtherTestApp'); await parseServer.start(); const app = express(); - app.use("/parse", parseServer.app); + app.use('/parse', parseServer.app); const server = app.listen(12667); - const obj = new Parse.Object("AnObject"); + const obj = new Parse.Object('AnObject'); await obj.save(); - const q = await new Parse.Query("AnObject").first(); + const q = await new Parse.Query('AnObject').first(); expect(obj.id).toEqual(q.id); await new Promise(resolve => server.close(resolve)); }); - it("has createLiveQueryServer", done => { + it('has createLiveQueryServer', done => { // original implementation through the factory expect(typeof ParseServer.ParseServer.createLiveQueryServer).toEqual( - "function" + 'function' ); // For import calls expect(typeof ParseServer.default.createLiveQueryServer).toEqual( - "function" + 'function' ); done(); }); - it("exposes correct adapters", done => { + it('exposes correct adapters', done => { expect(ParseServer.S3Adapter).toThrow( - "S3Adapter is not provided by parse-server anymore; please install @parse/s3-files-adapter" + 'S3Adapter is not provided by parse-server anymore; please install @parse/s3-files-adapter' ); expect(ParseServer.GCSAdapter).toThrow( - "GCSAdapter is not provided by parse-server anymore; please install @parse/gcs-files-adapter" + 'GCSAdapter is not provided by parse-server anymore; please install @parse/gcs-files-adapter' ); expect(ParseServer.FileSystemAdapter).toThrow(); expect(ParseServer.InMemoryCacheAdapter).toThrow(); @@ -355,118 +355,118 @@ describe("server", () => { done(); }); - it("properly gives publicServerURL when set", done => { - reconfigureServer({ publicServerURL: "https://myserver.com/1" }).then( + it('properly gives publicServerURL when set', done => { + reconfigureServer({ publicServerURL: 'https://myserver.com/1' }).then( () => { - const config = Config.get("test", "http://localhost:8378/1"); - expect(config.mount).toEqual("https://myserver.com/1"); + const config = Config.get('test', 'http://localhost:8378/1'); + expect(config.mount).toEqual('https://myserver.com/1'); done(); } ); }); - it("properly removes trailing slash in mount", done => { + it('properly removes trailing slash in mount', done => { reconfigureServer({}).then(() => { - const config = Config.get("test", "http://localhost:8378/1/"); - expect(config.mount).toEqual("http://localhost:8378/1"); + const config = Config.get('test', 'http://localhost:8378/1/'); + expect(config.mount).toEqual('http://localhost:8378/1'); done(); }); }); - it("should throw when getting invalid mount", done => { - reconfigureServer({ publicServerURL: "blabla:/some" }).catch(error => { + it('should throw when getting invalid mount', done => { + reconfigureServer({ publicServerURL: 'blabla:/some' }).catch(error => { expect(error).toEqual( - "publicServerURL should be a valid HTTPS URL starting with https://" + 'publicServerURL should be a valid HTTPS URL starting with https://' ); done(); }); }); - it("should throw when extendSessionOnUse is invalid", async () => { + it('should throw when extendSessionOnUse is invalid', async () => { await expectAsync( reconfigureServer({ - extendSessionOnUse: "yolo", + extendSessionOnUse: 'yolo', }) - ).toBeRejectedWith("extendSessionOnUse must be a boolean value"); + ).toBeRejectedWith('extendSessionOnUse must be a boolean value'); }); - it("should throw when revokeSessionOnPasswordReset is invalid", async () => { + it('should throw when revokeSessionOnPasswordReset is invalid', async () => { await expectAsync( reconfigureServer({ - revokeSessionOnPasswordReset: "yolo", + revokeSessionOnPasswordReset: 'yolo', }) - ).toBeRejectedWith("revokeSessionOnPasswordReset must be a boolean value"); + ).toBeRejectedWith('revokeSessionOnPasswordReset must be a boolean value'); }); - it("fails if the session length is not a number", done => { - reconfigureServer({ sessionLength: "test" }) + it('fails if the session length is not a number', done => { + reconfigureServer({ sessionLength: 'test' }) .then(done.fail) .catch(error => { - expect(error).toEqual("Session length must be a valid number."); + expect(error).toEqual('Session length must be a valid number.'); done(); }); }); - it("fails if the session length is less than or equal to 0", done => { - reconfigureServer({ sessionLength: "-33" }) + it('fails if the session length is less than or equal to 0', done => { + reconfigureServer({ sessionLength: '-33' }) .then(done.fail) .catch(error => { - expect(error).toEqual("Session length must be a value greater than 0."); - return reconfigureServer({ sessionLength: "0" }); + expect(error).toEqual('Session length must be a value greater than 0.'); + return reconfigureServer({ sessionLength: '0' }); }) .catch(error => { - expect(error).toEqual("Session length must be a value greater than 0."); + expect(error).toEqual('Session length must be a value greater than 0.'); done(); }); }); - it("ignores the session length when expireInactiveSessions set to false", done => { + it('ignores the session length when expireInactiveSessions set to false', done => { reconfigureServer({ - sessionLength: "-33", + sessionLength: '-33', expireInactiveSessions: false, }) .then(() => reconfigureServer({ - sessionLength: "0", + sessionLength: '0', expireInactiveSessions: false, }) ) .then(done); }); - it("fails if default limit is negative", async () => { + it('fails if default limit is negative', async () => { await expectAsync(reconfigureServer({ defaultLimit: -1 })).toBeRejectedWith( - "Default limit must be a value greater than 0." + 'Default limit must be a value greater than 0.' ); }); - it("fails if default limit is wrong type", async () => { - for (const value of ["invalid", {}, [], true]) { + it('fails if default limit is wrong type', async () => { + for (const value of ['invalid', {}, [], true]) { await expectAsync( reconfigureServer({ defaultLimit: value }) - ).toBeRejectedWith("Default limit must be a number."); + ).toBeRejectedWith('Default limit must be a number.'); } }); - it("fails if default limit is zero", async () => { + it('fails if default limit is zero', async () => { await expectAsync(reconfigureServer({ defaultLimit: 0 })).toBeRejectedWith( - "Default limit must be a value greater than 0." + 'Default limit must be a value greater than 0.' ); }); - it("fails if maxLimit is negative", done => { + it('fails if maxLimit is negative', done => { reconfigureServer({ maxLimit: -100 }).catch(error => { - expect(error).toEqual("Max limit must be a value greater than 0."); + expect(error).toEqual('Max limit must be a value greater than 0.'); done(); }); }); - it("fails if you try to set revokeSessionOnPasswordReset to non-boolean", done => { - reconfigureServer({ revokeSessionOnPasswordReset: "non-bool" }).catch(done); + it('fails if you try to set revokeSessionOnPasswordReset to non-boolean', done => { + reconfigureServer({ revokeSessionOnPasswordReset: 'non-bool' }).catch(done); }); - it("fails if you provides invalid ip in masterKeyIps", done => { - reconfigureServer({ masterKeyIps: ["invalidIp", "1.2.3.4"] }).catch( + it('fails if you provides invalid ip in masterKeyIps', done => { + reconfigureServer({ masterKeyIps: ['invalidIp', '1.2.3.4'] }).catch( error => { expect(error).toEqual( 'The Parse Server option "masterKeyIps" contains an invalid IP address "invalidIp".' @@ -476,32 +476,32 @@ describe("server", () => { ); }); - it("should succeed if you provide valid ip in masterKeyIps", done => { + it('should succeed if you provide valid ip in masterKeyIps', done => { reconfigureServer({ - masterKeyIps: ["1.2.3.4", "2001:0db8:0000:0042:0000:8a2e:0370:7334"], + masterKeyIps: ['1.2.3.4', '2001:0db8:0000:0042:0000:8a2e:0370:7334'], }).then(done); }); - it("should set default masterKeyIps for IPv4 and IPv6 localhost", () => { - const definitions = require("../lib/Options/Definitions.js"); + it('should set default masterKeyIps for IPv4 and IPv6 localhost', () => { + const definitions = require('../lib/Options/Definitions.js'); expect(definitions.ParseServerOptions.masterKeyIps.default).toEqual([ - "127.0.0.1", - "::1", + '127.0.0.1', + '::1', ]); }); - it("should load a middleware", done => { + it('should load a middleware', done => { const obj = { middleware: function (req, res, next) { next(); }, }; - const spy = spyOn(obj, "middleware").and.callThrough(); + const spy = spyOn(obj, 'middleware').and.callThrough(); reconfigureServer({ middleware: obj.middleware, }) .then(() => { - const query = new Parse.Query("AnObject"); + const query = new Parse.Query('AnObject'); return query.find(); }) .then(() => { @@ -511,9 +511,9 @@ describe("server", () => { .catch(done.fail); }); - it("should allow direct access", async () => { + it('should allow direct access', async () => { const RESTController = Parse.CoreManager.getRESTController(); - const spy = spyOn(Parse.CoreManager, "setRESTController").and.callThrough(); + const spy = spyOn(Parse.CoreManager, 'setRESTController').and.callThrough(); await reconfigureServer({ directAccess: true, }); @@ -521,135 +521,135 @@ describe("server", () => { Parse.CoreManager.setRESTController(RESTController); }); - it("should load a middleware from string", done => { + it('should load a middleware from string', done => { reconfigureServer({ - middleware: "spec/support/CustomMiddleware", + middleware: 'spec/support/CustomMiddleware', }) .then(() => { - return request({ url: "http://localhost:8378/1" }).then(fail, res => { + return request({ url: 'http://localhost:8378/1' }).then(fail, res => { // Just check that the middleware set the header - expect(res.headers["x-yolo"]).toBe("1"); + expect(res.headers['x-yolo']).toBe('1'); done(); }); }) .catch(done.fail); }); - it("can call start", async () => { - await reconfigureServer({ appId: "aTestApp" }); + it('can call start', async () => { + await reconfigureServer({ appId: 'aTestApp' }); const config = { ...defaultConfiguration, - appId: "aTestApp", - masterKey: "aTestMasterKey", - serverURL: "http://localhost:12701/parse", + appId: 'aTestApp', + masterKey: 'aTestMasterKey', + serverURL: 'http://localhost:12701/parse', }; const parseServer = new ParseServer.ParseServer(config); await parseServer.start(); - expect(Parse.applicationId).toEqual("aTestApp"); - expect(Parse.serverURL).toEqual("http://localhost:12701/parse"); + expect(Parse.applicationId).toEqual('aTestApp'); + expect(Parse.serverURL).toEqual('http://localhost:12701/parse'); const app = express(); - app.use("/parse", parseServer.app); + app.use('/parse', parseServer.app); const server = app.listen(12701); - const testObject = new Parse.Object("TestObject"); + const testObject = new Parse.Object('TestObject'); await expectAsync(testObject.save()).toBeResolved(); await new Promise(resolve => server.close(resolve)); }); - it("start is required to mount", async () => { - await reconfigureServer({ appId: "aTestApp" }); + it('start is required to mount', async () => { + await reconfigureServer({ appId: 'aTestApp' }); const config = { ...defaultConfiguration, - appId: "aTestApp", - masterKey: "aTestMasterKey", - serverURL: "http://localhost:12701/parse", + appId: 'aTestApp', + masterKey: 'aTestMasterKey', + serverURL: 'http://localhost:12701/parse', }; const parseServer = new ParseServer.ParseServer(config); - expect(Parse.applicationId).toEqual("aTestApp"); - expect(Parse.serverURL).toEqual("http://localhost:12701/parse"); + expect(Parse.applicationId).toEqual('aTestApp'); + expect(Parse.serverURL).toEqual('http://localhost:12701/parse'); const app = express(); - app.use("/parse", parseServer.app); + app.use('/parse', parseServer.app); const server = app.listen(12701); const response = await request({ headers: { - "X-Parse-Application-Id": "aTestApp", + 'X-Parse-Application-Id': 'aTestApp', }, - method: "POST", - url: "http://localhost:12701/parse/classes/TestObject", + method: 'POST', + url: 'http://localhost:12701/parse/classes/TestObject', }).catch(e => new Parse.Error(e.data.code, e.data.error)); expect(response).toEqual( new Parse.Error( Parse.Error.INTERNAL_SERVER_ERROR, - "Invalid server state: initialized" + 'Invalid server state: initialized' ) ); const health = await request({ - url: "http://localhost:12701/parse/health", + url: 'http://localhost:12701/parse/health', }).catch(e => e); - spyOn(console, "warn").and.callFake(() => {}); + spyOn(console, 'warn').and.callFake(() => {}); const verify = await ParseServer.default.verifyServerUrl(); expect(verify).not.toBeTrue(); expect(console.warn).toHaveBeenCalledWith( `\nWARNING, Unable to connect to 'http://localhost:12701/parse'. Cloud code and push notifications may be unavailable!\n` ); - expect(health.data.status).toBe("initialized"); + expect(health.data.status).toBe('initialized'); expect(health.status).toBe(503); await new Promise(resolve => server.close(resolve)); }); - it("can get starting state", async () => { - await reconfigureServer({ appId: "test2" }); + it('can get starting state', async () => { + await reconfigureServer({ appId: 'test2' }); const parseServer = new ParseServer.ParseServer({ ...defaultConfiguration, - appId: "test2", - masterKey: "abc", - serverURL: "http://localhost:12668/parse", + appId: 'test2', + masterKey: 'abc', + serverURL: 'http://localhost:12668/parse', async cloud() { await new Promise(resolve => setTimeout(resolve, 2000)); }, }); - const express = require("express"); + const express = require('express'); const app = express(); - app.use("/parse", parseServer.app); + app.use('/parse', parseServer.app); const server = app.listen(12668); const startingPromise = parseServer.start(); const health = await request({ - url: "http://localhost:12668/parse/health", + url: 'http://localhost:12668/parse/health', }).catch(e => e); - expect(health.data.status).toBe("starting"); + expect(health.data.status).toBe('starting'); expect(health.status).toBe(503); - expect(health.headers["retry-after"]).toBe("1"); + expect(health.headers['retry-after']).toBe('1'); const response = await ParseServer.default.verifyServerUrl(); expect(response).toBeTrue(); await startingPromise; await new Promise(resolve => server.close(resolve)); }); - it("should load masterKey", async () => { + it('should load masterKey', async () => { await reconfigureServer({ - masterKey: () => "testMasterKey", + masterKey: () => 'testMasterKey', masterKeyTtl: 1000, // TTL is set }); - await new Parse.Object("TestObject").save(); + await new Parse.Object('TestObject').save(); const config = Config.get(Parse.applicationId); - expect(config.masterKeyCache.masterKey).toEqual("testMasterKey"); + expect(config.masterKeyCache.masterKey).toEqual('testMasterKey'); expect(config.masterKeyCache.expiresAt.getTime()).toBeGreaterThan( Date.now() ); }); - it("should not reload if ttl is not set", async () => { + it('should not reload if ttl is not set', async () => { const masterKeySpy = jasmine .createSpy() - .and.returnValue(Promise.resolve("initialMasterKey")); + .and.returnValue(Promise.resolve('initialMasterKey')); await reconfigureServer({ masterKey: masterKeySpy, masterKeyTtl: null, // No TTL set }); - await new Parse.Object("TestObject").save(); + await new Parse.Object('TestObject').save(); const config = Config.get(Parse.applicationId); const firstMasterKey = config.masterKeyCache.masterKey; @@ -658,18 +658,18 @@ describe("server", () => { await config.loadMasterKey(); const secondMasterKey = config.masterKeyCache.masterKey; - expect(firstMasterKey).toEqual("initialMasterKey"); - expect(secondMasterKey).toEqual("initialMasterKey"); + expect(firstMasterKey).toEqual('initialMasterKey'); + expect(secondMasterKey).toEqual('initialMasterKey'); expect(masterKeySpy).toHaveBeenCalledTimes(1); // Should only be called once expect(config.masterKeyCache.expiresAt).toBeNull(); // TTL is not set, so expiresAt should remain null }); - it("should reload masterKey if ttl is set and expired", async () => { + it('should reload masterKey if ttl is set and expired', async () => { const masterKeySpy = jasmine .createSpy() .and.returnValues( - Promise.resolve("firstMasterKey"), - Promise.resolve("secondMasterKey") + Promise.resolve('firstMasterKey'), + Promise.resolve('secondMasterKey') ); await reconfigureServer({ @@ -677,40 +677,40 @@ describe("server", () => { masterKeyTtl: 1 / 1000, // TTL is set to 1ms }); - await new Parse.Object("TestObject").save(); + await new Parse.Object('TestObject').save(); await new Promise(resolve => setTimeout(resolve, 10)); - await new Parse.Object("TestObject").save(); + await new Parse.Object('TestObject').save(); const config = Config.get(Parse.applicationId); expect(masterKeySpy).toHaveBeenCalledTimes(2); - expect(config.masterKeyCache.masterKey).toEqual("secondMasterKey"); + expect(config.masterKeyCache.masterKey).toEqual('secondMasterKey'); }); - it("should not fail when Google signin is introduced without the optional clientId", done => { - const jwt = require("jsonwebtoken"); - const authUtils = require("../lib/Adapters/Auth/utils"); + it('should not fail when Google signin is introduced without the optional clientId', done => { + const jwt = require('jsonwebtoken'); + const authUtils = require('../lib/Adapters/Auth/utils'); reconfigureServer({ auth: { google: {} }, }) .then(() => { const fakeClaim = { - iss: "https://accounts.google.com", - aud: "secret", + iss: 'https://accounts.google.com', + aud: 'secret', exp: Date.now(), - sub: "the_user_id", + sub: 'the_user_id', }; - const fakeDecodedToken = { header: { kid: "123", alg: "RS256" } }; - spyOn(authUtils, "getHeaderFromToken").and.callFake( + const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; + spyOn(authUtils, 'getHeaderFromToken').and.callFake( () => fakeDecodedToken ); - spyOn(jwt, "verify").and.callFake(() => fakeClaim); + spyOn(jwt, 'verify').and.callFake(() => fakeClaim); const user = new Parse.User(); user - .linkWith("google", { - authData: { id: "the_user_id", id_token: "the_token" }, + .linkWith('google', { + authData: { id: 'the_user_id', id_token: 'the_token' }, }) .then(done); }) diff --git a/spec/parsers.spec.js b/spec/parsers.spec.js index 0527d07b6b..413bdb5156 100644 --- a/spec/parsers.spec.js +++ b/spec/parsers.spec.js @@ -7,77 +7,77 @@ const { arrayParser, moduleOrObjectParser, nullParser, -} = require("../lib/Options/parsers"); +} = require('../lib/Options/parsers'); -describe("parsers", () => { - it("parses correctly with numberParser", () => { - const parser = numberParser("key"); +describe('parsers', () => { + it('parses correctly with numberParser', () => { + const parser = numberParser('key'); expect(parser(2)).toEqual(2); - expect(parser("2")).toEqual(2); + expect(parser('2')).toEqual(2); expect(() => { - parser("string"); + parser('string'); }).toThrow(); }); - it("parses correctly with numberOrStringParser", () => { - const parser = numberOrStringParser("key"); - expect(parser("100d")).toEqual("100d"); + it('parses correctly with numberOrStringParser', () => { + const parser = numberOrStringParser('key'); + expect(parser('100d')).toEqual('100d'); expect(parser(100)).toEqual(100); expect(() => { parser(undefined); }).toThrow(); }); - it("parses correctly with numberOrBoolParser", () => { - const parser = numberOrBoolParser("key"); + it('parses correctly with numberOrBoolParser', () => { + const parser = numberOrBoolParser('key'); expect(parser(true)).toEqual(true); expect(parser(false)).toEqual(false); - expect(parser("true")).toEqual(true); - expect(parser("false")).toEqual(false); + expect(parser('true')).toEqual(true); + expect(parser('false')).toEqual(false); expect(parser(1)).toEqual(1); - expect(parser("1")).toEqual(1); + expect(parser('1')).toEqual(1); }); - it("parses correctly with booleanParser", () => { + it('parses correctly with booleanParser', () => { const parser = booleanParser; expect(parser(true)).toEqual(true); expect(parser(false)).toEqual(false); - expect(parser("true")).toEqual(true); - expect(parser("false")).toEqual(false); + expect(parser('true')).toEqual(true); + expect(parser('false')).toEqual(false); expect(parser(1)).toEqual(true); expect(parser(2)).toEqual(false); }); - it("parses correctly with objectParser", () => { + it('parses correctly with objectParser', () => { const parser = objectParser; - expect(parser({ hello: "world" })).toEqual({ hello: "world" }); - expect(parser('{"hello": "world"}')).toEqual({ hello: "world" }); + expect(parser({ hello: 'world' })).toEqual({ hello: 'world' }); + expect(parser('{"hello": "world"}')).toEqual({ hello: 'world' }); expect(() => { - parser("string"); + parser('string'); }).toThrow(); }); - it("parses correctly with moduleOrObjectParser", () => { + it('parses correctly with moduleOrObjectParser', () => { const parser = moduleOrObjectParser; - expect(parser({ hello: "world" })).toEqual({ hello: "world" }); - expect(parser('{"hello": "world"}')).toEqual({ hello: "world" }); - expect(parser("string")).toEqual("string"); + expect(parser({ hello: 'world' })).toEqual({ hello: 'world' }); + expect(parser('{"hello": "world"}')).toEqual({ hello: 'world' }); + expect(parser('string')).toEqual('string'); }); - it("parses correctly with arrayParser", () => { + it('parses correctly with arrayParser', () => { const parser = arrayParser; expect(parser([1, 2, 3])).toEqual([1, 2, 3]); expect(parser('{"hello": "world"}')).toEqual(['{"hello": "world"}']); - expect(parser("1,2,3")).toEqual(["1", "2", "3"]); + expect(parser('1,2,3')).toEqual(['1', '2', '3']); expect(() => { parser(1); }).toThrow(); }); - it("parses correctly with nullParser", () => { + it('parses correctly with nullParser', () => { const parser = nullParser; - expect(parser("null")).toEqual(null); + expect(parser('null')).toEqual(null); expect(parser(1)).toEqual(1); - expect(parser("blabla")).toEqual("blabla"); + expect(parser('blabla')).toEqual('blabla'); }); }); diff --git a/spec/rest.spec.js b/spec/rest.spec.js index 7c641e886a..8955a98642 100644 --- a/spec/rest.spec.js +++ b/spec/rest.spec.js @@ -1,50 +1,50 @@ -"use strict"; +'use strict'; // These tests check the "create" / "update" functionality of the REST API. -const auth = require("../lib/Auth"); -const Config = require("../lib/Config"); -const Parse = require("parse/node").Parse; -const rest = require("../lib/rest"); -const RestWrite = require("../lib/RestWrite"); -const request = require("../lib/request"); +const auth = require('../lib/Auth'); +const Config = require('../lib/Config'); +const Parse = require('parse/node').Parse; +const rest = require('../lib/rest'); +const RestWrite = require('../lib/RestWrite'); +const request = require('../lib/request'); let config; let database; -describe("rest create", () => { +describe('rest create', () => { beforeEach(() => { - config = Config.get("test"); + config = Config.get('test'); database = config.database; }); - it("handles _id", done => { + it('handles _id', done => { rest - .create(config, auth.nobody(config), "Foo", {}) - .then(() => database.adapter.find("Foo", { fields: {} }, {}, {})) + .create(config, auth.nobody(config), 'Foo', {}) + .then(() => database.adapter.find('Foo', { fields: {} }, {}, {})) .then(results => { expect(results.length).toEqual(1); const obj = results[0]; - expect(typeof obj.objectId).toEqual("string"); + expect(typeof obj.objectId).toEqual('string'); expect(obj.objectId.length).toEqual(10); expect(obj._id).toBeUndefined(); done(); }); }); - it("can use custom _id size", done => { + it('can use custom _id size', done => { config.objectIdSize = 20; rest - .create(config, auth.nobody(config), "Foo", {}) - .then(() => database.adapter.find("Foo", { fields: {} }, {}, {})) + .create(config, auth.nobody(config), 'Foo', {}) + .then(() => database.adapter.find('Foo', { fields: {} }, {}, {})) .then(results => { expect(results.length).toEqual(1); const obj = results[0]; - expect(typeof obj.objectId).toEqual("string"); + expect(typeof obj.objectId).toEqual('string'); expect(obj.objectId.length).toEqual(20); done(); }); }); - it("should use objectId from client when allowCustomObjectId true", async () => { + it('should use objectId from client when allowCustomObjectId true', async () => { config.allowCustomObjectId = true; // use time as unique custom id for test reusability @@ -56,13 +56,13 @@ describe("rest create", () => { const { status, response: { objectId }, - } = await rest.create(config, auth.nobody(config), "MyClass", obj); + } = await rest.create(config, auth.nobody(config), 'MyClass', obj); expect(status).toEqual(201); expect(objectId).toEqual(customId); }); - it("should throw on invalid objectId when allowCustomObjectId true", () => { + it('should throw on invalid objectId when allowCustomObjectId true', () => { config.allowCustomObjectId = true; const objIdNull = { @@ -74,42 +74,42 @@ describe("rest create", () => { }; const objIdEmpty = { - objectId: "", + objectId: '', }; - const err = "objectId must not be empty, null or undefined"; + const err = 'objectId must not be empty, null or undefined'; expect(() => - rest.create(config, auth.nobody(config), "MyClass", objIdEmpty) + rest.create(config, auth.nobody(config), 'MyClass', objIdEmpty) ).toThrowError(err); expect(() => - rest.create(config, auth.nobody(config), "MyClass", objIdNull) + rest.create(config, auth.nobody(config), 'MyClass', objIdNull) ).toThrowError(err); expect(() => - rest.create(config, auth.nobody(config), "MyClass", objIdUndef) + rest.create(config, auth.nobody(config), 'MyClass', objIdUndef) ).toThrowError(err); }); - it("should generate objectId when not set by client with allowCustomObjectId true", async () => { + it('should generate objectId when not set by client with allowCustomObjectId true', async () => { config.allowCustomObjectId = true; const { status, response: { objectId }, - } = await rest.create(config, auth.nobody(config), "MyClass", {}); + } = await rest.create(config, auth.nobody(config), 'MyClass', {}); expect(status).toEqual(201); expect(objectId).toBeDefined(); }); - it("is backwards compatible when _id size changes", done => { + it('is backwards compatible when _id size changes', done => { rest - .create(config, auth.nobody(config), "Foo", { size: 10 }) + .create(config, auth.nobody(config), 'Foo', { size: 10 }) .then(() => { config.objectIdSize = 20; - return rest.find(config, auth.nobody(config), "Foo", { size: 10 }); + return rest.find(config, auth.nobody(config), 'Foo', { size: 10 }); }) .then(response => { expect(response.results.length).toEqual(1); @@ -117,23 +117,23 @@ describe("rest create", () => { return rest.update( config, auth.nobody(config), - "Foo", + 'Foo', { objectId: response.results[0].objectId }, { update: 20 } ); }) .then(() => { - return rest.find(config, auth.nobody(config), "Foo", { size: 10 }); + return rest.find(config, auth.nobody(config), 'Foo', { size: 10 }); }) .then(response => { expect(response.results.length).toEqual(1); expect(response.results[0].objectId.length).toEqual(10); expect(response.results[0].update).toEqual(20); - return rest.create(config, auth.nobody(config), "Foo", { size: 20 }); + return rest.create(config, auth.nobody(config), 'Foo', { size: 20 }); }) .then(() => { config.objectIdSize = 10; - return rest.find(config, auth.nobody(config), "Foo", { size: 20 }); + return rest.find(config, auth.nobody(config), 'Foo', { size: 20 }); }) .then(response => { expect(response.results.length).toEqual(1); @@ -142,16 +142,16 @@ describe("rest create", () => { }); }); - describe("with maintenance key", () => { + describe('with maintenance key', () => { let req; async function getObject(id) { const res = await request({ headers: { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }, - method: "GET", + method: 'GET', url: `http://localhost:8378/1/classes/TestObject/${id}`, }); @@ -161,27 +161,27 @@ describe("rest create", () => { beforeEach(() => { req = { headers: { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "X-Parse-Maintenance-Key": "testing", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Maintenance-Key': 'testing', }, - method: "POST", - url: "http://localhost:8378/1/classes/TestObject", + method: 'POST', + url: 'http://localhost:8378/1/classes/TestObject', }; }); - it("allows createdAt", async () => { - const createdAt = { __type: "Date", iso: "2019-01-01T00:00:00.000Z" }; + it('allows createdAt', async () => { + const createdAt = { __type: 'Date', iso: '2019-01-01T00:00:00.000Z' }; req.body = { createdAt }; const res = await request(req); expect(res.data.createdAt).toEqual(createdAt.iso); }); - it("allows createdAt and updatedAt", async () => { - const createdAt = { __type: "Date", iso: "2019-01-01T00:00:00.000Z" }; - const updatedAt = { __type: "Date", iso: "2019-02-01T00:00:00.000Z" }; + it('allows createdAt and updatedAt', async () => { + const createdAt = { __type: 'Date', iso: '2019-01-01T00:00:00.000Z' }; + const updatedAt = { __type: 'Date', iso: '2019-02-01T00:00:00.000Z' }; req.body = { createdAt, updatedAt }; const res = await request(req); @@ -191,9 +191,9 @@ describe("rest create", () => { expect(obj.updatedAt).toEqual(updatedAt.iso); }); - it("allows createdAt, updatedAt, and additional field", async () => { - const createdAt = { __type: "Date", iso: "2019-01-01T00:00:00.000Z" }; - const updatedAt = { __type: "Date", iso: "2019-02-01T00:00:00.000Z" }; + it('allows createdAt, updatedAt, and additional field', async () => { + const createdAt = { __type: 'Date', iso: '2019-01-01T00:00:00.000Z' }; + const updatedAt = { __type: 'Date', iso: '2019-02-01T00:00:00.000Z' }; req.body = { createdAt, updatedAt, testing: 123 }; const res = await request(req); @@ -204,9 +204,9 @@ describe("rest create", () => { expect(obj.testing).toEqual(123); }); - it("cannot set updatedAt dated before createdAt", async () => { - const createdAt = { __type: "Date", iso: "2019-01-01T00:00:00.000Z" }; - const updatedAt = { __type: "Date", iso: "2018-12-01T00:00:00.000Z" }; + it('cannot set updatedAt dated before createdAt', async () => { + const createdAt = { __type: 'Date', iso: '2019-01-01T00:00:00.000Z' }; + const updatedAt = { __type: 'Date', iso: '2018-12-01T00:00:00.000Z' }; req.body = { createdAt, updatedAt }; try { @@ -217,8 +217,8 @@ describe("rest create", () => { } }); - it("cannot set updatedAt without createdAt", async () => { - const updatedAt = { __type: "Date", iso: "2018-12-01T00:00:00.000Z" }; + it('cannot set updatedAt without createdAt', async () => { + const updatedAt = { __type: 'Date', iso: '2018-12-01T00:00:00.000Z' }; req.body = { updatedAt }; const res = await request(req); @@ -227,7 +227,7 @@ describe("rest create", () => { expect(obj.updatedAt).not.toEqual(updatedAt.iso); }); - it("handles bad types for createdAt and updatedAt", async () => { + it('handles bad types for createdAt and updatedAt', async () => { const createdAt = 12345; const updatedAt = true; req.body = { createdAt, updatedAt }; @@ -240,11 +240,11 @@ describe("rest create", () => { } }); - it("cannot set createdAt or updatedAt without maintenance key", async () => { - const createdAt = { __type: "Date", iso: "2019-01-01T00:00:00.000Z" }; - const updatedAt = { __type: "Date", iso: "2019-02-01T00:00:00.000Z" }; + it('cannot set createdAt or updatedAt without maintenance key', async () => { + const createdAt = { __type: 'Date', iso: '2019-01-01T00:00:00.000Z' }; + const updatedAt = { __type: 'Date', iso: '2019-02-01T00:00:00.000Z' }; req.body = { createdAt, updatedAt }; - delete req.headers["X-Parse-Maintenance-Key"]; + delete req.headers['X-Parse-Maintenance-Key']; const res = await request(req); @@ -253,25 +253,25 @@ describe("rest create", () => { }); }); - it_id("6c30306f-328c-47f2-88a7-2deffaee997f")(it)( - "handles array, object, date", + it_id('6c30306f-328c-47f2-88a7-2deffaee997f')(it)( + 'handles array, object, date', done => { const now = new Date(); const obj = { array: [1, 2, 3], - object: { foo: "bar" }, + object: { foo: 'bar' }, date: Parse._encode(now), }; rest - .create(config, auth.nobody(config), "MyClass", obj) + .create(config, auth.nobody(config), 'MyClass', obj) .then(() => database.adapter.find( - "MyClass", + 'MyClass', { fields: { - array: { type: "Array" }, - object: { type: "Object" }, - date: { type: "Date" }, + array: { type: 'Array' }, + object: { type: 'Object' }, + date: { type: 'Date' }, }, }, {}, @@ -282,53 +282,53 @@ describe("rest create", () => { expect(results.length).toEqual(1); const mob = results[0]; expect(mob.array instanceof Array).toBe(true); - expect(typeof mob.object).toBe("object"); - expect(mob.date.__type).toBe("Date"); + expect(typeof mob.object).toBe('object'); + expect(mob.date.__type).toBe('Date'); expect(new Date(mob.date.iso).getTime()).toBe(now.getTime()); done(); }); } ); - it("handles object and subdocument", done => { - const obj = { subdoc: { foo: "bar", wu: "tan" } }; + it('handles object and subdocument', done => { + const obj = { subdoc: { foo: 'bar', wu: 'tan' } }; - Parse.Cloud.beforeSave("MyClass", function () { + Parse.Cloud.beforeSave('MyClass', function () { // this beforeSave trigger should do nothing but can mess with the object }); rest - .create(config, auth.nobody(config), "MyClass", obj) - .then(() => database.adapter.find("MyClass", { fields: {} }, {}, {})) + .create(config, auth.nobody(config), 'MyClass', obj) + .then(() => database.adapter.find('MyClass', { fields: {} }, {}, {})) .then(results => { expect(results.length).toEqual(1); const mob = results[0]; - expect(typeof mob.subdoc).toBe("object"); - expect(mob.subdoc.foo).toBe("bar"); - expect(mob.subdoc.wu).toBe("tan"); - expect(typeof mob.objectId).toEqual("string"); - const obj = { "subdoc.wu": "clan" }; + expect(typeof mob.subdoc).toBe('object'); + expect(mob.subdoc.foo).toBe('bar'); + expect(mob.subdoc.wu).toBe('tan'); + expect(typeof mob.objectId).toEqual('string'); + const obj = { 'subdoc.wu': 'clan' }; return rest.update( config, auth.nobody(config), - "MyClass", + 'MyClass', { objectId: mob.objectId }, obj ); }) - .then(() => database.adapter.find("MyClass", { fields: {} }, {}, {})) + .then(() => database.adapter.find('MyClass', { fields: {} }, {}, {})) .then(results => { expect(results.length).toEqual(1); const mob = results[0]; - expect(typeof mob.subdoc).toBe("object"); - expect(mob.subdoc.foo).toBe("bar"); - expect(mob.subdoc.wu).toBe("clan"); + expect(typeof mob.subdoc).toBe('object'); + expect(mob.subdoc.foo).toBe('bar'); + expect(mob.subdoc.wu).toBe('clan'); done(); }) .catch(done.fail); }); - it("handles create on non-existent class when disabled client class creation", done => { + it('handles create on non-existent class when disabled client class creation', done => { const customConfig = Object.assign({}, config, { allowClientClassCreation: false, }); @@ -336,131 +336,131 @@ describe("rest create", () => { .create( customConfig, auth.nobody(customConfig), - "ClientClassCreation", + 'ClientClassCreation', {} ) .then( () => { - fail("Should throw an error"); + fail('Should throw an error'); done(); }, err => { expect(err.code).toEqual(Parse.Error.OPERATION_FORBIDDEN); expect(err.message).toEqual( - "This user is not allowed to access " + - "non-existent class: ClientClassCreation" + 'This user is not allowed to access ' + + 'non-existent class: ClientClassCreation' ); done(); } ); }); - it("handles create on existent class when disabled client class creation", async () => { + it('handles create on existent class when disabled client class creation', async () => { const customConfig = Object.assign({}, config, { allowClientClassCreation: false, }); const schema = await config.database.loadSchema(); const actualSchema = await schema.addClassIfNotExists( - "ClientClassCreation", + 'ClientClassCreation', {} ); - expect(actualSchema.className).toEqual("ClientClassCreation"); + expect(actualSchema.className).toEqual('ClientClassCreation'); await schema.reloadData({ clearCache: true }); // Should not throw await rest.create( customConfig, auth.nobody(customConfig), - "ClientClassCreation", + 'ClientClassCreation', {} ); }); - it("handles user signup", done => { + it('handles user signup', done => { const user = { - username: "asdf", - password: "zxcv", - foo: "bar", + username: 'asdf', + password: 'zxcv', + foo: 'bar', }; - rest.create(config, auth.nobody(config), "_User", user).then(r => { + rest.create(config, auth.nobody(config), '_User', user).then(r => { expect(Object.keys(r.response).length).toEqual(3); - expect(typeof r.response.objectId).toEqual("string"); - expect(typeof r.response.createdAt).toEqual("string"); - expect(typeof r.response.sessionToken).toEqual("string"); + expect(typeof r.response.objectId).toEqual('string'); + expect(typeof r.response.createdAt).toEqual('string'); + expect(typeof r.response.sessionToken).toEqual('string'); done(); }); }); - it("handles anonymous user signup", done => { + it('handles anonymous user signup', done => { const data1 = { authData: { anonymous: { - id: "00000000-0000-0000-0000-000000000001", + id: '00000000-0000-0000-0000-000000000001', }, }, }; const data2 = { authData: { anonymous: { - id: "00000000-0000-0000-0000-000000000002", + id: '00000000-0000-0000-0000-000000000002', }, }, }; let username1; rest - .create(config, auth.nobody(config), "_User", data1) + .create(config, auth.nobody(config), '_User', data1) .then(r => { - expect(typeof r.response.objectId).toEqual("string"); - expect(typeof r.response.createdAt).toEqual("string"); - expect(typeof r.response.sessionToken).toEqual("string"); - expect(typeof r.response.username).toEqual("string"); - return rest.create(config, auth.nobody(config), "_User", data1); + expect(typeof r.response.objectId).toEqual('string'); + expect(typeof r.response.createdAt).toEqual('string'); + expect(typeof r.response.sessionToken).toEqual('string'); + expect(typeof r.response.username).toEqual('string'); + return rest.create(config, auth.nobody(config), '_User', data1); }) .then(r => { - expect(typeof r.response.objectId).toEqual("string"); - expect(typeof r.response.createdAt).toEqual("string"); - expect(typeof r.response.username).toEqual("string"); - expect(typeof r.response.updatedAt).toEqual("string"); + expect(typeof r.response.objectId).toEqual('string'); + expect(typeof r.response.createdAt).toEqual('string'); + expect(typeof r.response.username).toEqual('string'); + expect(typeof r.response.updatedAt).toEqual('string'); username1 = r.response.username; - return rest.create(config, auth.nobody(config), "_User", data2); + return rest.create(config, auth.nobody(config), '_User', data2); }) .then(r => { - expect(typeof r.response.objectId).toEqual("string"); - expect(typeof r.response.createdAt).toEqual("string"); - expect(typeof r.response.sessionToken).toEqual("string"); - return rest.create(config, auth.nobody(config), "_User", data2); + expect(typeof r.response.objectId).toEqual('string'); + expect(typeof r.response.createdAt).toEqual('string'); + expect(typeof r.response.sessionToken).toEqual('string'); + return rest.create(config, auth.nobody(config), '_User', data2); }) .then(r => { - expect(typeof r.response.objectId).toEqual("string"); - expect(typeof r.response.createdAt).toEqual("string"); - expect(typeof r.response.username).toEqual("string"); - expect(typeof r.response.updatedAt).toEqual("string"); + expect(typeof r.response.objectId).toEqual('string'); + expect(typeof r.response.createdAt).toEqual('string'); + expect(typeof r.response.username).toEqual('string'); + expect(typeof r.response.updatedAt).toEqual('string'); expect(r.response.username).not.toEqual(username1); done(); }); }); - it("handles anonymous user signup and upgrade to new user", done => { + it('handles anonymous user signup and upgrade to new user', done => { const data1 = { authData: { anonymous: { - id: "00000000-0000-0000-0000-000000000001", + id: '00000000-0000-0000-0000-000000000001', }, }, }; const updatedData = { authData: { anonymous: null }, - username: "hello", - password: "world", + username: 'hello', + password: 'world', }; let objectId; rest - .create(config, auth.nobody(config), "_User", data1) + .create(config, auth.nobody(config), '_User', data1) .then(r => { - expect(typeof r.response.objectId).toEqual("string"); - expect(typeof r.response.createdAt).toEqual("string"); - expect(typeof r.response.sessionToken).toEqual("string"); + expect(typeof r.response.objectId).toEqual('string'); + expect(typeof r.response.createdAt).toEqual('string'); + expect(typeof r.response.sessionToken).toEqual('string'); objectId = r.response.objectId; return auth.getAuthForSessionToken({ config, @@ -471,19 +471,19 @@ describe("rest create", () => { return rest.update( config, sessionAuth, - "_User", + '_User', { objectId }, updatedData ); }) .then(() => { return Parse.User.logOut().then(() => { - return Parse.User.logIn("hello", "world"); + return Parse.User.logIn('hello', 'world'); }); }) .then(r => { expect(r.id).toEqual(objectId); - expect(r.get("username")).toEqual("hello"); + expect(r.get('username')).toEqual('hello'); done(); }) .catch(err => { @@ -492,25 +492,25 @@ describe("rest create", () => { }); }); - it("handles no anonymous users config", done => { + it('handles no anonymous users config', done => { const NoAnnonConfig = Object.assign({}, config); NoAnnonConfig.authDataManager.setEnableAnonymousUsers(false); const data1 = { authData: { anonymous: { - id: "00000000-0000-0000-0000-000000000001", + id: '00000000-0000-0000-0000-000000000001', }, }, }; - rest.create(NoAnnonConfig, auth.nobody(NoAnnonConfig), "_User", data1).then( + rest.create(NoAnnonConfig, auth.nobody(NoAnnonConfig), '_User', data1).then( () => { - fail("Should throw an error"); + fail('Should throw an error'); done(); }, err => { expect(err.code).toEqual(Parse.Error.UNSUPPORTED_SERVICE); expect(err.message).toEqual( - "This authentication method is unsupported." + 'This authentication method is unsupported.' ); NoAnnonConfig.authDataManager.setEnableAnonymousUsers(true); done(); @@ -518,32 +518,32 @@ describe("rest create", () => { ); }); - it("test facebook signup and login", done => { + it('test facebook signup and login', done => { const data = { authData: { facebook: { - id: "8675309", - access_token: "jenny", + id: '8675309', + access_token: 'jenny', }, }, }; let newUserSignedUpByFacebookObjectId; rest - .create(config, auth.nobody(config), "_User", data) + .create(config, auth.nobody(config), '_User', data) .then(r => { - expect(typeof r.response.objectId).toEqual("string"); - expect(typeof r.response.createdAt).toEqual("string"); - expect(typeof r.response.sessionToken).toEqual("string"); + expect(typeof r.response.objectId).toEqual('string'); + expect(typeof r.response.createdAt).toEqual('string'); + expect(typeof r.response.sessionToken).toEqual('string'); newUserSignedUpByFacebookObjectId = r.response.objectId; - return rest.create(config, auth.nobody(config), "_User", data); + return rest.create(config, auth.nobody(config), '_User', data); }) .then(r => { - expect(typeof r.response.objectId).toEqual("string"); - expect(typeof r.response.createdAt).toEqual("string"); - expect(typeof r.response.username).toEqual("string"); - expect(typeof r.response.updatedAt).toEqual("string"); + expect(typeof r.response.objectId).toEqual('string'); + expect(typeof r.response.createdAt).toEqual('string'); + expect(typeof r.response.username).toEqual('string'); + expect(typeof r.response.updatedAt).toEqual('string'); expect(r.response.objectId).toEqual(newUserSignedUpByFacebookObjectId); - return rest.find(config, auth.master(config), "_Session", { + return rest.find(config, auth.master(config), '_Session', { sessionToken: r.response.sessionToken, }); }) @@ -559,24 +559,24 @@ describe("rest create", () => { }); }); - it("stores pointers", done => { + it('stores pointers', done => { const obj = { - foo: "bar", + foo: 'bar', aPointer: { - __type: "Pointer", - className: "JustThePointer", - objectId: "qwerty1234", // make it 10 chars to match PG storage + __type: 'Pointer', + className: 'JustThePointer', + objectId: 'qwerty1234', // make it 10 chars to match PG storage }, }; rest - .create(config, auth.nobody(config), "APointerDarkly", obj) + .create(config, auth.nobody(config), 'APointerDarkly', obj) .then(() => database.adapter.find( - "APointerDarkly", + 'APointerDarkly', { fields: { - foo: { type: "String" }, - aPointer: { type: "Pointer", targetClass: "JustThePointer" }, + foo: { type: 'String' }, + aPointer: { type: 'Pointer', targetClass: 'JustThePointer' }, }, }, {}, @@ -586,36 +586,36 @@ describe("rest create", () => { .then(results => { expect(results.length).toEqual(1); const output = results[0]; - expect(typeof output.foo).toEqual("string"); - expect(typeof output._p_aPointer).toEqual("undefined"); + expect(typeof output.foo).toEqual('string'); + expect(typeof output._p_aPointer).toEqual('undefined'); expect(output._p_aPointer).toBeUndefined(); expect(output.aPointer).toEqual({ - __type: "Pointer", - className: "JustThePointer", - objectId: "qwerty1234", + __type: 'Pointer', + className: 'JustThePointer', + objectId: 'qwerty1234', }); done(); }); }); - it("stores pointers to objectIds larger than 10 characters", done => { + it('stores pointers to objectIds larger than 10 characters', done => { const obj = { - foo: "bar", + foo: 'bar', aPointer: { - __type: "Pointer", - className: "JustThePointer", - objectId: "49F62F92-9B56-46E7-A3D4-BBD14C52F666", + __type: 'Pointer', + className: 'JustThePointer', + objectId: '49F62F92-9B56-46E7-A3D4-BBD14C52F666', }, }; rest - .create(config, auth.nobody(config), "APointerDarkly", obj) + .create(config, auth.nobody(config), 'APointerDarkly', obj) .then(() => database.adapter.find( - "APointerDarkly", + 'APointerDarkly', { fields: { - foo: { type: "String" }, - aPointer: { type: "Pointer", targetClass: "JustThePointer" }, + foo: { type: 'String' }, + aPointer: { type: 'Pointer', targetClass: 'JustThePointer' }, }, }, {}, @@ -625,78 +625,78 @@ describe("rest create", () => { .then(results => { expect(results.length).toEqual(1); const output = results[0]; - expect(typeof output.foo).toEqual("string"); - expect(typeof output._p_aPointer).toEqual("undefined"); + expect(typeof output.foo).toEqual('string'); + expect(typeof output._p_aPointer).toEqual('undefined'); expect(output._p_aPointer).toBeUndefined(); expect(output.aPointer).toEqual({ - __type: "Pointer", - className: "JustThePointer", - objectId: "49F62F92-9B56-46E7-A3D4-BBD14C52F666", + __type: 'Pointer', + className: 'JustThePointer', + objectId: '49F62F92-9B56-46E7-A3D4-BBD14C52F666', }); done(); }); }); - it("cannot set objectId", done => { + it('cannot set objectId', done => { const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; request({ headers: headers, - method: "POST", - url: "http://localhost:8378/1/classes/TestObject", + method: 'POST', + url: 'http://localhost:8378/1/classes/TestObject', body: JSON.stringify({ - foo: "bar", - objectId: "hello", + foo: 'bar', + objectId: 'hello', }), }).then(fail, response => { const b = response.data; expect(b.code).toEqual(105); - expect(b.error).toEqual("objectId is an invalid field name."); + expect(b.error).toEqual('objectId is an invalid field name.'); done(); }); }); - it("cannot set id", done => { + it('cannot set id', done => { const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; request({ headers: headers, - method: "POST", - url: "http://localhost:8378/1/classes/TestObject", + method: 'POST', + url: 'http://localhost:8378/1/classes/TestObject', body: JSON.stringify({ - foo: "bar", - id: "hello", + foo: 'bar', + id: 'hello', }), }).then(fail, response => { const b = response.data; expect(b.code).toEqual(105); - expect(b.error).toEqual("id is an invalid field name."); + expect(b.error).toEqual('id is an invalid field name.'); done(); }); }); - it("test default session length", done => { + it('test default session length', done => { const user = { - username: "asdf", - password: "zxcv", - foo: "bar", + username: 'asdf', + password: 'zxcv', + foo: 'bar', }; const now = new Date(); rest - .create(config, auth.nobody(config), "_User", user) + .create(config, auth.nobody(config), '_User', user) .then(r => { expect(Object.keys(r.response).length).toEqual(3); - expect(typeof r.response.objectId).toEqual("string"); - expect(typeof r.response.createdAt).toEqual("string"); - expect(typeof r.response.sessionToken).toEqual("string"); - return rest.find(config, auth.master(config), "_Session", { + expect(typeof r.response.objectId).toEqual('string'); + expect(typeof r.response.createdAt).toEqual('string'); + expect(typeof r.response.sessionToken).toEqual('string'); + return rest.find(config, auth.master(config), '_Session', { sessionToken: r.response.sessionToken, }); }) @@ -715,24 +715,24 @@ describe("rest create", () => { }); }); - it("test specified session length", done => { + it('test specified session length', done => { const user = { - username: "asdf", - password: "zxcv", - foo: "bar", + username: 'asdf', + password: 'zxcv', + foo: 'bar', }; const sessionLength = 3600, // 1 Hour ahead now = new Date(); // For reference later config.sessionLength = sessionLength; rest - .create(config, auth.nobody(config), "_User", user) + .create(config, auth.nobody(config), '_User', user) .then(r => { expect(Object.keys(r.response).length).toEqual(3); - expect(typeof r.response.objectId).toEqual("string"); - expect(typeof r.response.createdAt).toEqual("string"); - expect(typeof r.response.sessionToken).toEqual("string"); - return rest.find(config, auth.master(config), "_Session", { + expect(typeof r.response.objectId).toEqual('string'); + expect(typeof r.response.createdAt).toEqual('string'); + expect(typeof r.response.sessionToken).toEqual('string'); + return rest.find(config, auth.master(config), '_Session', { sessionToken: r.response.sessionToken, }); }) @@ -755,22 +755,22 @@ describe("rest create", () => { }); }); - it("can create a session with no expiration", done => { + it('can create a session with no expiration', done => { const user = { - username: "asdf", - password: "zxcv", - foo: "bar", + username: 'asdf', + password: 'zxcv', + foo: 'bar', }; config.expireInactiveSessions = false; rest - .create(config, auth.nobody(config), "_User", user) + .create(config, auth.nobody(config), '_User', user) .then(r => { expect(Object.keys(r.response).length).toEqual(3); - expect(typeof r.response.objectId).toEqual("string"); - expect(typeof r.response.createdAt).toEqual("string"); - expect(typeof r.response.sessionToken).toEqual("string"); - return rest.find(config, auth.master(config), "_Session", { + expect(typeof r.response.objectId).toEqual('string'); + expect(typeof r.response.createdAt).toEqual('string'); + expect(typeof r.response.sessionToken).toEqual('string'); + return rest.find(config, auth.master(config), '_Session', { sessionToken: r.response.sessionToken, }); }) @@ -789,24 +789,24 @@ describe("rest create", () => { }); }); - it("can create object in volatileClasses if masterKey", done => { + it('can create object in volatileClasses if masterKey', done => { rest - .create(config, auth.master(config), "_PushStatus", {}) + .create(config, auth.master(config), '_PushStatus', {}) .then(r => { expect(r.response.objectId.length).toBe(10); }) .then(() => { - rest.create(config, auth.master(config), "_JobStatus", {}).then(r => { + rest.create(config, auth.master(config), '_JobStatus', {}).then(r => { expect(r.response.objectId.length).toBe(10); done(); }); }); }); - it("cannot create object in volatileClasses if not masterKey", done => { + it('cannot create object in volatileClasses if not masterKey', done => { Promise.resolve() .then(() => { - return rest.create(config, auth.nobody(config), "_PushStatus", {}); + return rest.create(config, auth.nobody(config), '_PushStatus', {}); }) .catch(error => { expect(error.code).toEqual(119); @@ -814,66 +814,66 @@ describe("rest create", () => { }); }); - it("cannot get object in volatileClasses if not masterKey through pointer", async () => { - const masterKeyOnlyClassObject = new Parse.Object("_PushStatus"); + it('cannot get object in volatileClasses if not masterKey through pointer', async () => { + const masterKeyOnlyClassObject = new Parse.Object('_PushStatus'); await masterKeyOnlyClassObject.save(null, { useMasterKey: true }); - const obj2 = new Parse.Object("TestObject"); + const obj2 = new Parse.Object('TestObject'); // Anyone is can basically create a pointer to any object // or some developers can use master key in some hook to link // private objects to standard objects - obj2.set("pointer", masterKeyOnlyClassObject); + obj2.set('pointer', masterKeyOnlyClassObject); await obj2.save(); - const query = new Parse.Query("TestObject"); - query.include("pointer"); + const query = new Parse.Query('TestObject'); + query.include('pointer'); await expectAsync(query.get(obj2.id)).toBeRejectedWithError( "Clients aren't allowed to perform the get operation on the _PushStatus collection." ); }); - it_id("3ce563bf-93aa-4d0b-9af9-c5fb246ac9fc")(it)( - "cannot get object in _GlobalConfig if not masterKey through pointer", + it_id('3ce563bf-93aa-4d0b-9af9-c5fb246ac9fc')(it)( + 'cannot get object in _GlobalConfig if not masterKey through pointer', async () => { - await Parse.Config.save({ privateData: "secret" }, { privateData: true }); - const obj2 = new Parse.Object("TestObject"); - obj2.set("globalConfigPointer", { - __type: "Pointer", - className: "_GlobalConfig", + await Parse.Config.save({ privateData: 'secret' }, { privateData: true }); + const obj2 = new Parse.Object('TestObject'); + obj2.set('globalConfigPointer', { + __type: 'Pointer', + className: '_GlobalConfig', objectId: 1, }); await obj2.save(); - const query = new Parse.Query("TestObject"); - query.include("globalConfigPointer"); + const query = new Parse.Query('TestObject'); + query.include('globalConfigPointer'); await expectAsync(query.get(obj2.id)).toBeRejectedWithError( "Clients aren't allowed to perform the get operation on the _GlobalConfig collection." ); } ); - it("locks down session", done => { + it('locks down session', done => { let currentUser; - Parse.User.signUp("foo", "bar") + Parse.User.signUp('foo', 'bar') .then(user => { currentUser = user; const sessionToken = user.getSessionToken(); const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "X-Parse-Session-Token": sessionToken, + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Session-Token': sessionToken, }; let sessionId; return request({ headers: headers, - url: "http://localhost:8378/1/sessions/me", + url: 'http://localhost:8378/1/sessions/me', }) .then(response => { sessionId = response.data.objectId; return request({ headers, - method: "PUT", - url: "http://localhost:8378/1/sessions/" + sessionId, + method: 'PUT', + url: 'http://localhost:8378/1/sessions/' + sessionId, body: { - installationId: "yolo", + installationId: 'yolo', }, }); }) @@ -882,25 +882,25 @@ describe("rest create", () => { expect(res.data.code).toBe(105); return request({ headers, - method: "PUT", - url: "http://localhost:8378/1/sessions/" + sessionId, + method: 'PUT', + url: 'http://localhost:8378/1/sessions/' + sessionId, body: { - sessionToken: "yolo", + sessionToken: 'yolo', }, }); }) .then(done.fail, res => { expect(res.status).toBe(400); expect(res.data.code).toBe(105); - return Parse.User.signUp("other", "user"); + return Parse.User.signUp('other', 'user'); }) .then(otherUser => { const user = new Parse.User(); user.id = otherUser.id; return request({ headers, - method: "PUT", - url: "http://localhost:8378/1/sessions/" + sessionId, + method: 'PUT', + url: 'http://localhost:8378/1/sessions/' + sessionId, body: { user: Parse._encode(user), }, @@ -913,8 +913,8 @@ describe("rest create", () => { user.id = currentUser.id; return request({ headers, - method: "PUT", - url: "http://localhost:8378/1/sessions/" + sessionId, + method: 'PUT', + url: 'http://localhost:8378/1/sessions/' + sessionId, body: { user: Parse._encode(user), }, @@ -926,24 +926,24 @@ describe("rest create", () => { .catch(done.fail); }); - it("sets current user in new sessions", done => { + it('sets current user in new sessions', done => { let currentUser; - Parse.User.signUp("foo", "bar") + Parse.User.signUp('foo', 'bar') .then(user => { currentUser = user; const sessionToken = user.getSessionToken(); const headers = { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "X-Parse-Session-Token": sessionToken, - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Session-Token': sessionToken, + 'Content-Type': 'application/json', }; return request({ headers, - method: "POST", - url: "http://localhost:8378/1/sessions", + method: 'POST', + url: 'http://localhost:8378/1/sessions', body: { - user: { __type: "Pointer", className: "_User", objectId: "fakeId" }, + user: { __type: 'Pointer', className: '_User', objectId: 'fakeId' }, }, }); }) @@ -958,19 +958,19 @@ describe("rest create", () => { }); }); -describe("rest update", () => { - it("ignores createdAt", done => { - const config = Config.get("test"); +describe('rest update', () => { + it('ignores createdAt', done => { + const config = Config.get('test'); const nobody = auth.nobody(config); - const className = "Foo"; - const newCreatedAt = new Date("1970-01-01T00:00:00.000Z"); + const className = 'Foo'; + const newCreatedAt = new Date('1970-01-01T00:00:00.000Z'); rest .create(config, nobody, className, {}) .then(res => { const objectId = res.response.objectId; const restObject = { - createdAt: { __type: "Date", iso: newCreatedAt }, // should be ignored + createdAt: { __type: 'Date', iso: newCreatedAt }, // should be ignored }; return rest @@ -992,12 +992,12 @@ describe("rest update", () => { }); }); -describe("read-only masterKey", () => { - it("properly throws on rest.create, rest.update and rest.del", () => { - const config = Config.get("test"); +describe('read-only masterKey', () => { + it('properly throws on rest.create, rest.update and rest.del', () => { + const config = Config.get('test'); const readOnly = auth.readOnly(config); expect(() => { - rest.create(config, readOnly, "AnObject", {}); + rest.create(config, readOnly, 'AnObject', {}); }).toThrow( new Parse.Error( Parse.Error.OPERATION_FORBIDDEN, @@ -1005,27 +1005,27 @@ describe("read-only masterKey", () => { ) ); expect(() => { - rest.update(config, readOnly, "AnObject", {}); + rest.update(config, readOnly, 'AnObject', {}); }).toThrow(); expect(() => { - rest.del(config, readOnly, "AnObject", {}); + rest.del(config, readOnly, 'AnObject', {}); }).toThrow(); }); - it("properly blocks writes", async () => { + it('properly blocks writes', async () => { await reconfigureServer({ - readOnlyMasterKey: "yolo-read-only", + readOnlyMasterKey: 'yolo-read-only', }); try { await request({ url: `${Parse.serverURL}/classes/MyYolo`, - method: "POST", + method: 'POST', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Master-Key": "yolo-read-only", - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': 'yolo-read-only', + 'Content-Type': 'application/json', }, - body: { foo: "bar" }, + body: { foo: 'bar' }, }); fail(); } catch (res) { @@ -1037,52 +1037,52 @@ describe("read-only masterKey", () => { await reconfigureServer(); }); - it("should throw when masterKey and readOnlyMasterKey are the same", async () => { + it('should throw when masterKey and readOnlyMasterKey are the same', async () => { try { await reconfigureServer({ - masterKey: "yolo", - readOnlyMasterKey: "yolo", + masterKey: 'yolo', + readOnlyMasterKey: 'yolo', }); fail(); } catch (err) { expect(err).toEqual( - new Error("masterKey and readOnlyMasterKey should be different") + new Error('masterKey and readOnlyMasterKey should be different') ); } await reconfigureServer(); }); - it("should throw when masterKey and maintenanceKey are the same", async () => { + it('should throw when masterKey and maintenanceKey are the same', async () => { await expectAsync( reconfigureServer({ - masterKey: "yolo", - maintenanceKey: "yolo", + masterKey: 'yolo', + maintenanceKey: 'yolo', }) ).toBeRejectedWith( - new Error("masterKey and maintenanceKey should be different") + new Error('masterKey and maintenanceKey should be different') ); }); - it("should throw when trying to create RestWrite", () => { - const config = Config.get("test"); + it('should throw when trying to create RestWrite', () => { + const config = Config.get('test'); expect(() => { new RestWrite(config, auth.readOnly(config)); }).toThrow( new Parse.Error( Parse.Error.OPERATION_FORBIDDEN, - "Cannot perform a write operation when using readOnlyMasterKey" + 'Cannot perform a write operation when using readOnlyMasterKey' ) ); }); - it("should throw when trying to create schema", done => { + it('should throw when trying to create schema', done => { request({ - method: "POST", + method: 'POST', url: `${Parse.serverURL}/schemas`, headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Master-Key": "read-only-test", - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': 'read-only-test', + 'Content-Type': 'application/json', }, json: {}, }) @@ -1096,14 +1096,14 @@ describe("read-only masterKey", () => { }); }); - it("should throw when trying to create schema with a name", done => { + it('should throw when trying to create schema with a name', done => { request({ url: `${Parse.serverURL}/schemas/MyClass`, - method: "POST", + method: 'POST', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Master-Key": "read-only-test", - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': 'read-only-test', + 'Content-Type': 'application/json', }, json: {}, }) @@ -1117,14 +1117,14 @@ describe("read-only masterKey", () => { }); }); - it("should throw when trying to update schema", done => { + it('should throw when trying to update schema', done => { request({ url: `${Parse.serverURL}/schemas/MyClass`, - method: "PUT", + method: 'PUT', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Master-Key": "read-only-test", - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': 'read-only-test', + 'Content-Type': 'application/json', }, json: {}, }) @@ -1138,14 +1138,14 @@ describe("read-only masterKey", () => { }); }); - it("should throw when trying to delete schema", done => { + it('should throw when trying to delete schema', done => { request({ url: `${Parse.serverURL}/schemas/MyClass`, - method: "DELETE", + method: 'DELETE', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Master-Key": "read-only-test", - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': 'read-only-test', + 'Content-Type': 'application/json', }, json: {}, }) @@ -1159,14 +1159,14 @@ describe("read-only masterKey", () => { }); }); - it("should throw when trying to update the global config", done => { + it('should throw when trying to update the global config', done => { request({ url: `${Parse.serverURL}/config`, - method: "PUT", + method: 'PUT', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Master-Key": "read-only-test", - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': 'read-only-test', + 'Content-Type': 'application/json', }, json: {}, }) @@ -1180,14 +1180,14 @@ describe("read-only masterKey", () => { }); }); - it("should throw when trying to send push", done => { + it('should throw when trying to send push', done => { request({ url: `${Parse.serverURL}/push`, - method: "POST", + method: 'POST', headers: { - "X-Parse-Application-Id": Parse.applicationId, - "X-Parse-Master-Key": "read-only-test", - "Content-Type": "application/json", + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Master-Key': 'read-only-test', + 'Content-Type': 'application/json', }, json: {}, }) diff --git a/spec/schemas.spec.js b/spec/schemas.spec.js index 0ee6ef4448..b06245da44 100644 --- a/spec/schemas.spec.js +++ b/spec/schemas.spec.js @@ -1,27 +1,27 @@ -"use strict"; +'use strict'; -const Parse = require("parse/node").Parse; -const dd = require("deep-diff"); -const Config = require("../lib/Config"); -const request = require("../lib/request"); -const TestUtils = require("../lib/TestUtils"); +const Parse = require('parse/node').Parse; +const dd = require('deep-diff'); +const Config = require('../lib/Config'); +const request = require('../lib/request'); +const TestUtils = require('../lib/TestUtils'); const SchemaController = - require("../lib/Controllers/SchemaController").SchemaController; + require('../lib/Controllers/SchemaController').SchemaController; let config; const hasAllPODobject = () => { - const obj = new Parse.Object("HasAllPOD"); - obj.set("aNumber", 5); - obj.set("aString", "string"); - obj.set("aBool", true); - obj.set("aDate", new Date()); - obj.set("aObject", { k1: "value", k2: true, k3: 5 }); - obj.set("aArray", ["contents", true, 5]); - obj.set("aGeoPoint", new Parse.GeoPoint({ latitude: 0, longitude: 0 })); + const obj = new Parse.Object('HasAllPOD'); + obj.set('aNumber', 5); + obj.set('aString', 'string'); + obj.set('aBool', true); + obj.set('aDate', new Date()); + obj.set('aObject', { k1: 'value', k2: true, k3: 5 }); + obj.set('aArray', ['contents', true, 5]); + obj.set('aGeoPoint', new Parse.GeoPoint({ latitude: 0, longitude: 0 })); obj.set( - "aFile", - new Parse.File("f.txt", { base64: "V29ya2luZyBhdCBQYXJzZSBpcyBncmVhdCE=" }) + 'aFile', + new Parse.File('f.txt', { base64: 'V29ya2luZyBhdCBQYXJzZSBpcyBncmVhdCE=' }) ); const objACL = new Parse.ACL(); objACL.setPublicWriteAccess(false); @@ -31,176 +31,176 @@ const hasAllPODobject = () => { const defaultClassLevelPermissions = { ACL: { - "*": { + '*': { read: true, write: true, }, }, find: { - "*": true, + '*': true, }, count: { - "*": true, + '*': true, }, create: { - "*": true, + '*': true, }, get: { - "*": true, + '*': true, }, update: { - "*": true, + '*': true, }, addField: { - "*": true, + '*': true, }, delete: { - "*": true, + '*': true, }, protectedFields: { - "*": [], + '*': [], }, }; const plainOldDataSchema = { - className: "HasAllPOD", + className: 'HasAllPOD', fields: { //Default fields - ACL: { type: "ACL" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - objectId: { type: "String" }, + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + objectId: { type: 'String' }, //Custom fields - aNumber: { type: "Number" }, - aString: { type: "String" }, - aBool: { type: "Boolean" }, - aDate: { type: "Date" }, - aObject: { type: "Object" }, - aArray: { type: "Array" }, - aGeoPoint: { type: "GeoPoint" }, - aFile: { type: "File" }, + aNumber: { type: 'Number' }, + aString: { type: 'String' }, + aBool: { type: 'Boolean' }, + aDate: { type: 'Date' }, + aObject: { type: 'Object' }, + aArray: { type: 'Array' }, + aGeoPoint: { type: 'GeoPoint' }, + aFile: { type: 'File' }, }, classLevelPermissions: defaultClassLevelPermissions, }; const pointersAndRelationsSchema = { - className: "HasPointersAndRelations", + className: 'HasPointersAndRelations', fields: { //Default fields - ACL: { type: "ACL" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - objectId: { type: "String" }, + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + objectId: { type: 'String' }, //Custom fields aPointer: { - type: "Pointer", - targetClass: "HasAllPOD", + type: 'Pointer', + targetClass: 'HasAllPOD', }, aRelation: { - type: "Relation", - targetClass: "HasAllPOD", + type: 'Relation', + targetClass: 'HasAllPOD', }, }, classLevelPermissions: defaultClassLevelPermissions, }; const userSchema = { - className: "_User", + className: '_User', fields: { - objectId: { type: "String" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - ACL: { type: "ACL" }, - username: { type: "String" }, - password: { type: "String" }, - email: { type: "String" }, - emailVerified: { type: "Boolean" }, - authData: { type: "Object" }, + objectId: { type: 'String' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + ACL: { type: 'ACL' }, + username: { type: 'String' }, + password: { type: 'String' }, + email: { type: 'String' }, + emailVerified: { type: 'Boolean' }, + authData: { type: 'Object' }, }, classLevelPermissions: defaultClassLevelPermissions, }; const roleSchema = { - className: "_Role", + className: '_Role', fields: { - objectId: { type: "String" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - ACL: { type: "ACL" }, - name: { type: "String" }, - users: { type: "Relation", targetClass: "_User" }, - roles: { type: "Relation", targetClass: "_Role" }, + objectId: { type: 'String' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + ACL: { type: 'ACL' }, + name: { type: 'String' }, + users: { type: 'Relation', targetClass: '_User' }, + roles: { type: 'Relation', targetClass: '_Role' }, }, classLevelPermissions: defaultClassLevelPermissions, }; const noAuthHeaders = { - "X-Parse-Application-Id": "test", + 'X-Parse-Application-Id': 'test', }; const restKeyHeaders = { - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', }; const masterKeyHeaders = { - "X-Parse-Application-Id": "test", - "X-Parse-Master-Key": "test", - "Content-Type": "application/json", + 'X-Parse-Application-Id': 'test', + 'X-Parse-Master-Key': 'test', + 'Content-Type': 'application/json', }; -describe("schemas", () => { +describe('schemas', () => { beforeEach(async () => { await reconfigureServer(); - config = Config.get("test"); + config = Config.get('test'); }); - it("requires the master key to get all schemas", done => { + it('requires the master key to get all schemas', done => { request({ - url: "http://localhost:8378/1/schemas", + url: 'http://localhost:8378/1/schemas', json: true, headers: noAuthHeaders, }).then(fail, response => { //api.parse.com uses status code 401, but due to the lack of keys //being necessary in parse-server, 403 makes more sense expect(response.status).toEqual(403); - expect(response.data.error).toEqual("unauthorized"); + expect(response.data.error).toEqual('unauthorized'); done(); }); }); - it("requires the master key to get one schema", done => { + it('requires the master key to get one schema', done => { request({ - url: "http://localhost:8378/1/schemas/SomeSchema", + url: 'http://localhost:8378/1/schemas/SomeSchema', json: true, headers: restKeyHeaders, }).then(fail, response => { expect(response.status).toEqual(403); expect(response.data.error).toEqual( - "unauthorized: master key is required" + 'unauthorized: master key is required' ); done(); }); }); - it("asks for the master key if you use the rest key", done => { + it('asks for the master key if you use the rest key', done => { request({ - url: "http://localhost:8378/1/schemas", + url: 'http://localhost:8378/1/schemas', json: true, headers: restKeyHeaders, }).then(fail, response => { expect(response.status).toEqual(403); expect(response.data.error).toEqual( - "unauthorized: master key is required" + 'unauthorized: master key is required' ); done(); }); }); - it("creates _User schema when server starts", done => { + it('creates _User schema when server starts', done => { request({ - url: "http://localhost:8378/1/schemas", + url: 'http://localhost:8378/1/schemas', json: true, headers: masterKeyHeaders, }).then(response => { @@ -224,20 +224,20 @@ describe("schemas", () => { }); }); - it("responds with a list of schemas after creating objects", done => { + it('responds with a list of schemas after creating objects', done => { const obj1 = hasAllPODobject(); obj1 .save() .then(savedObj1 => { - const obj2 = new Parse.Object("HasPointersAndRelations"); - obj2.set("aPointer", savedObj1); - const relation = obj2.relation("aRelation"); + const obj2 = new Parse.Object('HasPointersAndRelations'); + obj2.set('aPointer', savedObj1); + const relation = obj2.relation('aRelation'); relation.add(obj1); return obj2.save(); }) .then(() => { request({ - url: "http://localhost:8378/1/schemas", + url: 'http://localhost:8378/1/schemas', json: true, headers: masterKeyHeaders, }).then(response => { @@ -267,22 +267,22 @@ describe("schemas", () => { }); }); - it("ensure refresh cache after creating a class", async done => { - spyOn(SchemaController.prototype, "reloadData").and.callFake(() => + it('ensure refresh cache after creating a class', async done => { + spyOn(SchemaController.prototype, 'reloadData').and.callFake(() => Promise.resolve() ); await request({ - url: "http://localhost:8378/1/schemas", - method: "POST", + url: 'http://localhost:8378/1/schemas', + method: 'POST', headers: masterKeyHeaders, json: true, body: { - className: "A", + className: 'A', }, }); const response = await request({ - url: "http://localhost:8378/1/schemas", - method: "GET", + url: 'http://localhost:8378/1/schemas', + method: 'GET', headers: masterKeyHeaders, json: true, }); @@ -291,13 +291,13 @@ describe("schemas", () => { userSchema, roleSchema, { - className: "A", + className: 'A', fields: { //Default fields - ACL: { type: "ACL" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - objectId: { type: "String" }, + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + objectId: { type: 'String' }, }, classLevelPermissions: defaultClassLevelPermissions, }, @@ -319,11 +319,11 @@ describe("schemas", () => { done(); }); - it("responds with a single schema", done => { + it('responds with a single schema', done => { const obj = hasAllPODobject(); obj.save().then(() => { request({ - url: "http://localhost:8378/1/schemas/HasAllPOD", + url: 'http://localhost:8378/1/schemas/HasAllPOD', json: true, headers: masterKeyHeaders, }).then(response => { @@ -333,63 +333,63 @@ describe("schemas", () => { }); }); - it("treats class names case sensitively", done => { + it('treats class names case sensitively', done => { const obj = hasAllPODobject(); obj.save().then(() => { request({ - url: "http://localhost:8378/1/schemas/HASALLPOD", + url: 'http://localhost:8378/1/schemas/HASALLPOD', json: true, headers: masterKeyHeaders, }).then(fail, response => { expect(response.status).toEqual(400); expect(response.data).toEqual({ code: 103, - error: "Class HASALLPOD does not exist.", + error: 'Class HASALLPOD does not exist.', }); done(); }); }); }); - it("requires the master key to create a schema", done => { + it('requires the master key to create a schema', done => { request({ - url: "http://localhost:8378/1/schemas", - method: "POST", + url: 'http://localhost:8378/1/schemas', + method: 'POST', json: true, headers: noAuthHeaders, body: { - className: "MyClass", + className: 'MyClass', }, }).then(fail, response => { expect(response.status).toEqual(403); - expect(response.data.error).toEqual("unauthorized"); + expect(response.data.error).toEqual('unauthorized'); done(); }); }); - it("sends an error if you use mismatching class names", done => { + it('sends an error if you use mismatching class names', done => { request({ - url: "http://localhost:8378/1/schemas/A", - method: "POST", + url: 'http://localhost:8378/1/schemas/A', + method: 'POST', headers: masterKeyHeaders, json: true, body: { - className: "B", + className: 'B', }, }).then(fail, response => { expect(response.status).toEqual(400); expect(response.data).toEqual({ code: Parse.Error.INVALID_CLASS_NAME, - error: "Class name mismatch between B and A.", + error: 'Class name mismatch between B and A.', }); done(); }); }); - it("sends an error if you use no class name", done => { + it('sends an error if you use no class name', done => { request({ - url: "http://localhost:8378/1/schemas", - method: "POST", + url: 'http://localhost:8378/1/schemas', + method: 'POST', headers: masterKeyHeaders, json: true, body: {}, @@ -397,64 +397,64 @@ describe("schemas", () => { expect(response.status).toEqual(400); expect(response.data).toEqual({ code: 135, - error: "POST /schemas needs a class name.", + error: 'POST /schemas needs a class name.', }); done(); }); }); - it("sends an error if you try to create the same class twice", done => { + it('sends an error if you try to create the same class twice', done => { request({ - url: "http://localhost:8378/1/schemas", - method: "POST", + url: 'http://localhost:8378/1/schemas', + method: 'POST', headers: masterKeyHeaders, json: true, body: { - className: "A", + className: 'A', }, }).then(() => { request({ - url: "http://localhost:8378/1/schemas", - method: "POST", + url: 'http://localhost:8378/1/schemas', + method: 'POST', headers: masterKeyHeaders, json: true, body: { - className: "A", + className: 'A', }, }).then(fail, response => { expect(response.status).toEqual(400); expect(response.data).toEqual({ code: Parse.Error.INVALID_CLASS_NAME, - error: "Class A already exists.", + error: 'Class A already exists.', }); done(); }); }); }); - it("responds with all fields when you create a class", done => { + it('responds with all fields when you create a class', done => { request({ - url: "http://localhost:8378/1/schemas", - method: "POST", + url: 'http://localhost:8378/1/schemas', + method: 'POST', headers: masterKeyHeaders, json: true, body: { - className: "NewClass", + className: 'NewClass', fields: { - foo: { type: "Number" }, - ptr: { type: "Pointer", targetClass: "SomeClass" }, + foo: { type: 'Number' }, + ptr: { type: 'Pointer', targetClass: 'SomeClass' }, }, }, }).then(response => { expect(response.data).toEqual({ - className: "NewClass", + className: 'NewClass', fields: { - ACL: { type: "ACL" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - objectId: { type: "String" }, - foo: { type: "Number" }, - ptr: { type: "Pointer", targetClass: "SomeClass" }, + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + objectId: { type: 'String' }, + foo: { type: 'Number' }, + ptr: { type: 'Pointer', targetClass: 'SomeClass' }, }, classLevelPermissions: defaultClassLevelPermissions, }); @@ -462,292 +462,292 @@ describe("schemas", () => { }); }); - it("responds with all fields and options when you create a class with field options", done => { + it('responds with all fields and options when you create a class with field options', done => { request({ - url: "http://localhost:8378/1/schemas", - method: "POST", + url: 'http://localhost:8378/1/schemas', + method: 'POST', headers: masterKeyHeaders, json: true, body: { - className: "NewClassWithOptions", + className: 'NewClassWithOptions', fields: { - foo1: { type: "Number" }, - foo2: { type: "Number", required: true, defaultValue: 10 }, + foo1: { type: 'Number' }, + foo2: { type: 'Number', required: true, defaultValue: 10 }, foo3: { - type: "String", + type: 'String', required: false, - defaultValue: "some string", + defaultValue: 'some string', }, - foo4: { type: "Date", required: true }, - foo5: { type: "Number", defaultValue: 5 }, - ptr: { type: "Pointer", targetClass: "SomeClass", required: false }, + foo4: { type: 'Date', required: true }, + foo5: { type: 'Number', defaultValue: 5 }, + ptr: { type: 'Pointer', targetClass: 'SomeClass', required: false }, defaultFalse: { - type: "Boolean", + type: 'Boolean', required: true, defaultValue: false, }, - defaultZero: { type: "Number", defaultValue: 0 }, - relation: { type: "Relation", targetClass: "SomeClass" }, + defaultZero: { type: 'Number', defaultValue: 0 }, + relation: { type: 'Relation', targetClass: 'SomeClass' }, }, }, }).then(async response => { expect(response.data).toEqual({ - className: "NewClassWithOptions", + className: 'NewClassWithOptions', fields: { - ACL: { type: "ACL" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - objectId: { type: "String" }, - foo1: { type: "Number" }, - foo2: { type: "Number", required: true, defaultValue: 10 }, + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + objectId: { type: 'String' }, + foo1: { type: 'Number' }, + foo2: { type: 'Number', required: true, defaultValue: 10 }, foo3: { - type: "String", + type: 'String', required: false, - defaultValue: "some string", + defaultValue: 'some string', }, - foo4: { type: "Date", required: true }, - foo5: { type: "Number", defaultValue: 5 }, - ptr: { type: "Pointer", targetClass: "SomeClass", required: false }, + foo4: { type: 'Date', required: true }, + foo5: { type: 'Number', defaultValue: 5 }, + ptr: { type: 'Pointer', targetClass: 'SomeClass', required: false }, defaultFalse: { - type: "Boolean", + type: 'Boolean', required: true, defaultValue: false, }, - defaultZero: { type: "Number", defaultValue: 0 }, - relation: { type: "Relation", targetClass: "SomeClass" }, + defaultZero: { type: 'Number', defaultValue: 0 }, + relation: { type: 'Relation', targetClass: 'SomeClass' }, }, classLevelPermissions: defaultClassLevelPermissions, }); - const obj = new Parse.Object("NewClassWithOptions"); + const obj = new Parse.Object('NewClassWithOptions'); try { await obj.save(); - fail("should fail"); + fail('should fail'); } catch (e) { expect(e.code).toEqual(142); } const date = new Date(); - obj.set("foo4", date); + obj.set('foo4', date); await obj.save(); - expect(obj.get("foo1")).toBeUndefined(); - expect(obj.get("foo2")).toEqual(10); - expect(obj.get("foo3")).toEqual("some string"); - expect(obj.get("foo4")).toEqual(date); - expect(obj.get("foo5")).toEqual(5); - expect(obj.get("ptr")).toBeUndefined(); - expect(obj.get("defaultFalse")).toEqual(false); - expect(obj.get("defaultZero")).toEqual(0); - expect(obj.get("ptr")).toBeUndefined(); - expect(obj.get("relation")).toBeUndefined(); + expect(obj.get('foo1')).toBeUndefined(); + expect(obj.get('foo2')).toEqual(10); + expect(obj.get('foo3')).toEqual('some string'); + expect(obj.get('foo4')).toEqual(date); + expect(obj.get('foo5')).toEqual(5); + expect(obj.get('ptr')).toBeUndefined(); + expect(obj.get('defaultFalse')).toEqual(false); + expect(obj.get('defaultZero')).toEqual(0); + expect(obj.get('ptr')).toBeUndefined(); + expect(obj.get('relation')).toBeUndefined(); done(); }); }); - it("try to set a relation field as a required field", async done => { + it('try to set a relation field as a required field', async done => { try { await request({ - url: "http://localhost:8378/1/schemas", - method: "POST", + url: 'http://localhost:8378/1/schemas', + method: 'POST', headers: masterKeyHeaders, json: true, body: { - className: "NewClassWithRelationRequired", + className: 'NewClassWithRelationRequired', fields: { - foo: { type: "String" }, + foo: { type: 'String' }, relation: { - type: "Relation", - targetClass: "SomeClass", + type: 'Relation', + targetClass: 'SomeClass', required: true, }, }, }, }); - fail("should fail"); + fail('should fail'); } catch (e) { expect(e.data.code).toEqual(111); } done(); }); - it("try to set a relation field with a default value", async done => { + it('try to set a relation field with a default value', async done => { try { await request({ - url: "http://localhost:8378/1/schemas", - method: "POST", + url: 'http://localhost:8378/1/schemas', + method: 'POST', headers: masterKeyHeaders, json: true, body: { - className: "NewClassRelationWithOptions", + className: 'NewClassRelationWithOptions', fields: { - foo: { type: "String" }, + foo: { type: 'String' }, relation: { - type: "Relation", - targetClass: "SomeClass", - defaultValue: { __type: "Relation", className: "_User" }, + type: 'Relation', + targetClass: 'SomeClass', + defaultValue: { __type: 'Relation', className: '_User' }, }, }, }, }); - fail("should fail"); + fail('should fail'); } catch (e) { expect(e.data.code).toEqual(111); } done(); }); - it("try to update schemas with a relation field with options", async done => { + it('try to update schemas with a relation field with options', async done => { await request({ - url: "http://localhost:8378/1/schemas", - method: "POST", + url: 'http://localhost:8378/1/schemas', + method: 'POST', headers: masterKeyHeaders, json: true, body: { - className: "NewClassRelationWithOptions", + className: 'NewClassRelationWithOptions', fields: { - foo: { type: "String" }, + foo: { type: 'String' }, }, }, }); try { await request({ - url: "http://localhost:8378/1/schemas/NewClassRelationWithOptions", - method: "POST", + url: 'http://localhost:8378/1/schemas/NewClassRelationWithOptions', + method: 'POST', headers: masterKeyHeaders, json: true, body: { - className: "NewClassRelationWithOptions", + className: 'NewClassRelationWithOptions', fields: { relation: { - type: "Relation", - targetClass: "SomeClass", + type: 'Relation', + targetClass: 'SomeClass', required: true, }, }, - _method: "PUT", + _method: 'PUT', }, }); - fail("should fail"); + fail('should fail'); } catch (e) { expect(e.data.code).toEqual(111); } try { await request({ - url: "http://localhost:8378/1/schemas/NewClassRelationWithOptions", - method: "POST", + url: 'http://localhost:8378/1/schemas/NewClassRelationWithOptions', + method: 'POST', headers: masterKeyHeaders, json: true, body: { - className: "NewClassRelationWithOptions", + className: 'NewClassRelationWithOptions', fields: { relation: { - type: "Relation", - targetClass: "SomeClass", - defaultValue: { __type: "Relation", className: "_User" }, + type: 'Relation', + targetClass: 'SomeClass', + defaultValue: { __type: 'Relation', className: '_User' }, }, }, - _method: "PUT", + _method: 'PUT', }, }); - fail("should fail"); + fail('should fail'); } catch (e) { expect(e.data.code).toEqual(111); } done(); }); - it("validated the data type of default values when creating a new class", async () => { + it('validated the data type of default values when creating a new class', async () => { try { await request({ - url: "http://localhost:8378/1/schemas", - method: "POST", + url: 'http://localhost:8378/1/schemas', + method: 'POST', headers: masterKeyHeaders, json: true, body: { - className: "NewClassWithValidation", + className: 'NewClassWithValidation', fields: { - foo: { type: "String", defaultValue: 10 }, + foo: { type: 'String', defaultValue: 10 }, }, }, }); - fail("should fail"); + fail('should fail'); } catch (e) { expect(e.data.error).toEqual( - "schema mismatch for NewClassWithValidation.foo default value; expected String but got Number" + 'schema mismatch for NewClassWithValidation.foo default value; expected String but got Number' ); } }); - it("validated the data type of default values when adding new fields", async () => { + it('validated the data type of default values when adding new fields', async () => { try { await request({ - url: "http://localhost:8378/1/schemas", - method: "POST", + url: 'http://localhost:8378/1/schemas', + method: 'POST', headers: masterKeyHeaders, json: true, body: { - className: "NewClassWithValidation", + className: 'NewClassWithValidation', fields: { - foo: { type: "String", defaultValue: "some value" }, + foo: { type: 'String', defaultValue: 'some value' }, }, }, }); await request({ - url: "http://localhost:8378/1/schemas/NewClassWithValidation", - method: "PUT", + url: 'http://localhost:8378/1/schemas/NewClassWithValidation', + method: 'PUT', headers: masterKeyHeaders, json: true, body: { - className: "NewClassWithValidation", + className: 'NewClassWithValidation', fields: { - foo2: { type: "String", defaultValue: 10 }, + foo2: { type: 'String', defaultValue: 10 }, }, }, }); - fail("should fail"); + fail('should fail'); } catch (e) { expect(e.data.error).toEqual( - "schema mismatch for NewClassWithValidation.foo2 default value; expected String but got Number" + 'schema mismatch for NewClassWithValidation.foo2 default value; expected String but got Number' ); } }); - it("responds with all fields when getting incomplete schema", done => { + it('responds with all fields when getting incomplete schema', done => { config.database .loadSchema() .then(schemaController => schemaController.addClassIfNotExists( - "_Installation", + '_Installation', {}, defaultClassLevelPermissions ) ) .then(() => { request({ - url: "http://localhost:8378/1/schemas/_Installation", + url: 'http://localhost:8378/1/schemas/_Installation', headers: masterKeyHeaders, json: true, }).then(response => { expect( dd(response.data, { - className: "_Installation", + className: '_Installation', fields: { - objectId: { type: "String" }, - updatedAt: { type: "Date" }, - createdAt: { type: "Date" }, - installationId: { type: "String" }, - deviceToken: { type: "String" }, - channels: { type: "Array" }, - deviceType: { type: "String" }, - pushType: { type: "String" }, - GCMSenderId: { type: "String" }, - timeZone: { type: "String" }, - badge: { type: "Number" }, - appIdentifier: { type: "String" }, - localeIdentifier: { type: "String" }, - appVersion: { type: "String" }, - appName: { type: "String" }, - parseVersion: { type: "String" }, - ACL: { type: "ACL" }, + objectId: { type: 'String' }, + updatedAt: { type: 'Date' }, + createdAt: { type: 'Date' }, + installationId: { type: 'String' }, + deviceToken: { type: 'String' }, + channels: { type: 'Array' }, + deviceType: { type: 'String' }, + pushType: { type: 'String' }, + GCMSenderId: { type: 'String' }, + timeZone: { type: 'String' }, + badge: { type: 'Number' }, + appIdentifier: { type: 'String' }, + localeIdentifier: { type: 'String' }, + appVersion: { type: 'String' }, + appName: { type: 'String' }, + parseVersion: { type: 'String' }, + ACL: { type: 'ACL' }, }, classLevelPermissions: defaultClassLevelPermissions, }) @@ -761,23 +761,23 @@ describe("schemas", () => { }); }); - it("lets you specify class name in both places", done => { + it('lets you specify class name in both places', done => { request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "POST", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'POST', headers: masterKeyHeaders, json: true, body: { - className: "NewClass", + className: 'NewClass', }, }).then(response => { expect(response.data).toEqual({ - className: "NewClass", + className: 'NewClass', fields: { - ACL: { type: "ACL" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - objectId: { type: "String" }, + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + objectId: { type: 'String' }, }, classLevelPermissions: defaultClassLevelPermissions, }); @@ -785,187 +785,187 @@ describe("schemas", () => { }); }); - it("requires the master key to modify schemas", done => { + it('requires the master key to modify schemas', done => { request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "POST", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'POST', headers: masterKeyHeaders, json: true, body: {}, }).then(() => { request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "PUT", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'PUT', headers: noAuthHeaders, json: true, body: {}, }).then(fail, response => { expect(response.status).toEqual(403); - expect(response.data.error).toEqual("unauthorized"); + expect(response.data.error).toEqual('unauthorized'); done(); }); }); }); - it("rejects class name mis-matches in put", done => { + it('rejects class name mis-matches in put', done => { request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "PUT", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'PUT', headers: masterKeyHeaders, json: true, - body: { className: "WrongClassName" }, + body: { className: 'WrongClassName' }, }).then(fail, response => { expect(response.status).toEqual(400); expect(response.data.code).toEqual(Parse.Error.INVALID_CLASS_NAME); expect(response.data.error).toEqual( - "Class name mismatch between WrongClassName and NewClass." + 'Class name mismatch between WrongClassName and NewClass.' ); done(); }); }); - it("refuses to add fields to non-existent classes", done => { + it('refuses to add fields to non-existent classes', done => { request({ - url: "http://localhost:8378/1/schemas/NoClass", - method: "PUT", + url: 'http://localhost:8378/1/schemas/NoClass', + method: 'PUT', headers: masterKeyHeaders, json: true, body: { fields: { - newField: { type: "String" }, + newField: { type: 'String' }, }, }, }).then(fail, response => { expect(response.status).toEqual(400); expect(response.data.code).toEqual(Parse.Error.INVALID_CLASS_NAME); - expect(response.data.error).toEqual("Class NoClass does not exist."); + expect(response.data.error).toEqual('Class NoClass does not exist.'); done(); }); }); - it("refuses to put to existing fields with different type, even if it would not be a change", done => { + it('refuses to put to existing fields with different type, even if it would not be a change', done => { const obj = hasAllPODobject(); obj.save().then(() => { request({ - url: "http://localhost:8378/1/schemas/HasAllPOD", - method: "PUT", + url: 'http://localhost:8378/1/schemas/HasAllPOD', + method: 'PUT', headers: masterKeyHeaders, json: true, body: { fields: { - aString: { type: "Number" }, + aString: { type: 'Number' }, }, }, }).then(fail, response => { expect(response.status).toEqual(400); expect(response.data.code).toEqual(255); expect(response.data.error).toEqual( - "Field aString exists, cannot update." + 'Field aString exists, cannot update.' ); done(); }); }); }); - it("refuses to delete non-existent fields", done => { + it('refuses to delete non-existent fields', done => { const obj = hasAllPODobject(); obj.save().then(() => { request({ - url: "http://localhost:8378/1/schemas/HasAllPOD", - method: "PUT", + url: 'http://localhost:8378/1/schemas/HasAllPOD', + method: 'PUT', headers: masterKeyHeaders, json: true, body: { fields: { - nonExistentKey: { __op: "Delete" }, + nonExistentKey: { __op: 'Delete' }, }, }, }).then(fail, response => { expect(response.status).toEqual(400); expect(response.data.code).toEqual(255); expect(response.data.error).toEqual( - "Field nonExistentKey does not exist, cannot delete." + 'Field nonExistentKey does not exist, cannot delete.' ); done(); }); }); }); - it("refuses to add a geopoint to a class that already has one", done => { + it('refuses to add a geopoint to a class that already has one', done => { const obj = hasAllPODobject(); obj.save().then(() => { request({ - url: "http://localhost:8378/1/schemas/HasAllPOD", - method: "PUT", + url: 'http://localhost:8378/1/schemas/HasAllPOD', + method: 'PUT', headers: masterKeyHeaders, json: true, body: { fields: { - newGeo: { type: "GeoPoint" }, + newGeo: { type: 'GeoPoint' }, }, }, }).then(fail, response => { expect(response.status).toEqual(400); expect(response.data.code).toEqual(Parse.Error.INCORRECT_TYPE); expect(response.data.error).toEqual( - "currently, only one GeoPoint field may exist in an object. Adding newGeo when aGeoPoint already exists." + 'currently, only one GeoPoint field may exist in an object. Adding newGeo when aGeoPoint already exists.' ); done(); }); }); }); - it("refuses to add two geopoints", done => { - const obj = new Parse.Object("NewClass"); - obj.set("aString", "aString"); + it('refuses to add two geopoints', done => { + const obj = new Parse.Object('NewClass'); + obj.set('aString', 'aString'); obj.save().then(() => { request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "PUT", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'PUT', headers: masterKeyHeaders, json: true, body: { fields: { - newGeo1: { type: "GeoPoint" }, - newGeo2: { type: "GeoPoint" }, + newGeo1: { type: 'GeoPoint' }, + newGeo2: { type: 'GeoPoint' }, }, }, }).then(fail, response => { expect(response.status).toEqual(400); expect(response.data.code).toEqual(Parse.Error.INCORRECT_TYPE); expect(response.data.error).toEqual( - "currently, only one GeoPoint field may exist in an object. Adding newGeo2 when newGeo1 already exists." + 'currently, only one GeoPoint field may exist in an object. Adding newGeo2 when newGeo1 already exists.' ); done(); }); }); }); - it("allows you to delete and add a geopoint in the same request", done => { - const obj = new Parse.Object("NewClass"); - obj.set("geo1", new Parse.GeoPoint({ latitude: 0, longitude: 0 })); + it('allows you to delete and add a geopoint in the same request', done => { + const obj = new Parse.Object('NewClass'); + obj.set('geo1', new Parse.GeoPoint({ latitude: 0, longitude: 0 })); obj.save().then(() => { request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "PUT", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'PUT', headers: masterKeyHeaders, json: true, body: { fields: { - geo2: { type: "GeoPoint" }, - geo1: { __op: "Delete" }, + geo2: { type: 'GeoPoint' }, + geo1: { __op: 'Delete' }, }, }, }).then(response => { expect( dd(response.data, { - className: "NewClass", + className: 'NewClass', fields: { - ACL: { type: "ACL" }, - createdAt: { type: "Date" }, - objectId: { type: "String" }, - updatedAt: { type: "Date" }, - geo2: { type: "GeoPoint" }, + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + objectId: { type: 'String' }, + updatedAt: { type: 'Date' }, + geo2: { type: 'GeoPoint' }, }, classLevelPermissions: defaultClassLevelPermissions, }) @@ -975,12 +975,12 @@ describe("schemas", () => { }); }); - it("put with no modifications returns all fields", done => { + it('put with no modifications returns all fields', done => { const obj = hasAllPODobject(); obj.save().then(() => { request({ - url: "http://localhost:8378/1/schemas/HasAllPOD", - method: "PUT", + url: 'http://localhost:8378/1/schemas/HasAllPOD', + method: 'PUT', headers: masterKeyHeaders, json: true, body: {}, @@ -991,51 +991,51 @@ describe("schemas", () => { }); }); - it("lets you add fields", done => { + it('lets you add fields', done => { request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "POST", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'POST', headers: masterKeyHeaders, json: true, body: {}, }).then(() => { request({ - method: "PUT", - url: "http://localhost:8378/1/schemas/NewClass", + method: 'PUT', + url: 'http://localhost:8378/1/schemas/NewClass', headers: masterKeyHeaders, json: true, body: { fields: { - newField: { type: "String" }, + newField: { type: 'String' }, }, }, }).then(response => { expect( dd(response.data, { - className: "NewClass", + className: 'NewClass', fields: { - ACL: { type: "ACL" }, - createdAt: { type: "Date" }, - objectId: { type: "String" }, - updatedAt: { type: "Date" }, - newField: { type: "String" }, + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + objectId: { type: 'String' }, + updatedAt: { type: 'Date' }, + newField: { type: 'String' }, }, classLevelPermissions: defaultClassLevelPermissions, }) ).toEqual(undefined); request({ - url: "http://localhost:8378/1/schemas/NewClass", + url: 'http://localhost:8378/1/schemas/NewClass', headers: masterKeyHeaders, json: true, }).then(response => { expect(response.data).toEqual({ - className: "NewClass", + className: 'NewClass', fields: { - ACL: { type: "ACL" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - objectId: { type: "String" }, - newField: { type: "String" }, + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + objectId: { type: 'String' }, + newField: { type: 'String' }, }, classLevelPermissions: defaultClassLevelPermissions, }); @@ -1045,62 +1045,62 @@ describe("schemas", () => { }); }); - it("lets you add fields with options", done => { + it('lets you add fields with options', done => { request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "POST", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'POST', headers: masterKeyHeaders, json: true, body: {}, }).then(() => { request({ - method: "PUT", - url: "http://localhost:8378/1/schemas/NewClass", + method: 'PUT', + url: 'http://localhost:8378/1/schemas/NewClass', headers: masterKeyHeaders, json: true, body: { fields: { newField: { - type: "String", + type: 'String', required: true, - defaultValue: "some value", + defaultValue: 'some value', }, }, }, }).then(response => { expect( dd(response.data, { - className: "NewClass", + className: 'NewClass', fields: { - ACL: { type: "ACL" }, - createdAt: { type: "Date" }, - objectId: { type: "String" }, - updatedAt: { type: "Date" }, + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + objectId: { type: 'String' }, + updatedAt: { type: 'Date' }, newField: { - type: "String", + type: 'String', required: true, - defaultValue: "some value", + defaultValue: 'some value', }, }, classLevelPermissions: defaultClassLevelPermissions, }) ).toEqual(undefined); request({ - url: "http://localhost:8378/1/schemas/NewClass", + url: 'http://localhost:8378/1/schemas/NewClass', headers: masterKeyHeaders, json: true, }).then(response => { expect(response.data).toEqual({ - className: "NewClass", + className: 'NewClass', fields: { - ACL: { type: "ACL" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - objectId: { type: "String" }, + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + objectId: { type: 'String' }, newField: { - type: "String", + type: 'String', required: true, - defaultValue: "some value", + defaultValue: 'some value', }, }, classLevelPermissions: defaultClassLevelPermissions, @@ -1111,315 +1111,315 @@ describe("schemas", () => { }); }); - it("should validate required fields", done => { + it('should validate required fields', done => { request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "POST", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'POST', headers: masterKeyHeaders, json: true, body: {}, }).then(() => { request({ - method: "PUT", - url: "http://localhost:8378/1/schemas/NewClass", + method: 'PUT', + url: 'http://localhost:8378/1/schemas/NewClass', headers: masterKeyHeaders, json: true, body: { fields: { newRequiredField: { - type: "String", + type: 'String', required: true, }, newRequiredFieldWithDefaultValue: { - type: "String", + type: 'String', required: true, - defaultValue: "some value", + defaultValue: 'some value', }, newNotRequiredField: { - type: "String", + type: 'String', required: false, }, newNotRequiredFieldWithDefaultValue: { - type: "String", + type: 'String', required: false, - defaultValue: "some value", + defaultValue: 'some value', }, newRegularFieldWithDefaultValue: { - type: "String", - defaultValue: "some value", + type: 'String', + defaultValue: 'some value', }, newRegularField: { - type: "String", + type: 'String', }, }, }, }).then(async () => { - let obj = new Parse.Object("NewClass"); + let obj = new Parse.Object('NewClass'); try { await obj.save(); - fail("Should fail"); + fail('Should fail'); } catch (e) { expect(e.code).toEqual(142); - expect(e.message).toEqual("newRequiredField is required"); + expect(e.message).toEqual('newRequiredField is required'); } - obj.set("newRequiredField", "some value"); + obj.set('newRequiredField', 'some value'); await obj.save(); - expect(obj.get("newRequiredField")).toEqual("some value"); - expect(obj.get("newRequiredFieldWithDefaultValue")).toEqual( - "some value" + expect(obj.get('newRequiredField')).toEqual('some value'); + expect(obj.get('newRequiredFieldWithDefaultValue')).toEqual( + 'some value' ); - expect(obj.get("newNotRequiredField")).toEqual(undefined); - expect(obj.get("newNotRequiredFieldWithDefaultValue")).toEqual( - "some value" + expect(obj.get('newNotRequiredField')).toEqual(undefined); + expect(obj.get('newNotRequiredFieldWithDefaultValue')).toEqual( + 'some value' ); - expect(obj.get("newRegularField")).toEqual(undefined); - obj.set("newRequiredField", null); + expect(obj.get('newRegularField')).toEqual(undefined); + obj.set('newRequiredField', null); try { await obj.save(); - fail("Should fail"); + fail('Should fail'); } catch (e) { expect(e.code).toEqual(142); - expect(e.message).toEqual("newRequiredField is required"); + expect(e.message).toEqual('newRequiredField is required'); } - obj.unset("newRequiredField"); + obj.unset('newRequiredField'); try { await obj.save(); - fail("Should fail"); + fail('Should fail'); } catch (e) { expect(e.code).toEqual(142); - expect(e.message).toEqual("newRequiredField is required"); + expect(e.message).toEqual('newRequiredField is required'); } - obj.set("newRequiredField", "some value2"); + obj.set('newRequiredField', 'some value2'); await obj.save(); - expect(obj.get("newRequiredField")).toEqual("some value2"); - expect(obj.get("newRequiredFieldWithDefaultValue")).toEqual( - "some value" + expect(obj.get('newRequiredField')).toEqual('some value2'); + expect(obj.get('newRequiredFieldWithDefaultValue')).toEqual( + 'some value' ); - expect(obj.get("newNotRequiredField")).toEqual(undefined); - expect(obj.get("newNotRequiredFieldWithDefaultValue")).toEqual( - "some value" + expect(obj.get('newNotRequiredField')).toEqual(undefined); + expect(obj.get('newNotRequiredFieldWithDefaultValue')).toEqual( + 'some value' ); - expect(obj.get("newRegularField")).toEqual(undefined); - obj.unset("newRequiredFieldWithDefaultValue"); + expect(obj.get('newRegularField')).toEqual(undefined); + obj.unset('newRequiredFieldWithDefaultValue'); try { await obj.save(); - fail("Should fail"); + fail('Should fail'); } catch (e) { expect(e.code).toEqual(142); expect(e.message).toEqual( - "newRequiredFieldWithDefaultValue is required" + 'newRequiredFieldWithDefaultValue is required' ); } - obj.set("newRequiredFieldWithDefaultValue", ""); + obj.set('newRequiredFieldWithDefaultValue', ''); try { await obj.save(); - fail("Should fail"); + fail('Should fail'); } catch (e) { expect(e.code).toEqual(142); expect(e.message).toEqual( - "newRequiredFieldWithDefaultValue is required" + 'newRequiredFieldWithDefaultValue is required' ); } - obj.set("newRequiredFieldWithDefaultValue", "some value2"); - obj.set("newNotRequiredField", ""); - obj.set("newNotRequiredFieldWithDefaultValue", null); - obj.unset("newRegularField"); + obj.set('newRequiredFieldWithDefaultValue', 'some value2'); + obj.set('newNotRequiredField', ''); + obj.set('newNotRequiredFieldWithDefaultValue', null); + obj.unset('newRegularField'); await obj.save(); - expect(obj.get("newRequiredField")).toEqual("some value2"); - expect(obj.get("newRequiredFieldWithDefaultValue")).toEqual( - "some value2" + expect(obj.get('newRequiredField')).toEqual('some value2'); + expect(obj.get('newRequiredFieldWithDefaultValue')).toEqual( + 'some value2' ); - expect(obj.get("newNotRequiredField")).toEqual(""); - expect(obj.get("newNotRequiredFieldWithDefaultValue")).toEqual(null); - expect(obj.get("newRegularField")).toEqual(undefined); - obj = new Parse.Object("NewClass"); - obj.set("newRequiredField", "some value3"); - obj.set("newRequiredFieldWithDefaultValue", "some value3"); - obj.set("newNotRequiredField", "some value3"); - obj.set("newNotRequiredFieldWithDefaultValue", "some value3"); - obj.set("newRegularField", "some value3"); + expect(obj.get('newNotRequiredField')).toEqual(''); + expect(obj.get('newNotRequiredFieldWithDefaultValue')).toEqual(null); + expect(obj.get('newRegularField')).toEqual(undefined); + obj = new Parse.Object('NewClass'); + obj.set('newRequiredField', 'some value3'); + obj.set('newRequiredFieldWithDefaultValue', 'some value3'); + obj.set('newNotRequiredField', 'some value3'); + obj.set('newNotRequiredFieldWithDefaultValue', 'some value3'); + obj.set('newRegularField', 'some value3'); await obj.save(); - expect(obj.get("newRequiredField")).toEqual("some value3"); - expect(obj.get("newRequiredFieldWithDefaultValue")).toEqual( - "some value3" + expect(obj.get('newRequiredField')).toEqual('some value3'); + expect(obj.get('newRequiredFieldWithDefaultValue')).toEqual( + 'some value3' ); - expect(obj.get("newNotRequiredField")).toEqual("some value3"); - expect(obj.get("newNotRequiredFieldWithDefaultValue")).toEqual( - "some value3" + expect(obj.get('newNotRequiredField')).toEqual('some value3'); + expect(obj.get('newNotRequiredFieldWithDefaultValue')).toEqual( + 'some value3' ); - expect(obj.get("newRegularField")).toEqual("some value3"); + expect(obj.get('newRegularField')).toEqual('some value3'); done(); }); }); }); - it("should validate required fields and set default values after before save trigger", async () => { + it('should validate required fields and set default values after before save trigger', async () => { await request({ - url: "http://localhost:8378/1/schemas", - method: "POST", + url: 'http://localhost:8378/1/schemas', + method: 'POST', headers: masterKeyHeaders, json: true, body: { - className: "NewClassForBeforeSaveTest", + className: 'NewClassForBeforeSaveTest', fields: { - foo1: { type: "String" }, - foo2: { type: "String", required: true }, + foo1: { type: 'String' }, + foo2: { type: 'String', required: true }, foo3: { - type: "String", + type: 'String', required: true, - defaultValue: "some default value 3", + defaultValue: 'some default value 3', }, - foo4: { type: "String", defaultValue: "some default value 4" }, + foo4: { type: 'String', defaultValue: 'some default value 4' }, }, }, }); - Parse.Cloud.beforeSave("NewClassForBeforeSaveTest", req => { - req.object.set("foo1", "some value 1"); - req.object.set("foo2", "some value 2"); - req.object.set("foo3", "some value 3"); - req.object.set("foo4", "some value 4"); + Parse.Cloud.beforeSave('NewClassForBeforeSaveTest', req => { + req.object.set('foo1', 'some value 1'); + req.object.set('foo2', 'some value 2'); + req.object.set('foo3', 'some value 3'); + req.object.set('foo4', 'some value 4'); }); - let obj = new Parse.Object("NewClassForBeforeSaveTest"); + let obj = new Parse.Object('NewClassForBeforeSaveTest'); await obj.save(); - expect(obj.get("foo1")).toEqual("some value 1"); - expect(obj.get("foo2")).toEqual("some value 2"); - expect(obj.get("foo3")).toEqual("some value 3"); - expect(obj.get("foo4")).toEqual("some value 4"); + expect(obj.get('foo1')).toEqual('some value 1'); + expect(obj.get('foo2')).toEqual('some value 2'); + expect(obj.get('foo3')).toEqual('some value 3'); + expect(obj.get('foo4')).toEqual('some value 4'); - Parse.Cloud.beforeSave("NewClassForBeforeSaveTest", req => { - req.object.set("foo1", "some value 1"); - req.object.set("foo2", "some value 2"); + Parse.Cloud.beforeSave('NewClassForBeforeSaveTest', req => { + req.object.set('foo1', 'some value 1'); + req.object.set('foo2', 'some value 2'); }); - obj = new Parse.Object("NewClassForBeforeSaveTest"); + obj = new Parse.Object('NewClassForBeforeSaveTest'); await obj.save(); - expect(obj.get("foo1")).toEqual("some value 1"); - expect(obj.get("foo2")).toEqual("some value 2"); - expect(obj.get("foo3")).toEqual("some default value 3"); - expect(obj.get("foo4")).toEqual("some default value 4"); + expect(obj.get('foo1')).toEqual('some value 1'); + expect(obj.get('foo2')).toEqual('some value 2'); + expect(obj.get('foo3')).toEqual('some default value 3'); + expect(obj.get('foo4')).toEqual('some default value 4'); - Parse.Cloud.beforeSave("NewClassForBeforeSaveTest", req => { - req.object.set("foo1", "some value 1"); - req.object.set("foo2", "some value 2"); - req.object.set("foo3", undefined); - req.object.unset("foo4"); + Parse.Cloud.beforeSave('NewClassForBeforeSaveTest', req => { + req.object.set('foo1', 'some value 1'); + req.object.set('foo2', 'some value 2'); + req.object.set('foo3', undefined); + req.object.unset('foo4'); }); - obj = new Parse.Object("NewClassForBeforeSaveTest"); - obj.set("foo3", "some value 3"); - obj.set("foo4", "some value 4"); + obj = new Parse.Object('NewClassForBeforeSaveTest'); + obj.set('foo3', 'some value 3'); + obj.set('foo4', 'some value 4'); await obj.save(); - expect(obj.get("foo1")).toEqual("some value 1"); - expect(obj.get("foo2")).toEqual("some value 2"); - expect(obj.get("foo3")).toEqual("some default value 3"); - expect(obj.get("foo4")).toEqual("some default value 4"); + expect(obj.get('foo1')).toEqual('some value 1'); + expect(obj.get('foo2')).toEqual('some value 2'); + expect(obj.get('foo3')).toEqual('some default value 3'); + expect(obj.get('foo4')).toEqual('some default value 4'); - Parse.Cloud.beforeSave("NewClassForBeforeSaveTest", req => { - req.object.set("foo1", "some value 1"); - req.object.set("foo2", undefined); - req.object.set("foo3", undefined); - req.object.unset("foo4"); + Parse.Cloud.beforeSave('NewClassForBeforeSaveTest', req => { + req.object.set('foo1', 'some value 1'); + req.object.set('foo2', undefined); + req.object.set('foo3', undefined); + req.object.unset('foo4'); }); - obj = new Parse.Object("NewClassForBeforeSaveTest"); - obj.set("foo2", "some value 2"); - obj.set("foo3", "some value 3"); - obj.set("foo4", "some value 4"); + obj = new Parse.Object('NewClassForBeforeSaveTest'); + obj.set('foo2', 'some value 2'); + obj.set('foo3', 'some value 3'); + obj.set('foo4', 'some value 4'); try { await obj.save(); - fail("should fail"); + fail('should fail'); } catch (e) { - expect(e.message).toEqual("foo2 is required"); + expect(e.message).toEqual('foo2 is required'); } - Parse.Cloud.beforeSave("NewClassForBeforeSaveTest", req => { - req.object.set("foo1", "some value 1"); - req.object.unset("foo2"); - req.object.set("foo3", undefined); - req.object.unset("foo4"); + Parse.Cloud.beforeSave('NewClassForBeforeSaveTest', req => { + req.object.set('foo1', 'some value 1'); + req.object.unset('foo2'); + req.object.set('foo3', undefined); + req.object.unset('foo4'); }); - obj = new Parse.Object("NewClassForBeforeSaveTest"); - obj.set("foo2", "some value 2"); - obj.set("foo3", "some value 3"); - obj.set("foo4", "some value 4"); + obj = new Parse.Object('NewClassForBeforeSaveTest'); + obj.set('foo2', 'some value 2'); + obj.set('foo3', 'some value 3'); + obj.set('foo4', 'some value 4'); try { await obj.save(); - fail("should fail"); + fail('should fail'); } catch (e) { - expect(e.message).toEqual("foo2 is required"); + expect(e.message).toEqual('foo2 is required'); } }); - it("lets you add fields to system schema", done => { + it('lets you add fields to system schema', done => { request({ - method: "POST", - url: "http://localhost:8378/1/schemas/_User", + method: 'POST', + url: 'http://localhost:8378/1/schemas/_User', headers: masterKeyHeaders, json: true, }).then(fail, () => { request({ - url: "http://localhost:8378/1/schemas/_User", - method: "PUT", + url: 'http://localhost:8378/1/schemas/_User', + method: 'PUT', headers: masterKeyHeaders, json: true, body: { fields: { - newField: { type: "String" }, + newField: { type: 'String' }, }, }, }).then(response => { delete response.data.indexes; expect( dd(response.data, { - className: "_User", + className: '_User', fields: { - objectId: { type: "String" }, - updatedAt: { type: "Date" }, - createdAt: { type: "Date" }, - username: { type: "String" }, - password: { type: "String" }, - email: { type: "String" }, - emailVerified: { type: "Boolean" }, - authData: { type: "Object" }, - newField: { type: "String" }, - ACL: { type: "ACL" }, + objectId: { type: 'String' }, + updatedAt: { type: 'Date' }, + createdAt: { type: 'Date' }, + username: { type: 'String' }, + password: { type: 'String' }, + email: { type: 'String' }, + emailVerified: { type: 'Boolean' }, + authData: { type: 'Object' }, + newField: { type: 'String' }, + ACL: { type: 'ACL' }, }, classLevelPermissions: { ...defaultClassLevelPermissions, protectedFields: { - "*": ["email"], + '*': ['email'], }, }, }) ).toBeUndefined(); request({ - url: "http://localhost:8378/1/schemas/_User", + url: 'http://localhost:8378/1/schemas/_User', headers: masterKeyHeaders, json: true, }).then(response => { delete response.data.indexes; expect( dd(response.data, { - className: "_User", + className: '_User', fields: { - objectId: { type: "String" }, - updatedAt: { type: "Date" }, - createdAt: { type: "Date" }, - username: { type: "String" }, - password: { type: "String" }, - email: { type: "String" }, - emailVerified: { type: "Boolean" }, - authData: { type: "Object" }, - newField: { type: "String" }, - ACL: { type: "ACL" }, + objectId: { type: 'String' }, + updatedAt: { type: 'Date' }, + createdAt: { type: 'Date' }, + username: { type: 'String' }, + password: { type: 'String' }, + email: { type: 'String' }, + emailVerified: { type: 'Boolean' }, + authData: { type: 'Object' }, + newField: { type: 'String' }, + ACL: { type: 'ACL' }, }, classLevelPermissions: defaultClassLevelPermissions, }) @@ -1430,12 +1430,12 @@ describe("schemas", () => { }); }); - it("lets you delete multiple fields and check schema", done => { + it('lets you delete multiple fields and check schema', done => { const simpleOneObject = () => { - const obj = new Parse.Object("SimpleOne"); - obj.set("aNumber", 5); - obj.set("aString", "string"); - obj.set("aBool", true); + const obj = new Parse.Object('SimpleOne'); + obj.set('aNumber', 5); + obj.set('aString', 'string'); + obj.set('aBool', true); return obj; }; @@ -1443,27 +1443,27 @@ describe("schemas", () => { .save() .then(() => { request({ - url: "http://localhost:8378/1/schemas/SimpleOne", - method: "PUT", + url: 'http://localhost:8378/1/schemas/SimpleOne', + method: 'PUT', headers: masterKeyHeaders, json: true, body: { fields: { - aString: { __op: "Delete" }, - aNumber: { __op: "Delete" }, + aString: { __op: 'Delete' }, + aNumber: { __op: 'Delete' }, }, }, }).then(response => { expect(response.data).toEqual({ - className: "SimpleOne", + className: 'SimpleOne', fields: { //Default fields - ACL: { type: "ACL" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - objectId: { type: "String" }, + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + objectId: { type: 'String' }, //Custom fields - aBool: { type: "Boolean" }, + aBool: { type: 'Boolean' }, }, classLevelPermissions: defaultClassLevelPermissions, }); @@ -1473,76 +1473,76 @@ describe("schemas", () => { }); }); - it("lets you delete multiple fields and add fields", done => { + it('lets you delete multiple fields and add fields', done => { const obj1 = hasAllPODobject(); obj1.save().then(() => { request({ - url: "http://localhost:8378/1/schemas/HasAllPOD", - method: "PUT", + url: 'http://localhost:8378/1/schemas/HasAllPOD', + method: 'PUT', headers: masterKeyHeaders, json: true, body: { fields: { - aString: { __op: "Delete" }, - aNumber: { __op: "Delete" }, - aNewString: { type: "String" }, - aNewNumber: { type: "Number" }, - aNewRelation: { type: "Relation", targetClass: "HasAllPOD" }, - aNewPointer: { type: "Pointer", targetClass: "HasAllPOD" }, + aString: { __op: 'Delete' }, + aNumber: { __op: 'Delete' }, + aNewString: { type: 'String' }, + aNewNumber: { type: 'Number' }, + aNewRelation: { type: 'Relation', targetClass: 'HasAllPOD' }, + aNewPointer: { type: 'Pointer', targetClass: 'HasAllPOD' }, }, }, }).then(response => { expect(response.data).toEqual({ - className: "HasAllPOD", + className: 'HasAllPOD', fields: { //Default fields - ACL: { type: "ACL" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - objectId: { type: "String" }, + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + objectId: { type: 'String' }, //Custom fields - aBool: { type: "Boolean" }, - aDate: { type: "Date" }, - aObject: { type: "Object" }, - aArray: { type: "Array" }, - aGeoPoint: { type: "GeoPoint" }, - aFile: { type: "File" }, - aNewNumber: { type: "Number" }, - aNewString: { type: "String" }, - aNewPointer: { type: "Pointer", targetClass: "HasAllPOD" }, - aNewRelation: { type: "Relation", targetClass: "HasAllPOD" }, + aBool: { type: 'Boolean' }, + aDate: { type: 'Date' }, + aObject: { type: 'Object' }, + aArray: { type: 'Array' }, + aGeoPoint: { type: 'GeoPoint' }, + aFile: { type: 'File' }, + aNewNumber: { type: 'Number' }, + aNewString: { type: 'String' }, + aNewPointer: { type: 'Pointer', targetClass: 'HasAllPOD' }, + aNewRelation: { type: 'Relation', targetClass: 'HasAllPOD' }, }, classLevelPermissions: defaultClassLevelPermissions, }); - const obj2 = new Parse.Object("HasAllPOD"); - obj2.set("aNewPointer", obj1); - const relation = obj2.relation("aNewRelation"); + const obj2 = new Parse.Object('HasAllPOD'); + obj2.set('aNewPointer', obj1); + const relation = obj2.relation('aNewRelation'); relation.add(obj1); obj2.save().then(done); //Just need to make sure saving works on the new object. }); }); }); - it("will not delete any fields if the additions are invalid", done => { + it('will not delete any fields if the additions are invalid', done => { const obj = hasAllPODobject(); obj.save().then(() => { request({ - url: "http://localhost:8378/1/schemas/HasAllPOD", - method: "PUT", + url: 'http://localhost:8378/1/schemas/HasAllPOD', + method: 'PUT', headers: masterKeyHeaders, json: true, body: { fields: { - fakeNewField: { type: "fake type" }, - aString: { __op: "Delete" }, + fakeNewField: { type: 'fake type' }, + aString: { __op: 'Delete' }, }, }, }).then(fail, response => { expect(response.data.code).toEqual(Parse.Error.INCORRECT_TYPE); - expect(response.data.error).toEqual("invalid field type: fake type"); + expect(response.data.error).toEqual('invalid field type: fake type'); request({ - method: "PUT", - url: "http://localhost:8378/1/schemas/HasAllPOD", + method: 'PUT', + url: 'http://localhost:8378/1/schemas/HasAllPOD', headers: masterKeyHeaders, json: true, }).then(response => { @@ -1553,25 +1553,25 @@ describe("schemas", () => { }); }); - it("requires the master key to delete schemas", done => { + it('requires the master key to delete schemas', done => { request({ - url: "http://localhost:8378/1/schemas/DoesntMatter", - method: "DELETE", + url: 'http://localhost:8378/1/schemas/DoesntMatter', + method: 'DELETE', headers: noAuthHeaders, json: true, }).then(fail, response => { expect(response.status).toEqual(403); - expect(response.data.error).toEqual("unauthorized"); + expect(response.data.error).toEqual('unauthorized'); done(); }); }); - it("refuses to delete non-empty collection", done => { + it('refuses to delete non-empty collection', done => { const obj = hasAllPODobject(); obj.save().then(() => { request({ - url: "http://localhost:8378/1/schemas/HasAllPOD", - method: "DELETE", + url: 'http://localhost:8378/1/schemas/HasAllPOD', + method: 'DELETE', headers: masterKeyHeaders, json: true, }).then(fail, response => { @@ -1584,26 +1584,26 @@ describe("schemas", () => { }); }); - it("fails when deleting collections with invalid class names", done => { + it('fails when deleting collections with invalid class names', done => { request({ - url: "http://localhost:8378/1/schemas/_GlobalConfig", - method: "DELETE", + url: 'http://localhost:8378/1/schemas/_GlobalConfig', + method: 'DELETE', headers: masterKeyHeaders, json: true, }).then(fail, response => { expect(response.status).toEqual(400); expect(response.data.code).toEqual(Parse.Error.INVALID_CLASS_NAME); expect(response.data.error).toEqual( - "Invalid classname: _GlobalConfig, classnames can only have alphanumeric characters and _, and must start with an alpha character " + 'Invalid classname: _GlobalConfig, classnames can only have alphanumeric characters and _, and must start with an alpha character ' ); done(); }); }); - it("does not fail when deleting nonexistant collections", done => { + it('does not fail when deleting nonexistant collections', done => { request({ - url: "http://localhost:8378/1/schemas/Missing", - method: "DELETE", + url: 'http://localhost:8378/1/schemas/Missing', + method: 'DELETE', headers: masterKeyHeaders, json: true, }).then(response => { @@ -1613,30 +1613,30 @@ describe("schemas", () => { }); }); - it("ensure refresh cache after deleting a class", async done => { - config = Config.get("test"); - spyOn(config.schemaCache, "del").and.callFake(() => {}); - spyOn(SchemaController.prototype, "reloadData").and.callFake(() => + it('ensure refresh cache after deleting a class', async done => { + config = Config.get('test'); + spyOn(config.schemaCache, 'del').and.callFake(() => {}); + spyOn(SchemaController.prototype, 'reloadData').and.callFake(() => Promise.resolve() ); await request({ - url: "http://localhost:8378/1/schemas", - method: "POST", + url: 'http://localhost:8378/1/schemas', + method: 'POST', headers: masterKeyHeaders, json: true, body: { - className: "A", + className: 'A', }, }); await request({ - method: "DELETE", - url: "http://localhost:8378/1/schemas/A", + method: 'DELETE', + url: 'http://localhost:8378/1/schemas/A', headers: masterKeyHeaders, json: true, }); const response = await request({ - url: "http://localhost:8378/1/schemas", - method: "GET", + url: 'http://localhost:8378/1/schemas', + method: 'GET', headers: masterKeyHeaders, json: true, }); @@ -1659,45 +1659,45 @@ describe("schemas", () => { done(); }); - it("deletes collections including join tables", done => { - const obj = new Parse.Object("MyClass"); - obj.set("data", "data"); + it('deletes collections including join tables', done => { + const obj = new Parse.Object('MyClass'); + obj.set('data', 'data'); obj .save() .then(() => { - const obj2 = new Parse.Object("MyOtherClass"); - const relation = obj2.relation("aRelation"); + const obj2 = new Parse.Object('MyOtherClass'); + const relation = obj2.relation('aRelation'); relation.add(obj); return obj2.save(); }) .then(obj2 => obj2.destroy()) .then(() => { request({ - url: "http://localhost:8378/1/schemas/MyOtherClass", - method: "DELETE", + url: 'http://localhost:8378/1/schemas/MyOtherClass', + method: 'DELETE', headers: masterKeyHeaders, json: true, }).then(response => { expect(response.status).toEqual(200); expect(response.data).toEqual({}); config.database - .collectionExists("_Join:aRelation:MyOtherClass") + .collectionExists('_Join:aRelation:MyOtherClass') .then(exists => { if (exists) { - fail("Relation collection should be deleted."); + fail('Relation collection should be deleted.'); done(); } - return config.database.collectionExists("MyOtherClass"); + return config.database.collectionExists('MyOtherClass'); }) .then(exists => { if (exists) { - fail("Class collection should be deleted."); + fail('Class collection should be deleted.'); done(); } }) .then(() => { request({ - url: "http://localhost:8378/1/schemas/MyOtherClass", + url: 'http://localhost:8378/1/schemas/MyOtherClass', headers: masterKeyHeaders, json: true, }).then(fail, response => { @@ -1707,7 +1707,7 @@ describe("schemas", () => { Parse.Error.INVALID_CLASS_NAME ); expect(response.data.error).toEqual( - "Class MyOtherClass does not exist." + 'Class MyOtherClass does not exist.' ); done(); }); @@ -1723,27 +1723,27 @@ describe("schemas", () => { ); }); - it("deletes schema when actual collection does not exist", done => { + it('deletes schema when actual collection does not exist', done => { request({ - method: "POST", - url: "http://localhost:8378/1/schemas/NewClassForDelete", + method: 'POST', + url: 'http://localhost:8378/1/schemas/NewClassForDelete', headers: masterKeyHeaders, json: true, body: { - className: "NewClassForDelete", + className: 'NewClassForDelete', }, }).then(response => { - expect(response.data.className).toEqual("NewClassForDelete"); + expect(response.data.className).toEqual('NewClassForDelete'); request({ - url: "http://localhost:8378/1/schemas/NewClassForDelete", - method: "DELETE", + url: 'http://localhost:8378/1/schemas/NewClassForDelete', + method: 'DELETE', headers: masterKeyHeaders, json: true, }).then(response => { expect(response.status).toEqual(200); expect(response.data).toEqual({}); config.database.loadSchema().then(schema => { - schema.hasClass("NewClassForDelete").then(exist => { + schema.hasClass('NewClassForDelete').then(exist => { expect(exist).toEqual(false); done(); }); @@ -1752,42 +1752,42 @@ describe("schemas", () => { }); }); - it("deletes schema when actual collection exists", done => { + it('deletes schema when actual collection exists', done => { request({ - method: "POST", - url: "http://localhost:8378/1/schemas/NewClassForDelete", + method: 'POST', + url: 'http://localhost:8378/1/schemas/NewClassForDelete', headers: masterKeyHeaders, json: true, body: { - className: "NewClassForDelete", + className: 'NewClassForDelete', }, }).then(response => { - expect(response.data.className).toEqual("NewClassForDelete"); + expect(response.data.className).toEqual('NewClassForDelete'); request({ - url: "http://localhost:8378/1/classes/NewClassForDelete", - method: "POST", + url: 'http://localhost:8378/1/classes/NewClassForDelete', + method: 'POST', headers: restKeyHeaders, json: true, }).then(response => { - expect(typeof response.data.objectId).toEqual("string"); + expect(typeof response.data.objectId).toEqual('string'); request({ - method: "DELETE", + method: 'DELETE', url: - "http://localhost:8378/1/classes/NewClassForDelete/" + + 'http://localhost:8378/1/classes/NewClassForDelete/' + response.data.objectId, headers: restKeyHeaders, json: true, }).then(() => { request({ - method: "DELETE", - url: "http://localhost:8378/1/schemas/NewClassForDelete", + method: 'DELETE', + url: 'http://localhost:8378/1/schemas/NewClassForDelete', headers: masterKeyHeaders, json: true, }).then(response => { expect(response.status).toEqual(200); expect(response.data).toEqual({}); config.database.loadSchema().then(schema => { - schema.hasClass("NewClassForDelete").then(exist => { + schema.hasClass('NewClassForDelete').then(exist => { expect(exist).toEqual(false); done(); }); @@ -1798,35 +1798,35 @@ describe("schemas", () => { }); }); - it("should set/get schema permissions", done => { + it('should set/get schema permissions', done => { request({ - method: "POST", - url: "http://localhost:8378/1/schemas/AClass", + method: 'POST', + url: 'http://localhost:8378/1/schemas/AClass', headers: masterKeyHeaders, json: true, body: { classLevelPermissions: { find: { - "*": true, + '*': true, }, create: { - "role:admin": true, + 'role:admin': true, }, }, }, }).then(() => { request({ - url: "http://localhost:8378/1/schemas/AClass", + url: 'http://localhost:8378/1/schemas/AClass', headers: masterKeyHeaders, json: true, }).then(response => { expect(response.status).toEqual(200); expect(response.data.classLevelPermissions).toEqual({ find: { - "*": true, + '*': true, }, create: { - "role:admin": true, + 'role:admin': true, }, get: {}, count: {}, @@ -1840,21 +1840,21 @@ describe("schemas", () => { }); }); - it("should fail setting schema permissions with invalid key", done => { - const object = new Parse.Object("AClass"); + it('should fail setting schema permissions with invalid key', done => { + const object = new Parse.Object('AClass'); object.save().then(() => { request({ - method: "PUT", - url: "http://localhost:8378/1/schemas/AClass", + method: 'PUT', + url: 'http://localhost:8378/1/schemas/AClass', headers: masterKeyHeaders, json: true, body: { classLevelPermissions: { find: { - "*": true, + '*': true, }, create: { - "role:admin": true, + 'role:admin': true, }, dummy: { some: true, @@ -1864,43 +1864,43 @@ describe("schemas", () => { }).then(fail, response => { expect(response.data.code).toEqual(107); expect(response.data.error).toEqual( - "dummy is not a valid operation for class level permissions" + 'dummy is not a valid operation for class level permissions' ); done(); }); }); }); - it("should not be able to add a field", done => { + it('should not be able to add a field', done => { request({ - method: "POST", - url: "http://localhost:8378/1/schemas/AClass", + method: 'POST', + url: 'http://localhost:8378/1/schemas/AClass', headers: masterKeyHeaders, json: true, body: { classLevelPermissions: { create: { - "*": true, + '*': true, }, find: { - "*": true, + '*': true, }, addField: { - "role:admin": true, + 'role:admin': true, }, }, }, }).then(() => { - const object = new Parse.Object("AClass"); - object.set("hello", "world"); + const object = new Parse.Object('AClass'); + object.set('hello', 'world'); return object.save().then( () => { - fail("should not be able to add a field"); + fail('should not be able to add a field'); done(); }, err => { expect(err.message).toEqual( - "Permission denied for action addField on class AClass." + 'Permission denied for action addField on class AClass.' ); done(); } @@ -1908,75 +1908,75 @@ describe("schemas", () => { }); }); - it("should be able to add a field", done => { + it('should be able to add a field', done => { request({ - method: "POST", - url: "http://localhost:8378/1/schemas/AClass", + method: 'POST', + url: 'http://localhost:8378/1/schemas/AClass', headers: masterKeyHeaders, json: true, body: { classLevelPermissions: { create: { - "*": true, + '*': true, }, addField: { - "*": true, + '*': true, }, }, }, }).then(() => { - const object = new Parse.Object("AClass"); - object.set("hello", "world"); + const object = new Parse.Object('AClass'); + object.set('hello', 'world'); return object.save().then( () => { done(); }, () => { - fail("should be able to add a field"); + fail('should be able to add a field'); done(); } ); }); }); - describe("Nested documents", () => { + describe('Nested documents', () => { beforeAll(async () => { - const testSchema = new Parse.Schema("test_7371"); + const testSchema = new Parse.Schema('test_7371'); testSchema.setCLP({ - create: { ["*"]: true }, - update: { ["*"]: true }, + create: { ['*']: true }, + update: { ['*']: true }, addField: {}, }); - testSchema.addObject("a"); + testSchema.addObject('a'); await testSchema.save(); }); - it("addField permission not required for adding a nested property", async () => { - const obj = new Parse.Object("test_7371"); - obj.set("a", {}); + it('addField permission not required for adding a nested property', async () => { + const obj = new Parse.Object('test_7371'); + obj.set('a', {}); await obj.save(); - obj.set("a.b", 2); + obj.set('a.b', 2); await obj.save(); }); - it("addField permission not required for modifying a nested property", async () => { - const obj = new Parse.Object("test_7371"); - obj.set("a", { b: 1 }); + it('addField permission not required for modifying a nested property', async () => { + const obj = new Parse.Object('test_7371'); + obj.set('a', { b: 1 }); await obj.save(); - obj.set("a.b", 2); + obj.set('a.b', 2); await obj.save(); }); }); - it("should aceept class-level permission with userid of any length", async done => { + it('should aceept class-level permission with userid of any length', async done => { await global.reconfigureServer({ customIdSize: 11, }); - const id = "e1evenChars"; + const id = 'e1evenChars'; const { data } = await request({ - method: "POST", - url: "http://localhost:8378/1/schemas/AClass", + method: 'POST', + url: 'http://localhost:8378/1/schemas/AClass', headers: masterKeyHeaders, json: true, body: { @@ -1993,16 +1993,16 @@ describe("schemas", () => { done(); }); - it("should allow set class-level permission for custom userid of any length and chars", async done => { + it('should allow set class-level permission for custom userid of any length and chars', async done => { await global.reconfigureServer({ allowCustomObjectId: true, }); - const symbolsId = "set:ID+symbol$=@llowed"; - const shortId = "1"; + const symbolsId = 'set:ID+symbol$=@llowed'; + const shortId = '1'; const { data } = await request({ - method: "POST", - url: "http://localhost:8378/1/schemas/AClass", + method: 'POST', + url: 'http://localhost:8378/1/schemas/AClass', headers: masterKeyHeaders, json: true, body: { @@ -2021,18 +2021,18 @@ describe("schemas", () => { done(); }); - it("should allow set ACL for custom userid", async done => { + it('should allow set ACL for custom userid', async done => { await global.reconfigureServer({ allowCustomObjectId: true, }); - const symbolsId = "symbols:id@allowed="; - const shortId = "1"; - const normalId = "tensymbols"; + const symbolsId = 'symbols:id@allowed='; + const shortId = '1'; + const normalId = 'tensymbols'; const { data } = await request({ - method: "POST", - url: "http://localhost:8378/1/classes/AClass", + method: 'POST', + url: 'http://localhost:8378/1/classes/AClass', headers: masterKeyHeaders, json: true, body: { @@ -2045,7 +2045,7 @@ describe("schemas", () => { }); const { data: created } = await request({ - method: "GET", + method: 'GET', url: `http://localhost:8378/1/classes/AClass/${data.objectId}`, headers: masterKeyHeaders, json: true, @@ -2057,16 +2057,16 @@ describe("schemas", () => { done(); }); - it("should throw with invalid userId (invalid char)", done => { + it('should throw with invalid userId (invalid char)', done => { request({ - method: "POST", - url: "http://localhost:8378/1/schemas/AClass", + method: 'POST', + url: 'http://localhost:8378/1/schemas/AClass', headers: masterKeyHeaders, json: true, body: { classLevelPermissions: { find: { - "12345_6789": true, + '12345_6789': true, }, }, }, @@ -2078,16 +2078,16 @@ describe("schemas", () => { }); }); - it("should throw with invalid * (spaces before)", done => { + it('should throw with invalid * (spaces before)', done => { request({ - method: "POST", - url: "http://localhost:8378/1/schemas/AClass", + method: 'POST', + url: 'http://localhost:8378/1/schemas/AClass', headers: masterKeyHeaders, json: true, body: { classLevelPermissions: { find: { - " *": true, + ' *': true, }, }, }, @@ -2099,16 +2099,16 @@ describe("schemas", () => { }); }); - it("should throw with invalid * (spaces after)", done => { + it('should throw with invalid * (spaces after)', done => { request({ - method: "POST", - url: "http://localhost:8378/1/schemas/AClass", + method: 'POST', + url: 'http://localhost:8378/1/schemas/AClass', headers: masterKeyHeaders, json: true, body: { classLevelPermissions: { find: { - "* ": true, + '* ': true, }, }, }, @@ -2120,16 +2120,16 @@ describe("schemas", () => { }); }); - it("should throw if permission is number", done => { + it('should throw if permission is number', done => { request({ - method: "POST", - url: "http://localhost:8378/1/schemas/AClass", + method: 'POST', + url: 'http://localhost:8378/1/schemas/AClass', headers: masterKeyHeaders, json: true, body: { classLevelPermissions: { find: { - "*": 1, + '*': 1, }, }, }, @@ -2141,16 +2141,16 @@ describe("schemas", () => { }); }); - it("should validate defaultAcl with class level permissions when request is not an object", async () => { + it('should validate defaultAcl with class level permissions when request is not an object', async () => { const response = await request({ - method: "POST", - url: "http://localhost:8378/1/schemas/AClass", + method: 'POST', + url: 'http://localhost:8378/1/schemas/AClass', headers: masterKeyHeaders, json: true, body: { classLevelPermissions: { ACL: { - "*": true, + '*': true, }, }, }, @@ -2161,16 +2161,16 @@ describe("schemas", () => { ); }); - it("should validate defaultAcl with class level permissions when request is an object and invalid key", async () => { + it('should validate defaultAcl with class level permissions when request is an object and invalid key', async () => { const response = await request({ - method: "POST", - url: "http://localhost:8378/1/schemas/AClass", + method: 'POST', + url: 'http://localhost:8378/1/schemas/AClass', headers: masterKeyHeaders, json: true, body: { classLevelPermissions: { ACL: { - "*": { + '*': { foo: true, }, }, @@ -2183,16 +2183,16 @@ describe("schemas", () => { ); }); - it("should validate defaultAcl with class level permissions when request is an object and invalid value", async () => { + it('should validate defaultAcl with class level permissions when request is an object and invalid value', async () => { const response = await request({ - method: "POST", - url: "http://localhost:8378/1/schemas/AClass", + method: 'POST', + url: 'http://localhost:8378/1/schemas/AClass', headers: masterKeyHeaders, json: true, body: { classLevelPermissions: { ACL: { - "*": { + '*': { read: 1, }, }, @@ -2205,16 +2205,16 @@ describe("schemas", () => { ); }); - it("should throw if permission is empty string", done => { + it('should throw if permission is empty string', done => { request({ - method: "POST", - url: "http://localhost:8378/1/schemas/AClass", + method: 'POST', + url: 'http://localhost:8378/1/schemas/AClass', headers: masterKeyHeaders, json: true, body: { classLevelPermissions: { find: { - "*": "", + '*': '', }, }, }, @@ -2228,8 +2228,8 @@ describe("schemas", () => { function setPermissionsOnClass(className, permissions, doPut) { return request({ - url: "http://localhost:8378/1/schemas/" + className, - method: doPut ? "PUT" : "POST", + url: 'http://localhost:8378/1/schemas/' + className, + method: doPut ? 'PUT' : 'POST', headers: masterKeyHeaders, json: true, body: { @@ -2243,20 +2243,20 @@ describe("schemas", () => { }); } - it("validate CLP 1", done => { + it('validate CLP 1', done => { const user = new Parse.User(); - user.setUsername("user"); - user.setPassword("user"); + user.setUsername('user'); + user.setPassword('user'); const admin = new Parse.User(); - admin.setUsername("admin"); - admin.setPassword("admin"); + admin.setUsername('admin'); + admin.setPassword('admin'); - const role = new Parse.Role("admin", new Parse.ACL()); + const role = new Parse.Role('admin', new Parse.ACL()); - setPermissionsOnClass("AClass", { + setPermissionsOnClass('AClass', { find: { - "role:admin": true, + 'role:admin': true, }, }) .then(() => { @@ -2265,34 +2265,34 @@ describe("schemas", () => { }); }) .then(() => { - role.relation("users").add(admin); + role.relation('users').add(admin); return role.save(null, { useMasterKey: true }); }) .then(() => { - return Parse.User.logIn("user", "user").then(() => { - const obj = new Parse.Object("AClass"); + return Parse.User.logIn('user', 'user').then(() => { + const obj = new Parse.Object('AClass'); return obj.save(null, { useMasterKey: true }); }); }) .then(() => { - const query = new Parse.Query("AClass"); + const query = new Parse.Query('AClass'); return query.find().then( () => { - fail("Use should hot be able to find!"); + fail('Use should hot be able to find!'); }, err => { expect(err.message).toEqual( - "Permission denied for action find on class AClass." + 'Permission denied for action find on class AClass.' ); return Promise.resolve(); } ); }) .then(() => { - return Parse.User.logIn("admin", "admin"); + return Parse.User.logIn('admin', 'admin'); }) .then(() => { - const query = new Parse.Query("AClass"); + const query = new Parse.Query('AClass'); return query.find(); }) .then(results => { @@ -2305,20 +2305,20 @@ describe("schemas", () => { }); }); - it("validate CLP 2", done => { + it('validate CLP 2', done => { const user = new Parse.User(); - user.setUsername("user"); - user.setPassword("user"); + user.setUsername('user'); + user.setPassword('user'); const admin = new Parse.User(); - admin.setUsername("admin"); - admin.setPassword("admin"); + admin.setUsername('admin'); + admin.setPassword('admin'); - const role = new Parse.Role("admin", new Parse.ACL()); + const role = new Parse.Role('admin', new Parse.ACL()); - setPermissionsOnClass("AClass", { + setPermissionsOnClass('AClass', { find: { - "role:admin": true, + 'role:admin': true, }, }) .then(() => { @@ -2327,24 +2327,24 @@ describe("schemas", () => { }); }) .then(() => { - role.relation("users").add(admin); + role.relation('users').add(admin); return role.save(null, { useMasterKey: true }); }) .then(() => { - return Parse.User.logIn("user", "user").then(() => { - const obj = new Parse.Object("AClass"); + return Parse.User.logIn('user', 'user').then(() => { + const obj = new Parse.Object('AClass'); return obj.save(null, { useMasterKey: true }); }); }) .then(() => { - const query = new Parse.Query("AClass"); + const query = new Parse.Query('AClass'); return query.find().then( () => { - fail("User should not be able to find!"); + fail('User should not be able to find!'); }, err => { expect(err.message).toEqual( - "Permission denied for action find on class AClass." + 'Permission denied for action find on class AClass.' ); return Promise.resolve(); } @@ -2353,33 +2353,33 @@ describe("schemas", () => { .then(() => { // let everyone see it now return setPermissionsOnClass( - "AClass", + 'AClass', { find: { - "role:admin": true, - "*": true, + 'role:admin': true, + '*': true, }, }, true ); }) .then(() => { - const query = new Parse.Query("AClass"); + const query = new Parse.Query('AClass'); return query.find().then( result => { expect(result.length).toBe(1); }, () => { - fail("User should be able to find!"); + fail('User should be able to find!'); done(); } ); }) .then(() => { - return Parse.User.logIn("admin", "admin"); + return Parse.User.logIn('admin', 'admin'); }) .then(() => { - const query = new Parse.Query("AClass"); + const query = new Parse.Query('AClass'); return query.find(); }) .then(results => { @@ -2392,20 +2392,20 @@ describe("schemas", () => { }); }); - it("validate CLP 3", done => { + it('validate CLP 3', done => { const user = new Parse.User(); - user.setUsername("user"); - user.setPassword("user"); + user.setUsername('user'); + user.setPassword('user'); const admin = new Parse.User(); - admin.setUsername("admin"); - admin.setPassword("admin"); + admin.setUsername('admin'); + admin.setPassword('admin'); - const role = new Parse.Role("admin", new Parse.ACL()); + const role = new Parse.Role('admin', new Parse.ACL()); - setPermissionsOnClass("AClass", { + setPermissionsOnClass('AClass', { find: { - "role:admin": true, + 'role:admin': true, }, }) .then(() => { @@ -2414,24 +2414,24 @@ describe("schemas", () => { }); }) .then(() => { - role.relation("users").add(admin); + role.relation('users').add(admin); return role.save(null, { useMasterKey: true }); }) .then(() => { - return Parse.User.logIn("user", "user").then(() => { - const obj = new Parse.Object("AClass"); + return Parse.User.logIn('user', 'user').then(() => { + const obj = new Parse.Object('AClass'); return obj.save(null, { useMasterKey: true }); }); }) .then(() => { - const query = new Parse.Query("AClass"); + const query = new Parse.Query('AClass'); return query.find().then( () => { - fail("User should not be able to find!"); + fail('User should not be able to find!'); }, err => { expect(err.message).toEqual( - "Permission denied for action find on class AClass." + 'Permission denied for action find on class AClass.' ); return Promise.resolve(); } @@ -2439,25 +2439,25 @@ describe("schemas", () => { }) .then(() => { // delete all CLP - return setPermissionsOnClass("AClass", null, true); + return setPermissionsOnClass('AClass', null, true); }) .then(() => { - const query = new Parse.Query("AClass"); + const query = new Parse.Query('AClass'); return query.find().then( result => { expect(result.length).toBe(1); }, () => { - fail("User should be able to find!"); + fail('User should be able to find!'); done(); } ); }) .then(() => { - return Parse.User.logIn("admin", "admin"); + return Parse.User.logIn('admin', 'admin'); }) .then(() => { - const query = new Parse.Query("AClass"); + const query = new Parse.Query('AClass'); return query.find(); }) .then(results => { @@ -2470,20 +2470,20 @@ describe("schemas", () => { }); }); - it("validate CLP 4", done => { + it('validate CLP 4', done => { const user = new Parse.User(); - user.setUsername("user"); - user.setPassword("user"); + user.setUsername('user'); + user.setPassword('user'); const admin = new Parse.User(); - admin.setUsername("admin"); - admin.setPassword("admin"); + admin.setUsername('admin'); + admin.setPassword('admin'); - const role = new Parse.Role("admin", new Parse.ACL()); + const role = new Parse.Role('admin', new Parse.ACL()); - setPermissionsOnClass("AClass", { + setPermissionsOnClass('AClass', { find: { - "role:admin": true, + 'role:admin': true, }, }) .then(() => { @@ -2492,24 +2492,24 @@ describe("schemas", () => { }); }) .then(() => { - role.relation("users").add(admin); + role.relation('users').add(admin); return role.save(null, { useMasterKey: true }); }) .then(() => { - return Parse.User.logIn("user", "user").then(() => { - const obj = new Parse.Object("AClass"); + return Parse.User.logIn('user', 'user').then(() => { + const obj = new Parse.Object('AClass'); return obj.save(null, { useMasterKey: true }); }); }) .then(() => { - const query = new Parse.Query("AClass"); + const query = new Parse.Query('AClass'); return query.find().then( () => { - fail("User should not be able to find!"); + fail('User should not be able to find!'); }, err => { expect(err.message).toEqual( - "Permission denied for action find on class AClass." + 'Permission denied for action find on class AClass.' ); return Promise.resolve(); } @@ -2518,16 +2518,16 @@ describe("schemas", () => { .then(() => { // borked CLP should not affec security return setPermissionsOnClass( - "AClass", + 'AClass', { found: { - "role:admin": true, + 'role:admin': true, }, }, true ).then( () => { - fail("Should not be able to save a borked CLP"); + fail('Should not be able to save a borked CLP'); }, () => { return Promise.resolve(); @@ -2535,24 +2535,24 @@ describe("schemas", () => { ); }) .then(() => { - const query = new Parse.Query("AClass"); + const query = new Parse.Query('AClass'); return query.find().then( () => { - fail("User should not be able to find!"); + fail('User should not be able to find!'); }, err => { expect(err.message).toEqual( - "Permission denied for action find on class AClass." + 'Permission denied for action find on class AClass.' ); return Promise.resolve(); } ); }) .then(() => { - return Parse.User.logIn("admin", "admin"); + return Parse.User.logIn('admin', 'admin'); }) .then(() => { - const query = new Parse.Query("AClass"); + const query = new Parse.Query('AClass'); return query.find(); }) .then(results => { @@ -2565,19 +2565,19 @@ describe("schemas", () => { }); }); - it("validate CLP 5", done => { + it('validate CLP 5', done => { const user = new Parse.User(); - user.setUsername("user"); - user.setPassword("user"); + user.setUsername('user'); + user.setPassword('user'); const user2 = new Parse.User(); - user2.setUsername("user2"); - user2.setPassword("user2"); + user2.setUsername('user2'); + user2.setPassword('user2'); const admin = new Parse.User(); - admin.setUsername("admin"); - admin.setPassword("admin"); + admin.setUsername('admin'); + admin.setPassword('admin'); - const role = new Parse.Role("admin", new Parse.ACL()); + const role = new Parse.Role('admin', new Parse.ACL()); Promise.resolve() .then(() => { @@ -2586,68 +2586,68 @@ describe("schemas", () => { }); }) .then(() => { - role.relation("users").add(admin); + role.relation('users').add(admin); return role.save(null, { useMasterKey: true }).then(() => { const perm = { find: {}, }; // let the user find - perm["find"][user.id] = true; - return setPermissionsOnClass("AClass", perm); + perm['find'][user.id] = true; + return setPermissionsOnClass('AClass', perm); }); }) .then(() => { - return Parse.User.logIn("user", "user").then(() => { - const obj = new Parse.Object("AClass"); + return Parse.User.logIn('user', 'user').then(() => { + const obj = new Parse.Object('AClass'); return obj.save(); }); }) .then(() => { - const query = new Parse.Query("AClass"); + const query = new Parse.Query('AClass'); return query.find().then( res => { expect(res.length).toEqual(1); }, () => { - fail("User should be able to find!"); + fail('User should be able to find!'); return Promise.resolve(); } ); }) .then(() => { - return Parse.User.logIn("admin", "admin"); + return Parse.User.logIn('admin', 'admin'); }) .then(() => { - const query = new Parse.Query("AClass"); + const query = new Parse.Query('AClass'); return query.find(); }) .then( () => { - fail("should not be able to read!"); + fail('should not be able to read!'); return Promise.resolve(); }, err => { expect(err.message).toEqual( - "Permission denied for action create on class AClass." + 'Permission denied for action create on class AClass.' ); return Promise.resolve(); } ) .then(() => { - return Parse.User.logIn("user2", "user2"); + return Parse.User.logIn('user2', 'user2'); }) .then(() => { - const query = new Parse.Query("AClass"); + const query = new Parse.Query('AClass'); return query.find(); }) .then( () => { - fail("should not be able to read!"); + fail('should not be able to read!'); return Promise.resolve(); }, err => { expect(err.message).toEqual( - "Permission denied for action find on class AClass." + 'Permission denied for action find on class AClass.' ); return Promise.resolve(); } @@ -2657,30 +2657,30 @@ describe("schemas", () => { }); }); - it("can query with include and CLP (issue #2005)", done => { - setPermissionsOnClass("AnotherObject", { - get: { "*": true }, + it('can query with include and CLP (issue #2005)', done => { + setPermissionsOnClass('AnotherObject', { + get: { '*': true }, find: {}, - create: { "*": true }, - update: { "*": true }, - delete: { "*": true }, - addField: { "*": true }, + create: { '*': true }, + update: { '*': true }, + delete: { '*': true }, + addField: { '*': true }, }) .then(() => { - const obj = new Parse.Object("AnObject"); - const anotherObject = new Parse.Object("AnotherObject"); + const obj = new Parse.Object('AnObject'); + const anotherObject = new Parse.Object('AnotherObject'); return obj.save({ anotherObject, }); }) .then(() => { - const query = new Parse.Query("AnObject"); - query.include("anotherObject"); + const query = new Parse.Query('AnObject'); + query.include('anotherObject'); return query.find(); }) .then(res => { expect(res.length).toBe(1); - expect(res[0].get("anotherObject")).not.toBeUndefined(); + expect(res[0].get('anotherObject')).not.toBeUndefined(); done(); }) .catch(err => { @@ -2689,42 +2689,42 @@ describe("schemas", () => { }); }); - it("can add field as master (issue #1257)", done => { - setPermissionsOnClass("AClass", { + it('can add field as master (issue #1257)', done => { + setPermissionsOnClass('AClass', { addField: {}, }) .then(() => { - const obj = new Parse.Object("AClass"); - obj.set("key", "value"); + const obj = new Parse.Object('AClass'); + obj.set('key', 'value'); return obj.save(null, { useMasterKey: true }); }) .then( obj => { - expect(obj.get("key")).toEqual("value"); + expect(obj.get('key')).toEqual('value'); done(); }, () => { - fail("should not fail"); + fail('should not fail'); done(); } ); }); - it("can login when addFields is false (issue #1355)", done => { + it('can login when addFields is false (issue #1355)', done => { setPermissionsOnClass( - "_User", + '_User', { - create: { "*": true }, + create: { '*': true }, addField: {}, }, true ) .then(() => { - return Parse.User.signUp("foo", "bar"); + return Parse.User.signUp('foo', 'bar'); }) .then( user => { - expect(user.getUsername()).toBe("foo"); + expect(user.getUsername()).toBe('foo'); done(); }, error => { @@ -2734,59 +2734,59 @@ describe("schemas", () => { ); }); - it("unset field in beforeSave should not stop object creation", done => { + it('unset field in beforeSave should not stop object creation', done => { const hook = { method: function (req) { - if (req.object.get("undesiredField")) { - req.object.unset("undesiredField"); + if (req.object.get('undesiredField')) { + req.object.unset('undesiredField'); } }, }; - spyOn(hook, "method").and.callThrough(); - Parse.Cloud.beforeSave("AnObject", hook.method); - setPermissionsOnClass("AnObject", { - get: { "*": true }, - find: { "*": true }, - create: { "*": true }, - update: { "*": true }, - delete: { "*": true }, + spyOn(hook, 'method').and.callThrough(); + Parse.Cloud.beforeSave('AnObject', hook.method); + setPermissionsOnClass('AnObject', { + get: { '*': true }, + find: { '*': true }, + create: { '*': true }, + update: { '*': true }, + delete: { '*': true }, addField: {}, }) .then(() => { - const obj = new Parse.Object("AnObject"); - obj.set("desiredField", "createMe"); + const obj = new Parse.Object('AnObject'); + obj.set('desiredField', 'createMe'); return obj.save(null, { useMasterKey: true }); }) .then(() => { - const obj = new Parse.Object("AnObject"); - obj.set("desiredField", "This value should be kept"); - obj.set("undesiredField", "This value should be IGNORED"); + const obj = new Parse.Object('AnObject'); + obj.set('desiredField', 'This value should be kept'); + obj.set('undesiredField', 'This value should be IGNORED'); return obj.save(); }) .then(() => { - const query = new Parse.Query("AnObject"); + const query = new Parse.Query('AnObject'); return query.find(); }) .then(results => { expect(results.length).toBe(2); - expect(results[0].has("desiredField")).toBe(true); - expect(results[1].has("desiredField")).toBe(true); - expect(results[0].has("undesiredField")).toBe(false); - expect(results[1].has("undesiredField")).toBe(false); + expect(results[0].has('desiredField')).toBe(true); + expect(results[1].has('desiredField')).toBe(true); + expect(results[0].has('undesiredField')).toBe(false); + expect(results[1].has('undesiredField')).toBe(false); expect(hook.method).toHaveBeenCalled(); done(); }); }); - it("gives correct response when deleting a schema with CLPs (regression test #1919)", done => { - new Parse.Object("MyClass") - .save({ data: "foo" }) + it('gives correct response when deleting a schema with CLPs (regression test #1919)', done => { + new Parse.Object('MyClass') + .save({ data: 'foo' }) .then(obj => obj.destroy()) - .then(() => setPermissionsOnClass("MyClass", { find: {}, get: {} }, true)) + .then(() => setPermissionsOnClass('MyClass', { find: {}, get: {} }, true)) .then(() => { request({ - method: "DELETE", - url: "http://localhost:8378/1/schemas/MyClass", + method: 'DELETE', + url: 'http://localhost:8378/1/schemas/MyClass', headers: masterKeyHeaders, json: true, }).then(response => { @@ -2797,37 +2797,37 @@ describe("schemas", () => { }); }); - it("regression test for #1991", done => { + it('regression test for #1991', done => { const user = new Parse.User(); - user.setUsername("user"); - user.setPassword("user"); - const role = new Parse.Role("admin", new Parse.ACL()); - const obj = new Parse.Object("AnObject"); + user.setUsername('user'); + user.setPassword('user'); + const role = new Parse.Role('admin', new Parse.ACL()); + const obj = new Parse.Object('AnObject'); Parse.Object.saveAll([user, role]) .then(() => { - role.relation("users").add(user); + role.relation('users').add(user); return role.save(null, { useMasterKey: true }); }) .then(() => { - return setPermissionsOnClass("AnObject", { - get: { "*": true }, - find: { "*": true }, - create: { "*": true }, - update: { "role:admin": true }, - delete: { "role:admin": true }, + return setPermissionsOnClass('AnObject', { + get: { '*': true }, + find: { '*': true }, + create: { '*': true }, + update: { 'role:admin': true }, + delete: { 'role:admin': true }, }); }) .then(() => { return obj.save(); }) .then(() => { - return Parse.User.logIn("user", "user"); + return Parse.User.logIn('user', 'user'); }) .then(() => { return obj.destroy(); }) .then(() => { - const query = new Parse.Query("AnObject"); + const query = new Parse.Query('AnObject'); return query.find(); }) .then(results => { @@ -2835,36 +2835,36 @@ describe("schemas", () => { done(); }) .catch(err => { - fail("should not fail"); + fail('should not fail'); jfail(err); done(); }); }); - it("regression test for #4409 (indexes override the clp)", done => { + it('regression test for #4409 (indexes override the clp)', done => { setPermissionsOnClass( - "_Role", + '_Role', { ACL: { - "*": { + '*': { read: true, write: true, }, }, - get: { "*": true }, - find: { "*": true }, - count: { "*": true }, - create: { "*": true }, + get: { '*': true }, + find: { '*': true }, + count: { '*': true }, + create: { '*': true }, }, true ) .then(() => { - const config = Config.get("test"); + const config = Config.get('test'); return config.database.adapter.updateSchemaWithIndexes(); }) .then(() => { return request({ - url: "http://localhost:8378/1/schemas/_Role", + url: 'http://localhost:8378/1/schemas/_Role', headers: masterKeyHeaders, json: true, }); @@ -2872,15 +2872,15 @@ describe("schemas", () => { .then(res => { expect(res.data.classLevelPermissions).toEqual({ ACL: { - "*": { + '*': { read: true, write: true, }, }, - get: { "*": true }, - find: { "*": true }, - count: { "*": true }, - create: { "*": true }, + get: { '*': true }, + find: { '*': true }, + count: { '*': true }, + create: { '*': true }, update: {}, delete: {}, addField: {}, @@ -2891,31 +2891,31 @@ describe("schemas", () => { .catch(done.fail); }); - it("regression test for #5177", async () => { + it('regression test for #5177', async () => { Parse.Object.disableSingleInstance(); - Parse.Cloud.beforeSave("AClass", () => {}); + Parse.Cloud.beforeSave('AClass', () => {}); await setPermissionsOnClass( - "AClass", + 'AClass', { - update: { "*": true }, + update: { '*': true }, }, false ); - const obj = new Parse.Object("AClass"); + const obj = new Parse.Object('AClass'); await obj.save({ key: 1 }, { useMasterKey: true }); - obj.increment("key", 10); + obj.increment('key', 10); const objectAgain = await obj.save(); - expect(objectAgain.get("key")).toBe(11); + expect(objectAgain.get('key')).toBe(11); }); - it("regression test for #2246", done => { - const profile = new Parse.Object("UserProfile"); + it('regression test for #2246', done => { + const profile = new Parse.Object('UserProfile'); const user = new Parse.User(); function initialize() { return user .save({ - username: "user", - password: "password", + username: 'user', + password: 'password', }) .then(() => { return profile.save({ user }).then(() => { @@ -2932,25 +2932,25 @@ describe("schemas", () => { initialize() .then(() => { return setPermissionsOnClass( - "UserProfile", + 'UserProfile', { - readUserFields: ["user"], - writeUserFields: ["user"], + readUserFields: ['user'], + writeUserFields: ['user'], }, true ); }) .then(() => { - return Parse.User.logIn("user", "password"); + return Parse.User.logIn('user', 'password'); }) .then(() => { - const query = new Parse.Query("_User"); - query.include("userProfile"); + const query = new Parse.Query('_User'); + query.include('userProfile'); return query.get(user.id); }) .then( user => { - expect(user.get("userProfile")).not.toBeUndefined(); + expect(user.get('userProfile')).not.toBeUndefined(); done(); }, err => { @@ -2960,15 +2960,15 @@ describe("schemas", () => { ); }); - it("should reject creating class schema with field with invalid key", async done => { + it('should reject creating class schema with field with invalid key', async done => { const config = Config.get(Parse.applicationId); const schemaController = await config.database.loadSchema(); - const fieldName = "1invalid"; + const fieldName = '1invalid'; const schemaCreation = () => - schemaController.addClassIfNotExists("AnObject", { - [fieldName]: { __type: "String" }, + schemaController.addClassIfNotExists('AnObject', { + [fieldName]: { __type: 'String' }, }); await expectAsync(schemaCreation()).toBeRejectedWith( @@ -2980,32 +2980,32 @@ describe("schemas", () => { done(); }); - it("should reject creating invalid field name", async done => { - const object = new Parse.Object("AnObject"); + it('should reject creating invalid field name', async done => { + const object = new Parse.Object('AnObject'); await expectAsync( object.save({ - "!12field": "field", + '!12field': 'field', }) ).toBeRejectedWith( new Parse.Error( Parse.Error.INVALID_KEY_NAME, - "Invalid key name: !12field" + 'Invalid key name: !12field' ) ); done(); }); - it("should be rejected if CLP operation is not an object", async done => { + it('should be rejected if CLP operation is not an object', async done => { const config = Config.get(Parse.applicationId); const schemaController = await config.database.loadSchema(); - const operationKey = "get"; + const operationKey = 'get'; const operation = true; const schemaSetup = async () => await schemaController.addClassIfNotExists( - "AnObject", + 'AnObject', {}, { [operationKey]: operation, @@ -3022,16 +3022,16 @@ describe("schemas", () => { done(); }); - it("should be rejected if CLP protectedFields is not an object", async done => { + it('should be rejected if CLP protectedFields is not an object', async done => { const config = Config.get(Parse.applicationId); const schemaController = await config.database.loadSchema(); - const operationKey = "get"; - const operation = "wrongtype"; + const operationKey = 'get'; + const operation = 'wrongtype'; const schemaSetup = async () => await schemaController.addClassIfNotExists( - "AnObject", + 'AnObject', {}, { [operationKey]: operation, @@ -3048,16 +3048,16 @@ describe("schemas", () => { done(); }); - it("should be rejected if CLP read/writeUserFields is not an array", async done => { + it('should be rejected if CLP read/writeUserFields is not an array', async done => { const config = Config.get(Parse.applicationId); const schemaController = await config.database.loadSchema(); - const operationKey = "readUserFields"; + const operationKey = 'readUserFields'; const operation = true; const schemaSetup = async () => await schemaController.addClassIfNotExists( - "AnObject", + 'AnObject', {}, { [operationKey]: operation, @@ -3074,17 +3074,17 @@ describe("schemas", () => { done(); }); - it("should be rejected if CLP pointerFields is not an array", async done => { + it('should be rejected if CLP pointerFields is not an array', async done => { const config = Config.get(Parse.applicationId); const schemaController = await config.database.loadSchema(); - const operationKey = "get"; - const entity = "pointerFields"; + const operationKey = 'get'; + const entity = 'pointerFields'; const value = {}; const schemaSetup = async () => await schemaController.addClassIfNotExists( - "AnObject", + 'AnObject', {}, { [operationKey]: { @@ -3103,7 +3103,7 @@ describe("schemas", () => { done(); }); - describe("index management", () => { + describe('index management', () => { beforeEach(async () => { await TestUtils.destroyAllDataPermanently(false); await config.database.adapter.performInitialization({ @@ -3111,17 +3111,17 @@ describe("schemas", () => { }); }); - it("cannot create index if field does not exist", done => { + it('cannot create index if field does not exist', done => { request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "POST", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'POST', headers: masterKeyHeaders, json: true, body: {}, }).then(() => { request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "PUT", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'PUT', headers: masterKeyHeaders, json: true, body: { @@ -3132,24 +3132,24 @@ describe("schemas", () => { }).then(fail, response => { expect(response.data.code).toBe(Parse.Error.INVALID_QUERY); expect(response.data.error).toBe( - "Field aString does not exist, cannot add index." + 'Field aString does not exist, cannot add index.' ); done(); }); }); }); - it("can create index on default field", done => { + it('can create index on default field', done => { request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "POST", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'POST', headers: masterKeyHeaders, json: true, body: {}, }).then(() => { request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "PUT", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'PUT', headers: masterKeyHeaders, json: true, body: { @@ -3164,22 +3164,22 @@ describe("schemas", () => { }); }); - it("cannot create compound index if field does not exist", done => { + it('cannot create compound index if field does not exist', done => { request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "POST", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'POST', headers: masterKeyHeaders, json: true, body: {}, }).then(() => { request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "PUT", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'PUT', headers: masterKeyHeaders, json: true, body: { fields: { - aString: { type: "String" }, + aString: { type: 'String' }, }, indexes: { name1: { aString: 1, bString: 1 }, @@ -3188,23 +3188,23 @@ describe("schemas", () => { }).then(fail, response => { expect(response.data.code).toBe(Parse.Error.INVALID_QUERY); expect(response.data.error).toBe( - "Field bString does not exist, cannot add index." + 'Field bString does not exist, cannot add index.' ); done(); }); }); }); - it("allows add index when you create a class", done => { + it('allows add index when you create a class', done => { request({ - url: "http://localhost:8378/1/schemas", - method: "POST", + url: 'http://localhost:8378/1/schemas', + method: 'POST', headers: masterKeyHeaders, json: true, body: { - className: "NewClass", + className: 'NewClass', fields: { - aString: { type: "String" }, + aString: { type: 'String' }, }, indexes: { name1: { aString: 1 }, @@ -3212,48 +3212,48 @@ describe("schemas", () => { }, }).then(response => { expect(response.data).toEqual({ - className: "NewClass", + className: 'NewClass', fields: { - ACL: { type: "ACL" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - objectId: { type: "String" }, - aString: { type: "String" }, + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + objectId: { type: 'String' }, + aString: { type: 'String' }, }, classLevelPermissions: defaultClassLevelPermissions, indexes: { name1: { aString: 1 }, }, }); - config.database.adapter.getIndexes("NewClass").then(indexes => { + config.database.adapter.getIndexes('NewClass').then(indexes => { expect(indexes.length).toBe(2); done(); }); }); }); - it("empty index returns nothing", done => { + it('empty index returns nothing', done => { request({ - url: "http://localhost:8378/1/schemas", - method: "POST", + url: 'http://localhost:8378/1/schemas', + method: 'POST', headers: masterKeyHeaders, json: true, body: { - className: "NewClass", + className: 'NewClass', fields: { - aString: { type: "String" }, + aString: { type: 'String' }, }, indexes: {}, }, }).then(response => { expect(response.data).toEqual({ - className: "NewClass", + className: 'NewClass', fields: { - ACL: { type: "ACL" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - objectId: { type: "String" }, - aString: { type: "String" }, + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + objectId: { type: 'String' }, + aString: { type: 'String' }, }, classLevelPermissions: defaultClassLevelPermissions, }); @@ -3261,22 +3261,22 @@ describe("schemas", () => { }); }); - it("lets you add indexes", done => { + it('lets you add indexes', done => { request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "POST", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'POST', headers: masterKeyHeaders, json: true, body: {}, }).then(() => { request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "PUT", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'PUT', headers: masterKeyHeaders, json: true, body: { fields: { - aString: { type: "String" }, + aString: { type: 'String' }, }, indexes: { name1: { aString: 1 }, @@ -3285,13 +3285,13 @@ describe("schemas", () => { }).then(response => { expect( dd(response.data, { - className: "NewClass", + className: 'NewClass', fields: { - ACL: { type: "ACL" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - objectId: { type: "String" }, - aString: { type: "String" }, + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + objectId: { type: 'String' }, + aString: { type: 'String' }, }, classLevelPermissions: defaultClassLevelPermissions, indexes: { @@ -3301,18 +3301,18 @@ describe("schemas", () => { }) ).toEqual(undefined); request({ - url: "http://localhost:8378/1/schemas/NewClass", + url: 'http://localhost:8378/1/schemas/NewClass', headers: masterKeyHeaders, json: true, }).then(response => { expect(response.data).toEqual({ - className: "NewClass", + className: 'NewClass', fields: { - ACL: { type: "ACL" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - objectId: { type: "String" }, - aString: { type: "String" }, + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + objectId: { type: 'String' }, + aString: { type: 'String' }, }, classLevelPermissions: defaultClassLevelPermissions, indexes: { @@ -3320,7 +3320,7 @@ describe("schemas", () => { name1: { aString: 1 }, }, }); - config.database.adapter.getIndexes("NewClass").then(indexes => { + config.database.adapter.getIndexes('NewClass').then(indexes => { expect(indexes.length).toEqual(2); done(); }); @@ -3329,24 +3329,24 @@ describe("schemas", () => { }); }); - it_only_db("mongo")( - "lets you add index with with pointer like structure", + it_only_db('mongo')( + 'lets you add index with with pointer like structure', done => { request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "POST", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'POST', headers: masterKeyHeaders, json: true, body: {}, }).then(() => { request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "PUT", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'PUT', headers: masterKeyHeaders, json: true, body: { fields: { - aPointer: { type: "Pointer", targetClass: "NewClass" }, + aPointer: { type: 'Pointer', targetClass: 'NewClass' }, }, indexes: { pointer: { _p_aPointer: 1 }, @@ -3355,13 +3355,13 @@ describe("schemas", () => { }).then(response => { expect( dd(response.data, { - className: "NewClass", + className: 'NewClass', fields: { - ACL: { type: "ACL" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - objectId: { type: "String" }, - aPointer: { type: "Pointer", targetClass: "NewClass" }, + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + objectId: { type: 'String' }, + aPointer: { type: 'Pointer', targetClass: 'NewClass' }, }, classLevelPermissions: defaultClassLevelPermissions, indexes: { @@ -3371,18 +3371,18 @@ describe("schemas", () => { }) ).toEqual(undefined); request({ - url: "http://localhost:8378/1/schemas/NewClass", + url: 'http://localhost:8378/1/schemas/NewClass', headers: masterKeyHeaders, json: true, }).then(response => { expect(response.data).toEqual({ - className: "NewClass", + className: 'NewClass', fields: { - ACL: { type: "ACL" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - objectId: { type: "String" }, - aPointer: { type: "Pointer", targetClass: "NewClass" }, + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + objectId: { type: 'String' }, + aPointer: { type: 'Pointer', targetClass: 'NewClass' }, }, classLevelPermissions: defaultClassLevelPermissions, indexes: { @@ -3390,7 +3390,7 @@ describe("schemas", () => { pointer: { _p_aPointer: 1 }, }, }); - config.database.adapter.getIndexes("NewClass").then(indexes => { + config.database.adapter.getIndexes('NewClass').then(indexes => { expect(indexes.length).toEqual(2); done(); }); @@ -3400,25 +3400,25 @@ describe("schemas", () => { } ); - it("lets you add multiple indexes", done => { + it('lets you add multiple indexes', done => { request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "POST", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'POST', headers: masterKeyHeaders, json: true, body: {}, }).then(() => { request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "PUT", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'PUT', headers: masterKeyHeaders, json: true, body: { fields: { - aString: { type: "String" }, - bString: { type: "String" }, - cString: { type: "String" }, - dString: { type: "String" }, + aString: { type: 'String' }, + bString: { type: 'String' }, + cString: { type: 'String' }, + dString: { type: 'String' }, }, indexes: { name1: { aString: 1 }, @@ -3429,16 +3429,16 @@ describe("schemas", () => { }).then(response => { expect( dd(response.data, { - className: "NewClass", + className: 'NewClass', fields: { - ACL: { type: "ACL" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - objectId: { type: "String" }, - aString: { type: "String" }, - bString: { type: "String" }, - cString: { type: "String" }, - dString: { type: "String" }, + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + objectId: { type: 'String' }, + aString: { type: 'String' }, + bString: { type: 'String' }, + cString: { type: 'String' }, + dString: { type: 'String' }, }, classLevelPermissions: defaultClassLevelPermissions, indexes: { @@ -3450,21 +3450,21 @@ describe("schemas", () => { }) ).toEqual(undefined); request({ - url: "http://localhost:8378/1/schemas/NewClass", + url: 'http://localhost:8378/1/schemas/NewClass', headers: masterKeyHeaders, json: true, }).then(response => { expect(response.data).toEqual({ - className: "NewClass", + className: 'NewClass', fields: { - ACL: { type: "ACL" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - objectId: { type: "String" }, - aString: { type: "String" }, - bString: { type: "String" }, - cString: { type: "String" }, - dString: { type: "String" }, + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + objectId: { type: 'String' }, + aString: { type: 'String' }, + bString: { type: 'String' }, + cString: { type: 'String' }, + dString: { type: 'String' }, }, classLevelPermissions: defaultClassLevelPermissions, indexes: { @@ -3474,7 +3474,7 @@ describe("schemas", () => { name3: { cString: 1, dString: 1 }, }, }); - config.database.adapter.getIndexes("NewClass").then(indexes => { + config.database.adapter.getIndexes('NewClass').then(indexes => { expect(indexes.length).toEqual(4); done(); }); @@ -3483,22 +3483,22 @@ describe("schemas", () => { }); }); - it("lets you delete indexes", done => { + it('lets you delete indexes', done => { request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "POST", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'POST', headers: masterKeyHeaders, json: true, body: {}, }).then(() => { request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "PUT", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'PUT', headers: masterKeyHeaders, json: true, body: { fields: { - aString: { type: "String" }, + aString: { type: 'String' }, }, indexes: { name1: { aString: 1 }, @@ -3507,13 +3507,13 @@ describe("schemas", () => { }).then(response => { expect( dd(response.data, { - className: "NewClass", + className: 'NewClass', fields: { - ACL: { type: "ACL" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - objectId: { type: "String" }, - aString: { type: "String" }, + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + objectId: { type: 'String' }, + aString: { type: 'String' }, }, classLevelPermissions: defaultClassLevelPermissions, indexes: { @@ -3523,31 +3523,31 @@ describe("schemas", () => { }) ).toEqual(undefined); request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "PUT", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'PUT', headers: masterKeyHeaders, json: true, body: { indexes: { - name1: { __op: "Delete" }, + name1: { __op: 'Delete' }, }, }, }).then(response => { expect(response.data).toEqual({ - className: "NewClass", + className: 'NewClass', fields: { - ACL: { type: "ACL" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - objectId: { type: "String" }, - aString: { type: "String" }, + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + objectId: { type: 'String' }, + aString: { type: 'String' }, }, classLevelPermissions: defaultClassLevelPermissions, indexes: { _id_: { _id: 1 }, }, }); - config.database.adapter.getIndexes("NewClass").then(indexes => { + config.database.adapter.getIndexes('NewClass').then(indexes => { expect(indexes.length).toEqual(1); done(); }); @@ -3556,24 +3556,24 @@ describe("schemas", () => { }); }); - it("lets you delete multiple indexes", done => { + it('lets you delete multiple indexes', done => { request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "POST", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'POST', headers: masterKeyHeaders, json: true, body: {}, }).then(() => { request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "PUT", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'PUT', headers: masterKeyHeaders, json: true, body: { fields: { - aString: { type: "String" }, - bString: { type: "String" }, - cString: { type: "String" }, + aString: { type: 'String' }, + bString: { type: 'String' }, + cString: { type: 'String' }, }, indexes: { name1: { aString: 1 }, @@ -3584,15 +3584,15 @@ describe("schemas", () => { }).then(response => { expect( dd(response.data, { - className: "NewClass", + className: 'NewClass', fields: { - ACL: { type: "ACL" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - objectId: { type: "String" }, - aString: { type: "String" }, - bString: { type: "String" }, - cString: { type: "String" }, + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + objectId: { type: 'String' }, + aString: { type: 'String' }, + bString: { type: 'String' }, + cString: { type: 'String' }, }, classLevelPermissions: defaultClassLevelPermissions, indexes: { @@ -3604,27 +3604,27 @@ describe("schemas", () => { }) ).toEqual(undefined); request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "PUT", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'PUT', headers: masterKeyHeaders, json: true, body: { indexes: { - name1: { __op: "Delete" }, - name2: { __op: "Delete" }, + name1: { __op: 'Delete' }, + name2: { __op: 'Delete' }, }, }, }).then(response => { expect(response.data).toEqual({ - className: "NewClass", + className: 'NewClass', fields: { - ACL: { type: "ACL" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - objectId: { type: "String" }, - aString: { type: "String" }, - bString: { type: "String" }, - cString: { type: "String" }, + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + objectId: { type: 'String' }, + aString: { type: 'String' }, + bString: { type: 'String' }, + cString: { type: 'String' }, }, classLevelPermissions: defaultClassLevelPermissions, indexes: { @@ -3632,7 +3632,7 @@ describe("schemas", () => { name3: { cString: 1 }, }, }); - config.database.adapter.getIndexes("NewClass").then(indexes => { + config.database.adapter.getIndexes('NewClass').then(indexes => { expect(indexes.length).toEqual(2); done(); }); @@ -3641,29 +3641,29 @@ describe("schemas", () => { }); }); - it("lets you add and delete indexes", async () => { + it('lets you add and delete indexes', async () => { // Wait due to index building in MongoDB on background process with collection lock const waitForIndexBuild = new Promise(r => setTimeout(r, 500)); await request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "POST", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'POST', headers: masterKeyHeaders, json: true, body: {}, }); let response = await request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "PUT", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'PUT', headers: masterKeyHeaders, json: true, body: { fields: { - aString: { type: "String" }, - bString: { type: "String" }, - cString: { type: "String" }, - dString: { type: "String" }, + aString: { type: 'String' }, + bString: { type: 'String' }, + cString: { type: 'String' }, + dString: { type: 'String' }, }, indexes: { name1: { aString: 1 }, @@ -3675,16 +3675,16 @@ describe("schemas", () => { expect( dd(response.data, { - className: "NewClass", + className: 'NewClass', fields: { - ACL: { type: "ACL" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - objectId: { type: "String" }, - aString: { type: "String" }, - bString: { type: "String" }, - cString: { type: "String" }, - dString: { type: "String" }, + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + objectId: { type: 'String' }, + aString: { type: 'String' }, + bString: { type: 'String' }, + cString: { type: 'String' }, + dString: { type: 'String' }, }, classLevelPermissions: defaultClassLevelPermissions, indexes: { @@ -3698,29 +3698,29 @@ describe("schemas", () => { await waitForIndexBuild; response = await request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "PUT", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'PUT', headers: masterKeyHeaders, json: true, body: { indexes: { - name1: { __op: "Delete" }, - name2: { __op: "Delete" }, + name1: { __op: 'Delete' }, + name2: { __op: 'Delete' }, }, }, }); expect(response.data).toEqual({ - className: "NewClass", + className: 'NewClass', fields: { - ACL: { type: "ACL" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - objectId: { type: "String" }, - aString: { type: "String" }, - bString: { type: "String" }, - cString: { type: "String" }, - dString: { type: "String" }, + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + objectId: { type: 'String' }, + aString: { type: 'String' }, + bString: { type: 'String' }, + cString: { type: 'String' }, + dString: { type: 'String' }, }, classLevelPermissions: defaultClassLevelPermissions, indexes: { @@ -3731,8 +3731,8 @@ describe("schemas", () => { await waitForIndexBuild; response = await request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "PUT", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'PUT', headers: masterKeyHeaders, json: true, body: { @@ -3743,16 +3743,16 @@ describe("schemas", () => { }); expect(response.data).toEqual({ - className: "NewClass", + className: 'NewClass', fields: { - ACL: { type: "ACL" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - objectId: { type: "String" }, - aString: { type: "String" }, - bString: { type: "String" }, - cString: { type: "String" }, - dString: { type: "String" }, + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + objectId: { type: 'String' }, + aString: { type: 'String' }, + bString: { type: 'String' }, + cString: { type: 'String' }, + dString: { type: 'String' }, }, classLevelPermissions: defaultClassLevelPermissions, indexes: { @@ -3763,54 +3763,54 @@ describe("schemas", () => { }); await waitForIndexBuild; - const indexes = await config.database.adapter.getIndexes("NewClass"); + const indexes = await config.database.adapter.getIndexes('NewClass'); expect(indexes.length).toEqual(3); }); - it("cannot delete index that does not exist", done => { + it('cannot delete index that does not exist', done => { request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "POST", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'POST', headers: masterKeyHeaders, json: true, body: {}, }).then(() => { request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "PUT", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'PUT', headers: masterKeyHeaders, json: true, body: { indexes: { - unknownIndex: { __op: "Delete" }, + unknownIndex: { __op: 'Delete' }, }, }, }).then(fail, response => { expect(response.data.code).toBe(Parse.Error.INVALID_QUERY); expect(response.data.error).toBe( - "Index unknownIndex does not exist, cannot delete." + 'Index unknownIndex does not exist, cannot delete.' ); done(); }); }); }); - it("cannot update index that exist", done => { + it('cannot update index that exist', done => { request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "POST", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'POST', headers: masterKeyHeaders, json: true, body: {}, }).then(() => { request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "PUT", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'PUT', headers: masterKeyHeaders, json: true, body: { fields: { - aString: { type: "String" }, + aString: { type: 'String' }, }, indexes: { name1: { aString: 1 }, @@ -3818,8 +3818,8 @@ describe("schemas", () => { }, }).then(() => { request({ - url: "http://localhost:8378/1/schemas/NewClass", - method: "PUT", + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'PUT', headers: masterKeyHeaders, json: true, body: { @@ -3830,7 +3830,7 @@ describe("schemas", () => { }).then(fail, response => { expect(response.data.code).toBe(Parse.Error.INVALID_QUERY); expect(response.data.error).toBe( - "Index name1 exists, cannot update." + 'Index name1 exists, cannot update.' ); done(); }); @@ -3838,22 +3838,22 @@ describe("schemas", () => { }); }); - it_id("5d0926b2-2d31-459d-a2b1-23ecc32e72a3")(it_exclude_dbs(["postgres"]))( - "get indexes on startup", + it_id('5d0926b2-2d31-459d-a2b1-23ecc32e72a3')(it_exclude_dbs(['postgres']))( + 'get indexes on startup', done => { - const obj = new Parse.Object("TestObject"); + const obj = new Parse.Object('TestObject'); obj .save() .then(() => { return reconfigureServer({ - appId: "test", - restAPIKey: "test", - publicServerURL: "http://localhost:8378/1", + appId: 'test', + restAPIKey: 'test', + publicServerURL: 'http://localhost:8378/1', }); }) .then(() => { request({ - url: "http://localhost:8378/1/schemas/TestObject", + url: 'http://localhost:8378/1/schemas/TestObject', headers: masterKeyHeaders, json: true, }).then(response => { @@ -3864,30 +3864,30 @@ describe("schemas", () => { } ); - it_id("9f2ba51a-6a9c-4b25-9da0-51c82ac65f90")(it_exclude_dbs(["postgres"]))( - "get compound indexes on startup", + it_id('9f2ba51a-6a9c-4b25-9da0-51c82ac65f90')(it_exclude_dbs(['postgres']))( + 'get compound indexes on startup', done => { - const obj = new Parse.Object("TestObject"); - obj.set("subject", "subject"); - obj.set("comment", "comment"); + const obj = new Parse.Object('TestObject'); + obj.set('subject', 'subject'); + obj.set('comment', 'comment'); obj .save() .then(() => { - return config.database.adapter.createIndex("TestObject", { - subject: "text", - comment: "text", + return config.database.adapter.createIndex('TestObject', { + subject: 'text', + comment: 'text', }); }) .then(() => { return reconfigureServer({ - appId: "test", - restAPIKey: "test", - publicServerURL: "http://localhost:8378/1", + appId: 'test', + restAPIKey: 'test', + publicServerURL: 'http://localhost:8378/1', }); }) .then(() => { request({ - url: "http://localhost:8378/1/schemas/TestObject", + url: 'http://localhost:8378/1/schemas/TestObject', headers: masterKeyHeaders, json: true, }).then(response => { @@ -3898,29 +3898,29 @@ describe("schemas", () => { ).toBeDefined(); expect( response.data.indexes.subject_text_comment_text.subject - ).toEqual("text"); + ).toEqual('text'); expect( response.data.indexes.subject_text_comment_text.comment - ).toEqual("text"); + ).toEqual('text'); done(); }); }); } ); - it_id("cbd5d897-b938-43a4-8f5a-5d02dd2be9be")(it_exclude_dbs(["postgres"]))( - "cannot update to duplicate value on unique index", + it_id('cbd5d897-b938-43a4-8f5a-5d02dd2be9be')(it_exclude_dbs(['postgres']))( + 'cannot update to duplicate value on unique index', done => { const index = { code: 1, }; - const obj1 = new Parse.Object("UniqueIndexClass"); - obj1.set("code", 1); - const obj2 = new Parse.Object("UniqueIndexClass"); - obj2.set("code", 2); + const obj1 = new Parse.Object('UniqueIndexClass'); + obj1.set('code', 1); + const obj2 = new Parse.Object('UniqueIndexClass'); + obj2.set('code', 2); const adapter = config.database.adapter; adapter - ._adaptiveCollection("UniqueIndexClass") + ._adaptiveCollection('UniqueIndexClass') .then(collection => { return collection._ensureSparseUniqueIndexInBackground(index); }) @@ -3931,7 +3931,7 @@ describe("schemas", () => { return obj2.save(); }) .then(() => { - obj1.set("code", 2); + obj1.set('code', 2); return obj1.save(); }) .then(done.fail) diff --git a/spec/support/CurrentSpecReporter.js b/spec/support/CurrentSpecReporter.js index 1ae756568a..144cd3ec19 100755 --- a/spec/support/CurrentSpecReporter.js +++ b/spec/support/CurrentSpecReporter.js @@ -1,6 +1,6 @@ // Sets a global variable to the current test spec // ex: global.currentSpec.description -const { performance } = require("perf_hooks"); +const { performance } = require('perf_hooks'); global.currentSpec = null; @@ -11,9 +11,9 @@ global.currentSpec = null; */ const flakyTests = [ // Timeout - "ParseLiveQuery handle invalid websocket payload length", + 'ParseLiveQuery handle invalid websocket payload length', // Unhandled promise rejection: TypeError: message.split is not a function - "rest query query internal field", + 'rest query query internal field', ]; /** The minimum execution time in seconds for a test to be considered slow. */ @@ -28,14 +28,14 @@ const duplicates = []; class CurrentSpecReporter { specStarted(spec) { if (timerMap[spec.fullName]) { - console.log("Duplicate spec: " + spec.fullName); + console.log('Duplicate spec: ' + spec.fullName); duplicates.push(spec.fullName); } timerMap[spec.fullName] = performance.now(); global.currentSpec = spec; } specDone(result) { - if (result.status === "excluded") { + if (result.status === 'excluded') { delete timerMap[result.fullName]; return; } @@ -58,15 +58,15 @@ global.displayTestStats = function () { Object.keys(timerMap).find(key => timerMap[key] === time) ); }); - console.log("\n"); + console.log('\n'); duplicates.forEach(spec => { - console.warn("Duplicate spec: " + spec); + console.warn('Duplicate spec: ' + spec); }); - console.log("\n"); + console.log('\n'); Object.keys(retryMap).forEach(spec => { console.warn(`Flaky test: ${spec} failed ${retryMap[spec]} times`); }); - console.log("\n"); + console.log('\n'); }; global.retryFlakyTests = function () { diff --git a/spec/support/CustomAuth.js b/spec/support/CustomAuth.js index 80a527803e..f6698e5b03 100644 --- a/spec/support/CustomAuth.js +++ b/spec/support/CustomAuth.js @@ -3,7 +3,7 @@ module.exports = { return Promise.resolve(); }, validateAuthData: function (authData) { - if (authData.token == "my-token") { + if (authData.token == 'my-token') { return Promise.resolve(); } return Promise.reject(); diff --git a/spec/support/CustomMiddleware.js b/spec/support/CustomMiddleware.js index bfef31c586..97e71bd67b 100644 --- a/spec/support/CustomMiddleware.js +++ b/spec/support/CustomMiddleware.js @@ -1,4 +1,4 @@ module.exports = function (req, res, next) { - res.set("X-Yolo", "1"); + res.set('X-Yolo', '1'); next(); }; diff --git a/spec/support/FailingServer.js b/spec/support/FailingServer.js index 579644468d..5fc0c90e81 100755 --- a/spec/support/FailingServer.js +++ b/spec/support/FailingServer.js @@ -1,20 +1,20 @@ #!/usr/bin/env node const MongoStorageAdapter = - require("../../lib/Adapters/Storage/Mongo/MongoStorageAdapter").default; + require('../../lib/Adapters/Storage/Mongo/MongoStorageAdapter').default; const { GridFSBucketAdapter, -} = require("../../lib/Adapters/Files/GridFSBucketAdapter"); +} = require('../../lib/Adapters/Files/GridFSBucketAdapter'); -const ParseServer = require("../../lib/index").ParseServer; +const ParseServer = require('../../lib/index').ParseServer; const databaseURI = - "mongodb://doesnotexist:27017/parseServerMongoAdapterTestDatabase"; + 'mongodb://doesnotexist:27017/parseServerMongoAdapterTestDatabase'; (async () => { try { await ParseServer.startApp({ - appId: "test", - masterKey: "test", + appId: 'test', + masterKey: 'test', databaseAdapter: new MongoStorageAdapter({ uri: databaseURI, mongoOptions: { diff --git a/spec/support/MockEmailAdapterWithOptions.js b/spec/support/MockEmailAdapterWithOptions.js index 0d24d91b28..71d23892ef 100644 --- a/spec/support/MockEmailAdapterWithOptions.js +++ b/spec/support/MockEmailAdapterWithOptions.js @@ -1,6 +1,6 @@ module.exports = options => { if (!options) { - throw "Options were not provided"; + throw 'Options were not provided'; } const adapter = { sendVerificationEmail: () => Promise.resolve(), diff --git a/spec/support/MockLdapServer.js b/spec/support/MockLdapServer.js index 05924ce8e9..270a9603cc 100644 --- a/spec/support/MockLdapServer.js +++ b/spec/support/MockLdapServer.js @@ -1,23 +1,23 @@ -const ldapjs = require("ldapjs"); -const fs = require("fs"); +const ldapjs = require('ldapjs'); +const fs = require('fs'); const tlsOptions = { - key: fs.readFileSync(__dirname + "/cert/key.pem"), - certificate: fs.readFileSync(__dirname + "/cert/cert.pem"), + key: fs.readFileSync(__dirname + '/cert/key.pem'), + certificate: fs.readFileSync(__dirname + '/cert/cert.pem'), }; function newServer(port, dn, provokeSearchError = false, ssl = false) { const server = ssl ? ldapjs.createServer(tlsOptions) : ldapjs.createServer(); - server.bind("o=example", function (req, res, next) { - if (req.dn.toString() !== dn || req.credentials !== "secret") { + server.bind('o=example', function (req, res, next) { + if (req.dn.toString() !== dn || req.credentials !== 'secret') { return next(new ldapjs.InvalidCredentialsError()); } res.end(); return next(); }); - server.search("o=example", function (req, res, next) { + server.search('o=example', function (req, res, next) { if (provokeSearchError) { res.end(ldapjs.LDAP_SIZE_LIMIT_EXCEEDED); return next(); @@ -25,18 +25,18 @@ function newServer(port, dn, provokeSearchError = false, ssl = false) { const obj = { dn: req.dn.toString(), attributes: { - objectclass: ["organization", "top"], - o: "example", + objectclass: ['organization', 'top'], + o: 'example', }, }; const group = { dn: req.dn.toString(), attributes: { - objectClass: ["groupOfUniqueNames", "top"], - uniqueMember: ["uid=testuser, o=example"], - cn: "powerusers", - ou: "powerusers", + objectClass: ['groupOfUniqueNames', 'top'], + uniqueMember: ['uid=testuser, o=example'], + cn: 'powerusers', + ou: 'powerusers', }, }; diff --git a/spec/support/dev.js b/spec/support/dev.js index a70c757cf9..3978dc2e9b 100644 --- a/spec/support/dev.js +++ b/spec/support/dev.js @@ -1,8 +1,8 @@ -const Config = require("../../lib/Config"); -const Parse = require("parse/node"); +const Config = require('../../lib/Config'); +const Parse = require('parse/node'); -const className = "AnObject"; -const defaultRoleName = "tester"; +const className = 'AnObject'; +const defaultRoleName = 'tester'; module.exports = { /* AnObject */ @@ -15,7 +15,7 @@ module.exports = { * @param {string} username - username base, will be postfixed with current time in millis; * @param {string} [password='password'] - optional, defaults to "password" if not set; */ - createUser: async (username, password = "password") => { + createUser: async (username, password = 'password') => { const user = new Parse.User({ username: username + Date.now(), password, @@ -34,7 +34,7 @@ module.exports = { logIn: async (userObject, password) => { return await Parse.User.logIn( userObject.getUsername(), - password || "password" + password || 'password' ); }, @@ -73,19 +73,19 @@ module.exports = { acl.setPublicReadAccess(read); acl.setPublicWriteAccess(write); - const role = new Parse.Object("_Role"); + const role = new Parse.Object('_Role'); role.setACL(acl); // generate name based on roleName or use exactName (if botth not provided name is generated) const name = roleName ? roleName + Date.now() : exactName; - role.set("name", name); + role.set('name', name); if (roles) { - role.relation("roles").add(roles); + role.relation('roles').add(roles); } if (users) { - role.relation("users").add(users); + role.relation('users').add(users); } await role.save({ useMasterKey: true }); diff --git a/spec/support/myoauth.js b/spec/support/myoauth.js index b14e9db385..2367ad62ce 100644 --- a/spec/support/myoauth.js +++ b/spec/support/myoauth.js @@ -2,7 +2,7 @@ // Returns a promise that fulfills iff this user id is valid. function validateAuthData(authData) { - if (authData.id == "12345" && authData.access_token == "12345") { + if (authData.id == '12345' && authData.access_token == '12345') { return Promise.resolve(); } return Promise.reject(); diff --git a/spec/vulnerabilities.spec.js b/spec/vulnerabilities.spec.js index 1b8c6ae7d7..f01b03b95a 100644 --- a/spec/vulnerabilities.spec.js +++ b/spec/vulnerabilities.spec.js @@ -1,7 +1,7 @@ -const request = require("../lib/request"); +const request = require('../lib/request'); -describe("Vulnerabilities", () => { - describe("(GHSA-8xq9-g7ch-35hg) Custom object ID allows to acquire role privilege", () => { +describe('Vulnerabilities', () => { + describe('(GHSA-8xq9-g7ch-35hg) Custom object ID allows to acquire role privilege', () => { beforeAll(async () => { await reconfigureServer({ allowCustomObjectId: true }); Parse.allowCustomObjectId = true; @@ -12,15 +12,15 @@ describe("Vulnerabilities", () => { Parse.allowCustomObjectId = false; }); - it("denies user creation with poisoned object ID", async () => { + it('denies user creation with poisoned object ID', async () => { await expectAsync( - new Parse.User({ id: "role:a", username: "a", password: "123" }).save() + new Parse.User({ id: 'role:a', username: 'a', password: '123' }).save() ).toBeRejectedWith( - new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, "Invalid object ID.") + new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Invalid object ID.') ); }); - describe("existing sessions for users with poisoned object ID", () => { + describe('existing sessions for users with poisoned object ID', () => { /** @type {Parse.User} */ let poisonedUser; /** @type {Parse.User} */ @@ -30,16 +30,16 @@ describe("Vulnerabilities", () => { const parseServer = await global.reconfigureServer(); const databaseController = parseServer.config.databaseController; [poisonedUser, innocentUser] = await Promise.all( - ["role:abc", "abc"].map(async id => { + ['role:abc', 'abc'].map(async id => { // Create the users directly on the db to bypass the user creation check - await databaseController.create("_User", { objectId: id }); + await databaseController.create('_User', { objectId: id }); // Use the master key to create a session for them to bypass the session check return Parse.User.loginAs(id); }) ); }); - it("refuses session token of user with poisoned object ID", async () => { + it('refuses session token of user with poisoned object ID', async () => { await expectAsync( new Parse.Query(Parse.User).find({ sessionToken: poisonedUser.getSessionToken(), @@ -47,7 +47,7 @@ describe("Vulnerabilities", () => { ).toBeRejectedWith( new Parse.Error( Parse.Error.INTERNAL_SERVER_ERROR, - "Invalid object ID." + 'Invalid object ID.' ) ); await new Parse.Query(Parse.User).find({ @@ -57,17 +57,17 @@ describe("Vulnerabilities", () => { }); }); - describe("Object prototype pollution", () => { + describe('Object prototype pollution', () => { it('denies object prototype to be polluted with keyword "constructor"', async () => { const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; const response = await request({ headers: headers, - method: "POST", - url: "http://localhost:8378/1/classes/PP", + method: 'POST', + url: 'http://localhost:8378/1/classes/PP', body: JSON.stringify({ obj: { constructor: { @@ -89,25 +89,25 @@ describe("Vulnerabilities", () => { it('denies object prototype to be polluted with keypath string "constructor"', async () => { const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; const objResponse = await request({ headers: headers, - method: "POST", - url: "http://localhost:8378/1/classes/PP", + method: 'POST', + url: 'http://localhost:8378/1/classes/PP', body: JSON.stringify({ obj: {}, }), }).catch(e => e); const pollResponse = await request({ headers: headers, - method: "PUT", + method: 'PUT', url: `http://localhost:8378/1/classes/PP/${objResponse.data.objectId}`, body: JSON.stringify({ - "obj.constructor.prototype.dummy": { - __op: "Increment", + 'obj.constructor.prototype.dummy': { + __op: 'Increment', amount: 1, }, }), @@ -124,15 +124,15 @@ describe("Vulnerabilities", () => { it('denies object prototype to be polluted with keyword "__proto__"', async () => { const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; const response = await request({ headers: headers, - method: "POST", - url: "http://localhost:8378/1/classes/PP", - body: JSON.stringify({ "obj.__proto__.dummy": 0 }), + method: 'POST', + url: 'http://localhost:8378/1/classes/PP', + body: JSON.stringify({ 'obj.__proto__.dummy': 0 }), }).catch(e => e); expect(response.status).toBe(400); const text = JSON.parse(response.text); @@ -144,21 +144,21 @@ describe("Vulnerabilities", () => { }); }); - describe("Request denylist", () => { - it("denies BSON type code data in write request by default", async () => { + describe('Request denylist', () => { + it('denies BSON type code data in write request by default', async () => { const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; const params = { headers: headers, - method: "POST", - url: "http://localhost:8378/1/classes/RCE", + method: 'POST', + url: 'http://localhost:8378/1/classes/RCE', body: JSON.stringify({ obj: { - _bsontype: "Code", - code: "delete Object.prototype.evalFunctions", + _bsontype: 'Code', + code: 'delete Object.prototype.evalFunctions', }, }), }; @@ -171,12 +171,12 @@ describe("Vulnerabilities", () => { ); }); - it("denies expanding existing object with polluted keys", async () => { - const obj = await new Parse.Object("RCE", { a: { foo: [] } }).save(); + it('denies expanding existing object with polluted keys', async () => { + const obj = await new Parse.Object('RCE', { a: { foo: [] } }).save(); await reconfigureServer({ - requestKeywordDenylist: ["foo"], + requestKeywordDenylist: ['foo'], }); - obj.addUnique("a.foo", "abc"); + obj.addUnique('a.foo', 'abc'); await expectAsync(obj.save()).toBeRejectedWith( new Parse.Error( Parse.Error.INVALID_KEY_NAME, @@ -185,9 +185,9 @@ describe("Vulnerabilities", () => { ); }); - it("denies creating a cloud trigger with polluted data", async () => { - Parse.Cloud.beforeSave("TestObject", ({ object }) => { - object.set("obj", { + it('denies creating a cloud trigger with polluted data', async () => { + Parse.Cloud.beforeSave('TestObject', ({ object }) => { + object.set('obj', { constructor: { prototype: { dummy: 0, @@ -195,7 +195,7 @@ describe("Vulnerabilities", () => { }, }); }); - await expectAsync(new Parse.Object("TestObject").save()).toBeRejectedWith( + await expectAsync(new Parse.Object('TestObject').save()).toBeRejectedWith( new Parse.Error( Parse.Error.INVALID_KEY_NAME, 'Prohibited keyword in request data: {"key":"constructor"}.' @@ -203,20 +203,20 @@ describe("Vulnerabilities", () => { ); }); - it("denies creating global config with polluted data", async () => { + it('denies creating global config with polluted data', async () => { const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-Master-Key": "test", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-Master-Key': 'test', }; const params = { - method: "PUT", - url: "http://localhost:8378/1/config", + method: 'PUT', + url: 'http://localhost:8378/1/config', json: true, body: { params: { - welcomeMesssage: "Welcome to Parse", - foo: { _bsontype: "Code", code: "shell" }, + welcomeMesssage: 'Welcome to Parse', + foo: { _bsontype: 'Code', code: 'shell' }, }, }, headers, @@ -230,17 +230,17 @@ describe("Vulnerabilities", () => { ); }); - it("denies direct database write wih prohibited keys", async () => { - const Config = require("../lib/Config"); + it('denies direct database write wih prohibited keys', async () => { + const Config = require('../lib/Config'); const config = Config.get(Parse.applicationId); const user = { - objectId: "1234567890", - username: "hello", - password: "pass", - _session_token: "abc", - foo: { _bsontype: "Code", code: "shell" }, + objectId: '1234567890', + username: 'hello', + password: 'pass', + _session_token: 'abc', + foo: { _bsontype: 'Code', code: 'shell' }, }; - await expectAsync(config.database.create("_User", user)).toBeRejectedWith( + await expectAsync(config.database.create('_User', user)).toBeRejectedWith( new Parse.Error( Parse.Error.INVALID_KEY_NAME, 'Prohibited keyword in request data: {"key":"_bsontype","value":"Code"}.' @@ -248,18 +248,18 @@ describe("Vulnerabilities", () => { ); }); - it("denies direct database update wih prohibited keys", async () => { - const Config = require("../lib/Config"); + it('denies direct database update wih prohibited keys', async () => { + const Config = require('../lib/Config'); const config = Config.get(Parse.applicationId); const user = { - objectId: "1234567890", - username: "hello", - password: "pass", - _session_token: "abc", - foo: { _bsontype: "Code", code: "shell" }, + objectId: '1234567890', + username: 'hello', + password: 'pass', + _session_token: 'abc', + foo: { _bsontype: 'Code', code: 'shell' }, }; await expectAsync( - config.database.update("_User", { _id: user.objectId }, user) + config.database.update('_User', { _id: user.objectId }, user) ).toBeRejectedWith( new Parse.Error( Parse.Error.INVALID_KEY_NAME, @@ -268,21 +268,21 @@ describe("Vulnerabilities", () => { ); }); - it_id("e8b5f1e1-8326-4c70-b5f4-1e8678dfff8d")(it)( - "denies creating a hook with polluted data", + it_id('e8b5f1e1-8326-4c70-b5f4-1e8678dfff8d')(it)( + 'denies creating a hook with polluted data', async () => { - const express = require("express"); + const express = require('express'); const port = 34567; - const hookServerURL = "http://localhost:" + port; + const hookServerURL = 'http://localhost:' + port; const app = express(); - app.use(express.json({ type: "*/*" })); + app.use(express.json({ type: '*/*' })); const server = await new Promise(resolve => { const res = app.listen(port, undefined, () => resolve(res)); }); - app.post("/BeforeSave", function (req, res) { + app.post('/BeforeSave', function (req, res) { const object = Parse.Object.fromJSON(req.body.object); - object.set("hello", "world"); - object.set("obj", { + object.set('hello', 'world'); + object.set('obj', { constructor: { prototype: { dummy: 0, @@ -292,12 +292,12 @@ describe("Vulnerabilities", () => { res.json({ success: object }); }); await Parse.Hooks.createTrigger( - "TestObject", - "beforeSave", - hookServerURL + "/BeforeSave" + 'TestObject', + 'beforeSave', + hookServerURL + '/BeforeSave' ); await expectAsync( - new Parse.Object("TestObject").save() + new Parse.Object('TestObject').save() ).toBeRejectedWith( new Parse.Error( Parse.Error.INVALID_KEY_NAME, @@ -308,23 +308,23 @@ describe("Vulnerabilities", () => { } ); - it("denies write request with custom denylist of key/value", async () => { + it('denies write request with custom denylist of key/value', async () => { await reconfigureServer({ - requestKeywordDenylist: [{ key: "a[K]ey", value: "aValue[123]*" }], + requestKeywordDenylist: [{ key: 'a[K]ey', value: 'aValue[123]*' }], }); const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; const params = { headers: headers, - method: "POST", - url: "http://localhost:8378/1/classes/RCE", + method: 'POST', + url: 'http://localhost:8378/1/classes/RCE', body: JSON.stringify({ obj: { - aKey: "aValue321", - code: "delete Object.prototype.evalFunctions", + aKey: 'aValue321', + code: 'delete Object.prototype.evalFunctions', }, }), }; @@ -337,24 +337,24 @@ describe("Vulnerabilities", () => { ); }); - it("denies write request with custom denylist of nested key/value", async () => { + it('denies write request with custom denylist of nested key/value', async () => { await reconfigureServer({ - requestKeywordDenylist: [{ key: "a[K]ey", value: "aValue[123]*" }], + requestKeywordDenylist: [{ key: 'a[K]ey', value: 'aValue[123]*' }], }); const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; const params = { headers: headers, - method: "POST", - url: "http://localhost:8378/1/classes/RCE", + method: 'POST', + url: 'http://localhost:8378/1/classes/RCE', body: JSON.stringify({ obj: { nested: { - aKey: "aValue321", - code: "delete Object.prototype.evalFunctions", + aKey: 'aValue321', + code: 'delete Object.prototype.evalFunctions', }, }, }), @@ -368,24 +368,24 @@ describe("Vulnerabilities", () => { ); }); - it("denies write request with custom denylist of key/value in array", async () => { + it('denies write request with custom denylist of key/value in array', async () => { await reconfigureServer({ - requestKeywordDenylist: [{ key: "a[K]ey", value: "aValue[123]*" }], + requestKeywordDenylist: [{ key: 'a[K]ey', value: 'aValue[123]*' }], }); const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; const params = { headers: headers, - method: "POST", - url: "http://localhost:8378/1/classes/RCE", + method: 'POST', + url: 'http://localhost:8378/1/classes/RCE', body: JSON.stringify({ obj: [ { - aKey: "aValue321", - code: "delete Object.prototype.evalFunctions", + aKey: 'aValue321', + code: 'delete Object.prototype.evalFunctions', }, ], }), @@ -399,23 +399,23 @@ describe("Vulnerabilities", () => { ); }); - it("denies write request with custom denylist of key", async () => { + it('denies write request with custom denylist of key', async () => { await reconfigureServer({ - requestKeywordDenylist: [{ key: "a[K]ey" }], + requestKeywordDenylist: [{ key: 'a[K]ey' }], }); const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; const params = { headers: headers, - method: "POST", - url: "http://localhost:8378/1/classes/RCE", + method: 'POST', + url: 'http://localhost:8378/1/classes/RCE', body: JSON.stringify({ obj: { - aKey: "aValue321", - code: "delete Object.prototype.evalFunctions", + aKey: 'aValue321', + code: 'delete Object.prototype.evalFunctions', }, }), }; @@ -428,23 +428,23 @@ describe("Vulnerabilities", () => { ); }); - it("denies write request with custom denylist of value", async () => { + it('denies write request with custom denylist of value', async () => { await reconfigureServer({ - requestKeywordDenylist: [{ value: "aValue[123]*" }], + requestKeywordDenylist: [{ value: 'aValue[123]*' }], }); const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }; const params = { headers: headers, - method: "POST", - url: "http://localhost:8378/1/classes/RCE", + method: 'POST', + url: 'http://localhost:8378/1/classes/RCE', body: JSON.stringify({ obj: { - aKey: "aValue321", - code: "delete Object.prototype.evalFunctions", + aKey: 'aValue321', + code: 'delete Object.prototype.evalFunctions', }, }), }; @@ -457,16 +457,16 @@ describe("Vulnerabilities", () => { ); }); - it("denies BSON type code data in file metadata", async () => { - const str = "Hello World!"; + it('denies BSON type code data in file metadata', async () => { + const str = 'Hello World!'; const data = []; for (let i = 0; i < str.length; i++) { data.push(str.charCodeAt(i)); } - const file = new Parse.File("hello.txt", data, "text/plain"); - file.addMetadata("obj", { - _bsontype: "Code", - code: "delete Object.prototype.evalFunctions", + const file = new Parse.File('hello.txt', data, 'text/plain'); + file.addMetadata('obj', { + _bsontype: 'Code', + code: 'delete Object.prototype.evalFunctions', }); await expectAsync(file.save()).toBeRejectedWith( new Parse.Error( @@ -476,16 +476,16 @@ describe("Vulnerabilities", () => { ); }); - it("denies BSON type code data in file tags", async () => { - const str = "Hello World!"; + it('denies BSON type code data in file tags', async () => { + const str = 'Hello World!'; const data = []; for (let i = 0; i < str.length; i++) { data.push(str.charCodeAt(i)); } - const file = new Parse.File("hello.txt", data, "text/plain"); - file.addTag("obj", { - _bsontype: "Code", - code: "delete Object.prototype.evalFunctions", + const file = new Parse.File('hello.txt', data, 'text/plain'); + file.addTag('obj', { + _bsontype: 'Code', + code: 'delete Object.prototype.evalFunctions', }); await expectAsync(file.save()).toBeRejectedWith( new Parse.Error( @@ -496,36 +496,36 @@ describe("Vulnerabilities", () => { }); }); - describe("Ignore non-matches", () => { - it("ignores write request that contains only fraction of denied keyword", async () => { + describe('Ignore non-matches', () => { + it('ignores write request that contains only fraction of denied keyword', async () => { await reconfigureServer({ - requestKeywordDenylist: [{ key: "abc" }], + requestKeywordDenylist: [{ key: 'abc' }], }); // Initially saving an object executes the keyword detection in RestWrite.js const obj = new TestObject({ a: { b: { c: 0 } } }); await expectAsync(obj.save()).toBeResolved(); // Modifying a nested key executes the keyword detection in DatabaseController.js - obj.increment("a.b.c"); + obj.increment('a.b.c'); await expectAsync(obj.save()).toBeResolved(); }); }); }); -describe("Postgres regex sanitizater", () => { - it("sanitizes the regex correctly to prevent Injection", async () => { +describe('Postgres regex sanitizater', () => { + it('sanitizes the regex correctly to prevent Injection', async () => { const user = new Parse.User(); - user.set("username", "username"); - user.set("password", "password"); - user.set("email", "email@example.com"); + user.set('username', 'username'); + user.set('password', 'password'); + user.set('email', 'email@example.com'); await user.signUp(); const response = await request({ - method: "GET", + method: 'GET', url: "http://localhost:8378/1/classes/_User?where[username][$regex]=A'B'%3BSELECT+PG_SLEEP(3)%3B--", headers: { - "Content-Type": "application/json", - "X-Parse-Application-Id": "test", - "X-Parse-REST-API-Key": "rest", + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', }, }); diff --git a/src/AccountLockout.js b/src/AccountLockout.js index 4181a2cc05..839b6e8e0a 100644 --- a/src/AccountLockout.js +++ b/src/AccountLockout.js @@ -1,5 +1,5 @@ // This class handles the Account Lockout Policy settings. -import Parse from "parse/node"; +import Parse from 'parse/node'; export class AccountLockout { constructor(user, config) { @@ -19,7 +19,7 @@ export class AccountLockout { _failed_login_count: value, }; - return this._config.database.update("_User", query, updateFields); + return this._config.database.update('_User', query, updateFields); } /** @@ -31,7 +31,7 @@ export class AccountLockout { _failed_login_count: { $exists: true }, }; - return this._config.database.find("_User", query).then(users => { + return this._config.database.find('_User', query).then(users => { if (Array.isArray(users) && users.length > 0) { return true; } else { @@ -61,10 +61,10 @@ export class AccountLockout { }; const updateFields = { - _failed_login_count: { __op: "Increment", amount: 1 }, + _failed_login_count: { __op: 'Increment', amount: 1 }, }; - return this._config.database.update("_User", query, updateFields); + return this._config.database.update('_User', query, updateFields); } /** @@ -89,14 +89,14 @@ export class AccountLockout { }; return this._config.database - .update("_User", query, updateFields) + .update('_User', query, updateFields) .catch(err => { if ( err && err.code && err.message && err.code === Parse.Error.OBJECT_NOT_FOUND && - err.message === "Object not found." + err.message === 'Object not found.' ) { return; // nothing to update so we are good } else { @@ -118,13 +118,13 @@ export class AccountLockout { _failed_login_count: { $gte: this._config.accountLockout.threshold }, }; - return this._config.database.find("_User", query).then(users => { + return this._config.database.find('_User', query).then(users => { if (Array.isArray(users) && users.length > 0) { throw new Parse.Error( Parse.Error.OBJECT_NOT_FOUND, - "Your account is locked due to multiple failed login attempts. Please try again after " + + 'Your account is locked due to multiple failed login attempts. Please try again after ' + this._config.accountLockout.duration + - " minute(s)" + ' minute(s)' ); } }); @@ -174,11 +174,11 @@ export class AccountLockout { return Promise.resolve(); } return this._config.database.update( - "_User", + '_User', { username: this._user.username }, { - _failed_login_count: { __op: "Delete" }, - _account_lockout_expires_at: { __op: "Delete" }, + _failed_login_count: { __op: 'Delete' }, + _account_lockout_expires_at: { __op: 'Delete' }, } ); } diff --git a/src/Auth.js b/src/Auth.js index ca4f68a6a3..6ae69c90f7 100644 --- a/src/Auth.js +++ b/src/Auth.js @@ -1,10 +1,10 @@ -const Parse = require("parse/node"); -import { isDeepStrictEqual } from "util"; -import { getRequestObject, resolveError } from "./triggers"; -import { logger } from "./logger"; -import { LRUCache as LRU } from "lru-cache"; -import RestQuery from "./RestQuery"; -import RestWrite from "./RestWrite"; +const Parse = require('parse/node'); +import { isDeepStrictEqual } from 'util'; +import { getRequestObject, resolveError } from './triggers'; +import { logger } from './logger'; +import { LRUCache as LRU } from 'lru-cache'; +import RestQuery from './RestQuery'; +import RestWrite from './RestWrite'; // An Auth object tells you who is requesting something and whether // the master key was used. @@ -98,7 +98,7 @@ const renewSessionIfNeeded = async ({ config, session, sessionToken }) => { config, auth: master(config), runBeforeFind: false, - className: "_Session", + className: '_Session', restWhere: { sessionToken }, restOptions: { limit: 1 }, }); @@ -113,13 +113,13 @@ const renewSessionIfNeeded = async ({ config, session, sessionToken }) => { await new RestWrite( config, master(config), - "_Session", + '_Session', { objectId: session.objectId }, { expiresAt: Parse._encode(expiresAt) } ).execute(); } catch (e) { if (e?.code !== Parse.Error.OBJECT_NOT_FOUND) { - logger.error("Could not update session expiry: ", e); + logger.error('Could not update session expiry: ', e); } } }; @@ -153,15 +153,15 @@ const getAuthForSessionToken = async function ({ if (config) { const restOptions = { limit: 1, - include: "user", + include: 'user', }; - const RestQuery = require("./RestQuery"); + const RestQuery = require('./RestQuery'); const query = await RestQuery({ method: RestQuery.Method.get, config, runBeforeFind: false, auth: master(config), - className: "_Session", + className: '_Session', restWhere: { sessionToken }, restOptions, }); @@ -170,16 +170,16 @@ const getAuthForSessionToken = async function ({ results = ( await new Parse.Query(Parse.Session) .limit(1) - .include("user") - .equalTo("sessionToken", sessionToken) + .include('user') + .equalTo('sessionToken', sessionToken) .find({ useMasterKey: true }) ).map(obj => obj.toJSON()); } - if (results.length !== 1 || !results[0]["user"]) { + if (results.length !== 1 || !results[0]['user']) { throw new Parse.Error( Parse.Error.INVALID_SESSION_TOKEN, - "Invalid session token" + 'Invalid session token' ); } const session = results[0]; @@ -188,24 +188,24 @@ const getAuthForSessionToken = async function ({ if (expiresAt < now) { throw new Parse.Error( Parse.Error.INVALID_SESSION_TOKEN, - "Session token is expired." + 'Session token is expired.' ); } const obj = session.user; if ( - typeof obj["objectId"] === "string" && - obj["objectId"].startsWith("role:") + typeof obj['objectId'] === 'string' && + obj['objectId'].startsWith('role:') ) { throw new Parse.Error( Parse.Error.INTERNAL_SERVER_ERROR, - "Invalid object ID." + 'Invalid object ID.' ); } delete obj.password; - obj["className"] = "_User"; - obj["sessionToken"] = sessionToken; + obj['className'] = '_User'; + obj['sessionToken'] = sessionToken; if (cacheController) { cacheController.user.put(sessionToken, obj); } @@ -228,13 +228,13 @@ var getAuthForLegacySessionToken = async function ({ var restOptions = { limit: 1, }; - const RestQuery = require("./RestQuery"); + const RestQuery = require('./RestQuery'); var query = await RestQuery({ method: RestQuery.Method.get, config, runBeforeFind: false, auth: master(config), - className: "_User", + className: '_User', restWhere: { _session_token: sessionToken }, restOptions, }); @@ -243,11 +243,11 @@ var getAuthForLegacySessionToken = async function ({ if (results.length !== 1) { throw new Parse.Error( Parse.Error.INVALID_SESSION_TOKEN, - "invalid legacy session token" + 'invalid legacy session token' ); } const obj = results[0]; - obj.className = "_User"; + obj.className = '_User'; const userObject = Parse.Object.fromJSON(obj); return new Auth({ config, @@ -279,24 +279,24 @@ Auth.prototype.getRolesForUser = async function () { if (this.config) { const restWhere = { users: { - __type: "Pointer", - className: "_User", + __type: 'Pointer', + className: '_User', objectId: this.user.id, }, }; - const RestQuery = require("./RestQuery"); + const RestQuery = require('./RestQuery'); const query = await RestQuery({ method: RestQuery.Method.find, runBeforeFind: false, config: this.config, auth: master(this.config), - className: "_Role", + className: '_Role', restWhere, }); await query.each(result => results.push(result)); } else { await new Parse.Query(Parse.Role) - .equalTo("users", this.user) + .equalTo('users', this.user) .each(result => results.push(result.toJSON()), { useMasterKey: true }); } return results; @@ -339,7 +339,7 @@ Auth.prototype._loadRoles = async function () { rolesMap.names ); this.userRoles = roleNames.map(r => { - return "role:" + r; + return 'role:' + r; }); this.fetchedRoles = true; this.rolePromise = null; @@ -370,7 +370,7 @@ Auth.prototype.getRolesByIds = async function (ins) { if (!this.config) { await new Parse.Query(Parse.Role) .containedIn( - "roles", + 'roles', ins.map(id => { const role = new Parse.Object(Parse.Role); role.id = id; @@ -381,19 +381,19 @@ Auth.prototype.getRolesByIds = async function (ins) { } else { const roles = ins.map(id => { return { - __type: "Pointer", - className: "_Role", + __type: 'Pointer', + className: '_Role', objectId: id, }; }); const restWhere = { roles: { $in: roles } }; - const RestQuery = require("./RestQuery"); + const RestQuery = require('./RestQuery'); const query = await RestQuery({ method: RestQuery.Method.find, config: this.config, runBeforeFind: false, auth: master(this.config), - className: "_Role", + className: '_Role', restWhere, }); await query.each(result => results.push(result)); @@ -456,7 +456,7 @@ const findUsersWithAuthData = async (config, authData, beforeFind) => { const adapter = config.authDataManager.getValidatorForProvider(provider)?.adapter; - if (beforeFind && typeof adapter?.beforeFind === "function") { + if (beforeFind && typeof adapter?.beforeFind === 'function') { await adapter.beforeFind(providerAuthData); } @@ -476,7 +476,7 @@ const findUsersWithAuthData = async (config, authData, beforeFind) => { } // Perform database query - return config.database.find("_User", { $or: validQueries }, { limit: 2 }); + return config.database.find('_User', { $or: validQueries }, { limit: 2 }); }; const hasMutatedAuthData = (authData, userAuthData) => { @@ -486,7 +486,7 @@ const hasMutatedAuthData = (authData, userAuthData) => { const mutatedAuthData = {}; Object.keys(authData).forEach(provider => { // Anonymous provider is not handled this way - if (provider === "anonymous") { + if (provider === 'anonymous') { return; } const providerData = authData[provider]; @@ -514,7 +514,7 @@ const checkIfUserHasProvidedConfiguredProvidersForLogin = ( provider => provider && provider.adapter && - provider.adapter.policy === "solo" && + provider.adapter.policy === 'solo' && authData[provider.name] ); @@ -529,7 +529,7 @@ const checkIfUserHasProvidedConfiguredProvidersForLogin = ( const hasProvidedAtLeastOneAdditionalProvider = savedUserProviders.some( provider => { let policy = provider.adapter.policy; - if (typeof policy === "function") { + if (typeof policy === 'function') { const requestObject = { ip: req.config.ip, user: req.auth.user, @@ -541,7 +541,7 @@ const checkIfUserHasProvidedConfiguredProvidersForLogin = ( userAuthData[provider.name] ); } - if (policy === "additional") { + if (policy === 'additional') { if (authData[provider.name]) { return true; } else { @@ -560,7 +560,7 @@ const checkIfUserHasProvidedConfiguredProvidersForLogin = ( throw new Parse.Error( Parse.Error.OTHER_CAUSE, - `Missing additional authData ${additionProvidersNotFound.join(",")}` + `Missing additional authData ${additionProvidersNotFound.join(',')}` ); }; @@ -568,16 +568,16 @@ const checkIfUserHasProvidedConfiguredProvidersForLogin = ( const handleAuthDataValidation = async (authData, req, foundUser) => { let user; if (foundUser) { - user = Parse.User.fromJSON({ className: "_User", ...foundUser }); + user = Parse.User.fromJSON({ className: '_User', ...foundUser }); // Find user by session and current objectId; only pass user if it's the current user or master key is provided } else if ( (req.auth && req.auth.user && - typeof req.getUserId === "function" && + typeof req.getUserId === 'function' && req.getUserId() === req.auth.user.id) || (req.auth && req.auth.isMaster && - typeof req.getUserId === "function" && + typeof req.getUserId === 'function' && req.getUserId()) ) { user = new Parse.User(); @@ -598,7 +598,7 @@ const handleAuthDataValidation = async (authData, req, foundUser) => { const acc = { authData: {}, authDataResponse: {} }; const authKeys = Object.keys(authData).sort(); for (const provider of authKeys) { - let method = ""; + let method = ''; try { if (authData[provider] === null) { acc.authData[provider] = null; @@ -610,7 +610,7 @@ const handleAuthDataValidation = async (authData, req, foundUser) => { if (!validator || authProvider.enabled === false) { throw new Parse.Error( Parse.Error.UNSUPPORTED_SERVICE, - "This authentication method is unsupported." + 'This authentication method is unsupported.' ); } let validationResult = await validator( @@ -643,7 +643,7 @@ const handleAuthDataValidation = async (authData, req, foundUser) => { } catch (err) { const e = resolveError(err, { code: Parse.Error.SCRIPT_FAILED, - message: "Auth failed. Unknown error.", + message: 'Auth failed. Unknown error.', }); const userString = req.auth && req.auth.user diff --git a/src/ClientSDK.js b/src/ClientSDK.js index f56f3df274..698729fc4f 100644 --- a/src/ClientSDK.js +++ b/src/ClientSDK.js @@ -1,8 +1,8 @@ -var semver = require("semver"); +var semver = require('semver'); function compatible(compatibleSDK) { return function (clientSDK) { - if (typeof clientSDK === "string") { + if (typeof clientSDK === 'string') { clientSDK = fromString(clientSDK); } // REST API, or custom SDK @@ -17,7 +17,7 @@ function compatible(compatibleSDK) { function supportsForwardDelete(clientSDK) { return compatible({ - js: ">=1.9.0", + js: '>=1.9.0', })(clientSDK); } diff --git a/src/Controllers/AdaptableController.js b/src/Controllers/AdaptableController.js index 1802e1592e..6fc576845b 100644 --- a/src/Controllers/AdaptableController.js +++ b/src/Controllers/AdaptableController.js @@ -28,7 +28,7 @@ export class AdaptableController { } expectedAdapterType() { - throw new Error("Subclasses should implement expectedAdapterType()"); + throw new Error('Subclasses should implement expectedAdapterType()'); } validateAdapter(adapter) { @@ -37,7 +37,7 @@ export class AdaptableController { static validateAdapter(adapter, self, ExpectedType) { if (!adapter) { - throw new Error(this.constructor.name + " requires an adapter"); + throw new Error(this.constructor.name + ' requires an adapter'); } const Type = ExpectedType || self.expectedAdapterType(); diff --git a/src/Controllers/AnalyticsController.js b/src/Controllers/AnalyticsController.js index dfb4a737e3..e6016a4a5b 100644 --- a/src/Controllers/AnalyticsController.js +++ b/src/Controllers/AnalyticsController.js @@ -1,5 +1,5 @@ -import AdaptableController from "./AdaptableController"; -import { AnalyticsAdapter } from "../Adapters/Analytics/AnalyticsAdapter"; +import AdaptableController from './AdaptableController'; +import { AnalyticsAdapter } from '../Adapters/Analytics/AnalyticsAdapter'; export class AnalyticsController extends AdaptableController { appOpened(req) { diff --git a/src/Controllers/CacheController.js b/src/Controllers/CacheController.js index c89fe97abc..0c645c5236 100644 --- a/src/Controllers/CacheController.js +++ b/src/Controllers/CacheController.js @@ -1,7 +1,7 @@ -import AdaptableController from "./AdaptableController"; -import CacheAdapter from "../Adapters/Cache/CacheAdapter"; +import AdaptableController from './AdaptableController'; +import CacheAdapter from '../Adapters/Cache/CacheAdapter'; -const KEY_SEPARATOR_CHAR = ":"; +const KEY_SEPARATOR_CHAR = ':'; function joinKeys(...keys) { return keys.join(KEY_SEPARATOR_CHAR); @@ -43,9 +43,9 @@ export class CacheController extends AdaptableController { constructor(adapter, appId, options = {}) { super(adapter, appId, options); - this.role = new SubCache("role", this); - this.user = new SubCache("user", this); - this.graphQL = new SubCache("graphQL", this); + this.role = new SubCache('role', this); + this.user = new SubCache('user', this); + this.graphQL = new SubCache('graphQL', this); } get(key) { diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index fd859e7f9e..ea815650ed 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -3,26 +3,26 @@ // Parse database. // @flow-disable-next -import { Parse } from "parse/node"; +import { Parse } from 'parse/node'; // @flow-disable-next -import _ from "lodash"; +import _ from 'lodash'; // @flow-disable-next -import intersect from "intersect"; +import intersect from 'intersect'; // @flow-disable-next -import deepcopy from "deepcopy"; -import logger from "../logger"; -import Utils from "../Utils"; -import * as SchemaController from "./SchemaController"; -import { StorageAdapter } from "../Adapters/Storage/StorageAdapter"; -import MongoStorageAdapter from "../Adapters/Storage/Mongo/MongoStorageAdapter"; -import PostgresStorageAdapter from "../Adapters/Storage/Postgres/PostgresStorageAdapter"; -import SchemaCache from "../Adapters/Cache/SchemaCache"; -import type { LoadSchemaOptions } from "./types"; -import type { ParseServerOptions } from "../Options"; +import deepcopy from 'deepcopy'; +import logger from '../logger'; +import Utils from '../Utils'; +import * as SchemaController from './SchemaController'; +import { StorageAdapter } from '../Adapters/Storage/StorageAdapter'; +import MongoStorageAdapter from '../Adapters/Storage/Mongo/MongoStorageAdapter'; +import PostgresStorageAdapter from '../Adapters/Storage/Postgres/PostgresStorageAdapter'; +import SchemaCache from '../Adapters/Cache/SchemaCache'; +import type { LoadSchemaOptions } from './types'; +import type { ParseServerOptions } from '../Options'; import type { QueryOptions, FullQueryOptions, -} from "../Adapters/Storage/StorageAdapter"; +} from '../Adapters/Storage/StorageAdapter'; function addWriteACL(query, acl) { const newQuery = _.cloneDeep(query); @@ -34,7 +34,7 @@ function addWriteACL(query, acl) { function addReadACL(query, acl) { const newQuery = _.cloneDeep(query); //Can't be any existing '_rperm' query, we don't allow client queries on that, no need to $and - newQuery._rperm = { $in: [null, "*", ...acl] }; + newQuery._rperm = { $in: [null, '*', ...acl] }; return newQuery; } @@ -58,17 +58,17 @@ const transformObjectACL = ({ ACL, ...result }) => { return result; }; -const specialQueryKeys = ["$and", "$or", "$nor", "_rperm", "_wperm"]; +const specialQueryKeys = ['$and', '$or', '$nor', '_rperm', '_wperm']; const specialMasterQueryKeys = [ ...specialQueryKeys, - "_email_verify_token", - "_perishable_token", - "_tombstone", - "_email_verify_token_expires_at", - "_failed_login_count", - "_account_lockout_expires_at", - "_password_changed_at", - "_password_history", + '_email_verify_token', + '_perishable_token', + '_tombstone', + '_email_verify_token_expires_at', + '_failed_login_count', + '_account_lockout_expires_at', + '_password_changed_at', + '_password_history', ]; const validateQuery = ( @@ -81,7 +81,7 @@ const validateQuery = ( isMaster = true; } if (query.ACL) { - throw new Parse.Error(Parse.Error.INVALID_QUERY, "Cannot query on ACL."); + throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Cannot query on ACL.'); } if (query.$or) { @@ -92,7 +92,7 @@ const validateQuery = ( } else { throw new Parse.Error( Parse.Error.INVALID_QUERY, - "Bad $or format - use an array value." + 'Bad $or format - use an array value.' ); } } @@ -105,7 +105,7 @@ const validateQuery = ( } else { throw new Parse.Error( Parse.Error.INVALID_QUERY, - "Bad $and format - use an array value." + 'Bad $and format - use an array value.' ); } } @@ -118,14 +118,14 @@ const validateQuery = ( } else { throw new Parse.Error( Parse.Error.INVALID_QUERY, - "Bad $nor format - use an array of at least 1 value." + 'Bad $nor format - use an array of at least 1 value.' ); } } Object.keys(query).forEach(key => { if (query && query[key] && query[key].$regex) { - if (typeof query[key].$options === "string") { + if (typeof query[key].$options === 'string') { if (!query[key].$options.match(/^[imxs]+$/)) { throw new Parse.Error( Parse.Error.INVALID_QUERY, @@ -170,12 +170,12 @@ const filterSensitiveData = ( ? schema.getClassLevelPermissions(className) : {}; if (perms) { - const isReadOperation = ["get", "find"].indexOf(operation) > -1; + const isReadOperation = ['get', 'find'].indexOf(operation) > -1; if (isReadOperation && perms.protectedFields) { // extract protectedFields added with the pointer-permission prefix const protectedFieldsPointerPerm = Object.keys(perms.protectedFields) - .filter(key => key.startsWith("userField:")) + .filter(key => key.startsWith('userField:')) .map(key => { return { key: key.substring(10), value: perms.protectedFields[key] }; }); @@ -226,7 +226,7 @@ const filterSensitiveData = ( } } - const isUserClass = className === "_User"; + const isUserClass = className === '_User'; if (isUserClass) { object.password = object._hashed_password; delete object._hashed_password; @@ -248,7 +248,7 @@ const filterSensitiveData = ( } for (const key in object) { - if (key.charAt(0) === "_") { + if (key.charAt(0) === '_') { delete object[key]; } } @@ -273,15 +273,15 @@ const filterSensitiveData = ( // one of the provided strings must provide the caller with // write permissions. const specialKeysForUpdate = [ - "_hashed_password", - "_perishable_token", - "_email_verify_token", - "_email_verify_token_expires_at", - "_account_lockout_expires_at", - "_failed_login_count", - "_perishable_token_expires_at", - "_password_changed_at", - "_password_history", + '_hashed_password', + '_perishable_token', + '_email_verify_token', + '_email_verify_token_expires_at', + '_account_lockout_expires_at', + '_failed_login_count', + '_perishable_token_expires_at', + '_password_changed_at', + '_password_history', ]; const isSpecialUpdateKey = key => { @@ -296,46 +296,46 @@ const flattenUpdateOperatorsForCreate = object => { for (const key in object) { if (object[key] && object[key].__op) { switch (object[key].__op) { - case "Increment": - if (typeof object[key].amount !== "number") { + case 'Increment': + if (typeof object[key].amount !== 'number') { throw new Parse.Error( Parse.Error.INVALID_JSON, - "objects to add must be an array" + 'objects to add must be an array' ); } object[key] = object[key].amount; break; - case "SetOnInsert": + case 'SetOnInsert': object[key] = object[key].amount; break; - case "Add": + case 'Add': if (!(object[key].objects instanceof Array)) { throw new Parse.Error( Parse.Error.INVALID_JSON, - "objects to add must be an array" + 'objects to add must be an array' ); } object[key] = object[key].objects; break; - case "AddUnique": + case 'AddUnique': if (!(object[key].objects instanceof Array)) { throw new Parse.Error( Parse.Error.INVALID_JSON, - "objects to add must be an array" + 'objects to add must be an array' ); } object[key] = object[key].objects; break; - case "Remove": + case 'Remove': if (!(object[key].objects instanceof Array)) { throw new Parse.Error( Parse.Error.INVALID_JSON, - "objects to add must be an array" + 'objects to add must be an array' ); } object[key] = []; break; - case "Delete": + case 'Delete': delete object[key]; break; default: @@ -349,17 +349,17 @@ const flattenUpdateOperatorsForCreate = object => { }; const transformAuthData = (className, object, schema) => { - if (object.authData && className === "_User") { + if (object.authData && className === '_User') { Object.keys(object.authData).forEach(provider => { const providerData = object.authData[provider]; const fieldName = `_auth_data_${provider}`; if (providerData == null) { object[fieldName] = { - __op: "Delete", + __op: 'Delete', }; } else { object[fieldName] = providerData; - schema.fields[fieldName] = { type: "Object" }; + schema.fields[fieldName] = { type: 'Object' }; } }); delete object.authData; @@ -374,7 +374,7 @@ const untransformObjectACL = ({ _rperm, _wperm, ...output }) => { if (!output.ACL[entry]) { output.ACL[entry] = { read: true }; } else { - output.ACL[entry]["read"] = true; + output.ACL[entry]['read'] = true; } }); @@ -382,7 +382,7 @@ const untransformObjectACL = ({ _rperm, _wperm, ...output }) => { if (!output.ACL[entry]) { output.ACL[entry] = { write: true }; } else { - output.ACL[entry]["write"] = true; + output.ACL[entry]['write'] = true; } }); } @@ -396,25 +396,25 @@ const untransformObjectACL = ({ _rperm, _wperm, ...output }) => { * @returns {string} the root name of the field */ const getRootFieldName = (fieldName: string): string => { - return fieldName.split(".")[0]; + return fieldName.split('.')[0]; }; const relationSchema = { - fields: { relatedId: { type: "String" }, owningId: { type: "String" } }, + fields: { relatedId: { type: 'String' }, owningId: { type: 'String' } }, }; const convertEmailToLowercase = (object, className, options) => { - if (className === "_User" && options.convertEmailToLowercase) { - if (typeof object["email"] === "string") { - object["email"] = object["email"].toLowerCase(); + if (className === '_User' && options.convertEmailToLowercase) { + if (typeof object['email'] === 'string') { + object['email'] = object['email'].toLowerCase(); } } }; const convertUsernameToLowercase = (object, className, options) => { - if (className === "_User" && options.convertUsernameToLowercase) { - if (typeof object["username"] === "string") { - object["username"] = object["username"].toLowerCase(); + if (className === '_User' && options.convertUsernameToLowercase) { + if (typeof object['username'] === 'string') { + object['username'] = object['username'].toLowerCase(); } } }; @@ -453,7 +453,7 @@ class DatabaseController { return Promise.reject( new Parse.Error( Parse.Error.INVALID_CLASS_NAME, - "invalid className: " + className + 'invalid className: ' + className ) ); } @@ -490,7 +490,7 @@ class DatabaseController { redirectClassNameForKey(className: string, key: string): Promise { return this.loadSchema().then(schema => { var t = schema.getExpectedType(className, key); - if (t != null && typeof t !== "string" && t.type === "Relation") { + if (t != null && typeof t !== 'string' && t.type === 'Relation') { return t.targetClass; } return className; @@ -560,7 +560,7 @@ class DatabaseController { return ( isMaster ? Promise.resolve() - : schemaController.validatePermission(className, aclGroup, "update") + : schemaController.validatePermission(className, aclGroup, 'update') ) .then(() => { relationUpdates = this.collectRelationUpdates( @@ -572,7 +572,7 @@ class DatabaseController { query = this.addPointerPermissions( schemaController, className, - "update", + 'update', query, aclGroup ); @@ -584,7 +584,7 @@ class DatabaseController { this.addPointerPermissions( schemaController, className, - "addField", + 'addField', query, aclGroup ), @@ -634,10 +634,10 @@ class DatabaseController { for (const updateOperation in update) { if ( update[updateOperation] && - typeof update[updateOperation] === "object" && + typeof update[updateOperation] === 'object' && Object.keys(update[updateOperation]).some( innerKey => - innerKey.includes("$") || innerKey.includes(".") + innerKey.includes('$') || innerKey.includes('.') ) ) { throw new Parse.Error( @@ -657,7 +657,7 @@ class DatabaseController { if (!result || !result.length) { throw new Parse.Error( Parse.Error.OBJECT_NOT_FOUND, - "Object not found." + 'Object not found.' ); } return {}; @@ -694,7 +694,7 @@ class DatabaseController { if (!result) { throw new Parse.Error( Parse.Error.OBJECT_NOT_FOUND, - "Object not found." + 'Object not found.' ); } if (validateOnly) { @@ -731,17 +731,17 @@ class DatabaseController { if (!op) { return; } - if (op.__op == "AddRelation") { + if (op.__op == 'AddRelation') { ops.push({ key, op }); deleteMe.push(key); } - if (op.__op == "RemoveRelation") { + if (op.__op == 'RemoveRelation') { ops.push({ key, op }); deleteMe.push(key); } - if (op.__op == "Batch") { + if (op.__op == 'Batch') { for (var x of op.ops) { process(x, key); } @@ -771,7 +771,7 @@ class DatabaseController { if (!op) { return; } - if (op.__op == "AddRelation") { + if (op.__op == 'AddRelation') { for (const object of op.objects) { pending.push( this.addRelation(key, className, objectId, object.objectId) @@ -779,7 +779,7 @@ class DatabaseController { } } - if (op.__op == "RemoveRelation") { + if (op.__op == 'RemoveRelation') { for (const object of op.objects) { pending.push( this.removeRelation(key, className, objectId, object.objectId) @@ -862,20 +862,20 @@ class DatabaseController { return ( isMaster ? Promise.resolve() - : schemaController.validatePermission(className, aclGroup, "delete") + : schemaController.validatePermission(className, aclGroup, 'delete') ).then(() => { if (!isMaster) { query = this.addPointerPermissions( schemaController, className, - "delete", + 'delete', query, aclGroup ); if (!query) { throw new Parse.Error( Parse.Error.OBJECT_NOT_FOUND, - "Object not found." + 'Object not found.' ); } } @@ -905,7 +905,7 @@ class DatabaseController { .catch(error => { // When deleting sessions while changing passwords, don't throw an error if they don't have any sessions. if ( - className === "_Session" && + className === '_Session' && error.code === Parse.Error.OBJECT_NOT_FOUND ) { return Promise.resolve({}); @@ -939,8 +939,8 @@ class DatabaseController { convertEmailToLowercase(object, className, this.options); convertUsernameToLowercase(object, className, this.options); - object.createdAt = { iso: object.createdAt, __type: "Date" }; - object.updatedAt = { iso: object.updatedAt, __type: "Date" }; + object.createdAt = { iso: object.createdAt, __type: 'Date' }; + object.updatedAt = { iso: object.updatedAt, __type: 'Date' }; var isMaster = acl === undefined; var aclGroup = acl || []; @@ -956,7 +956,7 @@ class DatabaseController { return ( isMaster ? Promise.resolve() - : schemaController.validatePermission(className, aclGroup, "create") + : schemaController.validatePermission(className, aclGroup, 'create') ) .then(() => schemaController.enforceClassExists(className)) .then(() => schemaController.getOneSchema(className, true)) @@ -1010,7 +1010,7 @@ class DatabaseController { if ( object[field] && object[field].__op && - object[field].__op === "Delete" + object[field].__op === 'Delete' ) { return false; } @@ -1021,7 +1021,7 @@ class DatabaseController { runOptions.addsField = true; const action = runOptions.action; - return schema.validatePermission(className, aclGroup, "addField", action); + return schema.validatePermission(className, aclGroup, 'addField', action); } return Promise.resolve(); } @@ -1077,7 +1077,7 @@ class DatabaseController { joinTableName(className, key), relationSchema, { relatedId: { $in: relatedIds } }, - { keys: ["owningId"] } + { keys: ['owningId'] } ) .then(results => results.map(result => result.owningId)); } @@ -1089,25 +1089,25 @@ class DatabaseController { // Search for an in-relation or equal-to-relation // Make it sequential for now, not sure of paralleization side effects const promises = []; - if (query["$or"]) { - const ors = query["$or"]; + if (query['$or']) { + const ors = query['$or']; promises.push( ...ors.map((aQuery, index) => { return this.reduceInRelation(className, aQuery, schema).then( aQuery => { - query["$or"][index] = aQuery; + query['$or'][index] = aQuery; } ); }) ); } - if (query["$and"]) { - const ands = query["$and"]; + if (query['$and']) { + const ands = query['$and']; promises.push( ...ands.map((aQuery, index) => { return this.reduceInRelation(className, aQuery, schema).then( aQuery => { - query["$and"][index] = aQuery; + query['$and'][index] = aQuery; } ); }) @@ -1115,35 +1115,35 @@ class DatabaseController { } const otherKeys = Object.keys(query).map(key => { - if (key === "$and" || key === "$or") { + if (key === '$and' || key === '$or') { return; } const t = schema.getExpectedType(className, key); - if (!t || t.type !== "Relation") { + if (!t || t.type !== 'Relation') { return Promise.resolve(query); } let queries: ?(any[]) = null; if ( query[key] && - (query[key]["$in"] || - query[key]["$ne"] || - query[key]["$nin"] || - query[key].__type == "Pointer") + (query[key]['$in'] || + query[key]['$ne'] || + query[key]['$nin'] || + query[key].__type == 'Pointer') ) { // Build the list of queries queries = Object.keys(query[key]).map(constraintKey => { let relatedIds; let isNegation = false; - if (constraintKey === "objectId") { + if (constraintKey === 'objectId') { relatedIds = [query[key].objectId]; - } else if (constraintKey == "$in") { - relatedIds = query[key]["$in"].map(r => r.objectId); - } else if (constraintKey == "$nin") { + } else if (constraintKey == '$in') { + relatedIds = query[key]['$in'].map(r => r.objectId); + } else if (constraintKey == '$nin') { isNegation = true; - relatedIds = query[key]["$nin"].map(r => r.objectId); - } else if (constraintKey == "$ne") { + relatedIds = query[key]['$nin'].map(r => r.objectId); + } else if (constraintKey == '$ne') { isNegation = true; - relatedIds = [query[key]["$ne"].objectId]; + relatedIds = [query[key]['$ne'].objectId]; } else { return; } @@ -1191,21 +1191,21 @@ class DatabaseController { query: any, queryOptions: any ): ?Promise { - if (query["$or"]) { + if (query['$or']) { return Promise.all( - query["$or"].map(aQuery => { + query['$or'].map(aQuery => { return this.reduceRelationKeys(className, aQuery, queryOptions); }) ); } - if (query["$and"]) { + if (query['$and']) { return Promise.all( - query["$and"].map(aQuery => { + query['$and'].map(aQuery => { return this.reduceRelationKeys(className, aQuery, queryOptions); }) ); } - var relatedTo = query["$relatedTo"]; + var relatedTo = query['$relatedTo']; if (relatedTo) { return this.relatedIds( relatedTo.object.className, @@ -1214,7 +1214,7 @@ class DatabaseController { queryOptions ) .then(ids => { - delete query["$relatedTo"]; + delete query['$relatedTo']; this.addInObjectIdsIds(ids, query); return this.reduceRelationKeys(className, query, queryOptions); }) @@ -1224,11 +1224,11 @@ class DatabaseController { addInObjectIdsIds(ids: ?Array = null, query: any) { const idsFromString: ?Array = - typeof query.objectId === "string" ? [query.objectId] : null; + typeof query.objectId === 'string' ? [query.objectId] : null; const idsFromEq: ?Array = - query.objectId && query.objectId["$eq"] ? [query.objectId["$eq"]] : null; + query.objectId && query.objectId['$eq'] ? [query.objectId['$eq']] : null; const idsFromIn: ?Array = - query.objectId && query.objectId["$in"] ? query.objectId["$in"] : null; + query.objectId && query.objectId['$in'] ? query.objectId['$in'] : null; // @flow-disable-next const allIds: Array> = [ @@ -1247,42 +1247,42 @@ class DatabaseController { } // Need to make sure we don't clobber existing shorthand $eq constraints on objectId. - if (!("objectId" in query)) { + if (!('objectId' in query)) { query.objectId = { $in: undefined, }; - } else if (typeof query.objectId === "string") { + } else if (typeof query.objectId === 'string') { query.objectId = { $in: undefined, $eq: query.objectId, }; } - query.objectId["$in"] = idsIntersection; + query.objectId['$in'] = idsIntersection; return query; } addNotInObjectIdsIds(ids: string[] = [], query: any) { const idsFromNin = - query.objectId && query.objectId["$nin"] ? query.objectId["$nin"] : []; + query.objectId && query.objectId['$nin'] ? query.objectId['$nin'] : []; let allIds = [...idsFromNin, ...ids].filter(list => list !== null); // make a set and spread to remove duplicates allIds = [...new Set(allIds)]; // Need to make sure we don't clobber existing shorthand $eq constraints on objectId. - if (!("objectId" in query)) { + if (!('objectId' in query)) { query.objectId = { $nin: undefined, }; - } else if (typeof query.objectId === "string") { + } else if (typeof query.objectId === 'string') { query.objectId = { $nin: undefined, $eq: query.objectId, }; } - query.objectId["$nin"] = allIds; + query.objectId['$nin'] = allIds; return query; } @@ -1328,11 +1328,11 @@ class DatabaseController { const aclGroup = acl || []; op = op || - (typeof query.objectId == "string" && Object.keys(query).length === 1 - ? "get" - : "find"); + (typeof query.objectId == 'string' && Object.keys(query).length === 1 + ? 'get' + : 'find'); // Count operation if counting - op = count === true ? "count" : op; + op = count === true ? 'count' : op; let classExists = true; return this.loadSchemaIfNeeded(validSchemaController).then( @@ -1393,8 +1393,8 @@ class DatabaseController { ); } if ( - !schema.fields[fieldName.split(".")[0]] && - fieldName !== "score" + !schema.fields[fieldName.split('.')[0]] && + fieldName !== 'score' ) { delete sort[fieldName]; } @@ -1433,17 +1433,17 @@ class DatabaseController { ); } if (!query) { - if (op === "get") { + if (op === 'get') { throw new Parse.Error( Parse.Error.OBJECT_NOT_FOUND, - "Object not found." + 'Object not found.' ); } else { return []; } } if (!isMaster) { - if (op === "update" || op === "delete") { + if (op === 'update' || op === 'delete') { query = addWriteACL(query, aclGroup); } else { query = addReadACL(query, aclGroup); @@ -1545,7 +1545,7 @@ class DatabaseController { .then((schema: any) => { return this.collectionExists(className) .then(() => - this.adapter.count(className, { fields: {} }, null, "", false) + this.adapter.count(className, { fields: {} }, null, '', false) ) .then(count => { if (count > 0) { @@ -1559,7 +1559,7 @@ class DatabaseController { .then(wasParseCollection => { if (wasParseCollection) { const relationFieldNames = Object.keys(schema.fields).filter( - fieldName => schema.fields[fieldName].type === "Relation" + fieldName => schema.fields[fieldName].type === 'Relation' ); return Promise.all( relationFieldNames.map(name => @@ -1581,7 +1581,7 @@ class DatabaseController { // in a similar way to json objectToEntriesStrings(query: any): Array { return Object.entries(query).map(a => - a.map(s => JSON.stringify(s)).join(":") + a.map(s => JSON.stringify(s)).join(':') ); } @@ -1677,13 +1677,13 @@ class DatabaseController { const perms = schema.getClassLevelPermissions(className); const userACL = aclGroup.filter(acl => { - return acl.indexOf("role:") != 0 && acl != "*"; + return acl.indexOf('role:') != 0 && acl != '*'; }); const groupKey = - ["get", "find", "count"].indexOf(operation) > -1 - ? "readUserFields" - : "writeUserFields"; + ['get', 'find', 'count'].indexOf(operation) > -1 + ? 'readUserFields' + : 'writeUserFields'; const permFields = []; @@ -1708,8 +1708,8 @@ class DatabaseController { } const userId = userACL[0]; const userPointer = { - __type: "Pointer", - className: "_User", + __type: 'Pointer', + className: '_User', objectId: userId, }; @@ -1717,20 +1717,20 @@ class DatabaseController { const fieldDescriptor = schema.getExpectedType(className, key); const fieldType = fieldDescriptor && - typeof fieldDescriptor === "object" && - Object.prototype.hasOwnProperty.call(fieldDescriptor, "type") + typeof fieldDescriptor === 'object' && + Object.prototype.hasOwnProperty.call(fieldDescriptor, 'type') ? fieldDescriptor.type : null; let queryClause; - if (fieldType === "Pointer") { + if (fieldType === 'Pointer') { // constraint for single pointer setup queryClause = { [key]: userPointer }; - } else if (fieldType === "Array") { + } else if (fieldType === 'Array') { // constraint for users-array setup queryClause = { [key]: { $all: [userPointer] } }; - } else if (fieldType === "Object") { + } else if (fieldType === 'Object') { // constraint for object setup queryClause = { [key]: userPointer }; } else { @@ -1805,7 +1805,7 @@ class DatabaseController { for (const key in protectedFields) { // skip userFields - if (key.startsWith("userField:")) { + if (key.startsWith('userField:')) { if (preserveKeys) { const fieldName = key.substring(10); if (!preserveKeys.includes(fieldName)) { @@ -1819,19 +1819,19 @@ class DatabaseController { } // add public tier - if (key === "*") { + if (key === '*') { protectedKeysSets.push(protectedFields[key]); continue; } if (authenticated) { - if (key === "authenticated") { + if (key === 'authenticated') { // for logged in users protectedKeysSets.push(protectedFields[key]); continue; } - if (roles[key] && key.startsWith("role:")) { + if (roles[key] && key.startsWith('role:')) { // add applicable roles protectedKeysSets.push(roles[key]); } @@ -1878,7 +1878,7 @@ class DatabaseController { commitTransactionalSession() { if (!this._transactionalSession) { - throw new Error("There is no transactional session to commit"); + throw new Error('There is no transactional session to commit'); } return this.adapter .commitTransactionalSession(this._transactionalSession) @@ -1889,7 +1889,7 @@ class DatabaseController { abortTransactionalSession() { if (!this._transactionalSession) { - throw new Error("There is no transactional session to abort"); + throw new Error('There is no transactional session to abort'); } return this.adapter .abortTransactionalSession(this._transactionalSession) @@ -1922,31 +1922,31 @@ class DatabaseController { ...SchemaController.defaultColumns._Idempotency, }, }; - await this.loadSchema().then(schema => schema.enforceClassExists("_User")); - await this.loadSchema().then(schema => schema.enforceClassExists("_Role")); + await this.loadSchema().then(schema => schema.enforceClassExists('_User')); + await this.loadSchema().then(schema => schema.enforceClassExists('_Role')); await this.loadSchema().then(schema => - schema.enforceClassExists("_Idempotency") + schema.enforceClassExists('_Idempotency') ); await this.adapter - .ensureUniqueness("_User", requiredUserFields, ["username"]) + .ensureUniqueness('_User', requiredUserFields, ['username']) .catch(error => { - logger.warn("Unable to ensure uniqueness for usernames: ", error); + logger.warn('Unable to ensure uniqueness for usernames: ', error); throw error; }); if (!this.options.enableCollationCaseComparison) { await this.adapter .ensureIndex( - "_User", + '_User', requiredUserFields, - ["username"], - "case_insensitive_username", + ['username'], + 'case_insensitive_username', true ) .catch(error => { logger.warn( - "Unable to create case insensitive username index: ", + 'Unable to create case insensitive username index: ', error ); throw error; @@ -1954,40 +1954,40 @@ class DatabaseController { await this.adapter .ensureIndex( - "_User", + '_User', requiredUserFields, - ["email"], - "case_insensitive_email", + ['email'], + 'case_insensitive_email', true ) .catch(error => { - logger.warn("Unable to create case insensitive email index: ", error); + logger.warn('Unable to create case insensitive email index: ', error); throw error; }); } await this.adapter - .ensureUniqueness("_User", requiredUserFields, ["email"]) + .ensureUniqueness('_User', requiredUserFields, ['email']) .catch(error => { logger.warn( - "Unable to ensure uniqueness for user email addresses: ", + 'Unable to ensure uniqueness for user email addresses: ', error ); throw error; }); await this.adapter - .ensureUniqueness("_Role", requiredRoleFields, ["name"]) + .ensureUniqueness('_Role', requiredRoleFields, ['name']) .catch(error => { - logger.warn("Unable to ensure uniqueness for role name: ", error); + logger.warn('Unable to ensure uniqueness for role name: ', error); throw error; }); await this.adapter - .ensureUniqueness("_Idempotency", requiredIdempotencyFields, ["reqId"]) + .ensureUniqueness('_Idempotency', requiredIdempotencyFields, ['reqId']) .catch(error => { logger.warn( - "Unable to ensure uniqueness for idempotency request ID: ", + 'Unable to ensure uniqueness for idempotency request ID: ', error ); throw error; @@ -2007,16 +2007,16 @@ class DatabaseController { } await this.adapter .ensureIndex( - "_Idempotency", + '_Idempotency', requiredIdempotencyFields, - ["expire"], - "ttl", + ['expire'], + 'ttl', false, options ) .catch(error => { logger.warn( - "Unable to create TTL index for idempotency expire date: ", + 'Unable to create TTL index for idempotency expire date: ', error ); throw error; @@ -2026,13 +2026,13 @@ class DatabaseController { } _expandResultOnKeyPath(object: any, key: string, value: any): any { - if (key.indexOf(".") < 0) { + if (key.indexOf('.') < 0) { object[key] = value[key]; return object; } - const path = key.split("."); + const path = key.split('.'); const firstKey = path[0]; - const nextPath = path.slice(1).join("."); + const nextPath = path.slice(1).join('.'); // Scan request data for denied keywords if (this.options && this.options.requestKeywordDenylist) { @@ -2071,9 +2071,9 @@ class DatabaseController { // determine if that was an op if ( keyUpdate && - typeof keyUpdate === "object" && + typeof keyUpdate === 'object' && keyUpdate.__op && - ["Add", "AddUnique", "Remove", "Increment", "SetOnInsert"].indexOf( + ['Add', 'AddUnique', 'Remove', 'Increment', 'SetOnInsert'].indexOf( keyUpdate.__op ) > -1 ) { @@ -2081,10 +2081,10 @@ class DatabaseController { // the op may have happened on a keypath this._expandResultOnKeyPath(response, key, result); // Revert array to object conversion on dot notation for arrays (e.g. "field.0.key") - if (key.includes(".")) { - const [field, index] = key.split("."); + if (key.includes('.')) { + const [field, index] = key.split('.'); const isArrayIndex = Array.from(index).every( - c => c >= "0" && c <= "9" + c => c >= '0' && c <= '9' ); if ( isArrayIndex && diff --git a/src/Controllers/FilesController.js b/src/Controllers/FilesController.js index d6cccc402e..20f1f1d588 100644 --- a/src/Controllers/FilesController.js +++ b/src/Controllers/FilesController.js @@ -1,12 +1,12 @@ // FilesController.js -import { randomHexString } from "../cryptoUtils"; -import AdaptableController from "./AdaptableController"; -import { validateFilename, FilesAdapter } from "../Adapters/Files/FilesAdapter"; -import path from "path"; -const Parse = require("parse").Parse; +import { randomHexString } from '../cryptoUtils'; +import AdaptableController from './AdaptableController'; +import { validateFilename, FilesAdapter } from '../Adapters/Files/FilesAdapter'; +import path from 'path'; +const Parse = require('parse').Parse; const legacyFilesRegex = new RegExp( - "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}-.*" + '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}-.*' ); export class FilesController extends AdaptableController { @@ -18,15 +18,15 @@ export class FilesController extends AdaptableController { const extname = path.extname(filename); const hasExtension = extname.length > 0; - const mime = (await import("mime")).default; + const mime = (await import('mime')).default; if (!hasExtension && contentType && mime.getExtension(contentType)) { - filename = filename + "." + mime.getExtension(contentType); + filename = filename + '.' + mime.getExtension(contentType); } else if (hasExtension && !contentType) { contentType = mime.getType(filename); } if (!this.options.preserveFileName) { - filename = randomHexString(32) + "_" + filename; + filename = randomHexString(32) + '_' + filename; } const location = await this.adapter.getFileLocation(config, filename); @@ -42,7 +42,7 @@ export class FilesController extends AdaptableController { } getMetadata(filename) { - if (typeof this.adapter.getMetadata === "function") { + if (typeof this.adapter.getMetadata === 'function') { return this.adapter.getMetadata(filename); } return Promise.resolve({}); @@ -59,39 +59,39 @@ export class FilesController extends AdaptableController { await Promise.all(promises); return; } - if (typeof object !== "object") { + if (typeof object !== 'object') { return; } for (const key in object) { const fileObject = object[key]; - if (fileObject && fileObject["__type"] === "File") { - if (fileObject["url"]) { + if (fileObject && fileObject['__type'] === 'File') { + if (fileObject['url']) { continue; } - const filename = fileObject["name"]; + const filename = fileObject['name']; // all filenames starting with "tfss-" should be from files.parsetfss.com // all filenames starting with a "-" seperated UUID should be from files.parse.com // all other filenames have been migrated or created from Parse Server if (config.fileKey === undefined) { - fileObject["url"] = await this.adapter.getFileLocation( + fileObject['url'] = await this.adapter.getFileLocation( config, filename ); } else { - if (filename.indexOf("tfss-") === 0) { - fileObject["url"] = - "http://files.parsetfss.com/" + + if (filename.indexOf('tfss-') === 0) { + fileObject['url'] = + 'http://files.parsetfss.com/' + config.fileKey + - "/" + + '/' + encodeURIComponent(filename); } else if (legacyFilesRegex.test(filename)) { - fileObject["url"] = - "http://files.parse.com/" + + fileObject['url'] = + 'http://files.parse.com/' + config.fileKey + - "/" + + '/' + encodeURIComponent(filename); } else { - fileObject["url"] = await this.adapter.getFileLocation( + fileObject['url'] = await this.adapter.getFileLocation( config, filename ); @@ -110,9 +110,9 @@ export class FilesController extends AdaptableController { } validateFilename(filename) { - if (typeof this.adapter.validateFilename === "function") { + if (typeof this.adapter.validateFilename === 'function') { const error = this.adapter.validateFilename(filename); - if (typeof error !== "string") { + if (typeof error !== 'string') { return error; } return new Parse.Error(Parse.Error.INVALID_FILE_NAME, error); diff --git a/src/Controllers/HooksController.js b/src/Controllers/HooksController.js index 4999913f78..6bfd1fe3a1 100644 --- a/src/Controllers/HooksController.js +++ b/src/Controllers/HooksController.js @@ -1,15 +1,15 @@ /** @flow weak */ -import * as triggers from "../triggers"; +import * as triggers from '../triggers'; // @flow-disable-next -import * as Parse from "parse/node"; +import * as Parse from 'parse/node'; // @flow-disable-next -import request from "../request"; -import { logger } from "../logger"; -import http from "http"; -import https from "https"; +import request from '../request'; +import { logger } from '../logger'; +import http from 'http'; +import https from 'https'; -const DefaultHooksCollectionName = "_Hooks"; +const DefaultHooksCollectionName = '_Hooks'; const HTTPAgents = { http: new http.Agent({ keepAlive: true }), https: new https.Agent({ keepAlive: true }), @@ -96,7 +96,7 @@ export class HooksController { } else if (hook.triggerName && hook.className && hook.url) { query = { className: hook.className, triggerName: hook.triggerName }; } else { - throw new Parse.Error(143, "invalid hook declaration"); + throw new Parse.Error(143, 'invalid hook declaration'); } return this.database .update(DefaultHooksCollectionName, query, hook, { upsert: true }) @@ -148,7 +148,7 @@ export class HooksController { hook.url = aHook.url; hook.triggerName = aHook.triggerName; } else { - throw new Parse.Error(143, "invalid hook declaration"); + throw new Parse.Error(143, 'invalid hook declaration'); } return this.addHook(hook); @@ -180,7 +180,7 @@ export class HooksController { ); } - throw new Parse.Error(143, "invalid hook declaration"); + throw new Parse.Error(143, 'invalid hook declaration'); } updateHook(aHook) { @@ -204,7 +204,7 @@ export class HooksController { } ); } - throw new Parse.Error(143, "invalid hook declaration"); + throw new Parse.Error(143, 'invalid hook declaration'); } } @@ -225,22 +225,22 @@ function wrapToHTTPRequest(hook, key) { const jsonRequest: any = { url: hook.url, headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, body: jsonBody, - method: "POST", + method: 'POST', }; - const agent = hook.url.startsWith("https") - ? HTTPAgents["https"] - : HTTPAgents["http"]; + const agent = hook.url.startsWith('https') + ? HTTPAgents['https'] + : HTTPAgents['http']; jsonRequest.agent = agent; if (key) { - jsonRequest.headers["X-Parse-Webhook-Key"] = key; + jsonRequest.headers['X-Parse-Webhook-Key'] = key; } else { logger.warn( - "Making outgoing webhook request without webhookKey being set!" + 'Making outgoing webhook request without webhookKey being set!' ); } return request(jsonRequest).then(response => { @@ -248,12 +248,12 @@ function wrapToHTTPRequest(hook, key) { let result; let body = response.data; if (body) { - if (typeof body === "string") { + if (typeof body === 'string') { try { body = JSON.parse(body); } catch (e) { err = { - error: "Malformed response", + error: 'Malformed response', code: -1, partialResponse: body.substring(0, 100), }; @@ -266,8 +266,8 @@ function wrapToHTTPRequest(hook, key) { } if (err) { throw err; - } else if (hook.triggerName === "beforeSave") { - if (typeof result === "object") { + } else if (hook.triggerName === 'beforeSave') { + if (typeof result === 'object') { delete result.createdAt; delete result.updatedAt; delete result.className; diff --git a/src/Controllers/LoggerController.js b/src/Controllers/LoggerController.js index 3d641cad04..0d32a41993 100644 --- a/src/Controllers/LoggerController.js +++ b/src/Controllers/LoggerController.js @@ -1,37 +1,37 @@ -import { Parse } from "parse/node"; -import AdaptableController from "./AdaptableController"; -import { LoggerAdapter } from "../Adapters/Logger/LoggerAdapter"; +import { Parse } from 'parse/node'; +import AdaptableController from './AdaptableController'; +import { LoggerAdapter } from '../Adapters/Logger/LoggerAdapter'; const MILLISECONDS_IN_A_DAY = 24 * 60 * 60 * 1000; const LOG_STRING_TRUNCATE_LENGTH = 1000; -const truncationMarker = "... (truncated)"; +const truncationMarker = '... (truncated)'; export const LogLevel = { - INFO: "info", - ERROR: "error", + INFO: 'info', + ERROR: 'error', }; export const LogOrder = { - DESCENDING: "desc", - ASCENDING: "asc", + DESCENDING: 'desc', + ASCENDING: 'asc', }; export const logLevels = [ - "error", - "warn", - "info", - "debug", - "verbose", - "silly", - "silent", + 'error', + 'warn', + 'info', + 'debug', + 'verbose', + 'silly', + 'silent', ]; export class LoggerController extends AdaptableController { - constructor(adapter, appId, options = { logLevel: "info" }) { + constructor(adapter, appId, options = { logLevel: 'info' }) { super(adapter, appId, options); - let level = "info"; + let level = 'info'; if (options.verbose) { - level = "verbose"; + level = 'verbose'; } if (options.logLevel) { level = options.logLevel; @@ -46,18 +46,18 @@ export class LoggerController extends AdaptableController { } maskSensitiveUrl(path) { - const urlString = "http://localhost" + path; // prepend dummy string to make a real URL + const urlString = 'http://localhost' + path; // prepend dummy string to make a real URL const urlObj = new URL(urlString); const query = urlObj.searchParams; - let sanitizedQuery = "?"; + let sanitizedQuery = '?'; for (const [key, value] of query) { - if (key !== "password") { + if (key !== 'password') { // normal value - sanitizedQuery += key + "=" + value + "&"; + sanitizedQuery += key + '=' + value + '&'; } else { // password value, redact it - sanitizedQuery += key + "=" + "********" + "&"; + sanitizedQuery += key + '=' + '********' + '&'; } } @@ -74,7 +74,7 @@ export class LoggerController extends AdaptableController { return e; } - if (typeof e === "string") { + if (typeof e === 'string') { return e.replace(/(password".?:.?")[^"]*"/g, '$1********"'); } // else it is an object... @@ -82,12 +82,12 @@ export class LoggerController extends AdaptableController { // check the url if (e.url) { // for strings - if (typeof e.url === "string") { + if (typeof e.url === 'string') { e.url = this.maskSensitiveUrl(e.url); } else if (Array.isArray(e.url)) { // for strings in array e.url = e.url.map(item => { - if (typeof item === "string") { + if (typeof item === 'string') { return this.maskSensitiveUrl(item); } @@ -98,8 +98,8 @@ export class LoggerController extends AdaptableController { if (e.body) { for (const key of Object.keys(e.body)) { - if (key === "password") { - e.body[key] = "********"; + if (key === 'password') { + e.body[key] = '********'; break; } } @@ -107,8 +107,8 @@ export class LoggerController extends AdaptableController { if (e.params) { for (const key of Object.keys(e.params)) { - if (key === "password") { - e.params[key] = "********"; + if (key === 'password') { + e.params[key] = '********'; break; } } @@ -124,7 +124,7 @@ export class LoggerController extends AdaptableController { args = [].concat( level, args.map(arg => { - if (typeof arg === "function") { + if (typeof arg === 'function') { return arg(); } return arg; @@ -134,27 +134,27 @@ export class LoggerController extends AdaptableController { } info() { - return this.log("info", arguments); + return this.log('info', arguments); } error() { - return this.log("error", arguments); + return this.log('error', arguments); } warn() { - return this.log("warn", arguments); + return this.log('warn', arguments); } verbose() { - return this.log("verbose", arguments); + return this.log('verbose', arguments); } debug() { - return this.log("debug", arguments); + return this.log('debug', arguments); } silly() { - return this.log("silly", arguments); + return this.log('silly', arguments); } logRequest({ method, url, headers, body }) { @@ -234,13 +234,13 @@ export class LoggerController extends AdaptableController { if (!this.adapter) { throw new Parse.Error( Parse.Error.PUSH_MISCONFIGURED, - "Logger adapter is not available" + 'Logger adapter is not available' ); } - if (typeof this.adapter.query !== "function") { + if (typeof this.adapter.query !== 'function') { throw new Parse.Error( Parse.Error.PUSH_MISCONFIGURED, - "Querying logs is not supported with this adapter" + 'Querying logs is not supported with this adapter' ); } options = LoggerController.parseOptions(options); diff --git a/src/Controllers/SchemaController.js b/src/Controllers/SchemaController.js index baeb9a1994..3ec4cfe468 100644 --- a/src/Controllers/SchemaController.js +++ b/src/Controllers/SchemaController.js @@ -15,176 +15,176 @@ // different databases. // TODO: hide all schema logic inside the database adapter. // @flow-disable-next -const Parse = require("parse/node").Parse; -import { StorageAdapter } from "../Adapters/Storage/StorageAdapter"; -import SchemaCache from "../Adapters/Cache/SchemaCache"; -import DatabaseController from "./DatabaseController"; -import Config from "../Config"; +const Parse = require('parse/node').Parse; +import { StorageAdapter } from '../Adapters/Storage/StorageAdapter'; +import SchemaCache from '../Adapters/Cache/SchemaCache'; +import DatabaseController from './DatabaseController'; +import Config from '../Config'; // @flow-disable-next -import deepcopy from "deepcopy"; +import deepcopy from 'deepcopy'; import type { Schema, SchemaFields, ClassLevelPermissions, SchemaField, LoadSchemaOptions, -} from "./types"; +} from './types'; const defaultColumns: { [string]: SchemaFields } = Object.freeze({ // Contain the default columns for every parse object type (except _Join collection) _Default: { - objectId: { type: "String" }, - createdAt: { type: "Date" }, - updatedAt: { type: "Date" }, - ACL: { type: "ACL" }, + objectId: { type: 'String' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + ACL: { type: 'ACL' }, }, // The additional default columns for the _User collection (in addition to DefaultCols) _User: { - username: { type: "String" }, - password: { type: "String" }, - email: { type: "String" }, - emailVerified: { type: "Boolean" }, - authData: { type: "Object" }, + username: { type: 'String' }, + password: { type: 'String' }, + email: { type: 'String' }, + emailVerified: { type: 'Boolean' }, + authData: { type: 'Object' }, }, // The additional default columns for the _Installation collection (in addition to DefaultCols) _Installation: { - installationId: { type: "String" }, - deviceToken: { type: "String" }, - channels: { type: "Array" }, - deviceType: { type: "String" }, - pushType: { type: "String" }, - GCMSenderId: { type: "String" }, - timeZone: { type: "String" }, - localeIdentifier: { type: "String" }, - badge: { type: "Number" }, - appVersion: { type: "String" }, - appName: { type: "String" }, - appIdentifier: { type: "String" }, - parseVersion: { type: "String" }, + installationId: { type: 'String' }, + deviceToken: { type: 'String' }, + channels: { type: 'Array' }, + deviceType: { type: 'String' }, + pushType: { type: 'String' }, + GCMSenderId: { type: 'String' }, + timeZone: { type: 'String' }, + localeIdentifier: { type: 'String' }, + badge: { type: 'Number' }, + appVersion: { type: 'String' }, + appName: { type: 'String' }, + appIdentifier: { type: 'String' }, + parseVersion: { type: 'String' }, }, // The additional default columns for the _Role collection (in addition to DefaultCols) _Role: { - name: { type: "String" }, - users: { type: "Relation", targetClass: "_User" }, - roles: { type: "Relation", targetClass: "_Role" }, + name: { type: 'String' }, + users: { type: 'Relation', targetClass: '_User' }, + roles: { type: 'Relation', targetClass: '_Role' }, }, // The additional default columns for the _Session collection (in addition to DefaultCols) _Session: { - user: { type: "Pointer", targetClass: "_User" }, - installationId: { type: "String" }, - sessionToken: { type: "String" }, - expiresAt: { type: "Date" }, - createdWith: { type: "Object" }, + user: { type: 'Pointer', targetClass: '_User' }, + installationId: { type: 'String' }, + sessionToken: { type: 'String' }, + expiresAt: { type: 'Date' }, + createdWith: { type: 'Object' }, }, _Product: { - productIdentifier: { type: "String" }, - download: { type: "File" }, - downloadName: { type: "String" }, - icon: { type: "File" }, - order: { type: "Number" }, - title: { type: "String" }, - subtitle: { type: "String" }, + productIdentifier: { type: 'String' }, + download: { type: 'File' }, + downloadName: { type: 'String' }, + icon: { type: 'File' }, + order: { type: 'Number' }, + title: { type: 'String' }, + subtitle: { type: 'String' }, }, _PushStatus: { - pushTime: { type: "String" }, - source: { type: "String" }, // rest or webui - query: { type: "String" }, // the stringified JSON query - payload: { type: "String" }, // the stringified JSON payload, - title: { type: "String" }, - expiry: { type: "Number" }, - expiration_interval: { type: "Number" }, - status: { type: "String" }, - numSent: { type: "Number" }, - numFailed: { type: "Number" }, - pushHash: { type: "String" }, - errorMessage: { type: "Object" }, - sentPerType: { type: "Object" }, - failedPerType: { type: "Object" }, - sentPerUTCOffset: { type: "Object" }, - failedPerUTCOffset: { type: "Object" }, - count: { type: "Number" }, // tracks # of batches queued and pending + pushTime: { type: 'String' }, + source: { type: 'String' }, // rest or webui + query: { type: 'String' }, // the stringified JSON query + payload: { type: 'String' }, // the stringified JSON payload, + title: { type: 'String' }, + expiry: { type: 'Number' }, + expiration_interval: { type: 'Number' }, + status: { type: 'String' }, + numSent: { type: 'Number' }, + numFailed: { type: 'Number' }, + pushHash: { type: 'String' }, + errorMessage: { type: 'Object' }, + sentPerType: { type: 'Object' }, + failedPerType: { type: 'Object' }, + sentPerUTCOffset: { type: 'Object' }, + failedPerUTCOffset: { type: 'Object' }, + count: { type: 'Number' }, // tracks # of batches queued and pending }, _JobStatus: { - jobName: { type: "String" }, - source: { type: "String" }, - status: { type: "String" }, - message: { type: "String" }, - params: { type: "Object" }, // params received when calling the job - finishedAt: { type: "Date" }, + jobName: { type: 'String' }, + source: { type: 'String' }, + status: { type: 'String' }, + message: { type: 'String' }, + params: { type: 'Object' }, // params received when calling the job + finishedAt: { type: 'Date' }, }, _JobSchedule: { - jobName: { type: "String" }, - description: { type: "String" }, - params: { type: "String" }, - startAfter: { type: "String" }, - daysOfWeek: { type: "Array" }, - timeOfDay: { type: "String" }, - lastRun: { type: "Number" }, - repeatMinutes: { type: "Number" }, + jobName: { type: 'String' }, + description: { type: 'String' }, + params: { type: 'String' }, + startAfter: { type: 'String' }, + daysOfWeek: { type: 'Array' }, + timeOfDay: { type: 'String' }, + lastRun: { type: 'Number' }, + repeatMinutes: { type: 'Number' }, }, _Hooks: { - functionName: { type: "String" }, - className: { type: "String" }, - triggerName: { type: "String" }, - url: { type: "String" }, + functionName: { type: 'String' }, + className: { type: 'String' }, + triggerName: { type: 'String' }, + url: { type: 'String' }, }, _GlobalConfig: { - objectId: { type: "String" }, - params: { type: "Object" }, - masterKeyOnly: { type: "Object" }, + objectId: { type: 'String' }, + params: { type: 'Object' }, + masterKeyOnly: { type: 'Object' }, }, _GraphQLConfig: { - objectId: { type: "String" }, - config: { type: "Object" }, + objectId: { type: 'String' }, + config: { type: 'Object' }, }, _Audience: { - objectId: { type: "String" }, - name: { type: "String" }, - query: { type: "String" }, //storing query as JSON string to prevent "Nested keys should not contain the '$' or '.' characters" error - lastUsed: { type: "Date" }, - timesUsed: { type: "Number" }, + objectId: { type: 'String' }, + name: { type: 'String' }, + query: { type: 'String' }, //storing query as JSON string to prevent "Nested keys should not contain the '$' or '.' characters" error + lastUsed: { type: 'Date' }, + timesUsed: { type: 'Number' }, }, _Idempotency: { - reqId: { type: "String" }, - expire: { type: "Date" }, + reqId: { type: 'String' }, + expire: { type: 'Date' }, }, }); // fields required for read or write operations on their respective classes. const requiredColumns = Object.freeze({ read: { - _User: ["username"], + _User: ['username'], }, write: { - _Product: ["productIdentifier", "icon", "order", "title", "subtitle"], - _Role: ["name", "ACL"], + _Product: ['productIdentifier', 'icon', 'order', 'title', 'subtitle'], + _Role: ['name', 'ACL'], }, }); -const invalidColumns = ["length"]; +const invalidColumns = ['length']; const systemClasses = Object.freeze([ - "_User", - "_Installation", - "_Role", - "_Session", - "_Product", - "_PushStatus", - "_JobStatus", - "_JobSchedule", - "_Audience", - "_Idempotency", + '_User', + '_Installation', + '_Role', + '_Session', + '_Product', + '_PushStatus', + '_JobStatus', + '_JobSchedule', + '_Audience', + '_Idempotency', ]); const volatileClasses = Object.freeze([ - "_JobStatus", - "_PushStatus", - "_Hooks", - "_GlobalConfig", - "_GraphQLConfig", - "_JobSchedule", - "_Audience", - "_Idempotency", + '_JobStatus', + '_PushStatus', + '_Hooks', + '_GlobalConfig', + '_GraphQLConfig', + '_JobSchedule', + '_Audience', + '_Idempotency', ]); // Anything that start with role @@ -255,17 +255,17 @@ function validateProtectedFieldsKey(key, userIdRegExp) { } const CLPValidKeys = Object.freeze([ - "ACL", - "find", - "count", - "get", - "create", - "update", - "delete", - "addField", - "readUserFields", - "writeUserFields", - "protectedFields", + 'ACL', + 'find', + 'count', + 'get', + 'create', + 'update', + 'delete', + 'addField', + 'readUserFields', + 'writeUserFields', + 'protectedFields', ]); // validation before setting class-level permissions on collection @@ -292,8 +292,8 @@ function validateCLP( validateCLPjson(operation, operationKey); if ( - operationKey === "readUserFields" || - operationKey === "writeUserFields" + operationKey === 'readUserFields' || + operationKey === 'writeUserFields' ) { // validate grouped pointer permissions // must be an array with field names @@ -306,7 +306,7 @@ function validateCLP( } // validate protected fields - if (operationKey === "protectedFields") { + if (operationKey === 'protectedFields') { for (const entity in operation) { // throws on unexpected key validateProtectedFieldsKey(entity, userIdRegExp); @@ -355,7 +355,7 @@ function validateCLP( // entity can be either: // "pointerFields": string[] - if (entity === "pointerFields") { + if (entity === 'pointerFields') { const pointerFields = operation[entity]; if (Array.isArray(pointerFields)) { @@ -374,30 +374,30 @@ function validateCLP( const permit = operation[entity]; - if (operationKey === "ACL") { - if (Object.prototype.toString.call(permit) !== "[object Object]") { + if (operationKey === 'ACL') { + if (Object.prototype.toString.call(permit) !== '[object Object]') { throw new Parse.Error( Parse.Error.INVALID_JSON, `'${permit}' is not a valid value for class level permissions acl` ); } const invalidKeys = Object.keys(permit).filter( - key => !["read", "write"].includes(key) + key => !['read', 'write'].includes(key) ); const invalidValues = Object.values(permit).filter( - key => typeof key !== "boolean" + key => typeof key !== 'boolean' ); if (invalidKeys.length) { throw new Parse.Error( Parse.Error.INVALID_JSON, - `'${invalidKeys.join(",")}' is not a valid key for class level permissions acl` + `'${invalidKeys.join(',')}' is not a valid key for class level permissions acl` ); } if (invalidValues.length) { throw new Parse.Error( Parse.Error.INVALID_JSON, - `'${invalidValues.join(",")}' is not a valid value for class level permissions acl` + `'${invalidValues.join(',')}' is not a valid value for class level permissions acl` ); } } else if (permit !== true) { @@ -411,7 +411,7 @@ function validateCLP( } function validateCLPjson(operation: any, operationKey: string) { - if (operationKey === "readUserFields" || operationKey === "writeUserFields") { + if (operationKey === 'readUserFields' || operationKey === 'writeUserFields') { if (!Array.isArray(operation)) { throw new Parse.Error( Parse.Error.INVALID_JSON, @@ -419,7 +419,7 @@ function validateCLPjson(operation: any, operationKey: string) { ); } } else { - if (typeof operation === "object" && operation !== null) { + if (typeof operation === 'object' && operation !== null) { // ok to proceed return; } else { @@ -446,9 +446,9 @@ function validatePointerPermission( if ( !( fields[fieldName] && - ((fields[fieldName].type == "Pointer" && - fields[fieldName].targetClass == "_User") || - fields[fieldName].type == "Array") + ((fields[fieldName].type == 'Pointer' && + fields[fieldName].targetClass == '_User') || + fields[fieldName].type == 'Array') ) ) { throw new Parse.Error( @@ -475,8 +475,8 @@ function classNameIsValid(className: string): boolean { // Valid fields must be alpha-numeric, and not start with an underscore or number // must not be a reserved key function fieldNameIsValid(fieldName: string, className: string): boolean { - if (className && className !== "_Hooks") { - if (fieldName === "className") { + if (className && className !== '_Hooks') { + if (fieldName === 'className') { return false; } } @@ -504,34 +504,34 @@ function fieldNameIsValidForClass( function invalidClassNameMessage(className: string): string { return ( - "Invalid classname: " + + 'Invalid classname: ' + className + - ", classnames can only have alphanumeric characters and _, and must start with an alpha character " + ', classnames can only have alphanumeric characters and _, and must start with an alpha character ' ); } const invalidJsonError = new Parse.Error( Parse.Error.INVALID_JSON, - "invalid JSON" + 'invalid JSON' ); const validNonRelationOrPointerTypes = [ - "Number", - "String", - "Boolean", - "Date", - "Object", - "Array", - "GeoPoint", - "File", - "Bytes", - "Polygon", + 'Number', + 'String', + 'Boolean', + 'Date', + 'Object', + 'Array', + 'GeoPoint', + 'File', + 'Bytes', + 'Polygon', ]; // Returns an error suitable for throwing if the type is invalid const fieldTypeIsInvalid = ({ type, targetClass }) => { - if (["Pointer", "Relation"].indexOf(type) >= 0) { + if (['Pointer', 'Relation'].indexOf(type) >= 0) { if (!targetClass) { return new Parse.Error(135, `type ${type} needs a class name`); - } else if (typeof targetClass !== "string") { + } else if (typeof targetClass !== 'string') { return invalidJsonError; } else if (!classNameIsValid(targetClass)) { return new Parse.Error( @@ -542,7 +542,7 @@ const fieldTypeIsInvalid = ({ type, targetClass }) => { return undefined; } } - if (typeof type !== "string") { + if (typeof type !== 'string') { return invalidJsonError; } if (validNonRelationOrPointerTypes.indexOf(type) < 0) { @@ -557,12 +557,12 @@ const fieldTypeIsInvalid = ({ type, targetClass }) => { const convertSchemaToAdapterSchema = (schema: any) => { schema = injectDefaultSchema(schema); delete schema.fields.ACL; - schema.fields._rperm = { type: "Array" }; - schema.fields._wperm = { type: "Array" }; + schema.fields._rperm = { type: 'Array' }; + schema.fields._wperm = { type: 'Array' }; - if (schema.className === "_User") { + if (schema.className === '_User') { delete schema.fields.password; - schema.fields._hashed_password = { type: "String" }; + schema.fields._hashed_password = { type: 'String' }; } return schema; @@ -572,12 +572,12 @@ const convertAdapterSchemaToParseSchema = ({ ...schema }) => { delete schema.fields._rperm; delete schema.fields._wperm; - schema.fields.ACL = { type: "ACL" }; + schema.fields.ACL = { type: 'ACL' }; - if (schema.className === "_User") { + if (schema.className === '_User') { delete schema.fields.authData; //Auth data is implicit delete schema.fields._hashed_password; - schema.fields.password = { type: "String" }; + schema.fields.password = { type: 'String' }; } if (schema.indexes && Object.keys(schema.indexes).length === 0) { @@ -669,46 +669,46 @@ const injectDefaultSchema = ({ return defaultSchema; }; -const _HooksSchema = { className: "_Hooks", fields: defaultColumns._Hooks }; +const _HooksSchema = { className: '_Hooks', fields: defaultColumns._Hooks }; const _GlobalConfigSchema = { - className: "_GlobalConfig", + className: '_GlobalConfig', fields: defaultColumns._GlobalConfig, }; const _GraphQLConfigSchema = { - className: "_GraphQLConfig", + className: '_GraphQLConfig', fields: defaultColumns._GraphQLConfig, }; const _PushStatusSchema = convertSchemaToAdapterSchema( injectDefaultSchema({ - className: "_PushStatus", + className: '_PushStatus', fields: {}, classLevelPermissions: {}, }) ); const _JobStatusSchema = convertSchemaToAdapterSchema( injectDefaultSchema({ - className: "_JobStatus", + className: '_JobStatus', fields: {}, classLevelPermissions: {}, }) ); const _JobScheduleSchema = convertSchemaToAdapterSchema( injectDefaultSchema({ - className: "_JobSchedule", + className: '_JobSchedule', fields: {}, classLevelPermissions: {}, }) ); const _AudienceSchema = convertSchemaToAdapterSchema( injectDefaultSchema({ - className: "_Audience", + className: '_Audience', fields: defaultColumns._Audience, classLevelPermissions: {}, }) ); const _IdempotencySchema = convertSchemaToAdapterSchema( injectDefaultSchema({ - className: "_Idempotency", + className: '_Idempotency', fields: defaultColumns._Idempotency, classLevelPermissions: {}, }) @@ -744,7 +744,7 @@ const dbTypeMatchesObjectType = ( }; const typeToString = (type: SchemaField | string): string => { - if (typeof type === "string") { + if (typeof type === 'string') { return type; } if (type.targetClass) { @@ -944,11 +944,11 @@ export default class SchemaController { if ( existingFields[name] && existingFields[name].type !== field.type && - field.__op !== "Delete" + field.__op !== 'Delete' ) { throw new Parse.Error(255, `Field ${name} exists, cannot update.`); } - if (!existingFields[name] && field.__op === "Delete") { + if (!existingFields[name] && field.__op === 'Delete') { throw new Parse.Error( 255, `Field ${name} does not exist, cannot delete.` @@ -980,7 +980,7 @@ export default class SchemaController { const deletedFields: string[] = []; const insertedFields = []; Object.keys(submittedFields).forEach(fieldName => { - if (submittedFields[fieldName].__op === "Delete") { + if (submittedFields[fieldName].__op === 'Delete') { deletedFields.push(fieldName); } else { insertedFields.push(fieldName); @@ -1079,7 +1079,7 @@ export default class SchemaController { // The schema still doesn't validate. Give up throw new Parse.Error( Parse.Error.INVALID_JSON, - "schema class name does not revalidate" + 'schema class name does not revalidate' ); }) ); @@ -1121,13 +1121,13 @@ export default class SchemaController { if (!fieldNameIsValid(fieldName, className)) { return { code: Parse.Error.INVALID_KEY_NAME, - error: "invalid field name: " + fieldName, + error: 'invalid field name: ' + fieldName, }; } if (!fieldNameIsValidForClass(fieldName, className)) { return { code: 136, - error: "field " + fieldName + " cannot be added", + error: 'field ' + fieldName + ' cannot be added', }; } const fieldType = fields[fieldName]; @@ -1137,11 +1137,11 @@ export default class SchemaController { } if (fieldType.defaultValue !== undefined) { let defaultValueType = getType(fieldType.defaultValue); - if (typeof defaultValueType === "string") { + if (typeof defaultValueType === 'string') { defaultValueType = { type: defaultValueType }; } else if ( - typeof defaultValueType === "object" && - fieldType.type === "Relation" + typeof defaultValueType === 'object' && + fieldType.type === 'Relation' ) { return { code: Parse.Error.INCORRECT_TYPE, @@ -1157,7 +1157,7 @@ export default class SchemaController { }; } } else if (fieldType.required) { - if (typeof fieldType === "object" && fieldType.type === "Relation") { + if (typeof fieldType === 'object' && fieldType.type === 'Relation') { return { code: Parse.Error.INCORRECT_TYPE, error: `The 'required' option is not applicable for ${typeToString(fieldType)}`, @@ -1172,17 +1172,17 @@ export default class SchemaController { } const geoPoints = Object.keys(fields).filter( - key => fields[key] && fields[key].type === "GeoPoint" + key => fields[key] && fields[key].type === 'GeoPoint' ); if (geoPoints.length > 1) { return { code: Parse.Error.INCORRECT_TYPE, error: - "currently, only one GeoPoint field may exist in an object. Adding " + + 'currently, only one GeoPoint field may exist in an object. Adding ' + geoPoints[1] + - " when " + + ' when ' + geoPoints[0] + - " already exists.", + ' already exists.', }; } validateCLP(classLevelPermissions, fields, this.userIdRegEx); @@ -1190,7 +1190,7 @@ export default class SchemaController { // Sets the Class-level permissions for a given className, which must exist. async setPermissions(className: string, perms: any, newSchema: SchemaFields) { - if (typeof perms === "undefined") { + if (typeof perms === 'undefined') { return Promise.resolve(); } validateCLP(perms, newSchema, this.userIdRegEx); @@ -1212,24 +1212,24 @@ export default class SchemaController { isValidation?: boolean, maintenance?: boolean ) { - if (fieldName.indexOf(".") > 0) { + if (fieldName.indexOf('.') > 0) { // "." for Nested Arrays // "." for Nested Objects // JSON Arrays are treated as Nested Objects - const [x, y] = fieldName.split("."); + const [x, y] = fieldName.split('.'); fieldName = x; - const isArrayIndex = Array.from(y).every(c => c >= "0" && c <= "9"); + const isArrayIndex = Array.from(y).every(c => c >= '0' && c <= '9'); if ( isArrayIndex && - !["sentPerUTCOffset", "failedPerUTCOffset"].includes(fieldName) + !['sentPerUTCOffset', 'failedPerUTCOffset'].includes(fieldName) ) { - type = "Array"; + type = 'Array'; } else { - type = "Object"; + type = 'Object'; } } let fieldNameToValidate = `${fieldName}`; - if (maintenance && fieldNameToValidate.charAt(0) === "_") { + if (maintenance && fieldNameToValidate.charAt(0) === '_') { fieldNameToValidate = fieldNameToValidate.substring(1); } if (!fieldNameIsValid(fieldNameToValidate, className)) { @@ -1245,13 +1245,13 @@ export default class SchemaController { } const expectedType = this.getExpectedType(className, fieldName); - if (typeof type === "string") { + if (typeof type === 'string') { type = ({ type }: SchemaField); } if (type.defaultValue !== undefined) { let defaultValueType = getType(type.defaultValue); - if (typeof defaultValueType === "string") { + if (typeof defaultValueType === 'string') { defaultValueType = { type: defaultValueType }; } if (!dbTypeMatchesObjectType(type, defaultValueType)) { @@ -1312,7 +1312,7 @@ export default class SchemaController { const { className, fieldName } = fields[i]; let { type } = fields[i]; const expectedType = this.getExpectedType(className, fieldName); - if (typeof type === "string") { + if (typeof type === 'string') { type = { type: type }; } if (!expectedType || !dbTypeMatchesObjectType(expectedType, type)) { @@ -1393,7 +1393,7 @@ export default class SchemaController { return Promise.all( fieldNames.map(fieldName => { const field = schemaFields[fieldName]; - if (field && field.type === "Relation") { + if (field && field.type === 'Relation') { //For relations, drop the _Join table return database.adapter.deleteClass( `_Join:${fieldName}:${className}` @@ -1423,14 +1423,14 @@ export default class SchemaController { const promises = []; for (const fieldName in object) { - if (object[fieldName] && getType(object[fieldName]) === "GeoPoint") { + if (object[fieldName] && getType(object[fieldName]) === 'GeoPoint') { geocount++; } if (geocount > 1) { return Promise.reject( new Parse.Error( Parse.Error.INCORRECT_TYPE, - "there can only be one geopoint field in a class" + 'there can only be one geopoint field in a class' ) ); } @@ -1443,7 +1443,7 @@ export default class SchemaController { if (!expected) { continue; } - if (fieldName === "ACL") { + if (fieldName === 'ACL') { // Every object has ACL implicitly. continue; } @@ -1479,9 +1479,9 @@ export default class SchemaController { const missingColumns = columns.filter(function (column) { if (query && query.objectId) { - if (object[column] && typeof object[column] === "object") { + if (object[column] && typeof object[column] === 'object') { // Trying to delete a required column - return object[column].__op == "Delete"; + return object[column].__op == 'Delete'; } // Not trying to do anything there return false; @@ -1492,7 +1492,7 @@ export default class SchemaController { if (missingColumns.length > 0) { throw new Parse.Error( Parse.Error.INCORRECT_TYPE, - missingColumns[0] + " is required." + missingColumns[0] + ' is required.' ); } return Promise.resolve(this); @@ -1520,7 +1520,7 @@ export default class SchemaController { return true; } const perms = classPermissions[operation]; - if (perms["*"]) { + if (perms['*']) { return true; } // Check permissions against the aclGroup provided (array of userId/roles) @@ -1554,17 +1554,17 @@ export default class SchemaController { const perms = classPermissions[operation]; // If only for authenticated users // make sure we have an aclGroup - if (perms["requiresAuthentication"]) { + if (perms['requiresAuthentication']) { // If aclGroup has * (public) if (!aclGroup || aclGroup.length == 0) { throw new Parse.Error( Parse.Error.OBJECT_NOT_FOUND, - "Permission denied, user needs to be authenticated." + 'Permission denied, user needs to be authenticated.' ); - } else if (aclGroup.indexOf("*") > -1 && aclGroup.length == 1) { + } else if (aclGroup.indexOf('*') > -1 && aclGroup.length == 1) { throw new Parse.Error( Parse.Error.OBJECT_NOT_FOUND, - "Permission denied, user needs to be authenticated." + 'Permission denied, user needs to be authenticated.' ); } // requiresAuthentication passed, just move forward @@ -1575,12 +1575,12 @@ export default class SchemaController { // No matching CLP, let's check the Pointer permissions // And handle those later const permissionField = - ["get", "find", "count"].indexOf(operation) > -1 - ? "readUserFields" - : "writeUserFields"; + ['get', 'find', 'count'].indexOf(operation) > -1 + ? 'readUserFields' + : 'writeUserFields'; // Reject create when write lockdown - if (permissionField == "writeUserFields" && operation == "create") { + if (permissionField == 'writeUserFields' && operation == 'create') { throw new Parse.Error( Parse.Error.OPERATION_FORBIDDEN, `Permission denied for action ${operation} on class ${className}.` @@ -1598,7 +1598,7 @@ export default class SchemaController { const pointerFields = classPermissions[operation].pointerFields; if (Array.isArray(pointerFields) && pointerFields.length > 0) { // any op except 'addField as part of create' is ok. - if (operation !== "addField" || action === "update") { + if (operation !== 'addField' || action === 'update') { // We can allow adding field on update flow only. return Promise.resolve(); } @@ -1641,7 +1641,7 @@ export default class SchemaController { ): ?(SchemaField | string) { if (this.schemaData[className]) { const expectedType = this.schemaData[className].fields[fieldName]; - return expectedType === "map" ? "Object" : expectedType; + return expectedType === 'map' ? 'Object' : expectedType; } return undefined; } @@ -1682,11 +1682,11 @@ function buildMergedSchemaObject( : Object.keys(defaultColumns[existingFields._id]); for (const oldField in existingFields) { if ( - oldField !== "_id" && - oldField !== "ACL" && - oldField !== "updatedAt" && - oldField !== "createdAt" && - oldField !== "objectId" + oldField !== '_id' && + oldField !== 'ACL' && + oldField !== 'updatedAt' && + oldField !== 'createdAt' && + oldField !== 'objectId' ) { if ( sysSchemaField.length > 0 && @@ -1695,14 +1695,14 @@ function buildMergedSchemaObject( continue; } const fieldIsDeleted = - putRequest[oldField] && putRequest[oldField].__op === "Delete"; + putRequest[oldField] && putRequest[oldField].__op === 'Delete'; if (!fieldIsDeleted) { newSchema[oldField] = existingFields[oldField]; } } } for (const newField in putRequest) { - if (newField !== "objectId" && putRequest[newField].__op !== "Delete") { + if (newField !== 'objectId' && putRequest[newField].__op !== 'Delete') { if ( sysSchemaField.length > 0 && sysSchemaField.indexOf(newField) !== -1 @@ -1731,23 +1731,23 @@ function thenValidateRequiredColumns(schemaPromise, className, object, query) { function getType(obj: any): ?(SchemaField | string) { const type = typeof obj; switch (type) { - case "boolean": - return "Boolean"; - case "string": - return "String"; - case "number": - return "Number"; - case "map": - case "object": + case 'boolean': + return 'Boolean'; + case 'string': + return 'String'; + case 'number': + return 'Number'; + case 'map': + case 'object': if (!obj) { return undefined; } return getObjectType(obj); - case "function": - case "symbol": - case "undefined": + case 'function': + case 'symbol': + case 'undefined': default: - throw "bad obj: " + obj; + throw 'bad obj: ' + obj; } } @@ -1756,83 +1756,83 @@ function getType(obj: any): ?(SchemaField | string) { // Returns null if the type is unknown. function getObjectType(obj): ?(SchemaField | string) { if (obj instanceof Array) { - return "Array"; + return 'Array'; } if (obj.__type) { switch (obj.__type) { - case "Pointer": + case 'Pointer': if (obj.className) { return { - type: "Pointer", + type: 'Pointer', targetClass: obj.className, }; } break; - case "Relation": + case 'Relation': if (obj.className) { return { - type: "Relation", + type: 'Relation', targetClass: obj.className, }; } break; - case "File": + case 'File': if (obj.name) { - return "File"; + return 'File'; } break; - case "Date": + case 'Date': if (obj.iso) { - return "Date"; + return 'Date'; } break; - case "GeoPoint": + case 'GeoPoint': if (obj.latitude != null && obj.longitude != null) { - return "GeoPoint"; + return 'GeoPoint'; } break; - case "Bytes": + case 'Bytes': if (obj.base64) { - return "Bytes"; + return 'Bytes'; } break; - case "Polygon": + case 'Polygon': if (obj.coordinates) { - return "Polygon"; + return 'Polygon'; } break; } throw new Parse.Error( Parse.Error.INCORRECT_TYPE, - "This is not a valid " + obj.__type + 'This is not a valid ' + obj.__type ); } - if (obj["$ne"]) { - return getObjectType(obj["$ne"]); + if (obj['$ne']) { + return getObjectType(obj['$ne']); } if (obj.__op) { switch (obj.__op) { - case "Increment": - return "Number"; - case "Delete": + case 'Increment': + return 'Number'; + case 'Delete': return null; - case "Add": - case "AddUnique": - case "Remove": - return "Array"; - case "AddRelation": - case "RemoveRelation": + case 'Add': + case 'AddUnique': + case 'Remove': + return 'Array'; + case 'AddRelation': + case 'RemoveRelation': return { - type: "Relation", + type: 'Relation', targetClass: obj.objects[0].className, }; - case "Batch": + case 'Batch': return getObjectType(obj.ops[0]); default: - throw "unexpected op: " + obj.__op; + throw 'unexpected op: ' + obj.__op; } } - return "Object"; + return 'Object'; } export { diff --git a/src/Controllers/UserController.js b/src/Controllers/UserController.js index a2f51e186e..f8a06192a3 100644 --- a/src/Controllers/UserController.js +++ b/src/Controllers/UserController.js @@ -1,14 +1,14 @@ -import { randomString } from "../cryptoUtils"; -import { inflate } from "../triggers"; -import AdaptableController from "./AdaptableController"; -import MailAdapter from "../Adapters/Email/MailAdapter"; -import rest from "../rest"; -import Parse from "parse/node"; -import AccountLockout from "../AccountLockout"; -import Config from "../Config"; - -var RestQuery = require("../RestQuery"); -var Auth = require("../Auth"); +import { randomString } from '../cryptoUtils'; +import { inflate } from '../triggers'; +import AdaptableController from './AdaptableController'; +import MailAdapter from '../Adapters/Email/MailAdapter'; +import rest from '../rest'; +import Parse from 'parse/node'; +import AccountLockout from '../AccountLockout'; +import Config from '../Config'; + +var RestQuery = require('../RestQuery'); +var Auth = require('../Auth'); export class UserController extends AdaptableController { constructor(adapter, appId, options = {}) { @@ -38,7 +38,7 @@ export class UserController extends AdaptableController { async setEmailVerifyToken(user, req, storage = {}) { const shouldSendEmail = this.shouldVerifyEmails === true || - (typeof this.shouldVerifyEmails === "function" && + (typeof this.shouldVerifyEmails === 'function' && (await Promise.resolve(this.shouldVerifyEmails(req))) === true); if (!shouldSendEmail) { return false; @@ -47,7 +47,7 @@ export class UserController extends AdaptableController { user._email_verify_token = randomString(25); if ( !storage.fieldsChangedByTrigger || - !storage.fieldsChangedByTrigger.includes("emailVerified") + !storage.fieldsChangedByTrigger.includes('emailVerified') ) { user.emailVerified = false; } @@ -70,7 +70,7 @@ export class UserController extends AdaptableController { const query = { _email_verify_token: token }; const updateFields = { emailVerified: true, - _email_verify_token: { __op: "Delete" }, + _email_verify_token: { __op: 'Delete' }, }; // if the email verify token needs to be validated then @@ -79,14 +79,14 @@ export class UserController extends AdaptableController { query.emailVerified = false; query._email_verify_token_expires_at = { $gt: Parse._encode(new Date()) }; - updateFields._email_verify_token_expires_at = { __op: "Delete" }; + updateFields._email_verify_token_expires_at = { __op: 'Delete' }; } const maintenanceAuth = Auth.maintenance(this.config); const restQuery = await RestQuery({ method: RestQuery.Method.get, config: this.config, auth: maintenanceAuth, - className: "_User", + className: '_User', restWhere: query, }); @@ -97,7 +97,7 @@ export class UserController extends AdaptableController { return await rest.update( this.config, maintenanceAuth, - "_User", + '_User', query, updateFields ); @@ -105,7 +105,7 @@ export class UserController extends AdaptableController { async checkResetTokenValidity(token) { const results = await this.config.database.find( - "_User", + '_User', { _perishable_token: token, }, @@ -113,7 +113,7 @@ export class UserController extends AdaptableController { Auth.maintenance(this.config) ); if (results.length !== 1) { - throw "Failed to reset password: username / email / token is invalid"; + throw 'Failed to reset password: username / email / token is invalid'; } if ( @@ -121,11 +121,11 @@ export class UserController extends AdaptableController { this.config.passwordPolicy.resetTokenValidityDuration ) { let expiresDate = results[0]._perishable_token_expires_at; - if (expiresDate && expiresDate.__type == "Date") { + if (expiresDate && expiresDate.__type == 'Date') { expiresDate = new Date(expiresDate.iso); } if (expiresDate < new Date()) { - throw "The password reset link has expired"; + throw 'The password reset link has expired'; } } @@ -149,7 +149,7 @@ export class UserController extends AdaptableController { config: this.config, runBeforeFind: false, auth: Auth.master(this.config), - className: "_User", + className: '_User', restWhere: where, }); const result = await query.execute(); @@ -168,10 +168,10 @@ export class UserController extends AdaptableController { // from this point onwards; do not use the `user` as it may not contain all fields. const fetchedUser = await this.getUserIfNeeded(user); let shouldSendEmail = this.config.sendUserEmailVerification; - if (typeof shouldSendEmail === "function") { + if (typeof shouldSendEmail === 'function') { const response = await Promise.resolve( this.config.sendUserEmailVerification({ - user: Parse.Object.fromJSON({ className: "_User", ...fetchedUser }), + user: Parse.Object.fromJSON({ className: '_User', ...fetchedUser }), master: req.auth?.isMaster, }) ); @@ -184,7 +184,7 @@ export class UserController extends AdaptableController { const options = { appName: this.config.appName, link: link, - user: inflate("_User", fetchedUser), + user: inflate('_User', fetchedUser), }; if (this.adapter.sendVerificationEmail) { this.adapter.sendVerificationEmail(options); @@ -204,7 +204,7 @@ export class UserController extends AdaptableController { let { _email_verify_token_expires_at } = user; if ( _email_verify_token_expires_at && - _email_verify_token_expires_at.__type === "Date" + _email_verify_token_expires_at.__type === 'Date' ) { _email_verify_token_expires_at = _email_verify_token_expires_at.iso; } @@ -217,7 +217,7 @@ export class UserController extends AdaptableController { return Promise.resolve(true); } const shouldSend = await this.setEmailVerifyToken(user, { - object: Parse.User.fromJSON(Object.assign({ className: "_User" }, user)), + object: Parse.User.fromJSON(Object.assign({ className: '_User' }, user)), master, installationId, ip, @@ -227,7 +227,7 @@ export class UserController extends AdaptableController { return; } return this.config.database.update( - "_User", + '_User', { username: user.username }, user ); @@ -265,7 +265,7 @@ export class UserController extends AdaptableController { } return this.config.database.update( - "_User", + '_User', { $or: [{ email }, { username: email, email: { $exists: false } }] }, token, {}, @@ -275,7 +275,7 @@ export class UserController extends AdaptableController { async sendPasswordResetEmail(email) { if (!this.adapter) { - throw "Trying to send a reset password but no adapter is set"; + throw 'Trying to send a reset password but no adapter is set'; // TODO: No adapter? } let user; @@ -285,7 +285,7 @@ export class UserController extends AdaptableController { this.config.passwordPolicy.resetTokenValidityDuration ) { const results = await this.config.database.find( - "_User", + '_User', { $or: [ { email, _perishable_token: { $exists: true } }, @@ -301,7 +301,7 @@ export class UserController extends AdaptableController { ); if (results.length == 1) { let expiresDate = results[0]._perishable_token_expires_at; - if (expiresDate && expiresDate.__type == "Date") { + if (expiresDate && expiresDate.__type == 'Date') { expiresDate = new Date(expiresDate.iso); } if (expiresDate > new Date()) { @@ -321,7 +321,7 @@ export class UserController extends AdaptableController { const options = { appName: this.config.appName, link: link, - user: inflate("_User", user), + user: inflate('_User', user), }; if (this.adapter.sendPasswordResetEmail) { @@ -351,34 +351,34 @@ export class UserController extends AdaptableController { defaultVerificationEmail({ link, user, appName }) { const text = - "Hi,\n\n" + - "You are being asked to confirm the e-mail address " + - user.get("email") + - " with " + + 'Hi,\n\n' + + 'You are being asked to confirm the e-mail address ' + + user.get('email') + + ' with ' + appName + - "\n\n" + - "" + - "Click here to confirm it:\n" + + '\n\n' + + '' + + 'Click here to confirm it:\n' + link; - const to = user.get("email"); - const subject = "Please verify your e-mail for " + appName; + const to = user.get('email'); + const subject = 'Please verify your e-mail for ' + appName; return { text, to, subject }; } defaultResetPasswordEmail({ link, user, appName }) { const text = - "Hi,\n\n" + - "You requested to reset your password for " + + 'Hi,\n\n' + + 'You requested to reset your password for ' + appName + - (user.get("username") - ? " (your username is '" + user.get("username") + "')" - : "") + - ".\n\n" + - "" + - "Click here to reset it:\n" + + (user.get('username') + ? " (your username is '" + user.get('username') + "')" + : '') + + '.\n\n' + + '' + + 'Click here to reset it:\n' + link; - const to = user.get("email") || user.get("username"); - const subject = "Password Reset for " + appName; + const to = user.get('email') || user.get('username'); + const subject = 'Password Reset for ' + appName; return { text, to, subject }; } } @@ -389,7 +389,7 @@ function updateUserPassword(user, password, config) { .update( config, Auth.master(config), - "_User", + '_User', { objectId: user.objectId }, { password: password, @@ -403,7 +403,7 @@ function buildEmailLink(destination, token, config) { if (config.parseFrameURL) { const destinationWithoutHost = destination.replace( config.publicServerURL, - "" + '' ); return `${config.parseFrameURL}?link=${encodeURIComponent(destinationWithoutHost)}&${token}`; diff --git a/src/Deprecator/Deprecations.js b/src/Deprecator/Deprecations.js index 98f458b68e..970364432b 100644 --- a/src/Deprecator/Deprecations.js +++ b/src/Deprecator/Deprecations.js @@ -16,6 +16,6 @@ * If there are no deprecations, this must return an empty array. */ module.exports = [ - { optionKey: "encodeParseObjectInCloudFunction", changeNewDefault: "true" }, - { optionKey: "enableInsecureAuthAdapters", changeNewDefault: "false" }, + { optionKey: 'encodeParseObjectInCloudFunction', changeNewDefault: 'true' }, + { optionKey: 'enableInsecureAuthAdapters', changeNewDefault: 'false' }, ]; diff --git a/src/Deprecator/Deprecator.js b/src/Deprecator/Deprecator.js index 856338feb3..4cbdacfced 100644 --- a/src/Deprecator/Deprecator.js +++ b/src/Deprecator/Deprecator.js @@ -1,5 +1,5 @@ -import logger from "../logger"; -import Deprecations from "./Deprecations"; +import logger from '../logger'; +import Deprecations from './Deprecations'; /** * The deprecator class. @@ -76,7 +76,7 @@ class Deprecator { static _logGeneric({ usage, solution }) { // Compose message let output = `DeprecationWarning: ${usage} is deprecated and will be removed in a future version.`; - output += solution ? ` ${solution}` : ""; + output += solution ? ` ${solution}` : ''; logger.warn(output); } @@ -101,7 +101,7 @@ class Deprecator { changeNewDefault, solution, }) { - const type = optionKey ? "option" : "environment key"; + const type = optionKey ? 'option' : 'environment key'; const key = optionKey ? optionKey : envKey; const keyAction = changeNewKey == null @@ -114,11 +114,11 @@ class Deprecator { let output = `DeprecationWarning: The Parse Server ${type} '${key}' `; output += changeNewKey ? `is deprecated and will be ${keyAction} in a future version.` - : ""; + : ''; output += changeNewDefault ? `default will change to '${changeNewDefault}' in a future version.` - : ""; - output += solution ? ` ${solution}` : ""; + : ''; + output += solution ? ` ${solution}` : ''; logger.warn(output); } } diff --git a/src/GraphQL/parseGraphQLUtils.js b/src/GraphQL/parseGraphQLUtils.js index bb0da68ea7..4f12db37af 100644 --- a/src/GraphQL/parseGraphQLUtils.js +++ b/src/GraphQL/parseGraphQLUtils.js @@ -1,11 +1,11 @@ -import Parse from "parse/node"; -import { GraphQLError } from "graphql"; +import Parse from 'parse/node'; +import { GraphQLError } from 'graphql'; export function enforceMasterKeyAccess(auth) { if (!auth.isMaster) { throw new Parse.Error( Parse.Error.OPERATION_FORBIDDEN, - "unauthorized: master key is required" + 'unauthorized: master key is required' ); } } @@ -17,29 +17,29 @@ export function toGraphQLError(error) { message = error.message; } else { code = Parse.Error.INTERNAL_SERVER_ERROR; - message = "Internal server error"; + message = 'Internal server error'; } return new GraphQLError(message, { extensions: { code } }); } export const extractKeysAndInclude = selectedFields => { selectedFields = selectedFields.filter( - field => !field.includes("__typename") + field => !field.includes('__typename') ); // Handles "id" field for both current and included objects selectedFields = selectedFields.map(field => { - if (field === "id") { - return "objectId"; + if (field === 'id') { + return 'objectId'; } - return field.endsWith(".id") - ? `${field.substring(0, field.lastIndexOf(".id"))}.objectId` + return field.endsWith('.id') + ? `${field.substring(0, field.lastIndexOf('.id'))}.objectId` : field; }); let keys = undefined; let include = undefined; if (selectedFields.length > 0) { - keys = [...new Set(selectedFields)].join(","); + keys = [...new Set(selectedFields)].join(','); // We can use this shortcut since optimization is handled // later on RestQuery, avoid overhead here. include = keys; @@ -49,7 +49,7 @@ export const extractKeysAndInclude = selectedFields => { // If authData is detected keys will not work properly // since authData has a special storage behavior // so we need to skip keys currently - keys: keys && keys.indexOf("authData") === -1 ? keys : undefined, + keys: keys && keys.indexOf('authData') === -1 ? keys : undefined, include, }; }; diff --git a/src/LiveQuery/RequestSchema.js b/src/LiveQuery/RequestSchema.js index 5bd1c278bd..6e0a0566b2 100644 --- a/src/LiveQuery/RequestSchema.js +++ b/src/LiveQuery/RequestSchema.js @@ -1,151 +1,151 @@ const general = { - title: "General request schema", - type: "object", + title: 'General request schema', + type: 'object', properties: { op: { - type: "string", - enum: ["connect", "subscribe", "unsubscribe", "update"], + type: 'string', + enum: ['connect', 'subscribe', 'unsubscribe', 'update'], }, }, - required: ["op"], + required: ['op'], }; const connect = { - title: "Connect operation schema", - type: "object", + title: 'Connect operation schema', + type: 'object', properties: { - op: "connect", + op: 'connect', applicationId: { - type: "string", + type: 'string', }, javascriptKey: { - type: "string", + type: 'string', }, masterKey: { - type: "string", + type: 'string', }, clientKey: { - type: "string", + type: 'string', }, windowsKey: { - type: "string", + type: 'string', }, restAPIKey: { - type: "string", + type: 'string', }, sessionToken: { - type: "string", + type: 'string', }, installationId: { - type: "string", + type: 'string', }, }, - required: ["op", "applicationId"], + required: ['op', 'applicationId'], additionalProperties: false, }; const subscribe = { - title: "Subscribe operation schema", - type: "object", + title: 'Subscribe operation schema', + type: 'object', properties: { - op: "subscribe", + op: 'subscribe', requestId: { - type: "number", + type: 'number', }, query: { - title: "Query field schema", - type: "object", + title: 'Query field schema', + type: 'object', properties: { className: { - type: "string", + type: 'string', }, where: { - type: "object", + type: 'object', }, keys: { - type: "array", + type: 'array', items: { - type: "string", + type: 'string', }, minItems: 1, uniqueItems: true, }, watch: { - type: "array", + type: 'array', items: { - type: "string", + type: 'string', }, minItems: 1, uniqueItems: true, }, }, - required: ["where", "className"], + required: ['where', 'className'], additionalProperties: false, }, sessionToken: { - type: "string", + type: 'string', }, }, - required: ["op", "requestId", "query"], + required: ['op', 'requestId', 'query'], additionalProperties: false, }; const update = { - title: "Update operation schema", - type: "object", + title: 'Update operation schema', + type: 'object', properties: { - op: "update", + op: 'update', requestId: { - type: "number", + type: 'number', }, query: { - title: "Query field schema", - type: "object", + title: 'Query field schema', + type: 'object', properties: { className: { - type: "string", + type: 'string', }, where: { - type: "object", + type: 'object', }, keys: { - type: "array", + type: 'array', items: { - type: "string", + type: 'string', }, minItems: 1, uniqueItems: true, }, watch: { - type: "array", + type: 'array', items: { - type: "string", + type: 'string', }, minItems: 1, uniqueItems: true, }, }, - required: ["where", "className"], + required: ['where', 'className'], additionalProperties: false, }, sessionToken: { - type: "string", + type: 'string', }, }, - required: ["op", "requestId", "query"], + required: ['op', 'requestId', 'query'], additionalProperties: false, }; const unsubscribe = { - title: "Unsubscribe operation schema", - type: "object", + title: 'Unsubscribe operation schema', + type: 'object', properties: { - op: "unsubscribe", + op: 'unsubscribe', requestId: { - type: "number", + type: 'number', }, }, - required: ["op", "requestId"], + required: ['op', 'requestId'], additionalProperties: false, }; diff --git a/src/LiveQuery/equalObjects.js b/src/LiveQuery/equalObjects.js index 4c5028a861..5bc3f5e957 100644 --- a/src/LiveQuery/equalObjects.js +++ b/src/LiveQuery/equalObjects.js @@ -8,14 +8,14 @@ function equalObjects(a, b) { if (typeof a !== typeof b) { return false; } - if (typeof a !== "object") { + if (typeof a !== 'object') { return a === b; } if (a === b) { return true; } - if (toString.call(a) === "[object Date]") { - if (toString.call(b) === "[object Date]") { + if (toString.call(a) === '[object Date]') { + if (toString.call(b) === '[object Date]') { return +a === +b; } return false; diff --git a/src/Options/Definitions.js b/src/Options/Definitions.js index 9e47017a48..faffb754ac 100644 --- a/src/Options/Definitions.js +++ b/src/Options/Definitions.js @@ -3,1074 +3,1074 @@ This code has been generated by resources/buildConfigDefinitions.js Do not edit manually, but update Options/index.js */ -var parsers = require("./parsers"); +var parsers = require('./parsers'); module.exports.SchemaOptions = { afterMigration: { - env: "PARSE_SERVER_SCHEMA_AFTER_MIGRATION", - help: "Execute a callback after running schema migrations.", + env: 'PARSE_SERVER_SCHEMA_AFTER_MIGRATION', + help: 'Execute a callback after running schema migrations.', }, beforeMigration: { - env: "PARSE_SERVER_SCHEMA_BEFORE_MIGRATION", - help: "Execute a callback before running schema migrations.", + env: 'PARSE_SERVER_SCHEMA_BEFORE_MIGRATION', + help: 'Execute a callback before running schema migrations.', }, definitions: { - env: "PARSE_SERVER_SCHEMA_DEFINITIONS", - help: "Rest representation on Parse.Schema https://docs.parseplatform.org/rest/guide/#adding-a-schema", + env: 'PARSE_SERVER_SCHEMA_DEFINITIONS', + help: 'Rest representation on Parse.Schema https://docs.parseplatform.org/rest/guide/#adding-a-schema', required: true, action: parsers.objectParser, default: [], }, deleteExtraFields: { - env: "PARSE_SERVER_SCHEMA_DELETE_EXTRA_FIELDS", - help: "Is true if Parse Server should delete any fields not defined in a schema definition. This should only be used during development.", + env: 'PARSE_SERVER_SCHEMA_DELETE_EXTRA_FIELDS', + help: 'Is true if Parse Server should delete any fields not defined in a schema definition. This should only be used during development.', action: parsers.booleanParser, default: false, }, lockSchemas: { - env: "PARSE_SERVER_SCHEMA_LOCK_SCHEMAS", - help: "Is true if Parse Server will reject any attempts to modify the schema while the server is running.", + env: 'PARSE_SERVER_SCHEMA_LOCK_SCHEMAS', + help: 'Is true if Parse Server will reject any attempts to modify the schema while the server is running.', action: parsers.booleanParser, default: false, }, recreateModifiedFields: { - env: "PARSE_SERVER_SCHEMA_RECREATE_MODIFIED_FIELDS", - help: "Is true if Parse Server should recreate any fields that are different between the current database schema and theschema definition. This should only be used during development.", + env: 'PARSE_SERVER_SCHEMA_RECREATE_MODIFIED_FIELDS', + help: 'Is true if Parse Server should recreate any fields that are different between the current database schema and theschema definition. This should only be used during development.', action: parsers.booleanParser, default: false, }, strict: { - env: "PARSE_SERVER_SCHEMA_STRICT", - help: "Is true if Parse Server should exit if schema update fail.", + env: 'PARSE_SERVER_SCHEMA_STRICT', + help: 'Is true if Parse Server should exit if schema update fail.', action: parsers.booleanParser, default: false, }, }; module.exports.ParseServerOptions = { accountLockout: { - env: "PARSE_SERVER_ACCOUNT_LOCKOUT", - help: "The account lockout policy for failed login attempts.", + env: 'PARSE_SERVER_ACCOUNT_LOCKOUT', + help: 'The account lockout policy for failed login attempts.', action: parsers.objectParser, - type: "AccountLockoutOptions", + type: 'AccountLockoutOptions', }, allowClientClassCreation: { - env: "PARSE_SERVER_ALLOW_CLIENT_CLASS_CREATION", - help: "Enable (or disable) client class creation, defaults to false", + env: 'PARSE_SERVER_ALLOW_CLIENT_CLASS_CREATION', + help: 'Enable (or disable) client class creation, defaults to false', action: parsers.booleanParser, default: false, }, allowCustomObjectId: { - env: "PARSE_SERVER_ALLOW_CUSTOM_OBJECT_ID", - help: "Enable (or disable) custom objectId", + env: 'PARSE_SERVER_ALLOW_CUSTOM_OBJECT_ID', + help: 'Enable (or disable) custom objectId', action: parsers.booleanParser, default: false, }, allowExpiredAuthDataToken: { - env: "PARSE_SERVER_ALLOW_EXPIRED_AUTH_DATA_TOKEN", - help: "Allow a user to log in even if the 3rd party authentication token that was used to sign in to their account has expired. If this is set to `false`, then the token will be validated every time the user signs in to their account. This refers to the token that is stored in the `_User.authData` field. Defaults to `false`.", + env: 'PARSE_SERVER_ALLOW_EXPIRED_AUTH_DATA_TOKEN', + help: 'Allow a user to log in even if the 3rd party authentication token that was used to sign in to their account has expired. If this is set to `false`, then the token will be validated every time the user signs in to their account. This refers to the token that is stored in the `_User.authData` field. Defaults to `false`.', action: parsers.booleanParser, default: false, }, allowHeaders: { - env: "PARSE_SERVER_ALLOW_HEADERS", - help: "Add headers to Access-Control-Allow-Headers", + env: 'PARSE_SERVER_ALLOW_HEADERS', + help: 'Add headers to Access-Control-Allow-Headers', action: parsers.arrayParser, }, allowOrigin: { - env: "PARSE_SERVER_ALLOW_ORIGIN", - help: "Sets origins for Access-Control-Allow-Origin. This can be a string for a single origin or an array of strings for multiple origins.", + env: 'PARSE_SERVER_ALLOW_ORIGIN', + help: 'Sets origins for Access-Control-Allow-Origin. This can be a string for a single origin or an array of strings for multiple origins.', action: parsers.arrayParser, }, analyticsAdapter: { - env: "PARSE_SERVER_ANALYTICS_ADAPTER", - help: "Adapter module for the analytics", + env: 'PARSE_SERVER_ANALYTICS_ADAPTER', + help: 'Adapter module for the analytics', action: parsers.moduleOrObjectParser, }, appId: { - env: "PARSE_SERVER_APPLICATION_ID", - help: "Your Parse Application ID", + env: 'PARSE_SERVER_APPLICATION_ID', + help: 'Your Parse Application ID', required: true, }, appName: { - env: "PARSE_SERVER_APP_NAME", - help: "Sets the app name", + env: 'PARSE_SERVER_APP_NAME', + help: 'Sets the app name', }, auth: { - env: "PARSE_SERVER_AUTH_PROVIDERS", - help: "Configuration for your authentication providers, as stringified JSON. See http://docs.parseplatform.org/parse-server/guide/#oauth-and-3rd-party-authentication", + env: 'PARSE_SERVER_AUTH_PROVIDERS', + help: 'Configuration for your authentication providers, as stringified JSON. See http://docs.parseplatform.org/parse-server/guide/#oauth-and-3rd-party-authentication', }, cacheAdapter: { - env: "PARSE_SERVER_CACHE_ADAPTER", - help: "Adapter module for the cache", + env: 'PARSE_SERVER_CACHE_ADAPTER', + help: 'Adapter module for the cache', action: parsers.moduleOrObjectParser, }, cacheMaxSize: { - env: "PARSE_SERVER_CACHE_MAX_SIZE", - help: "Sets the maximum size for the in memory cache, defaults to 10000", - action: parsers.numberParser("cacheMaxSize"), + env: 'PARSE_SERVER_CACHE_MAX_SIZE', + help: 'Sets the maximum size for the in memory cache, defaults to 10000', + action: parsers.numberParser('cacheMaxSize'), default: 10000, }, cacheTTL: { - env: "PARSE_SERVER_CACHE_TTL", - help: "Sets the TTL for the in memory cache (in ms), defaults to 5000 (5 seconds)", - action: parsers.numberParser("cacheTTL"), + env: 'PARSE_SERVER_CACHE_TTL', + help: 'Sets the TTL for the in memory cache (in ms), defaults to 5000 (5 seconds)', + action: parsers.numberParser('cacheTTL'), default: 5000, }, clientKey: { - env: "PARSE_SERVER_CLIENT_KEY", - help: "Key for iOS, MacOS, tvOS clients", + env: 'PARSE_SERVER_CLIENT_KEY', + help: 'Key for iOS, MacOS, tvOS clients', }, cloud: { - env: "PARSE_SERVER_CLOUD", - help: "Full path to your cloud code main.js", + env: 'PARSE_SERVER_CLOUD', + help: 'Full path to your cloud code main.js', }, cluster: { - env: "PARSE_SERVER_CLUSTER", - help: "Run with cluster, optionally set the number of processes default to os.cpus().length", + env: 'PARSE_SERVER_CLUSTER', + help: 'Run with cluster, optionally set the number of processes default to os.cpus().length', action: parsers.numberOrBooleanParser, }, collectionPrefix: { - env: "PARSE_SERVER_COLLECTION_PREFIX", - help: "A collection prefix for the classes", - default: "", + env: 'PARSE_SERVER_COLLECTION_PREFIX', + help: 'A collection prefix for the classes', + default: '', }, convertEmailToLowercase: { - env: "PARSE_SERVER_CONVERT_EMAIL_TO_LOWERCASE", - help: "Optional. If set to `true`, the `email` property of a user is automatically converted to lowercase before being stored in the database. Consequently, queries must match the case as stored in the database, which would be lowercase in this scenario. If `false`, the `email` property is stored as set, without any case modifications. Default is `false`.", + env: 'PARSE_SERVER_CONVERT_EMAIL_TO_LOWERCASE', + help: 'Optional. If set to `true`, the `email` property of a user is automatically converted to lowercase before being stored in the database. Consequently, queries must match the case as stored in the database, which would be lowercase in this scenario. If `false`, the `email` property is stored as set, without any case modifications. Default is `false`.', action: parsers.booleanParser, default: false, }, convertUsernameToLowercase: { - env: "PARSE_SERVER_CONVERT_USERNAME_TO_LOWERCASE", - help: "Optional. If set to `true`, the `username` property of a user is automatically converted to lowercase before being stored in the database. Consequently, queries must match the case as stored in the database, which would be lowercase in this scenario. If `false`, the `username` property is stored as set, without any case modifications. Default is `false`.", + env: 'PARSE_SERVER_CONVERT_USERNAME_TO_LOWERCASE', + help: 'Optional. If set to `true`, the `username` property of a user is automatically converted to lowercase before being stored in the database. Consequently, queries must match the case as stored in the database, which would be lowercase in this scenario. If `false`, the `username` property is stored as set, without any case modifications. Default is `false`.', action: parsers.booleanParser, default: false, }, customPages: { - env: "PARSE_SERVER_CUSTOM_PAGES", - help: "custom pages for password validation and reset", + env: 'PARSE_SERVER_CUSTOM_PAGES', + help: 'custom pages for password validation and reset', action: parsers.objectParser, - type: "CustomPagesOptions", + type: 'CustomPagesOptions', default: {}, }, databaseAdapter: { - env: "PARSE_SERVER_DATABASE_ADAPTER", - help: "Adapter module for the database; any options that are not explicitly described here are passed directly to the database client.", + env: 'PARSE_SERVER_DATABASE_ADAPTER', + help: 'Adapter module for the database; any options that are not explicitly described here are passed directly to the database client.', action: parsers.moduleOrObjectParser, }, databaseOptions: { - env: "PARSE_SERVER_DATABASE_OPTIONS", - help: "Options to pass to the database client", + env: 'PARSE_SERVER_DATABASE_OPTIONS', + help: 'Options to pass to the database client', action: parsers.objectParser, - type: "DatabaseOptions", + type: 'DatabaseOptions', }, databaseURI: { - env: "PARSE_SERVER_DATABASE_URI", - help: "The full URI to your database. Supported databases are mongodb or postgres.", + env: 'PARSE_SERVER_DATABASE_URI', + help: 'The full URI to your database. Supported databases are mongodb or postgres.', required: true, - default: "mongodb://localhost:27017/parse", + default: 'mongodb://localhost:27017/parse', }, defaultLimit: { - env: "PARSE_SERVER_DEFAULT_LIMIT", - help: "Default value for limit option on queries, defaults to `100`.", - action: parsers.numberParser("defaultLimit"), + env: 'PARSE_SERVER_DEFAULT_LIMIT', + help: 'Default value for limit option on queries, defaults to `100`.', + action: parsers.numberParser('defaultLimit'), default: 100, }, directAccess: { - env: "PARSE_SERVER_DIRECT_ACCESS", - help: "Set to `true` if Parse requests within the same Node.js environment as Parse Server should be routed to Parse Server directly instead of via the HTTP interface. Default is `false`.

If set to `false` then Parse requests within the same Node.js environment as Parse Server are executed as HTTP requests sent to Parse Server via the `serverURL`. For example, a `Parse.Query` in Cloud Code is calling Parse Server via a HTTP request. The server is essentially making a HTTP request to itself, unnecessarily using network resources such as network ports.

\u26A0\uFE0F In environments where multiple Parse Server instances run behind a load balancer and Parse requests within the current Node.js environment should be routed via the load balancer and distributed as HTTP requests among all instances via the `serverURL`, this should be set to `false`.", + env: 'PARSE_SERVER_DIRECT_ACCESS', + help: 'Set to `true` if Parse requests within the same Node.js environment as Parse Server should be routed to Parse Server directly instead of via the HTTP interface. Default is `false`.

If set to `false` then Parse requests within the same Node.js environment as Parse Server are executed as HTTP requests sent to Parse Server via the `serverURL`. For example, a `Parse.Query` in Cloud Code is calling Parse Server via a HTTP request. The server is essentially making a HTTP request to itself, unnecessarily using network resources such as network ports.

\u26A0\uFE0F In environments where multiple Parse Server instances run behind a load balancer and Parse requests within the current Node.js environment should be routed via the load balancer and distributed as HTTP requests among all instances via the `serverURL`, this should be set to `false`.', action: parsers.booleanParser, default: true, }, dotNetKey: { - env: "PARSE_SERVER_DOT_NET_KEY", - help: "Key for Unity and .Net SDK", + env: 'PARSE_SERVER_DOT_NET_KEY', + help: 'Key for Unity and .Net SDK', }, emailAdapter: { - env: "PARSE_SERVER_EMAIL_ADAPTER", - help: "Adapter module for email sending", + env: 'PARSE_SERVER_EMAIL_ADAPTER', + help: 'Adapter module for email sending', action: parsers.moduleOrObjectParser, }, emailVerifyTokenReuseIfValid: { - env: "PARSE_SERVER_EMAIL_VERIFY_TOKEN_REUSE_IF_VALID", - help: "Set to `true` if a email verification token should be reused in case another token is requested but there is a token that is still valid, i.e. has not expired. This avoids the often observed issue that a user requests multiple emails and does not know which link contains a valid token because each newly generated token would invalidate the previous token.

Default is `false`.
Requires option `verifyUserEmails: true`.", + env: 'PARSE_SERVER_EMAIL_VERIFY_TOKEN_REUSE_IF_VALID', + help: 'Set to `true` if a email verification token should be reused in case another token is requested but there is a token that is still valid, i.e. has not expired. This avoids the often observed issue that a user requests multiple emails and does not know which link contains a valid token because each newly generated token would invalidate the previous token.

Default is `false`.
Requires option `verifyUserEmails: true`.', action: parsers.booleanParser, default: false, }, emailVerifyTokenValidityDuration: { - env: "PARSE_SERVER_EMAIL_VERIFY_TOKEN_VALIDITY_DURATION", - help: "Set the validity duration of the email verification token in seconds after which the token expires. The token is used in the link that is set in the email. After the token expires, the link becomes invalid and a new link has to be sent. If the option is not set or set to `undefined`, then the token never expires.

For example, to expire the token after 2 hours, set a value of 7200 seconds (= 60 seconds * 60 minutes * 2 hours).

Default is `undefined`.
Requires option `verifyUserEmails: true`.", - action: parsers.numberParser("emailVerifyTokenValidityDuration"), + env: 'PARSE_SERVER_EMAIL_VERIFY_TOKEN_VALIDITY_DURATION', + help: 'Set the validity duration of the email verification token in seconds after which the token expires. The token is used in the link that is set in the email. After the token expires, the link becomes invalid and a new link has to be sent. If the option is not set or set to `undefined`, then the token never expires.

For example, to expire the token after 2 hours, set a value of 7200 seconds (= 60 seconds * 60 minutes * 2 hours).

Default is `undefined`.
Requires option `verifyUserEmails: true`.', + action: parsers.numberParser('emailVerifyTokenValidityDuration'), }, enableAnonymousUsers: { - env: "PARSE_SERVER_ENABLE_ANON_USERS", - help: "Enable (or disable) anonymous users, defaults to true", + env: 'PARSE_SERVER_ENABLE_ANON_USERS', + help: 'Enable (or disable) anonymous users, defaults to true', action: parsers.booleanParser, default: true, }, enableCollationCaseComparison: { - env: "PARSE_SERVER_ENABLE_COLLATION_CASE_COMPARISON", - help: "Optional. If set to `true`, the collation rule of case comparison for queries and indexes is enabled. Enable this option to run Parse Server with MongoDB Atlas Serverless or AWS Amazon DocumentDB. If `false`, the collation rule of case comparison is disabled. Default is `false`.", + env: 'PARSE_SERVER_ENABLE_COLLATION_CASE_COMPARISON', + help: 'Optional. If set to `true`, the collation rule of case comparison for queries and indexes is enabled. Enable this option to run Parse Server with MongoDB Atlas Serverless or AWS Amazon DocumentDB. If `false`, the collation rule of case comparison is disabled. Default is `false`.', action: parsers.booleanParser, default: false, }, enableExpressErrorHandler: { - env: "PARSE_SERVER_ENABLE_EXPRESS_ERROR_HANDLER", - help: "Enables the default express error handler for all errors", + env: 'PARSE_SERVER_ENABLE_EXPRESS_ERROR_HANDLER', + help: 'Enables the default express error handler for all errors', action: parsers.booleanParser, default: false, }, enableInsecureAuthAdapters: { - env: "PARSE_SERVER_ENABLE_INSECURE_AUTH_ADAPTERS", - help: "Enable (or disable) insecure auth adapters, defaults to true. Insecure auth adapters are deprecated and it is recommended to disable them.", + env: 'PARSE_SERVER_ENABLE_INSECURE_AUTH_ADAPTERS', + help: 'Enable (or disable) insecure auth adapters, defaults to true. Insecure auth adapters are deprecated and it is recommended to disable them.', action: parsers.booleanParser, default: true, }, encodeParseObjectInCloudFunction: { - env: "PARSE_SERVER_ENCODE_PARSE_OBJECT_IN_CLOUD_FUNCTION", - help: "If set to `true`, a `Parse.Object` that is in the payload when calling a Cloud Function will be converted to an instance of `Parse.Object`. If `false`, the object will not be converted and instead be a plain JavaScript object, which contains the raw data of a `Parse.Object` but is not an actual instance of `Parse.Object`. Default is `false`.

\u2139\uFE0F The expected behavior would be that the object is converted to an instance of `Parse.Object`, so you would normally set this option to `true`. The default is `false` because this is a temporary option that has been introduced to avoid a breaking change when fixing a bug where JavaScript objects are not converted to actual instances of `Parse.Object`.", + env: 'PARSE_SERVER_ENCODE_PARSE_OBJECT_IN_CLOUD_FUNCTION', + help: 'If set to `true`, a `Parse.Object` that is in the payload when calling a Cloud Function will be converted to an instance of `Parse.Object`. If `false`, the object will not be converted and instead be a plain JavaScript object, which contains the raw data of a `Parse.Object` but is not an actual instance of `Parse.Object`. Default is `false`.

\u2139\uFE0F The expected behavior would be that the object is converted to an instance of `Parse.Object`, so you would normally set this option to `true`. The default is `false` because this is a temporary option that has been introduced to avoid a breaking change when fixing a bug where JavaScript objects are not converted to actual instances of `Parse.Object`.', action: parsers.booleanParser, default: true, }, encryptionKey: { - env: "PARSE_SERVER_ENCRYPTION_KEY", - help: "Key for encrypting your files", + env: 'PARSE_SERVER_ENCRYPTION_KEY', + help: 'Key for encrypting your files', }, enforcePrivateUsers: { - env: "PARSE_SERVER_ENFORCE_PRIVATE_USERS", - help: "Set to true if new users should be created without public read and write access.", + env: 'PARSE_SERVER_ENFORCE_PRIVATE_USERS', + help: 'Set to true if new users should be created without public read and write access.', action: parsers.booleanParser, default: true, }, expireInactiveSessions: { - env: "PARSE_SERVER_EXPIRE_INACTIVE_SESSIONS", - help: "Sets whether we should expire the inactive sessions, defaults to true. If false, all new sessions are created with no expiration date.", + env: 'PARSE_SERVER_EXPIRE_INACTIVE_SESSIONS', + help: 'Sets whether we should expire the inactive sessions, defaults to true. If false, all new sessions are created with no expiration date.', action: parsers.booleanParser, default: true, }, extendSessionOnUse: { - env: "PARSE_SERVER_EXTEND_SESSION_ON_USE", + env: 'PARSE_SERVER_EXTEND_SESSION_ON_USE', help: "Whether Parse Server should automatically extend a valid session by the sessionLength. In order to reduce the number of session updates in the database, a session will only be extended when a request is received after at least half of the current session's lifetime has passed.", action: parsers.booleanParser, default: false, }, fileKey: { - env: "PARSE_SERVER_FILE_KEY", - help: "Key for your files", + env: 'PARSE_SERVER_FILE_KEY', + help: 'Key for your files', }, filesAdapter: { - env: "PARSE_SERVER_FILES_ADAPTER", - help: "Adapter module for the files sub-system", + env: 'PARSE_SERVER_FILES_ADAPTER', + help: 'Adapter module for the files sub-system', action: parsers.moduleOrObjectParser, }, fileUpload: { - env: "PARSE_SERVER_FILE_UPLOAD_OPTIONS", - help: "Options for file uploads", + env: 'PARSE_SERVER_FILE_UPLOAD_OPTIONS', + help: 'Options for file uploads', action: parsers.objectParser, - type: "FileUploadOptions", + type: 'FileUploadOptions', default: {}, }, graphQLPath: { - env: "PARSE_SERVER_GRAPHQL_PATH", - help: "Mount path for the GraphQL endpoint, defaults to /graphql", - default: "/graphql", + env: 'PARSE_SERVER_GRAPHQL_PATH', + help: 'Mount path for the GraphQL endpoint, defaults to /graphql', + default: '/graphql', }, graphQLSchema: { - env: "PARSE_SERVER_GRAPH_QLSCHEMA", - help: "Full path to your GraphQL custom schema.graphql file", + env: 'PARSE_SERVER_GRAPH_QLSCHEMA', + help: 'Full path to your GraphQL custom schema.graphql file', }, host: { - env: "PARSE_SERVER_HOST", - help: "The host to serve ParseServer on, defaults to 0.0.0.0", - default: "0.0.0.0", + env: 'PARSE_SERVER_HOST', + help: 'The host to serve ParseServer on, defaults to 0.0.0.0', + default: '0.0.0.0', }, idempotencyOptions: { - env: "PARSE_SERVER_EXPERIMENTAL_IDEMPOTENCY_OPTIONS", - help: "Options for request idempotency to deduplicate identical requests that may be caused by network issues. Caution, this is an experimental feature that may not be appropriate for production.", + env: 'PARSE_SERVER_EXPERIMENTAL_IDEMPOTENCY_OPTIONS', + help: 'Options for request idempotency to deduplicate identical requests that may be caused by network issues. Caution, this is an experimental feature that may not be appropriate for production.', action: parsers.objectParser, - type: "IdempotencyOptions", + type: 'IdempotencyOptions', default: {}, }, javascriptKey: { - env: "PARSE_SERVER_JAVASCRIPT_KEY", - help: "Key for the Javascript SDK", + env: 'PARSE_SERVER_JAVASCRIPT_KEY', + help: 'Key for the Javascript SDK', }, jsonLogs: { - env: "JSON_LOGS", - help: "Log as structured JSON objects", + env: 'JSON_LOGS', + help: 'Log as structured JSON objects', action: parsers.booleanParser, }, liveQuery: { - env: "PARSE_SERVER_LIVE_QUERY", + env: 'PARSE_SERVER_LIVE_QUERY', help: "parse-server's LiveQuery configuration object", action: parsers.objectParser, - type: "LiveQueryOptions", + type: 'LiveQueryOptions', }, liveQueryServerOptions: { - env: "PARSE_SERVER_LIVE_QUERY_SERVER_OPTIONS", - help: "Live query server configuration options (will start the liveQuery server)", + env: 'PARSE_SERVER_LIVE_QUERY_SERVER_OPTIONS', + help: 'Live query server configuration options (will start the liveQuery server)', action: parsers.objectParser, - type: "LiveQueryServerOptions", + type: 'LiveQueryServerOptions', }, loggerAdapter: { - env: "PARSE_SERVER_LOGGER_ADAPTER", - help: "Adapter module for the logging sub-system", + env: 'PARSE_SERVER_LOGGER_ADAPTER', + help: 'Adapter module for the logging sub-system', action: parsers.moduleOrObjectParser, }, logLevel: { - env: "PARSE_SERVER_LOG_LEVEL", - help: "Sets the level for logs", + env: 'PARSE_SERVER_LOG_LEVEL', + help: 'Sets the level for logs', }, logLevels: { - env: "PARSE_SERVER_LOG_LEVELS", - help: "(Optional) Overrides the log levels used internally by Parse Server to log events.", + env: 'PARSE_SERVER_LOG_LEVELS', + help: '(Optional) Overrides the log levels used internally by Parse Server to log events.', action: parsers.objectParser, - type: "LogLevels", + type: 'LogLevels', default: {}, }, logsFolder: { - env: "PARSE_SERVER_LOGS_FOLDER", + env: 'PARSE_SERVER_LOGS_FOLDER', help: "Folder for the logs (defaults to './logs'); set to null to disable file based logging", - default: "./logs", + default: './logs', }, maintenanceKey: { - env: "PARSE_SERVER_MAINTENANCE_KEY", - help: "(Optional) The maintenance key is used for modifying internal and read-only fields of Parse Server.

\u26A0\uFE0F This key is not intended to be used as part of a regular operation of Parse Server. This key is intended to conduct out-of-band changes such as one-time migrations or data correction tasks. Internal fields are not officially documented and may change at any time without publication in release changelogs. We strongly advice not to rely on internal fields as part of your regular operation and to investigate the implications of any planned changes *directly in the source code* of your current version of Parse Server.", + env: 'PARSE_SERVER_MAINTENANCE_KEY', + help: '(Optional) The maintenance key is used for modifying internal and read-only fields of Parse Server.

\u26A0\uFE0F This key is not intended to be used as part of a regular operation of Parse Server. This key is intended to conduct out-of-band changes such as one-time migrations or data correction tasks. Internal fields are not officially documented and may change at any time without publication in release changelogs. We strongly advice not to rely on internal fields as part of your regular operation and to investigate the implications of any planned changes *directly in the source code* of your current version of Parse Server.', required: true, }, maintenanceKeyIps: { - env: "PARSE_SERVER_MAINTENANCE_KEY_IPS", + env: 'PARSE_SERVER_MAINTENANCE_KEY_IPS', help: "(Optional) Restricts the use of maintenance key permissions to a list of IP addresses or ranges.

This option accepts a list of single IP addresses, for example `['10.0.0.1', '10.0.0.2']`. You can also use CIDR notation to specify an IP address range, for example `['10.0.1.0/24']`.

Special scenarios:
- Setting an empty array `[]` means that the maintenance key cannot be used even in Parse Server Cloud Code. This value cannot be set via an environment variable as there is no way to pass an empty array to Parse Server via an environment variable.
- Setting `['0.0.0.0/0', '::0']` means to allow any IPv4 and IPv6 address to use the maintenance key and effectively disables the IP filter.

Considerations:
- IPv4 and IPv6 addresses are not compared against each other. Each IP version (IPv4 and IPv6) needs to be considered separately. For example, `['0.0.0.0/0']` allows any IPv4 address and blocks every IPv6 address. Conversely, `['::0']` allows any IPv6 address and blocks every IPv4 address.
- Keep in mind that the IP version in use depends on the network stack of the environment in which Parse Server runs. A local environment may use a different IP version than a remote environment. For example, it's possible that locally the value `['0.0.0.0/0']` allows the request IP because the environment is using IPv4, but when Parse Server is deployed remotely the request IP is blocked because the remote environment is using IPv6.
- When setting the option via an environment variable the notation is a comma-separated string, for example `\"0.0.0.0/0,::0\"`.
- IPv6 zone indices (`%` suffix) are not supported, for example `fe80::1%eth0`, `fe80::1%1` or `::1%lo`.

Defaults to `['127.0.0.1', '::1']` which means that only `localhost`, the server instance on which Parse Server runs, is allowed to use the maintenance key.", action: parsers.arrayParser, - default: ["127.0.0.1", "::1"], + default: ['127.0.0.1', '::1'], }, masterKey: { - env: "PARSE_SERVER_MASTER_KEY", - help: "Your Parse Master Key", + env: 'PARSE_SERVER_MASTER_KEY', + help: 'Your Parse Master Key', required: true, }, masterKeyIps: { - env: "PARSE_SERVER_MASTER_KEY_IPS", + env: 'PARSE_SERVER_MASTER_KEY_IPS', help: "(Optional) Restricts the use of master key permissions to a list of IP addresses or ranges.

This option accepts a list of single IP addresses, for example `['10.0.0.1', '10.0.0.2']`. You can also use CIDR notation to specify an IP address range, for example `['10.0.1.0/24']`.

Special scenarios:
- Setting an empty array `[]` means that the master key cannot be used even in Parse Server Cloud Code. This value cannot be set via an environment variable as there is no way to pass an empty array to Parse Server via an environment variable.
- Setting `['0.0.0.0/0', '::0']` means to allow any IPv4 and IPv6 address to use the master key and effectively disables the IP filter.

Considerations:
- IPv4 and IPv6 addresses are not compared against each other. Each IP version (IPv4 and IPv6) needs to be considered separately. For example, `['0.0.0.0/0']` allows any IPv4 address and blocks every IPv6 address. Conversely, `['::0']` allows any IPv6 address and blocks every IPv4 address.
- Keep in mind that the IP version in use depends on the network stack of the environment in which Parse Server runs. A local environment may use a different IP version than a remote environment. For example, it's possible that locally the value `['0.0.0.0/0']` allows the request IP because the environment is using IPv4, but when Parse Server is deployed remotely the request IP is blocked because the remote environment is using IPv6.
- When setting the option via an environment variable the notation is a comma-separated string, for example `\"0.0.0.0/0,::0\"`.
- IPv6 zone indices (`%` suffix) are not supported, for example `fe80::1%eth0`, `fe80::1%1` or `::1%lo`.

Defaults to `['127.0.0.1', '::1']` which means that only `localhost`, the server instance on which Parse Server runs, is allowed to use the master key.", action: parsers.arrayParser, - default: ["127.0.0.1", "::1"], + default: ['127.0.0.1', '::1'], }, masterKeyTtl: { - env: "PARSE_SERVER_MASTER_KEY_TTL", - help: "(Optional) The duration in seconds for which the current `masterKey` is being used before it is requested again if `masterKey` is set to a function. If `masterKey` is not set to a function, this option has no effect. Default is `0`, which means the master key is requested by invoking the `masterKey` function every time the master key is used internally by Parse Server.", - action: parsers.numberParser("masterKeyTtl"), + env: 'PARSE_SERVER_MASTER_KEY_TTL', + help: '(Optional) The duration in seconds for which the current `masterKey` is being used before it is requested again if `masterKey` is set to a function. If `masterKey` is not set to a function, this option has no effect. Default is `0`, which means the master key is requested by invoking the `masterKey` function every time the master key is used internally by Parse Server.', + action: parsers.numberParser('masterKeyTtl'), }, maxLimit: { - env: "PARSE_SERVER_MAX_LIMIT", - help: "Max value for limit option on queries, defaults to unlimited", - action: parsers.numberParser("maxLimit"), + env: 'PARSE_SERVER_MAX_LIMIT', + help: 'Max value for limit option on queries, defaults to unlimited', + action: parsers.numberParser('maxLimit'), }, maxLogFiles: { - env: "PARSE_SERVER_MAX_LOG_FILES", + env: 'PARSE_SERVER_MAX_LOG_FILES', help: "Maximum number of logs to keep. If not set, no logs will be removed. This can be a number of files or number of days. If using days, add 'd' as the suffix. (default: null)", - action: parsers.numberOrStringParser("maxLogFiles"), + action: parsers.numberOrStringParser('maxLogFiles'), }, maxUploadSize: { - env: "PARSE_SERVER_MAX_UPLOAD_SIZE", - help: "Max file size for uploads, defaults to 20mb", - default: "20mb", + env: 'PARSE_SERVER_MAX_UPLOAD_SIZE', + help: 'Max file size for uploads, defaults to 20mb', + default: '20mb', }, middleware: { - env: "PARSE_SERVER_MIDDLEWARE", - help: "middleware for express server, can be string or function", + env: 'PARSE_SERVER_MIDDLEWARE', + help: 'middleware for express server, can be string or function', }, mountGraphQL: { - env: "PARSE_SERVER_MOUNT_GRAPHQL", - help: "Mounts the GraphQL endpoint", + env: 'PARSE_SERVER_MOUNT_GRAPHQL', + help: 'Mounts the GraphQL endpoint', action: parsers.booleanParser, default: false, }, mountPath: { - env: "PARSE_SERVER_MOUNT_PATH", - help: "Mount path for the server, defaults to /parse", - default: "/parse", + env: 'PARSE_SERVER_MOUNT_PATH', + help: 'Mount path for the server, defaults to /parse', + default: '/parse', }, mountPlayground: { - env: "PARSE_SERVER_MOUNT_PLAYGROUND", - help: "Mounts the GraphQL Playground - never use this option in production", + env: 'PARSE_SERVER_MOUNT_PLAYGROUND', + help: 'Mounts the GraphQL Playground - never use this option in production', action: parsers.booleanParser, default: false, }, objectIdSize: { - env: "PARSE_SERVER_OBJECT_ID_SIZE", + env: 'PARSE_SERVER_OBJECT_ID_SIZE', help: "Sets the number of characters in generated object id's, default 10", - action: parsers.numberParser("objectIdSize"), + action: parsers.numberParser('objectIdSize'), default: 10, }, pages: { - env: "PARSE_SERVER_PAGES", - help: "The options for pages such as password reset and email verification.", + env: 'PARSE_SERVER_PAGES', + help: 'The options for pages such as password reset and email verification.', action: parsers.objectParser, - type: "PagesOptions", + type: 'PagesOptions', default: {}, }, passwordPolicy: { - env: "PARSE_SERVER_PASSWORD_POLICY", - help: "The password policy for enforcing password related rules.", + env: 'PARSE_SERVER_PASSWORD_POLICY', + help: 'The password policy for enforcing password related rules.', action: parsers.objectParser, - type: "PasswordPolicyOptions", + type: 'PasswordPolicyOptions', }, playgroundPath: { - env: "PARSE_SERVER_PLAYGROUND_PATH", - help: "Mount path for the GraphQL Playground, defaults to /playground", - default: "/playground", + env: 'PARSE_SERVER_PLAYGROUND_PATH', + help: 'Mount path for the GraphQL Playground, defaults to /playground', + default: '/playground', }, port: { - env: "PORT", - help: "The port to run the ParseServer, defaults to 1337.", - action: parsers.numberParser("port"), + env: 'PORT', + help: 'The port to run the ParseServer, defaults to 1337.', + action: parsers.numberParser('port'), default: 1337, }, preserveFileName: { - env: "PARSE_SERVER_PRESERVE_FILE_NAME", - help: "Enable (or disable) the addition of a unique hash to the file names", + env: 'PARSE_SERVER_PRESERVE_FILE_NAME', + help: 'Enable (or disable) the addition of a unique hash to the file names', action: parsers.booleanParser, default: false, }, preventLoginWithUnverifiedEmail: { - env: "PARSE_SERVER_PREVENT_LOGIN_WITH_UNVERIFIED_EMAIL", - help: "Set to `true` to prevent a user from logging in if the email has not yet been verified and email verification is required.

Default is `false`.
Requires option `verifyUserEmails: true`.", + env: 'PARSE_SERVER_PREVENT_LOGIN_WITH_UNVERIFIED_EMAIL', + help: 'Set to `true` to prevent a user from logging in if the email has not yet been verified and email verification is required.

Default is `false`.
Requires option `verifyUserEmails: true`.', action: parsers.booleanParser, default: false, }, preventSignupWithUnverifiedEmail: { - env: "PARSE_SERVER_PREVENT_SIGNUP_WITH_UNVERIFIED_EMAIL", + env: 'PARSE_SERVER_PREVENT_SIGNUP_WITH_UNVERIFIED_EMAIL', help: "If set to `true` it prevents a user from signing up if the email has not yet been verified and email verification is required. In that case the server responds to the sign-up with HTTP status 400 and a Parse Error 205 `EMAIL_NOT_FOUND`. If set to `false` the server responds with HTTP status 200, and client SDKs return an unauthenticated Parse User without session token. In that case subsequent requests fail until the user's email address is verified.

Default is `false`.
Requires option `verifyUserEmails: true`.", action: parsers.booleanParser, default: false, }, protectedFields: { - env: "PARSE_SERVER_PROTECTED_FIELDS", - help: "Protected fields that should be treated with extra security when fetching details.", + env: 'PARSE_SERVER_PROTECTED_FIELDS', + help: 'Protected fields that should be treated with extra security when fetching details.', action: parsers.objectParser, default: { _User: { - "*": ["email"], + '*': ['email'], }, }, }, publicServerURL: { - env: "PARSE_PUBLIC_SERVER_URL", - help: "Public URL to your parse server with http:// or https://.", + env: 'PARSE_PUBLIC_SERVER_URL', + help: 'Public URL to your parse server with http:// or https://.', }, push: { - env: "PARSE_SERVER_PUSH", - help: "Configuration for push, as stringified JSON. See http://docs.parseplatform.org/parse-server/guide/#push-notifications", + env: 'PARSE_SERVER_PUSH', + help: 'Configuration for push, as stringified JSON. See http://docs.parseplatform.org/parse-server/guide/#push-notifications', action: parsers.objectParser, }, rateLimit: { - env: "PARSE_SERVER_RATE_LIMIT", + env: 'PARSE_SERVER_RATE_LIMIT', help: "Options to limit repeated requests to Parse Server APIs. This can be used to protect sensitive endpoints such as `/requestPasswordReset` from brute-force attacks or Parse Server as a whole from denial-of-service (DoS) attacks.

\u2139\uFE0F Mind the following limitations:
- rate limits applied per IP address; this limits protection against distributed denial-of-service (DDoS) attacks where many requests are coming from various IP addresses
- if multiple Parse Server instances are behind a load balancer or ran in a cluster, each instance will calculate it's own request rates, independent from other instances; this limits the applicability of this feature when using a load balancer and another rate limiting solution that takes requests across all instances into account may be more suitable
- this feature provides basic protection against denial-of-service attacks, but a more sophisticated solution works earlier in the request flow and prevents a malicious requests to even reach a server instance; it's therefore recommended to implement a solution according to architecture and user case.", action: parsers.arrayParser, - type: "RateLimitOptions[]", + type: 'RateLimitOptions[]', default: [], }, readOnlyMasterKey: { - env: "PARSE_SERVER_READ_ONLY_MASTER_KEY", - help: "Read-only key, which has the same capabilities as MasterKey without writes", + env: 'PARSE_SERVER_READ_ONLY_MASTER_KEY', + help: 'Read-only key, which has the same capabilities as MasterKey without writes', }, requestKeywordDenylist: { - env: "PARSE_SERVER_REQUEST_KEYWORD_DENYLIST", + env: 'PARSE_SERVER_REQUEST_KEYWORD_DENYLIST', help: 'An array of keys and values that are prohibited in database read and write requests to prevent potential security vulnerabilities. It is possible to specify only a key (`{"key":"..."}`), only a value (`{"value":"..."}`) or a key-value pair (`{"key":"...","value":"..."}`). The specification can use the following types: `boolean`, `numeric` or `string`, where `string` will be interpreted as a regex notation. Request data is deep-scanned for matching definitions to detect also any nested occurrences. Defaults are patterns that are likely to be used in malicious requests. Setting this option will override the default patterns.', action: parsers.arrayParser, default: [ { - key: "_bsontype", - value: "Code", + key: '_bsontype', + value: 'Code', }, { - key: "constructor", + key: 'constructor', }, { - key: "__proto__", + key: '__proto__', }, ], }, restAPIKey: { - env: "PARSE_SERVER_REST_API_KEY", - help: "Key for REST calls", + env: 'PARSE_SERVER_REST_API_KEY', + help: 'Key for REST calls', }, revokeSessionOnPasswordReset: { - env: "PARSE_SERVER_REVOKE_SESSION_ON_PASSWORD_RESET", + env: 'PARSE_SERVER_REVOKE_SESSION_ON_PASSWORD_RESET', help: "When a user changes their password, either through the reset password email or while logged in, all sessions are revoked if this is true. Set to false if you don't want to revoke sessions.", action: parsers.booleanParser, default: true, }, scheduledPush: { - env: "PARSE_SERVER_SCHEDULED_PUSH", - help: "Configuration for push scheduling, defaults to false.", + env: 'PARSE_SERVER_SCHEDULED_PUSH', + help: 'Configuration for push scheduling, defaults to false.', action: parsers.booleanParser, default: false, }, schema: { - env: "PARSE_SERVER_SCHEMA", - help: "Defined schema", + env: 'PARSE_SERVER_SCHEMA', + help: 'Defined schema', action: parsers.objectParser, - type: "SchemaOptions", + type: 'SchemaOptions', }, security: { - env: "PARSE_SERVER_SECURITY", - help: "The security options to identify and report weak security settings.", + env: 'PARSE_SERVER_SECURITY', + help: 'The security options to identify and report weak security settings.', action: parsers.objectParser, - type: "SecurityOptions", + type: 'SecurityOptions', default: {}, }, sendUserEmailVerification: { - env: "PARSE_SERVER_SEND_USER_EMAIL_VERIFICATION", - help: "Set to `false` to prevent sending of verification email. Supports a function with a return value of `true` or `false` for conditional email sending.

Default is `true`.
", + env: 'PARSE_SERVER_SEND_USER_EMAIL_VERIFICATION', + help: 'Set to `false` to prevent sending of verification email. Supports a function with a return value of `true` or `false` for conditional email sending.

Default is `true`.
', default: true, }, serverCloseComplete: { - env: "PARSE_SERVER_SERVER_CLOSE_COMPLETE", - help: "Callback when server has closed", + env: 'PARSE_SERVER_SERVER_CLOSE_COMPLETE', + help: 'Callback when server has closed', }, serverURL: { - env: "PARSE_SERVER_URL", - help: "URL to your parse server with http:// or https://.", + env: 'PARSE_SERVER_URL', + help: 'URL to your parse server with http:// or https://.', required: true, }, sessionLength: { - env: "PARSE_SERVER_SESSION_LENGTH", - help: "Session duration, in seconds, defaults to 1 year", - action: parsers.numberParser("sessionLength"), + env: 'PARSE_SERVER_SESSION_LENGTH', + help: 'Session duration, in seconds, defaults to 1 year', + action: parsers.numberParser('sessionLength'), default: 31536000, }, silent: { - env: "SILENT", - help: "Disables console output", + env: 'SILENT', + help: 'Disables console output', action: parsers.booleanParser, }, startLiveQueryServer: { - env: "PARSE_SERVER_START_LIVE_QUERY_SERVER", - help: "Starts the liveQuery server", + env: 'PARSE_SERVER_START_LIVE_QUERY_SERVER', + help: 'Starts the liveQuery server', action: parsers.booleanParser, }, trustProxy: { - env: "PARSE_SERVER_TRUST_PROXY", + env: 'PARSE_SERVER_TRUST_PROXY', help: 'The trust proxy settings. It is important to understand the exact setup of the reverse proxy, since this setting will trust values provided in the Parse Server API request. See the express trust proxy settings documentation. Defaults to `false`.', action: parsers.objectParser, default: [], }, userSensitiveFields: { - env: "PARSE_SERVER_USER_SENSITIVE_FIELDS", - help: "Personally identifiable information fields in the user table the should be removed for non-authorized users. Deprecated @see protectedFields", + env: 'PARSE_SERVER_USER_SENSITIVE_FIELDS', + help: 'Personally identifiable information fields in the user table the should be removed for non-authorized users. Deprecated @see protectedFields', action: parsers.arrayParser, }, verbose: { - env: "VERBOSE", - help: "Set the logging to verbose", + env: 'VERBOSE', + help: 'Set the logging to verbose', action: parsers.booleanParser, }, verifyUserEmails: { - env: "PARSE_SERVER_VERIFY_USER_EMAILS", - help: "Set to `true` to require users to verify their email address to complete the sign-up process. Supports a function with a return value of `true` or `false` for conditional verification.

Default is `false`.", + env: 'PARSE_SERVER_VERIFY_USER_EMAILS', + help: 'Set to `true` to require users to verify their email address to complete the sign-up process. Supports a function with a return value of `true` or `false` for conditional verification.

Default is `false`.', default: false, }, webhookKey: { - env: "PARSE_SERVER_WEBHOOK_KEY", - help: "Key sent with outgoing webhook calls", + env: 'PARSE_SERVER_WEBHOOK_KEY', + help: 'Key sent with outgoing webhook calls', }, }; module.exports.RateLimitOptions = { errorResponseMessage: { - env: "PARSE_SERVER_RATE_LIMIT_ERROR_RESPONSE_MESSAGE", - help: "The error message that should be returned in the body of the HTTP 429 response when the rate limit is hit. Default is `Too many requests.`.", - default: "Too many requests.", + env: 'PARSE_SERVER_RATE_LIMIT_ERROR_RESPONSE_MESSAGE', + help: 'The error message that should be returned in the body of the HTTP 429 response when the rate limit is hit. Default is `Too many requests.`.', + default: 'Too many requests.', }, includeInternalRequests: { - env: "PARSE_SERVER_RATE_LIMIT_INCLUDE_INTERNAL_REQUESTS", - help: "Optional, if `true` the rate limit will also apply to requests that are made in by Cloud Code, default is `false`. Note that a public Cloud Code function that triggers internal requests may circumvent rate limiting and be vulnerable to attacks.", + env: 'PARSE_SERVER_RATE_LIMIT_INCLUDE_INTERNAL_REQUESTS', + help: 'Optional, if `true` the rate limit will also apply to requests that are made in by Cloud Code, default is `false`. Note that a public Cloud Code function that triggers internal requests may circumvent rate limiting and be vulnerable to attacks.', action: parsers.booleanParser, default: false, }, includeMasterKey: { - env: "PARSE_SERVER_RATE_LIMIT_INCLUDE_MASTER_KEY", - help: "Optional, if `true` the rate limit will also apply to requests using the `masterKey`, default is `false`. Note that a public Cloud Code function that triggers internal requests using the `masterKey` may circumvent rate limiting and be vulnerable to attacks.", + env: 'PARSE_SERVER_RATE_LIMIT_INCLUDE_MASTER_KEY', + help: 'Optional, if `true` the rate limit will also apply to requests using the `masterKey`, default is `false`. Note that a public Cloud Code function that triggers internal requests using the `masterKey` may circumvent rate limiting and be vulnerable to attacks.', action: parsers.booleanParser, default: false, }, redisUrl: { - env: "PARSE_SERVER_RATE_LIMIT_REDIS_URL", - help: "Optional, the URL of the Redis server to store rate limit data. This allows to rate limit requests for multiple servers by calculating the sum of all requests across all servers. This is useful if multiple servers are processing requests behind a load balancer. For example, the limit of 10 requests is reached if each of 2 servers processed 5 requests.", + env: 'PARSE_SERVER_RATE_LIMIT_REDIS_URL', + help: 'Optional, the URL of the Redis server to store rate limit data. This allows to rate limit requests for multiple servers by calculating the sum of all requests across all servers. This is useful if multiple servers are processing requests behind a load balancer. For example, the limit of 10 requests is reached if each of 2 servers processed 5 requests.', }, requestCount: { - env: "PARSE_SERVER_RATE_LIMIT_REQUEST_COUNT", - help: "The number of requests that can be made per IP address within the time window set in `requestTimeWindow` before the rate limit is applied.", - action: parsers.numberParser("requestCount"), + env: 'PARSE_SERVER_RATE_LIMIT_REQUEST_COUNT', + help: 'The number of requests that can be made per IP address within the time window set in `requestTimeWindow` before the rate limit is applied.', + action: parsers.numberParser('requestCount'), }, requestMethods: { - env: "PARSE_SERVER_RATE_LIMIT_REQUEST_METHODS", - help: "Optional, the HTTP request methods to which the rate limit should be applied, default is all methods.", + env: 'PARSE_SERVER_RATE_LIMIT_REQUEST_METHODS', + help: 'Optional, the HTTP request methods to which the rate limit should be applied, default is all methods.', action: parsers.arrayParser, }, requestPath: { - env: "PARSE_SERVER_RATE_LIMIT_REQUEST_PATH", - help: "The path of the API route to be rate limited. Route paths, in combination with a request method, define the endpoints at which requests can be made. Route paths can be strings, string patterns, or regular expression. See: https://expressjs.com/en/guide/routing.html", + env: 'PARSE_SERVER_RATE_LIMIT_REQUEST_PATH', + help: 'The path of the API route to be rate limited. Route paths, in combination with a request method, define the endpoints at which requests can be made. Route paths can be strings, string patterns, or regular expression. See: https://expressjs.com/en/guide/routing.html', required: true, }, requestTimeWindow: { - env: "PARSE_SERVER_RATE_LIMIT_REQUEST_TIME_WINDOW", - help: "The window of time in milliseconds within which the number of requests set in `requestCount` can be made before the rate limit is applied.", - action: parsers.numberParser("requestTimeWindow"), + env: 'PARSE_SERVER_RATE_LIMIT_REQUEST_TIME_WINDOW', + help: 'The window of time in milliseconds within which the number of requests set in `requestCount` can be made before the rate limit is applied.', + action: parsers.numberParser('requestTimeWindow'), }, zone: { - env: "PARSE_SERVER_RATE_LIMIT_ZONE", + env: 'PARSE_SERVER_RATE_LIMIT_ZONE', help: "The type of rate limit to apply. The following types are supported:

- `global`: rate limit based on the number of requests made by all users
- `ip`: rate limit based on the IP address of the request
- `user`: rate limit based on the user ID of the request
- `session`: rate limit based on the session token of the request


:default: 'ip'", }, }; module.exports.SecurityOptions = { checkGroups: { - env: "PARSE_SERVER_SECURITY_CHECK_GROUPS", - help: "The security check groups to run. This allows to add custom security checks or override existing ones. Default are the groups defined in `CheckGroups.js`.", + env: 'PARSE_SERVER_SECURITY_CHECK_GROUPS', + help: 'The security check groups to run. This allows to add custom security checks or override existing ones. Default are the groups defined in `CheckGroups.js`.', action: parsers.arrayParser, }, enableCheck: { - env: "PARSE_SERVER_SECURITY_ENABLE_CHECK", - help: "Is true if Parse Server should check for weak security settings.", + env: 'PARSE_SERVER_SECURITY_ENABLE_CHECK', + help: 'Is true if Parse Server should check for weak security settings.', action: parsers.booleanParser, default: false, }, enableCheckLog: { - env: "PARSE_SERVER_SECURITY_ENABLE_CHECK_LOG", - help: "Is true if the security check report should be written to logs. This should only be enabled temporarily to not expose weak security settings in logs.", + env: 'PARSE_SERVER_SECURITY_ENABLE_CHECK_LOG', + help: 'Is true if the security check report should be written to logs. This should only be enabled temporarily to not expose weak security settings in logs.', action: parsers.booleanParser, default: false, }, }; module.exports.PagesOptions = { customRoutes: { - env: "PARSE_SERVER_PAGES_CUSTOM_ROUTES", - help: "The custom routes.", + env: 'PARSE_SERVER_PAGES_CUSTOM_ROUTES', + help: 'The custom routes.', action: parsers.arrayParser, - type: "PagesRoute[]", + type: 'PagesRoute[]', default: [], }, customUrls: { - env: "PARSE_SERVER_PAGES_CUSTOM_URLS", - help: "The URLs to the custom pages.", + env: 'PARSE_SERVER_PAGES_CUSTOM_URLS', + help: 'The URLs to the custom pages.', action: parsers.objectParser, - type: "PagesCustomUrlsOptions", + type: 'PagesCustomUrlsOptions', default: {}, }, enableLocalization: { - env: "PARSE_SERVER_PAGES_ENABLE_LOCALIZATION", - help: "Is true if pages should be localized; this has no effect on custom page redirects.", + env: 'PARSE_SERVER_PAGES_ENABLE_LOCALIZATION', + help: 'Is true if pages should be localized; this has no effect on custom page redirects.', action: parsers.booleanParser, default: false, }, enableRouter: { - env: "PARSE_SERVER_PAGES_ENABLE_ROUTER", - help: "Is true if the pages router should be enabled; this is required for any of the pages options to take effect.", + env: 'PARSE_SERVER_PAGES_ENABLE_ROUTER', + help: 'Is true if the pages router should be enabled; this is required for any of the pages options to take effect.', action: parsers.booleanParser, default: false, }, forceRedirect: { - env: "PARSE_SERVER_PAGES_FORCE_REDIRECT", - help: "Is true if responses should always be redirects and never content, false if the response type should depend on the request type (GET request -> content response; POST request -> redirect response).", + env: 'PARSE_SERVER_PAGES_FORCE_REDIRECT', + help: 'Is true if responses should always be redirects and never content, false if the response type should depend on the request type (GET request -> content response; POST request -> redirect response).', action: parsers.booleanParser, default: false, }, localizationFallbackLocale: { - env: "PARSE_SERVER_PAGES_LOCALIZATION_FALLBACK_LOCALE", - help: "The fallback locale for localization if no matching translation is provided for the given locale. This is only relevant when providing translation resources via JSON file.", - default: "en", + env: 'PARSE_SERVER_PAGES_LOCALIZATION_FALLBACK_LOCALE', + help: 'The fallback locale for localization if no matching translation is provided for the given locale. This is only relevant when providing translation resources via JSON file.', + default: 'en', }, localizationJsonPath: { - env: "PARSE_SERVER_PAGES_LOCALIZATION_JSON_PATH", - help: "The path to the JSON file for localization; the translations will be used to fill template placeholders according to the locale.", + env: 'PARSE_SERVER_PAGES_LOCALIZATION_JSON_PATH', + help: 'The path to the JSON file for localization; the translations will be used to fill template placeholders according to the locale.', }, pagesEndpoint: { - env: "PARSE_SERVER_PAGES_PAGES_ENDPOINT", + env: 'PARSE_SERVER_PAGES_PAGES_ENDPOINT', help: "The API endpoint for the pages. Default is 'apps'.", - default: "apps", + default: 'apps', }, pagesPath: { - env: "PARSE_SERVER_PAGES_PAGES_PATH", + env: 'PARSE_SERVER_PAGES_PAGES_PATH', help: "The path to the pages directory; this also defines where the static endpoint '/apps' points to. Default is the './public/' directory.", - default: "./public", + default: './public', }, placeholders: { - env: "PARSE_SERVER_PAGES_PLACEHOLDERS", - help: "The placeholder keys and values which will be filled in pages; this can be a simple object or a callback function.", + env: 'PARSE_SERVER_PAGES_PLACEHOLDERS', + help: 'The placeholder keys and values which will be filled in pages; this can be a simple object or a callback function.', action: parsers.objectParser, default: {}, }, }; module.exports.PagesRoute = { handler: { - env: "PARSE_SERVER_PAGES_ROUTE_HANDLER", - help: "The route handler that is an async function.", + env: 'PARSE_SERVER_PAGES_ROUTE_HANDLER', + help: 'The route handler that is an async function.', required: true, }, method: { - env: "PARSE_SERVER_PAGES_ROUTE_METHOD", + env: 'PARSE_SERVER_PAGES_ROUTE_METHOD', help: "The route method, e.g. 'GET' or 'POST'.", required: true, }, path: { - env: "PARSE_SERVER_PAGES_ROUTE_PATH", - help: "The route path.", + env: 'PARSE_SERVER_PAGES_ROUTE_PATH', + help: 'The route path.', required: true, }, }; module.exports.PagesCustomUrlsOptions = { emailVerificationLinkExpired: { - env: "PARSE_SERVER_PAGES_CUSTOM_URL_EMAIL_VERIFICATION_LINK_EXPIRED", - help: "The URL to the custom page for email verification -> link expired.", + env: 'PARSE_SERVER_PAGES_CUSTOM_URL_EMAIL_VERIFICATION_LINK_EXPIRED', + help: 'The URL to the custom page for email verification -> link expired.', }, emailVerificationLinkInvalid: { - env: "PARSE_SERVER_PAGES_CUSTOM_URL_EMAIL_VERIFICATION_LINK_INVALID", - help: "The URL to the custom page for email verification -> link invalid.", + env: 'PARSE_SERVER_PAGES_CUSTOM_URL_EMAIL_VERIFICATION_LINK_INVALID', + help: 'The URL to the custom page for email verification -> link invalid.', }, emailVerificationSendFail: { - env: "PARSE_SERVER_PAGES_CUSTOM_URL_EMAIL_VERIFICATION_SEND_FAIL", - help: "The URL to the custom page for email verification -> link send fail.", + env: 'PARSE_SERVER_PAGES_CUSTOM_URL_EMAIL_VERIFICATION_SEND_FAIL', + help: 'The URL to the custom page for email verification -> link send fail.', }, emailVerificationSendSuccess: { - env: "PARSE_SERVER_PAGES_CUSTOM_URL_EMAIL_VERIFICATION_SEND_SUCCESS", - help: "The URL to the custom page for email verification -> resend link -> success.", + env: 'PARSE_SERVER_PAGES_CUSTOM_URL_EMAIL_VERIFICATION_SEND_SUCCESS', + help: 'The URL to the custom page for email verification -> resend link -> success.', }, emailVerificationSuccess: { - env: "PARSE_SERVER_PAGES_CUSTOM_URL_EMAIL_VERIFICATION_SUCCESS", - help: "The URL to the custom page for email verification -> success.", + env: 'PARSE_SERVER_PAGES_CUSTOM_URL_EMAIL_VERIFICATION_SUCCESS', + help: 'The URL to the custom page for email verification -> success.', }, passwordReset: { - env: "PARSE_SERVER_PAGES_CUSTOM_URL_PASSWORD_RESET", - help: "The URL to the custom page for password reset.", + env: 'PARSE_SERVER_PAGES_CUSTOM_URL_PASSWORD_RESET', + help: 'The URL to the custom page for password reset.', }, passwordResetLinkInvalid: { - env: "PARSE_SERVER_PAGES_CUSTOM_URL_PASSWORD_RESET_LINK_INVALID", - help: "The URL to the custom page for password reset -> link invalid.", + env: 'PARSE_SERVER_PAGES_CUSTOM_URL_PASSWORD_RESET_LINK_INVALID', + help: 'The URL to the custom page for password reset -> link invalid.', }, passwordResetSuccess: { - env: "PARSE_SERVER_PAGES_CUSTOM_URL_PASSWORD_RESET_SUCCESS", - help: "The URL to the custom page for password reset -> success.", + env: 'PARSE_SERVER_PAGES_CUSTOM_URL_PASSWORD_RESET_SUCCESS', + help: 'The URL to the custom page for password reset -> success.', }, }; module.exports.CustomPagesOptions = { choosePassword: { - env: "PARSE_SERVER_CUSTOM_PAGES_CHOOSE_PASSWORD", - help: "choose password page path", + env: 'PARSE_SERVER_CUSTOM_PAGES_CHOOSE_PASSWORD', + help: 'choose password page path', }, expiredVerificationLink: { - env: "PARSE_SERVER_CUSTOM_PAGES_EXPIRED_VERIFICATION_LINK", - help: "expired verification link page path", + env: 'PARSE_SERVER_CUSTOM_PAGES_EXPIRED_VERIFICATION_LINK', + help: 'expired verification link page path', }, invalidLink: { - env: "PARSE_SERVER_CUSTOM_PAGES_INVALID_LINK", - help: "invalid link page path", + env: 'PARSE_SERVER_CUSTOM_PAGES_INVALID_LINK', + help: 'invalid link page path', }, invalidPasswordResetLink: { - env: "PARSE_SERVER_CUSTOM_PAGES_INVALID_PASSWORD_RESET_LINK", - help: "invalid password reset link page path", + env: 'PARSE_SERVER_CUSTOM_PAGES_INVALID_PASSWORD_RESET_LINK', + help: 'invalid password reset link page path', }, invalidVerificationLink: { - env: "PARSE_SERVER_CUSTOM_PAGES_INVALID_VERIFICATION_LINK", - help: "invalid verification link page path", + env: 'PARSE_SERVER_CUSTOM_PAGES_INVALID_VERIFICATION_LINK', + help: 'invalid verification link page path', }, linkSendFail: { - env: "PARSE_SERVER_CUSTOM_PAGES_LINK_SEND_FAIL", - help: "verification link send fail page path", + env: 'PARSE_SERVER_CUSTOM_PAGES_LINK_SEND_FAIL', + help: 'verification link send fail page path', }, linkSendSuccess: { - env: "PARSE_SERVER_CUSTOM_PAGES_LINK_SEND_SUCCESS", - help: "verification link send success page path", + env: 'PARSE_SERVER_CUSTOM_PAGES_LINK_SEND_SUCCESS', + help: 'verification link send success page path', }, parseFrameURL: { - env: "PARSE_SERVER_CUSTOM_PAGES_PARSE_FRAME_URL", - help: "for masking user-facing pages", + env: 'PARSE_SERVER_CUSTOM_PAGES_PARSE_FRAME_URL', + help: 'for masking user-facing pages', }, passwordResetSuccess: { - env: "PARSE_SERVER_CUSTOM_PAGES_PASSWORD_RESET_SUCCESS", - help: "password reset success page path", + env: 'PARSE_SERVER_CUSTOM_PAGES_PASSWORD_RESET_SUCCESS', + help: 'password reset success page path', }, verifyEmailSuccess: { - env: "PARSE_SERVER_CUSTOM_PAGES_VERIFY_EMAIL_SUCCESS", - help: "verify email success page path", + env: 'PARSE_SERVER_CUSTOM_PAGES_VERIFY_EMAIL_SUCCESS', + help: 'verify email success page path', }, }; module.exports.LiveQueryOptions = { classNames: { - env: "PARSE_SERVER_LIVEQUERY_CLASSNAMES", + env: 'PARSE_SERVER_LIVEQUERY_CLASSNAMES', help: "parse-server's LiveQuery classNames", action: parsers.arrayParser, }, pubSubAdapter: { - env: "PARSE_SERVER_LIVEQUERY_PUB_SUB_ADAPTER", - help: "LiveQuery pubsub adapter", + env: 'PARSE_SERVER_LIVEQUERY_PUB_SUB_ADAPTER', + help: 'LiveQuery pubsub adapter', action: parsers.moduleOrObjectParser, }, redisOptions: { - env: "PARSE_SERVER_LIVEQUERY_REDIS_OPTIONS", + env: 'PARSE_SERVER_LIVEQUERY_REDIS_OPTIONS', help: "parse-server's LiveQuery redisOptions", action: parsers.objectParser, }, redisURL: { - env: "PARSE_SERVER_LIVEQUERY_REDIS_URL", + env: 'PARSE_SERVER_LIVEQUERY_REDIS_URL', help: "parse-server's LiveQuery redisURL", }, wssAdapter: { - env: "PARSE_SERVER_LIVEQUERY_WSS_ADAPTER", - help: "Adapter module for the WebSocketServer", + env: 'PARSE_SERVER_LIVEQUERY_WSS_ADAPTER', + help: 'Adapter module for the WebSocketServer', action: parsers.moduleOrObjectParser, }, }; module.exports.LiveQueryServerOptions = { appId: { - env: "PARSE_LIVE_QUERY_SERVER_APP_ID", - help: "This string should match the appId in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same appId.", + env: 'PARSE_LIVE_QUERY_SERVER_APP_ID', + help: 'This string should match the appId in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same appId.', }, cacheTimeout: { - env: "PARSE_LIVE_QUERY_SERVER_CACHE_TIMEOUT", + env: 'PARSE_LIVE_QUERY_SERVER_CACHE_TIMEOUT', help: "Number in milliseconds. When clients provide the sessionToken to the LiveQuery server, the LiveQuery server will try to fetch its ParseUser's objectId from parse server and store it in the cache. The value defines the duration of the cache. Check the following Security section and our protocol specification for details, defaults to 5 * 1000 ms (5 seconds).", - action: parsers.numberParser("cacheTimeout"), + action: parsers.numberParser('cacheTimeout'), }, keyPairs: { - env: "PARSE_LIVE_QUERY_SERVER_KEY_PAIRS", - help: "A JSON object that serves as a whitelist of keys. It is used for validating clients when they try to connect to the LiveQuery server. Check the following Security section and our protocol specification for details.", + env: 'PARSE_LIVE_QUERY_SERVER_KEY_PAIRS', + help: 'A JSON object that serves as a whitelist of keys. It is used for validating clients when they try to connect to the LiveQuery server. Check the following Security section and our protocol specification for details.', action: parsers.objectParser, }, logLevel: { - env: "PARSE_LIVE_QUERY_SERVER_LOG_LEVEL", - help: "This string defines the log level of the LiveQuery server. We support VERBOSE, INFO, ERROR, NONE, defaults to INFO.", + env: 'PARSE_LIVE_QUERY_SERVER_LOG_LEVEL', + help: 'This string defines the log level of the LiveQuery server. We support VERBOSE, INFO, ERROR, NONE, defaults to INFO.', }, masterKey: { - env: "PARSE_LIVE_QUERY_SERVER_MASTER_KEY", - help: "This string should match the masterKey in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same masterKey.", + env: 'PARSE_LIVE_QUERY_SERVER_MASTER_KEY', + help: 'This string should match the masterKey in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same masterKey.', }, port: { - env: "PARSE_LIVE_QUERY_SERVER_PORT", - help: "The port to run the LiveQuery server, defaults to 1337.", - action: parsers.numberParser("port"), + env: 'PARSE_LIVE_QUERY_SERVER_PORT', + help: 'The port to run the LiveQuery server, defaults to 1337.', + action: parsers.numberParser('port'), default: 1337, }, pubSubAdapter: { - env: "PARSE_LIVE_QUERY_SERVER_PUB_SUB_ADAPTER", - help: "LiveQuery pubsub adapter", + env: 'PARSE_LIVE_QUERY_SERVER_PUB_SUB_ADAPTER', + help: 'LiveQuery pubsub adapter', action: parsers.moduleOrObjectParser, }, redisOptions: { - env: "PARSE_LIVE_QUERY_SERVER_REDIS_OPTIONS", + env: 'PARSE_LIVE_QUERY_SERVER_REDIS_OPTIONS', help: "parse-server's LiveQuery redisOptions", action: parsers.objectParser, }, redisURL: { - env: "PARSE_LIVE_QUERY_SERVER_REDIS_URL", + env: 'PARSE_LIVE_QUERY_SERVER_REDIS_URL', help: "parse-server's LiveQuery redisURL", }, serverURL: { - env: "PARSE_LIVE_QUERY_SERVER_SERVER_URL", - help: "This string should match the serverURL in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same serverURL.", + env: 'PARSE_LIVE_QUERY_SERVER_SERVER_URL', + help: 'This string should match the serverURL in use by your Parse Server. If you deploy the LiveQuery server alongside Parse Server, the LiveQuery server will try to use the same serverURL.', }, websocketTimeout: { - env: "PARSE_LIVE_QUERY_SERVER_WEBSOCKET_TIMEOUT", - help: "Number of milliseconds between ping/pong frames. The WebSocket server sends ping/pong frames to the clients to keep the WebSocket alive. This value defines the interval of the ping/pong frame from the server to clients, defaults to 10 * 1000 ms (10 s).", - action: parsers.numberParser("websocketTimeout"), + env: 'PARSE_LIVE_QUERY_SERVER_WEBSOCKET_TIMEOUT', + help: 'Number of milliseconds between ping/pong frames. The WebSocket server sends ping/pong frames to the clients to keep the WebSocket alive. This value defines the interval of the ping/pong frame from the server to clients, defaults to 10 * 1000 ms (10 s).', + action: parsers.numberParser('websocketTimeout'), }, wssAdapter: { - env: "PARSE_LIVE_QUERY_SERVER_WSS_ADAPTER", - help: "Adapter module for the WebSocketServer", + env: 'PARSE_LIVE_QUERY_SERVER_WSS_ADAPTER', + help: 'Adapter module for the WebSocketServer', action: parsers.moduleOrObjectParser, }, }; module.exports.IdempotencyOptions = { paths: { - env: "PARSE_SERVER_EXPERIMENTAL_IDEMPOTENCY_PATHS", - help: "An array of paths for which the feature should be enabled. The mount path must not be included, for example instead of `/parse/functions/myFunction` specifiy `functions/myFunction`. The entries are interpreted as regular expression, for example `functions/.*` matches all functions, `jobs/.*` matches all jobs, `classes/.*` matches all classes, `.*` matches all paths.", + env: 'PARSE_SERVER_EXPERIMENTAL_IDEMPOTENCY_PATHS', + help: 'An array of paths for which the feature should be enabled. The mount path must not be included, for example instead of `/parse/functions/myFunction` specifiy `functions/myFunction`. The entries are interpreted as regular expression, for example `functions/.*` matches all functions, `jobs/.*` matches all jobs, `classes/.*` matches all classes, `.*` matches all paths.', action: parsers.arrayParser, default: [], }, ttl: { - env: "PARSE_SERVER_EXPERIMENTAL_IDEMPOTENCY_TTL", - help: "The duration in seconds after which a request record is discarded from the database, defaults to 300s.", - action: parsers.numberParser("ttl"), + env: 'PARSE_SERVER_EXPERIMENTAL_IDEMPOTENCY_TTL', + help: 'The duration in seconds after which a request record is discarded from the database, defaults to 300s.', + action: parsers.numberParser('ttl'), default: 300, }, }; module.exports.AccountLockoutOptions = { duration: { - env: "PARSE_SERVER_ACCOUNT_LOCKOUT_DURATION", - help: "Set the duration in minutes that a locked-out account remains locked out before automatically becoming unlocked.

Valid values are greater than `0` and less than `100000`.", - action: parsers.numberParser("duration"), + env: 'PARSE_SERVER_ACCOUNT_LOCKOUT_DURATION', + help: 'Set the duration in minutes that a locked-out account remains locked out before automatically becoming unlocked.

Valid values are greater than `0` and less than `100000`.', + action: parsers.numberParser('duration'), }, threshold: { - env: "PARSE_SERVER_ACCOUNT_LOCKOUT_THRESHOLD", - help: "Set the number of failed sign-in attempts that will cause a user account to be locked. If the account is locked. The account will unlock after the duration set in the `duration` option has passed and no further login attempts have been made.

Valid values are greater than `0` and less than `1000`.", - action: parsers.numberParser("threshold"), + env: 'PARSE_SERVER_ACCOUNT_LOCKOUT_THRESHOLD', + help: 'Set the number of failed sign-in attempts that will cause a user account to be locked. If the account is locked. The account will unlock after the duration set in the `duration` option has passed and no further login attempts have been made.

Valid values are greater than `0` and less than `1000`.', + action: parsers.numberParser('threshold'), }, unlockOnPasswordReset: { - env: "PARSE_SERVER_ACCOUNT_LOCKOUT_UNLOCK_ON_PASSWORD_RESET", - help: "Set to `true` if the account should be unlocked after a successful password reset.

Default is `false`.
Requires options `duration` and `threshold` to be set.", + env: 'PARSE_SERVER_ACCOUNT_LOCKOUT_UNLOCK_ON_PASSWORD_RESET', + help: 'Set to `true` if the account should be unlocked after a successful password reset.

Default is `false`.
Requires options `duration` and `threshold` to be set.', action: parsers.booleanParser, default: false, }, }; module.exports.PasswordPolicyOptions = { doNotAllowUsername: { - env: "PARSE_SERVER_PASSWORD_POLICY_DO_NOT_ALLOW_USERNAME", - help: "Set to `true` to disallow the username as part of the password.

Default is `false`.", + env: 'PARSE_SERVER_PASSWORD_POLICY_DO_NOT_ALLOW_USERNAME', + help: 'Set to `true` to disallow the username as part of the password.

Default is `false`.', action: parsers.booleanParser, default: false, }, maxPasswordAge: { - env: "PARSE_SERVER_PASSWORD_POLICY_MAX_PASSWORD_AGE", - help: "Set the number of days after which a password expires. Login attempts fail if the user does not reset the password before expiration.", - action: parsers.numberParser("maxPasswordAge"), + env: 'PARSE_SERVER_PASSWORD_POLICY_MAX_PASSWORD_AGE', + help: 'Set the number of days after which a password expires. Login attempts fail if the user does not reset the password before expiration.', + action: parsers.numberParser('maxPasswordAge'), }, maxPasswordHistory: { - env: "PARSE_SERVER_PASSWORD_POLICY_MAX_PASSWORD_HISTORY", - help: "Set the number of previous password that will not be allowed to be set as new password. If the option is not set or set to `0`, no previous passwords will be considered.

Valid values are >= `0` and <= `20`.
Default is `0`.", - action: parsers.numberParser("maxPasswordHistory"), + env: 'PARSE_SERVER_PASSWORD_POLICY_MAX_PASSWORD_HISTORY', + help: 'Set the number of previous password that will not be allowed to be set as new password. If the option is not set or set to `0`, no previous passwords will be considered.

Valid values are >= `0` and <= `20`.
Default is `0`.', + action: parsers.numberParser('maxPasswordHistory'), }, resetPasswordSuccessOnInvalidEmail: { - env: "PARSE_SERVER_PASSWORD_POLICY_RESET_PASSWORD_SUCCESS_ON_INVALID_EMAIL", - help: "Set to `true` if a request to reset the password should return a success response even if the provided email address is invalid, or `false` if the request should return an error response if the email address is invalid.

Default is `true`.", + env: 'PARSE_SERVER_PASSWORD_POLICY_RESET_PASSWORD_SUCCESS_ON_INVALID_EMAIL', + help: 'Set to `true` if a request to reset the password should return a success response even if the provided email address is invalid, or `false` if the request should return an error response if the email address is invalid.

Default is `true`.', action: parsers.booleanParser, default: true, }, resetTokenReuseIfValid: { - env: "PARSE_SERVER_PASSWORD_POLICY_RESET_TOKEN_REUSE_IF_VALID", - help: "Set to `true` if a password reset token should be reused in case another token is requested but there is a token that is still valid, i.e. has not expired. This avoids the often observed issue that a user requests multiple emails and does not know which link contains a valid token because each newly generated token would invalidate the previous token.

Default is `false`.", + env: 'PARSE_SERVER_PASSWORD_POLICY_RESET_TOKEN_REUSE_IF_VALID', + help: 'Set to `true` if a password reset token should be reused in case another token is requested but there is a token that is still valid, i.e. has not expired. This avoids the often observed issue that a user requests multiple emails and does not know which link contains a valid token because each newly generated token would invalidate the previous token.

Default is `false`.', action: parsers.booleanParser, default: false, }, resetTokenValidityDuration: { - env: "PARSE_SERVER_PASSWORD_POLICY_RESET_TOKEN_VALIDITY_DURATION", - help: "Set the validity duration of the password reset token in seconds after which the token expires. The token is used in the link that is set in the email. After the token expires, the link becomes invalid and a new link has to be sent. If the option is not set or set to `undefined`, then the token never expires.

For example, to expire the token after 2 hours, set a value of 7200 seconds (= 60 seconds * 60 minutes * 2 hours).

Default is `undefined`.", - action: parsers.numberParser("resetTokenValidityDuration"), + env: 'PARSE_SERVER_PASSWORD_POLICY_RESET_TOKEN_VALIDITY_DURATION', + help: 'Set the validity duration of the password reset token in seconds after which the token expires. The token is used in the link that is set in the email. After the token expires, the link becomes invalid and a new link has to be sent. If the option is not set or set to `undefined`, then the token never expires.

For example, to expire the token after 2 hours, set a value of 7200 seconds (= 60 seconds * 60 minutes * 2 hours).

Default is `undefined`.', + action: parsers.numberParser('resetTokenValidityDuration'), }, validationError: { - env: "PARSE_SERVER_PASSWORD_POLICY_VALIDATION_ERROR", - help: "Set the error message to be sent.

Default is `Password does not meet the Password Policy requirements.`", + env: 'PARSE_SERVER_PASSWORD_POLICY_VALIDATION_ERROR', + help: 'Set the error message to be sent.

Default is `Password does not meet the Password Policy requirements.`', }, validatorCallback: { - env: "PARSE_SERVER_PASSWORD_POLICY_VALIDATOR_CALLBACK", - help: "Set a callback function to validate a password to be accepted.

If used in combination with `validatorPattern`, the password must pass both to be accepted.", + env: 'PARSE_SERVER_PASSWORD_POLICY_VALIDATOR_CALLBACK', + help: 'Set a callback function to validate a password to be accepted.

If used in combination with `validatorPattern`, the password must pass both to be accepted.', }, validatorPattern: { - env: "PARSE_SERVER_PASSWORD_POLICY_VALIDATOR_PATTERN", - help: "Set the regular expression validation pattern a password must match to be accepted.

If used in combination with `validatorCallback`, the password must pass both to be accepted.", + env: 'PARSE_SERVER_PASSWORD_POLICY_VALIDATOR_PATTERN', + help: 'Set the regular expression validation pattern a password must match to be accepted.

If used in combination with `validatorCallback`, the password must pass both to be accepted.', }, }; module.exports.FileUploadOptions = { enableForAnonymousUser: { - env: "PARSE_SERVER_FILE_UPLOAD_ENABLE_FOR_ANONYMOUS_USER", - help: "Is true if file upload should be allowed for anonymous users.", + env: 'PARSE_SERVER_FILE_UPLOAD_ENABLE_FOR_ANONYMOUS_USER', + help: 'Is true if file upload should be allowed for anonymous users.', action: parsers.booleanParser, default: false, }, enableForAuthenticatedUser: { - env: "PARSE_SERVER_FILE_UPLOAD_ENABLE_FOR_AUTHENTICATED_USER", - help: "Is true if file upload should be allowed for authenticated users.", + env: 'PARSE_SERVER_FILE_UPLOAD_ENABLE_FOR_AUTHENTICATED_USER', + help: 'Is true if file upload should be allowed for authenticated users.', action: parsers.booleanParser, default: true, }, enableForPublic: { - env: "PARSE_SERVER_FILE_UPLOAD_ENABLE_FOR_PUBLIC", - help: "Is true if file upload should be allowed for anyone, regardless of user authentication.", + env: 'PARSE_SERVER_FILE_UPLOAD_ENABLE_FOR_PUBLIC', + help: 'Is true if file upload should be allowed for anyone, regardless of user authentication.', action: parsers.booleanParser, default: false, }, fileExtensions: { - env: "PARSE_SERVER_FILE_UPLOAD_FILE_EXTENSIONS", + env: 'PARSE_SERVER_FILE_UPLOAD_FILE_EXTENSIONS', help: "Sets the allowed file extensions for uploading files. The extension is defined as an array of file extensions, or a regex pattern.

It is recommended to restrict the file upload extensions as much as possible. HTML files are especially problematic as they may be used by an attacker who uploads a HTML form to look legitimate under your app's domain name, or to compromise the session token of another user via accessing the browser's local storage.

Defaults to `^(?!(h|H)(t|T)(m|M)(l|L)?$)` which allows any file extension except HTML files.", action: parsers.arrayParser, - default: ["^(?!(h|H)(t|T)(m|M)(l|L)?$)"], + default: ['^(?!(h|H)(t|T)(m|M)(l|L)?$)'], }, }; module.exports.DatabaseOptions = { autoSelectFamily: { - env: "PARSE_SERVER_DATABASE_AUTO_SELECT_FAMILY", - help: "The MongoDB driver option to set whether the socket attempts to connect to IPv6 and IPv4 addresses until a connection is established. If available, the driver will select the first IPv6 address.", + env: 'PARSE_SERVER_DATABASE_AUTO_SELECT_FAMILY', + help: 'The MongoDB driver option to set whether the socket attempts to connect to IPv6 and IPv4 addresses until a connection is established. If available, the driver will select the first IPv6 address.', action: parsers.booleanParser, }, autoSelectFamilyAttemptTimeout: { - env: "PARSE_SERVER_DATABASE_AUTO_SELECT_FAMILY_ATTEMPT_TIMEOUT", - help: "The MongoDB driver option to specify the amount of time in milliseconds to wait for a connection attempt to finish before trying the next address when using the autoSelectFamily option. If set to a positive integer less than 10, the value 10 is used instead.", - action: parsers.numberParser("autoSelectFamilyAttemptTimeout"), + env: 'PARSE_SERVER_DATABASE_AUTO_SELECT_FAMILY_ATTEMPT_TIMEOUT', + help: 'The MongoDB driver option to specify the amount of time in milliseconds to wait for a connection attempt to finish before trying the next address when using the autoSelectFamily option. If set to a positive integer less than 10, the value 10 is used instead.', + action: parsers.numberParser('autoSelectFamilyAttemptTimeout'), }, connectTimeoutMS: { - env: "PARSE_SERVER_DATABASE_CONNECT_TIMEOUT_MS", - help: "The MongoDB driver option to specify the amount of time, in milliseconds, to wait to establish a single TCP socket connection to the server before raising an error. Specifying 0 disables the connection timeout.", - action: parsers.numberParser("connectTimeoutMS"), + env: 'PARSE_SERVER_DATABASE_CONNECT_TIMEOUT_MS', + help: 'The MongoDB driver option to specify the amount of time, in milliseconds, to wait to establish a single TCP socket connection to the server before raising an error. Specifying 0 disables the connection timeout.', + action: parsers.numberParser('connectTimeoutMS'), }, enableSchemaHooks: { - env: "PARSE_SERVER_DATABASE_ENABLE_SCHEMA_HOOKS", - help: "Enables database real-time hooks to update single schema cache. Set to `true` if using multiple Parse Servers instances connected to the same database. Failing to do so will cause a schema change to not propagate to all instances and re-syncing will only happen when the instances restart. To use this feature with MongoDB, a replica set cluster with [change stream](https://docs.mongodb.com/manual/changeStreams/#availability) support is required.", + env: 'PARSE_SERVER_DATABASE_ENABLE_SCHEMA_HOOKS', + help: 'Enables database real-time hooks to update single schema cache. Set to `true` if using multiple Parse Servers instances connected to the same database. Failing to do so will cause a schema change to not propagate to all instances and re-syncing will only happen when the instances restart. To use this feature with MongoDB, a replica set cluster with [change stream](https://docs.mongodb.com/manual/changeStreams/#availability) support is required.', action: parsers.booleanParser, default: false, }, maxPoolSize: { - env: "PARSE_SERVER_DATABASE_MAX_POOL_SIZE", - help: "The MongoDB driver option to set the maximum number of opened, cached, ready-to-use database connections maintained by the driver.", - action: parsers.numberParser("maxPoolSize"), + env: 'PARSE_SERVER_DATABASE_MAX_POOL_SIZE', + help: 'The MongoDB driver option to set the maximum number of opened, cached, ready-to-use database connections maintained by the driver.', + action: parsers.numberParser('maxPoolSize'), }, maxStalenessSeconds: { - env: "PARSE_SERVER_DATABASE_MAX_STALENESS_SECONDS", - help: "The MongoDB driver option to set the maximum replication lag for reads from secondary nodes.", - action: parsers.numberParser("maxStalenessSeconds"), + env: 'PARSE_SERVER_DATABASE_MAX_STALENESS_SECONDS', + help: 'The MongoDB driver option to set the maximum replication lag for reads from secondary nodes.', + action: parsers.numberParser('maxStalenessSeconds'), }, maxTimeMS: { - env: "PARSE_SERVER_DATABASE_MAX_TIME_MS", - help: "The MongoDB driver option to set a cumulative time limit in milliseconds for processing operations on a cursor.", - action: parsers.numberParser("maxTimeMS"), + env: 'PARSE_SERVER_DATABASE_MAX_TIME_MS', + help: 'The MongoDB driver option to set a cumulative time limit in milliseconds for processing operations on a cursor.', + action: parsers.numberParser('maxTimeMS'), }, minPoolSize: { - env: "PARSE_SERVER_DATABASE_MIN_POOL_SIZE", - help: "The MongoDB driver option to set the minimum number of opened, cached, ready-to-use database connections maintained by the driver.", - action: parsers.numberParser("minPoolSize"), + env: 'PARSE_SERVER_DATABASE_MIN_POOL_SIZE', + help: 'The MongoDB driver option to set the minimum number of opened, cached, ready-to-use database connections maintained by the driver.', + action: parsers.numberParser('minPoolSize'), }, retryWrites: { - env: "PARSE_SERVER_DATABASE_RETRY_WRITES", - help: "The MongoDB driver option to set whether to retry failed writes.", + env: 'PARSE_SERVER_DATABASE_RETRY_WRITES', + help: 'The MongoDB driver option to set whether to retry failed writes.', action: parsers.booleanParser, }, schemaCacheTtl: { - env: "PARSE_SERVER_DATABASE_SCHEMA_CACHE_TTL", - help: "The duration in seconds after which the schema cache expires and will be refetched from the database. Use this option if using multiple Parse Servers instances connected to the same database. A low duration will cause the schema cache to be updated too often, causing unnecessary database reads. A high duration will cause the schema to be updated too rarely, increasing the time required until schema changes propagate to all server instances. This feature can be used as an alternative or in conjunction with the option `enableSchemaHooks`. Default is infinite which means the schema cache never expires.", - action: parsers.numberParser("schemaCacheTtl"), + env: 'PARSE_SERVER_DATABASE_SCHEMA_CACHE_TTL', + help: 'The duration in seconds after which the schema cache expires and will be refetched from the database. Use this option if using multiple Parse Servers instances connected to the same database. A low duration will cause the schema cache to be updated too often, causing unnecessary database reads. A high duration will cause the schema to be updated too rarely, increasing the time required until schema changes propagate to all server instances. This feature can be used as an alternative or in conjunction with the option `enableSchemaHooks`. Default is infinite which means the schema cache never expires.', + action: parsers.numberParser('schemaCacheTtl'), }, socketTimeoutMS: { - env: "PARSE_SERVER_DATABASE_SOCKET_TIMEOUT_MS", - help: "The MongoDB driver option to specify the amount of time, in milliseconds, spent attempting to send or receive on a socket before timing out. Specifying 0 means no timeout.", - action: parsers.numberParser("socketTimeoutMS"), + env: 'PARSE_SERVER_DATABASE_SOCKET_TIMEOUT_MS', + help: 'The MongoDB driver option to specify the amount of time, in milliseconds, spent attempting to send or receive on a socket before timing out. Specifying 0 means no timeout.', + action: parsers.numberParser('socketTimeoutMS'), }, }; module.exports.AuthAdapter = { enabled: { - help: "Is `true` if the auth adapter is enabled, `false` otherwise.", + help: 'Is `true` if the auth adapter is enabled, `false` otherwise.', action: parsers.booleanParser, default: false, }, }; module.exports.LogLevels = { cloudFunctionError: { - env: "PARSE_SERVER_LOG_LEVELS_CLOUD_FUNCTION_ERROR", - help: "Log level used by the Cloud Code Functions on error. Default is `error`.", - default: "error", + env: 'PARSE_SERVER_LOG_LEVELS_CLOUD_FUNCTION_ERROR', + help: 'Log level used by the Cloud Code Functions on error. Default is `error`.', + default: 'error', }, cloudFunctionSuccess: { - env: "PARSE_SERVER_LOG_LEVELS_CLOUD_FUNCTION_SUCCESS", - help: "Log level used by the Cloud Code Functions on success. Default is `info`.", - default: "info", + env: 'PARSE_SERVER_LOG_LEVELS_CLOUD_FUNCTION_SUCCESS', + help: 'Log level used by the Cloud Code Functions on success. Default is `info`.', + default: 'info', }, triggerAfter: { - env: "PARSE_SERVER_LOG_LEVELS_TRIGGER_AFTER", - help: "Log level used by the Cloud Code Triggers `afterSave`, `afterDelete`, `afterFind`, `afterLogout`. Default is `info`.", - default: "info", + env: 'PARSE_SERVER_LOG_LEVELS_TRIGGER_AFTER', + help: 'Log level used by the Cloud Code Triggers `afterSave`, `afterDelete`, `afterFind`, `afterLogout`. Default is `info`.', + default: 'info', }, triggerBeforeError: { - env: "PARSE_SERVER_LOG_LEVELS_TRIGGER_BEFORE_ERROR", - help: "Log level used by the Cloud Code Triggers `beforeSave`, `beforeDelete`, `beforeFind`, `beforeLogin` on error. Default is `error`.", - default: "error", + env: 'PARSE_SERVER_LOG_LEVELS_TRIGGER_BEFORE_ERROR', + help: 'Log level used by the Cloud Code Triggers `beforeSave`, `beforeDelete`, `beforeFind`, `beforeLogin` on error. Default is `error`.', + default: 'error', }, triggerBeforeSuccess: { - env: "PARSE_SERVER_LOG_LEVELS_TRIGGER_BEFORE_SUCCESS", - help: "Log level used by the Cloud Code Triggers `beforeSave`, `beforeDelete`, `beforeFind`, `beforeLogin` on success. Default is `info`.", - default: "info", + env: 'PARSE_SERVER_LOG_LEVELS_TRIGGER_BEFORE_SUCCESS', + help: 'Log level used by the Cloud Code Triggers `beforeSave`, `beforeDelete`, `beforeFind`, `beforeLogin` on success. Default is `info`.', + default: 'info', }, }; diff --git a/src/Options/index.js b/src/Options/index.js index 959bd7b45f..b3c04462ca 100644 --- a/src/Options/index.js +++ b/src/Options/index.js @@ -1,13 +1,13 @@ // @flow -import { AnalyticsAdapter } from "../Adapters/Analytics/AnalyticsAdapter"; -import { CacheAdapter } from "../Adapters/Cache/CacheAdapter"; -import { MailAdapter } from "../Adapters/Email/MailAdapter"; -import { FilesAdapter } from "../Adapters/Files/FilesAdapter"; -import { LoggerAdapter } from "../Adapters/Logger/LoggerAdapter"; -import { PubSubAdapter } from "../Adapters/PubSub/PubSubAdapter"; -import { StorageAdapter } from "../Adapters/Storage/StorageAdapter"; -import { WSSAdapter } from "../Adapters/WebSocketServer/WSSAdapter"; -import { CheckGroup } from "../Security/CheckGroup"; +import { AnalyticsAdapter } from '../Adapters/Analytics/AnalyticsAdapter'; +import { CacheAdapter } from '../Adapters/Cache/CacheAdapter'; +import { MailAdapter } from '../Adapters/Email/MailAdapter'; +import { FilesAdapter } from '../Adapters/Files/FilesAdapter'; +import { LoggerAdapter } from '../Adapters/Logger/LoggerAdapter'; +import { PubSubAdapter } from '../Adapters/PubSub/PubSubAdapter'; +import { StorageAdapter } from '../Adapters/Storage/StorageAdapter'; +import { WSSAdapter } from '../Adapters/WebSocketServer/WSSAdapter'; +import { CheckGroup } from '../Security/CheckGroup'; export interface SchemaOptions { /* Rest representation on Parse.Schema https://docs.parseplatform.org/rest/guide/#adding-a-schema diff --git a/src/Options/parsers.js b/src/Options/parsers.js index 8e2a2ebf5c..384b5494ef 100644 --- a/src/Options/parsers.js +++ b/src/Options/parsers.js @@ -10,13 +10,13 @@ function numberParser(key) { function numberOrBoolParser(key) { return function (opt) { - if (typeof opt === "boolean") { + if (typeof opt === 'boolean') { return opt; } - if (opt === "true") { + if (opt === 'true') { return true; } - if (opt === "false") { + if (opt === 'false') { return false; } return numberParser(key)(opt); @@ -25,7 +25,7 @@ function numberOrBoolParser(key) { function numberOrStringParser(key) { return function (opt) { - if (typeof opt === "string") { + if (typeof opt === 'string') { return opt; } return numberParser(key)(opt); @@ -33,7 +33,7 @@ function numberOrStringParser(key) { } function objectParser(opt) { - if (typeof opt == "object") { + if (typeof opt == 'object') { return opt; } return JSON.parse(opt); @@ -42,15 +42,15 @@ function objectParser(opt) { function arrayParser(opt) { if (Array.isArray(opt)) { return opt; - } else if (typeof opt === "string") { - return opt.split(","); + } else if (typeof opt === 'string') { + return opt.split(','); } else { throw new Error(`${opt} should be a comma separated string or an array`); } } function moduleOrObjectParser(opt) { - if (typeof opt == "object") { + if (typeof opt == 'object') { return opt; } try { @@ -62,14 +62,14 @@ function moduleOrObjectParser(opt) { } function booleanParser(opt) { - if (opt == true || opt == "true" || opt == "1") { + if (opt == true || opt == 'true' || opt == '1') { return true; } return false; } function nullParser(opt) { - if (opt == "null") { + if (opt == 'null') { return null; } return opt; diff --git a/src/ParseServerRESTController.js b/src/ParseServerRESTController.js index 9ccc9564fa..03bf7fc3fa 100644 --- a/src/ParseServerRESTController.js +++ b/src/ParseServerRESTController.js @@ -1,17 +1,17 @@ -const Config = require("./Config"); -const Auth = require("./Auth"); -import RESTController from "parse/lib/node/RESTController"; -const Parse = require("parse/node"); +const Config = require('./Config'); +const Auth = require('./Auth'); +import RESTController from 'parse/lib/node/RESTController'; +const Parse = require('parse/node'); function getSessionToken(options) { - if (options && typeof options.sessionToken === "string") { + if (options && typeof options.sessionToken === 'string') { return Promise.resolve(options.sessionToken); } return Promise.resolve(null); } function getAuth(options = {}, config) { - const installationId = options.installationId || "cloud"; + const installationId = options.installationId || 'cloud'; if (options.useMasterKey) { return Promise.resolve( new Auth.Auth({ config, isMaster: true, installationId }) @@ -44,11 +44,11 @@ function ParseServerRESTController(applicationId, router) { path = path.slice(serverURL.pathname.length, path.length); } - if (path[0] !== "/") { - path = "/" + path; + if (path[0] !== '/') { + path = '/' + path; } - if (path === "/batch") { + if (path === '/batch') { const batch = transactionRetries => { let initialPromise = Promise.resolve(); if (data.transaction === true) { @@ -89,7 +89,7 @@ function ParseServerRESTController(applicationId, router) { if (data.transaction === true) { if ( result.find( - resultItem => typeof resultItem.error === "object" + resultItem => typeof resultItem.error === 'object' ) ) { return config.database @@ -113,7 +113,7 @@ function ParseServerRESTController(applicationId, router) { error && error.find( errorItem => - typeof errorItem.error === "object" && + typeof errorItem.error === 'object' && errorItem.error.code === 251 ) && transactionRetries > 0 @@ -128,7 +128,7 @@ function ParseServerRESTController(applicationId, router) { } let query; - if (method === "GET") { + if (method === 'GET') { query = data; } diff --git a/src/PromiseRouter.js b/src/PromiseRouter.js index 39bdd6d49b..d0754ddf67 100644 --- a/src/PromiseRouter.js +++ b/src/PromiseRouter.js @@ -5,18 +5,18 @@ // themselves use our routing information, without disturbing express // components that external developers may be modifying. -import Parse from "parse/node"; -import express from "express"; -import log from "./logger"; -import { inspect } from "util"; -const Layer = require("router/lib/layer"); +import Parse from 'parse/node'; +import express from 'express'; +import log from './logger'; +import { inspect } from 'util'; +const Layer = require('router/lib/layer'); function validateParameter(key, value) { - if (key == "className") { + if (key == 'className') { if (value.match(/_?[A-Za-z][A-Za-z_0-9]*/)) { return value; } - } else if (key == "objectId") { + } else if (key == 'objectId') { if (value.match(/[A-Za-z0-9]+/)) { return value; } @@ -54,13 +54,13 @@ export default class PromiseRouter { route(method, path, ...handlers) { switch (method) { - case "POST": - case "GET": - case "PUT": - case "DELETE": + case 'POST': + case 'GET': + case 'PUT': + case 'DELETE': break; default: - throw "cannot route method: " + method; + throw 'cannot route method: ' + method; } let handler = handlers[0]; @@ -123,7 +123,7 @@ export default class PromiseRouter { if (!match) { throw new Parse.Error( Parse.Error.INVALID_JSON, - "cannot route " + method + " " + path + 'cannot route ' + method + ' ' + path ); } request.params = match.params; @@ -157,7 +157,7 @@ function makeExpressHandler(appId, promiseHandler) { log.error( 'the handler did not include a "response" or a "location" field' ); - throw "control should not get here"; + throw 'control should not get here'; } log.logResponse({ method, url, result }); @@ -177,11 +177,11 @@ function makeExpressHandler(appId, promiseHandler) { } if (result.location) { - res.set("Location", result.location); + res.set('Location', result.location); // Override the default expressjs response // as it double encodes %encoded chars in URL if (!result.response) { - res.send("Found. Redirecting to " + result.location); + res.send('Found. Redirecting to ' + result.location); return; } } @@ -205,9 +205,9 @@ function makeExpressHandler(appId, promiseHandler) { function maskSensitiveUrl(req) { let maskUrl = req.originalUrl.toString(); const shouldMaskUrl = - req.method === "GET" && - req.originalUrl.includes("/login") && - !req.originalUrl.includes("classes"); + req.method === 'GET' && + req.originalUrl.includes('/login') && + !req.originalUrl.includes('classes'); if (shouldMaskUrl) { maskUrl = log.maskSensitiveUrl(maskUrl); } diff --git a/src/Push/PushWorker.js b/src/Push/PushWorker.js index 799670609a..d16542aeac 100644 --- a/src/Push/PushWorker.js +++ b/src/Push/PushWorker.js @@ -1,20 +1,20 @@ // @flow // @flow-disable-next -import deepcopy from "deepcopy"; -import AdaptableController from "../Controllers/AdaptableController"; -import { master } from "../Auth"; -import Config from "../Config"; -import { PushAdapter } from "../Adapters/Push/PushAdapter"; -import rest from "../rest"; -import { pushStatusHandler } from "../StatusHandler"; -import * as utils from "./utils"; -import { ParseMessageQueue } from "../ParseMessageQueue"; -import { PushQueue } from "./PushQueue"; -import logger from "../logger"; +import deepcopy from 'deepcopy'; +import AdaptableController from '../Controllers/AdaptableController'; +import { master } from '../Auth'; +import Config from '../Config'; +import { PushAdapter } from '../Adapters/Push/PushAdapter'; +import rest from '../rest'; +import { pushStatusHandler } from '../StatusHandler'; +import * as utils from './utils'; +import { ParseMessageQueue } from '../ParseMessageQueue'; +import { PushQueue } from './PushQueue'; +import logger from '../logger'; function groupByBadge(installations) { return installations.reduce((map, installation) => { - const badge = installation.badge + ""; + const badge = installation.badge + ''; map[badge] = map[badge] || []; map[badge].push(installation); return map; @@ -35,7 +35,7 @@ export class PushWorker { if (this.subscriber) { const subscriber = this.subscriber; subscriber.subscribe(this.channel); - subscriber.on("message", (channel, messageStr) => { + subscriber.on('message', (channel, messageStr) => { const workItem = JSON.parse(messageStr); this.run(workItem); }); @@ -49,7 +49,7 @@ export class PushWorker { delete query.where; pushStatus = pushStatusHandler(config, pushStatus.objectId); return rest - .find(config, auth, "_Installation", where, query) + .find(config, auth, '_Installation', where, query) .then(({ results }) => { if (results.length == 0) { return; diff --git a/src/Push/utils.js b/src/Push/utils.js index e8bfb00a5d..ce7023917e 100644 --- a/src/Push/utils.js +++ b/src/Push/utils.js @@ -1,5 +1,5 @@ -import Parse from "parse/node"; -import deepcopy from "deepcopy"; +import Parse from 'parse/node'; +import deepcopy from 'deepcopy'; export function isPushIncrementing(body) { if (!body.data || !body.data.badge) { @@ -7,19 +7,19 @@ export function isPushIncrementing(body) { } const badge = body.data.badge; - if (typeof badge == "string" && badge.toLowerCase() == "increment") { + if (typeof badge == 'string' && badge.toLowerCase() == 'increment') { return true; } return ( - typeof badge == "object" && - typeof badge.__op == "string" && - badge.__op.toLowerCase() == "increment" && + typeof badge == 'object' && + typeof badge.__op == 'string' && + badge.__op.toLowerCase() == 'increment' && Number(badge.amount) ); } -const localizableKeys = ["alert", "title"]; +const localizableKeys = ['alert', 'title']; export function getLocalesFromPush(body) { const data = body.data; @@ -114,17 +114,17 @@ export function groupByLocaleIdentifier(installations, locales = []) { export function validatePushType(where = {}, validPushTypes = []) { var deviceTypeField = where.deviceType || {}; var deviceTypes = []; - if (typeof deviceTypeField === "string") { + if (typeof deviceTypeField === 'string') { deviceTypes.push(deviceTypeField); - } else if (Array.isArray(deviceTypeField["$in"])) { - deviceTypes.concat(deviceTypeField["$in"]); + } else if (Array.isArray(deviceTypeField['$in'])) { + deviceTypes.concat(deviceTypeField['$in']); } for (var i = 0; i < deviceTypes.length; i++) { var deviceType = deviceTypes[i]; if (validPushTypes.indexOf(deviceType) < 0) { throw new Parse.Error( Parse.Error.PUSH_MISCONFIGURED, - deviceType + " is not supported push type." + deviceType + ' is not supported push type.' ); } } @@ -132,8 +132,8 @@ export function validatePushType(where = {}, validPushTypes = []) { export function applyDeviceTokenExists(where) { where = deepcopy(where); - if (!Object.prototype.hasOwnProperty.call(where, "deviceToken")) { - where["deviceToken"] = { $exists: true }; + if (!Object.prototype.hasOwnProperty.call(where, 'deviceToken')) { + where['deviceToken'] = { $exists: true }; } return where; } diff --git a/src/RestQuery.js b/src/RestQuery.js index 5aec10df99..f44bf0b572 100644 --- a/src/RestQuery.js +++ b/src/RestQuery.js @@ -1,12 +1,12 @@ // An object that encapsulates everything we need to run a 'find' // operation, encoded in the REST API format. -var SchemaController = require("./Controllers/SchemaController"); -var Parse = require("parse/node").Parse; -const triggers = require("./triggers"); -const { continueWhile } = require("parse/lib/node/promiseUtils"); -const AlwaysSelectedKeys = ["objectId", "createdAt", "updatedAt", "ACL"]; -const { enforceRoleSecurity } = require("./SharedRest"); +var SchemaController = require('./Controllers/SchemaController'); +var Parse = require('parse/node').Parse; +const triggers = require('./triggers'); +const { continueWhile } = require('parse/lib/node/promiseUtils'); +const AlwaysSelectedKeys = ['objectId', 'createdAt', 'updatedAt', 'ACL']; +const { enforceRoleSecurity } = require('./SharedRest'); // restOptions can include: // skip @@ -48,7 +48,7 @@ async function RestQuery({ context, }) { if (![RestQuery.Method.find, RestQuery.Method.get].includes(method)) { - throw new Parse.Error(Parse.Error.INVALID_QUERY, "bad query type"); + throw new Parse.Error(Parse.Error.INVALID_QUERY, 'bad query type'); } enforceRoleSecurity(method, className, auth); const result = runBeforeFind @@ -77,8 +77,8 @@ async function RestQuery({ } RestQuery.Method = Object.freeze({ - get: "get", - find: "find", + get: 'get', + find: 'find', }); /** @@ -114,11 +114,11 @@ function _UnsafeRestQuery( this.findOptions = {}; this.context = context || {}; if (!this.auth.isMaster) { - if (this.className == "_Session") { + if (this.className == '_Session') { if (!this.auth.user) { throw new Parse.Error( Parse.Error.INVALID_SESSION_TOKEN, - "Invalid session token" + 'Invalid session token' ); } this.restWhere = { @@ -126,8 +126,8 @@ function _UnsafeRestQuery( this.restWhere, { user: { - __type: "Pointer", - className: "_User", + __type: 'Pointer', + className: '_User', objectId: this.auth.user.id, }, }, @@ -146,33 +146,33 @@ function _UnsafeRestQuery( // For example, passing an arg of include=foo.bar,foo.baz could lead to // this.include = [['foo'], ['foo', 'baz'], ['foo', 'bar']] this.include = []; - let keysForInclude = ""; + let keysForInclude = ''; // If we have keys, we probably want to force some includes (n-1 level) // See issue: https://github.com/parse-community/parse-server/issues/3185 - if (Object.prototype.hasOwnProperty.call(restOptions, "keys")) { + if (Object.prototype.hasOwnProperty.call(restOptions, 'keys')) { keysForInclude = restOptions.keys; } // If we have keys, we probably want to force some includes (n-1 level) // in order to exclude specific keys. - if (Object.prototype.hasOwnProperty.call(restOptions, "excludeKeys")) { - keysForInclude += "," + restOptions.excludeKeys; + if (Object.prototype.hasOwnProperty.call(restOptions, 'excludeKeys')) { + keysForInclude += ',' + restOptions.excludeKeys; } if (keysForInclude.length > 0) { keysForInclude = keysForInclude - .split(",") + .split(',') .filter(key => { // At least 2 components - return key.split(".").length > 1; + return key.split('.').length > 1; }) .map(key => { // Slice the last component (a.b.c -> a.b) // Otherwise we'll include one level too much. - return key.slice(0, key.lastIndexOf(".")); + return key.slice(0, key.lastIndexOf('.')); }) - .join(","); + .join(','); // Concat the possibly present include string with the one from the keys // Dedup / sorting is handle in 'include' case. @@ -180,51 +180,51 @@ function _UnsafeRestQuery( if (!restOptions.include || restOptions.include.length == 0) { restOptions.include = keysForInclude; } else { - restOptions.include += "," + keysForInclude; + restOptions.include += ',' + keysForInclude; } } } for (var option in restOptions) { switch (option) { - case "keys": { + case 'keys': { const keys = restOptions.keys - .split(",") + .split(',') .filter(key => key.length > 0) .concat(AlwaysSelectedKeys); this.keys = Array.from(new Set(keys)); break; } - case "excludeKeys": { + case 'excludeKeys': { const exclude = restOptions.excludeKeys - .split(",") + .split(',') .filter(k => AlwaysSelectedKeys.indexOf(k) < 0); this.excludeKeys = Array.from(new Set(exclude)); break; } - case "count": + case 'count': this.doCount = true; break; - case "includeAll": + case 'includeAll': this.includeAll = true; break; - case "explain": - case "hint": - case "distinct": - case "pipeline": - case "skip": - case "limit": - case "readPreference": - case "comment": + case 'explain': + case 'hint': + case 'distinct': + case 'pipeline': + case 'skip': + case 'limit': + case 'readPreference': + case 'comment': this.findOptions[option] = restOptions[option]; break; - case "order": - var fields = restOptions.order.split(","); + case 'order': + var fields = restOptions.order.split(','); this.findOptions.sort = fields.reduce((sortMap, field) => { field = field.trim(); - if (field === "$score" || field === "-$score") { - sortMap.score = { $meta: "textScore" }; - } else if (field[0] == "-") { + if (field === '$score' || field === '-$score') { + sortMap.score = { $meta: 'textScore' }; + } else if (field[0] == '-') { sortMap[field.slice(1)] = -1; } else { sortMap[field] = 1; @@ -232,9 +232,9 @@ function _UnsafeRestQuery( return sortMap; }, {}); break; - case "include": { - const paths = restOptions.include.split(","); - if (paths.includes("*")) { + case 'include': { + const paths = restOptions.include.split(','); + if (paths.includes('*')) { this.includeAll = true; break; } @@ -243,32 +243,32 @@ function _UnsafeRestQuery( // Split each paths on . (a.b.c -> [a,b,c]) // reduce to create all paths // ([a,b,c] -> {a: true, 'a.b': true, 'a.b.c': true}) - return path.split(".").reduce((memo, path, index, parts) => { - memo[parts.slice(0, index + 1).join(".")] = true; + return path.split('.').reduce((memo, path, index, parts) => { + memo[parts.slice(0, index + 1).join('.')] = true; return memo; }, memo); }, {}); this.include = Object.keys(pathSet) .map(s => { - return s.split("."); + return s.split('.'); }) .sort((a, b) => { return a.length - b.length; // Sort by number of components }); break; } - case "redirectClassNameForKey": + case 'redirectClassNameForKey': this.redirectKey = restOptions.redirectClassNameForKey; this.redirectClassName = null; break; - case "includeReadPreference": - case "subqueryReadPreference": + case 'includeReadPreference': + case 'subqueryReadPreference': break; default: throw new Parse.Error( Parse.Error.INVALID_JSON, - "bad option: " + option + 'bad option: ' + option ); } } @@ -317,7 +317,7 @@ _UnsafeRestQuery.prototype.each = function (callback) { const { config, auth, className, restWhere, restOptions, clientSDK } = this; // if the limit is set, use it restOptions.limit = restOptions.limit || 100; - restOptions.order = "objectId"; + restOptions.order = 'objectId'; let finished = false; return continueWhile( @@ -383,7 +383,7 @@ _UnsafeRestQuery.prototype.getUserAndRoleACL = function () { return Promise.resolve(); } - this.findOptions.acl = ["*"]; + this.findOptions.acl = ['*']; if (this.auth.user) { return this.auth.getUserRoles().then(roles => { @@ -427,8 +427,8 @@ _UnsafeRestQuery.prototype.validateClientClassCreation = function () { if (hasClass !== true) { throw new Parse.Error( Parse.Error.OPERATION_FORBIDDEN, - "This user is not allowed to access " + - "non-existent class: " + + 'This user is not allowed to access ' + + 'non-existent class: ' + this.className ); } @@ -442,16 +442,16 @@ function transformInQuery(inQueryObject, className, results) { var values = []; for (var result of results) { values.push({ - __type: "Pointer", + __type: 'Pointer', className: className, objectId: result.objectId, }); } - delete inQueryObject["$inQuery"]; - if (Array.isArray(inQueryObject["$in"])) { - inQueryObject["$in"] = inQueryObject["$in"].concat(values); + delete inQueryObject['$inQuery']; + if (Array.isArray(inQueryObject['$in'])) { + inQueryObject['$in'] = inQueryObject['$in'].concat(values); } else { - inQueryObject["$in"] = values; + inQueryObject['$in'] = values; } } @@ -460,17 +460,17 @@ function transformInQuery(inQueryObject, className, results) { // The $inQuery clause turns into an $in with values that are just // pointers to the objects returned in the subquery. _UnsafeRestQuery.prototype.replaceInQuery = async function () { - var inQueryObject = findObjectWithKey(this.restWhere, "$inQuery"); + var inQueryObject = findObjectWithKey(this.restWhere, '$inQuery'); if (!inQueryObject) { return; } // The inQuery value must have precisely two keys - where and className - var inQueryValue = inQueryObject["$inQuery"]; + var inQueryValue = inQueryObject['$inQuery']; if (!inQueryValue.where || !inQueryValue.className) { throw new Parse.Error( Parse.Error.INVALID_QUERY, - "improper usage of $inQuery" + 'improper usage of $inQuery' ); } @@ -506,16 +506,16 @@ function transformNotInQuery(notInQueryObject, className, results) { var values = []; for (var result of results) { values.push({ - __type: "Pointer", + __type: 'Pointer', className: className, objectId: result.objectId, }); } - delete notInQueryObject["$notInQuery"]; - if (Array.isArray(notInQueryObject["$nin"])) { - notInQueryObject["$nin"] = notInQueryObject["$nin"].concat(values); + delete notInQueryObject['$notInQuery']; + if (Array.isArray(notInQueryObject['$nin'])) { + notInQueryObject['$nin'] = notInQueryObject['$nin'].concat(values); } else { - notInQueryObject["$nin"] = values; + notInQueryObject['$nin'] = values; } } @@ -524,17 +524,17 @@ function transformNotInQuery(notInQueryObject, className, results) { // The $notInQuery clause turns into a $nin with values that are just // pointers to the objects returned in the subquery. _UnsafeRestQuery.prototype.replaceNotInQuery = async function () { - var notInQueryObject = findObjectWithKey(this.restWhere, "$notInQuery"); + var notInQueryObject = findObjectWithKey(this.restWhere, '$notInQuery'); if (!notInQueryObject) { return; } // The notInQuery value must have precisely two keys - where and className - var notInQueryValue = notInQueryObject["$notInQuery"]; + var notInQueryValue = notInQueryObject['$notInQuery']; if (!notInQueryValue.where || !notInQueryValue.className) { throw new Parse.Error( Parse.Error.INVALID_QUERY, - "improper usage of $notInQuery" + 'improper usage of $notInQuery' ); } @@ -578,13 +578,13 @@ const getDeepestObjectFromKey = (json, key, idx, src) => { const transformSelect = (selectObject, key, objects) => { var values = []; for (var result of objects) { - values.push(key.split(".").reduce(getDeepestObjectFromKey, result)); + values.push(key.split('.').reduce(getDeepestObjectFromKey, result)); } - delete selectObject["$select"]; - if (Array.isArray(selectObject["$in"])) { - selectObject["$in"] = selectObject["$in"].concat(values); + delete selectObject['$select']; + if (Array.isArray(selectObject['$in'])) { + selectObject['$in'] = selectObject['$in'].concat(values); } else { - selectObject["$in"] = values; + selectObject['$in'] = values; } }; @@ -594,24 +594,24 @@ const transformSelect = (selectObject, key, objects) => { // the subquery. // Returns a possible-promise. _UnsafeRestQuery.prototype.replaceSelect = async function () { - var selectObject = findObjectWithKey(this.restWhere, "$select"); + var selectObject = findObjectWithKey(this.restWhere, '$select'); if (!selectObject) { return; } // The select value must have precisely two keys - query and key - var selectValue = selectObject["$select"]; + var selectValue = selectObject['$select']; // iOS SDK don't send where if not set, let it pass if ( !selectValue.query || !selectValue.key || - typeof selectValue.query !== "object" || + typeof selectValue.query !== 'object' || !selectValue.query.className || Object.keys(selectValue).length !== 2 ) { throw new Parse.Error( Parse.Error.INVALID_QUERY, - "improper usage of $select" + 'improper usage of $select' ); } @@ -647,13 +647,13 @@ _UnsafeRestQuery.prototype.replaceSelect = async function () { const transformDontSelect = (dontSelectObject, key, objects) => { var values = []; for (var result of objects) { - values.push(key.split(".").reduce(getDeepestObjectFromKey, result)); + values.push(key.split('.').reduce(getDeepestObjectFromKey, result)); } - delete dontSelectObject["$dontSelect"]; - if (Array.isArray(dontSelectObject["$nin"])) { - dontSelectObject["$nin"] = dontSelectObject["$nin"].concat(values); + delete dontSelectObject['$dontSelect']; + if (Array.isArray(dontSelectObject['$nin'])) { + dontSelectObject['$nin'] = dontSelectObject['$nin'].concat(values); } else { - dontSelectObject["$nin"] = values; + dontSelectObject['$nin'] = values; } }; @@ -663,23 +663,23 @@ const transformDontSelect = (dontSelectObject, key, objects) => { // the subquery. // Returns a possible-promise. _UnsafeRestQuery.prototype.replaceDontSelect = async function () { - var dontSelectObject = findObjectWithKey(this.restWhere, "$dontSelect"); + var dontSelectObject = findObjectWithKey(this.restWhere, '$dontSelect'); if (!dontSelectObject) { return; } // The dontSelect value must have precisely two keys - query and key - var dontSelectValue = dontSelectObject["$dontSelect"]; + var dontSelectValue = dontSelectObject['$dontSelect']; if ( !dontSelectValue.query || !dontSelectValue.key || - typeof dontSelectValue.query !== "object" || + typeof dontSelectValue.query !== 'object' || !dontSelectValue.query.className || Object.keys(dontSelectValue).length !== 2 ) { throw new Parse.Error( Parse.Error.INVALID_QUERY, - "improper usage of $dontSelect" + 'improper usage of $dontSelect' ); } const additionalOptions = { @@ -731,14 +731,14 @@ _UnsafeRestQuery.prototype.cleanResultAuthData = function (result) { }; const replaceEqualityConstraint = constraint => { - if (typeof constraint !== "object") { + if (typeof constraint !== 'object') { return constraint; } const equalToObject = {}; let hasDirectConstraint = false; let hasOperatorConstraint = false; for (const key in constraint) { - if (key.indexOf("$") !== 0) { + if (key.indexOf('$') !== 0) { hasDirectConstraint = true; equalToObject[key] = constraint[key]; } else { @@ -746,7 +746,7 @@ const replaceEqualityConstraint = constraint => { } } if (hasDirectConstraint && hasOperatorConstraint) { - constraint["$eq"] = equalToObject; + constraint['$eq'] = equalToObject; Object.keys(equalToObject).forEach(key => { delete constraint[key]; }); @@ -755,7 +755,7 @@ const replaceEqualityConstraint = constraint => { }; _UnsafeRestQuery.prototype.replaceEquality = function () { - if (typeof this.restWhere !== "object") { + if (typeof this.restWhere !== 'object') { return; } for (const key in this.restWhere) { @@ -773,7 +773,7 @@ _UnsafeRestQuery.prototype.runFind = async function (options = {}) { const findOptions = Object.assign({}, this.findOptions); if (this.keys) { findOptions.keys = this.keys.map(key => { - return key.split(".")[0]; + return key.split('.')[0]; }); } if (options.op) { @@ -785,7 +785,7 @@ _UnsafeRestQuery.prototype.runFind = async function (options = {}) { findOptions, this.auth ); - if (this.className === "_User" && !findOptions.explain) { + if (this.className === '_User' && !findOptions.explain) { for (var result of results) { this.cleanResultAuthData(result); } @@ -855,8 +855,8 @@ _UnsafeRestQuery.prototype.handleIncludeAll = function () { for (const field in schema.fields) { if ( (schema.fields[field].type && - schema.fields[field].type === "Pointer") || - (schema.fields[field].type && schema.fields[field].type === "Array") + schema.fields[field].type === 'Pointer') || + (schema.fields[field].type && schema.fields[field].type === 'Array') ) { includeFields.push([field]); keyFields.push(field); @@ -971,7 +971,7 @@ _UnsafeRestQuery.prototype.runAfterFindTrigger = function () { }; _UnsafeRestQuery.prototype.handleAuthAdapters = async function () { - if (this.className !== "_User" || this.findOptions.explain) { + if (this.className !== '_User' || this.findOptions.explain) { return; } await Promise.all( @@ -1006,9 +1006,9 @@ function includePath(config, auth, response, path, context, restOptions = {}) { } const includeRestOptions = {}; if (restOptions.keys) { - const keys = new Set(restOptions.keys.split(",")); + const keys = new Set(restOptions.keys.split(',')); const keySet = Array.from(keys).reduce((set, key) => { - const keyPath = key.split("."); + const keyPath = key.split('.'); let i = 0; for (i; i < path.length; i++) { if (path[i] != keyPath[i]) { @@ -1021,14 +1021,14 @@ function includePath(config, auth, response, path, context, restOptions = {}) { return set; }, new Set()); if (keySet.size > 0) { - includeRestOptions.keys = Array.from(keySet).join(","); + includeRestOptions.keys = Array.from(keySet).join(','); } } if (restOptions.excludeKeys) { - const excludeKeys = new Set(restOptions.excludeKeys.split(",")); + const excludeKeys = new Set(restOptions.excludeKeys.split(',')); const excludeKeySet = Array.from(excludeKeys).reduce((set, key) => { - const keyPath = key.split("."); + const keyPath = key.split('.'); let i = 0; for (i; i < path.length; i++) { if (path[i] != keyPath[i]) { @@ -1041,7 +1041,7 @@ function includePath(config, auth, response, path, context, restOptions = {}) { return set; }, new Set()); if (excludeKeySet.size > 0) { - includeRestOptions.excludeKeys = Array.from(excludeKeySet).join(","); + includeRestOptions.excludeKeys = Array.from(excludeKeySet).join(','); } } @@ -1071,7 +1071,7 @@ function includePath(config, auth, response, path, context, restOptions = {}) { restOptions: includeRestOptions, context: context, }); - return query.execute({ op: "get" }).then(results => { + return query.execute({ op: 'get' }).then(results => { results.className = className; return Promise.resolve(results); }); @@ -1081,10 +1081,10 @@ function includePath(config, auth, response, path, context, restOptions = {}) { return Promise.all(queryPromises).then(responses => { var replace = responses.reduce((replace, includeResponse) => { for (var obj of includeResponse.results) { - obj.__type = "Object"; + obj.__type = 'Object'; obj.className = includeResponse.className; - if (obj.className == "_User" && !auth.isMaster) { + if (obj.className == '_User' && !auth.isMaster) { delete obj.sessionToken; delete obj.authData; } @@ -1113,12 +1113,12 @@ function findPointers(object, path) { return object.map(x => findPointers(x, path)).flat(); } - if (typeof object !== "object" || !object) { + if (typeof object !== 'object' || !object) { return []; } if (path.length == 0) { - if (object === null || object.__type == "Pointer") { + if (object === null || object.__type == 'Pointer') { return [object]; } return []; @@ -1141,15 +1141,15 @@ function replacePointers(object, path, replace) { if (object instanceof Array) { return object .map(obj => replacePointers(obj, path, replace)) - .filter(obj => typeof obj !== "undefined"); + .filter(obj => typeof obj !== 'undefined'); } - if (typeof object !== "object" || !object) { + if (typeof object !== 'object' || !object) { return object; } if (path.length === 0) { - if (object && object.__type === "Pointer") { + if (object && object.__type === 'Pointer') { return replace[object.objectId]; } return object; @@ -1174,7 +1174,7 @@ function replacePointers(object, path, replace) { // Finds a subobject that has the given key, if there is one. // Returns undefined otherwise. function findObjectWithKey(root, key) { - if (typeof root !== "object") { + if (typeof root !== 'object') { return; } if (root instanceof Array) { diff --git a/src/RestWrite.js b/src/RestWrite.js index ad76db33d0..dcffaa6b75 100644 --- a/src/RestWrite.js +++ b/src/RestWrite.js @@ -2,21 +2,21 @@ // that writes to the database. // This could be either a "create" or an "update". -var SchemaController = require("./Controllers/SchemaController"); -var deepcopy = require("deepcopy"); - -const Auth = require("./Auth"); -const Utils = require("./Utils"); -var cryptoUtils = require("./cryptoUtils"); -var passwordCrypto = require("./password"); -var Parse = require("parse/node"); -var triggers = require("./triggers"); -var ClientSDK = require("./ClientSDK"); -const util = require("util"); -import RestQuery from "./RestQuery"; -import _ from "lodash"; -import logger from "./logger"; -import { requiredColumns } from "./Controllers/SchemaController"; +var SchemaController = require('./Controllers/SchemaController'); +var deepcopy = require('deepcopy'); + +const Auth = require('./Auth'); +const Utils = require('./Utils'); +var cryptoUtils = require('./cryptoUtils'); +var passwordCrypto = require('./password'); +var Parse = require('parse/node'); +var triggers = require('./triggers'); +var ClientSDK = require('./ClientSDK'); +const util = require('util'); +import RestQuery from './RestQuery'; +import _ from 'lodash'; +import logger from './logger'; +import { requiredColumns } from './Controllers/SchemaController'; // query and data are both provided in REST API format. So data // types are encoded by plain old objects. @@ -41,7 +41,7 @@ function RestWrite( if (auth.isReadOnly) { throw new Parse.Error( Parse.Error.OPERATION_FORBIDDEN, - "Cannot perform a write operation when using readOnlyMasterKey" + 'Cannot perform a write operation when using readOnlyMasterKey' ); } this.config = config; @@ -59,25 +59,25 @@ function RestWrite( if (!query) { if (this.config.allowCustomObjectId) { if ( - Object.prototype.hasOwnProperty.call(data, "objectId") && + Object.prototype.hasOwnProperty.call(data, 'objectId') && !data.objectId ) { throw new Parse.Error( Parse.Error.MISSING_OBJECT_ID, - "objectId must not be empty, null or undefined" + 'objectId must not be empty, null or undefined' ); } } else { if (data.objectId) { throw new Parse.Error( Parse.Error.INVALID_KEY_NAME, - "objectId is an invalid field name." + 'objectId is an invalid field name.' ); } if (data.id) { throw new Parse.Error( Parse.Error.INVALID_KEY_NAME, - "id is an invalid field name." + 'id is an invalid field name.' ); } } @@ -186,7 +186,7 @@ RestWrite.prototype.execute = function () { ) { throw new Parse.Error( Parse.Error.EMAIL_NOT_FOUND, - "User email is not verified." + 'User email is not verified.' ); } return this.response; @@ -199,7 +199,7 @@ RestWrite.prototype.getUserAndRoleACL = function () { return Promise.resolve(); } - this.runOptions.acl = ["*"]; + this.runOptions.acl = ['*']; if (this.auth.user) { return this.auth.getUserRoles().then(roles => { @@ -228,8 +228,8 @@ RestWrite.prototype.validateClientClassCreation = function () { if (hasClass !== true) { throw new Parse.Error( Parse.Error.OPERATION_FORBIDDEN, - "This user is not allowed to access " + - "non-existent class: " + + 'This user is not allowed to access ' + + 'non-existent class: ' + this.className ); } @@ -305,7 +305,7 @@ RestWrite.prototype.runBeforeSaveTrigger = function () { if (!result || result.length <= 0) { throw new Parse.Error( Parse.Error.OBJECT_NOT_FOUND, - "Object not found." + 'Object not found.' ); } }); @@ -387,9 +387,9 @@ RestWrite.prototype.setRequiredFieldsIfNeeded = function () { if ( this.data[fieldName] === undefined || this.data[fieldName] === null || - this.data[fieldName] === "" || - (typeof this.data[fieldName] === "object" && - this.data[fieldName].__op === "Delete") + this.data[fieldName] === '' || + (typeof this.data[fieldName] === 'object' && + this.data[fieldName].__op === 'Delete') ) { if ( setDefault && @@ -397,8 +397,8 @@ RestWrite.prototype.setRequiredFieldsIfNeeded = function () { schema.fields[fieldName].defaultValue !== null && schema.fields[fieldName].defaultValue !== undefined && (this.data[fieldName] === undefined || - (typeof this.data[fieldName] === "object" && - this.data[fieldName].__op === "Delete")) + (typeof this.data[fieldName] === 'object' && + this.data[fieldName].__op === 'Delete')) ) { this.data[fieldName] = schema.fields[fieldName].defaultValue; this.storage.fieldsChangedByTrigger = @@ -423,7 +423,7 @@ RestWrite.prototype.setRequiredFieldsIfNeeded = function () { schema?.classLevelPermissions?.ACL && !this.data.ACL && JSON.stringify(schema.classLevelPermissions.ACL) !== - JSON.stringify({ "*": { read: true, write: true } }) + JSON.stringify({ '*': { read: true, write: true } }) ) { const acl = deepcopy(schema.classLevelPermissions.ACL); if (acl.currentUser) { @@ -435,7 +435,7 @@ RestWrite.prototype.setRequiredFieldsIfNeeded = function () { this.data.ACL = acl; this.storage.fieldsChangedByTrigger = this.storage.fieldsChangedByTrigger || []; - this.storage.fieldsChangedByTrigger.push("ACL"); + this.storage.fieldsChangedByTrigger.push('ACL'); } // Add default fields @@ -444,18 +444,18 @@ RestWrite.prototype.setRequiredFieldsIfNeeded = function () { if ( this.auth.isMaintenance && this.data.createdAt && - this.data.createdAt.__type === "Date" + this.data.createdAt.__type === 'Date' ) { this.data.createdAt = this.data.createdAt.iso; - if (this.data.updatedAt && this.data.updatedAt.__type === "Date") { + if (this.data.updatedAt && this.data.updatedAt.__type === 'Date') { const createdAt = new Date(this.data.createdAt); const updatedAt = new Date(this.data.updatedAt.iso); if (updatedAt < createdAt) { throw new Parse.Error( Parse.Error.VALIDATION_ERROR, - "updatedAt cannot occur before createdAt" + 'updatedAt cannot occur before createdAt' ); } @@ -497,50 +497,50 @@ RestWrite.prototype.setRequiredFieldsIfNeeded = function () { // Does nothing if this isn't a user object. // Returns a promise for when we're done if it can't finish this tick. RestWrite.prototype.validateAuthData = function () { - if (this.className !== "_User") { + if (this.className !== '_User') { return; } const authData = this.data.authData; const hasUsernameAndPassword = - typeof this.data.username === "string" && - typeof this.data.password === "string"; + typeof this.data.username === 'string' && + typeof this.data.password === 'string'; if (!this.query && !authData) { if ( - typeof this.data.username !== "string" || + typeof this.data.username !== 'string' || _.isEmpty(this.data.username) ) { throw new Parse.Error( Parse.Error.USERNAME_MISSING, - "bad or missing username" + 'bad or missing username' ); } if ( - typeof this.data.password !== "string" || + typeof this.data.password !== 'string' || _.isEmpty(this.data.password) ) { throw new Parse.Error( Parse.Error.PASSWORD_MISSING, - "password is required" + 'password is required' ); } } if ( (authData && !Object.keys(authData).length) || - !Object.prototype.hasOwnProperty.call(this.data, "authData") + !Object.prototype.hasOwnProperty.call(this.data, 'authData') ) { // Nothing to validate here return; } else if ( - Object.prototype.hasOwnProperty.call(this.data, "authData") && + Object.prototype.hasOwnProperty.call(this.data, 'authData') && !this.data.authData ) { // Handle saving authData to null throw new Parse.Error( Parse.Error.UNSUPPORTED_SERVICE, - "This authentication method is unsupported." + 'This authentication method is unsupported.' ); } @@ -561,7 +561,7 @@ RestWrite.prototype.validateAuthData = function () { } throw new Parse.Error( Parse.Error.UNSUPPORTED_SERVICE, - "This authentication method is unsupported." + 'This authentication method is unsupported.' ); }; @@ -579,7 +579,7 @@ RestWrite.prototype.filteredObjectsByACL = function (objects) { }; RestWrite.prototype.getUserId = function () { - if (this.query && this.query.objectId && this.className === "_User") { + if (this.query && this.query.objectId && this.className === '_User') { return this.query.objectId; } else if (this.auth && this.auth.user && this.auth.user.id) { return this.auth.user.id; @@ -590,7 +590,7 @@ RestWrite.prototype.getUserId = function () { // we need after before save to ensure that the developer // is not currently duplicating auth data ID RestWrite.prototype.ensureUniqueAuthDataId = async function () { - if (this.className !== "_User" || !this.data.authData) { + if (this.className !== '_User' || !this.data.authData) { return; } @@ -607,7 +607,7 @@ RestWrite.prototype.ensureUniqueAuthDataId = async function () { if (results.length > 1) { throw new Parse.Error( Parse.Error.ACCOUNT_ALREADY_LINKED, - "this auth is already used" + 'this auth is already used' ); } // use data.objectId in case of login time and found user during handle validateAuthData @@ -615,7 +615,7 @@ RestWrite.prototype.ensureUniqueAuthDataId = async function () { if (results.length === 1 && userId !== results[0].objectId) { throw new Parse.Error( Parse.Error.ACCOUNT_ALREADY_LINKED, - "this auth is already used" + 'this auth is already used' ); } }; @@ -635,7 +635,7 @@ RestWrite.prototype.handleAuthData = async function (authData) { await Auth.handleAuthDataValidation(authData, this, userResult); throw new Parse.Error( Parse.Error.ACCOUNT_ALREADY_LINKED, - "this auth is already used" + 'this auth is already used' ); } @@ -651,7 +651,7 @@ RestWrite.prototype.handleAuthData = async function (authData) { // User found with provided authData if (results.length === 1) { - this.storage.authProvider = Object.keys(authData).join(","); + this.storage.authProvider = Object.keys(authData).join(','); const { hasMutatedAuthData, mutatedAuthData } = Auth.hasMutatedAuthData( authData, @@ -741,14 +741,14 @@ RestWrite.prototype.handleAuthData = async function (authData) { }; RestWrite.prototype.checkRestrictedFields = async function () { - if (this.className !== "_User") { + if (this.className !== '_User') { return; } if ( !this.auth.isMaintenance && !this.auth.isMaster && - "emailVerified" in this.data + 'emailVerified' in this.data ) { const error = `Clients aren't allowed to manually update email verification.`; throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, error); @@ -758,7 +758,7 @@ RestWrite.prototype.checkRestrictedFields = async function () { // The non-third-party parts of User transformation RestWrite.prototype.transformUser = async function () { var promise = Promise.resolve(); - if (this.className !== "_User") { + if (this.className !== '_User') { return promise; } @@ -770,12 +770,12 @@ RestWrite.prototype.transformUser = async function () { method: RestQuery.Method.find, config: this.config, auth: Auth.master(this.config), - className: "_Session", + className: '_Session', runBeforeFind: false, restWhere: { user: { - __type: "Pointer", - className: "_User", + __type: 'Pointer', + className: '_User', objectId: this.objectId(), }, }, @@ -796,10 +796,10 @@ RestWrite.prototype.transformUser = async function () { } if (this.query) { - this.storage["clearSessions"] = true; + this.storage['clearSessions'] = true; // Generate a new session only if the user requested if (!this.auth.isMaster && !this.auth.isMaintenance) { - this.storage["generateNewSession"] = true; + this.storage['generateNewSession'] = true; } } @@ -849,7 +849,7 @@ RestWrite.prototype._validateUserName = function () { if (results.length > 0) { throw new Parse.Error( Parse.Error.USERNAME_TAKEN, - "Account already exists for this username." + 'Account already exists for this username.' ); } return; @@ -869,7 +869,7 @@ RestWrite.prototype._validateUserName = function () { unique index will be used by the db for the query, this is an adequate solution. */ RestWrite.prototype._validateEmail = function () { - if (!this.data.email || this.data.email.__op === "Delete") { + if (!this.data.email || this.data.email.__op === 'Delete') { return Promise.resolve(); } // Validate basic email address format @@ -877,7 +877,7 @@ RestWrite.prototype._validateEmail = function () { return Promise.reject( new Parse.Error( Parse.Error.INVALID_EMAIL_ADDRESS, - "Email address format is invalid." + 'Email address format is invalid.' ) ); } @@ -897,14 +897,14 @@ RestWrite.prototype._validateEmail = function () { if (results.length > 0) { throw new Parse.Error( Parse.Error.EMAIL_TAKEN, - "Account already exists for this email address." + 'Account already exists for this email address.' ); } if ( !this.data.authData || !Object.keys(this.data.authData).length || (Object.keys(this.data.authData).length === 1 && - Object.keys(this.data.authData)[0] === "anonymous") + Object.keys(this.data.authData)[0] === 'anonymous') ) { // We updated the email, send a new validation const { originalObject, updatedObject } = this.buildParseObjects(); @@ -944,8 +944,8 @@ RestWrite.prototype._validatePasswordRequirements = function () { // b. making a custom password reset page that shows the requirements const policyError = this.config.passwordPolicy.validationError ? this.config.passwordPolicy.validationError - : "Password does not meet the Password Policy requirements."; - const containsUsernameError = "Password cannot contain your username."; + : 'Password does not meet the Password Policy requirements.'; + const containsUsernameError = 'Password cannot contain your username.'; // check whether the password meets the password strength requirements if ( @@ -971,7 +971,7 @@ RestWrite.prototype._validatePasswordRequirements = function () { } else { // retrieve the User object using objectId during password reset return this.config.database - .find("_User", { objectId: this.objectId() }) + .find('_User', { objectId: this.objectId() }) .then(results => { if (results.length != 1) { throw undefined; @@ -996,9 +996,9 @@ RestWrite.prototype._validatePasswordHistory = function () { if (this.query && this.config.passwordPolicy.maxPasswordHistory) { return this.config.database .find( - "_User", + '_User', { objectId: this.objectId() }, - { keys: ["_password_history", "_hashed_password"] }, + { keys: ['_password_history', '_hashed_password'] }, Auth.maintenance(this.config) ) .then(results => { @@ -1020,7 +1020,7 @@ RestWrite.prototype._validatePasswordHistory = function () { return passwordCrypto.compare(newPassword, hash).then(result => { if (result) { // reject if there is a match - return Promise.reject("REPEAT_PASSWORD"); + return Promise.reject('REPEAT_PASSWORD'); } return Promise.resolve(); }); @@ -1031,7 +1031,7 @@ RestWrite.prototype._validatePasswordHistory = function () { return Promise.resolve(); }) .catch(err => { - if (err === "REPEAT_PASSWORD") { + if (err === 'REPEAT_PASSWORD') { // a match was found return Promise.reject( new Parse.Error( @@ -1048,7 +1048,7 @@ RestWrite.prototype._validatePasswordHistory = function () { }; RestWrite.prototype.createSessionTokenIfNeeded = async function () { - if (this.className !== "_User") { + if (this.className !== '_User') { return; } // Don't generate session for updating user (this.query is set) unless authData exists @@ -1075,12 +1075,12 @@ RestWrite.prototype.createSessionTokenIfNeeded = async function () { // conditional statement below, as a developer may decide to execute expensive operations in them const verifyUserEmails = async () => this.config.verifyUserEmails === true || - (typeof this.config.verifyUserEmails === "function" && + (typeof this.config.verifyUserEmails === 'function' && (await Promise.resolve(this.config.verifyUserEmails(request))) === true); const preventLoginWithUnverifiedEmail = async () => this.config.preventLoginWithUnverifiedEmail === true || - (typeof this.config.preventLoginWithUnverifiedEmail === "function" && + (typeof this.config.preventLoginWithUnverifiedEmail === 'function' && (await Promise.resolve( this.config.preventLoginWithUnverifiedEmail(request) )) === true); @@ -1099,19 +1099,19 @@ RestWrite.prototype.createSessionTokenIfNeeded = async function () { RestWrite.prototype.createSessionToken = async function () { // cloud installationId from Cloud Code, // never create session tokens from there. - if (this.auth.installationId && this.auth.installationId === "cloud") { + if (this.auth.installationId && this.auth.installationId === 'cloud') { return; } if (this.storage.authProvider == null && this.data.authData) { - this.storage.authProvider = Object.keys(this.data.authData).join(","); + this.storage.authProvider = Object.keys(this.data.authData).join(','); } const { sessionData, createSession } = RestWrite.createSession(this.config, { userId: this.objectId(), createdWith: { - action: this.storage.authProvider ? "login" : "signup", - authProvider: this.storage.authProvider || "password", + action: this.storage.authProvider ? 'login' : 'signup', + authProvider: this.storage.authProvider || 'password', }, installationId: this.auth.installationId, }); @@ -1127,13 +1127,13 @@ RestWrite.createSession = function ( config, { userId, createdWith, installationId, additionalSessionData } ) { - const token = "r:" + cryptoUtils.newToken(); + const token = 'r:' + cryptoUtils.newToken(); const expiresAt = config.generateSessionExpiresAt(); const sessionData = { sessionToken: token, user: { - __type: "Pointer", - className: "_User", + __type: 'Pointer', + className: '_User', objectId: userId, }, createdWith, @@ -1152,7 +1152,7 @@ RestWrite.createSession = function ( new RestWrite( config, Auth.master(config), - "_Session", + '_Session', null, sessionData ).execute(), @@ -1161,15 +1161,15 @@ RestWrite.createSession = function ( // Delete email reset tokens if user is changing password or email. RestWrite.prototype.deleteEmailResetTokenIfNeeded = function () { - if (this.className !== "_User" || this.query === null) { + if (this.className !== '_User' || this.query === null) { // null query means create return; } - if ("password" in this.data || "email" in this.data) { + if ('password' in this.data || 'email' in this.data) { const addOps = { - _perishable_token: { __op: "Delete" }, - _perishable_token_expires_at: { __op: "Delete" }, + _perishable_token: { __op: 'Delete' }, + _perishable_token_expires_at: { __op: 'Delete' }, }; this.data = Object.assign(this.data, addOps); } @@ -1177,7 +1177,7 @@ RestWrite.prototype.deleteEmailResetTokenIfNeeded = function () { RestWrite.prototype.destroyDuplicatedSessions = function () { // Only for _Session, and at creation time - if (this.className != "_Session" || this.query) { + if (this.className != '_Session' || this.query) { return; } // Destroy the sessions in 'Background' @@ -1189,7 +1189,7 @@ RestWrite.prototype.destroyDuplicatedSessions = function () { return; } this.config.database.destroy( - "_Session", + '_Session', { user, installationId, @@ -1204,29 +1204,29 @@ RestWrite.prototype.destroyDuplicatedSessions = function () { RestWrite.prototype.handleFollowup = function () { if ( this.storage && - this.storage["clearSessions"] && + this.storage['clearSessions'] && this.config.revokeSessionOnPasswordReset ) { var sessionQuery = { user: { - __type: "Pointer", - className: "_User", + __type: 'Pointer', + className: '_User', objectId: this.objectId(), }, }; - delete this.storage["clearSessions"]; + delete this.storage['clearSessions']; return this.config.database - .destroy("_Session", sessionQuery) + .destroy('_Session', sessionQuery) .then(this.handleFollowup.bind(this)); } - if (this.storage && this.storage["generateNewSession"]) { - delete this.storage["generateNewSession"]; + if (this.storage && this.storage['generateNewSession']) { + delete this.storage['generateNewSession']; return this.createSessionToken().then(this.handleFollowup.bind(this)); } - if (this.storage && this.storage["sendVerificationEmail"]) { - delete this.storage["sendVerificationEmail"]; + if (this.storage && this.storage['sendVerificationEmail']) { + delete this.storage['sendVerificationEmail']; // Fire and forget! this.config.userController.sendVerificationEmail(this.data, { auth: this.auth, @@ -1238,14 +1238,14 @@ RestWrite.prototype.handleFollowup = function () { // Handles the _Session class specialness. // Does nothing if this isn't an _Session object. RestWrite.prototype.handleSession = function () { - if (this.response || this.className !== "_Session") { + if (this.response || this.className !== '_Session') { return; } if (!this.auth.user && !this.auth.isMaster && !this.auth.isMaintenance) { throw new Parse.Error( Parse.Error.INVALID_SESSION_TOKEN, - "Session token required." + 'Session token required.' ); } @@ -1253,7 +1253,7 @@ RestWrite.prototype.handleSession = function () { if (this.data.ACL) { throw new Parse.Error( Parse.Error.INVALID_KEY_NAME, - "Cannot set " + "ACL on a Session." + 'Cannot set ' + 'ACL on a Session.' ); } @@ -1275,8 +1275,8 @@ RestWrite.prototype.handleSession = function () { this.query, { user: { - __type: "Pointer", - className: "_User", + __type: 'Pointer', + className: '_User', objectId: this.auth.user.id, }, }, @@ -1288,7 +1288,7 @@ RestWrite.prototype.handleSession = function () { if (!this.query && !this.auth.isMaster && !this.auth.isMaintenance) { const additionalSessionData = {}; for (var key in this.data) { - if (key === "objectId" || key === "user") { + if (key === 'objectId' || key === 'user') { continue; } additionalSessionData[key] = this.data[key]; @@ -1299,7 +1299,7 @@ RestWrite.prototype.handleSession = function () { { userId: this.auth.user.id, createdWith: { - action: "create", + action: 'create', }, additionalSessionData, } @@ -1309,10 +1309,10 @@ RestWrite.prototype.handleSession = function () { if (!results.response) { throw new Parse.Error( Parse.Error.INTERNAL_SERVER_ERROR, - "Error creating session." + 'Error creating session.' ); } - sessionData["objectId"] = results.response["objectId"]; + sessionData['objectId'] = results.response['objectId']; this.response = { status: 201, location: results.location, @@ -1328,7 +1328,7 @@ RestWrite.prototype.handleSession = function () { // into an update. // Returns a promise for when we're done if it can't finish this tick. RestWrite.prototype.handleInstallation = function () { - if (this.response || this.className !== "_Installation") { + if (this.response || this.className !== '_Installation') { return; } @@ -1340,8 +1340,8 @@ RestWrite.prototype.handleInstallation = function () { ) { throw new Parse.Error( 135, - "at least one ID field (deviceToken, installationId) " + - "must be specified in this operation" + 'at least one ID field (deviceToken, installationId) ' + + 'must be specified in this operation' ); } @@ -1407,7 +1407,7 @@ RestWrite.prototype.handleInstallation = function () { promise = promise .then(() => { return this.config.database.find( - "_Installation", + '_Installation', { $or: orQueries, }, @@ -1436,7 +1436,7 @@ RestWrite.prototype.handleInstallation = function () { if (!objectIdMatch) { throw new Parse.Error( Parse.Error.OBJECT_NOT_FOUND, - "Object not found for update." + 'Object not found for update.' ); } if ( @@ -1446,7 +1446,7 @@ RestWrite.prototype.handleInstallation = function () { ) { throw new Parse.Error( 136, - "installationId may not be changed in this " + "operation" + 'installationId may not be changed in this ' + 'operation' ); } if ( @@ -1458,7 +1458,7 @@ RestWrite.prototype.handleInstallation = function () { ) { throw new Parse.Error( 136, - "deviceToken may not be changed in this " + "operation" + 'deviceToken may not be changed in this ' + 'operation' ); } if ( @@ -1468,7 +1468,7 @@ RestWrite.prototype.handleInstallation = function () { ) { throw new Parse.Error( 136, - "deviceType may not be changed in this " + "operation" + 'deviceType may not be changed in this ' + 'operation' ); } } @@ -1484,7 +1484,7 @@ RestWrite.prototype.handleInstallation = function () { if (!this.query && !this.data.deviceType && !idMatch) { throw new Parse.Error( 135, - "deviceType must be specified in this operation" + 'deviceType must be specified in this operation' ); } }) @@ -1494,17 +1494,17 @@ RestWrite.prototype.handleInstallation = function () { return; } else if ( deviceTokenMatches.length == 1 && - (!deviceTokenMatches[0]["installationId"] || !installationId) + (!deviceTokenMatches[0]['installationId'] || !installationId) ) { // Single match on device token but none on installationId, and either // the passed object or the match is missing an installationId, so we // can just return the match. - return deviceTokenMatches[0]["objectId"]; + return deviceTokenMatches[0]['objectId']; } else if (!this.data.installationId) { throw new Parse.Error( 132, - "Must specify installationId when deviceToken " + - "matches multiple Installation objects" + 'Must specify installationId when deviceToken ' + + 'matches multiple Installation objects' ); } else { // Multiple device token matches and we specified an installation ID, @@ -1519,9 +1519,9 @@ RestWrite.prototype.handleInstallation = function () { }, }; if (this.data.appIdentifier) { - delQuery["appIdentifier"] = this.data.appIdentifier; + delQuery['appIdentifier'] = this.data.appIdentifier; } - this.config.database.destroy("_Installation", delQuery).catch(err => { + this.config.database.destroy('_Installation', delQuery).catch(err => { if (err.code == Parse.Error.OBJECT_NOT_FOUND) { // no deletions were made. Can be ignored. return; @@ -1534,16 +1534,16 @@ RestWrite.prototype.handleInstallation = function () { } else { if ( deviceTokenMatches.length == 1 && - !deviceTokenMatches[0]["installationId"] + !deviceTokenMatches[0]['installationId'] ) { // Exactly one device token match and it doesn't have an installation // ID. This is the one case where we want to merge with the existing // object. const delQuery = { objectId: idMatch.objectId }; return this.config.database - .destroy("_Installation", delQuery) + .destroy('_Installation', delQuery) .then(() => { - return deviceTokenMatches[0]["objectId"]; + return deviceTokenMatches[0]['objectId']; }) .catch(err => { if (err.code == Parse.Error.OBJECT_NOT_FOUND) { @@ -1567,7 +1567,7 @@ RestWrite.prototype.handleInstallation = function () { // We have a unique install Id, use that to preserve // the interesting installation if (this.data.installationId) { - delQuery["installationId"] = { + delQuery['installationId'] = { $ne: this.data.installationId, }; } else if ( @@ -1576,7 +1576,7 @@ RestWrite.prototype.handleInstallation = function () { idMatch.objectId == this.data.objectId ) { // we passed an objectId, preserve that instalation - delQuery["objectId"] = { + delQuery['objectId'] = { $ne: idMatch.objectId, }; } else { @@ -1584,10 +1584,10 @@ RestWrite.prototype.handleInstallation = function () { return idMatch.objectId; } if (this.data.appIdentifier) { - delQuery["appIdentifier"] = this.data.appIdentifier; + delQuery['appIdentifier'] = this.data.appIdentifier; } this.config.database - .destroy("_Installation", delQuery) + .destroy('_Installation', delQuery) .catch(err => { if (err.code == Parse.Error.OBJECT_NOT_FOUND) { // no deletions were made. Can be ignored. @@ -1631,7 +1631,7 @@ RestWrite.prototype.runDatabaseOperation = function () { return; } - if (this.className === "_Role") { + if (this.className === '_Role') { this.config.cacheController.role.clear(); if (this.config.liveQueryController) { this.config.liveQueryController.clearCachedRoles(this.auth.user); @@ -1639,7 +1639,7 @@ RestWrite.prototype.runDatabaseOperation = function () { } if ( - this.className === "_User" && + this.className === '_User' && this.query && this.auth.isUnauthenticated() ) { @@ -1649,21 +1649,21 @@ RestWrite.prototype.runDatabaseOperation = function () { ); } - if (this.className === "_Product" && this.data.download) { + if (this.className === '_Product' && this.data.download) { this.data.downloadName = this.data.download.name; } // TODO: Add better detection for ACL, ensuring a user can't be locked from // their own user record. - if (this.data.ACL && this.data.ACL["*unresolved"]) { - throw new Parse.Error(Parse.Error.INVALID_ACL, "Invalid ACL."); + if (this.data.ACL && this.data.ACL['*unresolved']) { + throw new Parse.Error(Parse.Error.INVALID_ACL, 'Invalid ACL.'); } if (this.query) { // Force the user to not lockout // Matched with parse.com if ( - this.className === "_User" && + this.className === '_User' && this.data.ACL && this.auth.isMaster !== true && this.auth.isMaintenance !== true @@ -1672,7 +1672,7 @@ RestWrite.prototype.runDatabaseOperation = function () { } // update password timestamp if user password is being changed if ( - this.className === "_User" && + this.className === '_User' && this.data._hashed_password && this.config.passwordPolicy && this.config.passwordPolicy.maxPasswordAge @@ -1685,16 +1685,16 @@ RestWrite.prototype.runDatabaseOperation = function () { let defer = Promise.resolve(); // if password history is enabled then save the current password to history if ( - this.className === "_User" && + this.className === '_User' && this.data._hashed_password && this.config.passwordPolicy && this.config.passwordPolicy.maxPasswordHistory ) { defer = this.config.database .find( - "_User", + '_User', { objectId: this.objectId() }, - { keys: ["_password_history", "_hashed_password"] }, + { keys: ['_password_history', '_hashed_password'] }, Auth.maintenance(this.config) ) .then(results => { @@ -1741,13 +1741,13 @@ RestWrite.prototype.runDatabaseOperation = function () { }); } else { // Set the default ACL and password timestamp for the new _User - if (this.className === "_User") { + if (this.className === '_User') { var ACL = this.data.ACL; // default public r/w ACL if (!ACL) { ACL = {}; if (!this.config.enforcePrivateUsers) { - ACL["*"] = { read: true, write: false }; + ACL['*'] = { read: true, write: false }; } } // make sure the user is not locked down @@ -1773,7 +1773,7 @@ RestWrite.prototype.runDatabaseOperation = function () { ) .catch(error => { if ( - this.className !== "_User" || + this.className !== '_User' || error.code !== Parse.Error.DUPLICATE_VALUE ) { throw error; @@ -1783,22 +1783,22 @@ RestWrite.prototype.runDatabaseOperation = function () { if ( error && error.userInfo && - error.userInfo.duplicated_field === "username" + error.userInfo.duplicated_field === 'username' ) { throw new Parse.Error( Parse.Error.USERNAME_TAKEN, - "Account already exists for this username." + 'Account already exists for this username.' ); } if ( error && error.userInfo && - error.userInfo.duplicated_field === "email" + error.userInfo.duplicated_field === 'email' ) { throw new Parse.Error( Parse.Error.EMAIL_TAKEN, - "Account already exists for this email address." + 'Account already exists for this email address.' ); } @@ -1819,7 +1819,7 @@ RestWrite.prototype.runDatabaseOperation = function () { if (results.length > 0) { throw new Parse.Error( Parse.Error.USERNAME_TAKEN, - "Account already exists for this username." + 'Account already exists for this username.' ); } return this.config.database.find( @@ -1832,12 +1832,12 @@ RestWrite.prototype.runDatabaseOperation = function () { if (results.length > 0) { throw new Parse.Error( Parse.Error.EMAIL_TAKEN, - "Account already exists for this email address." + 'Account already exists for this email address.' ); } throw new Parse.Error( Parse.Error.DUPLICATE_VALUE, - "A duplicate value for a field with unique values was provided" + 'A duplicate value for a field with unique values was provided' ); }); }) @@ -1923,14 +1923,14 @@ RestWrite.prototype.runAfterSaveTrigger = function () { } }) .catch(function (err) { - logger.warn("afterSave caught an error", err); + logger.warn('afterSave caught an error', err); }); }; // A helper to figure out what location this operation happens at. RestWrite.prototype.location = function () { var middle = - this.className === "_User" ? "/users/" : "/classes/" + this.className + "/"; + this.className === '_User' ? '/users/' : '/classes/' + this.className + '/'; const mount = this.config.mount || this.config.serverURL; return mount + middle + this.data.objectId; }; @@ -1975,17 +1975,17 @@ RestWrite.prototype.buildParseObjects = function () { } const updatedObject = triggers.inflate(extraData, this.originalData); Object.keys(this.data).reduce(function (data, key) { - if (key.indexOf(".") > 0) { - if (typeof data[key].__op === "string") { + if (key.indexOf('.') > 0) { + if (typeof data[key].__op === 'string') { if (!readOnlyAttributes.includes(key)) { updatedObject.set(key, data[key]); } } else { // subdocument key with dot notation { 'x.y': v } => { 'x': { 'y' : v } }) - const splittedKey = key.split("."); + const splittedKey = key.split('.'); const parentProp = splittedKey[0]; let parentVal = updatedObject.get(parentProp); - if (typeof parentVal !== "object") { + if (typeof parentVal !== 'object') { parentVal = {}; } parentVal[splittedKey[1]] = data[key]; @@ -2005,7 +2005,7 @@ RestWrite.prototype.buildParseObjects = function () { }; RestWrite.prototype.cleanUserAuthData = function () { - if (this.response && this.response.response && this.className === "_User") { + if (this.response && this.response.response && this.className === '_User') { const user = this.response.response; if (user.authData) { Object.keys(user.authData).forEach(provider => { @@ -2027,15 +2027,15 @@ RestWrite.prototype._updateResponseWithData = function (response, data) { if (!pending[key]) { data[key] = this.originalData ? this.originalData[key] - : { __op: "Delete" }; + : { __op: 'Delete' }; this.storage.fieldsChangedByTrigger.push(key); } } const skipKeys = [...(requiredColumns.read[this.className] || [])]; if (!this.query) { - skipKeys.push("objectId", "createdAt"); + skipKeys.push('objectId', 'createdAt'); } else { - skipKeys.push("updatedAt"); + skipKeys.push('updatedAt'); delete response.objectId; } for (const key in response) { @@ -2045,7 +2045,7 @@ RestWrite.prototype._updateResponseWithData = function (response, data) { const value = response[key]; if ( value == null || - (value.__type && value.__type === "Pointer") || + (value.__type && value.__type === 'Pointer') || util.isDeepStrictEqual(data[key], value) || util.isDeepStrictEqual((this.originalData || {})[key], value) ) { @@ -2066,7 +2066,7 @@ RestWrite.prototype._updateResponseWithData = function (response, data) { // Strips operations from responses if (response[fieldName] && response[fieldName].__op) { delete response[fieldName]; - if (clientSupportsDelete && dataValue.__op == "Delete") { + if (clientSupportsDelete && dataValue.__op == 'Delete') { response[fieldName] = dataValue; } } diff --git a/src/Routers/AggregateRouter.js b/src/Routers/AggregateRouter.js index d422917b92..ec7e448b95 100644 --- a/src/Routers/AggregateRouter.js +++ b/src/Routers/AggregateRouter.js @@ -1,8 +1,8 @@ -import Parse from "parse/node"; -import * as middleware from "../middlewares"; -import rest from "../rest"; -import ClassesRouter from "./ClassesRouter"; -import UsersRouter from "./UsersRouter"; +import Parse from 'parse/node'; +import * as middleware from '../middlewares'; +import rest from '../rest'; +import ClassesRouter from './ClassesRouter'; +import UsersRouter from './UsersRouter'; export class AggregateRouter extends ClassesRouter { async handleFind(req) { @@ -31,7 +31,7 @@ export class AggregateRouter extends ClassesRouter { delete body.readPreference; } options.pipeline = AggregateRouter.getPipeline(body); - if (typeof body.where === "string") { + if (typeof body.where === 'string') { body.where = JSON.parse(body.where); } try { @@ -45,7 +45,7 @@ export class AggregateRouter extends ClassesRouter { req.info.context ); for (const result of response.results) { - if (typeof result === "object") { + if (typeof result === 'object') { UsersRouter.removeHiddenProperties(result); } } @@ -95,7 +95,7 @@ export class AggregateRouter extends ClassesRouter { if (keys.length !== 1) { throw new Parse.Error( Parse.Error.INVALID_QUERY, - `Pipeline stages should only have one key but found ${keys.join(", ")}.` + `Pipeline stages should only have one key but found ${keys.join(', ')}.` ); } return AggregateRouter.transformStage(keys[0], stage); @@ -103,24 +103,24 @@ export class AggregateRouter extends ClassesRouter { } static transformStage(stageName, stage) { - const skipKeys = ["distinct", "where"]; + const skipKeys = ['distinct', 'where']; if (skipKeys.includes(stageName)) { return; } - if (stageName[0] !== "$") { + if (stageName[0] !== '$') { throw new Parse.Error( Parse.Error.INVALID_QUERY, `Invalid aggregate stage '${stageName}'.` ); } - if (stageName === "$group") { - if (Object.prototype.hasOwnProperty.call(stage[stageName], "objectId")) { + if (stageName === '$group') { + if (Object.prototype.hasOwnProperty.call(stage[stageName], 'objectId')) { throw new Parse.Error( Parse.Error.INVALID_QUERY, `Cannot use 'objectId' in aggregation stage $group.` ); } - if (!Object.prototype.hasOwnProperty.call(stage[stageName], "_id")) { + if (!Object.prototype.hasOwnProperty.call(stage[stageName], '_id')) { throw new Parse.Error( Parse.Error.INVALID_QUERY, `Invalid parameter for query: group. Missing key _id` @@ -132,8 +132,8 @@ export class AggregateRouter extends ClassesRouter { mountRoutes() { this.route( - "GET", - "/aggregate/:className", + 'GET', + '/aggregate/:className', middleware.promiseEnforceMasterKeyAccess, req => { return this.handleFind(req); diff --git a/src/Routers/AnalyticsRouter.js b/src/Routers/AnalyticsRouter.js index 2379211f51..90ffcdcc4a 100644 --- a/src/Routers/AnalyticsRouter.js +++ b/src/Routers/AnalyticsRouter.js @@ -1,5 +1,5 @@ // AnalyticsRouter.js -import PromiseRouter from "../PromiseRouter"; +import PromiseRouter from '../PromiseRouter'; function appOpened(req) { const analyticsController = req.config.analyticsController; @@ -13,7 +13,7 @@ function trackEvent(req) { export class AnalyticsRouter extends PromiseRouter { mountRoutes() { - this.route("POST", "/events/AppOpened", appOpened); - this.route("POST", "/events/:eventName", trackEvent); + this.route('POST', '/events/AppOpened', appOpened); + this.route('POST', '/events/:eventName', trackEvent); } } diff --git a/src/Routers/AudiencesRouter.js b/src/Routers/AudiencesRouter.js index 97fbfb13fc..bd5fd15b6d 100644 --- a/src/Routers/AudiencesRouter.js +++ b/src/Routers/AudiencesRouter.js @@ -1,10 +1,10 @@ -import ClassesRouter from "./ClassesRouter"; -import rest from "../rest"; -import * as middleware from "../middlewares"; +import ClassesRouter from './ClassesRouter'; +import rest from '../rest'; +import * as middleware from '../middlewares'; export class AudiencesRouter extends ClassesRouter { className() { - return "_Audience"; + return '_Audience'; } handleFind(req) { @@ -21,7 +21,7 @@ export class AudiencesRouter extends ClassesRouter { .find( req.config, req.auth, - "_Audience", + '_Audience', body.where, options, req.info.clientSDK, @@ -46,40 +46,40 @@ export class AudiencesRouter extends ClassesRouter { mountRoutes() { this.route( - "GET", - "/push_audiences", + 'GET', + '/push_audiences', middleware.promiseEnforceMasterKeyAccess, req => { return this.handleFind(req); } ); this.route( - "GET", - "/push_audiences/:objectId", + 'GET', + '/push_audiences/:objectId', middleware.promiseEnforceMasterKeyAccess, req => { return this.handleGet(req); } ); this.route( - "POST", - "/push_audiences", + 'POST', + '/push_audiences', middleware.promiseEnforceMasterKeyAccess, req => { return this.handleCreate(req); } ); this.route( - "PUT", - "/push_audiences/:objectId", + 'PUT', + '/push_audiences/:objectId', middleware.promiseEnforceMasterKeyAccess, req => { return this.handleUpdate(req); } ); this.route( - "DELETE", - "/push_audiences/:objectId", + 'DELETE', + '/push_audiences/:objectId', middleware.promiseEnforceMasterKeyAccess, req => { return this.handleDelete(req); diff --git a/src/Routers/ClassesRouter.js b/src/Routers/ClassesRouter.js index 67bd042639..d3e04d3acd 100644 --- a/src/Routers/ClassesRouter.js +++ b/src/Routers/ClassesRouter.js @@ -1,16 +1,16 @@ -import PromiseRouter from "../PromiseRouter"; -import rest from "../rest"; -import _ from "lodash"; -import Parse from "parse/node"; -import { promiseEnsureIdempotency } from "../middlewares"; +import PromiseRouter from '../PromiseRouter'; +import rest from '../rest'; +import _ from 'lodash'; +import Parse from 'parse/node'; +import { promiseEnsureIdempotency } from '../middlewares'; const ALLOWED_GET_QUERY_KEYS = [ - "keys", - "include", - "excludeKeys", - "readPreference", - "includeReadPreference", - "subqueryReadPreference", + 'keys', + 'include', + 'excludeKeys', + 'readPreference', + 'includeReadPreference', + 'subqueryReadPreference', ]; export class ClassesRouter extends PromiseRouter { @@ -34,7 +34,7 @@ export class ClassesRouter extends PromiseRouter { if (body.redirectClassNameForKey) { options.redirectClassNameForKey = String(body.redirectClassNameForKey); } - if (typeof body.where === "string") { + if (typeof body.where === 'string') { body.where = JSON.parse(body.where); } return rest @@ -64,7 +64,7 @@ export class ClassesRouter extends PromiseRouter { if (ALLOWED_GET_QUERY_KEYS.indexOf(key) === -1) { throw new Parse.Error( Parse.Error.INVALID_QUERY, - "Improper encode of parameter" + 'Improper encode of parameter' ); } } @@ -78,13 +78,13 @@ export class ClassesRouter extends PromiseRouter { if (body.excludeKeys != null) { options.excludeKeys = String(body.excludeKeys); } - if (typeof body.readPreference === "string") { + if (typeof body.readPreference === 'string') { options.readPreference = body.readPreference; } - if (typeof body.includeReadPreference === "string") { + if (typeof body.includeReadPreference === 'string') { options.includeReadPreference = body.includeReadPreference; } - if (typeof body.subqueryReadPreference === "string") { + if (typeof body.subqueryReadPreference === 'string') { options.subqueryReadPreference = body.subqueryReadPreference; } @@ -102,11 +102,11 @@ export class ClassesRouter extends PromiseRouter { if (!response.results || response.results.length == 0) { throw new Parse.Error( Parse.Error.OBJECT_NOT_FOUND, - "Object not found." + 'Object not found.' ); } - if (this.className(req) === "_User") { + if (this.className(req) === '_User') { delete response.results[0].sessionToken; const user = response.results[0]; @@ -122,13 +122,13 @@ export class ClassesRouter extends PromiseRouter { handleCreate(req) { if ( - this.className(req) === "_User" && - typeof req.body?.objectId === "string" && - req.body.objectId.startsWith("role:") + this.className(req) === '_User' && + typeof req.body?.objectId === 'string' && + req.body.objectId.startsWith('role:') ) { throw new Parse.Error( Parse.Error.OPERATION_FORBIDDEN, - "Invalid object ID." + 'Invalid object ID.' ); } return rest.create( @@ -182,22 +182,22 @@ export class ClassesRouter extends PromiseRouter { static optionsFromBody(body, defaultLimit) { const allowConstraints = [ - "skip", - "limit", - "order", - "count", - "keys", - "excludeKeys", - "include", - "includeAll", - "redirectClassNameForKey", - "where", - "readPreference", - "includeReadPreference", - "subqueryReadPreference", - "hint", - "explain", - "comment", + 'skip', + 'limit', + 'order', + 'count', + 'keys', + 'excludeKeys', + 'include', + 'includeAll', + 'redirectClassNameForKey', + 'where', + 'readPreference', + 'includeReadPreference', + 'subqueryReadPreference', + 'hint', + 'explain', + 'comment', ]; for (const key of Object.keys(body)) { @@ -235,49 +235,49 @@ export class ClassesRouter extends PromiseRouter { if (body.includeAll) { options.includeAll = true; } - if (typeof body.readPreference === "string") { + if (typeof body.readPreference === 'string') { options.readPreference = body.readPreference; } - if (typeof body.includeReadPreference === "string") { + if (typeof body.includeReadPreference === 'string') { options.includeReadPreference = body.includeReadPreference; } - if (typeof body.subqueryReadPreference === "string") { + if (typeof body.subqueryReadPreference === 'string') { options.subqueryReadPreference = body.subqueryReadPreference; } if ( body.hint && - (typeof body.hint === "string" || typeof body.hint === "object") + (typeof body.hint === 'string' || typeof body.hint === 'object') ) { options.hint = body.hint; } if (body.explain) { options.explain = body.explain; } - if (body.comment && typeof body.comment === "string") { + if (body.comment && typeof body.comment === 'string') { options.comment = body.comment; } return options; } mountRoutes() { - this.route("GET", "/classes/:className", req => { + this.route('GET', '/classes/:className', req => { return this.handleFind(req); }); - this.route("GET", "/classes/:className/:objectId", req => { + this.route('GET', '/classes/:className/:objectId', req => { return this.handleGet(req); }); - this.route("POST", "/classes/:className", promiseEnsureIdempotency, req => { + this.route('POST', '/classes/:className', promiseEnsureIdempotency, req => { return this.handleCreate(req); }); this.route( - "PUT", - "/classes/:className/:objectId", + 'PUT', + '/classes/:className/:objectId', promiseEnsureIdempotency, req => { return this.handleUpdate(req); } ); - this.route("DELETE", "/classes/:className/:objectId", req => { + this.route('DELETE', '/classes/:className/:objectId', req => { return this.handleDelete(req); }); } diff --git a/src/Routers/CloudCodeRouter.js b/src/Routers/CloudCodeRouter.js index cc5a0dd4de..292d56c994 100644 --- a/src/Routers/CloudCodeRouter.js +++ b/src/Routers/CloudCodeRouter.js @@ -1,11 +1,11 @@ -import PromiseRouter from "../PromiseRouter"; -import Parse from "parse/node"; -import rest from "../rest"; -const triggers = require("../triggers"); -const middleware = require("../middlewares"); +import PromiseRouter from '../PromiseRouter'; +import Parse from 'parse/node'; +import rest from '../rest'; +const triggers = require('../triggers'); +const middleware = require('../middlewares'); function formatJobSchedule(job_schedule) { - if (typeof job_schedule.startAfter === "undefined") { + if (typeof job_schedule.startAfter === 'undefined') { job_schedule.startAfter = new Date().toISOString(); } return job_schedule; @@ -16,7 +16,7 @@ function validateJobSchedule(config, job_schedule) { if (job_schedule.jobName && !jobs[job_schedule.jobName]) { throw new Parse.Error( Parse.Error.INTERNAL_SERVER_ERROR, - "Cannot Schedule a job that is not deployed" + 'Cannot Schedule a job that is not deployed' ); } } @@ -24,32 +24,32 @@ function validateJobSchedule(config, job_schedule) { export class CloudCodeRouter extends PromiseRouter { mountRoutes() { this.route( - "GET", - "/cloud_code/jobs", + 'GET', + '/cloud_code/jobs', middleware.promiseEnforceMasterKeyAccess, CloudCodeRouter.getJobs ); this.route( - "GET", - "/cloud_code/jobs/data", + 'GET', + '/cloud_code/jobs/data', middleware.promiseEnforceMasterKeyAccess, CloudCodeRouter.getJobsData ); this.route( - "POST", - "/cloud_code/jobs", + 'POST', + '/cloud_code/jobs', middleware.promiseEnforceMasterKeyAccess, CloudCodeRouter.createJob ); this.route( - "PUT", - "/cloud_code/jobs/:objectId", + 'PUT', + '/cloud_code/jobs/:objectId', middleware.promiseEnforceMasterKeyAccess, CloudCodeRouter.editJob ); this.route( - "DELETE", - "/cloud_code/jobs/:objectId", + 'DELETE', + '/cloud_code/jobs/:objectId', middleware.promiseEnforceMasterKeyAccess, CloudCodeRouter.deleteJob ); @@ -57,7 +57,7 @@ export class CloudCodeRouter extends PromiseRouter { static getJobs(req) { return rest - .find(req.config, req.auth, "_JobSchedule", {}, {}) + .find(req.config, req.auth, '_JobSchedule', {}, {}) .then(scheduledJobs => { return { response: scheduledJobs.results, @@ -69,7 +69,7 @@ export class CloudCodeRouter extends PromiseRouter { const config = req.config; const jobs = triggers.getJobs(config.applicationId) || {}; return rest - .find(req.config, req.auth, "_JobSchedule", {}, {}) + .find(req.config, req.auth, '_JobSchedule', {}, {}) .then(scheduledJobs => { return { response: { @@ -86,7 +86,7 @@ export class CloudCodeRouter extends PromiseRouter { return rest.create( req.config, req.auth, - "_JobSchedule", + '_JobSchedule', formatJobSchedule(job_schedule), req.client, req.info.context @@ -101,7 +101,7 @@ export class CloudCodeRouter extends PromiseRouter { .update( req.config, req.auth, - "_JobSchedule", + '_JobSchedule', { objectId }, formatJobSchedule(job_schedule), undefined, @@ -117,7 +117,7 @@ export class CloudCodeRouter extends PromiseRouter { static deleteJob(req) { const { objectId } = req.params; return rest - .del(req.config, req.auth, "_JobSchedule", objectId, req.info.context) + .del(req.config, req.auth, '_JobSchedule', objectId, req.info.context) .then(response => { return { response, diff --git a/src/Routers/FeaturesRouter.js b/src/Routers/FeaturesRouter.js index cdfcd07092..a83b4d01cc 100644 --- a/src/Routers/FeaturesRouter.js +++ b/src/Routers/FeaturesRouter.js @@ -1,12 +1,12 @@ -import { version } from "../../package.json"; -import PromiseRouter from "../PromiseRouter"; -import * as middleware from "../middlewares"; +import { version } from '../../package.json'; +import PromiseRouter from '../PromiseRouter'; +import * as middleware from '../middlewares'; export class FeaturesRouter extends PromiseRouter { mountRoutes() { this.route( - "GET", - "/serverInfo", + 'GET', + '/serverInfo', middleware.promiseEnforceMasterKeyAccess, req => { const { config } = req; diff --git a/src/Routers/FilesRouter.js b/src/Routers/FilesRouter.js index 2b4a9e308a..28b7626351 100644 --- a/src/Routers/FilesRouter.js +++ b/src/Routers/FilesRouter.js @@ -1,29 +1,29 @@ -import express from "express"; -import * as Middlewares from "../middlewares"; -import Parse from "parse/node"; -import Config from "../Config"; -import logger from "../logger"; -const triggers = require("../triggers"); -const http = require("http"); -const Utils = require("../Utils"); +import express from 'express'; +import * as Middlewares from '../middlewares'; +import Parse from 'parse/node'; +import Config from '../Config'; +import logger from '../logger'; +const triggers = require('../triggers'); +const http = require('http'); +const Utils = require('../Utils'); const downloadFileFromURI = uri => { return new Promise((res, rej) => { http .get(uri, response => { - response.setDefaultEncoding("base64"); - let body = `data:${response.headers["content-type"]};base64,`; - response.on("data", data => (body += data)); - response.on("end", () => res(body)); + response.setDefaultEncoding('base64'); + let body = `data:${response.headers['content-type']};base64,`; + response.on('data', data => (body += data)); + response.on('end', () => res(body)); }) - .on("error", e => { + .on('error', e => { rej(`Error downloading file from ${uri}: ${e.message}`); }); }); }; const addFileDataIfNeeded = async file => { - if (file._source.format === "uri") { + if (file._source.format === 'uri') { const base64 = await downloadFileFromURI(file._source.uri); file._previousSave = file; file._data = base64; @@ -33,19 +33,19 @@ const addFileDataIfNeeded = async file => { }; export class FilesRouter { - expressRouter({ maxUploadSize = "20Mb" } = {}) { + expressRouter({ maxUploadSize = '20Mb' } = {}) { var router = express.Router(); - router.get("/files/:appId/:filename", this.getHandler); - router.get("/files/:appId/metadata/:filename", this.metadataHandler); + router.get('/files/:appId/:filename', this.getHandler); + router.get('/files/:appId/metadata/:filename', this.metadataHandler); - router.post("/files", function (req, res, next) { + router.post('/files', function (req, res, next) { next( - new Parse.Error(Parse.Error.INVALID_FILE_NAME, "Filename not provided.") + new Parse.Error(Parse.Error.INVALID_FILE_NAME, 'Filename not provided.') ); }); router.post( - "/files/:filename", + '/files/:filename', express.raw({ type: () => { return true; @@ -58,7 +58,7 @@ export class FilesRouter { ); router.delete( - "/files/:filename", + '/files/:filename', Middlewares.handleParseHeaders, Middlewares.handleParseSession, Middlewares.enforceMasterKeyAccess, @@ -73,7 +73,7 @@ export class FilesRouter { res.status(403); const err = new Parse.Error( Parse.Error.OPERATION_FORBIDDEN, - "Invalid application ID." + 'Invalid application ID.' ); res.json({ code: err.code, error: err.message }); return; @@ -82,9 +82,9 @@ export class FilesRouter { let filename = req.params.filename; try { const filesController = config.filesController; - const mime = (await import("mime")).default; + const mime = (await import('mime')).default; let contentType = mime.getType(filename); - let file = new Parse.File(filename, { base64: "" }, contentType); + let file = new Parse.File(filename, { base64: '' }, contentType); const triggerResult = await triggers.maybeRunFileTrigger( triggers.Types.beforeFind, { file }, @@ -101,8 +101,8 @@ export class FilesRouter { .handleFileStream(config, filename, req, res, contentType) .catch(() => { res.status(404); - res.set("Content-Type", "text/plain"); - res.end("File not found."); + res.set('Content-Type', 'text/plain'); + res.end('File not found.'); }); return; } @@ -111,15 +111,15 @@ export class FilesRouter { .getFileData(config, filename) .catch(() => { res.status(404); - res.set("Content-Type", "text/plain"); - res.end("File not found."); + res.set('Content-Type', 'text/plain'); + res.end('File not found.'); }); if (!data) { return; } file = new Parse.File( filename, - { base64: data.toString("base64") }, + { base64: data.toString('base64') }, contentType ); const afterFind = await triggers.maybeRunFileTrigger( @@ -131,15 +131,15 @@ export class FilesRouter { if (afterFind?.file) { contentType = mime.getType(afterFind.file._name); - data = Buffer.from(afterFind.file._data, "base64"); + data = Buffer.from(afterFind.file._data, 'base64'); } res.status(200); - res.set("Content-Type", contentType); - res.set("Content-Length", data.length); + res.set('Content-Type', contentType); + res.set('Content-Length', data.length); if (afterFind.forceDownload) { res.set( - "Content-Disposition", + 'Content-Disposition', `attachment;filename=${afterFind.file._name}` ); } @@ -163,7 +163,7 @@ export class FilesRouter { next( new Parse.Error( Parse.Error.FILE_SAVE_ERROR, - "File upload by anonymous user is disabled." + 'File upload by anonymous user is disabled.' ) ); return; @@ -177,7 +177,7 @@ export class FilesRouter { next( new Parse.Error( Parse.Error.FILE_SAVE_ERROR, - "File upload by authenticated user is disabled." + 'File upload by authenticated user is disabled.' ) ); return; @@ -186,18 +186,18 @@ export class FilesRouter { next( new Parse.Error( Parse.Error.FILE_SAVE_ERROR, - "File upload by public is disabled." + 'File upload by public is disabled.' ) ); return; } const filesController = config.filesController; const { filename } = req.params; - const contentType = req.get("Content-type"); + const contentType = req.get('Content-type'); if (!req.body || !req.body.length) { next( - new Parse.Error(Parse.Error.FILE_SAVE_ERROR, "Invalid file upload.") + new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'Invalid file upload.') ); return; } @@ -212,7 +212,7 @@ export class FilesRouter { if (!isMaster && fileExtensions) { const isValidExtension = extension => { return fileExtensions.some(ext => { - if (ext === "*") { + if (ext === '*') { return true; } const regex = new RegExp(ext); @@ -222,12 +222,12 @@ export class FilesRouter { }); }; let extension = contentType; - if (filename && filename.includes(".")) { - extension = filename.substring(filename.lastIndexOf(".") + 1); - } else if (contentType && contentType.includes("/")) { - extension = contentType.split("/")[1]; + if (filename && filename.includes('.')) { + extension = filename.substring(filename.lastIndexOf('.') + 1); + } else if (contentType && contentType.includes('/')) { + extension = contentType.split('/')[1]; } - extension = extension?.split(" ")?.join(""); + extension = extension?.split(' ')?.join(''); if (extension && !isValidExtension(extension)) { next( @@ -240,7 +240,7 @@ export class FilesRouter { } } - const base64 = req.body.toString("base64"); + const base64 = req.body.toString('base64'); const file = new Parse.File(filename, { base64 }, contentType); const { metadata = {}, tags = {} } = req.fileData || {}; try { @@ -281,7 +281,7 @@ export class FilesRouter { // if the ParseFile returned is type uri, download the file before saving it await addFileDataIfNeeded(fileObject.file); // update fileSize - const bufferData = Buffer.from(fileObject.file._data, "base64"); + const bufferData = Buffer.from(fileObject.file._data, 'base64'); fileObject.fileSize = Buffer.byteLength(bufferData); // prepare file options const fileOptions = { @@ -320,10 +320,10 @@ export class FilesRouter { req.auth ); res.status(201); - res.set("Location", saveResult.url); + res.set('Location', saveResult.url); res.json(saveResult); } catch (e) { - logger.error("Error creating a file: ", e); + logger.error('Error creating a file: ', e); const error = triggers.resolveError(e, { code: Parse.Error.FILE_SAVE_ERROR, message: `Could not store file: ${fileObject.file._name}.`, @@ -362,10 +362,10 @@ export class FilesRouter { // TODO: return useful JSON here? res.end(); } catch (e) { - logger.error("Error deleting a file: ", e); + logger.error('Error deleting a file: ', e); const error = triggers.resolveError(e, { code: Parse.Error.FILE_DELETE_ERROR, - message: "Could not delete file.", + message: 'Could not delete file.', }); next(error); } @@ -387,11 +387,11 @@ export class FilesRouter { } function isFileStreamable(req, filesController) { - const range = (req.get("Range") || "/-/").split("-"); + const range = (req.get('Range') || '/-/').split('-'); const start = Number(range[0]); const end = Number(range[1]); return ( (!isNaN(start) || !isNaN(end)) && - typeof filesController.adapter.handleFileStream === "function" + typeof filesController.adapter.handleFileStream === 'function' ); } diff --git a/src/Routers/FunctionsRouter.js b/src/Routers/FunctionsRouter.js index f0518524a0..c5ca520857 100644 --- a/src/Routers/FunctionsRouter.js +++ b/src/Routers/FunctionsRouter.js @@ -1,37 +1,37 @@ // FunctionsRouter.js -var Parse = require("parse/node").Parse, - triggers = require("../triggers"); +var Parse = require('parse/node').Parse, + triggers = require('../triggers'); -import PromiseRouter from "../PromiseRouter"; +import PromiseRouter from '../PromiseRouter'; import { promiseEnforceMasterKeyAccess, promiseEnsureIdempotency, -} from "../middlewares"; -import { jobStatusHandler } from "../StatusHandler"; -import _ from "lodash"; -import { logger } from "../logger"; +} from '../middlewares'; +import { jobStatusHandler } from '../StatusHandler'; +import _ from 'lodash'; +import { logger } from '../logger'; function parseObject(obj, config) { if (Array.isArray(obj)) { return obj.map(item => { return parseObject(item, config); }); - } else if (obj && obj.__type == "Date") { + } else if (obj && obj.__type == 'Date') { return Object.assign(new Date(obj.iso), obj); - } else if (obj && obj.__type == "File") { + } else if (obj && obj.__type == 'File') { return Parse.File.fromJSON(obj); } else if ( obj && - obj.__type == "Pointer" && + obj.__type == 'Pointer' && config.encodeParseObjectInCloudFunction ) { return Parse.Object.fromJSON({ - __type: "Pointer", + __type: 'Pointer', className: obj.className, objectId: obj.objectId, }); - } else if (obj && typeof obj === "object") { + } else if (obj && typeof obj === 'object') { return parseParams(obj, config); } else { return obj; @@ -45,21 +45,21 @@ function parseParams(params, config) { export class FunctionsRouter extends PromiseRouter { mountRoutes() { this.route( - "POST", - "/functions/:functionName", + 'POST', + '/functions/:functionName', promiseEnsureIdempotency, FunctionsRouter.handleCloudFunction ); this.route( - "POST", - "/jobs/:jobName", + 'POST', + '/jobs/:jobName', promiseEnsureIdempotency, promiseEnforceMasterKeyAccess, function (req) { return FunctionsRouter.handleCloudJob(req); } ); - this.route("POST", "/jobs", promiseEnforceMasterKeyAccess, function (req) { + this.route('POST', '/jobs', promiseEnforceMasterKeyAccess, function (req) { return FunctionsRouter.handleCloudJob(req); }); } @@ -70,7 +70,7 @@ export class FunctionsRouter extends PromiseRouter { const jobHandler = jobStatusHandler(req.config); const jobFunction = triggers.getJob(jobName, applicationId); if (!jobFunction) { - throw new Parse.Error(Parse.Error.SCRIPT_FAILED, "Invalid job."); + throw new Parse.Error(Parse.Error.SCRIPT_FAILED, 'Invalid job.'); } let params = Object.assign({}, req.body, req.query); params = parseParams(params, req.config); @@ -102,7 +102,7 @@ export class FunctionsRouter extends PromiseRouter { }); return { headers: { - "X-Parse-Job-Status-Id": jobStatus.objectId, + 'X-Parse-Job-Status-Id': jobStatus.objectId, }, response: {}, }; @@ -155,7 +155,7 @@ export class FunctionsRouter extends PromiseRouter { const { success, error } = FunctionsRouter.createResponseObject( result => { try { - if (req.config.logLevels.cloudFunctionSuccess !== "silent") { + if (req.config.logLevels.cloudFunctionSuccess !== 'silent') { const cleanInput = logger.truncateLogMessage( JSON.stringify(params) ); @@ -178,7 +178,7 @@ export class FunctionsRouter extends PromiseRouter { }, error => { try { - if (req.config.logLevels.cloudFunctionError !== "silent") { + if (req.config.logLevels.cloudFunctionError !== 'silent') { const cleanInput = logger.truncateLogMessage( JSON.stringify(params) ); diff --git a/src/Routers/GlobalConfigRouter.js b/src/Routers/GlobalConfigRouter.js index 06895720ad..04e4634cce 100644 --- a/src/Routers/GlobalConfigRouter.js +++ b/src/Routers/GlobalConfigRouter.js @@ -1,8 +1,8 @@ // global_config.js -import Parse from "parse/node"; -import PromiseRouter from "../PromiseRouter"; -import * as middleware from "../middlewares"; -import * as triggers from "../triggers"; +import Parse from 'parse/node'; +import PromiseRouter from '../PromiseRouter'; +import * as middleware from '../middlewares'; +import * as triggers from '../triggers'; const getConfigFromParams = params => { const config = new Parse.Config(); @@ -15,7 +15,7 @@ const getConfigFromParams = params => { export class GlobalConfigRouter extends PromiseRouter { getGlobalConfig(req) { return req.config.database - .find("_GlobalConfig", { objectId: "1" }, { limit: 1 }) + .find('_GlobalConfig', { objectId: '1' }, { limit: 1 }) .then(results => { if (results.length != 1) { // If there is no config in the database - return empty config. @@ -71,8 +71,8 @@ export class GlobalConfigRouter extends PromiseRouter { configObject.attributes = params; const results = await req.config.database.find( - "_GlobalConfig", - { objectId: "1" }, + '_GlobalConfig', + { objectId: '1' }, { limit: 1 } ); const isNew = results.length !== 1; @@ -90,8 +90,8 @@ export class GlobalConfigRouter extends PromiseRouter { ); if (isNew) { await req.config.database.update( - "_GlobalConfig", - { objectId: "1" }, + '_GlobalConfig', + { objectId: '1' }, update, { upsert: true }, true @@ -99,8 +99,8 @@ export class GlobalConfigRouter extends PromiseRouter { updatedConfigObject = configObject; } else { const result = await req.config.database.update( - "_GlobalConfig", - { objectId: "1" }, + '_GlobalConfig', + { objectId: '1' }, update, {}, true @@ -119,19 +119,19 @@ export class GlobalConfigRouter extends PromiseRouter { } catch (err) { const error = triggers.resolveError(err, { code: Parse.Error.SCRIPT_FAILED, - message: "Script failed. Unknown error.", + message: 'Script failed. Unknown error.', }); throw error; } } mountRoutes() { - this.route("GET", "/config", req => { + this.route('GET', '/config', req => { return this.getGlobalConfig(req); }); this.route( - "PUT", - "/config", + 'PUT', + '/config', middleware.promiseEnforceMasterKeyAccess, req => { return this.updateGlobalConfig(req); diff --git a/src/Routers/GraphQLRouter.js b/src/Routers/GraphQLRouter.js index 71624a8b83..5a328d47f3 100644 --- a/src/Routers/GraphQLRouter.js +++ b/src/Routers/GraphQLRouter.js @@ -1,8 +1,8 @@ -import Parse from "parse/node"; -import PromiseRouter from "../PromiseRouter"; -import * as middleware from "../middlewares"; +import Parse from 'parse/node'; +import PromiseRouter from '../PromiseRouter'; +import * as middleware from '../middlewares'; -const GraphQLConfigPath = "/graphql-config"; +const GraphQLConfigPath = '/graphql-config'; export class GraphQLRouter extends PromiseRouter { async getGraphQLConfig(req) { @@ -29,7 +29,7 @@ export class GraphQLRouter extends PromiseRouter { mountRoutes() { this.route( - "GET", + 'GET', GraphQLConfigPath, middleware.promiseEnforceMasterKeyAccess, req => { @@ -37,7 +37,7 @@ export class GraphQLRouter extends PromiseRouter { } ); this.route( - "PUT", + 'PUT', GraphQLConfigPath, middleware.promiseEnforceMasterKeyAccess, req => { diff --git a/src/Routers/HooksRouter.js b/src/Routers/HooksRouter.js index 14157d11e4..1e1f35c6ab 100644 --- a/src/Routers/HooksRouter.js +++ b/src/Routers/HooksRouter.js @@ -1,6 +1,6 @@ -import { Parse } from "parse/node"; -import PromiseRouter from "../PromiseRouter"; -import * as middleware from "../middlewares"; +import { Parse } from 'parse/node'; +import PromiseRouter from '../PromiseRouter'; +import * as middleware from '../middlewares'; export class HooksRouter extends PromiseRouter { createHook(aHook, config) { @@ -96,14 +96,14 @@ export class HooksRouter extends PromiseRouter { hook.triggerName = req.params.triggerName; hook.url = req.body.url; } else { - throw new Parse.Error(143, "invalid hook declaration"); + throw new Parse.Error(143, 'invalid hook declaration'); } return this.updateHook(hook, req.config); } handlePut(req) { var body = req.body || {}; - if (body.__op == "Delete") { + if (body.__op == 'Delete') { return this.handleDelete(req); } else { return this.handleUpdate(req); @@ -112,50 +112,50 @@ export class HooksRouter extends PromiseRouter { mountRoutes() { this.route( - "GET", - "/hooks/functions", + 'GET', + '/hooks/functions', middleware.promiseEnforceMasterKeyAccess, this.handleGetFunctions.bind(this) ); this.route( - "GET", - "/hooks/triggers", + 'GET', + '/hooks/triggers', middleware.promiseEnforceMasterKeyAccess, this.handleGetTriggers.bind(this) ); this.route( - "GET", - "/hooks/functions/:functionName", + 'GET', + '/hooks/functions/:functionName', middleware.promiseEnforceMasterKeyAccess, this.handleGetFunctions.bind(this) ); this.route( - "GET", - "/hooks/triggers/:className/:triggerName", + 'GET', + '/hooks/triggers/:className/:triggerName', middleware.promiseEnforceMasterKeyAccess, this.handleGetTriggers.bind(this) ); this.route( - "POST", - "/hooks/functions", + 'POST', + '/hooks/functions', middleware.promiseEnforceMasterKeyAccess, this.handlePost.bind(this) ); this.route( - "POST", - "/hooks/triggers", + 'POST', + '/hooks/triggers', middleware.promiseEnforceMasterKeyAccess, this.handlePost.bind(this) ); this.route( - "PUT", - "/hooks/functions/:functionName", + 'PUT', + '/hooks/functions/:functionName', middleware.promiseEnforceMasterKeyAccess, this.handlePut.bind(this) ); this.route( - "PUT", - "/hooks/triggers/:className/:triggerName", + 'PUT', + '/hooks/triggers/:className/:triggerName', middleware.promiseEnforceMasterKeyAccess, this.handlePut.bind(this) ); diff --git a/src/Routers/IAPValidationRouter.js b/src/Routers/IAPValidationRouter.js index ae3aef827c..d08cb3db9d 100644 --- a/src/Routers/IAPValidationRouter.js +++ b/src/Routers/IAPValidationRouter.js @@ -1,39 +1,39 @@ -import PromiseRouter from "../PromiseRouter"; -const request = require("../request"); -const rest = require("../rest"); -import Parse from "parse/node"; +import PromiseRouter from '../PromiseRouter'; +const request = require('../request'); +const rest = require('../rest'); +import Parse from 'parse/node'; // TODO move validation logic in IAPValidationController -const IAP_SANDBOX_URL = "https://sandbox.itunes.apple.com/verifyReceipt"; -const IAP_PRODUCTION_URL = "https://buy.itunes.apple.com/verifyReceipt"; +const IAP_SANDBOX_URL = 'https://sandbox.itunes.apple.com/verifyReceipt'; +const IAP_PRODUCTION_URL = 'https://buy.itunes.apple.com/verifyReceipt'; const APP_STORE_ERRORS = { - 21000: "The App Store could not read the JSON object you provided.", - 21002: "The data in the receipt-data property was malformed or missing.", - 21003: "The receipt could not be authenticated.", + 21000: 'The App Store could not read the JSON object you provided.', + 21002: 'The data in the receipt-data property was malformed or missing.', + 21003: 'The receipt could not be authenticated.', 21004: - "The shared secret you provided does not match the shared secret on file for your account.", - 21005: "The receipt server is not currently available.", - 21006: "This receipt is valid but the subscription has expired.", + 'The shared secret you provided does not match the shared secret on file for your account.', + 21005: 'The receipt server is not currently available.', + 21006: 'This receipt is valid but the subscription has expired.', 21007: - "This receipt is from the test environment, but it was sent to the production environment for verification. Send it to the test environment instead.", + 'This receipt is from the test environment, but it was sent to the production environment for verification. Send it to the test environment instead.', 21008: - "This receipt is from the production environment, but it was sent to the test environment for verification. Send it to the production environment instead.", + 'This receipt is from the production environment, but it was sent to the test environment for verification. Send it to the production environment instead.', }; function appStoreError(status) { status = parseInt(status); - var errorString = APP_STORE_ERRORS[status] || "unknown error."; + var errorString = APP_STORE_ERRORS[status] || 'unknown error.'; return { status: status, error: errorString }; } function validateWithAppStore(url, receipt) { return request({ url: url, - method: "POST", - body: { "receipt-data": receipt }, + method: 'POST', + body: { 'receipt-data': receipt }, headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, }).then(httpResponse => { const body = httpResponse.data; @@ -51,7 +51,7 @@ function getFileForProductIdentifier(productIdentifier, req) { .find( req.config, req.auth, - "_Product", + '_Product', { productIdentifier: productIdentifier }, undefined, req.info.clientSDK, @@ -63,7 +63,7 @@ function getFileForProductIdentifier(productIdentifier, req) { // Error not found or too many throw new Parse.Error( Parse.Error.OBJECT_NOT_FOUND, - "Object not found." + 'Object not found.' ); } @@ -81,19 +81,19 @@ export class IAPValidationRouter extends PromiseRouter { // TODO: Error, malformed request throw new Parse.Error( Parse.Error.INVALID_JSON, - "missing receipt or productIdentifier" + 'missing receipt or productIdentifier' ); } // Transform the object if there // otherwise assume it's in Base64 already - if (typeof receipt == "object") { - if (receipt["__type"] == "Bytes") { + if (typeof receipt == 'object') { + if (receipt['__type'] == 'Bytes') { receipt = receipt.base64; } } - if (process.env.TESTING == "1" && req.body?.bypassAppStoreValidation) { + if (process.env.TESTING == '1' && req.body?.bypassAppStoreValidation) { return getFileForProductIdentifier(productIdentifier, req); } @@ -127,6 +127,6 @@ export class IAPValidationRouter extends PromiseRouter { } mountRoutes() { - this.route("POST", "/validate_purchase", this.handleRequest); + this.route('POST', '/validate_purchase', this.handleRequest); } } diff --git a/src/Routers/InstallationsRouter.js b/src/Routers/InstallationsRouter.js index 493fee9a8b..0bb2a12e3f 100644 --- a/src/Routers/InstallationsRouter.js +++ b/src/Routers/InstallationsRouter.js @@ -1,12 +1,12 @@ // InstallationsRouter.js -import ClassesRouter from "./ClassesRouter"; -import rest from "../rest"; -import { promiseEnsureIdempotency } from "../middlewares"; +import ClassesRouter from './ClassesRouter'; +import rest from '../rest'; +import { promiseEnsureIdempotency } from '../middlewares'; export class InstallationsRouter extends ClassesRouter { className() { - return "_Installation"; + return '_Installation'; } handleFind(req) { @@ -22,7 +22,7 @@ export class InstallationsRouter extends ClassesRouter { .find( req.config, req.auth, - "_Installation", + '_Installation', body.where, options, req.info.clientSDK, @@ -34,24 +34,24 @@ export class InstallationsRouter extends ClassesRouter { } mountRoutes() { - this.route("GET", "/installations", req => { + this.route('GET', '/installations', req => { return this.handleFind(req); }); - this.route("GET", "/installations/:objectId", req => { + this.route('GET', '/installations/:objectId', req => { return this.handleGet(req); }); - this.route("POST", "/installations", promiseEnsureIdempotency, req => { + this.route('POST', '/installations', promiseEnsureIdempotency, req => { return this.handleCreate(req); }); this.route( - "PUT", - "/installations/:objectId", + 'PUT', + '/installations/:objectId', promiseEnsureIdempotency, req => { return this.handleUpdate(req); } ); - this.route("DELETE", "/installations/:objectId", req => { + this.route('DELETE', '/installations/:objectId', req => { return this.handleDelete(req); }); } diff --git a/src/Routers/LogsRouter.js b/src/Routers/LogsRouter.js index aebb9de0a7..0bc2e23455 100644 --- a/src/Routers/LogsRouter.js +++ b/src/Routers/LogsRouter.js @@ -1,12 +1,12 @@ -import { Parse } from "parse/node"; -import PromiseRouter from "../PromiseRouter"; -import * as middleware from "../middlewares"; +import { Parse } from 'parse/node'; +import PromiseRouter from '../PromiseRouter'; +import * as middleware from '../middlewares'; export class LogsRouter extends PromiseRouter { mountRoutes() { this.route( - "GET", - "/scriptlog", + 'GET', + '/scriptlog', middleware.promiseEnforceMasterKeyAccess, this.validateRequest, req => { @@ -19,7 +19,7 @@ export class LogsRouter extends PromiseRouter { if (!req.config || !req.config.loggerController) { throw new Parse.Error( Parse.Error.PUSH_MISCONFIGURED, - "Logger adapter is not available" + 'Logger adapter is not available' ); } } diff --git a/src/Routers/PagesRouter.js b/src/Routers/PagesRouter.js index 3ba78b0b4d..a8c37a4531 100644 --- a/src/Routers/PagesRouter.js +++ b/src/Routers/PagesRouter.js @@ -1,68 +1,68 @@ -import PromiseRouter from "../PromiseRouter"; -import Config from "../Config"; -import express from "express"; -import path from "path"; -import { promises as fs } from "fs"; -import { Parse } from "parse/node"; -import Utils from "../Utils"; -import mustache from "mustache"; -import Page from "../Page"; +import PromiseRouter from '../PromiseRouter'; +import Config from '../Config'; +import express from 'express'; +import path from 'path'; +import { promises as fs } from 'fs'; +import { Parse } from 'parse/node'; +import Utils from '../Utils'; +import mustache from 'mustache'; +import Page from '../Page'; // All pages with custom page key for reference and file name const pages = Object.freeze({ passwordReset: new Page({ - id: "passwordReset", - defaultFile: "password_reset.html", + id: 'passwordReset', + defaultFile: 'password_reset.html', }), passwordResetSuccess: new Page({ - id: "passwordResetSuccess", - defaultFile: "password_reset_success.html", + id: 'passwordResetSuccess', + defaultFile: 'password_reset_success.html', }), passwordResetLinkInvalid: new Page({ - id: "passwordResetLinkInvalid", - defaultFile: "password_reset_link_invalid.html", + id: 'passwordResetLinkInvalid', + defaultFile: 'password_reset_link_invalid.html', }), emailVerificationSuccess: new Page({ - id: "emailVerificationSuccess", - defaultFile: "email_verification_success.html", + id: 'emailVerificationSuccess', + defaultFile: 'email_verification_success.html', }), emailVerificationSendFail: new Page({ - id: "emailVerificationSendFail", - defaultFile: "email_verification_send_fail.html", + id: 'emailVerificationSendFail', + defaultFile: 'email_verification_send_fail.html', }), emailVerificationSendSuccess: new Page({ - id: "emailVerificationSendSuccess", - defaultFile: "email_verification_send_success.html", + id: 'emailVerificationSendSuccess', + defaultFile: 'email_verification_send_success.html', }), emailVerificationLinkInvalid: new Page({ - id: "emailVerificationLinkInvalid", - defaultFile: "email_verification_link_invalid.html", + id: 'emailVerificationLinkInvalid', + defaultFile: 'email_verification_link_invalid.html', }), emailVerificationLinkExpired: new Page({ - id: "emailVerificationLinkExpired", - defaultFile: "email_verification_link_expired.html", + id: 'emailVerificationLinkExpired', + defaultFile: 'email_verification_link_expired.html', }), }); // All page parameters for reference to be used as template placeholders or query params const pageParams = Object.freeze({ - appName: "appName", - appId: "appId", - token: "token", - username: "username", - error: "error", - locale: "locale", - publicServerUrl: "publicServerUrl", + appName: 'appName', + appId: 'appId', + token: 'token', + username: 'username', + error: 'error', + locale: 'locale', + publicServerUrl: 'publicServerUrl', }); // The header prefix to add page params as response headers -const pageParamHeaderPrefix = "x-parse-page-param-"; +const pageParamHeaderPrefix = 'x-parse-page-param-'; // The errors being thrown const errors = Object.freeze({ - jsonFailedFileLoading: "failed to load JSON file", + jsonFailedFileLoading: 'failed to load JSON file', fileOutsideAllowedScope: - "not allowed to read file outside of pages directory", + 'not allowed to read file outside of pages directory', }); export class PagesRouter extends PromiseRouter { @@ -75,10 +75,10 @@ export class PagesRouter extends PromiseRouter { // Set instance properties this.pagesConfig = pages; - this.pagesEndpoint = pages.pagesEndpoint ? pages.pagesEndpoint : "apps"; + this.pagesEndpoint = pages.pagesEndpoint ? pages.pagesEndpoint : 'apps'; this.pagesPath = pages.pagesPath - ? path.resolve("./", pages.pagesPath) - : path.resolve(__dirname, "../../public"); + ? path.resolve('./', pages.pagesPath) + : path.resolve(__dirname, '../../public'); this.loadJsonResource(); this.mountPagesRoutes(); this.mountCustomRoutes(); @@ -89,7 +89,7 @@ export class PagesRouter extends PromiseRouter { const config = req.config; const { token: rawToken } = req.query; const token = - rawToken && typeof rawToken !== "string" ? rawToken.toString() : rawToken; + rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken; if (!config) { this.invalidRequest(); @@ -156,7 +156,7 @@ export class PagesRouter extends PromiseRouter { const { token: rawToken } = req.query; const token = - rawToken && typeof rawToken !== "string" ? rawToken.toString() : rawToken; + rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken; if (!token) { return this.goToPage(req, pages.passwordResetLinkInvalid); @@ -186,18 +186,18 @@ export class PagesRouter extends PromiseRouter { const { new_password, token: rawToken } = req.body || {}; const token = - rawToken && typeof rawToken !== "string" ? rawToken.toString() : rawToken; + rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken; if ((!token || !new_password) && req.xhr === false) { return this.goToPage(req, pages.passwordResetLinkInvalid); } if (!token) { - throw new Parse.Error(Parse.Error.OTHER_CAUSE, "Missing token"); + throw new Parse.Error(Parse.Error.OTHER_CAUSE, 'Missing token'); } if (!new_password) { - throw new Parse.Error(Parse.Error.PASSWORD_MISSING, "Missing password"); + throw new Parse.Error(Parse.Error.PASSWORD_MISSING, 'Missing password'); } return config.userController @@ -220,7 +220,7 @@ export class PagesRouter extends PromiseRouter { if (result.success) { return Promise.resolve({ status: 200, - response: "Password successfully reset", + response: 'Password successfully reset', }); } if (result.err) { @@ -237,7 +237,7 @@ export class PagesRouter extends PromiseRouter { [pageParams.appName]: config.appName, }; - if (result?.err === "The password reset link has expired") { + if (result?.err === 'The password reset link has expired') { delete query[pageParams.token]; query[pageParams.token] = token; } @@ -272,7 +272,7 @@ export class PagesRouter extends PromiseRouter { ? true : responseType !== undefined ? responseType - : req.method == "POST"; + : req.method == 'POST'; // Include default parameters const defaultParams = this.getDefaultParams(config); @@ -334,13 +334,13 @@ export class PagesRouter extends PromiseRouter { */ staticRoute(req) { // Get requested path - const relativePath = req.params["resource"][0]; + const relativePath = req.params['resource'][0]; // Resolve requested path to absolute path const absolutePath = path.resolve(this.pagesPath, relativePath); // If the requested file is not a HTML file send its raw content - if (!absolutePath || !absolutePath.endsWith(".html")) { + if (!absolutePath || !absolutePath.endsWith('.html')) { return this.fileResponse(absolutePath); } @@ -395,7 +395,7 @@ export class PagesRouter extends PromiseRouter { locale = locale || this.pagesConfig.localizationFallbackLocale; // Get matching translation by locale, language or fallback locale - const language = locale.split("-")[0]; + const language = locale.split('-')[0]; const resource = this.jsonParameters[locale] || this.jsonParameters[language] || @@ -455,10 +455,10 @@ export class PagesRouter extends PromiseRouter { // Get config placeholders; can be an object, a function or an async function let configPlaceholders = - typeof this.pagesConfig.placeholders === "function" + typeof this.pagesConfig.placeholders === 'function' ? this.pagesConfig.placeholders(params) : Object.prototype.toString.call(this.pagesConfig.placeholders) === - "[object Object]" + '[object Object]' ? this.pagesConfig.placeholders : {}; if (configPlaceholders instanceof Promise) { @@ -522,7 +522,7 @@ export class PagesRouter extends PromiseRouter { throw errors.fileOutsideAllowedScope; } - return await fs.readFile(normalizedPath, "utf-8"); + return await fs.readFile(normalizedPath, 'utf-8'); } /** @@ -534,7 +534,7 @@ export class PagesRouter extends PromiseRouter { } try { const json = require( - path.resolve("./", this.pagesConfig.localizationJsonPath) + path.resolve('./', this.pagesConfig.localizationJsonPath) ); this.jsonParameters = json; } catch (e) { @@ -616,16 +616,16 @@ export class PagesRouter extends PromiseRouter { composePageUrl(file, publicServerUrl, locale) { let url = publicServerUrl; - url += url.endsWith("/") ? "" : "/"; - url += this.pagesEndpoint + "/"; - url += locale === undefined ? "" : locale + "/"; + url += url.endsWith('/') ? '' : '/'; + url += this.pagesEndpoint + '/'; + url += locale === undefined ? '' : locale + '/'; url += file; return url; } notFound() { return { - text: "Not found.", + text: 'Not found.', status: 404, }; } @@ -633,7 +633,7 @@ export class PagesRouter extends PromiseRouter { invalidRequest() { const error = new Error(); error.status = 403; - error.message = "unauthorized"; + error.message = 'unauthorized'; throw error; } @@ -654,7 +654,7 @@ export class PagesRouter extends PromiseRouter { mountPagesRoutes() { this.route( - "GET", + 'GET', `/${this.pagesEndpoint}/:appId/verify_email`, req => { this.setConfig(req); @@ -665,7 +665,7 @@ export class PagesRouter extends PromiseRouter { ); this.route( - "POST", + 'POST', `/${this.pagesEndpoint}/:appId/resend_verification_email`, req => { this.setConfig(req); @@ -676,7 +676,7 @@ export class PagesRouter extends PromiseRouter { ); this.route( - "GET", + 'GET', `/${this.pagesEndpoint}/choose_password`, req => { this.setConfig(req); @@ -687,7 +687,7 @@ export class PagesRouter extends PromiseRouter { ); this.route( - "POST", + 'POST', `/${this.pagesEndpoint}/:appId/request_password_reset`, req => { this.setConfig(req); @@ -698,7 +698,7 @@ export class PagesRouter extends PromiseRouter { ); this.route( - "GET", + 'GET', `/${this.pagesEndpoint}/:appId/request_password_reset`, req => { this.setConfig(req); @@ -735,7 +735,7 @@ export class PagesRouter extends PromiseRouter { mountStaticRoute() { this.route( - "GET", + 'GET', `/${this.pagesEndpoint}/*resource`, req => { this.setConfig(req, true); @@ -748,7 +748,7 @@ export class PagesRouter extends PromiseRouter { expressRouter() { const router = express.Router(); - router.use("/", super.expressRouter()); + router.use('/', super.expressRouter()); return router; } } diff --git a/src/Routers/PublicAPIRouter.js b/src/Routers/PublicAPIRouter.js index f6b79314ae..1f693fe8af 100644 --- a/src/Routers/PublicAPIRouter.js +++ b/src/Routers/PublicAPIRouter.js @@ -1,27 +1,27 @@ -import PromiseRouter from "../PromiseRouter"; -import Config from "../Config"; -import express from "express"; -import path from "path"; -import fs from "fs"; -import qs from "querystring"; -import { Parse } from "parse/node"; -import Deprecator from "../Deprecator/Deprecator"; - -const public_html = path.resolve(__dirname, "../../public_html"); -const views = path.resolve(__dirname, "../../views"); +import PromiseRouter from '../PromiseRouter'; +import Config from '../Config'; +import express from 'express'; +import path from 'path'; +import fs from 'fs'; +import qs from 'querystring'; +import { Parse } from 'parse/node'; +import Deprecator from '../Deprecator/Deprecator'; + +const public_html = path.resolve(__dirname, '../../public_html'); +const views = path.resolve(__dirname, '../../views'); export class PublicAPIRouter extends PromiseRouter { constructor() { super(); Deprecator.logRuntimeDeprecation({ - usage: "PublicAPIRouter", - solution: "pages.enableRouter", + usage: 'PublicAPIRouter', + solution: 'pages.enableRouter', }); } verifyEmail(req) { const { token: rawToken } = req.query; const token = - rawToken && typeof rawToken !== "string" ? rawToken.toString() : rawToken; + rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken; const appId = req.params.appId; const config = Config.get(appId); @@ -100,19 +100,19 @@ export class PublicAPIRouter extends PromiseRouter { if (!config.publicServerURL) { return resolve({ status: 404, - text: "Not found.", + text: 'Not found.', }); } // Should we keep the file in memory or leave like that? fs.readFile( - path.resolve(views, "choose_password"), - "utf-8", + path.resolve(views, 'choose_password'), + 'utf-8', (err, data) => { if (err) { return reject(err); } data = data.replace( - "PARSE_SERVER_URL", + 'PARSE_SERVER_URL', `'${config.publicServerURL}'` ); resolve({ @@ -136,7 +136,7 @@ export class PublicAPIRouter extends PromiseRouter { const { token: rawToken } = req.query; const token = - rawToken && typeof rawToken !== "string" ? rawToken.toString() : rawToken; + rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken; if (!token) { return this.invalidLink(req); @@ -173,18 +173,18 @@ export class PublicAPIRouter extends PromiseRouter { const { new_password, token: rawToken } = req.body || {}; const token = - rawToken && typeof rawToken !== "string" ? rawToken.toString() : rawToken; + rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken; if ((!token || !new_password) && req.xhr === false) { return this.invalidLink(req); } if (!token) { - throw new Parse.Error(Parse.Error.OTHER_CAUSE, "Missing token"); + throw new Parse.Error(Parse.Error.OTHER_CAUSE, 'Missing token'); } if (!new_password) { - throw new Parse.Error(Parse.Error.PASSWORD_MISSING, "Missing password"); + throw new Parse.Error(Parse.Error.PASSWORD_MISSING, 'Missing password'); } return config.userController @@ -210,7 +210,7 @@ export class PublicAPIRouter extends PromiseRouter { app: config.appName, }; - if (result?.err === "The password reset link has expired") { + if (result?.err === 'The password reset link has expired') { delete queryString.token; queryString.token = token; } @@ -220,7 +220,7 @@ export class PublicAPIRouter extends PromiseRouter { if (result.success) { return Promise.resolve({ status: 200, - response: "Password successfully reset", + response: 'Password successfully reset', }); } if (result.err) { @@ -264,7 +264,7 @@ export class PublicAPIRouter extends PromiseRouter { missingPublicServerURL() { return Promise.resolve({ - text: "Not found.", + text: 'Not found.', status: 404, }); } @@ -272,7 +272,7 @@ export class PublicAPIRouter extends PromiseRouter { invalidRequest() { const error = new Error(); error.status = 403; - error.message = "unauthorized"; + error.message = 'unauthorized'; throw error; } @@ -283,8 +283,8 @@ export class PublicAPIRouter extends PromiseRouter { mountRoutes() { this.route( - "GET", - "/apps/:appId/verify_email", + 'GET', + '/apps/:appId/verify_email', req => { this.setConfig(req); }, @@ -294,8 +294,8 @@ export class PublicAPIRouter extends PromiseRouter { ); this.route( - "POST", - "/apps/:appId/resend_verification_email", + 'POST', + '/apps/:appId/resend_verification_email', req => { this.setConfig(req); }, @@ -304,13 +304,13 @@ export class PublicAPIRouter extends PromiseRouter { } ); - this.route("GET", "/apps/choose_password", req => { + this.route('GET', '/apps/choose_password', req => { return this.changePassword(req); }); this.route( - "POST", - "/apps/:appId/request_password_reset", + 'POST', + '/apps/:appId/request_password_reset', req => { this.setConfig(req); }, @@ -320,8 +320,8 @@ export class PublicAPIRouter extends PromiseRouter { ); this.route( - "GET", - "/apps/:appId/request_password_reset", + 'GET', + '/apps/:appId/request_password_reset', req => { this.setConfig(req); }, @@ -333,8 +333,8 @@ export class PublicAPIRouter extends PromiseRouter { expressRouter() { const router = express.Router(); - router.use("/apps", express.static(public_html)); - router.use("/", super.expressRouter()); + router.use('/apps', express.static(public_html)); + router.use('/', super.expressRouter()); return router; } } diff --git a/src/Routers/PurgeRouter.js b/src/Routers/PurgeRouter.js index d30aea6d93..6d0aca0c3e 100644 --- a/src/Routers/PurgeRouter.js +++ b/src/Routers/PurgeRouter.js @@ -1,6 +1,6 @@ -import PromiseRouter from "../PromiseRouter"; -import * as middleware from "../middlewares"; -import Parse from "parse/node"; +import PromiseRouter from '../PromiseRouter'; +import * as middleware from '../middlewares'; +import Parse from 'parse/node'; export class PurgeRouter extends PromiseRouter { handlePurge(req) { @@ -14,9 +14,9 @@ export class PurgeRouter extends PromiseRouter { .purgeCollection(req.params.className) .then(() => { var cacheAdapter = req.config.cacheController; - if (req.params.className == "_Session") { + if (req.params.className == '_Session') { cacheAdapter.user.clear(); - } else if (req.params.className == "_Role") { + } else if (req.params.className == '_Role') { cacheAdapter.role.clear(); } return { response: {} }; @@ -31,8 +31,8 @@ export class PurgeRouter extends PromiseRouter { mountRoutes() { this.route( - "DELETE", - "/purge/:className", + 'DELETE', + '/purge/:className', middleware.promiseEnforceMasterKeyAccess, req => { return this.handlePurge(req); diff --git a/src/Routers/PushRouter.js b/src/Routers/PushRouter.js index 3c7bd55e8b..dee9968d6b 100644 --- a/src/Routers/PushRouter.js +++ b/src/Routers/PushRouter.js @@ -1,12 +1,12 @@ -import PromiseRouter from "../PromiseRouter"; -import * as middleware from "../middlewares"; -import { Parse } from "parse/node"; +import PromiseRouter from '../PromiseRouter'; +import * as middleware from '../middlewares'; +import { Parse } from 'parse/node'; export class PushRouter extends PromiseRouter { mountRoutes() { this.route( - "POST", - "/push", + 'POST', + '/push', middleware.promiseEnforceMasterKeyAccess, PushRouter.handlePOST ); @@ -23,7 +23,7 @@ export class PushRouter extends PromiseRouter { if (!pushController) { throw new Parse.Error( Parse.Error.PUSH_MISCONFIGURED, - "Push controller is not set" + 'Push controller is not set' ); } @@ -38,7 +38,7 @@ export class PushRouter extends PromiseRouter { pushStatusId = objectId; resolve({ headers: { - "X-Parse-Push-Status-Id": pushStatusId, + 'X-Parse-Push-Status-Id': pushStatusId, }, response: { result: true, @@ -61,14 +61,14 @@ export class PushRouter extends PromiseRouter { */ static getQueryCondition(req) { const body = req.body || {}; - const hasWhere = typeof body.where !== "undefined"; - const hasChannels = typeof body.channels !== "undefined"; + const hasWhere = typeof body.where !== 'undefined'; + const hasChannels = typeof body.channels !== 'undefined'; let where; if (hasWhere && hasChannels) { throw new Parse.Error( Parse.Error.PUSH_MISCONFIGURED, - "Channels and query can not be set at the same time." + 'Channels and query can not be set at the same time.' ); } else if (hasWhere) { where = body.where; diff --git a/src/Routers/RolesRouter.js b/src/Routers/RolesRouter.js index 142aebbda8..e6a10df77b 100644 --- a/src/Routers/RolesRouter.js +++ b/src/Routers/RolesRouter.js @@ -1,24 +1,24 @@ -import ClassesRouter from "./ClassesRouter"; +import ClassesRouter from './ClassesRouter'; export class RolesRouter extends ClassesRouter { className() { - return "_Role"; + return '_Role'; } mountRoutes() { - this.route("GET", "/roles", req => { + this.route('GET', '/roles', req => { return this.handleFind(req); }); - this.route("GET", "/roles/:objectId", req => { + this.route('GET', '/roles/:objectId', req => { return this.handleGet(req); }); - this.route("POST", "/roles", req => { + this.route('POST', '/roles', req => { return this.handleCreate(req); }); - this.route("PUT", "/roles/:objectId", req => { + this.route('PUT', '/roles/:objectId', req => { return this.handleUpdate(req); }); - this.route("DELETE", "/roles/:objectId", req => { + this.route('DELETE', '/roles/:objectId', req => { return this.handleDelete(req); }); } diff --git a/src/Routers/SchemasRouter.js b/src/Routers/SchemasRouter.js index 036c4a0ae6..e1e77906dc 100644 --- a/src/Routers/SchemasRouter.js +++ b/src/Routers/SchemasRouter.js @@ -1,10 +1,10 @@ // schemas.js -var Parse = require("parse/node").Parse, - SchemaController = require("../Controllers/SchemaController"); +var Parse = require('parse/node').Parse, + SchemaController = require('../Controllers/SchemaController'); -import PromiseRouter from "../PromiseRouter"; -import * as middleware from "../middlewares"; +import PromiseRouter from '../PromiseRouter'; +import * as middleware from '../middlewares'; function classNameMismatchResponse(bodyClass, pathClass) { throw new Parse.Error( @@ -37,7 +37,7 @@ function getOneSchema(req) { } else { throw new Parse.Error( Parse.Error.INTERNAL_SERVER_ERROR, - "Database adapter error." + 'Database adapter error.' ); } }); @@ -47,7 +47,7 @@ const checkIfDefinedSchemasIsUsed = req => { if (req.config?.schema?.lockSchemas === true) { throw new Parse.Error( Parse.Error.OPERATION_FORBIDDEN, - "Cannot perform this operation when schemas options is used." + 'Cannot perform this operation when schemas options is used.' ); } }; @@ -139,38 +139,38 @@ const deleteSchema = req => { export class SchemasRouter extends PromiseRouter { mountRoutes() { this.route( - "GET", - "/schemas", + 'GET', + '/schemas', middleware.promiseEnforceMasterKeyAccess, getAllSchemas ); this.route( - "GET", - "/schemas/:className", + 'GET', + '/schemas/:className', middleware.promiseEnforceMasterKeyAccess, getOneSchema ); this.route( - "POST", - "/schemas", + 'POST', + '/schemas', middleware.promiseEnforceMasterKeyAccess, createSchema ); this.route( - "POST", - "/schemas/:className", + 'POST', + '/schemas/:className', middleware.promiseEnforceMasterKeyAccess, createSchema ); this.route( - "PUT", - "/schemas/:className", + 'PUT', + '/schemas/:className', middleware.promiseEnforceMasterKeyAccess, modifySchema ); this.route( - "DELETE", - "/schemas/:className", + 'DELETE', + '/schemas/:className', middleware.promiseEnforceMasterKeyAccess, deleteSchema ); diff --git a/src/Routers/SecurityRouter.js b/src/Routers/SecurityRouter.js index 3f5724d985..7eaa2216e6 100644 --- a/src/Routers/SecurityRouter.js +++ b/src/Routers/SecurityRouter.js @@ -1,12 +1,12 @@ -import PromiseRouter from "../PromiseRouter"; -import * as middleware from "../middlewares"; -import CheckRunner from "../Security/CheckRunner"; +import PromiseRouter from '../PromiseRouter'; +import * as middleware from '../middlewares'; +import CheckRunner from '../Security/CheckRunner'; export class SecurityRouter extends PromiseRouter { mountRoutes() { this.route( - "GET", - "/security", + 'GET', + '/security', middleware.promiseEnforceMasterKeyAccess, this._enforceSecurityCheckEnabled, async req => { @@ -25,7 +25,7 @@ export class SecurityRouter extends PromiseRouter { const error = new Error(); error.status = 409; error.message = - "Enable Parse Server option `security.enableCheck` to run security check."; + 'Enable Parse Server option `security.enableCheck` to run security check.'; throw error; } } diff --git a/src/Routers/SessionsRouter.js b/src/Routers/SessionsRouter.js index 5b04a9e302..026dd9dc7f 100644 --- a/src/Routers/SessionsRouter.js +++ b/src/Routers/SessionsRouter.js @@ -1,12 +1,12 @@ -import ClassesRouter from "./ClassesRouter"; -import Parse from "parse/node"; -import rest from "../rest"; -import Auth from "../Auth"; -import RestWrite from "../RestWrite"; +import ClassesRouter from './ClassesRouter'; +import Parse from 'parse/node'; +import rest from '../rest'; +import Auth from '../Auth'; +import RestWrite from '../RestWrite'; export class SessionsRouter extends ClassesRouter { className() { - return "_Session"; + return '_Session'; } handleMe(req) { @@ -14,14 +14,14 @@ export class SessionsRouter extends ClassesRouter { if (!req.info || !req.info.sessionToken) { throw new Parse.Error( Parse.Error.INVALID_SESSION_TOKEN, - "Session token required." + 'Session token required.' ); } return rest .find( req.config, Auth.master(req.config), - "_Session", + '_Session', { sessionToken: req.info.sessionToken }, undefined, req.info.clientSDK, @@ -31,7 +31,7 @@ export class SessionsRouter extends ClassesRouter { if (!response.results || response.results.length == 0) { throw new Parse.Error( Parse.Error.INVALID_SESSION_TOKEN, - "Session token not found." + 'Session token not found.' ); } return { @@ -46,12 +46,12 @@ export class SessionsRouter extends ClassesRouter { // Issue #2720 // Calling without a session token would result in a not found user if (!user) { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, "invalid session"); + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'invalid session'); } const { sessionData, createSession } = RestWrite.createSession(config, { userId: user.id, createdWith: { - action: "upgrade", + action: 'upgrade', }, installationId: req.auth.installationId, }); @@ -60,12 +60,12 @@ export class SessionsRouter extends ClassesRouter { .then(() => { // delete the session token, use the db to skip beforeSave return config.database.update( - "_User", + '_User', { objectId: user.id, }, { - sessionToken: { __op: "Delete" }, + sessionToken: { __op: 'Delete' }, } ); }) @@ -75,25 +75,25 @@ export class SessionsRouter extends ClassesRouter { } mountRoutes() { - this.route("GET", "/sessions/me", req => { + this.route('GET', '/sessions/me', req => { return this.handleMe(req); }); - this.route("GET", "/sessions", req => { + this.route('GET', '/sessions', req => { return this.handleFind(req); }); - this.route("GET", "/sessions/:objectId", req => { + this.route('GET', '/sessions/:objectId', req => { return this.handleGet(req); }); - this.route("POST", "/sessions", req => { + this.route('POST', '/sessions', req => { return this.handleCreate(req); }); - this.route("PUT", "/sessions/:objectId", req => { + this.route('PUT', '/sessions/:objectId', req => { return this.handleUpdate(req); }); - this.route("DELETE", "/sessions/:objectId", req => { + this.route('DELETE', '/sessions/:objectId', req => { return this.handleDelete(req); }); - this.route("POST", "/upgradeToRevocableSession", req => { + this.route('POST', '/upgradeToRevocableSession', req => { return this.handleUpdateToRevocableSession(req); }); } diff --git a/src/Routers/UsersRouter.js b/src/Routers/UsersRouter.js index 6c5e6638fe..16fcf31688 100644 --- a/src/Routers/UsersRouter.js +++ b/src/Routers/UsersRouter.js @@ -1,25 +1,25 @@ // These methods handle the User-related routes. -import Parse from "parse/node"; -import Config from "../Config"; -import AccountLockout from "../AccountLockout"; -import ClassesRouter from "./ClassesRouter"; -import rest from "../rest"; -import Auth from "../Auth"; -import passwordCrypto from "../password"; +import Parse from 'parse/node'; +import Config from '../Config'; +import AccountLockout from '../AccountLockout'; +import ClassesRouter from './ClassesRouter'; +import rest from '../rest'; +import Auth from '../Auth'; +import passwordCrypto from '../password'; import { maybeRunTrigger, Types as TriggerTypes, getRequestObject, resolveError, -} from "../triggers"; -import { promiseEnsureIdempotency } from "../middlewares"; -import RestWrite from "../RestWrite"; -import { logger } from "../logger"; +} from '../triggers'; +import { promiseEnsureIdempotency } from '../middlewares'; +import RestWrite from '../RestWrite'; +import { logger } from '../logger'; export class UsersRouter extends ClassesRouter { className() { - return "_User"; + return '_User'; } /** @@ -30,7 +30,7 @@ export class UsersRouter extends ClassesRouter { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { // Regexp comes from Parse.Object.prototype.validate - if (key !== "__type" && !/^[A-Za-z][0-9A-Za-z_]*$/.test(key)) { + if (key !== '__type' && !/^[A-Za-z][0-9A-Za-z_]*$/.test(key)) { delete obj[key]; } } @@ -81,23 +81,23 @@ export class UsersRouter extends ClassesRouter { if (!username && !email) { throw new Parse.Error( Parse.Error.USERNAME_MISSING, - "username/email is required." + 'username/email is required.' ); } if (!password) { throw new Parse.Error( Parse.Error.PASSWORD_MISSING, - "password is required." + 'password is required.' ); } if ( - typeof password !== "string" || - (email && typeof email !== "string") || - (username && typeof username !== "string") + typeof password !== 'string' || + (email && typeof email !== 'string') || + (username && typeof username !== 'string') ) { throw new Parse.Error( Parse.Error.OBJECT_NOT_FOUND, - "Invalid username/password." + 'Invalid username/password.' ); } @@ -112,12 +112,12 @@ export class UsersRouter extends ClassesRouter { query = { $or: [{ username }, { email: username }] }; } return req.config.database - .find("_User", query, {}, Auth.maintenance(req.config)) + .find('_User', query, {}, Auth.maintenance(req.config)) .then(results => { if (!results.length) { throw new Parse.Error( Parse.Error.OBJECT_NOT_FOUND, - "Invalid username/password." + 'Invalid username/password.' ); } @@ -142,7 +142,7 @@ export class UsersRouter extends ClassesRouter { if (!isValidPassword) { throw new Parse.Error( Parse.Error.OBJECT_NOT_FOUND, - "Invalid username/password." + 'Invalid username/password.' ); } // Ensure the user isn't locked out @@ -156,7 +156,7 @@ export class UsersRouter extends ClassesRouter { ) { throw new Parse.Error( Parse.Error.OBJECT_NOT_FOUND, - "Invalid username/password." + 'Invalid username/password.' ); } // Create request object for verification functions @@ -165,7 +165,7 @@ export class UsersRouter extends ClassesRouter { ip: req.config.ip, installationId: req.auth.installationId, object: Parse.User.fromJSON( - Object.assign({ className: "_User" }, user) + Object.assign({ className: '_User' }, user) ), }; @@ -181,14 +181,14 @@ export class UsersRouter extends ClassesRouter { // conditional statement below, as a developer may decide to execute expensive operations in them const verifyUserEmails = async () => req.config.verifyUserEmails === true || - (typeof req.config.verifyUserEmails === "function" && + (typeof req.config.verifyUserEmails === 'function' && (await Promise.resolve( req.config.verifyUserEmails(request) )) === true); const preventLoginWithUnverifiedEmail = async () => req.config.preventLoginWithUnverifiedEmail === true || (typeof req.config.preventLoginWithUnverifiedEmail === - "function" && + 'function' && (await Promise.resolve( req.config.preventLoginWithUnverifiedEmail(request) )) === true); @@ -199,7 +199,7 @@ export class UsersRouter extends ClassesRouter { ) { throw new Parse.Error( Parse.Error.EMAIL_NOT_FOUND, - "User email is not verified." + 'User email is not verified.' ); } } @@ -218,7 +218,7 @@ export class UsersRouter extends ClassesRouter { if (!req.info || !req.info.sessionToken) { throw new Parse.Error( Parse.Error.INVALID_SESSION_TOKEN, - "Invalid session token" + 'Invalid session token' ); } const sessionToken = req.info.sessionToken; @@ -226,9 +226,9 @@ export class UsersRouter extends ClassesRouter { .find( req.config, Auth.master(req.config), - "_Session", + '_Session', { sessionToken }, - { include: "user" }, + { include: 'user' }, req.info.clientSDK, req.info.context ) @@ -240,7 +240,7 @@ export class UsersRouter extends ClassesRouter { ) { throw new Parse.Error( Parse.Error.INVALID_SESSION_TOKEN, - "Invalid session token" + 'Invalid session token' ); } else { const user = response.results[0].user; @@ -273,7 +273,7 @@ export class UsersRouter extends ClassesRouter { new RestWrite( req.config, req.auth, - "_User", + '_User', { objectId: user.objectId }, req.body || {}, user, @@ -295,13 +295,13 @@ export class UsersRouter extends ClassesRouter { // simply update _User object so that it will start enforcing from now changedAt = new Date(); req.config.database.update( - "_User", + '_User', { username: user.username }, { _password_changed_at: Parse._encode(changedAt) } ); } else { // check whether the password has expired - if (changedAt.__type == "Date") { + if (changedAt.__type == 'Date') { changedAt = new Date(changedAt.iso); } // Calculate the expiry time. @@ -313,7 +313,7 @@ export class UsersRouter extends ClassesRouter { // fail of current time is past password expiry time throw new Parse.Error( Parse.Error.OBJECT_NOT_FOUND, - "Your password has expired. Please reset your password." + 'Your password has expired. Please reset your password.' ); } } @@ -328,7 +328,7 @@ export class UsersRouter extends ClassesRouter { await maybeRunTrigger( TriggerTypes.beforeLogin, req.auth, - Parse.User.fromJSON(Object.assign({ className: "_User" }, user)), + Parse.User.fromJSON(Object.assign({ className: '_User' }, user)), null, req.config, req.info.context @@ -337,7 +337,7 @@ export class UsersRouter extends ClassesRouter { // If we have some new validated authData update directly if (validatedAuthData && Object.keys(validatedAuthData).length) { await req.config.database.update( - "_User", + '_User', { objectId: user.objectId }, { authData: validatedAuthData }, {} @@ -347,8 +347,8 @@ export class UsersRouter extends ClassesRouter { const { sessionData, createSession } = RestWrite.createSession(req.config, { userId: user.objectId, createdWith: { - action: "login", - authProvider: "password", + action: 'login', + authProvider: 'password', }, installationId: req.info.installationId, }); @@ -358,7 +358,7 @@ export class UsersRouter extends ClassesRouter { await createSession(); const afterLoginUser = Parse.User.fromJSON( - Object.assign({ className: "_User" }, user) + Object.assign({ className: '_User' }, user) ); await maybeRunTrigger( TriggerTypes.afterLogin, @@ -395,7 +395,7 @@ export class UsersRouter extends ClassesRouter { if (!req.auth.isMaster) { throw new Parse.Error( Parse.Error.OPERATION_FORBIDDEN, - "master key is required" + 'master key is required' ); } @@ -403,16 +403,16 @@ export class UsersRouter extends ClassesRouter { if (!userId) { throw new Parse.Error( Parse.Error.INVALID_VALUE, - "userId must not be empty, null, or undefined" + 'userId must not be empty, null, or undefined' ); } - const queryResults = await req.config.database.find("_User", { + const queryResults = await req.config.database.find('_User', { objectId: userId, }); const user = queryResults[0]; if (!user) { - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, "user not found"); + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'user not found'); } this._sanitizeAuthData(user); @@ -420,8 +420,8 @@ export class UsersRouter extends ClassesRouter { const { sessionData, createSession } = RestWrite.createSession(req.config, { userId, createdWith: { - action: "login", - authProvider: "masterkey", + action: 'login', + authProvider: 'masterkey', }, installationId: req.info.installationId, }); @@ -452,7 +452,7 @@ export class UsersRouter extends ClassesRouter { const records = await rest.find( req.config, Auth.master(req.config), - "_Session", + '_Session', { sessionToken: req.info.sessionToken }, undefined, req.info.clientSDK, @@ -462,7 +462,7 @@ export class UsersRouter extends ClassesRouter { await rest.del( req.config, Auth.master(req.config), - "_Session", + '_Session', records.results[0].objectId, req.info.context ); @@ -470,7 +470,7 @@ export class UsersRouter extends ClassesRouter { TriggerTypes.afterLogout, req.auth, Parse.Session.fromJSON( - Object.assign({ className: "_Session" }, records.results[0]) + Object.assign({ className: '_Session' }, records.results[0]) ), null, req.config @@ -491,11 +491,11 @@ export class UsersRouter extends ClassesRouter { emailVerifyTokenReuseIfValid: req.config.emailVerifyTokenReuseIfValid, }); } catch (e) { - if (typeof e === "string") { + if (typeof e === 'string') { // Maybe we need a Bad Configuration error, but the SDKs won't understand it. For now, Internal Server Error. throw new Parse.Error( Parse.Error.INTERNAL_SERVER_ERROR, - "An appName, publicServerURL, and emailAdapter are required for password reset and email verification functionality." + 'An appName, publicServerURL, and emailAdapter are required for password reset and email verification functionality.' ); } else { throw e; @@ -512,11 +512,11 @@ export class UsersRouter extends ClassesRouter { if (!email && !token) { throw new Parse.Error( Parse.Error.EMAIL_MISSING, - "you must provide an email" + 'you must provide an email' ); } if (token) { - const results = await req.config.database.find("_User", { + const results = await req.config.database.find('_User', { _perishable_token: token, _perishable_token_expires_at: { $lt: Parse._encode(new Date()) }, }); @@ -524,10 +524,10 @@ export class UsersRouter extends ClassesRouter { email = results[0].email; } } - if (typeof email !== "string") { + if (typeof email !== 'string') { throw new Parse.Error( Parse.Error.INVALID_EMAIL_ADDRESS, - "you must provide a valid email string" + 'you must provide a valid email string' ); } const userController = req.config.userController; @@ -559,18 +559,18 @@ export class UsersRouter extends ClassesRouter { if (!email) { throw new Parse.Error( Parse.Error.EMAIL_MISSING, - "you must provide an email" + 'you must provide an email' ); } - if (typeof email !== "string") { + if (typeof email !== 'string') { throw new Parse.Error( Parse.Error.INVALID_EMAIL_ADDRESS, - "you must provide a valid email string" + 'you must provide a valid email string' ); } const results = await req.config.database.find( - "_User", + '_User', { email: email }, {}, Auth.maintenance(req.config) @@ -616,20 +616,20 @@ export class UsersRouter extends ClassesRouter { if (!password) { throw new Parse.Error( Parse.Error.OTHER_CAUSE, - "You provided username or email, you need to also provide password." + 'You provided username or email, you need to also provide password.' ); } user = await this._authenticateUserFromRequest(req); } if (!challengeData) { - throw new Parse.Error(Parse.Error.OTHER_CAUSE, "Nothing to challenge."); + throw new Parse.Error(Parse.Error.OTHER_CAUSE, 'Nothing to challenge.'); } - if (typeof challengeData !== "object") { + if (typeof challengeData !== 'object') { throw new Parse.Error( Parse.Error.OTHER_CAUSE, - "challengeData should be an object." + 'challengeData should be an object.' ); } @@ -638,23 +638,23 @@ export class UsersRouter extends ClassesRouter { // Try to find user by authData if (authData) { - if (typeof authData !== "object") { + if (typeof authData !== 'object') { throw new Parse.Error( Parse.Error.OTHER_CAUSE, - "authData should be an object." + 'authData should be an object.' ); } if (user) { throw new Parse.Error( Parse.Error.OTHER_CAUSE, - "You cannot provide username/email and authData, only use one identification method." + 'You cannot provide username/email and authData, only use one identification method.' ); } if (Object.keys(authData).filter(key => authData[key].id).length > 1) { throw new Parse.Error( Parse.Error.OTHER_CAUSE, - "You cannot provide more than one authData provider with an id." + 'You cannot provide more than one authData provider with an id.' ); } @@ -664,13 +664,13 @@ export class UsersRouter extends ClassesRouter { if (!results[0] || results.length > 1) { throw new Parse.Error( Parse.Error.OBJECT_NOT_FOUND, - "User not found." + 'User not found.' ); } // Find the provider used to find the user const provider = Object.keys(authData).find(key => authData[key].id); - parseUser = Parse.User.fromJSON({ className: "_User", ...results[0] }); + parseUser = Parse.User.fromJSON({ className: '_User', ...results[0] }); request = getRequestObject( undefined, req.auth, @@ -694,13 +694,13 @@ export class UsersRouter extends ClassesRouter { } catch (e) { // Rewrite the error to avoid guess id attack logger.error(e); - throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, "User not found."); + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.'); } } if (!parseUser) { parseUser = user - ? Parse.User.fromJSON({ className: "_User", ...user }) + ? Parse.User.fromJSON({ className: '_User', ...user }) : undefined; } @@ -727,7 +727,7 @@ export class UsersRouter extends ClassesRouter { const { adapter: { challenge }, } = authAdapter; - if (typeof challenge === "function") { + if (typeof challenge === 'function') { const providerChallengeResponse = await challenge( challengeData[provider], authData && authData[provider], @@ -739,7 +739,7 @@ export class UsersRouter extends ClassesRouter { } catch (err) { const e = resolveError(err, { code: Parse.Error.SCRIPT_FAILED, - message: "Challenge failed. Unknown error.", + message: 'Challenge failed. Unknown error.', }); const userString = req.auth && req.auth.user ? req.auth.user.id : undefined; @@ -747,7 +747,7 @@ export class UsersRouter extends ClassesRouter { `Failed running auth step challenge for ${provider} for user ${userString} with Error: ` + JSON.stringify(e), { - authenticationStep: "challenge", + authenticationStep: 'challenge', error: e, user: userString, provider, @@ -760,49 +760,49 @@ export class UsersRouter extends ClassesRouter { } mountRoutes() { - this.route("GET", "/users", req => { + this.route('GET', '/users', req => { return this.handleFind(req); }); - this.route("POST", "/users", promiseEnsureIdempotency, req => { + this.route('POST', '/users', promiseEnsureIdempotency, req => { return this.handleCreate(req); }); - this.route("GET", "/users/me", req => { + this.route('GET', '/users/me', req => { return this.handleMe(req); }); - this.route("GET", "/users/:objectId", req => { + this.route('GET', '/users/:objectId', req => { return this.handleGet(req); }); - this.route("PUT", "/users/:objectId", promiseEnsureIdempotency, req => { + this.route('PUT', '/users/:objectId', promiseEnsureIdempotency, req => { return this.handleUpdate(req); }); - this.route("DELETE", "/users/:objectId", req => { + this.route('DELETE', '/users/:objectId', req => { return this.handleDelete(req); }); - this.route("GET", "/login", req => { + this.route('GET', '/login', req => { return this.handleLogIn(req); }); - this.route("POST", "/login", req => { + this.route('POST', '/login', req => { return this.handleLogIn(req); }); - this.route("POST", "/loginAs", req => { + this.route('POST', '/loginAs', req => { return this.handleLogInAs(req); }); - this.route("POST", "/logout", req => { + this.route('POST', '/logout', req => { return this.handleLogOut(req); }); - this.route("POST", "/requestPasswordReset", req => { + this.route('POST', '/requestPasswordReset', req => { return this.handleResetRequest(req); }); - this.route("POST", "/verificationEmailRequest", req => { + this.route('POST', '/verificationEmailRequest', req => { return this.handleVerificationEmailRequest(req); }); - this.route("GET", "/verifyPassword", req => { + this.route('GET', '/verifyPassword', req => { return this.handleVerifyPassword(req); }); - this.route("POST", "/verifyPassword", req => { + this.route('POST', '/verifyPassword', req => { return this.handleVerifyPassword(req); }); - this.route("POST", "/challenge", req => { + this.route('POST', '/challenge', req => { return this.handleChallenge(req); }); } diff --git a/src/SchemaMigrations/DefinedSchemas.js b/src/SchemaMigrations/DefinedSchemas.js index 11fe31a9ef..4c5fd94211 100644 --- a/src/SchemaMigrations/DefinedSchemas.js +++ b/src/SchemaMigrations/DefinedSchemas.js @@ -1,17 +1,17 @@ // @flow // @flow-disable-next Cannot resolve module `parse/node`. -const Parse = require("parse/node"); -import { logger } from "../logger"; -import Config from "../Config"; +const Parse = require('parse/node'); +import { logger } from '../logger'; +import Config from '../Config'; import { internalCreateSchema, internalUpdateSchema, -} from "../Routers/SchemasRouter"; -import { defaultColumns, systemClasses } from "../Controllers/SchemaController"; -import { ParseServerOptions } from "../Options"; -import * as Migrations from "./Migrations"; -import Auth from "../Auth"; -import rest from "../rest"; +} from '../Routers/SchemasRouter'; +import { defaultColumns, systemClasses } from '../Controllers/SchemaController'; +import { ParseServerOptions } from '../Options'; +import * as Migrations from './Migrations'; +import Auth from '../Auth'; +import rest from '../rest'; export class DefinedSchemas { config: ParseServerOptions; @@ -72,7 +72,7 @@ export class DefinedSchemas { async execute() { try { - logger.info("Running Migrations"); + logger.info('Running Migrations'); if (this.schemaOptions && this.schemaOptions.beforeMigration) { await Promise.resolve(this.schemaOptions.beforeMigration()); } @@ -83,10 +83,10 @@ export class DefinedSchemas { await Promise.resolve(this.schemaOptions.afterMigration()); } - logger.info("Running Migrations Completed"); + logger.info('Running Migrations Completed'); } catch (e) { logger.error(`Failed to run migrations: ${e}`); - if (process.env.NODE_ENV === "production") { + if (process.env.NODE_ENV === 'production') { process.exit(1); } } @@ -99,10 +99,10 @@ export class DefinedSchemas { // if we fail to get schema // pm2 or K8s and many other process managers will try to restart the process // after the exit - if (process.env.NODE_ENV === "production") { + if (process.env.NODE_ENV === 'production') { timeout = setTimeout(() => { logger.error( - "Timeout occurred during execution of migrations. Exiting..." + 'Timeout occurred during execution of migrations. Exiting...' ); process.exit(1); }, 20000); @@ -134,7 +134,7 @@ export class DefinedSchemas { await this.executeMigrations(); } else { logger.error(`Failed to run migrations: ${e}`); - if (process.env.NODE_ENV === "production") { + if (process.env.NODE_ENV === 'production') { process.exit(1); } } @@ -197,13 +197,13 @@ export class DefinedSchemas { const { response } = await rest.create( this.config, Auth.master(this.config), - "_Session", + '_Session', {} ); await rest.del( this.config, Auth.master(this.config), - "_Session", + '_Session', response.objectId ); } @@ -356,10 +356,10 @@ export class DefinedSchemas { fieldsToRecreate.forEach(field => { const from = field.from.type + - (field.from.targetClass ? ` (${field.from.targetClass})` : ""); + (field.from.targetClass ? ` (${field.from.targetClass})` : ''); const to = field.to.type + - (field.to.targetClass ? ` (${field.to.targetClass})` : ""); + (field.to.targetClass ? ` (${field.to.targetClass})` : ''); logger.warn( `The field "${field.fieldName}" type differ between the schema and the database for "${localSchema.className}"; Schema is defined as "${to}" and current database type is "${from}"` @@ -421,7 +421,7 @@ export class DefinedSchemas { // Apply new/changed indexes if (indexesToAdd.length) { logger.debug( - `Updating indexes for "${newLocalSchema.className}" : ${indexesToAdd.join(" ,")}` + `Updating indexes for "${newLocalSchema.className}" : ${indexesToAdd.join(' ,')}` ); indexesToAdd.forEach(o => newLocalSchema.addIndex(o.indexName, o.index)); await this.updateSchemaToDB(newLocalSchema); @@ -455,22 +455,22 @@ export class DefinedSchemas { } isProtectedIndex(className: string, indexName: string) { - const indexes = ["_id_"]; + const indexes = ['_id_']; switch (className) { - case "_User": + case '_User': indexes.push( - "case_insensitive_username", - "case_insensitive_email", - "username_1", - "email_1" + 'case_insensitive_username', + 'case_insensitive_email', + 'username_1', + 'email_1' ); break; - case "_Role": - indexes.push("name_1"); + case '_Role': + indexes.push('name_1'); break; - case "_Idempotency": - indexes.push("reqId_1"); + case '_Idempotency': + indexes.push('reqId_1'); break; } @@ -493,9 +493,9 @@ export class DefinedSchemas { fieldName: string, field: Migrations.FieldType ) { - if (field.type === "Relation") { + if (field.type === 'Relation') { newLocalSchema.addRelation(fieldName, field.targetClass); - } else if (field.type === "Pointer") { + } else if (field.type === 'Pointer') { newLocalSchema.addPointer(fieldName, field.targetClass, field); } else { newLocalSchema.addField(fieldName, field.type, field); diff --git a/src/SchemaMigrations/Migrations.js b/src/SchemaMigrations/Migrations.js index fe358d5059..114dc27338 100644 --- a/src/SchemaMigrations/Migrations.js +++ b/src/SchemaMigrations/Migrations.js @@ -11,18 +11,18 @@ export interface SchemaOptions { } export type FieldValueType = - | "String" - | "Boolean" - | "File" - | "Number" - | "Relation" - | "Pointer" - | "Date" - | "GeoPoint" - | "Polygon" - | "Array" - | "Object" - | "ACL"; + | 'String' + | 'Boolean' + | 'File' + | 'Number' + | 'Relation' + | 'Pointer' + | 'Date' + | 'GeoPoint' + | 'Polygon' + | 'Array' + | 'Object' + | 'ACL'; export interface FieldType { type: FieldValueType; @@ -31,7 +31,7 @@ export interface FieldType { targetClass?: string; } -type ClassNameType = "_User" | "_Role" | string; +type ClassNameType = '_User' | '_Role' | string; export interface ProtectedFieldsInterface { [key: string]: string[]; @@ -46,12 +46,12 @@ export interface IndexesInterface { } export type CLPOperation = - | "find" - | "count" - | "get" - | "update" - | "create" - | "delete"; + | 'find' + | 'count' + | 'get' + | 'update' + | 'create' + | 'delete'; // @Typescript 4.1+ // type CLPPermission = 'requiresAuthentication' | '*' | `user:${string}` | `role:${string}` type CLPValue = { [key: string]: boolean }; diff --git a/src/Security/Check.js b/src/Security/Check.js index acbcd486e7..c31480e73d 100644 --- a/src/Security/Check.js +++ b/src/Security/Check.js @@ -2,8 +2,8 @@ * @module SecurityCheck */ -import Utils from "../Utils"; -import { isFunction, isString } from "lodash"; +import Utils from '../Utils'; +import { isFunction, isString } from 'lodash'; /** * A security check. @@ -60,11 +60,11 @@ class Check { */ _validateParams(params) { Utils.validateParams(params, { - group: { t: "string", v: isString }, - title: { t: "string", v: isString }, - warning: { t: "string", v: isString }, - solution: { t: "string", v: isString }, - check: { t: "function", v: isFunction }, + group: { t: 'string', v: isString }, + title: { t: 'string', v: isString }, + warning: { t: 'string', v: isString }, + solution: { t: 'string', v: isString }, + check: { t: 'function', v: isFunction }, }); } } @@ -73,9 +73,9 @@ class Check { * The check state. */ const CheckState = Object.freeze({ - none: "none", - fail: "fail", - success: "success", + none: 'none', + fail: 'fail', + success: 'success', }); export default Check; diff --git a/src/Security/CheckRunner.js b/src/Security/CheckRunner.js index 0e5cb22950..86d38e4c10 100644 --- a/src/Security/CheckRunner.js +++ b/src/Security/CheckRunner.js @@ -1,8 +1,8 @@ -import Utils from "../Utils"; -import { CheckState } from "./Check"; -import * as CheckGroups from "./CheckGroups/CheckGroups"; -import logger from "../logger"; -import { isArray, isBoolean } from "lodash"; +import Utils from '../Utils'; +import { CheckState } from './Check'; +import * as CheckGroups from './CheckGroups/CheckGroups'; +import logger from '../logger'; +import { isArray, isBoolean } from 'lodash'; /** * The security check runner. @@ -33,10 +33,10 @@ class CheckRunner { * @params * @returns {Object} The security check report. */ - async run({ version = "1.0.0" } = {}) { + async run({ version = '1.0.0' } = {}) { // Instantiate check groups const groups = Object.values(this.checkGroups) - .filter(c => typeof c === "function") + .filter(c => typeof c === 'function') .map(CheckGroup => new CheckGroup()); // Run checks @@ -92,7 +92,7 @@ class CheckRunner { // Identify report version switch (version) { - case "1.0.0": + case '1.0.0': default: // For each check group for (const group of groups) { @@ -136,8 +136,8 @@ class CheckRunner { : s => logger.warn(s); // Declare output - const indent = " "; - let output = ""; + const indent = ' '; + let output = ''; let checksCount = 0; let failedChecksCount = 0; let skippedCheckCount = 0; @@ -169,8 +169,8 @@ class CheckRunner { `\n###################################` + `\n` + `\n${ - failedChecksCount > 0 ? "Warning: " : "" - }${failedChecksCount} weak security setting(s) found${failedChecksCount > 0 ? "!" : ""}` + + failedChecksCount > 0 ? 'Warning: ' : '' + }${failedChecksCount} weak security setting(s) found${failedChecksCount > 0 ? '!' : ''}` + `\n${checksCount} check(s) executed` + `\n${skippedCheckCount} check(s) skipped` + `\n` + @@ -188,11 +188,11 @@ class CheckRunner { _getLogIconForState(state) { switch (state) { case CheckState.success: - return "✅"; + return '✅'; case CheckState.fail: - return "❌"; + return '❌'; default: - return "ℹ️"; + return 'ℹ️'; } } @@ -202,9 +202,9 @@ class CheckRunner { */ _validateParams(params) { Utils.validateParams(params, { - enableCheck: { t: "boolean", v: isBoolean, o: true }, - enableCheckLog: { t: "boolean", v: isBoolean, o: true }, - checkGroups: { t: "array", v: isArray, o: true }, + enableCheck: { t: 'boolean', v: isBoolean, o: true }, + enableCheckLog: { t: 'boolean', v: isBoolean, o: true }, + checkGroups: { t: 'array', v: isArray, o: true }, }); } } diff --git a/src/SharedRest.js b/src/SharedRest.js index 19b3871f23..da6550c2dd 100644 --- a/src/SharedRest.js +++ b/src/SharedRest.js @@ -1,15 +1,15 @@ const classesWithMasterOnlyAccess = [ - "_JobStatus", - "_PushStatus", - "_Hooks", - "_GlobalConfig", - "_JobSchedule", - "_Idempotency", + '_JobStatus', + '_PushStatus', + '_Hooks', + '_GlobalConfig', + '_JobSchedule', + '_Idempotency', ]; // Disallowing access to the _Role collection except by master key function enforceRoleSecurity(method, className, auth) { - if (className === "_Installation" && !auth.isMaster && !auth.isMaintenance) { - if (method === "delete" || method === "find") { + if (className === '_Installation' && !auth.isMaster && !auth.isMaintenance) { + if (method === 'delete' || method === 'find') { const error = `Clients aren't allowed to perform the ${method} operation on the installation collection.`; throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, error); } @@ -28,7 +28,7 @@ function enforceRoleSecurity(method, className, auth) { // readOnly masterKey is not allowed if ( auth.isReadOnly && - (method === "delete" || method === "create" || method === "update") + (method === 'delete' || method === 'create' || method === 'update') ) { const error = `read-only masterKey isn't allowed to perform the ${method} operation.`; throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, error); diff --git a/src/StatusHandler.js b/src/StatusHandler.js index d2ee7606f1..0b8468f3d3 100644 --- a/src/StatusHandler.js +++ b/src/StatusHandler.js @@ -1,18 +1,18 @@ -import { md5Hash, newObjectId } from "./cryptoUtils"; -import { KeyPromiseQueue } from "./KeyPromiseQueue"; -import { logger } from "./logger"; -import rest from "./rest"; -import Auth from "./Auth"; +import { md5Hash, newObjectId } from './cryptoUtils'; +import { KeyPromiseQueue } from './KeyPromiseQueue'; +import { logger } from './logger'; +import rest from './rest'; +import Auth from './Auth'; -const PUSH_STATUS_COLLECTION = "_PushStatus"; -const JOB_STATUS_COLLECTION = "_JobStatus"; +const PUSH_STATUS_COLLECTION = '_PushStatus'; +const JOB_STATUS_COLLECTION = '_JobStatus'; const pushPromiseQueue = new KeyPromiseQueue(); const jobPromiseQueue = new KeyPromiseQueue(); const incrementOp = function (object = {}, key, amount = 1) { if (!object[key]) { - object[key] = { __op: "Increment", amount: amount }; + object[key] = { __op: 'Increment', amount: amount }; } else { object[key].amount += amount; } @@ -84,8 +84,8 @@ export function jobStatusHandler(config) { jobStatus = { objectId, jobName, - status: "running", - source: "api", + status: 'running', + source: 'api', createdAt: now, // lockdown! ACL: {}, @@ -95,27 +95,27 @@ export function jobStatusHandler(config) { }; const setMessage = function (message) { - if (!message || typeof message !== "string") { + if (!message || typeof message !== 'string') { return Promise.resolve(); } return handler.update({ objectId }, { message }); }; const setSucceeded = function (message) { - return setFinalStatus("succeeded", message); + return setFinalStatus('succeeded', message); }; const setFailed = function (message) { - return setFinalStatus("failed", message); + return setFinalStatus('failed', message); }; const setFinalStatus = function (status, message = undefined) { const finishedAt = new Date(); const update = { status, finishedAt }; - if (message && typeof message === "string") { + if (message && typeof message === 'string') { update.message = message; } - if (message instanceof Error && typeof message.message === "string") { + if (message instanceof Error && typeof message.message === 'string') { update.message = message.message; } return handler.update({ objectId }, update); @@ -134,31 +134,31 @@ export function pushStatusHandler(config, existingObjectId) { const database = config.database; const handler = restStatusHandler(PUSH_STATUS_COLLECTION, config); let objectId = existingObjectId; - const setInitial = function (body = {}, where, options = { source: "rest" }) { + const setInitial = function (body = {}, where, options = { source: 'rest' }) { const now = new Date(); let pushTime = now.toISOString(); - let status = "pending"; - if (Object.prototype.hasOwnProperty.call(body, "push_time")) { + let status = 'pending'; + if (Object.prototype.hasOwnProperty.call(body, 'push_time')) { if (config.hasPushScheduledSupport) { pushTime = body.push_time; - status = "scheduled"; + status = 'scheduled'; } else { logger.warn( - "Trying to schedule a push while server is not configured." + 'Trying to schedule a push while server is not configured.' ); - logger.warn("Push will be sent immediately"); + logger.warn('Push will be sent immediately'); } } const data = body.data || {}; const payloadString = JSON.stringify(data); let pushHash; - if (typeof data.alert === "string") { + if (typeof data.alert === 'string') { pushHash = md5Hash(data.alert); - } else if (typeof data.alert === "object") { + } else if (typeof data.alert === 'object') { pushHash = md5Hash(JSON.stringify(data.alert)); } else { - pushHash = "d41d8cd98f00b204e9800998ecf8427e"; + pushHash = 'd41d8cd98f00b204e9800998ecf8427e'; } const object = { pushTime, @@ -190,11 +190,11 @@ export function pushStatusHandler(config, existingObjectId) { ); return handler.update( { - status: "pending", + status: 'pending', objectId: objectId, }, { - status: "running", + status: 'running', count: batches, } ); @@ -223,7 +223,7 @@ export function pushStatusHandler(config, existingObjectId) { ? `sentPerType.${deviceType}` : `failedPerType.${deviceType}`; memo[key] = incrementOp(memo, key); - if (typeof UTCOffset !== "undefined") { + if (typeof UTCOffset !== 'undefined') { const offsetKey = result.transmitted ? `sentPerUTCOffset.${UTCOffset}` : `failedPerUTCOffset.${UTCOffset}`; @@ -243,24 +243,24 @@ export function pushStatusHandler(config, existingObjectId) { const error = result.response.error; // GCM / FCM HTTP v1 API errors; see: // https://firebase.google.com/docs/reference/fcm/rest/v1/ErrorCode - if (error === "NotRegistered" || error === "InvalidRegistration") { + if (error === 'NotRegistered' || error === 'InvalidRegistration') { devicesToRemove.push(token); } // FCM API v2 errors; see: // https://firebase.google.com/docs/cloud-messaging/manage-tokens // https://github.com/firebase/functions-samples/blob/703c0359eacf07a551751d1319d34f912a2cd828/Node/fcm-notifications/functions/index.js#L89-L93C16 if ( - error?.code === "messaging/registration-token-not-registered" || - error?.code === "messaging/invalid-registration-token" || - (error?.code === "messaging/invalid-argument" && + error?.code === 'messaging/registration-token-not-registered' || + error?.code === 'messaging/invalid-registration-token' || + (error?.code === 'messaging/invalid-argument' && error?.message === - "The registration token is not a valid FCM registration token") + 'The registration token is not a valid FCM registration token') ) { devicesToRemove.push(token); } // APNS errors; see: // https://developer.apple.com/documentation/usernotifications/handling-notification-responses-from-apns - if (error === "Unregistered" || error === "BadDeviceToken") { + if (error === 'Unregistered' || error === 'BadDeviceToken') { devicesToRemove.push(token); } } @@ -278,10 +278,10 @@ export function pushStatusHandler(config, existingObjectId) { logger.verbose(`_PushStatus ${objectId}: needs cleanup`, { devicesToRemove, }); - ["numSent", "numFailed"].forEach(key => { + ['numSent', 'numFailed'].forEach(key => { if (update[key] > 0) { update[key] = { - __op: "Increment", + __op: 'Increment', amount: update[key], }; } else { @@ -294,17 +294,17 @@ export function pushStatusHandler(config, existingObjectId) { `Removing device tokens on ${devicesToRemove.length} _Installations` ); database.update( - "_Installation", + '_Installation', { deviceToken: { $in: devicesToRemove } }, - { deviceToken: { __op: "Delete" } }, + { deviceToken: { __op: 'Delete' } }, { acl: undefined, many: true, } ); } - incrementOp(update, "count", -1); - update.status = "running"; + incrementOp(update, 'count', -1); + update.status = 'running'; return handler.update({ objectId }, update).then(res => { if (res && res.count === 0) { @@ -317,19 +317,19 @@ export function pushStatusHandler(config, existingObjectId) { return handler.update( { objectId }, { - status: "succeeded", - count: { __op: "Delete" }, + status: 'succeeded', + count: { __op: 'Delete' }, } ); }; const fail = function (err) { - if (typeof err === "string") { + if (typeof err === 'string') { err = { message: err }; } const update = { errorMessage: err, - status: "failed", + status: 'failed', }; return handler.update({ objectId }, update); }; @@ -343,7 +343,7 @@ export function pushStatusHandler(config, existingObjectId) { }; // define objectId to be dynamic - Object.defineProperty(rval, "objectId", { + Object.defineProperty(rval, 'objectId', { get: () => objectId, }); diff --git a/src/TestUtils.js b/src/TestUtils.js index 29713639a2..d5f2494b2f 100644 --- a/src/TestUtils.js +++ b/src/TestUtils.js @@ -1,5 +1,5 @@ -import AppCache from "./cache"; -import SchemaCache from "./Adapters/Cache/SchemaCache"; +import AppCache from './cache'; +import SchemaCache from './Adapters/Cache/SchemaCache'; /** * Destroys all data in the database @@ -7,7 +7,7 @@ import SchemaCache from "./Adapters/Cache/SchemaCache"; */ export function destroyAllDataPermanently(fast) { if (!process.env.TESTING) { - throw "Only supported in test environment"; + throw 'Only supported in test environment'; } return Promise.all( Object.keys(AppCache.cache).map(appId => { @@ -62,9 +62,9 @@ export class Connections { } track(server) { - server.on("connection", socket => { + server.on('connection', socket => { this.sockets.add(socket); - socket.on("close", () => { + socket.on('close', () => { this.sockets.delete(socket); }); }); diff --git a/src/Utils.js b/src/Utils.js index acd03fa068..46194c33d1 100644 --- a/src/Utils.js +++ b/src/Utils.js @@ -4,8 +4,8 @@ * @description General purpose utilities. */ -const path = require("path"); -const fs = require("fs").promises; +const path = require('path'); +const fs = require('fs').promises; /** * The general purpose utilities. @@ -59,7 +59,7 @@ class Utils { } // Check file for language exists - const language = locale.split("-")[0]; + const language = locale.split('-')[0]; const languagePath = path.join(basePath, language, file); const languageFileExists = await Utils.fileExists(languagePath); @@ -104,12 +104,12 @@ class Utils { * @param {Object} result * @returns {Object} The flattened object. **/ - static flattenObject(obj, parentKey, delimiter = ".", result = {}) { + static flattenObject(obj, parentKey, delimiter = '.', result = {}) { for (const key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { const newKey = parentKey ? parentKey + delimiter + key : key; - if (typeof obj[key] === "object" && obj[key] !== null) { + if (typeof obj[key] === 'object' && obj[key] !== null) { this.flattenObject(obj[key], newKey, delimiter, result); } else { result[newKey] = obj[key]; @@ -214,24 +214,24 @@ class Utils { **/ static relativeTimeToDate(text, now = new Date()) { text = text.toLowerCase(); - let parts = text.split(" "); + let parts = text.split(' '); // Filter out whitespace - parts = parts.filter(part => part !== ""); + parts = parts.filter(part => part !== ''); - const future = parts[0] === "in"; - const past = parts[parts.length - 1] === "ago"; + const future = parts[0] === 'in'; + const past = parts[parts.length - 1] === 'ago'; - if (!future && !past && text !== "now") { + if (!future && !past && text !== 'now') { return { - status: "error", + status: 'error', info: "Time should either start with 'in' or end with 'ago'", }; } if (future && past) { return { - status: "error", + status: 'error', info: "Time cannot have both 'in' and 'ago'", }; } @@ -244,10 +244,10 @@ class Utils { parts = parts.slice(0, parts.length - 1); } - if (parts.length % 2 !== 0 && text !== "now") { + if (parts.length % 2 !== 0 && text !== 'now') { return { - status: "error", - info: "Invalid time string. Dangling unit or number.", + status: 'error', + info: 'Invalid time string. Dangling unit or number.', }; } @@ -261,56 +261,56 @@ class Utils { const val = Number(num); if (!Number.isInteger(val)) { return { - status: "error", + status: 'error', info: `'${num}' is not an integer.`, }; } switch (interval) { - case "yr": - case "yrs": - case "year": - case "years": + case 'yr': + case 'yrs': + case 'year': + case 'years': seconds += val * 31536000; // 365 * 24 * 60 * 60 break; - case "wk": - case "wks": - case "week": - case "weeks": + case 'wk': + case 'wks': + case 'week': + case 'weeks': seconds += val * 604800; // 7 * 24 * 60 * 60 break; - case "d": - case "day": - case "days": + case 'd': + case 'day': + case 'days': seconds += val * 86400; // 24 * 60 * 60 break; - case "hr": - case "hrs": - case "hour": - case "hours": + case 'hr': + case 'hrs': + case 'hour': + case 'hours': seconds += val * 3600; // 60 * 60 break; - case "min": - case "mins": - case "minute": - case "minutes": + case 'min': + case 'mins': + case 'minute': + case 'minutes': seconds += val * 60; break; - case "sec": - case "secs": - case "second": - case "seconds": + case 'sec': + case 'secs': + case 'second': + case 'seconds': seconds += val; break; default: return { - status: "error", + status: 'error', info: `Invalid interval: '${interval}'`, }; } @@ -319,20 +319,20 @@ class Utils { const milliseconds = seconds * 1000; if (future) { return { - status: "success", - info: "future", + status: 'success', + info: 'future', result: new Date(now.valueOf() + milliseconds), }; } else if (past) { return { - status: "success", - info: "past", + status: 'success', + info: 'past', result: new Date(now.valueOf() - milliseconds), }; } else { return { - status: "success", - info: "present", + status: 'success', + info: 'present', result: new Date(now.valueOf()), }; } @@ -347,7 +347,7 @@ class Utils { */ static objectContainsKeyValue(obj, key, value) { const isMatch = (a, b) => - (typeof a === "string" && new RegExp(b).test(a)) || a === b; + (typeof a === 'string' && new RegExp(b).test(a)) || a === b; const isKeyMatch = k => isMatch(k, key); const isValueMatch = v => isMatch(v, value); for (const [k, v] of Object.entries(obj)) { @@ -364,7 +364,7 @@ class Utils { return true; } if ( - ["[object Object]", "[object Array]"].includes( + ['[object Object]', '[object Array]'].includes( Object.prototype.toString.call(v) ) ) { @@ -410,7 +410,7 @@ class Utils { * // Output: { a: 1, e: 4, c: 2, d: 3 } */ static addNestedKeysToRoot(obj, key) { - if (obj[key] && typeof obj[key] === "object") { + if (obj[key] && typeof obj[key] === 'object') { // Add nested keys to root Object.assign(obj, { ...obj[key] }); // Delete original nested key @@ -427,7 +427,7 @@ class Utils { static encodeForUrl(input) { return encodeURIComponent(input).replace( /[!'.()*]/g, - char => "%" + char.charCodeAt(0).toString(16).toUpperCase() + char => '%' + char.charCodeAt(0).toString(16).toUpperCase() ); } } diff --git a/src/batch.js b/src/batch.js index 5525348f30..459e9e9f11 100644 --- a/src/batch.js +++ b/src/batch.js @@ -1,11 +1,11 @@ -const Parse = require("parse/node").Parse; -const path = require("path"); +const Parse = require('parse/node').Parse; +const path = require('path'); // These methods handle batch requests. -const batchPath = "/batch"; +const batchPath = '/batch'; // Mounts a batch-handler onto a PromiseRouter. function mountOnto(router) { - router.route("POST", batchPath, req => { + router.route('POST', batchPath, req => { return handleBatch(router, req); }); } @@ -30,10 +30,10 @@ function makeBatchRoutingPathFunction(originalUrl, serverURL, publicServerURL) { if (requestPath.slice(0, apiPrefix.length) != apiPrefix) { throw new Parse.Error( Parse.Error.INVALID_JSON, - "cannot route batch path " + requestPath + 'cannot route batch path ' + requestPath ); } - return path.posix.join("/", requestPath.slice(apiPrefix.length)); + return path.posix.join('/', requestPath.slice(apiPrefix.length)); }; if ( @@ -59,9 +59,9 @@ function makeBatchRoutingPathFunction(originalUrl, serverURL, publicServerURL) { : publicPath.length; const newPath = path.posix.join( - "/", + '/', localPath, - "/", + '/', requestPath.slice(pathLengthToUse) ); @@ -79,7 +79,7 @@ function handleBatch(router, req) { if (!Array.isArray(req.body?.requests)) { throw new Parse.Error( Parse.Error.INVALID_JSON, - "requests must be an array" + 'requests must be an array' ); } @@ -89,7 +89,7 @@ function handleBatch(router, req) { // we need to figure out the API prefix, so that we can strip it // from all the subrequests. if (!req.originalUrl.endsWith(batchPath)) { - throw "internal routing problem - expected url to end with batch"; + throw 'internal routing problem - expected url to end with batch'; } const makeRoutablePath = makeBatchRoutingPathFunction( @@ -131,7 +131,7 @@ function handleBatch(router, req) { return Promise.all(promises) .then(results => { if (req.body?.transaction === true) { - if (results.find(result => typeof result.error === "object")) { + if (results.find(result => typeof result.error === 'object')) { return req.config.database .abortTransactionalSession() .then(() => { @@ -154,7 +154,7 @@ function handleBatch(router, req) { error.response && error.response.find( errorItem => - typeof errorItem.error === "object" && + typeof errorItem.error === 'object' && errorItem.error.code === 251 ) && transactionRetries > 0 diff --git a/src/cache.js b/src/cache.js index 7c7400e37b..71c01f4a14 100644 --- a/src/cache.js +++ b/src/cache.js @@ -1,4 +1,4 @@ -import { InMemoryCache } from "./Adapters/Cache/InMemoryCache"; +import { InMemoryCache } from './Adapters/Cache/InMemoryCache'; export var AppCache = new InMemoryCache({ ttl: NaN }); export default AppCache; diff --git a/src/cli/parse-live-query-server.js b/src/cli/parse-live-query-server.js index edb2443ece..525a202a26 100644 --- a/src/cli/parse-live-query-server.js +++ b/src/cli/parse-live-query-server.js @@ -1,6 +1,6 @@ -import definitions from "./definitions/parse-live-query-server"; -import runner from "./utils/runner"; -import { ParseServer } from "../index"; +import definitions from './definitions/parse-live-query-server'; +import runner from './utils/runner'; +import { ParseServer } from '../index'; runner({ definitions, diff --git a/src/cli/parse-server.js b/src/cli/parse-server.js index 0b20d23f30..00ed5a2a34 100755 --- a/src/cli/parse-server.js +++ b/src/cli/parse-server.js @@ -1,74 +1,74 @@ /* eslint-disable no-console */ -import ParseServer from "../index"; -import definitions from "./definitions/parse-server"; -import cluster from "cluster"; -import os from "os"; -import runner from "./utils/runner"; +import ParseServer from '../index'; +import definitions from './definitions/parse-server'; +import cluster from 'cluster'; +import os from 'os'; +import runner from './utils/runner'; const help = function () { - console.log(" Get Started guide:"); - console.log(""); - console.log(" Please have a look at the get started guide!"); - console.log(" http://docs.parseplatform.org/parse-server/guide/"); - console.log(""); - console.log(""); - console.log(" Usage with npm start"); - console.log(""); - console.log(" $ npm start -- path/to/config.json"); + console.log(' Get Started guide:'); + console.log(''); + console.log(' Please have a look at the get started guide!'); + console.log(' http://docs.parseplatform.org/parse-server/guide/'); + console.log(''); + console.log(''); + console.log(' Usage with npm start'); + console.log(''); + console.log(' $ npm start -- path/to/config.json'); console.log( - " $ npm start -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL" + ' $ npm start -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL' ); console.log( - " $ npm start -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL" + ' $ npm start -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL' ); - console.log(""); - console.log(""); - console.log(" Usage:"); - console.log(""); - console.log(" $ parse-server path/to/config.json"); + console.log(''); + console.log(''); + console.log(' Usage:'); + console.log(''); + console.log(' $ parse-server path/to/config.json'); console.log( - " $ parse-server -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL" + ' $ parse-server -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL' ); console.log( - " $ parse-server -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL" + ' $ parse-server -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL' ); - console.log(""); + console.log(''); }; runner({ definitions, help, - usage: "[options] ", + usage: '[options] ', start: function (program, options, logOptions) { if (!options.appId || !options.masterKey) { program.outputHelp(); - console.error(""); + console.error(''); console.error( - "\u001b[31mERROR: appId and masterKey are required\u001b[0m" + '\u001b[31mERROR: appId and masterKey are required\u001b[0m' ); - console.error(""); + console.error(''); process.exit(1); } - if (options["liveQuery.classNames"]) { + if (options['liveQuery.classNames']) { options.liveQuery = options.liveQuery || {}; - options.liveQuery.classNames = options["liveQuery.classNames"]; - delete options["liveQuery.classNames"]; + options.liveQuery.classNames = options['liveQuery.classNames']; + delete options['liveQuery.classNames']; } - if (options["liveQuery.redisURL"]) { + if (options['liveQuery.redisURL']) { options.liveQuery = options.liveQuery || {}; - options.liveQuery.redisURL = options["liveQuery.redisURL"]; - delete options["liveQuery.redisURL"]; + options.liveQuery.redisURL = options['liveQuery.redisURL']; + delete options['liveQuery.redisURL']; } - if (options["liveQuery.redisOptions"]) { + if (options['liveQuery.redisOptions']) { options.liveQuery = options.liveQuery || {}; - options.liveQuery.redisOptions = options["liveQuery.redisOptions"]; - delete options["liveQuery.redisOptions"]; + options.liveQuery.redisOptions = options['liveQuery.redisOptions']; + delete options['liveQuery.redisOptions']; } if (options.cluster) { const numCPUs = - typeof options.cluster === "number" + typeof options.cluster === 'number' ? options.cluster : os.cpus().length; if (cluster.isMaster) { @@ -76,7 +76,7 @@ runner({ for (let i = 0; i < numCPUs; i++) { cluster.fork(); } - cluster.on("exit", (worker, code) => { + cluster.on('exit', (worker, code) => { console.log( `worker ${worker.process.pid} died (${code})... Restarting` ); @@ -96,7 +96,7 @@ runner({ ParseServer.startApp(options) .then(() => { logOptions(); - console.log(""); + console.log(''); printSuccessMessage(); }) .catch(e => { @@ -107,22 +107,22 @@ runner({ function printSuccessMessage() { console.log( - "[" + process.pid + "] parse-server running on " + options.serverURL + '[' + process.pid + '] parse-server running on ' + options.serverURL ); if (options.mountGraphQL) { console.log( - "[" + + '[' + process.pid + - "] GraphQL running on http://localhost:" + + '] GraphQL running on http://localhost:' + options.port + options.graphQLPath ); } if (options.mountPlayground) { console.log( - "[" + + '[' + process.pid + - "] Playground running on http://localhost:" + + '] Playground running on http://localhost:' + options.port + options.playgroundPath ); diff --git a/src/cloud-code/Parse.Cloud.js b/src/cloud-code/Parse.Cloud.js index 11250bbb24..412510b6f6 100644 --- a/src/cloud-code/Parse.Cloud.js +++ b/src/cloud-code/Parse.Cloud.js @@ -1,31 +1,31 @@ -import { Parse } from "parse/node"; -import * as triggers from "../triggers"; -import { addRateLimit } from "../middlewares"; -const Config = require("../Config"); +import { Parse } from 'parse/node'; +import * as triggers from '../triggers'; +import { addRateLimit } from '../middlewares'; +const Config = require('../Config'); function isParseObjectConstructor(object) { return ( - typeof object === "function" && - Object.prototype.hasOwnProperty.call(object, "className") + typeof object === 'function' && + Object.prototype.hasOwnProperty.call(object, 'className') ); } function validateValidator(validator) { - if (!validator || typeof validator === "function") { + if (!validator || typeof validator === 'function') { return; } const fieldOptions = { - type: ["Any"], + type: ['Any'], constant: [Boolean], - default: ["Any"], - options: [Array, "function", "Any"], + default: ['Any'], + options: [Array, 'function', 'Any'], required: [Boolean], error: [String], }; const allowedKeys = { requireUser: [Boolean], - requireAnyUserRoles: [Array, "function"], - requireAllUserRoles: [Array, "function"], + requireAnyUserRoles: [Array, 'function'], + requireAllUserRoles: [Array, 'function'], requireMaster: [Boolean], validateMasterKey: [Boolean], skipWithMasterKey: [Boolean], @@ -35,15 +35,15 @@ function validateValidator(validator) { }; const getType = fn => { if (Array.isArray(fn)) { - return "array"; + return 'array'; } - if (fn === "Any" || fn === "function") { + if (fn === 'Any' || fn === 'function') { return fn; } const type = typeof fn; - if (typeof fn === "function") { + if (typeof fn === 'function') { const match = fn && fn.toString().match(/^\s*function (\w+)/); - return (match ? match[1] : "function").toLowerCase(); + return (match ? match[1] : 'function').toLowerCase(); } return type; }; @@ -54,15 +54,15 @@ function validateValidator(validator) { } const types = parameter.map(type => getType(type)); const type = getType(validatorParam); - if (!types.includes(type) && !types.includes("Any")) { + if (!types.includes(type) && !types.includes('Any')) { throw `Invalid type for Cloud Function validation key ${key}. Expected ${types.join( - "|" + '|' )}, actual ${type}`; } }; for (const key in validator) { checkKey(key, allowedKeys, validator[key]); - if (key === "fields" || key === "requireUserKeys") { + if (key === 'fields' || key === 'requireUserKeys') { const values = validator[key]; if (Array.isArray(values)) { continue; @@ -79,15 +79,15 @@ function validateValidator(validator) { const getRoute = parseClass => { const route = { - _User: "users", - _Session: "sessions", - "@File": "files", - "@Config": "config", - }[parseClass] || "classes"; - if (parseClass === "@File") { + _User: 'users', + _Session: 'sessions', + '@File': 'files', + '@Config': 'config', + }[parseClass] || 'classes'; + if (parseClass === '@File') { return `/${route}/:id?(.*)`; } - if (parseClass === "@Config") { + if (parseClass === '@Config') { return `/${route}`; } return `/${route}/${parseClass}/:id?(.*)`; @@ -203,7 +203,7 @@ ParseCloud.beforeSave = function (parseClass, handler, validationHandler) { addRateLimit( { requestPath: getRoute(className), - requestMethods: ["POST", "PUT"], + requestMethods: ['POST', 'PUT'], ...validationHandler.rateLimit, }, Parse.applicationId, @@ -250,7 +250,7 @@ ParseCloud.beforeDelete = function (parseClass, handler, validationHandler) { addRateLimit( { requestPath: getRoute(className), - requestMethods: "DELETE", + requestMethods: 'DELETE', ...validationHandler.rateLimit, }, Parse.applicationId, @@ -283,8 +283,8 @@ ParseCloud.beforeDelete = function (parseClass, handler, validationHandler) { * @param {Function} func The function to run before a login. This function can be async and should take one parameter a {@link Parse.Cloud.TriggerRequest}; */ ParseCloud.beforeLogin = function (handler, validationHandler) { - let className = "_User"; - if (typeof handler === "string" || isParseObjectConstructor(handler)) { + let className = '_User'; + if (typeof handler === 'string' || isParseObjectConstructor(handler)) { // validation will occur downstream, this is to maintain internal // code consistency with the other hook types. className = triggers.getClassName(handler); @@ -301,7 +301,7 @@ ParseCloud.beforeLogin = function (handler, validationHandler) { addRateLimit( { requestPath: `/login`, - requestMethods: "POST", + requestMethods: 'POST', ...validationHandler.rateLimit, }, Parse.applicationId, @@ -330,8 +330,8 @@ ParseCloud.beforeLogin = function (handler, validationHandler) { * @param {Function} func The function to run after a login. This function can be async and should take one parameter a {@link Parse.Cloud.TriggerRequest}; */ ParseCloud.afterLogin = function (handler) { - let className = "_User"; - if (typeof handler === "string" || isParseObjectConstructor(handler)) { + let className = '_User'; + if (typeof handler === 'string' || isParseObjectConstructor(handler)) { // validation will occur downstream, this is to maintain internal // code consistency with the other hook types. className = triggers.getClassName(handler); @@ -364,8 +364,8 @@ ParseCloud.afterLogin = function (handler) { * @param {Function} func The function to run after a logout. This function can be async and should take one parameter a {@link Parse.Cloud.TriggerRequest}; */ ParseCloud.afterLogout = function (handler) { - let className = "_Session"; - if (typeof handler === "string" || isParseObjectConstructor(handler)) { + let className = '_Session'; + if (typeof handler === 'string' || isParseObjectConstructor(handler)) { // validation will occur downstream, this is to maintain internal // code consistency with the other hook types. className = triggers.getClassName(handler); @@ -490,7 +490,7 @@ ParseCloud.beforeFind = function (parseClass, handler, validationHandler) { addRateLimit( { requestPath: getRoute(className), - requestMethods: "GET", + requestMethods: 'GET', ...validationHandler.rateLimit, }, Parse.applicationId, @@ -591,7 +591,7 @@ ParseCloud.sendEmail = function (data) { const emailAdapter = config.userController.adapter; if (!emailAdapter) { config.loggerController.error( - "Failed to send email because no mail adapter is configured for Parse Server." + 'Failed to send email because no mail adapter is configured for Parse Server.' ); return; } @@ -686,7 +686,7 @@ ParseCloud._removeAllHooks = () => { ParseCloud.useMasterKey = () => { // eslint-disable-next-line console.warn( - "Parse.Cloud.useMasterKey is deprecated (and has no effect anymore) on parse-server, please refer to the cloud code migration notes: http://docs.parseplatform.org/parse-server/guide/#master-key-must-be-passed-explicitly" + 'Parse.Cloud.useMasterKey is deprecated (and has no effect anymore) on parse-server, please refer to the cloud code migration notes: http://docs.parseplatform.org/parse-server/guide/#master-key-must-be-passed-explicitly' ); }; diff --git a/src/cloud-code/Parse.Server.js b/src/cloud-code/Parse.Server.js index 607a3e850c..71295618f2 100644 --- a/src/cloud-code/Parse.Server.js +++ b/src/cloud-code/Parse.Server.js @@ -10,10 +10,10 @@ const ParseServer = {}; * ... */ ParseServer.RateLimitZone = Object.freeze({ - global: "global", - session: "session", - user: "user", - ip: "ip", + global: 'global', + session: 'session', + user: 'user', + ip: 'ip', }); module.exports = ParseServer; diff --git a/src/cryptoUtils.js b/src/cryptoUtils.js index bde97ceebd..1b0337ea45 100644 --- a/src/cryptoUtils.js +++ b/src/cryptoUtils.js @@ -1,16 +1,16 @@ /* @flow */ -import { randomBytes, createHash } from "crypto"; +import { randomBytes, createHash } from 'crypto'; // Returns a new random hex string of the given even size. export function randomHexString(size: number): string { if (size === 0) { - throw new Error("Zero-length randomHexString is useless."); + throw new Error('Zero-length randomHexString is useless.'); } if (size % 2 !== 0) { - throw new Error("randomHexString size must be divisible by 2."); + throw new Error('randomHexString size must be divisible by 2.'); } - return randomBytes(size / 2).toString("hex"); + return randomBytes(size / 2).toString('hex'); } // Returns a new random alphanumeric string of the given size. @@ -21,11 +21,11 @@ export function randomHexString(size: number): string { // length is long enough and doesn't need to be uniform. export function randomString(size: number): string { if (size === 0) { - throw new Error("Zero-length randomString is useless."); + throw new Error('Zero-length randomString is useless.'); } const chars = - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789"; - let objectId = ""; + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + 'abcdefghijklmnopqrstuvwxyz' + '0123456789'; + let objectId = ''; const bytes = randomBytes(size); for (let i = 0; i < bytes.length; ++i) { objectId += chars[bytes.readUInt8(i) % chars.length]; @@ -44,5 +44,5 @@ export function newToken(): string { } export function md5Hash(string: string): string { - return createHash("md5").update(string).digest("hex"); + return createHash('md5').update(string).digest('hex'); } diff --git a/src/defaults.js b/src/defaults.js index 7d8c21166a..ba603424a8 100644 --- a/src/defaults.js +++ b/src/defaults.js @@ -1,9 +1,9 @@ -import { nullParser } from "./Options/parsers"; -const { ParseServerOptions } = require("./Options/Definitions"); +import { nullParser } from './Options/parsers'; +const { ParseServerOptions } = require('./Options/Definitions'); const logsFolder = (() => { - let folder = "./logs/"; - if (typeof process !== "undefined" && process.env.TESTING === "1") { - folder = "./test_logs/"; + let folder = './logs/'; + if (typeof process !== 'undefined' && process.env.TESTING === '1') { + folder = './test_logs/'; } if (process.env.PARSE_SERVER_LOGS_FOLDER) { folder = nullParser(process.env.PARSE_SERVER_LOGS_FOLDER); @@ -13,13 +13,13 @@ const logsFolder = (() => { const { verbose, level } = (() => { const verbose = process.env.VERBOSE ? true : false; - return { verbose, level: verbose ? "verbose" : undefined }; + return { verbose, level: verbose ? 'verbose' : undefined }; })(); const DefinitionDefaults = Object.keys(ParseServerOptions).reduce( (memo, key) => { const def = ParseServerOptions[key]; - if (Object.prototype.hasOwnProperty.call(def, "default")) { + if (Object.prototype.hasOwnProperty.call(def, 'default')) { memo[key] = def.default; } return memo; diff --git a/src/middlewares.js b/src/middlewares.js index 8e25cfac12..2e77b4916f 100644 --- a/src/middlewares.js +++ b/src/middlewares.js @@ -1,50 +1,50 @@ -import AppCache from "./cache"; -import Parse from "parse/node"; -import auth from "./Auth"; -import Config from "./Config"; -import ClientSDK from "./ClientSDK"; -import defaultLogger from "./logger"; -import rest from "./rest"; -import MongoStorageAdapter from "./Adapters/Storage/Mongo/MongoStorageAdapter"; -import PostgresStorageAdapter from "./Adapters/Storage/Postgres/PostgresStorageAdapter"; -import rateLimit from "express-rate-limit"; -import { RateLimitOptions } from "./Options/Definitions"; -import { pathToRegexp } from "path-to-regexp"; -import RedisStore from "rate-limit-redis"; -import { createClient } from "redis"; -import { BlockList, isIPv4 } from "net"; +import AppCache from './cache'; +import Parse from 'parse/node'; +import auth from './Auth'; +import Config from './Config'; +import ClientSDK from './ClientSDK'; +import defaultLogger from './logger'; +import rest from './rest'; +import MongoStorageAdapter from './Adapters/Storage/Mongo/MongoStorageAdapter'; +import PostgresStorageAdapter from './Adapters/Storage/Postgres/PostgresStorageAdapter'; +import rateLimit from 'express-rate-limit'; +import { RateLimitOptions } from './Options/Definitions'; +import { pathToRegexp } from 'path-to-regexp'; +import RedisStore from 'rate-limit-redis'; +import { createClient } from 'redis'; +import { BlockList, isIPv4 } from 'net'; export const DEFAULT_ALLOWED_HEADERS = - "X-Parse-Master-Key, X-Parse-REST-API-Key, X-Parse-Javascript-Key, X-Parse-Application-Id, X-Parse-Client-Version, X-Parse-Session-Token, X-Requested-With, X-Parse-Revocable-Session, X-Parse-Request-Id, Content-Type, Pragma, Cache-Control"; + 'X-Parse-Master-Key, X-Parse-REST-API-Key, X-Parse-Javascript-Key, X-Parse-Application-Id, X-Parse-Client-Version, X-Parse-Session-Token, X-Requested-With, X-Parse-Revocable-Session, X-Parse-Request-Id, Content-Type, Pragma, Cache-Control'; const getMountForRequest = function (req) { const mountPathLength = req.originalUrl.length - req.url.length; const mountPath = req.originalUrl.slice(0, mountPathLength); - return req.protocol + "://" + req.get("host") + mountPath; + return req.protocol + '://' + req.get('host') + mountPath; }; const getBlockList = (ipRangeList, store) => { - if (store.get("blockList")) { - return store.get("blockList"); + if (store.get('blockList')) { + return store.get('blockList'); } const blockList = new BlockList(); ipRangeList.forEach(fullIp => { - if (fullIp === "::/0" || fullIp === "::") { - store.set("allowAllIpv6", true); + if (fullIp === '::/0' || fullIp === '::') { + store.set('allowAllIpv6', true); return; } - if (fullIp === "0.0.0.0/0" || fullIp === "0.0.0.0") { - store.set("allowAllIpv4", true); + if (fullIp === '0.0.0.0/0' || fullIp === '0.0.0.0') { + store.set('allowAllIpv4', true); return; } - const [ip, mask] = fullIp.split("/"); + const [ip, mask] = fullIp.split('/'); if (!mask) { - blockList.addAddress(ip, isIPv4(ip) ? "ipv4" : "ipv6"); + blockList.addAddress(ip, isIPv4(ip) ? 'ipv4' : 'ipv6'); } else { - blockList.addSubnet(ip, Number(mask), isIPv4(ip) ? "ipv4" : "ipv6"); + blockList.addSubnet(ip, Number(mask), isIPv4(ip) ? 'ipv4' : 'ipv6'); } }); - store.set("blockList", blockList); + store.set('blockList', blockList); return blockList; }; @@ -55,13 +55,13 @@ export const checkIp = (ip, ipRangeList, store) => { if (store.get(ip)) { return true; } - if (store.get("allowAllIpv4") && incomingIpIsV4) { + if (store.get('allowAllIpv4') && incomingIpIsV4) { return true; } - if (store.get("allowAllIpv6") && !incomingIpIsV4) { + if (store.get('allowAllIpv6') && !incomingIpIsV4) { return true; } - const result = blockList.check(ip, incomingIpIsV4 ? "ipv4" : "ipv6"); + const result = blockList.check(ip, incomingIpIsV4 ? 'ipv4' : 'ipv6'); // If the ip is in the list, we store the result in the store // so we have a optimized path for the next request @@ -81,27 +81,27 @@ export async function handleParseHeaders(req, res, next) { var mount = getMountForRequest(req); let context = {}; - if (req.get("X-Parse-Cloud-Context") != null) { + if (req.get('X-Parse-Cloud-Context') != null) { try { - context = JSON.parse(req.get("X-Parse-Cloud-Context")); - if (Object.prototype.toString.call(context) !== "[object Object]") { - throw "Context is not an object"; + context = JSON.parse(req.get('X-Parse-Cloud-Context')); + if (Object.prototype.toString.call(context) !== '[object Object]') { + throw 'Context is not an object'; } } catch (e) { return malformedContext(req, res); } } var info = { - appId: req.get("X-Parse-Application-Id"), - sessionToken: req.get("X-Parse-Session-Token"), - masterKey: req.get("X-Parse-Master-Key"), - maintenanceKey: req.get("X-Parse-Maintenance-Key"), - installationId: req.get("X-Parse-Installation-Id"), - clientKey: req.get("X-Parse-Client-Key"), - javascriptKey: req.get("X-Parse-Javascript-Key"), - dotNetKey: req.get("X-Parse-Windows-Key"), - restAPIKey: req.get("X-Parse-REST-API-Key"), - clientVersion: req.get("X-Parse-Client-Version"), + appId: req.get('X-Parse-Application-Id'), + sessionToken: req.get('X-Parse-Session-Token'), + masterKey: req.get('X-Parse-Master-Key'), + maintenanceKey: req.get('X-Parse-Maintenance-Key'), + installationId: req.get('X-Parse-Installation-Id'), + clientKey: req.get('X-Parse-Client-Key'), + javascriptKey: req.get('X-Parse-Javascript-Key'), + dotNetKey: req.get('X-Parse-Windows-Key'), + restAPIKey: req.get('X-Parse-REST-API-Key'), + clientVersion: req.get('X-Parse-Client-Version'), context: context, }; @@ -152,7 +152,7 @@ export async function handleParseHeaders(req, res, next) { AppCache.get(req.body._ApplicationId).masterKey === info.masterKey) ) { info.appId = req.body._ApplicationId; - info.javascriptKey = req.body._JavaScriptKey || ""; + info.javascriptKey = req.body._JavaScriptKey || ''; delete req.body._ApplicationId; delete req.body._JavaScriptKey; // TODO: test that the REST API formats generated by the other @@ -180,9 +180,9 @@ export async function handleParseHeaders(req, res, next) { try { info.context = JSON.parse(req.body._context); if ( - Object.prototype.toString.call(info.context) !== "[object Object]" + Object.prototype.toString.call(info.context) !== '[object Object]' ) { - throw "Context is not an object"; + throw 'Context is not an object'; } } catch (e) { return malformedContext(req, res); @@ -191,7 +191,7 @@ export async function handleParseHeaders(req, res, next) { delete req.body._context; } if (req.body._ContentType) { - req.headers["content-type"] = req.body._ContentType; + req.headers['content-type'] = req.body._ContentType; delete req.body._ContentType; } } else { @@ -199,7 +199,7 @@ export async function handleParseHeaders(req, res, next) { } } - if (info.sessionToken && typeof info.sessionToken !== "string") { + if (info.sessionToken && typeof info.sessionToken !== 'string') { info.sessionToken = info.sessionToken.toString(); } @@ -211,12 +211,12 @@ export async function handleParseHeaders(req, res, next) { req.fileData = req.body.fileData; // We need to repopulate req.body with a buffer var base64 = req.body.base64; - req.body = Buffer.from(base64, "base64"); + req.body = Buffer.from(base64, 'base64'); } const clientIp = getClientIp(req); const config = Config.get(info.appId, mount); - if (config.state && config.state !== "ok") { + if (config.state && config.state !== 'ok') { res.status(500); res.json({ code: Parse.Error.INTERNAL_SERVER_ERROR, @@ -289,7 +289,7 @@ export async function handleParseHeaders(req, res, next) { var isReadOnlyMaster = info.masterKey === req.config.readOnlyMasterKey; if ( - typeof req.config.readOnlyMasterKey != "undefined" && + typeof req.config.readOnlyMasterKey != 'undefined' && req.config.readOnlyMasterKey && isReadOnlyMaster ) { @@ -304,7 +304,7 @@ export async function handleParseHeaders(req, res, next) { // Client keys are not required in parse-server, but if any have been configured in the server, validate them // to preserve original behavior. - const keys = ["clientKey", "javascriptKey", "dotNetKey", "restAPIKey"]; + const keys = ['clientKey', 'javascriptKey', 'dotNetKey', 'restAPIKey']; const oneKeyConfigured = keys.some(function (key) { return req.config[key] !== undefined; }); @@ -316,7 +316,7 @@ export async function handleParseHeaders(req, res, next) { return invalidRequest(req, res); } - if (req.url == "/login") { + if (req.url == '/login') { delete info.sessionToken; } @@ -353,7 +353,7 @@ const handleRateLimit = async (req, res, next) => { throw err; } req.config.loggerController.error( - "An unknown error occured when attempting to apply the rate limiter: ", + 'An unknown error occured when attempting to apply the rate limiter: ', err ); } @@ -372,15 +372,15 @@ const handleRateLimit = async (req, res, next) => { export const handleParseSession = async (req, res, next) => { try { const info = req.info; - if (req.auth || req.url === "/sessions/me") { + if (req.auth || req.url === '/sessions/me') { next(); return; } let requestAuth = null; if ( info.sessionToken && - req.url === "/upgradeToRevocableSession" && - info.sessionToken.indexOf("r:") != 0 + req.url === '/upgradeToRevocableSession' && + info.sessionToken.indexOf('r:') != 0 ) { requestAuth = await auth.getAuthForLegacySessionToken({ config: req.config, @@ -403,7 +403,7 @@ export const handleParseSession = async (req, res, next) => { } // TODO: Determine the correct error scenario. req.config.loggerController.error( - "error getting auth for sessionToken", + 'error getting auth for sessionToken', error ); throw new Parse.Error(Parse.Error.UNKNOWN_ERROR, error); @@ -423,19 +423,19 @@ function httpAuth(req) { var appId, masterKey, javascriptKey; // parse header - var authPrefix = "basic "; + var authPrefix = 'basic '; var match = header.toLowerCase().indexOf(authPrefix); if (match == 0) { var encodedAuth = header.substring(authPrefix.length, header.length); - var credentials = decodeBase64(encodedAuth).split(":"); + var credentials = decodeBase64(encodedAuth).split(':'); if (credentials.length == 2) { appId = credentials[0]; var key = credentials[1]; - var jsKeyPrefix = "javascript-key="; + var jsKeyPrefix = 'javascript-key='; var matchKey = key.indexOf(jsKeyPrefix); if (matchKey == 0) { @@ -450,7 +450,7 @@ function httpAuth(req) { } function decodeBase64(str) { - return Buffer.from(str, "base64").toString(); + return Buffer.from(str, 'base64').toString(); } export function allowCrossDomain(appId) { @@ -458,27 +458,27 @@ export function allowCrossDomain(appId) { const config = Config.get(appId, getMountForRequest(req)); let allowHeaders = DEFAULT_ALLOWED_HEADERS; if (config && config.allowHeaders) { - allowHeaders += `, ${config.allowHeaders.join(", ")}`; + allowHeaders += `, ${config.allowHeaders.join(', ')}`; } const baseOrigins = - typeof config?.allowOrigin === "string" + typeof config?.allowOrigin === 'string' ? [config.allowOrigin] - : (config?.allowOrigin ?? ["*"]); + : (config?.allowOrigin ?? ['*']); const requestOrigin = req.headers.origin; const allowOrigins = requestOrigin && baseOrigins.includes(requestOrigin) ? requestOrigin : baseOrigins[0]; - res.header("Access-Control-Allow-Origin", allowOrigins); - res.header("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE,OPTIONS"); - res.header("Access-Control-Allow-Headers", allowHeaders); + res.header('Access-Control-Allow-Origin', allowOrigins); + res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS'); + res.header('Access-Control-Allow-Headers', allowHeaders); res.header( - "Access-Control-Expose-Headers", - "X-Parse-Job-Status-Id, X-Parse-Push-Status-Id" + 'Access-Control-Expose-Headers', + 'X-Parse-Job-Status-Id, X-Parse-Push-Status-Id' ); // intercept OPTIONS method - if ("OPTIONS" == req.method) { + if ('OPTIONS' == req.method) { res.sendStatus(200); } else { next(); @@ -487,7 +487,7 @@ export function allowCrossDomain(appId) { } export function allowMethodOverride(req, res, next) { - if (req.method === "POST" && req.body?._method) { + if (req.method === 'POST' && req.body?._method) { req.originalMethod = req.method; req.method = req.body._method; delete req.body._method; @@ -515,7 +515,7 @@ export function handleParseErrors(err, req, res, next) { } res.status(httpStatus); res.json({ code: err.code, error: err.message }); - log.error("Parse error: ", err); + log.error('Parse error: ', err); } else if (err.status && err.message) { res.status(err.status); res.json({ error: err.message }); @@ -523,11 +523,11 @@ export function handleParseErrors(err, req, res, next) { next(err); } } else { - log.error("Uncaught internal server error.", err, err.stack); + log.error('Uncaught internal server error.', err, err.stack); res.status(500); res.json({ code: Parse.Error.INTERNAL_SERVER_ERROR, - message: "Internal server error.", + message: 'Internal server error.', }); if (!(process && process.env.TESTING)) { next(err); @@ -548,14 +548,14 @@ export function promiseEnforceMasterKeyAccess(request) { if (!request.auth.isMaster) { const error = new Error(); error.status = 403; - error.message = "unauthorized: master key is required"; + error.message = 'unauthorized: master key is required'; throw error; } return Promise.resolve(); } export const addRateLimit = (route, config, cloud) => { - if (typeof config === "string") { + if (typeof config === 'string') { config = Config.get(config); } for (const key in route) { @@ -575,12 +575,12 @@ export const addRateLimit = (route, config, cloud) => { const client = createClient({ url: route.redisUrl, }); - client.on("error", err => { - log.error("Middlewares addRateLimit Redis client error", { error: err }); + client.on('error', err => { + log.error('Middlewares addRateLimit Redis client error', { error: err }); }); - client.on("connect", () => {}); - client.on("reconnecting", () => {}); - client.on("ready", () => {}); + client.on('connect', () => {}); + client.on('reconnecting', () => {}); + client.on('ready', () => {}); redisStore.connectionPromise = async () => { if (client.isOpen) { return; @@ -599,9 +599,9 @@ export const addRateLimit = (route, config, cloud) => { }, }); } - let transformPath = route.requestPath.split("/*").join("/(.*)"); - if (transformPath === "*") { - transformPath = "(.*)"; + let transformPath = route.requestPath.split('/*').join('/(.*)'); + if (transformPath === '*') { + transformPath = '(.*)'; } config.rateLimits.push({ path: pathToRegexp(transformPath), @@ -618,7 +618,7 @@ export const addRateLimit = (route, config, cloud) => { }; }, skip: request => { - if (request.ip === "127.0.0.1" && !route.includeInternalRequests) { + if (request.ip === '127.0.0.1' && !route.includeInternalRequests) { return true; } if (route.includeMasterKey) { @@ -652,7 +652,7 @@ export const addRateLimit = (route, config, cloud) => { handleParseSession(request, null, resolve) ); } - if (request.auth?.user?.id && request.zone === "user") { + if (request.auth?.user?.id && request.zone === 'user') { return request.auth.user.id; } } @@ -683,19 +683,19 @@ export function promiseEnsureIdempotency(req) { } // Get parameters const config = req.config; - const requestId = ((req || {}).headers || {})["x-parse-request-id"]; + const requestId = ((req || {}).headers || {})['x-parse-request-id']; const { paths, ttl } = config.idempotencyOptions; if (!requestId || !config.idempotencyOptions) { return Promise.resolve(); } // Request path may contain trailing slashes, depending on the original request, so remove // leading and trailing slashes to make it easier to specify paths in the configuration - const reqPath = req.path.replace(/^\/|\/$/, ""); + const reqPath = req.path.replace(/^\/|\/$/, ''); // Determine whether idempotency is enabled for current request path let match = false; for (const path of paths) { // Assume one wants a path to always match from the beginning to prevent any mistakes - const regex = new RegExp(path.charAt(0) === "^" ? path : "^" + path); + const regex = new RegExp(path.charAt(0) === '^' ? path : '^' + path); if (reqPath.match(regex)) { match = true; break; @@ -709,7 +709,7 @@ export function promiseEnsureIdempotency(req) { new Date().setSeconds(new Date().getSeconds() + ttl) ); return rest - .create(config, auth.master(config), "_Idempotency", { + .create(config, auth.master(config), '_Idempotency', { reqId: requestId, expire: Parse._encode(expiryDate), }) @@ -717,7 +717,7 @@ export function promiseEnsureIdempotency(req) { if (e.code == Parse.Error.DUPLICATE_VALUE) { throw new Parse.Error( Parse.Error.DUPLICATE_REQUEST, - "Duplicate request" + 'Duplicate request' ); } throw e; @@ -733,7 +733,7 @@ function malformedContext(req, res) { res.status(400); res.json({ code: Parse.Error.INVALID_JSON, - error: "Invalid object for context.", + error: 'Invalid object for context.', }); } @@ -746,6 +746,6 @@ function malformedContext(req, res) { * http://localhost:1337/parse//functions/testFunction */ export function allowDoubleForwardSlash(req, res, next) { - req.url = req.url.startsWith("//") ? req.url.substring(1) : req.url; + req.url = req.url.startsWith('//') ? req.url.substring(1) : req.url; next(); } diff --git a/src/password.js b/src/password.js index 02b3ccae69..eebec14368 100644 --- a/src/password.js +++ b/src/password.js @@ -1,9 +1,9 @@ // Tools for encrypting and decrypting passwords. // Basically promise-friendly wrappers for bcrypt. -var bcrypt = require("bcryptjs"); +var bcrypt = require('bcryptjs'); try { - const _bcrypt = require("@node-rs/bcrypt"); + const _bcrypt = require('@node-rs/bcrypt'); bcrypt = { hash: _bcrypt.hash, compare: _bcrypt.verify, diff --git a/src/request.js b/src/request.js index 83f494b63b..e0f1a04339 100644 --- a/src/request.js +++ b/src/request.js @@ -1,26 +1,26 @@ -import querystring from "querystring"; -import log from "./logger"; -import { http, https } from "follow-redirects"; -import { parse } from "url"; +import querystring from 'querystring'; +import log from './logger'; +import { http, https } from 'follow-redirects'; +import { parse } from 'url'; class HTTPResponse { constructor(response, body) { let _text, _data; this.status = response.statusCode; this.headers = response.headers || {}; - this.cookies = this.headers["set-cookie"]; + this.cookies = this.headers['set-cookie']; - if (typeof body == "string") { + if (typeof body == 'string') { _text = body; } else if (Buffer.isBuffer(body)) { this.buffer = body; - } else if (typeof body == "object") { + } else if (typeof body == 'object') { _data = body; } const getText = () => { if (!_text && this.buffer) { - _text = this.buffer.toString("utf-8"); + _text = this.buffer.toString('utf-8'); } else if (!_text && _data) { _text = JSON.stringify(_data); } @@ -38,18 +38,18 @@ class HTTPResponse { return _data; }; - Object.defineProperty(this, "body", { + Object.defineProperty(this, 'body', { get: () => { return body; }, }); - Object.defineProperty(this, "text", { + Object.defineProperty(this, 'text', { enumerable: true, get: getText, }); - Object.defineProperty(this, "data", { + Object.defineProperty(this, 'data', { enumerable: true, get: getData, }); @@ -57,17 +57,17 @@ class HTTPResponse { } const clients = { - "http:": http, - "https:": https, + 'http:': http, + 'https:': https, }; function makeCallback(resolve, reject) { return function (response) { const chunks = []; - response.on("data", chunk => { + response.on('data', chunk => { chunks.push(chunk); }); - response.on("end", () => { + response.on('end', () => { const body = Buffer.concat(chunks); const httpResponse = new HTTPResponse(response, body); @@ -78,12 +78,12 @@ function makeCallback(resolve, reject) { return resolve(httpResponse); } }); - response.on("error", reject); + response.on('error', reject); }; } const encodeBody = function ({ body, headers = {} }) { - if (typeof body !== "object") { + if (typeof body !== 'object') { return { body, headers }; } var contentTypeKeys = Object.keys(headers).filter(key => { @@ -95,13 +95,13 @@ const encodeBody = function ({ body, headers = {} }) { // As per https://parse.com/docs/cloudcode/guide#cloud-code-advanced-sending-a-post-request the default encoding is supposedly x-www-form-urlencoded body = querystring.stringify(body); - headers["Content-Type"] = "application/x-www-form-urlencoded"; + headers['Content-Type'] = 'application/x-www-form-urlencoded'; } else { /* istanbul ignore next */ if (contentTypeKeys.length > 1) { log.error( - "Parse.Cloud.httpRequest", - "multiple content-type headers are set." + 'Parse.Cloud.httpRequest', + 'multiple content-type headers are set.' ); } // There maybe many, we'll just take the 1st one @@ -126,9 +126,9 @@ function httpRequest(options) { } options = Object.assign(options, encodeBody(options)); // support params options - if (typeof options.params === "object") { + if (typeof options.params === 'object') { options.qs = options.params; - } else if (typeof options.params === "string") { + } else if (typeof options.params === 'string') { options.qs = querystring.parse(options.params); } const client = clients[url.protocol]; @@ -146,7 +146,7 @@ function httpRequest(options) { }; if (requestOptions.headers) { Object.keys(requestOptions.headers).forEach(key => { - if (typeof requestOptions.headers[key] === "undefined") { + if (typeof requestOptions.headers[key] === 'undefined') { delete requestOptions.headers[key]; } }); @@ -171,7 +171,7 @@ function httpRequest(options) { if (options.body) { req.write(options.body); } - req.on("error", error => { + req.on('error', error => { reject(error); }); req.end(); diff --git a/src/rest.js b/src/rest.js index 4af242ca74..9282c36294 100644 --- a/src/rest.js +++ b/src/rest.js @@ -7,12 +7,12 @@ // routes. That's useful for the routes that do really similar // things. -var Parse = require("parse/node").Parse; +var Parse = require('parse/node').Parse; -var RestQuery = require("./RestQuery"); -var RestWrite = require("./RestWrite"); -var triggers = require("./triggers"); -const { enforceRoleSecurity } = require("./SharedRest"); +var RestQuery = require('./RestQuery'); +var RestWrite = require('./RestWrite'); +var triggers = require('./triggers'); +const { enforceRoleSecurity } = require('./SharedRest'); function checkTriggers(className, config, types) { return types.some(triggerType => { @@ -80,18 +80,18 @@ const get = async ( // Returns a promise that doesn't resolve to any useful value. function del(config, auth, className, objectId, context) { - if (typeof objectId !== "string") { - throw new Parse.Error(Parse.Error.INVALID_JSON, "bad objectId"); + if (typeof objectId !== 'string') { + throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad objectId'); } - if (className === "_User" && auth.isUnauthenticated()) { + if (className === '_User' && auth.isUnauthenticated()) { throw new Parse.Error( Parse.Error.SESSION_MISSING, - "Insufficient auth to delete user" + 'Insufficient auth to delete user' ); } - enforceRoleSecurity("delete", className, auth); + enforceRoleSecurity('delete', className, auth); let inflatedObject; let schemaController; @@ -99,11 +99,11 @@ function del(config, auth, className, objectId, context) { return Promise.resolve() .then(async () => { const hasTriggers = checkTriggers(className, config, [ - "beforeDelete", - "afterDelete", + 'beforeDelete', + 'afterDelete', ]); const hasLiveQuery = checkLiveQuery(className, config); - if (hasTriggers || hasLiveQuery || className == "_Session") { + if (hasTriggers || hasLiveQuery || className == '_Session') { const query = await RestQuery({ method: RestQuery.Method.get, config, @@ -111,19 +111,19 @@ function del(config, auth, className, objectId, context) { className, restWhere: { objectId }, }); - return query.execute({ op: "delete" }).then(response => { + return query.execute({ op: 'delete' }).then(response => { if (response && response.results && response.results.length) { const firstResult = response.results[0]; firstResult.className = className; if ( - className === "_Session" && + className === '_Session' && !auth.isMaster && !auth.isMaintenance ) { if (!auth.user || firstResult.user.objectId !== auth.user.id) { throw new Parse.Error( Parse.Error.INVALID_SESSION_TOKEN, - "Invalid session token" + 'Invalid session token' ); } } @@ -141,7 +141,7 @@ function del(config, auth, className, objectId, context) { } throw new Parse.Error( Parse.Error.OBJECT_NOT_FOUND, - "Object not found for delete." + 'Object not found for delete.' ); }); } @@ -159,7 +159,7 @@ function del(config, auth, className, objectId, context) { schemaController = s; const options = {}; if (!auth.isMaster && !auth.isMaintenance) { - options.acl = ["*"]; + options.acl = ['*']; if (auth.user) { options.acl.push(auth.user.id); options.acl = options.acl.concat(auth.userRoles); @@ -200,7 +200,7 @@ function del(config, auth, className, objectId, context) { // Returns a promise for a {response, status, location} object. function create(config, auth, className, restObject, clientSDK, context) { - enforceRoleSecurity("create", className, auth); + enforceRoleSecurity('create', className, auth); var write = new RestWrite( config, auth, @@ -226,13 +226,13 @@ function update( clientSDK, context ) { - enforceRoleSecurity("update", className, auth); + enforceRoleSecurity('update', className, auth); return Promise.resolve() .then(async () => { const hasTriggers = checkTriggers(className, config, [ - "beforeSave", - "afterSave", + 'beforeSave', + 'afterSave', ]); const hasLiveQuery = checkLiveQuery(className, config); if (hasTriggers || hasLiveQuery) { @@ -248,7 +248,7 @@ function update( context, }); return query.execute({ - op: "update", + op: 'update', }); } return Promise.resolve({}); @@ -267,7 +267,7 @@ function update( originalRestObject, clientSDK, context, - "update" + 'update' ).execute(); }) .catch(error => { @@ -278,12 +278,12 @@ function update( function handleSessionMissingError(error, className, auth) { // If we're trying to update a user without / with bad session token if ( - className === "_User" && + className === '_User' && error.code === Parse.Error.OBJECT_NOT_FOUND && !auth.isMaster && !auth.isMaintenance ) { - throw new Parse.Error(Parse.Error.SESSION_MISSING, "Insufficient auth."); + throw new Parse.Error(Parse.Error.SESSION_MISSING, 'Insufficient auth.'); } throw error; } diff --git a/src/vendor/mongodbUrl.js b/src/vendor/mongodbUrl.js index b6d3ee7ef4..68b9271a26 100644 --- a/src/vendor/mongodbUrl.js +++ b/src/vendor/mongodbUrl.js @@ -3,9 +3,9 @@ * See https://github.com/nodejs/node for licensing information. */ -"use strict"; +'use strict'; -import punycode from "punycode/punycode.js"; +import punycode from 'punycode/punycode.js'; exports.parse = urlParse; exports.resolve = urlResolve; @@ -42,27 +42,27 @@ const simplePathPattern = /^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/; // protocols that can allow "unsafe" and "unwise" chars. const unsafeProtocol = { javascript: true, - "javascript:": true, + 'javascript:': true, }; // protocols that never have a hostname. const hostlessProtocol = { javascript: true, - "javascript:": true, + 'javascript:': true, }; // protocols that always contain a // bit. const slashedProtocol = { http: true, - "http:": true, + 'http:': true, https: true, - "https:": true, + 'https:': true, ftp: true, - "ftp:": true, + 'ftp:': true, gopher: true, - "gopher:": true, + 'gopher:': true, file: true, - "file:": true, + 'file:': true, }; -const querystring = require("querystring"); +const querystring = require('querystring'); /* istanbul ignore next: improve coverage */ function urlParse(url, parseQueryString, slashesDenoteHost) { @@ -77,7 +77,7 @@ function urlParse(url, parseQueryString, slashesDenoteHost) { /* istanbul ignore next: improve coverage */ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { - if (typeof url !== "string") { + if (typeof url !== 'string') { throw new TypeError('Parameter "url" must be a string, not ' + typeof url); } @@ -87,7 +87,7 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { var hasHash = false; var start = -1; var end = -1; - var rest = ""; + var rest = ''; var lastPos = 0; var i = 0; for (var inWs = false, split = false; i < url.length; ++i) { @@ -132,7 +132,7 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { if (i - lastPos > 0) { rest += url.slice(lastPos, i); } - rest += "/"; + rest += '/'; lastPos = i + 1; break; } @@ -179,7 +179,7 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { this.query = this.search.slice(1); } } else if (parseQueryString) { - this.search = ""; + this.search = ''; this.query = {}; } return this; @@ -279,7 +279,7 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { } if (nonHost === -1) { this.host = rest.slice(start); - rest = ""; + rest = ''; } else { this.host = rest.slice(start, nonHost); rest = rest.slice(nonHost); @@ -290,8 +290,8 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { // we've indicated that there is a hostname, // so even if it's empty, it has to be present. - if (typeof this.hostname !== "string") { - this.hostname = ""; + if (typeof this.hostname !== 'string') { + this.hostname = ''; } var hostname = this.hostname; @@ -321,16 +321,16 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { this.hostname = punycode.toASCII(this.hostname); } - var p = this.port ? ":" + this.port : ""; - var h = this.hostname || ""; + var p = this.port ? ':' + this.port : ''; + var h = this.hostname || ''; this.host = h + p; // strip [ and ] from the hostname // the host field still retains them, though if (ipv6Hostname) { this.hostname = this.hostname.slice(1, -1); - if (rest[0] !== "/") { - rest = "/" + rest; + if (rest[0] !== '/') { + rest = '/' + rest; } } } @@ -373,7 +373,7 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { } } else if (parseQueryString) { // no query string, but parseQueryString still requested - this.search = ""; + this.search = ''; this.query = {}; } @@ -389,13 +389,13 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { this.pathname = rest.slice(0, firstIdx); } if (slashedProtocol[lowerProto] && this.hostname && !this.pathname) { - this.pathname = "/"; + this.pathname = '/'; } // to support http.request if (this.pathname || this.search) { - const p = this.pathname || ""; - const s = this.search || ""; + const p = this.pathname || ''; + const s = this.search || ''; this.path = p + s; } @@ -415,7 +415,7 @@ function validateHostname(self, rest, hostname) { if (i - lastPos > 0) { if (i - lastPos > 63) { self.hostname = hostname.slice(0, lastPos + 63); - return "/" + hostname.slice(lastPos + 63) + rest; + return '/' + hostname.slice(lastPos + 63) + rest; } } lastPos = i + 1; @@ -438,7 +438,7 @@ function validateHostname(self, rest, hostname) { // Invalid host character self.hostname = hostname.slice(0, i); if (i < hostname.length) { - return "/" + hostname.slice(i) + rest; + return '/' + hostname.slice(i) + rest; } break; } @@ -446,7 +446,7 @@ function validateHostname(self, rest, hostname) { /* istanbul ignore next: improve coverage */ function autoEscapeStr(rest) { - var newRest = ""; + var newRest = ''; var lastPos = 0; for (var i = 0; i < rest.length; ++i) { // Automatically escape all delimiters and unwise characters from RFC 2396 @@ -456,98 +456,98 @@ function autoEscapeStr(rest) { if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } - newRest += "%09"; + newRest += '%09'; lastPos = i + 1; break; case 10: // '\n' if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } - newRest += "%0A"; + newRest += '%0A'; lastPos = i + 1; break; case 13: // '\r' if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } - newRest += "%0D"; + newRest += '%0D'; lastPos = i + 1; break; case 32: // ' ' if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } - newRest += "%20"; + newRest += '%20'; lastPos = i + 1; break; case 34: // '"' if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } - newRest += "%22"; + newRest += '%22'; lastPos = i + 1; break; case 39: // '\'' if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } - newRest += "%27"; + newRest += '%27'; lastPos = i + 1; break; case 60: // '<' if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } - newRest += "%3C"; + newRest += '%3C'; lastPos = i + 1; break; case 62: // '>' if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } - newRest += "%3E"; + newRest += '%3E'; lastPos = i + 1; break; case 92: // '\\' if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } - newRest += "%5C"; + newRest += '%5C'; lastPos = i + 1; break; case 94: // '^' if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } - newRest += "%5E"; + newRest += '%5E'; lastPos = i + 1; break; case 96: // '`' if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } - newRest += "%60"; + newRest += '%60'; lastPos = i + 1; break; case 123: // '{' if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } - newRest += "%7B"; + newRest += '%7B'; lastPos = i + 1; break; case 124: // '|' if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } - newRest += "%7C"; + newRest += '%7C'; lastPos = i + 1; break; case 125: // '}' if (i - lastPos > 0) { newRest += rest.slice(lastPos, i); } - newRest += "%7D"; + newRest += '%7D'; lastPos = i + 1; break; } @@ -569,12 +569,12 @@ function urlFormat(obj) { // If it's an obj, this is a no-op. // this way, you can call url_format() on strings // to clean up potentially wonky urls. - if (typeof obj === "string") { + if (typeof obj === 'string') { obj = urlParse(obj); - } else if (typeof obj !== "object" || obj === null) { + } else if (typeof obj !== 'object' || obj === null) { throw new TypeError( 'Parameter "urlObj" must be an object, not ' + - (obj === null ? "null" : typeof obj) + (obj === null ? 'null' : typeof obj) ); } else if (!(obj instanceof Url)) { return Url.prototype.format.call(obj); @@ -585,42 +585,42 @@ function urlFormat(obj) { /* istanbul ignore next: improve coverage */ Url.prototype.format = function () { - var auth = this.auth || ""; + var auth = this.auth || ''; if (auth) { auth = encodeAuth(auth); - auth += "@"; + auth += '@'; } - var protocol = this.protocol || ""; - var pathname = this.pathname || ""; - var hash = this.hash || ""; + var protocol = this.protocol || ''; + var pathname = this.pathname || ''; + var hash = this.hash || ''; var host = false; - var query = ""; + var query = ''; if (this.host) { host = auth + this.host; } else if (this.hostname) { host = auth + - (this.hostname.indexOf(":") === -1 + (this.hostname.indexOf(':') === -1 ? this.hostname - : "[" + this.hostname + "]"); + : '[' + this.hostname + ']'); if (this.port) { - host += ":" + this.port; + host += ':' + this.port; } } - if (this.query !== null && typeof this.query === "object") { + if (this.query !== null && typeof this.query === 'object') { query = querystring.stringify(this.query); } - var search = this.search || (query && "?" + query) || ""; + var search = this.search || (query && '?' + query) || ''; if (protocol && protocol.charCodeAt(protocol.length - 1) !== 58 /*:*/) { - protocol += ":"; + protocol += ':'; } - var newPathname = ""; + var newPathname = ''; var lastPos = 0; for (var i = 0; i < pathname.length; ++i) { switch (pathname.charCodeAt(i)) { @@ -628,14 +628,14 @@ Url.prototype.format = function () { if (i - lastPos > 0) { newPathname += pathname.slice(lastPos, i); } - newPathname += "%23"; + newPathname += '%23'; lastPos = i + 1; break; case 63: // '?' if (i - lastPos > 0) { newPathname += pathname.slice(lastPos, i); } - newPathname += "%3F"; + newPathname += '%3F'; lastPos = i + 1; break; } @@ -654,21 +654,21 @@ Url.prototype.format = function () { this.slashes || ((!protocol || slashedProtocol[protocol]) && host !== false) ) { - host = "//" + (host || ""); + host = '//' + (host || ''); if (pathname && pathname.charCodeAt(0) !== 47 /*/*/) { - pathname = "/" + pathname; + pathname = '/' + pathname; } } else if (!host) { - host = ""; + host = ''; } - search = search.replace("#", "%23"); + search = search.replace('#', '%23'); if (hash && hash.charCodeAt(0) !== 35 /*#*/) { - hash = "#" + hash; + hash = '#' + hash; } if (search && search.charCodeAt(0) !== 63 /*?*/) { - search = "?" + search; + search = '?' + search; } return protocol + host + pathname + search + hash; @@ -694,7 +694,7 @@ function urlResolveObject(source, relative) { /* istanbul ignore next: improve coverage */ Url.prototype.resolveObject = function (relative) { - if (typeof relative === "string") { + if (typeof relative === 'string') { var rel = new Url(); rel.parse(relative, false, true); relative = rel; @@ -712,7 +712,7 @@ Url.prototype.resolveObject = function (relative) { result.hash = relative.hash; // if the relative url is empty, then there's nothing left to do here. - if (relative.href === "") { + if (relative.href === '') { result.href = result.format(); return result; } @@ -723,7 +723,7 @@ Url.prototype.resolveObject = function (relative) { var rkeys = Object.keys(relative); for (var rk = 0; rk < rkeys.length; rk++) { var rkey = rkeys[rk]; - if (rkey !== "protocol") { + if (rkey !== 'protocol') { result[rkey] = relative[rkey]; } } @@ -734,7 +734,7 @@ Url.prototype.resolveObject = function (relative) { result.hostname && !result.pathname ) { - result.path = result.pathname = "/"; + result.path = result.pathname = '/'; } result.href = result.format(); @@ -766,7 +766,7 @@ Url.prototype.resolveObject = function (relative) { !/^file:?$/.test(relative.protocol) && !hostlessProtocol[relative.protocol] ) { - const relPath = (relative.pathname || "").split("/"); + const relPath = (relative.pathname || '').split('/'); while (relPath.length) { const shifted = relPath.shift(); if (shifted) { @@ -775,31 +775,31 @@ Url.prototype.resolveObject = function (relative) { } } if (!relative.host) { - relative.host = ""; + relative.host = ''; } if (!relative.hostname) { - relative.hostname = ""; + relative.hostname = ''; } - if (relPath[0] !== "") { - relPath.unshift(""); + if (relPath[0] !== '') { + relPath.unshift(''); } if (relPath.length < 2) { - relPath.unshift(""); + relPath.unshift(''); } - result.pathname = relPath.join("/"); + result.pathname = relPath.join('/'); } else { result.pathname = relative.pathname; } result.search = relative.search; result.query = relative.query; - result.host = relative.host || ""; + result.host = relative.host || ''; result.auth = relative.auth; result.hostname = relative.hostname || relative.host; result.port = relative.port; // to support http.request if (result.pathname || result.search) { - var p = result.pathname || ""; - var s = result.search || ""; + var p = result.pathname || ''; + var s = result.search || ''; result.path = p + s; } result.slashes = result.slashes || relative.slashes; @@ -807,14 +807,14 @@ Url.prototype.resolveObject = function (relative) { return result; } - var isSourceAbs = result.pathname && result.pathname.charAt(0) === "/"; + var isSourceAbs = result.pathname && result.pathname.charAt(0) === '/'; var isRelAbs = - relative.host || (relative.pathname && relative.pathname.charAt(0) === "/"); + relative.host || (relative.pathname && relative.pathname.charAt(0) === '/'); var mustEndAbs = isRelAbs || isSourceAbs || (result.host && relative.pathname); var removeAllDots = mustEndAbs; - var srcPath = (result.pathname && result.pathname.split("/")) || []; - var relPath = (relative.pathname && relative.pathname.split("/")) || []; + var srcPath = (result.pathname && result.pathname.split('/')) || []; + var relPath = (relative.pathname && relative.pathname.split('/')) || []; var psychotic = result.protocol && !slashedProtocol[result.protocol]; // if the url is a non-slashed url, then relative @@ -823,21 +823,21 @@ Url.prototype.resolveObject = function (relative) { // result.protocol has already been set by now. // Later on, put the first path part into the host field. if (psychotic) { - result.hostname = ""; + result.hostname = ''; result.port = null; if (result.host) { - if (srcPath[0] === "") { + if (srcPath[0] === '') { srcPath[0] = result.host; } else { srcPath.unshift(result.host); } } - result.host = ""; + result.host = ''; if (relative.protocol) { relative.hostname = null; relative.port = null; if (relative.host) { - if (relPath[0] === "") { + if (relPath[0] === '') { relPath[0] = relative.host; } else { relPath.unshift(relative.host); @@ -845,15 +845,15 @@ Url.prototype.resolveObject = function (relative) { } relative.host = null; } - mustEndAbs = mustEndAbs && (relPath[0] === "" || srcPath[0] === ""); + mustEndAbs = mustEndAbs && (relPath[0] === '' || srcPath[0] === ''); } if (isRelAbs) { // it's absolute. result.host = - relative.host || relative.host === "" ? relative.host : result.host; + relative.host || relative.host === '' ? relative.host : result.host; result.hostname = - relative.hostname || relative.hostname === "" + relative.hostname || relative.hostname === '' ? relative.hostname : result.hostname; result.search = relative.search; @@ -880,8 +880,8 @@ Url.prototype.resolveObject = function (relative) { //this especially happens in cases like //url.resolveObject('mailto:local1@domain1', 'local2@domain2') const authInHost = - result.host && result.host.indexOf("@") > 0 - ? result.host.split("@") + result.host && result.host.indexOf('@') > 0 + ? result.host.split('@') : false; if (authInHost) { result.auth = authInHost.shift(); @@ -893,8 +893,8 @@ Url.prototype.resolveObject = function (relative) { //to support http.request if (result.pathname !== null || result.search !== null) { result.path = - (result.pathname ? result.pathname : "") + - (result.search ? result.search : ""); + (result.pathname ? result.pathname : '') + + (result.search ? result.search : ''); } result.href = result.format(); return result; @@ -906,7 +906,7 @@ Url.prototype.resolveObject = function (relative) { result.pathname = null; //to support http.request if (result.search) { - result.path = "/" + result.search; + result.path = '/' + result.search; } else { result.path = null; } @@ -920,17 +920,17 @@ Url.prototype.resolveObject = function (relative) { var last = srcPath.slice(-1)[0]; var hasTrailingSlash = ((result.host || relative.host || srcPath.length > 1) && - (last === "." || last === "..")) || - last === ""; + (last === '.' || last === '..')) || + last === ''; // strip single dots, resolve double dots to parent dir // if the path tries to go above the root, `up` ends up > 0 var up = 0; for (var i = srcPath.length; i >= 0; i--) { last = srcPath[i]; - if (last === ".") { + if (last === '.') { spliceOne(srcPath, i); - } else if (last === "..") { + } else if (last === '..') { spliceOne(srcPath, i); up++; } else if (up) { @@ -942,38 +942,38 @@ Url.prototype.resolveObject = function (relative) { // if the path is allowed to go above the root, restore leading ..s if (!mustEndAbs && !removeAllDots) { for (; up--; up) { - srcPath.unshift(".."); + srcPath.unshift('..'); } } if ( mustEndAbs && - srcPath[0] !== "" && - (!srcPath[0] || srcPath[0].charAt(0) !== "/") + srcPath[0] !== '' && + (!srcPath[0] || srcPath[0].charAt(0) !== '/') ) { - srcPath.unshift(""); + srcPath.unshift(''); } - if (hasTrailingSlash && srcPath.join("/").substr(-1) !== "/") { - srcPath.push(""); + if (hasTrailingSlash && srcPath.join('/').substr(-1) !== '/') { + srcPath.push(''); } var isAbsolute = - srcPath[0] === "" || (srcPath[0] && srcPath[0].charAt(0) === "/"); + srcPath[0] === '' || (srcPath[0] && srcPath[0].charAt(0) === '/'); // put the host back if (psychotic) { if (isAbsolute) { - result.hostname = result.host = ""; + result.hostname = result.host = ''; } else { - result.hostname = result.host = srcPath.length ? srcPath.shift() : ""; + result.hostname = result.host = srcPath.length ? srcPath.shift() : ''; } //occasionally the auth can get stuck only in host //this especially happens in cases like //url.resolveObject('mailto:local1@domain1', 'local2@domain2') const authInHost = - result.host && result.host.indexOf("@") > 0 - ? result.host.split("@") + result.host && result.host.indexOf('@') > 0 + ? result.host.split('@') : false; if (authInHost) { result.auth = authInHost.shift(); @@ -984,21 +984,21 @@ Url.prototype.resolveObject = function (relative) { mustEndAbs = mustEndAbs || (result.host && srcPath.length); if (mustEndAbs && !isAbsolute) { - srcPath.unshift(""); + srcPath.unshift(''); } if (!srcPath.length) { result.pathname = null; result.path = null; } else { - result.pathname = srcPath.join("/"); + result.pathname = srcPath.join('/'); } //to support request.http if (result.pathname !== null || result.search !== null) { result.path = - (result.pathname ? result.pathname : "") + - (result.search ? result.search : ""); + (result.pathname ? result.pathname : '') + + (result.search ? result.search : ''); } result.auth = relative.auth || result.auth; result.slashes = result.slashes || relative.slashes; @@ -1012,7 +1012,7 @@ Url.prototype.parseHost = function () { var port = portPattern.exec(host); if (port) { port = port[0]; - if (port !== ":") { + if (port !== ':') { this.port = port.slice(1); } host = host.slice(0, host.length - port.length); @@ -1033,12 +1033,12 @@ function spliceOne(list, index) { var hexTable = new Array(256); for (var i = 0; i < 256; ++i) { - hexTable[i] = "%" + ((i < 16 ? "0" : "") + i.toString(16)).toUpperCase(); + hexTable[i] = '%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase(); } /* istanbul ignore next: improve coverage */ function encodeAuth(str) { // faster encodeURIComponent alternative for encoding auth uri components - var out = ""; + var out = ''; var lastPos = 0; for (var i = 0; i < str.length; ++i) { var c = str.charCodeAt(i); From d4f4df9cc95a4445215070728cdd1827a555023c Mon Sep 17 00:00:00 2001 From: Daniel Date: Sun, 18 May 2025 20:48:51 +1000 Subject: [PATCH 6/7] change printwidth --- .prettierrc | 21 +- spec/AccountLockoutPolicy.spec.js | 129 +- spec/AdaptableController.spec.js | 6 +- spec/AdapterLoader.spec.js | 12 +- spec/AggregateRouter.spec.js | 12 +- spec/AudienceRouter.spec.js | 192 +- spec/Auth.spec.js | 10 +- spec/AuthenticationAdapters.spec.js | 205 +- spec/AuthenticationAdaptersV2.spec.js | 64 +- spec/CLI.spec.js | 99 +- spec/CacheController.spec.js | 3 +- spec/Client.spec.js | 15 +- spec/CloudCode.Validator.spec.js | 123 +- spec/CloudCode.spec.js | 253 +- spec/CloudCodeLogger.spec.js | 354 +- spec/DatabaseController.spec.js | 133 +- spec/DefinedSchemas.spec.js | 85 +- spec/Deprecator.spec.js | 12 +- spec/EmailVerificationToken.spec.js | 168 +- spec/EventEmitterPubSub.spec.js | 3 +- spec/FilesController.spec.js | 122 +- spec/GridFSBucketStorageAdapter.spec.js | 83 +- spec/HTTPRequest.spec.js | 4 +- spec/Idempotency.spec.js | 22 +- spec/InMemoryCacheAdapter.spec.js | 10 +- spec/InstallationsRouter.spec.js | 168 +- spec/JobSchedule.spec.js | 40 +- spec/LdapAuth.spec.js | 94 +- spec/LoggerController.spec.js | 7 +- spec/LogsRouter.spec.js | 11 +- spec/Middlewares.spec.js | 114 +- spec/MongoStorageAdapter.spec.js | 103 +- spec/MongoTransform.spec.js | 10 +- spec/NullCacheAdapter.spec.js | 10 +- spec/OAuth1.spec.js | 7 +- spec/PagesRouter.spec.js | 191 +- spec/Parse.Push.spec.js | 55 +- spec/ParseAPI.spec.js | 197 +- spec/ParseCloudCodePublisher.spec.js | 6 +- spec/ParseConfigKey.spec.js | 97 +- spec/ParseFile.spec.js | 175 +- spec/ParseGeoPoint.spec.js | 313 +- spec/ParseGlobalConfig.spec.js | 63 +- spec/ParseGraphQLClassNameTransformer.spec.js | 13 +- spec/ParseGraphQLController.spec.js | 80 +- spec/ParseGraphQLSchema.spec.js | 111 +- spec/ParseGraphQLServer.spec.js | 2887 ++++++----------- spec/ParseHooks.spec.js | 327 +- spec/ParseInstallation.spec.js | 501 +-- spec/ParseLiveQuery.spec.js | 129 +- spec/ParseLiveQueryRedis.spec.js | 13 +- spec/ParseLiveQueryServer.spec.js | 512 +-- spec/ParseObject.spec.js | 164 +- spec/ParsePolygon.spec.js | 83 +- spec/ParsePubSub.spec.js | 29 +- spec/ParseQuery.Aggregate.spec.js | 1479 ++++----- spec/ParseQuery.Comment.spec.js | 30 +- spec/ParseQuery.FullTextSearch.spec.js | 481 ++- spec/ParseQuery.hint.spec.js | 528 ++- spec/ParseQuery.spec.js | 886 ++--- spec/ParseRelation.spec.js | 14 +- spec/ParseRole.spec.js | 72 +- spec/ParseServer.spec.js | 4 +- spec/ParseServerRESTController.spec.js | 45 +- spec/ParseUser.spec.js | 292 +- spec/ParseWebSocket.spec.js | 3 +- spec/ParseWebSocketServer.spec.js | 34 +- spec/PasswordPolicy.spec.js | 120 +- spec/PointerPermissions.spec.js | 256 +- spec/PostgresConfigParser.spec.js | 8 +- spec/PostgresStorageAdapter.spec.js | 76 +- spec/ProtectedFields.spec.js | 72 +- spec/PublicAPI.spec.js | 110 +- spec/PurchaseValidation.spec.js | 8 +- spec/PushController.spec.js | 723 ++--- spec/PushQueue.spec.js | 13 +- spec/PushWorker.spec.js | 10 +- spec/RateLimit.spec.js | 31 +- spec/ReadPreferenceOption.spec.js | 26 +- spec/RedisCacheAdapter.spec.js | 3 +- spec/RegexVulnerabilities.spec.js | 54 +- spec/RestQuery.spec.js | 147 +- spec/RevocableSessionsUpgrade.spec.js | 7 +- spec/Schema.spec.js | 160 +- spec/SchemaPerformance.spec.js | 64 +- spec/SecurityCheck.spec.js | 16 +- spec/SecurityCheckGroups.spec.js | 3 +- spec/SessionTokenCache.spec.js | 3 +- spec/Subscription.spec.js | 66 +- spec/UserController.spec.js | 79 +- spec/UserPII.spec.js | 135 +- spec/ValidationAndPasswordsReset.spec.js | 149 +- spec/VerifyUserPassword.spec.js | 98 +- spec/WinstonLoggerAdapter.spec.js | 46 +- spec/batch.spec.js | 33 +- spec/cryptoUtils.spec.js | 8 +- spec/defaultGraphQLTypes.spec.js | 213 +- spec/features.spec.js | 4 +- spec/graphQLObjectsQueries.js | 66 +- spec/helper.js | 68 +- spec/index.spec.js | 85 +- spec/rest.spec.js | 193 +- spec/schemas.spec.js | 312 +- spec/support/CurrentSpecReporter.js | 9 +- spec/support/FailingServer.js | 10 +- spec/support/dev.js | 5 +- spec/vulnerabilities.spec.js | 44 +- src/AccountLockout.js | 37 +- src/Auth.js | 128 +- src/Controllers/AdaptableController.js | 31 +- src/Controllers/AnalyticsController.js | 6 +- src/Controllers/DatabaseController.js | 1035 +++--- src/Controllers/FilesController.js | 20 +- src/Controllers/HooksController.js | 78 +- src/Controllers/LoggerController.js | 18 +- src/Controllers/SchemaController.js | 307 +- src/Controllers/UserController.js | 44 +- src/Deprecator/Deprecator.js | 12 +- src/GraphQL/parseGraphQLUtils.js | 9 +- src/ParseServerRESTController.js | 38 +- src/PromiseRouter.js | 9 +- src/Push/PushWorker.js | 43 +- src/Push/utils.js | 5 +- src/RestQuery.js | 73 +- src/RestWrite.js | 404 +-- src/Routers/AggregateRouter.js | 21 +- src/Routers/AudiencesRouter.js | 32 +- src/Routers/ClassesRouter.js | 59 +- src/Routers/CloudCodeRouter.js | 30 +- src/Routers/FeaturesRouter.js | 111 +- src/Routers/FilesRouter.js | 84 +- src/Routers/FunctionsRouter.js | 31 +- src/Routers/GlobalConfigRouter.js | 11 +- src/Routers/GraphQLRouter.js | 22 +- src/Routers/HooksRouter.js | 44 +- src/Routers/IAPValidationRouter.js | 10 +- src/Routers/InstallationsRouter.js | 21 +- src/Routers/LogsRouter.js | 5 +- src/Routers/PagesRouter.js | 47 +- src/Routers/PublicAPIRouter.js | 32 +- src/Routers/PurgeRouter.js | 11 +- src/Routers/PushRouter.js | 12 +- src/Routers/SchemasRouter.js | 37 +- src/Routers/SecurityRouter.js | 3 +- src/Routers/SessionsRouter.js | 10 +- src/Routers/UsersRouter.js | 178 +- src/SchemaMigrations/DefinedSchemas.js | 90 +- src/SchemaMigrations/Migrations.js | 13 +- src/Security/CheckRunner.js | 10 +- src/SharedRest.js | 5 +- src/StatusHandler.js | 18 +- src/Utils.js | 29 +- src/batch.js | 61 +- src/cli/parse-server.js | 33 +- src/cloud-code/Parse.Cloud.js | 43 +- src/cryptoUtils.js | 3 +- src/defaults.js | 17 +- src/middlewares.js | 58 +- src/request.js | 14 +- src/rest.js | 90 +- src/vendor/mongodbUrl.js | 77 +- 161 files changed, 6726 insertions(+), 12994 deletions(-) diff --git a/.prettierrc b/.prettierrc index 788c8595ea..31fa426fac 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,16 +1,5 @@ -{ - "arrowParens": "avoid", - "bracketSpacing": true, - "embeddedLanguageFormatting": "auto", - "htmlWhitespaceSensitivity": "css", - "insertPragma": false, - "jsxBracketSameLine": false, - "printWidth": 80, - "proseWrap": "preserve", - "quoteProps": "as-needed", - "semi": true, - "singleQuote": true, - "tabWidth": 2, - "trailingComma": "es5", - "useTabs": false -} +semi: true +trailingComma: "es5" +singleQuote: true +arrowParens: "avoid" +printWidth: 100 \ No newline at end of file diff --git a/spec/AccountLockoutPolicy.spec.js b/spec/AccountLockoutPolicy.spec.js index 9d7ba6d2c2..da8048adab 100644 --- a/spec/AccountLockoutPolicy.spec.js +++ b/spec/AccountLockoutPolicy.spec.js @@ -18,12 +18,7 @@ const loginWithWrongCredentialsShouldFail = function (username, password) { }); }; -const isAccountLockoutError = function ( - username, - password, - duration, - waitTime -) { +const isAccountLockoutError = function (username, password, duration, waitTime) { return new Promise((resolve, reject) => { setTimeout(() => { Parse.User.logIn(username, password) @@ -57,28 +52,17 @@ describe('Account Lockout Policy: ', () => { return user.signUp(null); }) .then(() => { - return loginWithWrongCredentialsShouldFail( - 'username1', - 'incorrect password 1' - ); + return loginWithWrongCredentialsShouldFail('username1', 'incorrect password 1'); }) .then(() => { - return loginWithWrongCredentialsShouldFail( - 'username1', - 'incorrect password 2' - ); + return loginWithWrongCredentialsShouldFail('username1', 'incorrect password 2'); }) .then(() => { - return loginWithWrongCredentialsShouldFail( - 'username1', - 'incorrect password 3' - ); + return loginWithWrongCredentialsShouldFail('username1', 'incorrect password 3'); }) .then(() => done()) .catch(err => { - fail( - 'allow unlimited failed login attempts failed: ' + JSON.stringify(err) - ); + fail('allow unlimited failed login attempts failed: ' + JSON.stringify(err)); done(); }); }); @@ -100,15 +84,11 @@ describe('Account Lockout Policy: ', () => { .catch(err => { if ( err && - err === - 'Account lockout duration should be greater than 0 and less than 100000' + err === 'Account lockout duration should be greater than 0 and less than 100000' ) { done(); } else { - fail( - 'set duration to an invalid number test failed: ' + - JSON.stringify(err) - ); + fail('set duration to an invalid number test failed: ' + JSON.stringify(err)); done(); } }); @@ -131,15 +111,11 @@ describe('Account Lockout Policy: ', () => { .catch(err => { if ( err && - err === - 'Account lockout threshold should be an integer greater than 0 and less than 1000' + err === 'Account lockout threshold should be an integer greater than 0 and less than 1000' ) { done(); } else { - fail( - 'set threshold to an invalid number test failed: ' + - JSON.stringify(err) - ); + fail('set threshold to an invalid number test failed: ' + JSON.stringify(err)); done(); } }); @@ -162,14 +138,11 @@ describe('Account Lockout Policy: ', () => { .catch(err => { if ( err && - err === - 'Account lockout threshold should be an integer greater than 0 and less than 1000' + err === 'Account lockout threshold should be an integer greater than 0 and less than 1000' ) { done(); } else { - fail( - 'threshold value < 1 is invalid test failed: ' + JSON.stringify(err) - ); + fail('threshold value < 1 is invalid test failed: ' + JSON.stringify(err)); done(); } }); @@ -192,15 +165,11 @@ describe('Account Lockout Policy: ', () => { .catch(err => { if ( err && - err === - 'Account lockout threshold should be an integer greater than 0 and less than 1000' + err === 'Account lockout threshold should be an integer greater than 0 and less than 1000' ) { done(); } else { - fail( - 'threshold value > 999 is invalid test failed: ' + - JSON.stringify(err) - ); + fail('threshold value > 999 is invalid test failed: ' + JSON.stringify(err)); done(); } }); @@ -223,14 +192,11 @@ describe('Account Lockout Policy: ', () => { .catch(err => { if ( err && - err === - 'Account lockout duration should be greater than 0 and less than 100000' + err === 'Account lockout duration should be greater than 0 and less than 100000' ) { done(); } else { - fail( - 'duration value < 1 is invalid test failed: ' + JSON.stringify(err) - ); + fail('duration value < 1 is invalid test failed: ' + JSON.stringify(err)); done(); } }); @@ -253,15 +219,11 @@ describe('Account Lockout Policy: ', () => { .catch(err => { if ( err && - err === - 'Account lockout duration should be greater than 0 and less than 100000' + err === 'Account lockout duration should be greater than 0 and less than 100000' ) { done(); } else { - fail( - 'duration value > 99999 is invalid test failed: ' + - JSON.stringify(err) - ); + fail('duration value > 99999 is invalid test failed: ' + JSON.stringify(err)); done(); } }); @@ -283,16 +245,10 @@ describe('Account Lockout Policy: ', () => { return user.signUp(); }) .then(() => { - return loginWithWrongCredentialsShouldFail( - 'username2', - 'wrong password' - ); + return loginWithWrongCredentialsShouldFail('username2', 'wrong password'); }) .then(() => { - return loginWithWrongCredentialsShouldFail( - 'username2', - 'wrong password' - ); + return loginWithWrongCredentialsShouldFail('username2', 'wrong password'); }) .then(() => { return isAccountLockoutError('username2', 'wrong password', 1, 1); @@ -301,10 +257,7 @@ describe('Account Lockout Policy: ', () => { done(); }) .catch(err => { - fail( - 'lock account after failed login attempts test failed: ' + - JSON.stringify(err) - ); + fail('lock account after failed login attempts test failed: ' + JSON.stringify(err)); done(); }); }); @@ -325,16 +278,10 @@ describe('Account Lockout Policy: ', () => { return user.signUp(); }) .then(() => { - return loginWithWrongCredentialsShouldFail( - 'username3', - 'wrong password' - ); + return loginWithWrongCredentialsShouldFail('username3', 'wrong password'); }) .then(() => { - return loginWithWrongCredentialsShouldFail( - 'username3', - 'wrong password' - ); + return loginWithWrongCredentialsShouldFail('username3', 'wrong password'); }) .then(() => { return isAccountLockoutError('username3', 'wrong password', 0.05, 1); @@ -347,10 +294,7 @@ describe('Account Lockout Policy: ', () => { done(); }) .catch(err => { - fail( - 'account should be locked for duration mins test failed: ' + - JSON.stringify(err) - ); + fail('account should be locked for duration mins test failed: ' + JSON.stringify(err)); done(); }); }); @@ -371,16 +315,10 @@ describe('Account Lockout Policy: ', () => { return user.signUp(); }) .then(() => { - return loginWithWrongCredentialsShouldFail( - 'username4', - 'wrong password' - ); + return loginWithWrongCredentialsShouldFail('username4', 'wrong password'); }) .then(() => { - return loginWithWrongCredentialsShouldFail( - 'username4', - 'wrong password' - ); + return loginWithWrongCredentialsShouldFail('username4', 'wrong password'); }) .then(() => { // allow locked user to login after 3 seconds with a valid userid and password @@ -428,10 +366,7 @@ describe('lockout with password reset option', () => { }; await reconfigureServer(config); - sendPasswordResetEmail = spyOn( - config.emailAdapter, - 'sendPasswordResetEmail' - ).and.callThrough(); + sendPasswordResetEmail = spyOn(config.emailAdapter, 'sendPasswordResetEmail').and.callThrough(); } it('accepts valid unlockOnPasswordReset option', async () => { @@ -451,9 +386,7 @@ describe('lockout with password reset option', () => { }); it('uses default value if unlockOnPasswordReset is not set', async () => { - await expectAsync( - setup({ unlockOnPasswordReset: undefined }) - ).toBeResolved(); + await expectAsync(setup({ unlockOnPasswordReset: undefined })).toBeResolved(); const parseConfig = Config.get(Parse.applicationId); expect(parseConfig.accountLockout.unlockOnPasswordReset).toBe( @@ -473,9 +406,7 @@ describe('lockout with password reset option', () => { user.setEmail('mail@example.com'); await user.signUp(); - await expectAsync( - Parse.User.logIn(username, 'incorrectPassword') - ).toBeRejected(); + await expectAsync(Parse.User.logIn(username, 'incorrectPassword')).toBeRejected(); await expectAsync(Parse.User.logIn(username, password)).toBeRejected(); await Parse.User.requestPasswordReset(user.getEmail()); @@ -510,9 +441,7 @@ describe('lockout with password reset option', () => { user.setEmail('mail@example.com'); await user.signUp(); - await expectAsync( - Parse.User.logIn(username, 'incorrectPassword') - ).toBeRejected(); + await expectAsync(Parse.User.logIn(username, 'incorrectPassword')).toBeRejected(); await expectAsync(Parse.User.logIn(username, password)).toBeRejected(); await Parse.User.requestPasswordReset(user.getEmail()); diff --git a/spec/AdaptableController.spec.js b/spec/AdaptableController.spec.js index e82e1d37c9..4cda42e162 100644 --- a/spec/AdaptableController.spec.js +++ b/spec/AdaptableController.spec.js @@ -1,8 +1,6 @@ -const AdaptableController = - require('../lib/Controllers/AdaptableController').AdaptableController; +const AdaptableController = require('../lib/Controllers/AdaptableController').AdaptableController; const FilesAdapter = require('../lib/Adapters/Files/FilesAdapter').default; -const FilesController = - require('../lib/Controllers/FilesController').FilesController; +const FilesController = require('../lib/Controllers/FilesController').FilesController; const MockController = function (options) { AdaptableController.call(this, options); diff --git a/spec/AdapterLoader.spec.js b/spec/AdapterLoader.spec.js index 2c8a518ef6..dd726bc768 100644 --- a/spec/AdapterLoader.spec.js +++ b/spec/AdapterLoader.spec.js @@ -30,9 +30,7 @@ describe('AdapterLoader', () => { }); it('should instantiate an adapter from string that is module', done => { - const adapterPath = require('path').resolve( - './lib/Adapters/Files/FilesAdapter' - ); + const adapterPath = require('path').resolve('./lib/Adapters/Files/FilesAdapter'); const adapter = loadAdapter({ adapter: adapterPath, }); @@ -120,9 +118,7 @@ describe('AdapterLoader', () => { }); it('should load custom push adapter from string (#3544)', done => { - const adapterPath = require('path').resolve( - './spec/support/MockPushAdapter' - ); + const adapterPath = require('path').resolve('./spec/support/MockPushAdapter'); const options = { ios: { bundleId: 'bundle.id', @@ -146,9 +142,7 @@ describe('AdapterLoader', () => { }); it('should load custom database adapter from config', done => { - const adapterPath = require('path').resolve( - './spec/support/MockDatabaseAdapter' - ); + const adapterPath = require('path').resolve('./spec/support/MockDatabaseAdapter'); const options = { databaseURI: 'oracledb://user:password@localhost:1521/freepdb1', collectionPrefix: '', diff --git a/spec/AggregateRouter.spec.js b/spec/AggregateRouter.spec.js index bd48c7afed..96aedcc313 100644 --- a/spec/AggregateRouter.spec.js +++ b/spec/AggregateRouter.spec.js @@ -1,5 +1,4 @@ -const AggregateRouter = - require('../lib/Routers/AggregateRouter').AggregateRouter; +const AggregateRouter = require('../lib/Routers/AggregateRouter').AggregateRouter; describe('AggregateRouter', () => { it('get pipeline from Array', () => { @@ -160,17 +159,12 @@ describe('AggregateRouter', () => { it('should throw with invalid stage', () => { expect(() => AggregateRouter.getPipeline([{ foo: 'bar' }])).toThrow( - new Parse.Error( - Parse.Error.INVALID_QUERY, - `Invalid aggregate stage 'foo'.` - ) + new Parse.Error(Parse.Error.INVALID_QUERY, `Invalid aggregate stage 'foo'.`) ); }); it('should throw with invalid group', () => { - expect(() => - AggregateRouter.getPipeline([{ $group: { objectId: 'bar' } }]) - ).toThrow( + expect(() => AggregateRouter.getPipeline([{ $group: { objectId: 'bar' } }])).toThrow( new Parse.Error( Parse.Error.INVALID_QUERY, `Cannot use 'objectId' in aggregation stage $group.` diff --git a/spec/AudienceRouter.spec.js b/spec/AudienceRouter.spec.js index e1f15a75ad..da0ebdc96c 100644 --- a/spec/AudienceRouter.spec.js +++ b/spec/AudienceRouter.spec.js @@ -2,8 +2,7 @@ const auth = require('../lib/Auth'); const Config = require('../lib/Config'); const rest = require('../lib/rest'); const request = require('../lib/request'); -const AudiencesRouter = - require('../lib/Routers/AudiencesRouter').AudiencesRouter; +const AudiencesRouter = require('../lib/Routers/AudiencesRouter').AudiencesRouter; describe('AudiencesRouter', () => { it('uses find condition from request.body', done => { @@ -32,12 +31,7 @@ describe('AudiencesRouter', () => { rest .create(config, auth.nobody(config), '_Audience', androidAudienceRequest) .then(() => { - return rest.create( - config, - auth.nobody(config), - '_Audience', - iosAudienceRequest - ); + return rest.create(config, auth.nobody(config), '_Audience', iosAudienceRequest); }) .then(() => { return router.handleFind(request); @@ -79,12 +73,7 @@ describe('AudiencesRouter', () => { rest .create(config, auth.nobody(config), '_Audience', androidAudienceRequest) .then(() => { - return rest.create( - config, - auth.nobody(config), - '_Audience', - iosAudienceRequest - ); + return rest.create(config, auth.nobody(config), '_Audience', iosAudienceRequest); }) .then(() => { return router.handleFind(request); @@ -125,12 +114,7 @@ describe('AudiencesRouter', () => { rest .create(config, auth.nobody(config), '_Audience', androidAudienceRequest) .then(() => { - return rest.create( - config, - auth.nobody(config), - '_Audience', - iosAudienceRequest - ); + return rest.create(config, auth.nobody(config), '_Audience', iosAudienceRequest); }) .then(() => { return router.handleFind(request); @@ -169,14 +153,7 @@ describe('AudiencesRouter', () => { const router = new AudiencesRouter(); rest .create(config, auth.nobody(config), '_Audience', androidAudienceRequest) - .then(() => - rest.create( - config, - auth.nobody(config), - '_Audience', - iosAudienceRequest - ) - ) + .then(() => rest.create(config, auth.nobody(config), '_Audience', iosAudienceRequest)) .then(() => router.handleFind(request)) .then(res => { const response = res.response; @@ -190,60 +167,47 @@ describe('AudiencesRouter', () => { }); }); - it_exclude_dbs(['postgres'])( - 'query installations with limit = 0 and count = 1', - done => { - const config = Config.get('test'); - const androidAudienceRequest = { - name: 'Android Users', - query: '{ "test": "android" }', - }; - const iosAudienceRequest = { - name: 'Iphone Users', - query: '{ "test": "ios" }', - }; - const request = { - config: config, - auth: auth.master(config), - body: {}, - query: { - limit: 0, - count: 1, - }, - info: {}, - }; + it_exclude_dbs(['postgres'])('query installations with limit = 0 and count = 1', done => { + const config = Config.get('test'); + const androidAudienceRequest = { + name: 'Android Users', + query: '{ "test": "android" }', + }; + const iosAudienceRequest = { + name: 'Iphone Users', + query: '{ "test": "ios" }', + }; + const request = { + config: config, + auth: auth.master(config), + body: {}, + query: { + limit: 0, + count: 1, + }, + info: {}, + }; - const router = new AudiencesRouter(); - rest - .create( - config, - auth.nobody(config), - '_Audience', - androidAudienceRequest - ) - .then(() => { - return rest.create( - config, - auth.nobody(config), - '_Audience', - iosAudienceRequest - ); - }) - .then(() => { - return router.handleFind(request); - }) - .then(res => { - const response = res.response; - expect(response.results.length).toEqual(0); - expect(response.count).toEqual(2); - done(); - }) - .catch(err => { - fail(JSON.stringify(err)); - done(); - }); - } - ); + const router = new AudiencesRouter(); + rest + .create(config, auth.nobody(config), '_Audience', androidAudienceRequest) + .then(() => { + return rest.create(config, auth.nobody(config), '_Audience', iosAudienceRequest); + }) + .then(() => { + return router.handleFind(request); + }) + .then(res => { + const response = res.response; + expect(response.results.length).toEqual(0); + expect(response.count).toEqual(2); + done(); + }) + .catch(err => { + fail(JSON.stringify(err)); + done(); + }); + }); it('should create, read, update and delete audiences throw api', done => { Parse._request( @@ -252,54 +216,49 @@ describe('AudiencesRouter', () => { { name: 'My Audience', query: JSON.stringify({ deviceType: 'ios' }) }, { useMasterKey: true } ).then(() => { - Parse._request('GET', 'push_audiences', {}, { useMasterKey: true }).then( - results => { - expect(results.results.length).toEqual(1); - expect(results.results[0].name).toEqual('My Audience'); - expect(results.results[0].query.deviceType).toEqual('ios'); + Parse._request('GET', 'push_audiences', {}, { useMasterKey: true }).then(results => { + expect(results.results.length).toEqual(1); + expect(results.results[0].name).toEqual('My Audience'); + expect(results.results[0].query.deviceType).toEqual('ios'); + Parse._request( + 'GET', + `push_audiences/${results.results[0].objectId}`, + {}, + { useMasterKey: true } + ).then(results => { + expect(results.name).toEqual('My Audience'); + expect(results.query.deviceType).toEqual('ios'); Parse._request( - 'GET', - `push_audiences/${results.results[0].objectId}`, - {}, + 'PUT', + `push_audiences/${results.objectId}`, + { name: 'My Audience 2' }, { useMasterKey: true } - ).then(results => { - expect(results.name).toEqual('My Audience'); - expect(results.query.deviceType).toEqual('ios'); + ).then(() => { Parse._request( - 'PUT', + 'GET', `push_audiences/${results.objectId}`, - { name: 'My Audience 2' }, + {}, { useMasterKey: true } - ).then(() => { + ).then(results => { + expect(results.name).toEqual('My Audience 2'); + expect(results.query.deviceType).toEqual('ios'); Parse._request( - 'GET', + 'DELETE', `push_audiences/${results.objectId}`, {}, { useMasterKey: true } - ).then(results => { - expect(results.name).toEqual('My Audience 2'); - expect(results.query.deviceType).toEqual('ios'); - Parse._request( - 'DELETE', - `push_audiences/${results.objectId}`, - {}, - { useMasterKey: true } - ).then(() => { - Parse._request( - 'GET', - 'push_audiences', - {}, - { useMasterKey: true } - ).then(results => { + ).then(() => { + Parse._request('GET', 'push_audiences', {}, { useMasterKey: true }).then( + results => { expect(results.results.length).toEqual(0); done(); - }); - }); + } + ); }); }); }); - } - ); + }); + }); }); }); @@ -361,8 +320,7 @@ describe('AudiencesRouter', () => { it_id('af1111b5-3251-4b40-8f06-fb0fc624fa91')(it_exclude_dbs(['postgres']))( 'should support legacy parse.com audience fields', done => { - const database = Config.get(Parse.applicationId).database.adapter - .database; + const database = Config.get(Parse.applicationId).database.adapter.database; const now = new Date(); Parse._request( 'POST', diff --git a/spec/Auth.spec.js b/spec/Auth.spec.js index a055cda5bc..a718a1c2d7 100644 --- a/spec/Auth.spec.js +++ b/spec/Auth.spec.js @@ -239,16 +239,10 @@ describe('extendSessionOnUse', () => { const { shouldUpdateSessionExpiry } = require('../lib/Auth'); let update = new Date(Date.now() - 86410 * 1000); - const res = shouldUpdateSessionExpiry( - { sessionLength: 86460 }, - { updatedAt: update } - ); + const res = shouldUpdateSessionExpiry({ sessionLength: 86460 }, { updatedAt: update }); update = new Date(Date.now() - 43210 * 1000); - const res2 = shouldUpdateSessionExpiry( - { sessionLength: 86460 }, - { updatedAt: update } - ); + const res2 = shouldUpdateSessionExpiry({ sessionLength: 86460 }, { updatedAt: update }); expect(res).toBe(true); expect(res2).toBe(false); diff --git a/spec/AuthenticationAdapters.spec.js b/spec/AuthenticationAdapters.spec.js index 28a29fcf85..419a6efc19 100644 --- a/spec/AuthenticationAdapters.spec.js +++ b/spec/AuthenticationAdapters.spec.js @@ -1,7 +1,6 @@ const request = require('../lib/request'); const Config = require('../lib/Config'); -const defaultColumns = - require('../lib/Controllers/SchemaController').defaultColumns; +const defaultColumns = require('../lib/Controllers/SchemaController').defaultColumns; const authenticationLoader = require('../lib/Adapters/Auth'); const path = require('path'); @@ -218,10 +217,7 @@ describe('AuthenticationProviders', function () { ok(model.extended(), 'Should have used the subclass.'); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); - strictEqual( - provider.authData.expiration_date, - provider.synchronizedExpiration - ); + strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); ok(model._isLinked('myoauth'), 'User should be linked to myoauth'); await model._unlinkFrom('myoauth'); @@ -234,11 +230,7 @@ describe('AuthenticationProviders', function () { const res = await config.database.adapter.find( '_User', { - fields: Object.assign( - {}, - defaultColumns._Default, - defaultColumns._Installation - ), + fields: Object.assign({}, defaultColumns._Default, defaultColumns._Installation), }, { objectId: model.id }, {} @@ -261,12 +253,8 @@ describe('AuthenticationProviders', function () { function validateAuthenticationHandler(authenticationHandler) { expect(authenticationHandler).not.toBeUndefined(); - expect(typeof authenticationHandler.getValidatorForProvider).toBe( - 'function' - ); - expect(typeof authenticationHandler.getValidatorForProvider).toBe( - 'function' - ); + expect(typeof authenticationHandler.getValidatorForProvider).toBe('function'); + expect(typeof authenticationHandler.getValidatorForProvider).toBe('function'); } function validateAuthenticationAdapter(authAdapter) { @@ -288,10 +276,7 @@ describe('AuthenticationProviders', function () { return Promise.resolve(); }, validateAuthData: function (authData) { - if ( - authData.id == validAuthData.id && - authData.token == validAuthData.token - ) { + if (authData.id == validAuthData.id && authData.token == validAuthData.token) { return Promise.resolve(); } return Promise.reject(); @@ -306,9 +291,7 @@ describe('AuthenticationProviders', function () { }); validateAuthenticationHandler(authenticationHandler); - const { validator } = authenticationHandler.getValidatorForProvider( - 'customAuthentication' - ); + const { validator } = authenticationHandler.getValidatorForProvider('customAuthentication'); validateValidator(validator); validator(validAuthData, {}, {}).then( @@ -331,9 +314,7 @@ describe('AuthenticationProviders', function () { }); validateAuthenticationHandler(authenticationHandler); - const { validator } = authenticationHandler.getValidatorForProvider( - 'customAuthentication' - ); + const { validator } = authenticationHandler.getValidatorForProvider('customAuthentication'); validateValidator(validator); validator( { @@ -361,9 +342,7 @@ describe('AuthenticationProviders', function () { }); validateAuthenticationHandler(authenticationHandler); - const { validator } = authenticationHandler.getValidatorForProvider( - 'customAuthentication' - ); + const { validator } = authenticationHandler.getValidatorForProvider('customAuthentication'); validateValidator(validator); validator( @@ -390,8 +369,10 @@ describe('AuthenticationProviders', function () { appSecret: 'secret', }, }; - const { adapter, appIds, providerOptions } = - authenticationLoader.loadAuthAdapter('facebook', options); + const { adapter, appIds, providerOptions } = authenticationLoader.loadAuthAdapter( + 'facebook', + options + ); validateAuthenticationAdapter(adapter); expect(appIds).toEqual(['a', 'b']); expect(providerOptions).toEqual(options.facebook); @@ -411,12 +392,12 @@ describe('AuthenticationProviders', function () { const authData = { access_token: 'badtoken', }; - const { adapter, appIds, providerOptions } = - authenticationLoader.loadAuthAdapter('facebook', options); + const { adapter, appIds, providerOptions } = authenticationLoader.loadAuthAdapter( + 'facebook', + options + ); await adapter.validateAppId(appIds, authData, providerOptions); - expect( - httpsRequest.get.calls.first().args[0].includes('appsecret_proof') - ).toBe(true); + expect(httpsRequest.get.calls.first().args[0].includes('appsecret_proof')).toBe(true); }); it('should throw error when Facebook request appId is wrong data type', async () => { @@ -433,11 +414,11 @@ describe('AuthenticationProviders', function () { const authData = { access_token: 'badtoken', }; - const { adapter, appIds, providerOptions } = - authenticationLoader.loadAuthAdapter('facebook', options); - await expectAsync( - adapter.validateAppId(appIds, authData, providerOptions) - ).toBeRejectedWith( + const { adapter, appIds, providerOptions } = authenticationLoader.loadAuthAdapter( + 'facebook', + options + ); + await expectAsync(adapter.validateAppId(appIds, authData, providerOptions)).toBeRejectedWith( new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'appIds must be an array.') ); }); @@ -457,14 +438,9 @@ describe('AuthenticationProviders', function () { id: 'test', access_token: 'test', }; - const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter( - 'facebook', - options - ); + const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter('facebook', options); await adapter.validateAuthData(authData, providerOptions); - expect( - httpsRequest.get.calls.first().args[0].includes('appsecret_proof') - ).toBe(true); + expect(httpsRequest.get.calls.first().args[0].includes('appsecret_proof')).toBe(true); }); it('properly loads a custom adapter with options', () => { @@ -475,8 +451,10 @@ describe('AuthenticationProviders', function () { appIds: ['a', 'b'], }, }; - const { adapter, appIds, providerOptions } = - authenticationLoader.loadAuthAdapter('custom', options); + const { adapter, appIds, providerOptions } = authenticationLoader.loadAuthAdapter( + 'custom', + options + ); validateAuthenticationAdapter(adapter); expect(appIds).toEqual(['a', 'b']); expect(providerOptions).toEqual(options.custom); @@ -494,10 +472,7 @@ describe('AuthenticationProviders', function () { const provider = getMockMyOauthProvider(); Parse.User._registerAuthenticationProvider(provider); await expectAsync(Parse.User._logInWith('myoauth')).toBeRejectedWith( - new Parse.Error( - Parse.Error.UNSUPPORTED_SERVICE, - 'This authentication method is unsupported.' - ) + new Parse.Error(Parse.Error.UNSUPPORTED_SERVICE, 'This authentication method is unsupported.') ); }); }); @@ -518,10 +493,7 @@ describe('google auth adapter', () => { it('should not decode invalid id_token', async () => { try { - await google.validateAuthData( - { id: 'the_user_id', id_token: 'the_token' }, - {} - ); + await google.validateAuthData({ id: 'the_user_id', id_token: 'the_token' }, {}); fail(); } catch (e) { expect(e.message).toBe('provided token does not decode as JWT'); @@ -665,10 +637,7 @@ describe('keycloak auth adapter', () => { id: 'fakeid', access_token: 'sometoken', }; - const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter( - 'keycloak', - options - ); + const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter('keycloak', options); try { await adapter.validateAuthData(authData, providerOptions); fail(); @@ -695,10 +664,7 @@ describe('keycloak auth adapter', () => { id: 'fakeid', access_token: 'sometoken', }; - const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter( - 'keycloak', - options - ); + const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter('keycloak', options); try { await adapter.validateAuthData(authData, providerOptions); fail(); @@ -725,10 +691,7 @@ describe('keycloak auth adapter', () => { id: 'fakeid', access_token: 'sometoken', }; - const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter( - 'keycloak', - options - ); + const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter('keycloak', options); try { await adapter.validateAuthData(authData, providerOptions); fail(); @@ -753,10 +716,7 @@ describe('keycloak auth adapter', () => { id: 'fakeid', access_token: 'sometoken', }; - const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter( - 'keycloak', - options - ); + const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter('keycloak', options); try { await adapter.validateAuthData(authData, providerOptions); fail(); @@ -789,10 +749,7 @@ describe('keycloak auth adapter', () => { roles: ['role1'], groups: ['group1'], }; - const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter( - 'keycloak', - options - ); + const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter('keycloak', options); try { await adapter.validateAuthData(authData, providerOptions); fail(); @@ -825,10 +782,7 @@ describe('keycloak auth adapter', () => { roles: ['role1'], groups: ['group1'], }; - const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter( - 'keycloak', - options - ); + const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter('keycloak', options); try { await adapter.validateAuthData(authData, providerOptions); fail(); @@ -861,10 +815,7 @@ describe('keycloak auth adapter', () => { roles: ['role1'], groups: ['group1'], }; - const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter( - 'keycloak', - options - ); + const { adapter, providerOptions } = authenticationLoader.loadAuthAdapter('keycloak', options); await adapter.validateAuthData(authData, providerOptions); expect(httpsRequest.get).toHaveBeenCalledWith({ host: 'http://example.com', @@ -914,9 +865,7 @@ describe('apple signin auth adapter', () => { it('should throw error if public key used to encode token is not available', async () => { const fakeDecodedToken = { header: { kid: '789', alg: 'RS256' } }; try { - spyOn(authUtils, 'getHeaderFromToken').and.callFake( - () => fakeDecodedToken.header - ); + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken.header); await apple.validateAuthData( { id: 'the_user_id', token: 'the_token' }, @@ -939,9 +888,7 @@ describe('apple signin auth adapter', () => { }; const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake( - () => fakeDecodedToken.header - ); + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken.header); spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); spyOn(jwt, 'verify').and.callFake(() => fakeClaim); @@ -950,9 +897,7 @@ describe('apple signin auth adapter', () => { { clientId: 'secret' } ); expect(result).toEqual(fakeClaim); - expect(jwt.verify.calls.first().args[2].algorithms).toEqual( - fakeDecodedToken.header.alg - ); + expect(jwt.verify.calls.first().args[2].algorithms).toEqual(fakeDecodedToken.header.alg); }); it('should not verify invalid id_token', async () => { @@ -1207,9 +1152,7 @@ describe('phant auth adapter', () => { }; const { adapter } = authenticationLoader.loadAuthAdapter('phantauth', {}); - spyOn(httpsRequest, 'get').and.callFake(() => - Promise.resolve({ sub: 'invalidID' }) - ); + spyOn(httpsRequest, 'get').and.callFake(() => Promise.resolve({ sub: 'invalidID' })); try { await adapter.validateAuthData(authData); fail(); @@ -1261,9 +1204,7 @@ describe('facebook limited auth adapter', () => { header: { kid: '789', alg: 'RS256' }, }; try { - spyOn(authUtils, 'getHeaderFromToken').and.callFake( - () => fakeDecodedToken.header - ); + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken.header); await facebook.validateAuthData( { id: 'the_user_id', token: 'the_token' }, @@ -1288,9 +1229,7 @@ describe('facebook limited auth adapter', () => { }; const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake( - () => fakeDecodedToken.header - ); + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken.header); spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); spyOn(jwt, 'verify').and.callFake(() => fakeClaim); @@ -1299,9 +1238,7 @@ describe('facebook limited auth adapter', () => { { clientId: 'secret' } ); expect(result).toEqual(fakeClaim); - expect(jwt.verify.calls.first().args[2].algorithms).toEqual( - fakeDecodedToken.header.alg - ); + expect(jwt.verify.calls.first().args[2].algorithms).toEqual(fakeDecodedToken.header.alg); } ); @@ -1345,9 +1282,7 @@ describe('facebook limited auth adapter', () => { }; const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake( - () => fakeDecodedToken - ); + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); spyOn(jwt, 'verify').and.callFake(() => fakeClaim); @@ -1370,9 +1305,7 @@ describe('facebook limited auth adapter', () => { }; const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake( - () => fakeDecodedToken - ); + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); spyOn(jwt, 'verify').and.callFake(() => fakeClaim); @@ -1395,9 +1328,7 @@ describe('facebook limited auth adapter', () => { }; const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake( - () => fakeDecodedToken - ); + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); spyOn(jwt, 'verify').and.callFake(() => fakeClaim); @@ -1418,9 +1349,7 @@ describe('facebook limited auth adapter', () => { }; const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake( - () => fakeDecodedToken - ); + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); spyOn(jwt, 'verify').and.callFake(() => fakeClaim); @@ -1555,9 +1484,7 @@ describe('facebook limited auth adapter', () => { }; const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake( - () => fakeDecodedToken - ); + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey); spyOn(jwt, 'verify').and.callFake(() => fakeClaim); @@ -1736,9 +1663,7 @@ describe('OTP TOTP auth adatper', () => { }, { sessionToken: user.getSessionToken() } ) - ).toBeRejectedWith( - new Parse.Error(Parse.Error.OTHER_CAUSE, 'Invalid MFA token') - ); + ).toBeRejectedWith(new Parse.Error(Parse.Error.OTHER_CAUSE, 'Invalid MFA token')); await user.fetch({ useMasterKey: true }); expect(user.get('authData').mfa.secret).toEqual(secret.base32); }); @@ -1758,13 +1683,8 @@ describe('OTP TOTP auth adatper', () => { { authData: { mfa: { secret: secret.base32, token } } }, { sessionToken: user.getSessionToken() } ); - await expectAsync( - Parse.User.logIn('username', 'password') - ).toBeRejectedWith( - new Parse.Error( - Parse.Error.OTHER_CAUSE, - 'Missing additional authData mfa' - ) + await expectAsync(Parse.User.logIn('username', 'password')).toBeRejectedWith( + new Parse.Error(Parse.Error.OTHER_CAUSE, 'Missing additional authData mfa') ); }); @@ -1842,10 +1762,7 @@ describe('OTP SMS auth adatper', () => { const user = await Parse.User.signUp('username', 'password'); const sessionToken = user.getSessionToken(); const spy = spyOn(mfa, 'sendSMS').and.callThrough(); - await user.save( - { authData: { mfa: { mobile: '+11111111111' } } }, - { sessionToken } - ); + await user.save({ authData: { mfa: { mobile: '+11111111111' } } }, { sessionToken }); await user.fetch({ sessionToken }); expect(user.get('authData')).toEqual({ mfa: { status: 'disabled' } }); expect(spy).toHaveBeenCalledWith(code, '+11111111111'); @@ -1855,10 +1772,7 @@ describe('OTP SMS auth adatper', () => { expect(authData['+11111111111']).toBeDefined(); expect(Object.keys(authData['+11111111111'])).toEqual(['token', 'expiry']); - await user.save( - { authData: { mfa: { mobile, token: code } } }, - { sessionToken } - ); + await user.save({ authData: { mfa: { mobile, token: code } } }, { sessionToken }); await user.fetch({ sessionToken }); expect(user.get('authData')).toEqual({ mfa: { status: 'enabled' } }); }); @@ -1878,13 +1792,8 @@ describe('OTP SMS auth adatper', () => { spy.calls.reset(); - await expectAsync( - Parse.User.logIn('username', 'password') - ).toBeRejectedWith( - new Parse.Error( - Parse.Error.OTHER_CAUSE, - 'Missing additional authData mfa' - ) + await expectAsync(Parse.User.logIn('username', 'password')).toBeRejectedWith( + new Parse.Error(Parse.Error.OTHER_CAUSE, 'Missing additional authData mfa') ); const res = await request({ headers, diff --git a/spec/AuthenticationAdaptersV2.spec.js b/spec/AuthenticationAdaptersV2.spec.js index 486e6b4dc2..3167dc7029 100644 --- a/spec/AuthenticationAdaptersV2.spec.js +++ b/spec/AuthenticationAdaptersV2.spec.js @@ -13,8 +13,7 @@ describe('Auth Adapter features', () => { validateAuthData: () => Promise.resolve(), }; const baseAdapter2 = { - validateAppId: appIds => - appIds[0] === 'test' ? Promise.resolve() : Promise.reject(), + validateAppId: appIds => (appIds[0] === 'test' ? Promise.resolve() : Promise.reject()), validateAuthData: () => Promise.resolve(), appIds: ['test'], options: { anOption: true }, @@ -367,15 +366,11 @@ describe('Auth Adapter features', () => { break; } } - expect(afterSpy).toHaveBeenCalledWith( - { id: 'modernAdapter3Data' }, - undefined, - { - ip: '127.0.0.1', - user, - master: false, - } - ); + expect(afterSpy).toHaveBeenCalledWith({ id: 'modernAdapter3Data' }, undefined, { + ip: '127.0.0.1', + user, + master: false, + }); expect(spy).toHaveBeenCalled(); }); @@ -854,10 +849,7 @@ describe('Auth Adapter features', () => { expect(firstCall[2].user.id).toEqual(user.id); expect(firstCall.length).toEqual(3); - await user.save( - { authData: { baseAdapter2: payload } }, - { useMasterKey: true } - ); + await user.save({ authData: { baseAdapter2: payload } }, { useMasterKey: true }); const secondCall = baseAdapter2.validateAuthData.calls.argsFor(1); expect(secondCall[0]).toEqual(payload); @@ -883,8 +875,7 @@ describe('Auth Adapter features', () => { }; const throwInSetup = { validateAppId: () => Promise.resolve(), - validateSetUp: () => - Promise.reject('You cannot signup with that setup data.'), + validateSetUp: () => Promise.reject('You cannot signup with that setup data.'), validateUpdate: () => Promise.resolve(), validateLogin: () => Promise.resolve(), }; @@ -892,8 +883,7 @@ describe('Auth Adapter features', () => { const throwInUpdate = { validateAppId: () => Promise.resolve(), validateSetUp: () => Promise.resolve(), - validateUpdate: () => - Promise.reject('You cannot update with that update data.'), + validateUpdate: () => Promise.reject('You cannot update with that update data.'), validateLogin: () => Promise.resolve(), }; @@ -901,8 +891,7 @@ describe('Auth Adapter features', () => { validateAppId: () => Promise.resolve(), validateSetUp: () => Promise.resolve(), validateUpdate: () => Promise.resolve(), - validateLogin: () => - Promise.reject('You cannot login with that login data.'), + validateLogin: () => Promise.reject('You cannot login with that login data.'), }; await reconfigureServer({ auth: { challengeAdapter: throwInChallengeAdapter }, @@ -925,10 +914,7 @@ describe('Auth Adapter features', () => { `Failed running auth step challenge for challengeAdapter for user undefined with Error: {"message":"Invalid challenge data: yolo","code":${Parse.Error.SCRIPT_FAILED}}`, { authenticationStep: 'challenge', - error: new Parse.Error( - Parse.Error.SCRIPT_FAILED, - 'Invalid challenge data: yolo' - ), + error: new Parse.Error(Parse.Error.SCRIPT_FAILED, 'Invalid challenge data: yolo'), user: undefined, provider: 'challengeAdapter', } @@ -941,10 +927,7 @@ describe('Auth Adapter features', () => { await expectAsync( user.save({ authData: { modernAdapter: { id: 'modernAdapter' } } }) ).toBeRejectedWith( - new Parse.Error( - Parse.Error.SCRIPT_FAILED, - 'You cannot signup with that setup data.' - ) + new Parse.Error(Parse.Error.SCRIPT_FAILED, 'You cannot signup with that setup data.') ); expect(logger.error).toHaveBeenCalledWith( `Failed running auth step validateSetUp for modernAdapter for user undefined with Error: {"message":"You cannot signup with that setup data.","code":${Parse.Error.SCRIPT_FAILED}}`, @@ -970,10 +953,7 @@ describe('Auth Adapter features', () => { { sessionToken: user.getSessionToken() } ) ).toBeRejectedWith( - new Parse.Error( - Parse.Error.SCRIPT_FAILED, - 'You cannot update with that update data.' - ) + new Parse.Error(Parse.Error.SCRIPT_FAILED, 'You cannot update with that update data.') ); expect(logger.error).toHaveBeenCalledWith( @@ -1001,19 +981,13 @@ describe('Auth Adapter features', () => { await expectAsync( user2.save({ authData: { modernAdapter: { id: 'modernAdapter' } } }) ).toBeRejectedWith( - new Parse.Error( - Parse.Error.SCRIPT_FAILED, - 'You cannot login with that login data.' - ) + new Parse.Error(Parse.Error.SCRIPT_FAILED, 'You cannot login with that login data.') ); expect(logger.error).toHaveBeenCalledWith( `Failed running auth step validateLogin for modernAdapter for user ${user.id} with Error: {"message":"You cannot login with that login data.","code":${Parse.Error.SCRIPT_FAILED}}`, { authenticationStep: 'validateLogin', - error: new Parse.Error( - Parse.Error.SCRIPT_FAILED, - 'You cannot login with that login data.' - ), + error: new Parse.Error(Parse.Error.SCRIPT_FAILED, 'You cannot login with that login data.'), user: user.id, provider: 'modernAdapter', } @@ -1128,9 +1102,7 @@ describe('Auth Adapter features', () => { }, }), }) - ).toBeRejectedWithError( - 'You provided username or email, you need to also provide password.' - ); + ).toBeRejectedWithError('You provided username or email, you need to also provide password.'); await expectAsync( requestWithExpectedError({ @@ -1233,9 +1205,7 @@ describe('Auth Adapter features', () => { }, }), }) - ).toBeRejectedWithError( - 'You cannot provide more than one authData provider with an id.' - ); + ).toBeRejectedWithError('You cannot provide more than one authData provider with an id.'); const res = await request({ headers: headers, diff --git a/spec/CLI.spec.js b/spec/CLI.spec.js index 06369fbdd4..76f8e3952d 100644 --- a/spec/CLI.spec.js +++ b/spec/CLI.spec.js @@ -1,8 +1,7 @@ 'use strict'; let commander; const definitions = require('../lib/cli/definitions/parse-server').default; -const liveQueryDefinitions = - require('../lib/cli/definitions/parse-live-query-server').default; +const liveQueryDefinitions = require('../lib/cli/definitions/parse-live-query-server').default; const path = require('path'); const { spawn } = require('child_process'); @@ -83,15 +82,12 @@ describe('commander additions', () => { it('should load properly use args over env', () => { commander.loadDefinitions(testDefinitions); - commander.parse( - ['node', './CLI.spec.js', '--arg0', 'arg0Value', '--arg4', ''], - { - PROGRAM_ARG_0: 'arg0ENVValue', - PROGRAM_ARG_1: 'arg1ENVValue', - PROGRAM_ARG_2: '4', - PROGRAM_ARG_4: 'arg4ENVValue', - } - ); + commander.parse(['node', './CLI.spec.js', '--arg0', 'arg0Value', '--arg4', ''], { + PROGRAM_ARG_0: 'arg0ENVValue', + PROGRAM_ARG_1: 'arg1ENVValue', + PROGRAM_ARG_2: '4', + PROGRAM_ARG_4: 'arg4ENVValue', + }); expect(commander.arg0).toEqual('arg0Value'); expect(commander.arg1).toEqual('arg1ENVValue'); expect(commander.arg2).toEqual(4); @@ -114,13 +110,7 @@ describe('commander additions', () => { spyOn(console, 'log').and.callFake(() => {}); commander.loadDefinitions(testDefinitions); commander.parse( - [ - 'node', - './CLI.spec.js', - '--arg0', - 'arg0Value', - './spec/configs/CLIConfig.json', - ], + ['node', './CLI.spec.js', '--arg0', 'arg0Value', './spec/configs/CLIConfig.json'], { PROGRAM_ARG_0: 'arg0ENVValue', PROGRAM_ARG_1: 'arg1ENVValue', @@ -137,13 +127,7 @@ describe('commander additions', () => { commander.loadDefinitions(testDefinitions); expect(() => { commander.parse( - [ - 'node', - './CLI.spec.js', - '--arg0', - 'arg0Value', - './spec/configs/CLIConfigFail.json', - ], + ['node', './CLI.spec.js', '--arg0', 'arg0Value', './spec/configs/CLIConfigFail.json'], { PROGRAM_ARG_0: 'arg0ENVValue', PROGRAM_ARG_1: 'arg1ENVValue', @@ -156,11 +140,7 @@ describe('commander additions', () => { it('should fail when too many apps are set', done => { commander.loadDefinitions(testDefinitions); expect(() => { - commander.parse([ - 'node', - './CLI.spec.js', - './spec/configs/CLIConfigFailTooManyApps.json', - ]); + commander.parse(['node', './CLI.spec.js', './spec/configs/CLIConfigFailTooManyApps.json']); }).toThrow('Multiple apps are not supported'); done(); }); @@ -168,11 +148,7 @@ describe('commander additions', () => { it('should load config from apps', done => { spyOn(console, 'log').and.callFake(() => {}); commander.loadDefinitions(testDefinitions); - commander.parse([ - 'node', - './CLI.spec.js', - './spec/configs/CLIConfigApps.json', - ]); + commander.parse(['node', './CLI.spec.js', './spec/configs/CLIConfigApps.json']); const options = commander.getOptions(); expect(options.arg1).toBe('my_app'); expect(options.arg2).toBe(8888); @@ -184,11 +160,7 @@ describe('commander additions', () => { it('should fail when passing an invalid arguement', done => { commander.loadDefinitions(testDefinitions); expect(() => { - commander.parse([ - 'node', - './CLI.spec.js', - './spec/configs/CLIConfigUnknownArg.json', - ]); + commander.parse(['node', './CLI.spec.js', './spec/configs/CLIConfigUnknownArg.json']); }).toThrow('error: unknown option myArg'); done(); }); @@ -227,10 +199,7 @@ describe('LiveQuery definitions', () => { if (typeof definition.env !== 'undefined') { expect(typeof definition.env).toBe('string'); } - expect(typeof definition.help).toBe( - 'string', - `help for ${key} should be a string` - ); + expect(typeof definition.help).toBe('string', `help for ${key} should be a string`); if (typeof definition.required !== 'undefined') { expect(typeof definition.required).toBe('boolean'); } @@ -289,32 +258,18 @@ describe('execution', () => { } }); - it_id('a0ab74b4-f805-4e03-b31d-b5cd59e64495')(it)( - 'should start Parse Server', - done => { - const env = { ...process.env }; - env.NODE_OPTIONS = '--dns-result-order=ipv4first --trace-deprecation'; - childProcess = spawn( - binPath, - [ - '--appId', - 'test', - '--masterKey', - 'test', - '--databaseURI', - databaseURI, - '--port', - '1339', - ], - { env } - ); - handleStdout(childProcess, done, aggregatedData, [ - 'parse-server running on', - ]); - handleStderr(childProcess, done); - handleError(childProcess, done); - } - ); + it_id('a0ab74b4-f805-4e03-b31d-b5cd59e64495')(it)('should start Parse Server', done => { + const env = { ...process.env }; + env.NODE_OPTIONS = '--dns-result-order=ipv4first --trace-deprecation'; + childProcess = spawn( + binPath, + ['--appId', 'test', '--masterKey', 'test', '--databaseURI', databaseURI, '--port', '1339'], + { env } + ); + handleStdout(childProcess, done, aggregatedData, ['parse-server running on']); + handleStderr(childProcess, done); + handleError(childProcess, done); + }); it_id('d7165081-b133-4cba-901b-19128ce41301')(it)( 'should start Parse Server with GraphQL', @@ -386,9 +341,7 @@ describe('execution', () => { ['--databaseURI', databaseURI, './spec/configs/CLIConfigAuth.json'], { env } ); - handleStdout(childProcess, done, aggregatedData, [ - 'parse-server running on', - ]); + handleStdout(childProcess, done, aggregatedData, ['parse-server running on']); handleStderr(childProcess, done); handleError(childProcess, done); } diff --git a/spec/CacheController.spec.js b/spec/CacheController.spec.js index ff401f904e..de07126214 100644 --- a/spec/CacheController.spec.js +++ b/spec/CacheController.spec.js @@ -1,5 +1,4 @@ -const CacheController = - require('../lib/Controllers/CacheController.js').default; +const CacheController = require('../lib/Controllers/CacheController.js').default; describe('CacheController', function () { let FakeCacheAdapter; diff --git a/spec/Client.spec.js b/spec/Client.spec.js index 5bf24cbcb8..0de226204a 100644 --- a/spec/Client.spec.js +++ b/spec/Client.spec.js @@ -1,6 +1,5 @@ const Client = require('../lib/LiveQuery/Client').Client; -const ParseWebSocket = - require('../lib/LiveQuery/ParseWebSocketServer').ParseWebSocket; +const ParseWebSocket = require('../lib/LiveQuery/ParseWebSocketServer').ParseWebSocket; describe('Client', function () { it('can be initialized', function () { @@ -88,9 +87,7 @@ describe('Client', function () { }; const client = new Client(1, {}); - expect(client._toJSONWithFields(parseObjectJSON, null)).toBe( - parseObjectJSON - ); + expect(client._toJSONWithFields(parseObjectJSON, null)).toBe(parseObjectJSON); }); it('can generate ParseObject JSON with undefined selected field', function () { @@ -104,9 +101,7 @@ describe('Client', function () { }; const client = new Client(1, {}); - expect(client._toJSONWithFields(parseObjectJSON, undefined)).toBe( - parseObjectJSON - ); + expect(client._toJSONWithFields(parseObjectJSON, undefined)).toBe(parseObjectJSON); }); it('can generate ParseObject JSON with selected fields', function () { @@ -142,9 +137,7 @@ describe('Client', function () { test: 'test', }; const client = new Client(1, {}); - const limitedParseObject = client._toJSONWithFields(parseObjectJSON, [ - 'name', - ]); + const limitedParseObject = client._toJSONWithFields(parseObjectJSON, ['name']); expect(limitedParseObject).toEqual({ className: 'test', diff --git a/spec/CloudCode.Validator.spec.js b/spec/CloudCode.Validator.spec.js index fce6f2c21e..9b33ddc213 100644 --- a/spec/CloudCode.Validator.spec.js +++ b/spec/CloudCode.Validator.spec.js @@ -7,10 +7,7 @@ const validatorSuccess = () => { return true; }; function testConfig() { - return Parse.Config.save( - { internal: 'i', string: 's', number: 12 }, - { internal: true } - ); + return Parse.Config.save({ internal: 'i', string: 's', number: 12 }, { internal: true }); } describe('cloud validator', () => { @@ -151,9 +148,7 @@ describe('cloud validator', () => { fail('function should have failed.'); } catch (error) { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual( - 'Validation failed. Please login to continue.' - ); + expect(error.message).toEqual('Validation failed. Please login to continue.'); done(); } }); @@ -197,9 +192,7 @@ describe('cloud validator', () => { }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual( - 'Validation failed. Please specify data for a.' - ); + expect(error.message).toEqual('Validation failed. Please specify data for a.'); done(); }); }); @@ -244,9 +237,7 @@ describe('cloud validator', () => { }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual( - 'Validation failed. Invalid type for data. Expected: array' - ); + expect(error.message).toEqual('Validation failed. Invalid type for data. Expected: array'); done(); }); }); @@ -289,9 +280,7 @@ describe('cloud validator', () => { }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual( - 'Validation failed. Invalid type for data. Expected: string' - ); + expect(error.message).toEqual('Validation failed. Invalid type for data. Expected: string'); done(); }); }); @@ -343,9 +332,7 @@ describe('cloud validator', () => { }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual( - 'Validation failed. Please specify data for data.' - ); + expect(error.message).toEqual('Validation failed. Please specify data for data.'); done(); }); }); @@ -365,8 +352,7 @@ describe('cloud validator', () => { options: s => { return s.length >= 4 && s.length <= 50; }, - error: - 'Validation failed. Expected length of data to be between 4 and 50.', + error: 'Validation failed. Expected length of data to be between 4 and 50.', }, }, } @@ -406,9 +392,7 @@ describe('cloud validator', () => { }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual( - 'Validation failed. Invalid type for data. Expected: string' - ); + expect(error.message).toEqual('Validation failed. Invalid type for data. Expected: string'); done(); }); }); @@ -487,9 +471,7 @@ describe('cloud validator', () => { }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual( - 'Validation failed. Invalid option for data. Expected: a' - ); + expect(error.message).toEqual('Validation failed. Invalid option for data. Expected: a'); done(); }); }); @@ -517,9 +499,7 @@ describe('cloud validator', () => { }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual( - 'Validation failed. Invalid option for data. Expected: a, b' - ); + expect(error.message).toEqual('Validation failed. Invalid option for data. Expected: a, b'); done(); }); }); @@ -550,9 +530,7 @@ describe('cloud validator', () => { }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual( - 'Validation failed. Expected data to be between 1 and 5.' - ); + expect(error.message).toEqual('Validation failed. Expected data to be between 1 and 5.'); done(); }); }); @@ -581,9 +559,7 @@ describe('cloud validator', () => { }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual( - 'Validation failed. String should be at least 5 characters' - ); + expect(error.message).toEqual('Validation failed. String should be at least 5 characters'); done(); }); }); @@ -639,9 +615,7 @@ describe('cloud validator', () => { }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual( - 'Validation failed. Invalid value for data.' - ); + expect(error.message).toEqual('Validation failed. Invalid value for data.'); done(); }); }); @@ -817,9 +791,7 @@ describe('cloud validator', () => { fail('function should have failed.'); } catch (error) { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual( - 'Validation failed. Please specify data for foo.' - ); + expect(error.message).toEqual('Validation failed. Please specify data for foo.'); done(); } }); @@ -873,9 +845,7 @@ describe('cloud validator', () => { fail('should not have been able to save without userkey'); } catch (error) { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual( - 'Validation failed. Please set data for name on your account.' - ); + expect(error.message).toEqual('Validation failed. Please set data for name on your account.'); done(); } }); @@ -1025,9 +995,7 @@ describe('cloud validator', () => { }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual( - 'Validation failed. Please login to continue.' - ); + expect(error.message).toEqual('Validation failed. Please login to continue.'); done(); }); }); @@ -1048,9 +1016,7 @@ describe('cloud validator', () => { await Parse.Cloud.run('cloudFunction'); fail('cloud validator should have failed.'); } catch (e) { - expect(e.message).toBe( - 'Validation failed. User does not match the required roles.' - ); + expect(e.message).toBe('Validation failed. User does not match the required roles.'); } const roleACL = new Parse.ACL(); roleACL.setPublicReadAccess(true); @@ -1077,9 +1043,7 @@ describe('cloud validator', () => { await Parse.Cloud.run('cloudFunction'); fail('cloud validator should have failed.'); } catch (e) { - expect(e.message).toBe( - 'Validation failed. User does not match all the required roles.' - ); + expect(e.message).toBe('Validation failed. User does not match all the required roles.'); } const roleACL = new Parse.ACL(); roleACL.setPublicReadAccess(true); @@ -1112,9 +1076,7 @@ describe('cloud validator', () => { await Parse.Cloud.run('cloudFunction'); fail('cloud validator should have failed.'); } catch (e) { - expect(e.message).toBe( - 'Validation failed. User does not match the required roles.' - ); + expect(e.message).toBe('Validation failed. User does not match the required roles.'); } const roleACL = new Parse.ACL(); roleACL.setPublicReadAccess(true); @@ -1143,9 +1105,7 @@ describe('cloud validator', () => { await Parse.Cloud.run('cloudFunction'); fail('cloud validator should have failed.'); } catch (e) { - expect(e.message).toBe( - 'Validation failed. User does not match all the required roles.' - ); + expect(e.message).toBe('Validation failed. User does not match all the required roles.'); } const roleACL = new Parse.ACL(); roleACL.setPublicReadAccess(true); @@ -1233,9 +1193,7 @@ describe('cloud validator', () => { }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual( - 'Validation failed. Please login to continue.' - ); + expect(error.message).toEqual('Validation failed. Please login to continue.'); done(); }); }); @@ -1260,9 +1218,7 @@ describe('cloud validator', () => { }) .catch(error => { expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual( - 'Validation failed. Please specify data for bar.' - ); + expect(error.message).toEqual('Validation failed. Please specify data for bar.'); done(); }); }); @@ -1605,16 +1561,13 @@ describe('cloud validator', () => { } ); - it_id('b18b9a6a-0e35-4b60-9771-30f53501df3c')(it)( - 'validate afterSave Parse.Config', - async () => { - Parse.Cloud.afterSave(Parse.Config, () => {}, validatorSuccess); - const config = await testConfig(); - expect(config.get('internal')).toBe('i'); - expect(config.get('string')).toBe('s'); - expect(config.get('number')).toBe(12); - } - ); + it_id('b18b9a6a-0e35-4b60-9771-30f53501df3c')(it)('validate afterSave Parse.Config', async () => { + Parse.Cloud.afterSave(Parse.Config, () => {}, validatorSuccess); + const config = await testConfig(); + expect(config.get('internal')).toBe('i'); + expect(config.get('string')).toBe('s'); + expect(config.get('number')).toBe(12); + }); it_id('ef761222-1758-4614-b984-da84d73fc10c')(it)( 'validate afterSave Parse.Config fail', @@ -1678,8 +1631,7 @@ describe('cloud validator', () => { { field: 'requiredUser', value: true, - error: - 'requiredUser is not a supported parameter for Cloud Function validations.', + error: 'requiredUser is not a supported parameter for Cloud Function validations.', }, { field: 'requireUser', @@ -1735,9 +1687,7 @@ describe('cloud validator', () => { Parse.Cloud.define('myFunction', () => {}, { [field.field]: field.value, }); - fail( - `Expected error registering invalid Cloud Function validation ${field.field}.` - ); + fail(`Expected error registering invalid Cloud Function validation ${field.field}.`); } catch (e) { expect(e).toBe(field.error); } @@ -1749,8 +1699,7 @@ describe('cloud validator', () => { { field: 'otherKey', value: true, - error: - 'otherKey is not a supported parameter for Cloud Function validations.', + error: 'otherKey is not a supported parameter for Cloud Function validations.', }, { field: 'constant', @@ -1780,9 +1729,7 @@ describe('cloud validator', () => { }, }, }); - fail( - `Expected error registering invalid Cloud Function validation ${field.field}.` - ); + fail(`Expected error registering invalid Cloud Function validation ${field.field}.`); } catch (e) { expect(e).toBe(field.error); } @@ -1794,9 +1741,7 @@ describe('cloud validator', () => { }, }, }); - fail( - `Expected error registering invalid Cloud Function validation ${field.field}.` - ); + fail(`Expected error registering invalid Cloud Function validation ${field.field}.`); } catch (e) { expect(e).toBe(field.error); } diff --git a/spec/CloudCode.spec.js b/spec/CloudCode.spec.js index 1c56c6dbe5..5a5ea93952 100644 --- a/spec/CloudCode.spec.js +++ b/spec/CloudCode.spec.js @@ -25,25 +25,19 @@ describe('Cloud Code', () => { cloud: __dirname + '/cloud/cloudCodeRelativeFile.js', }).then(() => { Parse.Cloud.run('cloudCodeInFile', {}).then(result => { - expect(result).toEqual( - 'It is possible to define cloud code in a file.' - ); + expect(result).toEqual('It is possible to define cloud code in a file.'); done(); }); }); }); it('can load relative cloud code file', done => { - reconfigureServer({ cloud: './spec/cloud/cloudCodeAbsoluteFile.js' }).then( - () => { - Parse.Cloud.run('cloudCodeInFile', {}).then(result => { - expect(result).toEqual( - 'It is possible to define cloud code in a file.' - ); - done(); - }); - } - ); + reconfigureServer({ cloud: './spec/cloud/cloudCodeAbsoluteFile.js' }).then(() => { + Parse.Cloud.run('cloudCodeInFile', {}).then(result => { + expect(result).toEqual('It is possible to define cloud code in a file.'); + done(); + }); + }); }); it('can load cloud code as a module', async () => { @@ -106,9 +100,7 @@ describe('Cloud Code', () => { const config = Parse.Server; let currentConfig = Config.get('test'); const server = require('../lib/cloud-code/Parse.Server'); - expect(Object.keys(config)).toEqual( - Object.keys({ ...currentConfig, ...server }) - ); + expect(Object.keys(config)).toEqual(Object.keys({ ...currentConfig, ...server })); config.silent = false; Parse.Server = config; currentConfig = Config.get('test'); @@ -174,9 +166,7 @@ describe('Cloud Code', () => { Parse.Cloud.run('cloudCodeWithError').then( () => done.fail('should not succeed'), e => { - expect(e).toEqual( - new Parse.Error(Parse.Error.SCRIPT_FAILED, 'foo is not defined') - ); + expect(e).toEqual(new Parse.Error(Parse.Error.SCRIPT_FAILED, 'foo is not defined')); done(); } ); @@ -224,9 +214,7 @@ describe('Cloud Code', () => { obj.set('foo', 'bar'); obj.save().then( function () { - fail( - 'Should not have been able to save BeforeSaveFailWithErrorCode class.' - ); + fail('Should not have been able to save BeforeSaveFailWithErrorCode class.'); done(); }, function (error) { @@ -953,18 +941,10 @@ describe('Cloud Code', () => { expect(req.params.dateList[0].getTime()).toBe(1463907600000); expect(req.params.complexStructure.date[0] instanceof Date).toBe(true); expect(req.params.complexStructure.date[0].getTime()).toBe(1463907600000); - expect(req.params.complexStructure.deepDate.date[0] instanceof Date).toBe( - true - ); - expect(req.params.complexStructure.deepDate.date[0].getTime()).toBe( - 1463907600000 - ); - expect( - req.params.complexStructure.deepDate2[0].date instanceof Date - ).toBe(true); - expect(req.params.complexStructure.deepDate2[0].date.getTime()).toBe( - 1463907600000 - ); + expect(req.params.complexStructure.deepDate.date[0] instanceof Date).toBe(true); + expect(req.params.complexStructure.deepDate.date[0].getTime()).toBe(1463907600000); + expect(req.params.complexStructure.deepDate2[0].date instanceof Date).toBe(true); + expect(req.params.complexStructure.deepDate2[0].date.getTime()).toBe(1463907600000); // Regression for #2294 expect(req.params.file instanceof Parse.File).toBe(true); expect(req.params.file.url()).toEqual('https://some.url'); @@ -1402,11 +1382,7 @@ describe('Cloud Code', () => { requireMaster: true, } ); - await Parse.Cloud.run( - 'deleteAccount', - { object: user.toPointer() }, - { useMasterKey: true } - ); + await Parse.Cloud.run('deleteAccount', { object: user.toPointer() }, { useMasterKey: true }); }); it('allow cloud to encode Parse Objects', async () => { @@ -1428,11 +1404,7 @@ describe('Cloud Code', () => { requireMaster: true, } ); - await Parse.Cloud.run( - 'deleteAccount', - { object: user.toPointer() }, - { useMasterKey: true } - ); + await Parse.Cloud.run('deleteAccount', { object: user.toPointer() }, { useMasterKey: true }); }); it('beforeSave should not affect fetched pointers', done => { @@ -1443,9 +1415,7 @@ describe('Cloud Code', () => { }); const TestObject = Parse.Object.extend('TestObject'); - const BeforeSaveUnchangedObject = Parse.Object.extend( - 'BeforeSaveUnchanged' - ); + const BeforeSaveUnchangedObject = Parse.Object.extend('BeforeSaveUnchanged'); const BeforeSaveChangedObject = Parse.Object.extend('BeforeSaveChanged'); const aTestObject = new TestObject(); @@ -1455,27 +1425,19 @@ describe('Cloud Code', () => { .then(aTestObject => { const aBeforeSaveUnchangedObject = new BeforeSaveUnchangedObject(); aBeforeSaveUnchangedObject.set('aTestObject', aTestObject); - expect( - aBeforeSaveUnchangedObject.get('aTestObject').get('foo') - ).toEqual('bar'); + expect(aBeforeSaveUnchangedObject.get('aTestObject').get('foo')).toEqual('bar'); return aBeforeSaveUnchangedObject.save(); }) .then(aBeforeSaveUnchangedObject => { - expect( - aBeforeSaveUnchangedObject.get('aTestObject').get('foo') - ).toEqual('bar'); + expect(aBeforeSaveUnchangedObject.get('aTestObject').get('foo')).toEqual('bar'); const aBeforeSaveChangedObject = new BeforeSaveChangedObject(); aBeforeSaveChangedObject.set('aTestObject', aTestObject); - expect(aBeforeSaveChangedObject.get('aTestObject').get('foo')).toEqual( - 'bar' - ); + expect(aBeforeSaveChangedObject.get('aTestObject').get('foo')).toEqual('bar'); return aBeforeSaveChangedObject.save(); }) .then(aBeforeSaveChangedObject => { - expect(aBeforeSaveChangedObject.get('aTestObject').get('foo')).toEqual( - 'bar' - ); + expect(aBeforeSaveChangedObject.get('aTestObject').get('foo')).toEqual('bar'); expect(aBeforeSaveChangedObject.get('foo')).toEqual('baz'); done(); }); @@ -1698,21 +1660,14 @@ describe('Cloud Code', () => { return object; }); - const obj = new Parse.Role( - 'TestRole', - new Parse.ACL({ '*': { read: true, write: true } }) - ); + const obj = new Parse.Role('TestRole', new Parse.ACL({ '*': { read: true, write: true } })); await obj.save(); - expect(obj.getACL()).toEqual( - new Parse.ACL({ '*': { read: true, write: true } }) - ); + expect(obj.getACL()).toEqual(new Parse.ACL({ '*': { read: true, write: true } })); expect(obj.get('name')).toEqual('TestRole'); await obj.fetch(); - expect(obj.getACL()).toEqual( - new Parse.ACL({ '*': { read: true, write: true } }) - ); + expect(obj.getACL()).toEqual(new Parse.ACL({ '*': { read: true, write: true } })); expect(obj.get('name')).toEqual('TestRole'); }); @@ -1860,10 +1815,7 @@ describe('Cloud Code', () => { const jobStatusId = response.headers['x-parse-job-status-id']; const checkJobStatus = async () => { const jobStatus = await getJobStatus(jobStatusId); - return ( - jobStatus.get('finishedAt') && - jobStatus.get('message') === 'Hello, world!!!' - ); + return jobStatus.get('finishedAt') && jobStatus.get('message') === 'Hello, world!!!'; }; while (!(await checkJobStatus())) { await new Promise(resolve => setTimeout(resolve, 100)); @@ -2031,10 +1983,7 @@ describe('Cloud Code', () => { it('should set the failure message on the job error', async () => { Parse.Cloud.job('myJobError', () => { - throw new Parse.Error( - Parse.Error.OBJECT_NOT_FOUND, - 'Something went wrong' - ); + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Something went wrong'); }); const job = await Parse.Cloud.startJob('myJobError'); let jobStatus, status; @@ -2367,32 +2316,29 @@ describe('beforeFind hooks', () => { ); }); - it_id('6ef0d226-af30-4dfd-8306-972a1b4becd3')(it)( - 'should handle empty where', - done => { - Parse.Cloud.beforeFind('MyObject', req => { - const otherQuery = new Parse.Query('MyObject'); - otherQuery.equalTo('some', true); - return Parse.Query.or(req.query, otherQuery); - }); + it_id('6ef0d226-af30-4dfd-8306-972a1b4becd3')(it)('should handle empty where', done => { + Parse.Cloud.beforeFind('MyObject', req => { + const otherQuery = new Parse.Query('MyObject'); + otherQuery.equalTo('some', true); + return Parse.Query.or(req.query, otherQuery); + }); - request({ - url: 'http://localhost:8378/1/classes/MyObject', - headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-REST-API-Key': 'rest', - }, - }).then( - () => { - done(); - }, - err => { - fail(err); - done(); - } - ); - } - ); + request({ + url: 'http://localhost:8378/1/classes/MyObject', + headers: { + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-REST-API-Key': 'rest', + }, + }).then( + () => { + done(); + }, + err => { + fail(err); + done(); + } + ); + }); it('should handle sorting where', done => { Parse.Cloud.beforeFind('MyObject', req => { @@ -2786,9 +2732,7 @@ describe('afterFind hooks', () => { return req.query; }); - const obj0 = new Parse.Object('MyObject') - .set('white', true) - .set('black', true); + const obj0 = new Parse.Object('MyObject').set('white', true).set('black', true); obj0.save().then(() => { new Parse.Query('MyObject').first().then(result => { expect(result.get('white')).toBe(true); @@ -2799,9 +2743,7 @@ describe('afterFind hooks', () => { }); it('should not alter select', done => { - const obj0 = new Parse.Object('MyObject') - .set('white', true) - .set('black', true); + const obj0 = new Parse.Object('MyObject').set('white', true).set('black', true); obj0.save().then(() => { new Parse.Query('MyObject').first().then(result => { expect(result.get('white')).toBe(true); @@ -2928,14 +2870,10 @@ describe('afterFind hooks', () => { it('should validate triggers correctly', () => { expect(() => { Parse.Cloud.beforeSave('_Session', () => {}); - }).toThrow( - 'Only the afterLogout trigger is allowed for the _Session class.' - ); + }).toThrow('Only the afterLogout trigger is allowed for the _Session class.'); expect(() => { Parse.Cloud.afterSave('_Session', () => {}); - }).toThrow( - 'Only the afterLogout trigger is allowed for the _Session class.' - ); + }).toThrow('Only the afterLogout trigger is allowed for the _Session class.'); expect(() => { Parse.Cloud.beforeSave('_PushStatus', () => {}); }).toThrow('Only afterSave is allowed on _PushStatus'); @@ -2944,44 +2882,28 @@ describe('afterFind hooks', () => { }).not.toThrow(); expect(() => { Parse.Cloud.beforeLogin(() => {}); - }).not.toThrow( - 'Only the _User class is allowed for the beforeLogin and afterLogin triggers' - ); + }).not.toThrow('Only the _User class is allowed for the beforeLogin and afterLogin triggers'); expect(() => { Parse.Cloud.beforeLogin('_User', () => {}); - }).not.toThrow( - 'Only the _User class is allowed for the beforeLogin and afterLogin triggers' - ); + }).not.toThrow('Only the _User class is allowed for the beforeLogin and afterLogin triggers'); expect(() => { Parse.Cloud.beforeLogin(Parse.User, () => {}); - }).not.toThrow( - 'Only the _User class is allowed for the beforeLogin and afterLogin triggers' - ); + }).not.toThrow('Only the _User class is allowed for the beforeLogin and afterLogin triggers'); expect(() => { Parse.Cloud.beforeLogin('SomeClass', () => {}); - }).toThrow( - 'Only the _User class is allowed for the beforeLogin and afterLogin triggers' - ); + }).toThrow('Only the _User class is allowed for the beforeLogin and afterLogin triggers'); expect(() => { Parse.Cloud.afterLogin(() => {}); - }).not.toThrow( - 'Only the _User class is allowed for the beforeLogin and afterLogin triggers' - ); + }).not.toThrow('Only the _User class is allowed for the beforeLogin and afterLogin triggers'); expect(() => { Parse.Cloud.afterLogin('_User', () => {}); - }).not.toThrow( - 'Only the _User class is allowed for the beforeLogin and afterLogin triggers' - ); + }).not.toThrow('Only the _User class is allowed for the beforeLogin and afterLogin triggers'); expect(() => { Parse.Cloud.afterLogin(Parse.User, () => {}); - }).not.toThrow( - 'Only the _User class is allowed for the beforeLogin and afterLogin triggers' - ); + }).not.toThrow('Only the _User class is allowed for the beforeLogin and afterLogin triggers'); expect(() => { Parse.Cloud.afterLogin('SomeClass', () => {}); - }).toThrow( - 'Only the _User class is allowed for the beforeLogin and afterLogin triggers' - ); + }).toThrow('Only the _User class is allowed for the beforeLogin and afterLogin triggers'); expect(() => { Parse.Cloud.afterLogout(() => {}); }).not.toThrow(); @@ -2990,14 +2912,10 @@ describe('afterFind hooks', () => { }).not.toThrow(); expect(() => { Parse.Cloud.afterLogout('_User', () => {}); - }).toThrow( - 'Only the _Session class is allowed for the afterLogout trigger.' - ); + }).toThrow('Only the _Session class is allowed for the afterLogout trigger.'); expect(() => { Parse.Cloud.afterLogout('SomeClass', () => {}); - }).toThrow( - 'Only the _Session class is allowed for the afterLogout trigger.' - ); + }).toThrow('Only the _Session class is allowed for the afterLogout trigger.'); }); it_id('c16159b5-e8ee-42d5-8fe3-e2f7c006881d')(it)( @@ -3745,17 +3663,14 @@ describe('saveFile hooks', () => { const createFileSpy = spyOn(mockAdapter, 'createFile').and.callThrough(); Parse.Cloud.beforeSave(Parse.File, () => { const newFile = new Parse.File('some-file.txt'); - newFile._url = - 'http://www.somewhere.com/parse/files/some-app-id/some-file.txt'; + newFile._url = 'http://www.somewhere.com/parse/files/some-app-id/some-file.txt'; return newFile; }); const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain'); const result = await file.save({ useMasterKey: true }); expect(result).toBe(file); expect(result._name).toBe('some-file.txt'); - expect(result._url).toBe( - 'http://www.somewhere.com/parse/files/some-app-id/some-file.txt' - ); + expect(result._url).toBe('http://www.somewhere.com/parse/files/some-app-id/some-file.txt'); expect(createFileSpy).not.toHaveBeenCalled(); }); @@ -3807,11 +3722,7 @@ describe('saveFile hooks', () => { Parse.Cloud.beforeSave(Parse.File, async req => { expect(req.triggerName).toEqual('beforeSave'); expect(req.fileSize).toBe(3); - const newFile = new Parse.File( - 'donald_duck.pdf', - [4, 5, 6], - 'application/pdf' - ); + const newFile = new Parse.File('donald_duck.pdf', [4, 5, 6], 'application/pdf'); newFile.setMetadata({ foo: 'bar' }); newFile.setTags({ tagA: 'some-tag' }); return newFile; @@ -3836,9 +3747,7 @@ describe('saveFile hooks', () => { newOptions ); const expectedFileName = 'donald_duck.pdf'; - expect(file._name.indexOf(expectedFileName)).toBe( - file._name.length - expectedFileName.length - ); + expect(file._name.indexOf(expectedFileName)).toBe(file._name.length - expectedFileName.length); }); it('beforeSave(Parse.File) should contain metadata and tags saved from client', async () => { @@ -3890,8 +3799,7 @@ describe('saveFile hooks', () => { Parse.Cloud.beforeSave(Parse.File, req => { expect(req.fileSize).toBe(3); const newFile = new Parse.File('some-file.txt'); - newFile._url = - 'http://www.somewhere.com/parse/files/some-app-id/some-file.txt'; + newFile._url = 'http://www.somewhere.com/parse/files/some-app-id/some-file.txt'; return newFile; }); Parse.Cloud.afterSave(Parse.File, req => { @@ -3901,9 +3809,7 @@ describe('saveFile hooks', () => { const result = await file.save({ useMasterKey: true }); expect(result).toBe(result); expect(result._name).toBe('some-file.txt'); - expect(result._url).toBe( - 'http://www.somewhere.com/parse/files/some-app-id/some-file.txt' - ); + expect(result._url).toBe('http://www.somewhere.com/parse/files/some-app-id/some-file.txt'); expect(createFileSpy).not.toHaveBeenCalled(); }); @@ -3942,11 +3848,7 @@ describe('saveFile hooks', () => { Parse.Cloud.beforeSave(Parse.File, async req => { expect(req.fileSize).toBe(3); expect(req.master).toBe(true); - const newFile = new Parse.File( - 'donald_duck.pdf', - [4, 5, 6, 7, 8, 9], - 'application/pdf' - ); + const newFile = new Parse.File('donald_duck.pdf', [4, 5, 6, 7, 8, 9], 'application/pdf'); return newFile; }); Parse.Cloud.afterSave(Parse.File, async req => { @@ -4104,9 +4006,7 @@ describe('Parse.File hooks', () => { }).catch(e => { throw new Parse.Error(e.data.code, e.data.error); }) - ).toBeRejectedWith( - new Parse.Error(Parse.Error.SCRIPT_FAILED, 'unauthorized') - ); + ).toBeRejectedWith(new Parse.Error(Parse.Error.SCRIPT_FAILED, 'unauthorized')); expect(hooks.beforeFind).toHaveBeenCalled(); expect(hooks.afterFind).not.toHaveBeenCalled(); @@ -4137,9 +4037,7 @@ describe('Parse.File hooks', () => { }).catch(e => { throw new Parse.Error(e.data.code, e.data.error); }) - ).toBeRejectedWith( - new Parse.Error(Parse.Error.SCRIPT_FAILED, 'unauthorized') - ); + ).toBeRejectedWith(new Parse.Error(Parse.Error.SCRIPT_FAILED, 'unauthorized')); for (const hook in hooks) { expect(hooks[hook]).toHaveBeenCalled(); } @@ -4160,18 +4058,13 @@ describe('Parse.File hooks', () => { 'X-Parse-Session-Token': user.getSessionToken(), }, }); - expect(response.headers['content-disposition']).toBe( - `attachment;filename=${file._name}` - ); + expect(response.headers['content-disposition']).toBe(`attachment;filename=${file._name}`); }); }); describe('Cloud Config hooks', () => { function testConfig() { - return Parse.Config.save( - { internal: 'i', string: 's', number: 12 }, - { internal: true } - ); + return Parse.Config.save({ internal: 'i', string: 's', number: 12 }, { internal: true }); } it_id('997fe20a-96f7-454a-a5b0-c155b8d02f05')(it)( diff --git a/spec/CloudCodeLogger.spec.js b/spec/CloudCodeLogger.spec.js index 6768063083..de59b41bb7 100644 --- a/spec/CloudCodeLogger.spec.js +++ b/spec/CloudCodeLogger.spec.js @@ -1,5 +1,4 @@ -const LoggerController = - require('../lib/Controllers/LoggerController').LoggerController; +const LoggerController = require('../lib/Controllers/LoggerController').LoggerController; const WinstonLoggerAdapter = require('../lib/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter; const fs = require('fs'); @@ -31,109 +30,88 @@ describe('Cloud Code Logger', () => { .then(() => Parse.User.logIn(user.get('username'), 'abc')); }) .then(() => { - spy = spyOn( - Config.get('test').loggerController.adapter, - 'log' - ).and.callThrough(); + spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough(); }); }); // Note that helpers takes care of logout. // see helpers.js:afterEach - it_id('02d53b97-3ec7-46fb-abb6-176fd6e85590')(it)( - 'should expose log to functions', - () => { - const spy = spyOn( - Config.get('test').loggerController, - 'log' - ).and.callThrough(); - Parse.Cloud.define('loggerTest', req => { - req.log.info('logTest', 'info log', { info: 'some log' }); - req.log.error('logTest', 'error log', { error: 'there was an error' }); - return {}; - }); + it_id('02d53b97-3ec7-46fb-abb6-176fd6e85590')(it)('should expose log to functions', () => { + const spy = spyOn(Config.get('test').loggerController, 'log').and.callThrough(); + Parse.Cloud.define('loggerTest', req => { + req.log.info('logTest', 'info log', { info: 'some log' }); + req.log.error('logTest', 'error log', { error: 'there was an error' }); + return {}; + }); - return Parse.Cloud.run('loggerTest').then(() => { - expect(spy).toHaveBeenCalledTimes(3); - const cloudFunctionMessage = spy.calls.all()[2]; - const errorMessage = spy.calls.all()[1]; - const infoMessage = spy.calls.all()[0]; - expect(cloudFunctionMessage.args[0]).toBe('info'); - expect(cloudFunctionMessage.args[1][1].params).toEqual({}); - expect(cloudFunctionMessage.args[1][0]).toMatch( - /Ran cloud function loggerTest for user [^ ]* with:\n {2}Input: {}\n {2}Result: {}/ - ); - expect(cloudFunctionMessage.args[1][1].functionName).toEqual( - 'loggerTest' - ); - expect(errorMessage.args[0]).toBe('error'); - expect(errorMessage.args[1][2].error).toBe('there was an error'); - expect(errorMessage.args[1][0]).toBe('logTest'); - expect(errorMessage.args[1][1]).toBe('error log'); - expect(infoMessage.args[0]).toBe('info'); - expect(infoMessage.args[1][2].info).toBe('some log'); - expect(infoMessage.args[1][0]).toBe('logTest'); - expect(infoMessage.args[1][1]).toBe('info log'); - }); - } - ); + return Parse.Cloud.run('loggerTest').then(() => { + expect(spy).toHaveBeenCalledTimes(3); + const cloudFunctionMessage = spy.calls.all()[2]; + const errorMessage = spy.calls.all()[1]; + const infoMessage = spy.calls.all()[0]; + expect(cloudFunctionMessage.args[0]).toBe('info'); + expect(cloudFunctionMessage.args[1][1].params).toEqual({}); + expect(cloudFunctionMessage.args[1][0]).toMatch( + /Ran cloud function loggerTest for user [^ ]* with:\n {2}Input: {}\n {2}Result: {}/ + ); + expect(cloudFunctionMessage.args[1][1].functionName).toEqual('loggerTest'); + expect(errorMessage.args[0]).toBe('error'); + expect(errorMessage.args[1][2].error).toBe('there was an error'); + expect(errorMessage.args[1][0]).toBe('logTest'); + expect(errorMessage.args[1][1]).toBe('error log'); + expect(infoMessage.args[0]).toBe('info'); + expect(infoMessage.args[1][2].info).toBe('some log'); + expect(infoMessage.args[1][0]).toBe('logTest'); + expect(infoMessage.args[1][1]).toBe('info log'); + }); + }); - it_id('768412f5-d32f-4134-89a6-08949781a6c0')(it)( - 'trigger should obfuscate password', - done => { - Parse.Cloud.beforeSave(Parse.User, req => { - return req.object; - }); + it_id('768412f5-d32f-4134-89a6-08949781a6c0')(it)('trigger should obfuscate password', done => { + Parse.Cloud.beforeSave(Parse.User, req => { + return req.object; + }); - Parse.User.signUp('tester123', 'abc') - .then(() => { - const entry = spy.calls.mostRecent().args; - expect(entry[1]).not.toMatch(/password":"abc/); - expect(entry[1]).toMatch(/\*\*\*\*\*\*\*\*/); - done(); - }) - .then(null, e => done.fail(e)); - } - ); + Parse.User.signUp('tester123', 'abc') + .then(() => { + const entry = spy.calls.mostRecent().args; + expect(entry[1]).not.toMatch(/password":"abc/); + expect(entry[1]).toMatch(/\*\*\*\*\*\*\*\*/); + done(); + }) + .then(null, e => done.fail(e)); + }); - it_id('3c394047-272e-4728-9d02-9eaa660d2ed2')(it)( - 'should expose log to trigger', - done => { - Parse.Cloud.beforeSave('MyObject', req => { - req.log.info('beforeSave MyObject', 'info log', { info: 'some log' }); - req.log.error('beforeSave MyObject', 'error log', { - error: 'there was an error', - }); - return {}; + it_id('3c394047-272e-4728-9d02-9eaa660d2ed2')(it)('should expose log to trigger', done => { + Parse.Cloud.beforeSave('MyObject', req => { + req.log.info('beforeSave MyObject', 'info log', { info: 'some log' }); + req.log.error('beforeSave MyObject', 'error log', { + error: 'there was an error', }); + return {}; + }); - const obj = new Parse.Object('MyObject'); - obj.save().then(() => { - const lastCalls = spy.calls.all().reverse(); - const cloudTriggerMessage = lastCalls[0].args; - const errorMessage = lastCalls[1].args; - const infoMessage = lastCalls[2].args; - expect(cloudTriggerMessage[0]).toBe('info'); - expect(cloudTriggerMessage[2].triggerType).toEqual('beforeSave'); - expect(cloudTriggerMessage[1]).toMatch( - /beforeSave triggered for MyObject for user [^ ]*\n {2}Input: {}\n {2}Result: {"object":{}}/ - ); - expect(cloudTriggerMessage[2].user).toBe(user.id); - expect(errorMessage[0]).toBe('error'); - expect(errorMessage[3].error).toBe('there was an error'); - expect(errorMessage[1] + ' ' + errorMessage[2]).toBe( - 'beforeSave MyObject error log' - ); - expect(infoMessage[0]).toBe('info'); - expect(infoMessage[3].info).toBe('some log'); - expect(infoMessage[1] + ' ' + infoMessage[2]).toBe( - 'beforeSave MyObject info log' - ); - done(); - }); - } - ); + const obj = new Parse.Object('MyObject'); + obj.save().then(() => { + const lastCalls = spy.calls.all().reverse(); + const cloudTriggerMessage = lastCalls[0].args; + const errorMessage = lastCalls[1].args; + const infoMessage = lastCalls[2].args; + expect(cloudTriggerMessage[0]).toBe('info'); + expect(cloudTriggerMessage[2].triggerType).toEqual('beforeSave'); + expect(cloudTriggerMessage[1]).toMatch( + /beforeSave triggered for MyObject for user [^ ]*\n {2}Input: {}\n {2}Result: {"object":{}}/ + ); + expect(cloudTriggerMessage[2].user).toBe(user.id); + expect(errorMessage[0]).toBe('error'); + expect(errorMessage[3].error).toBe('there was an error'); + expect(errorMessage[1] + ' ' + errorMessage[2]).toBe('beforeSave MyObject error log'); + expect(infoMessage[0]).toBe('info'); + expect(infoMessage[3].info).toBe('some log'); + expect(infoMessage[1] + ' ' + infoMessage[2]).toBe('beforeSave MyObject info log'); + done(); + }); + }); it('should truncate really long lines when asked to', () => { const logController = new LoggerController(new WinstonLoggerAdapter()); @@ -163,65 +141,56 @@ describe('Cloud Code Logger', () => { } ); - it_id('9857e15d-bb18-478d-8a67-fdaad3e89565')(it)( - 'should log an afterSave', - done => { - Parse.Cloud.afterSave('MyObject', () => {}); - new Parse.Object('MyObject') - .save() - .then(() => { - const log = spy.calls.mostRecent().args; - expect(log[2].triggerType).toEqual('afterSave'); - done(); - }) - // catch errors - not that the error is actually useful :( - .then(null, e => done.fail(e)); - } - ); - - it_id('ec13a296-f8b1-4fc6-985a-3593462edd9c')(it)( - 'should log a denied beforeSave', - done => { - Parse.Cloud.beforeSave('MyObject', () => { - throw 'uh oh!'; - }); - - new Parse.Object('MyObject') - .save() - .then( - () => done.fail('this is not supposed to succeed'), - () => new Promise(resolve => setTimeout(resolve, 100)) - ) - .then(() => { - const logs = spy.calls.all().reverse(); - const log = logs[1].args; // 0 is the 'uh oh!' from rejection... - expect(log[0]).toEqual('error'); - const error = log[2].error; - expect(error instanceof Parse.Error).toBeTruthy(); - expect(error.code).toBe(Parse.Error.SCRIPT_FAILED); - expect(error.message).toBe('uh oh!'); - done(); - }); - } - ); + it_id('9857e15d-bb18-478d-8a67-fdaad3e89565')(it)('should log an afterSave', done => { + Parse.Cloud.afterSave('MyObject', () => {}); + new Parse.Object('MyObject') + .save() + .then(() => { + const log = spy.calls.mostRecent().args; + expect(log[2].triggerType).toEqual('afterSave'); + done(); + }) + // catch errors - not that the error is actually useful :( + .then(null, e => done.fail(e)); + }); - it_id('3e0caa45-60d6-41af-829a-fd389710c132')(it)( - 'should log cloud function success', - done => { - Parse.Cloud.define('aFunction', () => { - return 'it worked!'; - }); + it_id('ec13a296-f8b1-4fc6-985a-3593462edd9c')(it)('should log a denied beforeSave', done => { + Parse.Cloud.beforeSave('MyObject', () => { + throw 'uh oh!'; + }); - Parse.Cloud.run('aFunction', { foo: 'bar' }).then(() => { - const log = spy.calls.mostRecent().args; - expect(log[0]).toEqual('info'); - expect(log[1]).toMatch( - /Ran cloud function aFunction for user [^ ]* with:\n {2}Input: {"foo":"bar"}\n {2}Result: "it worked!/ - ); + new Parse.Object('MyObject') + .save() + .then( + () => done.fail('this is not supposed to succeed'), + () => new Promise(resolve => setTimeout(resolve, 100)) + ) + .then(() => { + const logs = spy.calls.all().reverse(); + const log = logs[1].args; // 0 is the 'uh oh!' from rejection... + expect(log[0]).toEqual('error'); + const error = log[2].error; + expect(error instanceof Parse.Error).toBeTruthy(); + expect(error.code).toBe(Parse.Error.SCRIPT_FAILED); + expect(error.message).toBe('uh oh!'); done(); }); - } - ); + }); + + it_id('3e0caa45-60d6-41af-829a-fd389710c132')(it)('should log cloud function success', done => { + Parse.Cloud.define('aFunction', () => { + return 'it worked!'; + }); + + Parse.Cloud.run('aFunction', { foo: 'bar' }).then(() => { + const log = spy.calls.mostRecent().args; + expect(log[0]).toEqual('info'); + expect(log[1]).toMatch( + /Ran cloud function aFunction for user [^ ]* with:\n {2}Input: {"foo":"bar"}\n {2}Result: "it worked!/ + ); + done(); + }); + }); it_id('8088de8a-7cba-4035-8b05-4a903307e674')(it)( 'should log cloud function execution using the custom log level', @@ -235,9 +204,7 @@ describe('Cloud Code Logger', () => { }); await Parse.Cloud.run('aFunction', { foo: 'bar' }).then(() => { - const log = spy.calls - .allArgs() - .find(log => log[1].startsWith('Ran cloud function '))?.[0]; + const log = spy.calls.allArgs().find(log => log[1].startsWith('Ran cloud function '))?.[0]; expect(log).toEqual('info'); }); @@ -249,10 +216,7 @@ describe('Cloud Code Logger', () => { }, }); - spy = spyOn( - Config.get('test').loggerController.adapter, - 'log' - ).and.callThrough(); + spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough(); try { await Parse.Cloud.run('bFunction', { foo: 'bar' }); @@ -260,9 +224,7 @@ describe('Cloud Code Logger', () => { } catch { const log = spy.calls .allArgs() - .find(log => - log[1].startsWith('Failed running cloud function bFunction for ') - )?.[0]; + .find(log => log[1].startsWith('Failed running cloud function bFunction for '))?.[0]; expect(log).toEqual('info'); done(); } @@ -283,24 +245,17 @@ describe('Cloud Code Logger', () => { }, }); - spy = spyOn( - Config.get('test').loggerController.adapter, - 'log' - ).and.callThrough(); + spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough(); const obj = new Parse.Object('TestClass'); await obj.save(); return { beforeSave: spy.calls .allArgs() - .find(log => - log[1].startsWith('beforeSave triggered for TestClass for user ') - )?.[0], + .find(log => log[1].startsWith('beforeSave triggered for TestClass for user '))?.[0], afterSave: spy.calls .allArgs() - .find(log => - log[1].startsWith('afterSave triggered for TestClass for user ') - )?.[0], + .find(log => log[1].startsWith('afterSave triggered for TestClass for user '))?.[0], }; }; @@ -311,34 +266,31 @@ describe('Cloud Code Logger', () => { expect(calls).toEqual({ beforeSave: 'warn', afterSave: undefined }); }); - it_id('97e0eafa-cde6-4a9a-9e53-7db98bacbc62')(it)( - 'should log cloud function failure', - done => { - Parse.Cloud.define('aFunction', () => { - throw 'it failed!'; - }); + it_id('97e0eafa-cde6-4a9a-9e53-7db98bacbc62')(it)('should log cloud function failure', done => { + Parse.Cloud.define('aFunction', () => { + throw 'it failed!'; + }); - Parse.Cloud.run('aFunction', { foo: 'bar' }) - .catch(() => {}) - .then(() => { - const logs = spy.calls.all().reverse(); - expect(logs[0].args[1]).toBe('Parse error: '); - expect(logs[0].args[2].message).toBe('it failed!'); + Parse.Cloud.run('aFunction', { foo: 'bar' }) + .catch(() => {}) + .then(() => { + const logs = spy.calls.all().reverse(); + expect(logs[0].args[1]).toBe('Parse error: '); + expect(logs[0].args[2].message).toBe('it failed!'); - const log = logs[1].args; - expect(log[0]).toEqual('error'); - expect(log[1]).toMatch( - /Failed running cloud function aFunction for user [^ ]* with:\n {2}Input: {"foo":"bar"}\n {2}Error:/ - ); - const errorString = JSON.stringify( - new Parse.Error(Parse.Error.SCRIPT_FAILED, 'it failed!') - ); - expect(log[1].indexOf(errorString)).toBeGreaterThan(0); - done(); - }) - .catch(done.fail); - } - ); + const log = logs[1].args; + expect(log[0]).toEqual('error'); + expect(log[1]).toMatch( + /Failed running cloud function aFunction for user [^ ]* with:\n {2}Input: {"foo":"bar"}\n {2}Error:/ + ); + const errorString = JSON.stringify( + new Parse.Error(Parse.Error.SCRIPT_FAILED, 'it failed!') + ); + expect(log[1].indexOf(errorString)).toBeGreaterThan(0); + done(); + }) + .catch(done.fail); + }); xit('should log a changed beforeSave indicating a change', done => { pending('needs more work.....'); @@ -413,17 +365,12 @@ describe('Cloud Code Logger', () => { Parse.Cloud.define('bFunction', () => { throw new Error('Failed'); }); - spy = spyOn( - Config.get('test').loggerController.adapter, - 'log' - ).and.callThrough(); + spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough(); await Parse.Cloud.run('aFunction', { foo: 'bar' }); expect(spy).toHaveBeenCalledTimes(0); - await expectAsync( - Parse.Cloud.run('bFunction', { foo: 'bar' }) - ).toBeRejected(); + await expectAsync(Parse.Cloud.run('bFunction', { foo: 'bar' })).toBeRejected(); // Not "Failed running cloud function message..." expect(spy).toHaveBeenCalledTimes(1); }); @@ -442,10 +389,7 @@ describe('Cloud Code Logger', () => { Parse.Cloud.beforeSave('TestClass', () => {}); Parse.Cloud.afterSave('TestClass', () => {}); - spy = spyOn( - Config.get('test').loggerController.adapter, - 'log' - ).and.callThrough(); + spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough(); const obj = new Parse.Object('TestClass'); await obj.save(); diff --git a/spec/DatabaseController.spec.js b/spec/DatabaseController.spec.js index 9fdc656217..0f0cfd4ed8 100644 --- a/spec/DatabaseController.spec.js +++ b/spec/DatabaseController.spec.js @@ -77,9 +77,7 @@ describe('DatabaseController', function () { schemaController.testPermissionsForClassName .withArgs(CLASS_NAME, ACL_GROUP, OPERATION) .and.returnValue(true); - schemaController.getClassLevelPermissions - .withArgs(CLASS_NAME) - .and.returnValue(clp); + schemaController.getClassLevelPermissions.withArgs(CLASS_NAME).and.returnValue(clp); const output = databaseController.addPointerPermissions( schemaController, @@ -101,9 +99,7 @@ describe('DatabaseController', function () { schemaController.testPermissionsForClassName .withArgs(CLASS_NAME, ACL_GROUP, OPERATION) .and.returnValue(false); - schemaController.getClassLevelPermissions - .withArgs(CLASS_NAME) - .and.returnValue(clp); + schemaController.getClassLevelPermissions.withArgs(CLASS_NAME).and.returnValue(clp); schemaController.getExpectedType .withArgs(CLASS_NAME, 'user') .and.returnValue({ type: 'Pointer' }); @@ -128,9 +124,7 @@ describe('DatabaseController', function () { schemaController.testPermissionsForClassName .withArgs(CLASS_NAME, ACL_GROUP, OPERATION) .and.returnValue(false); - schemaController.getClassLevelPermissions - .withArgs(CLASS_NAME) - .and.returnValue(clp); + schemaController.getClassLevelPermissions.withArgs(CLASS_NAME).and.returnValue(clp); schemaController.getExpectedType .withArgs(CLASS_NAME, 'users') .and.returnValue({ type: 'Array' }); @@ -158,9 +152,7 @@ describe('DatabaseController', function () { schemaController.testPermissionsForClassName .withArgs(CLASS_NAME, ACL_GROUP, OPERATION) .and.returnValue(false); - schemaController.getClassLevelPermissions - .withArgs(CLASS_NAME) - .and.returnValue(clp); + schemaController.getClassLevelPermissions.withArgs(CLASS_NAME).and.returnValue(clp); schemaController.getExpectedType .withArgs(CLASS_NAME, 'user') .and.returnValue({ type: 'Object' }); @@ -188,9 +180,7 @@ describe('DatabaseController', function () { schemaController.testPermissionsForClassName .withArgs(CLASS_NAME, ACL_GROUP, OPERATION) .and.returnValue(false); - schemaController.getClassLevelPermissions - .withArgs(CLASS_NAME) - .and.returnValue(clp); + schemaController.getClassLevelPermissions.withArgs(CLASS_NAME).and.returnValue(clp); schemaController.getExpectedType .withArgs(CLASS_NAME, 'user') .and.returnValue({ type: 'Pointer' }); @@ -217,9 +207,7 @@ describe('DatabaseController', function () { schemaController.testPermissionsForClassName .withArgs(CLASS_NAME, ACL_GROUP, OPERATION) .and.returnValue(false); - schemaController.getClassLevelPermissions - .withArgs(CLASS_NAME) - .and.returnValue(clp); + schemaController.getClassLevelPermissions.withArgs(CLASS_NAME).and.returnValue(clp); schemaController.getExpectedType .withArgs(CLASS_NAME, 'user') .and.returnValue({ type: 'Pointer' }); @@ -255,9 +243,7 @@ describe('DatabaseController', function () { schemaController.testPermissionsForClassName .withArgs(CLASS_NAME, ACL_GROUP, OPERATION) .and.returnValue(false); - schemaController.getClassLevelPermissions - .withArgs(CLASS_NAME) - .and.returnValue(clp); + schemaController.getClassLevelPermissions.withArgs(CLASS_NAME).and.returnValue(clp); schemaController.getExpectedType .withArgs(CLASS_NAME, 'user') .and.returnValue({ type: 'Pointer' }); @@ -281,9 +267,7 @@ describe('DatabaseController', function () { schemaController.testPermissionsForClassName .withArgs(CLASS_NAME, ACL_GROUP, OPERATION) .and.returnValue(false); - schemaController.getClassLevelPermissions - .withArgs(CLASS_NAME) - .and.returnValue(clp); + schemaController.getClassLevelPermissions.withArgs(CLASS_NAME).and.returnValue(clp); schemaController.getExpectedType .withArgs(CLASS_NAME, 'user') .and.returnValue({ type: 'Pointer' }); @@ -311,9 +295,7 @@ describe('DatabaseController', function () { schemaController.testPermissionsForClassName .withArgs(CLASS_NAME, ACL_GROUP, OPERATION) .and.returnValue(false); - schemaController.getClassLevelPermissions - .withArgs(CLASS_NAME) - .and.returnValue(clp); + schemaController.getClassLevelPermissions.withArgs(CLASS_NAME).and.returnValue(clp); schemaController.getExpectedType .withArgs(CLASS_NAME, 'user') .and.returnValue({ type: 'Number' }); @@ -351,19 +333,13 @@ describe('DatabaseController', function () { it('reduceOrOperation', done => { expect(databaseController.reduceOrOperation({ a: 1 })).toEqual({ a: 1 }); - expect( - databaseController.reduceOrOperation({ $or: [{ a: 1 }, { b: 2 }] }) - ).toEqual({ + expect(databaseController.reduceOrOperation({ $or: [{ a: 1 }, { b: 2 }] })).toEqual({ $or: [{ a: 1 }, { b: 2 }], }); - expect( - databaseController.reduceOrOperation({ $or: [{ a: 1 }, { a: 2 }] }) - ).toEqual({ + expect(databaseController.reduceOrOperation({ $or: [{ a: 1 }, { a: 2 }] })).toEqual({ $or: [{ a: 1 }, { a: 2 }], }); - expect( - databaseController.reduceOrOperation({ $or: [{ a: 1 }, { a: 1 }] }) - ).toEqual({ a: 1 }); + expect(databaseController.reduceOrOperation({ $or: [{ a: 1 }, { a: 1 }] })).toEqual({ a: 1 }); expect( databaseController.reduceOrOperation({ $or: [{ a: 1, b: 2, c: 3 }, { a: 1 }], @@ -379,19 +355,13 @@ describe('DatabaseController', function () { it('reduceAndOperation', done => { expect(databaseController.reduceAndOperation({ a: 1 })).toEqual({ a: 1 }); - expect( - databaseController.reduceAndOperation({ $and: [{ a: 1 }, { b: 2 }] }) - ).toEqual({ + expect(databaseController.reduceAndOperation({ $and: [{ a: 1 }, { b: 2 }] })).toEqual({ $and: [{ a: 1 }, { b: 2 }], }); - expect( - databaseController.reduceAndOperation({ $and: [{ a: 1 }, { a: 2 }] }) - ).toEqual({ + expect(databaseController.reduceAndOperation({ $and: [{ a: 1 }, { a: 2 }] })).toEqual({ $and: [{ a: 1 }, { a: 2 }], }); - expect( - databaseController.reduceAndOperation({ $and: [{ a: 1 }, { a: 1 }] }) - ).toEqual({ + expect(databaseController.reduceAndOperation({ $and: [{ a: 1 }, { a: 1 }] })).toEqual({ a: 1, }); expect( @@ -425,10 +395,7 @@ describe('DatabaseController', function () { }); it('should support caseInsensitive without enableCollationCaseComparison option', async () => { - const databaseController = new DatabaseController( - dummyStorageAdapter, - {} - ); + const databaseController = new DatabaseController(dummyStorageAdapter, {}); const spy = spyOn(dummyStorageAdapter, 'find'); spy.and.callThrough(); await databaseController.find('_User', {}, { caseInsensitive: true }); @@ -439,8 +406,7 @@ describe('DatabaseController', function () { 'should create insensitive indexes without enableCollationCaseComparison', async () => { await reconfigureServer({ - databaseURI: - 'mongodb://localhost:27017/enableCollationCaseComparisonFalse', + databaseURI: 'mongodb://localhost:27017/enableCollationCaseComparisonFalse', databaseAdapter: undefined, }); const user = new Parse.User(); @@ -450,9 +416,7 @@ describe('DatabaseController', function () { email: 'example@example.com', }); const schemas = await Parse.Schema.all(); - const UserSchema = schemas.find( - ({ className }) => className === '_User' - ); + const UserSchema = schemas.find(({ className }) => className === '_User'); expect(UserSchema.indexes).toEqual({ _id_: { _id: 1 }, username_1: { username: 1 }, @@ -468,8 +432,7 @@ describe('DatabaseController', function () { async () => { await reconfigureServer({ enableCollationCaseComparison: true, - databaseURI: - 'mongodb://localhost:27017/enableCollationCaseComparisonTrue', + databaseURI: 'mongodb://localhost:27017/enableCollationCaseComparisonTrue', databaseAdapter: undefined, }); const user = new Parse.User(); @@ -479,9 +442,7 @@ describe('DatabaseController', function () { email: 'example@example.com', }); const schemas = await Parse.Schema.all(); - const UserSchema = schemas.find( - ({ className }) => className === '_User' - ); + const UserSchema = schemas.find(({ className }) => className === '_User'); expect(UserSchema.indexes).toEqual({ _id_: { _id: 1 }, username_1: { username: 1 }, @@ -512,10 +473,7 @@ describe('DatabaseController', function () { }; it('should not transform email to lower case without convertEmailToLowercase option on create', async () => { - const databaseController = new DatabaseController( - dummyStorageAdapter, - {} - ); + const databaseController = new DatabaseController(dummyStorageAdapter, {}); const spy = spyOn(dummyStorageAdapter, 'createObject'); spy.and.callThrough(); await databaseController.create('_User', { @@ -543,17 +501,10 @@ describe('DatabaseController', function () { }); it('should not transform email to lower case without convertEmailToLowercase option on update', async () => { - const databaseController = new DatabaseController( - dummyStorageAdapter, - {} - ); + const databaseController = new DatabaseController(dummyStorageAdapter, {}); const spy = spyOn(dummyStorageAdapter, 'findOneAndUpdate'); spy.and.callThrough(); - await databaseController.update( - '_User', - { id: 'example' }, - { email: 'EXAMPLE@EXAMPLE.COM' } - ); + await databaseController.update('_User', { id: 'example' }, { email: 'EXAMPLE@EXAMPLE.COM' }); expect(spy.calls.all()[0].args[3]).toEqual({ email: 'EXAMPLE@EXAMPLE.COM', }); @@ -565,11 +516,7 @@ describe('DatabaseController', function () { }); const spy = spyOn(dummyStorageAdapter, 'findOneAndUpdate'); spy.and.callThrough(); - await databaseController.update( - '_User', - { id: 'example' }, - { email: 'EXAMPLE@EXAMPLE.COM' } - ); + await databaseController.update('_User', { id: 'example' }, { email: 'EXAMPLE@EXAMPLE.COM' }); expect(spy.calls.all()[0].args[3]).toEqual({ email: 'example@example.com', }); @@ -617,10 +564,7 @@ describe('DatabaseController', function () { }; it('should not transform username to lower case without convertUsernameToLowercase option on create', async () => { - const databaseController = new DatabaseController( - dummyStorageAdapter, - {} - ); + const databaseController = new DatabaseController(dummyStorageAdapter, {}); const spy = spyOn(dummyStorageAdapter, 'createObject'); spy.and.callThrough(); await databaseController.create('_User', { @@ -648,17 +592,10 @@ describe('DatabaseController', function () { }); it('should not transform username to lower case without convertUsernameToLowercase option on update', async () => { - const databaseController = new DatabaseController( - dummyStorageAdapter, - {} - ); + const databaseController = new DatabaseController(dummyStorageAdapter, {}); const spy = spyOn(dummyStorageAdapter, 'findOneAndUpdate'); spy.and.callThrough(); - await databaseController.update( - '_User', - { id: 'example' }, - { username: 'EXAMPLE' } - ); + await databaseController.update('_User', { id: 'example' }, { username: 'EXAMPLE' }); expect(spy.calls.all()[0].args[3]).toEqual({ username: 'EXAMPLE', }); @@ -670,11 +607,7 @@ describe('DatabaseController', function () { }); const spy = spyOn(dummyStorageAdapter, 'findOneAndUpdate'); spy.and.callThrough(); - await databaseController.update( - '_User', - { id: 'example' }, - { username: 'EXAMPLE' } - ); + await databaseController.update('_User', { id: 'example' }, { username: 'EXAMPLE' }); expect(spy.calls.all()[0].args[3]).toEqual({ username: 'example', }); @@ -699,15 +632,7 @@ describe('DatabaseController', function () { }); function buildCLP(pointerNames) { - const OPERATIONS = [ - 'count', - 'find', - 'get', - 'create', - 'update', - 'delete', - 'addField', - ]; + const OPERATIONS = ['count', 'find', 'get', 'create', 'update', 'delete', 'addField']; const clp = OPERATIONS.reduce((acc, op) => { acc[op] = {}; diff --git a/spec/DefinedSchemas.spec.js b/spec/DefinedSchemas.spec.js index 2be26f69c7..a4dfd59555 100644 --- a/spec/DefinedSchemas.spec.js +++ b/spec/DefinedSchemas.spec.js @@ -23,10 +23,7 @@ describe('DefinedSchemas', () => { it('should keep default fields if not provided', async () => { const server = await reconfigureServer(); // Will perform create - await new DefinedSchemas( - { definitions: [{ className: 'Test' }] }, - server.config - ).execute(); + await new DefinedSchemas({ definitions: [{ className: 'Test' }] }, server.config).execute(); let schema = await new Parse.Schema('Test').get(); const expectedFields = { objectId: { type: 'String' }, @@ -38,10 +35,7 @@ describe('DefinedSchemas', () => { await server.config.schemaCache.clear(); // Will perform update - await new DefinedSchemas( - { definitions: [{ className: 'Test' }] }, - server.config - ).execute(); + await new DefinedSchemas({ definitions: [{ className: 'Test' }] }, server.config).execute(); schema = await new Parse.Schema('Test').get(); expect(schema.fields).toEqual(expectedFields); }); @@ -209,9 +203,7 @@ describe('DefinedSchemas', () => { await new DefinedSchemas( { - definitions: [ - { className: 'Test', fields: { aField: { type: 'String' } } }, - ], + definitions: [{ className: 'Test', fields: { aField: { type: 'String' } } }], }, server.config ).execute(); @@ -219,10 +211,7 @@ describe('DefinedSchemas', () => { let schema = await new Parse.Schema('Test').get(); expect(schema.fields.aField).toBeDefined(); - await new DefinedSchemas( - { definitions: [{ className: 'Test' }] }, - server.config - ).execute(); + await new DefinedSchemas({ definitions: [{ className: 'Test' }] }, server.config).execute(); schema = await new Parse.Schema('Test').get(); expect(schema.fields).toEqual({ @@ -238,9 +227,7 @@ describe('DefinedSchemas', () => { await new DefinedSchemas( { - definitions: [ - { className: 'Test', fields: { aField: { type: 'String' } } }, - ], + definitions: [{ className: 'Test', fields: { aField: { type: 'String' } } }], }, server.config ).execute(); @@ -266,9 +253,7 @@ describe('DefinedSchemas', () => { await new DefinedSchemas( { - definitions: [ - { className: 'Test', fields: { aField: { type: 'String' } } }, - ], + definitions: [{ className: 'Test', fields: { aField: { type: 'String' } } }], }, server.config ).execute(); @@ -282,9 +267,7 @@ describe('DefinedSchemas', () => { await new DefinedSchemas( { recreateModifiedFields: true, - definitions: [ - { className: 'Test', fields: { aField: { type: 'Number' } } }, - ], + definitions: [{ className: 'Test', fields: { aField: { type: 'Number' } } }], }, server.config ).execute(); @@ -300,9 +283,7 @@ describe('DefinedSchemas', () => { await new DefinedSchemas( { - definitions: [ - { className: 'Test', fields: { aField: { type: 'String' } } }, - ], + definitions: [{ className: 'Test', fields: { aField: { type: 'String' } } }], }, server.config ).execute(); @@ -315,9 +296,7 @@ describe('DefinedSchemas', () => { await new DefinedSchemas( { - definitions: [ - { className: 'Test', fields: { aField: { type: 'Number' } } }, - ], + definitions: [{ className: 'Test', fields: { aField: { type: 'Number' } } }], }, server.config ).execute(); @@ -333,9 +312,7 @@ describe('DefinedSchemas', () => { await new DefinedSchemas( { - definitions: [ - { className: 'Test', fields: { aField: { type: 'String' } } }, - ], + definitions: [{ className: 'Test', fields: { aField: { type: 'String' } } }], }, server.config ).execute(); @@ -481,12 +458,7 @@ describe('DefinedSchemas', () => { it('should detect protected indexes for _User class', () => { const definedSchema = new DefinedSchemas({}, {}); - const protectedUserIndexes = [ - '_id_', - 'case_insensitive_email', - 'username_1', - 'email_1', - ]; + const protectedUserIndexes = ['_id_', 'case_insensitive_email', 'username_1', 'email_1']; protectedUserIndexes.forEach(field => { expect(definedSchema.isProtectedIndex('_User', field)).toEqual(true); }); @@ -501,12 +473,8 @@ describe('DefinedSchemas', () => { it('should detect protected indexes for _Idempotency class', () => { const definedSchema = new DefinedSchemas({}, {}); - expect(definedSchema.isProtectedIndex('_Idempotency', 'reqId_1')).toEqual( - true - ); - expect(definedSchema.isProtectedIndex('_Idempotency', 'test')).toEqual( - false - ); + expect(definedSchema.isProtectedIndex('_Idempotency', 'reqId_1')).toEqual(true); + expect(definedSchema.isProtectedIndex('_Idempotency', 'test')).toEqual(false); }); it('should not detect protected indexes on user defined class', () => { @@ -519,13 +487,9 @@ describe('DefinedSchemas', () => { 'name_1', ]; protectedIndexes.forEach(field => { - expect(definedSchema.isProtectedIndex('ExampleClass', field)).toEqual( - false - ); + expect(definedSchema.isProtectedIndex('ExampleClass', field)).toEqual(false); }); - expect(definedSchema.isProtectedIndex('ExampleClass', '_id_')).toEqual( - true - ); + expect(definedSchema.isProtectedIndex('ExampleClass', '_id_')).toEqual(true); }); }); @@ -648,13 +612,9 @@ describe('DefinedSchemas', () => { const schemas = await Parse.Schema.all(); // Role could be flaky since all system classes are not ensured // at start up by the DefinedSchema system - expect( - schemas.filter(({ className }) => className !== '_Role').length - ).toEqual(3); + expect(schemas.filter(({ className }) => className !== '_Role').length).toEqual(3); - await expectAsync( - new Parse.Schema('TheNewTest').save() - ).toBeRejectedWithError( + await expectAsync(new Parse.Schema('TheNewTest').save()).toBeRejectedWithError( 'Cannot perform this operation when schemas options is used.' ); @@ -714,16 +674,12 @@ describe('DefinedSchemas', () => { await new DefinedSchemas( { - definitions: [ - { className: 'Test', fields: { aField: { type: 'String' } } }, - ], + definitions: [{ className: 'Test', fields: { aField: { type: 'String' } } }], }, server.config ).execute(); - expect(logger.error).toHaveBeenCalledWith( - `Failed to run migrations: ${error.toString()}` - ); + expect(logger.error).toHaveBeenCalledWith(`Failed to run migrations: ${error.toString()}`); }); it_id('a18bf4f2-25c8-4de3-b986-19cb1ab163b8')(it)( @@ -755,8 +711,7 @@ describe('DefinedSchemas', () => { ]); const testSchema = (await Parse.Schema.all()).find( - ({ className }) => - className === migrationOptions.definitions[0].className + ({ className }) => className === migrationOptions.definitions[0].className ); expect(testSchema.indexes.aField).toEqual({ aField: 1 }); diff --git a/spec/Deprecator.spec.js b/spec/Deprecator.spec.js index d84a0f8f6c..3af5d10c31 100644 --- a/spec/Deprecator.spec.js +++ b/spec/Deprecator.spec.js @@ -6,9 +6,7 @@ describe('Deprecator', () => { let deprecations = []; beforeEach(async () => { - deprecations = [ - { optionKey: 'exampleKey', changeNewDefault: 'exampleNewDefault' }, - ]; + deprecations = [{ optionKey: 'exampleKey', changeNewDefault: 'exampleNewDefault' }]; }); it('deprecations are an array', async () => { @@ -16,9 +14,7 @@ describe('Deprecator', () => { }); it('logs deprecation for new default', async () => { - deprecations = [ - { optionKey: 'exampleKey', changeNewDefault: 'exampleNewDefault' }, - ]; + deprecations = [{ optionKey: 'exampleKey', changeNewDefault: 'exampleNewDefault' }]; spyOn(Deprecator, '_getDeprecations').and.callFake(() => deprecations); const logger = require('../lib/logger').logger; @@ -31,9 +27,7 @@ describe('Deprecator', () => { }); it('does not log deprecation for new default if option is set manually', async () => { - deprecations = [ - { optionKey: 'exampleKey', changeNewDefault: 'exampleNewDefault' }, - ]; + deprecations = [{ optionKey: 'exampleKey', changeNewDefault: 'exampleNewDefault' }]; spyOn(Deprecator, '_getDeprecations').and.callFake(() => deprecations); const logSpy = spyOn(Deprecator, '_logOption').and.callFake(() => {}); diff --git a/spec/EmailVerificationToken.spec.js b/spec/EmailVerificationToken.spec.js index 597ef68388..bc8d4e3e0d 100644 --- a/spec/EmailVerificationToken.spec.js +++ b/spec/EmailVerificationToken.spec.js @@ -190,10 +190,7 @@ describe('Email Verification Token Expiration:', () => { followRedirects: false, }); expect(response.status).toEqual(302); - const verifiedUser = await Parse.User.logIn( - 'testEmailVerifyTokenValidity', - 'expiringToken' - ); + const verifiedUser = await Parse.User.logIn('testEmailVerifyTokenValidity', 'expiringToken'); expect(typeof verifiedUser).toBe('object'); expect(verifiedUser.get('emailVerified')).toBe(true); } @@ -302,76 +299,64 @@ describe('Email Verification Token Expiration:', () => { ); }); - it_id('9365c53c-b8b4-41f7-a3c1-77882f76a89c')(it)( - 'can conditionally send emails', - async () => { - let sendEmailOptions; - const emailAdapter = { - sendVerificationEmail: options => { - sendEmailOptions = options; - }, - sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => {}, - }; - const verifyUserEmails = { - method(req) { - expect(Object.keys(req)).toEqual([ - 'original', - 'object', - 'master', - 'ip', - 'installationId', - ]); - return false; - }, - }; - const verifySpy = spyOn(verifyUserEmails, 'method').and.callThrough(); - await reconfigureServer({ - appName: 'emailVerifyToken', - verifyUserEmails: verifyUserEmails.method, - emailAdapter: emailAdapter, - emailVerifyTokenValidityDuration: 5, // 5 seconds - publicServerURL: 'http://localhost:8378/1', - }); - const beforeSave = { - method(req) { - req.object.set('emailVerified', true); - }, - }; - const saveSpy = spyOn(beforeSave, 'method').and.callThrough(); - const emailSpy = spyOn( - emailAdapter, - 'sendVerificationEmail' - ).and.callThrough(); - Parse.Cloud.beforeSave(Parse.User, beforeSave.method); - const user = new Parse.User(); - user.setUsername('sets_email_verify_token_expires_at'); - user.setPassword('expiringToken'); - user.set('email', 'user@example.com'); - await user.signUp(); + it_id('9365c53c-b8b4-41f7-a3c1-77882f76a89c')(it)('can conditionally send emails', async () => { + let sendEmailOptions; + const emailAdapter = { + sendVerificationEmail: options => { + sendEmailOptions = options; + }, + sendPasswordResetEmail: () => Promise.resolve(), + sendMail: () => {}, + }; + const verifyUserEmails = { + method(req) { + expect(Object.keys(req)).toEqual(['original', 'object', 'master', 'ip', 'installationId']); + return false; + }, + }; + const verifySpy = spyOn(verifyUserEmails, 'method').and.callThrough(); + await reconfigureServer({ + appName: 'emailVerifyToken', + verifyUserEmails: verifyUserEmails.method, + emailAdapter: emailAdapter, + emailVerifyTokenValidityDuration: 5, // 5 seconds + publicServerURL: 'http://localhost:8378/1', + }); + const beforeSave = { + method(req) { + req.object.set('emailVerified', true); + }, + }; + const saveSpy = spyOn(beforeSave, 'method').and.callThrough(); + const emailSpy = spyOn(emailAdapter, 'sendVerificationEmail').and.callThrough(); + Parse.Cloud.beforeSave(Parse.User, beforeSave.method); + const user = new Parse.User(); + user.setUsername('sets_email_verify_token_expires_at'); + user.setPassword('expiringToken'); + user.set('email', 'user@example.com'); + await user.signUp(); - const config = Config.get('test'); - const results = await config.database.find( - '_User', - { - username: 'sets_email_verify_token_expires_at', - }, - {}, - Auth.maintenance(config) - ); + const config = Config.get('test'); + const results = await config.database.find( + '_User', + { + username: 'sets_email_verify_token_expires_at', + }, + {}, + Auth.maintenance(config) + ); - expect(results.length).toBe(1); - const user_data = results[0]; - expect(typeof user_data).toBe('object'); - expect(user_data.emailVerified).toEqual(true); - expect(user_data._email_verify_token).toBeUndefined(); - expect(user_data._email_verify_token_expires_at).toBeUndefined(); - expect(emailSpy).not.toHaveBeenCalled(); - expect(saveSpy).toHaveBeenCalled(); - expect(sendEmailOptions).toBeUndefined(); - expect(verifySpy).toHaveBeenCalled(); - } - ); + expect(results.length).toBe(1); + const user_data = results[0]; + expect(typeof user_data).toBe('object'); + expect(user_data.emailVerified).toEqual(true); + expect(user_data._email_verify_token).toBeUndefined(); + expect(user_data._email_verify_token_expires_at).toBeUndefined(); + expect(emailSpy).not.toHaveBeenCalled(); + expect(saveSpy).toHaveBeenCalled(); + expect(sendEmailOptions).toBeUndefined(); + expect(verifySpy).toHaveBeenCalled(); + }); it_id('b3549300-bed7-4a5e-bed5-792dbfead960')(it)( 'can conditionally send emails and allow conditional login', @@ -454,10 +439,7 @@ describe('Email Verification Token Expiration:', () => { publicServerURL: 'http://localhost:8378/1', sendUserEmailVerification: sendVerificationEmail.method, }); - const emailSpy = spyOn( - emailAdapter, - 'sendVerificationEmail' - ).and.callThrough(); + const emailSpy = spyOn(emailAdapter, 'sendVerificationEmail').and.callThrough(); const newUser = new Parse.User(); newUser.setUsername('unsets_email_verify_token_expires_at'); newUser.setPassword('expiringToken'); @@ -529,10 +511,7 @@ describe('Email Verification Token Expiration:', () => { emailVerifyTokenValidityDuration: 5, // 5 seconds publicServerURL: 'http://localhost:8378/1', }); - const emailSpy = spyOn( - emailAdapter, - 'sendVerificationEmail' - ).and.callThrough(); + const emailSpy = spyOn(emailAdapter, 'sendVerificationEmail').and.callThrough(); const newUser = new Parse.User(); newUser.setUsername('unsets_email_verify_token_expires_at'); newUser.setPassword('expiringToken'); @@ -600,9 +579,7 @@ describe('Email Verification Token Expiration:', () => { expect(typeof verifiedUser).toBe('object'); expect(verifiedUser.emailVerified).toEqual(true); expect(typeof verifiedUser._email_verify_token).toBe('undefined'); - expect(typeof verifiedUser._email_verify_token_expires_at).toBe( - 'undefined' - ); + expect(typeof verifiedUser._email_verify_token_expires_at).toBe('undefined'); } ); @@ -835,12 +812,7 @@ describe('Email Verification Token Expiration:', () => { // query for this user again const userAfterRequest = await config.database - .find( - '_User', - { username: 'resends_verification_token' }, - {}, - Auth.maintenance(config) - ) + .find('_User', { username: 'resends_verification_token' }, {}, Auth.maintenance(config)) .then(results => { return results[0]; }); @@ -872,10 +844,7 @@ describe('Email Verification Token Expiration:', () => { return true; }, }; - const verifyUserEmailsSpy = spyOn( - verifyUserEmails, - 'method' - ).and.callThrough(); + const verifyUserEmailsSpy = spyOn(verifyUserEmails, 'method').and.callThrough(); await reconfigureServer({ appName: 'test', publicServerURL: 'http://localhost:1337/1', @@ -889,9 +858,7 @@ describe('Email Verification Token Expiration:', () => { }), }); - await expectAsync( - Parse.User.requestEmailVerification('test@example.com') - ).toBeResolved(); + await expectAsync(Parse.User.requestEmailVerification('test@example.com')).toBeResolved(); expect(verifyUserEmailsSpy).toHaveBeenCalledTimes(1); }); @@ -1012,9 +979,7 @@ describe('Email Verification Token Expiration:', () => { // Verify that token & expiration haven't been changed for this new request expect(typeof userAfterRequest).toBe('object'); expect(userBeforeRequest._email_verify_token).toBeDefined(); - expect(userBeforeRequest._email_verify_token).toEqual( - userAfterRequest._email_verify_token - ); + expect(userBeforeRequest._email_verify_token).toEqual(userAfterRequest._email_verify_token); expect(userBeforeRequest._email_verify_token_expires_at).toBeDefined(); expect(userBeforeRequest._email_verify_token_expires_at).toEqual( userAfterRequest._email_verify_token_expires_at @@ -1243,10 +1208,7 @@ describe('Email Verification Token Expiration:', () => { followRedirects: false, }); expect(response.status).toEqual(302); - user = await Parse.User.logIn( - 'testEmailVerifyTokenValidity', - 'expiringToken' - ); + user = await Parse.User.logIn('testEmailVerifyTokenValidity', 'expiringToken'); expect(typeof user).toBe('object'); expect(user.get('emailVerified')).toBe(true); diff --git a/spec/EventEmitterPubSub.spec.js b/spec/EventEmitterPubSub.spec.js index f14f51ce25..00358646de 100644 --- a/spec/EventEmitterPubSub.spec.js +++ b/spec/EventEmitterPubSub.spec.js @@ -1,5 +1,4 @@ -const EventEmitterPubSub = - require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub; +const EventEmitterPubSub = require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub; describe('EventEmitterPubSub', function () { it('can publish and subscribe', function () { diff --git a/spec/FilesController.spec.js b/spec/FilesController.spec.js index 4c5880ccf4..287a89fb23 100644 --- a/spec/FilesController.spec.js +++ b/spec/FilesController.spec.js @@ -1,5 +1,4 @@ -const LoggerController = - require('../lib/Controllers/LoggerController').LoggerController; +const LoggerController = require('../lib/Controllers/LoggerController').LoggerController; const WinstonLoggerAdapter = require('../lib/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter; const GridFSBucketAdapter = @@ -24,23 +23,12 @@ const mockAdapter = { describe('FilesController', () => { it('should properly expand objects with sync getFileLocation', async () => { const config = Config.get(Parse.applicationId); - const gridFSAdapter = new GridFSBucketAdapter( - 'mongodb://localhost:27017/parse' - ); + const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse'); gridFSAdapter.getFileLocation = (config, filename) => { - return ( - config.mount + - '/files/' + - config.applicationId + - '/' + - encodeURIComponent(filename) - ); + return config.mount + '/files/' + config.applicationId + '/' + encodeURIComponent(filename); }; const filesController = new FilesController(gridFSAdapter); - const result = await filesController.expandFilesInObject( - config, - function () {} - ); + const result = await filesController.expandFilesInObject(config, function () {}); expect(result).toBeUndefined(); @@ -58,24 +46,13 @@ describe('FilesController', () => { it('should properly expand objects with async getFileLocation', async () => { const config = Config.get(Parse.applicationId); - const gridFSAdapter = new GridFSBucketAdapter( - 'mongodb://localhost:27017/parse' - ); + const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse'); gridFSAdapter.getFileLocation = async (config, filename) => { await Promise.resolve(); - return ( - config.mount + - '/files/' + - config.applicationId + - '/' + - encodeURIComponent(filename) - ); + return config.mount + '/files/' + config.applicationId + '/' + encodeURIComponent(filename); }; const filesController = new FilesController(gridFSAdapter); - const result = await filesController.expandFilesInObject( - config, - function () {} - ); + const result = await filesController.expandFilesInObject(config, function () {}); expect(result).toBeUndefined(); @@ -93,9 +70,7 @@ describe('FilesController', () => { it('should call getFileLocation when config.fileKey is undefined', async () => { const config = {}; - const gridFSAdapter = new GridFSBucketAdapter( - 'mongodb://localhost:27017/parse' - ); + const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse'); const fullFile = { name: 'mock-name', @@ -108,18 +83,13 @@ describe('FilesController', () => { const anObject = { aFile: fullFile }; await filesController.expandFilesInObject(config, anObject); - expect(gridFSAdapter.getFileLocation).toHaveBeenCalledWith( - config, - fullFile.name - ); + expect(gridFSAdapter.getFileLocation).toHaveBeenCalledWith(config, fullFile.name); expect(anObject.aFile.url).toEqual('mock-url'); }); it('should call getFileLocation when config.fileKey is defined', async () => { const config = { fileKey: 'mock-key' }; - const gridFSAdapter = new GridFSBucketAdapter( - 'mongodb://localhost:27017/parse' - ); + const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse'); const fullFile = { name: 'mock-name', @@ -132,37 +102,25 @@ describe('FilesController', () => { const anObject = { aFile: fullFile }; await filesController.expandFilesInObject(config, anObject); - expect(gridFSAdapter.getFileLocation).toHaveBeenCalledWith( - config, - fullFile.name - ); + expect(gridFSAdapter.getFileLocation).toHaveBeenCalledWith(config, fullFile.name); expect(anObject.aFile.url).toEqual('mock-url'); }); - it_only_db('mongo')( - 'should pass databaseOptions to GridFSBucketAdapter', - async () => { - await reconfigureServer({ - databaseURI: 'mongodb://localhost:27017/parse', - filesAdapter: null, - databaseAdapter: null, - databaseOptions: { - retryWrites: true, - }, - }); - const config = Config.get(Parse.applicationId); - expect(config.database.adapter._mongoOptions.retryWrites).toBeTrue(); - expect( - config.filesController.adapter._mongoOptions.retryWrites - ).toBeTrue(); - expect( - config.filesController.adapter._mongoOptions.enableSchemaHooks - ).toBeUndefined(); - expect( - config.filesController.adapter._mongoOptions.schemaCacheTtl - ).toBeUndefined(); - } - ); + it_only_db('mongo')('should pass databaseOptions to GridFSBucketAdapter', async () => { + await reconfigureServer({ + databaseURI: 'mongodb://localhost:27017/parse', + filesAdapter: null, + databaseAdapter: null, + databaseOptions: { + retryWrites: true, + }, + }); + const config = Config.get(Parse.applicationId); + expect(config.database.adapter._mongoOptions.retryWrites).toBeTrue(); + expect(config.filesController.adapter._mongoOptions.retryWrites).toBeTrue(); + expect(config.filesController.adapter._mongoOptions.enableSchemaHooks).toBeUndefined(); + expect(config.filesController.adapter._mongoOptions.schemaCacheTtl).toBeUndefined(); + }); it('should create a server log on failure', done => { const logController = new LoggerController(new WinstonLoggerAdapter()); @@ -174,16 +132,12 @@ describe('FilesController', () => { () => setImmediate(() => Promise.resolve('done')) ) .then(() => new Promise(resolve => setTimeout(resolve, 200))) - .then(() => - logController.getLogs({ from: Date.now() - 1000, size: 1000 }) - ) + .then(() => logController.getLogs({ from: Date.now() - 1000, size: 1000 })) .then(logs => { // we get two logs here: 1. the source of the failure to save the file // and 2 the message that will be sent back to the client. - const log1 = logs.find( - x => x.message === 'Error creating a file: it failed with xyz' - ); + const log1 = logs.find(x => x.message === 'Error creating a file: it failed with xyz'); expect(log1.level).toBe('error'); const log2 = logs.find(x => x.message === 'it failed with xyz'); @@ -212,9 +166,7 @@ describe('FilesController', () => { it('should add a unique hash to the file name when the preserveFileName option is false', async () => { const config = Config.get(Parse.applicationId); - const gridFSAdapter = new GridFSBucketAdapter( - 'mongodb://localhost:27017/parse' - ); + const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse'); spyOn(gridFSAdapter, 'createFile'); gridFSAdapter.createFile.and.returnValue(Promise.resolve()); const fileName = 'randomFileName.pdf'; @@ -233,9 +185,7 @@ describe('FilesController', () => { it('should not add a unique hash to the file name when the preserveFileName option is true', async () => { const config = Config.get(Parse.applicationId); - const gridFSAdapter = new GridFSBucketAdapter( - 'mongodb://localhost:27017/parse' - ); + const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse'); spyOn(gridFSAdapter, 'createFile'); gridFSAdapter.createFile.and.returnValue(Promise.resolve()); const fileName = 'randomFileName.pdf'; @@ -246,9 +196,7 @@ describe('FilesController', () => { await filesController.createFile(config, fileName); expect(gridFSAdapter.createFile).toHaveBeenCalledTimes(1); - expect(gridFSAdapter.createFile.calls.mostRecent().args[0]).toEqual( - fileName - ); + expect(gridFSAdapter.createFile.calls.mostRecent().args[0]).toEqual(fileName); }); it('should handle adapter without getMetadata', async () => { @@ -261,18 +209,14 @@ describe('FilesController', () => { }); it('should reject slashes in file names', done => { - const gridFSAdapter = new GridFSBucketAdapter( - 'mongodb://localhost:27017/parse' - ); + const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse'); const fileName = 'foo/randomFileName.pdf'; expect(gridFSAdapter.validateFilename(fileName)).not.toBe(null); done(); }); it('should also reject slashes in file names', done => { - const gridFSAdapter = new GridFSBucketAdapter( - 'mongodb://localhost:27017/parse' - ); + const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse'); const fileName = 'foo/randomFileName.pdf'; expect(gridFSAdapter.validateFilename(fileName)).not.toBe(null); done(); diff --git a/spec/GridFSBucketStorageAdapter.spec.js b/spec/GridFSBucketStorageAdapter.spec.js index bbcf17f37d..88da2bbd2d 100644 --- a/spec/GridFSBucketStorageAdapter.spec.js +++ b/spec/GridFSBucketStorageAdapter.spec.js @@ -46,8 +46,7 @@ describe_only_db('mongo')('GridFSBucket', () => { await expectMissingFile(encryptedAdapter, 'myFileName'); const originalString = 'abcdefghi'; await encryptedAdapter.createFile('myFileName', originalString); - const unencryptedResult = - await unencryptedAdapter.getFileData('myFileName'); + const unencryptedResult = await unencryptedAdapter.getFileData('myFileName'); expect(unencryptedResult.toString('utf8')).not.toBe(originalString); const encryptedResult = await encryptedAdapter.getFileData('myFileName'); expect(encryptedResult.toString('utf8')).toBe(originalString); @@ -72,8 +71,7 @@ describe_only_db('mongo')('GridFSBucket', () => { const unencryptedResult2 = await unencryptedAdapter.getFileData(fileName2); expect(unencryptedResult2.toString('utf8')).toBe(data2); //Check if encrypted adapter can read data and make sure it's not the same as unEncrypted adapter - const { rotated, notRotated } = - await encryptedAdapter.rotateEncryptionKey(); + const { rotated, notRotated } = await encryptedAdapter.rotateEncryptionKey(); expect(rotated.length).toEqual(2); expect( rotated.filter(function (value) { @@ -100,28 +98,18 @@ describe_only_db('mongo')('GridFSBucket', () => { it('should rotate key of all old encrypted GridFS files to encrypted files', async () => { const oldEncryptionKey = 'oldKeyThatILoved'; - const oldEncryptedAdapter = new GridFSBucketAdapter( - databaseURI, - {}, - oldEncryptionKey - ); - const encryptedAdapter = new GridFSBucketAdapter( - databaseURI, - {}, - 'newKeyThatILove' - ); + const oldEncryptedAdapter = new GridFSBucketAdapter(databaseURI, {}, oldEncryptionKey); + const encryptedAdapter = new GridFSBucketAdapter(databaseURI, {}, 'newKeyThatILove'); const fileName1 = 'file1.txt'; const data1 = 'hello world'; const fileName2 = 'file2.txt'; const data2 = 'hello new world'; //Store unecrypted files await oldEncryptedAdapter.createFile(fileName1, data1); - const oldEncryptedResult1 = - await oldEncryptedAdapter.getFileData(fileName1); + const oldEncryptedResult1 = await oldEncryptedAdapter.getFileData(fileName1); expect(oldEncryptedResult1.toString('utf8')).toBe(data1); await oldEncryptedAdapter.createFile(fileName2, data2); - const oldEncryptedResult2 = - await oldEncryptedAdapter.getFileData(fileName2); + const oldEncryptedResult2 = await oldEncryptedAdapter.getFileData(fileName2); expect(oldEncryptedResult2.toString('utf8')).toBe(data2); //Check if encrypted adapter can read data and make sure it's not the same as unEncrypted adapter const { rotated, notRotated } = await encryptedAdapter.rotateEncryptionKey({ @@ -167,11 +155,7 @@ describe_only_db('mongo')('GridFSBucket', () => { it('should rotate key of all old encrypted GridFS files to unencrypted files', async () => { const oldEncryptionKey = 'oldKeyThatILoved'; - const oldEncryptedAdapter = new GridFSBucketAdapter( - databaseURI, - {}, - oldEncryptionKey - ); + const oldEncryptedAdapter = new GridFSBucketAdapter(databaseURI, {}, oldEncryptionKey); const unEncryptedAdapter = new GridFSBucketAdapter(databaseURI); const fileName1 = 'file1.txt'; const data1 = 'hello world'; @@ -179,18 +163,15 @@ describe_only_db('mongo')('GridFSBucket', () => { const data2 = 'hello new world'; //Store unecrypted files await oldEncryptedAdapter.createFile(fileName1, data1); - const oldEncryptedResult1 = - await oldEncryptedAdapter.getFileData(fileName1); + const oldEncryptedResult1 = await oldEncryptedAdapter.getFileData(fileName1); expect(oldEncryptedResult1.toString('utf8')).toBe(data1); await oldEncryptedAdapter.createFile(fileName2, data2); - const oldEncryptedResult2 = - await oldEncryptedAdapter.getFileData(fileName2); + const oldEncryptedResult2 = await oldEncryptedAdapter.getFileData(fileName2); expect(oldEncryptedResult2.toString('utf8')).toBe(data2); //Check if unEncrypted adapter can read data and make sure it's not the same as oldEncrypted adapter - const { rotated, notRotated } = - await unEncryptedAdapter.rotateEncryptionKey({ - oldKey: oldEncryptionKey, - }); + const { rotated, notRotated } = await unEncryptedAdapter.rotateEncryptionKey({ + oldKey: oldEncryptionKey, + }); expect(rotated.length).toEqual(2); expect( rotated.filter(function (value) { @@ -231,16 +212,8 @@ describe_only_db('mongo')('GridFSBucket', () => { it('should only encrypt specified fileNames', async () => { const oldEncryptionKey = 'oldKeyThatILoved'; - const oldEncryptedAdapter = new GridFSBucketAdapter( - databaseURI, - {}, - oldEncryptionKey - ); - const encryptedAdapter = new GridFSBucketAdapter( - databaseURI, - {}, - 'newKeyThatILove' - ); + const oldEncryptedAdapter = new GridFSBucketAdapter(databaseURI, {}, oldEncryptionKey); + const encryptedAdapter = new GridFSBucketAdapter(databaseURI, {}, 'newKeyThatILove'); const unEncryptedAdapter = new GridFSBucketAdapter(databaseURI); const fileName1 = 'file1.txt'; const data1 = 'hello world'; @@ -248,12 +221,10 @@ describe_only_db('mongo')('GridFSBucket', () => { const data2 = 'hello new world'; //Store unecrypted files await oldEncryptedAdapter.createFile(fileName1, data1); - const oldEncryptedResult1 = - await oldEncryptedAdapter.getFileData(fileName1); + const oldEncryptedResult1 = await oldEncryptedAdapter.getFileData(fileName1); expect(oldEncryptedResult1.toString('utf8')).toBe(data1); await oldEncryptedAdapter.createFile(fileName2, data2); - const oldEncryptedResult2 = - await oldEncryptedAdapter.getFileData(fileName2); + const oldEncryptedResult2 = await oldEncryptedAdapter.getFileData(fileName2); expect(oldEncryptedResult2.toString('utf8')).toBe(data2); //Inject unecrypted file to see if causes an issue const fileName3 = 'file3.txt'; @@ -309,16 +280,8 @@ describe_only_db('mongo')('GridFSBucket', () => { it("should return fileNames of those it can't encrypt with the new key", async () => { const oldEncryptionKey = 'oldKeyThatILoved'; - const oldEncryptedAdapter = new GridFSBucketAdapter( - databaseURI, - {}, - oldEncryptionKey - ); - const encryptedAdapter = new GridFSBucketAdapter( - databaseURI, - {}, - 'newKeyThatILove' - ); + const oldEncryptedAdapter = new GridFSBucketAdapter(databaseURI, {}, oldEncryptionKey); + const encryptedAdapter = new GridFSBucketAdapter(databaseURI, {}, 'newKeyThatILove'); const unEncryptedAdapter = new GridFSBucketAdapter(databaseURI); const fileName1 = 'file1.txt'; const data1 = 'hello world'; @@ -326,12 +289,10 @@ describe_only_db('mongo')('GridFSBucket', () => { const data2 = 'hello new world'; //Store unecrypted files await oldEncryptedAdapter.createFile(fileName1, data1); - const oldEncryptedResult1 = - await oldEncryptedAdapter.getFileData(fileName1); + const oldEncryptedResult1 = await oldEncryptedAdapter.getFileData(fileName1); expect(oldEncryptedResult1.toString('utf8')).toBe(data1); await oldEncryptedAdapter.createFile(fileName2, data2); - const oldEncryptedResult2 = - await oldEncryptedAdapter.getFileData(fileName2); + const oldEncryptedResult2 = await oldEncryptedAdapter.getFileData(fileName2); expect(oldEncryptedResult2.toString('utf8')).toBe(data2); //Inject unecrypted file to see if causes an issue const fileName3 = 'file3.txt'; @@ -493,9 +454,7 @@ describe_only_db('mongo')('GridFSBucket', () => { await db.admin().serverStatus(); expect(false).toBe(true); } catch (e) { - expect(e.message).toEqual( - 'Client must be connected before running operations' - ); + expect(e.message).toEqual('Client must be connected before running operations'); } }); }); diff --git a/spec/HTTPRequest.spec.js b/spec/HTTPRequest.spec.js index c3162a6178..b138a010b0 100644 --- a/spec/HTTPRequest.spec.js +++ b/spec/HTTPRequest.spec.js @@ -119,9 +119,7 @@ describe('httpRequest', () => { const result = httpRequest.encodeBody(options); expect(result.body).toEqual('foo=bar'); - expect(result.headers['Content-Type']).toEqual( - 'application/x-www-form-urlencoded' - ); + expect(result.headers['Content-Type']).toEqual('application/x-www-form-urlencoded'); }); it('should encode a JSON body', () => { diff --git a/spec/Idempotency.spec.js b/spec/Idempotency.spec.js index 49b7b14ea9..fd666d4360 100644 --- a/spec/Idempotency.spec.js +++ b/spec/Idempotency.spec.js @@ -24,12 +24,7 @@ describe('Idempotency', () => { { reqId: reqId }, { limit: 1 } ); - await rest.del( - config, - auth.master(config), - '_Idempotency', - res.results[0].objectId - ); + await rest.del(config, auth.master(config), '_Idempotency', res.results[0].objectId); } async function setup(options) { await reconfigureServer({ @@ -45,20 +40,13 @@ describe('Idempotency', () => { jasmine.DEFAULT_TIMEOUT_INTERVAL = 200000; } await setup({ - paths: [ - 'functions/.*', - 'jobs/.*', - 'classes/.*', - 'users', - 'installations', - ], + paths: ['functions/.*', 'jobs/.*', 'classes/.*', 'users', 'installations'], ttl: ttl, }); }); afterEach(() => { - jasmine.DEFAULT_TIMEOUT_INTERVAL = - process.env.PARSE_SERVER_TEST_TIMEOUT || 10000; + jasmine.DEFAULT_TIMEOUT_INTERVAL = process.env.PARSE_SERVER_TEST_TIMEOUT || 10000; }); // Tests @@ -276,9 +264,7 @@ describe('Idempotency', () => { it_id('0ecd2cd2-dafb-4a2b-bb2b-9ad4c9aca777')(it)( 'should re-throw any other error unchanged when writing request entry fails for any other reason', async () => { - spyOn(rest, 'create').and.rejectWith( - new Parse.Error(0, 'some other error') - ); + spyOn(rest, 'create').and.rejectWith(new Parse.Error(0, 'some other error')); Parse.Cloud.define('myFunction', () => {}); const params = { method: 'POST', diff --git a/spec/InMemoryCacheAdapter.spec.js b/spec/InMemoryCacheAdapter.spec.js index ebbf957f9f..add976fbc9 100644 --- a/spec/InMemoryCacheAdapter.spec.js +++ b/spec/InMemoryCacheAdapter.spec.js @@ -1,5 +1,4 @@ -const InMemoryCacheAdapter = - require('../lib/Adapters/Cache/InMemoryCacheAdapter').default; +const InMemoryCacheAdapter = require('../lib/Adapters/Cache/InMemoryCacheAdapter').default; describe('InMemoryCacheAdapter', function () { const KEY = 'hello'; @@ -17,12 +16,7 @@ describe('InMemoryCacheAdapter', function () { }); // Verify all methods return promises. - Promise.all([ - cache.put(KEY, VALUE), - cache.del(KEY), - cache.get(KEY), - cache.clear(), - ]).then(() => { + Promise.all([cache.put(KEY, VALUE), cache.del(KEY), cache.get(KEY), cache.clear()]).then(() => { done(); }); }); diff --git a/spec/InstallationsRouter.spec.js b/spec/InstallationsRouter.spec.js index 4df5f89f72..8e5a80c135 100644 --- a/spec/InstallationsRouter.spec.js +++ b/spec/InstallationsRouter.spec.js @@ -1,8 +1,7 @@ const auth = require('../lib/Auth'); const Config = require('../lib/Config'); const rest = require('../lib/rest'); -const InstallationsRouter = - require('../lib/Routers/InstallationsRouter').InstallationsRouter; +const InstallationsRouter = require('../lib/Routers/InstallationsRouter').InstallationsRouter; describe('InstallationsRouter', () => { it('uses find condition from request.body', done => { @@ -29,19 +28,9 @@ describe('InstallationsRouter', () => { const router = new InstallationsRouter(); rest - .create( - config, - auth.nobody(config), - '_Installation', - androidDeviceRequest - ) + .create(config, auth.nobody(config), '_Installation', androidDeviceRequest) .then(() => { - return rest.create( - config, - auth.nobody(config), - '_Installation', - iosDeviceRequest - ); + return rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest); }) .then(() => { return router.handleFind(request); @@ -81,19 +70,9 @@ describe('InstallationsRouter', () => { const router = new InstallationsRouter(); rest - .create( - config, - auth.nobody(config), - '_Installation', - androidDeviceRequest - ) + .create(config, auth.nobody(config), '_Installation', androidDeviceRequest) .then(() => { - return rest.create( - config, - auth.nobody(config), - '_Installation', - iosDeviceRequest - ); + return rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest); }) .then(() => { return router.handleFind(request); @@ -132,19 +111,9 @@ describe('InstallationsRouter', () => { Config.get('test'); const router = new InstallationsRouter(); rest - .create( - config, - auth.nobody(config), - '_Installation', - androidDeviceRequest - ) + .create(config, auth.nobody(config), '_Installation', androidDeviceRequest) .then(() => { - return rest.create( - config, - auth.nobody(config), - '_Installation', - iosDeviceRequest - ); + return rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest); }) .then(() => { return router.handleFind(request); @@ -182,20 +151,8 @@ describe('InstallationsRouter', () => { const router = new InstallationsRouter(); rest - .create( - config, - auth.nobody(config), - '_Installation', - androidDeviceRequest - ) - .then(() => - rest.create( - config, - auth.nobody(config), - '_Installation', - iosDeviceRequest - ) - ) + .create(config, auth.nobody(config), '_Installation', androidDeviceRequest) + .then(() => rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest)) .then(() => router.handleFind(request)) .then(res => { const response = res.response; @@ -230,18 +187,8 @@ describe('InstallationsRouter', () => { }; const router = new InstallationsRouter(); - await rest.create( - config, - auth.nobody(config), - '_Installation', - androidDeviceRequest - ); - await rest.create( - config, - auth.nobody(config), - '_Installation', - iosDeviceRequest - ); + await rest.create(config, auth.nobody(config), '_Installation', androidDeviceRequest); + await rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest); let res = await router.handleFind(request); let response = res.response; expect(response.results.length).toEqual(2); @@ -256,58 +203,45 @@ describe('InstallationsRouter', () => { expect(response.count).toEqual(2); }); - it_exclude_dbs(['postgres'])( - 'query installations with limit = 0 and count = 1', - done => { - const config = Config.get('test'); - const androidDeviceRequest = { - installationId: '12345678-abcd-abcd-abcd-123456789abc', - deviceType: 'android', - }; - const iosDeviceRequest = { - installationId: '12345678-abcd-abcd-abcd-123456789abd', - deviceType: 'ios', - }; - const request = { - config: config, - auth: auth.master(config), - body: {}, - query: { - limit: 0, - count: 1, - }, - info: {}, - }; + it_exclude_dbs(['postgres'])('query installations with limit = 0 and count = 1', done => { + const config = Config.get('test'); + const androidDeviceRequest = { + installationId: '12345678-abcd-abcd-abcd-123456789abc', + deviceType: 'android', + }; + const iosDeviceRequest = { + installationId: '12345678-abcd-abcd-abcd-123456789abd', + deviceType: 'ios', + }; + const request = { + config: config, + auth: auth.master(config), + body: {}, + query: { + limit: 0, + count: 1, + }, + info: {}, + }; - const router = new InstallationsRouter(); - rest - .create( - config, - auth.nobody(config), - '_Installation', - androidDeviceRequest - ) - .then(() => { - return rest.create( - config, - auth.nobody(config), - '_Installation', - iosDeviceRequest - ); - }) - .then(() => { - return router.handleFind(request); - }) - .then(res => { - const response = res.response; - expect(response.results.length).toEqual(0); - expect(response.count).toEqual(2); - done(); - }) - .catch(err => { - fail(JSON.stringify(err)); - done(); - }); - } - ); + const router = new InstallationsRouter(); + rest + .create(config, auth.nobody(config), '_Installation', androidDeviceRequest) + .then(() => { + return rest.create(config, auth.nobody(config), '_Installation', iosDeviceRequest); + }) + .then(() => { + return router.handleFind(request); + }) + .then(res => { + const response = res.response; + expect(response.results.length).toEqual(0); + expect(response.count).toEqual(2); + done(); + }) + .catch(err => { + fail(JSON.stringify(err)); + done(); + }); + }); }); diff --git a/spec/JobSchedule.spec.js b/spec/JobSchedule.spec.js index feb37ec6a2..b4a75764dd 100644 --- a/spec/JobSchedule.spec.js +++ b/spec/JobSchedule.spec.js @@ -46,21 +46,17 @@ describe('JobSchedule', () => { }); it('should reject access when not using masterKey (/jobs)', done => { - request( - Object.assign( - { url: Parse.serverURL + '/cloud_code/jobs' }, - defaultOptions - ) - ).then(done.fail, () => done()); + request(Object.assign({ url: Parse.serverURL + '/cloud_code/jobs' }, defaultOptions)).then( + done.fail, + () => done() + ); }); it('should reject access when not using masterKey (/jobs/data)', done => { - request( - Object.assign( - { url: Parse.serverURL + '/cloud_code/jobs/data' }, - defaultOptions - ) - ).then(done.fail, () => done()); + request(Object.assign({ url: Parse.serverURL + '/cloud_code/jobs/data' }, defaultOptions)).then( + done.fail, + () => done() + ); }); it('should reject access when not using masterKey (PUT /jobs/id)', done => { @@ -82,12 +78,10 @@ describe('JobSchedule', () => { }); it('should allow access when using masterKey (GET /jobs)', done => { - request( - Object.assign( - { url: Parse.serverURL + '/cloud_code/jobs' }, - masterKeyOptions - ) - ).then(done, done.fail); + request(Object.assign({ url: Parse.serverURL + '/cloud_code/jobs' }, masterKeyOptions)).then( + done, + done.fail + ); }); it('should create a job schedule', done => { @@ -107,10 +101,7 @@ describe('JobSchedule', () => { }) .then(() => { return request( - Object.assign( - { url: Parse.serverURL + '/cloud_code/jobs' }, - masterKeyOptions - ) + Object.assign({ url: Parse.serverURL + '/cloud_code/jobs' }, masterKeyOptions) ); }) .then(res => { @@ -267,10 +258,7 @@ describe('JobSchedule', () => { }) .then(() => { return request( - Object.assign( - { url: Parse.serverURL + '/cloud_code/jobs/data' }, - masterKeyOptions - ) + Object.assign({ url: Parse.serverURL + '/cloud_code/jobs/data' }, masterKeyOptions) ); }) .then(response => { diff --git a/spec/LdapAuth.spec.js b/spec/LdapAuth.spec.js index 4540487236..ea30f59f0c 100644 --- a/spec/LdapAuth.spec.js +++ b/spec/LdapAuth.spec.js @@ -26,40 +26,24 @@ describe('Ldap Auth', () => { url: `ldap://localhost:${port}`, dn: 'uid={{id}}, o=example', }; - await ldap.validateAuthData( - { id: 'testuser', password: 'secret' }, - options - ); + await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options); server.close(done); }); it('Should succeed with right credentials when LDAPS is used and certifcate is not checked', async done => { - const server = await mockLdapServer( - sslport, - 'uid=testuser, o=example', - false, - true - ); + const server = await mockLdapServer(sslport, 'uid=testuser, o=example', false, true); const options = { suffix: 'o=example', url: `ldaps://localhost:${sslport}`, dn: 'uid={{id}}, o=example', tlsOptions: { rejectUnauthorized: false }, }; - await ldap.validateAuthData( - { id: 'testuser', password: 'secret' }, - options - ); + await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options); server.close(done); }); it('Should succeed when LDAPS is used and the presented certificate is the expected certificate', async done => { - const server = await mockLdapServer( - sslport, - 'uid=testuser, o=example', - false, - true - ); + const server = await mockLdapServer(sslport, 'uid=testuser, o=example', false, true); const options = { suffix: 'o=example', url: `ldaps://localhost:${sslport}`, @@ -69,20 +53,12 @@ describe('Ldap Auth', () => { rejectUnauthorized: true, }, }; - await ldap.validateAuthData( - { id: 'testuser', password: 'secret' }, - options - ); + await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options); server.close(done); }); it('Should fail when LDAPS is used and the presented certificate is not the expected certificate', async done => { - const server = await mockLdapServer( - sslport, - 'uid=testuser, o=example', - false, - true - ); + const server = await mockLdapServer(sslport, 'uid=testuser, o=example', false, true); const options = { suffix: 'o=example', url: `ldaps://localhost:${sslport}`, @@ -93,10 +69,7 @@ describe('Ldap Auth', () => { }, }; try { - await ldap.validateAuthData( - { id: 'testuser', password: 'secret' }, - options - ); + await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options); fail(); } catch (err) { expect(err.message).toBe('LDAPS: Certificate mismatch'); @@ -105,12 +78,7 @@ describe('Ldap Auth', () => { }); it('Should fail when LDAPS is used certifcate matches but credentials are wrong', async done => { - const server = await mockLdapServer( - sslport, - 'uid=testuser, o=example', - false, - true - ); + const server = await mockLdapServer(sslport, 'uid=testuser, o=example', false, true); const options = { suffix: 'o=example', url: `ldaps://localhost:${sslport}`, @@ -121,10 +89,7 @@ describe('Ldap Auth', () => { }, }; try { - await ldap.validateAuthData( - { id: 'testuser', password: 'wrong!' }, - options - ); + await ldap.validateAuthData({ id: 'testuser', password: 'wrong!' }, options); fail(); } catch (err) { expect(err.message).toBe('LDAP: Wrong username or password'); @@ -140,10 +105,7 @@ describe('Ldap Auth', () => { dn: 'uid={{id}}, o=example', }; try { - await ldap.validateAuthData( - { id: 'testuser', password: 'wrong!' }, - options - ); + await ldap.validateAuthData({ id: 'testuser', password: 'wrong!' }, options); fail(); } catch (err) { expect(err.message).toBe('LDAP: Wrong username or password'); @@ -158,13 +120,9 @@ describe('Ldap Auth', () => { url: `ldap://localhost:${port}`, dn: 'uid={{id}}, o=example', groupCn: 'powerusers', - groupFilter: - '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))', + groupFilter: '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))', }; - await ldap.validateAuthData( - { id: 'testuser', password: 'secret' }, - options - ); + await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options); server.close(done); }); @@ -175,14 +133,10 @@ describe('Ldap Auth', () => { url: `ldap://localhost:${port}`, dn: 'uid={{id}}, o=example', groupCn: 'groupTheUserIsNotIn', - groupFilter: - '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))', + groupFilter: '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))', }; try { - await ldap.validateAuthData( - { id: 'testuser', password: 'secret' }, - options - ); + await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options); fail(); } catch (err) { expect(err.message).toBe('LDAP: User not in group'); @@ -197,14 +151,10 @@ describe('Ldap Auth', () => { url: `ldap://localhost:${port}`, dn: 'uid={{id}}, o=example', groupCn: 'powerusers', - groupFilter: - '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))', + groupFilter: '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))', }; try { - await ldap.validateAuthData( - { id: 'testuser', password: 'secret' }, - options - ); + await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options); fail(); } catch (err) { expect(err.message).toBe('LDAP group search failed'); @@ -219,14 +169,10 @@ describe('Ldap Auth', () => { url: `ldap://localhost:${port}`, dn: 'uid={{id}}, o=example', groupCn: 'powerusers', - groupFilter: - '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))', + groupFilter: '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))', }; try { - await ldap.validateAuthData( - { id: 'testuser', password: 'secret' }, - options - ); + await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options); fail(); } catch (err) { expect(err.message).toBe('LDAP group search failed'); @@ -258,9 +204,7 @@ describe('Ldap Auth', () => { const authData = { authData: { id: 'testuser', password: 'secret' } }; const returnedUser = await Parse.User.logInWith('ldap', authData); const query = new Parse.Query('User'); - const user = await query - .equalTo('objectId', returnedUser.id) - .first({ useMasterKey: true }); + const user = await query.equalTo('objectId', returnedUser.id).first({ useMasterKey: true }); expect(user.get('authData')).toEqual({ ldap: { id: 'testuser' } }); expect(user.get('authData').ldap.password).toBeUndefined(); server.close(done); diff --git a/spec/LoggerController.spec.js b/spec/LoggerController.spec.js index cb7fc1453c..eee9bba8a8 100644 --- a/spec/LoggerController.spec.js +++ b/spec/LoggerController.spec.js @@ -1,5 +1,4 @@ -const LoggerController = - require('../lib/Controllers/LoggerController').LoggerController; +const LoggerController = require('../lib/Controllers/LoggerController').LoggerController; const WinstonLoggerAdapter = require('../lib/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter; @@ -28,9 +27,7 @@ describe('LoggerController', () => { expect(LoggerController.validDateTime()).toBe(null); expect(LoggerController.validDateTime('String')).toBe(null); expect(LoggerController.validDateTime(123456).getTime()).toBe(123456); - expect( - LoggerController.validDateTime('2016-01-01Z00:00:00').getTime() - ).toBe(1451606400000); + expect(LoggerController.validDateTime('2016-01-01Z00:00:00').getTime()).toBe(1451606400000); done(); }); diff --git a/spec/LogsRouter.spec.js b/spec/LogsRouter.spec.js index ffe886abef..a53072ef86 100644 --- a/spec/LogsRouter.spec.js +++ b/spec/LogsRouter.spec.js @@ -2,8 +2,7 @@ const request = require('../lib/request'); const LogsRouter = require('../lib/Routers/LogsRouter').LogsRouter; -const LoggerController = - require('../lib/Controllers/LoggerController').LoggerController; +const LoggerController = require('../lib/Controllers/LoggerController').LoggerController; const WinstonLoggerAdapter = require('../lib/Adapters/Logger/WinstonLoggerAdapter').WinstonLoggerAdapter; @@ -95,9 +94,7 @@ describe_only(() => { const body = response.data; expect(response.status).toEqual(200); // 4th entry is our actual GET request - expect(body[2].url).toEqual( - '/1/login?username=test&password=********' - ); + expect(body[2].url).toEqual('/1/login?username=test&password=********'); expect(body[2].message).toEqual( 'REQUEST for [GET] /1/login?username=test&password=********: {}' ); @@ -132,9 +129,7 @@ describe_only(() => { const body = response.data; expect(response.status).toEqual(200); // 4th entry is our actual GET request - expect(body[2].url).toEqual( - '/1/login?username=test&password=********' - ); + expect(body[2].url).toEqual('/1/login?username=test&password=********'); expect(body[2].message).toEqual( 'REQUEST for [GET] /1/login?username=test&password=********: {}' ); diff --git a/spec/Middlewares.spec.js b/spec/Middlewares.spec.js index b0e6110637..2ad29768bd 100644 --- a/spec/Middlewares.spec.js +++ b/spec/Middlewares.spec.js @@ -32,22 +32,19 @@ describe('middlewares', () => { AppCache.del(fakeReq.body._ApplicationId); }); - it_id('4cc18d90-1763-4725-97fa-f63fb4692fc4')(it)( - 'should use _ContentType if provided', - done => { - AppCachePut(fakeReq.body._ApplicationId, { - masterKeyIps: ['127.0.0.1'], - }); - expect(fakeReq.headers['content-type']).toEqual(undefined); - const contentType = 'image/jpeg'; - fakeReq.body._ContentType = contentType; - middlewares.handleParseHeaders(fakeReq, fakeRes, () => { - expect(fakeReq.headers['content-type']).toEqual(contentType); - expect(fakeReq.body._ContentType).toEqual(undefined); - done(); - }); - } - ); + it_id('4cc18d90-1763-4725-97fa-f63fb4692fc4')(it)('should use _ContentType if provided', done => { + AppCachePut(fakeReq.body._ApplicationId, { + masterKeyIps: ['127.0.0.1'], + }); + expect(fakeReq.headers['content-type']).toEqual(undefined); + const contentType = 'image/jpeg'; + fakeReq.body._ContentType = contentType; + middlewares.handleParseHeaders(fakeReq, fakeRes, () => { + expect(fakeReq.headers['content-type']).toEqual(contentType); + expect(fakeReq.body._ContentType).toEqual(undefined); + done(); + }); + }); it('should give invalid response when keys are configured but no key supplied', async () => { AppCachePut(fakeReq.body._ApplicationId, { @@ -165,9 +162,7 @@ describe('middlewares', () => { fakeReq.ip = '127.0.0.1'; fakeReq.headers['x-parse-master-key'] = 'masterKey'; - const error = await middlewares - .handleParseHeaders(fakeReq, fakeRes, () => {}) - .catch(e => e); + const error = await middlewares.handleParseHeaders(fakeReq, fakeRes, () => {}).catch(e => e); expect(error).toBeDefined(); expect(error.message).toEqual(`unauthorized`); @@ -187,9 +182,7 @@ describe('middlewares', () => { fakeReq.ip = '10.0.0.2'; fakeReq.headers['x-parse-maintenance-key'] = 'masterKey'; - const error = await middlewares - .handleParseHeaders(fakeReq, fakeRes, () => {}) - .catch(e => e); + const error = await middlewares.handleParseHeaders(fakeReq, fakeRes, () => {}).catch(e => e); expect(error).toBeDefined(); expect(error.message).toEqual(`unauthorized`); @@ -207,9 +200,7 @@ describe('middlewares', () => { }); fakeReq.ip = '10.0.0.1'; fakeReq.headers['x-parse-master-key'] = 'masterKey'; - await new Promise(resolve => - middlewares.handleParseHeaders(fakeReq, fakeRes, resolve) - ); + await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve)); expect(fakeReq.auth.isMaster).toBe(true); } ); @@ -223,9 +214,7 @@ describe('middlewares', () => { }); fakeReq.ip = '10.0.0.1'; fakeReq.headers['x-parse-master-key'] = 'masterKey'; - await new Promise(resolve => - middlewares.handleParseHeaders(fakeReq, fakeRes, resolve) - ); + await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve)); expect(fakeReq.auth.isMaster).toBe(true); } ); @@ -242,9 +231,7 @@ describe('middlewares', () => { headers[key] = value; }, }; - const allowCrossDomain = middlewares.allowCrossDomain( - fakeReq.body._ApplicationId - ); + const allowCrossDomain = middlewares.allowCrossDomain(fakeReq.body._ApplicationId); allowCrossDomain(fakeReq, res, () => {}); expect(Object.keys(headers).length).toBe(4); expect(headers['Access-Control-Expose-Headers']).toBe( @@ -262,21 +249,15 @@ describe('middlewares', () => { headers[key] = value; }, }; - const allowCrossDomain = middlewares.allowCrossDomain( - fakeReq.body._ApplicationId - ); + const allowCrossDomain = middlewares.allowCrossDomain(fakeReq.body._ApplicationId); allowCrossDomain(fakeReq, res, () => {}); - expect(headers['Access-Control-Allow-Headers']).toContain( - middlewares.DEFAULT_ALLOWED_HEADERS - ); + expect(headers['Access-Control-Allow-Headers']).toContain(middlewares.DEFAULT_ALLOWED_HEADERS); AppCachePut(fakeReq.body._ApplicationId, { allowHeaders: [], }); allowCrossDomain(fakeReq, res, () => {}); - expect(headers['Access-Control-Allow-Headers']).toContain( - middlewares.DEFAULT_ALLOWED_HEADERS - ); + expect(headers['Access-Control-Allow-Headers']).toContain(middlewares.DEFAULT_ALLOWED_HEADERS); }); it('should append custom headers to Access-Control-Allow-Headers if allowHeaders provided', () => { @@ -289,16 +270,10 @@ describe('middlewares', () => { headers[key] = value; }, }; - const allowCrossDomain = middlewares.allowCrossDomain( - fakeReq.body._ApplicationId - ); + const allowCrossDomain = middlewares.allowCrossDomain(fakeReq.body._ApplicationId); allowCrossDomain(fakeReq, res, () => {}); - expect(headers['Access-Control-Allow-Headers']).toContain( - 'Header-1, Header-2' - ); - expect(headers['Access-Control-Allow-Headers']).toContain( - middlewares.DEFAULT_ALLOWED_HEADERS - ); + expect(headers['Access-Control-Allow-Headers']).toContain('Header-1, Header-2'); + expect(headers['Access-Control-Allow-Headers']).toContain(middlewares.DEFAULT_ALLOWED_HEADERS); }); it('should set default Access-Control-Allow-Origin if allowOrigin is empty', () => { @@ -311,9 +286,7 @@ describe('middlewares', () => { headers[key] = value; }, }; - const allowCrossDomain = middlewares.allowCrossDomain( - fakeReq.body._ApplicationId - ); + const allowCrossDomain = middlewares.allowCrossDomain(fakeReq.body._ApplicationId); allowCrossDomain(fakeReq, res, () => {}); expect(headers['Access-Control-Allow-Origin']).toEqual('*'); }); @@ -328,13 +301,9 @@ describe('middlewares', () => { headers[key] = value; }, }; - const allowCrossDomain = middlewares.allowCrossDomain( - fakeReq.body._ApplicationId - ); + const allowCrossDomain = middlewares.allowCrossDomain(fakeReq.body._ApplicationId); allowCrossDomain(fakeReq, res, () => {}); - expect(headers['Access-Control-Allow-Origin']).toEqual( - 'https://parseplatform.org/' - ); + expect(headers['Access-Control-Allow-Origin']).toEqual('https://parseplatform.org/'); }); it('should support multiple origins if several are defined in allowOrigin as an array', () => { @@ -347,9 +316,7 @@ describe('middlewares', () => { headers[key] = value; }, }; - const allowCrossDomain = middlewares.allowCrossDomain( - fakeReq.body._ApplicationId - ); + const allowCrossDomain = middlewares.allowCrossDomain(fakeReq.body._ApplicationId); // Test with the first domain fakeReq.headers.origin = 'https://a.com'; allowCrossDomain(fakeReq, res, () => {}); @@ -402,24 +369,18 @@ describe('middlewares', () => { expect(middlewares.checkIp(ip, ['::'], new Map())).toBe(true); expect(middlewares.checkIp(ip, ['0.0.0.0'], new Map())).toBe(false); expect(middlewares.checkIp(ip, ['0.0.0.0/0'], new Map())).toBe(false); - expect(middlewares.checkIp(ip, ['123.123.123.123'], new Map())).toBe( - false - ); + expect(middlewares.checkIp(ip, ['123.123.123.123'], new Map())).toBe(false); }); expect(middlewares.checkIp(ipv6, [anotherIpv6], new Map())).toBe(false); expect(middlewares.checkIp(ipv6, [ipv6], new Map())).toBe(true); - expect( - middlewares.checkIp(ipv6, ['2001:db8:85a3:0:0:8a2e:0:0/100'], new Map()) - ).toBe(true); + expect(middlewares.checkIp(ipv6, ['2001:db8:85a3:0:0:8a2e:0:0/100'], new Map())).toBe(true); expect(middlewares.checkIp(ipv4, ['::'], new Map())).toBe(false); expect(middlewares.checkIp(ipv4, ['::/0'], new Map())).toBe(false); expect(middlewares.checkIp(ipv4, ['0.0.0.0'], new Map())).toBe(true); expect(middlewares.checkIp(ipv4, ['0.0.0.0/0'], new Map())).toBe(true); - expect(middlewares.checkIp(ipv4, ['123.123.123.123'], new Map())).toBe( - false - ); + expect(middlewares.checkIp(ipv4, ['123.123.123.123'], new Map())).toBe(false); expect(middlewares.checkIp(ipv4, [ipv4], new Map())).toBe(true); expect(middlewares.checkIp(ipv4, ['192.168.0.0/24'], new Map())).toBe(true); @@ -428,22 +389,15 @@ describe('middlewares', () => { // ::ffff:127.0.0.1 is a padded ipv4 address but not ::1 expect(middlewares.checkIp(localhostV62, ['::1'], new Map())).toBe(false); // ::ffff:127.0.0.1 is a padded ipv4 address and is a match for 127.0.0.1 - expect(middlewares.checkIp(localhostV62, ['127.0.0.1'], new Map())).toBe( - true - ); + expect(middlewares.checkIp(localhostV62, ['127.0.0.1'], new Map())).toBe(true); }); it('should match address with cache', () => { const ipv6 = '2001:0db8:85a3:0000:0000:8a2e:0370:7334'; const cache1 = new Map(); - const spyBlockListCheck = spyOn( - BlockList.prototype, - 'check' - ).and.callThrough(); + const spyBlockListCheck = spyOn(BlockList.prototype, 'check').and.callThrough(); expect(middlewares.checkIp(ipv6, ['::'], cache1)).toBe(true); - expect(cache1.get('2001:0db8:85a3:0000:0000:8a2e:0370:7334')).toBe( - undefined - ); + expect(cache1.get('2001:0db8:85a3:0000:0000:8a2e:0370:7334')).toBe(undefined); expect(cache1.get('allowAllIpv6')).toBe(true); expect(spyBlockListCheck).toHaveBeenCalledTimes(0); diff --git a/spec/MongoStorageAdapter.spec.js b/spec/MongoStorageAdapter.spec.js index 2d9b0220b1..2b3bfb3d7e 100644 --- a/spec/MongoStorageAdapter.spec.js +++ b/spec/MongoStorageAdapter.spec.js @@ -1,10 +1,8 @@ 'use strict'; -const MongoStorageAdapter = - require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter').default; +const MongoStorageAdapter = require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter').default; const { MongoClient, Collection } = require('mongodb'); -const databaseURI = - 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase'; +const databaseURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase'; const request = require('../lib/request'); const Config = require('../lib/Config'); const TestUtils = require('../lib/TestUtils'); @@ -78,9 +76,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { }); adapter .createObject('Foo', { fields: {} }, { objectId: 'abcde' }) - .then(() => - adapter._rawFind('Foo', { $where: `sleep(${maxTimeMS / 2})` }) - ) + .then(() => adapter._rawFind('Foo', { $where: `sleep(${maxTimeMS / 2})` })) .then( () => done(), err => { @@ -97,9 +93,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { }); adapter .createObject('Foo', { fields: {} }, { objectId: 'abcde' }) - .then(() => - adapter._rawFind('Foo', { $where: `sleep(${maxTimeMS * 2})` }) - ) + .then(() => adapter._rawFind('Foo', { $where: `sleep(${maxTimeMS * 2})` })) .then( () => { done.fail('Find succeeded despite taking too long!'); @@ -374,9 +368,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { await adapter.database.admin().serverStatus(); expect(false).toBe(true); } catch (e) { - expect(e.message).toEqual( - 'Client must be connected before running operations' - ); + expect(e.message).toEqual('Client must be connected before running operations'); } }); @@ -398,49 +390,42 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { it('getClass if not exists', async () => { const adapter = new MongoStorageAdapter({ uri: databaseURI }); - await expectAsync(adapter.getClass('UnknownClass')).toBeRejectedWith( - undefined - ); + await expectAsync(adapter.getClass('UnknownClass')).toBeRejectedWith(undefined); }); - it_only_mongodb_version('<5.1 || >=6')( - 'should use index for caseInsensitive query', - async () => { - const user = new Parse.User(); - user.set('username', 'Bugs'); - user.set('password', 'Bunny'); - await user.signUp(); - - const database = Config.get(Parse.applicationId).database; - await database.adapter.dropAllIndexes('_User'); - - const preIndexPlan = await database.find( - '_User', - { username: 'bugs' }, - { caseInsensitive: true, explain: true } - ); + it_only_mongodb_version('<5.1 || >=6')('should use index for caseInsensitive query', async () => { + const user = new Parse.User(); + user.set('username', 'Bugs'); + user.set('password', 'Bunny'); + await user.signUp(); - const schema = await new Parse.Schema('_User').get(); + const database = Config.get(Parse.applicationId).database; + await database.adapter.dropAllIndexes('_User'); - await database.adapter.ensureIndex( - '_User', - schema, - ['username'], - 'case_insensitive_username', - true - ); + const preIndexPlan = await database.find( + '_User', + { username: 'bugs' }, + { caseInsensitive: true, explain: true } + ); - const postIndexPlan = await database.find( - '_User', - { username: 'bugs' }, - { caseInsensitive: true, explain: true } - ); - expect(preIndexPlan.executionStats.executionStages.stage).toBe( - 'COLLSCAN' - ); - expect(postIndexPlan.executionStats.executionStages.stage).toBe('FETCH'); - } - ); + const schema = await new Parse.Schema('_User').get(); + + await database.adapter.ensureIndex( + '_User', + schema, + ['username'], + 'case_insensitive_username', + true + ); + + const postIndexPlan = await database.find( + '_User', + { username: 'bugs' }, + { caseInsensitive: true, explain: true } + ); + expect(preIndexPlan.executionStats.executionStages.stage).toBe('COLLSCAN'); + expect(postIndexPlan.executionStats.executionStages.stage).toBe('FETCH'); + }); it('should delete field without index', async () => { const database = Config.get(Parse.applicationId).database; @@ -448,9 +433,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { obj.set('test', 1); await obj.save(); const schemaBeforeDeletion = await new Parse.Schema('MyObject').get(); - await database.adapter.deleteFields('MyObject', schemaBeforeDeletion, [ - 'test', - ]); + await database.adapter.deleteFields('MyObject', schemaBeforeDeletion, ['test']); const schemaAfterDeletion = await new Parse.Schema('MyObject').get(); expect(schemaBeforeDeletion.fields.test).toBeDefined(); expect(schemaAfterDeletion.fields.test).toBeUndefined(); @@ -462,12 +445,8 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { obj.set('test', 1); await obj.save(); const schemaBeforeDeletion = await new Parse.Schema('MyObject').get(); - await database.adapter.ensureIndex('MyObject', schemaBeforeDeletion, [ - 'test', - ]); - await database.adapter.deleteFields('MyObject', schemaBeforeDeletion, [ - 'test', - ]); + await database.adapter.ensureIndex('MyObject', schemaBeforeDeletion, ['test']); + await database.adapter.deleteFields('MyObject', schemaBeforeDeletion, ['test']); const schemaAfterDeletion = await new Parse.Schema('MyObject').get(); expect(schemaBeforeDeletion.fields.test).toBeDefined(); expect(schemaAfterDeletion.fields.test).toBeUndefined(); @@ -515,9 +494,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { let found = false; Collection.prototype.findOneAndUpdate.calls.all().forEach(call => { found = true; - expect(call.args[2].session.transaction.state).toBe( - 'TRANSACTION_COMMITTED' - ); + expect(call.args[2].session.transaction.state).toBe('TRANSACTION_COMMITTED'); }); expect(found).toBe(true); }); diff --git a/spec/MongoTransform.spec.js b/spec/MongoTransform.spec.js index 8d30b897ee..60adb4b7f0 100644 --- a/spec/MongoTransform.spec.js +++ b/spec/MongoTransform.spec.js @@ -461,11 +461,7 @@ describe('parseObjectToMongoObjectForCreate', () => { it('$regex in $all list', done => { const input = { arrayField: { - $all: [ - { $regex: '^\\Qone\\E' }, - { $regex: '^\\Qtwo\\E' }, - { $regex: '^\\Qthree\\E' }, - ], + $all: [{ $regex: '^\\Qone\\E' }, { $regex: '^\\Qtwo\\E' }, { $regex: '^\\Qthree\\E' }], }, }; const outputValue = { @@ -626,9 +622,7 @@ describe('relativeTimeToDate', () => { describe('Error cases', () => { it('should error if string is completely gibberish', () => { - expect( - Utils.relativeTimeToDate('gibberishasdnklasdnjklasndkl123j123') - ).toEqual({ + expect(Utils.relativeTimeToDate('gibberishasdnklasdnjklasndkl123j123')).toEqual({ status: 'error', info: "Time should either start with 'in' or end with 'ago'", }); diff --git a/spec/NullCacheAdapter.spec.js b/spec/NullCacheAdapter.spec.js index 26d7322299..f5d5e508f4 100644 --- a/spec/NullCacheAdapter.spec.js +++ b/spec/NullCacheAdapter.spec.js @@ -1,5 +1,4 @@ -const NullCacheAdapter = - require('../lib/Adapters/Cache/NullCacheAdapter').default; +const NullCacheAdapter = require('../lib/Adapters/Cache/NullCacheAdapter').default; describe('NullCacheAdapter', function () { const KEY = 'hello'; @@ -11,12 +10,7 @@ describe('NullCacheAdapter', function () { }); // Verify all methods return promises. - Promise.all([ - cache.put(KEY, VALUE), - cache.del(KEY), - cache.get(KEY), - cache.clear(), - ]).then(() => { + Promise.all([cache.put(KEY, VALUE), cache.del(KEY), cache.get(KEY), cache.clear()]).then(() => { done(); }); }); diff --git a/spec/OAuth1.spec.js b/spec/OAuth1.spec.js index 6d61ca054b..34dc8b6925 100644 --- a/spec/OAuth1.spec.js +++ b/spec/OAuth1.spec.js @@ -39,12 +39,7 @@ describe('OAuth', function () { const consumer_secret = 'world'; const auth_token_secret = 'secret'; - request = OAuth.signRequest( - request, - oauth_params, - consumer_secret, - auth_token_secret - ); + request = OAuth.signRequest(request, oauth_params, consumer_secret, auth_token_secret); jequal( request.headers['Authorization'], 'OAuth oauth_consumer_key="hello", oauth_nonce="AAAAAAAAAAAAAAAAA", oauth_signature="8K95bpQcDi9Nd2GkhumTVcw4%2BXw%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="123450000", oauth_token="token", oauth_version="1.0"' diff --git a/spec/PagesRouter.spec.js b/spec/PagesRouter.spec.js index 8ac5aa4d64..8b82fcd316 100644 --- a/spec/PagesRouter.spec.js +++ b/spec/PagesRouter.spec.js @@ -7,8 +7,7 @@ const Utils = require('../lib/Utils'); const { Page } = require('../lib/Page'); const Config = require('../lib/Config'); const Definitions = require('../lib/Options/Definitions'); -const UserController = - require('../lib/Controllers/UserController').UserController; +const UserController = require('../lib/Controllers/UserController').UserController; const { PagesRouter, pages, @@ -120,9 +119,7 @@ describe('Pages Router', () => { }); it('request_password_reset: responds with AJAX success', async () => { - spyOn(UserController.prototype, 'updatePassword').and.callFake(() => - Promise.resolve() - ); + spyOn(UserController.prototype, 'updatePassword').and.callFake(() => Promise.resolve()); const res = await request({ method: 'POST', url: 'http://localhost:8378/1/apps/test/request_password_reset', @@ -184,8 +181,7 @@ describe('Pages Router', () => { let readFile; let exampleLocale; - const fillPlaceholders = (text, fill) => - text.replace(/({{2,3}.*?}{2,3})/g, fill); + const fillPlaceholders = (text, fill) => text.replace(/({{2,3}.*?}{2,3})/g, fill); async function reconfigureServerWithPagesConfig(pagesConfig) { config.pages = pagesConfig; await reconfigureServer(config); @@ -195,14 +191,8 @@ describe('Pages Router', () => { router = new PagesRouter(); readFile = spyOn(fs, 'readFile').and.callThrough(); goToPage = spyOn(PagesRouter.prototype, 'goToPage').and.callThrough(); - pageResponse = spyOn( - PagesRouter.prototype, - 'pageResponse' - ).and.callThrough(); - redirectResponse = spyOn( - PagesRouter.prototype, - 'redirectResponse' - ).and.callThrough(); + pageResponse = spyOn(PagesRouter.prototype, 'pageResponse').and.callThrough(); + redirectResponse = spyOn(PagesRouter.prototype, 'redirectResponse').and.callThrough(); exampleLocale = 'de-AT'; config = { appId: 'test', @@ -241,9 +231,9 @@ describe('Pages Router', () => { expect(Config.get(Parse.applicationId).pages.localizationJsonPath).toBe( Definitions.PagesOptions.localizationJsonPath.default ); - expect( - Config.get(Parse.applicationId).pages.localizationFallbackLocale - ).toBe(Definitions.PagesOptions.localizationFallbackLocale.default); + expect(Config.get(Parse.applicationId).pages.localizationFallbackLocale).toBe( + Definitions.PagesOptions.localizationFallbackLocale.default + ); expect(Config.get(Parse.applicationId).pages.placeholders).toBe( Definitions.PagesOptions.placeholders.default ); @@ -312,18 +302,14 @@ describe('Pages Router', () => { { customRoutes: {} }, ]; for (const option of options) { - await expectAsync( - reconfigureServerWithPagesConfig(option) - ).toBeRejected(); + await expectAsync(reconfigureServerWithPagesConfig(option)).toBeRejected(); } }); }); describe('placeholders', () => { it('replaces placeholder in response content', async () => { - await expectAsync( - router.goToPage(req, pages.passwordResetLinkInvalid) - ).toBeResolved(); + await expectAsync(router.goToPage(req, pages.passwordResetLinkInvalid)).toBeResolved(); expect(readFile.calls.all()[0].returnValue).toBeDefined(); const originalContent = await readFile.calls.all()[0].returnValue; @@ -336,9 +322,7 @@ describe('Pages Router', () => { }); it('removes undefined placeholder in response content', async () => { - await expectAsync( - router.goToPage(req, pages.passwordReset) - ).toBeResolved(); + await expectAsync(router.goToPage(req, pages.passwordReset)).toBeResolved(); expect(readFile.calls.all()[0].returnValue).toBeDefined(); const originalContent = await readFile.calls.all()[0].returnValue; @@ -393,9 +377,7 @@ describe('Pages Router', () => { method: 'GET', }); expect(response.status).toEqual(200); - expect(response.text).toContain( - (await config.pages.placeholders()).title - ); + expect(response.text).toContain((await config.pages.placeholders()).title); }); }); @@ -403,28 +385,20 @@ describe('Pages Router', () => { it('returns default file if localization is disabled', async () => { delete req.config.pages.enableLocalization; - await expectAsync( - router.goToPage(req, pages.passwordResetLinkInvalid) - ).toBeResolved(); + await expectAsync(router.goToPage(req, pages.passwordResetLinkInvalid)).toBeResolved(); expect(pageResponse.calls.all()[0].args[0]).toBeDefined(); expect(pageResponse.calls.all()[0].args[0]).not.toMatch( - new RegExp( - `\/de(-AT)?\/${pages.passwordResetLinkInvalid.defaultFile}` - ) + new RegExp(`\/de(-AT)?\/${pages.passwordResetLinkInvalid.defaultFile}`) ); }); it('returns default file if no locale is specified', async () => { delete req.query.locale; - await expectAsync( - router.goToPage(req, pages.passwordResetLinkInvalid) - ).toBeResolved(); + await expectAsync(router.goToPage(req, pages.passwordResetLinkInvalid)).toBeResolved(); expect(pageResponse.calls.all()[0].args[0]).toBeDefined(); expect(pageResponse.calls.all()[0].args[0]).not.toMatch( - new RegExp( - `\/de(-AT)?\/${pages.passwordResetLinkInvalid.defaultFile}` - ) + new RegExp(`\/de(-AT)?\/${pages.passwordResetLinkInvalid.defaultFile}`) ); }); @@ -433,9 +407,7 @@ describe('Pages Router', () => { passwordResetLinkInvalid: 'http://invalid-link.example.com', }; - await expectAsync( - router.goToPage(req, pages.passwordResetLinkInvalid) - ).toBeResolved(); + await expectAsync(router.goToPage(req, pages.passwordResetLinkInvalid)).toBeResolved(); expect(pageResponse).not.toHaveBeenCalled(); expect(redirectResponse.calls.all()[0].args[0]).toBe( req.config.pages.customUrls.passwordResetLinkInvalid @@ -443,14 +415,10 @@ describe('Pages Router', () => { }); it('returns file for locale match', async () => { - await expectAsync( - router.goToPage(req, pages.passwordResetLinkInvalid) - ).toBeResolved(); + await expectAsync(router.goToPage(req, pages.passwordResetLinkInvalid)).toBeResolved(); expect(pageResponse.calls.all()[0].args[0]).toBeDefined(); expect(pageResponse.calls.all()[0].args[0]).toMatch( - new RegExp( - `\/${req.query.locale}\/${pages.passwordResetLinkInvalid.defaultFile}` - ) + new RegExp(`\/${req.query.locale}\/${pages.passwordResetLinkInvalid.defaultFile}`) ); }); @@ -462,9 +430,7 @@ describe('Pages Router', () => { ); }); - await expectAsync( - router.goToPage(req, pages.passwordResetLinkInvalid) - ).toBeResolved(); + await expectAsync(router.goToPage(req, pages.passwordResetLinkInvalid)).toBeResolved(); expect(pageResponse.calls.all()[0].args[0]).toBeDefined(); expect(pageResponse.calls.all()[0].args[0]).toMatch( new RegExp(`\/de\/${pages.passwordResetLinkInvalid.defaultFile}`) @@ -474,14 +440,10 @@ describe('Pages Router', () => { it('returns default file for neither locale nor language match', async () => { req.query.locale = 'yo-LO'; - await expectAsync( - router.goToPage(req, pages.passwordResetLinkInvalid) - ).toBeResolved(); + await expectAsync(router.goToPage(req, pages.passwordResetLinkInvalid)).toBeResolved(); expect(pageResponse.calls.all()[0].args[0]).toBeDefined(); expect(pageResponse.calls.all()[0].args[0]).not.toMatch( - new RegExp( - `\/yo(-LO)?\/${pages.passwordResetLinkInvalid.defaultFile}` - ) + new RegExp(`\/yo(-LO)?\/${pages.passwordResetLinkInvalid.defaultFile}`) ); }); }); @@ -538,8 +500,7 @@ describe('Pages Router', () => { expect(response.status).toBe(200); // Ensure page response contains translation of fallback locale - const translation = - jsonResource[config.pages.localizationFallbackLocale].translation; + const translation = jsonResource[config.pages.localizationFallbackLocale].translation; for (const value of Object.values(translation)) { const valueWithoutPlaceholder = fillPlaceholders(value, ''); expect(response.text).toContain(valueWithoutPlaceholder); @@ -610,9 +571,7 @@ describe('Pages Router', () => { it('localizes feature page with JSON resource and fills placeholders in JSON values', async () => { // Fake any page to load the JSON page file - spyOnProperty(Page.prototype, 'defaultFile').and.returnValue( - jsonPageFile - ); + spyOnProperty(Page.prototype, 'defaultFile').and.returnValue(jsonPageFile); const response = await request({ url: `http://localhost:8378/1/apps/test/request_password_reset?token=exampleToken&locale=${exampleLocale}`, @@ -635,18 +594,14 @@ describe('Pages Router', () => { describe('response type', () => { it('returns a file for GET request', async () => { - await expectAsync( - router.goToPage(req, pages.passwordResetLinkInvalid) - ).toBeResolved(); + await expectAsync(router.goToPage(req, pages.passwordResetLinkInvalid)).toBeResolved(); expect(pageResponse).toHaveBeenCalled(); expect(redirectResponse).not.toHaveBeenCalled(); }); it('returns a redirect for POST request', async () => { req.method = 'POST'; - await expectAsync( - router.goToPage(req, pages.passwordResetLinkInvalid) - ).toBeResolved(); + await expectAsync(router.goToPage(req, pages.passwordResetLinkInvalid)).toBeResolved(); expect(pageResponse).not.toHaveBeenCalled(); expect(redirectResponse).toHaveBeenCalled(); }); @@ -658,9 +613,7 @@ describe('Pages Router', () => { for (const method of ['GET', 'POST']) { req.method = method; - await expectAsync( - router.goToPage(req, pages.passwordResetLinkInvalid) - ).toBeResolved(); + await expectAsync(router.goToPage(req, pages.passwordResetLinkInvalid)).toBeResolved(); expect(pageResponse).not.toHaveBeenCalled(); expect(redirectResponse).toHaveBeenCalled(); } @@ -718,8 +671,7 @@ describe('Pages Router', () => { const appId = linkResponse.headers['x-parse-page-param-appid']; const token = linkResponse.headers['x-parse-page-param-token']; const locale = linkResponse.headers['x-parse-page-param-locale']; - const publicServerUrl = - linkResponse.headers['x-parse-page-param-publicserverurl']; + const publicServerUrl = linkResponse.headers['x-parse-page-param-publicserverurl']; const passwordResetPagePath = pageResponse.calls.all()[0].args[0]; expect(appId).toBeDefined(); expect(token).toBeDefined(); @@ -774,9 +726,7 @@ describe('Pages Router', () => { const pagePath = pageResponse.calls.all()[0].args[0]; expect(pagePath).toMatch( - new RegExp( - `\/${exampleLocale}\/${pages.passwordResetLinkInvalid.defaultFile}` - ) + new RegExp(`\/${exampleLocale}\/${pages.passwordResetLinkInvalid.defaultFile}`) ); }); @@ -807,9 +757,7 @@ describe('Pages Router', () => { const pagePath = pageResponse.calls.all()[0].args[0]; expect(pagePath).toMatch( - new RegExp( - `\/${exampleLocale}\/${pages.emailVerificationSuccess.defaultFile}` - ) + new RegExp(`\/${exampleLocale}\/${pages.emailVerificationSuccess.defaultFile}`) ); } ); @@ -842,17 +790,13 @@ describe('Pages Router', () => { const appId = linkResponse.headers['x-parse-page-param-appid']; const locale = linkResponse.headers['x-parse-page-param-locale']; - const publicServerUrl = - linkResponse.headers['x-parse-page-param-publicserverurl']; - const invalidVerificationPagePath = - pageResponse.calls.all()[0].args[0]; + const publicServerUrl = linkResponse.headers['x-parse-page-param-publicserverurl']; + const invalidVerificationPagePath = pageResponse.calls.all()[0].args[0]; expect(appId).toBeDefined(); expect(locale).toBe(exampleLocale); expect(publicServerUrl).toBeDefined(); expect(invalidVerificationPagePath).toMatch( - new RegExp( - `\/${exampleLocale}\/${pages.emailVerificationLinkInvalid.defaultFile}` - ) + new RegExp(`\/${exampleLocale}\/${pages.emailVerificationLinkInvalid.defaultFile}`) ); const formUrl = `${publicServerUrl}/apps/${appId}/resend_verification_email`; @@ -901,25 +845,18 @@ describe('Pages Router', () => { const appId = linkResponse.headers['x-parse-page-param-appid']; const locale = linkResponse.headers['x-parse-page-param-locale']; - const publicServerUrl = - linkResponse.headers['x-parse-page-param-publicserverurl']; + const publicServerUrl = linkResponse.headers['x-parse-page-param-publicserverurl']; await jasmine.timeout(); - const invalidVerificationPagePath = - pageResponse.calls.all()[0].args[0]; + const invalidVerificationPagePath = pageResponse.calls.all()[0].args[0]; expect(appId).toBeDefined(); expect(locale).toBe(exampleLocale); expect(publicServerUrl).toBeDefined(); expect(invalidVerificationPagePath).toMatch( - new RegExp( - `\/${exampleLocale}\/${pages.emailVerificationLinkInvalid.defaultFile}` - ) + new RegExp(`\/${exampleLocale}\/${pages.emailVerificationLinkInvalid.defaultFile}`) ); - spyOn( - UserController.prototype, - 'resendVerificationEmail' - ).and.callFake(() => + spyOn(UserController.prototype, 'resendVerificationEmail').and.callFake(() => Promise.reject('failed to resend verification email') ); @@ -963,8 +900,7 @@ describe('Pages Router', () => { describe('failing with missing parameters', () => { it('verifyEmail: throws on missing server configuration', async () => { delete req.config; - const verifyEmail = req => - (() => new PagesRouter().verifyEmail(req)).bind(null); + const verifyEmail = req => (() => new PagesRouter().verifyEmail(req)).bind(null); expect(verifyEmail(req)).toThrow(); }); @@ -984,8 +920,7 @@ describe('Pages Router', () => { it('resetPassword: throws on missing server configuration', async () => { delete req.config; - const resetPassword = req => - (() => new PagesRouter().resetPassword(req)).bind(null); + const resetPassword = req => (() => new PagesRouter().resetPassword(req)).bind(null); expect(resetPassword(req)).toThrow(); }); @@ -996,9 +931,7 @@ describe('Pages Router', () => { const verifyEmail = req => new PagesRouter().verifyEmail(req); await verifyEmail(req); - expect(goToPage.calls.all()[0].args[1]).toBe( - pages.emailVerificationLinkInvalid - ); + expect(goToPage.calls.all()[0].args[1]).toBe(pages.emailVerificationLinkInvalid); }); it('resetPassword: responds with page choose password with error message on failed password update', async () => { @@ -1029,8 +962,7 @@ describe('Pages Router', () => { req.config.userController = { updatePassword: () => Promise.reject(error), }; - const resetPassword = req => - new PagesRouter().resetPassword(req).catch(e => e); + const resetPassword = req => new PagesRouter().resetPassword(req).catch(e => e); const response = await resetPassword(req); expect(response.code).toBe(Parse.Error.OTHER_CAUSE); @@ -1067,10 +999,7 @@ describe('Pages Router', () => { }, ]; await reconfigureServer(config); - const handlerSpy = spyOn( - config.pages.customRoutes[0], - 'handler' - ).and.callThrough(); + const handlerSpy = spyOn(config.pages.customRoutes[0], 'handler').and.callThrough(); const url = `${config.publicServerURL}/apps/${config.appId}/custom_page`; const response = await request({ @@ -1094,10 +1023,7 @@ describe('Pages Router', () => { }, }, ]; - const handlerSpy = spyOn( - config.pages.customRoutes[0], - 'handler' - ).and.callThrough(); + const handlerSpy = spyOn(config.pages.customRoutes[0], 'handler').and.callThrough(); await reconfigureServer(config); const url = `${config.publicServerURL}/apps/${config.appId}/custom_page`; @@ -1132,14 +1058,8 @@ describe('Pages Router', () => { }, }, ]; - const getHandlerSpy = spyOn( - config.pages.customRoutes[0], - 'handler' - ).and.callThrough(); - const postHandlerSpy = spyOn( - config.pages.customRoutes[1], - 'handler' - ).and.callThrough(); + const getHandlerSpy = spyOn(config.pages.customRoutes[0], 'handler').and.callThrough(); + const postHandlerSpy = spyOn(config.pages.customRoutes[1], 'handler').and.callThrough(); await reconfigureServer(config); const url = `${config.publicServerURL}/apps/${config.appId}/custom_page`; @@ -1178,10 +1098,7 @@ describe('Pages Router', () => { }, ]; await reconfigureServer(config); - const handlerSpy = spyOn( - config.pages.customRoutes[0], - 'handler' - ).and.callThrough(); + const handlerSpy = spyOn(config.pages.customRoutes[0], 'handler').and.callThrough(); const url = `${config.publicServerURL}/apps/${config.appId}/custom_page`; const response = await request({ @@ -1202,10 +1119,7 @@ describe('Pages Router', () => { }, ]; await reconfigureServer(config); - const handlerSpy = spyOn( - config.pages.customRoutes[0], - 'handler' - ).and.callThrough(); + const handlerSpy = spyOn(config.pages.customRoutes[0], 'handler').and.callThrough(); const url = `${config.publicServerURL}/apps/${config.appId}/custom_page`; const response = await request({ @@ -1242,15 +1156,12 @@ describe('Pages Router', () => { const appId = linkResponse.headers['x-parse-page-param-appid']; const token = linkResponse.headers['x-parse-page-param-token']; - const publicServerUrl = - linkResponse.headers['x-parse-page-param-publicserverurl']; + const publicServerUrl = linkResponse.headers['x-parse-page-param-publicserverurl']; const passwordResetPagePath = pageResponse.calls.all()[0].args[0]; expect(appId).toBeDefined(); expect(token).toBeDefined(); expect(publicServerUrl).toBeDefined(); - expect(passwordResetPagePath).toMatch( - new RegExp(`\/${pages.passwordReset.defaultFile}`) - ); + expect(passwordResetPagePath).toMatch(new RegExp(`\/${pages.passwordReset.defaultFile}`)); pageResponse.calls.reset(); const formUrl = `${publicServerUrl}/${config.pages.pagesEndpoint}/${appId}/request_password_reset`; @@ -1293,9 +1204,7 @@ describe('Pages Router', () => { }); expect(linkResponse.status).toBe(200); const pagePath = pageResponse.calls.all()[0].args[0]; - expect(pagePath).toMatch( - new RegExp(`\/${pages.emailVerificationSuccess.defaultFile}`) - ); + expect(pagePath).toMatch(new RegExp(`\/${pages.emailVerificationSuccess.defaultFile}`)); } ); }); diff --git a/spec/Parse.Push.spec.js b/spec/Parse.Push.spec.js index 9edbe43c62..61dbdba103 100644 --- a/spec/Parse.Push.spec.js +++ b/spec/Parse.Push.spec.js @@ -94,10 +94,7 @@ const setup = function () { const installations = []; while (installations.length != 10) { const installation = new Parse.Object('_Installation'); - installation.set( - 'installationId', - 'installation_' + installations.length - ); + installation.set('installationId', 'installation_' + installations.length); installation.set('deviceToken', 'device_token_' + installations.length); installation.set('badge', installations.length); installation.set('originalBadge', installations.length); @@ -114,23 +111,20 @@ const setup = function () { }; describe('Parse.Push', () => { - it_id('d1e591c4-2b21-466b-9ee2-5be467b6b771')(it)( - 'should properly send push', - async () => { - const { sendToInstallationSpy } = await setup(); - const pushStatusId = await Parse.Push.send({ - where: { - deviceType: 'ios', - }, - data: { - badge: 'Increment', - alert: 'Hello world!', - }, - }); - await pushCompleted(pushStatusId); - expect(sendToInstallationSpy.calls.count()).toEqual(10); - } - ); + it_id('d1e591c4-2b21-466b-9ee2-5be467b6b771')(it)('should properly send push', async () => { + const { sendToInstallationSpy } = await setup(); + const pushStatusId = await Parse.Push.send({ + where: { + deviceType: 'ios', + }, + data: { + badge: 'Increment', + alert: 'Hello world!', + }, + }); + await pushCompleted(pushStatusId); + expect(sendToInstallationSpy.calls.count()).toEqual(10); + }); it_id('2a58e3c7-b6f3-4261-a384-6c893b2ac3f3')(it)( 'should properly send push with lowercaseIncrement', @@ -203,9 +197,7 @@ describe('Parse.Push', () => { const body = response.data; expect(body.results.length).toEqual(1); expect(body.results[0].query).toEqual('{"deviceType":"ios"}'); - expect(body.results[0].payload).toEqual( - '{"badge":"increment","alert":"Hello world!"}' - ); + expect(body.results[0].payload).toEqual('{"badge":"increment","alert":"Hello world!"}'); } ); @@ -258,10 +250,7 @@ describe('Parse.Push', () => { // add 1 iOS installation which we will omit & add later on const iOSInstallation = new Parse.Object('_Installation'); - iOSInstallation.set( - 'installationId', - 'installation_' + installations.length - ); + iOSInstallation.set('installationId', 'installation_' + installations.length); iOSInstallation.set('deviceToken', 'device_token_' + installations.length); iOSInstallation.set('deviceType', 'ios'); installations.push(iOSInstallation); @@ -331,14 +320,8 @@ describe('Parse.Push', () => { const iOSInstallations = []; while (iOSInstallations.length !== devices / 100) { const iOSInstallation = new Parse.Object('_Installation'); - iOSInstallation.set( - 'installationId', - 'installation_' + installations.length - ); - iOSInstallation.set( - 'deviceToken', - 'device_token_' + installations.length - ); + iOSInstallation.set('installationId', 'installation_' + installations.length); + iOSInstallation.set('deviceToken', 'device_token_' + installations.length); iOSInstallation.set('deviceType', 'ios'); installations.push(iOSInstallation); iOSInstallations.push(iOSInstallation); diff --git a/spec/ParseAPI.spec.js b/spec/ParseAPI.spec.js index b7d3a8f350..148ef3cb79 100644 --- a/spec/ParseAPI.spec.js +++ b/spec/ParseAPI.spec.js @@ -29,12 +29,7 @@ describe('miscellaneous', () => { obj.set('foo', 'bar'); await obj.save(); const config = Config.get(defaultConfiguration.appId); - const results = await config.database.adapter.find( - 'TestObject', - { fields: {} }, - {}, - {} - ); + const results = await config.database.adapter.find('TestObject', { fields: {} }, {}, {}); expect(results.length).toEqual(1); expect(results[0]['foo']).toEqual('bar'); }); @@ -270,11 +265,7 @@ describe('miscellaneous', () => { const config = Config.get('test'); config.database.adapter .addFieldIfNotExists('_User', 'randomField', { type: 'String' }) - .then(() => - config.database.adapter.ensureUniqueness('_User', userSchema, [ - 'randomField', - ]) - ) + .then(() => config.database.adapter.ensureUniqueness('_User', userSchema, ['randomField'])) .then(() => { const user = new Parse.User(); user.setPassword('asdf'); @@ -341,28 +332,25 @@ describe('miscellaneous', () => { } ); - it_id('bef99522-bcfd-4f79-ba9e-3c3845550401')(it)( - 'save various data types', - function (done) { - const obj = new TestObject(); - obj.set('date', new Date()); - obj.set('array', [1, 2, 3]); - obj.set('object', { one: 1, two: 2 }); - obj - .save() - .then(() => { - const obj2 = new TestObject({ objectId: obj.id }); - return obj2.fetch(); - }) - .then(obj2 => { - expect(obj2.get('date') instanceof Date).toBe(true); - expect(obj2.get('array') instanceof Array).toBe(true); - expect(obj2.get('object') instanceof Array).toBe(false); - expect(obj2.get('object') instanceof Object).toBe(true); - done(); - }); - } - ); + it_id('bef99522-bcfd-4f79-ba9e-3c3845550401')(it)('save various data types', function (done) { + const obj = new TestObject(); + obj.set('date', new Date()); + obj.set('array', [1, 2, 3]); + obj.set('object', { one: 1, two: 2 }); + obj + .save() + .then(() => { + const obj2 = new TestObject({ objectId: obj.id }); + return obj2.fetch(); + }) + .then(obj2 => { + expect(obj2.get('date') instanceof Date).toBe(true); + expect(obj2.get('array') instanceof Array).toBe(true); + expect(obj2.get('object') instanceof Array).toBe(false); + expect(obj2.get('object') instanceof Object).toBe(true); + done(); + }); + }); it('query with limit', function (done) { const baz = new TestObject({ foo: 'baz' }); @@ -673,30 +661,27 @@ describe('miscellaneous', () => { }); }); - it_only_db('mongo')( - 'pointer reassign on nested fields is working properly (#7391)', - async () => { - const obj = new Parse.Object('GameScore'); // This object will include nested pointers - const ptr1 = new Parse.Object('GameScore'); - await ptr1.save(); // Obtain a unique id - const ptr2 = new Parse.Object('GameScore'); - await ptr2.save(); // Obtain a unique id - obj.set('data', { ptr: ptr1 }); - await obj.save(); - - obj.set('data.ptr', ptr2); - await obj.save(); - - const obj2 = await new Parse.Query('GameScore').get(obj.id); - expect(obj2.get('data').ptr.id).toBe(ptr2.id); - - const query = new Parse.Query('GameScore'); - query.equalTo('data.ptr', ptr2); - const res = await query.find(); - expect(res.length).toBe(1); - expect(res[0].get('data').ptr.id).toBe(ptr2.id); - } - ); + it_only_db('mongo')('pointer reassign on nested fields is working properly (#7391)', async () => { + const obj = new Parse.Object('GameScore'); // This object will include nested pointers + const ptr1 = new Parse.Object('GameScore'); + await ptr1.save(); // Obtain a unique id + const ptr2 = new Parse.Object('GameScore'); + await ptr2.save(); // Obtain a unique id + obj.set('data', { ptr: ptr1 }); + await obj.save(); + + obj.set('data.ptr', ptr2); + await obj.save(); + + const obj2 = await new Parse.Query('GameScore').get(obj.id); + expect(obj2.get('data').ptr.id).toBe(ptr2.id); + + const query = new Parse.Query('GameScore'); + query.equalTo('data.ptr', ptr2); + const res = await query.find(); + expect(res.length).toBe(1); + expect(res[0].get('data').ptr.id).toBe(ptr2.id); + }); it('test afterSave get full object on create and update', function (done) { let triggerTime = 0; @@ -1020,10 +1005,7 @@ describe('miscellaneous', () => { ...obj.toJSON(), }), }); - expect(Object.keys(saveResponse.data).sort()).toEqual([ - 'createdAt', - 'objectId', - ]); + expect(Object.keys(saveResponse.data).sort()).toEqual(['createdAt', 'objectId']); obj.id = saveResponse.data.objectId; const response = await request({ method: 'PUT', @@ -1043,13 +1025,7 @@ describe('miscellaneous', () => { }), }); const body = response.data; - expect(Object.keys(body).sort()).toEqual([ - 'c', - 'd', - 'e', - 'f', - 'updatedAt', - ]); + expect(Object.keys(body).sort()).toEqual(['c', 'd', 'e', 'f', 'updatedAt']); expect(body.a).toBeUndefined(); expect(body.c).toEqual(3); // 2+1 expect(body.d.length).toBe(2); @@ -1110,10 +1086,7 @@ describe('miscellaneous', () => { ...obj.toJSON(), }), }); - expect(Object.keys(saveResponse.data).sort()).toEqual([ - 'createdAt', - 'objectId', - ]); + expect(Object.keys(saveResponse.data).sort()).toEqual(['createdAt', 'objectId']); obj.id = saveResponse.data.objectId; const response = await request({ method: 'PUT', @@ -1133,13 +1106,7 @@ describe('miscellaneous', () => { }), }); const body = response.data; - expect(Object.keys(body).sort()).toEqual([ - 'c', - 'd', - 'e', - 'f', - 'updatedAt', - ]); + expect(Object.keys(body).sort()).toEqual(['c', 'd', 'e', 'f', 'updatedAt']); expect(body.a).toBeUndefined(); expect(body.c).toEqual(3); // 2+1 expect(body.d.length).toBe(2); @@ -1267,8 +1234,7 @@ describe('miscellaneous', () => { request({ method: 'DELETE', headers: headers, - url: - 'http://localhost:8378/1/classes/GameScore/' + response.data.objectId, + url: 'http://localhost:8378/1/classes/GameScore/' + response.data.objectId, }).then(() => { expect(triggerTime).toEqual(2); done(); @@ -1450,9 +1416,7 @@ describe('miscellaneous', () => { }, e => { expect(e.code).toEqual(Parse.Error.SCRIPT_FAILED); - expect(e.message).toEqual( - 'Invalid function: "somethingThatDoesDefinitelyNotExist"' - ); + expect(e.message).toEqual('Invalid function: "somethingThatDoesDefinitelyNotExist"'); done(); } ); @@ -1740,39 +1704,36 @@ describe('miscellaneous', () => { }); }); - it_id('8f99ee20-3da7-45ec-b867-ea0eb87524a9')(it)( - 'purge all objects in class', - done => { - const object = new Parse.Object('TestObject'); - object.set('foo', 'bar'); - const object2 = new Parse.Object('TestObject'); - object2.set('alice', 'wonderland'); - Parse.Object.saveAll([object, object2]) - .then(() => { + it_id('8f99ee20-3da7-45ec-b867-ea0eb87524a9')(it)('purge all objects in class', done => { + const object = new Parse.Object('TestObject'); + object.set('foo', 'bar'); + const object2 = new Parse.Object('TestObject'); + object2.set('alice', 'wonderland'); + Parse.Object.saveAll([object, object2]) + .then(() => { + const query = new Parse.Query(TestObject); + return query.count(); + }) + .then(count => { + expect(count).toBe(2); + const headers = { + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-Master-Key': 'test', + }; + request({ + method: 'DELETE', + headers: headers, + url: 'http://localhost:8378/1/purge/TestObject', + }).then(() => { const query = new Parse.Query(TestObject); - return query.count(); - }) - .then(count => { - expect(count).toBe(2); - const headers = { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key': 'test', - }; - request({ - method: 'DELETE', - headers: headers, - url: 'http://localhost:8378/1/purge/TestObject', - }).then(() => { - const query = new Parse.Query(TestObject); - return query.count().then(count => { - expect(count).toBe(0); - done(); - }); + return query.count().then(count => { + expect(count).toBe(0); + done(); }); }); - } - ); + }); + }); it('fail on purge all objects in class without master key', done => { const headers = { @@ -1789,9 +1750,7 @@ describe('miscellaneous', () => { fail('Should not succeed'); }) .catch(response => { - expect(response.data.error).toEqual( - 'unauthorized: master key is required' - ); + expect(response.data.error).toEqual('unauthorized: master key is required'); done(); }); }); @@ -1934,9 +1893,7 @@ describe_only_db('mongo')('legacy _acl', () => { .then(() => { const config = Config.get('test'); const adapter = config.database.adapter; - return adapter - ._adaptiveCollection('Report') - .then(collection => collection.find({})); + return adapter._adaptiveCollection('Report').then(collection => collection.find({})); }) .then(results => { expect(results.length).toBe(1); diff --git a/spec/ParseCloudCodePublisher.spec.js b/spec/ParseCloudCodePublisher.spec.js index cc4e123320..ef00960687 100644 --- a/spec/ParseCloudCodePublisher.spec.js +++ b/spec/ParseCloudCodePublisher.spec.js @@ -15,11 +15,7 @@ describe('ParseCloudCodePublisher', function () { on: jasmine.createSpy('on'), }), }; - jasmine.mockLibrary( - '../lib/LiveQuery/ParsePubSub', - 'ParsePubSub', - mockParsePubSub - ); + jasmine.mockLibrary('../lib/LiveQuery/ParsePubSub', 'ParsePubSub', mockParsePubSub); done(); }); diff --git a/spec/ParseConfigKey.spec.js b/spec/ParseConfigKey.spec.js index 1b6ee32dff..b39df065e0 100644 --- a/spec/ParseConfigKey.spec.js +++ b/spec/ParseConfigKey.spec.js @@ -1,8 +1,7 @@ const Config = require('../lib/Config'); describe('Config Keys', () => { - const invalidKeyErrorMessage = - 'Invalid key\\(s\\) found in Parse Server configuration'; + const invalidKeyErrorMessage = 'Invalid key\\(s\\) found in Parse Server configuration'; let loggerErrorSpy; beforeEach(async () => { @@ -17,9 +16,7 @@ describe('Config Keys', () => { invalidKey: 1, }) ).toBeResolved(); - const error = loggerErrorSpy.calls - .all() - .reduce((s, call) => (s += call.args[0]), ''); + const error = loggerErrorSpy.calls.all().reduce((s, call) => (s += call.args[0]), ''); expect(error).toMatch(invalidKeyErrorMessage); }); @@ -34,9 +31,7 @@ describe('Config Keys', () => { }, }) ).toBeResolved(); - const error = loggerErrorSpy.calls - .all() - .reduce((s, call) => (s += call.args[0]), ''); + const error = loggerErrorSpy.calls.all().reduce((s, call) => (s += call.args[0]), ''); expect(error).toMatch(invalidKeyErrorMessage); expect(error).toMatch(`invalidKey`); expect(error).toMatch(`EmailVerificationSendFail`); @@ -51,9 +46,7 @@ describe('Config Keys', () => { }, }) ).toBeResolved(); - const error = loggerErrorSpy.calls - .all() - .reduce((s, call) => (s += call.args[0]), ''); + const error = loggerErrorSpy.calls.all().reduce((s, call) => (s += call.args[0]), ''); expect(error).toMatch(invalidKeyErrorMessage); expect(error).toMatch(`MasterKey`); }); @@ -61,60 +54,48 @@ describe('Config Keys', () => { it('recognizes invalid keys in rateLimit', async () => { await expectAsync( reconfigureServer({ - rateLimit: [ - { invalidKey: 1 }, - { RequestPath: 1 }, - { RequestTimeWindow: 1 }, - ], + rateLimit: [{ invalidKey: 1 }, { RequestPath: 1 }, { RequestTimeWindow: 1 }], }) ).toBeRejected(); - const error = loggerErrorSpy.calls - .all() - .reduce((s, call) => (s += call.args[0]), ''); + const error = loggerErrorSpy.calls.all().reduce((s, call) => (s += call.args[0]), ''); expect(error).toMatch(invalidKeyErrorMessage); expect(error).toMatch('rateLimit\\[0\\]\\.invalidKey'); expect(error).toMatch('rateLimit\\[1\\]\\.RequestPath'); expect(error).toMatch('rateLimit\\[2\\]\\.RequestTimeWindow'); }); - it_only_db('mongo')( - 'recognizes valid keys in default configuration', - async () => { - await expectAsync( - reconfigureServer({ - ...defaultConfiguration, - }) - ).toBeResolved(); - expect( - loggerErrorSpy.calls.all().reduce((s, call) => (s += call.args[0]), '') - ).not.toMatch(invalidKeyErrorMessage); - } - ); + it_only_db('mongo')('recognizes valid keys in default configuration', async () => { + await expectAsync( + reconfigureServer({ + ...defaultConfiguration, + }) + ).toBeResolved(); + expect(loggerErrorSpy.calls.all().reduce((s, call) => (s += call.args[0]), '')).not.toMatch( + invalidKeyErrorMessage + ); + }); - it_only_db('mongo')( - 'recognizes valid keys in databaseOptions (MongoDB)', - async () => { - await expectAsync( - reconfigureServer({ - databaseURI: 'mongodb://localhost:27017/parse', - filesAdapter: null, - databaseAdapter: null, - databaseOptions: { - retryWrites: true, - maxTimeMS: 1000, - maxStalenessSeconds: 10, - maxPoolSize: 10, - minPoolSize: 5, - connectTimeoutMS: 5000, - socketTimeoutMS: 5000, - autoSelectFamily: true, - autoSelectFamilyAttemptTimeout: 3000, - }, - }) - ).toBeResolved(); - expect( - loggerErrorSpy.calls.all().reduce((s, call) => (s += call.args[0]), '') - ).not.toMatch(invalidKeyErrorMessage); - } - ); + it_only_db('mongo')('recognizes valid keys in databaseOptions (MongoDB)', async () => { + await expectAsync( + reconfigureServer({ + databaseURI: 'mongodb://localhost:27017/parse', + filesAdapter: null, + databaseAdapter: null, + databaseOptions: { + retryWrites: true, + maxTimeMS: 1000, + maxStalenessSeconds: 10, + maxPoolSize: 10, + minPoolSize: 5, + connectTimeoutMS: 5000, + socketTimeoutMS: 5000, + autoSelectFamily: true, + autoSelectFamilyAttemptTimeout: 3000, + }, + }) + ).toBeResolved(); + expect(loggerErrorSpy.calls.all().reduce((s, call) => (s += call.args[0]), '')).not.toMatch( + invalidKeyErrorMessage + ); + }); }); diff --git a/spec/ParseFile.spec.js b/spec/ParseFile.spec.js index 6cc5ac3d89..b2ae6078d9 100644 --- a/spec/ParseFile.spec.js +++ b/spec/ParseFile.spec.js @@ -28,9 +28,7 @@ describe('Parse.File testing', () => { }).then(response => { const b = response.data; expect(b.name).toMatch(/_file.txt$/); - expect(b.url).toMatch( - /^http:\/\/localhost:8378\/1\/files\/test\/.*file.txt$/ - ); + expect(b.url).toMatch(/^http:\/\/localhost:8378\/1\/files\/test\/.*file.txt$/); request({ url: b.url }).then(response => { const body = response.text; expect(body).toEqual('argle bargle'); @@ -58,9 +56,7 @@ describe('Parse.File testing', () => { }); const b = response.data; expect(b.name).toMatch(/_file.html/); - expect(b.url).toMatch( - /^http:\/\/localhost:8378\/1\/files\/test\/.*file.html$/ - ); + expect(b.url).toMatch(/^http:\/\/localhost:8378\/1\/files\/test\/.*file.html$/); response = await request({ url: b.url }); const body = response.text; try { @@ -84,9 +80,7 @@ describe('Parse.File testing', () => { }).then(response => { const b = response.data; expect(b.name).toMatch(/_file.txt$/); - expect(b.url).toMatch( - /^http:\/\/localhost:8378\/1\/files\/test\/.*file.txt$/ - ); + expect(b.url).toMatch(/^http:\/\/localhost:8378\/1\/files\/test\/.*file.txt$/); request({ url: b.url }).then(response => { expect(response.text).toEqual('argle bargle'); done(); @@ -108,9 +102,7 @@ describe('Parse.File testing', () => { }).then(response => { const b = response.data; expect(b.name).toMatch(/_testfile.txt$/); - expect(b.url).toMatch( - /^http:\/\/localhost:8378\/1\/files\/test\/.*testfile.txt$/ - ); + expect(b.url).toMatch(/^http:\/\/localhost:8378\/1\/files\/test\/.*testfile.txt$/); request({ url: b.url }).then(response => { const body = response.text; expect(body).toEqual('check one two'); @@ -152,9 +144,7 @@ describe('Parse.File testing', () => { body: 'the file body', }).then(response => { const b = response.data; - expect(b.url).toMatch( - /^http:\/\/localhost:8378\/1\/files\/test\/.*thefile.jpg$/ - ); + expect(b.url).toMatch(/^http:\/\/localhost:8378\/1\/files\/test\/.*thefile.jpg$/); // missing X-Parse-Master-Key header request({ method: 'DELETE', @@ -200,9 +190,7 @@ describe('Parse.File testing', () => { }).then(response => { const b = response.data; expect(b.name).toMatch(/_file.jpg$/); - expect(b.url).toMatch( - /^http:\/\/localhost:8378\/1\/files\/.*file.jpg$/ - ); + expect(b.url).toMatch(/^http:\/\/localhost:8378\/1\/files\/.*file.jpg$/); request({ url: b.url }).then(response => { const body = response.text; expect(body).toEqual('argle bargle'); @@ -540,9 +528,7 @@ describe('Parse.File testing', () => { }) .then(result => { const fileAgain = result.get('oldfile'); - expect(fileAgain.url()).toEqual( - 'http://files.parsetfss.com/test/tfss-123.txt' - ); + expect(fileAgain.url()).toEqual('http://files.parsetfss.com/test/tfss-123.txt'); done(); }) .catch(e => { @@ -1122,26 +1108,16 @@ describe('Parse.File testing', () => { }); let file = new Parse.File('hello.txt', data, 'text/plain'); await expectAsync(file.save()).toBeRejectedWith( - new Parse.Error( - Parse.Error.FILE_SAVE_ERROR, - 'File upload by public is disabled.' - ) + new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'File upload by public is disabled.') ); file = new Parse.File('hello.txt', data, 'text/plain'); const anonUser = await Parse.AnonymousUtils.logIn(); - await expectAsync( - file.save({ sessionToken: anonUser.getSessionToken() }) - ).toBeRejectedWith( - new Parse.Error( - Parse.Error.FILE_SAVE_ERROR, - 'File upload by anonymous user is disabled.' - ) + await expectAsync(file.save({ sessionToken: anonUser.getSessionToken() })).toBeRejectedWith( + new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'File upload by anonymous user is disabled.') ); file = new Parse.File('hello.txt', data, 'text/plain'); const authUser = await Parse.User.signUp('user', 'password'); - await expectAsync( - file.save({ sessionToken: authUser.getSessionToken() }) - ).toBeResolved(); + await expectAsync(file.save({ sessionToken: authUser.getSessionToken() })).toBeResolved(); }); it('allows file upload with master key', async () => { @@ -1166,26 +1142,16 @@ describe('Parse.File testing', () => { }); let file = new Parse.File('hello.txt', data, 'text/plain'); await expectAsync(file.save()).toBeRejectedWith( - new Parse.Error( - Parse.Error.FILE_SAVE_ERROR, - 'File upload by public is disabled.' - ) + new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'File upload by public is disabled.') ); file = new Parse.File('hello.txt', data, 'text/plain'); const anonUser = await Parse.AnonymousUtils.logIn(); - await expectAsync( - file.save({ sessionToken: anonUser.getSessionToken() }) - ).toBeRejectedWith( - new Parse.Error( - Parse.Error.FILE_SAVE_ERROR, - 'File upload by anonymous user is disabled.' - ) + await expectAsync(file.save({ sessionToken: anonUser.getSessionToken() })).toBeRejectedWith( + new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'File upload by anonymous user is disabled.') ); file = new Parse.File('hello.txt', data, 'text/plain'); const authUser = await Parse.User.signUp('user', 'password'); - await expectAsync( - file.save({ sessionToken: authUser.getSessionToken() }) - ).toBeRejectedWith( + await expectAsync(file.save({ sessionToken: authUser.getSessionToken() })).toBeRejectedWith( new Parse.Error( Parse.Error.FILE_SAVE_ERROR, 'File upload by authenticated user is disabled.' @@ -1205,14 +1171,10 @@ describe('Parse.File testing', () => { await expectAsync(file.save()).toBeResolved(); file = new Parse.File('hello.txt', data, 'text/plain'); const anonUser = await Parse.AnonymousUtils.logIn(); - await expectAsync( - file.save({ sessionToken: anonUser.getSessionToken() }) - ).toBeResolved(); + await expectAsync(file.save({ sessionToken: anonUser.getSessionToken() })).toBeResolved(); file = new Parse.File('hello.txt', data, 'text/plain'); const authUser = await Parse.User.signUp('user', 'password'); - await expectAsync( - file.save({ sessionToken: authUser.getSessionToken() }) - ).toBeResolved(); + await expectAsync(file.save({ sessionToken: authUser.getSessionToken() })).toBeResolved(); }); it('allows file upload only for public', async () => { @@ -1227,19 +1189,12 @@ describe('Parse.File testing', () => { await expectAsync(file.save()).toBeResolved(); file = new Parse.File('hello.txt', data, 'text/plain'); const anonUser = await Parse.AnonymousUtils.logIn(); - await expectAsync( - file.save({ sessionToken: anonUser.getSessionToken() }) - ).toBeRejectedWith( - new Parse.Error( - Parse.Error.FILE_SAVE_ERROR, - 'File upload by anonymous user is disabled.' - ) + await expectAsync(file.save({ sessionToken: anonUser.getSessionToken() })).toBeRejectedWith( + new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'File upload by anonymous user is disabled.') ); file = new Parse.File('hello.txt', data, 'text/plain'); const authUser = await Parse.User.signUp('user', 'password'); - await expectAsync( - file.save({ sessionToken: authUser.getSessionToken() }) - ).toBeRejectedWith( + await expectAsync(file.save({ sessionToken: authUser.getSessionToken() })).toBeRejectedWith( new Parse.Error( Parse.Error.FILE_SAVE_ERROR, 'File upload by authenticated user is disabled.' @@ -1257,21 +1212,14 @@ describe('Parse.File testing', () => { }); let file = new Parse.File('hello.txt', data, 'text/plain'); await expectAsync(file.save()).toBeRejectedWith( - new Parse.Error( - Parse.Error.FILE_SAVE_ERROR, - 'File upload by public is disabled.' - ) + new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'File upload by public is disabled.') ); file = new Parse.File('hello.txt', data, 'text/plain'); const anonUser = await Parse.AnonymousUtils.logIn(); - await expectAsync( - file.save({ sessionToken: anonUser.getSessionToken() }) - ).toBeResolved(); + await expectAsync(file.save({ sessionToken: anonUser.getSessionToken() })).toBeResolved(); file = new Parse.File('hello.txt', data, 'text/plain'); const authUser = await Parse.User.signUp('user', 'password'); - await expectAsync( - file.save({ sessionToken: authUser.getSessionToken() }) - ).toBeRejectedWith( + await expectAsync(file.save({ sessionToken: authUser.getSessionToken() })).toBeRejectedWith( new Parse.Error( Parse.Error.FILE_SAVE_ERROR, 'File upload by authenticated user is disabled.' @@ -1289,26 +1237,16 @@ describe('Parse.File testing', () => { }); let file = new Parse.File('hello.txt', data, 'text/plain'); await expectAsync(file.save()).toBeRejectedWith( - new Parse.Error( - Parse.Error.FILE_SAVE_ERROR, - 'File upload by public is disabled.' - ) + new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'File upload by public is disabled.') ); file = new Parse.File('hello.txt', data, 'text/plain'); const anonUser = await Parse.AnonymousUtils.logIn(); - await expectAsync( - file.save({ sessionToken: anonUser.getSessionToken() }) - ).toBeRejectedWith( - new Parse.Error( - Parse.Error.FILE_SAVE_ERROR, - 'File upload by anonymous user is disabled.' - ) + await expectAsync(file.save({ sessionToken: anonUser.getSessionToken() })).toBeRejectedWith( + new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'File upload by anonymous user is disabled.') ); file = new Parse.File('hello.txt', data, 'text/plain'); const authUser = await Parse.User.signUp('user', 'password'); - await expectAsync( - file.save({ sessionToken: authUser.getSessionToken() }) - ).toBeResolved(); + await expectAsync(file.save({ sessionToken: authUser.getSessionToken() })).toBeResolved(); }); it('rejects invalid fileUpload configuration', async () => { @@ -1320,11 +1258,7 @@ describe('Parse.File testing', () => { { fileUpload: 'string' }, ]; const validConfigs = [{ fileUpload: {} }]; - const keys = [ - 'enableForPublic', - 'enableForAnonymousUser', - 'enableForAuthenticatedUser', - ]; + const keys = ['enableForPublic', 'enableForAnonymousUser', 'enableForAuthenticatedUser']; const invalidValues = [[], {}, 1, 'string', null]; const validValues = [undefined, true, false]; for (const config of invalidConfigs) { @@ -1337,14 +1271,12 @@ describe('Parse.File testing', () => { } for (const key of keys) { for (const value of invalidValues) { - await expectAsync( - reconfigureServer({ fileUpload: { [key]: value } }) - ).toBeRejectedWith(`fileUpload.${key} must be a boolean value.`); + await expectAsync(reconfigureServer({ fileUpload: { [key]: value } })).toBeRejectedWith( + `fileUpload.${key} must be a boolean value.` + ); } for (const value of validValues) { - await expectAsync( - reconfigureServer({ fileUpload: { [key]: value } }) - ).toBeResolved(); + await expectAsync(reconfigureServer({ fileUpload: { [key]: value } })).toBeResolved(); } } await expectAsync( @@ -1379,10 +1311,7 @@ describe('Parse.File testing', () => { throw new Error(e.data.error); }) ).toBeRejectedWith( - new Parse.Error( - Parse.Error.FILE_SAVE_ERROR, - `File upload of extension html is disabled.` - ) + new Parse.Error(Parse.Error.FILE_SAVE_ERROR, `File upload of extension html is disabled.`) ); }); @@ -1406,10 +1335,7 @@ describe('Parse.File testing', () => { throw new Error(e.data.error); }) ).toBeRejectedWith( - new Parse.Error( - Parse.Error.FILE_SAVE_ERROR, - `File upload of extension html is disabled.` - ) + new Parse.Error(Parse.Error.FILE_SAVE_ERROR, `File upload of extension html is disabled.`) ); }); @@ -1439,11 +1365,7 @@ describe('Parse.File testing', () => { 'X-Parse-REST-API-Key': 'rest', }; - const values = [ - 'file.png.html', - 'file.txt.png.html', - 'file.png.txt.html', - ]; + const values = ['file.png.html', 'file.txt.png.html', 'file.png.txt.html']; for (const value of values) { await expectAsync( @@ -1456,10 +1378,7 @@ describe('Parse.File testing', () => { throw new Error(e.data.error); }) ).toBeRejectedWith( - new Parse.Error( - Parse.Error.FILE_SAVE_ERROR, - `File upload of extension html is disabled.` - ) + new Parse.Error(Parse.Error.FILE_SAVE_ERROR, `File upload of extension html is disabled.`) ); } }); @@ -1497,10 +1416,7 @@ describe('Parse.File testing', () => { throw new Error(e.data.error); }) ).toBeRejectedWith( - new Parse.Error( - Parse.Error.INVALID_FILE_NAME, - `Filename contains invalid characters.` - ) + new Parse.Error(Parse.Error.INVALID_FILE_NAME, `Filename contains invalid characters.`) ); } }); @@ -1554,10 +1470,7 @@ describe('Parse.File testing', () => { throw new Error(e.data.error); }) ).toBeRejectedWith( - new Parse.Error( - Parse.Error.FILE_SAVE_ERROR, - `File upload of extension html is disabled.` - ) + new Parse.Error(Parse.Error.FILE_SAVE_ERROR, `File upload of extension html is disabled.`) ); await expectAsync( request({ @@ -1579,8 +1492,7 @@ describe('Parse.File testing', () => { _ApplicationId: 'test', _JavaScriptKey: 'test', _ContentType: 'audio/wav', - base64: - 'UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA', + base64: 'UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA', }), }) ).toBeResolved(); @@ -1607,10 +1519,7 @@ describe('Parse.File testing', () => { throw new Error(e.data.error); }) ).toBeRejectedWith( - new Parse.Error( - Parse.Error.FILE_SAVE_ERROR, - `File upload of extension html is disabled.` - ) + new Parse.Error(Parse.Error.FILE_SAVE_ERROR, `File upload of extension html is disabled.`) ); }); @@ -1633,9 +1542,7 @@ describe('Parse.File testing', () => { }); const b = response.data; expect(b.name).toMatch(/_file.html$/); - expect(b.url).toMatch( - /^http:\/\/localhost:8378\/1\/files\/test\/.*file.html$/ - ); + expect(b.url).toMatch(/^http:\/\/localhost:8378\/1\/files\/test\/.*file.html$/); }); }); }); diff --git a/spec/ParseGeoPoint.spec.js b/spec/ParseGeoPoint.spec.js index e20a6cc4c1..ac104c1a47 100644 --- a/spec/ParseGeoPoint.spec.js +++ b/spec/ParseGeoPoint.spec.js @@ -76,25 +76,22 @@ describe('Parse.GeoPoint testing', () => { }); // TODO: This should also have support in postgres, or higher level database agnostic support. - it_exclude_dbs(['postgres'])( - 'updating geo point exception two fields', - async done => { - const point = new Parse.GeoPoint(20, 20); - const obj = new TestObject(); - obj.set('locationOne', point); - await obj.save(); - obj.set('locationTwo', point); - obj.save().then( - () => { - fail('expected error'); - }, - err => { - equal(err.code, Parse.Error.INCORRECT_TYPE); - done(); - } - ); - } - ); + it_exclude_dbs(['postgres'])('updating geo point exception two fields', async done => { + const point = new Parse.GeoPoint(20, 20); + const obj = new TestObject(); + obj.set('locationOne', point); + await obj.save(); + obj.set('locationTwo', point); + obj.save().then( + () => { + fail('expected error'); + }, + err => { + equal(err.code, Parse.Error.INCORRECT_TYPE); + done(); + } + ); + }); it_id('bbd9e2f6-7f61-458f-98f2-4a563586cd8d')(it)('geo line', async done => { const line = []; @@ -146,27 +143,24 @@ describe('Parse.GeoPoint testing', () => { ); }); - it_id('e1e86b38-b8a4-4109-8330-a324fe628e0c')(it)( - 'geo max distance medium', - async () => { - const objects = []; - [0, 1, 2].map(function (i) { - const obj = new TestObject(); - const point = new Parse.GeoPoint(0.0, i * 45.0); - obj.set('location', point); - obj.set('index', i); - objects.push(obj); - }); - await Parse.Object.saveAll(objects); - const query = new Parse.Query(TestObject); - const point = new Parse.GeoPoint(1.0, -1.0); - query.withinRadians('location', point, 3.14 * 0.5); - const results = await query.find(); - equal(results.length, 2); - equal(results[0].get('index'), 0); - equal(results[1].get('index'), 1); - } - ); + it_id('e1e86b38-b8a4-4109-8330-a324fe628e0c')(it)('geo max distance medium', async () => { + const objects = []; + [0, 1, 2].map(function (i) { + const obj = new TestObject(); + const point = new Parse.GeoPoint(0.0, i * 45.0); + obj.set('location', point); + obj.set('index', i); + objects.push(obj); + }); + await Parse.Object.saveAll(objects); + const query = new Parse.Query(TestObject); + const point = new Parse.GeoPoint(1.0, -1.0); + query.withinRadians('location', point, 3.14 * 0.5); + const results = await query.find(); + equal(results.length, 2); + equal(results[0].get('index'), 0); + equal(results[1].get('index'), 1); + }); it('geo max distance small', async () => { const objects = []; @@ -288,42 +282,36 @@ describe('Parse.GeoPoint testing', () => { equal(results.length, 0); }); - it_id('9e35a89e-bc2c-4ec5-b25a-8d1890a55233')(it)( - 'returns nearest location', - async () => { - await makeSomeGeoPoints(); - const sfo = new Parse.GeoPoint(37.6189722, -122.3748889); - const query = new Parse.Query(TestObject); - query.near('location', sfo); - const results = await query.find(); - equal(results[0].get('name'), 'San Francisco'); - equal(results[1].get('name'), 'Sacramento'); - } - ); + it_id('9e35a89e-bc2c-4ec5-b25a-8d1890a55233')(it)('returns nearest location', async () => { + await makeSomeGeoPoints(); + const sfo = new Parse.GeoPoint(37.6189722, -122.3748889); + const query = new Parse.Query(TestObject); + query.near('location', sfo); + const results = await query.find(); + equal(results[0].get('name'), 'San Francisco'); + equal(results[1].get('name'), 'Sacramento'); + }); - it_id('6df434b0-142d-4302-bbc6-a6ec5a9d9c68')(it)( - 'works with geobox queries', - done => { - const inbound = new Parse.GeoPoint(1.5, 1.5); - const onbound = new Parse.GeoPoint(10, 10); - const outbound = new Parse.GeoPoint(20, 20); - const obj1 = new Parse.Object('TestObject', { location: inbound }); - const obj2 = new Parse.Object('TestObject', { location: onbound }); - const obj3 = new Parse.Object('TestObject', { location: outbound }); - Parse.Object.saveAll([obj1, obj2, obj3]) - .then(() => { - const sw = new Parse.GeoPoint(0, 0); - const ne = new Parse.GeoPoint(10, 10); - const query = new Parse.Query(TestObject); - query.withinGeoBox('location', sw, ne); - return query.find(); - }) - .then(results => { - equal(results.length, 2); - done(); - }); - } - ); + it_id('6df434b0-142d-4302-bbc6-a6ec5a9d9c68')(it)('works with geobox queries', done => { + const inbound = new Parse.GeoPoint(1.5, 1.5); + const onbound = new Parse.GeoPoint(10, 10); + const outbound = new Parse.GeoPoint(20, 20); + const obj1 = new Parse.Object('TestObject', { location: inbound }); + const obj2 = new Parse.Object('TestObject', { location: onbound }); + const obj3 = new Parse.Object('TestObject', { location: outbound }); + Parse.Object.saveAll([obj1, obj2, obj3]) + .then(() => { + const sw = new Parse.GeoPoint(0, 0); + const ne = new Parse.GeoPoint(10, 10); + const query = new Parse.Query(TestObject); + query.withinGeoBox('location', sw, ne); + return query.find(); + }) + .then(results => { + equal(results.length, 2); + done(); + }); + }); it('supports a sub-object with a geo point', async () => { const point = new Parse.GeoPoint(44.0, -11.0); @@ -374,88 +362,82 @@ describe('Parse.GeoPoint testing', () => { }); }); - it_id('d9fbc5c6-f767-47d6-bb44-3858eb9df15a')(it)( - 'supports withinPolygon open path', - done => { - const inbound = new Parse.GeoPoint(1.5, 1.5); - const onbound = new Parse.GeoPoint(10, 10); - const outbound = new Parse.GeoPoint(20, 20); - const obj1 = new Parse.Object('Polygon', { location: inbound }); - const obj2 = new Parse.Object('Polygon', { location: onbound }); - const obj3 = new Parse.Object('Polygon', { location: outbound }); - Parse.Object.saveAll([obj1, obj2, obj3]) - .then(() => { - const where = { - location: { - $geoWithin: { - $polygon: [ - { __type: 'GeoPoint', latitude: 0, longitude: 0 }, - { __type: 'GeoPoint', latitude: 0, longitude: 10 }, - { __type: 'GeoPoint', latitude: 10, longitude: 10 }, - { __type: 'GeoPoint', latitude: 10, longitude: 0 }, - ], - }, - }, - }; - return request({ - method: 'POST', - url: Parse.serverURL + '/classes/Polygon', - body: { where, _method: 'GET' }, - headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, - 'Content-Type': 'application/json', + it_id('d9fbc5c6-f767-47d6-bb44-3858eb9df15a')(it)('supports withinPolygon open path', done => { + const inbound = new Parse.GeoPoint(1.5, 1.5); + const onbound = new Parse.GeoPoint(10, 10); + const outbound = new Parse.GeoPoint(20, 20); + const obj1 = new Parse.Object('Polygon', { location: inbound }); + const obj2 = new Parse.Object('Polygon', { location: onbound }); + const obj3 = new Parse.Object('Polygon', { location: outbound }); + Parse.Object.saveAll([obj1, obj2, obj3]) + .then(() => { + const where = { + location: { + $geoWithin: { + $polygon: [ + { __type: 'GeoPoint', latitude: 0, longitude: 0 }, + { __type: 'GeoPoint', latitude: 0, longitude: 10 }, + { __type: 'GeoPoint', latitude: 10, longitude: 10 }, + { __type: 'GeoPoint', latitude: 10, longitude: 0 }, + ], }, - }); - }) - .then(resp => { - expect(resp.data.results.length).toBe(2); - done(); - }, done.fail); - } - ); + }, + }; + return request({ + method: 'POST', + url: Parse.serverURL + '/classes/Polygon', + body: { where, _method: 'GET' }, + headers: { + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, + 'Content-Type': 'application/json', + }, + }); + }) + .then(resp => { + expect(resp.data.results.length).toBe(2); + done(); + }, done.fail); + }); - it_id('3ec537bd-839a-4c93-a48b-b4a249820074')(it)( - 'supports withinPolygon closed path', - done => { - const inbound = new Parse.GeoPoint(1.5, 1.5); - const onbound = new Parse.GeoPoint(10, 10); - const outbound = new Parse.GeoPoint(20, 20); - const obj1 = new Parse.Object('Polygon', { location: inbound }); - const obj2 = new Parse.Object('Polygon', { location: onbound }); - const obj3 = new Parse.Object('Polygon', { location: outbound }); - Parse.Object.saveAll([obj1, obj2, obj3]) - .then(() => { - const where = { - location: { - $geoWithin: { - $polygon: [ - { __type: 'GeoPoint', latitude: 0, longitude: 0 }, - { __type: 'GeoPoint', latitude: 0, longitude: 10 }, - { __type: 'GeoPoint', latitude: 10, longitude: 10 }, - { __type: 'GeoPoint', latitude: 10, longitude: 0 }, - { __type: 'GeoPoint', latitude: 0, longitude: 0 }, - ], - }, - }, - }; - return request({ - method: 'POST', - url: Parse.serverURL + '/classes/Polygon', - body: { where, _method: 'GET' }, - headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, - 'Content-Type': 'application/json', + it_id('3ec537bd-839a-4c93-a48b-b4a249820074')(it)('supports withinPolygon closed path', done => { + const inbound = new Parse.GeoPoint(1.5, 1.5); + const onbound = new Parse.GeoPoint(10, 10); + const outbound = new Parse.GeoPoint(20, 20); + const obj1 = new Parse.Object('Polygon', { location: inbound }); + const obj2 = new Parse.Object('Polygon', { location: onbound }); + const obj3 = new Parse.Object('Polygon', { location: outbound }); + Parse.Object.saveAll([obj1, obj2, obj3]) + .then(() => { + const where = { + location: { + $geoWithin: { + $polygon: [ + { __type: 'GeoPoint', latitude: 0, longitude: 0 }, + { __type: 'GeoPoint', latitude: 0, longitude: 10 }, + { __type: 'GeoPoint', latitude: 10, longitude: 10 }, + { __type: 'GeoPoint', latitude: 10, longitude: 0 }, + { __type: 'GeoPoint', latitude: 0, longitude: 0 }, + ], }, - }); - }) - .then(resp => { - expect(resp.data.results.length).toBe(2); - done(); - }, done.fail); - } - ); + }, + }; + return request({ + method: 'POST', + url: Parse.serverURL + '/classes/Polygon', + body: { where, _method: 'GET' }, + headers: { + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, + 'Content-Type': 'application/json', + }, + }); + }) + .then(resp => { + expect(resp.data.results.length).toBe(2); + done(); + }, done.fail); + }); it_id('0a248e11-3598-480a-9ab5-8a0b259258e4')(it)( 'supports withinPolygon Polygon object', @@ -773,11 +755,7 @@ describe('Parse.GeoPoint testing', () => { await Parse.Object.saveAll([obj1, obj2]); - const q = new Parse.Query(TestObject).withinKilometers( - 'location', - inside, - 5 - ); + const q = new Parse.Query(TestObject).withinKilometers('location', inside, 5); const count = await q.count(); equal(count, 1); @@ -795,16 +773,8 @@ describe('Parse.GeoPoint testing', () => { await Parse.Object.saveAll([obj1, obj2, obj3]); - const q1 = new Parse.Query(TestObject).withinKilometers( - 'location', - inside, - 5 - ); - const q2 = new Parse.Query(TestObject).withinKilometers( - 'location', - middle, - 5 - ); + const q1 = new Parse.Query(TestObject).withinKilometers('location', inside, 5); + const q2 = new Parse.Query(TestObject).withinKilometers('location', middle, 5); const query = Parse.Query.or(q1, q2); const count = await query.count(); @@ -826,10 +796,7 @@ describe('Parse.GeoPoint testing', () => { }); await Parse.Object.saveAll([tmp, tmp2]); const query = new Parse.Query(TestObject); - query.notEqualTo( - 'location', - new Parse.GeoPoint({ latitude: 0, longitude: 0 }) - ); + query.notEqualTo('location', new Parse.GeoPoint({ latitude: 0, longitude: 0 })); const results = await query.find(); expect(results.length).toEqual(1); } diff --git a/spec/ParseGlobalConfig.spec.js b/spec/ParseGlobalConfig.spec.js index b118a27638..08b478c72c 100644 --- a/spec/ParseGlobalConfig.spec.js +++ b/spec/ParseGlobalConfig.spec.js @@ -186,46 +186,43 @@ describe('a GlobalConfig', () => { }); }); - it_id('5ebbd0cf-d1a5-49d9-aac7-5216abc5cb62')(it)( - 'properly handles delete op', - done => { + it_id('5ebbd0cf-d1a5-49d9-aac7-5216abc5cb62')(it)('properly handles delete op', done => { + request({ + method: 'PUT', + url: 'http://localhost:8378/1/config', + json: true, + body: { + params: { + companies: { __op: 'Delete' }, + counter: { __op: 'Delete' }, + internalParam: { __op: 'Delete' }, + foo: 'bar', + }, + }, + headers, + }).then(response => { + const body = response.data; + expect(response.status).toEqual(200); + expect(body.result).toEqual(true); request({ - method: 'PUT', url: 'http://localhost:8378/1/config', json: true, - body: { - params: { - companies: { __op: 'Delete' }, - counter: { __op: 'Delete' }, - internalParam: { __op: 'Delete' }, - foo: 'bar', - }, - }, headers, }).then(response => { const body = response.data; - expect(response.status).toEqual(200); - expect(body.result).toEqual(true); - request({ - url: 'http://localhost:8378/1/config', - json: true, - headers, - }).then(response => { - const body = response.data; - try { - expect(response.status).toEqual(200); - expect(body.params.companies).toBeUndefined(); - expect(body.params.counter).toBeUndefined(); - expect(body.params.foo).toBe('bar'); - expect(Object.keys(body.params).length).toBe(1); - } catch (e) { - jfail(e); - } - done(); - }); + try { + expect(response.status).toEqual(200); + expect(body.params.companies).toBeUndefined(); + expect(body.params.counter).toBeUndefined(); + expect(body.params.foo).toBe('bar'); + expect(Object.keys(body.params).length).toBe(1); + } catch (e) { + jfail(e); + } + done(); }); - } - ); + }); + }); it('fail to update if master key is missing', done => { request({ diff --git a/spec/ParseGraphQLClassNameTransformer.spec.js b/spec/ParseGraphQLClassNameTransformer.spec.js index c2a89e2f60..d8a4dd6020 100644 --- a/spec/ParseGraphQLClassNameTransformer.spec.js +++ b/spec/ParseGraphQLClassNameTransformer.spec.js @@ -1,11 +1,12 @@ -const { - transformClassNameToGraphQL, -} = require('../lib/GraphQL/transformers/className'); +const { transformClassNameToGraphQL } = require('../lib/GraphQL/transformers/className'); describe('transformClassNameToGraphQL', () => { it('should remove starting _ and tansform first letter to upper case', () => { - expect( - ['_User', '_user', 'User', 'user'].map(transformClassNameToGraphQL) - ).toEqual(['User', 'User', 'User', 'User']); + expect(['_User', '_user', 'User', 'user'].map(transformClassNameToGraphQL)).toEqual([ + 'User', + 'User', + 'User', + 'User', + ]); }); }); diff --git a/spec/ParseGraphQLController.spec.js b/spec/ParseGraphQLController.spec.js index 4ff28ffc93..9eed8f52be 100644 --- a/spec/ParseGraphQLController.spec.js +++ b/spec/ParseGraphQLController.spec.js @@ -36,10 +36,7 @@ describe('ParseGraphQLController', () => { const defaultFind = databaseController.find.bind(databaseController); databaseController.find = async (className, query, ...args) => { - if ( - className === GraphQLConfigClassName && - isEqual(query, { objectId: GraphQLConfigId }) - ) { + if (className === GraphQLConfigClassName && isEqual(query, { objectId: GraphQLConfigId })) { const graphQLConfigRecord = getConfigFromDb(); return graphQLConfigRecord ? [graphQLConfigRecord] : []; } else { @@ -48,12 +45,7 @@ describe('ParseGraphQLController', () => { }; const defaultUpdate = databaseController.update.bind(databaseController); - databaseController.update = async ( - className, - query, - update, - fullQueryOptions - ) => { + databaseController.update = async (className, query, update, fullQueryOptions) => { databaseUpdateArgs = [className, query, update, fullQueryOptions]; if ( className === GraphQLConfigClassName && @@ -86,9 +78,7 @@ describe('ParseGraphQLController', () => { cacheController, mountGraphQL: false, }) - ).toThrow( - 'ParseGraphQLController requires a "databaseController" to be instantiated.' - ); + ).toThrow('ParseGraphQLController requires a "databaseController" to be instantiated.'); }); it('should construct without a cacheController', () => { expect( @@ -198,9 +188,9 @@ describe('ParseGraphQLController', () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); - expectAsync( - parseGraphQLController.updateGraphQLConfig() - ).toBeRejectedWith('You must provide a graphQLConfig!'); + expectAsync(parseGraphQLController.updateGraphQLConfig()).toBeRejectedWith( + 'You must provide a graphQLConfig!' + ); }); it('should correct update the graphQLConfig object using the databaseController', async () => { @@ -232,32 +222,22 @@ describe('ParseGraphQLController', () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); - expectAsync( - parseGraphQLController.updateGraphQLConfig([]) - ).toBeRejected(); - expectAsync( - parseGraphQLController.updateGraphQLConfig(function () {}) - ).toBeRejected(); - expectAsync( - parseGraphQLController.updateGraphQLConfig(Promise.resolve({})) - ).toBeRejected(); - expectAsync( - parseGraphQLController.updateGraphQLConfig('') - ).toBeRejected(); - expectAsync( - parseGraphQLController.updateGraphQLConfig({}) - ).toBeResolvedTo(successfulUpdateResponse); + expectAsync(parseGraphQLController.updateGraphQLConfig([])).toBeRejected(); + expectAsync(parseGraphQLController.updateGraphQLConfig(function () {})).toBeRejected(); + expectAsync(parseGraphQLController.updateGraphQLConfig(Promise.resolve({}))).toBeRejected(); + expectAsync(parseGraphQLController.updateGraphQLConfig('')).toBeRejected(); + expectAsync(parseGraphQLController.updateGraphQLConfig({})).toBeResolvedTo( + successfulUpdateResponse + ); }); it('should throw if graphQLConfig has an invalid root key', async () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); - expectAsync( - parseGraphQLController.updateGraphQLConfig({ invalidKey: true }) - ).toBeRejected(); - expectAsync( - parseGraphQLController.updateGraphQLConfig({}) - ).toBeResolvedTo(successfulUpdateResponse); + expectAsync(parseGraphQLController.updateGraphQLConfig({ invalidKey: true })).toBeRejected(); + expectAsync(parseGraphQLController.updateGraphQLConfig({})).toBeResolvedTo( + successfulUpdateResponse + ); }); it('should throw if graphQLConfig has invalid class filters', async () => { const parseGraphQLController = new ParseGraphQLController({ @@ -295,9 +275,7 @@ describe('ParseGraphQLController', () => { const parseGraphQLController = new ParseGraphQLController({ databaseController, }); - expectAsync( - parseGraphQLController.updateGraphQLConfig({ classConfigs: {} }) - ).toBeRejected(); + expectAsync(parseGraphQLController.updateGraphQLConfig({ classConfigs: {} })).toBeRejected(); expectAsync( parseGraphQLController.updateGraphQLConfig({ classConfigs: [null] }) ).toBeRejected(); @@ -311,9 +289,9 @@ describe('ParseGraphQLController', () => { classConfigs: [{ className: 'ValidClass' }, null], }) ).toBeRejected(); - expectAsync( - parseGraphQLController.updateGraphQLConfig({ classConfigs: [] }) - ).toBeResolvedTo(successfulUpdateResponse); + expectAsync(parseGraphQLController.updateGraphQLConfig({ classConfigs: [] })).toBeResolvedTo( + successfulUpdateResponse + ); expectAsync( parseGraphQLController.updateGraphQLConfig({ classConfigs: [ @@ -936,34 +914,26 @@ describe('ParseGraphQLController', () => { let cacheBeforeValue; let cacheAfterValue; - cacheBeforeValue = await cacheController.graphQL.get( - mountedController.configCacheKey - ); + cacheBeforeValue = await cacheController.graphQL.get(mountedController.configCacheKey); expect(cacheBeforeValue).toBeNull(); await mountedController.updateGraphQLConfig({ enabledForClasses: ['SuperCar'], }); - cacheAfterValue = await cacheController.graphQL.get( - mountedController.configCacheKey - ); + cacheAfterValue = await cacheController.graphQL.get(mountedController.configCacheKey); expect(cacheAfterValue).toEqual({ enabledForClasses: ['SuperCar'] }); // reset removeConfigFromDb(); cacheController.graphQL.clear(); - cacheBeforeValue = await cacheController.graphQL.get( - unmountedController.configCacheKey - ); + cacheBeforeValue = await cacheController.graphQL.get(unmountedController.configCacheKey); expect(cacheBeforeValue).toBeNull(); await unmountedController.updateGraphQLConfig({ enabledForClasses: ['SuperCar'], }); - cacheAfterValue = await cacheController.graphQL.get( - unmountedController.configCacheKey - ); + cacheAfterValue = await cacheController.graphQL.get(unmountedController.configCacheKey); expect(cacheAfterValue).toBeNull(); }); }); diff --git a/spec/ParseGraphQLSchema.spec.js b/spec/ParseGraphQLSchema.spec.js index 212f87092f..199c52756c 100644 --- a/spec/ParseGraphQLSchema.spec.js +++ b/spec/ParseGraphQLSchema.spec.js @@ -26,9 +26,9 @@ describe('ParseGraphQLSchema', () => { expect(() => new ParseGraphQLSchema()).toThrow( 'You must provide a parseGraphQLController instance!' ); - expect( - () => new ParseGraphQLSchema({ parseGraphQLController: {} }) - ).toThrow('You must provide a databaseController instance!'); + expect(() => new ParseGraphQLSchema({ parseGraphQLController: {} })).toThrow( + 'You must provide a databaseController instance!' + ); expect( () => new ParseGraphQLSchema({ @@ -74,9 +74,7 @@ describe('ParseGraphQLSchema', () => { expect(graphQLTypes).not.toBe(parseGraphQLSchema.graphQLTypes); expect(graphQLQueries).not.toBe(parseGraphQLSchema.graphQLQueries); expect(graphQLMutations).not.toBe(parseGraphQLSchema.graphQLMutations); - expect(graphQLSubscriptions).not.toBe( - parseGraphQLSchema.graphQLSubscriptions - ); + expect(graphQLSubscriptions).not.toBe(parseGraphQLSchema.graphQLSubscriptions); }); it('should load a brand new GraphQL Schema if graphQLConfig changes', async () => { @@ -113,9 +111,7 @@ describe('ParseGraphQLSchema', () => { expect(graphQLTypes).not.toBe(parseGraphQLSchema.graphQLTypes); expect(graphQLQueries).not.toBe(parseGraphQLSchema.graphQLQueries); expect(graphQLMutations).not.toBe(parseGraphQLSchema.graphQLMutations); - expect(graphQLSubscriptions).not.toBe( - parseGraphQLSchema.graphQLSubscriptions - ); + expect(graphQLSubscriptions).not.toBe(parseGraphQLSchema.graphQLSubscriptions); }); }); @@ -140,9 +136,7 @@ describe('ParseGraphQLSchema', () => { expect(parseGraphQLSchema.addGraphQLType(type)).toBe(type); expect(parseGraphQLSchema.graphQLTypes).toContain(type); expect( - parseGraphQLSchema.addGraphQLType( - new GraphQLObjectType({ name: 'SomeClass' }) - ) + parseGraphQLSchema.addGraphQLType(new GraphQLObjectType({ name: 'SomeClass' })) ).toBeUndefined(); expect(logged).toBeTruthy(); }); @@ -163,10 +157,7 @@ describe('ParseGraphQLSchema', () => { expect(parseGraphQLSchema.addGraphQLType(type, true)).toBe(type); expect(parseGraphQLSchema.graphQLTypes).toContain(type); expect(() => - parseGraphQLSchema.addGraphQLType( - new GraphQLObjectType({ name: 'SomeClass' }), - true - ) + parseGraphQLSchema.addGraphQLType(new GraphQLObjectType({ name: 'SomeClass' }), true) ).toThrowError( 'Type SomeClass could not be added to the auto schema because it collided with an existing type.' ); @@ -189,9 +180,7 @@ describe('ParseGraphQLSchema', () => { }); await parseGraphQLSchema.load(); expect( - parseGraphQLSchema.addGraphQLType( - new GraphQLObjectType({ name: 'String' }) - ) + parseGraphQLSchema.addGraphQLType(new GraphQLObjectType({ name: 'String' })) ).toBeUndefined(); expect(logged).toBeTruthy(); }); @@ -232,13 +221,9 @@ describe('ParseGraphQLSchema', () => { }); await parseGraphQLSchema.load(); const field = {}; - expect(parseGraphQLSchema.addGraphQLQuery('someClasses', field)).toBe( - field - ); + expect(parseGraphQLSchema.addGraphQLQuery('someClasses', field)).toBe(field); expect(parseGraphQLSchema.graphQLQueries['someClasses']).toBe(field); - expect( - parseGraphQLSchema.addGraphQLQuery('someClasses', {}) - ).toBeUndefined(); + expect(parseGraphQLSchema.addGraphQLQuery('someClasses', {})).toBeUndefined(); expect(logged).toBeTruthy(); }); @@ -255,13 +240,9 @@ describe('ParseGraphQLSchema', () => { }); await parseGraphQLSchema.load(); const field = {}; - expect(parseGraphQLSchema.addGraphQLQuery('someClasses', field)).toBe( - field - ); + expect(parseGraphQLSchema.addGraphQLQuery('someClasses', field)).toBe(field); expect(parseGraphQLSchema.graphQLQueries['someClasses']).toBe(field); - expect(() => - parseGraphQLSchema.addGraphQLQuery('someClasses', {}, true) - ).toThrowError( + expect(() => parseGraphQLSchema.addGraphQLQuery('someClasses', {}, true)).toThrowError( 'Query someClasses could not be added to the auto schema because it collided with an existing field.' ); }); @@ -300,9 +281,7 @@ describe('ParseGraphQLSchema', () => { await parseGraphQLSchema.load(); delete parseGraphQLSchema.graphQLQueries.viewer; const field = {}; - expect( - parseGraphQLSchema.addGraphQLQuery('viewer', field, true, true) - ).toBe(field); + expect(parseGraphQLSchema.addGraphQLQuery('viewer', field, true, true)).toBe(field); expect(parseGraphQLSchema.graphQLQueries['viewer']).toBe(field); }); }); @@ -325,15 +304,9 @@ describe('ParseGraphQLSchema', () => { }); await parseGraphQLSchema.load(); const field = {}; - expect( - parseGraphQLSchema.addGraphQLMutation('createSomeClass', field) - ).toBe(field); - expect(parseGraphQLSchema.graphQLMutations['createSomeClass']).toBe( - field - ); - expect( - parseGraphQLSchema.addGraphQLMutation('createSomeClass', {}) - ).toBeUndefined(); + expect(parseGraphQLSchema.addGraphQLMutation('createSomeClass', field)).toBe(field); + expect(parseGraphQLSchema.graphQLMutations['createSomeClass']).toBe(field); + expect(parseGraphQLSchema.addGraphQLMutation('createSomeClass', {})).toBeUndefined(); expect(logged).toBeTruthy(); }); @@ -350,15 +323,9 @@ describe('ParseGraphQLSchema', () => { }); await parseGraphQLSchema.load(); const field = {}; - expect( - parseGraphQLSchema.addGraphQLMutation('createSomeClass', field) - ).toBe(field); - expect(parseGraphQLSchema.graphQLMutations['createSomeClass']).toBe( - field - ); - expect(() => - parseGraphQLSchema.addGraphQLMutation('createSomeClass', {}, true) - ).toThrowError( + expect(parseGraphQLSchema.addGraphQLMutation('createSomeClass', field)).toBe(field); + expect(parseGraphQLSchema.graphQLMutations['createSomeClass']).toBe(field); + expect(() => parseGraphQLSchema.addGraphQLMutation('createSomeClass', {}, true)).toThrowError( 'Mutation createSomeClass could not be added to the auto schema because it collided with an existing field.' ); }); @@ -379,9 +346,7 @@ describe('ParseGraphQLSchema', () => { appId, }); await parseGraphQLSchema.load(); - expect( - parseGraphQLSchema.addGraphQLMutation('signUp', {}) - ).toBeUndefined(); + expect(parseGraphQLSchema.addGraphQLMutation('signUp', {})).toBeUndefined(); expect(logged).toBeTruthy(); }); @@ -399,9 +364,7 @@ describe('ParseGraphQLSchema', () => { await parseGraphQLSchema.load(); delete parseGraphQLSchema.graphQLMutations.signUp; const field = {}; - expect( - parseGraphQLSchema.addGraphQLMutation('signUp', field, true, true) - ).toBe(field); + expect(parseGraphQLSchema.addGraphQLMutation('signUp', field, true, true)).toBe(field); expect(parseGraphQLSchema.graphQLMutations['signUp']).toBe(field); }); }); @@ -471,17 +434,11 @@ describe('ParseGraphQLSchema', () => { const mutations2 = parseGraphQLSchema.graphQLMutations; expect(schema1).not.toBe(schema2); expect(types1).not.toBe(types2); - expect(types1.map(type => type.name).sort()).toEqual( - types2.map(type => type.name).sort() - ); + expect(types1.map(type => type.name).sort()).toEqual(types2.map(type => type.name).sort()); expect(queries1).not.toBe(queries2); - expect(Object.keys(queries1).sort()).toEqual( - Object.keys(queries2).sort() - ); + expect(Object.keys(queries1).sort()).toEqual(Object.keys(queries2).sort()); expect(mutations1).not.toBe(mutations2); - expect(Object.keys(mutations1).sort()).toEqual( - Object.keys(mutations2).sort() - ); + expect(Object.keys(mutations1).sort()).toEqual(Object.keys(mutations2).sort()); }); it('should not generate duplicate types when colliding the same name', async () => { @@ -507,17 +464,11 @@ describe('ParseGraphQLSchema', () => { const mutations2 = parseGraphQLSchema.graphQLMutations; expect(schema1).not.toBe(schema2); expect(types1).not.toBe(types2); - expect(types1.map(type => type.name).sort()).toEqual( - types2.map(type => type.name).sort() - ); + expect(types1.map(type => type.name).sort()).toEqual(types2.map(type => type.name).sort()); expect(queries1).not.toBe(queries2); - expect(Object.keys(queries1).sort()).toEqual( - Object.keys(queries2).sort() - ); + expect(Object.keys(queries1).sort()).toEqual(Object.keys(queries2).sort()); expect(mutations1).not.toBe(mutations2); - expect(Object.keys(mutations1).sort()).toEqual( - Object.keys(mutations2).sort() - ); + expect(Object.keys(mutations1).sort()).toEqual(Object.keys(mutations2).sort()); }); it('should not generate duplicate queries when query name collide', async () => { @@ -541,14 +492,10 @@ describe('ParseGraphQLSchema', () => { const mutations2 = parseGraphQLSchema.graphQLMutations; expect(schema1).not.toBe(schema2); expect(queries1).not.toBe(queries2); - expect(Object.keys(queries1).sort()).toEqual( - Object.keys(queries2).sort() - ); + expect(Object.keys(queries1).sort()).toEqual(Object.keys(queries2).sort()); expect(mutations1).not.toBe(mutations2); expect( - Object.keys(mutations1) - .concat('createCars', 'updateCars', 'deleteCars') - .sort() + Object.keys(mutations1).concat('createCars', 'updateCars', 'deleteCars').sort() ).toEqual(Object.keys(mutations2).sort()); }); }); diff --git a/spec/ParseGraphQLServer.spec.js b/spec/ParseGraphQLServer.spec.js index 3fb4486a30..48dbe158f0 100644 --- a/spec/ParseGraphQLServer.spec.js +++ b/spec/ParseGraphQLServer.spec.js @@ -1,8 +1,7 @@ const http = require('http'); const express = require('express'); const req = require('../lib/request'); -const fetch = (...args) => - import('node-fetch').then(({ default: fetch }) => fetch(...args)); +const fetch = (...args) => import('node-fetch').then(({ default: fetch }) => fetch(...args)); const FormData = require('form-data'); const ws = require('ws'); require('./helper'); @@ -11,9 +10,7 @@ const { updateCLP } = require('./support/dev'); const pluralize = require('pluralize'); const { getMainDefinition } = require('@apollo/client/utilities'); const createUploadLink = (...args) => - import('apollo-upload-client/createUploadLink.mjs').then(({ default: fn }) => - fn(...args) - ); + import('apollo-upload-client/createUploadLink.mjs').then(({ default: fn }) => fn(...args)); const { SubscriptionClient } = require('subscriptions-transport-ws'); const { WebSocketLink } = require('@apollo/client/link/ws'); const { mergeSchemas } = require('@graphql-tools/schema'); @@ -41,12 +38,7 @@ const { ReadPreference, Collection } = require('mongodb'); const { v4: uuidv4 } = require('uuid'); function handleError(e) { - if ( - e && - e.networkError && - e.networkError.result && - e.networkError.result.errors - ) { + if (e && e.networkError && e.networkError.result && e.networkError.result.errors) { fail(e.networkError.result.errors); } else { fail(e); @@ -70,9 +62,7 @@ describe('ParseGraphQLServer', () => { describe('constructor', () => { it('should require a parseServer instance', () => { - expect(() => new ParseGraphQLServer()).toThrow( - 'You must provide a parseServer instance!' - ); + expect(() => new ParseGraphQLServer()).toThrow('You must provide a parseServer instance!'); }); it('should require config.graphQLPath', () => { @@ -108,9 +98,7 @@ describe('ParseGraphQLServer', () => { const parseGraphQLServer = new ParseGraphQLServer(parseServer, { graphQLPath: 'graphql', }); - expect(parseGraphQLServer.parseGraphQLSchema.log.adapter).toBe( - loggerAdapter - ); + expect(parseGraphQLServer.parseGraphQLSchema.log.adapter).toBe(loggerAdapter); }); }); @@ -147,9 +135,7 @@ describe('ParseGraphQLServer', () => { "should return schema and context with req's info, config and auth", async () => { const options = await parseGraphQLServer._getGraphQLOptions(); - expect(options.schema).toEqual( - parseGraphQLServer.parseGraphQLSchema.graphQLSchema - ); + expect(options.schema).toEqual(parseGraphQLServer.parseGraphQLSchema.graphQLSchema); const contextResponse = await options.context({ req, res }); expect(contextResponse.info).toEqual(req.info); expect(contextResponse.config).toEqual(req.config); @@ -161,30 +147,18 @@ describe('ParseGraphQLServer', () => { const originalLoad = parseGraphQLServer.parseGraphQLSchema.load; let counter = 0; parseGraphQLServer.parseGraphQLSchema.load = () => ++counter; - expect((await parseGraphQLServer._getGraphQLOptions(req)).schema).toEqual( - 1 - ); - expect((await parseGraphQLServer._getGraphQLOptions(req)).schema).toEqual( - 2 - ); - expect((await parseGraphQLServer._getGraphQLOptions(req)).schema).toEqual( - 3 - ); + expect((await parseGraphQLServer._getGraphQLOptions(req)).schema).toEqual(1); + expect((await parseGraphQLServer._getGraphQLOptions(req)).schema).toEqual(2); + expect((await parseGraphQLServer._getGraphQLOptions(req)).schema).toEqual(3); parseGraphQLServer.parseGraphQLSchema.load = originalLoad; }); }); describe('_transformMaxUploadSizeToBytes', () => { it('should transform to bytes', () => { - expect(parseGraphQLServer._transformMaxUploadSizeToBytes('20mb')).toBe( - 20971520 - ); - expect(parseGraphQLServer._transformMaxUploadSizeToBytes('333Gb')).toBe( - 357556027392 - ); - expect( - parseGraphQLServer._transformMaxUploadSizeToBytes('123456KB') - ).toBe(126418944); + expect(parseGraphQLServer._transformMaxUploadSizeToBytes('20mb')).toBe(20971520); + expect(parseGraphQLServer._transformMaxUploadSizeToBytes('333Gb')).toBe(357556027392); + expect(parseGraphQLServer._transformMaxUploadSizeToBytes('123456KB')).toBe(126418944); }); }); @@ -196,9 +170,7 @@ describe('ParseGraphQLServer', () => { expect(() => parseGraphQLServer.applyGraphQL({})).toThrow( 'You must provide an Express.js app instance!' ); - expect(() => - parseGraphQLServer.applyGraphQL(new express()) - ).not.toThrow(); + expect(() => parseGraphQLServer.applyGraphQL(new express())).not.toThrow(); }); it('should apply middlewares at config.graphQLPath', () => { @@ -225,9 +197,7 @@ describe('ParseGraphQLServer', () => { expect(() => parseGraphQLServer.applyPlayground({})).toThrow( 'You must provide an Express.js app instance!' ); - expect(() => - parseGraphQLServer.applyPlayground(new express()) - ).not.toThrow(); + expect(() => parseGraphQLServer.applyPlayground(new express())).not.toThrow(); }); it('should require initialization with config.playgroundPath', () => { @@ -261,9 +231,7 @@ describe('ParseGraphQLServer', () => { new ParseGraphQLServer(parseServer, { graphQLPath: 'graphql', }).createSubscriptions({}) - ).toThrow( - 'You must provide a config.subscriptionsPath to createSubscriptions!' - ); + ).toThrow('You must provide a config.subscriptionsPath to createSubscriptions!'); }); }); @@ -292,9 +260,9 @@ describe('ParseGraphQLServer', () => { throw new Error('Network request failed'); }, }; - await expectAsync( - parseGraphQLServer.setGraphQLConfig({}) - ).toBeRejectedWith(new Error('Network request failed')); + await expectAsync(parseGraphQLServer.setGraphQLConfig({})).toBeRejectedWith( + new Error('Network request failed') + ); }); it('should return the response from parseGraphQLController', async () => { parseGraphQLServer.parseGraphQLController = { @@ -302,11 +270,9 @@ describe('ParseGraphQLServer', () => { return { response: { result: true } }; }, }; - await expectAsync(parseGraphQLServer.setGraphQLConfig({})).toBeResolvedTo( - { - response: { result: true }, - } - ); + await expectAsync(parseGraphQLServer.setGraphQLConfig({})).toBeResolvedTo({ + response: { result: true }, + }); }); }); @@ -375,8 +341,7 @@ describe('ParseGraphQLServer', () => { role.getUsers().add(user3); role = await role.save(); - const schemaController = - await parseServer.config.databaseController.loadSchema(); + const schemaController = await parseServer.config.databaseController.loadSchema(); try { await schemaController.addClassIfNotExists( 'GraphQLClass', @@ -421,10 +386,7 @@ describe('ParseGraphQLServer', () => { {} ); } catch (err) { - if ( - !(err instanceof Parse.Error) || - err.message !== 'Class GraphQLClass already exists.' - ) { + if (!(err instanceof Parse.Error) || err.message !== 'Class GraphQLClass already exists.') { throw err; } } @@ -483,12 +445,9 @@ describe('ParseGraphQLServer', () => { const expressApp = express(); httpServer = http.createServer(expressApp); expressApp.use('/parse', _parseServer.app); - parseLiveQueryServer = await ParseServer.createLiveQueryServer( - httpServer, - { - port: 1338, - } - ); + parseLiveQueryServer = await ParseServer.createLiveQueryServer(httpServer, { + port: 1338, + }); parseGraphQLServer = new ParseGraphQLServer(_parseServer, { graphQLPath: '/graphql', playgroundPath: '/playground', @@ -521,9 +480,7 @@ describe('ParseGraphQLServer', () => { link: split( ({ query }) => { const { kind, operation } = getMainDefinition(query); - return ( - kind === 'OperationDefinition' && operation === 'subscription' - ); + return kind === 'OperationDefinition' && operation === 'subscription'; }, wsLink, httpLink @@ -571,9 +528,7 @@ describe('ParseGraphQLServer', () => { const { response: { headers }, } = context; - expect(headers.get('access-control-allow-origin')).toEqual( - 'http://example.com' - ); + expect(headers.get('access-control-allow-origin')).toEqual('http://example.com'); checked = true; return response; }); @@ -716,10 +671,7 @@ describe('ParseGraphQLServer', () => { }) ).data['__type']; expect(fileType.kind).toEqual('OBJECT'); - expect(fileType.fields.map(field => field.name).sort()).toEqual([ - 'name', - 'url', - ]); + expect(fileType.fields.map(field => field.name).sort()).toEqual(['name', 'url']); }); it('should have Class interface type', async () => { @@ -762,9 +714,7 @@ describe('ParseGraphQLServer', () => { }) ).data['__type']; expect(readPreferenceType.kind).toEqual('ENUM'); - expect( - readPreferenceType.enumValues.map(value => value.name).sort() - ).toEqual([ + expect(readPreferenceType.enumValues.map(value => value.name).sort()).toEqual([ 'NEAREST', 'PRIMARY', 'PRIMARY_PREFERRED', @@ -806,16 +756,10 @@ describe('ParseGraphQLServer', () => { }) ).data['__schema'].types.map(type => type.name); - const expectedTypes = [ - 'ParseObject', - 'Date', - 'FileInfo', - 'ReadPreference', - 'Upload', - ]; - expect( - expectedTypes.every(type => schemaTypes.indexOf(type) !== -1) - ).toBeTruthy(JSON.stringify(schemaTypes.types)); + const expectedTypes = ['ParseObject', 'Date', 'FileInfo', 'ReadPreference', 'Upload']; + expect(expectedTypes.every(type => schemaTypes.indexOf(type) !== -1)).toBeTruthy( + JSON.stringify(schemaTypes.types) + ); }); }); @@ -920,10 +864,7 @@ describe('ParseGraphQLServer', () => { .map(field => field.name) .sort(); - expect(createFilePayloadFields).toEqual([ - 'clientMutationId', - 'fileInfo', - ]); + expect(createFilePayloadFields).toEqual(['clientMutationId', 'fileInfo']); }); it('should have clientMutationId in call function input', async () => { @@ -945,11 +886,7 @@ describe('ParseGraphQLServer', () => { .map(field => field.name) .sort(); - expect(callFunctionInputFields).toEqual([ - 'clientMutationId', - 'functionName', - 'params', - ]); + expect(callFunctionInputFields).toEqual(['clientMutationId', 'functionName', 'params']); }); it('should have clientMutationId in call function payload', async () => { @@ -971,10 +908,7 @@ describe('ParseGraphQLServer', () => { .map(field => field.name) .sort(); - expect(callFunctionPayloadFields).toEqual([ - 'clientMutationId', - 'result', - ]); + expect(callFunctionPayloadFields).toEqual(['clientMutationId', 'result']); }); it('should have clientMutationId in sign up mutation input', async () => { @@ -1033,12 +967,7 @@ describe('ParseGraphQLServer', () => { ).data['__type'].inputFields .map(field => field.name) .sort(); - expect(inputFields).toEqual([ - 'authData', - 'clientMutationId', - 'password', - 'username', - ]); + expect(inputFields).toEqual(['authData', 'clientMutationId', 'password', 'username']); }); it('should have clientMutationId in log in mutation payload', async () => { @@ -1118,11 +1047,7 @@ describe('ParseGraphQLServer', () => { .map(field => field.name) .sort(); - expect(inputFields).toEqual([ - 'clientMutationId', - 'name', - 'schemaFields', - ]); + expect(inputFields).toEqual(['clientMutationId', 'name', 'schemaFields']); }); it('should have clientMutationId in createClass mutation payload', async () => { @@ -1162,11 +1087,7 @@ describe('ParseGraphQLServer', () => { .map(field => field.name) .sort(); - expect(inputFields).toEqual([ - 'clientMutationId', - 'name', - 'schemaFields', - ]); + expect(inputFields).toEqual(['clientMutationId', 'name', 'schemaFields']); }); it('should have clientMutationId in updateClass mutation payload', async () => { @@ -1251,10 +1172,7 @@ describe('ParseGraphQLServer', () => { .map(field => field.name) .sort(); - expect(createObjectInputFields).toEqual([ - 'clientMutationId', - 'fields', - ]); + expect(createObjectInputFields).toEqual(['clientMutationId', 'fields']); }); it('should have clientMutationId in custom create object mutation payload', async () => { @@ -1279,10 +1197,7 @@ describe('ParseGraphQLServer', () => { .map(field => field.name) .sort(); - expect(createObjectPayloadFields).toEqual([ - 'clientMutationId', - 'someClass', - ]); + expect(createObjectPayloadFields).toEqual(['clientMutationId', 'someClass']); }); it('should have clientMutationId in custom update object mutation input', async () => { @@ -1307,11 +1222,7 @@ describe('ParseGraphQLServer', () => { .map(field => field.name) .sort(); - expect(createObjectInputFields).toEqual([ - 'clientMutationId', - 'fields', - 'id', - ]); + expect(createObjectInputFields).toEqual(['clientMutationId', 'fields', 'id']); }); it('should have clientMutationId in custom update object mutation payload', async () => { @@ -1336,10 +1247,7 @@ describe('ParseGraphQLServer', () => { .map(field => field.name) .sort(); - expect(createObjectPayloadFields).toEqual([ - 'clientMutationId', - 'someClass', - ]); + expect(createObjectPayloadFields).toEqual(['clientMutationId', 'someClass']); }); it('should have clientMutationId in custom delete object mutation input', async () => { @@ -1389,10 +1297,7 @@ describe('ParseGraphQLServer', () => { .map(field => field.name) .sort(); - expect(createObjectPayloadFields).toEqual([ - 'clientMutationId', - 'someClass', - ]); + expect(createObjectPayloadFields).toEqual(['clientMutationId', 'someClass']); }); }); @@ -1426,9 +1331,9 @@ describe('ParseGraphQLServer', () => { 'CreateUserFieldsInput', 'UpdateUserFieldsInput', ]; - expect( - expectedTypes.every(type => schemaTypes.indexOf(type) !== -1) - ).toBeTruthy(JSON.stringify(schemaTypes)); + expect(expectedTypes.every(type => schemaTypes.indexOf(type) !== -1)).toBeTruthy( + JSON.stringify(schemaTypes) + ); }); it('should ArrayResult contains all types', async () => { @@ -1453,8 +1358,7 @@ describe('ParseGraphQLServer', () => { }); it('should update schema when it changes', async () => { - const schemaController = - await parseServer.config.databaseController.loadSchema(); + const schemaController = await parseServer.config.databaseController.loadSchema(); await schemaController.updateClass('_User', { foo: { type: 'String' }, }); @@ -1509,8 +1413,7 @@ describe('ParseGraphQLServer', () => { it_id('d6a23a2f-ca18-4b15-bc73-3e636f99e6bc')(it)( 'should only include types in the enabledForClasses list', async () => { - const schemaController = - await parseServer.config.databaseController.loadSchema(); + const schemaController = await parseServer.config.databaseController.loadSchema(); await schemaController.addClassIfNotExists('SuperCar', { foo: { type: 'String' }, }); @@ -1544,8 +1447,7 @@ describe('ParseGraphQLServer', () => { it_id('1db2aceb-d24e-4929-ba43-8dbb5d0395e1')(it)( 'should not include types in the disabledForClasses list', async () => { - const schemaController = - await parseServer.config.databaseController.loadSchema(); + const schemaController = await parseServer.config.databaseController.loadSchema(); await schemaController.addClassIfNotExists('SuperCar', { foo: { type: 'String' }, }); @@ -1863,8 +1765,7 @@ describe('ParseGraphQLServer', () => { it_id('4af763b1-ff86-43c7-ba30-060a1c07e730')(it)( 'should only allow the supplied create and update fields for a class', async () => { - const schemaController = - await parseServer.config.databaseController.loadSchema(); + const schemaController = await parseServer.config.databaseController.loadSchema(); await schemaController.addClassIfNotExists('SuperCar', { engine: { type: 'String' }, doors: { type: 'Number' }, @@ -1892,9 +1793,7 @@ describe('ParseGraphQLServer', () => { apolloClient.query({ query: gql` mutation InvalidCreateSuperCar { - createSuperCar( - input: { fields: { engine: "diesel", mileage: 1000 } } - ) { + createSuperCar(input: { fields: { engine: "diesel", mileage: 1000 } }) { superCar { id } @@ -1908,9 +1807,7 @@ describe('ParseGraphQLServer', () => { query: gql` mutation ValidCreateSuperCar { createSuperCar( - input: { - fields: { engine: "diesel", doors: 5, price: "£10000" } - } + input: { fields: { engine: "diesel", doors: 5, price: "£10000" } } ) { superCar { id @@ -1927,9 +1824,7 @@ describe('ParseGraphQLServer', () => { apolloClient.query({ query: gql` mutation InvalidUpdateSuperCar($id: ID!) { - updateSuperCar( - input: { id: $id, fields: { engine: "petrol" } } - ) { + updateSuperCar(input: { id: $id, fields: { engine: "petrol" } }) { clientMutationId } } @@ -1944,9 +1839,7 @@ describe('ParseGraphQLServer', () => { await apolloClient.query({ query: gql` mutation ValidUpdateSuperCar($id: ID!) { - updateSuperCar( - input: { id: $id, fields: { mileage: 2000 } } - ) { + updateSuperCar(input: { id: $id, fields: { mileage: 2000 } }) { clientMutationId } } @@ -1963,8 +1856,7 @@ describe('ParseGraphQLServer', () => { it_id('fc9237e9-3e63-4b55-9c1d-e6269f613a93')(it)( 'should handle required fields from the Parse class', async () => { - const schemaController = - await parseServer.config.databaseController.loadSchema(); + const schemaController = await parseServer.config.databaseController.loadSchema(); await schemaController.addClassIfNotExists('SuperCar', { engine: { type: 'String', required: true }, doors: { type: 'Number', required: true }, @@ -1990,15 +1882,9 @@ describe('ParseGraphQLServer', () => { } `, }); - expect( - __type.inputFields.find(o => o.name === 'price').type.kind - ).toEqual('SCALAR'); - expect( - __type.inputFields.find(o => o.name === 'engine').type.kind - ).toEqual('NON_NULL'); - expect( - __type.inputFields.find(o => o.name === 'doors').type.kind - ).toEqual('NON_NULL'); + expect(__type.inputFields.find(o => o.name === 'price').type.kind).toEqual('SCALAR'); + expect(__type.inputFields.find(o => o.name === 'engine').type.kind).toEqual('NON_NULL'); + expect(__type.inputFields.find(o => o.name === 'doors').type.kind).toEqual('NON_NULL'); const { data: { __type: __type2 }, @@ -2016,23 +1902,16 @@ describe('ParseGraphQLServer', () => { } `, }); - expect( - __type2.fields.find(o => o.name === 'price').type.kind - ).toEqual('SCALAR'); - expect( - __type2.fields.find(o => o.name === 'engine').type.kind - ).toEqual('NON_NULL'); - expect( - __type2.fields.find(o => o.name === 'doors').type.kind - ).toEqual('NON_NULL'); + expect(__type2.fields.find(o => o.name === 'price').type.kind).toEqual('SCALAR'); + expect(__type2.fields.find(o => o.name === 'engine').type.kind).toEqual('NON_NULL'); + expect(__type2.fields.find(o => o.name === 'doors').type.kind).toEqual('NON_NULL'); } ); it_id('83b6895a-7dfd-4e3b-a5ce-acdb1fa39705')(it)( 'should only allow the supplied output fields for a class', async () => { - const schemaController = - await parseServer.config.databaseController.loadSchema(); + const schemaController = await parseServer.config.databaseController.loadSchema(); await schemaController.addClassIfNotExists('SuperCar', { engine: { type: 'String' }, @@ -2153,8 +2032,7 @@ describe('ParseGraphQLServer', () => { 'should only allow the supplied constraint fields for a class', async () => { try { - const schemaController = - await parseServer.config.databaseController.loadSchema(); + const schemaController = await parseServer.config.databaseController.loadSchema(); await schemaController.addClassIfNotExists('SuperCar', { model: { type: 'String' }, @@ -2191,11 +2069,7 @@ describe('ParseGraphQLServer', () => { apolloClient.query({ query: gql` query FindSuperCar { - superCars( - where: { - insuranceCertificate: { equalTo: "private-file.pdf" } - } - ) { + superCars(where: { insuranceCertificate: { equalTo: "private-file.pdf" } }) { count } } @@ -2235,8 +2109,7 @@ describe('ParseGraphQLServer', () => { it_id('a3bdbd5d-8779-42fe-91a1-7a7f90a6177b')(it)( 'should only allow the supplied sort fields for a class', async () => { - const schemaController = - await parseServer.config.databaseController.loadSchema(); + const schemaController = await parseServer.config.databaseController.loadSchema(); await schemaController.addClassIfNotExists('SuperCar', { engine: { type: 'String' }, @@ -2464,12 +2337,8 @@ describe('ParseGraphQLServer', () => { `, }); - expect(findResult.data.someClasses.edges[0].node.objectId).toBe( - obj1.id - ); - expect(findResult.data.someClasses.edges[1].node.objectId).toBe( - obj2.id - ); + expect(findResult.data.someClasses.edges[0].node.objectId).toBe(obj1.id); + expect(findResult.data.someClasses.edges[1].node.objectId).toBe(obj2.id); const nodeResult = await apolloClient.query({ query: gql` @@ -2496,14 +2365,10 @@ describe('ParseGraphQLServer', () => { }, }); - expect(nodeResult.data.node1.id).toBe( - findResult.data.someClasses.edges[0].node.id - ); + expect(nodeResult.data.node1.id).toBe(findResult.data.someClasses.edges[0].node.id); expect(nodeResult.data.node1.objectId).toBe(obj1.id); expect(nodeResult.data.node1.someField).toBe('some value 1'); - expect(nodeResult.data.node2.id).toBe( - findResult.data.someClasses.edges[1].node.id - ); + expect(nodeResult.data.node2.id).toBe(findResult.data.someClasses.edges[1].node.id); expect(nodeResult.data.node2.objectId).toBe(obj2.id); expect(nodeResult.data.node2.someField).toBe('some value 2'); }); @@ -2527,16 +2392,10 @@ describe('ParseGraphQLServer', () => { addStrings: [{ name: "stringField" }] addArrays: [{ name: "arrayField" }] addPointers: [ - { - name: "pointerField" - targetClassName: "SecondaryObject" - } + { name: "pointerField", targetClassName: "SecondaryObject" } ] addRelations: [ - { - name: "relationField" - targetClassName: "SecondaryObject" - } + { name: "relationField", targetClassName: "SecondaryObject" } ] } } @@ -2631,10 +2490,7 @@ describe('ParseGraphQLServer', () => { $id6: ID! ) { secondaryObject1: updateSecondaryObject( - input: { - id: $id1 - fields: { someField: "some value 11" } - } + input: { id: $id1, fields: { someField: "some value 11" } } ) { secondaryObject { id @@ -2643,10 +2499,7 @@ describe('ParseGraphQLServer', () => { } } secondaryObject2: updateSecondaryObject( - input: { - id: $id2 - fields: { someField: "some value 22" } - } + input: { id: $id2, fields: { someField: "some value 22" } } ) { secondaryObject { id @@ -2654,10 +2507,7 @@ describe('ParseGraphQLServer', () => { } } secondaryObject3: updateSecondaryObject( - input: { - id: $id3 - fields: { someField: "some value 33" } - } + input: { id: $id3, fields: { someField: "some value 33" } } ) { secondaryObject { objectId @@ -2665,10 +2515,7 @@ describe('ParseGraphQLServer', () => { } } secondaryObject4: updateSecondaryObject( - input: { - id: $id4 - fields: { someField: "some value 44" } - } + input: { id: $id4, fields: { someField: "some value 44" } } ) { secondaryObject { id @@ -2676,20 +2523,14 @@ describe('ParseGraphQLServer', () => { } } secondaryObject5: updateSecondaryObject( - input: { - id: $id5 - fields: { someField: "some value 55" } - } + input: { id: $id5, fields: { someField: "some value 55" } } ) { secondaryObject { id } } secondaryObject6: updateSecondaryObject( - input: { - id: $id6 - fields: { someField: "some value 66" } - } + input: { id: $id6, fields: { someField: "some value 66" } } ) { secondaryObject { objectId @@ -2698,18 +2539,12 @@ describe('ParseGraphQLServer', () => { } `, variables: { - id1: createSecondaryObjectsResult.data.secondaryObject1 - .secondaryObject.id, - id2: createSecondaryObjectsResult.data.secondaryObject2 - .secondaryObject.id, - id3: createSecondaryObjectsResult.data.secondaryObject3 - .secondaryObject.objectId, - id4: createSecondaryObjectsResult.data.secondaryObject4 - .secondaryObject.objectId, - id5: createSecondaryObjectsResult.data.secondaryObject5 - .secondaryObject.id, - id6: createSecondaryObjectsResult.data.secondaryObject6 - .secondaryObject.objectId, + id1: createSecondaryObjectsResult.data.secondaryObject1.secondaryObject.id, + id2: createSecondaryObjectsResult.data.secondaryObject2.secondaryObject.id, + id3: createSecondaryObjectsResult.data.secondaryObject3.secondaryObject.objectId, + id4: createSecondaryObjectsResult.data.secondaryObject4.secondaryObject.objectId, + id5: createSecondaryObjectsResult.data.secondaryObject5.secondaryObject.id, + id6: createSecondaryObjectsResult.data.secondaryObject6.secondaryObject.objectId, }, context: { headers: { @@ -2720,39 +2555,26 @@ describe('ParseGraphQLServer', () => { const deleteSecondaryObjectsResult = await apolloClient.mutate({ mutation: gql` - mutation DeleteSecondaryObjects( - $id1: ID! - $id3: ID! - $id5: ID! - $id6: ID! - ) { - secondaryObject1: deleteSecondaryObject( - input: { id: $id1 } - ) { + mutation DeleteSecondaryObjects($id1: ID!, $id3: ID!, $id5: ID!, $id6: ID!) { + secondaryObject1: deleteSecondaryObject(input: { id: $id1 }) { secondaryObject { id objectId someField } } - secondaryObject3: deleteSecondaryObject( - input: { id: $id3 } - ) { + secondaryObject3: deleteSecondaryObject(input: { id: $id3 }) { secondaryObject { objectId someField } } - secondaryObject5: deleteSecondaryObject( - input: { id: $id5 } - ) { + secondaryObject5: deleteSecondaryObject(input: { id: $id5 }) { secondaryObject { id } } - secondaryObject6: deleteSecondaryObject( - input: { id: $id6 } - ) { + secondaryObject6: deleteSecondaryObject(input: { id: $id6 }) { secondaryObject { objectId } @@ -2760,14 +2582,10 @@ describe('ParseGraphQLServer', () => { } `, variables: { - id1: updateSecondaryObjectsResult.data.secondaryObject1 - .secondaryObject.id, - id3: updateSecondaryObjectsResult.data.secondaryObject3 - .secondaryObject.objectId, - id5: updateSecondaryObjectsResult.data.secondaryObject5 - .secondaryObject.id, - id6: updateSecondaryObjectsResult.data.secondaryObject6 - .secondaryObject.objectId, + id1: updateSecondaryObjectsResult.data.secondaryObject1.secondaryObject.id, + id3: updateSecondaryObjectsResult.data.secondaryObject3.secondaryObject.objectId, + id5: updateSecondaryObjectsResult.data.secondaryObject5.secondaryObject.id, + id6: updateSecondaryObjectsResult.data.secondaryObject6.secondaryObject.objectId, }, context: { headers: { @@ -2791,10 +2609,8 @@ describe('ParseGraphQLServer', () => { } `, variables: { - id2: updateSecondaryObjectsResult.data.secondaryObject2 - .secondaryObject.id, - id4: updateSecondaryObjectsResult.data.secondaryObject4 - .secondaryObject.objectId, + id2: updateSecondaryObjectsResult.data.secondaryObject2.secondaryObject.id, + id4: updateSecondaryObjectsResult.data.secondaryObject4.secondaryObject.objectId, }, context: { headers: { @@ -2819,12 +2635,7 @@ describe('ParseGraphQLServer', () => { { OR: [ { id: { equalTo: $id2 } } - { - AND: [ - { id: { equalTo: $id4 } } - { objectId: { equalTo: $id4 } } - ] - } + { AND: [{ id: { equalTo: $id4 } }, { objectId: { equalTo: $id4 } }] } ] } { id: { notEqualTo: $id1 } } @@ -2848,16 +2659,12 @@ describe('ParseGraphQLServer', () => { } `, variables: { - id1: deleteSecondaryObjectsResult.data.secondaryObject1 - .secondaryObject.objectId, + id1: deleteSecondaryObjectsResult.data.secondaryObject1.secondaryObject.objectId, id2: getSecondaryObjectsResult.data.secondaryObject2.id, - id3: deleteSecondaryObjectsResult.data.secondaryObject3 - .secondaryObject.objectId, + id3: deleteSecondaryObjectsResult.data.secondaryObject3.secondaryObject.objectId, id4: getSecondaryObjectsResult.data.secondaryObject4.objectId, - id5: deleteSecondaryObjectsResult.data.secondaryObject5 - .secondaryObject.id, - id6: deleteSecondaryObjectsResult.data.secondaryObject6 - .secondaryObject.objectId, + id5: deleteSecondaryObjectsResult.data.secondaryObject5.secondaryObject.id, + id6: deleteSecondaryObjectsResult.data.secondaryObject6.secondaryObject.objectId, }, context: { headers: { @@ -2866,9 +2673,7 @@ describe('ParseGraphQLServer', () => { }, }); - expect( - findSecondaryObjectsResult.data.secondaryObjects.count - ).toEqual(2); + expect(findSecondaryObjectsResult.data.secondaryObjects.count).toEqual(2); expect( findSecondaryObjectsResult.data.secondaryObjects.edges .map(value => value.node.someField) @@ -2885,22 +2690,16 @@ describe('ParseGraphQLServer', () => { getSecondaryObjectsResult.data.secondaryObject4.objectId, ]; expect( - findSecondaryObjectsResult.data.secondaryObjects.edges[0].node - .objectId - ).not.toBe( - findSecondaryObjectsResult.data.secondaryObjects.edges[1].node - .objectId - ); + findSecondaryObjectsResult.data.secondaryObjects.edges[0].node.objectId + ).not.toBe(findSecondaryObjectsResult.data.secondaryObjects.edges[1].node.objectId); expect( originalIds.includes( - findSecondaryObjectsResult.data.secondaryObjects.edges[0].node - .objectId + findSecondaryObjectsResult.data.secondaryObjects.edges[0].node.objectId ) ).toBeTrue(); expect( originalIds.includes( - findSecondaryObjectsResult.data.secondaryObjects.edges[1].node - .objectId + findSecondaryObjectsResult.data.secondaryObjects.edges[1].node.objectId ) ).toBeTrue(); @@ -2917,9 +2716,7 @@ describe('ParseGraphQLServer', () => { stringField: "some value" arrayField: [1, "abc", $pointer] pointerField: { link: $secondaryObject2 } - relationField: { - add: [$secondaryObject2, $secondaryObject4] - } + relationField: { add: [$secondaryObject2, $secondaryObject4] } } } ) { @@ -2956,13 +2753,10 @@ describe('ParseGraphQLServer', () => { pointer: { __type: 'Pointer', className: 'SecondaryObject', - objectId: - getSecondaryObjectsResult.data.secondaryObject4.objectId, + objectId: getSecondaryObjectsResult.data.secondaryObject4.objectId, }, - secondaryObject2: - getSecondaryObjectsResult.data.secondaryObject2.id, - secondaryObject4: - getSecondaryObjectsResult.data.secondaryObject4.objectId, + secondaryObject2: getSecondaryObjectsResult.data.secondaryObject2.id, + secondaryObject4: getSecondaryObjectsResult.data.secondaryObject4.objectId, }, context: { headers: { @@ -2983,9 +2777,7 @@ describe('ParseGraphQLServer', () => { id: $id fields: { pointerField: { link: $secondaryObject4 } - relationField: { - remove: [$secondaryObject2, $secondaryObject4] - } + relationField: { remove: [$secondaryObject2, $secondaryObject4] } } } ) { @@ -3019,12 +2811,9 @@ describe('ParseGraphQLServer', () => { } `, variables: { - id: createPrimaryObjectResult.data.createPrimaryObject - .primaryObject.id, - secondaryObject2: - getSecondaryObjectsResult.data.secondaryObject2.id, - secondaryObject4: - getSecondaryObjectsResult.data.secondaryObject4.objectId, + id: createPrimaryObjectResult.data.createPrimaryObject.primaryObject.id, + secondaryObject2: getSecondaryObjectsResult.data.secondaryObject2.id, + secondaryObject4: getSecondaryObjectsResult.data.secondaryObject4.objectId, }, context: { headers: { @@ -3034,20 +2823,18 @@ describe('ParseGraphQLServer', () => { }); expect( - createPrimaryObjectResult.data.createPrimaryObject.primaryObject - .stringField + createPrimaryObjectResult.data.createPrimaryObject.primaryObject.stringField ).toEqual('some value'); expect( - createPrimaryObjectResult.data.createPrimaryObject.primaryObject - .arrayField + createPrimaryObjectResult.data.createPrimaryObject.primaryObject.arrayField ).toEqual([ { __typename: 'Element', value: 1 }, { __typename: 'Element', value: 'abc' }, { __typename: 'SecondaryObject', someField: 'some value 44' }, ]); expect( - createPrimaryObjectResult.data.createPrimaryObject.primaryObject - .pointerField.someField + createPrimaryObjectResult.data.createPrimaryObject.primaryObject.pointerField + .someField ).toEqual('some value 22'); expect( createPrimaryObjectResult.data.createPrimaryObject.primaryObject.relationField.edges @@ -3055,24 +2842,21 @@ describe('ParseGraphQLServer', () => { .sort() ).toEqual(['some value 22', 'some value 44']); expect( - updatePrimaryObjectResult.data.updatePrimaryObject.primaryObject - .stringField + updatePrimaryObjectResult.data.updatePrimaryObject.primaryObject.stringField ).toEqual('some value'); expect( - updatePrimaryObjectResult.data.updatePrimaryObject.primaryObject - .arrayField + updatePrimaryObjectResult.data.updatePrimaryObject.primaryObject.arrayField ).toEqual([ { __typename: 'Element', value: 1 }, { __typename: 'Element', value: 'abc' }, { __typename: 'SecondaryObject', someField: 'some value 44' }, ]); expect( - updatePrimaryObjectResult.data.updatePrimaryObject.primaryObject - .pointerField.someField + updatePrimaryObjectResult.data.updatePrimaryObject.primaryObject.pointerField + .someField ).toEqual('some value 44'); expect( - updatePrimaryObjectResult.data.updatePrimaryObject.primaryObject - .relationField.edges + updatePrimaryObjectResult.data.updatePrimaryObject.primaryObject.relationField.edges ).toEqual([]); } catch (e) { handleError(e); @@ -3105,9 +2889,7 @@ describe('ParseGraphQLServer', () => { const result = await apolloClient.mutate({ mutation: gql` mutation { - class1: createClass( - input: { name: "Class1", clientMutationId: "cmid1" } - ) { + class1: createClass(input: { name: "Class1", clientMutationId: "cmid1" }) { clientMutationId class { name @@ -3118,11 +2900,7 @@ describe('ParseGraphQLServer', () => { } } class2: createClass( - input: { - name: "Class2" - schemaFields: null - clientMutationId: "cmid2" - } + input: { name: "Class2", schemaFields: null, clientMutationId: "cmid2" } ) { clientMutationId class { @@ -3134,11 +2912,7 @@ describe('ParseGraphQLServer', () => { } } class3: createClass( - input: { - name: "Class3" - schemaFields: {} - clientMutationId: "cmid3" - } + input: { name: "Class3", schemaFields: {}, clientMutationId: "cmid3" } ) { clientMutationId class { @@ -3311,8 +3085,8 @@ describe('ParseGraphQLServer', () => { clientMutationId: result.data[fieldName].clientMutationId, class: { name: result.data[fieldName].class.name, - schemaFields: result.data[fieldName].class.schemaFields.sort( - (a, b) => (a.name > b.name ? 1 : -1) + schemaFields: result.data[fieldName].class.schemaFields.sort((a, b) => + a.name > b.name ? 1 : -1 ), __typename: result.data[fieldName].class.__typename, }, @@ -3475,8 +3249,8 @@ describe('ParseGraphQLServer', () => { .filter(schemaClass => !schemaClass.name.startsWith('_')) .sort((a, b) => (a.name > b.name ? 1 : -1)); findResult.data.classes.forEach(schemaClass => { - schemaClass.schemaFields = schemaClass.schemaFields.sort( - (a, b) => (a.name > b.name ? 1 : -1) + schemaClass.schemaFields = schemaClass.schemaFields.sort((a, b) => + a.name > b.name ? 1 : -1 ); }); expect(findResult.data.classes).toEqual([ @@ -3601,12 +3375,8 @@ describe('ParseGraphQLServer', () => { }); fail('should fail'); } catch (e) { - expect(e.graphQLErrors[0].extensions.code).toEqual( - Parse.Error.OPERATION_FORBIDDEN - ); - expect(e.graphQLErrors[0].message).toEqual( - 'unauthorized: master key is required' - ); + expect(e.graphQLErrors[0].extensions.code).toEqual(Parse.Error.OPERATION_FORBIDDEN); + expect(e.graphQLErrors[0].message).toEqual('unauthorized: master key is required'); } }); @@ -3636,12 +3406,8 @@ describe('ParseGraphQLServer', () => { }); fail('should fail'); } catch (e) { - expect(e.graphQLErrors[0].extensions.code).toEqual( - Parse.Error.INVALID_KEY_NAME - ); - expect(e.graphQLErrors[0].message).toEqual( - 'Duplicated field name: someField' - ); + expect(e.graphQLErrors[0].extensions.code).toEqual(Parse.Error.INVALID_KEY_NAME); + expect(e.graphQLErrors[0].message).toEqual('Duplicated field name: someField'); } }); @@ -3766,13 +3532,9 @@ describe('ParseGraphQLServer', () => { }, }); result.data.createClass.class.schemaFields = - result.data.createClass.class.schemaFields.sort((a, b) => - a.name > b.name ? 1 : -1 - ); + result.data.createClass.class.schemaFields.sort((a, b) => (a.name > b.name ? 1 : -1)); result.data.updateClass.class.schemaFields = - result.data.updateClass.class.schemaFields.sort((a, b) => - a.name > b.name ? 1 : -1 - ); + result.data.updateClass.class.schemaFields.sort((a, b) => (a.name > b.name ? 1 : -1)); expect(result).toEqual({ data: { createClass: { @@ -3887,10 +3649,9 @@ describe('ParseGraphQLServer', () => { }, }, }); - getResult.data.class.schemaFields = - getResult.data.class.schemaFields.sort((a, b) => - a.name > b.name ? 1 : -1 - ); + getResult.data.class.schemaFields = getResult.data.class.schemaFields.sort((a, b) => + a.name > b.name ? 1 : -1 + ); expect(getResult.data).toEqual({ class: { name: 'MyNewClass', @@ -3982,12 +3743,8 @@ describe('ParseGraphQLServer', () => { }); fail('should fail'); } catch (e) { - expect(e.graphQLErrors[0].extensions.code).toEqual( - Parse.Error.OPERATION_FORBIDDEN - ); - expect(e.graphQLErrors[0].message).toEqual( - 'unauthorized: master key is required' - ); + expect(e.graphQLErrors[0].extensions.code).toEqual(Parse.Error.OPERATION_FORBIDDEN); + expect(e.graphQLErrors[0].message).toEqual('unauthorized: master key is required'); } }); @@ -4038,12 +3795,8 @@ describe('ParseGraphQLServer', () => { }); fail('should fail'); } catch (e) { - expect(e.graphQLErrors[0].extensions.code).toEqual( - Parse.Error.INVALID_KEY_NAME - ); - expect(e.graphQLErrors[0].message).toEqual( - 'Duplicated field name: someField' - ); + expect(e.graphQLErrors[0].extensions.code).toEqual(Parse.Error.INVALID_KEY_NAME); + expect(e.graphQLErrors[0].message).toEqual('Duplicated field name: someField'); } }); @@ -4070,12 +3823,8 @@ describe('ParseGraphQLServer', () => { }); fail('should fail'); } catch (e) { - expect(e.graphQLErrors[0].extensions.code).toEqual( - Parse.Error.INVALID_CLASS_NAME - ); - expect(e.graphQLErrors[0].message).toEqual( - 'Class SomeInexistentClass does not exist.' - ); + expect(e.graphQLErrors[0].extensions.code).toEqual(Parse.Error.INVALID_CLASS_NAME); + expect(e.graphQLErrors[0].message).toEqual('Class SomeInexistentClass does not exist.'); } }); @@ -4117,13 +3866,9 @@ describe('ParseGraphQLServer', () => { }, }); result.data.createClass.class.schemaFields = - result.data.createClass.class.schemaFields.sort((a, b) => - a.name > b.name ? 1 : -1 - ); + result.data.createClass.class.schemaFields.sort((a, b) => (a.name > b.name ? 1 : -1)); result.data.deleteClass.class.schemaFields = - result.data.deleteClass.class.schemaFields.sort((a, b) => - a.name > b.name ? 1 : -1 - ); + result.data.deleteClass.class.schemaFields.sort((a, b) => (a.name > b.name ? 1 : -1)); expect(result).toEqual({ data: { createClass: { @@ -4181,12 +3926,8 @@ describe('ParseGraphQLServer', () => { }); fail('should fail'); } catch (e) { - expect(e.graphQLErrors[0].extensions.code).toEqual( - Parse.Error.INVALID_CLASS_NAME - ); - expect(e.graphQLErrors[0].message).toEqual( - 'Class MyNewClass does not exist.' - ); + expect(e.graphQLErrors[0].extensions.code).toEqual(Parse.Error.INVALID_CLASS_NAME); + expect(e.graphQLErrors[0].message).toEqual('Class MyNewClass does not exist.'); } } catch (e) { handleError(e); @@ -4225,12 +3966,8 @@ describe('ParseGraphQLServer', () => { }); fail('should fail'); } catch (e) { - expect(e.graphQLErrors[0].extensions.code).toEqual( - Parse.Error.OPERATION_FORBIDDEN - ); - expect(e.graphQLErrors[0].message).toEqual( - 'unauthorized: master key is required' - ); + expect(e.graphQLErrors[0].extensions.code).toEqual(Parse.Error.OPERATION_FORBIDDEN); + expect(e.graphQLErrors[0].message).toEqual('unauthorized: master key is required'); } }); @@ -4252,12 +3989,8 @@ describe('ParseGraphQLServer', () => { }); fail('should fail'); } catch (e) { - expect(e.graphQLErrors[0].extensions.code).toEqual( - Parse.Error.INVALID_CLASS_NAME - ); - expect(e.graphQLErrors[0].message).toEqual( - 'Class SomeInexistentClass does not exist.' - ); + expect(e.graphQLErrors[0].extensions.code).toEqual(Parse.Error.INVALID_CLASS_NAME); + expect(e.graphQLErrors[0].message).toEqual('Class SomeInexistentClass does not exist.'); } }); @@ -4274,12 +4007,8 @@ describe('ParseGraphQLServer', () => { }); fail('should fail'); } catch (e) { - expect(e.graphQLErrors[0].extensions.code).toEqual( - Parse.Error.OPERATION_FORBIDDEN - ); - expect(e.graphQLErrors[0].message).toEqual( - 'unauthorized: master key is required' - ); + expect(e.graphQLErrors[0].extensions.code).toEqual(Parse.Error.OPERATION_FORBIDDEN); + expect(e.graphQLErrors[0].message).toEqual('unauthorized: master key is required'); } }); @@ -4296,12 +4025,8 @@ describe('ParseGraphQLServer', () => { }); fail('should fail'); } catch (e) { - expect(e.graphQLErrors[0].extensions.code).toEqual( - Parse.Error.OPERATION_FORBIDDEN - ); - expect(e.graphQLErrors[0].message).toEqual( - 'unauthorized: master key is required' - ); + expect(e.graphQLErrors[0].extensions.code).toEqual(Parse.Error.OPERATION_FORBIDDEN); + expect(e.graphQLErrors[0].message).toEqual('unauthorized: master key is required'); } }); }); @@ -4340,82 +4065,69 @@ describe('ParseGraphQLServer', () => { expect(new Date(result.updatedAt)).toEqual(obj.updatedAt); }); - it_only_db('mongo')( - 'should return child objects in array fields', - async () => { - const obj1 = new Parse.Object('Customer'); - const obj2 = new Parse.Object('SomeClass'); - const obj3 = new Parse.Object('Customer'); + it_only_db('mongo')('should return child objects in array fields', async () => { + const obj1 = new Parse.Object('Customer'); + const obj2 = new Parse.Object('SomeClass'); + const obj3 = new Parse.Object('Customer'); - obj1.set('someCustomerField', 'imCustomerOne'); - const arrayField = [42.42, 42, 'string', true]; - obj1.set('arrayField', arrayField); - await obj1.save(); + obj1.set('someCustomerField', 'imCustomerOne'); + const arrayField = [42.42, 42, 'string', true]; + obj1.set('arrayField', arrayField); + await obj1.save(); - obj2.set('someClassField', 'imSomeClassTwo'); - await obj2.save(); + obj2.set('someClassField', 'imSomeClassTwo'); + await obj2.save(); - obj3.set('manyRelations', [obj1, obj2]); - await obj3.save(); + obj3.set('manyRelations', [obj1, obj2]); + await obj3.save(); - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const result = ( - await apolloClient.query({ - query: gql` - query GetCustomer($id: ID!) { - customer(id: $id) { - objectId - manyRelations { - ... on Customer { - objectId - someCustomerField - arrayField { - ... on Element { - value - } + const result = ( + await apolloClient.query({ + query: gql` + query GetCustomer($id: ID!) { + customer(id: $id) { + objectId + manyRelations { + ... on Customer { + objectId + someCustomerField + arrayField { + ... on Element { + value } } - ... on SomeClass { - objectId - someClassField - } } - createdAt - updatedAt + ... on SomeClass { + objectId + someClassField + } } + createdAt + updatedAt } - `, - variables: { - id: obj3.id, - }, - }) - ).data.customer; + } + `, + variables: { + id: obj3.id, + }, + }) + ).data.customer; - expect(result.objectId).toEqual(obj3.id); - expect(result.manyRelations.length).toEqual(2); + expect(result.objectId).toEqual(obj3.id); + expect(result.manyRelations.length).toEqual(2); - const customerSubObject = result.manyRelations.find( - o => o.objectId === obj1.id - ); - const someClassSubObject = result.manyRelations.find( - o => o.objectId === obj2.id - ); + const customerSubObject = result.manyRelations.find(o => o.objectId === obj1.id); + const someClassSubObject = result.manyRelations.find(o => o.objectId === obj2.id); - expect(customerSubObject).toBeDefined(); - expect(someClassSubObject).toBeDefined(); - expect(customerSubObject.someCustomerField).toEqual( - 'imCustomerOne' - ); - const formatedArrayField = customerSubObject.arrayField.map( - elem => elem.value - ); - expect(formatedArrayField).toEqual(arrayField); - expect(someClassSubObject.someClassField).toEqual( - 'imSomeClassTwo' - ); - } - ); + expect(customerSubObject).toBeDefined(); + expect(someClassSubObject).toBeDefined(); + expect(customerSubObject.someCustomerField).toEqual('imCustomerOne'); + const formatedArrayField = customerSubObject.arrayField.map(elem => elem.value); + expect(formatedArrayField).toEqual(arrayField); + expect(someClassSubObject.someClassField).toEqual('imSomeClassTwo'); + }); it('should return many child objects in allow cyclic query', async () => { const obj1 = new Parse.Object('Employee'); @@ -4535,8 +4247,7 @@ describe('ParseGraphQLServer', () => { await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); async function getObject(className, id, headers) { - const alias = - className.charAt(0).toLowerCase() + className.slice(1); + const alias = className.charAt(0).toLowerCase() + className.slice(1); const specificQueryResult = await apolloClient.query({ query: gql` query GetSomeObject($id: ID!) { @@ -4562,15 +4273,14 @@ describe('ParseGraphQLServer', () => { objects .slice(0, 3) .map(obj => - expectAsync( - getObject(obj.className, obj.id) - ).toBeRejectedWith(jasmine.stringMatching('Object not found')) + expectAsync(getObject(obj.className, obj.id)).toBeRejectedWith( + jasmine.stringMatching('Object not found') + ) ) ); - expect( - (await getObject(object4.className, object4.id)).data.get - .someField - ).toEqual('someValue4'); + expect((await getObject(object4.className, object4.id)).data.get.someField).toEqual( + 'someValue4' + ); await Promise.all( objects.map(async obj => expect( @@ -4757,9 +4467,7 @@ describe('ParseGraphQLServer', () => { }); expect(result1.data.get.pointerToUser.username).toBeUndefined(); - expect( - result2.data.graphQLClass.pointerToUser.username - ).toBeDefined(); + expect(result2.data.graphQLClass.pointerToUser.username).toBeDefined(); }); it('should respect protectedFields', async done => { @@ -4872,22 +4580,12 @@ describe('ParseGraphQLServer', () => { let foundGraphQLClassReadPreference = false; let foundUserClassReadPreference = false; Collection.prototype.find.calls.all().forEach(call => { - if ( - call.object.s.namespace.collection.indexOf( - 'GraphQLClass' - ) >= 0 - ) { + if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) { foundGraphQLClassReadPreference = true; - expect(call.object.s.readPreference.mode).toBe( - ReadPreference.PRIMARY - ); - } else if ( - call.object.s.namespace.collection.indexOf('_User') >= 0 - ) { + expect(call.object.s.readPreference.mode).toBe(ReadPreference.PRIMARY); + } else if (call.object.s.namespace.collection.indexOf('_User') >= 0) { foundUserClassReadPreference = true; - expect(call.object.s.readPreference.mode).toBe( - ReadPreference.PRIMARY - ); + expect(call.object.s.readPreference.mode).toBe(ReadPreference.PRIMARY); } }); @@ -4908,10 +4606,7 @@ describe('ParseGraphQLServer', () => { await apolloClient.query({ query: gql` query GetSomeObject($id: ID!) { - graphQLClass( - id: $id - options: { readPreference: SECONDARY } - ) { + graphQLClass(id: $id, options: { readPreference: SECONDARY }) { pointerToUser { username } @@ -4931,21 +4626,12 @@ describe('ParseGraphQLServer', () => { let foundGraphQLClassReadPreference = false; let foundUserClassReadPreference = false; Collection.prototype.find.calls.all().forEach(call => { - if ( - call.object.s.namespace.collection.indexOf('GraphQLClass') >= - 0 - ) { + if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) { foundGraphQLClassReadPreference = true; - expect(call.args[1].readPreference).toBe( - ReadPreference.SECONDARY - ); - } else if ( - call.object.s.namespace.collection.indexOf('_User') >= 0 - ) { + expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY); + } else if (call.object.s.namespace.collection.indexOf('_User') >= 0) { foundUserClassReadPreference = true; - expect(call.args[1].readPreference).toBe( - ReadPreference.SECONDARY - ); + expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY); } }); @@ -4965,10 +4651,7 @@ describe('ParseGraphQLServer', () => { query GetSomeObject($id: ID!) { graphQLClass( id: $id - options: { - readPreference: SECONDARY - includeReadPreference: NEAREST - } + options: { readPreference: SECONDARY, includeReadPreference: NEAREST } ) { pointerToUser { username @@ -4989,21 +4672,12 @@ describe('ParseGraphQLServer', () => { let foundGraphQLClassReadPreference = false; let foundUserClassReadPreference = false; Collection.prototype.find.calls.all().forEach(call => { - if ( - call.object.s.namespace.collection.indexOf('GraphQLClass') >= - 0 - ) { + if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) { foundGraphQLClassReadPreference = true; - expect(call.args[1].readPreference).toBe( - ReadPreference.SECONDARY - ); - } else if ( - call.object.s.namespace.collection.indexOf('_User') >= 0 - ) { + expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY); + } else if (call.object.s.namespace.collection.indexOf('_User') >= 0) { foundUserClassReadPreference = true; - expect(call.args[1].readPreference).toBe( - ReadPreference.NEAREST - ); + expect(call.args[1].readPreference).toBe(ReadPreference.NEAREST); } }); @@ -5206,9 +4880,7 @@ describe('ParseGraphQLServer', () => { }); expect( - result.data.graphQLClasses.edges - .map(object => object.node.someField) - .sort() + result.data.graphQLClasses.edges.map(object => object.node.someField).sort() ).toEqual(['someValue1', 'someValue3']); }); @@ -5284,9 +4956,7 @@ describe('ParseGraphQLServer', () => { }); expect( - result.data.graphQLClasses.edges - .map(object => object.node.someField) - .sort() + result.data.graphQLClasses.edges.map(object => object.node.someField).sort() ).toEqual(['someValue1', 'someValue2']); }); @@ -5303,9 +4973,7 @@ describe('ParseGraphQLServer', () => { const result = await apolloClient.query({ query: gql` - query FullTextSearchTests( - $where: FullTextSearchTestWhereInput - ) { + query FullTextSearchTests($where: FullTextSearchTestWhereInput) { fullTextSearchTests(where: $where) { edges { node { @@ -5333,9 +5001,7 @@ describe('ParseGraphQLServer', () => { }, }); - expect( - result.data.fullTextSearchTests.edges[0].node.objectId - ).toEqual(obj.id); + expect(result.data.fullTextSearchTests.edges[0].node.objectId).toEqual(obj.id); } catch (e) { handleError(e); } @@ -5430,12 +5096,7 @@ describe('ParseGraphQLServer', () => { $skip: Int $first: Int ) { - find: someClasses( - where: $where - order: $order - skip: $skip - first: $first - ) { + find: someClasses(where: $where, order: $order, skip: $skip, first: $first) { edges { node { someField @@ -5456,9 +5117,10 @@ describe('ParseGraphQLServer', () => { }, }); - expect( - result.data.find.edges.map(obj => obj.node.someField) - ).toEqual(['someValue14', 'someValue17']); + expect(result.data.find.edges.map(obj => obj.node.someField)).toEqual([ + 'someValue14', + 'someValue17', + ]); } ); @@ -5483,13 +5145,7 @@ describe('ParseGraphQLServer', () => { await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const find = async ({ - skip, - after, - first, - before, - last, - } = {}) => { + const find = async ({ skip, after, first, before, last } = {}) => { return await apolloClient.query({ query: gql` query FindSomeObjects( @@ -5536,163 +5192,137 @@ describe('ParseGraphQLServer', () => { }; let result = await find(); - expect( - result.data.someClasses.edges.map(edge => edge.node.numberField) - ).toEqual(numberArray(0, 99)); - expect(result.data.someClasses.count).toEqual(100); - expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual( - false + expect(result.data.someClasses.edges.map(edge => edge.node.numberField)).toEqual( + numberArray(0, 99) ); + expect(result.data.someClasses.count).toEqual(100); + expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual(false); expect(result.data.someClasses.pageInfo.startCursor).toEqual( result.data.someClasses.edges[0].cursor ); expect(result.data.someClasses.pageInfo.endCursor).toEqual( result.data.someClasses.edges[99].cursor ); - expect(result.data.someClasses.pageInfo.hasNextPage).toEqual( - false - ); + expect(result.data.someClasses.pageInfo.hasNextPage).toEqual(false); result = await find({ first: 10 }); - expect( - result.data.someClasses.edges.map(edge => edge.node.numberField) - ).toEqual(numberArray(0, 9)); - expect(result.data.someClasses.count).toEqual(100); - expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual( - false + expect(result.data.someClasses.edges.map(edge => edge.node.numberField)).toEqual( + numberArray(0, 9) ); + expect(result.data.someClasses.count).toEqual(100); + expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual(false); expect(result.data.someClasses.pageInfo.startCursor).toEqual( result.data.someClasses.edges[0].cursor ); expect(result.data.someClasses.pageInfo.endCursor).toEqual( result.data.someClasses.edges[9].cursor ); - expect(result.data.someClasses.pageInfo.hasNextPage).toEqual( - true - ); + expect(result.data.someClasses.pageInfo.hasNextPage).toEqual(true); result = await find({ first: 10, after: result.data.someClasses.pageInfo.endCursor, }); - expect( - result.data.someClasses.edges.map(edge => edge.node.numberField) - ).toEqual(numberArray(10, 19)); - expect(result.data.someClasses.count).toEqual(100); - expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual( - true + expect(result.data.someClasses.edges.map(edge => edge.node.numberField)).toEqual( + numberArray(10, 19) ); + expect(result.data.someClasses.count).toEqual(100); + expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual(true); expect(result.data.someClasses.pageInfo.startCursor).toEqual( result.data.someClasses.edges[0].cursor ); expect(result.data.someClasses.pageInfo.endCursor).toEqual( result.data.someClasses.edges[9].cursor ); - expect(result.data.someClasses.pageInfo.hasNextPage).toEqual( - true - ); + expect(result.data.someClasses.pageInfo.hasNextPage).toEqual(true); result = await find({ last: 10 }); - expect( - result.data.someClasses.edges.map(edge => edge.node.numberField) - ).toEqual(numberArray(90, 99)); - expect(result.data.someClasses.count).toEqual(100); - expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual( - true + expect(result.data.someClasses.edges.map(edge => edge.node.numberField)).toEqual( + numberArray(90, 99) ); + expect(result.data.someClasses.count).toEqual(100); + expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual(true); expect(result.data.someClasses.pageInfo.startCursor).toEqual( result.data.someClasses.edges[0].cursor ); expect(result.data.someClasses.pageInfo.endCursor).toEqual( result.data.someClasses.edges[9].cursor ); - expect(result.data.someClasses.pageInfo.hasNextPage).toEqual( - false - ); + expect(result.data.someClasses.pageInfo.hasNextPage).toEqual(false); result = await find({ last: 10, before: result.data.someClasses.pageInfo.startCursor, }); - expect( - result.data.someClasses.edges.map(edge => edge.node.numberField) - ).toEqual(numberArray(80, 89)); - expect(result.data.someClasses.count).toEqual(100); - expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual( - true + expect(result.data.someClasses.edges.map(edge => edge.node.numberField)).toEqual( + numberArray(80, 89) ); + expect(result.data.someClasses.count).toEqual(100); + expect(result.data.someClasses.pageInfo.hasPreviousPage).toEqual(true); expect(result.data.someClasses.pageInfo.startCursor).toEqual( result.data.someClasses.edges[0].cursor ); expect(result.data.someClasses.pageInfo.endCursor).toEqual( result.data.someClasses.edges[9].cursor ); - expect(result.data.someClasses.pageInfo.hasNextPage).toEqual( - true - ); + expect(result.data.someClasses.pageInfo.hasNextPage).toEqual(true); } ); - it_id('4f6a5f20-9642-4cf0-b31d-e739672a9096')(it)( - 'should support count', - async () => { - await prepareData(); + it_id('4f6a5f20-9642-4cf0-b31d-e739672a9096')(it)('should support count', async () => { + await prepareData(); - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const where = { - someField: { - in: ['someValue1', 'someValue2', 'someValue3'], - }, - OR: [ - { - pointerToUser: { - have: { - objectId: { - equalTo: user5.id, - }, + const where = { + someField: { + in: ['someValue1', 'someValue2', 'someValue3'], + }, + OR: [ + { + pointerToUser: { + have: { + objectId: { + equalTo: user5.id, }, }, }, - { - id: { - equalTo: object1.id, - }, + }, + { + id: { + equalTo: object1.id, }, - ], - }; + }, + ], + }; - const result = await apolloClient.query({ - query: gql` - query FindSomeObjects( - $where: GraphQLClassWhereInput - $first: Int - ) { - find: graphQLClasses(where: $where, first: $first) { - edges { - node { - id - } + const result = await apolloClient.query({ + query: gql` + query FindSomeObjects($where: GraphQLClassWhereInput, $first: Int) { + find: graphQLClasses(where: $where, first: $first) { + edges { + node { + id } - count } + count } - `, - variables: { - where, - first: 0, - }, - context: { - headers: { - 'X-Parse-Master-Key': 'test', - }, + } + `, + variables: { + where, + first: 0, + }, + context: { + headers: { + 'X-Parse-Master-Key': 'test', }, - }); + }, + }); - expect(result.data.find.edges).toEqual([]); - expect(result.data.find.count).toEqual(2); - } - ); + expect(result.data.find.edges).toEqual([]); + expect(result.data.find.count).toEqual(2); + }); it('should only count', async () => { await prepareData(); @@ -5761,10 +5391,7 @@ describe('ParseGraphQLServer', () => { const result = await apolloClient.query({ query: gql` query FindSomeObjects($limit: Int) { - find: someClasses( - where: { id: { exists: true } } - first: $limit - ) { + find: someClasses(where: { id: { exists: true } }, first: $limit) { edges { node { id @@ -5848,13 +5475,9 @@ describe('ParseGraphQLServer', () => { }); expect(result1.data.find.edges[0].node.someField).toBeDefined(); - expect( - result1.data.find.edges[0].node.pointerToUser - ).toBeUndefined(); + expect(result1.data.find.edges[0].node.pointerToUser).toBeUndefined(); expect(result2.data.find.edges[0].node.someField).toBeDefined(); - expect( - result2.data.find.edges[0].node.pointerToUser - ).toBeDefined(); + expect(result2.data.find.edges[0].node.pointerToUser).toBeDefined(); } ); @@ -5916,12 +5539,8 @@ describe('ParseGraphQLServer', () => { }, }, }); - expect( - result1.data.find.edges[0].node.pointerToUser.username - ).toBeUndefined(); - expect( - result2.data.find.edges[0].node.pointerToUser.username - ).toBeDefined(); + expect(result1.data.find.edges[0].node.pointerToUser.username).toBeUndefined(); + expect(result2.data.find.edges[0].node.pointerToUser.username).toBeDefined(); }); describe_only_db('mongo')('read preferences', () => { @@ -5956,21 +5575,12 @@ describe('ParseGraphQLServer', () => { let foundGraphQLClassReadPreference = false; let foundUserClassReadPreference = false; Collection.prototype.find.calls.all().forEach(call => { - if ( - call.object.s.namespace.collection.indexOf('GraphQLClass') >= - 0 - ) { + if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) { foundGraphQLClassReadPreference = true; - expect(call.object.s.readPreference.mode).toBe( - ReadPreference.PRIMARY - ); - } else if ( - call.object.s.namespace.collection.indexOf('_User') >= 0 - ) { + expect(call.object.s.readPreference.mode).toBe(ReadPreference.PRIMARY); + } else if (call.object.s.namespace.collection.indexOf('_User') >= 0) { foundUserClassReadPreference = true; - expect(call.object.s.readPreference.mode).toBe( - ReadPreference.PRIMARY - ); + expect(call.object.s.readPreference.mode).toBe(ReadPreference.PRIMARY); } }); @@ -5988,9 +5598,7 @@ describe('ParseGraphQLServer', () => { await apolloClient.query({ query: gql` query FindSomeObjects { - find: graphQLClasses( - options: { readPreference: SECONDARY } - ) { + find: graphQLClasses(options: { readPreference: SECONDARY }) { edges { node { pointerToUser { @@ -6011,21 +5619,12 @@ describe('ParseGraphQLServer', () => { let foundGraphQLClassReadPreference = false; let foundUserClassReadPreference = false; Collection.prototype.find.calls.all().forEach(call => { - if ( - call.object.s.namespace.collection.indexOf('GraphQLClass') >= - 0 - ) { + if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) { foundGraphQLClassReadPreference = true; - expect(call.args[1].readPreference).toBe( - ReadPreference.SECONDARY - ); - } else if ( - call.object.s.namespace.collection.indexOf('_User') >= 0 - ) { + expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY); + } else if (call.object.s.namespace.collection.indexOf('_User') >= 0) { foundUserClassReadPreference = true; - expect(call.args[1].readPreference).toBe( - ReadPreference.SECONDARY - ); + expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY); } }); @@ -6044,10 +5643,7 @@ describe('ParseGraphQLServer', () => { query: gql` query FindSomeObjects { graphQLClasses( - options: { - readPreference: SECONDARY - includeReadPreference: NEAREST - } + options: { readPreference: SECONDARY, includeReadPreference: NEAREST } ) { edges { node { @@ -6069,21 +5665,12 @@ describe('ParseGraphQLServer', () => { let foundGraphQLClassReadPreference = false; let foundUserClassReadPreference = false; Collection.prototype.find.calls.all().forEach(call => { - if ( - call.object.s.namespace.collection.indexOf('GraphQLClass') >= - 0 - ) { + if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) { foundGraphQLClassReadPreference = true; - expect(call.args[1].readPreference).toBe( - ReadPreference.SECONDARY - ); - } else if ( - call.object.s.namespace.collection.indexOf('_User') >= 0 - ) { + expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY); + } else if (call.object.s.namespace.collection.indexOf('_User') >= 0) { foundUserClassReadPreference = true; - expect(call.args[1].readPreference).toBe( - ReadPreference.NEAREST - ); + expect(call.args[1].readPreference).toBe(ReadPreference.NEAREST); } }); @@ -6104,10 +5691,7 @@ describe('ParseGraphQLServer', () => { query FindSomeObjects($where: GraphQLClassWhereInput) { find: graphQLClasses( where: $where - options: { - readPreference: SECONDARY - subqueryReadPreference: NEAREST - } + options: { readPreference: SECONDARY, subqueryReadPreference: NEAREST } ) { edges { node { @@ -6138,22 +5722,12 @@ describe('ParseGraphQLServer', () => { let foundGraphQLClassReadPreference = false; let foundUserClassReadPreference = false; Collection.prototype.find.calls.all().forEach(call => { - if ( - call.object.s.namespace.collection.indexOf( - 'GraphQLClass' - ) >= 0 - ) { + if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) { foundGraphQLClassReadPreference = true; - expect(call.args[1].readPreference).toBe( - ReadPreference.SECONDARY - ); - } else if ( - call.object.s.namespace.collection.indexOf('_User') >= 0 - ) { + expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY); + } else if (call.object.s.namespace.collection.indexOf('_User') >= 0) { foundUserClassReadPreference = true; - expect(call.args[1].readPreference).toBe( - ReadPreference.NEAREST - ); + expect(call.args[1].readPreference).toBe(ReadPreference.NEAREST); } }); @@ -6197,65 +5771,59 @@ describe('ParseGraphQLServer', () => { handleError(e); } - expect( - result.data.graphQLClasses.edges.map(edge => edge.node.objectId) - ).toEqual([object3.id, object1.id, object2.id]); + expect(result.data.graphQLClasses.edges.map(edge => edge.node.objectId)).toEqual([ + object3.id, + object1.id, + object2.id, + ]); }); - it_only_db('mongo')( - 'should order by multiple fields on a relation field', - async () => { - await prepareData(); + it_only_db('mongo')('should order by multiple fields on a relation field', async () => { + await prepareData(); - const parentObject = new Parse.Object('ParentClass'); - const relation = parentObject.relation('graphQLClasses'); - relation.add(object1); - relation.add(object2); - relation.add(object3); - await parentObject.save(); + const parentObject = new Parse.Object('ParentClass'); + const relation = parentObject.relation('graphQLClasses'); + relation.add(object1); + relation.add(object2); + relation.add(object3); + await parentObject.save(); - await resetGraphQLCache(); + await resetGraphQLCache(); - let result; - try { - result = await apolloClient.query({ - query: gql` - query OrderByMultipleFieldsOnRelation( - $id: ID! - $order: [GraphQLClassOrder!] - ) { - parentClass(id: $id) { - graphQLClasses(order: $order) { - edges { - node { - objectId - } + let result; + try { + result = await apolloClient.query({ + query: gql` + query OrderByMultipleFieldsOnRelation($id: ID!, $order: [GraphQLClassOrder!]) { + parentClass(id: $id) { + graphQLClasses(order: $order) { + edges { + node { + objectId } } } } - `, - variables: { - id: parentObject.id, - order: ['someOtherField_DESC', 'someField_ASC'], - }, - context: { - headers: { - 'X-Parse-Master-Key': 'test', - }, + } + `, + variables: { + id: parentObject.id, + order: ['someOtherField_DESC', 'someField_ASC'], + }, + context: { + headers: { + 'X-Parse-Master-Key': 'test', }, - }); - } catch (e) { - handleError(e); - } - - expect( - result.data.parentClass.graphQLClasses.edges.map( - edge => edge.node.objectId - ) - ).toEqual([object3.id, object1.id, object2.id]); + }, + }); + } catch (e) { + handleError(e); } - ); + + expect( + result.data.parentClass.graphQLClasses.edges.map(edge => edge.node.objectId) + ).toEqual([object3.id, object1.id, object2.id]); + }); it_id('47a6adf3-1cb4-4d92-b74c-e480363f9cb5')(it)( 'should support including relation', @@ -6316,9 +5884,7 @@ describe('ParseGraphQLServer', () => { expect(result1.data.roles.edges[0].node.roles).toBeUndefined(); expect(result2.data.roles.edges[0].node.name).toBeDefined(); expect(result2.data.roles.edges[0].node.users).toBeDefined(); - expect( - result2.data.roles.edges[0].node.users.edges[0].node.username - ).toBeDefined(); + expect(result2.data.roles.edges[0].node.users.edges[0].node.username).toBeDefined(); expect(result2.data.roles.edges[0].node.roles).toBeUndefined(); } ); @@ -6359,13 +5925,9 @@ describe('ParseGraphQLServer', () => { }, }); - expect(result.data.createCustomer.clientMutationId).toEqual( - clientMutationId - ); + expect(result.data.createCustomer.clientMutationId).toEqual(clientMutationId); expect(result.data.createCustomer.customer.id).toBeDefined(); - expect(result.data.createCustomer.customer.someField).toEqual( - 'someValue' - ); + expect(result.data.createCustomer.customer.someField).toEqual('someValue'); const customer = await new Parse.Query('Customer').get( result.data.createCustomer.customer.objectId @@ -6383,8 +5945,7 @@ describe('ParseGraphQLServer', () => { await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); async function createObject(className, headers) { - const getClassName = - className.charAt(0).toLowerCase() + className.slice(1); + const getClassName = className.charAt(0).toLowerCase() + className.slice(1); const result = await apolloClient.mutate({ mutation: gql` mutation CreateSomeObject { @@ -6401,8 +5962,7 @@ describe('ParseGraphQLServer', () => { }, }); - const specificCreate = - result.data[`create${className}`][getClassName]; + const specificCreate = result.data[`create${className}`][getClassName]; expect(specificCreate.id).toBeDefined(); expect(specificCreate.createdAt).toBeDefined(); @@ -6410,9 +5970,7 @@ describe('ParseGraphQLServer', () => { } await expectAsync(createObject('GraphQLClass')).toBeRejectedWith( - jasmine.stringMatching( - 'Permission denied for action create on class GraphQLClass' - ) + jasmine.stringMatching('Permission denied for action create on class GraphQLClass') ); await expectAsync(createObject('PublicClass')).toBeResolved(); await expectAsync( @@ -6446,9 +6004,7 @@ describe('ParseGraphQLServer', () => { 'X-Parse-Session-Token': user4.getSessionToken(), }) ).toBeRejectedWith( - jasmine.stringMatching( - 'Permission denied for action create on class GraphQLClass' - ) + jasmine.stringMatching('Permission denied for action create on class GraphQLClass') ); await expectAsync( createObject('PublicClass', { @@ -6492,16 +6048,10 @@ describe('ParseGraphQLServer', () => { }, }); - expect(result.data.updateCustomer.clientMutationId).toEqual( - clientMutationId - ); + expect(result.data.updateCustomer.clientMutationId).toEqual(clientMutationId); expect(result.data.updateCustomer.customer.updatedAt).toBeDefined(); - expect(result.data.updateCustomer.customer.someField1).toEqual( - 'someField1Value2' - ); - expect(result.data.updateCustomer.customer.someField2).toEqual( - 'someField2Value1' - ); + expect(result.data.updateCustomer.customer.someField1).toEqual('someField1Value2'); + expect(result.data.updateCustomer.customer.someField2).toEqual('someField2Value1'); await obj.fetch(); @@ -6519,10 +6069,7 @@ describe('ParseGraphQLServer', () => { const result = await apolloClient.mutate({ mutation: gql` - mutation UpdateCustomer( - $id: ID! - $fields: UpdateCustomerFieldsInput - ) { + mutation UpdateCustomer($id: ID!, $fields: UpdateCustomerFieldsInput) { updateCustomer(input: { id: $id, fields: $fields }) { customer { id @@ -6539,9 +6086,7 @@ describe('ParseGraphQLServer', () => { }, }); - expect(result.data.updateCustomer.customer.objectId).toEqual( - obj.id - ); + expect(result.data.updateCustomer.customer.objectId).toEqual(obj.id); await obj.fetch(); @@ -6750,8 +6295,7 @@ describe('ParseGraphQLServer', () => { await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); function updateObject(className, id, fields, headers) { - const mutationName = - className.charAt(0).toLowerCase() + className.slice(1); + const mutationName = className.charAt(0).toLowerCase() + className.slice(1); return apolloClient.mutate({ mutation: gql` @@ -6797,8 +6341,7 @@ describe('ParseGraphQLServer', () => { someField: 'changedValue1', }) ).data[`update${object4.className}`][ - object4.className.charAt(0).toLowerCase() + - object4.className.slice(1) + object4.className.charAt(0).toLowerCase() + object4.className.slice(1) ].updatedAt ).toBeDefined(); await object4.fetch({ useMasterKey: true }); @@ -6814,8 +6357,7 @@ describe('ParseGraphQLServer', () => { { 'X-Parse-Master-Key': 'test' } ) ).data[`update${obj.className}`][ - obj.className.charAt(0).toLowerCase() + - obj.className.slice(1) + obj.className.charAt(0).toLowerCase() + obj.className.slice(1) ].updatedAt ).toBeDefined(); await obj.fetch({ useMasterKey: true }); @@ -6833,8 +6375,7 @@ describe('ParseGraphQLServer', () => { { 'X-Parse-Session-Token': user1.getSessionToken() } ) ).data[`update${obj.className}`][ - obj.className.charAt(0).toLowerCase() + - obj.className.slice(1) + obj.className.charAt(0).toLowerCase() + obj.className.slice(1) ].updatedAt ).toBeDefined(); await obj.fetch({ useMasterKey: true }); @@ -6852,8 +6393,7 @@ describe('ParseGraphQLServer', () => { { 'X-Parse-Session-Token': user2.getSessionToken() } ) ).data[`update${obj.className}`][ - obj.className.charAt(0).toLowerCase() + - obj.className.slice(1) + obj.className.charAt(0).toLowerCase() + obj.className.slice(1) ].updatedAt ).toBeDefined(); await obj.fetch({ useMasterKey: true }); @@ -6871,8 +6411,7 @@ describe('ParseGraphQLServer', () => { { 'X-Parse-Session-Token': user3.getSessionToken() } ) ).data[`update${obj.className}`][ - obj.className.charAt(0).toLowerCase() + - obj.className.slice(1) + obj.className.charAt(0).toLowerCase() + obj.className.slice(1) ].updatedAt ).toBeDefined(); await obj.fetch({ useMasterKey: true }); @@ -6914,8 +6453,7 @@ describe('ParseGraphQLServer', () => { { 'X-Parse-Session-Token': user4.getSessionToken() } ) ).data[`update${object4.className}`][ - object4.className.charAt(0).toLowerCase() + - object4.className.slice(1) + object4.className.charAt(0).toLowerCase() + object4.className.slice(1) ].updatedAt ).toBeDefined(); await object4.fetch({ useMasterKey: true }); @@ -6944,8 +6482,7 @@ describe('ParseGraphQLServer', () => { { 'X-Parse-Session-Token': user5.getSessionToken() } ) ).data[`update${object3.className}`][ - object3.className.charAt(0).toLowerCase() + - object3.className.slice(1) + object3.className.charAt(0).toLowerCase() + object3.className.slice(1) ].updatedAt ).toBeDefined(); await object3.fetch({ useMasterKey: true }); @@ -6959,8 +6496,7 @@ describe('ParseGraphQLServer', () => { { 'X-Parse-Session-Token': user5.getSessionToken() } ) ).data[`update${object4.className}`][ - object4.className.charAt(0).toLowerCase() + - object4.className.slice(1) + object4.className.charAt(0).toLowerCase() + object4.className.slice(1) ].updatedAt ).toBeDefined(); await object4.fetch({ useMasterKey: true }); @@ -7000,22 +6536,14 @@ describe('ParseGraphQLServer', () => { }, }); - expect(result.data.deleteCustomer.clientMutationId).toEqual( - clientMutationId - ); - expect(result.data.deleteCustomer.customer.objectId).toEqual( - obj.id - ); - expect(result.data.deleteCustomer.customer.someField1).toEqual( - 'someField1Value1' - ); - expect(result.data.deleteCustomer.customer.someField2).toEqual( - 'someField2Value1' - ); + expect(result.data.deleteCustomer.clientMutationId).toEqual(clientMutationId); + expect(result.data.deleteCustomer.customer.objectId).toEqual(obj.id); + expect(result.data.deleteCustomer.customer.someField1).toEqual('someField1Value1'); + expect(result.data.deleteCustomer.customer.someField2).toEqual('someField2Value1'); - await expectAsync( - obj.fetch({ useMasterKey: true }) - ).toBeRejectedWith(jasmine.stringMatching('Object not found')); + await expectAsync(obj.fetch({ useMasterKey: true })).toBeRejectedWith( + jasmine.stringMatching('Object not found') + ); }); it('should respect level permissions', async () => { @@ -7024,8 +6552,7 @@ describe('ParseGraphQLServer', () => { await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); function deleteObject(className, id, headers) { - const mutationName = - className.charAt(0).toLowerCase() + className.slice(1); + const mutationName = className.charAt(0).toLowerCase() + className.slice(1); return apolloClient.mutate({ mutation: gql` mutation DeleteSomeObject( @@ -7050,9 +6577,9 @@ describe('ParseGraphQLServer', () => { await Promise.all( objects.slice(0, 3).map(async obj => { const originalFieldValue = obj.get('someField'); - await expectAsync( - deleteObject(obj.className, obj.id) - ).toBeRejectedWith(jasmine.stringMatching('Object not found')); + await expectAsync(deleteObject(obj.className, obj.id)).toBeRejectedWith( + jasmine.stringMatching('Object not found') + ); await obj.fetch({ useMasterKey: true }); expect(obj.get('someField')).toEqual(originalFieldValue); }) @@ -7071,52 +6598,42 @@ describe('ParseGraphQLServer', () => { ); expect( (await deleteObject(object4.className, object4.id)).data.delete[ - object4.className.charAt(0).toLowerCase() + - object4.className.slice(1) + object4.className.charAt(0).toLowerCase() + object4.className.slice(1) ] ).toEqual({ objectId: object4.id, __typename: 'PublicClass' }); - await expectAsync( - object4.fetch({ useMasterKey: true }) - ).toBeRejectedWith(jasmine.stringMatching('Object not found')); + await expectAsync(object4.fetch({ useMasterKey: true })).toBeRejectedWith( + jasmine.stringMatching('Object not found') + ); expect( ( await deleteObject(object1.className, object1.id, { 'X-Parse-Master-Key': 'test', }) - ).data.delete[ - object1.className.charAt(0).toLowerCase() + - object1.className.slice(1) - ] + ).data.delete[object1.className.charAt(0).toLowerCase() + object1.className.slice(1)] ).toEqual({ objectId: object1.id, __typename: 'GraphQLClass' }); - await expectAsync( - object1.fetch({ useMasterKey: true }) - ).toBeRejectedWith(jasmine.stringMatching('Object not found')); + await expectAsync(object1.fetch({ useMasterKey: true })).toBeRejectedWith( + jasmine.stringMatching('Object not found') + ); expect( ( await deleteObject(object2.className, object2.id, { 'X-Parse-Session-Token': user2.getSessionToken(), }) - ).data.delete[ - object2.className.charAt(0).toLowerCase() + - object2.className.slice(1) - ] + ).data.delete[object2.className.charAt(0).toLowerCase() + object2.className.slice(1)] ).toEqual({ objectId: object2.id, __typename: 'GraphQLClass' }); - await expectAsync( - object2.fetch({ useMasterKey: true }) - ).toBeRejectedWith(jasmine.stringMatching('Object not found')); + await expectAsync(object2.fetch({ useMasterKey: true })).toBeRejectedWith( + jasmine.stringMatching('Object not found') + ); expect( ( await deleteObject(object3.className, object3.id, { 'X-Parse-Session-Token': user5.getSessionToken(), }) - ).data.delete[ - object3.className.charAt(0).toLowerCase() + - object3.className.slice(1) - ] + ).data.delete[object3.className.charAt(0).toLowerCase() + object3.className.slice(1)] ).toEqual({ objectId: object3.id, __typename: 'GraphQLClass' }); - await expectAsync( - object3.fetch({ useMasterKey: true }) - ).toBeRejectedWith(jasmine.stringMatching('Object not found')); + await expectAsync(object3.fetch({ useMasterKey: true })).toBeRejectedWith( + jasmine.stringMatching('Object not found') + ); }); it('should respect level permissions with specific class mutation', async () => { @@ -7125,8 +6642,7 @@ describe('ParseGraphQLServer', () => { await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); function deleteObject(className, id, headers) { - const mutationName = - className.charAt(0).toLowerCase() + className.slice(1); + const mutationName = className.charAt(0).toLowerCase() + className.slice(1); return apolloClient.mutate({ mutation: gql` mutation DeleteSomeObject( @@ -7151,9 +6667,9 @@ describe('ParseGraphQLServer', () => { await Promise.all( objects.slice(0, 3).map(async obj => { const originalFieldValue = obj.get('someField'); - await expectAsync( - deleteObject(obj.className, obj.id) - ).toBeRejectedWith(jasmine.stringMatching('Object not found')); + await expectAsync(deleteObject(obj.className, obj.id)).toBeRejectedWith( + jasmine.stringMatching('Object not found') + ); await obj.fetch({ useMasterKey: true }); expect(obj.get('someField')).toEqual(originalFieldValue); }) @@ -7173,53 +6689,47 @@ describe('ParseGraphQLServer', () => { expect( (await deleteObject(object4.className, object4.id)).data[ `delete${object4.className}` - ][ - object4.className.charAt(0).toLowerCase() + - object4.className.slice(1) - ].objectId + ][object4.className.charAt(0).toLowerCase() + object4.className.slice(1)].objectId ).toEqual(object4.id); - await expectAsync( - object4.fetch({ useMasterKey: true }) - ).toBeRejectedWith(jasmine.stringMatching('Object not found')); + await expectAsync(object4.fetch({ useMasterKey: true })).toBeRejectedWith( + jasmine.stringMatching('Object not found') + ); expect( ( await deleteObject(object1.className, object1.id, { 'X-Parse-Master-Key': 'test', }) ).data[`delete${object1.className}`][ - object1.className.charAt(0).toLowerCase() + - object1.className.slice(1) + object1.className.charAt(0).toLowerCase() + object1.className.slice(1) ].objectId ).toEqual(object1.id); - await expectAsync( - object1.fetch({ useMasterKey: true }) - ).toBeRejectedWith(jasmine.stringMatching('Object not found')); + await expectAsync(object1.fetch({ useMasterKey: true })).toBeRejectedWith( + jasmine.stringMatching('Object not found') + ); expect( ( await deleteObject(object2.className, object2.id, { 'X-Parse-Session-Token': user2.getSessionToken(), }) ).data[`delete${object2.className}`][ - object2.className.charAt(0).toLowerCase() + - object2.className.slice(1) + object2.className.charAt(0).toLowerCase() + object2.className.slice(1) ].objectId ).toEqual(object2.id); - await expectAsync( - object2.fetch({ useMasterKey: true }) - ).toBeRejectedWith(jasmine.stringMatching('Object not found')); + await expectAsync(object2.fetch({ useMasterKey: true })).toBeRejectedWith( + jasmine.stringMatching('Object not found') + ); expect( ( await deleteObject(object3.className, object3.id, { 'X-Parse-Session-Token': user5.getSessionToken(), }) ).data[`delete${object3.className}`][ - object3.className.charAt(0).toLowerCase() + - object3.className.slice(1) + object3.className.charAt(0).toLowerCase() + object3.className.slice(1) ].objectId ).toEqual(object3.id); - await expectAsync( - object3.fetch({ useMasterKey: true }) - ).toBeRejectedWith(jasmine.stringMatching('Object not found')); + await expectAsync(object3.fetch({ useMasterKey: true })).toBeRejectedWith( + jasmine.stringMatching('Object not found') + ); }); }); @@ -7257,10 +6767,7 @@ describe('ParseGraphQLServer', () => { { latitude: 30, longitude: 30 }, ], }; - const nullFields = Object.keys(fields).reduce( - (acc, k) => ({ ...acc, [k]: null }), - {} - ); + const nullFields = Object.keys(fields).reduce((acc, k) => ({ ...acc, [k]: null }), {}); const result = await apolloClient.mutate({ mutation: gql` mutation CreateCustomer($input: CreateCustomerInput!) { @@ -7418,10 +6925,7 @@ describe('ParseGraphQLServer', () => { }, }) ); - body.append( - 'map', - JSON.stringify({ 1: ['variables.input.upload'] }) - ); + body.append('map', JSON.stringify({ 1: ['variables.input.upload'] })); body.append('1', 'My File Content', { filename: 'myFileName.txt', contentType: 'text/plain', @@ -7437,9 +6941,7 @@ describe('ParseGraphQLServer', () => { const result = JSON.parse(await res.text()); - expect(result.data.createFile.clientMutationId).toEqual( - clientMutationId - ); + expect(result.data.createFile.clientMutationId).toEqual(clientMutationId); expect(result.data.createFile.fileInfo.name).toEqual( jasmine.stringMatching(/_myFileName.txt$/) ); @@ -7487,11 +6989,7 @@ describe('ParseGraphQLServer', () => { }, }); - const { - id, - username: resultUserName, - email: resultEmail, - } = result.data.viewer.user; + const { id, username: resultUserName, email: resultEmail } = result.data.viewer.user; expect(id).toBeDefined(); expect(resultUserName).toEqual(userName); expect(resultEmail).toEqual(email); @@ -7598,8 +7096,7 @@ describe('ParseGraphQLServer', () => { describe('Users Mutations', () => { const challengeAdapter = { - validateAuthData: () => - Promise.resolve({ response: { someData: true } }), + validateAuthData: () => Promise.resolve({ response: { someData: true } }), validateAppId: () => Promise.resolve(), challenge: () => Promise.resolve({ someData: true }), options: { anOption: true }, @@ -7646,9 +7143,7 @@ describe('ParseGraphQLServer', () => { }, }); - expect(result.data.createUser.clientMutationId).toEqual( - clientMutationId - ); + expect(result.data.createUser.clientMutationId).toEqual(clientMutationId); expect(result.data.createUser.user.authDataResponse).toEqual({ challengeAdapter: { someData: true }, }); @@ -7717,9 +7212,7 @@ describe('ParseGraphQLServer', () => { expect(result.data.signUp.viewer.sessionToken).toBeDefined(); expect(result.data.signUp.viewer.user.someField).toEqual('someValue'); expect(result.data.signUp.viewer.user.aPointer.id).toBeDefined(); - expect(result.data.signUp.viewer.user.aPointer.username).toEqual( - 'user2' - ); + expect(result.data.signUp.viewer.user.aPointer.username).toEqual('user2'); expect(typeof result.data.signUp.viewer.sessionToken).toBe('string'); expect(result.data.signUp.viewer.user.authDataResponse).toEqual({ challengeAdapter: { someData: true }, @@ -7787,20 +7280,12 @@ describe('ParseGraphQLServer', () => { }, }); - expect(result.data.logInWith.clientMutationId).toEqual( - clientMutationId - ); + expect(result.data.logInWith.clientMutationId).toEqual(clientMutationId); expect(result.data.logInWith.viewer.sessionToken).toBeDefined(); - expect(result.data.logInWith.viewer.user.someField).toEqual( - 'someValue' - ); - expect(typeof result.data.logInWith.viewer.sessionToken).toBe( - 'string' - ); + expect(result.data.logInWith.viewer.user.someField).toEqual('someValue'); + expect(typeof result.data.logInWith.viewer.sessionToken).toBe('string'); expect(result.data.logInWith.viewer.user.aPointer.id).toBeDefined(); - expect(result.data.logInWith.viewer.user.aPointer.username).toEqual( - 'user2' - ); + expect(result.data.logInWith.viewer.user.aPointer.username).toEqual('user2'); expect(result.data.logInWith.viewer.user.authDataResponse).toEqual({ challengeAdapter: { someData: true }, }); @@ -7851,9 +7336,7 @@ describe('ParseGraphQLServer', () => { expect(challengeCall[3].isChallenge).toBeTruthy(); expect(challengeCall[3].object.id).toEqual(user.id); expect(challengeCall[3].original.id).toEqual(user.id); - expect(result.data.challenge.clientMutationId).toEqual( - clientMutationId - ); + expect(result.data.challenge.clientMutationId).toEqual(clientMutationId); expect(result.data.challenge.challengeData).toEqual({ challengeAdapter: { someData: true }, }); @@ -8045,9 +7528,7 @@ describe('ParseGraphQLServer', () => { }, }); - expect(result.data.resetPassword.clientMutationId).toEqual( - clientMutationId - ); + expect(result.data.resetPassword.clientMutationId).toEqual(clientMutationId); expect(result.data.resetPassword.ok).toBeTruthy(); }); @@ -8081,9 +7562,7 @@ describe('ParseGraphQLServer', () => { await Parse.User.requestPasswordReset('user1@user1.user1'); await apolloClient.mutate({ mutation: gql` - mutation ConfirmResetPassword( - $input: ConfirmResetPasswordInput! - ) { + mutation ConfirmResetPassword($input: ConfirmResetPasswordInput!) { confirmResetPassword(input: $input) { clientMutationId ok @@ -8145,9 +7624,7 @@ describe('ParseGraphQLServer', () => { await Parse.User.logOut(); const result = await apolloClient.mutate({ mutation: gql` - mutation SendVerificationEmail( - $input: SendVerificationEmailInput! - ) { + mutation SendVerificationEmail($input: SendVerificationEmailInput!) { sendVerificationEmail(input: $input) { clientMutationId ok @@ -8162,9 +7639,7 @@ describe('ParseGraphQLServer', () => { }, }); - expect(result.data.sendVerificationEmail.clientMutationId).toEqual( - clientMutationId - ); + expect(result.data.sendVerificationEmail.clientMutationId).toEqual(clientMutationId); expect(result.data.sendVerificationEmail.ok).toBeTruthy(); }); }); @@ -8288,9 +7763,7 @@ describe('ParseGraphQLServer', () => { }, }); - expect(result.data.callCloudCode.clientMutationId).toEqual( - clientMutationId - ); + expect(result.data.callCloudCode.clientMutationId).toEqual(clientMutationId); expect(result.data.callCloudCode.result).toEqual('Hello world!'); } catch (e) { handleError(e); @@ -8326,24 +7799,12 @@ describe('ParseGraphQLServer', () => { expect(req.params.date.getTime()).toBe(1463907600000); expect(req.params.dateList[0] instanceof Date).toBe(true); expect(req.params.dateList[0].getTime()).toBe(1463907600000); - expect(req.params.complexStructure.date[0] instanceof Date).toBe( - true - ); - expect(req.params.complexStructure.date[0].getTime()).toBe( - 1463907600000 - ); - expect( - req.params.complexStructure.deepDate.date[0] instanceof Date - ).toBe(true); - expect(req.params.complexStructure.deepDate.date[0].getTime()).toBe( - 1463907600000 - ); - expect( - req.params.complexStructure.deepDate2[0].date instanceof Date - ).toBe(true); - expect( - req.params.complexStructure.deepDate2[0].date.getTime() - ).toBe(1463907600000); + expect(req.params.complexStructure.date[0] instanceof Date).toBe(true); + expect(req.params.complexStructure.date[0].getTime()).toBe(1463907600000); + expect(req.params.complexStructure.deepDate.date[0] instanceof Date).toBe(true); + expect(req.params.complexStructure.deepDate.date[0].getTime()).toBe(1463907600000); + expect(req.params.complexStructure.deepDate2[0].date instanceof Date).toBe(true); + expect(req.params.complexStructure.deepDate2[0].date.getTime()).toBe(1463907600000); // Regression for #2294 expect(req.params.file instanceof Parse.File).toBe(true); expect(req.params.file.url()).toEqual('https://some.url'); @@ -8456,9 +7917,12 @@ describe('ParseGraphQLServer', () => { }) ).data['__type']; expect(functionEnum.kind).toEqual('ENUM'); - expect( - functionEnum.enumValues.map(value => value.name).sort() - ).toEqual(['_underscored', 'a', 'b', 'contains1Number']); + expect(functionEnum.enumValues.map(value => value.name).sort()).toEqual([ + '_underscored', + 'a', + 'b', + 'contains1Number', + ]); } catch (e) { handleError(e); } @@ -8466,10 +7930,7 @@ describe('ParseGraphQLServer', () => { it('should warn functions not matching GraphQL allowed names', async () => { try { - spyOn( - parseGraphQLServer.parseGraphQLSchema.log, - 'warn' - ).and.callThrough(); + spyOn(parseGraphQLServer.parseGraphQLSchema.log, 'warn').and.callThrough(); Parse.Cloud.define('a', async () => { return 'hello a'; @@ -8498,9 +7959,7 @@ describe('ParseGraphQLServer', () => { }) ).data['__type']; expect(functionEnum.kind).toEqual('ENUM'); - expect( - functionEnum.enumValues.map(value => value.name).sort() - ).toEqual(['a']); + expect(functionEnum.enumValues.map(value => value.name).sort()).toEqual(['a']); expect( parseGraphQLServer.parseGraphQLSchema.log.warn.calls .all() @@ -8524,9 +7983,7 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass( - input: { name: "SomeClass", schemaFields: $schemaFields } - ) { + createClass(input: { name: "SomeClass", schemaFields: $schemaFields }) { clientMutationId } } @@ -8571,9 +8028,7 @@ describe('ParseGraphQLServer', () => { someClass(id: $id) { someField } - someClasses( - where: { someField: { equalTo: $someFieldValue } } - ) { + someClasses(where: { someField: { equalTo: $someFieldValue } }) { edges { node { someField @@ -8603,9 +8058,7 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass( - input: { name: "SomeClass", schemaFields: $schemaFields } - ) { + createClass(input: { name: "SomeClass", schemaFields: $schemaFields }) { clientMutationId } } @@ -8650,9 +8103,7 @@ describe('ParseGraphQLServer', () => { someClass(id: $id) { someField } - someClasses( - where: { someField: { equalTo: $someFieldValue } } - ) { + someClasses(where: { someField: { equalTo: $someFieldValue } }) { edges { node { someField @@ -8682,9 +8133,7 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass( - input: { name: "SomeClass", schemaFields: $schemaFields } - ) { + createClass(input: { name: "SomeClass", schemaFields: $schemaFields }) { clientMutationId } } @@ -8729,9 +8178,7 @@ describe('ParseGraphQLServer', () => { someClass(id: $id) { someField } - someClasses( - where: { someField: { equalTo: $someFieldValue } } - ) { + someClasses(where: { someField: { equalTo: $someFieldValue } }) { edges { node { someField @@ -8762,19 +8209,14 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass( - input: { name: "SomeClass", schemaFields: $schemaFields } - ) { + createClass(input: { name: "SomeClass", schemaFields: $schemaFields }) { clientMutationId } } `, variables: { schemaFields: { - addBooleans: [ - { name: 'someFieldTrue' }, - { name: 'someFieldFalse' }, - ], + addBooleans: [{ name: 'someFieldTrue' }, { name: 'someFieldFalse' }], }, }, context: { @@ -8840,12 +8282,8 @@ describe('ParseGraphQLServer', () => { }, }); - expect(typeof getResult.data.someClass.someFieldTrue).toEqual( - 'boolean' - ); - expect(typeof getResult.data.someClass.someFieldFalse).toEqual( - 'boolean' - ); + expect(typeof getResult.data.someClass.someFieldTrue).toEqual('boolean'); + expect(typeof getResult.data.someClass.someFieldFalse).toEqual('boolean'); expect(getResult.data.someClass.someFieldTrue).toEqual(true); expect(getResult.data.someClass.someFieldFalse).toEqual(false); expect(getResult.data.someClasses.edges.length).toEqual(1); @@ -8861,9 +8299,7 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass( - input: { name: "SomeClass", schemaFields: $schemaFields } - ) { + createClass(input: { name: "SomeClass", schemaFields: $schemaFields }) { clientMutationId } } @@ -8922,9 +8358,7 @@ describe('ParseGraphQLServer', () => { }, }); - expect(new Date(getResult.data.someClass.someField)).toEqual( - someFieldValue - ); + expect(new Date(getResult.data.someClass.someField)).toEqual(someFieldValue); expect(getResult.data.someClasses.edges.length).toEqual(1); } catch (e) { handleError(e); @@ -8952,205 +8386,202 @@ describe('ParseGraphQLServer', () => { expect(schema.fields.updatedAt.type).toEqual('Date'); }); - it_id('93e748f6-ad9b-4c31-8e1e-c5685e2382fb')(it)( - 'should support ACL', - async () => { - const someClass = new Parse.Object('SomeClass'); - await someClass.save(); + it_id('93e748f6-ad9b-4c31-8e1e-c5685e2382fb')(it)('should support ACL', async () => { + const someClass = new Parse.Object('SomeClass'); + await someClass.save(); - const roleACL = new Parse.ACL(); - roleACL.setPublicReadAccess(true); + const roleACL = new Parse.ACL(); + roleACL.setPublicReadAccess(true); - const user = new Parse.User(); - user.set('username', 'username'); - user.set('password', 'password'); - user.setACL(roleACL); - await user.signUp(); + const user = new Parse.User(); + user.set('username', 'username'); + user.set('password', 'password'); + user.setACL(roleACL); + await user.signUp(); - const user2 = new Parse.User(); - user2.set('username', 'username2'); - user2.set('password', 'password2'); - user2.setACL(roleACL); - await user2.signUp(); + const user2 = new Parse.User(); + user2.set('username', 'username2'); + user2.set('password', 'password2'); + user2.setACL(roleACL); + await user2.signUp(); - const role = new Parse.Role('aRole', roleACL); - await role.save(); + const role = new Parse.Role('aRole', roleACL); + await role.save(); - const role2 = new Parse.Role('aRole2', roleACL); - await role2.save(); + const role2 = new Parse.Role('aRole2', roleACL); + await role2.save(); - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const gqlUser = ( - await apolloClient.query({ - query: gql` - query getUser($id: ID!) { - user(id: $id) { - id - } + const gqlUser = ( + await apolloClient.query({ + query: gql` + query getUser($id: ID!) { + user(id: $id) { + id } - `, - variables: { id: user.id }, - }) - ).data.user; - const { - data: { createSomeClass }, - } = await apolloClient.mutate({ - mutation: gql` - mutation Create($fields: CreateSomeClassFieldsInput) { - createSomeClass(input: { fields: $fields }) { - someClass { - id - objectId - ACL { - users { - userId - read - write - } - roles { - roleName - read - write - } - public { - read - write - } + } + `, + variables: { id: user.id }, + }) + ).data.user; + const { + data: { createSomeClass }, + } = await apolloClient.mutate({ + mutation: gql` + mutation Create($fields: CreateSomeClassFieldsInput) { + createSomeClass(input: { fields: $fields }) { + someClass { + id + objectId + ACL { + users { + userId + read + write + } + roles { + roleName + read + write + } + public { + read + write } } } } - `, - variables: { - fields: { - ACL: { - users: [ - { userId: gqlUser.id, read: true, write: true }, - { userId: user2.id, read: true, write: false }, - ], - roles: [ - { roleName: 'aRole', read: true, write: false }, - { roleName: 'aRole2', read: false, write: true }, - ], - public: { read: true, write: true }, - }, + } + `, + variables: { + fields: { + ACL: { + users: [ + { userId: gqlUser.id, read: true, write: true }, + { userId: user2.id, read: true, write: false }, + ], + roles: [ + { roleName: 'aRole', read: true, write: false }, + { roleName: 'aRole2', read: false, write: true }, + ], + public: { read: true, write: true }, }, }, - }); + }, + }); - const expectedCreateACL = { - __typename: 'ACL', - users: [ - { - userId: toGlobalId('_User', user.id), - read: true, - write: true, - __typename: 'UserACL', - }, - { - userId: toGlobalId('_User', user2.id), - read: true, - write: false, - __typename: 'UserACL', - }, - ], - roles: [ - { - roleName: 'aRole', - read: true, - write: false, - __typename: 'RoleACL', - }, - { - roleName: 'aRole2', - read: false, - write: true, - __typename: 'RoleACL', - }, - ], - public: { read: true, write: true, __typename: 'PublicACL' }, - }; - const query1 = new Parse.Query('SomeClass'); - const obj1 = ( - await query1.get(createSomeClass.someClass.objectId, { - useMasterKey: true, - }) - ).toJSON(); - expect(obj1.ACL[user.id]).toEqual({ read: true, write: true }); - expect(obj1.ACL[user2.id]).toEqual({ read: true }); - expect(obj1.ACL['role:aRole']).toEqual({ read: true }); - expect(obj1.ACL['role:aRole2']).toEqual({ write: true }); - expect(obj1.ACL['*']).toEqual({ read: true, write: true }); - expect(createSomeClass.someClass.ACL).toEqual(expectedCreateACL); + const expectedCreateACL = { + __typename: 'ACL', + users: [ + { + userId: toGlobalId('_User', user.id), + read: true, + write: true, + __typename: 'UserACL', + }, + { + userId: toGlobalId('_User', user2.id), + read: true, + write: false, + __typename: 'UserACL', + }, + ], + roles: [ + { + roleName: 'aRole', + read: true, + write: false, + __typename: 'RoleACL', + }, + { + roleName: 'aRole2', + read: false, + write: true, + __typename: 'RoleACL', + }, + ], + public: { read: true, write: true, __typename: 'PublicACL' }, + }; + const query1 = new Parse.Query('SomeClass'); + const obj1 = ( + await query1.get(createSomeClass.someClass.objectId, { + useMasterKey: true, + }) + ).toJSON(); + expect(obj1.ACL[user.id]).toEqual({ read: true, write: true }); + expect(obj1.ACL[user2.id]).toEqual({ read: true }); + expect(obj1.ACL['role:aRole']).toEqual({ read: true }); + expect(obj1.ACL['role:aRole2']).toEqual({ write: true }); + expect(obj1.ACL['*']).toEqual({ read: true, write: true }); + expect(createSomeClass.someClass.ACL).toEqual(expectedCreateACL); - const { - data: { updateSomeClass }, - } = await apolloClient.mutate({ - mutation: gql` - mutation Update($id: ID!, $fields: UpdateSomeClassFieldsInput) { - updateSomeClass(input: { id: $id, fields: $fields }) { - someClass { - id - objectId - ACL { - users { - userId - read - write - } - roles { - roleName - read - write - } - public { - read - write - } + const { + data: { updateSomeClass }, + } = await apolloClient.mutate({ + mutation: gql` + mutation Update($id: ID!, $fields: UpdateSomeClassFieldsInput) { + updateSomeClass(input: { id: $id, fields: $fields }) { + someClass { + id + objectId + ACL { + users { + userId + read + write + } + roles { + roleName + read + write + } + public { + read + write } } } } - `, - variables: { - id: createSomeClass.someClass.id, - fields: { - ACL: { - roles: [{ roleName: 'aRole', write: true, read: true }], - public: { read: true, write: false }, - }, + } + `, + variables: { + id: createSomeClass.someClass.id, + fields: { + ACL: { + roles: [{ roleName: 'aRole', write: true, read: true }], + public: { read: true, write: false }, }, }, - }); + }, + }); - const expectedUpdateACL = { - __typename: 'ACL', - users: null, - roles: [ - { - roleName: 'aRole', - read: true, - write: true, - __typename: 'RoleACL', - }, - ], - public: { read: true, write: false, __typename: 'PublicACL' }, - }; + const expectedUpdateACL = { + __typename: 'ACL', + users: null, + roles: [ + { + roleName: 'aRole', + read: true, + write: true, + __typename: 'RoleACL', + }, + ], + public: { read: true, write: false, __typename: 'PublicACL' }, + }; - const query2 = new Parse.Query('SomeClass'); - const obj2 = ( - await query2.get(createSomeClass.someClass.objectId, { - useMasterKey: true, - }) - ).toJSON(); + const query2 = new Parse.Query('SomeClass'); + const obj2 = ( + await query2.get(createSomeClass.someClass.objectId, { + useMasterKey: true, + }) + ).toJSON(); - expect(obj2.ACL['role:aRole']).toEqual({ write: true, read: true }); - expect(obj2.ACL[user.id]).toBeUndefined(); - expect(obj2.ACL['*']).toEqual({ read: true }); - expect(updateSomeClass.someClass.ACL).toEqual(expectedUpdateACL); - } - ); + expect(obj2.ACL['role:aRole']).toEqual({ write: true, read: true }); + expect(obj2.ACL[user.id]).toBeUndefined(); + expect(obj2.ACL['*']).toEqual({ read: true }); + expect(updateSomeClass.someClass.ACL).toEqual(expectedUpdateACL); + }); it('should support pointer on create', async () => { const company = new Parse.Object('Company'); @@ -9344,77 +8775,68 @@ describe('ParseGraphQLServer', () => { expect(result.company.name).toEqual('imACompany2'); }); - it_only_db('mongo')( - 'should support relation and nested relation on create', - async () => { - const company = new Parse.Object('Company'); - company.set('name', 'imACompany1'); - await company.save(); + it_only_db('mongo')('should support relation and nested relation on create', async () => { + const company = new Parse.Object('Company'); + company.set('name', 'imACompany1'); + await company.save(); - const country = new Parse.Object('Country'); - country.set('name', 'imACountry'); - country.relation('companies').add(company); - await country.save(); + const country = new Parse.Object('Country'); + country.set('name', 'imACountry'); + country.relation('companies').add(company); + await country.save(); - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const { - data: { - createCountry: { country: result }, - }, - } = await apolloClient.mutate({ - mutation: gql` - mutation CreateCountry($fields: CreateCountryFieldsInput) { - createCountry(input: { fields: $fields }) { - country { - id - objectId - name - companies { - edges { - node { - id - objectId - name - } + const { + data: { + createCountry: { country: result }, + }, + } = await apolloClient.mutate({ + mutation: gql` + mutation CreateCountry($fields: CreateCountryFieldsInput) { + createCountry(input: { fields: $fields }) { + country { + id + objectId + name + companies { + edges { + node { + id + objectId + name } } } } } - `, - variables: { - fields: { - name: 'imACountry2', - companies: { - add: [company.id], - createAndAdd: [ - { - name: 'imACompany2', - }, - { - name: 'imACompany3', - }, - ], - }, + } + `, + variables: { + fields: { + name: 'imACountry2', + companies: { + add: [company.id], + createAndAdd: [ + { + name: 'imACompany2', + }, + { + name: 'imACompany3', + }, + ], }, }, - }); + }, + }); - expect(result.id).toBeDefined(); - expect(result.name).toEqual('imACountry2'); - expect(result.companies.edges.length).toEqual(3); - expect( - result.companies.edges.some(o => o.node.objectId === company.id) - ).toBeTruthy(); - expect( - result.companies.edges.some(o => o.node.name === 'imACompany2') - ).toBeTruthy(); - expect( - result.companies.edges.some(o => o.node.name === 'imACompany3') - ).toBeTruthy(); - } - ); + expect(result.id).toBeDefined(); + expect(result.name).toEqual('imACountry2'); + expect(result.companies.edges.length).toEqual(3); + expect(result.companies.edges.some(o => o.node.objectId === company.id)).toBeTruthy(); + expect(result.companies.edges.some(o => o.node.name === 'imACompany2')).toBeTruthy(); + expect(result.companies.edges.some(o => o.node.name === 'imACompany3')).toBeTruthy(); + }); it_only_db('mongo')('should support deep nested creation', async () => { const team = new Parse.Object('Team'); @@ -9510,152 +8932,132 @@ describe('ParseGraphQLServer', () => { ).toBeTruthy(); }); - it_only_db('mongo')( - 'should support relation and nested relation on update', - async () => { - const company1 = new Parse.Object('Company'); - company1.set('name', 'imACompany1'); - await company1.save(); + it_only_db('mongo')('should support relation and nested relation on update', async () => { + const company1 = new Parse.Object('Company'); + company1.set('name', 'imACompany1'); + await company1.save(); - const company2 = new Parse.Object('Company'); - company2.set('name', 'imACompany2'); - await company2.save(); + const company2 = new Parse.Object('Company'); + company2.set('name', 'imACompany2'); + await company2.save(); - const country = new Parse.Object('Country'); - country.set('name', 'imACountry'); - country.relation('companies').add(company1); - await country.save(); + const country = new Parse.Object('Country'); + country.set('name', 'imACountry'); + country.relation('companies').add(company1); + await country.save(); - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const { - data: { - updateCountry: { country: result }, - }, - } = await apolloClient.mutate({ - mutation: gql` - mutation UpdateCountry( - $id: ID! - $fields: UpdateCountryFieldsInput - ) { - updateCountry(input: { id: $id, fields: $fields }) { - country { - id - objectId - companies { - edges { - node { - id - objectId - name - } + const { + data: { + updateCountry: { country: result }, + }, + } = await apolloClient.mutate({ + mutation: gql` + mutation UpdateCountry($id: ID!, $fields: UpdateCountryFieldsInput) { + updateCountry(input: { id: $id, fields: $fields }) { + country { + id + objectId + companies { + edges { + node { + id + objectId + name } } } } } - `, - variables: { - id: country.id, - fields: { - companies: { - add: [company2.id], - remove: [company1.id], - createAndAdd: [ - { - name: 'imACompany3', - }, - ], - }, + } + `, + variables: { + id: country.id, + fields: { + companies: { + add: [company2.id], + remove: [company1.id], + createAndAdd: [ + { + name: 'imACompany3', + }, + ], }, }, - }); + }, + }); - expect(result.objectId).toEqual(country.id); - expect(result.companies.edges.length).toEqual(2); - expect( - result.companies.edges.some(o => o.node.objectId === company2.id) - ).toBeTruthy(); - expect( - result.companies.edges.some(o => o.node.name === 'imACompany3') - ).toBeTruthy(); - expect( - result.companies.edges.some(o => o.node.objectId === company1.id) - ).toBeFalsy(); - } - ); + expect(result.objectId).toEqual(country.id); + expect(result.companies.edges.length).toEqual(2); + expect(result.companies.edges.some(o => o.node.objectId === company2.id)).toBeTruthy(); + expect(result.companies.edges.some(o => o.node.name === 'imACompany3')).toBeTruthy(); + expect(result.companies.edges.some(o => o.node.objectId === company1.id)).toBeFalsy(); + }); - it_only_db('mongo')( - 'should support nested relation on create with filter', - async () => { - const company = new Parse.Object('Company'); - company.set('name', 'imACompany1'); - await company.save(); + it_only_db('mongo')('should support nested relation on create with filter', async () => { + const company = new Parse.Object('Company'); + company.set('name', 'imACompany1'); + await company.save(); - const country = new Parse.Object('Country'); - country.set('name', 'imACountry'); - country.relation('companies').add(company); - await country.save(); + const country = new Parse.Object('Country'); + country.set('name', 'imACountry'); + country.relation('companies').add(company); + await country.save(); - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const { - data: { - createCountry: { country: result }, - }, - } = await apolloClient.mutate({ - mutation: gql` - mutation CreateCountry( - $fields: CreateCountryFieldsInput - $where: CompanyWhereInput - ) { - createCountry(input: { fields: $fields }) { - country { - id - name - companies(where: $where) { - edges { - node { - id - name - } + const { + data: { + createCountry: { country: result }, + }, + } = await apolloClient.mutate({ + mutation: gql` + mutation CreateCountry($fields: CreateCountryFieldsInput, $where: CompanyWhereInput) { + createCountry(input: { fields: $fields }) { + country { + id + name + companies(where: $where) { + edges { + node { + id + name } } } } } - `, - variables: { - where: { - name: { - equalTo: 'imACompany2', - }, + } + `, + variables: { + where: { + name: { + equalTo: 'imACompany2', }, - fields: { - name: 'imACountry2', - companies: { - add: [company.id], - createAndAdd: [ - { - name: 'imACompany2', - }, - { - name: 'imACompany3', - }, - ], - }, + }, + fields: { + name: 'imACountry2', + companies: { + add: [company.id], + createAndAdd: [ + { + name: 'imACompany2', + }, + { + name: 'imACompany3', + }, + ], }, }, - }); + }, + }); - expect(result.id).toBeDefined(); - expect(result.name).toEqual('imACountry2'); - expect(result.companies.edges.length).toEqual(1); - expect( - result.companies.edges.some(o => o.node.name === 'imACompany2') - ).toBeTruthy(); - } - ); + expect(result.id).toBeDefined(); + expect(result.name).toEqual('imACountry2'); + expect(result.companies.edges.length).toEqual(1); + expect(result.companies.edges.some(o => o.node.name === 'imACompany2')).toBeTruthy(); + }); it_only_db('mongo')('should support relation on query', async () => { const company1 = new Parse.Object('Company'); @@ -9702,12 +9104,8 @@ describe('ParseGraphQLServer', () => { expect(result1.objectId).toEqual(country.id); expect(result1.companies.edges.length).toEqual(2); - expect( - result1.companies.edges.some(o => o.node.objectId === company1.id) - ).toBeTruthy(); - expect( - result1.companies.edges.some(o => o.node.objectId === company2.id) - ).toBeTruthy(); + expect(result1.companies.edges.some(o => o.node.objectId === company1.id)).toBeTruthy(); + expect(result1.companies.edges.some(o => o.node.objectId === company2.id)).toBeTruthy(); // With where const { @@ -9999,10 +9397,7 @@ describe('ParseGraphQLServer', () => { }, }) ); - body.append( - 'map', - JSON.stringify({ 1: ['variables.input.upload'] }) - ); + body.append('map', JSON.stringify({ 1: ['variables.input.upload'] })); body.append('1', 'My File Content', { filename: 'myFileName.txt', contentType: 'text/plain', @@ -10029,9 +9424,7 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass( - input: { name: "SomeClass", schemaFields: $schemaFields } - ) { + createClass(input: { name: "SomeClass", schemaFields: $schemaFields }) { clientMutationId } } @@ -10114,10 +9507,7 @@ describe('ParseGraphQLServer', () => { }, }) ); - body2.append( - 'map', - JSON.stringify({ 1: ['variables.fields3.someField.upload'] }) - ); + body2.append('map', JSON.stringify({ 1: ['variables.fields3.someField.upload'] })); body2.append('1', 'My File Content', { filename: 'myFileName.txt', contentType: 'text/plain', @@ -10130,24 +9520,24 @@ describe('ParseGraphQLServer', () => { }); expect(res.status).toEqual(200); const result2 = JSON.parse(await res.text()); - expect( - result2.data.createSomeClass1.someClass.someField.name - ).toEqual(jasmine.stringMatching(/_myFileName.txt$/)); - expect( - result2.data.createSomeClass1.someClass.someField.url - ).toEqual(jasmine.stringMatching(/_myFileName.txt$/)); - expect( - result2.data.createSomeClass2.someClass.someField.name - ).toEqual(jasmine.stringMatching(/_myFileName.txt$/)); - expect( - result2.data.createSomeClass2.someClass.someField.url - ).toEqual(jasmine.stringMatching(/_myFileName.txt$/)); - expect( - result2.data.createSomeClass3.someClass.someField.name - ).toEqual(jasmine.stringMatching(/_myFileName.txt$/)); - expect( - result2.data.createSomeClass3.someClass.someField.url - ).toEqual(jasmine.stringMatching(/_myFileName.txt$/)); + expect(result2.data.createSomeClass1.someClass.someField.name).toEqual( + jasmine.stringMatching(/_myFileName.txt$/) + ); + expect(result2.data.createSomeClass1.someClass.someField.url).toEqual( + jasmine.stringMatching(/_myFileName.txt$/) + ); + expect(result2.data.createSomeClass2.someClass.someField.name).toEqual( + jasmine.stringMatching(/_myFileName.txt$/) + ); + expect(result2.data.createSomeClass2.someClass.someField.url).toEqual( + jasmine.stringMatching(/_myFileName.txt$/) + ); + expect(result2.data.createSomeClass3.someClass.someField.name).toEqual( + jasmine.stringMatching(/_myFileName.txt$/) + ); + expect(result2.data.createSomeClass3.someClass.someField.url).toEqual( + jasmine.stringMatching(/_myFileName.txt$/) + ); const schema = await new Parse.Schema('SomeClass').get(); expect(schema.fields.someField.type).toEqual('File'); @@ -10161,9 +9551,7 @@ describe('ParseGraphQLServer', () => { url } } - findSomeClass1: someClasses( - where: { someField: { exists: true } } - ) { + findSomeClass1: someClasses(where: { someField: { exists: true } }) { edges { node { someField { @@ -10173,9 +9561,7 @@ describe('ParseGraphQLServer', () => { } } } - findSomeClass2: someClasses( - where: { someField: { exists: true } } - ) { + findSomeClass2: someClasses(where: { someField: { exists: true } }) { edges { node { someField { @@ -10210,9 +9596,7 @@ describe('ParseGraphQLServer', () => { const mutationResult = await apolloClient.mutate({ mutation: gql` mutation UnlinkFile($id: ID!) { - updateSomeClass( - input: { id: $id, fields: { someField: null } } - ) { + updateSomeClass(input: { id: $id, fields: { someField: null } }) { someClass { someField { name @@ -10226,9 +9610,7 @@ describe('ParseGraphQLServer', () => { id: result2.data.createSomeClass3.someClass.id, }, }); - expect( - mutationResult.data.updateSomeClass.someClass.someField - ).toEqual(null); + expect(mutationResult.data.updateSomeClass.someClass.someField).toEqual(null); } catch (e) { handleError(e); } @@ -10240,14 +9622,10 @@ describe('ParseGraphQLServer', () => { publicServerURL: 'http://localhost:13377/parse', }); await createGQLFromParseServer(parseServer); - const schemaController = - await parseServer.config.databaseController.loadSchema(); - await schemaController.addClassIfNotExists( - 'SomeClassWithRequiredFile', - { - someField: { type: 'File', required: true }, - } - ); + const schemaController = await parseServer.config.databaseController.loadSchema(); + await schemaController.addClassIfNotExists('SomeClassWithRequiredFile', { + someField: { type: 'File', required: true }, + }); await resetGraphQLCache(); await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); @@ -10279,10 +9657,7 @@ describe('ParseGraphQLServer', () => { }, }) ); - body.append( - 'map', - JSON.stringify({ 1: ['variables.fields.someField.upload'] }) - ); + body.append('map', JSON.stringify({ 1: ['variables.fields.someField.upload'] })); body.append('1', 'My File Content', { filename: 'myFileName.txt', contentType: 'text/plain', @@ -10297,12 +9672,10 @@ describe('ParseGraphQLServer', () => { const resText = await res.text(); const result = JSON.parse(resText); expect( - result.data.createSomeClassWithRequiredFile - .someClassWithRequiredFile.someField.name + result.data.createSomeClassWithRequiredFile.someClassWithRequiredFile.someField.name ).toEqual(jasmine.stringMatching(/_myFileName.txt$/)); expect( - result.data.createSomeClassWithRequiredFile - .someClassWithRequiredFile.someField.url + result.data.createSomeClassWithRequiredFile.someClassWithRequiredFile.someField.url ).toEqual(jasmine.stringMatching(/_myFileName.txt$/)); } catch (e) { handleError(e); @@ -10382,12 +9755,8 @@ describe('ParseGraphQLServer', () => { 'map', JSON.stringify({ 1: ['variables.fields.someFileField.upload'], - 2: [ - 'variables.fields.somePointerField.createAndLink.someFileField.upload', - ], - 3: [ - 'variables.fields.someRelationField.createAndAdd.0.someFileField.upload', - ], + 2: ['variables.fields.somePointerField.createAndLink.someFileField.upload'], + 3: ['variables.fields.someRelationField.createAndAdd.0.someFileField.upload'], }) ); body.append('1', 'My File Content someFileField', { @@ -10410,16 +9779,14 @@ describe('ParseGraphQLServer', () => { }); expect(res.status).toEqual(200); const result = await res.json(); + expect(result.data.createSomeClass.someClass.someFileField.name).toEqual( + jasmine.stringMatching(/_someFileField.txt$/) + ); + expect(result.data.createSomeClass.someClass.somePointerField.someFileField.name).toEqual( + jasmine.stringMatching(/_somePointerField.txt$/) + ); expect( - result.data.createSomeClass.someClass.someFileField.name - ).toEqual(jasmine.stringMatching(/_someFileField.txt$/)); - expect( - result.data.createSomeClass.someClass.somePointerField.someFileField - .name - ).toEqual(jasmine.stringMatching(/_somePointerField.txt$/)); - expect( - result.data.createSomeClass.someClass.someRelationField.edges[0] - .node.someFileField.name + result.data.createSomeClass.someClass.someRelationField.edges[0].node.someFileField.name ).toEqual(jasmine.stringMatching(/_someRelationField.txt$/)); }); @@ -10450,10 +9817,7 @@ describe('ParseGraphQLServer', () => { }, }) ); - body.append( - 'map', - JSON.stringify({ 1: ['variables.input.upload'] }) - ); + body.append('map', JSON.stringify({ 1: ['variables.input.upload'] })); body.append('1', 'My File Content', { // No extension, the system should add it from mimetype filename: 'myFileName', @@ -10506,10 +9870,7 @@ describe('ParseGraphQLServer', () => { body.append( '1', // In this test file parse server is setup with 1kb limit - Buffer.alloc( - parseGraphQLServer._transformMaxUploadSizeToBytes('2kb'), - 1 - ), + Buffer.alloc(parseGraphQLServer._transformMaxUploadSizeToBytes('2kb'), 1), { filename: 'myFileName.txt', contentType: 'text/plain', @@ -10539,9 +9900,7 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass( - input: { name: "SomeClass", schemaFields: $schemaFields } - ) { + createClass(input: { name: "SomeClass", schemaFields: $schemaFields }) { clientMutationId } } @@ -10618,9 +9977,7 @@ describe('ParseGraphQLServer', () => { // Checks class query results expect(someClasses.edges.length).toEqual(1); - expect(someClasses.edges[0].node.someObjectField).toEqual( - someObjectFieldValue - ); + expect(someClasses.edges[0].node.someObjectField).toEqual(someObjectFieldValue); } catch (e) { handleError(e); } @@ -10733,21 +10090,16 @@ describe('ParseGraphQLServer', () => { }, }); - const { obj1, obj2, onlyObj1, onlyObj2, all, none } = - queryResult.data; + const { obj1, obj2, onlyObj1, onlyObj2, all, none } = queryResult.data; expect(obj1.someObjectField).toEqual(someObjectFieldValue1); expect(obj2.someObjectField).toEqual(someObjectFieldValue2); // Checks class query results expect(onlyObj1.edges.length).toEqual(1); - expect(onlyObj1.edges[0].node.someObjectField).toEqual( - someObjectFieldValue1 - ); + expect(onlyObj1.edges[0].node.someObjectField).toEqual(someObjectFieldValue1); expect(onlyObj2.edges.length).toEqual(1); - expect(onlyObj2.edges[0].node.someObjectField).toEqual( - someObjectFieldValue2 - ); + expect(onlyObj2.edges[0].node.someObjectField).toEqual(someObjectFieldValue2); expect(all.edges.length).toEqual(2); expect(none.edges.length).toEqual(0); } catch (e) { @@ -10774,9 +10126,7 @@ describe('ParseGraphQLServer', () => { createClass( input: { name: "SomeClass" - schemaFields: { - addObjects: [{ name: "someObjectField" }] - } + schemaFields: { addObjects: [{ name: "someObjectField" }] } } ) { clientMutationId @@ -10873,12 +10223,10 @@ describe('ParseGraphQLServer', () => { const { edges } = someClasses; expect(edges.length).toEqual(2); expect( - edges.find(result => result.node.id === create1.someClass.id).node - .someObjectField + edges.find(result => result.node.id === create1.someClass.id).node.someObjectField ).toEqual(someObjectFieldValue1); expect( - edges.find(result => result.node.id === create2.someClass.id).node - .someObjectField + edges.find(result => result.node.id === create2.someClass.id).node.someObjectField ).toEqual(someObjectFieldValue2); } catch (e) { handleError(e); @@ -10887,20 +10235,12 @@ describe('ParseGraphQLServer', () => { it('should support array values', async () => { try { - const someArrayFieldValue = [ - 1, - 'foo', - ['bar'], - { lorem: 'ipsum' }, - true, - ]; + const someArrayFieldValue = [1, 'foo', ['bar'], { lorem: 'ipsum' }, true]; await apolloClient.mutate({ mutation: gql` mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass( - input: { name: "SomeClass", schemaFields: $schemaFields } - ) { + createClass(input: { name: "SomeClass", schemaFields: $schemaFields }) { clientMutationId } } @@ -10970,9 +10310,7 @@ describe('ParseGraphQLServer', () => { const { someArrayField } = getResult.data.someClass; expect(Array.isArray(someArrayField)).toBeTruthy(); - expect(someArrayField.map(element => element.value)).toEqual( - someArrayFieldValue - ); + expect(someArrayField.map(element => element.value)).toEqual(someArrayFieldValue); expect(getResult.data.someClasses.edges.length).toEqual(1); } catch (e) { handleError(e); @@ -11018,10 +10356,7 @@ describe('ParseGraphQLServer', () => { input: { name: "SomeClass" schemaFields: { - addStrings: [ - { name: "someStringField" } - { name: "someNullField" } - ] + addStrings: [{ name: "someStringField" }, { name: "someNullField" }] addNumbers: [{ name: "someNumberField" }] addBooleans: [{ name: "someBooleanField" }] addObjects: [{ name: "someObjectField" }] @@ -11064,10 +10399,7 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` - mutation UpdateSomeObject( - $id: ID! - $fields: UpdateSomeClassFieldsInput - ) { + mutation UpdateSomeObject($id: ID!, $fields: UpdateSomeClassFieldsInput) { updateSomeClass(input: { id: $id, fields: $fields }) { clientMutationId } @@ -11106,295 +10438,253 @@ describe('ParseGraphQLServer', () => { expect(getResult.data.someClass.someNumberField).toBeFalsy(); expect(getResult.data.someClass.someBooleanField).toBeFalsy(); expect(getResult.data.someClass.someObjectField).toBeFalsy(); - expect(getResult.data.someClass.someNullField).toEqual( - 'now it has a string' - ); + expect(getResult.data.someClass.someNullField).toEqual('now it has a string'); } catch (e) { handleError(e); } }); - it_id('43303db7-c5a7-4bc0-91c3-57e03fffa225')(it)( - 'should support Bytes', - async () => { - try { - const someFieldValue = 'aGVsbG8gd29ybGQ='; + it_id('43303db7-c5a7-4bc0-91c3-57e03fffa225')(it)('should support Bytes', async () => { + try { + const someFieldValue = 'aGVsbG8gd29ybGQ='; - await apolloClient.mutate({ - mutation: gql` - mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass( - input: { name: "SomeClass", schemaFields: $schemaFields } - ) { - clientMutationId - } + await apolloClient.mutate({ + mutation: gql` + mutation CreateClass($schemaFields: SchemaFieldsInput) { + createClass(input: { name: "SomeClass", schemaFields: $schemaFields }) { + clientMutationId } - `, - variables: { - schemaFields: { - addBytes: [{ name: 'someField' }], - }, + } + `, + variables: { + schemaFields: { + addBytes: [{ name: 'someField' }], }, - context: { - headers: { - 'X-Parse-Master-Key': 'test', - }, + }, + context: { + headers: { + 'X-Parse-Master-Key': 'test', }, - }); + }, + }); - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const schema = await new Parse.Schema('SomeClass').get(); - expect(schema.fields.someField.type).toEqual('Bytes'); + const schema = await new Parse.Schema('SomeClass').get(); + expect(schema.fields.someField.type).toEqual('Bytes'); - const createResult = await apolloClient.mutate({ - mutation: gql` - mutation CreateSomeObject( - $fields1: CreateSomeClassFieldsInput - $fields2: CreateSomeClassFieldsInput - ) { - createSomeClass1: createSomeClass( - input: { fields: $fields1 } - ) { - someClass { - id - } - } - createSomeClass2: createSomeClass( - input: { fields: $fields2 } - ) { - someClass { - id - } + const createResult = await apolloClient.mutate({ + mutation: gql` + mutation CreateSomeObject( + $fields1: CreateSomeClassFieldsInput + $fields2: CreateSomeClassFieldsInput + ) { + createSomeClass1: createSomeClass(input: { fields: $fields1 }) { + someClass { + id } } - `, - variables: { - fields1: { - someField: someFieldValue, - }, - fields2: { - someField: someFieldValue, - }, + createSomeClass2: createSomeClass(input: { fields: $fields2 }) { + someClass { + id + } + } + } + `, + variables: { + fields1: { + someField: someFieldValue, }, - }); + fields2: { + someField: someFieldValue, + }, + }, + }); - const getResult = await apolloClient.query({ - query: gql` - query GetSomeObject($id: ID!, $someFieldValue: Bytes) { - someClass(id: $id) { - someField - } - someClasses( - where: { someField: { equalTo: $someFieldValue } } - ) { - edges { - node { - id - someField - } + const getResult = await apolloClient.query({ + query: gql` + query GetSomeObject($id: ID!, $someFieldValue: Bytes) { + someClass(id: $id) { + someField + } + someClasses(where: { someField: { equalTo: $someFieldValue } }) { + edges { + node { + id + someField } } } - `, - variables: { - id: createResult.data.createSomeClass1.someClass.id, - someFieldValue, - }, - }); + } + `, + variables: { + id: createResult.data.createSomeClass1.someClass.id, + someFieldValue, + }, + }); - expect(typeof getResult.data.someClass.someField).toEqual( - 'string' - ); - expect(getResult.data.someClass.someField).toEqual( - someFieldValue - ); - expect(getResult.data.someClasses.edges.length).toEqual(2); - } catch (e) { - handleError(e); - } + expect(typeof getResult.data.someClass.someField).toEqual('string'); + expect(getResult.data.someClass.someField).toEqual(someFieldValue); + expect(getResult.data.someClasses.edges.length).toEqual(2); + } catch (e) { + handleError(e); } - ); + }); - it_id('6a253e47-6959-4427-b841-c0c1fa77cf01')(it)( - 'should support Geo Points', - async () => { - try { - const someFieldValue = { - __typename: 'GeoPoint', - latitude: 45, - longitude: 45, - }; + it_id('6a253e47-6959-4427-b841-c0c1fa77cf01')(it)('should support Geo Points', async () => { + try { + const someFieldValue = { + __typename: 'GeoPoint', + latitude: 45, + longitude: 45, + }; - await apolloClient.mutate({ - mutation: gql` - mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass( - input: { name: "SomeClass", schemaFields: $schemaFields } - ) { - clientMutationId - } + await apolloClient.mutate({ + mutation: gql` + mutation CreateClass($schemaFields: SchemaFieldsInput) { + createClass(input: { name: "SomeClass", schemaFields: $schemaFields }) { + clientMutationId } - `, - variables: { - schemaFields: { - addGeoPoint: { name: 'someField' }, - }, + } + `, + variables: { + schemaFields: { + addGeoPoint: { name: 'someField' }, }, - context: { - headers: { - 'X-Parse-Master-Key': 'test', - }, + }, + context: { + headers: { + 'X-Parse-Master-Key': 'test', }, - }); + }, + }); - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); - const schema = await new Parse.Schema('SomeClass').get(); - expect(schema.fields.someField.type).toEqual('GeoPoint'); + const schema = await new Parse.Schema('SomeClass').get(); + expect(schema.fields.someField.type).toEqual('GeoPoint'); - const createResult = await apolloClient.mutate({ - mutation: gql` - mutation CreateSomeObject( - $fields: CreateSomeClassFieldsInput - ) { - createSomeClass(input: { fields: $fields }) { - someClass { - id - } + const createResult = await apolloClient.mutate({ + mutation: gql` + mutation CreateSomeObject($fields: CreateSomeClassFieldsInput) { + createSomeClass(input: { fields: $fields }) { + someClass { + id } } - `, - variables: { - fields: { - someField: { - latitude: someFieldValue.latitude, - longitude: someFieldValue.longitude, - }, + } + `, + variables: { + fields: { + someField: { + latitude: someFieldValue.latitude, + longitude: someFieldValue.longitude, }, }, - }); + }, + }); - const getResult = await apolloClient.query({ - query: gql` - query GetSomeObject($id: ID!) { - someClass(id: $id) { - someField { - latitude - longitude - } + const getResult = await apolloClient.query({ + query: gql` + query GetSomeObject($id: ID!) { + someClass(id: $id) { + someField { + latitude + longitude } - someClasses(where: { someField: { exists: true } }) { - edges { - node { - id - someField { - latitude - longitude - } + } + someClasses(where: { someField: { exists: true } }) { + edges { + node { + id + someField { + latitude + longitude } } } } - `, - variables: { - id: createResult.data.createSomeClass.someClass.id, - }, - }); + } + `, + variables: { + id: createResult.data.createSomeClass.someClass.id, + }, + }); - expect(typeof getResult.data.someClass.someField).toEqual( - 'object' - ); - expect(getResult.data.someClass.someField).toEqual( - someFieldValue - ); - expect(getResult.data.someClasses.edges.length).toEqual(1); + expect(typeof getResult.data.someClass.someField).toEqual('object'); + expect(getResult.data.someClass.someField).toEqual(someFieldValue); + expect(getResult.data.someClasses.edges.length).toEqual(1); - const getGeoWhere = await apolloClient.query({ - query: gql` - query GeoQuery($latitude: Float!, $longitude: Float!) { - nearSphere: someClasses( - where: { - someField: { - nearSphere: { - latitude: $latitude - longitude: $longitude - } - } - } - ) { - edges { - node { - id - } + const getGeoWhere = await apolloClient.query({ + query: gql` + query GeoQuery($latitude: Float!, $longitude: Float!) { + nearSphere: someClasses( + where: { + someField: { nearSphere: { latitude: $latitude, longitude: $longitude } } + } + ) { + edges { + node { + id } } - geoWithin: someClasses( - where: { - someField: { - geoWithin: { - centerSphere: { - distance: 10 - center: { - latitude: $latitude - longitude: $longitude - } - } + } + geoWithin: someClasses( + where: { + someField: { + geoWithin: { + centerSphere: { + distance: 10 + center: { latitude: $latitude, longitude: $longitude } } } } - ) { - edges { - node { - id - } + } + ) { + edges { + node { + id } } - within: someClasses( - where: { - someField: { - within: { - box: { - bottomLeft: { - latitude: $latitude - longitude: $longitude - } - upperRight: { - latitude: $latitude - longitude: $longitude - } - } + } + within: someClasses( + where: { + someField: { + within: { + box: { + bottomLeft: { latitude: $latitude, longitude: $longitude } + upperRight: { latitude: $latitude, longitude: $longitude } } } } - ) { - edges { - node { - id - } + } + ) { + edges { + node { + id } } } - `, - variables: { - latitude: 45, - longitude: 45, - }, - }); - expect(getGeoWhere.data.nearSphere.edges[0].node.id).toEqual( - createResult.data.createSomeClass.someClass.id - ); - expect(getGeoWhere.data.geoWithin.edges[0].node.id).toEqual( - createResult.data.createSomeClass.someClass.id - ); - expect(getGeoWhere.data.within.edges[0].node.id).toEqual( - createResult.data.createSomeClass.someClass.id - ); - } catch (e) { - handleError(e); - } + } + `, + variables: { + latitude: 45, + longitude: 45, + }, + }); + expect(getGeoWhere.data.nearSphere.edges[0].node.id).toEqual( + createResult.data.createSomeClass.someClass.id + ); + expect(getGeoWhere.data.geoWithin.edges[0].node.id).toEqual( + createResult.data.createSomeClass.someClass.id + ); + expect(getGeoWhere.data.within.edges[0].node.id).toEqual( + createResult.data.createSomeClass.someClass.id + ); + } catch (e) { + handleError(e); } - ); + }); it('should support Polygons', async () => { try { @@ -11411,9 +10701,7 @@ describe('ParseGraphQLServer', () => { await apolloClient.mutate({ mutation: gql` mutation CreateClass($schemaFields: SchemaFieldsInput) { - createClass( - input: { name: "SomeClass", schemaFields: $schemaFields } - ) { + createClass(input: { name: "SomeClass", schemaFields: $schemaFields }) { clientMutationId } } @@ -11479,9 +10767,7 @@ describe('ParseGraphQLServer', () => { }, }); - expect(typeof getResult.data.someClass.somePolygonField).toEqual( - 'object' - ); + expect(typeof getResult.data.someClass.somePolygonField).toEqual('object'); expect(getResult.data.someClass.somePolygonField).toEqual( somePolygonFieldValue.map(geoPoint => ({ ...geoPoint, @@ -11492,11 +10778,7 @@ describe('ParseGraphQLServer', () => { const getIntersect = await apolloClient.query({ query: gql` query IntersectQuery($point: GeoPointInput!) { - someClasses( - where: { - somePolygonField: { geoIntersects: { point: $point } } - } - ) { + someClasses(where: { somePolygonField: { geoIntersects: { point: $point } } }) { edges { node { id @@ -11570,9 +10852,7 @@ describe('ParseGraphQLServer', () => { }, }); - expect(getResult.data.someClass.someField).toEqual( - someFieldValue.base64 - ); + expect(getResult.data.someClass.someField).toEqual(someFieldValue.base64); const updatedSomeFieldValue = { __type: 'Bytes', @@ -11581,10 +10861,7 @@ describe('ParseGraphQLServer', () => { const updatedResult = await apolloClient.mutate({ mutation: gql` - mutation UpdateSomeObject( - $id: ID! - $fields: UpdateSomeClassFieldsInput - ) { + mutation UpdateSomeObject($id: ID!, $fields: UpdateSomeClassFieldsInput) { updateSomeClass(input: { id: $id, fields: $fields }) { someClass { updatedAt @@ -11625,9 +10902,7 @@ describe('ParseGraphQLServer', () => { }); const findResults = findResult.data.someClasses.edges; expect(findResults.length).toBe(1); - expect(findResults[0].node.id).toBe( - createResult.data.createSomeClass.someClass.id - ); + expect(findResults[0].node.id).toBe(createResult.data.createSomeClass.someClass.id); }); }); @@ -11802,9 +11077,7 @@ describe('ParseGraphQLServer', () => { `, }); parseGraphQLServer.applyGraphQL(expressApp); - await new Promise(resolve => - httpServer.listen({ port: 13377 }, resolve) - ); + await new Promise(resolve => httpServer.listen({ port: 13377 }, resolve)); const httpLink = await createUploadLink({ uri: 'http://localhost:13377/graphql', fetch, @@ -11858,8 +11131,7 @@ describe('ParseGraphQLServer', () => { }); it('order option should continue working', async () => { - const schemaController = - await parseServer.config.databaseController.loadSchema(); + const schemaController = await parseServer.config.databaseController.loadSchema(); await schemaController.addClassIfNotExists('SuperCar', { engine: { type: 'String' }, @@ -12029,9 +11301,7 @@ describe('ParseGraphQLServer', () => { }); parseGraphQLServer.applyGraphQL(expressApp); - await new Promise(resolve => - httpServer.listen({ port: 13377 }, resolve) - ); + await new Promise(resolve => httpServer.listen({ port: 13377 }, resolve)); const httpLink = await createUploadLink({ uri: 'http://localhost:13377/graphql', fetch, @@ -12093,13 +11363,9 @@ describe('ParseGraphQLServer', () => { } `, }); - expect(result.data.customQueryWithAutoTypeReturn.objectId).toEqual( - obj.id - ); + expect(result.data.customQueryWithAutoTypeReturn.objectId).toEqual(obj.id); expect(result.data.customQueryWithAutoTypeReturn.name).toEqual('aname'); - expect(result.data.customQueryWithAutoTypeReturn.nameUpperCase).toEqual( - 'ANAME' - ); + expect(result.data.customQueryWithAutoTypeReturn.nameUpperCase).toEqual('ANAME'); expect(result.data.customQueryWithAutoTypeReturn.type).toEqual('robot'); }); @@ -12163,9 +11429,7 @@ describe('ParseGraphQLServer', () => { }); expect(result.data.q1.edges.length).toEqual(1); expect(result.data.q2.edges.length).toEqual(1); - expect(result.data.q1.edges[0].node.id).toEqual( - result.data.q2.edges[0].node.id - ); + expect(result.data.q1.edges[0].node.id).toEqual(result.data.q2.edges[0].node.id); }); it('can resolve a custom extend type', async () => { @@ -12205,9 +11469,7 @@ describe('ParseGraphQLServer', () => { variables: { id: obj.id, name: 'anewname', type: 'human' }, mutation: gql` mutation someClass($id: ID!, $name: String!, $type: TypeEnum!) { - updateSomeClass( - input: { id: $id, fields: { name: $name, type: $type } } - ) { + updateSomeClass(input: { id: $id, fields: { name: $name, type: $type } }) { someClass { nameUpperCase type @@ -12216,9 +11478,7 @@ describe('ParseGraphQLServer', () => { } `, }); - expect(result3.data.updateSomeClass.someClass.nameUpperCase).toEqual( - 'ANEWNAME' - ); + expect(result3.data.updateSomeClass.someClass.nameUpperCase).toEqual('ANEWNAME'); expect(result3.data.updateSomeClass.someClass.type).toEqual('human'); }); }); @@ -12236,14 +11496,11 @@ describe('ParseGraphQLServer', () => { httpServer = http.createServer(expressApp); parseGraphQLServer = new ParseGraphQLServer(parseServer, { graphQLPath: '/graphql', - graphQLCustomTypeDefs: ({ autoSchema }) => - mergeSchemas({ schemas: [autoSchema] }), + graphQLCustomTypeDefs: ({ autoSchema }) => mergeSchemas({ schemas: [autoSchema] }), }); parseGraphQLServer.applyGraphQL(expressApp); - await new Promise(resolve => - httpServer.listen({ port: 13377 }, resolve) - ); + await new Promise(resolve => httpServer.listen({ port: 13377 }, resolve)); const httpLink = await createUploadLink({ uri: 'http://localhost:13377/graphql', fetch, diff --git a/spec/ParseHooks.spec.js b/spec/ParseHooks.spec.js index a3958ee9b0..43d287bf6a 100644 --- a/spec/ParseHooks.spec.js +++ b/spec/ParseHooks.spec.js @@ -53,136 +53,121 @@ describe('Hooks', () => { ); }); - it_id('26c9a13d-3d71-452e-a91c-9a4589be021c')(it)( - 'should CRUD a function registration', - done => { - // Create - Parse.Hooks.createFunction('My-Test-Function', 'http://someurl') - .then(response => { - expect(response.functionName).toBe('My-Test-Function'); - expect(response.url).toBe('http://someurl'); - // Find - return Parse.Hooks.getFunction('My-Test-Function'); - }) - .then(response => { - expect(response.objectId).toBeUndefined(); - expect(response.url).toBe('http://someurl'); - return Parse.Hooks.updateFunction( - 'My-Test-Function', - 'http://anotherurl' - ); - }) - .then(res => { - expect(res.objectId).toBeUndefined(); - expect(res.functionName).toBe('My-Test-Function'); - expect(res.url).toBe('http://anotherurl'); - // delete - return Parse.Hooks.removeFunction('My-Test-Function'); - }) - .then(() => { - // Find again! but should be deleted - return Parse.Hooks.getFunction('My-Test-Function').then( - res => { - fail('Failed to delete hook'); - fail(res); - done(); - return Promise.resolve(); - }, - err => { - expect(err.code).toBe(143); - expect(err.message).toBe( - 'no function named: My-Test-Function is defined' - ); - done(); - return Promise.resolve(); - } - ); - }) - .catch(error => { - jfail(error); - done(); - }); - } - ); - - it_id('7a81069e-2ee9-47fb-8e27-1120eda09e99')(it)( - 'should CRUD a trigger registration', - done => { - // Create - Parse.Hooks.createTrigger('MyClass', 'beforeDelete', 'http://someurl') - .then( + it_id('26c9a13d-3d71-452e-a91c-9a4589be021c')(it)('should CRUD a function registration', done => { + // Create + Parse.Hooks.createFunction('My-Test-Function', 'http://someurl') + .then(response => { + expect(response.functionName).toBe('My-Test-Function'); + expect(response.url).toBe('http://someurl'); + // Find + return Parse.Hooks.getFunction('My-Test-Function'); + }) + .then(response => { + expect(response.objectId).toBeUndefined(); + expect(response.url).toBe('http://someurl'); + return Parse.Hooks.updateFunction('My-Test-Function', 'http://anotherurl'); + }) + .then(res => { + expect(res.objectId).toBeUndefined(); + expect(res.functionName).toBe('My-Test-Function'); + expect(res.url).toBe('http://anotherurl'); + // delete + return Parse.Hooks.removeFunction('My-Test-Function'); + }) + .then(() => { + // Find again! but should be deleted + return Parse.Hooks.getFunction('My-Test-Function').then( res => { - expect(res.className).toBe('MyClass'); - expect(res.triggerName).toBe('beforeDelete'); - expect(res.url).toBe('http://someurl'); - // Find - return Parse.Hooks.getTrigger('MyClass', 'beforeDelete'); - }, - err => { - fail(err); + fail('Failed to delete hook'); + fail(res); done(); - } - ) - .then( - res => { - expect(res).not.toBe(null); - expect(res).not.toBe(undefined); - expect(res.objectId).toBeUndefined(); - expect(res.url).toBe('http://someurl'); - // delete - return Parse.Hooks.updateTrigger( - 'MyClass', - 'beforeDelete', - 'http://anotherurl' - ); + return Promise.resolve(); }, err => { - jfail(err); + expect(err.code).toBe(143); + expect(err.message).toBe('no function named: My-Test-Function is defined'); done(); + return Promise.resolve(); } - ) - .then( - res => { - expect(res.className).toBe('MyClass'); - expect(res.url).toBe('http://anotherurl'); - expect(res.objectId).toBeUndefined(); + ); + }) + .catch(error => { + jfail(error); + done(); + }); + }); - return Parse.Hooks.removeTrigger('MyClass', 'beforeDelete'); - }, - err => { - jfail(err); - done(); - } - ) - .then( - () => { - // Find again! but should be deleted - return Parse.Hooks.getTrigger('MyClass', 'beforeDelete'); - }, - err => { - jfail(err); - done(); - } - ) - .then( - function () { - fail('should not succeed'); - done(); - }, - err => { - if (err) { - expect(err).not.toBe(null); - expect(err).not.toBe(undefined); - expect(err.code).toBe(143); - expect(err.message).toBe('class MyClass does not exist'); - } else { - fail('should have errored'); - } - done(); + it_id('7a81069e-2ee9-47fb-8e27-1120eda09e99')(it)('should CRUD a trigger registration', done => { + // Create + Parse.Hooks.createTrigger('MyClass', 'beforeDelete', 'http://someurl') + .then( + res => { + expect(res.className).toBe('MyClass'); + expect(res.triggerName).toBe('beforeDelete'); + expect(res.url).toBe('http://someurl'); + // Find + return Parse.Hooks.getTrigger('MyClass', 'beforeDelete'); + }, + err => { + fail(err); + done(); + } + ) + .then( + res => { + expect(res).not.toBe(null); + expect(res).not.toBe(undefined); + expect(res.objectId).toBeUndefined(); + expect(res.url).toBe('http://someurl'); + // delete + return Parse.Hooks.updateTrigger('MyClass', 'beforeDelete', 'http://anotherurl'); + }, + err => { + jfail(err); + done(); + } + ) + .then( + res => { + expect(res.className).toBe('MyClass'); + expect(res.url).toBe('http://anotherurl'); + expect(res.objectId).toBeUndefined(); + + return Parse.Hooks.removeTrigger('MyClass', 'beforeDelete'); + }, + err => { + jfail(err); + done(); + } + ) + .then( + () => { + // Find again! but should be deleted + return Parse.Hooks.getTrigger('MyClass', 'beforeDelete'); + }, + err => { + jfail(err); + done(); + } + ) + .then( + function () { + fail('should not succeed'); + done(); + }, + err => { + if (err) { + expect(err).not.toBe(null); + expect(err).not.toBe(undefined); + expect(err.code).toBe(143); + expect(err.message).toBe('class MyClass does not exist'); + } else { + fail('should have errored'); } - ); - } - ); + done(); + } + ); + }); it('should fail to register hooks without Master Key', done => { request({ @@ -209,10 +194,7 @@ describe('Hooks', () => { .then(() => jasmine.timeout()) .then( () => { - return Parse.Hooks.createFunction( - 'my_new_function', - 'http://url.com' - ); + return Parse.Hooks.createFunction('my_new_function', 'http://url.com'); }, () => { fail('should create a new function'); @@ -227,9 +209,7 @@ describe('Hooks', () => { expect(err).not.toBe(null); if (err) { expect(err.code).toBe(143); - expect(err.message).toBe( - 'function name: my_new_function already exists' - ); + expect(err.message).toBe('function name: my_new_function already exists'); } return Parse.Hooks.removeFunction('my_new_function'); } @@ -252,11 +232,7 @@ describe('Hooks', () => { Parse.Hooks.createTrigger('MyClass', 'beforeSave', 'http://url.com') .then( () => { - return Parse.Hooks.createTrigger( - 'MyClass', - 'beforeSave', - 'http://url.com' - ); + return Parse.Hooks.createTrigger('MyClass', 'beforeSave', 'http://url.com'); }, () => { fail('should create a new trigger'); @@ -271,9 +247,7 @@ describe('Hooks', () => { expect(err).not.toBe(null); if (err) { expect(err.code).toBe(143); - expect(err.message).toBe( - 'class MyClass already has trigger beforeSave' - ); + expect(err.message).toBe('class MyClass already has trigger beforeSave'); } return Parse.Hooks.removeTrigger('MyClass', 'beforeSave'); } @@ -301,9 +275,7 @@ describe('Hooks', () => { expect(err).not.toBe(null); if (err) { expect(err.code).toBe(143); - expect(err.message).toBe( - 'no function named: A_COOL_FUNCTION is defined' - ); + expect(err.message).toBe('no function named: A_COOL_FUNCTION is defined'); } return Parse.Hooks.getFunction('A_COOL_FUNCTION'); } @@ -318,9 +290,7 @@ describe('Hooks', () => { expect(err).not.toBe(null); if (err) { expect(err.code).toBe(143); - expect(err.message).toBe( - 'no function named: A_COOL_FUNCTION is defined' - ); + expect(err.message).toBe('no function named: A_COOL_FUNCTION is defined'); } done(); } @@ -400,18 +370,9 @@ describe('Hooks', () => { const promises = []; for (let i = 0; i < 5; i++) { promises.push( - Parse.Hooks.createTrigger( - 'MyClass' + i, - 'beforeSave', - 'http://url.com/beforeSave/' + i - ) - ); - promises.push( - Parse.Hooks.createFunction( - 'AFunction' + i, - 'http://url.com/function' + i - ) + Parse.Hooks.createTrigger('MyClass' + i, 'beforeSave', 'http://url.com/beforeSave/' + i) ); + promises.push(Parse.Hooks.createFunction('AFunction' + i, 'http://url.com/function' + i)); } Promise.all(promises) @@ -419,22 +380,12 @@ describe('Hooks', () => { function () { for (let i = 0; i < 5; i++) { // Delete everything from memory, as the server just started - triggers.removeTrigger( - 'beforeSave', - 'MyClass' + i, - Parse.applicationId - ); + triggers.removeTrigger('beforeSave', 'MyClass' + i, Parse.applicationId); triggers.removeFunction('AFunction' + i, Parse.applicationId); expect( - triggers.getTrigger( - 'MyClass' + i, - 'beforeSave', - Parse.applicationId - ) - ).toBeUndefined(); - expect( - triggers.getFunction('AFunction' + i, Parse.applicationId) + triggers.getTrigger('MyClass' + i, 'beforeSave', Parse.applicationId) ).toBeUndefined(); + expect(triggers.getFunction('AFunction' + i, Parse.applicationId)).toBeUndefined(); } const hooksController = new HooksController( Parse.applicationId, @@ -452,11 +403,7 @@ describe('Hooks', () => { function () { for (let i = 0; i < 5; i++) { expect( - triggers.getTrigger( - 'MyClass' + i, - 'beforeSave', - Parse.applicationId - ) + triggers.getTrigger('MyClass' + i, 'beforeSave', Parse.applicationId) ).not.toBeUndefined(); expect( triggers.getFunction('AFunction' + i, Parse.applicationId) @@ -480,10 +427,7 @@ describe('Hooks', () => { res.json({ success: 'OK!' }); }); - Parse.Hooks.createFunction( - 'SOME_TEST_FUNCTION', - hookServerURL + '/SomeFunction' - ) + Parse.Hooks.createFunction('SOME_TEST_FUNCTION', hookServerURL + '/SomeFunction') .then( function () { return Parse.Cloud.run('SOME_TEST_FUNCTION'); @@ -515,10 +459,7 @@ describe('Hooks', () => { res.json({ error: { code: 1337, error: 'hacking that one!' } }); }); // The function is deleted as the DB is dropped between calls - Parse.Hooks.createFunction( - 'SOME_TEST_FUNCTION', - hookServerURL + '/SomeFunctionError' - ) + Parse.Hooks.createFunction('SOME_TEST_FUNCTION', hookServerURL + '/SomeFunctionError') .then( function () { return Parse.Cloud.run('SOME_TEST_FUNCTION'); @@ -559,10 +500,7 @@ describe('Hooks', () => { } }); - Parse.Hooks.createFunction( - 'SOME_TEST_FUNCTION', - hookServerURL + '/ExpectingKey' - ) + Parse.Hooks.createFunction('SOME_TEST_FUNCTION', hookServerURL + '/ExpectingKey') .then( function () { return Parse.Cloud.run('SOME_TEST_FUNCTION'); @@ -599,10 +537,7 @@ describe('Hooks', () => { } }); - Parse.Hooks.createFunction( - 'SOME_TEST_FUNCTION', - hookServerURL + '/ExpectingKeyAlso' - ) + Parse.Hooks.createFunction('SOME_TEST_FUNCTION', hookServerURL + '/ExpectingKeyAlso') .then( function () { return Parse.Cloud.run('SOME_TEST_FUNCTION'); @@ -645,11 +580,7 @@ describe('Hooks', () => { res.json({ success: object }); }); // The function is deleted as the DB is dropped between calls - Parse.Hooks.createTrigger( - 'SomeRandomObject', - 'beforeSave', - hookServerURL + '/BeforeSaveSome' - ) + Parse.Hooks.createTrigger('SomeRandomObject', 'beforeSave', hookServerURL + '/BeforeSaveSome') .then(function () { const obj = new Parse.Object('SomeRandomObject'); return obj.save(); @@ -716,11 +647,7 @@ describe('Hooks', () => { }); }); // The function is deleted as the DB is dropped between calls - Parse.Hooks.createTrigger( - 'SomeRandomObject', - 'afterSave', - hookServerURL + '/AfterSaveSome' - ) + Parse.Hooks.createTrigger('SomeRandomObject', 'afterSave', hookServerURL + '/AfterSaveSome') .then(function () { const obj = new Parse.Object('SomeRandomObject'); return obj.save(); @@ -729,9 +656,7 @@ describe('Hooks', () => { return new Promise(resolve => { setTimeout(() => { expect(triggerCount).toBe(1); - new Parse.Query('AnotherObject') - .get(newObjectId) - .then(r => resolve(r)); + new Parse.Query('AnotherObject').get(newObjectId).then(r => resolve(r)); }, 500); }); }) diff --git a/spec/ParseInstallation.spec.js b/spec/ParseInstallation.spec.js index 23baca454f..74b67bc6fa 100644 --- a/spec/ParseInstallation.spec.js +++ b/spec/ParseInstallation.spec.js @@ -10,19 +10,14 @@ const request = require('../lib/request'); let config; let database; -const defaultColumns = - require('../lib/Controllers/SchemaController').defaultColumns; +const defaultColumns = require('../lib/Controllers/SchemaController').defaultColumns; const delay = function delay(delay) { return new Promise(resolve => setTimeout(resolve, delay)); }; const installationSchema = { - fields: Object.assign( - {}, - defaultColumns._Default, - defaultColumns._Installation - ), + fields: Object.assign({}, defaultColumns._Default, defaultColumns._Installation), }; describe('Installations', () => { @@ -40,9 +35,7 @@ describe('Installations', () => { }; rest .create(config, auth.nobody(config), '_Installation', input) - .then(() => - database.adapter.find('_Installation', installationSchema, {}, {}) - ) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); const obj = results[0]; @@ -58,8 +51,7 @@ describe('Installations', () => { }); it('creates an ios installation with ids', done => { - const t = - '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; + const t = '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; const device = 'ios'; const input = { deviceToken: t, @@ -67,9 +59,7 @@ describe('Installations', () => { }; rest .create(config, auth.nobody(config), '_Installation', input) - .then(() => - database.adapter.find('_Installation', installationSchema, {}, {}) - ) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); const obj = results[0]; @@ -93,9 +83,7 @@ describe('Installations', () => { }; rest .create(config, auth.nobody(config), '_Installation', input) - .then(() => - database.adapter.find('_Installation', installationSchema, {}, {}) - ) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); const obj = results[0]; @@ -120,9 +108,7 @@ describe('Installations', () => { }; rest .create(config, auth.nobody(config), '_Installation', input) - .then(() => - database.adapter.find('_Installation', installationSchema, {}, {}) - ) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); const obj = results[0]; @@ -142,8 +128,7 @@ describe('Installations', () => { }); it('creates an ios installation with all fields', done => { - const t = - '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; + const t = '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; const device = 'ios'; const input = { deviceToken: t, @@ -152,9 +137,7 @@ describe('Installations', () => { }; rest .create(config, auth.nobody(config), '_Installation', input) - .then(() => - database.adapter.find('_Installation', installationSchema, {}, {}) - ) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); const obj = results[0]; @@ -261,8 +244,7 @@ describe('Installations', () => { }); it('creates an object with custom fields', done => { - const t = - '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; + const t = '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; const input = { deviceToken: t, deviceType: 'ios', @@ -271,9 +253,7 @@ describe('Installations', () => { }; rest .create(config, auth.nobody(config), '_Installation', input) - .then(() => - database.adapter.find('_Installation', installationSchema, {}, {}) - ) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); const obj = results[0]; @@ -289,8 +269,7 @@ describe('Installations', () => { it('merging when installationId already exists', done => { const installId1 = '12345678-abcd-abcd-abcd-123456789abc'; - const t = - '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; + const t = '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; const input = { deviceToken: t, deviceType: 'ios', @@ -301,9 +280,7 @@ describe('Installations', () => { let secondObject; rest .create(config, auth.nobody(config), '_Installation', input) - .then(() => - database.adapter.find('_Installation', installationSchema, {}, {}) - ) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); firstObject = results[0]; @@ -312,9 +289,7 @@ describe('Installations', () => { input['foo'] = 'bar'; return rest.create(config, auth.nobody(config), '_Installation', input); }) - .then(() => - database.adapter.find('_Installation', installationSchema, {}, {}) - ) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); secondObject = results[0]; @@ -330,8 +305,7 @@ describe('Installations', () => { it('merging when two objects both only have one id', done => { const installId = '12345678-abcd-abcd-abcd-123456789abc'; - const t = - '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; + const t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; const input1 = { installationId: installId, deviceType: 'ios', @@ -349,22 +323,13 @@ describe('Installations', () => { let secondObject; rest .create(config, auth.nobody(config), '_Installation', input1) - .then(() => - database.adapter.find('_Installation', installationSchema, {}, {}) - ) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); firstObject = results[0]; - return rest.create( - config, - auth.nobody(config), - '_Installation', - input2 - ); + return rest.create(config, auth.nobody(config), '_Installation', input2); }) - .then(() => - database.adapter.find('_Installation', installationSchema, {}, {}) - ) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(2); if (results[0]['_id'] == firstObject._id) { @@ -372,16 +337,9 @@ describe('Installations', () => { } else { secondObject = results[0]; } - return rest.create( - config, - auth.nobody(config), - '_Installation', - input3 - ); + return rest.create(config, auth.nobody(config), '_Installation', input3); }) - .then(() => - database.adapter.find('_Installation', installationSchema, {}, {}) - ) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); expect(results[0]['_id']).toEqual(secondObject._id); @@ -397,8 +355,7 @@ describe('Installations', () => { const installId1 = '11111111-abcd-abcd-abcd-123456789abc'; const installId2 = '22222222-abcd-abcd-abcd-123456789abc'; const installId3 = '33333333-abcd-abcd-abcd-123456789abc'; - const t = - '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; + const t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; const input = { installationId: installId1, deviceType: 'ios', @@ -449,48 +406,35 @@ describe('Installations', () => { }); }); - it_id('95955e90-04bc-4437-920e-b84bc30dba01')(it)( - 'updating with new channels', - done => { - const input = { - installationId: '12345678-abcd-abcd-abcd-123456789abc', - deviceType: 'android', - channels: ['foo', 'bar'], - }; - rest - .create(config, auth.nobody(config), '_Installation', input) - .then(() => - database.adapter.find('_Installation', installationSchema, {}, {}) - ) - .then(results => { - expect(results.length).toEqual(1); - const objectId = results[0].objectId; - const update = { - channels: ['baz'], - }; - return rest.update( - config, - auth.nobody(config), - '_Installation', - { objectId }, - update - ); - }) - .then(() => - database.adapter.find('_Installation', installationSchema, {}, {}) - ) - .then(results => { - expect(results.length).toEqual(1); - expect(results[0].channels.length).toEqual(1); - expect(results[0].channels[0]).toEqual('baz'); - done(); - }) - .catch(error => { - jfail(error); - done(); - }); - } - ); + it_id('95955e90-04bc-4437-920e-b84bc30dba01')(it)('updating with new channels', done => { + const input = { + installationId: '12345678-abcd-abcd-abcd-123456789abc', + deviceType: 'android', + channels: ['foo', 'bar'], + }; + rest + .create(config, auth.nobody(config), '_Installation', input) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .then(results => { + expect(results.length).toEqual(1); + const objectId = results[0].objectId; + const update = { + channels: ['baz'], + }; + return rest.update(config, auth.nobody(config), '_Installation', { objectId }, update); + }) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) + .then(results => { + expect(results.length).toEqual(1); + expect(results[0].channels.length).toEqual(1); + expect(results[0].channels[0]).toEqual('baz'); + done(); + }) + .catch(error => { + jfail(error); + done(); + }); + }); it('update android fails with new installation id', done => { const installId1 = '12345678-abcd-abcd-abcd-123456789abc'; @@ -502,9 +446,7 @@ describe('Installations', () => { }; rest .create(config, auth.nobody(config), '_Installation', input) - .then(() => - database.adapter.find('_Installation', installationSchema, {}, {}) - ) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); input = { installationId: installId2 }; @@ -527,10 +469,8 @@ describe('Installations', () => { }); it('update ios fails with new deviceToken and no installationId', done => { - const a = - '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; - const b = - '91433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; + const a = '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; + const b = '91433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; let input = { deviceToken: a, deviceType: 'ios', @@ -538,9 +478,7 @@ describe('Installations', () => { }; rest .create(config, auth.nobody(config), '_Installation', input) - .then(() => - database.adapter.find('_Installation', installationSchema, {}, {}) - ) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); input = { deviceToken: b }; @@ -563,10 +501,8 @@ describe('Installations', () => { it('update ios updates device token', done => { const installId = '12345678-abcd-abcd-abcd-123456789abc'; - const t = - '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; - const u = - '91433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; + const t = '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; + const u = '91433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306'; let input = { installationId: installId, deviceType: 'ios', @@ -575,9 +511,7 @@ describe('Installations', () => { }; rest .create(config, auth.nobody(config), '_Installation', input) - .then(() => - database.adapter.find('_Installation', installationSchema, {}, {}) - ) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); input = { @@ -593,9 +527,7 @@ describe('Installations', () => { input ); }) - .then(() => - database.adapter.find('_Installation', installationSchema, {}, {}) - ) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); expect(results[0].deviceToken).toEqual(u); @@ -616,9 +548,7 @@ describe('Installations', () => { }; rest .create(config, auth.nobody(config), '_Installation', input) - .then(() => - database.adapter.find('_Installation', installationSchema, {}, {}) - ) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); input = { @@ -651,9 +581,7 @@ describe('Installations', () => { }; rest .create(config, auth.nobody(config), '_Installation', input) - .then(() => - database.adapter.find('_Installation', installationSchema, {}, {}) - ) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); input = { @@ -667,9 +595,7 @@ describe('Installations', () => { input ); }) - .then(() => - database.adapter.find('_Installation', installationSchema, {}, {}) - ) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); expect(results[0]['custom']).toEqual('allowed'); @@ -680,8 +606,7 @@ describe('Installations', () => { it('update android device token with duplicate device token', async () => { const installId1 = '11111111-abcd-abcd-abcd-123456789abc'; const installId2 = '22222222-abcd-abcd-abcd-123456789abc'; - const t = - '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; + const t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; let input = { installationId: installId1, @@ -740,8 +665,7 @@ describe('Installations', () => { it('update ios device token with duplicate device token', done => { const installId1 = '11111111-abcd-abcd-abcd-123456789abc'; const installId2 = '22222222-abcd-abcd-abcd-123456789abc'; - const t = - '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; + const t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; let input = { installationId: installId1, deviceToken: t, @@ -819,8 +743,7 @@ describe('Installations', () => { xit('update ios device token with duplicate token different app', done => { const installId1 = '11111111-abcd-abcd-abcd-123456789abc'; const installId2 = '22222222-abcd-abcd-abcd-123456789abc'; - const t = - '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; + const t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; const input = { installationId: installId1, deviceToken: t, @@ -834,9 +757,7 @@ describe('Installations', () => { input.appIdentifier = 'bar'; return rest.create(config, auth.nobody(config), '_Installation', input); }) - .then(() => - database.adapter.find('_Installation', installationSchema, {}, {}) - ) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { // The first object should have been deleted during merge expect(results.length).toEqual(1); @@ -851,17 +772,14 @@ describe('Installations', () => { it('update ios token and channels', done => { const installId = '12345678-abcd-abcd-abcd-123456789abc'; - const t = - '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; + const t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; let input = { installationId: installId, deviceType: 'ios', }; rest .create(config, auth.nobody(config), '_Installation', input) - .then(() => - database.adapter.find('_Installation', installationSchema, {}, {}) - ) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); input = { @@ -876,9 +794,7 @@ describe('Installations', () => { input ); }) - .then(() => - database.adapter.find('_Installation', installationSchema, {}, {}) - ) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); expect(results[0].installationId).toEqual(installId); @@ -894,8 +810,7 @@ describe('Installations', () => { it('update ios linking two existing objects', done => { const installId = '12345678-abcd-abcd-abcd-123456789abc'; - const t = - '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; + const t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; let input = { installationId: installId, deviceType: 'ios', @@ -910,12 +825,7 @@ describe('Installations', () => { return rest.create(config, auth.nobody(config), '_Installation', input); }) .then(() => - database.adapter.find( - '_Installation', - installationSchema, - { deviceToken: t }, - {} - ) + database.adapter.find('_Installation', installationSchema, { deviceToken: t }, {}) ) .then(results => { expect(results.length).toEqual(1); @@ -932,9 +842,7 @@ describe('Installations', () => { input ); }) - .then(() => - database.adapter.find('_Installation', installationSchema, {}, {}) - ) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); expect(results[0].installationId).toEqual(installId); @@ -952,8 +860,7 @@ describe('Installations', () => { 'update is linking two existing objects w/ increment', done => { const installId = '12345678-abcd-abcd-abcd-123456789abc'; - const t = - '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; + const t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; let input = { installationId: installId, deviceType: 'ios', @@ -965,20 +872,10 @@ describe('Installations', () => { deviceToken: t, deviceType: 'ios', }; - return rest.create( - config, - auth.nobody(config), - '_Installation', - input - ); + return rest.create(config, auth.nobody(config), '_Installation', input); }) .then(() => - database.adapter.find( - '_Installation', - installationSchema, - { deviceToken: t }, - {} - ) + database.adapter.find('_Installation', installationSchema, { deviceToken: t }, {}) ) .then(results => { expect(results.length).toEqual(1); @@ -999,9 +896,7 @@ describe('Installations', () => { input ); }) - .then(() => - database.adapter.find('_Installation', installationSchema, {}, {}) - ) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); expect(results[0].installationId).toEqual(installId); @@ -1019,8 +914,7 @@ describe('Installations', () => { it('update is linking two existing with installation id', done => { const installId = '12345678-abcd-abcd-abcd-123456789abc'; - const t = - '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; + const t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; let input = { installationId: installId, deviceType: 'ios', @@ -1029,9 +923,7 @@ describe('Installations', () => { let tokenObj; rest .create(config, auth.nobody(config), '_Installation', input) - .then(() => - database.adapter.find('_Installation', installationSchema, {}, {}) - ) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); installObj = results[0]; @@ -1042,12 +934,7 @@ describe('Installations', () => { return rest.create(config, auth.nobody(config), '_Installation', input); }) .then(() => - database.adapter.find( - '_Installation', - installationSchema, - { deviceToken: t }, - {} - ) + database.adapter.find('_Installation', installationSchema, { deviceToken: t }, {}) ) .then(results => { expect(results.length).toEqual(1); @@ -1089,8 +976,7 @@ describe('Installations', () => { 'update is linking two existing with installation id w/ op', done => { const installId = '12345678-abcd-abcd-abcd-123456789abc'; - const t = - '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; + const t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; let input = { installationId: installId, deviceType: 'ios', @@ -1099,9 +985,7 @@ describe('Installations', () => { let tokenObj; rest .create(config, auth.nobody(config), '_Installation', input) - .then(() => - database.adapter.find('_Installation', installationSchema, {}, {}) - ) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); installObj = results[0]; @@ -1109,20 +993,10 @@ describe('Installations', () => { deviceToken: t, deviceType: 'ios', }; - return rest.create( - config, - auth.nobody(config), - '_Installation', - input - ); + return rest.create(config, auth.nobody(config), '_Installation', input); }) .then(() => - database.adapter.find( - '_Installation', - installationSchema, - { deviceToken: t }, - {} - ) + database.adapter.find('_Installation', installationSchema, { deviceToken: t }, {}) ) .then(results => { expect(results.length).toEqual(1); @@ -1177,8 +1051,7 @@ describe('Installations', () => { // imported installation, then we should reuse the existing installation // object in case the developer already added additional fields via Data // Browser or REST API (e.g. channel targeting info). - const t = - '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; + const t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; const installId = '12345678-abcd-abcd-abcd-123456789abc'; let input = { deviceToken: t, @@ -1186,9 +1059,7 @@ describe('Installations', () => { }; rest .create(config, auth.nobody(config), '_Installation', input) - .then(() => - database.adapter.find('_Installation', installationSchema, {}, {}) - ) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); input = { @@ -1198,9 +1069,7 @@ describe('Installations', () => { }; return rest.create(config, auth.nobody(config), '_Installation', input); }) - .then(() => - database.adapter.find('_Installation', installationSchema, {}, {}) - ) + .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); expect(results[0].deviceToken).toEqual(t); @@ -1230,9 +1099,7 @@ describe('Installations', () => { }; return request({ headers: headers, - url: - 'http://localhost:8378/1/installations/' + - createResult.response.objectId, + url: 'http://localhost:8378/1/installations/' + createResult.response.objectId, }).then(response => { const body = response.data; expect(body.objectId).toEqual(createResult.response.objectId); @@ -1318,31 +1185,29 @@ describe('Installations', () => { installationId: installId, deviceType: device, }; - rest - .create(config, auth.nobody(config), '_Installation', input) - .then(() => { - const query = new Parse.Query(Parse.Installation); - query.equalTo('installationId', installId); - query - .first({ useMasterKey: true }) - .then(installation => { - return installation.save( - { - key: 'value', - }, - { useMasterKey: true } - ); - }) - .then( - () => { - done(); + rest.create(config, auth.nobody(config), '_Installation', input).then(() => { + const query = new Parse.Query(Parse.Installation); + query.equalTo('installationId', installId); + query + .first({ useMasterKey: true }) + .then(installation => { + return installation.save( + { + key: 'value', }, - err => { - jfail(err); - done(); - } + { useMasterKey: true } ); - }); + }) + .then( + () => { + done(); + }, + err => { + jfail(err); + done(); + } + ); + }); }); it('should properly reject updating installationId', done => { @@ -1352,93 +1217,85 @@ describe('Installations', () => { installationId: installId, deviceType: device, }; - rest - .create(config, auth.nobody(config), '_Installation', input) - .then(() => { - const query = new Parse.Query(Parse.Installation); - query.equalTo('installationId', installId); - query - .first({ useMasterKey: true }) - .then(installation => { - return installation.save( - { - key: 'value', - installationId: '22222222-abcd-abcd-abcd-123456789abc', - }, - { useMasterKey: true } - ); - }) - .then( - () => { - fail('should not succeed'); - done(); + rest.create(config, auth.nobody(config), '_Installation', input).then(() => { + const query = new Parse.Query(Parse.Installation); + query.equalTo('installationId', installId); + query + .first({ useMasterKey: true }) + .then(installation => { + return installation.save( + { + key: 'value', + installationId: '22222222-abcd-abcd-abcd-123456789abc', }, - err => { - expect(err.code).toBe(136); - expect(err.message).toBe( - 'installationId may not be changed in this operation' - ); - done(); - } + { useMasterKey: true } ); - }); + }) + .then( + () => { + fail('should not succeed'); + done(); + }, + err => { + expect(err.code).toBe(136); + expect(err.message).toBe('installationId may not be changed in this operation'); + done(); + } + ); + }); }); - it_id('e581faea-c1b4-4c64-af8c-52287ce6cd06')(it)( - 'can use push with beforeSave', - async () => { - const input = { - deviceToken: - '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306', + it_id('e581faea-c1b4-4c64-af8c-52287ce6cd06')(it)('can use push with beforeSave', async () => { + const input = { + deviceToken: '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306', + deviceType: 'ios', + }; + await rest.create(config, auth.nobody(config), '_Installation', input); + const functions = { + beforeSave() {}, + afterSave() {}, + }; + spyOn(functions, 'beforeSave').and.callThrough(); + spyOn(functions, 'afterSave').and.callThrough(); + Parse.Cloud.beforeSave(Parse.Installation, functions.beforeSave); + Parse.Cloud.afterSave(Parse.Installation, functions.afterSave); + await Parse.Push.send({ + where: { deviceType: 'ios', - }; - await rest.create(config, auth.nobody(config), '_Installation', input); - const functions = { - beforeSave() {}, - afterSave() {}, - }; - spyOn(functions, 'beforeSave').and.callThrough(); - spyOn(functions, 'afterSave').and.callThrough(); - Parse.Cloud.beforeSave(Parse.Installation, functions.beforeSave); - Parse.Cloud.afterSave(Parse.Installation, functions.afterSave); - await Parse.Push.send({ - where: { - deviceType: 'ios', - }, - data: { - badge: 'increment', - alert: 'Hello world!', - }, - }); + }, + data: { + badge: 'increment', + alert: 'Hello world!', + }, + }); - await Parse.Push.send({ - where: { - deviceType: 'ios', - }, - data: { - badge: 'increment', - alert: 'Hello world!', - }, - }); + await Parse.Push.send({ + where: { + deviceType: 'ios', + }, + data: { + badge: 'increment', + alert: 'Hello world!', + }, + }); - await Parse.Push.send({ - where: { - deviceType: 'ios', - }, - data: { - badge: 'increment', - alert: 'Hello world!', - }, - }); - await new Promise(resolve => setTimeout(resolve, 1000)); - const installation = await new Parse.Query(Parse.Installation).first({ - useMasterKey: true, - }); - expect(installation.get('badge')).toEqual(3); - expect(functions.beforeSave).not.toHaveBeenCalled(); - expect(functions.afterSave).not.toHaveBeenCalled(); - } - ); + await Parse.Push.send({ + where: { + deviceType: 'ios', + }, + data: { + badge: 'increment', + alert: 'Hello world!', + }, + }); + await new Promise(resolve => setTimeout(resolve, 1000)); + const installation = await new Parse.Query(Parse.Installation).first({ + useMasterKey: true, + }); + expect(installation.get('badge')).toEqual(3); + expect(functions.beforeSave).not.toHaveBeenCalled(); + expect(functions.afterSave).not.toHaveBeenCalled(); + }); // TODO: Look at additional tests from installation_collection_test.go:882 // TODO: Do we need to support _tombstone disabling of installations? diff --git a/spec/ParseLiveQuery.spec.js b/spec/ParseLiveQuery.spec.js index f5ab808781..9c2f988850 100644 --- a/spec/ParseLiveQuery.spec.js +++ b/spec/ParseLiveQuery.spec.js @@ -1,16 +1,11 @@ 'use strict'; const http = require('http'); const Auth = require('../lib/Auth'); -const UserController = - require('../lib/Controllers/UserController').UserController; +const UserController = require('../lib/Controllers/UserController').UserController; const Config = require('../lib/Config'); const ParseServer = require('../lib/index').ParseServer; const triggers = require('../lib/triggers'); -const { - resolvingPromise, - sleep, - getConnectionsCount, -} = require('../lib/TestUtils'); +const { resolvingPromise, sleep, getConnectionsCount } = require('../lib/TestUtils'); const request = require('../lib/request'); const validatorFail = () => { throw 'you are not authorized'; @@ -21,8 +16,7 @@ describe('ParseLiveQuery', function () { Parse.CoreManager.getLiveQueryController().setDefaultLiveQueryClient(null); }); afterEach(async () => { - const client = - await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); + const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); await client.close(); }); it('access user on onLiveQueryEvent disconnect', async done => { @@ -41,8 +35,7 @@ describe('ParseLiveQuery', function () { await requestedUser.signUp(); const query = new Parse.Query(TestObject); await query.subscribe(); - const client = - await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); + const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); await client.close(); }); @@ -582,9 +575,7 @@ describe('ParseLiveQuery', function () { }); const query = new Parse.Query(TestObject); query.equalTo('objectId', object.id); - await expectAsync(query.subscribe()).toBeRejectedWith( - new Error('You shall not pass!') - ); + await expectAsync(query.subscribe()).toBeRejectedWith(new Error('You shall not pass!')); }); it('can log on beforeConnect throw', async () => { @@ -627,9 +618,7 @@ describe('ParseLiveQuery', function () { }); const query = new Parse.Query(TestObject); query.equalTo('objectId', object.id); - await expectAsync(query.subscribe()).toBeRejectedWith( - new Error('You shall not subscribe!') - ); + await expectAsync(query.subscribe()).toBeRejectedWith(new Error('You shall not subscribe!')); }); it('can log on beforeSubscribe error', async () => { @@ -650,9 +639,7 @@ describe('ParseLiveQuery', function () { }); const query = new Parse.Query(TestObject); - await expectAsync(query.subscribe()).toBeRejectedWith( - new Error('foo is not defined') - ); + await expectAsync(query.subscribe()).toBeRejectedWith(new Error('foo is not defined')); expect(logger.error).toHaveBeenCalledWith( `Failed running beforeSubscribe on TestObject for session undefined with:\n Error: {"message":"foo is not defined","code":141}` @@ -895,9 +882,7 @@ describe('ParseLiveQuery', function () { startLiveQueryServer: true, }); const query = new Parse.Query(Parse.Session); - await expectAsync(query.subscribe()).toBeRejectedWith( - new Error('Invalid session token') - ); + await expectAsync(query.subscribe()).toBeRejectedWith(new Error('Invalid session token')); }); it_id('4ccc9508-ae6a-46ec-932a-9f5e49ab3b9e')(it)( @@ -925,8 +910,7 @@ describe('ParseLiveQuery', function () { // 0x89 = 10001001 = ping // 0xfe = 11111110 = first bit is masking the remaining 7 are 1111110 or 126 the payload length // https://tools.ietf.org/html/rfc6455#section-5.2 - const client = - await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); + const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); client.socket._socket.write(Buffer.from([0x89, 0xfe])); subscription.on('update', async object => { @@ -1177,53 +1161,45 @@ describe('ParseLiveQuery', function () { await object.save(); }); - it_id('2f95d8a9-7675-45ba-a4a6-e45cb7efb1fb')(it)( - 'does shutdown liveQuery server', - async () => { - await reconfigureServer({ appId: 'test_app_id' }); - const config = { - appId: 'hello_test', - masterKey: 'world', - port: 1345, - mountPath: '/1', - serverURL: 'http://localhost:1345/1', - liveQuery: { - classNames: ['Yolo'], - }, - startLiveQueryServer: true, - verbose: false, - silent: true, - }; - if (process.env.PARSE_SERVER_TEST_DB === 'postgres') { - config.databaseAdapter = new databaseAdapter.constructor({ - uri: databaseURI, - collectionPrefix: 'test_', - }); - config.filesAdapter = defaultConfiguration.filesAdapter; - } - const server = await ParseServer.startApp(config); - const client = - await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); - client.serverURL = 'ws://localhost:1345/1'; - const query = await new Parse.Query('Yolo').subscribe(); - let liveQueryConnectionCount = await getConnectionsCount( - server.liveQueryServer.server - ); - expect(liveQueryConnectionCount > 0).toBe(true); - await Promise.all([ - server.handleShutdown(), - new Promise(resolve => query.on('close', resolve)), - ]); - await sleep(100); - expect(server.liveQueryServer.server.address()).toBeNull(); - expect(server.liveQueryServer.subscriber.isOpen).toBeFalse(); - - liveQueryConnectionCount = await getConnectionsCount( - server.liveQueryServer.server - ); - expect(liveQueryConnectionCount).toBe(0); + it_id('2f95d8a9-7675-45ba-a4a6-e45cb7efb1fb')(it)('does shutdown liveQuery server', async () => { + await reconfigureServer({ appId: 'test_app_id' }); + const config = { + appId: 'hello_test', + masterKey: 'world', + port: 1345, + mountPath: '/1', + serverURL: 'http://localhost:1345/1', + liveQuery: { + classNames: ['Yolo'], + }, + startLiveQueryServer: true, + verbose: false, + silent: true, + }; + if (process.env.PARSE_SERVER_TEST_DB === 'postgres') { + config.databaseAdapter = new databaseAdapter.constructor({ + uri: databaseURI, + collectionPrefix: 'test_', + }); + config.filesAdapter = defaultConfiguration.filesAdapter; } - ); + const server = await ParseServer.startApp(config); + const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); + client.serverURL = 'ws://localhost:1345/1'; + const query = await new Parse.Query('Yolo').subscribe(); + let liveQueryConnectionCount = await getConnectionsCount(server.liveQueryServer.server); + expect(liveQueryConnectionCount > 0).toBe(true); + await Promise.all([ + server.handleShutdown(), + new Promise(resolve => query.on('close', resolve)), + ]); + await sleep(100); + expect(server.liveQueryServer.server.address()).toBeNull(); + expect(server.liveQueryServer.subscriber.isOpen).toBeFalse(); + + liveQueryConnectionCount = await getConnectionsCount(server.liveQueryServer.server); + expect(liveQueryConnectionCount).toBe(0); + }); it_id('45655b74-716f-4fa1-a058-67eb21f3c3db')(it)( 'does shutdown separate liveQuery server', @@ -1261,8 +1237,7 @@ describe('ParseLiveQuery', function () { expect(parseServer.liveQueryServer.server).not.toBe(parseServer.server); // Open a connection to the liveQuery server - const client = - await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); + const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); client.serverURL = 'ws://localhost:1346/1'; const query = await new Parse.Query('Yolo').subscribe(); @@ -1281,9 +1256,7 @@ describe('ParseLiveQuery', function () { expect(health.status).toBe('ok'); let parseConnectionCount = await getConnectionsCount(parseServer.server); - let liveQueryConnectionCount = await getConnectionsCount( - parseServer.liveQueryServer.server - ); + let liveQueryConnectionCount = await getConnectionsCount(parseServer.liveQueryServer.server); expect(parseConnectionCount > 0).toBe(true); expect(liveQueryConnectionCount > 0).toBe(true); @@ -1297,9 +1270,7 @@ describe('ParseLiveQuery', function () { expect(parseServer.liveQueryServer.subscriber.isOpen).toBeFalse(); parseConnectionCount = await getConnectionsCount(parseServer.server); - liveQueryConnectionCount = await getConnectionsCount( - parseServer.liveQueryServer.server - ); + liveQueryConnectionCount = await getConnectionsCount(parseServer.liveQueryServer.server); expect(parseConnectionCount).toBe(0); expect(liveQueryConnectionCount).toBe(0); } diff --git a/spec/ParseLiveQueryRedis.spec.js b/spec/ParseLiveQueryRedis.spec.js index 96d68f1464..deb84bafb2 100644 --- a/spec/ParseLiveQueryRedis.spec.js +++ b/spec/ParseLiveQueryRedis.spec.js @@ -1,8 +1,7 @@ if (process.env.PARSE_SERVER_TEST_CACHE === 'redis') { describe('ParseLiveQuery redis', () => { afterEach(async () => { - const client = - await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); + const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); client.close(); }); it('can connect', async () => { @@ -48,15 +47,9 @@ if (process.env.PARSE_SERVER_TEST_CACHE === 'redis') { redisURL: 'redis://localhost:6379', }, }); - expect( - server.config.liveQueryController.liveQueryPublisher.parsePublisher - .isOpen - ).toBeTrue(); + expect(server.config.liveQueryController.liveQueryPublisher.parsePublisher.isOpen).toBeTrue(); await server.config.liveQueryController.connect(); - expect( - server.config.liveQueryController.liveQueryPublisher.parsePublisher - .isOpen - ).toBeTrue(); + expect(server.config.liveQueryController.liveQueryPublisher.parsePublisher.isOpen).toBeTrue(); expect(server.liveQueryServer.subscriber.isOpen).toBe(true); await server.liveQueryServer.connect(); expect(server.liveQueryServer.subscriber.isOpen).toBe(true); diff --git a/spec/ParseLiveQueryServer.spec.js b/spec/ParseLiveQueryServer.spec.js index 59648250e3..9961b2503d 100644 --- a/spec/ParseLiveQueryServer.spec.js +++ b/spec/ParseLiveQueryServer.spec.js @@ -1,9 +1,7 @@ const Parse = require('parse/node'); -const ParseLiveQueryServer = - require('../lib/LiveQuery/ParseLiveQueryServer').ParseLiveQueryServer; +const ParseLiveQueryServer = require('../lib/LiveQuery/ParseLiveQueryServer').ParseLiveQueryServer; const ParseServer = require('../lib/ParseServer').default; -const LiveQueryController = - require('../lib/Controllers/LiveQueryController').LiveQueryController; +const LiveQueryController = require('../lib/Controllers/LiveQueryController').LiveQueryController; const auth = require('../lib/Auth'); // Global mock info @@ -42,33 +40,15 @@ describe('ParseLiveQueryServer', function () { // Mock Subscription const mockSubscriotion = function () { this.addClientSubscription = jasmine.createSpy('addClientSubscription'); - this.deleteClientSubscription = jasmine.createSpy( - 'deleteClientSubscription' - ); + this.deleteClientSubscription = jasmine.createSpy('deleteClientSubscription'); }; - jasmine.mockLibrary( - '../lib/LiveQuery/Subscription', - 'Subscription', - mockSubscriotion - ); + jasmine.mockLibrary('../lib/LiveQuery/Subscription', 'Subscription', mockSubscriotion); // Mock queryHash - const mockQueryHash = jasmine - .createSpy('matchesQuery') - .and.returnValue(queryHashValue); - jasmine.mockLibrary( - '../lib/LiveQuery/QueryTools', - 'queryHash', - mockQueryHash - ); + const mockQueryHash = jasmine.createSpy('matchesQuery').and.returnValue(queryHashValue); + jasmine.mockLibrary('../lib/LiveQuery/QueryTools', 'queryHash', mockQueryHash); // Mock matchesQuery - const mockMatchesQuery = jasmine - .createSpy('matchesQuery') - .and.returnValue(true); - jasmine.mockLibrary( - '../lib/LiveQuery/QueryTools', - 'matchesQuery', - mockMatchesQuery - ); + const mockMatchesQuery = jasmine.createSpy('matchesQuery').and.returnValue(true); + jasmine.mockLibrary('../lib/LiveQuery/QueryTools', 'matchesQuery', mockMatchesQuery); // Mock ParsePubSub const mockParsePubSub = { createPublisher: function () { @@ -84,35 +64,24 @@ describe('ParseLiveQueryServer', function () { }; }, }; - jasmine.mockLibrary( - '../lib/LiveQuery/ParsePubSub', - 'ParsePubSub', - mockParsePubSub - ); - spyOn(auth, 'getAuthForSessionToken').and.callFake( - ({ sessionToken, cacheController }) => { - if (typeof sessionToken === 'undefined') { - return Promise.reject(); - } - if (sessionToken === null) { - return Promise.reject(); - } - if (sessionToken === 'pleaseThrow') { - return Promise.reject(); - } - if (sessionToken === 'invalid') { - return Promise.reject( - new Parse.Error( - Parse.Error.INVALID_SESSION_TOKEN, - 'invalid session token' - ) - ); - } - return Promise.resolve( - new auth.Auth({ cacheController, user: { id: testUserId } }) + jasmine.mockLibrary('../lib/LiveQuery/ParsePubSub', 'ParsePubSub', mockParsePubSub); + spyOn(auth, 'getAuthForSessionToken').and.callFake(({ sessionToken, cacheController }) => { + if (typeof sessionToken === 'undefined') { + return Promise.reject(); + } + if (sessionToken === null) { + return Promise.reject(); + } + if (sessionToken === 'pleaseThrow') { + return Promise.reject(); + } + if (sessionToken === 'invalid') { + return Promise.reject( + new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'invalid session token') ); } - ); + return Promise.resolve(new auth.Auth({ cacheController, user: { id: testUserId } })); + }); done(); }); @@ -127,10 +96,7 @@ describe('ParseLiveQueryServer', function () { it('can be initialized from ParseServer', async () => { const httpServer = {}; - const parseLiveQueryServer = await ParseServer.createLiveQueryServer( - httpServer, - {} - ); + const parseLiveQueryServer = await ParseServer.createLiveQueryServer(httpServer, {}); expect(parseLiveQueryServer.clientId).toBeUndefined(); expect(parseLiveQueryServer.clients.size).toBe(0); @@ -138,12 +104,9 @@ describe('ParseLiveQueryServer', function () { }); it('can be initialized from ParseServer without httpServer', async () => { - const parseLiveQueryServer = await ParseServer.createLiveQueryServer( - undefined, - { - port: 22345, - } - ); + const parseLiveQueryServer = await ParseServer.createLiveQueryServer(undefined, { + port: 22345, + }); expect(parseLiveQueryServer.clientId).toBeUndefined(); expect(parseLiveQueryServer.clients.size).toBe(0); @@ -232,10 +195,7 @@ describe('ParseLiveQueryServer', function () { }) .then(parseServer => { saveSpy = spyOn(parseServer.config.liveQueryController, 'onAfterSave'); - deleteSpy = spyOn( - parseServer.config.liveQueryController, - 'onAfterDelete' - ); + deleteSpy = spyOn(parseServer.config.liveQueryController, 'onAfterDelete'); return setPermissionsOnClass('Yolo', { create: { '*': true }, delete: { '*': true }, @@ -288,9 +248,7 @@ describe('ParseLiveQueryServer', function () { const parseWebSocket = { clientId: -1, }; - parseLiveQueryServer._validateKeys = jasmine - .createSpy('validateKeys') - .and.returnValue(true); + parseLiveQueryServer._validateKeys = jasmine.createSpy('validateKeys').and.returnValue(true); await parseLiveQueryServer._handleConnect(parseWebSocket, { sessionToken: 'token', }); @@ -394,10 +352,7 @@ describe('ParseLiveQueryServer', function () { // TODO(check subscription constructor to verify we pass the right argument) // Make sure we add clientInfo to the subscription const subscription = classSubscriptions.get('hash'); - expect(subscription.addClientSubscription).toHaveBeenCalledWith( - clientId, - requestId - ); + expect(subscription.addClientSubscription).toHaveBeenCalledWith(clientId, requestId); // Make sure we add subscriptionInfo to the client const args = client.addSubscriptionInfo.calls.first().args; expect(args[0]).toBe(requestId); @@ -426,13 +381,7 @@ describe('ParseLiveQueryServer', function () { }, keys: ['test'], }; - await addMockSubscription( - parseLiveQueryServer, - clientId, - requestId, - parseWebSocket, - query - ); + await addMockSubscription(parseLiveQueryServer, clientId, requestId, parseWebSocket, query); // Add subscription for mock client 2 const parseWebSocketAgain = { clientId: clientIdAgain, @@ -523,8 +472,7 @@ describe('ParseLiveQueryServer', function () { parseWebSocket ); // Mock client.getSubscriptionInfo - const subscriptionInfo = - client.addSubscriptionInfo.calls.mostRecent().args[1]; + const subscriptionInfo = client.addSubscriptionInfo.calls.mostRecent().args[1]; client.getSubscriptionInfo = function () { return subscriptionInfo; }; @@ -537,10 +485,7 @@ describe('ParseLiveQueryServer', function () { // Make sure we delete subscription from client expect(client.deleteSubscriptionInfo).toHaveBeenCalledWith(requestId); // Make sure we delete client from subscription - expect(subscription.deleteClientSubscription).toHaveBeenCalledWith( - clientId, - requestId - ); + expect(subscription.deleteClientSubscription).toHaveBeenCalledWith(clientId, requestId); // Make sure we clear subscription in the server const subscriptions = parseLiveQueryServer.subscriptions; expect(subscriptions.size).toBe(0); @@ -572,8 +517,7 @@ describe('ParseLiveQueryServer', function () { it('can set subscribe command message handler for a parseWebSocket', function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Register mock connect/subscribe/unsubscribe handler for the server - parseLiveQueryServer._handleSubscribe = - jasmine.createSpy('_handleSubscribe'); + parseLiveQueryServer._handleSubscribe = jasmine.createSpy('_handleSubscribe'); // Make mock parseWebsocket const EventEmitter = require('events'); const parseWebSocket = new EventEmitter(); @@ -597,8 +541,7 @@ describe('ParseLiveQueryServer', function () { it('can set unsubscribe command message handler for a parseWebSocket', function () { const parseLiveQueryServer = new ParseLiveQueryServer({}); // Register mock connect/subscribe/unsubscribe handler for the server - parseLiveQueryServer._handleUnsubscribe = - jasmine.createSpy('_handleSubscribe'); + parseLiveQueryServer._handleUnsubscribe = jasmine.createSpy('_handleSubscribe'); // Make mock parseWebsocket const EventEmitter = require('events'); const parseWebSocket = new EventEmitter(); @@ -613,8 +556,7 @@ describe('ParseLiveQueryServer', function () { // Trigger message event parseWebSocket.emit('message', unsubscribeRequest); // Make sure _handleUnsubscribe is called - const args = - parseLiveQueryServer._handleUnsubscribe.calls.mostRecent().args; + const args = parseLiveQueryServer._handleUnsubscribe.calls.mostRecent().args; expect(args[0]).toBe(parseWebSocket); expect(JSON.stringify(args[1])).toBe(unsubscribeRequest); }); @@ -642,13 +584,11 @@ describe('ParseLiveQueryServer', function () { // Trigger message event parseWebSocket.emit('message', updateRequest); // Make sure _handleUnsubscribe is called - const args = - parseLiveQueryServer._handleUpdateSubscription.calls.mostRecent().args; + const args = parseLiveQueryServer._handleUpdateSubscription.calls.mostRecent().args; expect(args[0]).toBe(parseWebSocket); expect(JSON.stringify(args[1])).toBe(updateRequest); expect(parseLiveQueryServer._handleUnsubscribe).toHaveBeenCalled(); - const unsubArgs = - parseLiveQueryServer._handleUnsubscribe.calls.mostRecent().args; + const unsubArgs = parseLiveQueryServer._handleUnsubscribe.calls.mostRecent().args; expect(unsubArgs.length).toBe(3); expect(unsubArgs[2]).toBe(false); expect(parseLiveQueryServer._handleSubscribe).toHaveBeenCalled(); @@ -1012,44 +952,18 @@ describe('ParseLiveQueryServer', function () { client.sessionToken = 'sessionToken'; // Mock queryHash for this special test - const mockQueryHash = jasmine - .createSpy('matchesQuery') - .and.returnValue('hash1'); - jasmine.mockLibrary( - '../lib/LiveQuery/QueryTools', - 'queryHash', - mockQueryHash - ); + const mockQueryHash = jasmine.createSpy('matchesQuery').and.returnValue('hash1'); + jasmine.mockLibrary('../lib/LiveQuery/QueryTools', 'queryHash', mockQueryHash); // Add mock subscription 1 const requestId2 = 2; - await addMockSubscription( - parseLiveQueryServer, - clientId, - requestId2, - null, - null, - 'hash1' - ); + await addMockSubscription(parseLiveQueryServer, clientId, requestId2, null, null, 'hash1'); // Mock queryHash for this special test - const mockQueryHash2 = jasmine - .createSpy('matchesQuery') - .and.returnValue('hash2'); - jasmine.mockLibrary( - '../lib/LiveQuery/QueryTools', - 'queryHash', - mockQueryHash2 - ); + const mockQueryHash2 = jasmine.createSpy('matchesQuery').and.returnValue('hash2'); + jasmine.mockLibrary('../lib/LiveQuery/QueryTools', 'queryHash', mockQueryHash2); // Add mock subscription 2 const requestId3 = 3; - await addMockSubscription( - parseLiveQueryServer, - clientId, - requestId3, - null, - null, - 'hash2' - ); + await addMockSubscription(parseLiveQueryServer, clientId, requestId3, null, null, 'hash2'); // Mock _matchesSubscription to return matching // In order to mimic a leave, then enter, we need original match return true // and the current match return false, then the other way around @@ -1108,12 +1022,7 @@ describe('ParseLiveQueryServer', function () { // Add mock subscription const requestId = 2; - await addMockSubscription( - parseLiveQueryServer, - clientId, - requestId, - parseWebSocket - ); + await addMockSubscription(parseLiveQueryServer, clientId, requestId, parseWebSocket); // Mock _matchesSubscription to return matching parseLiveQueryServer._matchesSubscription = function (parseObject) { if (!parseObject) { @@ -1197,13 +1106,7 @@ describe('ParseLiveQueryServer', function () { }, keys: ['test'], }; - await addMockSubscription( - parseLiveQueryServer, - clientId, - requestId, - parseWebSocket, - query - ); + await addMockSubscription(parseLiveQueryServer, clientId, requestId, parseWebSocket, query); // Mock _matchesSubscription to return matching parseLiveQueryServer._matchesSubscription = function (parseObject) { if (!parseObject) { @@ -1253,13 +1156,7 @@ describe('ParseLiveQueryServer', function () { }, watch: ['yolo'], }; - await addMockSubscription( - parseLiveQueryServer, - clientId, - requestId, - parseWebSocket, - query - ); + await addMockSubscription(parseLiveQueryServer, clientId, requestId, parseWebSocket, query); // Mock _matchesSubscription to return matching parseLiveQueryServer._matchesSubscription = function (parseObject) { if (!parseObject) { @@ -1296,12 +1193,8 @@ describe('ParseLiveQueryServer', function () { match: jasmine.createSpy('match'), }; - expect(parseLiveQueryServer._matchesSubscription(null, subscription)).toBe( - false - ); - expect( - parseLiveQueryServer._matchesSubscription(undefined, subscription) - ).toBe(false); + expect(parseLiveQueryServer._matchesSubscription(null, subscription)).toBe(false); + expect(parseLiveQueryServer._matchesSubscription(undefined, subscription)).toBe(false); // Make sure subscription.match is not called expect(subscription.match).not.toHaveBeenCalled(); }); @@ -1313,9 +1206,7 @@ describe('ParseLiveQueryServer', function () { query: {}, }; const parseObject = {}; - expect( - parseLiveQueryServer._matchesSubscription(parseObject, subscription) - ).toBe(true); + expect(parseLiveQueryServer._matchesSubscription(parseObject, subscription)).toBe(true); // Make sure matchesQuery is called const matchesQuery = require('../lib/LiveQuery/QueryTools').matchesQuery; expect(matchesQuery).toHaveBeenCalledWith(parseObject, subscription.query); @@ -1421,30 +1312,24 @@ describe('ParseLiveQueryServer', function () { const client = {}; const requestId = 0; - parseLiveQueryServer - ._matchesACL(undefined, client, requestId) - .then(function (isMatched) { - expect(isMatched).toBe(true); - done(); - }); + parseLiveQueryServer._matchesACL(undefined, client, requestId).then(function (isMatched) { + expect(isMatched).toBe(true); + done(); + }); }); it('can match ACL with none exist requestId', function (done) { const parseLiveQueryServer = new ParseLiveQueryServer({}); const acl = new Parse.ACL(); const client = { - getSubscriptionInfo: jasmine - .createSpy('getSubscriptionInfo') - .and.returnValue(undefined), + getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue(undefined), }; const requestId = 0; - parseLiveQueryServer - ._matchesACL(acl, client, requestId) - .then(function (isMatched) { - expect(isMatched).toBe(false); - done(); - }); + parseLiveQueryServer._matchesACL(acl, client, requestId).then(function (isMatched) { + expect(isMatched).toBe(false); + done(); + }); }); it('can match ACL with public read access', function (done) { @@ -1452,20 +1337,16 @@ describe('ParseLiveQueryServer', function () { const acl = new Parse.ACL(); acl.setPublicReadAccess(true); const client = { - getSubscriptionInfo: jasmine - .createSpy('getSubscriptionInfo') - .and.returnValue({ - sessionToken: 'sessionToken', - }), + getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({ + sessionToken: 'sessionToken', + }), }; const requestId = 0; - parseLiveQueryServer - ._matchesACL(acl, client, requestId) - .then(function (isMatched) { - expect(isMatched).toBe(true); - done(); - }); + parseLiveQueryServer._matchesACL(acl, client, requestId).then(function (isMatched) { + expect(isMatched).toBe(true); + done(); + }); }); it('can match ACL with valid subscription sessionToken', function (done) { @@ -1473,20 +1354,16 @@ describe('ParseLiveQueryServer', function () { const acl = new Parse.ACL(); acl.setReadAccess(testUserId, true); const client = { - getSubscriptionInfo: jasmine - .createSpy('getSubscriptionInfo') - .and.returnValue({ - sessionToken: 'sessionToken', - }), + getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({ + sessionToken: 'sessionToken', + }), }; const requestId = 0; - parseLiveQueryServer - ._matchesACL(acl, client, requestId) - .then(function (isMatched) { - expect(isMatched).toBe(true); - done(); - }); + parseLiveQueryServer._matchesACL(acl, client, requestId).then(function (isMatched) { + expect(isMatched).toBe(true); + done(); + }); }); it('can match ACL with valid client sessionToken', function (done) { @@ -1496,20 +1373,16 @@ describe('ParseLiveQueryServer', function () { // Mock sessionTokenCache will return false when sessionToken is undefined const client = { sessionToken: 'sessionToken', - getSubscriptionInfo: jasmine - .createSpy('getSubscriptionInfo') - .and.returnValue({ - sessionToken: undefined, - }), + getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({ + sessionToken: undefined, + }), }; const requestId = 0; - parseLiveQueryServer - ._matchesACL(acl, client, requestId) - .then(function (isMatched) { - expect(isMatched).toBe(true); - done(); - }); + parseLiveQueryServer._matchesACL(acl, client, requestId).then(function (isMatched) { + expect(isMatched).toBe(true); + done(); + }); }); it('can match ACL with invalid subscription and client sessionToken', function (done) { @@ -1519,20 +1392,16 @@ describe('ParseLiveQueryServer', function () { // Mock sessionTokenCache will return false when sessionToken is undefined const client = { sessionToken: undefined, - getSubscriptionInfo: jasmine - .createSpy('getSubscriptionInfo') - .and.returnValue({ - sessionToken: undefined, - }), + getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({ + sessionToken: undefined, + }), }; const requestId = 0; - parseLiveQueryServer - ._matchesACL(acl, client, requestId) - .then(function (isMatched) { - expect(isMatched).toBe(false); - done(); - }); + parseLiveQueryServer._matchesACL(acl, client, requestId).then(function (isMatched) { + expect(isMatched).toBe(false); + done(); + }); }); it('can match ACL with subscription sessionToken checking error', function (done) { @@ -1542,20 +1411,16 @@ describe('ParseLiveQueryServer', function () { // Mock sessionTokenCache will return error when sessionToken is null, this is just // the behaviour of our mock sessionTokenCache, not real sessionTokenCache const client = { - getSubscriptionInfo: jasmine - .createSpy('getSubscriptionInfo') - .and.returnValue({ - sessionToken: null, - }), + getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({ + sessionToken: null, + }), }; const requestId = 0; - parseLiveQueryServer - ._matchesACL(acl, client, requestId) - .then(function (isMatched) { - expect(isMatched).toBe(false); - done(); - }); + parseLiveQueryServer._matchesACL(acl, client, requestId).then(function (isMatched) { + expect(isMatched).toBe(false); + done(); + }); }); it('can match ACL with client sessionToken checking error', function (done) { @@ -1565,20 +1430,16 @@ describe('ParseLiveQueryServer', function () { // Mock sessionTokenCache will return error when sessionToken is null const client = { sessionToken: null, - getSubscriptionInfo: jasmine - .createSpy('getSubscriptionInfo') - .and.returnValue({ - sessionToken: null, - }), + getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({ + sessionToken: null, + }), }; const requestId = 0; - parseLiveQueryServer - ._matchesACL(acl, client, requestId) - .then(function (isMatched) { - expect(isMatched).toBe(false); - done(); - }); + parseLiveQueryServer._matchesACL(acl, client, requestId).then(function (isMatched) { + expect(isMatched).toBe(false); + done(); + }); }); it("won't match ACL that doesn't have public read or any roles", function (done) { @@ -1586,20 +1447,16 @@ describe('ParseLiveQueryServer', function () { const acl = new Parse.ACL(); acl.setPublicReadAccess(false); const client = { - getSubscriptionInfo: jasmine - .createSpy('getSubscriptionInfo') - .and.returnValue({ - sessionToken: 'sessionToken', - }), + getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({ + sessionToken: 'sessionToken', + }), }; const requestId = 0; - parseLiveQueryServer - ._matchesACL(acl, client, requestId) - .then(function (isMatched) { - expect(isMatched).toBe(false); - done(); - }); + parseLiveQueryServer._matchesACL(acl, client, requestId).then(function (isMatched) { + expect(isMatched).toBe(false); + done(); + }); }); it("won't match non-public ACL with role when there is no user", function (done) { @@ -1608,9 +1465,7 @@ describe('ParseLiveQueryServer', function () { acl.setPublicReadAccess(false); acl.setRoleReadAccess('livequery', true); const client = { - getSubscriptionInfo: jasmine - .createSpy('getSubscriptionInfo') - .and.returnValue({}), + getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({}), }; const requestId = 0; @@ -1629,11 +1484,9 @@ describe('ParseLiveQueryServer', function () { acl.setPublicReadAccess(false); acl.setRoleReadAccess('otherLiveQueryRead', true); const client = { - getSubscriptionInfo: jasmine - .createSpy('getSubscriptionInfo') - .and.returnValue({ - sessionToken: 'sessionToken', - }), + getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({ + sessionToken: 'sessionToken', + }), }; const requestId = 0; @@ -1654,29 +1507,22 @@ describe('ParseLiveQueryServer', function () { return Promise.resolve([]); } //Return a role with the name "liveQueryRead" as that is what was set on the ACL - const liveQueryRole = new Parse.Role( - 'liveQueryRead', - new Parse.ACL() - ); + const liveQueryRole = new Parse.Role('liveQueryRead', new Parse.ACL()); liveQueryRole.id = 'abcdef1234'; return Promise.resolve([liveQueryRole]); }, }; }); - parseLiveQueryServer - ._matchesACL(acl, client, requestId) - .then(function (isMatched) { - expect(isMatched).toBe(false); - done(); - }); + parseLiveQueryServer._matchesACL(acl, client, requestId).then(function (isMatched) { + expect(isMatched).toBe(false); + done(); + }); - parseLiveQueryServer - ._matchesACL(acl, client, requestId) - .then(function (isMatched) { - expect(isMatched).toBe(false); - done(); - }); + parseLiveQueryServer._matchesACL(acl, client, requestId).then(function (isMatched) { + expect(isMatched).toBe(false); + done(); + }); }); it('will match ACL with role based read access set to true', function (done) { @@ -1685,11 +1531,9 @@ describe('ParseLiveQueryServer', function () { acl.setPublicReadAccess(false); acl.setRoleReadAccess('liveQueryRead', true); const client = { - getSubscriptionInfo: jasmine - .createSpy('getSubscriptionInfo') - .and.returnValue({ - sessionToken: 'sessionToken', - }), + getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({ + sessionToken: 'sessionToken', + }), }; const requestId = 0; @@ -1710,19 +1554,13 @@ describe('ParseLiveQueryServer', function () { return Promise.resolve([]); } //Return a role with the name "liveQueryRead" as that is what was set on the ACL - const liveQueryRole = new Parse.Role( - 'liveQueryRead', - new Parse.ACL() - ); + const liveQueryRole = new Parse.Role('liveQueryRead', new Parse.ACL()); liveQueryRole.id = 'abcdef1234'; return Promise.resolve([liveQueryRole]); }, each(callback) { //Return a role with the name "liveQueryRead" as that is what was set on the ACL - const liveQueryRole = new Parse.Role( - 'liveQueryRead', - new Parse.ACL() - ); + const liveQueryRole = new Parse.Role('liveQueryRead', new Parse.ACL()); liveQueryRole.id = 'abcdef1234'; callback(liveQueryRole); return Promise.resolve(); @@ -1730,12 +1568,10 @@ describe('ParseLiveQueryServer', function () { }; }); - parseLiveQueryServer - ._matchesACL(acl, client, requestId) - .then(function (isMatched) { - expect(isMatched).toBe(true); - done(); - }); + parseLiveQueryServer._matchesACL(acl, client, requestId).then(function (isMatched) { + expect(isMatched).toBe(true); + done(); + }); }); describe('class level permissions', () => { @@ -1746,11 +1582,9 @@ describe('ParseLiveQueryServer', function () { // Mock sessionTokenCache will return false when sessionToken is undefined const client = { sessionToken: 'sessionToken', - getSubscriptionInfo: jasmine - .createSpy('getSubscriptionInfo') - .and.returnValue({ - sessionToken: undefined, - }), + getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({ + sessionToken: undefined, + }), }; const requestId = 0; @@ -1777,11 +1611,9 @@ describe('ParseLiveQueryServer', function () { // Mock sessionTokenCache will return false when sessionToken is undefined const client = { sessionToken: 'sessionToken', - getSubscriptionInfo: jasmine - .createSpy('getSubscriptionInfo') - .and.returnValue({ - sessionToken: undefined, - }), + getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({ + sessionToken: undefined, + }), }; const requestId = 0; @@ -1808,11 +1640,9 @@ describe('ParseLiveQueryServer', function () { // Mock sessionTokenCache will return false when sessionToken is undefined const client = { sessionToken: 'sessionToken', - getSubscriptionInfo: jasmine - .createSpy('getSubscriptionInfo') - .and.returnValue({ - sessionToken: undefined, - }), + getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({ + sessionToken: undefined, + }), }; const requestId = 0; @@ -1846,9 +1676,7 @@ describe('ParseLiveQueryServer', function () { clientKey: 'test', }; - expect( - parseLiveQueryServer._validateKeys(request, parseLiveQueryServer.keyPairs) - ).toBeTruthy(); + expect(parseLiveQueryServer._validateKeys(request, parseLiveQueryServer.keyPairs)).toBeTruthy(); }); it('can validate key when invalid key is provided', function () { @@ -1889,9 +1717,7 @@ describe('ParseLiveQueryServer', function () { const parseLiveQueryServer = new ParseLiveQueryServer({}, {}); const request = {}; - expect( - parseLiveQueryServer._validateKeys(request, parseLiveQueryServer.keyPairs) - ).toBeTruthy(); + expect(parseLiveQueryServer._validateKeys(request, parseLiveQueryServer.keyPairs)).toBeTruthy(); }); it('can validate client has master key when valid', function () { @@ -1907,9 +1733,7 @@ describe('ParseLiveQueryServer', function () { masterKey: 'test', }; - expect( - parseLiveQueryServer._hasMasterKey(request, parseLiveQueryServer.keyPairs) - ).toBeTruthy(); + expect(parseLiveQueryServer._hasMasterKey(request, parseLiveQueryServer.keyPairs)).toBeTruthy(); }); it("can validate client doesn't have master key when invalid", function () { @@ -1940,9 +1764,7 @@ describe('ParseLiveQueryServer', function () { } ); - expect( - parseLiveQueryServer._hasMasterKey({}, parseLiveQueryServer.keyPairs) - ).not.toBeTruthy(); + expect(parseLiveQueryServer._hasMasterKey({}, parseLiveQueryServer.keyPairs)).not.toBeTruthy(); }); it("can validate client doesn't have master key when validKeyPairs is empty", function () { @@ -1961,19 +1783,15 @@ describe('ParseLiveQueryServer', function () { const acl = new Parse.ACL(); acl.setPublicReadAccess(false); const client = { - getSubscriptionInfo: jasmine - .createSpy('getSubscriptionInfo') - .and.returnValue({}), + getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({}), hasMasterKey: true, }; const requestId = 0; - parseLiveQueryServer - ._matchesACL(acl, client, requestId) - .then(function (isMatched) { - expect(isMatched).toBe(true); - done(); - }); + parseLiveQueryServer._matchesACL(acl, client, requestId).then(function (isMatched) { + expect(isMatched).toBe(true); + done(); + }); }); it("won't match non-public ACL when client has no master key", function (done) { @@ -1981,26 +1799,21 @@ describe('ParseLiveQueryServer', function () { const acl = new Parse.ACL(); acl.setPublicReadAccess(false); const client = { - getSubscriptionInfo: jasmine - .createSpy('getSubscriptionInfo') - .and.returnValue({}), + getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({}), hasMasterKey: false, }; const requestId = 0; - parseLiveQueryServer - ._matchesACL(acl, client, requestId) - .then(function (isMatched) { - expect(isMatched).toBe(false); - done(); - }); + parseLiveQueryServer._matchesACL(acl, client, requestId).then(function (isMatched) { + expect(isMatched).toBe(false); + done(); + }); }); it('should properly pull auth from cache', () => { const parseLiveQueryServer = new ParseLiveQueryServer({}); const promise = parseLiveQueryServer.getAuthForSessionToken('sessionToken'); - const secondPromise = - parseLiveQueryServer.getAuthForSessionToken('sessionToken'); + const secondPromise = parseLiveQueryServer.getAuthForSessionToken('sessionToken'); // should be in the cache expect(parseLiveQueryServer.authCache.get('sessionToken')).toBe(promise); // should be the same promise returned @@ -2030,10 +1843,7 @@ describe('ParseLiveQueryServer', function () { }); afterEach(function () { - jasmine.restoreLibrary( - '../lib/LiveQuery/ParseWebSocketServer', - 'ParseWebSocketServer' - ); + jasmine.restoreLibrary('../lib/LiveQuery/ParseWebSocketServer', 'ParseWebSocketServer'); jasmine.restoreLibrary('../lib/LiveQuery/Client', 'Client'); jasmine.restoreLibrary('../lib/LiveQuery/Subscription', 'Subscription'); jasmine.restoreLibrary('../lib/LiveQuery/QueryTools', 'queryHash'); @@ -2089,10 +1899,7 @@ describe('ParseLiveQueryServer', function () { }; subscription.className = query.className; subscription.hash = customQueryHashValue || queryHashValue; - if ( - subscription.clientRequestIds && - subscription.clientRequestIds.has(clientId) - ) { + if (subscription.clientRequestIds && subscription.clientRequestIds.has(clientId)) { subscription.clientRequestIds.get(clientId).push(requestId); } else { subscription.clientRequestIds = new Map([[clientId, [requestId]]]); @@ -2165,10 +1972,7 @@ describe('LiveQueryController', () => { }, }) .then(parseServer => { - saveSpy = spyOn( - parseServer.config.liveQueryController, - 'onAfterSave' - ).and.callThrough(); + saveSpy = spyOn(parseServer.config.liveQueryController, 'onAfterSave').and.callThrough(); deleteSpy = spyOn( parseServer.config.liveQueryController, 'onAfterDelete' diff --git a/spec/ParseObject.spec.js b/spec/ParseObject.spec.js index e0e5091b1c..d2061f89fc 100644 --- a/spec/ParseObject.spec.js +++ b/spec/ParseObject.spec.js @@ -213,9 +213,7 @@ describe('Parse.Object testing', () => { const object = new TestObject({ foo: 'bar' }); object.save().then(function () { const endTime = new Date(); - const startDiff = Math.abs( - startTime.getTime() - object.createdAt.getTime() - ); + const startDiff = Math.abs(startTime.getTime() - object.createdAt.getTime()); ok(startDiff < 5000); const endDiff = Math.abs(endTime.getTime() - object.createdAt.getTime()); @@ -312,15 +310,7 @@ describe('Parse.Object testing', () => { it('invalid __type', function (done) { const item = new Parse.Object('Item'); - const types = [ - 'Pointer', - 'File', - 'Date', - 'GeoPoint', - 'Bytes', - 'Polygon', - 'Relation', - ]; + const types = ['Pointer', 'File', 'Date', 'GeoPoint', 'Bytes', 'Polygon', 'Relation']; const tests = types.map(type => { const test = new Parse.Object('Item'); test.set('foo', { @@ -445,10 +435,7 @@ describe('Parse.Object testing', () => { const query = new Parse.Query('SimpleObject'); query.get(simple.id).then( function (simpleAgain) { - ok( - !simpleAgain.has('child'), - 'child should have been removed.' - ); + ok(!simpleAgain.has('child'), 'child should have been removed.'); done(); }, function (simpleAgain, error) { @@ -576,9 +563,7 @@ describe('Parse.Object testing', () => { jfail(error); }); on_db('postgres', () => { - expect(error.message).toEqual( - 'Postgres does not support AddUnique operator.' - ); + expect(error.message).toEqual('Postgres does not support AddUnique operator.'); }); done(); } @@ -608,55 +593,49 @@ describe('Parse.Object testing', () => { expect(result.get('items')).toEqual(obj.get('items')); }); - it_only_db('mongo')( - 'can increment array nested fields missing index', - async () => { + it_only_db('mongo')('can increment array nested fields missing index', async () => { + const obj = new TestObject(); + obj.set('items', []); + await obj.save(); + obj.increment('items.1.count', 15); + await obj.save(); + expect(obj.toJSON().items[0]).toBe(null); + expect(obj.toJSON().items[1].count).toBe(15); + const query = new Parse.Query(TestObject); + const result = await query.get(obj.id); + expect(result.get('items')[0]).toBe(null); + expect(result.get('items')[1].count).toBe(15); + expect(result.get('items')).toEqual(obj.get('items')); + }); + + it_id('44097c6f-d0ca-4dc5-aa8a-3dd2d9ac645a')(it)('can query array nested fields', async () => { + const objects = []; + for (let i = 0; i < 10; i++) { const obj = new TestObject(); - obj.set('items', []); - await obj.save(); - obj.increment('items.1.count', 15); - await obj.save(); - expect(obj.toJSON().items[0]).toBe(null); - expect(obj.toJSON().items[1].count).toBe(15); - const query = new Parse.Query(TestObject); - const result = await query.get(obj.id); - expect(result.get('items')[0]).toBe(null); - expect(result.get('items')[1].count).toBe(15); - expect(result.get('items')).toEqual(obj.get('items')); + obj.set('items', [i, { value: i }]); + objects.push(obj); } - ); + await Parse.Object.saveAll(objects); + let query = new Parse.Query(TestObject); + query.greaterThan('items.1.value', 5); + let result = await query.find(); + expect(result.length).toBe(4); - it_id('44097c6f-d0ca-4dc5-aa8a-3dd2d9ac645a')(it)( - 'can query array nested fields', - async () => { - const objects = []; - for (let i = 0; i < 10; i++) { - const obj = new TestObject(); - obj.set('items', [i, { value: i }]); - objects.push(obj); - } - await Parse.Object.saveAll(objects); - let query = new Parse.Query(TestObject); - query.greaterThan('items.1.value', 5); - let result = await query.find(); - expect(result.length).toBe(4); - - query = new Parse.Query(TestObject); - query.lessThan('items.0', 3); - result = await query.find(); - expect(result.length).toBe(3); - - query = new Parse.Query(TestObject); - query.equalTo('items.0', 5); - result = await query.find(); - expect(result.length).toBe(1); - - query = new Parse.Query(TestObject); - query.notEqualTo('items.0', 5); - result = await query.find(); - expect(result.length).toBe(9); - } - ); + query = new Parse.Query(TestObject); + query.lessThan('items.0', 3); + result = await query.find(); + expect(result.length).toBe(3); + + query = new Parse.Query(TestObject); + query.equalTo('items.0', 5); + result = await query.find(); + expect(result.length).toBe(1); + + query = new Parse.Query(TestObject); + query.notEqualTo('items.0', 5); + result = await query.find(); + expect(result.length).toBe(9); + }); it('addUnique with object', function (done) { const x1 = new Parse.Object('X'); @@ -677,12 +656,7 @@ describe('Parse.Object testing', () => { .then( x3 => { const stuff = x3.get('stuff'); - const target = [ - 1, - { hello: 'world' }, - { foo: 'bar' }, - { bar: 'baz' }, - ]; + const target = [1, { hello: 'world' }, { foo: 'bar' }, { bar: 'baz' }]; expect(stuff.length).toEqual(target.length); let found = 0; for (const thing in target) { @@ -1401,11 +1375,7 @@ describe('Parse.Object testing', () => { return Parse.Object.fetchAll(items); }) .then(function (fetchedItemsAgain) { - equal( - fetchedItemsAgain.length, - numItems, - 'Number of items fetched should not change' - ); + equal(fetchedItemsAgain.length, numItems, 'Number of items fetched should not change'); fetchedItemsAgain.forEach(function (item, i) { equal(item.get('x'), i * 2); }); @@ -1482,11 +1452,7 @@ describe('Parse.Object testing', () => { .then(function () { return Parse.Object.fetchAll(items).then( function (fetchedItemsAgain) { - equal( - fetchedItemsAgain.length, - numItems, - 'Number of items fetched should not change' - ); + equal(fetchedItemsAgain.length, numItems, 'Number of items fetched should not change'); fetchedItemsAgain.forEach(function (item, i) { equal(item.get('x'), i * 2); }); @@ -1629,11 +1595,7 @@ describe('Parse.Object testing', () => { return Parse.Object.fetchAllIfNeeded(items); }) .then(function (fetchedItems) { - equal( - fetchedItems.length, - numItems, - 'Number of items should not change' - ); + equal(fetchedItems.length, numItems, 'Number of items should not change'); fetchedItems.forEach(function (item, i) { equal(item.get('x'), i); }); @@ -1675,11 +1637,7 @@ describe('Parse.Object testing', () => { const items = container.get('items'); return Parse.Object.fetchAllIfNeeded(items).then( function (fetchedItems) { - equal( - fetchedItems.length, - numItems, - 'Number of items should not change' - ); + equal(fetchedItems.length, numItems, 'Number of items should not change'); fetchedItems.forEach(function (item, j) { equal(item.get('x'), j); }); @@ -1745,19 +1703,11 @@ describe('Parse.Object testing', () => { className: 'User', }); - equal( - User2.className, - 'User', - 'className is not rewritten when allowCustomUserClass(true)' - ); + equal(User2.className, 'User', 'className is not rewritten when allowCustomUserClass(true)'); // Set back to default so as not to break other tests. Parse.User.allowCustomUserClass(false); - equal( - Parse.CoreManager.get('PERFORM_USER_REWRITE'), - true, - 'PERFORM_USER_REWRITE is reset' - ); + equal(Parse.CoreManager.get('PERFORM_USER_REWRITE'), true, 'PERFORM_USER_REWRITE is reset'); const user = new User2(); user.set('name', 'Me'); @@ -1783,22 +1733,14 @@ describe('Parse.Object testing', () => { return t2.fetch(); }) .then(function (t2) { - equal( - t2.get('test'), - 'test', - 'Fetch should have grabbed ' + "'test' property." - ); + equal(t2.get('test'), 'test', 'Fetch should have grabbed ' + "'test' property."); const t3 = TestObject.createWithoutData(t2.id); t3.set('test', 'not test'); return t3.fetch(); }) .then( function (t3) { - equal( - t3.get('test'), - 'test', - "Fetch should have grabbed server 'test' property." - ); + equal(t3.get('test'), 'test', "Fetch should have grabbed server 'test' property."); done(); }, function (error) { diff --git a/spec/ParsePolygon.spec.js b/spec/ParsePolygon.spec.js index aa0293326d..b53846d4ba 100644 --- a/spec/ParsePolygon.spec.js +++ b/spec/ParsePolygon.spec.js @@ -62,49 +62,46 @@ describe('Parse.Polygon testing', () => { }, done.fail); }); - it_id('3019353b-d5b3-4e53-bcb1-716418328bdd')(it)( - 'polygon equalTo (open/closed) path', - done => { - const openPoints = [ - [0, 0], - [0, 1], - [1, 1], - [1, 0], - ]; - const closedPoints = [ - [0, 0], - [0, 1], - [1, 1], - [1, 0], - [0, 0], - ]; - const openPolygon = new Parse.Polygon(openPoints); - const closedPolygon = new Parse.Polygon(closedPoints); - const obj = new TestObject(); - obj.set('polygon', openPolygon); - return obj - .save() - .then(() => { - const query = new Parse.Query(TestObject); - query.equalTo('polygon', openPolygon); - return query.find(); - }) - .then(results => { - const polygon = results[0].get('polygon'); - equal(polygon instanceof Parse.Polygon, true); - equal(polygon.coordinates, closedPoints); - const query = new Parse.Query(TestObject); - query.equalTo('polygon', closedPolygon); - return query.find(); - }) - .then(results => { - const polygon = results[0].get('polygon'); - equal(polygon instanceof Parse.Polygon, true); - equal(polygon.coordinates, closedPoints); - done(); - }, done.fail); - } - ); + it_id('3019353b-d5b3-4e53-bcb1-716418328bdd')(it)('polygon equalTo (open/closed) path', done => { + const openPoints = [ + [0, 0], + [0, 1], + [1, 1], + [1, 0], + ]; + const closedPoints = [ + [0, 0], + [0, 1], + [1, 1], + [1, 0], + [0, 0], + ]; + const openPolygon = new Parse.Polygon(openPoints); + const closedPolygon = new Parse.Polygon(closedPoints); + const obj = new TestObject(); + obj.set('polygon', openPolygon); + return obj + .save() + .then(() => { + const query = new Parse.Query(TestObject); + query.equalTo('polygon', openPolygon); + return query.find(); + }) + .then(results => { + const polygon = results[0].get('polygon'); + equal(polygon instanceof Parse.Polygon, true); + equal(polygon.coordinates, closedPoints); + const query = new Parse.Query(TestObject); + query.equalTo('polygon', closedPolygon); + return query.find(); + }) + .then(results => { + const polygon = results[0].get('polygon'); + equal(polygon instanceof Parse.Polygon, true); + equal(polygon.coordinates, closedPoints); + done(); + }, done.fail); + }); it('polygon update', done => { const oldCoords = [ diff --git a/spec/ParsePubSub.spec.js b/spec/ParsePubSub.spec.js index 323a72eda6..063c35728d 100644 --- a/spec/ParsePubSub.spec.js +++ b/spec/ParsePubSub.spec.js @@ -7,11 +7,7 @@ describe('ParsePubSub', function () { createPublisher: jasmine.createSpy('createPublisherRedis'), createSubscriber: jasmine.createSpy('createSubscriberRedis'), }; - jasmine.mockLibrary( - '../lib/Adapters/PubSub/RedisPubSub', - 'RedisPubSub', - mockRedisPubSub - ); + jasmine.mockLibrary('../lib/Adapters/PubSub/RedisPubSub', 'RedisPubSub', mockRedisPubSub); // Mock EventEmitterPubSub const mockEventEmitterPubSub = { createPublisher: jasmine.createSpy('createPublisherEventEmitter'), @@ -31,8 +27,7 @@ describe('ParsePubSub', function () { redisOptions: { socket_keepalive: true }, }); - const RedisPubSub = - require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; + const RedisPubSub = require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; const EventEmitterPubSub = require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub; expect(RedisPubSub.createPublisher).toHaveBeenCalledWith({ @@ -45,8 +40,7 @@ describe('ParsePubSub', function () { it('can create event emitter publisher', function () { ParsePubSub.createPublisher({}); - const RedisPubSub = - require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; + const RedisPubSub = require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; const EventEmitterPubSub = require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub; expect(RedisPubSub.createPublisher).not.toHaveBeenCalled(); @@ -59,8 +53,7 @@ describe('ParsePubSub', function () { redisOptions: { socket_keepalive: true }, }); - const RedisPubSub = - require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; + const RedisPubSub = require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; const EventEmitterPubSub = require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub; expect(RedisPubSub.createSubscriber).toHaveBeenCalledWith({ @@ -73,8 +66,7 @@ describe('ParsePubSub', function () { it('can create event emitter subscriber', function () { ParsePubSub.createSubscriber({}); - const RedisPubSub = - require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; + const RedisPubSub = require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; const EventEmitterPubSub = require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub; expect(RedisPubSub.createSubscriber).not.toHaveBeenCalled(); @@ -96,8 +88,7 @@ describe('ParsePubSub', function () { }); expect(adapter.createSubscriber).toHaveBeenCalled(); - const RedisPubSub = - require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; + const RedisPubSub = require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; const EventEmitterPubSub = require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub; expect(RedisPubSub.createSubscriber).not.toHaveBeenCalled(); @@ -125,8 +116,7 @@ describe('ParsePubSub', function () { }); expect(adapter.createSubscriber).toHaveBeenCalled(); - const RedisPubSub = - require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; + const RedisPubSub = require('../lib/Adapters/PubSub/RedisPubSub').RedisPubSub; const EventEmitterPubSub = require('../lib/Adapters/PubSub/EventEmitterPubSub').EventEmitterPubSub; expect(RedisPubSub.createSubscriber).not.toHaveBeenCalled(); @@ -137,9 +127,6 @@ describe('ParsePubSub', function () { afterEach(function () { jasmine.restoreLibrary('../lib/Adapters/PubSub/RedisPubSub', 'RedisPubSub'); - jasmine.restoreLibrary( - '../lib/Adapters/PubSub/EventEmitterPubSub', - 'EventEmitterPubSub' - ); + jasmine.restoreLibrary('../lib/Adapters/PubSub/EventEmitterPubSub', 'EventEmitterPubSub'); }); }); diff --git a/spec/ParseQuery.Aggregate.spec.js b/spec/ParseQuery.Aggregate.spec.js index d49ba726a7..c2ea6dec16 100644 --- a/spec/ParseQuery.Aggregate.spec.js +++ b/spec/ParseQuery.Aggregate.spec.js @@ -104,15 +104,9 @@ describe('Parse.Query Aggregate testing', () => { get(Parse.serverURL + '/aggregate/TestObject', options) .then(resp => { expect(resp.results.length).toBe(3); - expect( - Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId') - ).toBe(true); - expect( - Object.prototype.hasOwnProperty.call(resp.results[1], 'objectId') - ).toBe(true); - expect( - Object.prototype.hasOwnProperty.call(resp.results[2], 'objectId') - ).toBe(true); + expect(Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId')).toBe(true); + expect(Object.prototype.hasOwnProperty.call(resp.results[1], 'objectId')).toBe(true); + expect(Object.prototype.hasOwnProperty.call(resp.results[2], 'objectId')).toBe(true); expect(resp.results[0].objectId).not.toBe(undefined); expect(resp.results[1].objectId).not.toBe(undefined); expect(resp.results[2].objectId).not.toBe(undefined); @@ -121,200 +115,166 @@ describe('Parse.Query Aggregate testing', () => { .catch(done.fail); }); - it_id('0ab0d776-e45d-419a-9b35-3d11933b77d1')(it)( - 'group by pipeline operator', - async () => { - const options = Object.assign({}, masterKeyOptions, { - body: { - pipeline: { - $group: { _id: '$name' }, - }, + it_id('0ab0d776-e45d-419a-9b35-3d11933b77d1')(it)('group by pipeline operator', async () => { + const options = Object.assign({}, masterKeyOptions, { + body: { + pipeline: { + $group: { _id: '$name' }, }, - }); - const resp = await get( - Parse.serverURL + '/aggregate/TestObject', - options - ); - expect(resp.results.length).toBe(3); - expect( - Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId') - ).toBe(true); - expect( - Object.prototype.hasOwnProperty.call(resp.results[1], 'objectId') - ).toBe(true); - expect( - Object.prototype.hasOwnProperty.call(resp.results[2], 'objectId') - ).toBe(true); - expect(resp.results[0].objectId).not.toBe(undefined); - expect(resp.results[1].objectId).not.toBe(undefined); - expect(resp.results[2].objectId).not.toBe(undefined); - } - ); + }, + }); + const resp = await get(Parse.serverURL + '/aggregate/TestObject', options); + expect(resp.results.length).toBe(3); + expect(Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId')).toBe(true); + expect(Object.prototype.hasOwnProperty.call(resp.results[1], 'objectId')).toBe(true); + expect(Object.prototype.hasOwnProperty.call(resp.results[2], 'objectId')).toBe(true); + expect(resp.results[0].objectId).not.toBe(undefined); + expect(resp.results[1].objectId).not.toBe(undefined); + expect(resp.results[2].objectId).not.toBe(undefined); + }); - it_id('b6b42145-7eb4-47aa-ada6-8c1444420e07')(it)( - 'group by empty object', - done => { - const obj = new TestObject(); - const pipeline = [ - { - $group: { _id: {} }, - }, - ]; - obj - .save() - .then(() => { - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - expect(results[0].objectId).toEqual(null); - done(); - }); - } - ); + it_id('b6b42145-7eb4-47aa-ada6-8c1444420e07')(it)('group by empty object', done => { + const obj = new TestObject(); + const pipeline = [ + { + $group: { _id: {} }, + }, + ]; + obj + .save() + .then(() => { + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + expect(results[0].objectId).toEqual(null); + done(); + }); + }); - it_id('0f5f6869-e675-41b9-9ad2-52b201124fb0')(it)( - 'group by empty string', - done => { - const obj = new TestObject(); - const pipeline = [ - { - $group: { _id: '' }, - }, - ]; - obj - .save() - .then(() => { - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - expect(results[0].objectId).toEqual(null); - done(); - }); - } - ); + it_id('0f5f6869-e675-41b9-9ad2-52b201124fb0')(it)('group by empty string', done => { + const obj = new TestObject(); + const pipeline = [ + { + $group: { _id: '' }, + }, + ]; + obj + .save() + .then(() => { + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + expect(results[0].objectId).toEqual(null); + done(); + }); + }); - it_id('b9c4f1b4-47f4-4ff4-88fb-586711f57e4a')(it)( - 'group by empty array', - done => { - const obj = new TestObject(); - const pipeline = [ - { - $group: { _id: [] }, - }, - ]; - obj - .save() - .then(() => { - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - expect(results[0].objectId).toEqual(null); - done(); - }); - } - ); + it_id('b9c4f1b4-47f4-4ff4-88fb-586711f57e4a')(it)('group by empty array', done => { + const obj = new TestObject(); + const pipeline = [ + { + $group: { _id: [] }, + }, + ]; + obj + .save() + .then(() => { + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + expect(results[0].objectId).toEqual(null); + done(); + }); + }); - it_id('bf5ee3e5-986c-4994-9c8d-79310283f602')(it)( - 'group by multiple columns ', - done => { - const obj1 = new TestObject(); - const obj2 = new TestObject(); - const obj3 = new TestObject(); - const pipeline = [ - { - $group: { - _id: { - score: '$score', - views: '$views', - }, - count: { $sum: 1 }, + it_id('bf5ee3e5-986c-4994-9c8d-79310283f602')(it)('group by multiple columns ', done => { + const obj1 = new TestObject(); + const obj2 = new TestObject(); + const obj3 = new TestObject(); + const pipeline = [ + { + $group: { + _id: { + score: '$score', + views: '$views', }, + count: { $sum: 1 }, }, - ]; - Parse.Object.saveAll([obj1, obj2, obj3]) - .then(() => { - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - expect(results.length).toEqual(5); - done(); - }); - } - ); + }, + ]; + Parse.Object.saveAll([obj1, obj2, obj3]) + .then(() => { + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + expect(results.length).toEqual(5); + done(); + }); + }); - it_id('3e652c61-78e1-4541-83ac-51ad1def9874')(it)( - 'group by date object', - done => { - const obj1 = new TestObject(); - const obj2 = new TestObject(); - const obj3 = new TestObject(); - const pipeline = [ - { - $group: { - _id: { - day: { $dayOfMonth: '$_updated_at' }, - month: { $month: '$_created_at' }, - year: { $year: '$_created_at' }, - }, - count: { $sum: 1 }, + it_id('3e652c61-78e1-4541-83ac-51ad1def9874')(it)('group by date object', done => { + const obj1 = new TestObject(); + const obj2 = new TestObject(); + const obj3 = new TestObject(); + const pipeline = [ + { + $group: { + _id: { + day: { $dayOfMonth: '$_updated_at' }, + month: { $month: '$_created_at' }, + year: { $year: '$_created_at' }, }, + count: { $sum: 1 }, }, - ]; - Parse.Object.saveAll([obj1, obj2, obj3]) - .then(() => { - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - const createdAt = new Date(obj1.createdAt); - expect(results[0].objectId.day).toEqual(createdAt.getUTCDate()); - expect(results[0].objectId.month).toEqual( - createdAt.getUTCMonth() + 1 - ); - expect(results[0].objectId.year).toEqual(createdAt.getUTCFullYear()); - done(); - }); - } - ); + }, + ]; + Parse.Object.saveAll([obj1, obj2, obj3]) + .then(() => { + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + const createdAt = new Date(obj1.createdAt); + expect(results[0].objectId.day).toEqual(createdAt.getUTCDate()); + expect(results[0].objectId.month).toEqual(createdAt.getUTCMonth() + 1); + expect(results[0].objectId.year).toEqual(createdAt.getUTCFullYear()); + done(); + }); + }); - it_id('5d3a0f73-1f49-46f3-9be5-caf1eaefec79')(it)( - 'group by date object transform', - done => { - const obj1 = new TestObject(); - const obj2 = new TestObject(); - const obj3 = new TestObject(); - const pipeline = [ - { - $group: { - _id: { - day: { $dayOfMonth: '$updatedAt' }, - month: { $month: '$createdAt' }, - year: { $year: '$createdAt' }, - }, - count: { $sum: 1 }, + it_id('5d3a0f73-1f49-46f3-9be5-caf1eaefec79')(it)('group by date object transform', done => { + const obj1 = new TestObject(); + const obj2 = new TestObject(); + const obj3 = new TestObject(); + const pipeline = [ + { + $group: { + _id: { + day: { $dayOfMonth: '$updatedAt' }, + month: { $month: '$createdAt' }, + year: { $year: '$createdAt' }, }, + count: { $sum: 1 }, }, - ]; - Parse.Object.saveAll([obj1, obj2, obj3]) - .then(() => { - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - const createdAt = new Date(obj1.createdAt); - expect(results[0].objectId.day).toEqual(createdAt.getUTCDate()); - expect(results[0].objectId.month).toEqual( - createdAt.getUTCMonth() + 1 - ); - expect(results[0].objectId.year).toEqual(createdAt.getUTCFullYear()); - done(); - }); - } - ); + }, + ]; + Parse.Object.saveAll([obj1, obj2, obj3]) + .then(() => { + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + const createdAt = new Date(obj1.createdAt); + expect(results[0].objectId.day).toEqual(createdAt.getUTCDate()); + expect(results[0].objectId.month).toEqual(createdAt.getUTCMonth() + 1); + expect(results[0].objectId.year).toEqual(createdAt.getUTCFullYear()); + done(); + }); + }); it_id('1f9b10f7-dc0e-467f-b506-a303b9c36258')(it)('group by number', done => { const options = Object.assign({}, masterKeyOptions, { @@ -325,15 +285,12 @@ describe('Parse.Query Aggregate testing', () => { get(Parse.serverURL + '/aggregate/TestObject', options) .then(resp => { expect(resp.results.length).toBe(2); - expect( - Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId') - ).toBe(true); - expect( - Object.prototype.hasOwnProperty.call(resp.results[1], 'objectId') - ).toBe(true); - expect( - resp.results.sort((a, b) => (a.objectId > b.objectId ? 1 : -1)) - ).toEqual([{ objectId: 10 }, { objectId: 20 }]); + expect(Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId')).toBe(true); + expect(Object.prototype.hasOwnProperty.call(resp.results[1], 'objectId')).toBe(true); + expect(resp.results.sort((a, b) => (a.objectId > b.objectId ? 1 : -1))).toEqual([ + { objectId: 10 }, + { objectId: 20 }, + ]); done(); }) .catch(done.fail); @@ -448,12 +405,8 @@ describe('Parse.Query Aggregate testing', () => { expect(results.length).toEqual(4); for (let i = 0; i < results.length; i++) { const item = results[i]; - expect( - Object.prototype.hasOwnProperty.call(item, 'updatedAt') - ).toEqual(true); - expect( - Object.prototype.hasOwnProperty.call(item, 'objectId') - ).toEqual(false); + expect(Object.prototype.hasOwnProperty.call(item, 'updatedAt')).toEqual(true); + expect(Object.prototype.hasOwnProperty.call(item, 'objectId')).toEqual(false); } done(); }); @@ -532,35 +485,26 @@ describe('Parse.Query Aggregate testing', () => { } ); - it_id('bf3c2704-b721-4b1b-92fa-e1b129ae4aff')(it)( - 'group by pointer', - done => { - const pointer1 = new TestObject(); - const pointer2 = new TestObject(); - const obj1 = new TestObject({ pointer: pointer1 }); - const obj2 = new TestObject({ pointer: pointer2 }); - const obj3 = new TestObject({ pointer: pointer1 }); - const pipeline = [{ $group: { _id: '$pointer' } }]; - Parse.Object.saveAll([pointer1, pointer2, obj1, obj2, obj3]) - .then(() => { - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - expect(results.length).toEqual(3); - expect( - results.some(result => result.objectId === pointer1.id) - ).toEqual(true); - expect( - results.some(result => result.objectId === pointer2.id) - ).toEqual(true); - expect(results.some(result => result.objectId === null)).toEqual( - true - ); - done(); - }); - } - ); + it_id('bf3c2704-b721-4b1b-92fa-e1b129ae4aff')(it)('group by pointer', done => { + const pointer1 = new TestObject(); + const pointer2 = new TestObject(); + const obj1 = new TestObject({ pointer: pointer1 }); + const obj2 = new TestObject({ pointer: pointer2 }); + const obj3 = new TestObject({ pointer: pointer1 }); + const pipeline = [{ $group: { _id: '$pointer' } }]; + Parse.Object.saveAll([pointer1, pointer2, obj1, obj2, obj3]) + .then(() => { + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + expect(results.length).toEqual(3); + expect(results.some(result => result.objectId === pointer1.id)).toEqual(true); + expect(results.some(result => result.objectId === pointer2.id)).toEqual(true); + expect(results.some(result => result.objectId === null)).toEqual(true); + done(); + }); + }); it_id('9ee9e8c0-a590-4af9-97a9-4b8e5080ffae')(it)('group sum query', done => { const options = Object.assign({}, masterKeyOptions, { @@ -570,9 +514,7 @@ describe('Parse.Query Aggregate testing', () => { }); get(Parse.serverURL + '/aggregate/TestObject', options) .then(resp => { - expect( - Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId') - ).toBe(true); + expect(Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId')).toBe(true); expect(resp.results[0].objectId).toBe(null); expect(resp.results[0].total).toBe(50); done(); @@ -580,26 +522,21 @@ describe('Parse.Query Aggregate testing', () => { .catch(done.fail); }); - it_id('39133cd6-5bdf-4917-b672-a9d7a9157b6f')(it)( - 'group count query', - done => { - const options = Object.assign({}, masterKeyOptions, { - body: { - $group: { _id: null, total: { $sum: 1 } }, - }, - }); - get(Parse.serverURL + '/aggregate/TestObject', options) - .then(resp => { - expect( - Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId') - ).toBe(true); - expect(resp.results[0].objectId).toBe(null); - expect(resp.results[0].total).toBe(4); - done(); - }) - .catch(done.fail); - } - ); + it_id('39133cd6-5bdf-4917-b672-a9d7a9157b6f')(it)('group count query', done => { + const options = Object.assign({}, masterKeyOptions, { + body: { + $group: { _id: null, total: { $sum: 1 } }, + }, + }); + get(Parse.serverURL + '/aggregate/TestObject', options) + .then(resp => { + expect(Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId')).toBe(true); + expect(resp.results[0].objectId).toBe(null); + expect(resp.results[0].total).toBe(4); + done(); + }) + .catch(done.fail); + }); it_id('48685ff3-066f-4353-82e7-87f39d812ff7')(it)('group min query', done => { const options = Object.assign({}, masterKeyOptions, { @@ -609,9 +546,7 @@ describe('Parse.Query Aggregate testing', () => { }); get(Parse.serverURL + '/aggregate/TestObject', options) .then(resp => { - expect( - Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId') - ).toBe(true); + expect(Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId')).toBe(true); expect(resp.results[0].objectId).toBe(null); expect(resp.results[0].minScore).toBe(10); done(); @@ -627,9 +562,7 @@ describe('Parse.Query Aggregate testing', () => { }); get(Parse.serverURL + '/aggregate/TestObject', options) .then(resp => { - expect( - Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId') - ).toBe(true); + expect(Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId')).toBe(true); expect(resp.results[0].objectId).toBe(null); expect(resp.results[0].maxScore).toBe(20); done(); @@ -645,9 +578,7 @@ describe('Parse.Query Aggregate testing', () => { }); get(Parse.serverURL + '/aggregate/TestObject', options) .then(resp => { - expect( - Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId') - ).toBe(true); + expect(Object.prototype.hasOwnProperty.call(resp.results[0], 'objectId')).toBe(true); expect(resp.results[0].objectId).toBe(null); expect(resp.results[0].avgScore).toBe(12.5); done(); @@ -669,47 +600,41 @@ describe('Parse.Query Aggregate testing', () => { .catch(done.fail); }); - it_id('c892a3d2-8ae8-4b88-bf2b-3c958e1cacd8')(it)( - 'sort ascending query', - done => { - const options = Object.assign({}, masterKeyOptions, { - body: { - $sort: { name: 1 }, - }, - }); - get(Parse.serverURL + '/aggregate/TestObject', options) - .then(resp => { - expect(resp.results.length).toBe(4); - expect(resp.results[0].name).toBe('bar'); - expect(resp.results[1].name).toBe('dpl'); - expect(resp.results[2].name).toBe('foo'); - expect(resp.results[3].name).toBe('foo'); - done(); - }) - .catch(done.fail); - } - ); + it_id('c892a3d2-8ae8-4b88-bf2b-3c958e1cacd8')(it)('sort ascending query', done => { + const options = Object.assign({}, masterKeyOptions, { + body: { + $sort: { name: 1 }, + }, + }); + get(Parse.serverURL + '/aggregate/TestObject', options) + .then(resp => { + expect(resp.results.length).toBe(4); + expect(resp.results[0].name).toBe('bar'); + expect(resp.results[1].name).toBe('dpl'); + expect(resp.results[2].name).toBe('foo'); + expect(resp.results[3].name).toBe('foo'); + done(); + }) + .catch(done.fail); + }); - it_id('79d4bc2e-8b69-42ec-8526-20d17e968ab3')(it)( - 'sort decending query', - done => { - const options = Object.assign({}, masterKeyOptions, { - body: { - $sort: { name: -1 }, - }, - }); - get(Parse.serverURL + '/aggregate/TestObject', options) - .then(resp => { - expect(resp.results.length).toBe(4); - expect(resp.results[0].name).toBe('foo'); - expect(resp.results[1].name).toBe('foo'); - expect(resp.results[2].name).toBe('dpl'); - expect(resp.results[3].name).toBe('bar'); - done(); - }) - .catch(done.fail); - } - ); + it_id('79d4bc2e-8b69-42ec-8526-20d17e968ab3')(it)('sort decending query', done => { + const options = Object.assign({}, masterKeyOptions, { + body: { + $sort: { name: -1 }, + }, + }); + get(Parse.serverURL + '/aggregate/TestObject', options) + .then(resp => { + expect(resp.results.length).toBe(4); + expect(resp.results[0].name).toBe('foo'); + expect(resp.results[1].name).toBe('foo'); + expect(resp.results[2].name).toBe('dpl'); + expect(resp.results[3].name).toBe('bar'); + done(); + }) + .catch(done.fail); + }); it_id('b3d97d48-bd6b-444d-be64-cc1fd4738266')(it)('skip query', done => { const options = Object.assign({}, masterKeyOptions, { @@ -725,29 +650,26 @@ describe('Parse.Query Aggregate testing', () => { .catch(done.fail); }); - it_id('4a7daee3-5ba1-4c8b-b406-1846a73a64c8')(it)( - 'match comparison date query', - done => { - const today = new Date(); - const yesterday = new Date(); - const tomorrow = new Date(); - yesterday.setDate(today.getDate() - 1); - tomorrow.setDate(today.getDate() + 1); - const obj1 = new TestObject({ dateField: yesterday }); - const obj2 = new TestObject({ dateField: today }); - const obj3 = new TestObject({ dateField: tomorrow }); - const pipeline = [{ $match: { dateField: { $lt: tomorrow } } }]; - Parse.Object.saveAll([obj1, obj2, obj3]) - .then(() => { - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - expect(results.length).toBe(2); - done(); - }); - } - ); + it_id('4a7daee3-5ba1-4c8b-b406-1846a73a64c8')(it)('match comparison date query', done => { + const today = new Date(); + const yesterday = new Date(); + const tomorrow = new Date(); + yesterday.setDate(today.getDate() - 1); + tomorrow.setDate(today.getDate() + 1); + const obj1 = new TestObject({ dateField: yesterday }); + const obj2 = new TestObject({ dateField: today }); + const obj3 = new TestObject({ dateField: tomorrow }); + const pipeline = [{ $match: { dateField: { $lt: tomorrow } } }]; + Parse.Object.saveAll([obj1, obj2, obj3]) + .then(() => { + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + expect(results.length).toBe(2); + done(); + }); + }); it_id('d98c8c20-6dac-4d74-8228-85a1ae46a7d0')(it)( 'should aggregate with Date object (directAccess)', @@ -769,93 +691,78 @@ describe('Parse.Query Aggregate testing', () => { } ); - it_id('3d73d23a-fce1-4ac0-972a-50f6a550f348')(it)( - 'match comparison query', - done => { - const options = Object.assign({}, masterKeyOptions, { - body: { - $match: { score: { $gt: 15 } }, - }, - }); - get(Parse.serverURL + '/aggregate/TestObject', options) - .then(resp => { - expect(resp.results.length).toBe(1); - expect(resp.results[0].score).toBe(20); - done(); - }) - .catch(done.fail); - } - ); + it_id('3d73d23a-fce1-4ac0-972a-50f6a550f348')(it)('match comparison query', done => { + const options = Object.assign({}, masterKeyOptions, { + body: { + $match: { score: { $gt: 15 } }, + }, + }); + get(Parse.serverURL + '/aggregate/TestObject', options) + .then(resp => { + expect(resp.results.length).toBe(1); + expect(resp.results[0].score).toBe(20); + done(); + }) + .catch(done.fail); + }); - it_id('11772059-6c93-41ac-8dfe-e55b6c97e16f')(it)( - 'match multiple comparison query', - done => { - const options = Object.assign({}, masterKeyOptions, { - body: { - $match: { score: { $gt: 5, $lt: 15 } }, - }, - }); - get(Parse.serverURL + '/aggregate/TestObject', options) - .then(resp => { - expect(resp.results.length).toBe(3); - expect(resp.results[0].score).toBe(10); - expect(resp.results[1].score).toBe(10); - expect(resp.results[2].score).toBe(10); - done(); - }) - .catch(done.fail); - } - ); + it_id('11772059-6c93-41ac-8dfe-e55b6c97e16f')(it)('match multiple comparison query', done => { + const options = Object.assign({}, masterKeyOptions, { + body: { + $match: { score: { $gt: 5, $lt: 15 } }, + }, + }); + get(Parse.serverURL + '/aggregate/TestObject', options) + .then(resp => { + expect(resp.results.length).toBe(3); + expect(resp.results[0].score).toBe(10); + expect(resp.results[1].score).toBe(10); + expect(resp.results[2].score).toBe(10); + done(); + }) + .catch(done.fail); + }); - it_id('ca2efb04-8f73-40ca-a5fc-79d0032bc398')(it)( - 'match complex comparison query', - done => { - const options = Object.assign({}, masterKeyOptions, { - body: { - $match: { - score: { $gt: 5, $lt: 15 }, - views: { $gt: 850, $lt: 1000 }, - }, + it_id('ca2efb04-8f73-40ca-a5fc-79d0032bc398')(it)('match complex comparison query', done => { + const options = Object.assign({}, masterKeyOptions, { + body: { + $match: { + score: { $gt: 5, $lt: 15 }, + views: { $gt: 850, $lt: 1000 }, }, - }); - get(Parse.serverURL + '/aggregate/TestObject', options) - .then(resp => { - expect(resp.results.length).toBe(1); - expect(resp.results[0].score).toBe(10); - expect(resp.results[0].views).toBe(900); - done(); - }) - .catch(done.fail); - } - ); + }, + }); + get(Parse.serverURL + '/aggregate/TestObject', options) + .then(resp => { + expect(resp.results.length).toBe(1); + expect(resp.results[0].score).toBe(10); + expect(resp.results[0].views).toBe(900); + done(); + }) + .catch(done.fail); + }); - it_id('5ef9dcbe-fe54-4db2-b8fb-58c87c6ff072')(it)( - 'match comparison and equality query', - done => { - const options = Object.assign({}, masterKeyOptions, { - body: { - $match: { score: { $gt: 5, $lt: 15 }, views: 900 }, - }, - }); - get(Parse.serverURL + '/aggregate/TestObject', options) - .then(resp => { - expect(resp.results.length).toBe(1); - expect(resp.results[0].score).toBe(10); - expect(resp.results[0].views).toBe(900); - done(); - }) - .catch(done.fail); - } - ); + it_id('5ef9dcbe-fe54-4db2-b8fb-58c87c6ff072')(it)('match comparison and equality query', done => { + const options = Object.assign({}, masterKeyOptions, { + body: { + $match: { score: { $gt: 5, $lt: 15 }, views: 900 }, + }, + }); + get(Parse.serverURL + '/aggregate/TestObject', options) + .then(resp => { + expect(resp.results.length).toBe(1); + expect(resp.results[0].score).toBe(10); + expect(resp.results[0].views).toBe(900); + done(); + }) + .catch(done.fail); + }); it_id('c910a6af-58df-46aa-bbf8-da014a04cdcd')(it)('match $or query', done => { const options = Object.assign({}, masterKeyOptions, { body: { $match: { - $or: [ - { score: { $gt: 15, $lt: 25 } }, - { views: { $gt: 750, $lt: 850 } }, - ], + $or: [{ score: { $gt: 15, $lt: 25 } }, { views: { $gt: 750, $lt: 850 } }], }, }, }); @@ -874,162 +781,128 @@ describe('Parse.Query Aggregate testing', () => { .catch(done.fail); }); - it_id('0f768dc2-0675-4e45-a763-5ca9c895fa5f')(it)( - 'match objectId query', - done => { - const obj1 = new TestObject(); - const obj2 = new TestObject(); - Parse.Object.saveAll([obj1, obj2]) - .then(() => { - const pipeline = [{ $match: { _id: obj1.id } }]; - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - expect(results.length).toEqual(1); - expect(results[0].objectId).toEqual(obj1.id); - done(); - }); - } - ); - - it_id('27349e04-0d9d-453f-ad85-1a811631582d')(it)( - 'match field query', - done => { - const obj1 = new TestObject({ name: 'TestObject1' }); - const obj2 = new TestObject({ name: 'TestObject2' }); - Parse.Object.saveAll([obj1, obj2]) - .then(() => { - const pipeline = [{ $match: { name: 'TestObject1' } }]; - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - expect(results.length).toEqual(1); - expect(results[0].objectId).toEqual(obj1.id); - done(); - }); - } - ); - - it_id('9222e025-d450-4699-8d5b-c5cf9a64fb24')(it)( - 'match pointer query', - done => { - const pointer1 = new PointerObject(); - const pointer2 = new PointerObject(); - const obj1 = new TestObject({ pointer: pointer1 }); - const obj2 = new TestObject({ pointer: pointer2 }); - const obj3 = new TestObject({ pointer: pointer1 }); - - Parse.Object.saveAll([pointer1, pointer2, obj1, obj2, obj3]) - .then(() => { - const pipeline = [{ $match: { pointer: pointer1.id } }]; - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - expect(results.length).toEqual(2); - expect(results[0].pointer.objectId).toEqual(pointer1.id); - expect(results[1].pointer.objectId).toEqual(pointer1.id); - expect(results.some(result => result.objectId === obj1.id)).toEqual( - true - ); - expect(results.some(result => result.objectId === obj3.id)).toEqual( - true - ); - done(); - }); - } - ); - - it_id('3a1e2cdc-52c7-4060-bc90-b06d557d85ce')(it_exclude_dbs(['postgres']))( - 'match exists query', - done => { - const pipeline = [{ $match: { score: { $exists: true } } }]; - const query = new Parse.Query(TestObject); - query.aggregate(pipeline).then(results => { - expect(results.length).toEqual(4); + it_id('0f768dc2-0675-4e45-a763-5ca9c895fa5f')(it)('match objectId query', done => { + const obj1 = new TestObject(); + const obj2 = new TestObject(); + Parse.Object.saveAll([obj1, obj2]) + .then(() => { + const pipeline = [{ $match: { _id: obj1.id } }]; + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + expect(results.length).toEqual(1); + expect(results[0].objectId).toEqual(obj1.id); done(); }); - } - ); + }); - it_id('0adea3f4-73f7-4b48-a7dd-c764ceb947ec')(it)( - 'match date query - createdAt', - done => { - const obj1 = new TestObject(); - const obj2 = new TestObject(); + it_id('27349e04-0d9d-453f-ad85-1a811631582d')(it)('match field query', done => { + const obj1 = new TestObject({ name: 'TestObject1' }); + const obj2 = new TestObject({ name: 'TestObject2' }); + Parse.Object.saveAll([obj1, obj2]) + .then(() => { + const pipeline = [{ $match: { name: 'TestObject1' } }]; + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + expect(results.length).toEqual(1); + expect(results[0].objectId).toEqual(obj1.id); + done(); + }); + }); - Parse.Object.saveAll([obj1, obj2]) - .then(() => { - const now = new Date(); - const today = new Date( - now.getFullYear(), - now.getMonth(), - now.getDate() - ); - const pipeline = [{ $match: { createdAt: { $gte: today } } }]; - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - // Four objects were created initially, we added two more. - expect(results.length).toEqual(6); - done(); - }); - } - ); + it_id('9222e025-d450-4699-8d5b-c5cf9a64fb24')(it)('match pointer query', done => { + const pointer1 = new PointerObject(); + const pointer2 = new PointerObject(); + const obj1 = new TestObject({ pointer: pointer1 }); + const obj2 = new TestObject({ pointer: pointer2 }); + const obj3 = new TestObject({ pointer: pointer1 }); + + Parse.Object.saveAll([pointer1, pointer2, obj1, obj2, obj3]) + .then(() => { + const pipeline = [{ $match: { pointer: pointer1.id } }]; + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + expect(results.length).toEqual(2); + expect(results[0].pointer.objectId).toEqual(pointer1.id); + expect(results[1].pointer.objectId).toEqual(pointer1.id); + expect(results.some(result => result.objectId === obj1.id)).toEqual(true); + expect(results.some(result => result.objectId === obj3.id)).toEqual(true); + done(); + }); + }); - it_id('cdc0eecb-f547-4881-84cc-c06fb46a636a')(it)( - 'match date query - updatedAt', + it_id('3a1e2cdc-52c7-4060-bc90-b06d557d85ce')(it_exclude_dbs(['postgres']))( + 'match exists query', done => { - const obj1 = new TestObject(); - const obj2 = new TestObject(); - - Parse.Object.saveAll([obj1, obj2]) - .then(() => { - const now = new Date(); - const today = new Date( - now.getFullYear(), - now.getMonth(), - now.getDate() - ); - const pipeline = [{ $match: { updatedAt: { $gte: today } } }]; - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - // Four objects were added initially, we added two more. - expect(results.length).toEqual(6); - done(); - }); + const pipeline = [{ $match: { score: { $exists: true } } }]; + const query = new Parse.Query(TestObject); + query.aggregate(pipeline).then(results => { + expect(results.length).toEqual(4); + done(); + }); } ); - it_id('621fe00a-1127-4341-a8e1-fc579b7ed8bd')(it)( - 'match date query - empty', - done => { - const obj1 = new TestObject(); - const obj2 = new TestObject(); + it_id('0adea3f4-73f7-4b48-a7dd-c764ceb947ec')(it)('match date query - createdAt', done => { + const obj1 = new TestObject(); + const obj2 = new TestObject(); + + Parse.Object.saveAll([obj1, obj2]) + .then(() => { + const now = new Date(); + const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); + const pipeline = [{ $match: { createdAt: { $gte: today } } }]; + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + // Four objects were created initially, we added two more. + expect(results.length).toEqual(6); + done(); + }); + }); - Parse.Object.saveAll([obj1, obj2]) - .then(() => { - const now = new Date(); - const future = new Date( - now.getFullYear(), - now.getMonth() + 1, - now.getDate() - ); - const pipeline = [{ $match: { createdAt: future } }]; - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - expect(results.length).toEqual(0); - done(); - }); - } - ); + it_id('cdc0eecb-f547-4881-84cc-c06fb46a636a')(it)('match date query - updatedAt', done => { + const obj1 = new TestObject(); + const obj2 = new TestObject(); + + Parse.Object.saveAll([obj1, obj2]) + .then(() => { + const now = new Date(); + const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); + const pipeline = [{ $match: { updatedAt: { $gte: today } } }]; + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + // Four objects were added initially, we added two more. + expect(results.length).toEqual(6); + done(); + }); + }); + + it_id('621fe00a-1127-4341-a8e1-fc579b7ed8bd')(it)('match date query - empty', done => { + const obj1 = new TestObject(); + const obj2 = new TestObject(); + + Parse.Object.saveAll([obj1, obj2]) + .then(() => { + const now = new Date(); + const future = new Date(now.getFullYear(), now.getMonth() + 1, now.getDate()); + const pipeline = [{ $match: { createdAt: future } }]; + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + expect(results.length).toEqual(0); + done(); + }); + }); it_id('802ffc99-861b-4b72-90a6-0c666a2e3fd8')(it_exclude_dbs(['postgres']))( 'match pointer with operator query', @@ -1050,12 +923,8 @@ describe('Parse.Query Aggregate testing', () => { expect(results.length).toEqual(2); expect(results[0].pointer.objectId).toEqual(pointer.id); expect(results[1].pointer.objectId).toEqual(pointer.id); - expect(results.some(result => result.objectId === obj1.id)).toEqual( - true - ); - expect(results.some(result => result.objectId === obj2.id)).toEqual( - true - ); + expect(results.some(result => result.objectId === obj1.id)).toEqual(true); + expect(results.some(result => result.objectId === obj2.id)).toEqual(true); done(); }); } @@ -1134,87 +1003,76 @@ describe('Parse.Query Aggregate testing', () => { .catch(done.fail); }); - it_id('69224bbb-8ea0-4ab4-af23-398b6432f668')(it)( - 'multiple project query', - done => { - const options = Object.assign({}, masterKeyOptions, { - body: { - $project: { name: 1, score: 1, sender: 1 }, - }, - }); - get(Parse.serverURL + '/aggregate/TestObject', options) - .then(resp => { - resp.results.forEach(result => { - expect(result.objectId).not.toBe(undefined); - expect(result.name).not.toBe(undefined); - expect(result.score).not.toBe(undefined); - expect(result.sender).not.toBe(undefined); - expect(result.size).toBe(undefined); - }); - done(); - }) - .catch(done.fail); - } - ); - - it_id('97ce4c7c-8d9f-4ffd-9352-394bc9867bab')(it)( - 'project pointer query', - done => { - const pointer = new PointerObject(); - const obj = new TestObject({ pointer, name: 'hello' }); - - obj - .save() - .then(() => { - const pipeline = [ - { $match: { _id: obj.id } }, - { $project: { pointer: 1, name: 1, createdAt: 1 } }, - ]; - const query = new Parse.Query(TestObject); - return query.aggregate(pipeline); - }) - .then(results => { - expect(results.length).toEqual(1); - expect(results[0].name).toEqual('hello'); - expect(results[0].createdAt).not.toBe(undefined); - expect(results[0].pointer.objectId).toEqual(pointer.id); - done(); + it_id('69224bbb-8ea0-4ab4-af23-398b6432f668')(it)('multiple project query', done => { + const options = Object.assign({}, masterKeyOptions, { + body: { + $project: { name: 1, score: 1, sender: 1 }, + }, + }); + get(Parse.serverURL + '/aggregate/TestObject', options) + .then(resp => { + resp.results.forEach(result => { + expect(result.objectId).not.toBe(undefined); + expect(result.name).not.toBe(undefined); + expect(result.score).not.toBe(undefined); + expect(result.sender).not.toBe(undefined); + expect(result.size).toBe(undefined); }); - } - ); + done(); + }) + .catch(done.fail); + }); - it_id('3940aac3-ac49-4279-8083-af9096de636f')(it)( - 'project with group query', - done => { - const options = Object.assign({}, masterKeyOptions, { - body: { - $project: { score: 1 }, - $group: { _id: '$score', score: { $sum: '$score' } }, - }, + it_id('97ce4c7c-8d9f-4ffd-9352-394bc9867bab')(it)('project pointer query', done => { + const pointer = new PointerObject(); + const obj = new TestObject({ pointer, name: 'hello' }); + + obj + .save() + .then(() => { + const pipeline = [ + { $match: { _id: obj.id } }, + { $project: { pointer: 1, name: 1, createdAt: 1 } }, + ]; + const query = new Parse.Query(TestObject); + return query.aggregate(pipeline); + }) + .then(results => { + expect(results.length).toEqual(1); + expect(results[0].name).toEqual('hello'); + expect(results[0].createdAt).not.toBe(undefined); + expect(results[0].pointer.objectId).toEqual(pointer.id); + done(); }); - get(Parse.serverURL + '/aggregate/TestObject', options) - .then(resp => { - expect(resp.results.length).toBe(2); - resp.results.forEach(result => { - expect( - Object.prototype.hasOwnProperty.call(result, 'objectId') - ).toBe(true); - expect(result.name).toBe(undefined); - expect(result.sender).toBe(undefined); - expect(result.size).toBe(undefined); - expect(result.score).not.toBe(undefined); - if (result.objectId === 10) { - expect(result.score).toBe(30); - } - if (result.objectId === 20) { - expect(result.score).toBe(20); - } - }); - done(); - }) - .catch(done.fail); - } - ); + }); + + it_id('3940aac3-ac49-4279-8083-af9096de636f')(it)('project with group query', done => { + const options = Object.assign({}, masterKeyOptions, { + body: { + $project: { score: 1 }, + $group: { _id: '$score', score: { $sum: '$score' } }, + }, + }); + get(Parse.serverURL + '/aggregate/TestObject', options) + .then(resp => { + expect(resp.results.length).toBe(2); + resp.results.forEach(result => { + expect(Object.prototype.hasOwnProperty.call(result, 'objectId')).toBe(true); + expect(result.name).toBe(undefined); + expect(result.sender).toBe(undefined); + expect(result.size).toBe(undefined); + expect(result.score).not.toBe(undefined); + if (result.objectId === 10) { + expect(result.score).toBe(30); + } + if (result.objectId === 20) { + expect(result.score).toBe(20); + } + }); + done(); + }) + .catch(done.fail); + }); it('class does not exist return empty', done => { const options = Object.assign({}, masterKeyOptions, { @@ -1258,43 +1116,37 @@ describe('Parse.Query Aggregate testing', () => { .catch(done.fail); }); - it_id('ef157f86-c456-4a4c-8dac-81910bd0f716')(it)( - 'distinct query with where', - done => { - const options = Object.assign({}, masterKeyOptions, { - body: { - distinct: 'score', - $where: { - name: 'bar', - }, + it_id('ef157f86-c456-4a4c-8dac-81910bd0f716')(it)('distinct query with where', done => { + const options = Object.assign({}, masterKeyOptions, { + body: { + distinct: 'score', + $where: { + name: 'bar', }, - }); - get(Parse.serverURL + '/aggregate/TestObject', options) - .then(resp => { - expect(resp.results[0]).toBe(10); - done(); - }) - .catch(done.fail); - } - ); + }, + }); + get(Parse.serverURL + '/aggregate/TestObject', options) + .then(resp => { + expect(resp.results[0]).toBe(10); + done(); + }) + .catch(done.fail); + }); - it_id('7f5275cc-2c34-42bc-8a09-43378419c326')(it)( - 'distinct query with where string', - done => { - const options = Object.assign({}, masterKeyOptions, { - body: { - distinct: 'score', - $where: JSON.stringify({ name: 'bar' }), - }, - }); - get(Parse.serverURL + '/aggregate/TestObject', options) - .then(resp => { - expect(resp.results[0]).toBe(10); - done(); - }) - .catch(done.fail); - } - ); + it_id('7f5275cc-2c34-42bc-8a09-43378419c326')(it)('distinct query with where string', done => { + const options = Object.assign({}, masterKeyOptions, { + body: { + distinct: 'score', + $where: JSON.stringify({ name: 'bar' }), + }, + }); + get(Parse.serverURL + '/aggregate/TestObject', options) + .then(resp => { + expect(resp.results[0]).toBe(10); + done(); + }) + .catch(done.fail); + }); it_id('383b7248-e457-4373-8d5c-f9359384347e')(it)('distinct nested', done => { const options = Object.assign({}, masterKeyOptions, { @@ -1310,31 +1162,24 @@ describe('Parse.Query Aggregate testing', () => { .catch(done.fail); }); - it_id('20f14464-adb7-428c-ac7a-5a91a1952a64')(it)( - 'distinct pointer', - done => { - const pointer1 = new PointerObject(); - const pointer2 = new PointerObject(); - const obj1 = new TestObject({ pointer: pointer1 }); - const obj2 = new TestObject({ pointer: pointer2 }); - const obj3 = new TestObject({ pointer: pointer1 }); - Parse.Object.saveAll([pointer1, pointer2, obj1, obj2, obj3]) - .then(() => { - const query = new Parse.Query(TestObject); - return query.distinct('pointer'); - }) - .then(results => { - expect(results.length).toEqual(2); - expect( - results.some(result => result.objectId === pointer1.id) - ).toEqual(true); - expect( - results.some(result => result.objectId === pointer2.id) - ).toEqual(true); - done(); - }); - } - ); + it_id('20f14464-adb7-428c-ac7a-5a91a1952a64')(it)('distinct pointer', done => { + const pointer1 = new PointerObject(); + const pointer2 = new PointerObject(); + const obj1 = new TestObject({ pointer: pointer1 }); + const obj2 = new TestObject({ pointer: pointer2 }); + const obj3 = new TestObject({ pointer: pointer1 }); + Parse.Object.saveAll([pointer1, pointer2, obj1, obj2, obj3]) + .then(() => { + const query = new Parse.Query(TestObject); + return query.distinct('pointer'); + }) + .then(results => { + expect(results.length).toEqual(2); + expect(results.some(result => result.objectId === pointer1.id)).toEqual(true); + expect(results.some(result => result.objectId === pointer2.id)).toEqual(true); + done(); + }); + }); it_id('91e6cb94-2837-44b7-b057-0c4965057caa')(it)( 'distinct class does not exist return empty', @@ -1386,76 +1231,64 @@ describe('Parse.Query Aggregate testing', () => { .catch(done.fail); }); - it_id('633fde06-c4af-474b-9841-3ccabc24dd4f')(it)( - 'distinct objectId', - async () => { - const query = new Parse.Query(TestObject); - const results = await query.distinct('objectId'); - expect(results.length).toBe(4); - } - ); + it_id('633fde06-c4af-474b-9841-3ccabc24dd4f')(it)('distinct objectId', async () => { + const query = new Parse.Query(TestObject); + const results = await query.distinct('objectId'); + expect(results.length).toBe(4); + }); - it_id('8f9706f4-2703-42f1-b524-f2f7e72bbfe7')(it)( - 'distinct createdAt', - async () => { - const object1 = new TestObject({ createdAt_test: true }); - await object1.save(); - const object2 = new TestObject({ createdAt_test: true }); - await object2.save(); - const query = new Parse.Query(TestObject); - query.equalTo('createdAt_test', true); - const results = await query.distinct('createdAt'); - expect(results.length).toBe(2); - } - ); + it_id('8f9706f4-2703-42f1-b524-f2f7e72bbfe7')(it)('distinct createdAt', async () => { + const object1 = new TestObject({ createdAt_test: true }); + await object1.save(); + const object2 = new TestObject({ createdAt_test: true }); + await object2.save(); + const query = new Parse.Query(TestObject); + query.equalTo('createdAt_test', true); + const results = await query.distinct('createdAt'); + expect(results.length).toBe(2); + }); - it_id('3562e600-8ce5-4d6d-96df-8ff969e81421')(it)( - 'distinct updatedAt', - async () => { - const object1 = new TestObject({ updatedAt_test: true }); - await object1.save(); - const object2 = new TestObject(); - await object2.save(); - object2.set('updatedAt_test', true); - await object2.save(); - const query = new Parse.Query(TestObject); - query.equalTo('updatedAt_test', true); - const results = await query.distinct('updatedAt'); - expect(results.length).toBe(2); - } - ); + it_id('3562e600-8ce5-4d6d-96df-8ff969e81421')(it)('distinct updatedAt', async () => { + const object1 = new TestObject({ updatedAt_test: true }); + await object1.save(); + const object2 = new TestObject(); + await object2.save(); + object2.set('updatedAt_test', true); + await object2.save(); + const query = new Parse.Query(TestObject); + query.equalTo('updatedAt_test', true); + const results = await query.distinct('updatedAt'); + expect(results.length).toBe(2); + }); - it_id('5012cfb1-b0aa-429d-a94f-d32d8aa0b7f9')(it)( - 'distinct null field', - done => { - const options = Object.assign({}, masterKeyOptions, { - body: { distinct: 'distinctField' }, - }); - const user1 = new Parse.User(); - user1.setUsername('distinct_1'); - user1.setPassword('password'); - user1.set('distinctField', 'one'); - - const user2 = new Parse.User(); - user2.setUsername('distinct_2'); - user2.setPassword('password'); - user2.set('distinctField', null); - user1 - .signUp() - .then(() => { - return user2.signUp(); - }) - .then(() => { - return get(Parse.serverURL + '/aggregate/_User', options); - }) - .then(resp => { - expect(resp.results.length).toEqual(1); - expect(resp.results).toEqual(['one']); - done(); - }) - .catch(done.fail); - } - ); + it_id('5012cfb1-b0aa-429d-a94f-d32d8aa0b7f9')(it)('distinct null field', done => { + const options = Object.assign({}, masterKeyOptions, { + body: { distinct: 'distinctField' }, + }); + const user1 = new Parse.User(); + user1.setUsername('distinct_1'); + user1.setPassword('password'); + user1.set('distinctField', 'one'); + + const user2 = new Parse.User(); + user2.setUsername('distinct_2'); + user2.setPassword('password'); + user2.set('distinctField', null); + user1 + .signUp() + .then(() => { + return user2.signUp(); + }) + .then(() => { + return get(Parse.serverURL + '/aggregate/_User', options); + }) + .then(resp => { + expect(resp.results.length).toEqual(1); + expect(resp.results).toEqual(['one']); + done(); + }) + .catch(done.fail); + }); it_id('d9c19419-e99d-4d9f-b7f3-418e49ee47dd')(it)( 'does not return sensitive hidden properties', @@ -1570,16 +1403,9 @@ describe('Parse.Query Aggregate testing', () => { // Create geo index which is required for `geoNear` query const database = Config.get(Parse.applicationId).database; const schema = await new Parse.Schema('GeoObject').save(); - await database.adapter.ensureIndex( - 'GeoObject', - schema, - ['location'], - undefined, - false, - { - indexType: '2dsphere', - } - ); + await database.adapter.ensureIndex('GeoObject', schema, ['location'], undefined, false, { + indexType: '2dsphere', + }); // Create objects const GeoObject = Parse.Object.extend('GeoObject'); const obj1 = new GeoObject({ @@ -1630,16 +1456,9 @@ describe('Parse.Query Aggregate testing', () => { // Create geo index which is required for `geoNear` query const database = Config.get(Parse.applicationId).database; const schema = await new Parse.Schema('GeoObject').save(); - await database.adapter.ensureIndex( - 'GeoObject', - schema, - ['location'], - undefined, - false, - { - indexType: '2dsphere', - } - ); + await database.adapter.ensureIndex('GeoObject', schema, ['location'], undefined, false, { + indexType: '2dsphere', + }); // Create objects const GeoObject = Parse.Object.extend('GeoObject'); const obj1 = new GeoObject({ @@ -1679,58 +1498,48 @@ describe('Parse.Query Aggregate testing', () => { await database.adapter.deleteAllClasses(false); }); - it_only_db('mongo')( - 'aggregate geoNear with near legacy coordinate pair', - async () => { - // Create geo index which is required for `geoNear` query - const database = Config.get(Parse.applicationId).database; - const schema = await new Parse.Schema('GeoObject').save(); - await database.adapter.ensureIndex( - 'GeoObject', - schema, - ['location'], - undefined, - false, - { - indexType: '2dsphere', - } - ); - // Create objects - const GeoObject = Parse.Object.extend('GeoObject'); - const obj1 = new GeoObject({ - value: 1, - location: new Parse.GeoPoint(1, 1), - date: new Date(1), - }); - const obj2 = new GeoObject({ - value: 2, - location: new Parse.GeoPoint(2, 1), - date: new Date(2), - }); - const obj3 = new GeoObject({ - value: 3, - location: new Parse.GeoPoint(3, 1), - date: new Date(3), - }); - await Parse.Object.saveAll([obj1, obj2, obj3]); - // Create query - const pipeline = [ - { - $geoNear: { - near: [1, 1], - key: 'location', - spherical: true, - distanceField: 'dist', - }, + it_only_db('mongo')('aggregate geoNear with near legacy coordinate pair', async () => { + // Create geo index which is required for `geoNear` query + const database = Config.get(Parse.applicationId).database; + const schema = await new Parse.Schema('GeoObject').save(); + await database.adapter.ensureIndex('GeoObject', schema, ['location'], undefined, false, { + indexType: '2dsphere', + }); + // Create objects + const GeoObject = Parse.Object.extend('GeoObject'); + const obj1 = new GeoObject({ + value: 1, + location: new Parse.GeoPoint(1, 1), + date: new Date(1), + }); + const obj2 = new GeoObject({ + value: 2, + location: new Parse.GeoPoint(2, 1), + date: new Date(2), + }); + const obj3 = new GeoObject({ + value: 3, + location: new Parse.GeoPoint(3, 1), + date: new Date(3), + }); + await Parse.Object.saveAll([obj1, obj2, obj3]); + // Create query + const pipeline = [ + { + $geoNear: { + near: [1, 1], + key: 'location', + spherical: true, + distanceField: 'dist', }, - ]; - const query = new Parse.Query(GeoObject); - const results = await query.aggregate(pipeline); - // Check results - expect(results.length).toEqual(3); - await database.adapter.deleteAllClasses(false); - } - ); + }, + ]; + const query = new Parse.Query(GeoObject); + const results = await query.aggregate(pipeline); + // Check results + expect(results.length).toEqual(3); + await database.adapter.deleteAllClasses(false); + }); it_only_db('mongo')('aggregate handle mongodb errors', async () => { const pipeline = [ diff --git a/spec/ParseQuery.Comment.spec.js b/spec/ParseQuery.Comment.spec.js index 3b3fd07ce2..7b37f2a2c2 100644 --- a/spec/ParseQuery.Comment.spec.js +++ b/spec/ParseQuery.Comment.spec.js @@ -58,9 +58,7 @@ describe_only_db('mongo')('Parse.Query with comment testing', () => { }, }); await request(options); - const result = await database - .collection('system.profile') - .findOne({}, { sort: { ts: -1 } }); + const result = await database.collection('system.profile').findOne({}, { sort: { ts: -1 } }); expect(result.command.explain.comment).toBe(comment); }); @@ -69,12 +67,9 @@ describe_only_db('mongo')('Parse.Query with comment testing', () => { const object = new TestObject(); object.set('name', 'object'); await object.save(); - const collection = - await config.database.adapter._adaptiveCollection('TestObject'); + const collection = await config.database.adapter._adaptiveCollection('TestObject'); await collection._rawFind({ name: 'object' }, { comment: comment }); - const result = await database - .collection('system.profile') - .findOne({}, { sort: { ts: -1 } }); + const result = await database.collection('system.profile').findOne({}, { sort: { ts: -1 } }); expect(result.command.comment).toBe(comment); }); @@ -88,16 +83,10 @@ describe_only_db('mongo')('Parse.Query with comment testing', () => { object2.set('name', 'object'); await object2.save(); - const collection = - await config.database.adapter._adaptiveCollection('TestObject'); - const countResult = await collection.count( - { name: 'object' }, - { comment: comment } - ); + const collection = await config.database.adapter._adaptiveCollection('TestObject'); + const countResult = await collection.count({ name: 'object' }, { comment: comment }); expect(countResult).toEqual(2); - const result = await database - .collection('system.profile') - .findOne({}, { sort: { ts: -1 } }); + const result = await database.collection('system.profile').findOne({}, { sort: { ts: -1 } }); expect(result.command.comment).toBe(comment); }); @@ -106,15 +95,12 @@ describe_only_db('mongo')('Parse.Query with comment testing', () => { const object = new TestObject(); object.set('name', 'object'); await object.save(); - const collection = - await config.database.adapter._adaptiveCollection('TestObject'); + const collection = await config.database.adapter._adaptiveCollection('TestObject'); await collection.aggregate([{ $group: { _id: '$name' } }], { explain: true, comment: comment, }); - const result = await database - .collection('system.profile') - .findOne({}, { sort: { ts: -1 } }); + const result = await database.collection('system.profile').findOne({}, { sort: { ts: -1 } }); expect(result.command.explain.comment).toBe(comment); }); }); diff --git a/spec/ParseQuery.FullTextSearch.spec.js b/spec/ParseQuery.FullTextSearch.spec.js index edbbccc7f2..e0629e2554 100644 --- a/spec/ParseQuery.FullTextSearch.spec.js +++ b/spec/ParseQuery.FullTextSearch.spec.js @@ -16,70 +16,56 @@ const fullTextHelper = async () => { 'Cafe con Leche', ]; await Parse.Object.saveAll( - subjects.map(subject => - new Parse.Object('TestObject').set({ subject, comment: subject }) - ) + subjects.map(subject => new Parse.Object('TestObject').set({ subject, comment: subject })) ); }; describe('Parse.Query Full Text Search testing', () => { - it_id('77ba6779-6584-4e09-8e7e-31f89e741d6a')(it)( - 'fullTextSearch: $search', - async () => { - await fullTextHelper(); - const query = new Parse.Query('TestObject'); - query.fullText('subject', 'coffee'); - const results = await query.find(); - expect(results.length).toBe(3); - } - ); + it_id('77ba6779-6584-4e09-8e7e-31f89e741d6a')(it)('fullTextSearch: $search', async () => { + await fullTextHelper(); + const query = new Parse.Query('TestObject'); + query.fullText('subject', 'coffee'); + const results = await query.find(); + expect(results.length).toBe(3); + }); - it_id('d1992ea6-6d92-4bfa-a487-2a49fbcf8f0d')(it)( - 'fullTextSearch: $search, sort', - async () => { - await fullTextHelper(); - const query = new Parse.Query('TestObject'); - query.fullText('subject', 'coffee'); - query.select('$score'); - query.ascending('$score'); - const results = await query.find(); - expect(results.length).toBe(3); - expect(results[0].get('score')); - expect(results[1].get('score')); - expect(results[2].get('score')); - } - ); + it_id('d1992ea6-6d92-4bfa-a487-2a49fbcf8f0d')(it)('fullTextSearch: $search, sort', async () => { + await fullTextHelper(); + const query = new Parse.Query('TestObject'); + query.fullText('subject', 'coffee'); + query.select('$score'); + query.ascending('$score'); + const results = await query.find(); + expect(results.length).toBe(3); + expect(results[0].get('score')); + expect(results[1].get('score')); + expect(results[2].get('score')); + }); - it_id('07172595-50de-4be2-984a-d3136bebb22e')(it)( - 'fulltext descending by $score', - async () => { - await fullTextHelper(); - const query = new Parse.Query('TestObject'); - query.fullText('subject', 'coffee'); - query.descending('$score'); - query.select('$score'); - const [first, second, third] = await query.find(); - expect(first).toBeDefined(); - expect(second).toBeDefined(); - expect(third).toBeDefined(); - expect(first.get('score')); - expect(second.get('score')); - expect(third.get('score')); - expect(first.get('score') >= second.get('score')).toBeTrue(); - expect(second.get('score') >= third.get('score')).toBeTrue(); - } - ); + it_id('07172595-50de-4be2-984a-d3136bebb22e')(it)('fulltext descending by $score', async () => { + await fullTextHelper(); + const query = new Parse.Query('TestObject'); + query.fullText('subject', 'coffee'); + query.descending('$score'); + query.select('$score'); + const [first, second, third] = await query.find(); + expect(first).toBeDefined(); + expect(second).toBeDefined(); + expect(third).toBeDefined(); + expect(first.get('score')); + expect(second.get('score')); + expect(third.get('score')); + expect(first.get('score') >= second.get('score')).toBeTrue(); + expect(second.get('score') >= third.get('score')).toBeTrue(); + }); - it_id('8e821973-3fae-4e7c-8152-766228a18cdd')(it)( - 'fullTextSearch: $language', - async () => { - await fullTextHelper(); - const query = new Parse.Query('TestObject'); - query.fullText('subject', 'leche', { language: 'spanish' }); - const resp = await query.find(); - expect(resp.length).toBe(2); - } - ); + it_id('8e821973-3fae-4e7c-8152-766228a18cdd')(it)('fullTextSearch: $language', async () => { + await fullTextHelper(); + const query = new Parse.Query('TestObject'); + query.fullText('subject', 'leche', { language: 'spanish' }); + const resp = await query.find(); + expect(resp.length).toBe(2); + }); it_id('7d3da216-9582-40ee-a2fe-8316feaf5c0c')(it)( 'fullTextSearch: $diacriticSensitive', @@ -120,10 +106,7 @@ describe('Parse.Query Full Text Search testing', () => { } }; await expectAsync(invalidQuery()).toBeRejectedWith( - new Parse.Error( - Parse.Error.INVALID_JSON, - 'bad $text: $search, should be object' - ) + new Parse.Error(Parse.Error.INVALID_JSON, 'bad $text: $search, should be object') ); } ); @@ -135,10 +118,7 @@ describe('Parse.Query Full Text Search testing', () => { const query = new Parse.Query('TestObject'); query.fullText('subject', 'leche', { language: true }); await expectAsync(query.find()).toBeRejectedWith( - new Parse.Error( - Parse.Error.INVALID_JSON, - 'bad $text: $language, should be string' - ) + new Parse.Error(Parse.Error.INVALID_JSON, 'bad $text: $language, should be string') ); } ); @@ -150,10 +130,7 @@ describe('Parse.Query Full Text Search testing', () => { const query = new Parse.Query('TestObject'); query.fullText('subject', 'leche', { caseSensitive: 'string' }); await expectAsync(query.find()).toBeRejectedWith( - new Parse.Error( - Parse.Error.INVALID_JSON, - 'bad $text: $caseSensitive, should be boolean' - ) + new Parse.Error(Parse.Error.INVALID_JSON, 'bad $text: $caseSensitive, should be boolean') ); } ); @@ -174,206 +151,198 @@ describe('Parse.Query Full Text Search testing', () => { ); }); -describe_only_db('mongo')( - '[mongodb] Parse.Query Full Text Search testing', - () => { - it('fullTextSearch: does not create text index if compound index exist', async () => { - await fullTextHelper(); - await databaseAdapter.dropAllIndexes('TestObject'); - let indexes = await databaseAdapter.getIndexes('TestObject'); - expect(indexes.length).toEqual(1); - await databaseAdapter.createIndex('TestObject', { - subject: 'text', - comment: 'text', - }); - indexes = await databaseAdapter.getIndexes('TestObject'); - const query = new Parse.Query('TestObject'); - query.fullText('subject', 'coffee'); - query.select('$score'); - query.ascending('$score'); - const results = await query.find(); - expect(results.length).toBe(3); - expect(results[0].get('score')); - expect(results[1].get('score')); - expect(results[2].get('score')); +describe_only_db('mongo')('[mongodb] Parse.Query Full Text Search testing', () => { + it('fullTextSearch: does not create text index if compound index exist', async () => { + await fullTextHelper(); + await databaseAdapter.dropAllIndexes('TestObject'); + let indexes = await databaseAdapter.getIndexes('TestObject'); + expect(indexes.length).toEqual(1); + await databaseAdapter.createIndex('TestObject', { + subject: 'text', + comment: 'text', + }); + indexes = await databaseAdapter.getIndexes('TestObject'); + const query = new Parse.Query('TestObject'); + query.fullText('subject', 'coffee'); + query.select('$score'); + query.ascending('$score'); + const results = await query.find(); + expect(results.length).toBe(3); + expect(results[0].get('score')); + expect(results[1].get('score')); + expect(results[2].get('score')); - indexes = await databaseAdapter.getIndexes('TestObject'); - expect(indexes.length).toEqual(2); + indexes = await databaseAdapter.getIndexes('TestObject'); + expect(indexes.length).toEqual(2); - const schemas = await new Parse.Schema('TestObject').get(); - expect(schemas.indexes._id_).toBeDefined(); - expect(schemas.indexes._id_._id).toEqual(1); - expect(schemas.indexes.subject_text_comment_text).toBeDefined(); - expect(schemas.indexes.subject_text_comment_text.subject).toEqual('text'); - expect(schemas.indexes.subject_text_comment_text.comment).toEqual('text'); - }); + const schemas = await new Parse.Schema('TestObject').get(); + expect(schemas.indexes._id_).toBeDefined(); + expect(schemas.indexes._id_._id).toEqual(1); + expect(schemas.indexes.subject_text_comment_text).toBeDefined(); + expect(schemas.indexes.subject_text_comment_text.subject).toEqual('text'); + expect(schemas.indexes.subject_text_comment_text.comment).toEqual('text'); + }); - it('fullTextSearch: does not create text index if schema compound index exist', done => { - fullTextHelper() - .then(() => { - return databaseAdapter.dropAllIndexes('TestObject'); - }) - .then(() => { - return databaseAdapter.getIndexes('TestObject'); - }) - .then(indexes => { - expect(indexes.length).toEqual(1); - return request({ - method: 'PUT', - url: 'http://localhost:8378/1/schemas/TestObject', - headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'X-Parse-Master-Key': 'test', - 'Content-Type': 'application/json', - }, - body: { - indexes: { - text_test: { subject: 'text', comment: 'text' }, - }, + it('fullTextSearch: does not create text index if schema compound index exist', done => { + fullTextHelper() + .then(() => { + return databaseAdapter.dropAllIndexes('TestObject'); + }) + .then(() => { + return databaseAdapter.getIndexes('TestObject'); + }) + .then(indexes => { + expect(indexes.length).toEqual(1); + return request({ + method: 'PUT', + url: 'http://localhost:8378/1/schemas/TestObject', + headers: { + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Master-Key': 'test', + 'Content-Type': 'application/json', + }, + body: { + indexes: { + text_test: { subject: 'text', comment: 'text' }, }, - }); - }) - .then(() => { - return databaseAdapter.getIndexes('TestObject'); - }) - .then(indexes => { - expect(indexes.length).toEqual(2); - const where = { - subject: { - $text: { - $search: { - $term: 'coffee', - }, + }, + }); + }) + .then(() => { + return databaseAdapter.getIndexes('TestObject'); + }) + .then(indexes => { + expect(indexes.length).toEqual(2); + const where = { + subject: { + $text: { + $search: { + $term: 'coffee', }, }, - }; - return request({ - method: 'POST', - url: 'http://localhost:8378/1/classes/TestObject', - body: { where, _method: 'GET' }, - headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', - }, - }); - }) - .then(resp => { - expect(resp.data.results.length).toEqual(3); - return databaseAdapter.getIndexes('TestObject'); - }) - .then(indexes => { - expect(indexes.length).toEqual(2); - request({ - url: 'http://localhost:8378/1/schemas/TestObject', - headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Master-Key': 'test', - 'Content-Type': 'application/json', - }, - }).then(response => { - const body = response.data; - expect(body.indexes._id_).toBeDefined(); - expect(body.indexes._id_._id).toEqual(1); - expect(body.indexes.text_test).toBeDefined(); - expect(body.indexes.text_test.subject).toEqual('text'); - expect(body.indexes.text_test.comment).toEqual('text'); - done(); - }); - }) - .catch(done.fail); - }); + }, + }; + return request({ + method: 'POST', + url: 'http://localhost:8378/1/classes/TestObject', + body: { where, _method: 'GET' }, + headers: { + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', + }, + }); + }) + .then(resp => { + expect(resp.data.results.length).toEqual(3); + return databaseAdapter.getIndexes('TestObject'); + }) + .then(indexes => { + expect(indexes.length).toEqual(2); + request({ + url: 'http://localhost:8378/1/schemas/TestObject', + headers: { + 'X-Parse-Application-Id': 'test', + 'X-Parse-Master-Key': 'test', + 'Content-Type': 'application/json', + }, + }).then(response => { + const body = response.data; + expect(body.indexes._id_).toBeDefined(); + expect(body.indexes._id_._id).toEqual(1); + expect(body.indexes.text_test).toBeDefined(); + expect(body.indexes.text_test.subject).toEqual('text'); + expect(body.indexes.text_test.comment).toEqual('text'); + done(); + }); + }) + .catch(done.fail); + }); - it('fullTextSearch: $diacriticSensitive - false', async () => { - await fullTextHelper(); - const query = new Parse.Query('TestObject'); - query.fullText('subject', 'CAFÉ', { diacriticSensitive: false }); - const resp = await query.find(); - expect(resp.length).toBe(2); - }); + it('fullTextSearch: $diacriticSensitive - false', async () => { + await fullTextHelper(); + const query = new Parse.Query('TestObject'); + query.fullText('subject', 'CAFÉ', { diacriticSensitive: false }); + const resp = await query.find(); + expect(resp.length).toBe(2); + }); - it('fullTextSearch: $caseSensitive', async () => { - await fullTextHelper(); - const query = new Parse.Query('TestObject'); - query.fullText('subject', 'Coffee', { caseSensitive: true }); - const results = await query.find(); - expect(results.length).toBe(1); - }); - } -); + it('fullTextSearch: $caseSensitive', async () => { + await fullTextHelper(); + const query = new Parse.Query('TestObject'); + query.fullText('subject', 'Coffee', { caseSensitive: true }); + const results = await query.find(); + expect(results.length).toBe(1); + }); +}); -describe_only_db('postgres')( - '[postgres] Parse.Query Full Text Search testing', - () => { - it('fullTextSearch: $diacriticSensitive - false', done => { - fullTextHelper() - .then(() => { - const where = { - subject: { - $text: { - $search: { - $term: 'CAFÉ', - $diacriticSensitive: false, - }, +describe_only_db('postgres')('[postgres] Parse.Query Full Text Search testing', () => { + it('fullTextSearch: $diacriticSensitive - false', done => { + fullTextHelper() + .then(() => { + const where = { + subject: { + $text: { + $search: { + $term: 'CAFÉ', + $diacriticSensitive: false, }, }, - }; - return request({ - method: 'POST', - url: 'http://localhost:8378/1/classes/TestObject', - body: { where, _method: 'GET' }, - headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', - }, - }); - }) - .then(resp => { - fail( - `$diacriticSensitive - false should not supported: ${JSON.stringify(resp)}` - ); - done(); - }) - .catch(err => { - expect(err.data.code).toEqual(Parse.Error.INVALID_JSON); - done(); + }, + }; + return request({ + method: 'POST', + url: 'http://localhost:8378/1/classes/TestObject', + body: { where, _method: 'GET' }, + headers: { + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', + }, }); - }); + }) + .then(resp => { + fail(`$diacriticSensitive - false should not supported: ${JSON.stringify(resp)}`); + done(); + }) + .catch(err => { + expect(err.data.code).toEqual(Parse.Error.INVALID_JSON); + done(); + }); + }); - it('fullTextSearch: $caseSensitive', done => { - fullTextHelper() - .then(() => { - const where = { - subject: { - $text: { - $search: { - $term: 'Coffee', - $caseSensitive: true, - }, + it('fullTextSearch: $caseSensitive', done => { + fullTextHelper() + .then(() => { + const where = { + subject: { + $text: { + $search: { + $term: 'Coffee', + $caseSensitive: true, }, }, - }; - return request({ - method: 'POST', - url: 'http://localhost:8378/1/classes/TestObject', - body: { where, _method: 'GET' }, - headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest', - 'Content-Type': 'application/json', - }, - }); - }) - .then(resp => { - fail(`$caseSensitive should not supported: ${JSON.stringify(resp)}`); - done(); - }) - .catch(err => { - expect(err.data.code).toEqual(Parse.Error.INVALID_JSON); - done(); + }, + }; + return request({ + method: 'POST', + url: 'http://localhost:8378/1/classes/TestObject', + body: { where, _method: 'GET' }, + headers: { + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'Content-Type': 'application/json', + }, }); - }); - } -); + }) + .then(resp => { + fail(`$caseSensitive should not supported: ${JSON.stringify(resp)}`); + done(); + }) + .catch(err => { + expect(err.data.code).toEqual(Parse.Error.INVALID_JSON); + done(); + }); + }); +}); diff --git a/spec/ParseQuery.hint.spec.js b/spec/ParseQuery.hint.spec.js index 3137299977..0905eb7d32 100644 --- a/spec/ParseQuery.hint.spec.js +++ b/spec/ParseQuery.hint.spec.js @@ -27,354 +27,244 @@ describe_only_db('mongo')('Parse.Query hint', () => { await TestUtils.destroyAllDataPermanently(false); }); - it_only_mongodb_version('<5.1 || >=6 <8')( - 'query find with hint string', - async () => { - const object = new TestObject(); - await object.save(); - - const collection = - await config.database.adapter._adaptiveCollection('TestObject'); - let explain = await collection._rawFind( - { _id: object.id }, - { explain: true } - ); - expect(explain.queryPlanner.winningPlan.stage).toBe('IDHACK'); - explain = await collection._rawFind( - { _id: object.id }, - { hint: '_id_', explain: true } - ); - expect(explain.queryPlanner.winningPlan.stage).toBe('FETCH'); - expect(explain.queryPlanner.winningPlan.inputStage.indexName).toBe( - '_id_' - ); - } - ); + it_only_mongodb_version('<5.1 || >=6 <8')('query find with hint string', async () => { + const object = new TestObject(); + await object.save(); + + const collection = await config.database.adapter._adaptiveCollection('TestObject'); + let explain = await collection._rawFind({ _id: object.id }, { explain: true }); + expect(explain.queryPlanner.winningPlan.stage).toBe('IDHACK'); + explain = await collection._rawFind({ _id: object.id }, { hint: '_id_', explain: true }); + expect(explain.queryPlanner.winningPlan.stage).toBe('FETCH'); + expect(explain.queryPlanner.winningPlan.inputStage.indexName).toBe('_id_'); + }); it_only_mongodb_version('>=8')('query find with hint string', async () => { const object = new TestObject(); await object.save(); - const collection = - await config.database.adapter._adaptiveCollection('TestObject'); - let explain = await collection._rawFind( - { _id: object.id }, - { explain: true } - ); + const collection = await config.database.adapter._adaptiveCollection('TestObject'); + let explain = await collection._rawFind({ _id: object.id }, { explain: true }); expect(explain.queryPlanner.winningPlan.stage).toBe('EXPRESS_IXSCAN'); - explain = await collection._rawFind( - { _id: object.id }, - { hint: '_id_', explain: true } - ); + explain = await collection._rawFind({ _id: object.id }, { hint: '_id_', explain: true }); expect(explain.queryPlanner.winningPlan.stage).toBe('FETCH'); expect(explain.queryPlanner.winningPlan.inputStage.indexName).toBe('_id_'); }); - it_only_mongodb_version('<5.1 || >=6 <8')( - 'query find with hint object', - async () => { - const object = new TestObject(); - await object.save(); - - const collection = - await config.database.adapter._adaptiveCollection('TestObject'); - let explain = await collection._rawFind( - { _id: object.id }, - { explain: true } - ); - expect(explain.queryPlanner.winningPlan.stage).toBe('IDHACK'); - explain = await collection._rawFind( - { _id: object.id }, - { hint: { _id: 1 }, explain: true } - ); - expect(explain.queryPlanner.winningPlan.stage).toBe('FETCH'); - expect(explain.queryPlanner.winningPlan.inputStage.keyPattern).toEqual({ - _id: 1, - }); - } - ); + it_only_mongodb_version('<5.1 || >=6 <8')('query find with hint object', async () => { + const object = new TestObject(); + await object.save(); + + const collection = await config.database.adapter._adaptiveCollection('TestObject'); + let explain = await collection._rawFind({ _id: object.id }, { explain: true }); + expect(explain.queryPlanner.winningPlan.stage).toBe('IDHACK'); + explain = await collection._rawFind({ _id: object.id }, { hint: { _id: 1 }, explain: true }); + expect(explain.queryPlanner.winningPlan.stage).toBe('FETCH'); + expect(explain.queryPlanner.winningPlan.inputStage.keyPattern).toEqual({ + _id: 1, + }); + }); it_only_mongodb_version('>=8')('query find with hint object', async () => { const object = new TestObject(); await object.save(); - const collection = - await config.database.adapter._adaptiveCollection('TestObject'); - let explain = await collection._rawFind( - { _id: object.id }, - { explain: true } - ); + const collection = await config.database.adapter._adaptiveCollection('TestObject'); + let explain = await collection._rawFind({ _id: object.id }, { explain: true }); expect(explain.queryPlanner.winningPlan.stage).toBe('EXPRESS_IXSCAN'); - explain = await collection._rawFind( - { _id: object.id }, - { hint: { _id: 1 }, explain: true } - ); + explain = await collection._rawFind({ _id: object.id }, { hint: { _id: 1 }, explain: true }); expect(explain.queryPlanner.winningPlan.stage).toBe('FETCH'); expect(explain.queryPlanner.winningPlan.inputStage.keyPattern).toEqual({ _id: 1, }); }); - it_only_mongodb_version('<7')( - 'query aggregate with hint string', - async () => { - const object = new TestObject({ foo: 'bar' }); - await object.save(); + it_only_mongodb_version('<7')('query aggregate with hint string', async () => { + const object = new TestObject({ foo: 'bar' }); + await object.save(); - const collection = - await config.database.adapter._adaptiveCollection('TestObject'); - let result = await collection.aggregate([{ $group: { _id: '$foo' } }], { - explain: true, - }); - let queryPlanner = result[0].stages[0].$cursor.queryPlanner; - expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); - expect(queryPlanner.winningPlan.inputStage.stage).toBe('COLLSCAN'); - expect(queryPlanner.winningPlan.inputStage.inputStage).toBeUndefined(); + const collection = await config.database.adapter._adaptiveCollection('TestObject'); + let result = await collection.aggregate([{ $group: { _id: '$foo' } }], { + explain: true, + }); + let queryPlanner = result[0].stages[0].$cursor.queryPlanner; + expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); + expect(queryPlanner.winningPlan.inputStage.stage).toBe('COLLSCAN'); + expect(queryPlanner.winningPlan.inputStage.inputStage).toBeUndefined(); + + result = await collection.aggregate([{ $group: { _id: '$foo' } }], { + hint: '_id_', + explain: true, + }); + queryPlanner = result[0].stages[0].$cursor.queryPlanner; + expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); + expect(queryPlanner.winningPlan.inputStage.stage).toBe('FETCH'); + expect(queryPlanner.winningPlan.inputStage.inputStage.stage).toBe('IXSCAN'); + expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); + }); - result = await collection.aggregate([{ $group: { _id: '$foo' } }], { - hint: '_id_', + it_only_mongodb_version('>=7')('query aggregate with hint string', async () => { + const object = new TestObject({ foo: 'bar' }); + await object.save(); + + const collection = await config.database.adapter._adaptiveCollection('TestObject'); + let result = await collection.aggregate([{ $group: { _id: '$foo' } }], { + explain: true, + }); + let queryPlanner = result[0].queryPlanner; + expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('COLLSCAN'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage).toBeUndefined(); + + result = await collection.aggregate([{ $group: { _id: '$foo' } }], { + hint: '_id_', + explain: true, + }); + queryPlanner = result[0].queryPlanner; + expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('FETCH'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.stage).toBe('IXSCAN'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName).toBe('_id_'); + }); + + it_only_mongodb_version('<7')('query aggregate with hint object', async () => { + const object = new TestObject({ foo: 'bar' }); + await object.save(); + + const collection = await config.database.adapter._adaptiveCollection('TestObject'); + let result = await collection.aggregate([{ $group: { _id: '$foo' } }], { + explain: true, + }); + let queryPlanner = result[0].stages[0].$cursor.queryPlanner; + expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); + expect(queryPlanner.winningPlan.inputStage.stage).toBe('COLLSCAN'); + expect(queryPlanner.winningPlan.inputStage.inputStage).toBeUndefined(); + + result = await collection.aggregate([{ $group: { _id: '$foo' } }], { + hint: { _id: 1 }, + explain: true, + }); + queryPlanner = result[0].stages[0].$cursor.queryPlanner; + expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); + expect(queryPlanner.winningPlan.inputStage.stage).toBe('FETCH'); + expect(queryPlanner.winningPlan.inputStage.inputStage.stage).toBe('IXSCAN'); + expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); + expect(queryPlanner.winningPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); + }); + + it_only_mongodb_version('>=7')('query aggregate with hint object', async () => { + const object = new TestObject({ foo: 'bar' }); + await object.save(); + + const collection = await config.database.adapter._adaptiveCollection('TestObject'); + let result = await collection.aggregate([{ $group: { _id: '$foo' } }], { + explain: true, + }); + let queryPlanner = result[0].queryPlanner; + expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('COLLSCAN'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage).toBeUndefined(); + + result = await collection.aggregate([{ $group: { _id: '$foo' } }], { + hint: { _id: 1 }, + explain: true, + }); + queryPlanner = result[0].queryPlanner; + expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('FETCH'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.stage).toBe('IXSCAN'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName).toBe('_id_'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); + }); + + it_only_mongodb_version('<5.1 || >=6')('query find with hint (rest)', async () => { + const object = new TestObject(); + await object.save(); + let options = Object.assign({}, masterKeyOptions, { + url: Parse.serverURL + '/classes/TestObject', + qs: { explain: true, - }); - queryPlanner = result[0].stages[0].$cursor.queryPlanner; - expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); - expect(queryPlanner.winningPlan.inputStage.stage).toBe('FETCH'); - expect(queryPlanner.winningPlan.inputStage.inputStage.stage).toBe( - 'IXSCAN' - ); - expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe( - '_id_' - ); - } - ); - - it_only_mongodb_version('>=7')( - 'query aggregate with hint string', - async () => { - const object = new TestObject({ foo: 'bar' }); - await object.save(); - - const collection = - await config.database.adapter._adaptiveCollection('TestObject'); - let result = await collection.aggregate([{ $group: { _id: '$foo' } }], { + }, + }); + let response = await request(options); + let explain = response.data.results; + expect(explain.queryPlanner.winningPlan.inputStage.stage).toBe('COLLSCAN'); + + options = Object.assign({}, masterKeyOptions, { + url: Parse.serverURL + '/classes/TestObject', + qs: { explain: true, - }); - let queryPlanner = result[0].queryPlanner; - expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); - expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe( - 'COLLSCAN' - ); - expect( - queryPlanner.winningPlan.queryPlan.inputStage.inputStage - ).toBeUndefined(); - - result = await collection.aggregate([{ $group: { _id: '$foo' } }], { hint: '_id_', + }, + }); + response = await request(options); + explain = response.data.results; + expect(explain.queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); + }); + + it_only_mongodb_version('<7')('query aggregate with hint (rest)', async () => { + const object = new TestObject({ foo: 'bar' }); + await object.save(); + let options = Object.assign({}, masterKeyOptions, { + url: Parse.serverURL + '/aggregate/TestObject', + qs: { explain: true, - }); - queryPlanner = result[0].queryPlanner; - expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); - expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('FETCH'); - expect( - queryPlanner.winningPlan.queryPlan.inputStage.inputStage.stage - ).toBe('IXSCAN'); - expect( - queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName - ).toBe('_id_'); - } - ); - - it_only_mongodb_version('<7')( - 'query aggregate with hint object', - async () => { - const object = new TestObject({ foo: 'bar' }); - await object.save(); - - const collection = - await config.database.adapter._adaptiveCollection('TestObject'); - let result = await collection.aggregate([{ $group: { _id: '$foo' } }], { - explain: true, - }); - let queryPlanner = result[0].stages[0].$cursor.queryPlanner; - expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); - expect(queryPlanner.winningPlan.inputStage.stage).toBe('COLLSCAN'); - expect(queryPlanner.winningPlan.inputStage.inputStage).toBeUndefined(); - - result = await collection.aggregate([{ $group: { _id: '$foo' } }], { - hint: { _id: 1 }, + $group: JSON.stringify({ _id: '$foo' }), + }, + }); + let response = await request(options); + let queryPlanner = response.data.results[0].stages[0].$cursor.queryPlanner; + expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); + expect(queryPlanner.winningPlan.inputStage.stage).toBe('COLLSCAN'); + expect(queryPlanner.winningPlan.inputStage.inputStage).toBeUndefined(); + + options = Object.assign({}, masterKeyOptions, { + url: Parse.serverURL + '/aggregate/TestObject', + qs: { explain: true, - }); - queryPlanner = result[0].stages[0].$cursor.queryPlanner; - expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); - expect(queryPlanner.winningPlan.inputStage.stage).toBe('FETCH'); - expect(queryPlanner.winningPlan.inputStage.inputStage.stage).toBe( - 'IXSCAN' - ); - expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe( - '_id_' - ); - expect(queryPlanner.winningPlan.inputStage.inputStage.keyPattern).toEqual( - { _id: 1 } - ); - } - ); - - it_only_mongodb_version('>=7')( - 'query aggregate with hint object', - async () => { - const object = new TestObject({ foo: 'bar' }); - await object.save(); - - const collection = - await config.database.adapter._adaptiveCollection('TestObject'); - let result = await collection.aggregate([{ $group: { _id: '$foo' } }], { + hint: '_id_', + $group: JSON.stringify({ _id: '$foo' }), + }, + }); + response = await request(options); + queryPlanner = response.data.results[0].stages[0].$cursor.queryPlanner; + expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); + expect(queryPlanner.winningPlan.inputStage.stage).toBe('FETCH'); + expect(queryPlanner.winningPlan.inputStage.inputStage.stage).toBe('IXSCAN'); + expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); + expect(queryPlanner.winningPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); + }); + + it_only_mongodb_version('>=7')('query aggregate with hint (rest)', async () => { + const object = new TestObject({ foo: 'bar' }); + await object.save(); + let options = Object.assign({}, masterKeyOptions, { + url: Parse.serverURL + '/aggregate/TestObject', + qs: { explain: true, - }); - let queryPlanner = result[0].queryPlanner; - expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); - expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe( - 'COLLSCAN' - ); - expect( - queryPlanner.winningPlan.queryPlan.inputStage.inputStage - ).toBeUndefined(); - - result = await collection.aggregate([{ $group: { _id: '$foo' } }], { - hint: { _id: 1 }, + $group: JSON.stringify({ _id: '$foo' }), + }, + }); + let response = await request(options); + let queryPlanner = response.data.results[0].queryPlanner; + expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('COLLSCAN'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage).toBeUndefined(); + + options = Object.assign({}, masterKeyOptions, { + url: Parse.serverURL + '/aggregate/TestObject', + qs: { explain: true, - }); - queryPlanner = result[0].queryPlanner; - expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); - expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('FETCH'); - expect( - queryPlanner.winningPlan.queryPlan.inputStage.inputStage.stage - ).toBe('IXSCAN'); - expect( - queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName - ).toBe('_id_'); - expect( - queryPlanner.winningPlan.queryPlan.inputStage.inputStage.keyPattern - ).toEqual({ _id: 1 }); - } - ); - - it_only_mongodb_version('<5.1 || >=6')( - 'query find with hint (rest)', - async () => { - const object = new TestObject(); - await object.save(); - let options = Object.assign({}, masterKeyOptions, { - url: Parse.serverURL + '/classes/TestObject', - qs: { - explain: true, - }, - }); - let response = await request(options); - let explain = response.data.results; - expect(explain.queryPlanner.winningPlan.inputStage.stage).toBe( - 'COLLSCAN' - ); - - options = Object.assign({}, masterKeyOptions, { - url: Parse.serverURL + '/classes/TestObject', - qs: { - explain: true, - hint: '_id_', - }, - }); - response = await request(options); - explain = response.data.results; - expect( - explain.queryPlanner.winningPlan.inputStage.inputStage.indexName - ).toBe('_id_'); - } - ); - - it_only_mongodb_version('<7')( - 'query aggregate with hint (rest)', - async () => { - const object = new TestObject({ foo: 'bar' }); - await object.save(); - let options = Object.assign({}, masterKeyOptions, { - url: Parse.serverURL + '/aggregate/TestObject', - qs: { - explain: true, - $group: JSON.stringify({ _id: '$foo' }), - }, - }); - let response = await request(options); - let queryPlanner = - response.data.results[0].stages[0].$cursor.queryPlanner; - expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); - expect(queryPlanner.winningPlan.inputStage.stage).toBe('COLLSCAN'); - expect(queryPlanner.winningPlan.inputStage.inputStage).toBeUndefined(); - - options = Object.assign({}, masterKeyOptions, { - url: Parse.serverURL + '/aggregate/TestObject', - qs: { - explain: true, - hint: '_id_', - $group: JSON.stringify({ _id: '$foo' }), - }, - }); - response = await request(options); - queryPlanner = response.data.results[0].stages[0].$cursor.queryPlanner; - expect(queryPlanner.winningPlan.stage).toBe('PROJECTION_SIMPLE'); - expect(queryPlanner.winningPlan.inputStage.stage).toBe('FETCH'); - expect(queryPlanner.winningPlan.inputStage.inputStage.stage).toBe( - 'IXSCAN' - ); - expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe( - '_id_' - ); - expect(queryPlanner.winningPlan.inputStage.inputStage.keyPattern).toEqual( - { _id: 1 } - ); - } - ); - - it_only_mongodb_version('>=7')( - 'query aggregate with hint (rest)', - async () => { - const object = new TestObject({ foo: 'bar' }); - await object.save(); - let options = Object.assign({}, masterKeyOptions, { - url: Parse.serverURL + '/aggregate/TestObject', - qs: { - explain: true, - $group: JSON.stringify({ _id: '$foo' }), - }, - }); - let response = await request(options); - let queryPlanner = response.data.results[0].queryPlanner; - expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); - expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe( - 'COLLSCAN' - ); - expect( - queryPlanner.winningPlan.queryPlan.inputStage.inputStage - ).toBeUndefined(); - - options = Object.assign({}, masterKeyOptions, { - url: Parse.serverURL + '/aggregate/TestObject', - qs: { - explain: true, - hint: '_id_', - $group: JSON.stringify({ _id: '$foo' }), - }, - }); - response = await request(options); - queryPlanner = response.data.results[0].queryPlanner; - expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); - expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('FETCH'); - expect( - queryPlanner.winningPlan.queryPlan.inputStage.inputStage.stage - ).toBe('IXSCAN'); - expect( - queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName - ).toBe('_id_'); - expect( - queryPlanner.winningPlan.queryPlan.inputStage.inputStage.keyPattern - ).toEqual({ _id: 1 }); - } - ); + hint: '_id_', + $group: JSON.stringify({ _id: '$foo' }), + }, + }); + response = await request(options); + queryPlanner = response.data.results[0].queryPlanner; + expect(queryPlanner.winningPlan.queryPlan.stage).toBe('GROUP'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.stage).toBe('FETCH'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.stage).toBe('IXSCAN'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.indexName).toBe('_id_'); + expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); + }); }); diff --git a/spec/ParseQuery.spec.js b/spec/ParseQuery.spec.js index da1d50e026..7cd5771380 100644 --- a/spec/ParseQuery.spec.js +++ b/spec/ParseQuery.spec.js @@ -568,12 +568,7 @@ describe('Parse.Query testing', () => { const objectsList = []; objectsList.push( new DateSet({ - dates: makeDates([ - '2013-02-01', - '2013-02-02', - '2013-02-03', - '2013-02-04', - ]), + dates: makeDates(['2013-02-01', '2013-02-02', '2013-02-03', '2013-02-04']), }) ); objectsList.push( @@ -584,10 +579,7 @@ describe('Parse.Query testing', () => { Parse.Object.saveAll(objectsList).then(function () { const query = new Parse.Query(DateSet); - query.containsAll( - 'dates', - makeDates(['2013-02-01', '2013-02-02', '2013-02-03']) - ); + query.containsAll('dates', makeDates(['2013-02-01', '2013-02-02', '2013-02-03'])); query.find().then( function (results) { equal(results.length, 1); @@ -657,11 +649,7 @@ describe('Parse.Query testing', () => { qs: { where: JSON.stringify({ strings: { - $all: [ - { $regex: '^\\Qthe\\E' }, - { $regex: '^\\Qfox\\E' }, - { $regex: '^\\Qlazy\\E' }, - ], + $all: [{ $regex: '^\\Qthe\\E' }, { $regex: '^\\Qfox\\E' }, { $regex: '^\\Qlazy\\E' }], }, }), }, @@ -905,63 +893,60 @@ describe('Parse.Query testing', () => { ); }); - it_id('01a15195-dde2-4368-b996-d746a4ede3a1')(it)( - 'containedBy pointer array', - done => { - const objects = Array.from(Array(10).keys()).map(idx => { - const obj = new Parse.Object('Object'); - obj.set('key', idx); - return obj; - }); + it_id('01a15195-dde2-4368-b996-d746a4ede3a1')(it)('containedBy pointer array', done => { + const objects = Array.from(Array(10).keys()).map(idx => { + const obj = new Parse.Object('Object'); + obj.set('key', idx); + return obj; + }); - const parent = new Parse.Object('Parent'); - const parent2 = new Parse.Object('Parent'); - const parent3 = new Parse.Object('Parent'); + const parent = new Parse.Object('Parent'); + const parent2 = new Parse.Object('Parent'); + const parent3 = new Parse.Object('Parent'); - Parse.Object.saveAll(objects) - .then(() => { - // [0, 1, 2] - parent.set('objects', objects.slice(0, 3)); + Parse.Object.saveAll(objects) + .then(() => { + // [0, 1, 2] + parent.set('objects', objects.slice(0, 3)); - const shift = objects.shift(); - // [2, 0] - parent2.set('objects', [objects[1], shift]); + const shift = objects.shift(); + // [2, 0] + parent2.set('objects', [objects[1], shift]); - // [1, 2, 3, 4] - parent3.set('objects', objects.slice(1, 4)); + // [1, 2, 3, 4] + parent3.set('objects', objects.slice(1, 4)); - return Parse.Object.saveAll([parent, parent2, parent3]); - }) - .then(() => { - // [1, 2, 3, 4, 5, 6, 7, 8, 9] - const pointers = objects.map(object => object.toPointer()); + return Parse.Object.saveAll([parent, parent2, parent3]); + }) + .then(() => { + // [1, 2, 3, 4, 5, 6, 7, 8, 9] + const pointers = objects.map(object => object.toPointer()); - // Return all Parent where all parent.objects are contained in objects - return request({ - url: Parse.serverURL + '/classes/Parent', - qs: { - where: JSON.stringify({ - objects: { - $containedBy: pointers, - }, - }), - }, - headers: { - 'X-Parse-Application-Id': Parse.applicationId, - 'X-Parse-Javascript-Key': Parse.javaScriptKey, - 'Content-Type': 'application/json', - }, - }); - }) - .then(response => { - const results = response.data; - expect(results.results[0].objectId).not.toBeUndefined(); - expect(results.results[0].objectId).toBe(parent3.id); - expect(results.results.length).toBe(1); - done(); + // Return all Parent where all parent.objects are contained in objects + return request({ + url: Parse.serverURL + '/classes/Parent', + qs: { + where: JSON.stringify({ + objects: { + $containedBy: pointers, + }, + }), + }, + headers: { + 'X-Parse-Application-Id': Parse.applicationId, + 'X-Parse-Javascript-Key': Parse.javaScriptKey, + 'Content-Type': 'application/json', + }, }); - } - ); + }) + .then(response => { + const results = response.data; + expect(results.results[0].objectId).not.toBeUndefined(); + expect(results.results[0].objectId).toBe(parent3.id); + expect(results.results.length).toBe(1); + done(); + }); + }); it('containedBy number array', done => { const options = Object.assign({}, masterKeyOptions, { @@ -976,12 +961,7 @@ describe('Parse.Query testing', () => { const obj3 = new TestObject({ numbers: [1, 2, 3, 4] }); Parse.Object.saveAll([obj1, obj2, obj3]) .then(() => { - return request( - Object.assign( - { url: Parse.serverURL + '/classes/TestObject' }, - options - ) - ); + return request(Object.assign({ url: Parse.serverURL + '/classes/TestObject' }, options)); }) .then(response => { const results = response.data; @@ -1003,12 +983,7 @@ describe('Parse.Query testing', () => { const obj3 = new TestObject({ numbers: [1, 2, 3, 4] }); Parse.Object.saveAll([obj1, obj2, obj3]) .then(() => { - return request( - Object.assign( - { url: Parse.serverURL + '/classes/TestObject' }, - options - ) - ); + return request(Object.assign({ url: Parse.serverURL + '/classes/TestObject' }, options)); }) .then(response => { const results = response.data; @@ -1027,12 +1002,7 @@ describe('Parse.Query testing', () => { obj .save() .then(() => { - return request( - Object.assign( - { url: Parse.serverURL + '/classes/TestObject' }, - options - ) - ); + return request(Object.assign({ url: Parse.serverURL + '/classes/TestObject' }, options)); }) .then(done.fail) .catch(response => { @@ -1046,9 +1016,7 @@ describe('Parse.Query testing', () => { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; - Parse.Object.saveAll( - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) - ).then(function () { + Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber)).then(function () { const query = new Parse.Query(BoxedNumber); query.equalTo('number', 3); query.find().then(function (results) { @@ -1062,9 +1030,7 @@ describe('Parse.Query testing', () => { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; - Parse.Object.saveAll( - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) - ).then(function () { + Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber)).then(function () { const query = new Parse.Query(BoxedNumber); query.equalTo('number', undefined); query.find().then(function (results) { @@ -1078,9 +1044,7 @@ describe('Parse.Query testing', () => { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; - Parse.Object.saveAll( - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) - ).then(function () { + Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber)).then(function () { const query = new Parse.Query(BoxedNumber); query.lessThan('number', 7); query.find().then(function (results) { @@ -1094,9 +1058,7 @@ describe('Parse.Query testing', () => { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; - Parse.Object.saveAll( - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) - ).then(function () { + Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber)).then(function () { const query = new Parse.Query(BoxedNumber); query.lessThanOrEqualTo('number', 7); query.find().then(function (results) { @@ -1146,9 +1108,7 @@ describe('Parse.Query testing', () => { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; - Parse.Object.saveAll( - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) - ).then(function () { + Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber)).then(function () { const query = new Parse.Query(BoxedNumber); query.greaterThan('number', 7); query.find().then(function (results) { @@ -1162,9 +1122,7 @@ describe('Parse.Query testing', () => { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; - Parse.Object.saveAll( - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) - ).then(function () { + Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber)).then(function () { const query = new Parse.Query(BoxedNumber); query.greaterThanOrEqualTo('number', 7); query.find().then(function (results) { @@ -1214,9 +1172,7 @@ describe('Parse.Query testing', () => { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; - Parse.Object.saveAll( - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) - ).then(function () { + Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber)).then(function () { const query = new Parse.Query(BoxedNumber); query.lessThanOrEqualTo('number', 7); query.greaterThanOrEqualTo('number', 7); @@ -1231,9 +1187,7 @@ describe('Parse.Query testing', () => { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; - Parse.Object.saveAll( - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) - ).then(function () { + Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber)).then(function () { const query = new Parse.Query(BoxedNumber); query.lessThan('number', 9); query.greaterThan('number', 3); @@ -1248,9 +1202,7 @@ describe('Parse.Query testing', () => { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; - Parse.Object.saveAll( - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) - ).then(function () { + Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber)).then(function () { const query = new Parse.Query(BoxedNumber); query.notEqualTo('number', 5); query.find().then(function (results) { @@ -1338,12 +1290,12 @@ describe('Parse.Query testing', () => { const obj1 = new TestObject({ field: false }); const obj2 = new TestObject({ field: true }); Parse.Object.saveAll([obj1, obj2]).then(() => { - request( - Object.assign({ url: Parse.serverURL + '/classes/TestObject' }, options) - ).then(resp => { - equal(resp.data.results.length, 1); - done(); - }); + request(Object.assign({ url: Parse.serverURL + '/classes/TestObject' }, options)).then( + resp => { + equal(resp.data.results.length, 1); + done(); + } + ); }); }); @@ -1356,12 +1308,12 @@ describe('Parse.Query testing', () => { const obj1 = new TestObject({ field: false }); const obj2 = new TestObject({ field: null }); Parse.Object.saveAll([obj1, obj2]).then(() => { - return request( - Object.assign({ url: Parse.serverURL + '/classes/TestObject' }, options) - ).then(resp => { - equal(resp.data.results.length, 1); - done(); - }); + return request(Object.assign({ url: Parse.serverURL + '/classes/TestObject' }, options)).then( + resp => { + equal(resp.data.results.length, 1); + done(); + } + ); }); }); @@ -1369,9 +1321,7 @@ describe('Parse.Query testing', () => { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; - Parse.Object.saveAll( - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) - ).then(function () { + Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber)).then(function () { const query = new Parse.Query(BoxedNumber); query.containedIn('number', [3, 5, 7, 9, 11]); query.find().then(function (results) { @@ -1425,9 +1375,7 @@ describe('Parse.Query testing', () => { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; - Parse.Object.saveAll( - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) - ).then(function () { + Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber)).then(function () { const query = new Parse.Query(BoxedNumber); query.notContainedIn('number', [3, 5, 7, 9, 11]); query.find().then(function (results) { @@ -1441,16 +1389,9 @@ describe('Parse.Query testing', () => { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; - Parse.Object.saveAll( - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) - ).then(function (list) { + Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber)).then(function (list) { const query = new Parse.Query(BoxedNumber); - query.containedIn('objectId', [ - list[2].id, - list[3].id, - list[0].id, - 'NONSENSE', - ]); + query.containedIn('objectId', [list[2].id, list[3].id, list[0].id, 'NONSENSE']); query.ascending('number'); query.find().then(function (results) { if (results.length != 3) { @@ -1469,9 +1410,7 @@ describe('Parse.Query testing', () => { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; - Parse.Object.saveAll( - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) - ).then(function (list) { + Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber)).then(function (list) { const query = new Parse.Query(BoxedNumber); query.equalTo('objectId', list[4].id); query.find().then(function (results) { @@ -1490,9 +1429,7 @@ describe('Parse.Query testing', () => { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; - Parse.Object.saveAll( - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) - ).then(function () { + Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber)).then(function () { const query = new Parse.Query(BoxedNumber); query.equalTo('number', 17); query.find().then(function (results) { @@ -1513,50 +1450,44 @@ describe('Parse.Query testing', () => { }); it('get', function (done) { - Parse.Object.saveAll([new TestObject({ foo: 'bar' })]).then( - function (items) { - ok(items[0]); - const objectId = items[0].id; - const query = new Parse.Query(TestObject); - query.get(objectId).then(function (result) { - ok(result); - equal(result.id, objectId); - equal(result.get('foo'), 'bar'); - ok(result.createdAt instanceof Date); - ok(result.updatedAt instanceof Date); - done(); - }); - } - ); + Parse.Object.saveAll([new TestObject({ foo: 'bar' })]).then(function (items) { + ok(items[0]); + const objectId = items[0].id; + const query = new Parse.Query(TestObject); + query.get(objectId).then(function (result) { + ok(result); + equal(result.id, objectId); + equal(result.get('foo'), 'bar'); + ok(result.createdAt instanceof Date); + ok(result.updatedAt instanceof Date); + done(); + }); + }); }); it('get undefined', function (done) { - Parse.Object.saveAll([new TestObject({ foo: 'bar' })]).then( - function (items) { - ok(items[0]); - const query = new Parse.Query(TestObject); - query.get(undefined).then(fail, () => done()); - } - ); + Parse.Object.saveAll([new TestObject({ foo: 'bar' })]).then(function (items) { + ok(items[0]); + const query = new Parse.Query(TestObject); + query.get(undefined).then(fail, () => done()); + }); }); it('get error', function (done) { - Parse.Object.saveAll([new TestObject({ foo: 'bar' })]).then( - function (items) { - ok(items[0]); - const query = new Parse.Query(TestObject); - query.get('InvalidObjectID').then( - function () { - ok(false, 'The get should have failed.'); - done(); - }, - function (error) { - equal(error.code, Parse.Error.OBJECT_NOT_FOUND); - done(); - } - ); - } - ); + Parse.Object.saveAll([new TestObject({ foo: 'bar' })]).then(function (items) { + ok(items[0]); + const query = new Parse.Query(TestObject); + query.get('InvalidObjectID').then( + function () { + ok(false, 'The get should have failed.'); + done(); + }, + function (error) { + equal(error.code, Parse.Error.OBJECT_NOT_FOUND); + done(); + } + ); + }); }); it('first', function (done) { @@ -1582,17 +1513,16 @@ describe('Parse.Query testing', () => { }); it('first with two results', function (done) { - Parse.Object.saveAll([ - new TestObject({ foo: 'bar' }), - new TestObject({ foo: 'bar' }), - ]).then(function () { - const query = new Parse.Query(TestObject); - query.equalTo('foo', 'bar'); - query.first().then(function (result) { - equal(result.get('foo'), 'bar'); - done(); - }); - }); + Parse.Object.saveAll([new TestObject({ foo: 'bar' }), new TestObject({ foo: 'bar' })]).then( + function () { + const query = new Parse.Query(TestObject); + query.equalTo('foo', 'bar'); + query.first().then(function (result) { + equal(result.get('foo'), 'bar'); + done(); + }); + } + ); }); it('first with error', function (done) { @@ -1614,62 +1544,54 @@ describe('Parse.Query testing', () => { const item2 = new TestObject(); const container1 = new Container({ item: item1 }); const container2 = new Container({ item: item2 }); - Parse.Object.saveAll([item1, item2, container1, container2]).then( - function () { - const query = new Parse.Query(Container); - query.notEqualTo('item', item1); - query.find().then(function (results) { - equal(results.length, 1); - done(); - }); - } - ); + Parse.Object.saveAll([item1, item2, container1, container2]).then(function () { + const query = new Parse.Query(Container); + query.notEqualTo('item', item1); + query.find().then(function (results) { + equal(results.length, 1); + done(); + }); + }); }); it('skip', function (done) { - Parse.Object.saveAll([new TestObject(), new TestObject()]).then( - function () { - const query = new Parse.Query(TestObject); - query.skip(1); + Parse.Object.saveAll([new TestObject(), new TestObject()]).then(function () { + const query = new Parse.Query(TestObject); + query.skip(1); + query.find().then(function (results) { + equal(results.length, 1); + query.skip(3); query.find().then(function (results) { - equal(results.length, 1); - query.skip(3); - query.find().then(function (results) { - equal(results.length, 0); - done(); - }); + equal(results.length, 0); + done(); }); - } - ); + }); + }); }); it("skip doesn't affect count", function (done) { - Parse.Object.saveAll([new TestObject(), new TestObject()]).then( - function () { - const query = new Parse.Query(TestObject); + Parse.Object.saveAll([new TestObject(), new TestObject()]).then(function () { + const query = new Parse.Query(TestObject); + query.count().then(function (count) { + equal(count, 2); + query.skip(1); query.count().then(function (count) { equal(count, 2); - query.skip(1); + query.skip(3); query.count().then(function (count) { equal(count, 2); - query.skip(3); - query.count().then(function (count) { - equal(count, 2); - done(); - }); + done(); }); }); - } - ); + }); + }); }); it('count', function (done) { const makeBoxedNumber = function (i) { return new BoxedNumber({ number: i }); }; - Parse.Object.saveAll( - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber) - ).then(function () { + Parse.Object.saveAll([0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(makeBoxedNumber)).then(function () { const query = new Parse.Query(BoxedNumber); query.greaterThan('number', 1); query.count().then(function (count) { @@ -1722,9 +1644,7 @@ describe('Parse.Query testing', () => { const objects = testSet.map(e => new Parse.Object('Test', e)); Parse.Object.saveAll(objects) - .then(() => - new Parse.Query('Test').addDescending('sortField.value').first() - ) + .then(() => new Parse.Query('Test').addDescending('sortField.value').first()) .then(result => { expect(result.get('sortField').value).toBe('Z'); return new Parse.Query('Test').addAscending('sortField.value').first(); @@ -1745,14 +1665,10 @@ describe('Parse.Query testing', () => { const objects = testSet.map(e => new Parse.Object('Test', e)); Parse.Object.saveAll(objects) - .then(() => - new Parse.Query('Test').addDescending('sortField.value.field').first() - ) + .then(() => new Parse.Query('Test').addDescending('sortField.value.field').first()) .then(result => { expect(result.get('sortField').value.field).toBe('Z'); - return new Parse.Query('Test') - .addAscending('sortField.value.field') - .first(); + return new Parse.Query('Test').addAscending('sortField.value.field').first(); }) .then(result => { expect(result.get('sortField').value.field).toBe('A'); @@ -1772,14 +1688,10 @@ describe('Parse.Query testing', () => { const objects = testSet.map(e => new Parse.Object('Test', e)); Parse.Object.saveAll(objects) - .then(() => - new Parse.Query('Test').addDescending('sortField.value').first() - ) + .then(() => new Parse.Query('Test').addDescending('sortField.value').first()) .then(result => { expect(result.get('sortField').value).toBe(10); - return new Parse.Query('Test') - .addAscending('sortField.value') - .first(); + return new Parse.Query('Test').addAscending('sortField.value').first(); }) .then(result => { expect(result.get('sortField').value).toBe(1); @@ -1800,14 +1712,10 @@ describe('Parse.Query testing', () => { const objects = testSet.map(e => new Parse.Object('Test', e)); Parse.Object.saveAll(objects) - .then(() => - new Parse.Query('Test').addDescending('sortField.value.field').first() - ) + .then(() => new Parse.Query('Test').addDescending('sortField.value.field').first()) .then(result => { expect(result.get('sortField').value.field).toBe(10); - return new Parse.Query('Test') - .addAscending('sortField.value.field') - .first(); + return new Parse.Query('Test').addAscending('sortField.value.field').first(); }) .then(result => { expect(result.get('sortField').value.field).toBe(1); @@ -2716,9 +2624,7 @@ describe('Parse.Query testing', () => { expect(results.length).toBe(1); const figure = results[0]; expect(figure.get('consistsOf').length).toBe(1); - expect(figure.get('consistsOf')[0].get('color').get('hex')).toBe( - '#133733' - ); + expect(figure.get('consistsOf')[0].get('color').get('hex')).toBe('#133733'); done(); }, () => { @@ -2865,31 +2771,21 @@ describe('Parse.Query testing', () => { const options = Object.assign({}, masterKeyOptions, { qs: { where: JSON.stringify({ - $nor: [ - { rating: { $gt: highValue } }, - { rating: { $lte: lowValue } }, - ], + $nor: [{ rating: { $gt: highValue } }, { rating: { $lte: lowValue } }], }), }, }); Parse.Object.saveAll(objects) .then(() => { - return request( - Object.assign( - { url: Parse.serverURL + '/classes/TestObject' }, - options - ) - ); + return request(Object.assign({ url: Parse.serverURL + '/classes/TestObject' }, options)); }) .then(response => { const results = response.data; expect(results.results.length).toBe(highValue - lowValue); - expect( - results.results.every( - res => res.rating > lowValue && res.rating <= highValue - ) - ).toBe(true); + expect(results.results.every(res => res.rating > lowValue && res.rating <= highValue)).toBe( + true + ); done(); }); }); @@ -2904,12 +2800,7 @@ describe('Parse.Query testing', () => { obj .save() .then(() => { - return request( - Object.assign( - { url: Parse.serverURL + '/classes/TestObject' }, - options - ) - ); + return request(Object.assign({ url: Parse.serverURL + '/classes/TestObject' }, options)); }) .then(done.fail) .catch(response => { @@ -2928,12 +2819,7 @@ describe('Parse.Query testing', () => { obj .save() .then(() => { - return request( - Object.assign( - { url: Parse.serverURL + '/classes/TestObject' }, - options - ) - ); + return request(Object.assign({ url: Parse.serverURL + '/classes/TestObject' }, options)); }) .then(done.fail) .catch(response => { @@ -3053,11 +2939,7 @@ describe('Parse.Query testing', () => { const subQuery2 = new Parse.Query(PersonObject); subQuery2.matchesKeyInQuery('hometown', 'location', query2); const mainQuery = new Parse.Query(PersonObject); - mainQuery.doesNotMatchKeyInQuery( - 'objectId', - 'objectId', - Parse.Query.or(subQuery, subQuery2) - ); + mainQuery.doesNotMatchKeyInQuery('objectId', 'objectId', Parse.Query.or(subQuery, subQuery2)); mainQuery.find().then(function (results) { equal(results.length, 1); equal(results[0].get('name'), 'Bob'); @@ -3357,16 +3239,8 @@ describe('Parse.Query testing', () => { ok(result1.updatedAt, 'expected object updatedAt to be set'); ok(!result1.dirty(), 'expected result not to be dirty'); strictEqual(result1.get('foo'), 'baz'); - strictEqual( - result1.get('bar'), - undefined, - "expected 'bar' field to be unset" - ); - strictEqual( - result1.get('qux'), - undefined, - "expected 'qux' field to be unset" - ); + strictEqual(result1.get('bar'), undefined, "expected 'bar' field to be unset"); + strictEqual(result1.get('qux'), undefined, "expected 'qux' field to be unset"); const result2 = await result1.fetch(); strictEqual(result2.get('foo'), 'baz'); @@ -3381,21 +3255,9 @@ describe('Parse.Query testing', () => { ok(result3.createdAt, 'expected object createdAt to be set'); ok(result3.updatedAt, 'expected object updatedAt to be set'); ok(!result3.dirty(), 'expected result not to be dirty'); - strictEqual( - result3.get('foo'), - undefined, - "expected 'foo' field to be unset" - ); - strictEqual( - result3.get('bar'), - undefined, - "expected 'bar' field to be unset" - ); - strictEqual( - result3.get('qux'), - undefined, - "expected 'qux' field to be unset" - ); + strictEqual(result3.get('foo'), undefined, "expected 'foo' field to be unset"); + strictEqual(result3.get('bar'), undefined, "expected 'bar' field to be unset"); + strictEqual(result3.get('qux'), undefined, "expected 'qux' field to be unset"); obj._clearServerData(); const query3 = new Parse.Query(TestObject); @@ -3405,21 +3267,9 @@ describe('Parse.Query testing', () => { ok(result4.createdAt, 'expected object createdAt to be set'); ok(result4.updatedAt, 'expected object updatedAt to be set'); ok(!result4.dirty(), 'expected result not to be dirty'); - strictEqual( - result4.get('foo'), - undefined, - "expected 'foo' field to be unset" - ); - strictEqual( - result4.get('bar'), - undefined, - "expected 'bar' field to be unset" - ); - strictEqual( - result4.get('qux'), - undefined, - "expected 'qux' field to be unset" - ); + strictEqual(result4.get('foo'), undefined, "expected 'foo' field to be unset"); + strictEqual(result4.get('bar'), undefined, "expected 'bar' field to be unset"); + strictEqual(result4.get('qux'), undefined, "expected 'qux' field to be unset"); obj._clearServerData(); const query4 = new Parse.Query(TestObject); @@ -3430,16 +3280,8 @@ describe('Parse.Query testing', () => { ok(result5.updatedAt, 'expected object updatedAt to be set'); ok(!result5.dirty(), 'expected result not to be dirty'); strictEqual(result5.get('foo'), 'baz'); - strictEqual( - result5.get('bar'), - undefined, - "expected 'bar' field to be unset" - ); - strictEqual( - result5.get('qux'), - undefined, - "expected 'qux' field to be unset" - ); + strictEqual(result5.get('bar'), undefined, "expected 'bar' field to be unset"); + strictEqual(result5.get('qux'), undefined, "expected 'qux' field to be unset"); obj._clearServerData(); const query5 = new Parse.Query(TestObject); @@ -3449,11 +3291,7 @@ describe('Parse.Query testing', () => { ok(!result6.dirty(), 'expected result not to be dirty'); strictEqual(result6.get('foo'), 'baz'); strictEqual(result6.get('bar'), 1); - strictEqual( - result6.get('qux'), - undefined, - "expected 'qux' field to be unset" - ); + strictEqual(result6.get('qux'), undefined, "expected 'qux' field to be unset"); obj._clearServerData(); const query6 = new Parse.Query(TestObject); @@ -3473,11 +3311,7 @@ describe('Parse.Query testing', () => { ok(!result8.dirty(), 'expected result not to be dirty'); strictEqual(result8.get('foo'), 'baz'); strictEqual(result8.get('bar'), 1); - strictEqual( - result8.get('qux'), - undefined, - "expected 'qux' field to be unset" - ); + strictEqual(result8.get('qux'), undefined, "expected 'qux' field to be unset"); obj._clearServerData(); const query8 = new Parse.Query(TestObject); @@ -3539,14 +3373,8 @@ describe('Parse.Query testing', () => { headers: masterKeyHeaders, }); ok(response4.data.results[0].objectId, 'expected objectId to be set'); - ok( - response4.data.results[0].createdAt, - 'expected object createdAt to be set' - ); - ok( - response4.data.results[0].updatedAt, - 'expected object updatedAt to be set' - ); + ok(response4.data.results[0].createdAt, 'expected object createdAt to be set'); + ok(response4.data.results[0].updatedAt, 'expected object updatedAt to be set'); expect(response4.data.results[0].foo).toBeUndefined(); expect(response4.data.results[0].bar).toBeUndefined(); expect(response4.data.results[0].hello).toBeUndefined(); @@ -3560,14 +3388,8 @@ describe('Parse.Query testing', () => { headers: masterKeyHeaders, }); ok(response5.data.results[0].objectId, 'expected objectId to be set'); - ok( - response5.data.results[0].createdAt, - 'expected object createdAt to be set' - ); - ok( - response5.data.results[0].updatedAt, - 'expected object updatedAt to be set' - ); + ok(response5.data.results[0].createdAt, 'expected object createdAt to be set'); + ok(response5.data.results[0].updatedAt, 'expected object updatedAt to be set'); expect(response5.data.results[0].foo).toBe('baz'); expect(response5.data.results[0].bar).toBe(1); expect(response5.data.results[0].hello).toBe('world'); @@ -3586,14 +3408,8 @@ describe('Parse.Query testing', () => { headers: masterKeyHeaders, }); ok(response.data.results[0].objectId, 'expected objectId to be set'); - ok( - response.data.results[0].createdAt, - 'expected object createdAt to be set' - ); - ok( - response.data.results[0].updatedAt, - 'expected object updatedAt to be set' - ); + ok(response.data.results[0].createdAt, 'expected object createdAt to be set'); + ok(response.data.results[0].updatedAt, 'expected object updatedAt to be set'); expect(response.data.results[0].foo).toBeUndefined(); expect(response.data.results[0].bar).toBeUndefined(); expect(response.data.results[0].hello).toBeUndefined(); @@ -3607,14 +3423,8 @@ describe('Parse.Query testing', () => { headers: masterKeyHeaders, }); ok(response2.data.results[0].objectId, 'expected objectId to be set'); - ok( - response2.data.results[0].createdAt, - 'expected object createdAt to be set' - ); - ok( - response2.data.results[0].updatedAt, - 'expected object updatedAt to be set' - ); + ok(response2.data.results[0].createdAt, 'expected object createdAt to be set'); + ok(response2.data.results[0].updatedAt, 'expected object updatedAt to be set'); expect(response2.data.results[0].foo).toBe('baz'); expect(response2.data.results[0].bar).toBeUndefined(); expect(response2.data.results[0].hello).toBe('world'); @@ -3628,14 +3438,8 @@ describe('Parse.Query testing', () => { headers: masterKeyHeaders, }); ok(response3.data.results[0].objectId, 'expected objectId to be set'); - ok( - response3.data.results[0].createdAt, - 'expected object createdAt to be set' - ); - ok( - response3.data.results[0].updatedAt, - 'expected object updatedAt to be set' - ); + ok(response3.data.results[0].createdAt, 'expected object createdAt to be set'); + ok(response3.data.results[0].updatedAt, 'expected object updatedAt to be set'); expect(response3.data.results[0].foo).toBe('baz'); expect(response3.data.results[0].bar).toBe(1); expect(response3.data.results[0].hello).toBe('world'); @@ -3653,11 +3457,7 @@ describe('Parse.Query testing', () => { ok(result1.createdAt, 'expected object createdAt to be set'); ok(result1.updatedAt, 'expected object updatedAt to be set'); ok(!result1.dirty(), 'expected result not to be dirty'); - strictEqual( - result1.get('foo'), - undefined, - "expected 'bar' field to be unset" - ); + strictEqual(result1.get('foo'), undefined, "expected 'bar' field to be unset"); strictEqual(result1.get('bar'), 1); strictEqual(result1.get('qux'), 2); @@ -3698,11 +3498,7 @@ describe('Parse.Query testing', () => { ok(result5.createdAt, 'expected object createdAt to be set'); ok(result5.updatedAt, 'expected object updatedAt to be set'); ok(!result5.dirty(), 'expected result not to be dirty'); - strictEqual( - result5.get('foo'), - undefined, - "expected 'bar' field to be unset" - ); + strictEqual(result5.get('foo'), undefined, "expected 'bar' field to be unset"); strictEqual(result5.get('bar'), 1); strictEqual(result5.get('qux'), 2); @@ -3712,16 +3508,8 @@ describe('Parse.Query testing', () => { const result6 = await query5.first(); ok(result6.id, 'expected object id to be set'); ok(!result6.dirty(), 'expected result not to be dirty'); - strictEqual( - result6.get('foo'), - undefined, - "expected 'bar' field to be unset" - ); - strictEqual( - result6.get('bar'), - undefined, - "expected 'bar' field to be unset" - ); + strictEqual(result6.get('foo'), undefined, "expected 'bar' field to be unset"); + strictEqual(result6.get('bar'), undefined, "expected 'bar' field to be unset"); strictEqual(result6.get('qux'), 2); obj._clearServerData(); @@ -3730,21 +3518,9 @@ describe('Parse.Query testing', () => { const result7 = await query6.first(); ok(result7.id, 'expected object id to be set'); ok(!result7.dirty(), 'expected result not to be dirty'); - strictEqual( - result7.get('foo'), - undefined, - "expected 'bar' field to be unset" - ); - strictEqual( - result7.get('bar'), - undefined, - "expected 'bar' field to be unset" - ); - strictEqual( - result7.get('qux'), - undefined, - "expected 'bar' field to be unset" - ); + strictEqual(result7.get('foo'), undefined, "expected 'bar' field to be unset"); + strictEqual(result7.get('bar'), undefined, "expected 'bar' field to be unset"); + strictEqual(result7.get('qux'), undefined, "expected 'bar' field to be unset"); obj._clearServerData(); const query7 = new Parse.Query(TestObject); @@ -3752,11 +3528,7 @@ describe('Parse.Query testing', () => { const result8 = await query7.first(); ok(result8.id, 'expected object id to be set'); ok(!result8.dirty(), 'expected result not to be dirty'); - strictEqual( - result8.get('foo'), - undefined, - "expected 'bar' field to be unset" - ); + strictEqual(result8.get('foo'), undefined, "expected 'bar' field to be unset"); strictEqual(result8.get('bar'), 1); strictEqual(result8.get('qux'), 2); @@ -3766,16 +3538,8 @@ describe('Parse.Query testing', () => { const result9 = await query8.first(); ok(result9.id, 'expected object id to be set'); ok(!result9.dirty(), 'expected result not to be dirty'); - strictEqual( - result9.get('foo'), - undefined, - "expected 'bar' field to be unset" - ); - strictEqual( - result9.get('bar'), - undefined, - "expected 'bar' field to be unset" - ); + strictEqual(result9.get('foo'), undefined, "expected 'bar' field to be unset"); + strictEqual(result9.get('bar'), undefined, "expected 'bar' field to be unset"); strictEqual(result9.get('qux'), 2); obj._clearServerData(); @@ -3784,21 +3548,9 @@ describe('Parse.Query testing', () => { const result10 = await query9.first(); ok(result10.id, 'expected object id to be set'); ok(!result10.dirty(), 'expected result not to be dirty'); - strictEqual( - result10.get('foo'), - undefined, - "expected 'bar' field to be unset" - ); - strictEqual( - result10.get('bar'), - undefined, - "expected 'bar' field to be unset" - ); - strictEqual( - result10.get('qux'), - undefined, - "expected 'bar' field to be unset" - ); + strictEqual(result10.get('foo'), undefined, "expected 'bar' field to be unset"); + strictEqual(result10.get('bar'), undefined, "expected 'bar' field to be unset"); + strictEqual(result10.get('qux'), undefined, "expected 'bar' field to be unset"); }); it('exclude keys (arrays)', async () => { @@ -3814,14 +3566,8 @@ describe('Parse.Query testing', () => { headers: masterKeyHeaders, }); ok(response.data.results[0].objectId, 'expected objectId to be set'); - ok( - response.data.results[0].createdAt, - 'expected object createdAt to be set' - ); - ok( - response.data.results[0].updatedAt, - 'expected object updatedAt to be set' - ); + ok(response.data.results[0].createdAt, 'expected object createdAt to be set'); + ok(response.data.results[0].updatedAt, 'expected object updatedAt to be set'); expect(response.data.results[0].foo).toBeUndefined(); expect(response.data.results[0].hello).toBe('world'); @@ -3834,14 +3580,8 @@ describe('Parse.Query testing', () => { headers: masterKeyHeaders, }); ok(response2.data.results[0].objectId, 'expected objectId to be set'); - ok( - response2.data.results[0].createdAt, - 'expected object createdAt to be set' - ); - ok( - response2.data.results[0].updatedAt, - 'expected object updatedAt to be set' - ); + ok(response2.data.results[0].createdAt, 'expected object createdAt to be set'); + ok(response2.data.results[0].updatedAt, 'expected object updatedAt to be set'); expect(response2.data.results[0].foo).toBeUndefined(); expect(response2.data.results[0].hello).toBeUndefined(); @@ -3854,14 +3594,8 @@ describe('Parse.Query testing', () => { headers: masterKeyHeaders, }); ok(response3.data.results[0].objectId, 'expected objectId to be set'); - ok( - response3.data.results[0].createdAt, - 'expected object createdAt to be set' - ); - ok( - response3.data.results[0].updatedAt, - 'expected object updatedAt to be set' - ); + ok(response3.data.results[0].createdAt, 'expected object createdAt to be set'); + ok(response3.data.results[0].updatedAt, 'expected object updatedAt to be set'); expect(response3.data.results[0].foo).toBe('baz'); expect(response3.data.results[0].hello).toBe('world'); @@ -3874,14 +3608,8 @@ describe('Parse.Query testing', () => { headers: masterKeyHeaders, }); ok(response4.data.results[0].objectId, 'expected objectId to be set'); - ok( - response4.data.results[0].createdAt, - 'expected object createdAt to be set' - ); - ok( - response4.data.results[0].updatedAt, - 'expected object updatedAt to be set' - ); + ok(response4.data.results[0].createdAt, 'expected object createdAt to be set'); + ok(response4.data.results[0].updatedAt, 'expected object updatedAt to be set'); expect(response4.data.results[0].foo).toBe('baz'); expect(response4.data.results[0].hello).toBe('world'); }); @@ -3899,14 +3627,8 @@ describe('Parse.Query testing', () => { headers: masterKeyHeaders, }); ok(response.data.results[0].objectId, 'expected objectId to be set'); - ok( - response.data.results[0].createdAt, - 'expected object createdAt to be set' - ); - ok( - response.data.results[0].updatedAt, - 'expected object updatedAt to be set' - ); + ok(response.data.results[0].createdAt, 'expected object createdAt to be set'); + ok(response.data.results[0].updatedAt, 'expected object updatedAt to be set'); expect(response.data.results[0].foo).toBeUndefined(); expect(response.data.results[0].hello).toBe('world'); @@ -3919,14 +3641,8 @@ describe('Parse.Query testing', () => { headers: masterKeyHeaders, }); ok(response2.data.results[0].objectId, 'expected objectId to be set'); - ok( - response2.data.results[0].createdAt, - 'expected object createdAt to be set' - ); - ok( - response2.data.results[0].updatedAt, - 'expected object updatedAt to be set' - ); + ok(response2.data.results[0].createdAt, 'expected object createdAt to be set'); + ok(response2.data.results[0].updatedAt, 'expected object updatedAt to be set'); expect(response2.data.results[0].foo).toBe('baz'); expect(response2.data.results[0].hello).toBe('world'); @@ -3939,14 +3655,8 @@ describe('Parse.Query testing', () => { headers: masterKeyHeaders, }); ok(response3.data.results[0].objectId, 'expected objectId to be set'); - ok( - response3.data.results[0].createdAt, - 'expected object createdAt to be set' - ); - ok( - response3.data.results[0].updatedAt, - 'expected object updatedAt to be set' - ); + ok(response3.data.results[0].createdAt, 'expected object createdAt to be set'); + ok(response3.data.results[0].updatedAt, 'expected object updatedAt to be set'); expect(response3.data.results[0].foo).toBe('baz'); expect(response3.data.results[0].hello).toBeUndefined(); @@ -3959,14 +3669,8 @@ describe('Parse.Query testing', () => { headers: masterKeyHeaders, }); ok(response4.data.results[0].objectId, 'expected objectId to be set'); - ok( - response4.data.results[0].createdAt, - 'expected object createdAt to be set' - ); - ok( - response4.data.results[0].updatedAt, - 'expected object updatedAt to be set' - ); + ok(response4.data.results[0].createdAt, 'expected object createdAt to be set'); + ok(response4.data.results[0].updatedAt, 'expected object updatedAt to be set'); expect(response4.data.results[0].foo).toBeUndefined(); expect(response4.data.results[0].hello).toBeUndefined(); }); @@ -4086,11 +3790,7 @@ describe('Parse.Query testing', () => { ok(result.updatedAt, 'expected object updatedAt to be set'); ok(!result.dirty(), 'expected result not to be dirty'); strictEqual(result.get('foo'), 'baz'); - strictEqual( - result.get('bar'), - undefined, - 'expected "bar" field to be unset' - ); + strictEqual(result.get('bar'), undefined, 'expected "bar" field to be unset'); }) .then( function () { @@ -4104,43 +3804,40 @@ describe('Parse.Query testing', () => { }); }); - it_id('56b09b92-c756-4bae-8c32-1c32b5b4c397')(it)( - 'notEqual with array of pointers', - done => { - const children = []; - const parents = []; - const promises = []; - for (let i = 0; i < 2; i++) { - const proc = iter => { - const child = new Parse.Object('Child'); - children.push(child); - const parent = new Parse.Object('Parent'); - parents.push(parent); - promises.push( - child.save().then(() => { - parents[iter].set('child', [children[iter]]); - return parents[iter].save(); - }) - ); - }; - proc(i); - } - Promise.all(promises) - .then(() => { - const query = new Parse.Query('Parent'); - query.notEqualTo('child', children[0]); - return query.find(); - }) - .then(results => { - expect(results.length).toEqual(1); - expect(results[0].id).toEqual(parents[1].id); - done(); - }) - .catch(error => { - console.log(error); - }); + it_id('56b09b92-c756-4bae-8c32-1c32b5b4c397')(it)('notEqual with array of pointers', done => { + const children = []; + const parents = []; + const promises = []; + for (let i = 0; i < 2; i++) { + const proc = iter => { + const child = new Parse.Object('Child'); + children.push(child); + const parent = new Parse.Object('Parent'); + parents.push(parent); + promises.push( + child.save().then(() => { + parents[iter].set('child', [children[iter]]); + return parents[iter].save(); + }) + ); + }; + proc(i); } - ); + Promise.all(promises) + .then(() => { + const query = new Parse.Query('Parent'); + query.notEqualTo('child', children[0]); + return query.find(); + }) + .then(results => { + expect(results.length).toEqual(1); + expect(results[0].id).toEqual(parents[1].id); + done(); + }) + .catch(error => { + console.log(error); + }); + }); // PG don't support creating a null column it_exclude_dbs(['postgres'])('querying for null value', done => { @@ -4333,37 +4030,22 @@ describe('Parse.Query testing', () => { return user .save() .then(user => { - const objIdQuery = new Parse.Query('_User').equalTo( - 'objectId', - user.id - ); + const objIdQuery = new Parse.Query('_User').equalTo('objectId', user.id); const blockedUserQuery = user.relation('blockedUsers').query(); - const aResponseQuery = new Parse.Query( - 'MatchRelationshipActivityResponse' - ); + const aResponseQuery = new Parse.Query('MatchRelationshipActivityResponse'); aResponseQuery.equalTo('userA', user); aResponseQuery.equalTo('userAResponse', 1); - const bResponseQuery = new Parse.Query( - 'MatchRelationshipActivityResponse' - ); + const bResponseQuery = new Parse.Query('MatchRelationshipActivityResponse'); bResponseQuery.equalTo('userB', user); bResponseQuery.equalTo('userBResponse', 1); const matchOr = Parse.Query.or(aResponseQuery, bResponseQuery); const matchRelationshipA = new Parse.Query('_User'); - matchRelationshipA.matchesKeyInQuery( - 'objectId', - 'userAObjectId', - matchOr - ); + matchRelationshipA.matchesKeyInQuery('objectId', 'userAObjectId', matchOr); const matchRelationshipB = new Parse.Query('_User'); - matchRelationshipB.matchesKeyInQuery( - 'objectId', - 'userBObjectId', - matchOr - ); + matchRelationshipB.matchesKeyInQuery('objectId', 'userBObjectId', matchOr); const orQuery = Parse.Query.or( objIdQuery, @@ -4668,10 +4350,7 @@ describe('Parse.Query testing', () => { equal(foobarObj.get('barBaz').get('key'), 'value'); equal(foobarObj.get('barBaz').get('otherKey'), undefined); equal(foobarObj.get('barBaz').get('bazoo').get('some'), 'thing'); - equal( - foobarObj.get('barBaz').get('bazoo').get('otherSome'), - undefined - ); + equal(foobarObj.get('barBaz').get('bazoo').get('otherSome'), undefined); } else { fail('barBaz should be set'); } @@ -4848,12 +4527,7 @@ describe('Parse.Query testing', () => { includeAll: true, }, }); - return request( - Object.assign( - { url: Parse.serverURL + '/classes/Container' }, - options - ) - ); + return request(Object.assign({ url: Parse.serverURL + '/classes/Container' }, options)); }) .then(resp => { const result = resp.data.results[0]; @@ -5040,10 +4714,7 @@ describe('Parse.Query testing', () => { equal(foobarObj.get('barBaz').get('otherKey'), undefined); if (foobarObj.get('barBaz').has('bazoo')) { equal(foobarObj.get('barBaz').get('bazoo').get('some'), 'thing'); - equal( - foobarObj.get('barBaz').get('bazoo').get('otherSome'), - undefined - ); + equal(foobarObj.get('barBaz').get('bazoo').get('otherSome'), undefined); } else { fail('bazoo should be set'); } @@ -5135,11 +4806,7 @@ describe('Parse.Query testing', () => { .then(() => { const query = new Parse.Query('TestObject'); query.include('testPointerField'); - query.select([ - 'testPointerField', - 'testPointerField.otherField', - 'shouldBe', - ]); + query.select(['testPointerField', 'testPointerField.otherField', 'shouldBe']); return query.find(); }) .then(results => { @@ -5263,11 +4930,7 @@ describe('Parse.Query testing', () => { rolesOfTypeX.equalTo('type', 'x'); const groupsWithRoleX = new Parse.Query('Group'); - groupsWithRoleX.matchesKeyInQuery( - 'objectId', - 'belongsTo.objectId', - rolesOfTypeX - ); + groupsWithRoleX.matchesKeyInQuery('objectId', 'belongsTo.objectId', rolesOfTypeX); groupsWithRoleX.find().then(function (results) { equal(results.length, 1); @@ -5307,11 +4970,7 @@ describe('Parse.Query testing', () => { rolesOfTypeX.equalTo('type', 'x'); const groupsWithRoleX = new Parse.Query('Group'); - groupsWithRoleX.doesNotMatchKeyInQuery( - 'objectId', - 'belongsTo.objectId', - rolesOfTypeX - ); + groupsWithRoleX.doesNotMatchKeyInQuery('objectId', 'belongsTo.objectId', rolesOfTypeX); groupsWithRoleX.find().then(function (results) { equal(results.length, 1); @@ -5342,11 +5001,7 @@ describe('Parse.Query testing', () => { rolesOfTypeX.equalTo('type', 'x'); const groupsWithRoleX = new Parse.Query('Group'); - groupsWithRoleX.matchesKeyInQuery( - 'objectId', - 'belongsTo.objectId', - rolesOfTypeX - ); + groupsWithRoleX.matchesKeyInQuery('objectId', 'belongsTo.objectId', rolesOfTypeX); const results = await groupsWithRoleX.find(); equal(results.length, 1); @@ -5375,11 +5030,7 @@ describe('Parse.Query testing', () => { rolesOfTypeX.equalTo('type', 'x'); const groupsWithRoleX = new Parse.Query('Group'); - groupsWithRoleX.doesNotMatchKeyInQuery( - 'objectId', - 'belongsTo.objectId', - rolesOfTypeX - ); + groupsWithRoleX.doesNotMatchKeyInQuery('objectId', 'belongsTo.objectId', rolesOfTypeX); const results = await groupsWithRoleX.find(); equal(results.length, 1); @@ -5704,9 +5355,7 @@ describe('Parse.Query testing', () => { }); await parent.save(); - const query1 = await new Parse.Query('Parent') - .equalTo('some.nested.key.child', child) - .find(); + const query1 = await new Parse.Query('Parent').equalTo('some.nested.key.child', child).find(); expect(query1.length).toEqual(1); }); @@ -5749,10 +5398,7 @@ describe('Parse.Query testing', () => { await parent.save(); const query1 = await new Parse.Query('Parent') - .matchesQuery( - 'some.nested.key.child', - new Parse.Query('Child').equalTo('key', 'value') - ) + .matchesQuery('some.nested.key.child', new Parse.Query('Child').equalTo('key', 'value')) .find(); expect(query1.length).toEqual(1); diff --git a/spec/ParseRelation.spec.js b/spec/ParseRelation.spec.js index 93ae42225a..f0c746065d 100644 --- a/spec/ParseRelation.spec.js +++ b/spec/ParseRelation.spec.js @@ -193,11 +193,7 @@ describe('Parse.Relation testing', () => { const list = await query.find(); equal(list.length, 1, 'There should only be one element'); ok(list[0] instanceof ChildObject, 'Should be of type ChildObject'); - equal( - list[0].id, - childObjects[2].id, - 'We should have gotten back the right result' - ); + equal(list[0].id, childObjects[2].id, 'We should have gotten back the right result'); }); it('queries on relation fields', async () => { @@ -267,9 +263,7 @@ describe('Parse.Relation testing', () => { .then(() => { const objectsWithChild0InBothChildren = new Parse.Query(ParentObject); objectsWithChild0InBothChildren.containedIn('child', [childObjects[0]]); - objectsWithChild0InBothChildren.containedIn('otherChild', [ - childObjects[0], - ]); + objectsWithChild0InBothChildren.containedIn('otherChild', [childObjects[0]]); return objectsWithChild0InBothChildren.find(); }) .then(objectsWithChild0InBothChildren => { @@ -279,9 +273,7 @@ describe('Parse.Relation testing', () => { .then(() => { const objectsWithChild4andOtherChild1 = new Parse.Query(ParentObject); objectsWithChild4andOtherChild1.containedIn('child', [childObjects[4]]); - objectsWithChild4andOtherChild1.containedIn('otherChild', [ - childObjects[1], - ]); + objectsWithChild4andOtherChild1.containedIn('otherChild', [childObjects[1]]); return objectsWithChild4andOtherChild1.find(); }) .then(objects => { diff --git a/spec/ParseRole.spec.js b/spec/ParseRole.spec.js index 30b6d083dc..6adda4830d 100644 --- a/spec/ParseRole.spec.js +++ b/spec/ParseRole.spec.js @@ -144,10 +144,7 @@ describe('Parse Role testing', () => { return Promise.all(promises); }; - const restExecute = spyOn( - RestQuery._UnsafeRestQuery.prototype, - 'execute' - ).and.callThrough(); + const restExecute = spyOn(RestQuery._UnsafeRestQuery.prototype, 'execute').and.callThrough(); let user, auth, getAllRolesSpy; createTestUser() @@ -176,10 +173,7 @@ describe('Parse Role testing', () => { isMaster: true, user: user, }); - getAllRolesSpy = spyOn( - auth, - '_getAllRolesNamesForRoleIds' - ).and.callThrough(); + getAllRolesSpy = spyOn(auth, '_getAllRolesNamesForRoleIds').and.callThrough(); return auth._loadRoles(); }) @@ -266,46 +260,34 @@ describe('Parse Role testing', () => { const moderator = new Parse.Role('Moderator', new Parse.ACL()); const superModerator = new Parse.Role('SuperModerator', new Parse.ACL()); const contentManager = new Parse.Role('ContentManager', new Parse.ACL()); - const superContentManager = new Parse.Role( - 'SuperContentManager', - new Parse.ACL() - ); - Parse.Object.saveAll( - [admin, moderator, contentManager, superModerator, superContentManager], - { - useMasterKey: true, - } - ) + const superContentManager = new Parse.Role('SuperContentManager', new Parse.ACL()); + Parse.Object.saveAll([admin, moderator, contentManager, superModerator, superContentManager], { + useMasterKey: true, + }) .then(() => { contentManager.getRoles().add([moderator, superContentManager]); moderator.getRoles().add([admin, superModerator]); superContentManager.getRoles().add(superModerator); return Parse.Object.saveAll( - [ - admin, - moderator, - contentManager, - superModerator, - superContentManager, - ], - { useMasterKey: true } + [admin, moderator, contentManager, superModerator, superContentManager], + { + useMasterKey: true, + } ); }) .then(() => { const auth = new Auth({ config: Config.get('test'), isMaster: true }); // For each role, fetch their sibling, what they inherit // return with result and roleId for later comparison - const promises = [admin, moderator, contentManager, superModerator].map( - role => { - return auth._getAllRolesNamesForRoleIds([role.id]).then(result => { - return Promise.resolve({ - id: role.id, - name: role.get('name'), - roleNames: result, - }); + const promises = [admin, moderator, contentManager, superModerator].map(role => { + return auth._getAllRolesNamesForRoleIds([role.id]).then(result => { + return Promise.resolve({ + id: role.id, + name: role.get('name'), + roleNames: result, }); - } - ); + }); + }); return Promise.all(promises); }) @@ -591,17 +573,15 @@ describe('Parse Role testing', () => { users.add(user); role.save({}, { useMasterKey: true }).then(() => { const otherUser = new Parse.User(); - otherUser - .save({ username: 'otherUser', password: 'otherUser' }) - .then(otherUser => { - const query = new Parse.Query(Parse.Role); - query.equalTo('name', 'admin'); - query.equalTo('users', otherUser); - query.find().then(function (roles) { - expect(roles.length).toEqual(0); - done(); - }); + otherUser.save({ username: 'otherUser', password: 'otherUser' }).then(otherUser => { + const query = new Parse.Query(Parse.Role); + query.equalTo('name', 'admin'); + query.equalTo('users', otherUser); + query.find().then(function (roles) { + expect(roles.length).toEqual(0); + done(); }); + }); }); }); }); diff --git a/spec/ParseServer.spec.js b/spec/ParseServer.spec.js index f2c4b5e0e2..ec12d6f7fd 100644 --- a/spec/ParseServer.spec.js +++ b/spec/ParseServer.spec.js @@ -43,9 +43,7 @@ describe('Server Url Checks', () => { }); it('does not have unhandled promise rejection in the case of load error', done => { - const parseServerProcess = spawn( - path.resolve(__dirname, './support/FailingServer.js') - ); + const parseServerProcess = spawn(path.resolve(__dirname, './support/FailingServer.js')); let stdout; let stderr; parseServerProcess.stdout.on('data', data => { diff --git a/spec/ParseServerRESTController.spec.js b/spec/ParseServerRESTController.spec.js index c51a976e85..f35b5c7a3c 100644 --- a/spec/ParseServerRESTController.spec.js +++ b/spec/ParseServerRESTController.spec.js @@ -73,14 +73,11 @@ describe('ParseServerRESTController', () => { spyOn(router, 'tryRouteRequest').and.callThrough(); RESTController = ParseServerRESTController(Parse.applicationId, router); const resp = await RESTController.request('POST', '/classes/MyObject'); - const { status, response, location } = - await router.tryRouteRequest.calls.all()[0].returnValue; + const { status, response, location } = await router.tryRouteRequest.calls.all()[0].returnValue; expect(status).toBe(201); expect(response).toEqual(resp); - expect(location).toBe( - `http://localhost:8378/1/classes/MyObject/${resp.objectId}` - ); + expect(location).toBe(`http://localhost:8378/1/classes/MyObject/${resp.objectId}`); }); it('should handle response status in batch', async () => { @@ -166,14 +163,9 @@ describe('ParseServerRESTController', () => { const results = await query.find(); expect(createSpy.calls.count()).toBe(2); for (let i = 0; i + 1 < createSpy.calls.length; i = i + 2) { - expect(createSpy.calls.argsFor(i)[3]).toBe( - createSpy.calls.argsFor(i + 1)[3] - ); + expect(createSpy.calls.argsFor(i)[3]).toBe(createSpy.calls.argsFor(i + 1)[3]); } - expect(results.map(result => result.get('key')).sort()).toEqual([ - 'value1', - 'value2', - ]); + expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']); }); it('should not save anything when one operation fails in a transaction', async () => { @@ -445,10 +437,7 @@ describe('ParseServerRESTController', () => { const query = new Parse.Query('MyObject'); const results = await query.find(); - expect(results.map(result => result.get('key')).sort()).toEqual([ - 'value1', - 'value2', - ]); + expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']); const query2 = new Parse.Query('MyObject2'); const results2 = await query2.find(); @@ -456,10 +445,7 @@ describe('ParseServerRESTController', () => { const query3 = new Parse.Query('MyObject3'); const results3 = await query3.find(); - expect(results3.map(result => result.get('key')).sort()).toEqual([ - 'value1', - 'value2', - ]); + expect(results3.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']); expect(createSpy.calls.count() >= 13).toEqual(true); let transactionalSession; @@ -545,14 +531,9 @@ describe('ParseServerRESTController', () => { const user = await Parse.User.signUp('user', 'pass'); const userId = user.id; await Parse.User.logOut(); - const res = await RESTController.request( - 'GET', - '/classes/_User', - undefined, - { - useMasterKey: true, - } - ); + const res = await RESTController.request('GET', '/classes/_User', undefined, { + useMasterKey: true, + }); expect(res.results.length).toBe(1); expect(res.results[0].objectId).toEqual(userId); }); @@ -563,9 +544,7 @@ describe('ParseServerRESTController', () => { username: '', password: 'world', }); - fail( - 'Success callback should not be called when passing an empty username.' - ); + fail('Success callback should not be called when passing an empty username.'); } catch (err) { expect(err.code).toBe(Parse.Error.USERNAME_MISSING); expect(err.message).toBe('bad or missing username'); @@ -578,9 +557,7 @@ describe('ParseServerRESTController', () => { username: 'hello', password: '', }); - fail( - 'Success callback should not be called when passing an empty password.' - ); + fail('Success callback should not be called when passing an empty password.'); } catch (err) { expect(err.code).toBe(Parse.Error.PASSWORD_MISSING); expect(err.message).toBe('password is required'); diff --git a/spec/ParseUser.spec.js b/spec/ParseUser.spec.js index e62b3ff734..9c08bfffad 100644 --- a/spec/ParseUser.spec.js +++ b/spec/ParseUser.spec.js @@ -7,8 +7,7 @@ 'use strict'; -const MongoStorageAdapter = - require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter').default; +const MongoStorageAdapter = require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter').default; const request = require('../lib/request'); const passwordCrypto = require('../lib/password'); const Config = require('../lib/Config'); @@ -17,31 +16,23 @@ const cryptoUtils = require('../lib/cryptoUtils'); describe('allowExpiredAuthDataToken option', () => { it('should accept true value', async () => { await reconfigureServer({ allowExpiredAuthDataToken: true }); - expect(Config.get(Parse.applicationId).allowExpiredAuthDataToken).toBe( - true - ); + expect(Config.get(Parse.applicationId).allowExpiredAuthDataToken).toBe(true); }); it('should accept false value', async () => { await reconfigureServer({ allowExpiredAuthDataToken: false }); - expect(Config.get(Parse.applicationId).allowExpiredAuthDataToken).toBe( - false - ); + expect(Config.get(Parse.applicationId).allowExpiredAuthDataToken).toBe(false); }); it('should default false', async () => { await reconfigureServer({}); - expect(Config.get(Parse.applicationId).allowExpiredAuthDataToken).toBe( - false - ); + expect(Config.get(Parse.applicationId).allowExpiredAuthDataToken).toBe(false); }); it('should enforce boolean values', async () => { const options = [[], 'a', '', 0, 1, {}, 'true', 'false']; for (const option of options) { - await expectAsync( - reconfigureServer({ allowExpiredAuthDataToken: option }) - ).toBeRejected(); + await expectAsync(reconfigureServer({ allowExpiredAuthDataToken: option })).toBeRejected(); } }); }); @@ -301,8 +292,7 @@ describe('Parse.User testing', () => { it_only_db('mongo')('should let legacy users without ACL login', async () => { await reconfigureServer(); - const databaseURI = - 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase'; + const databaseURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase'; const adapter = new MongoStorageAdapter({ collectionPrefix: 'test_', uri: databaseURI, @@ -321,8 +311,7 @@ describe('Parse.User testing', () => { const collection = await adapter._adaptiveCollection('_User'); await collection.insertOne({ // the hashed password is 'password' hashed - _hashed_password: - '$2b$10$mJ2ca2UbCM9hlojYHZxkQe8pyEXe5YMg0nMdvP4AJBeqlTEZJ6/Uu', + _hashed_password: '$2b$10$mJ2ca2UbCM9hlojYHZxkQe8pyEXe5YMg0nMdvP4AJBeqlTEZJ6/Uu', _session_token: 'xxx', email: 'xxx@a.b', username: 'oldUser', @@ -478,10 +467,7 @@ describe('Parse.User testing', () => { .then( function () { // This should have failed actually. - ok( - false, - "Shouldn't have been able to log in with garbage session token." - ); + ok(false, "Shouldn't have been able to log in with garbage session token."); }, function (error) { ok(error); @@ -635,10 +621,7 @@ describe('Parse.User testing', () => { await user.save({ ACL: acl }, { useMasterKey: true }); // Try to update from admin... should all work fine - await user.save( - { key: 'fromAdmin' }, - { sessionToken: admin.getSessionToken() } - ); + await user.save({ key: 'fromAdmin' }, { sessionToken: admin.getSessionToken() }); await user.fetch(); expect(user.toJSON().key).toEqual('fromAdmin'); @@ -956,11 +939,7 @@ describe('Parse.User testing', () => { equal(aliceAgain.get('username'), 'alice'); equal(aliceAgain.id, alice.id, 'currentUser should have objectId'); ok(aliceAgain.getSessionToken(), 'currentUser should have a sessionToken'); - equal( - alice.get('password'), - undefined, - 'currentUser should not have password' - ); + equal(alice.get('password'), undefined, 'currentUser should not have password'); done(); }); @@ -980,16 +959,9 @@ describe('Parse.User testing', () => { delete Parse.User._currentUserMatchesDisk; const userFromDisk = Parse.User.current(); - equal( - userFromDisk.get('password'), - undefined, - 'password should not be in attributes' - ); + equal(userFromDisk.get('password'), undefined, 'password should not be in attributes'); equal(userFromDisk.id, id, 'id should be set'); - ok( - userFromDisk.getSessionToken(), - 'currentUser should have a sessionToken' - ); + ok(userFromDisk.getSessionToken(), 'currentUser should have a sessionToken'); done(); }); }); @@ -1023,11 +995,7 @@ describe('Parse.User testing', () => { 'saving user should not remove existing fields' ); - equal( - userInMemory.get('some_field'), - 1, - 'saving user should save specified field' - ); + equal(userInMemory.get('some_field'), 1, 'saving user should save specified field'); equal( userInMemory.get('password'), @@ -1053,10 +1021,7 @@ describe('Parse.User testing', () => { ok(userInMemory.createdAt instanceof Date); - ok( - userInMemory.getSessionToken(), - 'user should have a sessionToken after saving' - ); + ok(userInMemory.getSessionToken(), 'user should have a sessionToken after saving'); // Force the current user to read from localStorage, and check again delete Parse.User._currentUser; @@ -1069,11 +1034,7 @@ describe('Parse.User testing', () => { 'userFromDisk should have previously existing fields' ); - equal( - userFromDisk.get('some_field'), - 1, - 'userFromDisk should have saved field' - ); + equal(userFromDisk.get('some_field'), 1, 'userFromDisk should have saved field'); equal( userFromDisk.get('password'), @@ -1099,10 +1060,7 @@ describe('Parse.User testing', () => { ok(userFromDisk.createdAt instanceof Date); - ok( - userFromDisk.getSessionToken(), - 'userFromDisk should have a sessionToken' - ); + ok(userFromDisk.getSessionToken(), 'userFromDisk should have a sessionToken'); done(); }, @@ -1299,10 +1257,7 @@ describe('Parse.User testing', () => { ok(model.extended(), 'Should have used subclass.'); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); - strictEqual( - provider.authData.expiration_date, - provider.synchronizedExpiration - ); + strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); ok(model._isLinked('facebook'), 'User should be linked to facebook'); done(); }); @@ -1348,10 +1303,7 @@ describe('Parse.User testing', () => { ok(model.extended(), 'Should have used subclass.'); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); - strictEqual( - provider.authData.expiration_date, - provider.synchronizedExpiration - ); + strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); ok(model._isLinked('facebook'), 'User should be linked to facebook'); Parse.Cloud.run('checkLogin').then(done, done); @@ -1359,10 +1311,7 @@ describe('Parse.User testing', () => { it('log in with provider and update token', async done => { const provider = getMockFacebookProvider(); - const secondProvider = getMockFacebookProviderWithIdToken( - '8675309', - 'jenny_valid_token' - ); + const secondProvider = getMockFacebookProviderWithIdToken('8675309', 'jenny_valid_token'); Parse.User._registerAuthenticationProvider(provider); await Parse.User._logInWith('facebook'); Parse.User._registerAuthenticationProvider(secondProvider); @@ -1444,10 +1393,7 @@ describe('Parse.User testing', () => { ok(model.extended(), 'Should have used the subclass.'); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); - strictEqual( - provider.authData.expiration_date, - provider.synchronizedExpiration - ); + strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); ok(model._isLinked('facebook'), 'User should be linked to facebook'); Parse.User.logOut().then(async () => { @@ -1455,10 +1401,7 @@ describe('Parse.User testing', () => { provider.loggedOut = false; const innerModel = await Parse.User._logInWith('facebook'); ok(innerModel instanceof Parse.User, 'Model should be a Parse.User'); - ok( - innerModel === Parse.User.current(), - 'Returned model should be the current user' - ); + ok(innerModel === Parse.User.current(), 'Returned model should be the current user'); ok(provider.authData.id === provider.synchronizedUserId); ok(provider.authData.access_token === provider.synchronizedAuthToken); ok(innerModel._isLinked('facebook'), 'User should be linked to facebook'); @@ -1644,10 +1587,7 @@ describe('Parse.User testing', () => { strictEqual(Parse.User.current(), model); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); - strictEqual( - provider.authData.expiration_date, - provider.synchronizedExpiration - ); + strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); ok(model._isLinked('facebook'), 'User should be linked'); done(); }); @@ -1666,10 +1606,7 @@ describe('Parse.User testing', () => { strictEqual(Parse.User.current(), model); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); - strictEqual( - provider.authData.expiration_date, - provider.synchronizedExpiration - ); + strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); ok(model._isLinked('facebook'), 'User should be linked.'); const user2 = new Parse.User(); user2.set('username', 'testLinkWithProviderToAlreadyLinkedUser2'); @@ -1764,10 +1701,7 @@ describe('Parse.User testing', () => { ok(model.extended(), 'Should have used the subclass.'); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); - strictEqual( - provider.authData.expiration_date, - provider.synchronizedExpiration - ); + strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); ok(model._isLinked('facebook'), 'User should be linked to facebook.'); await model._unlinkFrom('facebook'); ok(!model._isLinked('facebook'), 'User should not be linked.'); @@ -1786,10 +1720,7 @@ describe('Parse.User testing', () => { ok(model.extended(), 'Should have used the subclass.'); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); - strictEqual( - provider.authData.expiration_date, - provider.synchronizedExpiration - ); + strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); ok(model._isLinked('facebook'), 'User should be linked to facebook'); await model._unlinkFrom('facebook'); @@ -1816,10 +1747,7 @@ describe('Parse.User testing', () => { ok(model.extended(), 'Should have used the subclass.'); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); - strictEqual( - provider.authData.expiration_date, - provider.synchronizedExpiration - ); + strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); ok(model._isLinked('facebook'), 'User should be linked to facebook'); Parse.User._registerAuthenticationProvider(mockProvider); const objectId = model.id; @@ -1832,10 +1760,7 @@ describe('Parse.User testing', () => { it('link multiple providers and updates token', async done => { const provider = getMockFacebookProvider(); - const secondProvider = getMockFacebookProviderWithIdToken( - '8675309', - 'jenny_valid_token' - ); + const secondProvider = getMockFacebookProviderWithIdToken('8675309', 'jenny_valid_token'); const mockProvider = getMockMyOauthProvider(); Parse.User._registerAuthenticationProvider(provider); @@ -1862,10 +1787,7 @@ describe('Parse.User testing', () => { ok(model.extended(), 'Should have used the subclass.'); strictEqual(provider.authData.id, provider.synchronizedUserId); strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); - strictEqual( - provider.authData.expiration_date, - provider.synchronizedExpiration - ); + strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); ok(model._isLinked('facebook'), 'User should be linked to facebook'); Parse.User._registerAuthenticationProvider(mockProvider); const objectId = model.id; @@ -1917,9 +1839,7 @@ describe('Parse.User testing', () => { // make sure the location header is properly set expect(userId).not.toBeUndefined(); expect(body.objectId).toEqual(userId); - expect(response.headers.location).toEqual( - Parse.serverURL + '/users/' + userId - ); + expect(response.headers.location).toEqual(Parse.serverURL + '/users/' + userId); done(); }); }); @@ -1951,9 +1871,7 @@ describe('Parse.User testing', () => { // In this case, we want success as it was valid once. // If the client needs an updated token, do lock the user out defaultConfiguration.auth.shortLivedAuth.setValidAccessToken('otherToken'); - await expectAsync( - Parse.User._logInWith('shortLivedAuth', {}) - ).toBeRejected(); + await expectAsync(Parse.User._logInWith('shortLivedAuth', {})).toBeRejected(); }); it('should allow PUT request with stale auth Data', done => { @@ -1982,9 +1900,7 @@ describe('Parse.User testing', () => { // Simulate a remotely expired token (like a short lived one) // In this case, we want success as it was valid once. // If the client needs an updated one, do lock the user out - defaultConfiguration.auth.shortLivedAuth.setValidAccessToken( - 'otherToken' - ); + defaultConfiguration.auth.shortLivedAuth.setValidAccessToken('otherToken'); return request({ method: 'PUT', url: Parse.serverURL + '/users/' + Parse.User.current().id, @@ -2237,10 +2153,7 @@ describe('Parse.User testing', () => { equal(users.length, 2); users.forEach(user => { expect(user.getSessionToken()).toBeUndefined(); - ok( - !user.getSessionToken(), - 'user should not have a session token.' - ); + ok(!user.getSessionToken(), 'user should not have a session token.'); }); done(); }, @@ -2356,10 +2269,7 @@ describe('Parse.User testing', () => { user2.set('username', 'Test1'); user2.set('password', 'test'); await expectAsync(user2.signUp()).toBeRejectedWith( - new Parse.Error( - Parse.Error.USERNAME_TAKEN, - 'Account already exists for this username.' - ) + new Parse.Error(Parse.Error.USERNAME_TAKEN, 'Account already exists for this username.') ); } ); @@ -2376,10 +2286,7 @@ describe('Parse.User testing', () => { user2.setUsername('Test1'); user2.setPassword('test'); await expectAsync(user2.signUp()).toBeRejectedWith( - new Parse.Error( - Parse.Error.USERNAME_TAKEN, - 'Account already exists for this username.' - ) + new Parse.Error(Parse.Error.USERNAME_TAKEN, 'Account already exists for this username.') ); } ); @@ -2398,10 +2305,7 @@ describe('Parse.User testing', () => { user2.setPassword('test'); user2.setEmail('Test@Example.Com'); await expectAsync(user2.signUp()).toBeRejectedWith( - new Parse.Error( - Parse.Error.EMAIL_TAKEN, - 'Account already exists for this email address.' - ) + new Parse.Error(Parse.Error.EMAIL_TAKEN, 'Account already exists for this email address.') ); } ); @@ -2423,10 +2327,7 @@ describe('Parse.User testing', () => { user2.setEmail('Test@Example.Com'); await expectAsync(user2.save()).toBeRejectedWith( - new Parse.Error( - Parse.Error.EMAIL_TAKEN, - 'Account already exists for this email address.' - ) + new Parse.Error(Parse.Error.EMAIL_TAKEN, 'Account already exists for this email address.') ); } ); @@ -2434,8 +2335,7 @@ describe('Parse.User testing', () => { describe('anonymous users', () => { it('should not fail on case insensitive matches', async () => { spyOn(cryptoUtils, 'randomString').and.returnValue('abcdefghijklmnop'); - const logIn = id => - Parse.User.logInWith('anonymous', { authData: { id } }); + const logIn = id => Parse.User.logInWith('anonymous', { authData: { id } }); const user1 = await logIn('test1'); const username1 = user1.get('username'); @@ -2781,8 +2681,7 @@ describe('Parse.User testing', () => { }); it('password format matches hosted parse', done => { - const hashed = - '$2a$10$8/wZJyEuiEaobBBqzTG.jeY.XSFJd0rzaN//ososvEI4yLqI.4aie'; + const hashed = '$2a$10$8/wZJyEuiEaobBBqzTG.jeY.XSFJd0rzaN//ososvEI4yLqI.4aie'; passwordCrypto.compare('test', hashed).then( pass => { expect(pass).toBe(true); @@ -2936,8 +2835,7 @@ describe('Parse.User testing', () => { '_User', { username: 'user', - _hashed_password: - '$2a$10$8/wZJyEuiEaobBBqzTG.jeY.XSFJd0rzaN//ososvEI4yLqI.4aie', + _hashed_password: '$2a$10$8/wZJyEuiEaobBBqzTG.jeY.XSFJd0rzaN//ososvEI4yLqI.4aie', _auth_data_facebook: null, }, {} @@ -2971,8 +2869,7 @@ describe('Parse.User testing', () => { '_User', { username: 'user', - _hashed_password: - '$2a$10$8/wZJyEuiEaobBBqzTG.jeY.XSFJd0rzaN//ososvEI4yLqI.4aie', + _hashed_password: '$2a$10$8/wZJyEuiEaobBBqzTG.jeY.XSFJd0rzaN//ososvEI4yLqI.4aie', _auth_data_facebook: null, }, {} @@ -3181,9 +3078,7 @@ describe('Parse.User testing', () => { expect(emailCalled).toBeTruthy(); expect(emailOptions).toBeDefined(); expect(err.status).toBe(400); - expect(err.text).toMatch( - '{"code":125,"error":"you must provide a valid email string"}' - ); + expect(err.text).toMatch('{"code":125,"error":"you must provide a valid email string"}'); done(); }); } @@ -3502,9 +3397,7 @@ describe('Parse.User testing', () => { done(); }) .catch(err => { - expect(err.message).toBe( - "Clients aren't allowed to manually update email verification." - ); + expect(err.message).toBe("Clients aren't allowed to manually update email verification."); done(); }); }); @@ -3665,10 +3558,7 @@ describe('Parse.User testing', () => { await user.signUp(); user.set('_email_verify_token', 'bad', { ignoreValidation: true }); await expectAsync(user.save()).toBeRejectedWith( - new Parse.Error( - Parse.Error.INVALID_KEY_NAME, - 'Invalid field name: _email_verify_token.' - ) + new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'Invalid field name: _email_verify_token.') ); }); @@ -3791,9 +3681,7 @@ describe('Parse.User testing', () => { }); xit('should not send a verification email if the user signed up using oauth', done => { - pending( - 'this test fails. See: https://github.com/parse-community/parse-server/issues/5097' - ); + pending('this test fails. See: https://github.com/parse-community/parse-server/issues/5097'); let emailCalledCount = 0; const emailAdapter = { sendVerificationEmail: () => { @@ -4227,9 +4115,7 @@ describe('Parse.User testing', () => { it('should throw when enforcePrivateUsers is invalid', async () => { const options = [[], 'a', 0, {}]; for (const option of options) { - await expectAsync( - reconfigureServer({ enforcePrivateUsers: option }) - ).toBeRejected(); + await expectAsync(reconfigureServer({ enforcePrivateUsers: option })).toBeRejected(); } }); @@ -4252,45 +4138,36 @@ describe('Parse.User testing', () => { }); describe('issue #4897', () => { - it_only_db('mongo')( - 'should be able to login with a legacy user (no ACL)', - async () => { - // This issue is a side effect of the locked users and legacy users which don't have ACL's - // In this scenario, a legacy user wasn't be able to login as there's no ACL on it - await reconfigureServer(); - const database = Config.get(Parse.applicationId).database; - const collection = await database.adapter._adaptiveCollection('_User'); - await collection.insertOne({ - _id: 'ABCDEF1234', - name: '', - email: '', - username: '', - _hashed_password: '', - _auth_data_facebook: { - id: '8675309', - access_token: 'jenny', - }, - sessionToken: '', - }); - const provider = getMockFacebookProvider(); - Parse.User._registerAuthenticationProvider(provider); - const model = await Parse.User._logInWith('facebook', {}); - expect(model.id).toBe('ABCDEF1234'); - ok(model instanceof Parse.User, 'Model should be a Parse.User'); - strictEqual(Parse.User.current(), model); - ok(model.extended(), 'Should have used subclass.'); - strictEqual(provider.authData.id, provider.synchronizedUserId); - strictEqual( - provider.authData.access_token, - provider.synchronizedAuthToken - ); - strictEqual( - provider.authData.expiration_date, - provider.synchronizedExpiration - ); - ok(model._isLinked('facebook'), 'User should be linked to facebook'); - } - ); + it_only_db('mongo')('should be able to login with a legacy user (no ACL)', async () => { + // This issue is a side effect of the locked users and legacy users which don't have ACL's + // In this scenario, a legacy user wasn't be able to login as there's no ACL on it + await reconfigureServer(); + const database = Config.get(Parse.applicationId).database; + const collection = await database.adapter._adaptiveCollection('_User'); + await collection.insertOne({ + _id: 'ABCDEF1234', + name: '', + email: '', + username: '', + _hashed_password: '', + _auth_data_facebook: { + id: '8675309', + access_token: 'jenny', + }, + sessionToken: '', + }); + const provider = getMockFacebookProvider(); + Parse.User._registerAuthenticationProvider(provider); + const model = await Parse.User._logInWith('facebook', {}); + expect(model.id).toBe('ABCDEF1234'); + ok(model instanceof Parse.User, 'Model should be a Parse.User'); + strictEqual(Parse.User.current(), model); + ok(model.extended(), 'Should have used subclass.'); + strictEqual(provider.authData.id, provider.synchronizedUserId); + strictEqual(provider.authData.access_token, provider.synchronizedAuthToken); + strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration); + ok(model._isLinked('facebook'), 'User should be linked to facebook'); + }); }); it('should strip out authdata in LiveQuery', async () => { @@ -4342,8 +4219,7 @@ describe('Parse.User testing', () => { expect(calls[key]).toHaveBeenCalled(); } subscription.unsubscribe(); - const client = - await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); + const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); client.close(); await new Promise(resolve => setTimeout(resolve, 10)); }); @@ -4506,9 +4382,7 @@ describe('login as other user', () => { done(); } catch (err) { expect(err.data.code).toBe(Parse.Error.INVALID_VALUE); - expect(err.data.error).toBe( - 'userId must not be empty, null, or undefined' - ); + expect(err.data.error).toBe('userId must not be empty, null, or undefined'); } const sessionsQuery = new Parse.Query(Parse.Session); @@ -4560,9 +4434,7 @@ describe('allowClientClassCreation option', () => { it('should enforce boolean values', async () => { const options = [[], 'a', '', 0, 1, {}, 'true', 'false']; for (const option of options) { - await expectAsync( - reconfigureServer({ allowClientClassCreation: option }) - ).toBeRejected(); + await expectAsync(reconfigureServer({ allowClientClassCreation: option })).toBeRejected(); } }); @@ -4573,18 +4445,14 @@ describe('allowClientClassCreation option', () => { it('should accept false value', async () => { await reconfigureServer({ allowClientClassCreation: false }); - expect(Config.get(Parse.applicationId).allowClientClassCreation).toBe( - false - ); + expect(Config.get(Parse.applicationId).allowClientClassCreation).toBe(false); }); it('should default false', async () => { // remove predefined allowClientClassCreation:true on global defaultConfiguration delete defaultConfiguration.allowClientClassCreation; await reconfigureServer(defaultConfiguration); - expect(Config.get(Parse.applicationId).allowClientClassCreation).toBe( - false - ); + expect(Config.get(Parse.applicationId).allowClientClassCreation).toBe(false); // Need to set it back to true to avoid other test fails defaultConfiguration.allowClientClassCreation = true; }); diff --git a/spec/ParseWebSocket.spec.js b/spec/ParseWebSocket.spec.js index b8b25b7b97..fe64bce1be 100644 --- a/spec/ParseWebSocket.spec.js +++ b/spec/ParseWebSocket.spec.js @@ -1,5 +1,4 @@ -const ParseWebSocket = - require('../lib/LiveQuery/ParseWebSocketServer').ParseWebSocket; +const ParseWebSocket = require('../lib/LiveQuery/ParseWebSocketServer').ParseWebSocket; describe('ParseWebSocket', function () { it('can be initialized', function () { diff --git a/spec/ParseWebSocketServer.spec.js b/spec/ParseWebSocketServer.spec.js index 89ed1ccb53..5955ee3241 100644 --- a/spec/ParseWebSocketServer.spec.js +++ b/spec/ParseWebSocketServer.spec.js @@ -1,6 +1,4 @@ -const { - ParseWebSocketServer, -} = require('../lib/LiveQuery/ParseWebSocketServer'); +const { ParseWebSocketServer } = require('../lib/LiveQuery/ParseWebSocketServer'); const EventEmitter = require('events'); describe('ParseWebSocketServer', function () { @@ -18,13 +16,9 @@ describe('ParseWebSocketServer', function () { const onConnectCallback = jasmine.createSpy('onConnectCallback'); const http = require('http'); const server = http.createServer(); - const parseWebSocketServer = new ParseWebSocketServer( - server, - onConnectCallback, - { - websocketTimeout: 5, - } - ).server; + const parseWebSocketServer = new ParseWebSocketServer(server, onConnectCallback, { + websocketTimeout: 5, + }).server; const ws = new EventEmitter(); ws.readyState = 0; ws.OPEN = 0; @@ -86,13 +80,9 @@ describe('ParseWebSocketServer', function () { const onConnectCallback = jasmine.createSpy('onConnectCallback'); const http = require('http'); const server = http.createServer(); - const parseWebSocketServer = new ParseWebSocketServer( - server, - onConnectCallback, - { - websocketTimeout: 10, - } - ).server; + const parseWebSocketServer = new ParseWebSocketServer(server, onConnectCallback, { + websocketTimeout: 10, + }).server; const ws = new EventEmitter(); ws.readyState = 0; @@ -119,13 +109,9 @@ describe('ParseWebSocketServer', function () { const onConnectCallback = jasmine.createSpy('onConnectCallback'); const http = require('http'); const server = http.createServer(); - const parseWebSocketServer = new ParseWebSocketServer( - server, - onConnectCallback, - { - websocketTimeout: 5, - } - ).server; + const parseWebSocketServer = new ParseWebSocketServer(server, onConnectCallback, { + websocketTimeout: 5, + }).server; const ws = new EventEmitter(); ws.readyState = 0; ws.OPEN = 0; diff --git a/spec/PasswordPolicy.spec.js b/spec/PasswordPolicy.spec.js index 1c15b8191a..b8ad67c391 100644 --- a/spec/PasswordPolicy.spec.js +++ b/spec/PasswordPolicy.spec.js @@ -220,9 +220,7 @@ describe('Password Policy: ', () => { }); fail('should have thrown.'); } catch (e) { - expect(e).toBe( - 'You cannot use resetTokenReuseIfValid without resetTokenValidityDuration' - ); + expect(e).toBe('You cannot use resetTokenReuseIfValid without resetTokenValidityDuration'); } done(); }); @@ -236,15 +234,11 @@ describe('Password Policy: ', () => { publicServerURL: 'http://localhost:8378/1', }) .then(() => { - fail( - 'passwordPolicy.resetTokenValidityDuration "not a number" test failed' - ); + fail('passwordPolicy.resetTokenValidityDuration "not a number" test failed'); done(); }) .catch(err => { - expect(err).toEqual( - 'passwordPolicy.resetTokenValidityDuration must be a positive number' - ); + expect(err).toEqual('passwordPolicy.resetTokenValidityDuration must be a positive number'); done(); }); }); @@ -262,9 +256,7 @@ describe('Password Policy: ', () => { done(); }) .catch(err => { - expect(err).toEqual( - 'passwordPolicy.resetTokenValidityDuration must be a positive number' - ); + expect(err).toEqual('passwordPolicy.resetTokenValidityDuration must be a positive number'); done(); }); }); @@ -302,9 +294,7 @@ describe('Password Policy: ', () => { done(); }) .catch(err => { - expect(err).toEqual( - 'passwordPolicy.validatorCallback must be a function.' - ); + expect(err).toEqual('passwordPolicy.validatorCallback must be a function.'); done(); }); }); @@ -324,9 +314,7 @@ describe('Password Policy: ', () => { user .signUp() .then(() => { - fail( - 'Should have failed as password does not conform to the policy.' - ); + fail('Should have failed as password does not conform to the policy.'); done(); }) .catch(error => { @@ -351,9 +339,7 @@ describe('Password Policy: ', () => { user .signUp() .then(() => { - fail( - 'Should have failed as password does not conform to the policy.' - ); + fail('Should have failed as password does not conform to the policy.'); done(); }) .catch(error => { @@ -378,15 +364,11 @@ describe('Password Policy: ', () => { user .signUp() .then(() => { - fail( - 'Should have failed as password does not conform to the policy.' - ); + fail('Should have failed as password does not conform to the policy.'); done(); }) .catch(error => { - expect(error.message).toEqual( - 'Cannot sign up user with an empty password.' - ); + expect(error.message).toEqual('Cannot sign up user with an empty password.'); done(); }); }); @@ -427,9 +409,7 @@ describe('Password Policy: ', () => { }) .catch(error => { jfail(error); - fail( - 'Signup should have succeeded as password conforms to the policy.' - ); + fail('Signup should have succeeded as password conforms to the policy.'); done(); }); }); @@ -470,9 +450,7 @@ describe('Password Policy: ', () => { }) .catch(error => { jfail(error); - fail( - 'Signup should have succeeded as password conforms to the policy.' - ); + fail('Signup should have succeeded as password conforms to the policy.'); done(); }); }); @@ -493,9 +471,7 @@ describe('Password Policy: ', () => { user .signUp() .then(() => { - fail( - 'Should have failed as password does not conform to the policy.' - ); + fail('Should have failed as password does not conform to the policy.'); done(); }) .catch(error => { @@ -562,9 +538,7 @@ describe('Password Policy: ', () => { user .signUp() .then(() => { - fail( - 'Should have failed as password does not conform to the policy.' - ); + fail('Should have failed as password does not conform to the policy.'); done(); }) .catch(error => { @@ -590,9 +564,7 @@ describe('Password Policy: ', () => { user .signUp() .then(() => { - fail( - 'Should have failed as password does not conform to the policy.' - ); + fail('Should have failed as password does not conform to the policy.'); done(); }) .catch(error => { @@ -844,9 +816,7 @@ describe('Password Policy: ', () => { done(); }) .catch(err => { - expect(err).toEqual( - 'passwordPolicy.doNotAllowUsername must be a boolean value.' - ); + expect(err).toEqual('passwordPolicy.doNotAllowUsername must be a boolean value.'); done(); }); }); @@ -872,9 +842,7 @@ describe('Password Policy: ', () => { }) .catch(error => { expect(error.code).toEqual(142); - expect(error.message).toEqual( - 'Password cannot contain your username.' - ); + expect(error.message).toEqual('Password cannot contain your username.'); done(); }); }); @@ -1185,9 +1153,7 @@ describe('Password Policy: ', () => { done(); }) .catch(err => { - expect(err).toEqual( - 'passwordPolicy.maxPasswordAge must be a positive number' - ); + expect(err).toEqual('passwordPolicy.maxPasswordAge must be a positive number'); done(); }); }); @@ -1205,9 +1171,7 @@ describe('Password Policy: ', () => { done(); }) .catch(err => { - expect(err).toEqual( - 'passwordPolicy.maxPasswordAge must be a positive number' - ); + expect(err).toEqual('passwordPolicy.maxPasswordAge must be a positive number'); done(); }); }); @@ -1325,9 +1289,7 @@ describe('Password Policy: ', () => { done(); }) .catch(error => { - expect(error.code).toEqual( - Parse.Error.OBJECT_NOT_FOUND - ); + expect(error.code).toEqual(Parse.Error.OBJECT_NOT_FOUND); expect(error.message).toEqual( 'Your password has expired. Please reset your password.' ); @@ -1453,13 +1415,11 @@ describe('Password Policy: ', () => { expect(error.message).toEqual( 'Your password has expired. Please reset your password.' ); - Parse.User.requestPasswordReset('user1@parse.com').catch( - err => { - jfail(err); - fail('Reset password request should not fail'); - done(); - } - ); + Parse.User.requestPasswordReset('user1@parse.com').catch(err => { + jfail(err); + fail('Reset password request should not fail'); + done(); + }); }); }, 1000); }) @@ -1485,9 +1445,7 @@ describe('Password Policy: ', () => { done(); }) .catch(err => { - expect(err).toEqual( - 'passwordPolicy.maxPasswordHistory must be an integer ranging 0 - 20' - ); + expect(err).toEqual('passwordPolicy.maxPasswordHistory must be an integer ranging 0 - 20'); done(); }); }); @@ -1505,9 +1463,7 @@ describe('Password Policy: ', () => { done(); }) .catch(err => { - expect(err).toEqual( - 'passwordPolicy.maxPasswordHistory must be an integer ranging 0 - 20' - ); + expect(err).toEqual('passwordPolicy.maxPasswordHistory must be an integer ranging 0 - 20'); done(); }); }); @@ -1525,9 +1481,7 @@ describe('Password Policy: ', () => { done(); }) .catch(err => { - expect(err).toEqual( - 'passwordPolicy.maxPasswordHistory must be an integer ranging 0 - 20' - ); + expect(err).toEqual('passwordPolicy.maxPasswordHistory must be an integer ranging 0 - 20'); done(); }); }); @@ -1635,15 +1589,11 @@ describe('Password Policy: ', () => { return user.save(); }) .then(() => { - fail( - 'should have failed because the new password is same as the old' - ); + fail('should have failed because the new password is same as the old'); done(); }) .catch(error => { - expect(error.message).toEqual( - 'New password should not be the same as last 5 passwords.' - ); + expect(error.message).toEqual('New password should not be the same as last 5 passwords.'); expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); done(); }); @@ -1689,15 +1639,11 @@ describe('Password Policy: ', () => { return user.save(); }) .then(() => { - fail( - 'should have failed because the new password is same as the old' - ); + fail('should have failed because the new password is same as the old'); done(); }) .catch(error => { - expect(error.message).toEqual( - 'New password should not be the same as last 5 passwords.' - ); + expect(error.message).toEqual('New password should not be the same as last 5 passwords.'); expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); done(); }); @@ -1750,9 +1696,7 @@ describe('Password Policy: ', () => { done(); }) .catch(() => { - fail( - 'should have succeeded because the new password is not in history' - ); + fail('should have succeeded because the new password is not in history'); done(); }); }); diff --git a/spec/PointerPermissions.spec.js b/spec/PointerPermissions.spec.js index bc99a357a7..94257681c5 100644 --- a/spec/PointerPermissions.spec.js +++ b/spec/PointerPermissions.spec.js @@ -30,11 +30,7 @@ describe('Pointer Permissions', () => { }) .then(() => { return config.database.loadSchema().then(schema => { - return schema.updateClass( - 'AnObject', - {}, - { readUserFields: ['owner'] } - ); + return schema.updateClass('AnObject', {}, { readUserFields: ['owner'] }); }); }) .then(() => { @@ -521,11 +517,7 @@ describe('Pointer Permissions', () => { .then(() => { return config.database.loadSchema().then(schema => { // Lock the update, and let only owner write - return schema.updateClass( - 'AnObject', - {}, - { update: {}, writeUserFields: ['owner'] } - ); + return schema.updateClass('AnObject', {}, { update: {}, writeUserFields: ['owner'] }); }); }) .then(() => { @@ -578,11 +570,7 @@ describe('Pointer Permissions', () => { .then(() => { return config.database.loadSchema().then(schema => { // Lock the update, and let only owner write - return schema.updateClass( - 'AnObject', - {}, - { update: {}, writeUserFields: ['owner'] } - ); + return schema.updateClass('AnObject', {}, { update: {}, writeUserFields: ['owner'] }); }); }) .then(() => { @@ -635,11 +623,7 @@ describe('Pointer Permissions', () => { .then(() => { return config.database.loadSchema().then(schema => { // Lock the update, and let only owner write - return schema.updateClass( - 'AnObject', - {}, - { update: {}, writeUserFields: ['owner'] } - ); + return schema.updateClass('AnObject', {}, { update: {}, writeUserFields: ['owner'] }); }); }) .then(() => { @@ -1089,78 +1073,75 @@ describe('Pointer Permissions', () => { } }); - it_id('1bbb9ed6-5558-4ce5-a238-b1a2015d273f')(it)( - 'should work with write', - async done => { - const config = Config.get(Parse.applicationId); - const user = new Parse.User(); - const user2 = new Parse.User(); - user.set({ - username: 'user1', - password: 'password', - }); - user2.set({ - username: 'user2', - password: 'password', - }); - const obj = new Parse.Object('AnObject'); - const obj2 = new Parse.Object('AnObject'); + it_id('1bbb9ed6-5558-4ce5-a238-b1a2015d273f')(it)('should work with write', async done => { + const config = Config.get(Parse.applicationId); + const user = new Parse.User(); + const user2 = new Parse.User(); + user.set({ + username: 'user1', + password: 'password', + }); + user2.set({ + username: 'user2', + password: 'password', + }); + const obj = new Parse.Object('AnObject'); + const obj2 = new Parse.Object('AnObject'); - await Parse.Object.saveAll([user, user2]); + await Parse.Object.saveAll([user, user2]); - obj.set('owner', user); - obj.set('readers', [user2]); - obj2.set('owner', user2); - obj2.set('readers', [user]); - await Parse.Object.saveAll([obj, obj2]); + obj.set('owner', user); + obj.set('readers', [user2]); + obj2.set('owner', user2); + obj2.set('readers', [user]); + await Parse.Object.saveAll([obj, obj2]); - const schema = await config.database.loadSchema(); - await schema.updateClass( - 'AnObject', - {}, - { - writeUserFields: ['owner'], - readUserFields: ['readers', 'owner'], - } - ); + const schema = await config.database.loadSchema(); + await schema.updateClass( + 'AnObject', + {}, + { + writeUserFields: ['owner'], + readUserFields: ['readers', 'owner'], + } + ); - await Parse.User.logIn('user1', 'password'); + await Parse.User.logIn('user1', 'password'); - obj2.set('hello', 'world'); - try { - await obj2.save(); - done.fail('User should not be able to update obj2'); - } catch (err) { - // User 1 should not be able to update obj2 - expect(err.code).toBe(Parse.Error.OBJECT_NOT_FOUND); - } + obj2.set('hello', 'world'); + try { + await obj2.save(); + done.fail('User should not be able to update obj2'); + } catch (err) { + // User 1 should not be able to update obj2 + expect(err.code).toBe(Parse.Error.OBJECT_NOT_FOUND); + } - obj.set('hello', 'world'); - try { - await obj.save(); - } catch (err) { - done.fail('User should be able to update'); - } + obj.set('hello', 'world'); + try { + await obj.save(); + } catch (err) { + done.fail('User should be able to update'); + } - await Parse.User.logIn('user2', 'password'); + await Parse.User.logIn('user2', 'password'); - try { - const q = new Parse.Query('AnObject'); - const res = await q.find(); - expect(res.length).toBe(2); - res.forEach(result => { - if (result.id == obj.id) { - expect(result.get('hello')).toBe('world'); - } else { - expect(result.id).toBe(obj2.id); - } - }); - done(); - } catch (err) { - done.fail('failed'); - } + try { + const q = new Parse.Query('AnObject'); + const res = await q.find(); + expect(res.length).toBe(2); + res.forEach(result => { + if (result.id == obj.id) { + expect(result.get('hello')).toBe('world'); + } else { + expect(result.id).toBe(obj2.id); + } + }); + done(); + } catch (err) { + done.fail('failed'); } - ); + }); it('should let a proper user find', async done => { const config = Config.get(Parse.applicationId); @@ -1191,11 +1172,7 @@ describe('Pointer Permissions', () => { await Parse.Object.saveAll([obj, obj2]); const schema = await config.database.loadSchema(); - await schema.updateClass( - 'AnObject', - {}, - { find: {}, get: {}, readUserFields: ['owners'] } - ); + await schema.updateClass('AnObject', {}, { find: {}, get: {}, readUserFields: ['owners'] }); let q = new Parse.Query('AnObject'); let result = await q.find(); @@ -1260,11 +1237,7 @@ describe('Pointer Permissions', () => { await Parse.Object.saveAll([obj, obj2]); const schema = await config.database.loadSchema(); - await schema.updateClass( - 'AnObject', - {}, - { find: {}, get: {}, readUserFields: ['owners'] } - ); + await schema.updateClass('AnObject', {}, { find: {}, get: {}, readUserFields: ['owners'] }); for (const owner of ['user1', 'user2']) { await Parse.User.logIn(owner, 'password'); @@ -1310,11 +1283,7 @@ describe('Pointer Permissions', () => { await Parse.Object.saveAll([obj, obj2]); const schema = await config.database.loadSchema(); - await schema.updateClass( - 'AnObject', - {}, - { find: {}, get: {}, readUserFields: ['owners'] } - ); + await schema.updateClass('AnObject', {}, { find: {}, get: {}, readUserFields: ['owners'] }); for (const owner of ['user1', 'user2']) { try { @@ -1570,11 +1539,7 @@ describe('Pointer Permissions', () => { const schema = await config.database.loadSchema(); // Lock the update, and let only owners write - await schema.updateClass( - 'AnObject', - {}, - { update: {}, writeUserFields: ['owners'] } - ); + await schema.updateClass('AnObject', {}, { update: {}, writeUserFields: ['owners'] }); await Parse.User.logIn('user1', 'password'); try { @@ -1623,11 +1588,7 @@ describe('Pointer Permissions', () => { const schema = await config.database.loadSchema(); // Lock the update, and let only owners write - await schema.updateClass( - 'AnObject', - {}, - { update: {}, writeUserFields: ['owners'] } - ); + await schema.updateClass('AnObject', {}, { update: {}, writeUserFields: ['owners'] }); for (const owner of ['user2', 'user3']) { await Parse.User.logIn(owner, 'password'); @@ -1677,11 +1638,7 @@ describe('Pointer Permissions', () => { const schema = await config.database.loadSchema(); // Lock the update, and let only owners write - await schema.updateClass( - 'AnObject', - {}, - { update: {}, writeUserFields: ['owners'] } - ); + await schema.updateClass('AnObject', {}, { update: {}, writeUserFields: ['owners'] }); for (const owner of ['user2', 'user3']) { await Parse.User.logIn(owner, 'password'); @@ -1733,11 +1690,7 @@ describe('Pointer Permissions', () => { const schema = await config.database.loadSchema(); // Lock reading, and let only owners read - await schema.updateClass( - 'AnObject', - {}, - { find: {}, get: {}, readUserFields: ['owners'] } - ); + await schema.updateClass('AnObject', {}, { find: {}, get: {}, readUserFields: ['owners'] }); await Parse.User.logIn('user1', 'password'); try { @@ -1951,10 +1904,7 @@ describe('Pointer Permissions', () => { } try { - const objectAgain = await object.save( - { hello: 'baz' }, - { useMasterKey: true } - ); + const objectAgain = await object.save({ hello: 'baz' }, { useMasterKey: true }); expect(objectAgain.get('hello')).toBe('baz'); done(); } catch (err) { @@ -2035,13 +1985,9 @@ describe('Pointer Permissions', () => { const actionDelete = obj => obj.destroy(); const actionAddFieldOnCreate = () => new Parse.Object(className, { ['extra' + Date.now()]: 'field' }).save(); - const actionAddFieldOnUpdate = obj => - obj.save({ ['another' + Date.now()]: 'field' }); + const actionAddFieldOnUpdate = obj => obj.save({ ['another' + Date.now()]: 'field' }); - const OBJECT_NOT_FOUND = new Parse.Error( - Parse.Error.OBJECT_NOT_FOUND, - 'Object not found.' - ); + const OBJECT_NOT_FOUND = new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.'); const PERMISSION_DENIED = jasmine.stringMatching('Permission denied'); async function createUser(username, password = 'password') { @@ -2082,10 +2028,7 @@ describe('Pointer Permissions', () => { async function initialize() { await Config.get(Parse.applicationId).schemaCache.clear(); - [user1, user2] = await Promise.all([ - createUser('user1'), - createUser('user2'), - ]); + [user1, user2] = await Promise.all([createUser('user1'), createUser('user2')]); obj1 = new Parse.Object(className, { owner: user1, @@ -2132,9 +2075,7 @@ describe('Pointer Permissions', () => { await logIn(user2); - await expectAsync(actionGet(obj1.id)).toBeRejectedWith( - OBJECT_NOT_FOUND - ); + await expectAsync(actionGet(obj1.id)).toBeRejectedWith(OBJECT_NOT_FOUND); done(); } ); @@ -2297,9 +2238,7 @@ describe('Pointer Permissions', () => { await logIn(user2); - await expectAsync(actionUpdate(obj1)).toBeRejectedWith( - OBJECT_NOT_FOUND - ); + await expectAsync(actionUpdate(obj1)).toBeRejectedWith(OBJECT_NOT_FOUND); done(); } ); @@ -2354,9 +2293,7 @@ describe('Pointer Permissions', () => { await logIn(user2); - await expectAsync(actionDelete(obj1)).toBeRejectedWith( - OBJECT_NOT_FOUND - ); + await expectAsync(actionDelete(obj1)).toBeRejectedWith(OBJECT_NOT_FOUND); done(); } ); @@ -2436,9 +2373,7 @@ describe('Pointer Permissions', () => { owner: user1, extra: 'field', }); - await expectAsync(newObject.save()).toBeRejectedWith( - PERMISSION_DENIED - ); + await expectAsync(newObject.save()).toBeRejectedWith(PERMISSION_DENIED); done(); }); @@ -2471,9 +2406,7 @@ describe('Pointer Permissions', () => { await logIn(user2); - await expectAsync(actionAddFieldOnUpdate(obj1)).toBeRejectedWith( - OBJECT_NOT_FOUND - ); + await expectAsync(actionAddFieldOnUpdate(obj1)).toBeRejectedWith(OBJECT_NOT_FOUND); done(); }); @@ -2610,9 +2543,7 @@ describe('Pointer Permissions', () => { await logIn(user1); - await expectAsync(actionGet(obj3.id)).toBeRejectedWith( - OBJECT_NOT_FOUND - ); + await expectAsync(actionGet(obj3.id)).toBeRejectedWith(OBJECT_NOT_FOUND); done(); } ); @@ -2799,9 +2730,7 @@ describe('Pointer Permissions', () => { await logIn(user2); - await expectAsync(actionUpdate(obj3)).toBeRejectedWith( - OBJECT_NOT_FOUND - ); + await expectAsync(actionUpdate(obj3)).toBeRejectedWith(OBJECT_NOT_FOUND); done(); } ); @@ -2870,9 +2799,7 @@ describe('Pointer Permissions', () => { await logIn(user1); - await expectAsync(actionDelete(obj3)).toBeRejectedWith( - OBJECT_NOT_FOUND - ); + await expectAsync(actionDelete(obj3)).toBeRejectedWith(OBJECT_NOT_FOUND); done(); } ); @@ -2953,9 +2880,7 @@ describe('Pointer Permissions', () => { moderators: user1, extra: 'field', }); - await expectAsync(newObject.save()).toBeRejectedWith( - PERMISSION_DENIED - ); + await expectAsync(newObject.save()).toBeRejectedWith(PERMISSION_DENIED); done(); }); @@ -2990,9 +2915,7 @@ describe('Pointer Permissions', () => { await logIn(user1); - await expectAsync(actionAddFieldOnUpdate(obj2)).toBeRejectedWith( - OBJECT_NOT_FOUND - ); + await expectAsync(actionAddFieldOnUpdate(obj2)).toBeRejectedWith(OBJECT_NOT_FOUND); done(); } @@ -3028,10 +2951,7 @@ describe('Pointer Permissions', () => { async function initialize() { await Config.get(Parse.applicationId).schemaCache.clear(); - [user1, user2] = await Promise.all([ - createUser('user1'), - createUser('user2'), - ]); + [user1, user2] = await Promise.all([createUser('user1'), createUser('user2')]); // User1 owns object1 // User2 owns object2 @@ -3130,9 +3050,7 @@ describe('Pointer Permissions', () => { await logIn(user1); - await expectAsync(actionDelete(obj2)).toBeRejectedWith( - OBJECT_NOT_FOUND - ); + await expectAsync(actionDelete(obj2)).toBeRejectedWith(OBJECT_NOT_FOUND); done(); }); diff --git a/spec/PostgresConfigParser.spec.js b/spec/PostgresConfigParser.spec.js index 58a2af87bd..f4efc42114 100644 --- a/spec/PostgresConfigParser.spec.js +++ b/spec/PostgresConfigParser.spec.js @@ -60,9 +60,7 @@ dbOptionsTest[ dbOptionsTest[`${baseURI}?rejectUnauthorized=true`] = { ssl: { rejectUnauthorized: true }, }; -dbOptionsTest[ - `${baseURI}?max=5&query_timeout=100&idleTimeoutMillis=1000&keepAlive=true` -] = { +dbOptionsTest[`${baseURI}?max=5&query_timeout=100&idleTimeoutMillis=1000&keepAlive=true`] = { max: 5, query_timeout: 100, idleTimeoutMillis: 1000, @@ -96,9 +94,7 @@ describe('PostgresConfigParser.getDatabaseOptionsFromURI', () => { }); it('max should take precedence over poolSize', () => { - const result = parser.getDatabaseOptionsFromURI( - `${baseURI}?poolSize=20&max=12` - ); + const result = parser.getDatabaseOptionsFromURI(`${baseURI}?poolSize=20&max=12`); expect(result.poolSize).toBeUndefined(); expect(result.max).toEqual(12); diff --git a/spec/PostgresStorageAdapter.spec.js b/spec/PostgresStorageAdapter.spec.js index 71c2d60110..f194de5d3d 100644 --- a/spec/PostgresStorageAdapter.spec.js +++ b/spec/PostgresStorageAdapter.spec.js @@ -146,9 +146,7 @@ describe_only_db('postgres')('PostgresStorageAdapter', () => { }, }; await adapter.createClass('MyClass', schema); - await expectAsync(adapter.getClass('UnknownClass')).toBeRejectedWith( - undefined - ); + await expectAsync(adapter.getClass('UnknownClass')).toBeRejectedWith(undefined); }); it('$relativeTime should error on $eq', async () => { @@ -166,10 +164,13 @@ describe_only_db('postgres')('PostgresStorageAdapter', () => { }; const client = adapter._client; await adapter.createTable(tableName, schema); - await client.none( - 'INSERT INTO $1:name ($2:name, $3:name) VALUES ($4, $5)', - [tableName, 'objectId', 'username', 'Bugs', 'Bunny'] - ); + await client.none('INSERT INTO $1:name ($2:name, $3:name) VALUES ($4, $5)', [ + tableName, + 'objectId', + 'username', + 'Bugs', + 'Bunny', + ]); const database = Config.get(Parse.applicationId).database; await database.loadSchema({ clearCache: true }); try { @@ -206,10 +207,13 @@ describe_only_db('postgres')('PostgresStorageAdapter', () => { }; const client = adapter._client; await adapter.createTable(tableName, schema); - await client.none( - 'INSERT INTO $1:name ($2:name, $3:name) VALUES ($4, $5)', - [tableName, 'objectId', 'username', 'Bugs', 'Bunny'] - ); + await client.none('INSERT INTO $1:name ($2:name, $3:name) VALUES ($4, $5)', [ + tableName, + 'objectId', + 'username', + 'Bugs', + 'Bunny', + ]); const database = Config.get(Parse.applicationId).database; await database.loadSchema({ clearCache: true }); try { @@ -246,10 +250,13 @@ describe_only_db('postgres')('PostgresStorageAdapter', () => { }; const client = adapter._client; await adapter.createTable(tableName, schema); - await client.none( - 'INSERT INTO $1:name ($2:name, $3:name) VALUES ($4, $5)', - [tableName, 'objectId', 'username', 'Bugs', 'Bunny'] - ); + await client.none('INSERT INTO $1:name ($2:name, $3:name) VALUES ($4, $5)', [ + tableName, + 'objectId', + 'username', + 'Bugs', + 'Bunny', + ]); const database = Config.get(Parse.applicationId).database; await database.loadSchema({ clearCache: true }); try { @@ -286,22 +293,21 @@ describe_only_db('postgres')('PostgresStorageAdapter', () => { }; const client = adapter._client; await adapter.createTable(tableName, schema); - await client.none( - 'INSERT INTO $1:name ($2:name, $3:name) VALUES ($4, $5)', - [tableName, 'objectId', 'username', 'Bugs', 'Bunny'] - ); + await client.none('INSERT INTO $1:name ($2:name, $3:name) VALUES ($4, $5)', [ + tableName, + 'objectId', + 'username', + 'Bugs', + 'Bunny', + ]); //Postgres won't take advantage of the index until it has a lot of records because sequential is faster for small db's await client.none( 'INSERT INTO $1:name ($2:name, $3:name) SELECT gen_random_uuid(), gen_random_uuid() FROM generate_series(1,5000)', [tableName, 'objectId', 'username'] ); const caseInsensitiveData = 'bugs'; - const originalQuery = - 'SELECT * FROM $1:name WHERE lower($2:name)=lower($3)'; - const analyzedExplainQuery = adapter.createExplainableQuery( - originalQuery, - true - ); + const originalQuery = 'SELECT * FROM $1:name WHERE lower($2:name)=lower($3)'; + const analyzedExplainQuery = adapter.createExplainableQuery(originalQuery, true); const preIndexPlan = await client.one(analyzedExplainQuery, [ tableName, 'objectId', @@ -397,13 +403,7 @@ describe_only_db('postgres')('PostgresStorageAdapter', () => { const indexName = 'test_case_insensitive_column'; const schema = await new Parse.Schema('_User').get(); - await adapter.ensureIndex( - tableName, - schema, - [fieldToSearch], - indexName, - true - ); + await adapter.ensureIndex(tableName, schema, [fieldToSearch], indexName, true); //Check using find method for Parse const postIndexPlan = await database.find( @@ -481,12 +481,8 @@ describe_only_db('postgres')('PostgresStorageAdapter', () => { const database = Config.get(Parse.applicationId).database; //Create index before data is inserted - await adapter.ensureUniqueness(firstTableName, firstTableSchema, [ - uniqueField, - ]); - await adapter.ensureUniqueness(secondTableName, secondTableSchema, [ - uniqueField, - ]); + await adapter.ensureUniqueness(firstTableName, firstTableSchema, [uniqueField]); + await adapter.ensureUniqueness(secondTableName, secondTableSchema, [uniqueField]); //Postgres won't take advantage of the index until it has a lot of records because sequential is faster for small db's const client = adapter._client; @@ -570,9 +566,7 @@ describe_only_db('postgres')('PostgresStorageAdapter', () => { const qs = "SELECT format('%I.%I(%s)', ns.nspname, p.proname, oidvectortypes(p.proargtypes)) FROM pg_proc p INNER JOIN pg_namespace ns ON (p.pronamespace = ns.oid) WHERE p.proname = 'idempotency_delete_expired_records'"; const foundFunction = await client.one(qs); - expect(foundFunction.format).toBe( - 'public.idempotency_delete_expired_records()' - ); + expect(foundFunction.format).toBe('public.idempotency_delete_expired_records()'); await adapter.deleteIdempotencyFunction(); await client.none(qs); }); diff --git a/spec/ProtectedFields.spec.js b/spec/ProtectedFields.spec.js index db51184935..8195985dcb 100644 --- a/spec/ProtectedFields.spec.js +++ b/spec/ProtectedFields.spec.js @@ -1,13 +1,7 @@ const Config = require('../lib/Config'); const Parse = require('parse/node'); const request = require('../lib/request'); -const { - className, - createRole, - createUser, - logIn, - updateCLP, -} = require('./support/dev'); +const { className, createRole, createUser, logIn, updateCLP } = require('./support/dev'); describe('ProtectedFields', function () { it('should handle and empty protectedFields', async function () { @@ -90,15 +84,9 @@ describe('ProtectedFields', function () { }; await reconfigureServer({ protectedFields }); - const objA = await new Parse.Object('ClassA') - .set('foo', 'zzz') - .set('bar', 'yyy') - .save(); + const objA = await new Parse.Object('ClassA').set('foo', 'zzz').set('bar', 'yyy').save(); - const objB = await new Parse.Object('ClassB') - .set('foo', 'zzz') - .set('bar', 'yyy') - .save(); + const objB = await new Parse.Object('ClassB').set('foo', 'zzz').set('bar', 'yyy').save(); const [fetchedA, fetchedB] = await Promise.all([ new Parse.Query('ClassA').get(objA.id), @@ -134,15 +122,9 @@ describe('ProtectedFields', function () { user.setACL(acl); await user.save(); - const objA = await new Parse.Object('ClassA') - .set('foo', 'zzz') - .set('bar', 'yyy') - .save(); + const objA = await new Parse.Object('ClassA').set('foo', 'zzz').set('bar', 'yyy').save(); - const objB = await new Parse.Object('ClassB') - .set('foo', 'zzz') - .set('bar', 'yyy') - .save(); + const objB = await new Parse.Object('ClassB').set('foo', 'zzz').set('bar', 'yyy').save(); const [fetchedUser, fetchedA, fetchedB] = await Promise.all([ new Parse.Query(Parse.User).get(user.id), @@ -835,10 +817,7 @@ describe('ProtectedFields', function () { }, }) ).toBeRejectedWith( - new Parse.Error( - Parse.Error.INVALID_JSON, - `Default field '${field}' can not be protected` - ) + new Parse.Error(Parse.Error.INVALID_JSON, `Default field '${field}' can not be protected`) ); } }); @@ -961,10 +940,7 @@ describe('ProtectedFields', function () { await Parse.User.logOut(); - [user1, user2] = await Promise.all([ - createUser('user1'), - createUser('user2'), - ]); + [user1, user2] = await Promise.all([createUser('user1'), createUser('user2')]); obj1 = new Parse.Object(className); obj2 = new Parse.Object(className); @@ -1163,10 +1139,7 @@ describe('ProtectedFields', function () { async function initialize() { await Config.get(Parse.applicationId).schemaCache.clear(); - [user1, user2] = await Promise.all([ - createUser('user1'), - createUser('user2'), - ]); + [user1, user2] = await Promise.all([createUser('user1'), createUser('user2')]); obj1 = new Parse.Object(className); obj2 = new Parse.Object(className); @@ -1256,10 +1229,7 @@ describe('ProtectedFields', function () { const object = await obj1.fetch(); - expect(object.get('test')).toBe( - undefined, - 'Field should not be visible - protected by role' - ); + expect(object.get('test')).toBe(undefined, 'Field should not be visible - protected by role'); expect(object.get('owner')).toBe( undefined, 'Field should not be visible - protected by role' @@ -1464,9 +1434,7 @@ describe('ProtectedFields', function () { expect(object.get('test')).toBeDefined( "Being both auhenticated and having a role leads to clearing protection on 'test' (by role rules)" ); - expect(object.get('owner')).toBeDefined( - 'All authenticated users allowed to see "owner"' - ); + expect(object.get('owner')).toBeDefined('All authenticated users allowed to see "owner"'); expect(object.get('testers')).toBeDefined(); done(); @@ -1600,10 +1568,7 @@ describe('ProtectedFields', function () { undefined, 'Should NOT be in response - protected by "userField:owner"' ); - expect(object.owner).toBe( - undefined, - 'Should not be in response - not included in "keys"' - ); + expect(object.owner).toBe(undefined, 'Should not be in response - not included in "keys"'); done(); }); @@ -1646,10 +1611,7 @@ describe('ProtectedFields', function () { undefined, 'Should not be in response - protected by "userField:owner"' ); - expect(object.owner).toBe( - undefined, - 'Should not be in response - not included in "keys"' - ); + expect(object.owner).toBe(undefined, 'Should not be in response - not included in "keys"'); done(); }); @@ -1679,10 +1641,7 @@ describe('ProtectedFields', function () { undefined, 'Should not be in response - protected by "userField:owner"' ); - expect(object['owner']).toBe( - undefined, - 'Should not be in response - not included in "keys"' - ); + expect(object['owner']).toBe(undefined, 'Should not be in response - not included in "keys"'); done(); }); @@ -1715,10 +1674,7 @@ describe('ProtectedFields', function () { undefined, 'Should not be in response - protected by "userField:owner"' ); - expect(object.owner).toBe( - undefined, - 'Should not be in response - not included in "keys"' - ); + expect(object.owner).toBe(undefined, 'Should not be in response - not included in "keys"'); done(); }); diff --git a/spec/PublicAPI.spec.js b/spec/PublicAPI.spec.js index 4ee8bbd863..940417ad24 100644 --- a/spec/PublicAPI.spec.js +++ b/spec/PublicAPI.spec.js @@ -55,13 +55,10 @@ describe('public API', () => { }); it('should get invalid_link.html', done => { - request( - 'http://localhost:8378/1/apps/invalid_link.html', - (err, httpResponse) => { - expect(httpResponse.status).toBe(200); - done(); - } - ); + request('http://localhost:8378/1/apps/invalid_link.html', (err, httpResponse) => { + expect(httpResponse.status).toBe(200); + done(); + }); }); it('should get choose_password', done => { @@ -69,34 +66,25 @@ describe('public API', () => { appName: 'unused', publicServerURL: 'http://localhost:8378/1', }).then(() => { - request( - 'http://localhost:8378/1/apps/choose_password?id=test', - (err, httpResponse) => { - expect(httpResponse.status).toBe(200); - done(); - } - ); + request('http://localhost:8378/1/apps/choose_password?id=test', (err, httpResponse) => { + expect(httpResponse.status).toBe(200); + done(); + }); }); }); it('should get verify_email_success.html', done => { - request( - 'http://localhost:8378/1/apps/verify_email_success.html', - (err, httpResponse) => { - expect(httpResponse.status).toBe(200); - done(); - } - ); + request('http://localhost:8378/1/apps/verify_email_success.html', (err, httpResponse) => { + expect(httpResponse.status).toBe(200); + done(); + }); }); it('should get password_reset_success.html', done => { - request( - 'http://localhost:8378/1/apps/password_reset_success.html', - (err, httpResponse) => { - expect(httpResponse.status).toBe(200); - done(); - } - ); + request('http://localhost:8378/1/apps/password_reset_success.html', (err, httpResponse) => { + expect(httpResponse.status).toBe(200); + done(); + }); }); }); @@ -105,33 +93,24 @@ describe('public API without publicServerURL', () => { await reconfigureServer({ appName: 'unused' }); }); it('should get 404 on verify_email', done => { - request( - 'http://localhost:8378/1/apps/test/verify_email', - (err, httpResponse) => { - expect(httpResponse.status).toBe(404); - done(); - } - ); + request('http://localhost:8378/1/apps/test/verify_email', (err, httpResponse) => { + expect(httpResponse.status).toBe(404); + done(); + }); }); it('should get 404 choose_password', done => { - request( - 'http://localhost:8378/1/apps/choose_password?id=test', - (err, httpResponse) => { - expect(httpResponse.status).toBe(404); - done(); - } - ); + request('http://localhost:8378/1/apps/choose_password?id=test', (err, httpResponse) => { + expect(httpResponse.status).toBe(404); + done(); + }); }); it('should get 404 on request_password_reset', done => { - request( - 'http://localhost:8378/1/apps/test/request_password_reset', - (err, httpResponse) => { - expect(httpResponse.status).toBe(404); - done(); - } - ); + request('http://localhost:8378/1/apps/test/request_password_reset', (err, httpResponse) => { + expect(httpResponse.status).toBe(404); + done(); + }); }); }); @@ -141,33 +120,24 @@ describe('public API supplied with invalid application id', () => { }); it('should get 403 on verify_email', done => { - request( - 'http://localhost:8378/1/apps/invalid/verify_email', - (err, httpResponse) => { - expect(httpResponse.status).toBe(403); - done(); - } - ); + request('http://localhost:8378/1/apps/invalid/verify_email', (err, httpResponse) => { + expect(httpResponse.status).toBe(403); + done(); + }); }); it('should get 403 choose_password', done => { - request( - 'http://localhost:8378/1/apps/choose_password?id=invalid', - (err, httpResponse) => { - expect(httpResponse.status).toBe(403); - done(); - } - ); + request('http://localhost:8378/1/apps/choose_password?id=invalid', (err, httpResponse) => { + expect(httpResponse.status).toBe(403); + done(); + }); }); it('should get 403 on get of request_password_reset', done => { - request( - 'http://localhost:8378/1/apps/invalid/request_password_reset', - (err, httpResponse) => { - expect(httpResponse.status).toBe(403); - done(); - } - ); + request('http://localhost:8378/1/apps/invalid/request_password_reset', (err, httpResponse) => { + expect(httpResponse.status).toBe(403); + done(); + }); }); it('should get 403 on post of request_password_reset', done => { diff --git a/spec/PurchaseValidation.spec.js b/spec/PurchaseValidation.spec.js index 6350cabdf0..478b81260e 100644 --- a/spec/PurchaseValidation.spec.js +++ b/spec/PurchaseValidation.spec.js @@ -146,9 +146,7 @@ describe('test validate_receipt endpoint', () => { fail('Body is not an object'); } else { expect(body.status).toBe(21002); - expect(body.error).toBe( - 'The data in the receipt-data property was malformed or missing.' - ); + expect(body.error).toBe('The data in the receipt-data property was malformed or missing.'); } }); @@ -178,9 +176,7 @@ describe('test validate_receipt endpoint', () => { return product.save(); }) .then(function (productAgain) { - expect(productAgain.get('downloadName')).toEqual( - productAgain.get('download').name() - ); + expect(productAgain.get('downloadName')).toEqual(productAgain.get('download').name()); expect(productAgain.get('title')).toEqual('a new title'); done(); }) diff --git a/spec/PushController.spec.js b/spec/PushController.spec.js index 9b82298c16..f3c331a924 100644 --- a/spec/PushController.spec.js +++ b/spec/PushController.spec.js @@ -1,6 +1,5 @@ 'use strict'; -const PushController = - require('../lib/Controllers/PushController').PushController; +const PushController = require('../lib/Controllers/PushController').PushController; const StatusHandler = require('../lib/StatusHandler'); const Config = require('../lib/Config'); const validatePushType = require('../lib/Push/utils').validatePushType; @@ -40,9 +39,7 @@ const pushCompleted = async pushId => { const sendPush = (body, where, config, auth, now) => { const pushController = new PushController(); return new Promise((resolve, reject) => { - pushController - .sendPush(body, where, config, auth, resolve, now) - .catch(reject); + pushController.sendPush(body, where, config, auth, resolve, now).catch(reject); }); }; @@ -171,81 +168,70 @@ describe('PushController', () => { done(); }); - it_id('01e3e1b8-fad2-4249-b664-5a3efaab8cb1')(it)( - 'properly increment badges', - async () => { - const pushAdapter = { - send: function (body, installations) { - const badge = body.data.badge; - installations.forEach(installation => { - expect(installation.badge).toEqual(badge); - expect(installation.originalBadge + 1).toEqual(installation.badge); - }); - return successfulTransmissions(body, installations); - }, - getValidPushTypes: function () { - return ['ios', 'android']; - }, - }; - await reconfigureServer({ - push: { adapter: pushAdapter }, - }); - const payload = { - data: { - alert: 'Hello World!', - badge: 'Increment', - }, - }; - const installations = []; - while (installations.length != 10) { - const installation = new Parse.Object('_Installation'); - installation.set( - 'installationId', - 'installation_' + installations.length - ); - installation.set('deviceToken', 'device_token_' + installations.length); - installation.set('badge', installations.length); - installation.set('originalBadge', installations.length); - installation.set('deviceType', 'ios'); - installations.push(installation); - } + it_id('01e3e1b8-fad2-4249-b664-5a3efaab8cb1')(it)('properly increment badges', async () => { + const pushAdapter = { + send: function (body, installations) { + const badge = body.data.badge; + installations.forEach(installation => { + expect(installation.badge).toEqual(badge); + expect(installation.originalBadge + 1).toEqual(installation.badge); + }); + return successfulTransmissions(body, installations); + }, + getValidPushTypes: function () { + return ['ios', 'android']; + }, + }; + await reconfigureServer({ + push: { adapter: pushAdapter }, + }); + const payload = { + data: { + alert: 'Hello World!', + badge: 'Increment', + }, + }; + const installations = []; + while (installations.length != 10) { + const installation = new Parse.Object('_Installation'); + installation.set('installationId', 'installation_' + installations.length); + installation.set('deviceToken', 'device_token_' + installations.length); + installation.set('badge', installations.length); + installation.set('originalBadge', installations.length); + installation.set('deviceType', 'ios'); + installations.push(installation); + } - while (installations.length != 15) { - const installation = new Parse.Object('_Installation'); - installation.set( - 'installationId', - 'installation_' + installations.length - ); - installation.set('deviceToken', 'device_token_' + installations.length); - installation.set('badge', installations.length); - installation.set('originalBadge', installations.length); - installation.set('deviceType', 'android'); - installations.push(installation); - } - const config = Config.get(Parse.applicationId); - const auth = { - isMaster: true, - }; - await Parse.Object.saveAll(installations); - const pushStatusId = await sendPush(payload, {}, config, auth); - await pushCompleted(pushStatusId); + while (installations.length != 15) { + const installation = new Parse.Object('_Installation'); + installation.set('installationId', 'installation_' + installations.length); + installation.set('deviceToken', 'device_token_' + installations.length); + installation.set('badge', installations.length); + installation.set('originalBadge', installations.length); + installation.set('deviceType', 'android'); + installations.push(installation); + } + const config = Config.get(Parse.applicationId); + const auth = { + isMaster: true, + }; + await Parse.Object.saveAll(installations); + const pushStatusId = await sendPush(payload, {}, config, auth); + await pushCompleted(pushStatusId); - // Check we actually sent 15 pushes. - const pushStatus = await Parse.Push.getPushStatus(pushStatusId); - expect(pushStatus.get('numSent')).toBe(15); + // Check we actually sent 15 pushes. + const pushStatus = await Parse.Push.getPushStatus(pushStatusId); + expect(pushStatus.get('numSent')).toBe(15); - // Check that the installations were actually updated. - const query = new Parse.Query('_Installation'); - const results = await query.find({ useMasterKey: true }); - expect(results.length).toBe(15); - for (let i = 0; i < 15; i++) { - const installation = results[i]; - expect(installation.get('badge')).toBe( - parseInt(installation.get('originalBadge')) + 1 - ); - } + // Check that the installations were actually updated. + const query = new Parse.Query('_Installation'); + const results = await query.find({ useMasterKey: true }); + expect(results.length).toBe(15); + for (let i = 0; i < 15; i++) { + const installation = results[i]; + expect(installation.get('badge')).toBe(parseInt(installation.get('originalBadge')) + 1); } - ); + }); it_id('14afcedf-e65d-41cd-981e-07f32df84c14')(it)( 'properly increment badges by more than 1', @@ -275,10 +261,7 @@ describe('PushController', () => { const installations = []; while (installations.length != 10) { const installation = new Parse.Object('_Installation'); - installation.set( - 'installationId', - 'installation_' + installations.length - ); + installation.set('installationId', 'installation_' + installations.length); installation.set('deviceToken', 'device_token_' + installations.length); installation.set('badge', installations.length); installation.set('originalBadge', installations.length); @@ -288,10 +271,7 @@ describe('PushController', () => { while (installations.length != 15) { const installation = new Parse.Object('_Installation'); - installation.set( - 'installationId', - 'installation_' + installations.length - ); + installation.set('installationId', 'installation_' + installations.length); installation.set('deviceToken', 'device_token_' + installations.length); installation.set('badge', installations.length); installation.set('originalBadge', installations.length); @@ -313,72 +293,64 @@ describe('PushController', () => { expect(results.length).toBe(15); for (let i = 0; i < 15; i++) { const installation = results[i]; - expect(installation.get('badge')).toBe( - parseInt(installation.get('originalBadge')) + 3 - ); + expect(installation.get('badge')).toBe(parseInt(installation.get('originalBadge')) + 3); } } ); - it_id('758dd579-aa91-4010-9033-8d48d3463644')(it)( - 'properly set badges to 1', - async () => { - const pushAdapter = { - send: function (body, installations) { - const badge = body.data.badge; - installations.forEach(installation => { - expect(installation.badge).toEqual(badge); - expect(1).toEqual(installation.badge); - }); - return successfulTransmissions(body, installations); - }, - getValidPushTypes: function () { - return ['ios']; - }, - }; - await reconfigureServer({ - push: { adapter: pushAdapter }, - }); - const payload = { - data: { - alert: 'Hello World!', - badge: 1, - }, - }; - const installations = []; - while (installations.length != 10) { - const installation = new Parse.Object('_Installation'); - installation.set( - 'installationId', - 'installation_' + installations.length - ); - installation.set('deviceToken', 'device_token_' + installations.length); - installation.set('badge', installations.length); - installation.set('originalBadge', installations.length); - installation.set('deviceType', 'ios'); - installations.push(installation); - } + it_id('758dd579-aa91-4010-9033-8d48d3463644')(it)('properly set badges to 1', async () => { + const pushAdapter = { + send: function (body, installations) { + const badge = body.data.badge; + installations.forEach(installation => { + expect(installation.badge).toEqual(badge); + expect(1).toEqual(installation.badge); + }); + return successfulTransmissions(body, installations); + }, + getValidPushTypes: function () { + return ['ios']; + }, + }; + await reconfigureServer({ + push: { adapter: pushAdapter }, + }); + const payload = { + data: { + alert: 'Hello World!', + badge: 1, + }, + }; + const installations = []; + while (installations.length != 10) { + const installation = new Parse.Object('_Installation'); + installation.set('installationId', 'installation_' + installations.length); + installation.set('deviceToken', 'device_token_' + installations.length); + installation.set('badge', installations.length); + installation.set('originalBadge', installations.length); + installation.set('deviceType', 'ios'); + installations.push(installation); + } - const config = Config.get(Parse.applicationId); - const auth = { - isMaster: true, - }; - await Parse.Object.saveAll(installations); - const pushStatusId = await sendPush(payload, {}, config, auth); - await pushCompleted(pushStatusId); - const pushStatus = await Parse.Push.getPushStatus(pushStatusId); - expect(pushStatus.get('numSent')).toBe(10); + const config = Config.get(Parse.applicationId); + const auth = { + isMaster: true, + }; + await Parse.Object.saveAll(installations); + const pushStatusId = await sendPush(payload, {}, config, auth); + await pushCompleted(pushStatusId); + const pushStatus = await Parse.Push.getPushStatus(pushStatusId); + expect(pushStatus.get('numSent')).toBe(10); - // Check that the installations were actually updated. - const query = new Parse.Query('_Installation'); - const results = await query.find({ useMasterKey: true }); - expect(results.length).toBe(10); - for (let i = 0; i < 10; i++) { - const installation = results[i]; - expect(installation.get('badge')).toBe(1); - } + // Check that the installations were actually updated. + const query = new Parse.Query('_Installation'); + const results = await query.find({ useMasterKey: true }); + expect(results.length).toBe(10); + for (let i = 0; i < 10; i++) { + const installation = results[i]; + expect(installation.get('badge')).toBe(1); } - ); + }); it_id('75c39ae3-06ac-4354-b321-931e81c5a927')(it)( 'properly set badges to 1 with complex query #2903 #3022', @@ -392,10 +364,7 @@ describe('PushController', () => { const installations = []; while (installations.length != 10) { const installation = new Parse.Object('_Installation'); - installation.set( - 'installationId', - 'installation_' + installations.length - ); + installation.set('installationId', 'installation_' + installations.length); installation.set('deviceToken', 'device_token_' + installations.length); installation.set('badge', installations.length); installation.set('originalBadge', installations.length); @@ -441,131 +410,119 @@ describe('PushController', () => { } ); - it_id('667f31c0-b458-4f61-ab57-668c04e3cc0b')(it)( - 'properly creates _PushStatus', - async () => { - const pushStatusAfterSave = { - handler: function () {}, - }; - const spy = spyOn(pushStatusAfterSave, 'handler').and.callThrough(); - Parse.Cloud.afterSave('_PushStatus', pushStatusAfterSave.handler); - const installations = []; - while (installations.length != 10) { - const installation = new Parse.Object('_Installation'); - installation.set( - 'installationId', - 'installation_' + installations.length - ); - installation.set('deviceToken', 'device_token_' + installations.length); - installation.set('badge', installations.length); - installation.set('originalBadge', installations.length); - installation.set('deviceType', 'ios'); - installations.push(installation); - } + it_id('667f31c0-b458-4f61-ab57-668c04e3cc0b')(it)('properly creates _PushStatus', async () => { + const pushStatusAfterSave = { + handler: function () {}, + }; + const spy = spyOn(pushStatusAfterSave, 'handler').and.callThrough(); + Parse.Cloud.afterSave('_PushStatus', pushStatusAfterSave.handler); + const installations = []; + while (installations.length != 10) { + const installation = new Parse.Object('_Installation'); + installation.set('installationId', 'installation_' + installations.length); + installation.set('deviceToken', 'device_token_' + installations.length); + installation.set('badge', installations.length); + installation.set('originalBadge', installations.length); + installation.set('deviceType', 'ios'); + installations.push(installation); + } - while (installations.length != 15) { - const installation = new Parse.Object('_Installation'); - installation.set( - 'installationId', - 'installation_' + installations.length - ); - installation.set('deviceToken', 'device_token_' + installations.length); - installation.set('deviceType', 'android'); - installations.push(installation); - } - const payload = { - data: { - alert: 'Hello World!', - badge: 1, - }, - }; + while (installations.length != 15) { + const installation = new Parse.Object('_Installation'); + installation.set('installationId', 'installation_' + installations.length); + installation.set('deviceToken', 'device_token_' + installations.length); + installation.set('deviceType', 'android'); + installations.push(installation); + } + const payload = { + data: { + alert: 'Hello World!', + badge: 1, + }, + }; - const pushAdapter = { - send: function (body, installations) { - return successfulIOS(body, installations); - }, - getValidPushTypes: function () { - return ['ios']; - }, - }; - await reconfigureServer({ - push: { adapter: pushAdapter }, - }); - const config = Config.get(Parse.applicationId); - const auth = { - isMaster: true, - }; - await Parse.Object.saveAll(installations); - const pushStatusId = await sendPush(payload, {}, config, auth); - await pushCompleted(pushStatusId); - const result = await Parse.Push.getPushStatus(pushStatusId); - expect(result.createdAt instanceof Date).toBe(true); - expect(result.updatedAt instanceof Date).toBe(true); - expect(result.id.length).toBe(10); - expect(result.get('source')).toEqual('rest'); - expect(result.get('query')).toEqual(JSON.stringify({})); - expect(typeof result.get('payload')).toEqual('string'); - expect(JSON.parse(result.get('payload'))).toEqual(payload.data); - expect(result.get('status')).toEqual('succeeded'); - expect(result.get('numSent')).toEqual(10); - expect(result.get('sentPerType')).toEqual({ - ios: 10, // 10 ios - }); - expect(result.get('numFailed')).toEqual(5); - expect(result.get('failedPerType')).toEqual({ - android: 5, // android - }); - try { - // Try to get it without masterKey - const query = new Parse.Query('_PushStatus'); - await query.find(); - fail(); - } catch (error) { - expect(error.code).toBe(119); - } + const pushAdapter = { + send: function (body, installations) { + return successfulIOS(body, installations); + }, + getValidPushTypes: function () { + return ['ios']; + }, + }; + await reconfigureServer({ + push: { adapter: pushAdapter }, + }); + const config = Config.get(Parse.applicationId); + const auth = { + isMaster: true, + }; + await Parse.Object.saveAll(installations); + const pushStatusId = await sendPush(payload, {}, config, auth); + await pushCompleted(pushStatusId); + const result = await Parse.Push.getPushStatus(pushStatusId); + expect(result.createdAt instanceof Date).toBe(true); + expect(result.updatedAt instanceof Date).toBe(true); + expect(result.id.length).toBe(10); + expect(result.get('source')).toEqual('rest'); + expect(result.get('query')).toEqual(JSON.stringify({})); + expect(typeof result.get('payload')).toEqual('string'); + expect(JSON.parse(result.get('payload'))).toEqual(payload.data); + expect(result.get('status')).toEqual('succeeded'); + expect(result.get('numSent')).toEqual(10); + expect(result.get('sentPerType')).toEqual({ + ios: 10, // 10 ios + }); + expect(result.get('numFailed')).toEqual(5); + expect(result.get('failedPerType')).toEqual({ + android: 5, // android + }); + try { + // Try to get it without masterKey + const query = new Parse.Query('_PushStatus'); + await query.find(); + fail(); + } catch (error) { + expect(error.code).toBe(119); + } - function getPushStatus(callIndex) { - return spy.calls.all()[callIndex].args[0].object; - } - expect(spy).toHaveBeenCalled(); - expect(spy.calls.count()).toBe(4); - const allCalls = spy.calls.all(); - let pendingCount = 0; - let runningCount = 0; - let succeedCount = 0; - allCalls.forEach((call, index) => { - expect(call.args.length).toBe(1); - const object = call.args[0].object; - expect(object instanceof Parse.Object).toBe(true); - const pushStatus = getPushStatus(index); - if (pushStatus.get('status') === 'pending') { - pendingCount += 1; - } - if (pushStatus.get('status') === 'running') { - runningCount += 1; - } - if (pushStatus.get('status') === 'succeeded') { - succeedCount += 1; - } - if ( - pushStatus.get('status') === 'running' && - pushStatus.get('numSent') > 0 - ) { - expect(pushStatus.get('numSent')).toBe(10); - expect(pushStatus.get('numFailed')).toBe(5); - expect(pushStatus.get('failedPerType')).toEqual({ - android: 5, - }); - expect(pushStatus.get('sentPerType')).toEqual({ - ios: 10, - }); - } - }); - expect(pendingCount).toBe(1); - expect(runningCount).toBe(2); - expect(succeedCount).toBe(1); + function getPushStatus(callIndex) { + return spy.calls.all()[callIndex].args[0].object; } - ); + expect(spy).toHaveBeenCalled(); + expect(spy.calls.count()).toBe(4); + const allCalls = spy.calls.all(); + let pendingCount = 0; + let runningCount = 0; + let succeedCount = 0; + allCalls.forEach((call, index) => { + expect(call.args.length).toBe(1); + const object = call.args[0].object; + expect(object instanceof Parse.Object).toBe(true); + const pushStatus = getPushStatus(index); + if (pushStatus.get('status') === 'pending') { + pendingCount += 1; + } + if (pushStatus.get('status') === 'running') { + runningCount += 1; + } + if (pushStatus.get('status') === 'succeeded') { + succeedCount += 1; + } + if (pushStatus.get('status') === 'running' && pushStatus.get('numSent') > 0) { + expect(pushStatus.get('numSent')).toBe(10); + expect(pushStatus.get('numFailed')).toBe(5); + expect(pushStatus.get('failedPerType')).toEqual({ + android: 5, + }); + expect(pushStatus.get('sentPerType')).toEqual({ + ios: 10, + }); + } + }); + expect(pendingCount).toBe(1); + expect(runningCount).toBe(2); + expect(succeedCount).toBe(1); + }); it_id('30e0591a-56de-4720-8c60-7d72291b532a')(it)( 'properly creates _PushStatus without serverURL', @@ -697,10 +654,7 @@ describe('PushController', () => { const installations = []; while (installations.length != 5) { const installation = new Parse.Object('_Installation'); - installation.set( - 'installationId', - 'installation_' + installations.length - ); + installation.set('installationId', 'installation_' + installations.length); installation.set('deviceToken', 'device_token_' + installations.length); installation.set('badge', installations.length); installation.set('originalBadge', installations.length); @@ -745,10 +699,7 @@ describe('PushController', () => { const installations = []; while (installations.length != 5) { const installation = new Parse.Object('_Installation'); - installation.set( - 'installationId', - 'installation_' + installations.length - ); + installation.set('installationId', 'installation_' + installations.length); installation.set('deviceToken', 'device_token_' + installations.length); installation.set('badge', installations.length); installation.set('originalBadge', installations.length); @@ -844,10 +795,7 @@ describe('PushController', () => { const installations = []; while (installations.length != 10) { const installation = new Parse.Object('_Installation'); - installation.set( - 'installationId', - 'installation_' + installations.length - ); + installation.set('installationId', 'installation_' + installations.length); installation.set('deviceToken', 'device_token_' + installations.length); installation.set('badge', installations.length); installation.set('originalBadge', installations.length); @@ -897,10 +845,7 @@ describe('PushController', () => { const installations = []; while (installations.length != 10) { const installation = new Parse.Object('_Installation'); - installation.set( - 'installationId', - 'installation_' + installations.length - ); + installation.set('installationId', 'installation_' + installations.length); installation.set('deviceToken', 'device_token_' + installations.length); installation.set('badge', installations.length); installation.set('originalBadge', installations.length); @@ -954,10 +899,7 @@ describe('PushController', () => { const installations = []; while (installations.length != 5) { const installation = new Parse.Object('_Installation'); - installation.set( - 'installationId', - 'installation_' + installations.length - ); + installation.set('installationId', 'installation_' + installations.length); installation.set('deviceToken', 'device_token_' + installations.length); installation.set('badge', installations.length); installation.set('originalBadge', installations.length); @@ -966,10 +908,7 @@ describe('PushController', () => { } while (installations.length != 15) { const installation = new Parse.Object('_Installation'); - installation.set( - 'installationId', - 'installation_' + installations.length - ); + installation.set('installationId', 'installation_' + installations.length); installation.set('badge', installations.length); installation.set('originalBadge', installations.length); installation.set('deviceType', 'ios'); @@ -1019,10 +958,7 @@ describe('PushController', () => { const installations = []; while (installations.length != 5) { const installation = new Parse.Object('_Installation'); - installation.set( - 'installationId', - 'installation_' + installations.length - ); + installation.set('installationId', 'installation_' + installations.length); installation.set('badge', installations.length); installation.set('originalBadge', installations.length); installation.set('deviceType', 'ios'); @@ -1069,10 +1005,7 @@ describe('PushController', () => { const installations = []; while (installations.length != 5) { const installation = new Parse.Object('_Installation'); - installation.set( - 'installationId', - 'installation_' + installations.length - ); + installation.set('installationId', 'installation_' + installations.length); installation.set('deviceToken', 'device_token_' + installations.length); installation.set('badge', installations.length); installation.set('originalBadge', installations.length); @@ -1102,110 +1035,103 @@ describe('PushController', () => { // No installation is in es so only 1 call for fr, and another for default }); - it_id('ef2e5569-50c3-40c2-ab49-175cdbd5f024')(it)( - 'should update audiences', - async () => { - const pushAdapter = { - send: function (body, installations) { - return successfulTransmissions(body, installations); - }, - getValidPushTypes: function () { - return ['ios']; - }, - }; - spyOn(pushAdapter, 'send').and.callThrough(); - await reconfigureServer({ - push: { adapter: pushAdapter }, - }); - const config = Config.get(Parse.applicationId); - const auth = { - isMaster: true, - }; - let audienceId = null; - const now = new Date(); - let timesUsed = 0; - const where = { - deviceType: 'ios', - }; - const installations = []; - while (installations.length != 5) { - const installation = new Parse.Object('_Installation'); - installation.set( - 'installationId', - 'installation_' + installations.length - ); - installation.set('deviceToken', 'device_token_' + installations.length); - installation.set('badge', installations.length); - installation.set('originalBadge', installations.length); - installation.set('deviceType', 'ios'); - installations.push(installation); - } - await Parse.Object.saveAll(installations); + it_id('ef2e5569-50c3-40c2-ab49-175cdbd5f024')(it)('should update audiences', async () => { + const pushAdapter = { + send: function (body, installations) { + return successfulTransmissions(body, installations); + }, + getValidPushTypes: function () { + return ['ios']; + }, + }; + spyOn(pushAdapter, 'send').and.callThrough(); + await reconfigureServer({ + push: { adapter: pushAdapter }, + }); + const config = Config.get(Parse.applicationId); + const auth = { + isMaster: true, + }; + let audienceId = null; + const now = new Date(); + let timesUsed = 0; + const where = { + deviceType: 'ios', + }; + const installations = []; + while (installations.length != 5) { + const installation = new Parse.Object('_Installation'); + installation.set('installationId', 'installation_' + installations.length); + installation.set('deviceToken', 'device_token_' + installations.length); + installation.set('badge', installations.length); + installation.set('originalBadge', installations.length); + installation.set('deviceType', 'ios'); + installations.push(installation); + } + await Parse.Object.saveAll(installations); - // Create an audience - const query = new Parse.Query('_Audience'); - query.descending('createdAt'); - query.equalTo('query', JSON.stringify(where)); - const parseResults = results => { - if (results.length > 0) { - audienceId = results[0].id; - timesUsed = results[0].get('timesUsed'); - if (!isFinite(timesUsed)) { - timesUsed = 0; - } + // Create an audience + const query = new Parse.Query('_Audience'); + query.descending('createdAt'); + query.equalTo('query', JSON.stringify(where)); + const parseResults = results => { + if (results.length > 0) { + audienceId = results[0].id; + timesUsed = results[0].get('timesUsed'); + if (!isFinite(timesUsed)) { + timesUsed = 0; } - }; - const audience = new Parse.Object('_Audience'); - audience.set('name', 'testAudience'); - audience.set('query', JSON.stringify(where)); - await Parse.Object.saveAll(audience); - await query.find({ useMasterKey: true }).then(parseResults); - - const body = { - data: { alert: 'hello' }, - audience_id: audienceId, - }; - const pushStatusId = await sendPush(body, where, config, auth); - await pushCompleted(pushStatusId); - expect(pushAdapter.send.calls.count()).toBe(1); - const firstCall = pushAdapter.send.calls.first(); - expect(firstCall.args[0].data).toEqual({ - alert: 'hello', - }); - expect(firstCall.args[1].length).toBe(5); + } + }; + const audience = new Parse.Object('_Audience'); + audience.set('name', 'testAudience'); + audience.set('query', JSON.stringify(where)); + await Parse.Object.saveAll(audience); + await query.find({ useMasterKey: true }).then(parseResults); + + const body = { + data: { alert: 'hello' }, + audience_id: audienceId, + }; + const pushStatusId = await sendPush(body, where, config, auth); + await pushCompleted(pushStatusId); + expect(pushAdapter.send.calls.count()).toBe(1); + const firstCall = pushAdapter.send.calls.first(); + expect(firstCall.args[0].data).toEqual({ + alert: 'hello', + }); + expect(firstCall.args[1].length).toBe(5); - // Get the audience we used above. - const audienceQuery = new Parse.Query('_Audience'); - audienceQuery.equalTo('objectId', audienceId); - const results = await audienceQuery.find({ useMasterKey: true }); + // Get the audience we used above. + const audienceQuery = new Parse.Query('_Audience'); + audienceQuery.equalTo('objectId', audienceId); + const results = await audienceQuery.find({ useMasterKey: true }); - expect(results[0].get('query')).toBe(JSON.stringify(where)); - expect(results[0].get('timesUsed')).toBe(timesUsed + 1); - expect(results[0].get('lastUsed')).not.toBeLessThan(now); - } - ); + expect(results[0].get('query')).toBe(JSON.stringify(where)); + expect(results[0].get('timesUsed')).toBe(timesUsed + 1); + expect(results[0].get('lastUsed')).not.toBeLessThan(now); + }); describe('pushTimeHasTimezoneComponent', () => { it('should be accurate', () => { - expect( - PushController.pushTimeHasTimezoneComponent('2017-09-06T17:14:01.048Z') - ).toBe(true, 'UTC time'); - expect( - PushController.pushTimeHasTimezoneComponent('2007-04-05T12:30-02:00') - ).toBe(true, 'Timezone offset'); - expect( - PushController.pushTimeHasTimezoneComponent( - '2007-04-05T12:30:00.000Z-02:00' - ) - ).toBe(true, 'Seconds + Milliseconds + Timezone offset'); + expect(PushController.pushTimeHasTimezoneComponent('2017-09-06T17:14:01.048Z')).toBe( + true, + 'UTC time' + ); + expect(PushController.pushTimeHasTimezoneComponent('2007-04-05T12:30-02:00')).toBe( + true, + 'Timezone offset' + ); + expect(PushController.pushTimeHasTimezoneComponent('2007-04-05T12:30:00.000Z-02:00')).toBe( + true, + 'Seconds + Milliseconds + Timezone offset' + ); - expect( - PushController.pushTimeHasTimezoneComponent('2017-09-06T17:14:01.048') - ).toBe(false, 'No timezone'); - expect(PushController.pushTimeHasTimezoneComponent('2017-09-06')).toBe( + expect(PushController.pushTimeHasTimezoneComponent('2017-09-06T17:14:01.048')).toBe( false, - 'YY-MM-DD' + 'No timezone' ); + expect(PushController.pushTimeHasTimezoneComponent('2017-09-06')).toBe(false, 'YY-MM-DD'); }); }); @@ -1236,10 +1162,7 @@ describe('PushController', () => { date: noTimezone, isLocalTime: true, }) - ).toBe( - `2017-09-${day}T${expectedHour.toString().padStart(2, '0')}:14:01.048`, - 'No timezone' - ); + ).toBe(`2017-09-${day}T${expectedHour.toString().padStart(2, '0')}:14:01.048`, 'No timezone'); expect( PushController.formatPushTime({ date: new Date('2017-09-06'), @@ -1380,9 +1303,7 @@ describe('PushController', () => { ); const pushStatus = await Parse.Push.getPushStatus(pushStatusId); expect(pushStatus.get('expiry')).toBeDefined('expiry must be set'); - expect(pushStatus.get('expiry')).toEqual( - new Date('2017-09-25T13:50:10.452Z').valueOf() - ); + expect(pushStatus.get('expiry')).toEqual(new Date('2017-09-25T13:50:10.452Z').valueOf()); expect(pushStatus.get('expiration_interval')).toBeDefined( 'expiration_interval must be defined' diff --git a/spec/PushQueue.spec.js b/spec/PushQueue.spec.js index 8cf42e81a7..db851ba22e 100644 --- a/spec/PushQueue.spec.js +++ b/spec/PushQueue.spec.js @@ -22,10 +22,7 @@ describe('PushQueue', () => { }) .then(() => { const config = Config.get(Parse.applicationId); - expect(config.pushWorker.channel).toEqual( - 'my-specific-channel', - 'pushWorker.channel' - ); + expect(config.pushWorker.channel).toEqual('my-specific-channel', 'pushWorker.channel'); expect(config.pushControllerQueue.channel).toEqual( 'my-specific-channel', 'pushWorker.channel' @@ -54,13 +51,9 @@ describe('PushQueue', () => { }) .then(() => { const config = Config.get(Parse.applicationId); - expect(PushQueue.defaultPushChannel()).toEqual( - 'test-parse-server-push' - ); + expect(PushQueue.defaultPushChannel()).toEqual('test-parse-server-push'); expect(config.pushWorker.channel).toEqual('test-parse-server-push'); - expect(config.pushControllerQueue.channel).toEqual( - 'test-parse-server-push' - ); + expect(config.pushControllerQueue.channel).toEqual('test-parse-server-push'); }) .then(done, done.fail); }); diff --git a/spec/PushWorker.spec.js b/spec/PushWorker.spec.js index 12a0ece5df..7c413c58c8 100644 --- a/spec/PushWorker.spec.js +++ b/spec/PushWorker.spec.js @@ -31,14 +31,8 @@ describe('PushWorker', () => { const installations = []; while (installations.length != 10) { const installation = new Parse.Object('_Installation'); - installation.set( - 'installationId', - 'installation_' + installations.length - ); - installation.set( - 'deviceToken', - 'device_token_' + installations.length - ); + installation.set('installationId', 'installation_' + installations.length); + installation.set('deviceToken', 'device_token_' + installations.length); installation.set('badge', 1); installation.set('deviceType', 'ios'); installations.push(installation); diff --git a/spec/RateLimit.spec.js b/spec/RateLimit.spec.js index aa62b6655d..30c585a61f 100644 --- a/spec/RateLimit.spec.js +++ b/spec/RateLimit.spec.js @@ -1,5 +1,4 @@ -const RedisCacheAdapter = - require('../lib/Adapters/Cache/RedisCacheAdapter').default; +const RedisCacheAdapter = require('../lib/Adapters/Cache/RedisCacheAdapter').default; describe('rate limit', () => { it('can limit cloud functions', async () => { Parse.Cloud.define('test', () => 'Abc'); @@ -238,9 +237,7 @@ describe('rate limit', () => { await expectAsync(new Parse.Query('TestObject').first()).toBeRejectedWith( new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') ); - await expectAsync( - new Parse.Query('TestObject').get('abc') - ).toBeRejectedWith( + await expectAsync(new Parse.Query('TestObject').get('abc')).toBeRejectedWith( new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests') ); }); @@ -372,15 +369,8 @@ describe('rate limit', () => { }, }; fakeReq.ip = '127.0.0.1'; - let fakeRes = jasmine.createSpyObj('fakeRes', [ - 'end', - 'status', - 'setHeader', - 'json', - ]); - await new Promise(resolve => - middlewares.handleParseHeaders(fakeReq, fakeRes, resolve) - ); + let fakeRes = jasmine.createSpyObj('fakeRes', ['end', 'status', 'setHeader', 'json']); + await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve)); fakeReq.ip = '127.0.0.2'; fakeRes = jasmine.createSpyObj('fakeRes', ['end', 'status', 'setHeader']); let resolvingPromise; @@ -446,8 +436,7 @@ describe('rate limit', () => { it('can validate rateLimit', async () => { const Config = require('../lib/Config'); - const validateRateLimit = ({ rateLimit }) => - Config.validateRateLimit(rateLimit); + const validateRateLimit = ({ rateLimit }) => Config.validateRateLimit(rateLimit); expect(() => validateRateLimit({ rateLimit: 'a', @@ -458,9 +447,9 @@ describe('rate limit', () => { expect(() => validateRateLimit({ rateLimit: ['a'] })).toThrow( 'rateLimit must be an array of objects' ); - expect(() => - validateRateLimit({ rateLimit: [{ requestPath: [] }] }) - ).toThrow('rateLimit.requestPath must be a string'); + expect(() => validateRateLimit({ rateLimit: [{ requestPath: [] }] })).toThrow( + 'rateLimit.requestPath must be a string' + ); expect(() => validateRateLimit({ rateLimit: [{ requestTimeWindow: [], requestPath: 'a' }], @@ -492,9 +481,7 @@ describe('rate limit', () => { ).toThrow('rateLimit.includeInternalRequests must be a boolean'); expect(() => validateRateLimit({ - rateLimit: [ - { requestCount: [], requestTimeWindow: 1000, requestPath: 'a' }, - ], + rateLimit: [{ requestCount: [], requestTimeWindow: 1000, requestPath: 'a' }], }) ).toThrow('rateLimit.requestCount must be a number'); expect(() => diff --git a/spec/ReadPreferenceOption.spec.js b/spec/ReadPreferenceOption.spec.js index 7cc662b771..67b976674b 100644 --- a/spec/ReadPreferenceOption.spec.js +++ b/spec/ReadPreferenceOption.spec.js @@ -31,9 +31,7 @@ describe_only_db('mongo')('Read preference option', () => { Collection.prototype.find.calls.all().forEach(call => { if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) { myObjectReadPreference = true; - expect(call.object.s.readPreference.mode).toBe( - ReadPreference.PRIMARY - ); + expect(call.object.s.readPreference.mode).toBe(ReadPreference.PRIMARY); } }); @@ -46,9 +44,7 @@ describe_only_db('mongo')('Read preference option', () => { }); xit('should preserve the read preference set (#4831)', async () => { - const { - MongoStorageAdapter, - } = require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter'); + const { MongoStorageAdapter } = require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter'); const adapterOptions = { uri: 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase', mongoOptions: { @@ -421,10 +417,7 @@ describe_only_db('mongo')('Read preference option', () => { const response = await request({ method: 'GET', - url: - 'http://localhost:8378/1/classes/MyObject/' + - obj0.id + - '?readPreference=SECONDARY', + url: 'http://localhost:8378/1/classes/MyObject/' + obj0.id + '?readPreference=SECONDARY', headers: { 'X-Parse-Application-Id': 'test', 'X-Parse-REST-API-Key': 'rest', @@ -459,10 +452,7 @@ describe_only_db('mongo')('Read preference option', () => { const response = await request({ method: 'GET', - url: - 'http://localhost:8378/1/classes/MyObject/' + - obj0.id + - '?readPreference=SECONDARY', + url: 'http://localhost:8378/1/classes/MyObject/' + obj0.id + '?readPreference=SECONDARY', headers: { 'X-Parse-Application-Id': 'test', 'X-Parse-REST-API-Key': 'rest', @@ -728,9 +718,7 @@ describe_only_db('mongo')('Read preference option', () => { const firstResult = results[0]; expect(firstResult.get('boolKey')).toBe(false); expect(firstResult.get('myObject1').get('boolKey')).toBe(true); - expect(firstResult.get('myObject1').get('myObject0').get('boolKey')).toBe( - false - ); + expect(firstResult.get('myObject1').get('myObject0').get('boolKey')).toBe(false); let myObjectReadPreference0 = null; let myObjectReadPreference1 = null; @@ -781,9 +769,7 @@ describe_only_db('mongo')('Read preference option', () => { const firstResult = results[0]; expect(firstResult.get('boolKey')).toBe(false); expect(firstResult.get('myObject1').get('boolKey')).toBe(true); - expect(firstResult.get('myObject1').get('myObject0').get('boolKey')).toBe( - false - ); + expect(firstResult.get('myObject1').get('myObject0').get('boolKey')).toBe(false); let myObjectReadPreference0 = null; let myObjectReadPreference1 = null; diff --git a/spec/RedisCacheAdapter.spec.js b/spec/RedisCacheAdapter.spec.js index cdabf34ea2..9b88e857c4 100644 --- a/spec/RedisCacheAdapter.spec.js +++ b/spec/RedisCacheAdapter.spec.js @@ -1,5 +1,4 @@ -const RedisCacheAdapter = - require('../lib/Adapters/Cache/RedisCacheAdapter').default; +const RedisCacheAdapter = require('../lib/Adapters/Cache/RedisCacheAdapter').default; function wait(sleep) { return new Promise(function (resolve) { diff --git a/spec/RegexVulnerabilities.spec.js b/spec/RegexVulnerabilities.spec.js index 5cba845119..8418494bac 100644 --- a/spec/RegexVulnerabilities.spec.js +++ b/spec/RegexVulnerabilities.spec.js @@ -102,30 +102,27 @@ describe('Regex Vulnerabilities', () => { expect(user.get('emailVerified')).toEqual(false); }); - it_id('92bbb86d-bcda-49fa-8d79-aa0501078044')(it)( - 'should work with plain token', - async () => { - expect(user.get('emailVerified')).toEqual(false); - const current = await request({ - method: 'GET', - url: `http://localhost:8378/1/classes/_User/${user.id}`, - json: true, - headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-Rest-API-Key': 'test', - 'X-Parse-Maintenance-Key': 'test2', - 'Content-Type': 'application/json', - }, - }).then(res => res.data); - // It should work - await request({ - url: `${serverURL}/apps/test/verify_email?token=${current._email_verify_token}`, - method: 'GET', - }); - await user.fetch({ useMasterKey: true }); - expect(user.get('emailVerified')).toEqual(true); - } - ); + it_id('92bbb86d-bcda-49fa-8d79-aa0501078044')(it)('should work with plain token', async () => { + expect(user.get('emailVerified')).toEqual(false); + const current = await request({ + method: 'GET', + url: `http://localhost:8378/1/classes/_User/${user.id}`, + json: true, + headers: { + 'X-Parse-Application-Id': 'test', + 'X-Parse-Rest-API-Key': 'test', + 'X-Parse-Maintenance-Key': 'test2', + 'Content-Type': 'application/json', + }, + }).then(res => res.data); + // It should work + await request({ + url: `${serverURL}/apps/test/verify_email?token=${current._email_verify_token}`, + method: 'GET', + }); + await user.fetch({ useMasterKey: true }); + expect(user.get('emailVerified')).toEqual(true); + }); }); describe('on password reset', () => { @@ -151,9 +148,7 @@ describe('Regex Vulnerabilities', () => { method: 'GET', }); expect(passwordResetResponse.status).toEqual(302); - expect(passwordResetResponse.headers.location).toMatch( - `\\/invalid\\_link\\.html` - ); + expect(passwordResetResponse.headers.location).toMatch(`\\/invalid\\_link\\.html`); await request({ url: `${serverURL}/apps/test/request_password_reset`, method: 'POST', @@ -213,10 +208,7 @@ describe('Regex Vulnerabilities', () => { new_password: 'newpassword', }, }); - const userAgain = await Parse.User.logIn( - 'someemail@somedomain.com', - 'newpassword' - ); + const userAgain = await Parse.User.logIn('someemail@somedomain.com', 'newpassword'); expect(userAgain.id).toEqual(objectId); }); }); diff --git a/spec/RestQuery.spec.js b/spec/RestQuery.spec.js index 9949419857..131433e177 100644 --- a/spec/RestQuery.spec.js +++ b/spec/RestQuery.spec.js @@ -138,13 +138,7 @@ describe('rest query', () => { }) .then(() => { queryWhere.photo.objectId = photo.objectId; - return rest.find( - config, - nobody, - 'TestActivity', - queryWhere, - queryOptions - ); + return rest.find(config, nobody, 'TestActivity', queryWhere, queryOptions); }) .then(response => { const results = response.results; @@ -164,22 +158,19 @@ describe('rest query', () => { const customConfig = Object.assign({}, config, { allowClientClassCreation: false, }); - rest - .find(customConfig, auth.nobody(customConfig), 'ClientClassCreation', {}) - .then( - () => { - fail('Should throw an error'); - done(); - }, - err => { - expect(err.code).toEqual(Parse.Error.OPERATION_FORBIDDEN); - expect(err.message).toEqual( - 'This user is not allowed to access ' + - 'non-existent class: ClientClassCreation' - ); - done(); - } - ); + rest.find(customConfig, auth.nobody(customConfig), 'ClientClassCreation', {}).then( + () => { + fail('Should throw an error'); + done(); + }, + err => { + expect(err.code).toEqual(Parse.Error.OPERATION_FORBIDDEN); + expect(err.message).toEqual( + 'This user is not allowed to access ' + 'non-existent class: ClientClassCreation' + ); + done(); + } + ); }); it('query existent class when disabled client class creation', async () => { @@ -187,10 +178,7 @@ describe('rest query', () => { allowClientClassCreation: false, }); const schema = await config.database.loadSchema(); - const actualSchema = await schema.addClassIfNotExists( - 'ClientClassCreation', - {} - ); + const actualSchema = await schema.addClassIfNotExists('ClientClassCreation', {}); expect(actualSchema.className).toEqual('ClientClassCreation'); await schema.reloadData({ clearCache: true }); @@ -217,13 +205,8 @@ describe('rest query', () => { ]; await Promise.all([ ...internalFields.map(field => - expectAsync( - new Parse.Query(Parse.User).exists(field).find() - ).toBeRejectedWith( - new Parse.Error( - Parse.Error.INVALID_KEY_NAME, - `Invalid key name: ${field}` - ) + expectAsync(new Parse.Query(Parse.User).exists(field).find()).toBeRejectedWith( + new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid key name: ${field}`) ) ), ...internalFields.map(field => @@ -257,9 +240,7 @@ describe('rest query', () => { ); await Promise.all([ new Parse.Query('Test').exists('test').find(), - expectAsync( - new Parse.Query('Test').exists('zip').find() - ).toBeRejectedWith( + expectAsync(new Parse.Query('Test').exists('zip').find()).toBeRejectedWith( new Parse.Error( Parse.Error.OPERATION_FORBIDDEN, 'This user is not allowed to query zip on class Test' @@ -280,10 +261,7 @@ describe('rest query', () => { await expectAsync( new Parse.Query('TestObject').matchesQuery('user', subQuery).find() ).toBeRejectedWith( - new Parse.Error( - Parse.Error.INVALID_KEY_NAME, - 'Invalid key name: _perishable_token' - ) + new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'Invalid key name: _perishable_token') ); }); @@ -361,13 +339,7 @@ describe('rest query', () => { return rest.create(config, nobody, 'TestObject', { foo: 'qux' }); }) .then(() => { - return rest.find( - config, - nobody, - 'TestObject', - {}, - { limit: 0, count: 1 } - ); + return rest.find(config, nobody, 'TestObject', {}, { limit: 0, count: 1 }); }) .then(response => { expect(response.results.length).toEqual(0); @@ -420,38 +392,32 @@ describe('RestQuery.each', () => { beforeEach(() => { config = Config.get('test'); }); - it_id('3416c90b-ee2e-4bb5-9231-46cd181cd0a2')(it)( - 'should run each', - async () => { - const objects = []; - while (objects.length != 10) { - objects.push(new Parse.Object('Object', { value: objects.length })); - } - const config = Config.get('test'); - await Parse.Object.saveAll(objects); - const query = await RestQuery({ - method: RestQuery.Method.find, - config, - auth: auth.master(config), - className: 'Object', - restWhere: { value: { $gt: 2 } }, - restOptions: { limit: 2 }, - }); - const spy = spyOn(query, 'execute').and.callThrough(); - const classSpy = spyOn( - RestQuery._UnsafeRestQuery.prototype, - 'execute' - ).and.callThrough(); - const results = []; - await query.each(result => { - expect(result.value).toBeGreaterThan(2); - results.push(result); - }); - expect(spy.calls.count()).toBe(0); - expect(classSpy.calls.count()).toBe(4); - expect(results.length).toBe(7); + it_id('3416c90b-ee2e-4bb5-9231-46cd181cd0a2')(it)('should run each', async () => { + const objects = []; + while (objects.length != 10) { + objects.push(new Parse.Object('Object', { value: objects.length })); } - ); + const config = Config.get('test'); + await Parse.Object.saveAll(objects); + const query = await RestQuery({ + method: RestQuery.Method.find, + config, + auth: auth.master(config), + className: 'Object', + restWhere: { value: { $gt: 2 } }, + restOptions: { limit: 2 }, + }); + const spy = spyOn(query, 'execute').and.callThrough(); + const classSpy = spyOn(RestQuery._UnsafeRestQuery.prototype, 'execute').and.callThrough(); + const results = []; + await query.each(result => { + expect(result.value).toBeGreaterThan(2); + results.push(result); + }); + expect(spy.calls.count()).toBe(0); + expect(classSpy.calls.count()).toBe(4); + expect(results.length).toBe(7); + }); it_id('0fe22501-4b18-461e-b87d-82ceac4a496e')(it)( 'should work with query on relations', @@ -505,10 +471,7 @@ describe('RestQuery.each', () => { restOptions: { limit: 1 }, }); - const classSpy = spyOn( - RestQuery._UnsafeRestQuery.prototype, - 'execute' - ).and.callThrough(); + const classSpy = spyOn(RestQuery._UnsafeRestQuery.prototype, 'execute').and.callThrough(); const resultsOne = []; const resultsTwo = []; await queryOne.each(result => { @@ -538,16 +501,14 @@ describe('RestQuery.each', () => { return jsonObject; }); - rest - .create(config, nobody, 'TestObject2', { todelete: true, tokeep: true }) - .then(response => { - expect(response.response.toadd).toBeTruthy(); - expect(response.response.tokeep).toBeTruthy(); - expect(response.response.tobeaddbefore).toBeTruthy(); - expect(response.response.tobeaddbeforeandremoveafter).toBeUndefined(); - expect(response.response.todelete).toBeUndefined(); - done(); - }); + rest.create(config, nobody, 'TestObject2', { todelete: true, tokeep: true }).then(response => { + expect(response.response.toadd).toBeTruthy(); + expect(response.response.tokeep).toBeTruthy(); + expect(response.response.tobeaddbefore).toBeTruthy(); + expect(response.response.tobeaddbeforeandremoveafter).toBeUndefined(); + expect(response.response.todelete).toBeUndefined(); + done(); + }); }); it('test afterSave should not affect save response', async () => { diff --git a/spec/RevocableSessionsUpgrade.spec.js b/spec/RevocableSessionsUpgrade.spec.js index 8820444ba4..ca7b5a98d6 100644 --- a/spec/RevocableSessionsUpgrade.spec.js +++ b/spec/RevocableSessionsUpgrade.spec.js @@ -39,12 +39,7 @@ describe_only_db('mongo')('revocable sessions', () => { return schemaController.getOneSchema('_User', true); }) .then(schema => { - return config.database.adapter.find( - '_User', - schema, - { objectId: '1234567890' }, - {} - ); + return config.database.adapter.find('_User', schema, { objectId: '1234567890' }, {}); }) .then(results => { expect(results.length).toBe(1); diff --git a/spec/Schema.spec.js b/spec/Schema.spec.js index ec1f5b70df..2192678797 100644 --- a/spec/Schema.spec.js +++ b/spec/Schema.spec.js @@ -15,10 +15,7 @@ const hasAllPODobject = () => { obj.set('aObject', { k1: 'value', k2: true, k3: 5 }); obj.set('aArray', ['contents', true, 5]); obj.set('aGeoPoint', new Parse.GeoPoint({ latitude: 0, longitude: 0 })); - obj.set( - 'aFile', - new Parse.File('f.txt', { base64: 'V29ya2luZyBhdCBQYXJzZSBpcyBncmVhdCE=' }) - ); + obj.set('aFile', new Parse.File('f.txt', { base64: 'V29ya2luZyBhdCBQYXJzZSBpcyBncmVhdCE=' })); return obj; }; @@ -286,9 +283,7 @@ describe('SchemaController', () => { fail('Class permissions should have rejected this query.'); }, err => { - expect(err.message).toEqual( - 'Permission denied for action count on class Stuff.' - ); + expect(err.message).toEqual('Permission denied for action count on class Stuff.'); done(); } ) @@ -447,19 +442,9 @@ describe('SchemaController', () => { schema .validateObject('NewClass', { foo: 2 }) .then(() => schema.reloadData()) - .then(() => - schema.updateClass( - 'NewClass', - {}, - newLevelPermissions, - {}, - config.database - ) - ) + .then(() => schema.updateClass('NewClass', {}, newLevelPermissions, {}, config.database)) .then(actualSchema => { - expect( - dd(actualSchema.classLevelPermissions, newLevelPermissions) - ).toEqual(undefined); + expect(dd(actualSchema.classLevelPermissions, newLevelPermissions)).toEqual(undefined); done(); }) .catch(error => { @@ -551,14 +536,12 @@ describe('SchemaController', () => { it('refuses to create classes with invalid names', done => { config.database.loadSchema().then(schema => { - schema - .addClassIfNotExists('_InvalidName', { foo: { type: 'String' } }) - .catch(error => { - expect(error.message).toEqual( - 'Invalid classname: _InvalidName, classnames can only have alphanumeric characters and _, and must start with an alpha character ' - ); - done(); - }); + schema.addClassIfNotExists('_InvalidName', { foo: { type: 'String' } }).catch(error => { + expect(error.message).toEqual( + 'Invalid classname: _InvalidName, classnames can only have alphanumeric characters and _, and must start with an alpha character ' + ); + done(); + }); }); }); @@ -580,9 +563,7 @@ describe('SchemaController', () => { it('refuses to explicitly create the default fields for custom classes', done => { config.database .loadSchema() - .then(schema => - schema.addClassIfNotExists('NewClass', { objectId: { type: 'String' } }) - ) + .then(schema => schema.addClassIfNotExists('NewClass', { objectId: { type: 'String' } })) .catch(error => { expect(error.code).toEqual(136); expect(error.message).toEqual('field objectId cannot be added'); @@ -749,15 +730,7 @@ describe('SchemaController', () => { schema .validateObject('NewClass', {}) .then(() => schema.reloadData()) - .then(() => - schema.updateClass( - 'NewClass', - {}, - levelPermissions, - {}, - config.database - ) - ) + .then(() => schema.updateClass('NewClass', {}, levelPermissions, {}, config.database)) .then(done.fail) .catch(error => { expect(error.code).toEqual(Parse.Error.INVALID_JSON); @@ -786,15 +759,7 @@ describe('SchemaController', () => { schema .validateObject('NewClass', {}) .then(() => schema.reloadData()) - .then(() => - schema.updateClass( - 'NewClass', - {}, - levelPermissions, - {}, - config.database - ) - ) + .then(() => schema.updateClass('NewClass', {}, levelPermissions, {}, config.database)) .then(done.fail) .catch(error => { expect(error.code).toEqual(Parse.Error.INVALID_JSON); @@ -1067,9 +1032,7 @@ describe('SchemaController', () => { it('refuses to delete invalid fields', done => { config.database .loadSchema() - .then(schema => - schema.deleteField('invalid field name', 'ValidClassName') - ) + .then(schema => schema.deleteField('invalid field name', 'ValidClassName')) .catch(error => { expect(error.code).toEqual(Parse.Error.INVALID_KEY_NAME); done(); @@ -1105,9 +1068,7 @@ describe('SchemaController', () => { .then(schema => schema.deleteField('missingField', 'HasAllPOD')) .catch(error => { expect(error.code).toEqual(255); - expect(error.message).toEqual( - 'Field missingField does not exist, cannot delete.' - ); + expect(error.message).toEqual('Field missingField does not exist, cannot delete.'); done(); }); }); @@ -1123,35 +1084,19 @@ describe('SchemaController', () => { relation.add(obj1); return obj2.save(); }) - .then(() => - config.database.collectionExists( - '_Join:aRelation:HasPointersAndRelations' - ) - ) + .then(() => config.database.collectionExists('_Join:aRelation:HasPointersAndRelations')) .then(exists => { if (!exists) { fail('Relation collection ' + 'should exist after save.'); } }) .then(() => config.database.loadSchema()) - .then(schema => - schema.deleteField( - 'aRelation', - 'HasPointersAndRelations', - config.database - ) - ) - .then(() => - config.database.collectionExists( - '_Join:aRelation:HasPointersAndRelations' - ) - ) + .then(schema => schema.deleteField('aRelation', 'HasPointersAndRelations', config.database)) + .then(() => config.database.collectionExists('_Join:aRelation:HasPointersAndRelations')) .then( exists => { if (exists) { - fail( - 'Relation collection should not exist after deleting relation field.' - ); + fail('Relation collection should not exist after deleting relation field.'); } done(); }, @@ -1197,9 +1142,7 @@ describe('SchemaController', () => { }; expect(dd(actualSchema, expectedSchema)).toEqual(undefined); }) - .then(() => - config.database.collectionExists('_Join:relationField:NewClass') - ) + .then(() => config.database.collectionExists('_Join:relationField:NewClass')) .then(exist => { on_db( 'postgres', @@ -1212,9 +1155,7 @@ describe('SchemaController', () => { } ); }) - .then(() => - schema.deleteField('relationField', 'NewClass', config.database) - ) + .then(() => schema.deleteField('relationField', 'NewClass', config.database)) .then(() => schema.reloadData()) .then(() => { const expectedSchema = { @@ -1223,9 +1164,7 @@ describe('SchemaController', () => { createdAt: { type: 'Date' }, ACL: { type: 'ACL' }, }; - expect(dd(schema.schemaData.NewClass.fields, expectedSchema)).toEqual( - undefined - ); + expect(dd(schema.schemaData.NewClass.fields, expectedSchema)).toEqual(undefined); }) .then(done) .catch(done.fail); @@ -1238,9 +1177,7 @@ describe('SchemaController', () => { const obj2 = hasAllPODobject(); Parse.Object.saveAll([obj1, obj2]) .then(() => config.database.loadSchema()) - .then(schema => - schema.deleteField('aString', 'HasAllPOD', config.database) - ) + .then(schema => schema.deleteField('aString', 'HasAllPOD', config.database)) .then(() => new Parse.Query('HasAllPOD').get(obj1.id)) .then(obj1Reloaded => { expect(obj1Reloaded.get('aString')).toEqual(undefined); @@ -1248,10 +1185,7 @@ describe('SchemaController', () => { obj1Reloaded .save() .then(obj1reloadedAgain => { - expect(obj1reloadedAgain.get('aString')).toEqual([ - 'not a string', - 'this time', - ]); + expect(obj1reloadedAgain.get('aString')).toEqual(['not a string', 'this time']); return new Parse.Query('HasAllPOD').get(obj2.id); }) .then(obj2reloaded => { @@ -1278,9 +1212,7 @@ describe('SchemaController', () => { expect(obj1.get('aPointer').id).toEqual(obj1.id); }) .then(() => config.database.loadSchema()) - .then(schema => - schema.deleteField('aPointer', 'NewClass', config.database) - ) + .then(schema => schema.deleteField('aPointer', 'NewClass', config.database)) .then(() => new Parse.Query('NewClass').get(obj1.id)) .then(obj1 => { expect(obj1.get('aPointer')).toEqual(undefined); @@ -1437,29 +1369,19 @@ describe('SchemaController', () => { it('properly handles volatile _Schemas', async done => { await reconfigureServer(); function validateSchemaStructure(schema) { - expect(Object.prototype.hasOwnProperty.call(schema, 'className')).toBe( - true - ); + expect(Object.prototype.hasOwnProperty.call(schema, 'className')).toBe(true); expect(Object.prototype.hasOwnProperty.call(schema, 'fields')).toBe(true); - expect( - Object.prototype.hasOwnProperty.call(schema, 'classLevelPermissions') - ).toBe(true); + expect(Object.prototype.hasOwnProperty.call(schema, 'classLevelPermissions')).toBe(true); } function validateSchemaDataStructure(schemaData) { Object.keys(schemaData).forEach(className => { const schema = schemaData[className]; // Hooks has className... if (className != '_Hooks') { - expect( - Object.prototype.hasOwnProperty.call(schema, 'className') - ).toBe(false); + expect(Object.prototype.hasOwnProperty.call(schema, 'className')).toBe(false); } - expect(Object.prototype.hasOwnProperty.call(schema, 'fields')).toBe( - false - ); - expect( - Object.prototype.hasOwnProperty.call(schema, 'classLevelPermissions') - ).toBe(false); + expect(Object.prototype.hasOwnProperty.call(schema, 'fields')).toBe(false); + expect(Object.prototype.hasOwnProperty.call(schema, 'classLevelPermissions')).toBe(false); }); } let schema; @@ -1484,11 +1406,7 @@ describe('SchemaController', () => { it('should not throw on null field types', async () => { const schema = await config.database.loadSchema(); - const result = await schema.enforceFieldExists( - 'NewClass', - 'fieldName', - null - ); + const result = await schema.enforceFieldExists('NewClass', 'fieldName', null); expect(result).toBeUndefined(); }); @@ -1544,9 +1462,7 @@ describe('Class Level Permissions for requiredAuth', () => { done(); }, e => { - expect(e.message).toEqual( - 'Permission denied, user needs to be authenticated.' - ); + expect(e.message).toEqual('Permission denied, user needs to be authenticated.'); done(); } ); @@ -1645,9 +1561,7 @@ describe('Class Level Permissions for requiredAuth', () => { done(); }, e => { - expect(e.message).toEqual( - 'Permission denied, user needs to be authenticated.' - ); + expect(e.message).toEqual('Permission denied, user needs to be authenticated.'); done(); } ); @@ -1735,9 +1649,7 @@ describe('Class Level Permissions for requiredAuth', () => { done(); }, e => { - expect(e.message).toEqual( - 'Permission denied, user needs to be authenticated.' - ); + expect(e.message).toEqual('Permission denied, user needs to be authenticated.'); done(); } ); @@ -1782,9 +1694,7 @@ describe('Class Level Permissions for requiredAuth', () => { done(); }, e => { - expect(e.message).toEqual( - 'Permission denied, user needs to be authenticated.' - ); + expect(e.message).toEqual('Permission denied, user needs to be authenticated.'); done(); } ); diff --git a/spec/SchemaPerformance.spec.js b/spec/SchemaPerformance.spec.js index 6d6f3571c0..aa4944afa9 100644 --- a/spec/SchemaPerformance.spec.js +++ b/spec/SchemaPerformance.spec.js @@ -209,49 +209,41 @@ describe('Schema Performance', function () { expect(getAllSpy.calls.count()).toBe(2); }); - it_id('9dd70965-b683-4cb8-b43a-44c1f4def9f4')(it)( - 'does reload with schemaCacheTtl', - async () => { - const databaseURI = - process.env.PARSE_SERVER_TEST_DB === 'postgres' - ? process.env.PARSE_SERVER_TEST_DATABASE_URI - : 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase'; - await reconfigureServer({ - databaseAdapter: undefined, - databaseURI, - silent: false, - databaseOptions: { schemaCacheTtl: 1000 }, - }); - const SchemaController = - require('../lib/Controllers/SchemaController').SchemaController; - const spy = spyOn( - SchemaController.prototype, - 'reloadData' - ).and.callThrough(); - Object.defineProperty(spy, 'reloadCalls', { - get: () => - spy.calls.all().filter(call => call.args[0].clearCache).length, - }); + it_id('9dd70965-b683-4cb8-b43a-44c1f4def9f4')(it)('does reload with schemaCacheTtl', async () => { + const databaseURI = + process.env.PARSE_SERVER_TEST_DB === 'postgres' + ? process.env.PARSE_SERVER_TEST_DATABASE_URI + : 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase'; + await reconfigureServer({ + databaseAdapter: undefined, + databaseURI, + silent: false, + databaseOptions: { schemaCacheTtl: 1000 }, + }); + const SchemaController = require('../lib/Controllers/SchemaController').SchemaController; + const spy = spyOn(SchemaController.prototype, 'reloadData').and.callThrough(); + Object.defineProperty(spy, 'reloadCalls', { + get: () => spy.calls.all().filter(call => call.args[0].clearCache).length, + }); - const object = new TestObject(); - object.set('foo', 'bar'); - await object.save(); + const object = new TestObject(); + object.set('foo', 'bar'); + await object.save(); - spy.calls.reset(); + spy.calls.reset(); - object.set('foo', 'bar'); - await object.save(); + object.set('foo', 'bar'); + await object.save(); - expect(spy.reloadCalls).toBe(0); + expect(spy.reloadCalls).toBe(0); - await new Promise(resolve => setTimeout(resolve, 1100)); + await new Promise(resolve => setTimeout(resolve, 1100)); - object.set('foo', 'bar'); - await object.save(); + object.set('foo', 'bar'); + await object.save(); - expect(spy.reloadCalls).toBe(1); - } - ); + expect(spy.reloadCalls).toBe(1); + }); it_id('b0ae21f2-c947-48ed-a0db-e8900d45a4c8')(it)( 'cannot set invalid databaseOptions', diff --git a/spec/SecurityCheck.spec.js b/spec/SecurityCheck.spec.js index 9c70c86170..b51a1e1179 100644 --- a/spec/SecurityCheck.spec.js +++ b/spec/SecurityCheck.spec.js @@ -105,9 +105,7 @@ describe('Security Check', () => { { enableCheckLog: [] }, ]; for (const option of options) { - await expectAsync( - reconfigureServerWithSecurityConfig(option) - ).toBeRejected(); + await expectAsync(reconfigureServerWithSecurityConfig(option)).toBeRejected(); } }); }); @@ -235,17 +233,13 @@ describe('Security Check', () => { it('throws if subclassed incorrectly', async () => { class InvalidGroup1 extends CheckGroup {} - expect((() => new InvalidGroup1()).bind()).toThrow( - 'Check group has no name.' - ); + expect((() => new InvalidGroup1()).bind()).toThrow('Check group has no name.'); class InvalidGroup2 extends CheckGroup { setName() { return groupName; } } - expect((() => new InvalidGroup2()).bind()).toThrow( - 'Check group has no checks.' - ); + expect((() => new InvalidGroup2()).bind()).toThrow('Check group has no checks.'); }); it('runs checks', async () => { @@ -339,9 +333,7 @@ describe('Security Check', () => { const checkGroups = [Group]; const runner = new CheckRunner({ checkGroups, enableCheckLog: true }); const report = await runner.run(); - const titles = report.report.groups.flatMap(group => - group.checks.map(check => check.title) - ); + const titles = report.report.groups.flatMap(group => group.checks.map(check => check.title)); expect(titles.length).toBe(2); for (const title of titles) { diff --git a/spec/SecurityCheckGroups.spec.js b/spec/SecurityCheckGroups.spec.js index 41a4fa2e82..21409a78c1 100644 --- a/spec/SecurityCheckGroups.spec.js +++ b/spec/SecurityCheckGroups.spec.js @@ -68,8 +68,7 @@ describe('Security Check Groups', () => { it('checks succeed correctly', async () => { const config = Config.get(Parse.applicationId); const uri = config.database.adapter._uri; - config.database.adapter._uri = - 'protocol://user:aMoreSecur3Passwor7!@example.com'; + config.database.adapter._uri = 'protocol://user:aMoreSecur3Passwor7!@example.com'; const group = new CheckGroupDatabase(); await group.run(); expect(group.checks()[0].checkState()).toBe(CheckState.success); diff --git a/spec/SessionTokenCache.spec.js b/spec/SessionTokenCache.spec.js index b61b5559be..6b3c83df62 100644 --- a/spec/SessionTokenCache.spec.js +++ b/spec/SessionTokenCache.spec.js @@ -1,5 +1,4 @@ -const SessionTokenCache = - require('../lib/LiveQuery/SessionTokenCache').SessionTokenCache; +const SessionTokenCache = require('../lib/LiveQuery/SessionTokenCache').SessionTokenCache; describe('SessionTokenCache', function () { beforeEach(function (done) { diff --git a/spec/Subscription.spec.js b/spec/Subscription.spec.js index 854086f1c9..9c7aa4c550 100644 --- a/spec/Subscription.spec.js +++ b/spec/Subscription.spec.js @@ -7,11 +7,7 @@ describe('Subscription', function () { }); it('can be initialized', function () { - const subscription = new Subscription( - 'className', - { key: 'value' }, - 'hash' - ); + const subscription = new Subscription('className', { key: 'value' }, 'hash'); expect(subscription.className).toBe('className'); expect(subscription.query).toEqual({ key: 'value' }); @@ -20,32 +16,20 @@ describe('Subscription', function () { }); it('can check it has subscribing clients', function () { - const subscription = new Subscription( - 'className', - { key: 'value' }, - 'hash' - ); + const subscription = new Subscription('className', { key: 'value' }, 'hash'); expect(subscription.hasSubscribingClient()).toBe(false); }); it('can check it does not have subscribing clients', function () { - const subscription = new Subscription( - 'className', - { key: 'value' }, - 'hash' - ); + const subscription = new Subscription('className', { key: 'value' }, 'hash'); subscription.addClientSubscription(1, 1); expect(subscription.hasSubscribingClient()).toBe(true); }); it('can add one request for one client', function () { - const subscription = new Subscription( - 'className', - { key: 'value' }, - 'hash' - ); + const subscription = new Subscription('className', { key: 'value' }, 'hash'); subscription.addClientSubscription(1, 1); expect(subscription.clientRequestIds.size).toBe(1); @@ -53,11 +37,7 @@ describe('Subscription', function () { }); it('can add requests for one client', function () { - const subscription = new Subscription( - 'className', - { key: 'value' }, - 'hash' - ); + const subscription = new Subscription('className', { key: 'value' }, 'hash'); subscription.addClientSubscription(1, 1); subscription.addClientSubscription(1, 2); @@ -66,11 +46,7 @@ describe('Subscription', function () { }); it('can add requests for clients', function () { - const subscription = new Subscription( - 'className', - { key: 'value' }, - 'hash' - ); + const subscription = new Subscription('className', { key: 'value' }, 'hash'); subscription.addClientSubscription(1, 1); subscription.addClientSubscription(1, 2); subscription.addClientSubscription(2, 2); @@ -82,22 +58,14 @@ describe('Subscription', function () { }); it('can delete requests for nonexistent client', function () { - const subscription = new Subscription( - 'className', - { key: 'value' }, - 'hash' - ); + const subscription = new Subscription('className', { key: 'value' }, 'hash'); subscription.deleteClientSubscription(1, 1); expect(logger.error).toHaveBeenCalled(); }); it('can delete nonexistent request for one client', function () { - const subscription = new Subscription( - 'className', - { key: 'value' }, - 'hash' - ); + const subscription = new Subscription('className', { key: 'value' }, 'hash'); subscription.addClientSubscription(1, 1); subscription.deleteClientSubscription(1, 2); @@ -107,11 +75,7 @@ describe('Subscription', function () { }); it('can delete some requests for one client', function () { - const subscription = new Subscription( - 'className', - { key: 'value' }, - 'hash' - ); + const subscription = new Subscription('className', { key: 'value' }, 'hash'); subscription.addClientSubscription(1, 1); subscription.addClientSubscription(1, 2); subscription.deleteClientSubscription(1, 2); @@ -122,11 +86,7 @@ describe('Subscription', function () { }); it('can delete all requests for one client', function () { - const subscription = new Subscription( - 'className', - { key: 'value' }, - 'hash' - ); + const subscription = new Subscription('className', { key: 'value' }, 'hash'); subscription.addClientSubscription(1, 1); subscription.addClientSubscription(1, 2); subscription.deleteClientSubscription(1, 1); @@ -137,11 +97,7 @@ describe('Subscription', function () { }); it('can delete requests for multiple clients', function () { - const subscription = new Subscription( - 'className', - { key: 'value' }, - 'hash' - ); + const subscription = new Subscription('className', { key: 'value' }, 'hash'); subscription.addClientSubscription(1, 1); subscription.addClientSubscription(1, 2); subscription.addClientSubscription(2, 1); diff --git a/spec/UserController.spec.js b/spec/UserController.spec.js index 5eab88cccf..7d044722de 100644 --- a/spec/UserController.spec.js +++ b/spec/UserController.spec.js @@ -6,51 +6,48 @@ const { resolvingPromise } = require('../lib/TestUtils'); describe('UserController', () => { describe('sendVerificationEmail', () => { describe('parseFrameURL not provided', () => { - it_id('61338330-eca7-4c33-8816-7ff05966f43b')(it)( - 'uses publicServerURL', - async () => { - await reconfigureServer({ - publicServerURL: 'http://www.example.com', - customPages: { - parseFrameURL: undefined, - }, - verifyUserEmails: true, - emailAdapter, - appName: 'test', - }); + it_id('61338330-eca7-4c33-8816-7ff05966f43b')(it)('uses publicServerURL', async () => { + await reconfigureServer({ + publicServerURL: 'http://www.example.com', + customPages: { + parseFrameURL: undefined, + }, + verifyUserEmails: true, + emailAdapter, + appName: 'test', + }); - let emailOptions; - const sendPromise = resolvingPromise(); - emailAdapter.sendVerificationEmail = options => { - emailOptions = options; - sendPromise.resolve(); - }; + let emailOptions; + const sendPromise = resolvingPromise(); + emailAdapter.sendVerificationEmail = options => { + emailOptions = options; + sendPromise.resolve(); + }; - const username = 'verificationUser'; - const user = new Parse.User(); - user.setUsername(username); - user.setPassword('pass'); - user.setEmail('verification@example.com'); - await user.signUp(); - await sendPromise; + const username = 'verificationUser'; + const user = new Parse.User(); + user.setUsername(username); + user.setPassword('pass'); + user.setEmail('verification@example.com'); + await user.signUp(); + await sendPromise; - const config = Config.get('test'); - const rawUser = await config.database.find( - '_User', - { username }, - {}, - Auth.maintenance(config) - ); - const rawUsername = rawUser[0].username; - const rawToken = rawUser[0]._email_verify_token; - expect(rawToken).toBeDefined(); - expect(rawUsername).toBe(username); + const config = Config.get('test'); + const rawUser = await config.database.find( + '_User', + { username }, + {}, + Auth.maintenance(config) + ); + const rawUsername = rawUser[0].username; + const rawToken = rawUser[0]._email_verify_token; + expect(rawToken).toBeDefined(); + expect(rawUsername).toBe(username); - expect(emailOptions.link).toEqual( - `http://www.example.com/apps/test/verify_email?token=${rawToken}` - ); - } - ); + expect(emailOptions.link).toEqual( + `http://www.example.com/apps/test/verify_email?token=${rawToken}` + ); + }); }); describe('parseFrameURL provided', () => { diff --git a/spec/UserPII.spec.js b/spec/UserPII.spec.js index 6aee1a14e3..a94e3ca469 100644 --- a/spec/UserPII.spec.js +++ b/spec/UserPII.spec.js @@ -18,12 +18,7 @@ describe('Personally Identifiable Information', () => { user = await Parse.User.logIn(user.get('username'), 'abc'); const acl = new Parse.ACL(); acl.setPublicReadAccess(true); - await user - .set('email', EMAIL) - .set('zip', ZIP) - .set('ssn', SSN) - .setACL(acl) - .save(); + await user.set('email', EMAIL).set('zip', ZIP).set('ssn', SSN).setACL(acl).save(); done(); }); @@ -91,14 +86,12 @@ describe('Personally Identifiable Information', () => { it('should get PII via API with Find using master key', done => { return Parse.User.logOut().then(() => - new Parse.Query(Parse.User) - .first({ useMasterKey: true }) - .then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(EMAIL); - expect(fetchedUser.get('zip')).toBe(ZIP); - expect(fetchedUser.get('ssn')).toBe(SSN); - done(); - }) + new Parse.Query(Parse.User).first({ useMasterKey: true }).then(fetchedUser => { + expect(fetchedUser.get('email')).toBe(EMAIL); + expect(fetchedUser.get('zip')).toBe(ZIP); + expect(fetchedUser.get('ssn')).toBe(SSN); + done(); + }) ); }); @@ -124,14 +117,12 @@ describe('Personally Identifiable Information', () => { it('should get PII via API with Get using master key', done => { return Parse.User.logOut().then(() => - new Parse.Query(Parse.User) - .get(user.id, { useMasterKey: true }) - .then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(EMAIL); - expect(fetchedUser.get('zip')).toBe(ZIP); - expect(fetchedUser.get('ssn')).toBe(SSN); - done(); - }) + new Parse.Query(Parse.User).get(user.id, { useMasterKey: true }).then(fetchedUser => { + expect(fetchedUser.get('email')).toBe(EMAIL); + expect(fetchedUser.get('zip')).toBe(ZIP); + expect(fetchedUser.get('ssn')).toBe(SSN); + done(); + }) ); }); @@ -324,14 +315,12 @@ describe('Personally Identifiable Information', () => { it('should get PII via API with Find using master key', done => { Parse.User.logOut().then(() => - new Parse.Query(Parse.User) - .first({ useMasterKey: true }) - .then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(EMAIL); - expect(fetchedUser.get('zip')).toBe(ZIP); - expect(fetchedUser.get('ssn')).toBe(SSN); - done(); - }) + new Parse.Query(Parse.User).first({ useMasterKey: true }).then(fetchedUser => { + expect(fetchedUser.get('email')).toBe(EMAIL); + expect(fetchedUser.get('zip')).toBe(ZIP); + expect(fetchedUser.get('ssn')).toBe(SSN); + done(); + }) ); }); @@ -357,14 +346,12 @@ describe('Personally Identifiable Information', () => { it('should get PII via API with Get using master key', done => { Parse.User.logOut().then(() => - new Parse.Query(Parse.User) - .get(user.id, { useMasterKey: true }) - .then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(EMAIL); - expect(fetchedUser.get('zip')).toBe(ZIP); - expect(fetchedUser.get('ssn')).toBe(SSN); - done(); - }) + new Parse.Query(Parse.User).get(user.id, { useMasterKey: true }).then(fetchedUser => { + expect(fetchedUser.get('email')).toBe(EMAIL); + expect(fetchedUser.get('zip')).toBe(ZIP); + expect(fetchedUser.get('ssn')).toBe(SSN); + done(); + }) ); }); @@ -501,17 +488,11 @@ describe('Personally Identifiable Information', () => { let adminUser; beforeEach(async done => { - const adminRole = await new Parse.Role( - 'Administrator', - new Parse.ACL() - ).save(null, { + const adminRole = await new Parse.Role('Administrator', new Parse.ACL()).save(null, { useMasterKey: true, }); - const managementRole = new Parse.Role( - 'managementOf_user' + user.id, - new Parse.ACL(user) - ); + const managementRole = new Parse.Role('managementOf_user' + user.id, new Parse.ACL(user)); managementRole.getRoles().add(adminRole); await managementRole.save(null, { useMasterKey: true }); @@ -521,10 +502,7 @@ describe('Personally Identifiable Information', () => { adminUser = await Parse.User.signUp('administrator', 'secure'); adminUser = await Parse.User.logIn(adminUser.get('username'), 'secure'); - await adminRole - .getUsers() - .add(adminUser) - .save(null, { useMasterKey: true }); + await adminRole.getUsers().add(adminUser).save(null, { useMasterKey: true }); done(); }); @@ -786,14 +764,12 @@ describe('Personally Identifiable Information', () => { it('should get PII via API with Find using master key', done => { Parse.User.logOut().then(() => - new Parse.Query(Parse.User) - .first({ useMasterKey: true }) - .then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(EMAIL); - expect(fetchedUser.get('zip')).toBe(ZIP); - expect(fetchedUser.get('ssn')).toBe(SSN); - done(); - }) + new Parse.Query(Parse.User).first({ useMasterKey: true }).then(fetchedUser => { + expect(fetchedUser.get('email')).toBe(EMAIL); + expect(fetchedUser.get('zip')).toBe(ZIP); + expect(fetchedUser.get('ssn')).toBe(SSN); + done(); + }) ); }); @@ -819,14 +795,12 @@ describe('Personally Identifiable Information', () => { it('should get PII via API with Get using master key', done => { Parse.User.logOut().then(() => - new Parse.Query(Parse.User) - .get(user.id, { useMasterKey: true }) - .then(fetchedUser => { - expect(fetchedUser.get('email')).toBe(EMAIL); - expect(fetchedUser.get('zip')).toBe(ZIP); - expect(fetchedUser.get('ssn')).toBe(SSN); - done(); - }) + new Parse.Query(Parse.User).get(user.id, { useMasterKey: true }).then(fetchedUser => { + expect(fetchedUser.get('email')).toBe(EMAIL); + expect(fetchedUser.get('zip')).toBe(ZIP); + expect(fetchedUser.get('ssn')).toBe(SSN); + done(); + }) ); }); @@ -966,17 +940,11 @@ describe('Personally Identifiable Information', () => { let adminUser; beforeEach(async done => { - const adminRole = await new Parse.Role( - 'Administrator', - new Parse.ACL() - ).save(null, { + const adminRole = await new Parse.Role('Administrator', new Parse.ACL()).save(null, { useMasterKey: true, }); - const managementRole = new Parse.Role( - 'managementOf_user' + user.id, - new Parse.ACL(user) - ); + const managementRole = new Parse.Role('managementOf_user' + user.id, new Parse.ACL(user)); managementRole.getRoles().add(adminRole); await managementRole.save(null, { useMasterKey: true }); @@ -986,10 +954,7 @@ describe('Personally Identifiable Information', () => { adminUser = await Parse.User.signUp('administrator', 'secure'); adminUser = await Parse.User.logIn(adminUser.get('username'), 'secure'); - await adminRole - .getUsers() - .add(adminUser) - .save(null, { useMasterKey: true }); + await adminRole.getUsers().add(adminUser).save(null, { useMasterKey: true }); done(); }); @@ -1135,11 +1100,7 @@ describe('Personally Identifiable Information', () => { .then(loggedInUser => (anotherUser = loggedInUser)) .then(() => Parse.User.logIn(anotherUser.get('username'), 'abc')) .then(() => - anotherUser - .set('email', ANOTHER_EMAIL) - .set('zip', ZIP) - .set('ssn', SSN) - .save() + anotherUser.set('email', ANOTHER_EMAIL).set('zip', ZIP).set('ssn', SSN).save() ) .then(() => done()); }); @@ -1174,9 +1135,7 @@ describe('Personally Identifiable Information', () => { new Parse.Query(Parse.User) .find() .then(fetchedUsers => { - const notCurrentUser = fetchedUsers.find( - u => u.id !== anotherUser.id - ); + const notCurrentUser = fetchedUsers.find(u => u.id !== anotherUser.id); expect(notCurrentUser.get('email')).toBe(undefined); expect(notCurrentUser.get('zip')).toBe(undefined); expect(notCurrentUser.get('ssn')).toBe(undefined); @@ -1189,9 +1148,7 @@ describe('Personally Identifiable Information', () => { new Parse.Query(Parse.User) .find() .then(fetchedUsers => { - const currentUser = fetchedUsers.find( - u => u.id === anotherUser.id - ); + const currentUser = fetchedUsers.find(u => u.id === anotherUser.id); expect(currentUser.get('email')).toBe(ANOTHER_EMAIL); expect(currentUser.get('zip')).toBe(ZIP); expect(currentUser.get('ssn')).toBe(SSN); diff --git a/spec/ValidationAndPasswordsReset.spec.js b/spec/ValidationAndPasswordsReset.spec.js index 6df0437b5c..14a4a92e2f 100644 --- a/spec/ValidationAndPasswordsReset.spec.js +++ b/spec/ValidationAndPasswordsReset.spec.js @@ -23,9 +23,7 @@ describe('Custom Pages, Email Verification, Password Reset', () => { expect(config.verifyEmailSuccessURL).toEqual('myVerifyEmailSuccess'); expect(config.choosePasswordURL).toEqual('myChoosePassword'); expect(config.passwordResetSuccessURL).toEqual('myPasswordResetSuccess'); - expect(config.parseFrameURL).toEqual( - 'http://example.com/handle-parse-iframe' - ); + expect(config.parseFrameURL).toEqual('http://example.com/handle-parse-iframe'); expect(config.verifyEmailURL).toEqual( 'https://my.public.server.com/1/apps/test/verify_email' ); @@ -164,59 +162,52 @@ describe('Custom Pages, Email Verification, Password Reset', () => { }, 200); }); - it_id('33d31119-c724-4f5d-83ec-f56815d23df3')(it)( - 'does send with a simple adapter', - done => { - let calls = 0; - const emailAdapter = { - sendMail: function (options) { - expect(options.to).toBe('testSendSimpleAdapter@parse.com'); - if (calls == 0) { - expect(options.subject).toEqual( - 'Please verify your e-mail for My Cool App' - ); - expect(options.text.match(/verify_email/)).not.toBe(null); - } else if (calls == 1) { - expect(options.subject).toEqual('Password Reset for My Cool App'); - expect(options.text.match(/request_password_reset/)).not.toBe(null); - } - calls++; - return Promise.resolve(); - }, - }; - reconfigureServer({ - appName: 'My Cool App', - verifyUserEmails: true, - emailAdapter: emailAdapter, - publicServerURL: 'http://localhost:8378/1', - }).then(async () => { - const user = new Parse.User(); - user.setPassword('asdf'); - user.setUsername('zxcv'); - user.set('email', 'testSendSimpleAdapter@parse.com'); - await user.signUp(); - await jasmine.timeout(); - expect(calls).toBe(1); - user - .fetch() - .then(user => { - return user.save(); - }) - .then(() => { - return Parse.User.requestPasswordReset( - 'testSendSimpleAdapter@parse.com' - ).catch(() => { - fail('Should not fail requesting a password'); - done(); - }); - }) - .then(() => { - expect(calls).toBe(2); + it_id('33d31119-c724-4f5d-83ec-f56815d23df3')(it)('does send with a simple adapter', done => { + let calls = 0; + const emailAdapter = { + sendMail: function (options) { + expect(options.to).toBe('testSendSimpleAdapter@parse.com'); + if (calls == 0) { + expect(options.subject).toEqual('Please verify your e-mail for My Cool App'); + expect(options.text.match(/verify_email/)).not.toBe(null); + } else if (calls == 1) { + expect(options.subject).toEqual('Password Reset for My Cool App'); + expect(options.text.match(/request_password_reset/)).not.toBe(null); + } + calls++; + return Promise.resolve(); + }, + }; + reconfigureServer({ + appName: 'My Cool App', + verifyUserEmails: true, + emailAdapter: emailAdapter, + publicServerURL: 'http://localhost:8378/1', + }).then(async () => { + const user = new Parse.User(); + user.setPassword('asdf'); + user.setUsername('zxcv'); + user.set('email', 'testSendSimpleAdapter@parse.com'); + await user.signUp(); + await jasmine.timeout(); + expect(calls).toBe(1); + user + .fetch() + .then(user => { + return user.save(); + }) + .then(() => { + return Parse.User.requestPasswordReset('testSendSimpleAdapter@parse.com').catch(() => { + fail('Should not fail requesting a password'); done(); }); - }); - } - ); + }) + .then(() => { + expect(calls).toBe(2); + done(); + }); + }); + }); it('prevents user from login if email is not verified but preventLoginWithUnverifiedEmail is set to true', done => { reconfigureServer({ @@ -299,10 +290,7 @@ describe('Custom Pages, Email Verification, Password Reset', () => { return true; }, }; - const verifyUserEmailsSpy = spyOn( - verifyUserEmails, - 'method' - ).and.callThrough(); + const verifyUserEmailsSpy = spyOn(verifyUserEmails, 'method').and.callThrough(); await reconfigureServer({ appName: 'test', publicServerURL: 'http://localhost:1337/1', @@ -424,10 +412,7 @@ describe('Custom Pages, Email Verification, Password Reset', () => { newUser.setUsername('zxcv'); newUser.set('email', 'test@example.com'); await expectAsync(newUser.signUp()).toBeRejectedWith( - new Parse.Error( - Parse.Error.EMAIL_NOT_FOUND, - 'User email is not verified.' - ) + new Parse.Error(Parse.Error.EMAIL_NOT_FOUND, 'User email is not verified.') ); const user = await new Parse.Query(Parse.User).first({ useMasterKey: true, @@ -454,9 +439,7 @@ describe('Custom Pages, Email Verification, Password Reset', () => { user.set('email', 'testInvalidConfig@parse.com'); user .signUp(null) - .then(() => - Parse.User.requestPasswordReset('testInvalidConfig@parse.com') - ) + .then(() => Parse.User.requestPasswordReset('testInvalidConfig@parse.com')) .then( () => { fail('sending password reset email should not have succeeded'); @@ -492,9 +475,7 @@ describe('Custom Pages, Email Verification, Password Reset', () => { user.set('email', 'testInvalidConfig@parse.com'); user .signUp(null) - .then(() => - Parse.User.requestPasswordReset('testInvalidConfig@parse.com') - ) + .then(() => Parse.User.requestPasswordReset('testInvalidConfig@parse.com')) .then( () => { fail('sending password reset email should not have succeeded'); @@ -527,9 +508,7 @@ describe('Custom Pages, Email Verification, Password Reset', () => { user.set('email', 'testInvalidConfig@parse.com'); user .signUp(null) - .then(() => - Parse.User.requestPasswordReset('testInvalidConfig@parse.com') - ) + .then(() => Parse.User.requestPasswordReset('testInvalidConfig@parse.com')) .then( () => { fail('sending password reset email should not have succeeded'); @@ -566,9 +545,7 @@ describe('Custom Pages, Email Verification, Password Reset', () => { user.set('email', 'testInvalidConfig@parse.com'); user .signUp(null) - .then(() => - Parse.User.requestPasswordReset('testInvalidConfig@parse.com') - ) + .then(() => Parse.User.requestPasswordReset('testInvalidConfig@parse.com')) .then( () => { done(); @@ -610,9 +587,7 @@ describe('Custom Pages, Email Verification, Password Reset', () => { user.setUsername('testValidConfig@parse.com'); user .signUp(null) - .then(() => - Parse.User.requestPasswordReset('testValidConfig@parse.com') - ) + .then(() => Parse.User.requestPasswordReset('testValidConfig@parse.com')) .then( () => { expect(adapter.sendMail).toHaveBeenCalled(); @@ -958,12 +933,7 @@ describe('Custom Pages, Email Verification, Password Reset', () => { function () { const config = Config.get('test'); config.database.adapter - .find( - '_User', - { fields: {} }, - { username: 'zxcv' }, - { limit: 1 } - ) + .find('_User', { fields: {} }, { username: 'zxcv' }, { limit: 1 }) .then(results => { // _perishable_token should be unset after reset password expect(results.length).toEqual(1); @@ -1289,13 +1259,8 @@ describe('Custom Pages, Email Verification, Password Reset', () => { }, }); - await expectAsync( - Parse.User.requestPasswordReset('test@example.com') - ).toBeRejectedWith( - new Parse.Error( - Parse.Error.OBJECT_NOT_FOUND, - 'A user with that email does not exist.' - ) + await expectAsync(Parse.User.requestPasswordReset('test@example.com')).toBeRejectedWith( + new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'A user with that email does not exist.') ); }); @@ -1315,9 +1280,7 @@ describe('Custom Pages, Email Verification, Password Reset', () => { resetPasswordSuccessOnInvalidEmail: value, }, }) - ).toBeRejectedWith( - 'resetPasswordSuccessOnInvalidEmail must be a boolean value' - ); + ).toBeRejectedWith('resetPasswordSuccessOnInvalidEmail must be a boolean value'); } }); }); diff --git a/spec/VerifyUserPassword.spec.js b/spec/VerifyUserPassword.spec.js index 659dedcba2..3d15a25e15 100644 --- a/spec/VerifyUserPassword.spec.js +++ b/spec/VerifyUserPassword.spec.js @@ -4,9 +4,7 @@ const request = require('../lib/request'); const MockEmailAdapterWithOptions = require('./support/MockEmailAdapterWithOptions'); const verifyPassword = function (login, password, isEmail = false) { - const body = !isEmail - ? { username: login, password } - : { email: login, password }; + const body = !isEmail ? { username: login, password } : { email: login, password }; return request({ url: Parse.serverURL + '/verifyPassword', headers: { @@ -19,12 +17,7 @@ const verifyPassword = function (login, password, isEmail = false) { .catch(err => err); }; -const isAccountLockoutError = function ( - username, - password, - duration, - waitTime -) { +const isAccountLockoutError = function (username, password, duration, waitTime) { return new Promise((resolve, reject) => { setTimeout(() => { Parse.User.logIn(username, password) @@ -119,9 +112,7 @@ describe('Verify User Password', () => { }) .catch(err => { expect(err.status).toBe(400); - expect(err.text).toMatch( - '{"code":200,"error":"username/email is required."}' - ); + expect(err.text).toMatch('{"code":200,"error":"username/email is required."}'); done(); }); }); @@ -152,9 +143,7 @@ describe('Verify User Password', () => { }) .catch(err => { expect(err.status).toBe(400); - expect(err.text).toMatch( - '{"code":200,"error":"username/email is required."}' - ); + expect(err.text).toMatch('{"code":200,"error":"username/email is required."}'); done(); }); }); @@ -171,9 +160,7 @@ describe('Verify User Password', () => { }) .then(res => { expect(res.status).toBe(400); - expect(res.text).toMatch( - '{"code":200,"error":"username/email is required."}' - ); + expect(res.text).toMatch('{"code":200,"error":"username/email is required."}'); done(); }) .catch(err => { @@ -194,9 +181,7 @@ describe('Verify User Password', () => { }) .then(res => { expect(res.status).toBe(400); - expect(res.text).toMatch( - '{"code":200,"error":"username/email is required."}' - ); + expect(res.text).toMatch('{"code":200,"error":"username/email is required."}'); done(); }) .catch(err => { @@ -217,9 +202,7 @@ describe('Verify User Password', () => { }) .then(res => { expect(res.status).toBe(400); - expect(res.text).toMatch( - '{"code":201,"error":"password is required."}' - ); + expect(res.text).toMatch('{"code":201,"error":"password is required."}'); done(); }) .catch(err => { @@ -389,11 +372,7 @@ describe('Verify User Password', () => { password: 'mypass', email: 'unverified-email@example.com', }); - const res = await verifyPassword( - 'unverified-email@example.com', - 'mypass', - true - ); + const res = await verifyPassword('unverified-email@example.com', 'mypass', true); expect(res.status).toBe(400); expect(res.data).toEqual({ code: Parse.Error.EMAIL_NOT_FOUND, @@ -434,10 +413,7 @@ describe('Verify User Password', () => { done(); }) .catch(err => { - fail( - 'lock account after failed login attempts test failed: ' + - JSON.stringify(err) - ); + fail('lock account after failed login attempts test failed: ' + JSON.stringify(err)); done(); }); }); @@ -470,12 +446,8 @@ describe('Verify User Password', () => { const res = response.data; expect(typeof res).toBe('object'); expect(typeof res['objectId']).toEqual('string'); - expect( - Object.prototype.hasOwnProperty.call(res, 'sessionToken') - ).toEqual(false); - expect(Object.prototype.hasOwnProperty.call(res, 'password')).toEqual( - false - ); + expect(Object.prototype.hasOwnProperty.call(res, 'sessionToken')).toEqual(false); + expect(Object.prototype.hasOwnProperty.call(res, 'password')).toEqual(false); done(); }) .catch(err => { @@ -498,12 +470,8 @@ describe('Verify User Password', () => { const res = response.data; expect(typeof res).toBe('object'); expect(typeof res['objectId']).toEqual('string'); - expect( - Object.prototype.hasOwnProperty.call(res, 'sessionToken') - ).toEqual(false); - expect(Object.prototype.hasOwnProperty.call(res, 'password')).toEqual( - false - ); + expect(Object.prototype.hasOwnProperty.call(res, 'sessionToken')).toEqual(false); + expect(Object.prototype.hasOwnProperty.call(res, 'password')).toEqual(false); done(); }); }); @@ -522,12 +490,8 @@ describe('Verify User Password', () => { const res = response.data; expect(typeof res).toBe('object'); expect(typeof res['objectId']).toEqual('string'); - expect( - Object.prototype.hasOwnProperty.call(res, 'sessionToken') - ).toEqual(false); - expect(Object.prototype.hasOwnProperty.call(res, 'password')).toEqual( - false - ); + expect(Object.prototype.hasOwnProperty.call(res, 'sessionToken')).toEqual(false); + expect(Object.prototype.hasOwnProperty.call(res, 'password')).toEqual(false); done(); }); }); @@ -557,12 +521,8 @@ describe('Verify User Password', () => { expect(typeof res).toBe('string'); const body = JSON.parse(res); expect(typeof body['objectId']).toEqual('string'); - expect( - Object.prototype.hasOwnProperty.call(body, 'sessionToken') - ).toEqual(false); - expect(Object.prototype.hasOwnProperty.call(body, 'password')).toEqual( - false - ); + expect(Object.prototype.hasOwnProperty.call(body, 'sessionToken')).toEqual(false); + expect(Object.prototype.hasOwnProperty.call(body, 'password')).toEqual(false); done(); }); }); @@ -592,12 +552,8 @@ describe('Verify User Password', () => { expect(typeof res).toBe('string'); const body = JSON.parse(res); expect(typeof body['objectId']).toEqual('string'); - expect( - Object.prototype.hasOwnProperty.call(body, 'sessionToken') - ).toEqual(false); - expect(Object.prototype.hasOwnProperty.call(body, 'password')).toEqual( - false - ); + expect(Object.prototype.hasOwnProperty.call(body, 'sessionToken')).toEqual(false); + expect(Object.prototype.hasOwnProperty.call(body, 'password')).toEqual(false); done(); }); }); @@ -624,12 +580,8 @@ describe('Verify User Password', () => { const res = response.data; expect(typeof res).toBe('object'); expect(typeof res['objectId']).toEqual('string'); - expect( - Object.prototype.hasOwnProperty.call(res, 'sessionToken') - ).toEqual(false); - expect(Object.prototype.hasOwnProperty.call(res, 'password')).toEqual( - false - ); + expect(Object.prototype.hasOwnProperty.call(res, 'sessionToken')).toEqual(false); + expect(Object.prototype.hasOwnProperty.call(res, 'password')).toEqual(false); done(); }); }); @@ -670,12 +622,8 @@ describe('Verify User Password', () => { json: true, }); expect(res.objectId).toBe(user.id); - expect(Object.prototype.hasOwnProperty.call(res, 'sessionToken')).toEqual( - false - ); - expect(Object.prototype.hasOwnProperty.call(res, 'password')).toEqual( - false - ); + expect(Object.prototype.hasOwnProperty.call(res, 'sessionToken')).toEqual(false); + expect(Object.prototype.hasOwnProperty.call(res, 'password')).toEqual(false); }); it('fails to verify password of user with unverified email with master key and ignoreEmailVerification=false', async () => { diff --git a/spec/WinstonLoggerAdapter.spec.js b/spec/WinstonLoggerAdapter.spec.js index f32ec35574..da9efd2610 100644 --- a/spec/WinstonLoggerAdapter.spec.js +++ b/spec/WinstonLoggerAdapter.spec.js @@ -21,9 +21,7 @@ describe_only(() => { if (results.length == 0) { fail('The adapter should return non-empty results'); } else { - const log = results.find( - x => x.message === 'testing info logs with 1234' - ); + const log = results.find(x => x.message === 'testing info logs with 1234'); expect(log.level).toEqual('info'); } // Check the error log @@ -35,9 +33,7 @@ describe_only(() => { level: 'error', }, errors => { - const log = errors.find( - x => x.message === 'testing info logs with 1234' - ); + const log = errors.find(x => x.message === 'testing info logs with 1234'); expect(log).toBeUndefined(); done(); } @@ -56,9 +52,7 @@ describe_only(() => { order: 'desc', }); expect(results.length > 0).toBeTruthy(); - const log = results.find( - x => x.message === 'testing info logs with replace' - ); + const log = results.find(x => x.message === 'testing info logs with replace'); expect(log); }); @@ -74,9 +68,7 @@ describe_only(() => { order: 'desc', }); expect(results.length > 0).toBeTruthy(); - const log = results.find( - x => x.message === 'testing info logs with {"hello":"world"}' - ); + const log = results.find(x => x.message === 'testing info logs with {"hello":"world"}'); expect(log); }); @@ -144,9 +136,7 @@ describe_only(() => { level: 'error', }); expect(results.length > 0).toBeTruthy(); - const log = results.find( - x => x.message === 'testing error logs with replace' - ); + const log = results.find(x => x.message === 'testing error logs with replace'); expect(log); }); @@ -162,9 +152,7 @@ describe_only(() => { order: 'desc', }); expect(results.length > 0).toBeTruthy(); - const log = results.find( - x => x.message === 'testing error logs with {"hello":"world"}' - ); + const log = results.find(x => x.message === 'testing error logs with {"hello":"world"}'); expect(log); }); @@ -237,20 +225,14 @@ describe_only(() => { it('verbose logs should interpolate string', async () => { await reconfigureServer({ verbose: true }); const winstonLoggerAdapter = new WinstonLoggerAdapter(); - winstonLoggerAdapter.log( - 'verbose', - 'testing verbose logs with %s', - 'replace' - ); + winstonLoggerAdapter.log('verbose', 'testing verbose logs with %s', 'replace'); const results = await winstonLoggerAdapter.query({ from: new Date(Date.now() - 500), size: 100, level: 'verbose', }); expect(results.length > 0).toBeTruthy(); - const log = results.find( - x => x.message === 'testing verbose logs with replace' - ); + const log = results.find(x => x.message === 'testing verbose logs with replace'); expect(log); }); @@ -267,9 +249,7 @@ describe_only(() => { order: 'desc', }); expect(results.length > 0).toBeTruthy(); - const log = results.find( - x => x.message === 'testing verbose logs with {"hello":"world"}' - ); + const log = results.find(x => x.message === 'testing verbose logs with {"hello":"world"}'); expect(log); }); @@ -284,9 +264,7 @@ describe_only(() => { order: 'desc', }); expect(results.length > 0).toBeTruthy(); - const log = results.find( - x => x.message === 'testing verbose logs with 123' - ); + const log = results.find(x => x.message === 'testing verbose logs with 123'); expect(log); }); @@ -298,8 +276,6 @@ describe_only(() => { hello: 'world', }); const firstLog = process.stdout.write.calls.first().args[0]; - expect(firstLog).toBe( - 'verbose: testing verbose logs with {"hello":"world"}\n' - ); + expect(firstLog).toBe('verbose: testing verbose logs with {"hello":"world"}\n'); }); }); diff --git a/spec/batch.spec.js b/spec/batch.spec.js index 0380ce762d..1dbd5714b6 100644 --- a/spec/batch.spec.js +++ b/spec/batch.spec.js @@ -23,9 +23,7 @@ describe('batch', () => { }); it('should return the proper url', () => { - const internalURL = batch.makeBatchRoutingPathFunction(originalURL)( - '/parse/classes/Object' - ); + const internalURL = batch.makeBatchRoutingPathFunction(originalURL)('/parse/classes/Object'); expect(internalURL).toEqual('/classes/Object'); }); @@ -167,10 +165,7 @@ describe('batch', () => { expect(createSpy.calls.count()).toBe(2); expect(createSpy.calls.argsFor(0)[3]).toEqual(null); expect(createSpy.calls.argsFor(1)[3]).toEqual(null); - expect(results.map(result => result.get('key')).sort()).toEqual([ - 'value1', - 'value2', - ]); + expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']); }); it('should handle a batch request with transaction = false', async () => { @@ -205,10 +200,7 @@ describe('batch', () => { expect(createSpy.calls.count()).toBe(2); expect(createSpy.calls.argsFor(0)[3]).toEqual(null); expect(createSpy.calls.argsFor(1)[3]).toEqual(null); - expect(results.map(result => result.get('key')).sort()).toEqual([ - 'value1', - 'value2', - ]); + expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']); }); if ( @@ -250,14 +242,9 @@ describe('batch', () => { const results = await query.find(); expect(createSpy.calls.count()).toBe(2); for (let i = 0; i + 1 < createSpy.calls.length; i = i + 2) { - expect(createSpy.calls.argsFor(i)[3]).toBe( - createSpy.calls.argsFor(i + 1)[3] - ); + expect(createSpy.calls.argsFor(i)[3]).toBe(createSpy.calls.argsFor(i + 1)[3]); } - expect(results.map(result => result.get('key')).sort()).toEqual([ - 'value1', - 'value2', - ]); + expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']); }); it('should not save anything when one operation fails in a transaction', async () => { @@ -548,10 +535,7 @@ describe('batch', () => { const query = new Parse.Query('MyObject'); const results = await query.find(); - expect(results.map(result => result.get('key')).sort()).toEqual([ - 'value1', - 'value2', - ]); + expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']); const query2 = new Parse.Query('MyObject2'); const results2 = await query2.find(); @@ -559,10 +543,7 @@ describe('batch', () => { const query3 = new Parse.Query('MyObject3'); const results3 = await query3.find(); - expect(results3.map(result => result.get('key')).sort()).toEqual([ - 'value1', - 'value2', - ]); + expect(results3.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']); expect(createSpy.calls.count() >= 13).toEqual(true); let transactionalSession; diff --git a/spec/cryptoUtils.spec.js b/spec/cryptoUtils.spec.js index a3b1c69718..8270e052cf 100644 --- a/spec/cryptoUtils.spec.js +++ b/spec/cryptoUtils.spec.js @@ -27,9 +27,7 @@ describe('randomString', () => { }); it('returns unique results', () => { - expect(givesUniqueResults(() => cryptoUtils.randomString(10), 100)).toBe( - true - ); + expect(givesUniqueResults(() => cryptoUtils.randomString(10), 100)).toBe(true); }); }); @@ -52,9 +50,7 @@ describe('randomHexString', () => { }); it('returns unique results', () => { - expect(givesUniqueResults(() => cryptoUtils.randomHexString(20), 100)).toBe( - true - ); + expect(givesUniqueResults(() => cryptoUtils.randomHexString(20), 100)).toBe(true); }); }); diff --git a/spec/defaultGraphQLTypes.spec.js b/spec/defaultGraphQLTypes.spec.js index e702ae0b31..4e3e311467 100644 --- a/spec/defaultGraphQLTypes.spec.js +++ b/spec/defaultGraphQLTypes.spec.js @@ -35,14 +35,9 @@ function createObjectField(name, value) { describe('defaultGraphQLTypes', () => { describe('TypeValidationError', () => { it('should be an error with specific message', () => { - const typeValidationError = new TypeValidationError( - 'somevalue', - 'sometype' - ); + const typeValidationError = new TypeValidationError('somevalue', 'sometype'); expect(typeValidationError).toEqual(jasmine.any(Error)); - expect(typeValidationError.message).toEqual( - 'somevalue is not a valid sometype' - ); + expect(typeValidationError.message).toEqual('somevalue is not a valid sometype'); }); }); @@ -53,18 +48,10 @@ describe('defaultGraphQLTypes', () => { }); it('should fail if not a string', () => { - expect(() => parseStringValue()).toThrow( - jasmine.stringMatching('is not a valid String') - ); - expect(() => parseStringValue({})).toThrow( - jasmine.stringMatching('is not a valid String') - ); - expect(() => parseStringValue([])).toThrow( - jasmine.stringMatching('is not a valid String') - ); - expect(() => parseStringValue(123)).toThrow( - jasmine.stringMatching('is not a valid String') - ); + expect(() => parseStringValue()).toThrow(jasmine.stringMatching('is not a valid String')); + expect(() => parseStringValue({})).toThrow(jasmine.stringMatching('is not a valid String')); + expect(() => parseStringValue([])).toThrow(jasmine.stringMatching('is not a valid String')); + expect(() => parseStringValue(123)).toThrow(jasmine.stringMatching('is not a valid String')); }); }); @@ -75,27 +62,15 @@ describe('defaultGraphQLTypes', () => { }); it('should fail if not a string', () => { - expect(() => parseIntValue()).toThrow( - jasmine.stringMatching('is not a valid Int') - ); - expect(() => parseIntValue({})).toThrow( - jasmine.stringMatching('is not a valid Int') - ); - expect(() => parseIntValue([])).toThrow( - jasmine.stringMatching('is not a valid Int') - ); - expect(() => parseIntValue(123)).toThrow( - jasmine.stringMatching('is not a valid Int') - ); + expect(() => parseIntValue()).toThrow(jasmine.stringMatching('is not a valid Int')); + expect(() => parseIntValue({})).toThrow(jasmine.stringMatching('is not a valid Int')); + expect(() => parseIntValue([])).toThrow(jasmine.stringMatching('is not a valid Int')); + expect(() => parseIntValue(123)).toThrow(jasmine.stringMatching('is not a valid Int')); }); it('should fail if not an integer string', () => { - expect(() => parseIntValue('a123')).toThrow( - jasmine.stringMatching('is not a valid Int') - ); - expect(() => parseIntValue('123.4')).toThrow( - jasmine.stringMatching('is not a valid Int') - ); + expect(() => parseIntValue('a123')).toThrow(jasmine.stringMatching('is not a valid Int')); + expect(() => parseIntValue('123.4')).toThrow(jasmine.stringMatching('is not a valid Int')); }); }); @@ -106,21 +81,13 @@ describe('defaultGraphQLTypes', () => { }); it('should fail if not a string', () => { - expect(() => parseFloatValue()).toThrow( - jasmine.stringMatching('is not a valid Float') - ); - expect(() => parseFloatValue({})).toThrow( - jasmine.stringMatching('is not a valid Float') - ); - expect(() => parseFloatValue([])).toThrow( - jasmine.stringMatching('is not a valid Float') - ); + expect(() => parseFloatValue()).toThrow(jasmine.stringMatching('is not a valid Float')); + expect(() => parseFloatValue({})).toThrow(jasmine.stringMatching('is not a valid Float')); + expect(() => parseFloatValue([])).toThrow(jasmine.stringMatching('is not a valid Float')); }); it('should fail if not a float string', () => { - expect(() => parseIntValue('a123')).toThrow( - jasmine.stringMatching('is not a valid Int') - ); + expect(() => parseIntValue('a123')).toThrow(jasmine.stringMatching('is not a valid Int')); }); }); @@ -133,15 +100,9 @@ describe('defaultGraphQLTypes', () => { }); it('should fail if not a boolean', () => { - expect(() => parseBooleanValue()).toThrow( - jasmine.stringMatching('is not a valid Boolean') - ); - expect(() => parseBooleanValue({})).toThrow( - jasmine.stringMatching('is not a valid Boolean') - ); - expect(() => parseBooleanValue([])).toThrow( - jasmine.stringMatching('is not a valid Boolean') - ); + expect(() => parseBooleanValue()).toThrow(jasmine.stringMatching('is not a valid Boolean')); + expect(() => parseBooleanValue({})).toThrow(jasmine.stringMatching('is not a valid Boolean')); + expect(() => parseBooleanValue([])).toThrow(jasmine.stringMatching('is not a valid Boolean')); expect(() => parseBooleanValue(123)).toThrow( jasmine.stringMatching('is not a valid Boolean') ); @@ -159,18 +120,10 @@ describe('defaultGraphQLTypes', () => { }); it('should fail if not a string', () => { - expect(() => parseDateIsoValue()).toThrow( - jasmine.stringMatching('is not a valid Date') - ); - expect(() => parseDateIsoValue({})).toThrow( - jasmine.stringMatching('is not a valid Date') - ); - expect(() => parseDateIsoValue([])).toThrow( - jasmine.stringMatching('is not a valid Date') - ); - expect(() => parseDateIsoValue(123)).toThrow( - jasmine.stringMatching('is not a valid Date') - ); + expect(() => parseDateIsoValue()).toThrow(jasmine.stringMatching('is not a valid Date')); + expect(() => parseDateIsoValue({})).toThrow(jasmine.stringMatching('is not a valid Date')); + expect(() => parseDateIsoValue([])).toThrow(jasmine.stringMatching('is not a valid Date')); + expect(() => parseDateIsoValue(123)).toThrow(jasmine.stringMatching('is not a valid Date')); }); it('should fail if not a date string', () => { @@ -321,18 +274,12 @@ describe('defaultGraphQLTypes', () => { }); it('should fail if not an array', () => { - expect(() => parseListValues()).toThrow( - jasmine.stringMatching('is not a valid List') - ); - expect(() => parseListValues({})).toThrow( - jasmine.stringMatching('is not a valid List') - ); + expect(() => parseListValues()).toThrow(jasmine.stringMatching('is not a valid List')); + expect(() => parseListValues({})).toThrow(jasmine.stringMatching('is not a valid List')); expect(() => parseListValues('some string')).toThrow( jasmine.stringMatching('is not a valid List') ); - expect(() => parseListValues(123)).toThrow( - jasmine.stringMatching('is not a valid List') - ); + expect(() => parseListValues(123)).toThrow(jasmine.stringMatching('is not a valid List')); }); }); @@ -356,18 +303,12 @@ describe('defaultGraphQLTypes', () => { }); it('should fail if not an array', () => { - expect(() => parseObjectFields()).toThrow( - jasmine.stringMatching('is not a valid Object') - ); - expect(() => parseObjectFields({})).toThrow( - jasmine.stringMatching('is not a valid Object') - ); + expect(() => parseObjectFields()).toThrow(jasmine.stringMatching('is not a valid Object')); + expect(() => parseObjectFields({})).toThrow(jasmine.stringMatching('is not a valid Object')); expect(() => parseObjectFields('some string')).toThrow( jasmine.stringMatching('is not a valid Object') ); - expect(() => parseObjectFields(123)).toThrow( - jasmine.stringMatching('is not a valid Object') - ); + expect(() => parseObjectFields(123)).toThrow(jasmine.stringMatching('is not a valid Object')); }); }); @@ -399,9 +340,7 @@ describe('defaultGraphQLTypes', () => { }); it('should fail if not an valid object or string', () => { - expect(() => parseLiteral({})).toThrow( - jasmine.stringMatching('is not a valid Date') - ); + expect(() => parseLiteral({})).toThrow(jasmine.stringMatching('is not a valid Date')); expect(() => parseLiteral( createValue(Kind.OBJECT, undefined, undefined, [ @@ -410,12 +349,8 @@ describe('defaultGraphQLTypes', () => { ]) ) ).toThrow(jasmine.stringMatching('is not a valid Date')); - expect(() => parseLiteral([])).toThrow( - jasmine.stringMatching('is not a valid Date') - ); - expect(() => parseLiteral(123)).toThrow( - jasmine.stringMatching('is not a valid Date') - ); + expect(() => parseLiteral([])).toThrow(jasmine.stringMatching('is not a valid Date')); + expect(() => parseLiteral(123)).toThrow(jasmine.stringMatching('is not a valid Date')); }); }); @@ -439,9 +374,7 @@ describe('defaultGraphQLTypes', () => { }); it('should fail if not an valid object or string', () => { - expect(() => parseValue({})).toThrow( - jasmine.stringMatching('is not a valid Date') - ); + expect(() => parseValue({})).toThrow(jasmine.stringMatching('is not a valid Date')); expect(() => parseValue({ __type: 'Foo', @@ -454,12 +387,8 @@ describe('defaultGraphQLTypes', () => { iso: 'foo', }) ).toThrow(jasmine.stringMatching('is not a valid Date')); - expect(() => parseValue([])).toThrow( - jasmine.stringMatching('is not a valid Date') - ); - expect(() => parseValue(123)).toThrow( - jasmine.stringMatching('is not a valid Date') - ); + expect(() => parseValue([])).toThrow(jasmine.stringMatching('is not a valid Date')); + expect(() => parseValue(123)).toThrow(jasmine.stringMatching('is not a valid Date')); }); }); @@ -486,21 +415,15 @@ describe('defaultGraphQLTypes', () => { }); it('should fail if not an valid object or string', () => { - expect(() => serialize({})).toThrow( - jasmine.stringMatching('is not a valid Date') - ); + expect(() => serialize({})).toThrow(jasmine.stringMatching('is not a valid Date')); expect(() => serialize({ __type: 'Foo', iso: '2019-05-09T23:12:00.000Z', }) ).toThrow(jasmine.stringMatching('is not a valid Date')); - expect(() => serialize([])).toThrow( - jasmine.stringMatching('is not a valid Date') - ); - expect(() => serialize(123)).toThrow( - jasmine.stringMatching('is not a valid Date') - ); + expect(() => serialize([])).toThrow(jasmine.stringMatching('is not a valid Date')); + expect(() => serialize(123)).toThrow(jasmine.stringMatching('is not a valid Date')); }); }); }); @@ -531,9 +454,7 @@ describe('defaultGraphQLTypes', () => { }); it('should fail if not an valid object or string', () => { - expect(() => parseLiteral({})).toThrow( - jasmine.stringMatching('is not a valid Bytes') - ); + expect(() => parseLiteral({})).toThrow(jasmine.stringMatching('is not a valid Bytes')); expect(() => parseLiteral( createValue(Kind.OBJECT, undefined, undefined, [ @@ -542,12 +463,8 @@ describe('defaultGraphQLTypes', () => { ]) ) ).toThrow(jasmine.stringMatching('is not a valid Bytes')); - expect(() => parseLiteral([])).toThrow( - jasmine.stringMatching('is not a valid Bytes') - ); - expect(() => parseLiteral(123)).toThrow( - jasmine.stringMatching('is not a valid Bytes') - ); + expect(() => parseLiteral([])).toThrow(jasmine.stringMatching('is not a valid Bytes')); + expect(() => parseLiteral(123)).toThrow(jasmine.stringMatching('is not a valid Bytes')); }); }); @@ -570,21 +487,15 @@ describe('defaultGraphQLTypes', () => { }); it('should fail if not an valid object or string', () => { - expect(() => parseValue({})).toThrow( - jasmine.stringMatching('is not a valid Bytes') - ); + expect(() => parseValue({})).toThrow(jasmine.stringMatching('is not a valid Bytes')); expect(() => parseValue({ __type: 'Foo', base64: 'bytesContent', }) ).toThrow(jasmine.stringMatching('is not a valid Bytes')); - expect(() => parseValue([])).toThrow( - jasmine.stringMatching('is not a valid Bytes') - ); - expect(() => parseValue(123)).toThrow( - jasmine.stringMatching('is not a valid Bytes') - ); + expect(() => parseValue([])).toThrow(jasmine.stringMatching('is not a valid Bytes')); + expect(() => parseValue(123)).toThrow(jasmine.stringMatching('is not a valid Bytes')); }); }); @@ -606,21 +517,15 @@ describe('defaultGraphQLTypes', () => { }); it('should fail if not an valid object or string', () => { - expect(() => serialize({})).toThrow( - jasmine.stringMatching('is not a valid Bytes') - ); + expect(() => serialize({})).toThrow(jasmine.stringMatching('is not a valid Bytes')); expect(() => serialize({ __type: 'Foo', base64: 'bytesContent', }) ).toThrow(jasmine.stringMatching('is not a valid Bytes')); - expect(() => serialize([])).toThrow( - jasmine.stringMatching('is not a valid Bytes') - ); - expect(() => serialize(123)).toThrow( - jasmine.stringMatching('is not a valid Bytes') - ); + expect(() => serialize([])).toThrow(jasmine.stringMatching('is not a valid Bytes')); + expect(() => serialize(123)).toThrow(jasmine.stringMatching('is not a valid Bytes')); }); }); }); @@ -653,9 +558,7 @@ describe('defaultGraphQLTypes', () => { }); it('should fail if not an valid object or string', () => { - expect(() => parseLiteral({})).toThrow( - jasmine.stringMatching('is not a valid File') - ); + expect(() => parseLiteral({})).toThrow(jasmine.stringMatching('is not a valid File')); expect(() => parseLiteral( createValue(Kind.OBJECT, undefined, undefined, [ @@ -665,12 +568,8 @@ describe('defaultGraphQLTypes', () => { ]) ) ).toThrow(jasmine.stringMatching('is not a valid File')); - expect(() => parseLiteral([])).toThrow( - jasmine.stringMatching('is not a valid File') - ); - expect(() => parseLiteral(123)).toThrow( - jasmine.stringMatching('is not a valid File') - ); + expect(() => parseLiteral([])).toThrow(jasmine.stringMatching('is not a valid File')); + expect(() => parseLiteral(123)).toThrow(jasmine.stringMatching('is not a valid File')); }); }); @@ -693,9 +592,7 @@ describe('defaultGraphQLTypes', () => { }); it('should fail if not an valid object or string', () => { - expect(() => serialize({})).toThrow( - jasmine.stringMatching('is not a valid File') - ); + expect(() => serialize({})).toThrow(jasmine.stringMatching('is not a valid File')); expect(() => serialize({ __type: 'Foo', @@ -703,12 +600,8 @@ describe('defaultGraphQLTypes', () => { url: 'myurl', }) ).toThrow(jasmine.stringMatching('is not a valid File')); - expect(() => serialize([])).toThrow( - jasmine.stringMatching('is not a valid File') - ); - expect(() => serialize(123)).toThrow( - jasmine.stringMatching('is not a valid File') - ); + expect(() => serialize([])).toThrow(jasmine.stringMatching('is not a valid File')); + expect(() => serialize(123)).toThrow(jasmine.stringMatching('is not a valid File')); }); }); }); diff --git a/spec/features.spec.js b/spec/features.spec.js index 23b43c0b2e..f138fe4cf6 100644 --- a/spec/features.spec.js +++ b/spec/features.spec.js @@ -29,9 +29,7 @@ describe('features', () => { 'X-Parse-REST-API-Key': 'rest', }, }); - done.fail( - 'The serverInfo request should be rejected without the master key' - ); + done.fail('The serverInfo request should be rejected without the master key'); } catch (error) { expect(error.status).toEqual(403); expect(error.data.error).toEqual('unauthorized: master key is required'); diff --git a/spec/graphQLObjectsQueries.js b/spec/graphQLObjectsQueries.js index 40bdca8b6d..f8783c67f8 100644 --- a/spec/graphQLObjectsQueries.js +++ b/spec/graphQLObjectsQueries.js @@ -1,7 +1,5 @@ const { offsetToCursor } = require('graphql-relay'); -const { - calculateSkipAndLimit, -} = require('../lib/GraphQL/helpers/objectsQueries'); +const { calculateSkipAndLimit } = require('../lib/GraphQL/helpers/objectsQueries'); describe('GraphQL objectsQueries', () => { describe('calculateSkipAndLimit', () => { @@ -18,9 +16,9 @@ describe('GraphQL objectsQueries', () => { expect(() => calculateSkipAndLimit(1, 1, offsetToCursor(1), -1)).toThrow( jasmine.stringMatching('Last should be a positive number') ); - expect(() => - calculateSkipAndLimit(1, 1, offsetToCursor(1), 1, offsetToCursor(-1)) - ).toThrow(jasmine.stringMatching('Before is not a valid curso')); + expect(() => calculateSkipAndLimit(1, 1, offsetToCursor(1), 1, offsetToCursor(-1))).toThrow( + jasmine.stringMatching('Before is not a valid curso') + ); }); it('should work only with skip', () => { @@ -32,9 +30,7 @@ describe('GraphQL objectsQueries', () => { }); it('should work only with after', () => { - expect( - calculateSkipAndLimit(undefined, undefined, offsetToCursor(9)) - ).toEqual({ + expect(calculateSkipAndLimit(undefined, undefined, offsetToCursor(9))).toEqual({ skip: 10, limit: undefined, needToPreCount: false, @@ -59,13 +55,7 @@ describe('GraphQL objectsQueries', () => { it('if before cursor is less than skipped items, no objects will be returned', () => { expect( - calculateSkipAndLimit( - 10, - 30, - offsetToCursor(9), - undefined, - offsetToCursor(5) - ) + calculateSkipAndLimit(10, 30, offsetToCursor(9), undefined, offsetToCursor(5)) ).toEqual({ skip: 20, limit: 0, @@ -75,13 +65,7 @@ describe('GraphQL objectsQueries', () => { it('if before cursor is greater than returned objects set by limit, nothing is changed', () => { expect( - calculateSkipAndLimit( - 10, - 30, - offsetToCursor(9), - undefined, - offsetToCursor(100) - ) + calculateSkipAndLimit(10, 30, offsetToCursor(9), undefined, offsetToCursor(100)) ).toEqual({ skip: 20, limit: 30, @@ -91,13 +75,7 @@ describe('GraphQL objectsQueries', () => { it('if before cursor is less than returned objects set by limit, limit is adjusted', () => { expect( - calculateSkipAndLimit( - 10, - 30, - offsetToCursor(9), - undefined, - offsetToCursor(40) - ) + calculateSkipAndLimit(10, 30, offsetToCursor(9), undefined, offsetToCursor(40)) ).toEqual({ skip: 20, limit: 20, @@ -106,9 +84,7 @@ describe('GraphQL objectsQueries', () => { }); it('last should work alone but requires pre count', () => { - expect( - calculateSkipAndLimit(undefined, undefined, undefined, 10) - ).toEqual({ + expect(calculateSkipAndLimit(undefined, undefined, undefined, 10)).toEqual({ skip: undefined, limit: 10, needToPreCount: true, @@ -116,9 +92,7 @@ describe('GraphQL objectsQueries', () => { }); it('last should be adjusted to max limit', () => { - expect( - calculateSkipAndLimit(undefined, undefined, undefined, 10, undefined, 5) - ).toEqual({ + expect(calculateSkipAndLimit(undefined, undefined, undefined, 10, undefined, 5)).toEqual({ skip: undefined, limit: 5, needToPreCount: true, @@ -126,19 +100,15 @@ describe('GraphQL objectsQueries', () => { }); it('no objects will be returned if last is equal to 0', () => { - expect(calculateSkipAndLimit(undefined, undefined, undefined, 0)).toEqual( - { - skip: undefined, - limit: 0, - needToPreCount: false, - } - ); + expect(calculateSkipAndLimit(undefined, undefined, undefined, 0)).toEqual({ + skip: undefined, + limit: 0, + needToPreCount: false, + }); }); it('nothing changes if last is bigger than the calculared limit', () => { - expect( - calculateSkipAndLimit(10, 30, offsetToCursor(9), 30, offsetToCursor(40)) - ).toEqual({ + expect(calculateSkipAndLimit(10, 30, offsetToCursor(9), 30, offsetToCursor(40))).toEqual({ skip: 20, limit: 20, needToPreCount: false, @@ -146,9 +116,7 @@ describe('GraphQL objectsQueries', () => { }); it('If last is small than limit, new limit is calculated', () => { - expect( - calculateSkipAndLimit(10, 30, offsetToCursor(9), 10, offsetToCursor(40)) - ).toEqual({ + expect(calculateSkipAndLimit(10, 30, offsetToCursor(9), 10, offsetToCursor(40))).toEqual({ skip: 30, limit: 10, needToPreCount: false, diff --git a/spec/helper.js b/spec/helper.js index 2c1786481e..29a8e7336f 100644 --- a/spec/helper.js +++ b/spec/helper.js @@ -13,8 +13,7 @@ if (dns.setDefaultResultOrder) { } // Sets up a Parse API server for testing. -jasmine.DEFAULT_TIMEOUT_INTERVAL = - process.env.PARSE_SERVER_TEST_TIMEOUT || 10000; +jasmine.DEFAULT_TIMEOUT_INTERVAL = process.env.PARSE_SERVER_TEST_TIMEOUT || 10000; jasmine.getEnv().addReporter(new CurrentSpecReporter()); jasmine.getEnv().addReporter(new SpecReporter()); global.retryFlakyTests(); @@ -47,19 +46,13 @@ const GridFSBucketAdapter = const FSAdapter = require('@parse/fs-files-adapter'); const PostgresStorageAdapter = require('../lib/Adapters/Storage/Postgres/PostgresStorageAdapter').default; -const MongoStorageAdapter = - require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter').default; -const RedisCacheAdapter = - require('../lib/Adapters/Cache/RedisCacheAdapter').default; +const MongoStorageAdapter = require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter').default; +const RedisCacheAdapter = require('../lib/Adapters/Cache/RedisCacheAdapter').default; const RESTController = require('parse/lib/node/RESTController').default; -const { - VolatileClassesSchemas, -} = require('../lib/Controllers/SchemaController'); - -const mongoURI = - 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase'; -const postgresURI = - 'postgres://localhost:5432/parse_server_postgres_adapter_test_database'; +const { VolatileClassesSchemas } = require('../lib/Controllers/SchemaController'); + +const mongoURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase'; +const postgresURI = 'postgres://localhost:5432/parse_server_postgres_adapter_test_database'; let databaseAdapter; let databaseURI; @@ -195,15 +188,10 @@ const reconfigureServer = async (changedConfiguration = {}) => { if (process.env.PARSE_SERVER_TEST_CACHE === 'redis') { defaultConfiguration.cacheAdapter = new RedisCacheAdapter(); } - const newConfiguration = Object.assign( - {}, - defaultConfiguration, - changedConfiguration, - { - mountPath: '/1', - port, - } - ); + const newConfiguration = Object.assign({}, defaultConfiguration, changedConfiguration, { + mountPath: '/1', + port, + }); cache.clear(); parseServer = await ParseServer.startApp(newConfiguration); Parse.CoreManager.setRESTController(RESTController); @@ -405,23 +393,21 @@ function mockShortLivedAuth() { } function mockFetch(mockResponses) { - global.fetch = jasmine - .createSpy('fetch') - .and.callFake((url, options = {}) => { - options.method ||= 'GET'; - const mockResponse = mockResponses.find( - mock => mock.url === url && mock.method === options.method - ); - - if (mockResponse) { - return Promise.resolve(mockResponse.response); - } - - return Promise.resolve({ - ok: false, - statusText: 'Unknown URL or method', - }); + global.fetch = jasmine.createSpy('fetch').and.callFake((url, options = {}) => { + options.method ||= 'GET'; + const mockResponse = mockResponses.find( + mock => mock.url === url && mock.method === options.method + ); + + if (mockResponse) { + return Promise.resolve(mockResponse.response); + } + + return Promise.resolve({ + ok: false, + statusText: 'Unknown URL or method', }); + }); } // This is polluting, but, it makes it way easier to directly port old tests. @@ -462,9 +448,7 @@ let testExclusionList = []; try { // Fetch test exclusion list testExclusionList = require('./testExclusionList.json'); - console.log( - `Using test exclusion list with ${testExclusionList.length} entries` - ); + console.log(`Using test exclusion list with ${testExclusionList.length} entries`); } catch (error) { if (error.code !== 'MODULE_NOT_FOUND') { throw error; diff --git a/spec/index.spec.js b/spec/index.spec.js index dcc141931a..0af24630b1 100644 --- a/spec/index.spec.js +++ b/spec/index.spec.js @@ -6,8 +6,7 @@ const ParseServer = require('../lib/index'); const Config = require('../lib/Config'); const express = require('express'); -const MongoStorageAdapter = - require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter').default; +const MongoStorageAdapter = require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter').default; describe('server', () => { it('requires a master key and app id', done => { @@ -39,8 +38,7 @@ describe('server', () => { request({ url: 'http://localhost:8378/1/classes/TestObject', headers: { - Authorization: - 'Basic ' + Buffer.from('test:' + 'test').toString('base64'), + Authorization: 'Basic ' + Buffer.from('test:' + 'test').toString('base64'), }, }).then(response => { expect(response.status).toEqual(200); @@ -54,9 +52,7 @@ describe('server', () => { request({ url: 'http://localhost:8378/1/classes/TestObject', headers: { - Authorization: - 'Basic ' + - Buffer.from('test:javascript-key=' + 'test').toString('base64'), + Authorization: 'Basic ' + Buffer.from('test:javascript-key=' + 'test').toString('base64'), }, }).then(response => { expect(response.status).toEqual(200); @@ -150,9 +146,7 @@ describe('server', () => { }, publicServerURL: 'http://localhost:8378/1', }; - await expectAsync(reconfigureServer(options)).toBeRejected( - 'MockMailAdapterConstructor' - ); + await expectAsync(reconfigureServer(options)).toBeRejected('MockMailAdapterConstructor'); }); }); @@ -332,13 +326,9 @@ describe('server', () => { it('has createLiveQueryServer', done => { // original implementation through the factory - expect(typeof ParseServer.ParseServer.createLiveQueryServer).toEqual( - 'function' - ); + expect(typeof ParseServer.ParseServer.createLiveQueryServer).toEqual('function'); // For import calls - expect(typeof ParseServer.default.createLiveQueryServer).toEqual( - 'function' - ); + expect(typeof ParseServer.default.createLiveQueryServer).toEqual('function'); done(); }); @@ -356,13 +346,11 @@ describe('server', () => { }); it('properly gives publicServerURL when set', done => { - reconfigureServer({ publicServerURL: 'https://myserver.com/1' }).then( - () => { - const config = Config.get('test', 'http://localhost:8378/1'); - expect(config.mount).toEqual('https://myserver.com/1'); - done(); - } - ); + reconfigureServer({ publicServerURL: 'https://myserver.com/1' }).then(() => { + const config = Config.get('test', 'http://localhost:8378/1'); + expect(config.mount).toEqual('https://myserver.com/1'); + done(); + }); }); it('properly removes trailing slash in mount', done => { @@ -375,9 +363,7 @@ describe('server', () => { it('should throw when getting invalid mount', done => { reconfigureServer({ publicServerURL: 'blabla:/some' }).catch(error => { - expect(error).toEqual( - 'publicServerURL should be a valid HTTPS URL starting with https://' - ); + expect(error).toEqual('publicServerURL should be a valid HTTPS URL starting with https://'); done(); }); }); @@ -442,9 +428,9 @@ describe('server', () => { it('fails if default limit is wrong type', async () => { for (const value of ['invalid', {}, [], true]) { - await expectAsync( - reconfigureServer({ defaultLimit: value }) - ).toBeRejectedWith('Default limit must be a number.'); + await expectAsync(reconfigureServer({ defaultLimit: value })).toBeRejectedWith( + 'Default limit must be a number.' + ); } }); @@ -466,14 +452,12 @@ describe('server', () => { }); it('fails if you provides invalid ip in masterKeyIps', done => { - reconfigureServer({ masterKeyIps: ['invalidIp', '1.2.3.4'] }).catch( - error => { - expect(error).toEqual( - 'The Parse Server option "masterKeyIps" contains an invalid IP address "invalidIp".' - ); - done(); - } - ); + reconfigureServer({ masterKeyIps: ['invalidIp', '1.2.3.4'] }).catch(error => { + expect(error).toEqual( + 'The Parse Server option "masterKeyIps" contains an invalid IP address "invalidIp".' + ); + done(); + }); }); it('should succeed if you provide valid ip in masterKeyIps', done => { @@ -484,10 +468,7 @@ describe('server', () => { it('should set default masterKeyIps for IPv4 and IPv6 localhost', () => { const definitions = require('../lib/Options/Definitions.js'); - expect(definitions.ParseServerOptions.masterKeyIps.default).toEqual([ - '127.0.0.1', - '::1', - ]); + expect(definitions.ParseServerOptions.masterKeyIps.default).toEqual(['127.0.0.1', '::1']); }); it('should load a middleware', done => { @@ -577,10 +558,7 @@ describe('server', () => { url: 'http://localhost:12701/parse/classes/TestObject', }).catch(e => new Parse.Error(e.data.code, e.data.error)); expect(response).toEqual( - new Parse.Error( - Parse.Error.INTERNAL_SERVER_ERROR, - 'Invalid server state: initialized' - ) + new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Invalid server state: initialized') ); const health = await request({ url: 'http://localhost:12701/parse/health', @@ -634,15 +612,11 @@ describe('server', () => { const config = Config.get(Parse.applicationId); expect(config.masterKeyCache.masterKey).toEqual('testMasterKey'); - expect(config.masterKeyCache.expiresAt.getTime()).toBeGreaterThan( - Date.now() - ); + expect(config.masterKeyCache.expiresAt.getTime()).toBeGreaterThan(Date.now()); }); it('should not reload if ttl is not set', async () => { - const masterKeySpy = jasmine - .createSpy() - .and.returnValue(Promise.resolve('initialMasterKey')); + const masterKeySpy = jasmine.createSpy().and.returnValue(Promise.resolve('initialMasterKey')); await reconfigureServer({ masterKey: masterKeySpy, @@ -667,10 +641,7 @@ describe('server', () => { it('should reload masterKey if ttl is set and expired', async () => { const masterKeySpy = jasmine .createSpy() - .and.returnValues( - Promise.resolve('firstMasterKey'), - Promise.resolve('secondMasterKey') - ); + .and.returnValues(Promise.resolve('firstMasterKey'), Promise.resolve('secondMasterKey')); await reconfigureServer({ masterKey: masterKeySpy, @@ -703,9 +674,7 @@ describe('server', () => { sub: 'the_user_id', }; const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } }; - spyOn(authUtils, 'getHeaderFromToken').and.callFake( - () => fakeDecodedToken - ); + spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken); spyOn(jwt, 'verify').and.callFake(() => fakeClaim); const user = new Parse.User(); user diff --git a/spec/rest.spec.js b/spec/rest.spec.js index 8955a98642..92e25c3941 100644 --- a/spec/rest.spec.js +++ b/spec/rest.spec.js @@ -79,17 +79,11 @@ describe('rest create', () => { const err = 'objectId must not be empty, null or undefined'; - expect(() => - rest.create(config, auth.nobody(config), 'MyClass', objIdEmpty) - ).toThrowError(err); + expect(() => rest.create(config, auth.nobody(config), 'MyClass', objIdEmpty)).toThrowError(err); - expect(() => - rest.create(config, auth.nobody(config), 'MyClass', objIdNull) - ).toThrowError(err); + expect(() => rest.create(config, auth.nobody(config), 'MyClass', objIdNull)).toThrowError(err); - expect(() => - rest.create(config, auth.nobody(config), 'MyClass', objIdUndef) - ).toThrowError(err); + expect(() => rest.create(config, auth.nobody(config), 'MyClass', objIdUndef)).toThrowError(err); }); it('should generate objectId when not set by client with allowCustomObjectId true', async () => { @@ -253,42 +247,39 @@ describe('rest create', () => { }); }); - it_id('6c30306f-328c-47f2-88a7-2deffaee997f')(it)( - 'handles array, object, date', - done => { - const now = new Date(); - const obj = { - array: [1, 2, 3], - object: { foo: 'bar' }, - date: Parse._encode(now), - }; - rest - .create(config, auth.nobody(config), 'MyClass', obj) - .then(() => - database.adapter.find( - 'MyClass', - { - fields: { - array: { type: 'Array' }, - object: { type: 'Object' }, - date: { type: 'Date' }, - }, + it_id('6c30306f-328c-47f2-88a7-2deffaee997f')(it)('handles array, object, date', done => { + const now = new Date(); + const obj = { + array: [1, 2, 3], + object: { foo: 'bar' }, + date: Parse._encode(now), + }; + rest + .create(config, auth.nobody(config), 'MyClass', obj) + .then(() => + database.adapter.find( + 'MyClass', + { + fields: { + array: { type: 'Array' }, + object: { type: 'Object' }, + date: { type: 'Date' }, }, - {}, - {} - ) + }, + {}, + {} ) - .then(results => { - expect(results.length).toEqual(1); - const mob = results[0]; - expect(mob.array instanceof Array).toBe(true); - expect(typeof mob.object).toBe('object'); - expect(mob.date.__type).toBe('Date'); - expect(new Date(mob.date.iso).getTime()).toBe(now.getTime()); - done(); - }); - } - ); + ) + .then(results => { + expect(results.length).toEqual(1); + const mob = results[0]; + expect(mob.array instanceof Array).toBe(true); + expect(typeof mob.object).toBe('object'); + expect(mob.date.__type).toBe('Date'); + expect(new Date(mob.date.iso).getTime()).toBe(now.getTime()); + done(); + }); + }); it('handles object and subdocument', done => { const obj = { subdoc: { foo: 'bar', wu: 'tan' } }; @@ -308,13 +299,7 @@ describe('rest create', () => { expect(mob.subdoc.wu).toBe('tan'); expect(typeof mob.objectId).toEqual('string'); const obj = { 'subdoc.wu': 'clan' }; - return rest.update( - config, - auth.nobody(config), - 'MyClass', - { objectId: mob.objectId }, - obj - ); + return rest.update(config, auth.nobody(config), 'MyClass', { objectId: mob.objectId }, obj); }) .then(() => database.adapter.find('MyClass', { fields: {} }, {}, {})) .then(results => { @@ -332,27 +317,19 @@ describe('rest create', () => { const customConfig = Object.assign({}, config, { allowClientClassCreation: false, }); - rest - .create( - customConfig, - auth.nobody(customConfig), - 'ClientClassCreation', - {} - ) - .then( - () => { - fail('Should throw an error'); - done(); - }, - err => { - expect(err.code).toEqual(Parse.Error.OPERATION_FORBIDDEN); - expect(err.message).toEqual( - 'This user is not allowed to access ' + - 'non-existent class: ClientClassCreation' - ); - done(); - } - ); + rest.create(customConfig, auth.nobody(customConfig), 'ClientClassCreation', {}).then( + () => { + fail('Should throw an error'); + done(); + }, + err => { + expect(err.code).toEqual(Parse.Error.OPERATION_FORBIDDEN); + expect(err.message).toEqual( + 'This user is not allowed to access ' + 'non-existent class: ClientClassCreation' + ); + done(); + } + ); }); it('handles create on existent class when disabled client class creation', async () => { @@ -360,20 +337,12 @@ describe('rest create', () => { allowClientClassCreation: false, }); const schema = await config.database.loadSchema(); - const actualSchema = await schema.addClassIfNotExists( - 'ClientClassCreation', - {} - ); + const actualSchema = await schema.addClassIfNotExists('ClientClassCreation', {}); expect(actualSchema.className).toEqual('ClientClassCreation'); await schema.reloadData({ clearCache: true }); // Should not throw - await rest.create( - customConfig, - auth.nobody(customConfig), - 'ClientClassCreation', - {} - ); + await rest.create(customConfig, auth.nobody(customConfig), 'ClientClassCreation', {}); }); it('handles user signup', done => { @@ -468,13 +437,7 @@ describe('rest create', () => { }); }) .then(sessionAuth => { - return rest.update( - config, - sessionAuth, - '_User', - { objectId }, - updatedData - ); + return rest.update(config, sessionAuth, '_User', { objectId }, updatedData); }) .then(() => { return Parse.User.logOut().then(() => { @@ -509,9 +472,7 @@ describe('rest create', () => { }, err => { expect(err.code).toEqual(Parse.Error.UNSUPPORTED_SERVICE); - expect(err.message).toEqual( - 'This authentication method is unsupported.' - ); + expect(err.message).toEqual('This authentication method is unsupported.'); NoAnnonConfig.authDataManager.setEnableAnonymousUsers(true); done(); } @@ -707,9 +668,7 @@ describe('rest create', () => { const actual = new Date(session.expiresAt.iso); const expected = new Date(now.getTime() + 1000 * 3600 * 24 * 365); - expect( - Math.abs(actual - expected) <= jasmine.DEFAULT_TIMEOUT_INTERVAL - ).toEqual(true); + expect(Math.abs(actual - expected) <= jasmine.DEFAULT_TIMEOUT_INTERVAL).toEqual(true); done(); }); @@ -743,9 +702,7 @@ describe('rest create', () => { const actual = new Date(session.expiresAt.iso); const expected = new Date(now.getTime() + sessionLength * 1000); - expect( - Math.abs(actual - expected) <= jasmine.DEFAULT_TIMEOUT_INTERVAL - ).toEqual(true); + expect(Math.abs(actual - expected) <= jasmine.DEFAULT_TIMEOUT_INTERVAL).toEqual(true); done(); }) @@ -973,14 +930,12 @@ describe('rest update', () => { createdAt: { __type: 'Date', iso: newCreatedAt }, // should be ignored }; - return rest - .update(config, nobody, className, { objectId }, restObject) - .then(() => { - const restWhere = { - objectId: objectId, - }; - return rest.find(config, nobody, className, restWhere, {}); - }); + return rest.update(config, nobody, className, { objectId }, restObject).then(() => { + const restWhere = { + objectId: objectId, + }; + return rest.find(config, nobody, className, restWhere, {}); + }); }) .then(res2 => { const updatedObject = res2.results[0]; @@ -1045,9 +1000,7 @@ describe('read-only masterKey', () => { }); fail(); } catch (err) { - expect(err).toEqual( - new Error('masterKey and readOnlyMasterKey should be different') - ); + expect(err).toEqual(new Error('masterKey and readOnlyMasterKey should be different')); } await reconfigureServer(); }); @@ -1058,9 +1011,7 @@ describe('read-only masterKey', () => { masterKey: 'yolo', maintenanceKey: 'yolo', }) - ).toBeRejectedWith( - new Error('masterKey and maintenanceKey should be different') - ); + ).toBeRejectedWith(new Error('masterKey and maintenanceKey should be different')); }); it('should throw when trying to create RestWrite', () => { @@ -1089,9 +1040,7 @@ describe('read-only masterKey', () => { .then(done.fail) .catch(res => { expect(res.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN); - expect(res.data.error).toBe( - "read-only masterKey isn't allowed to create a schema." - ); + expect(res.data.error).toBe("read-only masterKey isn't allowed to create a schema."); done(); }); }); @@ -1110,9 +1059,7 @@ describe('read-only masterKey', () => { .then(done.fail) .catch(res => { expect(res.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN); - expect(res.data.error).toBe( - "read-only masterKey isn't allowed to create a schema." - ); + expect(res.data.error).toBe("read-only masterKey isn't allowed to create a schema."); done(); }); }); @@ -1131,9 +1078,7 @@ describe('read-only masterKey', () => { .then(done.fail) .catch(res => { expect(res.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN); - expect(res.data.error).toBe( - "read-only masterKey isn't allowed to update a schema." - ); + expect(res.data.error).toBe("read-only masterKey isn't allowed to update a schema."); done(); }); }); @@ -1152,9 +1097,7 @@ describe('read-only masterKey', () => { .then(done.fail) .catch(res => { expect(res.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN); - expect(res.data.error).toBe( - "read-only masterKey isn't allowed to delete a schema." - ); + expect(res.data.error).toBe("read-only masterKey isn't allowed to delete a schema."); done(); }); }); @@ -1173,9 +1116,7 @@ describe('read-only masterKey', () => { .then(done.fail) .catch(res => { expect(res.data.code).toBe(Parse.Error.OPERATION_FORBIDDEN); - expect(res.data.error).toBe( - "read-only masterKey isn't allowed to update the config." - ); + expect(res.data.error).toBe("read-only masterKey isn't allowed to update the config."); done(); }); }); diff --git a/spec/schemas.spec.js b/spec/schemas.spec.js index b06245da44..8dfeb867ab 100644 --- a/spec/schemas.spec.js +++ b/spec/schemas.spec.js @@ -5,8 +5,7 @@ const dd = require('deep-diff'); const Config = require('../lib/Config'); const request = require('../lib/request'); const TestUtils = require('../lib/TestUtils'); -const SchemaController = - require('../lib/Controllers/SchemaController').SchemaController; +const SchemaController = require('../lib/Controllers/SchemaController').SchemaController; let config; @@ -19,10 +18,7 @@ const hasAllPODobject = () => { obj.set('aObject', { k1: 'value', k2: true, k3: 5 }); obj.set('aArray', ['contents', true, 5]); obj.set('aGeoPoint', new Parse.GeoPoint({ latitude: 0, longitude: 0 })); - obj.set( - 'aFile', - new Parse.File('f.txt', { base64: 'V29ya2luZyBhdCBQYXJzZSBpcyBncmVhdCE=' }) - ); + obj.set('aFile', new Parse.File('f.txt', { base64: 'V29ya2luZyBhdCBQYXJzZSBpcyBncmVhdCE=' })); const objACL = new Parse.ACL(); objACL.setPublicWriteAccess(false); obj.setACL(objACL); @@ -177,9 +173,7 @@ describe('schemas', () => { headers: restKeyHeaders, }).then(fail, response => { expect(response.status).toEqual(403); - expect(response.data.error).toEqual( - 'unauthorized: master key is required' - ); + expect(response.data.error).toEqual('unauthorized: master key is required'); done(); }); }); @@ -191,9 +185,7 @@ describe('schemas', () => { headers: restKeyHeaders, }).then(fail, response => { expect(response.status).toEqual(403); - expect(response.data.error).toEqual( - 'unauthorized: master key is required' - ); + expect(response.data.error).toEqual('unauthorized: master key is required'); done(); }); }); @@ -215,11 +207,7 @@ describe('schemas', () => { delete withoutIndexes.indexes; return withoutIndexes; }) - ).toEqual( - expected.results.sort((s1, s2) => - s1.className.localeCompare(s2.className) - ) - ); + ).toEqual(expected.results.sort((s1, s2) => s1.className.localeCompare(s2.className))); done(); }); }); @@ -242,12 +230,7 @@ describe('schemas', () => { headers: masterKeyHeaders, }).then(response => { const expected = { - results: [ - userSchema, - roleSchema, - plainOldDataSchema, - pointersAndRelationsSchema, - ], + results: [userSchema, roleSchema, plainOldDataSchema, pointersAndRelationsSchema], }; expect( response.data.results @@ -257,20 +240,14 @@ describe('schemas', () => { delete withoutIndexes.indexes; return withoutIndexes; }) - ).toEqual( - expected.results.sort((s1, s2) => - s1.className.localeCompare(s2.className) - ) - ); + ).toEqual(expected.results.sort((s1, s2) => s1.className.localeCompare(s2.className))); done(); }); }); }); it('ensure refresh cache after creating a class', async done => { - spyOn(SchemaController.prototype, 'reloadData').and.callFake(() => - Promise.resolve() - ); + spyOn(SchemaController.prototype, 'reloadData').and.callFake(() => Promise.resolve()); await request({ url: 'http://localhost:8378/1/schemas', method: 'POST', @@ -311,11 +288,7 @@ describe('schemas', () => { delete withoutIndexes.indexes; return withoutIndexes; }) - ).toEqual( - expected.results.sort((s1, s2) => - s1.className.localeCompare(s2.className) - ) - ); + ).toEqual(expected.results.sort((s1, s2) => s1.className.localeCompare(s2.className))); done(); }); @@ -715,11 +688,7 @@ describe('schemas', () => { config.database .loadSchema() .then(schemaController => - schemaController.addClassIfNotExists( - '_Installation', - {}, - defaultClassLevelPermissions - ) + schemaController.addClassIfNotExists('_Installation', {}, defaultClassLevelPermissions) ) .then(() => { request({ @@ -859,9 +828,7 @@ describe('schemas', () => { }).then(fail, response => { expect(response.status).toEqual(400); expect(response.data.code).toEqual(255); - expect(response.data.error).toEqual( - 'Field aString exists, cannot update.' - ); + expect(response.data.error).toEqual('Field aString exists, cannot update.'); done(); }); }); @@ -883,9 +850,7 @@ describe('schemas', () => { }).then(fail, response => { expect(response.status).toEqual(400); expect(response.data.code).toEqual(255); - expect(response.data.error).toEqual( - 'Field nonExistentKey does not exist, cannot delete.' - ); + expect(response.data.error).toEqual('Field nonExistentKey does not exist, cannot delete.'); done(); }); }); @@ -1165,13 +1130,9 @@ describe('schemas', () => { obj.set('newRequiredField', 'some value'); await obj.save(); expect(obj.get('newRequiredField')).toEqual('some value'); - expect(obj.get('newRequiredFieldWithDefaultValue')).toEqual( - 'some value' - ); + expect(obj.get('newRequiredFieldWithDefaultValue')).toEqual('some value'); expect(obj.get('newNotRequiredField')).toEqual(undefined); - expect(obj.get('newNotRequiredFieldWithDefaultValue')).toEqual( - 'some value' - ); + expect(obj.get('newNotRequiredFieldWithDefaultValue')).toEqual('some value'); expect(obj.get('newRegularField')).toEqual(undefined); obj.set('newRequiredField', null); try { @@ -1192,13 +1153,9 @@ describe('schemas', () => { obj.set('newRequiredField', 'some value2'); await obj.save(); expect(obj.get('newRequiredField')).toEqual('some value2'); - expect(obj.get('newRequiredFieldWithDefaultValue')).toEqual( - 'some value' - ); + expect(obj.get('newRequiredFieldWithDefaultValue')).toEqual('some value'); expect(obj.get('newNotRequiredField')).toEqual(undefined); - expect(obj.get('newNotRequiredFieldWithDefaultValue')).toEqual( - 'some value' - ); + expect(obj.get('newNotRequiredFieldWithDefaultValue')).toEqual('some value'); expect(obj.get('newRegularField')).toEqual(undefined); obj.unset('newRequiredFieldWithDefaultValue'); try { @@ -1206,9 +1163,7 @@ describe('schemas', () => { fail('Should fail'); } catch (e) { expect(e.code).toEqual(142); - expect(e.message).toEqual( - 'newRequiredFieldWithDefaultValue is required' - ); + expect(e.message).toEqual('newRequiredFieldWithDefaultValue is required'); } obj.set('newRequiredFieldWithDefaultValue', ''); try { @@ -1216,9 +1171,7 @@ describe('schemas', () => { fail('Should fail'); } catch (e) { expect(e.code).toEqual(142); - expect(e.message).toEqual( - 'newRequiredFieldWithDefaultValue is required' - ); + expect(e.message).toEqual('newRequiredFieldWithDefaultValue is required'); } obj.set('newRequiredFieldWithDefaultValue', 'some value2'); obj.set('newNotRequiredField', ''); @@ -1226,9 +1179,7 @@ describe('schemas', () => { obj.unset('newRegularField'); await obj.save(); expect(obj.get('newRequiredField')).toEqual('some value2'); - expect(obj.get('newRequiredFieldWithDefaultValue')).toEqual( - 'some value2' - ); + expect(obj.get('newRequiredFieldWithDefaultValue')).toEqual('some value2'); expect(obj.get('newNotRequiredField')).toEqual(''); expect(obj.get('newNotRequiredFieldWithDefaultValue')).toEqual(null); expect(obj.get('newRegularField')).toEqual(undefined); @@ -1240,13 +1191,9 @@ describe('schemas', () => { obj.set('newRegularField', 'some value3'); await obj.save(); expect(obj.get('newRequiredField')).toEqual('some value3'); - expect(obj.get('newRequiredFieldWithDefaultValue')).toEqual( - 'some value3' - ); + expect(obj.get('newRequiredFieldWithDefaultValue')).toEqual('some value3'); expect(obj.get('newNotRequiredField')).toEqual('some value3'); - expect(obj.get('newNotRequiredFieldWithDefaultValue')).toEqual( - 'some value3' - ); + expect(obj.get('newNotRequiredFieldWithDefaultValue')).toEqual('some value3'); expect(obj.get('newRegularField')).toEqual('some value3'); done(); }); @@ -1616,9 +1563,7 @@ describe('schemas', () => { it('ensure refresh cache after deleting a class', async done => { config = Config.get('test'); spyOn(config.schemaCache, 'del').and.callFake(() => {}); - spyOn(SchemaController.prototype, 'reloadData').and.callFake(() => - Promise.resolve() - ); + spyOn(SchemaController.prototype, 'reloadData').and.callFake(() => Promise.resolve()); await request({ url: 'http://localhost:8378/1/schemas', method: 'POST', @@ -1651,11 +1596,7 @@ describe('schemas', () => { delete withoutIndexes.indexes; return withoutIndexes; }) - ).toEqual( - expected.results.sort((s1, s2) => - s1.className.localeCompare(s2.className) - ) - ); + ).toEqual(expected.results.sort((s1, s2) => s1.className.localeCompare(s2.className))); done(); }); @@ -1703,12 +1644,8 @@ describe('schemas', () => { }).then(fail, response => { //Expect _SCHEMA entry to be gone. expect(response.status).toEqual(400); - expect(response.data.code).toEqual( - Parse.Error.INVALID_CLASS_NAME - ); - expect(response.data.error).toEqual( - 'Class MyOtherClass does not exist.' - ); + expect(response.data.code).toEqual(Parse.Error.INVALID_CLASS_NAME); + expect(response.data.error).toEqual('Class MyOtherClass does not exist.'); done(); }); }); @@ -1772,9 +1709,7 @@ describe('schemas', () => { expect(typeof response.data.objectId).toEqual('string'); request({ method: 'DELETE', - url: - 'http://localhost:8378/1/classes/NewClassForDelete/' + - response.data.objectId, + url: 'http://localhost:8378/1/classes/NewClassForDelete/' + response.data.objectId, headers: restKeyHeaders, json: true, }).then(() => { @@ -1899,9 +1834,7 @@ describe('schemas', () => { done(); }, err => { - expect(err.message).toEqual( - 'Permission denied for action addField on class AClass.' - ); + expect(err.message).toEqual('Permission denied for action addField on class AClass.'); done(); } ); @@ -2092,9 +2025,7 @@ describe('schemas', () => { }, }, }).then(fail, response => { - expect(response.data.error).toEqual( - "' *' is not a valid key for class level permissions" - ); + expect(response.data.error).toEqual("' *' is not a valid key for class level permissions"); done(); }); }); @@ -2113,9 +2044,7 @@ describe('schemas', () => { }, }, }).then(fail, response => { - expect(response.data.error).toEqual( - "'* ' is not a valid key for class level permissions" - ); + expect(response.data.error).toEqual("'* ' is not a valid key for class level permissions"); done(); }); }); @@ -2156,9 +2085,7 @@ describe('schemas', () => { }, }).catch(error => error.data); - expect(response.error).toEqual( - `'true' is not a valid value for class level permissions acl` - ); + expect(response.error).toEqual(`'true' is not a valid value for class level permissions acl`); }); it('should validate defaultAcl with class level permissions when request is an object and invalid key', async () => { @@ -2178,9 +2105,7 @@ describe('schemas', () => { }, }).catch(error => error.data); - expect(response.error).toEqual( - `'foo' is not a valid key for class level permissions acl` - ); + expect(response.error).toEqual(`'foo' is not a valid key for class level permissions acl`); }); it('should validate defaultAcl with class level permissions when request is an object and invalid value', async () => { @@ -2200,9 +2125,7 @@ describe('schemas', () => { }, }).catch(error => error.data); - expect(response.error).toEqual( - `'1' is not a valid value for class level permissions acl` - ); + expect(response.error).toEqual(`'1' is not a valid value for class level permissions acl`); }); it('should throw if permission is empty string', done => { @@ -2281,9 +2204,7 @@ describe('schemas', () => { fail('Use should hot be able to find!'); }, err => { - expect(err.message).toEqual( - 'Permission denied for action find on class AClass.' - ); + expect(err.message).toEqual('Permission denied for action find on class AClass.'); return Promise.resolve(); } ); @@ -2343,9 +2264,7 @@ describe('schemas', () => { fail('User should not be able to find!'); }, err => { - expect(err.message).toEqual( - 'Permission denied for action find on class AClass.' - ); + expect(err.message).toEqual('Permission denied for action find on class AClass.'); return Promise.resolve(); } ); @@ -2430,9 +2349,7 @@ describe('schemas', () => { fail('User should not be able to find!'); }, err => { - expect(err.message).toEqual( - 'Permission denied for action find on class AClass.' - ); + expect(err.message).toEqual('Permission denied for action find on class AClass.'); return Promise.resolve(); } ); @@ -2508,9 +2425,7 @@ describe('schemas', () => { fail('User should not be able to find!'); }, err => { - expect(err.message).toEqual( - 'Permission denied for action find on class AClass.' - ); + expect(err.message).toEqual('Permission denied for action find on class AClass.'); return Promise.resolve(); } ); @@ -2541,9 +2456,7 @@ describe('schemas', () => { fail('User should not be able to find!'); }, err => { - expect(err.message).toEqual( - 'Permission denied for action find on class AClass.' - ); + expect(err.message).toEqual('Permission denied for action find on class AClass.'); return Promise.resolve(); } ); @@ -2627,9 +2540,7 @@ describe('schemas', () => { return Promise.resolve(); }, err => { - expect(err.message).toEqual( - 'Permission denied for action create on class AClass.' - ); + expect(err.message).toEqual('Permission denied for action create on class AClass.'); return Promise.resolve(); } ) @@ -2646,9 +2557,7 @@ describe('schemas', () => { return Promise.resolve(); }, err => { - expect(err.message).toEqual( - 'Permission denied for action find on class AClass.' - ); + expect(err.message).toEqual('Permission denied for action find on class AClass.'); return Promise.resolve(); } ) @@ -2972,10 +2881,7 @@ describe('schemas', () => { }); await expectAsync(schemaCreation()).toBeRejectedWith( - new Parse.Error( - Parse.Error.INVALID_KEY_NAME, - `invalid field name: ${fieldName}` - ) + new Parse.Error(Parse.Error.INVALID_KEY_NAME, `invalid field name: ${fieldName}`) ); done(); }); @@ -2987,12 +2893,7 @@ describe('schemas', () => { object.save({ '!12field': 'field', }) - ).toBeRejectedWith( - new Parse.Error( - Parse.Error.INVALID_KEY_NAME, - 'Invalid key name: !12field' - ) - ); + ).toBeRejectedWith(new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'Invalid key name: !12field')); done(); }); @@ -3131,9 +3032,7 @@ describe('schemas', () => { }, }).then(fail, response => { expect(response.data.code).toBe(Parse.Error.INVALID_QUERY); - expect(response.data.error).toBe( - 'Field aString does not exist, cannot add index.' - ); + expect(response.data.error).toBe('Field aString does not exist, cannot add index.'); done(); }); }); @@ -3187,9 +3086,7 @@ describe('schemas', () => { }, }).then(fail, response => { expect(response.data.code).toBe(Parse.Error.INVALID_QUERY); - expect(response.data.error).toBe( - 'Field bString does not exist, cannot add index.' - ); + expect(response.data.error).toBe('Field bString does not exist, cannot add index.'); done(); }); }); @@ -3329,76 +3226,73 @@ describe('schemas', () => { }); }); - it_only_db('mongo')( - 'lets you add index with with pointer like structure', - done => { + it_only_db('mongo')('lets you add index with with pointer like structure', done => { + request({ + url: 'http://localhost:8378/1/schemas/NewClass', + method: 'POST', + headers: masterKeyHeaders, + json: true, + body: {}, + }).then(() => { request({ url: 'http://localhost:8378/1/schemas/NewClass', - method: 'POST', + method: 'PUT', headers: masterKeyHeaders, json: true, - body: {}, - }).then(() => { + body: { + fields: { + aPointer: { type: 'Pointer', targetClass: 'NewClass' }, + }, + indexes: { + pointer: { _p_aPointer: 1 }, + }, + }, + }).then(response => { + expect( + dd(response.data, { + className: 'NewClass', + fields: { + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + objectId: { type: 'String' }, + aPointer: { type: 'Pointer', targetClass: 'NewClass' }, + }, + classLevelPermissions: defaultClassLevelPermissions, + indexes: { + _id_: { _id: 1 }, + pointer: { _p_aPointer: 1 }, + }, + }) + ).toEqual(undefined); request({ url: 'http://localhost:8378/1/schemas/NewClass', - method: 'PUT', headers: masterKeyHeaders, json: true, - body: { + }).then(response => { + expect(response.data).toEqual({ + className: 'NewClass', fields: { + ACL: { type: 'ACL' }, + createdAt: { type: 'Date' }, + updatedAt: { type: 'Date' }, + objectId: { type: 'String' }, aPointer: { type: 'Pointer', targetClass: 'NewClass' }, }, + classLevelPermissions: defaultClassLevelPermissions, indexes: { + _id_: { _id: 1 }, pointer: { _p_aPointer: 1 }, }, - }, - }).then(response => { - expect( - dd(response.data, { - className: 'NewClass', - fields: { - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - objectId: { type: 'String' }, - aPointer: { type: 'Pointer', targetClass: 'NewClass' }, - }, - classLevelPermissions: defaultClassLevelPermissions, - indexes: { - _id_: { _id: 1 }, - pointer: { _p_aPointer: 1 }, - }, - }) - ).toEqual(undefined); - request({ - url: 'http://localhost:8378/1/schemas/NewClass', - headers: masterKeyHeaders, - json: true, - }).then(response => { - expect(response.data).toEqual({ - className: 'NewClass', - fields: { - ACL: { type: 'ACL' }, - createdAt: { type: 'Date' }, - updatedAt: { type: 'Date' }, - objectId: { type: 'String' }, - aPointer: { type: 'Pointer', targetClass: 'NewClass' }, - }, - classLevelPermissions: defaultClassLevelPermissions, - indexes: { - _id_: { _id: 1 }, - pointer: { _p_aPointer: 1 }, - }, - }); - config.database.adapter.getIndexes('NewClass').then(indexes => { - expect(indexes.length).toEqual(2); - done(); - }); + }); + config.database.adapter.getIndexes('NewClass').then(indexes => { + expect(indexes.length).toEqual(2); + done(); }); }); }); - } - ); + }); + }); it('lets you add multiple indexes', done => { request({ @@ -3787,9 +3681,7 @@ describe('schemas', () => { }, }).then(fail, response => { expect(response.data.code).toBe(Parse.Error.INVALID_QUERY); - expect(response.data.error).toBe( - 'Index unknownIndex does not exist, cannot delete.' - ); + expect(response.data.error).toBe('Index unknownIndex does not exist, cannot delete.'); done(); }); }); @@ -3829,9 +3721,7 @@ describe('schemas', () => { }, }).then(fail, response => { expect(response.data.code).toBe(Parse.Error.INVALID_QUERY); - expect(response.data.error).toBe( - 'Index name1 exists, cannot update.' - ); + expect(response.data.error).toBe('Index name1 exists, cannot update.'); done(); }); }); @@ -3893,15 +3783,9 @@ describe('schemas', () => { }).then(response => { expect(response.data.indexes._id_).toBeDefined(); expect(response.data.indexes._id_._id).toEqual(1); - expect( - response.data.indexes.subject_text_comment_text - ).toBeDefined(); - expect( - response.data.indexes.subject_text_comment_text.subject - ).toEqual('text'); - expect( - response.data.indexes.subject_text_comment_text.comment - ).toEqual('text'); + expect(response.data.indexes.subject_text_comment_text).toBeDefined(); + expect(response.data.indexes.subject_text_comment_text.subject).toEqual('text'); + expect(response.data.indexes.subject_text_comment_text.comment).toEqual('text'); done(); }); }); diff --git a/spec/support/CurrentSpecReporter.js b/spec/support/CurrentSpecReporter.js index 144cd3ec19..46434cb89f 100755 --- a/spec/support/CurrentSpecReporter.js +++ b/spec/support/CurrentSpecReporter.js @@ -39,8 +39,7 @@ class CurrentSpecReporter { delete timerMap[result.fullName]; return; } - timerMap[result.fullName] = - (performance.now() - timerMap[result.fullName]) / 1000; + timerMap[result.fullName] = (performance.now() - timerMap[result.fullName]) / 1000; global.currentSpec = null; } } @@ -102,14 +101,12 @@ global.retryFlakyTests = function () { exceptionCaught = exception; } const failed = - !spec.markedPending && - (exceptionCaught || spec.result.failedExpectations.length != 0); + !spec.markedPending && (exceptionCaught || spec.result.failedExpectations.length != 0); if (!failed) { break; } if (isFlaky) { - retryMap[spec.result.fullName] = - (retryMap[spec.result.fullName] || 0) + 1; + retryMap[spec.result.fullName] = (retryMap[spec.result.fullName] || 0) + 1; await global.afterEachFn(); } } diff --git a/spec/support/FailingServer.js b/spec/support/FailingServer.js index 5fc0c90e81..60112ae82c 100755 --- a/spec/support/FailingServer.js +++ b/spec/support/FailingServer.js @@ -1,14 +1,10 @@ #!/usr/bin/env node -const MongoStorageAdapter = - require('../../lib/Adapters/Storage/Mongo/MongoStorageAdapter').default; -const { - GridFSBucketAdapter, -} = require('../../lib/Adapters/Files/GridFSBucketAdapter'); +const MongoStorageAdapter = require('../../lib/Adapters/Storage/Mongo/MongoStorageAdapter').default; +const { GridFSBucketAdapter } = require('../../lib/Adapters/Files/GridFSBucketAdapter'); const ParseServer = require('../../lib/index').ParseServer; -const databaseURI = - 'mongodb://doesnotexist:27017/parseServerMongoAdapterTestDatabase'; +const databaseURI = 'mongodb://doesnotexist:27017/parseServerMongoAdapterTestDatabase'; (async () => { try { diff --git a/spec/support/dev.js b/spec/support/dev.js index 3978dc2e9b..3415387c14 100644 --- a/spec/support/dev.js +++ b/spec/support/dev.js @@ -32,10 +32,7 @@ module.exports = { * @param {string} [password='password'] - optional, defaults to "password" if not set; */ logIn: async (userObject, password) => { - return await Parse.User.logIn( - userObject.getUsername(), - password || 'password' - ); + return await Parse.User.logIn(userObject.getUsername(), password || 'password'); }, /** diff --git a/spec/vulnerabilities.spec.js b/spec/vulnerabilities.spec.js index f01b03b95a..ac80ccb8f9 100644 --- a/spec/vulnerabilities.spec.js +++ b/spec/vulnerabilities.spec.js @@ -15,9 +15,7 @@ describe('Vulnerabilities', () => { it('denies user creation with poisoned object ID', async () => { await expectAsync( new Parse.User({ id: 'role:a', username: 'a', password: '123' }).save() - ).toBeRejectedWith( - new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Invalid object ID.') - ); + ).toBeRejectedWith(new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Invalid object ID.')); }); describe('existing sessions for users with poisoned object ID', () => { @@ -45,10 +43,7 @@ describe('Vulnerabilities', () => { sessionToken: poisonedUser.getSessionToken(), }) ).toBeRejectedWith( - new Parse.Error( - Parse.Error.INTERNAL_SERVER_ERROR, - 'Invalid object ID.' - ) + new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Invalid object ID.') ); await new Parse.Query(Parse.User).find({ sessionToken: innocentUser.getSessionToken(), @@ -81,9 +76,7 @@ describe('Vulnerabilities', () => { expect(response.status).toBe(400); const text = JSON.parse(response.text); expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME); - expect(text.error).toBe( - 'Prohibited keyword in request data: {"key":"constructor"}.' - ); + expect(text.error).toBe('Prohibited keyword in request data: {"key":"constructor"}.'); expect(Object.prototype.dummy).toBeUndefined(); }); @@ -116,9 +109,7 @@ describe('Vulnerabilities', () => { expect(pollResponse.status).toBe(400); const text = JSON.parse(pollResponse.text); expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME); - expect(text.error).toBe( - 'Prohibited keyword in request data: {"key":"constructor"}.' - ); + expect(text.error).toBe('Prohibited keyword in request data: {"key":"constructor"}.'); expect(Object.prototype.dummy).toBeUndefined(); }); @@ -137,9 +128,7 @@ describe('Vulnerabilities', () => { expect(response.status).toBe(400); const text = JSON.parse(response.text); expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME); - expect(text.error).toBe( - 'Prohibited keyword in request data: {"key":"__proto__"}.' - ); + expect(text.error).toBe('Prohibited keyword in request data: {"key":"__proto__"}.'); expect(Object.prototype.dummy).toBeUndefined(); }); }); @@ -178,10 +167,7 @@ describe('Vulnerabilities', () => { }); obj.addUnique('a.foo', 'abc'); await expectAsync(obj.save()).toBeRejectedWith( - new Parse.Error( - Parse.Error.INVALID_KEY_NAME, - `Prohibited keyword in request data: "foo".` - ) + new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Prohibited keyword in request data: "foo".`) ); }); @@ -291,14 +277,8 @@ describe('Vulnerabilities', () => { }); res.json({ success: object }); }); - await Parse.Hooks.createTrigger( - 'TestObject', - 'beforeSave', - hookServerURL + '/BeforeSave' - ); - await expectAsync( - new Parse.Object('TestObject').save() - ).toBeRejectedWith( + await Parse.Hooks.createTrigger('TestObject', 'beforeSave', hookServerURL + '/BeforeSave'); + await expectAsync(new Parse.Object('TestObject').save()).toBeRejectedWith( new Parse.Error( Parse.Error.INVALID_KEY_NAME, 'Prohibited keyword in request data: {"key":"constructor"}.' @@ -423,9 +403,7 @@ describe('Vulnerabilities', () => { expect(response.status).toBe(400); const text = JSON.parse(response.text); expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME); - expect(text.error).toBe( - 'Prohibited keyword in request data: {"key":"a[K]ey"}.' - ); + expect(text.error).toBe('Prohibited keyword in request data: {"key":"a[K]ey"}.'); }); it('denies write request with custom denylist of value', async () => { @@ -452,9 +430,7 @@ describe('Vulnerabilities', () => { expect(response.status).toBe(400); const text = JSON.parse(response.text); expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME); - expect(text.error).toBe( - 'Prohibited keyword in request data: {"value":"aValue[123]*"}.' - ); + expect(text.error).toBe('Prohibited keyword in request data: {"value":"aValue[123]*"}.'); }); it('denies BSON type code data in file metadata', async () => { diff --git a/src/AccountLockout.js b/src/AccountLockout.js index 839b6e8e0a..13d655e6b7 100644 --- a/src/AccountLockout.js +++ b/src/AccountLockout.js @@ -82,27 +82,23 @@ export class AccountLockout { const updateFields = { _account_lockout_expires_at: Parse._encode( - new Date( - now.getTime() + this._config.accountLockout.duration * 60 * 1000 - ) + new Date(now.getTime() + this._config.accountLockout.duration * 60 * 1000) ), }; - return this._config.database - .update('_User', query, updateFields) - .catch(err => { - if ( - err && - err.code && - err.message && - err.code === Parse.Error.OBJECT_NOT_FOUND && - err.message === 'Object not found.' - ) { - return; // nothing to update so we are good - } else { - throw err; // unknown error - } - }); + return this._config.database.update('_User', query, updateFields).catch(err => { + if ( + err && + err.code && + err.message && + err.code === Parse.Error.OBJECT_NOT_FOUND && + err.message === 'Object not found.' + ) { + return; // nothing to update so we are good + } else { + throw err; // unknown error + } + }); } /** @@ -167,10 +163,7 @@ export class AccountLockout { * Removes the account lockout. */ unlockAccount() { - if ( - !this._config.accountLockout || - !this._config.accountLockout.unlockOnPasswordReset - ) { + if (!this._config.accountLockout || !this._config.accountLockout.unlockOnPasswordReset) { return Promise.resolve(); } return this._config.database.update( diff --git a/src/Auth.js b/src/Auth.js index 6ae69c90f7..a93f49051f 100644 --- a/src/Auth.js +++ b/src/Auth.js @@ -177,30 +177,18 @@ const getAuthForSessionToken = async function ({ } if (results.length !== 1 || !results[0]['user']) { - throw new Parse.Error( - Parse.Error.INVALID_SESSION_TOKEN, - 'Invalid session token' - ); + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid session token'); } const session = results[0]; const now = new Date(), expiresAt = session.expiresAt ? new Date(session.expiresAt.iso) : undefined; if (expiresAt < now) { - throw new Parse.Error( - Parse.Error.INVALID_SESSION_TOKEN, - 'Session token is expired.' - ); + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Session token is expired.'); } const obj = session.user; - if ( - typeof obj['objectId'] === 'string' && - obj['objectId'].startsWith('role:') - ) { - throw new Parse.Error( - Parse.Error.INTERNAL_SERVER_ERROR, - 'Invalid object ID.' - ); + if (typeof obj['objectId'] === 'string' && obj['objectId'].startsWith('role:')) { + throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Invalid object ID.'); } delete obj.password; @@ -220,11 +208,7 @@ const getAuthForSessionToken = async function ({ }); }; -var getAuthForLegacySessionToken = async function ({ - config, - sessionToken, - installationId, -}) { +var getAuthForLegacySessionToken = async function ({ config, sessionToken, installationId }) { var restOptions = { limit: 1, }; @@ -241,10 +225,7 @@ var getAuthForLegacySessionToken = async function ({ return query.execute().then(response => { var results = response.results; if (results.length !== 1) { - throw new Parse.Error( - Parse.Error.INVALID_SESSION_TOKEN, - 'invalid legacy session token' - ); + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'invalid legacy session token'); } const obj = results[0]; obj.className = '_User'; @@ -334,10 +315,7 @@ Auth.prototype._loadRoles = async function () { ); // run the recursive finding - const roleNames = await this._getAllRolesNamesForRoleIds( - rolesMap.ids, - rolesMap.names - ); + const roleNames = await this._getAllRolesNamesForRoleIds(rolesMap.ids, rolesMap.names); this.userRoles = roleNames.map(r => { return 'role:' + r; }); @@ -402,11 +380,7 @@ Auth.prototype.getRolesByIds = async function (ins) { }; // Given a list of roleIds, find all the parent roles, returns a promise with all names -Auth.prototype._getAllRolesNamesForRoleIds = function ( - roleIDs, - names = [], - queriedRoles = {} -) { +Auth.prototype._getAllRolesNamesForRoleIds = function (roleIDs, names = [], queriedRoles = {}) { const ins = roleIDs.filter(roleID => { const wasQueried = queriedRoles[roleID] !== true; queriedRoles[roleID] = true; @@ -436,11 +410,7 @@ Auth.prototype._getAllRolesNamesForRoleIds = function ( // store the new found names names = names.concat(resultMap.names); // find the next ones, circular roles will be cut - return this._getAllRolesNamesForRoleIds( - resultMap.ids, - names, - queriedRoles - ); + return this._getAllRolesNamesForRoleIds(resultMap.ids, names, queriedRoles); }) .then(names => { return Promise.resolve([...new Set(names)]); @@ -454,8 +424,7 @@ const findUsersWithAuthData = async (config, authData, beforeFind) => { providers.map(async provider => { const providerAuthData = authData[provider]; - const adapter = - config.authDataManager.getValidatorForProvider(provider)?.adapter; + const adapter = config.authDataManager.getValidatorForProvider(provider)?.adapter; if (beforeFind && typeof adapter?.beforeFind === 'function') { await adapter.beforeFind(providerAuthData); } @@ -512,10 +481,7 @@ const checkIfUserHasProvidedConfiguredProvidersForLogin = ( const hasProvidedASoloProvider = savedUserProviders.some( provider => - provider && - provider.adapter && - provider.adapter.policy === 'solo' && - authData[provider.name] + provider && provider.adapter && provider.adapter.policy === 'solo' && authData[provider.name] ); // Solo providers can be considered as safe, so we do not have to check if the user needs @@ -526,35 +492,26 @@ const checkIfUserHasProvidedConfiguredProvidersForLogin = ( } const additionProvidersNotFound = []; - const hasProvidedAtLeastOneAdditionalProvider = savedUserProviders.some( - provider => { - let policy = provider.adapter.policy; - if (typeof policy === 'function') { - const requestObject = { - ip: req.config.ip, - user: req.auth.user, - master: req.auth.isMaster, - }; - policy = policy.call( - provider.adapter, - requestObject, - userAuthData[provider.name] - ); - } - if (policy === 'additional') { - if (authData[provider.name]) { - return true; - } else { - // Push missing provider for error message - additionProvidersNotFound.push(provider.name); - } + const hasProvidedAtLeastOneAdditionalProvider = savedUserProviders.some(provider => { + let policy = provider.adapter.policy; + if (typeof policy === 'function') { + const requestObject = { + ip: req.config.ip, + user: req.auth.user, + master: req.auth.isMaster, + }; + policy = policy.call(provider.adapter, requestObject, userAuthData[provider.name]); + } + if (policy === 'additional') { + if (authData[provider.name]) { + return true; + } else { + // Push missing provider for error message + additionProvidersNotFound.push(provider.name); } } - ); - if ( - hasProvidedAtLeastOneAdditionalProvider || - !additionProvidersNotFound.length - ) { + }); + if (hasProvidedAtLeastOneAdditionalProvider || !additionProvidersNotFound.length) { return; } @@ -575,10 +532,7 @@ const handleAuthDataValidation = async (authData, req, foundUser) => { req.auth.user && typeof req.getUserId === 'function' && req.getUserId() === req.auth.user.id) || - (req.auth && - req.auth.isMaster && - typeof req.getUserId === 'function' && - req.getUserId()) + (req.auth && req.auth.isMaster && typeof req.getUserId === 'function' && req.getUserId()) ) { user = new Parse.User(); user.id = req.auth.isMaster ? req.getUserId() : req.auth.user.id; @@ -586,13 +540,7 @@ const handleAuthDataValidation = async (authData, req, foundUser) => { } const { updatedObject } = req.buildParseObjects(); - const requestObject = getRequestObject( - undefined, - req.auth, - updatedObject, - user, - req.config - ); + const requestObject = getRequestObject(undefined, req.auth, updatedObject, user, req.config); // Perform validation as step-by-step pipeline for better error consistency // and also to avoid to trigger a provider (like OTP SMS) if another one fails const acc = { authData: {}, authDataResponse: {} }; @@ -604,8 +552,7 @@ const handleAuthDataValidation = async (authData, req, foundUser) => { acc.authData[provider] = null; continue; } - const { validator } = - req.config.authDataManager.getValidatorForProvider(provider) || {}; + const { validator } = req.config.authDataManager.getValidatorForProvider(provider) || {}; const authProvider = (req.config.auth || {})[provider] || {}; if (!validator || authProvider.enabled === false) { throw new Parse.Error( @@ -613,12 +560,7 @@ const handleAuthDataValidation = async (authData, req, foundUser) => { 'This authentication method is unsupported.' ); } - let validationResult = await validator( - authData[provider], - req, - user, - requestObject - ); + let validationResult = await validator(authData[provider], req, user, requestObject); method = validationResult && validationResult.method; requestObject.triggerName = method; if (validationResult && validationResult.validator) { @@ -646,9 +588,7 @@ const handleAuthDataValidation = async (authData, req, foundUser) => { message: 'Auth failed. Unknown error.', }); const userString = - req.auth && req.auth.user - ? req.auth.user.id - : req.data.objectId || undefined; + req.auth && req.auth.user ? req.auth.user.id : req.data.objectId || undefined; logger.error( `Failed running auth step ${method} for ${provider} for user ${userString} with Error: ` + JSON.stringify(e), diff --git a/src/Controllers/AdaptableController.js b/src/Controllers/AdaptableController.js index 6fc576845b..15551a6e38 100644 --- a/src/Controllers/AdaptableController.js +++ b/src/Controllers/AdaptableController.js @@ -47,27 +47,20 @@ export class AdaptableController { } // Makes sure the prototype matches - const mismatches = Object.getOwnPropertyNames(Type.prototype).reduce( - (obj, key) => { - const adapterType = typeof adapter[key]; - const expectedType = typeof Type.prototype[key]; - if (adapterType !== expectedType) { - obj[key] = { - expected: expectedType, - actual: adapterType, - }; - } - return obj; - }, - {} - ); + const mismatches = Object.getOwnPropertyNames(Type.prototype).reduce((obj, key) => { + const adapterType = typeof adapter[key]; + const expectedType = typeof Type.prototype[key]; + if (adapterType !== expectedType) { + obj[key] = { + expected: expectedType, + actual: adapterType, + }; + } + return obj; + }, {}); if (Object.keys(mismatches).length > 0) { - throw new Error( - "Adapter prototype don't match expected prototype", - adapter, - mismatches - ); + throw new Error("Adapter prototype don't match expected prototype", adapter, mismatches); } } } diff --git a/src/Controllers/AnalyticsController.js b/src/Controllers/AnalyticsController.js index e6016a4a5b..af74681d86 100644 --- a/src/Controllers/AnalyticsController.js +++ b/src/Controllers/AnalyticsController.js @@ -18,11 +18,7 @@ export class AnalyticsController extends AdaptableController { trackEvent(req) { return Promise.resolve() .then(() => { - return this.adapter.trackEvent( - req.params.eventName, - req.body || {}, - req - ); + return this.adapter.trackEvent(req.params.eventName, req.body || {}, req); }) .then(response => { return { response: response || {} }; diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index ea815650ed..9c1fdc7033 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -19,10 +19,7 @@ import PostgresStorageAdapter from '../Adapters/Storage/Postgres/PostgresStorage import SchemaCache from '../Adapters/Cache/SchemaCache'; import type { LoadSchemaOptions } from './types'; import type { ParseServerOptions } from '../Options'; -import type { - QueryOptions, - FullQueryOptions, -} from '../Adapters/Storage/StorageAdapter'; +import type { QueryOptions, FullQueryOptions } from '../Adapters/Storage/StorageAdapter'; function addWriteACL(query, acl) { const newQuery = _.cloneDeep(query); @@ -86,35 +83,23 @@ const validateQuery = ( if (query.$or) { if (query.$or instanceof Array) { - query.$or.forEach(value => - validateQuery(value, isMaster, isMaintenance, update) - ); + query.$or.forEach(value => validateQuery(value, isMaster, isMaintenance, update)); } else { - throw new Parse.Error( - Parse.Error.INVALID_QUERY, - 'Bad $or format - use an array value.' - ); + throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Bad $or format - use an array value.'); } } if (query.$and) { if (query.$and instanceof Array) { - query.$and.forEach(value => - validateQuery(value, isMaster, isMaintenance, update) - ); + query.$and.forEach(value => validateQuery(value, isMaster, isMaintenance, update)); } else { - throw new Parse.Error( - Parse.Error.INVALID_QUERY, - 'Bad $and format - use an array value.' - ); + throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Bad $and format - use an array value.'); } } if (query.$nor) { if (query.$nor instanceof Array && query.$nor.length > 0) { - query.$nor.forEach(value => - validateQuery(value, isMaster, isMaintenance, update) - ); + query.$nor.forEach(value => validateQuery(value, isMaster, isMaintenance, update)); } else { throw new Parse.Error( Parse.Error.INVALID_QUERY, @@ -139,10 +124,7 @@ const validateQuery = ( ((!specialQueryKeys.includes(key) && !isMaster && !update) || (update && isMaster && !specialMasterQueryKeys.includes(key))) ) { - throw new Parse.Error( - Parse.Error.INVALID_KEY_NAME, - `Invalid key name: ${key}` - ); + throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid key name: ${key}`); } }); }; @@ -166,9 +148,7 @@ const filterSensitiveData = ( // replace protectedFields when using pointer-permissions const perms = - schema && schema.getClassLevelPermissions - ? schema.getClassLevelPermissions(className) - : {}; + schema && schema.getClassLevelPermissions ? schema.getClassLevelPermissions(className) : {}; if (perms) { const isReadOperation = ['get', 'find'].indexOf(operation) > -1; @@ -194,8 +174,7 @@ const filterSensitiveData = ( ); } else { pointerPermIncludesUser = - readUserFieldValue.objectId && - readUserFieldValue.objectId === userId; + readUserFieldValue.objectId && readUserFieldValue.objectId === userId; } } @@ -298,10 +277,7 @@ const flattenUpdateOperatorsForCreate = object => { switch (object[key].__op) { case 'Increment': if (typeof object[key].amount !== 'number') { - throw new Parse.Error( - Parse.Error.INVALID_JSON, - 'objects to add must be an array' - ); + throw new Parse.Error(Parse.Error.INVALID_JSON, 'objects to add must be an array'); } object[key] = object[key].amount; break; @@ -310,28 +286,19 @@ const flattenUpdateOperatorsForCreate = object => { break; case 'Add': if (!(object[key].objects instanceof Array)) { - throw new Parse.Error( - Parse.Error.INVALID_JSON, - 'objects to add must be an array' - ); + throw new Parse.Error(Parse.Error.INVALID_JSON, 'objects to add must be an array'); } object[key] = object[key].objects; break; case 'AddUnique': if (!(object[key].objects instanceof Array)) { - throw new Parse.Error( - Parse.Error.INVALID_JSON, - 'objects to add must be an array' - ); + throw new Parse.Error(Parse.Error.INVALID_JSON, 'objects to add must be an array'); } object[key] = object[key].objects; break; case 'Remove': if (!(object[key].objects instanceof Array)) { - throw new Parse.Error( - Parse.Error.INVALID_JSON, - 'objects to add must be an array' - ); + throw new Parse.Error(Parse.Error.INVALID_JSON, 'objects to add must be an array'); } object[key] = []; break; @@ -451,10 +418,7 @@ class DatabaseController { validateClassName(className: string): Promise { if (!SchemaController.classNameIsValid(className)) { return Promise.reject( - new Parse.Error( - Parse.Error.INVALID_CLASS_NAME, - 'invalid className: ' + className - ) + new Parse.Error(Parse.Error.INVALID_CLASS_NAME, 'invalid className: ' + className) ); } return Promise.resolve(); @@ -479,9 +443,7 @@ class DatabaseController { schemaController: SchemaController.SchemaController, options: LoadSchemaOptions = { clearCache: false } ): Promise { - return schemaController - ? Promise.resolve(schemaController) - : this.loadSchema(options); + return schemaController ? Promise.resolve(schemaController) : this.loadSchema(options); } // Returns a promise for the classname that is related to the given @@ -518,13 +480,7 @@ class DatabaseController { if (isMaster) { return Promise.resolve(); } - return this.canAddField( - schema, - className, - object, - aclGroup, - runOptions - ); + return this.canAddField(schema, className, object, aclGroup, runOptions); }) .then(() => { return schema.validateObject(className, object, query, maintenance); @@ -543,9 +499,7 @@ class DatabaseController { try { Utils.checkProhibitedKeywords(this.options, update); } catch (error) { - return Promise.reject( - new Parse.Error(Parse.Error.INVALID_KEY_NAME, error) - ); + return Promise.reject(new Parse.Error(Parse.Error.INVALID_KEY_NAME, error)); } const originalQuery = query; const originalUpdate = update; @@ -555,168 +509,150 @@ class DatabaseController { var isMaster = acl === undefined; var aclGroup = acl || []; - return this.loadSchemaIfNeeded(validSchemaController).then( - schemaController => { - return ( - isMaster - ? Promise.resolve() - : schemaController.validatePermission(className, aclGroup, 'update') - ) - .then(() => { - relationUpdates = this.collectRelationUpdates( + return this.loadSchemaIfNeeded(validSchemaController).then(schemaController => { + return ( + isMaster + ? Promise.resolve() + : schemaController.validatePermission(className, aclGroup, 'update') + ) + .then(() => { + relationUpdates = this.collectRelationUpdates(className, originalQuery.objectId, update); + if (!isMaster) { + query = this.addPointerPermissions( + schemaController, className, - originalQuery.objectId, - update + 'update', + query, + aclGroup ); - if (!isMaster) { - query = this.addPointerPermissions( - schemaController, - className, - 'update', - query, - aclGroup - ); - if (addsField) { - query = { - $and: [ - query, - this.addPointerPermissions( - schemaController, - className, - 'addField', - query, - aclGroup - ), - ], - }; - } - } - if (!query) { - return Promise.resolve(); - } - if (acl) { - query = addWriteACL(query, acl); - } - validateQuery(query, isMaster, false, true); - return schemaController - .getOneSchema(className, true) - .catch(error => { - // If the schema doesn't exist, pretend it exists with no fields. This behavior - // will likely need revisiting. - if (error === undefined) { - return { fields: {} }; - } - throw error; - }) - .then(schema => { - Object.keys(update).forEach(fieldName => { - if (fieldName.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) { - throw new Parse.Error( - Parse.Error.INVALID_KEY_NAME, - `Invalid field name for update: ${fieldName}` - ); - } - const rootFieldName = getRootFieldName(fieldName); - if ( - !SchemaController.fieldNameIsValid( - rootFieldName, - className - ) && - !isSpecialUpdateKey(rootFieldName) - ) { - throw new Parse.Error( - Parse.Error.INVALID_KEY_NAME, - `Invalid field name for update: ${fieldName}` - ); - } - }); - for (const updateOperation in update) { - if ( - update[updateOperation] && - typeof update[updateOperation] === 'object' && - Object.keys(update[updateOperation]).some( - innerKey => - innerKey.includes('$') || innerKey.includes('.') - ) - ) { - throw new Parse.Error( - Parse.Error.INVALID_NESTED_KEY, - "Nested keys should not contain the '$' or '.' characters" - ); - } - } - update = transformObjectACL(update); - convertEmailToLowercase(update, className, this.options); - convertUsernameToLowercase(update, className, this.options); - transformAuthData(className, update, schema); - if (validateOnly) { - return this.adapter - .find(className, schema, query, {}) - .then(result => { - if (!result || !result.length) { - throw new Parse.Error( - Parse.Error.OBJECT_NOT_FOUND, - 'Object not found.' - ); - } - return {}; - }); - } - if (many) { - return this.adapter.updateObjectsByQuery( - className, - schema, - query, - update, - this._transactionalSession - ); - } else if (upsert) { - return this.adapter.upsertOneObject( + if (addsField) { + query = { + $and: [ + query, + this.addPointerPermissions( + schemaController, className, - schema, + 'addField', query, - update, - this._transactionalSession + aclGroup + ), + ], + }; + } + } + if (!query) { + return Promise.resolve(); + } + if (acl) { + query = addWriteACL(query, acl); + } + validateQuery(query, isMaster, false, true); + return schemaController + .getOneSchema(className, true) + .catch(error => { + // If the schema doesn't exist, pretend it exists with no fields. This behavior + // will likely need revisiting. + if (error === undefined) { + return { fields: {} }; + } + throw error; + }) + .then(schema => { + Object.keys(update).forEach(fieldName => { + if (fieldName.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) { + throw new Parse.Error( + Parse.Error.INVALID_KEY_NAME, + `Invalid field name for update: ${fieldName}` ); - } else { - return this.adapter.findOneAndUpdate( - className, - schema, - query, - update, - this._transactionalSession + } + const rootFieldName = getRootFieldName(fieldName); + if ( + !SchemaController.fieldNameIsValid(rootFieldName, className) && + !isSpecialUpdateKey(rootFieldName) + ) { + throw new Parse.Error( + Parse.Error.INVALID_KEY_NAME, + `Invalid field name for update: ${fieldName}` ); } }); - }) - .then((result: any) => { - if (!result) { - throw new Parse.Error( - Parse.Error.OBJECT_NOT_FOUND, - 'Object not found.' - ); - } - if (validateOnly) { - return result; - } - return this.handleRelationUpdates( - className, - originalQuery.objectId, - update, - relationUpdates - ).then(() => { - return result; + for (const updateOperation in update) { + if ( + update[updateOperation] && + typeof update[updateOperation] === 'object' && + Object.keys(update[updateOperation]).some( + innerKey => innerKey.includes('$') || innerKey.includes('.') + ) + ) { + throw new Parse.Error( + Parse.Error.INVALID_NESTED_KEY, + "Nested keys should not contain the '$' or '.' characters" + ); + } + } + update = transformObjectACL(update); + convertEmailToLowercase(update, className, this.options); + convertUsernameToLowercase(update, className, this.options); + transformAuthData(className, update, schema); + if (validateOnly) { + return this.adapter.find(className, schema, query, {}).then(result => { + if (!result || !result.length) { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.'); + } + return {}; + }); + } + if (many) { + return this.adapter.updateObjectsByQuery( + className, + schema, + query, + update, + this._transactionalSession + ); + } else if (upsert) { + return this.adapter.upsertOneObject( + className, + schema, + query, + update, + this._transactionalSession + ); + } else { + return this.adapter.findOneAndUpdate( + className, + schema, + query, + update, + this._transactionalSession + ); + } }); - }) - .then(result => { - if (skipSanitization) { - return Promise.resolve(result); - } - return this._sanitizeDatabaseResult(originalUpdate, result); + }) + .then((result: any) => { + if (!result) { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.'); + } + if (validateOnly) { + return result; + } + return this.handleRelationUpdates( + className, + originalQuery.objectId, + update, + relationUpdates + ).then(() => { + return result; }); - } - ); + }) + .then(result => { + if (skipSanitization) { + return Promise.resolve(result); + } + return this._sanitizeDatabaseResult(originalUpdate, result); + }); + }); } // Collect all relation-updating operations from a REST-format update. @@ -759,12 +695,7 @@ class DatabaseController { // Processes relation-updating operations from a REST-format update. // Returns a promise that resolves when all updates have been performed - handleRelationUpdates( - className: string, - objectId: string, - update: any, - ops: any - ) { + handleRelationUpdates(className: string, objectId: string, update: any, ops: any) { var pending = []; objectId = update.objectId || objectId; ops.forEach(({ key, op }) => { @@ -773,17 +704,13 @@ class DatabaseController { } if (op.__op == 'AddRelation') { for (const object of op.objects) { - pending.push( - this.addRelation(key, className, objectId, object.objectId) - ); + pending.push(this.addRelation(key, className, objectId, object.objectId)); } } if (op.__op == 'RemoveRelation') { for (const object of op.objects) { - pending.push( - this.removeRelation(key, className, objectId, object.objectId) - ); + pending.push(this.removeRelation(key, className, objectId, object.objectId)); } } }); @@ -793,12 +720,7 @@ class DatabaseController { // Adds a relation. // Returns a promise that resolves successfully iff the add was successful. - addRelation( - key: string, - fromClassName: string, - fromId: string, - toId: string - ) { + addRelation(key: string, fromClassName: string, fromId: string, toId: string) { const doc = { relatedId: toId, owningId: fromId, @@ -815,12 +737,7 @@ class DatabaseController { // Removes a relation. // Returns a promise that resolves successfully iff the remove was // successful. - removeRelation( - key: string, - fromClassName: string, - fromId: string, - toId: string - ) { + removeRelation(key: string, fromClassName: string, fromId: string, toId: string) { var doc = { relatedId: toId, owningId: fromId, @@ -857,64 +774,56 @@ class DatabaseController { const isMaster = acl === undefined; const aclGroup = acl || []; - return this.loadSchemaIfNeeded(validSchemaController).then( - schemaController => { - return ( - isMaster - ? Promise.resolve() - : schemaController.validatePermission(className, aclGroup, 'delete') - ).then(() => { - if (!isMaster) { - query = this.addPointerPermissions( - schemaController, + return this.loadSchemaIfNeeded(validSchemaController).then(schemaController => { + return ( + isMaster + ? Promise.resolve() + : schemaController.validatePermission(className, aclGroup, 'delete') + ).then(() => { + if (!isMaster) { + query = this.addPointerPermissions( + schemaController, + className, + 'delete', + query, + aclGroup + ); + if (!query) { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.'); + } + } + // delete by query + if (acl) { + query = addWriteACL(query, acl); + } + validateQuery(query, isMaster, false, false); + return schemaController + .getOneSchema(className) + .catch(error => { + // If the schema doesn't exist, pretend it exists with no fields. This behavior + // will likely need revisiting. + if (error === undefined) { + return { fields: {} }; + } + throw error; + }) + .then(parseFormatSchema => + this.adapter.deleteObjectsByQuery( className, - 'delete', + parseFormatSchema, query, - aclGroup - ); - if (!query) { - throw new Parse.Error( - Parse.Error.OBJECT_NOT_FOUND, - 'Object not found.' - ); - } - } - // delete by query - if (acl) { - query = addWriteACL(query, acl); - } - validateQuery(query, isMaster, false, false); - return schemaController - .getOneSchema(className) - .catch(error => { - // If the schema doesn't exist, pretend it exists with no fields. This behavior - // will likely need revisiting. - if (error === undefined) { - return { fields: {} }; - } - throw error; - }) - .then(parseFormatSchema => - this.adapter.deleteObjectsByQuery( - className, - parseFormatSchema, - query, - this._transactionalSession - ) + this._transactionalSession ) - .catch(error => { - // When deleting sessions while changing passwords, don't throw an error if they don't have any sessions. - if ( - className === '_Session' && - error.code === Parse.Error.OBJECT_NOT_FOUND - ) { - return Promise.resolve({}); - } - throw error; - }); - }); - } - ); + ) + .catch(error => { + // When deleting sessions while changing passwords, don't throw an error if they don't have any sessions. + if (className === '_Session' && error.code === Parse.Error.OBJECT_NOT_FOUND) { + return Promise.resolve({}); + } + throw error; + }); + }); + }); } // Inserts an object into the database. @@ -929,9 +838,7 @@ class DatabaseController { try { Utils.checkProhibitedKeywords(this.options, object); } catch (error) { - return Promise.reject( - new Parse.Error(Parse.Error.INVALID_KEY_NAME, error) - ); + return Promise.reject(new Parse.Error(Parse.Error.INVALID_KEY_NAME, error)); } // Make a copy of the object, so we don't mutate the incoming data. const originalObject = object; @@ -944,11 +851,7 @@ class DatabaseController { var isMaster = acl === undefined; var aclGroup = acl || []; - const relationUpdates = this.collectRelationUpdates( - className, - null, - object - ); + const relationUpdates = this.collectRelationUpdates(className, null, object); return this.validateClassName(className) .then(() => this.loadSchemaIfNeeded(validSchemaController)) @@ -983,10 +886,7 @@ class DatabaseController { object, relationUpdates ).then(() => { - return this._sanitizeDatabaseResult( - originalObject, - result.ops[0] - ); + return this._sanitizeDatabaseResult(originalObject, result.ops[0]); }); }); }); @@ -1007,11 +907,7 @@ class DatabaseController { const schemaFields = Object.keys(classSchema.fields); const newKeys = fields.filter(field => { // Skip fields that are unset - if ( - object[field] && - object[field].__op && - object[field].__op === 'Delete' - ) { + if (object[field] && object[field].__op && object[field].__op === 'Delete') { return false; } return schemaFields.indexOf(getRootFieldName(field)) < 0; @@ -1056,22 +952,13 @@ class DatabaseController { queryOptions.skip = 0; } return this.adapter - .find( - joinTableName(className, key), - relationSchema, - { owningId }, - findOptions - ) + .find(joinTableName(className, key), relationSchema, { owningId }, findOptions) .then(results => results.map(result => result.relatedId)); } // Returns a promise for a list of owning ids given some related ids. // className here is the owning className. - owningIds( - className: string, - key: string, - relatedIds: string[] - ): Promise { + owningIds(className: string, key: string, relatedIds: string[]): Promise { return this.adapter .find( joinTableName(className, key), @@ -1093,11 +980,9 @@ class DatabaseController { const ors = query['$or']; promises.push( ...ors.map((aQuery, index) => { - return this.reduceInRelation(className, aQuery, schema).then( - aQuery => { - query['$or'][index] = aQuery; - } - ); + return this.reduceInRelation(className, aQuery, schema).then(aQuery => { + query['$or'][index] = aQuery; + }); }) ); } @@ -1105,11 +990,9 @@ class DatabaseController { const ands = query['$and']; promises.push( ...ands.map((aQuery, index) => { - return this.reduceInRelation(className, aQuery, schema).then( - aQuery => { - query['$and'][index] = aQuery; - } - ); + return this.reduceInRelation(className, aQuery, schema).then(aQuery => { + query['$and'][index] = aQuery; + }); }) ); } @@ -1186,11 +1069,7 @@ class DatabaseController { // Modifies query so that it no longer has $relatedTo // Returns a promise that resolves when query is mutated - reduceRelationKeys( - className: string, - query: any, - queryOptions: any - ): ?Promise { + reduceRelationKeys(className: string, query: any, queryOptions: any): ?Promise { if (query['$or']) { return Promise.all( query['$or'].map(aQuery => { @@ -1231,12 +1110,9 @@ class DatabaseController { query.objectId && query.objectId['$in'] ? query.objectId['$in'] : null; // @flow-disable-next - const allIds: Array> = [ - idsFromString, - idsFromEq, - idsFromIn, - ids, - ].filter(list => list !== null); + const allIds: Array> = [idsFromString, idsFromEq, idsFromIn, ids].filter( + list => list !== null + ); const totalLength = allIds.reduce((memo, list) => memo + list.length, 0); let idsIntersection = []; @@ -1263,8 +1139,7 @@ class DatabaseController { } addNotInObjectIdsIds(ids: string[] = [], query: any) { - const idsFromNin = - query.objectId && query.objectId['$nin'] ? query.objectId['$nin'] : []; + const idsFromNin = query.objectId && query.objectId['$nin'] ? query.objectId['$nin'] : []; let allIds = [...idsFromNin, ...ids].filter(list => list !== null); // make a set and spread to remove duplicates @@ -1327,205 +1202,170 @@ class DatabaseController { const isMaster = acl === undefined || isMaintenance; const aclGroup = acl || []; op = - op || - (typeof query.objectId == 'string' && Object.keys(query).length === 1 - ? 'get' - : 'find'); + op || (typeof query.objectId == 'string' && Object.keys(query).length === 1 ? 'get' : 'find'); // Count operation if counting op = count === true ? 'count' : op; let classExists = true; - return this.loadSchemaIfNeeded(validSchemaController).then( - schemaController => { - //Allow volatile classes if querying with Master (for _PushStatus) - //TODO: Move volatile classes concept into mongo adapter, postgres adapter shouldn't care - //that api.parse.com breaks when _PushStatus exists in mongo. - return schemaController - .getOneSchema(className, isMaster) - .catch(error => { - // Behavior for non-existent classes is kinda weird on Parse.com. Probably doesn't matter too much. - // For now, pretend the class exists but has no objects, - if (error === undefined) { - classExists = false; - return { fields: {} }; + return this.loadSchemaIfNeeded(validSchemaController).then(schemaController => { + //Allow volatile classes if querying with Master (for _PushStatus) + //TODO: Move volatile classes concept into mongo adapter, postgres adapter shouldn't care + //that api.parse.com breaks when _PushStatus exists in mongo. + return schemaController + .getOneSchema(className, isMaster) + .catch(error => { + // Behavior for non-existent classes is kinda weird on Parse.com. Probably doesn't matter too much. + // For now, pretend the class exists but has no objects, + if (error === undefined) { + classExists = false; + return { fields: {} }; + } + throw error; + }) + .then(schema => { + // Parse.com treats queries on _created_at and _updated_at as if they were queries on createdAt and updatedAt, + // so duplicate that behavior here. If both are specified, the correct behavior to match Parse.com is to + // use the one that appears first in the sort list. + if (sort._created_at) { + sort.createdAt = sort._created_at; + delete sort._created_at; + } + if (sort._updated_at) { + sort.updatedAt = sort._updated_at; + delete sort._updated_at; + } + const queryOptions = { + skip, + limit, + sort, + keys, + readPreference, + hint, + caseInsensitive: this.options.enableCollationCaseComparison ? false : caseInsensitive, + explain, + comment, + }; + Object.keys(sort).forEach(fieldName => { + if (fieldName.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) { + throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Cannot sort by ${fieldName}`); } - throw error; - }) - .then(schema => { - // Parse.com treats queries on _created_at and _updated_at as if they were queries on createdAt and updatedAt, - // so duplicate that behavior here. If both are specified, the correct behavior to match Parse.com is to - // use the one that appears first in the sort list. - if (sort._created_at) { - sort.createdAt = sort._created_at; - delete sort._created_at; + const rootFieldName = getRootFieldName(fieldName); + if (!SchemaController.fieldNameIsValid(rootFieldName, className)) { + throw new Parse.Error( + Parse.Error.INVALID_KEY_NAME, + `Invalid field name: ${fieldName}.` + ); } - if (sort._updated_at) { - sort.updatedAt = sort._updated_at; - delete sort._updated_at; + if (!schema.fields[fieldName.split('.')[0]] && fieldName !== 'score') { + delete sort[fieldName]; } - const queryOptions = { - skip, - limit, - sort, - keys, - readPreference, - hint, - caseInsensitive: this.options.enableCollationCaseComparison - ? false - : caseInsensitive, - explain, - comment, - }; - Object.keys(sort).forEach(fieldName => { - if (fieldName.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) { - throw new Parse.Error( - Parse.Error.INVALID_KEY_NAME, - `Cannot sort by ${fieldName}` + }); + return ( + isMaster + ? Promise.resolve() + : schemaController.validatePermission(className, aclGroup, op) + ) + .then(() => this.reduceRelationKeys(className, query, queryOptions)) + .then(() => this.reduceInRelation(className, query, schemaController)) + .then(() => { + let protectedFields; + if (!isMaster) { + query = this.addPointerPermissions( + schemaController, + className, + op, + query, + aclGroup ); - } - const rootFieldName = getRootFieldName(fieldName); - if ( - !SchemaController.fieldNameIsValid(rootFieldName, className) - ) { - throw new Parse.Error( - Parse.Error.INVALID_KEY_NAME, - `Invalid field name: ${fieldName}.` + /* Don't use projections to optimize the protectedFields since the protectedFields + based on pointer-permissions are determined after querying. The filtering can + overwrite the protected fields. */ + protectedFields = this.addProtectedFields( + schemaController, + className, + query, + aclGroup, + auth, + queryOptions ); } - if ( - !schema.fields[fieldName.split('.')[0]] && - fieldName !== 'score' - ) { - delete sort[fieldName]; + if (!query) { + if (op === 'get') { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.'); + } else { + return []; + } } - }); - return ( - isMaster - ? Promise.resolve() - : schemaController.validatePermission(className, aclGroup, op) - ) - .then(() => - this.reduceRelationKeys(className, query, queryOptions) - ) - .then(() => - this.reduceInRelation(className, query, schemaController) - ) - .then(() => { - let protectedFields; - if (!isMaster) { - query = this.addPointerPermissions( - schemaController, - className, - op, - query, - aclGroup - ); - /* Don't use projections to optimize the protectedFields since the protectedFields - based on pointer-permissions are determined after querying. The filtering can - overwrite the protected fields. */ - protectedFields = this.addProtectedFields( - schemaController, + if (!isMaster) { + if (op === 'update' || op === 'delete') { + query = addWriteACL(query, aclGroup); + } else { + query = addReadACL(query, aclGroup); + } + } + validateQuery(query, isMaster, isMaintenance, false); + if (count) { + if (!classExists) { + return 0; + } else { + return this.adapter.count( className, + schema, query, - aclGroup, - auth, - queryOptions + readPreference, + undefined, + hint, + comment ); } - if (!query) { - if (op === 'get') { - throw new Parse.Error( - Parse.Error.OBJECT_NOT_FOUND, - 'Object not found.' - ); - } else { - return []; - } - } - if (!isMaster) { - if (op === 'update' || op === 'delete') { - query = addWriteACL(query, aclGroup); - } else { - query = addReadACL(query, aclGroup); - } + } else if (distinct) { + if (!classExists) { + return []; + } else { + return this.adapter.distinct(className, schema, query, distinct); } - validateQuery(query, isMaster, isMaintenance, false); - if (count) { - if (!classExists) { - return 0; - } else { - return this.adapter.count( - className, - schema, - query, - readPreference, - undefined, - hint, - comment - ); - } - } else if (distinct) { - if (!classExists) { - return []; - } else { - return this.adapter.distinct( - className, - schema, - query, - distinct - ); - } - } else if (pipeline) { - if (!classExists) { - return []; - } else { - return this.adapter.aggregate( - className, - schema, - pipeline, - readPreference, - hint, - explain, - comment - ); - } - } else if (explain) { - return this.adapter.find( + } else if (pipeline) { + if (!classExists) { + return []; + } else { + return this.adapter.aggregate( className, schema, - query, - queryOptions + pipeline, + readPreference, + hint, + explain, + comment ); - } else { - return this.adapter - .find(className, schema, query, queryOptions) - .then(objects => - objects.map(object => { - object = untransformObjectACL(object); - return filterSensitiveData( - isMaster, - isMaintenance, - aclGroup, - auth, - op, - schemaController, - className, - protectedFields, - object - ); - }) - ) - .catch(error => { - throw new Parse.Error( - Parse.Error.INTERNAL_SERVER_ERROR, - error - ); - }); } - }); - }); - } - ); + } else if (explain) { + return this.adapter.find(className, schema, query, queryOptions); + } else { + return this.adapter + .find(className, schema, query, queryOptions) + .then(objects => + objects.map(object => { + object = untransformObjectACL(object); + return filterSensitiveData( + isMaster, + isMaintenance, + aclGroup, + auth, + op, + schemaController, + className, + protectedFields, + object + ); + }) + ) + .catch(error => { + throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, error); + }); + } + }); + }); + }); } deleteSchema(className: string): Promise { @@ -1544,9 +1384,7 @@ class DatabaseController { }) .then((schema: any) => { return this.collectionExists(className) - .then(() => - this.adapter.count(className, { fields: {} }, null, '', false) - ) + .then(() => this.adapter.count(className, { fields: {} }, null, '', false)) .then(count => { if (count > 0) { throw new Parse.Error( @@ -1580,9 +1418,7 @@ class DatabaseController { // key value pairs used in query objects. Each key value pair will represented // in a similar way to json objectToEntriesStrings(query: any): Array { - return Object.entries(query).map(a => - a.map(s => JSON.stringify(s)).join(':') - ); + return Object.entries(query).map(a => a.map(s => JSON.stringify(s)).join(':')); } // Naive logic reducer for OR operations meant to be used only for pointer permissions. @@ -1596,8 +1432,7 @@ class DatabaseController { repeat = false; for (let i = 0; i < queries.length - 1; i++) { for (let j = i + 1; j < queries.length; j++) { - const [shorter, longer] = - queries[i].length > queries[j].length ? [j, i] : [i, j]; + const [shorter, longer] = queries[i].length > queries[j].length ? [j, i] : [i, j]; const foundEntries = queries[shorter].reduce( (acc, entry) => acc + (queries[longer].includes(entry) ? 1 : 0), 0 @@ -1632,8 +1467,7 @@ class DatabaseController { repeat = false; for (let i = 0; i < queries.length - 1; i++) { for (let j = i + 1; j < queries.length; j++) { - const [shorter, longer] = - queries[i].length > queries[j].length ? [j, i] : [i, j]; + const [shorter, longer] = queries[i].length > queries[j].length ? [j, i] : [i, j]; const foundEntries = queries[shorter].reduce( (acc, entry) => acc + (queries[longer].includes(entry) ? 1 : 0), 0 @@ -1681,9 +1515,7 @@ class DatabaseController { }); const groupKey = - ['get', 'find', 'count'].indexOf(operation) > -1 - ? 'readUserFields' - : 'writeUserFields'; + ['get', 'find', 'count'].indexOf(operation) > -1 ? 'readUserFields' : 'writeUserFields'; const permFields = []; @@ -1748,9 +1580,7 @@ class DatabaseController { return Object.assign({}, query, queryClause); }); - return queries.length === 1 - ? queries[0] - : this.reduceOrOperation({ $or: queries }); + return queries.length === 1 ? queries[0] : this.reduceOrOperation({ $or: queries }); } else { return query; } @@ -1869,33 +1699,27 @@ class DatabaseController { } createTransactionalSession() { - return this.adapter - .createTransactionalSession() - .then(transactionalSession => { - this._transactionalSession = transactionalSession; - }); + return this.adapter.createTransactionalSession().then(transactionalSession => { + this._transactionalSession = transactionalSession; + }); } commitTransactionalSession() { if (!this._transactionalSession) { throw new Error('There is no transactional session to commit'); } - return this.adapter - .commitTransactionalSession(this._transactionalSession) - .then(() => { - this._transactionalSession = null; - }); + return this.adapter.commitTransactionalSession(this._transactionalSession).then(() => { + this._transactionalSession = null; + }); } abortTransactionalSession() { if (!this._transactionalSession) { throw new Error('There is no transactional session to abort'); } - return this.adapter - .abortTransactionalSession(this._transactionalSession) - .then(() => { - this._transactionalSession = null; - }); + return this.adapter.abortTransactionalSession(this._transactionalSession).then(() => { + this._transactionalSession = null; + }); } // TODO: create indexes on first creation of a _User object. Otherwise it's impossible to @@ -1924,72 +1748,43 @@ class DatabaseController { }; await this.loadSchema().then(schema => schema.enforceClassExists('_User')); await this.loadSchema().then(schema => schema.enforceClassExists('_Role')); - await this.loadSchema().then(schema => - schema.enforceClassExists('_Idempotency') - ); + await this.loadSchema().then(schema => schema.enforceClassExists('_Idempotency')); - await this.adapter - .ensureUniqueness('_User', requiredUserFields, ['username']) - .catch(error => { - logger.warn('Unable to ensure uniqueness for usernames: ', error); - throw error; - }); + await this.adapter.ensureUniqueness('_User', requiredUserFields, ['username']).catch(error => { + logger.warn('Unable to ensure uniqueness for usernames: ', error); + throw error; + }); if (!this.options.enableCollationCaseComparison) { await this.adapter - .ensureIndex( - '_User', - requiredUserFields, - ['username'], - 'case_insensitive_username', - true - ) + .ensureIndex('_User', requiredUserFields, ['username'], 'case_insensitive_username', true) .catch(error => { - logger.warn( - 'Unable to create case insensitive username index: ', - error - ); + logger.warn('Unable to create case insensitive username index: ', error); throw error; }); await this.adapter - .ensureIndex( - '_User', - requiredUserFields, - ['email'], - 'case_insensitive_email', - true - ) + .ensureIndex('_User', requiredUserFields, ['email'], 'case_insensitive_email', true) .catch(error => { logger.warn('Unable to create case insensitive email index: ', error); throw error; }); } - await this.adapter - .ensureUniqueness('_User', requiredUserFields, ['email']) - .catch(error => { - logger.warn( - 'Unable to ensure uniqueness for user email addresses: ', - error - ); - throw error; - }); + await this.adapter.ensureUniqueness('_User', requiredUserFields, ['email']).catch(error => { + logger.warn('Unable to ensure uniqueness for user email addresses: ', error); + throw error; + }); - await this.adapter - .ensureUniqueness('_Role', requiredRoleFields, ['name']) - .catch(error => { - logger.warn('Unable to ensure uniqueness for role name: ', error); - throw error; - }); + await this.adapter.ensureUniqueness('_Role', requiredRoleFields, ['name']).catch(error => { + logger.warn('Unable to ensure uniqueness for role name: ', error); + throw error; + }); await this.adapter .ensureUniqueness('_Idempotency', requiredIdempotencyFields, ['reqId']) .catch(error => { - logger.warn( - 'Unable to ensure uniqueness for idempotency request ID: ', - error - ); + logger.warn('Unable to ensure uniqueness for idempotency request ID: ', error); throw error; }); @@ -2006,19 +1801,9 @@ class DatabaseController { options.setIdempotencyFunction = true; } await this.adapter - .ensureIndex( - '_Idempotency', - requiredIdempotencyFields, - ['expire'], - 'ttl', - false, - options - ) + .ensureIndex('_Idempotency', requiredIdempotencyFields, ['expire'], 'ttl', false, options) .catch(error => { - logger.warn( - 'Unable to create TTL index for idempotency expire date: ', - error - ); + logger.warn('Unable to create TTL index for idempotency expire date: ', error); throw error; }); } @@ -2073,9 +1858,7 @@ class DatabaseController { keyUpdate && typeof keyUpdate === 'object' && keyUpdate.__op && - ['Add', 'AddUnique', 'Remove', 'Increment', 'SetOnInsert'].indexOf( - keyUpdate.__op - ) > -1 + ['Add', 'AddUnique', 'Remove', 'Increment', 'SetOnInsert'].indexOf(keyUpdate.__op) > -1 ) { // only valid ops that produce an actionable result // the op may have happened on a keypath @@ -2083,14 +1866,8 @@ class DatabaseController { // Revert array to object conversion on dot notation for arrays (e.g. "field.0.key") if (key.includes('.')) { const [field, index] = key.split('.'); - const isArrayIndex = Array.from(index).every( - c => c >= '0' && c <= '9' - ); - if ( - isArrayIndex && - Array.isArray(result[field]) && - !Array.isArray(response[field]) - ) { + const isArrayIndex = Array.from(index).every(c => c >= '0' && c <= '9'); + if (isArrayIndex && Array.isArray(result[field]) && !Array.isArray(response[field])) { response[field] = result[field]; } } @@ -2100,17 +1877,7 @@ class DatabaseController { } static _validateQuery: (any, boolean, boolean, boolean) => void; - static filterSensitiveData: ( - boolean, - boolean, - any[], - any, - any, - any, - string, - any[], - any - ) => void; + static filterSensitiveData: (boolean, boolean, any[], any, any, any, string, any[], any) => void; } module.exports = DatabaseController; diff --git a/src/Controllers/FilesController.js b/src/Controllers/FilesController.js index 20f1f1d588..21ab5efe4c 100644 --- a/src/Controllers/FilesController.js +++ b/src/Controllers/FilesController.js @@ -73,28 +73,16 @@ export class FilesController extends AdaptableController { // all filenames starting with a "-" seperated UUID should be from files.parse.com // all other filenames have been migrated or created from Parse Server if (config.fileKey === undefined) { - fileObject['url'] = await this.adapter.getFileLocation( - config, - filename - ); + fileObject['url'] = await this.adapter.getFileLocation(config, filename); } else { if (filename.indexOf('tfss-') === 0) { fileObject['url'] = - 'http://files.parsetfss.com/' + - config.fileKey + - '/' + - encodeURIComponent(filename); + 'http://files.parsetfss.com/' + config.fileKey + '/' + encodeURIComponent(filename); } else if (legacyFilesRegex.test(filename)) { fileObject['url'] = - 'http://files.parse.com/' + - config.fileKey + - '/' + - encodeURIComponent(filename); + 'http://files.parse.com/' + config.fileKey + '/' + encodeURIComponent(filename); } else { - fileObject['url'] = await this.adapter.getFileLocation( - config, - filename - ); + fileObject['url'] = await this.adapter.getFileLocation(config, filename); } } } diff --git a/src/Controllers/HooksController.js b/src/Controllers/HooksController.js index 6bfd1fe3a1..277104ef32 100644 --- a/src/Controllers/HooksController.js +++ b/src/Controllers/HooksController.js @@ -36,9 +36,7 @@ export class HooksController { } getFunction(functionName) { - return this._getHooks({ functionName: functionName }).then( - results => results[0] - ); + return this._getHooks({ functionName: functionName }).then(results => results[0]); } getFunctions() { @@ -73,14 +71,12 @@ export class HooksController { } _getHooks(query = {}) { - return this.database - .find(DefaultHooksCollectionName, query) - .then(results => { - return results.map(result => { - delete result.objectId; - return result; - }); + return this.database.find(DefaultHooksCollectionName, query).then(results => { + return results.map(result => { + delete result.objectId; + return result; }); + }); } _removeHooks(query) { @@ -109,19 +105,9 @@ export class HooksController { var wrappedFunction = wrapToHTTPRequest(hook, this._webhookKey); wrappedFunction.url = hook.url; if (hook.className) { - triggers.addTrigger( - hook.triggerName, - hook.className, - wrappedFunction, - this._applicationId - ); + triggers.addTrigger(hook.triggerName, hook.className, wrappedFunction, this._applicationId); } else { - triggers.addFunction( - hook.functionName, - wrappedFunction, - null, - this._applicationId - ); + triggers.addFunction(hook.functionName, wrappedFunction, null, this._applicationId); } } @@ -158,26 +144,21 @@ export class HooksController { if (aHook.functionName) { return this.getFunction(aHook.functionName).then(result => { if (result) { - throw new Parse.Error( - 143, - `function name: ${aHook.functionName} already exists` - ); + throw new Parse.Error(143, `function name: ${aHook.functionName} already exists`); } else { return this.createOrUpdateHook(aHook); } }); } else if (aHook.className && aHook.triggerName) { - return this.getTrigger(aHook.className, aHook.triggerName).then( - result => { - if (result) { - throw new Parse.Error( - 143, - `class ${aHook.className} already has trigger ${aHook.triggerName}` - ); - } - return this.createOrUpdateHook(aHook); + return this.getTrigger(aHook.className, aHook.triggerName).then(result => { + if (result) { + throw new Parse.Error( + 143, + `class ${aHook.className} already has trigger ${aHook.triggerName}` + ); } - ); + return this.createOrUpdateHook(aHook); + }); } throw new Parse.Error(143, 'invalid hook declaration'); @@ -189,20 +170,15 @@ export class HooksController { if (result) { return this.createOrUpdateHook(aHook); } - throw new Parse.Error( - 143, - `no function named: ${aHook.functionName} is defined` - ); + throw new Parse.Error(143, `no function named: ${aHook.functionName} is defined`); }); } else if (aHook.className && aHook.triggerName) { - return this.getTrigger(aHook.className, aHook.triggerName).then( - result => { - if (result) { - return this.createOrUpdateHook(aHook); - } - throw new Parse.Error(143, `class ${aHook.className} does not exist`); + return this.getTrigger(aHook.className, aHook.triggerName).then(result => { + if (result) { + return this.createOrUpdateHook(aHook); } - ); + throw new Parse.Error(143, `class ${aHook.className} does not exist`); + }); } throw new Parse.Error(143, 'invalid hook declaration'); } @@ -231,17 +207,13 @@ function wrapToHTTPRequest(hook, key) { method: 'POST', }; - const agent = hook.url.startsWith('https') - ? HTTPAgents['https'] - : HTTPAgents['http']; + const agent = hook.url.startsWith('https') ? HTTPAgents['https'] : HTTPAgents['http']; jsonRequest.agent = agent; if (key) { jsonRequest.headers['X-Parse-Webhook-Key'] = key; } else { - logger.warn( - 'Making outgoing webhook request without webhookKey being set!' - ); + logger.warn('Making outgoing webhook request without webhookKey being set!'); } return request(jsonRequest).then(response => { let err; diff --git a/src/Controllers/LoggerController.js b/src/Controllers/LoggerController.js index 0d32a41993..6dac43518f 100644 --- a/src/Controllers/LoggerController.js +++ b/src/Controllers/LoggerController.js @@ -16,15 +16,7 @@ export const LogOrder = { ASCENDING: 'asc', }; -export const logLevels = [ - 'error', - 'warn', - 'info', - 'debug', - 'verbose', - 'silly', - 'silent', -]; +export const logLevels = ['error', 'warn', 'info', 'debug', 'verbose', 'silly', 'silent']; export class LoggerController extends AdaptableController { constructor(adapter, appId, options = { logLevel: 'info' }) { @@ -197,8 +189,7 @@ export class LoggerController extends AdaptableController { truncateLogMessage(string) { if (string && string.length > LOG_STRING_TRUNCATE_LENGTH) { - const truncated = - string.substring(0, LOG_STRING_TRUNCATE_LENGTH) + truncationMarker; + const truncated = string.substring(0, LOG_STRING_TRUNCATE_LENGTH) + truncationMarker; return truncated; } @@ -232,10 +223,7 @@ export class LoggerController extends AdaptableController { // size (optional) Number of rows returned by search. Defaults to 10 getLogs(options = {}) { if (!this.adapter) { - throw new Parse.Error( - Parse.Error.PUSH_MISCONFIGURED, - 'Logger adapter is not available' - ); + throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED, 'Logger adapter is not available'); } if (typeof this.adapter.query !== 'function') { throw new Parse.Error( diff --git a/src/Controllers/SchemaController.js b/src/Controllers/SchemaController.js index 3ec4cfe468..ef3b96e594 100644 --- a/src/Controllers/SchemaController.js +++ b/src/Controllers/SchemaController.js @@ -269,11 +269,7 @@ const CLPValidKeys = Object.freeze([ ]); // validation before setting class-level permissions on collection -function validateCLP( - perms: ClassLevelPermissions, - fields: SchemaFields, - userIdRegExp: RegExp -) { +function validateCLP(perms: ClassLevelPermissions, fields: SchemaFields, userIdRegExp: RegExp) { if (!perms) { return; } @@ -291,10 +287,7 @@ function validateCLP( // throws when root fields are of wrong type validateCLPjson(operation, operationKey); - if ( - operationKey === 'readUserFields' || - operationKey === 'writeUserFields' - ) { + if (operationKey === 'readUserFields' || operationKey === 'writeUserFields') { // validate grouped pointer permissions // must be an array with field names for (const fieldName of operation) { @@ -381,12 +374,8 @@ function validateCLP( `'${permit}' is not a valid value for class level permissions acl` ); } - const invalidKeys = Object.keys(permit).filter( - key => !['read', 'write'].includes(key) - ); - const invalidValues = Object.values(permit).filter( - key => typeof key !== 'boolean' - ); + const invalidKeys = Object.keys(permit).filter(key => !['read', 'write'].includes(key)); + const invalidValues = Object.values(permit).filter(key => typeof key !== 'boolean'); if (invalidKeys.length) { throw new Parse.Error( Parse.Error.INVALID_JSON, @@ -431,11 +420,7 @@ function validateCLPjson(operation: any, operationKey: string) { } } -function validatePointerPermission( - fieldName: string, - fields: Object, - operation: string -) { +function validatePointerPermission(fieldName: string, fields: Object, operation: string) { // Uses collection schema to ensure the field is of type: // - Pointer<_User> (pointers) // - Array @@ -446,8 +431,7 @@ function validatePointerPermission( if ( !( fields[fieldName] && - ((fields[fieldName].type == 'Pointer' && - fields[fieldName].targetClass == '_User') || + ((fields[fieldName].type == 'Pointer' && fields[fieldName].targetClass == '_User') || fields[fieldName].type == 'Array') ) ) { @@ -480,16 +464,11 @@ function fieldNameIsValid(fieldName: string, className: string): boolean { return false; } } - return ( - classAndFieldRegex.test(fieldName) && !invalidColumns.includes(fieldName) - ); + return classAndFieldRegex.test(fieldName) && !invalidColumns.includes(fieldName); } // Checks that it's not trying to clobber one of the default fields of the class. -function fieldNameIsValidForClass( - fieldName: string, - className: string -): boolean { +function fieldNameIsValidForClass(fieldName: string, className: string): boolean { if (!fieldNameIsValid(fieldName, className)) { return false; } @@ -510,10 +489,7 @@ function invalidClassNameMessage(className: string): string { ); } -const invalidJsonError = new Parse.Error( - Parse.Error.INVALID_JSON, - 'invalid JSON' -); +const invalidJsonError = new Parse.Error(Parse.Error.INVALID_JSON, 'invalid JSON'); const validNonRelationOrPointerTypes = [ 'Number', 'String', @@ -534,10 +510,7 @@ const fieldTypeIsInvalid = ({ type, targetClass }) => { } else if (typeof targetClass !== 'string') { return invalidJsonError; } else if (!classNameIsValid(targetClass)) { - return new Parse.Error( - Parse.Error.INVALID_CLASS_NAME, - invalidClassNameMessage(targetClass) - ); + return new Parse.Error(Parse.Error.INVALID_CLASS_NAME, invalidClassNameMessage(targetClass)); } else { return undefined; } @@ -546,10 +519,7 @@ const fieldTypeIsInvalid = ({ type, targetClass }) => { return invalidJsonError; } if (validNonRelationOrPointerTypes.indexOf(type) < 0) { - return new Parse.Error( - Parse.Error.INCORRECT_TYPE, - `invalid field type: ${type}` - ); + return new Parse.Error(Parse.Error.INCORRECT_TYPE, `invalid field type: ${type}`); } return undefined; }; @@ -605,16 +575,14 @@ class SchemaData { data.classLevelPermissions = deepcopy(schema.classLevelPermissions); data.indexes = schema.indexes; - const classProtectedFields = - this.__protectedFields[schema.className]; + const classProtectedFields = this.__protectedFields[schema.className]; if (classProtectedFields) { for (const key in classProtectedFields) { const unq = new Set([ ...(data.classLevelPermissions.protectedFields[key] || []), ...classProtectedFields[key], ]); - data.classLevelPermissions.protectedFields[key] = - Array.from(unq); + data.classLevelPermissions.protectedFields[key] = Array.from(unq); } } @@ -648,12 +616,7 @@ class SchemaData { } } -const injectDefaultSchema = ({ - className, - fields, - classLevelPermissions, - indexes, -}: Schema) => { +const injectDefaultSchema = ({ className, fields, classLevelPermissions, indexes }: Schema) => { const defaultSchema: Schema = { className, fields: { @@ -724,10 +687,7 @@ const VolatileClassesSchemas = [ _IdempotencySchema, ]; -const dbTypeMatchesObjectType = ( - dbType: SchemaField | string, - objectType: SchemaField -) => { +const dbTypeMatchesObjectType = (dbType: SchemaField | string, objectType: SchemaField) => { if (dbType.type !== objectType.type) { return false; } @@ -819,9 +779,7 @@ export default class SchemaController { return this.reloadDataPromise; } - async getAllClasses( - options: LoadSchemaOptions = { clearCache: false } - ): Promise> { + async getAllClasses(options: LoadSchemaOptions = { clearCache: false }): Promise> { if (options.clearCache) { return this.setAllClasses(); } @@ -865,9 +823,7 @@ export default class SchemaController { return Promise.resolve(cached); } return this.setAllClasses().then(allSchemas => { - const oneSchema = allSchemas.find( - schema => schema.className === className - ); + const oneSchema = allSchemas.find(schema => schema.className === className); if (!oneSchema) { return Promise.reject(undefined); } @@ -888,18 +844,12 @@ export default class SchemaController { classLevelPermissions: any, indexes: any = {} ): Promise { - var validationError = this.validateNewClass( - className, - fields, - classLevelPermissions - ); + var validationError = this.validateNewClass(className, fields, classLevelPermissions); if (validationError) { if (validationError instanceof Parse.Error) { return Promise.reject(validationError); } else if (validationError.code && validationError.error) { - return Promise.reject( - new Parse.Error(validationError.code, validationError.error) - ); + return Promise.reject(new Parse.Error(validationError.code, validationError.error)); } return Promise.reject(validationError); } @@ -919,10 +869,7 @@ export default class SchemaController { return parseSchema; } catch (error) { if (error && error.code === Parse.Error.DUPLICATE_VALUE) { - throw new Parse.Error( - Parse.Error.INVALID_CLASS_NAME, - `Class ${className} already exists.` - ); + throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, `Class ${className} already exists.`); } else { throw error; } @@ -949,21 +896,14 @@ export default class SchemaController { throw new Parse.Error(255, `Field ${name} exists, cannot update.`); } if (!existingFields[name] && field.__op === 'Delete') { - throw new Parse.Error( - 255, - `Field ${name} does not exist, cannot delete.` - ); + throw new Parse.Error(255, `Field ${name} does not exist, cannot delete.`); } }); delete existingFields._rperm; delete existingFields._wperm; - const newSchema = buildMergedSchemaObject( - existingFields, - submittedFields - ); - const defaultFields = - defaultColumns[className] || defaultColumns._Default; + const newSchema = buildMergedSchemaObject(existingFields, submittedFields); + const defaultFields = defaultColumns[className] || defaultColumns._Default; const fullNewSchema = Object.assign({}, newSchema, defaultFields); const validationError = this.validateSchemaData( className, @@ -1004,11 +944,7 @@ export default class SchemaController { }) .then(results => { enforceFields = results.filter(result => !!result); - return this.setPermissions( - className, - classLevelPermissions, - newSchema - ); + return this.setPermissions(className, classLevelPermissions, newSchema); }) .then(() => this._dbAdapter.setIndexesWithSchemaFormat( @@ -1069,32 +1005,19 @@ export default class SchemaController { if (this.schemaData[className]) { return this; } else { - throw new Parse.Error( - Parse.Error.INVALID_JSON, - `Failed to add ${className}` - ); + throw new Parse.Error(Parse.Error.INVALID_JSON, `Failed to add ${className}`); } }) .catch(() => { // The schema still doesn't validate. Give up - throw new Parse.Error( - Parse.Error.INVALID_JSON, - 'schema class name does not revalidate' - ); + throw new Parse.Error(Parse.Error.INVALID_JSON, 'schema class name does not revalidate'); }) ); } - validateNewClass( - className: string, - fields: SchemaFields = {}, - classLevelPermissions: any - ): any { + validateNewClass(className: string, fields: SchemaFields = {}, classLevelPermissions: any): any { if (this.schemaData[className]) { - throw new Parse.Error( - Parse.Error.INVALID_CLASS_NAME, - `Class ${className} already exists.` - ); + throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, `Class ${className} already exists.`); } if (!classNameIsValid(className)) { return { @@ -1102,12 +1025,7 @@ export default class SchemaController { error: invalidClassNameMessage(className), }; } - return this.validateSchemaData( - className, - fields, - classLevelPermissions, - [] - ); + return this.validateSchemaData(className, fields, classLevelPermissions, []); } validateSchemaData( @@ -1139,10 +1057,7 @@ export default class SchemaController { let defaultValueType = getType(fieldType.defaultValue); if (typeof defaultValueType === 'string') { defaultValueType = { type: defaultValueType }; - } else if ( - typeof defaultValueType === 'object' && - fieldType.type === 'Relation' - ) { + } else if (typeof defaultValueType === 'object' && fieldType.type === 'Relation') { return { code: Parse.Error.INCORRECT_TYPE, error: `The 'default value' option is not applicable for ${typeToString(fieldType)}`, @@ -1219,10 +1134,7 @@ export default class SchemaController { const [x, y] = fieldName.split('.'); fieldName = x; const isArrayIndex = Array.from(y).every(c => c >= '0' && c <= '9'); - if ( - isArrayIndex && - !['sentPerUTCOffset', 'failedPerUTCOffset'].includes(fieldName) - ) { + if (isArrayIndex && !['sentPerUTCOffset', 'failedPerUTCOffset'].includes(fieldName)) { type = 'Array'; } else { type = 'Object'; @@ -1233,10 +1145,7 @@ export default class SchemaController { fieldNameToValidate = fieldNameToValidate.substring(1); } if (!fieldNameIsValid(fieldNameToValidate, className)) { - throw new Parse.Error( - Parse.Error.INVALID_KEY_NAME, - `Invalid field name: ${fieldName}.` - ); + throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid field name: ${fieldName}.`); } // If someone tries to create a new field with null/undefined as the value, return; @@ -1275,10 +1184,7 @@ export default class SchemaController { } // If type options do not change // we can safely return - if ( - isValidation || - JSON.stringify(expectedType) === JSON.stringify(type) - ) { + if (isValidation || JSON.stringify(expectedType) === JSON.stringify(type)) { return undefined; } // Field options are may be changed @@ -1316,20 +1222,13 @@ export default class SchemaController { type = { type: type }; } if (!expectedType || !dbTypeMatchesObjectType(expectedType, type)) { - throw new Parse.Error( - Parse.Error.INVALID_JSON, - `Could not add field ${fieldName}` - ); + throw new Parse.Error(Parse.Error.INVALID_JSON, `Could not add field ${fieldName}`); } } } // maintain compatibility - deleteField( - fieldName: string, - className: string, - database: DatabaseController - ) { + deleteField(fieldName: string, className: string, database: DatabaseController) { return this.deleteFields([fieldName], className, database); } @@ -1340,24 +1239,14 @@ export default class SchemaController { // Passing the database and prefix is necessary in order to drop relation collections // and remove fields from objects. Ideally the database would belong to // a database adapter and this function would close over it or access it via member. - deleteFields( - fieldNames: Array, - className: string, - database: DatabaseController - ) { + deleteFields(fieldNames: Array, className: string, database: DatabaseController) { if (!classNameIsValid(className)) { - throw new Parse.Error( - Parse.Error.INVALID_CLASS_NAME, - invalidClassNameMessage(className) - ); + throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, invalidClassNameMessage(className)); } fieldNames.forEach(fieldName => { if (!fieldNameIsValid(fieldName, className)) { - throw new Parse.Error( - Parse.Error.INVALID_KEY_NAME, - `invalid field name: ${fieldName}` - ); + throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `invalid field name: ${fieldName}`); } //Don't allow deleting the default fields. if (!fieldNameIsValidForClass(fieldName, className)) { @@ -1379,30 +1268,23 @@ export default class SchemaController { .then(schema => { fieldNames.forEach(fieldName => { if (!schema.fields[fieldName]) { - throw new Parse.Error( - 255, - `Field ${fieldName} does not exist, cannot delete.` - ); + throw new Parse.Error(255, `Field ${fieldName} does not exist, cannot delete.`); } }); const schemaFields = { ...schema.fields }; - return database.adapter - .deleteFields(className, schema, fieldNames) - .then(() => { - return Promise.all( - fieldNames.map(fieldName => { - const field = schemaFields[fieldName]; - if (field && field.type === 'Relation') { - //For relations, drop the _Join table - return database.adapter.deleteClass( - `_Join:${fieldName}:${className}` - ); - } - return Promise.resolve(); - }) - ); - }); + return database.adapter.deleteFields(className, schema, fieldNames).then(() => { + return Promise.all( + fieldNames.map(fieldName => { + const field = schemaFields[fieldName]; + if (field && field.type === 'Relation') { + //For relations, drop the _Join table + return database.adapter.deleteClass(`_Join:${fieldName}:${className}`); + } + return Promise.resolve(); + }) + ); + }); }) .then(() => { SchemaCache.clear(); @@ -1412,12 +1294,7 @@ export default class SchemaController { // Validates an object provided in REST format. // Returns a promise that resolves to the new schema if this object is // valid. - async validateObject( - className: string, - object: any, - query: any, - maintenance: boolean - ) { + async validateObject(className: string, object: any, query: any, maintenance: boolean) { let geocount = 0; const schema = await this.enforceClassExists(className); const promises = []; @@ -1447,15 +1324,7 @@ export default class SchemaController { // Every object has ACL implicitly. continue; } - promises.push( - schema.enforceFieldExists( - className, - fieldName, - expected, - true, - maintenance - ) - ); + promises.push(schema.enforceFieldExists(className, fieldName, expected, true, maintenance)); } const results = await Promise.all(promises); const enforceFields = results.filter(result => !!result); @@ -1490,19 +1359,12 @@ export default class SchemaController { }); if (missingColumns.length > 0) { - throw new Parse.Error( - Parse.Error.INCORRECT_TYPE, - missingColumns[0] + ' is required.' - ); + throw new Parse.Error(Parse.Error.INCORRECT_TYPE, missingColumns[0] + ' is required.'); } return Promise.resolve(this); } - testPermissionsForClassName( - className: string, - aclGroup: string[], - operation: string - ) { + testPermissionsForClassName(className: string, aclGroup: string[], operation: string) { return SchemaController.testPermissions( this.getClassLevelPermissions(className), aclGroup, @@ -1511,11 +1373,7 @@ export default class SchemaController { } // Tests that the class level permission let pass the operation for a given aclGroup - static testPermissions( - classPermissions: ?any, - aclGroup: string[], - operation: string - ): boolean { + static testPermissions(classPermissions: ?any, aclGroup: string[], operation: string): boolean { if (!classPermissions || !classPermissions[operation]) { return true; } @@ -1542,9 +1400,7 @@ export default class SchemaController { operation: string, action?: string ) { - if ( - SchemaController.testPermissions(classPermissions, aclGroup, operation) - ) { + if (SchemaController.testPermissions(classPermissions, aclGroup, operation)) { return Promise.resolve(); } @@ -1575,9 +1431,7 @@ export default class SchemaController { // No matching CLP, let's check the Pointer permissions // And handle those later const permissionField = - ['get', 'find', 'count'].indexOf(operation) > -1 - ? 'readUserFields' - : 'writeUserFields'; + ['get', 'find', 'count'].indexOf(operation) > -1 ? 'readUserFields' : 'writeUserFields'; // Reject create when write lockdown if (permissionField == 'writeUserFields' && operation == 'create') { @@ -1611,12 +1465,7 @@ export default class SchemaController { } // Validates an operation passes class-level-permissions set in the schema - validatePermission( - className: string, - aclGroup: string[], - operation: string, - action?: string - ) { + validatePermission(className: string, aclGroup: string[], operation: string, action?: string) { return SchemaController.validatePermission( this.getClassLevelPermissions(className), className, @@ -1627,18 +1476,12 @@ export default class SchemaController { } getClassLevelPermissions(className: string): any { - return ( - this.schemaData[className] && - this.schemaData[className].classLevelPermissions - ); + return this.schemaData[className] && this.schemaData[className].classLevelPermissions; } // Returns the expected type for a className+key combination // or undefined if the schema is not set - getExpectedType( - className: string, - fieldName: string - ): ?(SchemaField | string) { + getExpectedType(className: string, fieldName: string): ?(SchemaField | string) { if (this.schemaData[className]) { const expectedType = this.schemaData[className].fields[fieldName]; return expectedType === 'map' ? 'Object' : expectedType; @@ -1656,10 +1499,7 @@ export default class SchemaController { } // Returns a promise for a new Schema. -const load = ( - dbAdapter: StorageAdapter, - options: any -): Promise => { +const load = (dbAdapter: StorageAdapter, options: any): Promise => { const schema = new SchemaController(dbAdapter); ttl.duration = dbAdapter.schemaCacheTtl; return schema.reloadData(options).then(() => schema); @@ -1670,10 +1510,7 @@ const load = ( // does not include the default fields, as it is intended to be passed // to mongoSchemaFromFieldsAndClassName. No validation is done here, it // is done in mongoSchemaFromFieldsAndClassName. -function buildMergedSchemaObject( - existingFields: SchemaFields, - putRequest: any -): SchemaFields { +function buildMergedSchemaObject(existingFields: SchemaFields, putRequest: any): SchemaFields { const newSchema = {}; // @flow-disable-next const sysSchemaField = @@ -1688,14 +1525,10 @@ function buildMergedSchemaObject( oldField !== 'createdAt' && oldField !== 'objectId' ) { - if ( - sysSchemaField.length > 0 && - sysSchemaField.indexOf(oldField) !== -1 - ) { + if (sysSchemaField.length > 0 && sysSchemaField.indexOf(oldField) !== -1) { continue; } - const fieldIsDeleted = - putRequest[oldField] && putRequest[oldField].__op === 'Delete'; + const fieldIsDeleted = putRequest[oldField] && putRequest[oldField].__op === 'Delete'; if (!fieldIsDeleted) { newSchema[oldField] = existingFields[oldField]; } @@ -1703,10 +1536,7 @@ function buildMergedSchemaObject( } for (const newField in putRequest) { if (newField !== 'objectId' && putRequest[newField].__op !== 'Delete') { - if ( - sysSchemaField.length > 0 && - sysSchemaField.indexOf(newField) !== -1 - ) { + if (sysSchemaField.length > 0 && sysSchemaField.indexOf(newField) !== -1) { continue; } newSchema[newField] = putRequest[newField]; @@ -1802,10 +1632,7 @@ function getObjectType(obj): ?(SchemaField | string) { } break; } - throw new Parse.Error( - Parse.Error.INCORRECT_TYPE, - 'This is not a valid ' + obj.__type - ); + throw new Parse.Error(Parse.Error.INCORRECT_TYPE, 'This is not a valid ' + obj.__type); } if (obj['$ne']) { return getObjectType(obj['$ne']); diff --git a/src/Controllers/UserController.js b/src/Controllers/UserController.js index f8a06192a3..baaca8f5c3 100644 --- a/src/Controllers/UserController.js +++ b/src/Controllers/UserController.js @@ -94,13 +94,7 @@ export class UserController extends AdaptableController { if (result.results.length) { query.objectId = result.results[0].objectId; } - return await rest.update( - this.config, - maintenanceAuth, - '_User', - query, - updateFields - ); + return await rest.update(this.config, maintenanceAuth, '_User', query, updateFields); } async checkResetTokenValidity(token) { @@ -116,10 +110,7 @@ export class UserController extends AdaptableController { throw 'Failed to reset password: username / email / token is invalid'; } - if ( - this.config.passwordPolicy && - this.config.passwordPolicy.resetTokenValidityDuration - ) { + if (this.config.passwordPolicy && this.config.passwordPolicy.resetTokenValidityDuration) { let expiresDate = results[0]._perishable_token_expires_at; if (expiresDate && expiresDate.__type == 'Date') { expiresDate = new Date(expiresDate.iso); @@ -202,10 +193,7 @@ export class UserController extends AdaptableController { async regenerateEmailVerifyToken(user, master, installationId, ip) { const { _email_verify_token } = user; let { _email_verify_token_expires_at } = user; - if ( - _email_verify_token_expires_at && - _email_verify_token_expires_at.__type === 'Date' - ) { + if (_email_verify_token_expires_at && _email_verify_token_expires_at.__type === 'Date') { _email_verify_token_expires_at = _email_verify_token_expires_at.iso; } if ( @@ -226,11 +214,7 @@ export class UserController extends AdaptableController { if (!shouldSend) { return; } - return this.config.database.update( - '_User', - { username: user.username }, - user - ); + return this.config.database.update('_User', { username: user.username }, user); } async resendVerificationEmail(username, req, token) { @@ -255,10 +239,7 @@ export class UserController extends AdaptableController { setPasswordResetToken(email) { const token = { _perishable_token: randomString(25) }; - if ( - this.config.passwordPolicy && - this.config.passwordPolicy.resetTokenValidityDuration - ) { + if (this.config.passwordPolicy && this.config.passwordPolicy.resetTokenValidityDuration) { token._perishable_token_expires_at = Parse._encode( this.config.generatePasswordResetTokenExpiresAt() ); @@ -313,11 +294,7 @@ export class UserController extends AdaptableController { user = await this.setPasswordResetToken(email); } const token = encodeURIComponent(user._perishable_token); - const link = buildEmailLink( - this.config.requestResetPasswordURL, - token, - this.config - ); + const link = buildEmailLink(this.config.requestResetPasswordURL, token, this.config); const options = { appName: this.config.appName, link: link, @@ -370,9 +347,7 @@ export class UserController extends AdaptableController { 'Hi,\n\n' + 'You requested to reset your password for ' + appName + - (user.get('username') - ? " (your username is '" + user.get('username') + "')" - : '') + + (user.get('username') ? " (your username is '" + user.get('username') + "')" : '') + '.\n\n' + '' + 'Click here to reset it:\n' + @@ -401,10 +376,7 @@ function updateUserPassword(user, password, config) { function buildEmailLink(destination, token, config) { token = `token=${token}`; if (config.parseFrameURL) { - const destinationWithoutHost = destination.replace( - config.publicServerURL, - '' - ); + const destinationWithoutHost = destination.replace(config.publicServerURL, ''); return `${config.parseFrameURL}?link=${encodeURIComponent(destinationWithoutHost)}&${token}`; } else { diff --git a/src/Deprecator/Deprecator.js b/src/Deprecator/Deprecator.js index 4cbdacfced..27033c946d 100644 --- a/src/Deprecator/Deprecator.js +++ b/src/Deprecator/Deprecator.js @@ -94,13 +94,7 @@ class Deprecator { * automatically added to the message. It should only contain the instruction on how * to resolve this warning. */ - static _logOption({ - optionKey, - envKey, - changeNewKey, - changeNewDefault, - solution, - }) { + static _logOption({ optionKey, envKey, changeNewKey, changeNewDefault, solution }) { const type = optionKey ? 'option' : 'environment key'; const key = optionKey ? optionKey : envKey; const keyAction = @@ -112,9 +106,7 @@ class Deprecator { // Compose message let output = `DeprecationWarning: The Parse Server ${type} '${key}' `; - output += changeNewKey - ? `is deprecated and will be ${keyAction} in a future version.` - : ''; + output += changeNewKey ? `is deprecated and will be ${keyAction} in a future version.` : ''; output += changeNewDefault ? `default will change to '${changeNewDefault}' in a future version.` : ''; diff --git a/src/GraphQL/parseGraphQLUtils.js b/src/GraphQL/parseGraphQLUtils.js index 4f12db37af..d7164525a7 100644 --- a/src/GraphQL/parseGraphQLUtils.js +++ b/src/GraphQL/parseGraphQLUtils.js @@ -3,10 +3,7 @@ import { GraphQLError } from 'graphql'; export function enforceMasterKeyAccess(auth) { if (!auth.isMaster) { - throw new Parse.Error( - Parse.Error.OPERATION_FORBIDDEN, - 'unauthorized: master key is required' - ); + throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'unauthorized: master key is required'); } } @@ -23,9 +20,7 @@ export function toGraphQLError(error) { } export const extractKeysAndInclude = selectedFields => { - selectedFields = selectedFields.filter( - field => !field.includes('__typename') - ); + selectedFields = selectedFields.filter(field => !field.includes('__typename')); // Handles "id" field for both current and included objects selectedFields = selectedFields.map(field => { if (field === 'id') { diff --git a/src/ParseServerRESTController.js b/src/ParseServerRESTController.js index 03bf7fc3fa..b67d0b6bef 100644 --- a/src/ParseServerRESTController.js +++ b/src/ParseServerRESTController.js @@ -13,9 +13,7 @@ function getSessionToken(options) { function getAuth(options = {}, config) { const installationId = options.installationId || 'cloud'; if (options.useMasterKey) { - return Promise.resolve( - new Auth.Auth({ config, isMaster: true, installationId }) - ); + return Promise.resolve(new Auth.Auth({ config, isMaster: true, installationId })); } return getSessionToken(options).then(sessionToken => { if (sessionToken) { @@ -56,13 +54,7 @@ function ParseServerRESTController(applicationId, router) { } return initialPromise.then(() => { const promises = data.requests.map(request => { - return handleRequest( - request.method, - request.path, - request.body, - options, - config - ).then( + return handleRequest(request.method, request.path, request.body, options, config).then( response => { if (options.returnStatus) { const status = response._status; @@ -87,22 +79,14 @@ function ParseServerRESTController(applicationId, router) { return Promise.all(promises) .then(result => { if (data.transaction === true) { - if ( - result.find( - resultItem => typeof resultItem.error === 'object' - ) - ) { - return config.database - .abortTransactionalSession() - .then(() => { - return Promise.reject(result); - }); + if (result.find(resultItem => typeof resultItem.error === 'object')) { + return config.database.abortTransactionalSession().then(() => { + return Promise.reject(result); + }); } else { - return config.database - .commitTransactionalSession() - .then(() => { - return result; - }); + return config.database.commitTransactionalSession().then(() => { + return result; + }); } } else { return result; @@ -112,9 +96,7 @@ function ParseServerRESTController(applicationId, router) { if ( error && error.find( - errorItem => - typeof errorItem.error === 'object' && - errorItem.error.code === 251 + errorItem => typeof errorItem.error === 'object' && errorItem.error.code === 251 ) && transactionRetries > 0 ) { diff --git a/src/PromiseRouter.js b/src/PromiseRouter.js index d0754ddf67..3386daf223 100644 --- a/src/PromiseRouter.js +++ b/src/PromiseRouter.js @@ -121,10 +121,7 @@ export default class PromiseRouter { tryRouteRequest(method, path, request) { var match = this.match(method, path); if (!match) { - throw new Parse.Error( - Parse.Error.INVALID_JSON, - 'cannot route ' + method + ' ' + path - ); + throw new Parse.Error(Parse.Error.INVALID_JSON, 'cannot route ' + method + ' ' + path); } request.params = match.params; return new Promise((resolve, reject) => { @@ -154,9 +151,7 @@ function makeExpressHandler(appId, promiseHandler) { .then( result => { if (!result.response && !result.location && !result.text) { - log.error( - 'the handler did not include a "response" or a "location" field' - ); + log.error('the handler did not include a "response" or a "location" field'); throw 'control should not get here'; } diff --git a/src/Push/PushWorker.js b/src/Push/PushWorker.js index d16542aeac..2b3c4a2fb7 100644 --- a/src/Push/PushWorker.js +++ b/src/Push/PushWorker.js @@ -48,14 +48,12 @@ export class PushWorker { const where = utils.applyDeviceTokenExists(query.where); delete query.where; pushStatus = pushStatusHandler(config, pushStatus.objectId); - return rest - .find(config, auth, '_Installation', where, query) - .then(({ results }) => { - if (results.length == 0) { - return; - } - return this.sendToAdapter(body, results, pushStatus, config, UTCOffset); - }); + return rest.find(config, auth, '_Installation', where, query).then(({ results }) => { + if (results.length == 0) { + return; + } + return this.sendToAdapter(body, results, pushStatus, config, UTCOffset); + }); } sendToAdapter( @@ -72,31 +70,20 @@ export class PushWorker { const bodiesPerLocales = utils.bodiesPerLocales(body, locales); // Group installations on the specified locales (en, fr, default etc...) - const grouppedInstallations = utils.groupByLocaleIdentifier( - installations, - locales - ); + const grouppedInstallations = utils.groupByLocaleIdentifier(installations, locales); const promises = Object.keys(grouppedInstallations).map(locale => { const installations = grouppedInstallations[locale]; const body = bodiesPerLocales[locale]; - return this.sendToAdapter( - body, - installations, - pushStatus, - config, - UTCOffset - ); + return this.sendToAdapter(body, installations, pushStatus, config, UTCOffset); }); return Promise.all(promises); } if (!utils.isPushIncrementing(body)) { logger.verbose(`Sending push to ${installations.length}`); - return this.adapter - .send(body, installations, pushStatus.objectId) - .then(results => { - return pushStatus.trackSent(results, UTCOffset).then(() => results); - }); + return this.adapter.send(body, installations, pushStatus.objectId).then(results => { + return pushStatus.trackSent(results, UTCOffset).then(() => results); + }); } // Collect the badges to reduce the # of calls @@ -107,13 +94,7 @@ export class PushWorker { const payload = deepcopy(body); payload.data.badge = parseInt(badge); const installations = badgeInstallationsMap[badge]; - return this.sendToAdapter( - payload, - installations, - pushStatus, - config, - UTCOffset - ); + return this.sendToAdapter(payload, installations, pushStatus, config, UTCOffset); }); return Promise.all(promises); } diff --git a/src/Push/utils.js b/src/Push/utils.js index ce7023917e..5be13c272e 100644 --- a/src/Push/utils.js +++ b/src/Push/utils.js @@ -88,10 +88,7 @@ export function groupByLocaleIdentifier(installations, locales = []) { if (added) { return; } - if ( - installation.localeIdentifier && - installation.localeIdentifier.indexOf(locale) === 0 - ) { + if (installation.localeIdentifier && installation.localeIdentifier.indexOf(locale) === 0) { added = true; map[locale] = map[locale] || []; map[locale].push(installation); diff --git a/src/RestQuery.js b/src/RestQuery.js index f44bf0b572..ec47eb2e3d 100644 --- a/src/RestQuery.js +++ b/src/RestQuery.js @@ -116,10 +116,7 @@ function _UnsafeRestQuery( if (!this.auth.isMaster) { if (this.className == '_Session') { if (!this.auth.user) { - throw new Parse.Error( - Parse.Error.INVALID_SESSION_TOKEN, - 'Invalid session token' - ); + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid session token'); } this.restWhere = { $and: [ @@ -266,10 +263,7 @@ function _UnsafeRestQuery( case 'subqueryReadPreference': break; default: - throw new Parse.Error( - Parse.Error.INVALID_JSON, - 'bad option: ' + option - ); + throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad option: ' + option); } } } @@ -387,9 +381,7 @@ _UnsafeRestQuery.prototype.getUserAndRoleACL = function () { if (this.auth.user) { return this.auth.getUserRoles().then(roles => { - this.findOptions.acl = this.findOptions.acl.concat(roles, [ - this.auth.user.id, - ]); + this.findOptions.acl = this.findOptions.acl.concat(roles, [this.auth.user.id]); return; }); } else { @@ -427,9 +419,7 @@ _UnsafeRestQuery.prototype.validateClientClassCreation = function () { if (hasClass !== true) { throw new Parse.Error( Parse.Error.OPERATION_FORBIDDEN, - 'This user is not allowed to access ' + - 'non-existent class: ' + - this.className + 'This user is not allowed to access ' + 'non-existent class: ' + this.className ); } }); @@ -468,10 +458,7 @@ _UnsafeRestQuery.prototype.replaceInQuery = async function () { // The inQuery value must have precisely two keys - where and className var inQueryValue = inQueryObject['$inQuery']; if (!inQueryValue.where || !inQueryValue.className) { - throw new Parse.Error( - Parse.Error.INVALID_QUERY, - 'improper usage of $inQuery' - ); + throw new Parse.Error(Parse.Error.INVALID_QUERY, 'improper usage of $inQuery'); } const additionalOptions = { @@ -480,8 +467,7 @@ _UnsafeRestQuery.prototype.replaceInQuery = async function () { if (this.restOptions.subqueryReadPreference) { additionalOptions.readPreference = this.restOptions.subqueryReadPreference; - additionalOptions.subqueryReadPreference = - this.restOptions.subqueryReadPreference; + additionalOptions.subqueryReadPreference = this.restOptions.subqueryReadPreference; } else if (this.restOptions.readPreference) { additionalOptions.readPreference = this.restOptions.readPreference; } @@ -532,10 +518,7 @@ _UnsafeRestQuery.prototype.replaceNotInQuery = async function () { // The notInQuery value must have precisely two keys - where and className var notInQueryValue = notInQueryObject['$notInQuery']; if (!notInQueryValue.where || !notInQueryValue.className) { - throw new Parse.Error( - Parse.Error.INVALID_QUERY, - 'improper usage of $notInQuery' - ); + throw new Parse.Error(Parse.Error.INVALID_QUERY, 'improper usage of $notInQuery'); } const additionalOptions = { @@ -544,8 +527,7 @@ _UnsafeRestQuery.prototype.replaceNotInQuery = async function () { if (this.restOptions.subqueryReadPreference) { additionalOptions.readPreference = this.restOptions.subqueryReadPreference; - additionalOptions.subqueryReadPreference = - this.restOptions.subqueryReadPreference; + additionalOptions.subqueryReadPreference = this.restOptions.subqueryReadPreference; } else if (this.restOptions.readPreference) { additionalOptions.readPreference = this.restOptions.readPreference; } @@ -609,10 +591,7 @@ _UnsafeRestQuery.prototype.replaceSelect = async function () { !selectValue.query.className || Object.keys(selectValue).length !== 2 ) { - throw new Parse.Error( - Parse.Error.INVALID_QUERY, - 'improper usage of $select' - ); + throw new Parse.Error(Parse.Error.INVALID_QUERY, 'improper usage of $select'); } const additionalOptions = { @@ -621,8 +600,7 @@ _UnsafeRestQuery.prototype.replaceSelect = async function () { if (this.restOptions.subqueryReadPreference) { additionalOptions.readPreference = this.restOptions.subqueryReadPreference; - additionalOptions.subqueryReadPreference = - this.restOptions.subqueryReadPreference; + additionalOptions.subqueryReadPreference = this.restOptions.subqueryReadPreference; } else if (this.restOptions.readPreference) { additionalOptions.readPreference = this.restOptions.readPreference; } @@ -677,10 +655,7 @@ _UnsafeRestQuery.prototype.replaceDontSelect = async function () { !dontSelectValue.query.className || Object.keys(dontSelectValue).length !== 2 ) { - throw new Parse.Error( - Parse.Error.INVALID_QUERY, - 'improper usage of $dontSelect' - ); + throw new Parse.Error(Parse.Error.INVALID_QUERY, 'improper usage of $dontSelect'); } const additionalOptions = { redirectClassNameForKey: dontSelectValue.query.redirectClassNameForKey, @@ -688,8 +663,7 @@ _UnsafeRestQuery.prototype.replaceDontSelect = async function () { if (this.restOptions.subqueryReadPreference) { additionalOptions.readPreference = this.restOptions.subqueryReadPreference; - additionalOptions.subqueryReadPreference = - this.restOptions.subqueryReadPreference; + additionalOptions.subqueryReadPreference = this.restOptions.subqueryReadPreference; } else if (this.restOptions.readPreference) { additionalOptions.readPreference = this.restOptions.readPreference; } @@ -705,11 +679,7 @@ _UnsafeRestQuery.prototype.replaceDontSelect = async function () { }); return subquery.execute().then(response => { - transformDontSelect( - dontSelectObject, - dontSelectValue.key, - response.results - ); + transformDontSelect(dontSelectObject, dontSelectValue.key, response.results); // Keep replacing $dontSelect clauses return this.replaceDontSelect(); }); @@ -810,11 +780,9 @@ _UnsafeRestQuery.prototype.runCount = function () { this.findOptions.count = true; delete this.findOptions.skip; delete this.findOptions.limit; - return this.config.database - .find(this.className, this.restWhere, this.findOptions) - .then(c => { - this.response.count = c; - }); + return this.config.database.find(this.className, this.restWhere, this.findOptions).then(c => { + this.response.count = c; + }); }; _UnsafeRestQuery.prototype.denyProtectedFields = async function () { @@ -854,8 +822,7 @@ _UnsafeRestQuery.prototype.handleIncludeAll = function () { const keyFields = []; for (const field in schema.fields) { if ( - (schema.fields[field].type && - schema.fields[field].type === 'Pointer') || + (schema.fields[field].type && schema.fields[field].type === 'Pointer') || (schema.fields[field].type && schema.fields[field].type === 'Array') ) { includeFields.push([field]); @@ -1047,8 +1014,7 @@ function includePath(config, auth, response, path, context, restOptions = {}) { if (restOptions.includeReadPreference) { includeRestOptions.readPreference = restOptions.includeReadPreference; - includeRestOptions.includeReadPreference = - restOptions.includeReadPreference; + includeRestOptions.includeReadPreference = restOptions.includeReadPreference; } else if (restOptions.readPreference) { includeRestOptions.readPreference = restOptions.readPreference; } @@ -1062,8 +1028,7 @@ function includePath(config, auth, response, path, context, restOptions = {}) { where = { objectId: { $in: objectIds } }; } const query = await RestQuery({ - method: - objectIds.length === 1 ? RestQuery.Method.get : RestQuery.Method.find, + method: objectIds.length === 1 ? RestQuery.Method.get : RestQuery.Method.find, config, auth, className, diff --git a/src/RestWrite.js b/src/RestWrite.js index dcffaa6b75..39c11f4fd7 100644 --- a/src/RestWrite.js +++ b/src/RestWrite.js @@ -27,17 +27,7 @@ import { requiredColumns } from './Controllers/SchemaController'; // RestWrite will handle objectId, createdAt, and updatedAt for // everything. It also knows to use triggers and special modifications // for the _User class. -function RestWrite( - config, - auth, - className, - query, - data, - originalData, - clientSDK, - context, - action -) { +function RestWrite(config, auth, className, query, data, originalData, clientSDK, context, action) { if (auth.isReadOnly) { throw new Parse.Error( Parse.Error.OPERATION_FORBIDDEN, @@ -58,10 +48,7 @@ function RestWrite( if (!query) { if (this.config.allowCustomObjectId) { - if ( - Object.prototype.hasOwnProperty.call(data, 'objectId') && - !data.objectId - ) { + if (Object.prototype.hasOwnProperty.call(data, 'objectId') && !data.objectId) { throw new Parse.Error( Parse.Error.MISSING_OBJECT_ID, 'objectId must not be empty, null or undefined' @@ -69,16 +56,10 @@ function RestWrite( } } else { if (data.objectId) { - throw new Parse.Error( - Parse.Error.INVALID_KEY_NAME, - 'objectId is an invalid field name.' - ); + throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'objectId is an invalid field name.'); } if (data.id) { - throw new Parse.Error( - Parse.Error.INVALID_KEY_NAME, - 'id is an invalid field name.' - ); + throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'id is an invalid field name.'); } } } @@ -180,14 +161,8 @@ RestWrite.prototype.execute = function () { this.response.response.authDataResponse = this.authDataResponse; } } - if ( - this.storage.rejectSignup && - this.config.preventSignupWithUnverifiedEmail - ) { - throw new Parse.Error( - Parse.Error.EMAIL_NOT_FOUND, - 'User email is not verified.' - ); + if (this.storage.rejectSignup && this.config.preventSignupWithUnverifiedEmail) { + throw new Parse.Error(Parse.Error.EMAIL_NOT_FOUND, 'User email is not verified.'); } return this.response; }); @@ -203,9 +178,7 @@ RestWrite.prototype.getUserAndRoleACL = function () { if (this.auth.user) { return this.auth.getUserRoles().then(roles => { - this.runOptions.acl = this.runOptions.acl.concat(roles, [ - this.auth.user.id, - ]); + this.runOptions.acl = this.runOptions.acl.concat(roles, [this.auth.user.id]); return; }); } else { @@ -228,9 +201,7 @@ RestWrite.prototype.validateClientClassCreation = function () { if (hasClass !== true) { throw new Parse.Error( Parse.Error.OPERATION_FORBIDDEN, - 'This user is not allowed to access ' + - 'non-existent class: ' + - this.className + 'This user is not allowed to access ' + 'non-existent class: ' + this.className ); } }); @@ -259,11 +230,7 @@ RestWrite.prototype.runBeforeSaveTrigger = function () { // Avoid doing any setup for triggers if there is no 'beforeSave' trigger for this class. if ( - !triggers.triggerExists( - this.className, - triggers.Types.beforeSave, - this.config.applicationId - ) + !triggers.triggerExists(this.className, triggers.Types.beforeSave, this.config.applicationId) ) { return Promise.resolve(); } @@ -303,10 +270,7 @@ RestWrite.prototype.runBeforeSaveTrigger = function () { // In the case that there is no permission for the operation, it throws an error return databasePromise.then(result => { if (!result || result.length <= 0) { - throw new Parse.Error( - Parse.Error.OBJECT_NOT_FOUND, - 'Object not found.' - ); + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.'); } }); }) @@ -349,11 +313,7 @@ RestWrite.prototype.runBeforeSaveTrigger = function () { RestWrite.prototype.runBeforeLoginTrigger = async function (userData) { // Avoid doing any setup for triggers if there is no 'beforeLogin' trigger if ( - !triggers.triggerExists( - this.className, - triggers.Types.beforeLogin, - this.config.applicationId - ) + !triggers.triggerExists(this.className, triggers.Types.beforeLogin, this.config.applicationId) ) { return; } @@ -380,16 +340,13 @@ RestWrite.prototype.runBeforeLoginTrigger = async function (userData) { RestWrite.prototype.setRequiredFieldsIfNeeded = function () { if (this.data) { return this.validSchemaController.getAllClasses().then(allClasses => { - const schema = allClasses.find( - oneClass => oneClass.className === this.className - ); + const schema = allClasses.find(oneClass => oneClass.className === this.className); const setRequiredFieldIfNeeded = (fieldName, setDefault) => { if ( this.data[fieldName] === undefined || this.data[fieldName] === null || this.data[fieldName] === '' || - (typeof this.data[fieldName] === 'object' && - this.data[fieldName].__op === 'Delete') + (typeof this.data[fieldName] === 'object' && this.data[fieldName].__op === 'Delete') ) { if ( setDefault && @@ -397,23 +354,15 @@ RestWrite.prototype.setRequiredFieldsIfNeeded = function () { schema.fields[fieldName].defaultValue !== null && schema.fields[fieldName].defaultValue !== undefined && (this.data[fieldName] === undefined || - (typeof this.data[fieldName] === 'object' && - this.data[fieldName].__op === 'Delete')) + (typeof this.data[fieldName] === 'object' && this.data[fieldName].__op === 'Delete')) ) { this.data[fieldName] = schema.fields[fieldName].defaultValue; - this.storage.fieldsChangedByTrigger = - this.storage.fieldsChangedByTrigger || []; + this.storage.fieldsChangedByTrigger = this.storage.fieldsChangedByTrigger || []; if (this.storage.fieldsChangedByTrigger.indexOf(fieldName) < 0) { this.storage.fieldsChangedByTrigger.push(fieldName); } - } else if ( - schema.fields[fieldName] && - schema.fields[fieldName].required === true - ) { - throw new Parse.Error( - Parse.Error.VALIDATION_ERROR, - `${fieldName} is required` - ); + } else if (schema.fields[fieldName] && schema.fields[fieldName].required === true) { + throw new Parse.Error(Parse.Error.VALIDATION_ERROR, `${fieldName} is required`); } } }; @@ -433,8 +382,7 @@ RestWrite.prototype.setRequiredFieldsIfNeeded = function () { delete acl.currentUser; } this.data.ACL = acl; - this.storage.fieldsChangedByTrigger = - this.storage.fieldsChangedByTrigger || []; + this.storage.fieldsChangedByTrigger = this.storage.fieldsChangedByTrigger || []; this.storage.fieldsChangedByTrigger.push('ACL'); } @@ -472,9 +420,7 @@ RestWrite.prototype.setRequiredFieldsIfNeeded = function () { // Only assign new objectId if we are creating new object if (!this.data.objectId) { - this.data.objectId = cryptoUtils.newObjectId( - this.config.objectIdSize - ); + this.data.objectId = cryptoUtils.newObjectId(this.config.objectIdSize); } if (schema) { Object.keys(schema.fields).forEach(fieldName => { @@ -503,27 +449,14 @@ RestWrite.prototype.validateAuthData = function () { const authData = this.data.authData; const hasUsernameAndPassword = - typeof this.data.username === 'string' && - typeof this.data.password === 'string'; + typeof this.data.username === 'string' && typeof this.data.password === 'string'; if (!this.query && !authData) { - if ( - typeof this.data.username !== 'string' || - _.isEmpty(this.data.username) - ) { - throw new Parse.Error( - Parse.Error.USERNAME_MISSING, - 'bad or missing username' - ); + if (typeof this.data.username !== 'string' || _.isEmpty(this.data.username)) { + throw new Parse.Error(Parse.Error.USERNAME_MISSING, 'bad or missing username'); } - if ( - typeof this.data.password !== 'string' || - _.isEmpty(this.data.password) - ) { - throw new Parse.Error( - Parse.Error.PASSWORD_MISSING, - 'password is required' - ); + if (typeof this.data.password !== 'string' || _.isEmpty(this.data.password)) { + throw new Parse.Error(Parse.Error.PASSWORD_MISSING, 'password is required'); } } @@ -533,10 +466,7 @@ RestWrite.prototype.validateAuthData = function () { ) { // Nothing to validate here return; - } else if ( - Object.prototype.hasOwnProperty.call(this.data, 'authData') && - !this.data.authData - ) { + } else if (Object.prototype.hasOwnProperty.call(this.data, 'authData') && !this.data.authData) { // Handle saving authData to null throw new Parse.Error( Parse.Error.UNSUPPORTED_SERVICE, @@ -550,12 +480,7 @@ RestWrite.prototype.validateAuthData = function () { const providerAuthData = authData[provider] || {}; return !!Object.keys(providerAuthData).length; }); - if ( - canHandleAuthData || - hasUsernameAndPassword || - this.auth.isMaster || - this.getUserId() - ) { + if (canHandleAuthData || hasUsernameAndPassword || this.auth.isMaster || this.getUserId()) { return this.handleAuthData(authData); } } @@ -605,18 +530,12 @@ RestWrite.prototype.ensureUniqueAuthDataId = async function () { const r = await Auth.findUsersWithAuthData(this.config, this.data.authData); const results = this.filteredObjectsByACL(r); if (results.length > 1) { - throw new Parse.Error( - Parse.Error.ACCOUNT_ALREADY_LINKED, - 'this auth is already used' - ); + throw new Parse.Error(Parse.Error.ACCOUNT_ALREADY_LINKED, 'this auth is already used'); } // use data.objectId in case of login time and found user during handle validateAuthData const userId = this.getUserId() || this.data.objectId; if (results.length === 1 && userId !== results[0].objectId) { - throw new Parse.Error( - Parse.Error.ACCOUNT_ALREADY_LINKED, - 'this auth is already used' - ); + throw new Parse.Error(Parse.Error.ACCOUNT_ALREADY_LINKED, 'this auth is already used'); } }; @@ -626,23 +545,21 @@ RestWrite.prototype.handleAuthData = async function (authData) { const userId = this.getUserId(); const userResult = results[0]; - const foundUserIsNotCurrentUser = - userId && userResult && userId !== userResult.objectId; + const foundUserIsNotCurrentUser = userId && userResult && userId !== userResult.objectId; if (results.length > 1 || foundUserIsNotCurrentUser) { // To avoid https://github.com/parse-community/parse-server/security/advisories/GHSA-8w3j-g983-8jh5 // Let's run some validation before throwing await Auth.handleAuthDataValidation(authData, this, userResult); - throw new Parse.Error( - Parse.Error.ACCOUNT_ALREADY_LINKED, - 'this auth is already used' - ); + throw new Parse.Error(Parse.Error.ACCOUNT_ALREADY_LINKED, 'this auth is already used'); } // No user found with provided authData we need to validate if (!results.length) { - const { authData: validatedAuthData, authDataResponse } = - await Auth.handleAuthDataValidation(authData, this); + const { authData: validatedAuthData, authDataResponse } = await Auth.handleAuthDataValidation( + authData, + this + ); this.authDataResponse = authDataResponse; // Replace current authData by the new validated one this.data.authData = validatedAuthData; @@ -659,9 +576,7 @@ RestWrite.prototype.handleAuthData = async function (authData) { ); const isCurrentUserLoggedOrMaster = - (this.auth && - this.auth.user && - this.auth.user.id === userResult.objectId) || + (this.auth && this.auth.user && this.auth.user.id === userResult.objectId) || this.auth.isMaster; const isLogin = !userId; @@ -745,11 +660,7 @@ RestWrite.prototype.checkRestrictedFields = async function () { return; } - if ( - !this.auth.isMaintenance && - !this.auth.isMaster && - 'emailVerified' in this.data - ) { + if (!this.auth.isMaintenance && !this.auth.isMaster && 'emailVerified' in this.data) { const error = `Clients aren't allowed to manually update email verification.`; throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, error); } @@ -875,10 +786,7 @@ RestWrite.prototype._validateEmail = function () { // Validate basic email address format if (!this.data.email.match(/^.+@.+$/)) { return Promise.reject( - new Parse.Error( - Parse.Error.INVALID_EMAIL_ADDRESS, - 'Email address format is invalid.' - ) + new Parse.Error(Parse.Error.INVALID_EMAIL_ADDRESS, 'Email address format is invalid.') ); } // Case insensitive match, see note above function. @@ -915,11 +823,7 @@ RestWrite.prototype._validateEmail = function () { ip: this.config.ip, installationId: this.auth.installationId, }; - return this.config.userController.setEmailVerifyToken( - this.data, - request, - this.storage - ); + return this.config.userController.setEmailVerifyToken(this.data, request, this.storage); } }); }; @@ -954,9 +858,7 @@ RestWrite.prototype._validatePasswordRequirements = function () { (this.config.passwordPolicy.validatorCallback && !this.config.passwordPolicy.validatorCallback(this.data.password)) ) { - return Promise.reject( - new Parse.Error(Parse.Error.VALIDATION_ERROR, policyError) - ); + return Promise.reject(new Parse.Error(Parse.Error.VALIDATION_ERROR, policyError)); } // check whether password contain username @@ -964,28 +866,21 @@ RestWrite.prototype._validatePasswordRequirements = function () { if (this.data.username) { // username is not passed during password reset if (this.data.password.indexOf(this.data.username) >= 0) { - return Promise.reject( - new Parse.Error(Parse.Error.VALIDATION_ERROR, containsUsernameError) - ); + return Promise.reject(new Parse.Error(Parse.Error.VALIDATION_ERROR, containsUsernameError)); } } else { // retrieve the User object using objectId during password reset - return this.config.database - .find('_User', { objectId: this.objectId() }) - .then(results => { - if (results.length != 1) { - throw undefined; - } - if (this.data.password.indexOf(results[0].username) >= 0) { - return Promise.reject( - new Parse.Error( - Parse.Error.VALIDATION_ERROR, - containsUsernameError - ) - ); - } - return Promise.resolve(); - }); + return this.config.database.find('_User', { objectId: this.objectId() }).then(results => { + if (results.length != 1) { + throw undefined; + } + if (this.data.password.indexOf(results[0].username) >= 0) { + return Promise.reject( + new Parse.Error(Parse.Error.VALIDATION_ERROR, containsUsernameError) + ); + } + return Promise.resolve(); + }); } } return Promise.resolve(); @@ -1076,19 +971,13 @@ RestWrite.prototype.createSessionTokenIfNeeded = async function () { const verifyUserEmails = async () => this.config.verifyUserEmails === true || (typeof this.config.verifyUserEmails === 'function' && - (await Promise.resolve(this.config.verifyUserEmails(request))) === - true); + (await Promise.resolve(this.config.verifyUserEmails(request))) === true); const preventLoginWithUnverifiedEmail = async () => this.config.preventLoginWithUnverifiedEmail === true || (typeof this.config.preventLoginWithUnverifiedEmail === 'function' && - (await Promise.resolve( - this.config.preventLoginWithUnverifiedEmail(request) - )) === true); + (await Promise.resolve(this.config.preventLoginWithUnverifiedEmail(request))) === true); // If verification is required - if ( - (await verifyUserEmails()) && - (await preventLoginWithUnverifiedEmail()) - ) { + if ((await verifyUserEmails()) && (await preventLoginWithUnverifiedEmail())) { this.storage.rejectSignup = true; return; } @@ -1149,13 +1038,7 @@ RestWrite.createSession = function ( return { sessionData, createSession: () => - new RestWrite( - config, - Auth.master(config), - '_Session', - null, - sessionData - ).execute(), + new RestWrite(config, Auth.master(config), '_Session', null, sessionData).execute(), }; }; @@ -1202,11 +1085,7 @@ RestWrite.prototype.destroyDuplicatedSessions = function () { // Handles any followup logic RestWrite.prototype.handleFollowup = function () { - if ( - this.storage && - this.storage['clearSessions'] && - this.config.revokeSessionOnPasswordReset - ) { + if (this.storage && this.storage['clearSessions'] && this.config.revokeSessionOnPasswordReset) { var sessionQuery = { user: { __type: 'Pointer', @@ -1243,26 +1122,16 @@ RestWrite.prototype.handleSession = function () { } if (!this.auth.user && !this.auth.isMaster && !this.auth.isMaintenance) { - throw new Parse.Error( - Parse.Error.INVALID_SESSION_TOKEN, - 'Session token required.' - ); + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Session token required.'); } // TODO: Verify proper error to throw if (this.data.ACL) { - throw new Parse.Error( - Parse.Error.INVALID_KEY_NAME, - 'Cannot set ' + 'ACL on a Session.' - ); + throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'Cannot set ' + 'ACL on a Session.'); } if (this.query) { - if ( - this.data.user && - !this.auth.isMaster && - this.data.user.objectId != this.auth.user.id - ) { + if (this.data.user && !this.auth.isMaster && this.data.user.objectId != this.auth.user.id) { throw new Parse.Error(Parse.Error.INVALID_KEY_NAME); } else if (this.data.installationId) { throw new Parse.Error(Parse.Error.INVALID_KEY_NAME); @@ -1294,23 +1163,17 @@ RestWrite.prototype.handleSession = function () { additionalSessionData[key] = this.data[key]; } - const { sessionData, createSession } = RestWrite.createSession( - this.config, - { - userId: this.auth.user.id, - createdWith: { - action: 'create', - }, - additionalSessionData, - } - ); + const { sessionData, createSession } = RestWrite.createSession(this.config, { + userId: this.auth.user.id, + createdWith: { + action: 'create', + }, + additionalSessionData, + }); return createSession().then(results => { if (!results.response) { - throw new Parse.Error( - Parse.Error.INTERNAL_SERVER_ERROR, - 'Error creating session.' - ); + throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Error creating session.'); } sessionData['objectId'] = results.response['objectId']; this.response = { @@ -1340,8 +1203,7 @@ RestWrite.prototype.handleInstallation = function () { ) { throw new Parse.Error( 135, - 'at least one ID field (deviceToken, installationId) ' + - 'must be specified in this operation' + 'at least one ID field (deviceToken, installationId) ' + 'must be specified in this operation' ); } @@ -1368,12 +1230,7 @@ RestWrite.prototype.handleInstallation = function () { } // Updating _Installation but not updating anything critical - if ( - this.query && - !this.data.deviceToken && - !installationId && - !this.data.deviceType - ) { + if (this.query && !this.data.deviceToken && !installationId && !this.data.deviceType) { return; } @@ -1416,11 +1273,7 @@ RestWrite.prototype.handleInstallation = function () { }) .then(results => { results.forEach(result => { - if ( - this.query && - this.query.objectId && - result.objectId == this.query.objectId - ) { + if (this.query && this.query.objectId && result.objectId == this.query.objectId) { objectIdMatch = result; } if (result.installationId == installationId) { @@ -1434,20 +1287,14 @@ RestWrite.prototype.handleInstallation = function () { // Sanity checks when running a query if (this.query && this.query.objectId) { if (!objectIdMatch) { - throw new Parse.Error( - Parse.Error.OBJECT_NOT_FOUND, - 'Object not found for update.' - ); + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found for update.'); } if ( this.data.installationId && objectIdMatch.installationId && this.data.installationId !== objectIdMatch.installationId ) { - throw new Parse.Error( - 136, - 'installationId may not be changed in this ' + 'operation' - ); + throw new Parse.Error(136, 'installationId may not be changed in this ' + 'operation'); } if ( this.data.deviceToken && @@ -1456,20 +1303,14 @@ RestWrite.prototype.handleInstallation = function () { !this.data.installationId && !objectIdMatch.installationId ) { - throw new Parse.Error( - 136, - 'deviceToken may not be changed in this ' + 'operation' - ); + throw new Parse.Error(136, 'deviceToken may not be changed in this ' + 'operation'); } if ( this.data.deviceType && this.data.deviceType && this.data.deviceType !== objectIdMatch.deviceType ) { - throw new Parse.Error( - 136, - 'deviceType may not be changed in this ' + 'operation' - ); + throw new Parse.Error(136, 'deviceType may not be changed in this ' + 'operation'); } } @@ -1482,10 +1323,7 @@ RestWrite.prototype.handleInstallation = function () { } // need to specify deviceType only if it's new if (!this.query && !this.data.deviceType && !idMatch) { - throw new Parse.Error( - 135, - 'deviceType must be specified in this operation' - ); + throw new Parse.Error(135, 'deviceType must be specified in this operation'); } }) .then(() => { @@ -1532,10 +1370,7 @@ RestWrite.prototype.handleInstallation = function () { return; } } else { - if ( - deviceTokenMatches.length == 1 && - !deviceTokenMatches[0]['installationId'] - ) { + if (deviceTokenMatches.length == 1 && !deviceTokenMatches[0]['installationId']) { // Exactly one device token match and it doesn't have an installation // ID. This is the one case where we want to merge with the existing // object. @@ -1554,10 +1389,7 @@ RestWrite.prototype.handleInstallation = function () { throw err; }); } else { - if ( - this.data.deviceToken && - idMatch.deviceToken != this.data.deviceToken - ) { + if (this.data.deviceToken && idMatch.deviceToken != this.data.deviceToken) { // We're setting the device token on an existing installation, so // we should try cleaning out old installations that match this // device token. @@ -1586,16 +1418,14 @@ RestWrite.prototype.handleInstallation = function () { if (this.data.appIdentifier) { delQuery['appIdentifier'] = this.data.appIdentifier; } - this.config.database - .destroy('_Installation', delQuery) - .catch(err => { - if (err.code == Parse.Error.OBJECT_NOT_FOUND) { - // no deletions were made. Can be ignored. - return; - } - // rethrow the error - throw err; - }); + this.config.database.destroy('_Installation', delQuery).catch(err => { + if (err.code == Parse.Error.OBJECT_NOT_FOUND) { + // no deletions were made. Can be ignored. + return; + } + // rethrow the error + throw err; + }); } // In non-merge scenarios, just return the installation match id return idMatch.objectId; @@ -1619,10 +1449,7 @@ RestWrite.prototype.handleInstallation = function () { RestWrite.prototype.expandFilesForExistingObjects = async function () { // Check whether we have a short-circuited response - only then run expansion. if (this.response && this.response.response) { - await this.config.filesController.expandFilesInObject( - this.config, - this.response.response - ); + await this.config.filesController.expandFilesInObject(this.config, this.response.response); } }; @@ -1638,11 +1465,7 @@ RestWrite.prototype.runDatabaseOperation = function () { } } - if ( - this.className === '_User' && - this.query && - this.auth.isUnauthenticated() - ) { + if (this.className === '_User' && this.query && this.auth.isUnauthenticated()) { throw new Parse.Error( Parse.Error.SESSION_MISSING, `Cannot modify user ${this.query.objectId}.` @@ -1711,8 +1534,7 @@ RestWrite.prototype.runDatabaseOperation = function () { } //n-1 passwords go into history including last password while ( - oldPasswords.length > - Math.max(0, this.config.passwordPolicy.maxPasswordHistory - 2) + oldPasswords.length > Math.max(0, this.config.passwordPolicy.maxPasswordHistory - 2) ) { oldPasswords.shift(); } @@ -1754,48 +1576,28 @@ RestWrite.prototype.runDatabaseOperation = function () { ACL[this.data.objectId] = { read: true, write: true }; this.data.ACL = ACL; // password timestamp to be used when password expiry policy is enforced - if ( - this.config.passwordPolicy && - this.config.passwordPolicy.maxPasswordAge - ) { + if (this.config.passwordPolicy && this.config.passwordPolicy.maxPasswordAge) { this.data._password_changed_at = Parse._encode(new Date()); } } // Run a create return this.config.database - .create( - this.className, - this.data, - this.runOptions, - false, - this.validSchemaController - ) + .create(this.className, this.data, this.runOptions, false, this.validSchemaController) .catch(error => { - if ( - this.className !== '_User' || - error.code !== Parse.Error.DUPLICATE_VALUE - ) { + if (this.className !== '_User' || error.code !== Parse.Error.DUPLICATE_VALUE) { throw error; } // Quick check, if we were able to infer the duplicated field name - if ( - error && - error.userInfo && - error.userInfo.duplicated_field === 'username' - ) { + if (error && error.userInfo && error.userInfo.duplicated_field === 'username') { throw new Parse.Error( Parse.Error.USERNAME_TAKEN, 'Account already exists for this username.' ); } - if ( - error && - error.userInfo && - error.userInfo.duplicated_field === 'email' - ) { + if (error && error.userInfo && error.userInfo.duplicated_field === 'email') { throw new Parse.Error( Parse.Error.EMAIL_TAKEN, 'Account already exists for this email address.' @@ -1870,25 +1672,18 @@ RestWrite.prototype.runAfterSaveTrigger = function () { triggers.Types.afterSave, this.config.applicationId ); - const hasLiveQuery = this.config.liveQueryController.hasLiveQuery( - this.className - ); + const hasLiveQuery = this.config.liveQueryController.hasLiveQuery(this.className); if (!hasAfterSaveHook && !hasLiveQuery) { return Promise.resolve(); } const { originalObject, updatedObject } = this.buildParseObjects(); - updatedObject._handleSaveResponse( - this.response.response, - this.response.status || 200 - ); + updatedObject._handleSaveResponse(this.response.response, this.response.status || 200); if (hasLiveQuery) { this.config.database.loadSchema().then(schemaController => { // Notify LiveQueryServer if possible - const perms = schemaController.getClassLevelPermissions( - updatedObject.className - ); + const perms = schemaController.getClassLevelPermissions(updatedObject.className); this.config.liveQueryController.onAfterSave( updatedObject.className, updatedObject, @@ -1929,8 +1724,7 @@ RestWrite.prototype.runAfterSaveTrigger = function () { // A helper to figure out what location this operation happens at. RestWrite.prototype.location = function () { - var middle = - this.className === '_User' ? '/users/' : '/classes/' + this.className + '/'; + var middle = this.className === '_User' ? '/users/' : '/classes/' + this.className + '/'; const mount = this.config.mount || this.config.serverURL; return mount + middle + this.data.objectId; }; @@ -2025,9 +1819,7 @@ RestWrite.prototype._updateResponseWithData = function (response, data) { const [pending] = stateController.getPendingOps(this.pendingOps.identifier); for (const key in this.pendingOps.operations) { if (!pending[key]) { - data[key] = this.originalData - ? this.originalData[key] - : { __op: 'Delete' }; + data[key] = this.originalData ? this.originalData[key] : { __op: 'Delete' }; this.storage.fieldsChangedByTrigger.push(key); } } diff --git a/src/Routers/AggregateRouter.js b/src/Routers/AggregateRouter.js index ec7e448b95..cf9b5cd190 100644 --- a/src/Routers/AggregateRouter.js +++ b/src/Routers/AggregateRouter.js @@ -6,10 +6,7 @@ import UsersRouter from './UsersRouter'; export class AggregateRouter extends ClassesRouter { async handleFind(req) { - const body = Object.assign( - req.body || {}, - ClassesRouter.JSONFromQuery(req.query) - ); + const body = Object.assign(req.body || {}, ClassesRouter.JSONFromQuery(req.query)); const options = {}; if (body.distinct) { options.distinct = String(body.distinct); @@ -108,10 +105,7 @@ export class AggregateRouter extends ClassesRouter { return; } if (stageName[0] !== '$') { - throw new Parse.Error( - Parse.Error.INVALID_QUERY, - `Invalid aggregate stage '${stageName}'.` - ); + throw new Parse.Error(Parse.Error.INVALID_QUERY, `Invalid aggregate stage '${stageName}'.`); } if (stageName === '$group') { if (Object.prototype.hasOwnProperty.call(stage[stageName], 'objectId')) { @@ -131,14 +125,9 @@ export class AggregateRouter extends ClassesRouter { } mountRoutes() { - this.route( - 'GET', - '/aggregate/:className', - middleware.promiseEnforceMasterKeyAccess, - req => { - return this.handleFind(req); - } - ); + this.route('GET', '/aggregate/:className', middleware.promiseEnforceMasterKeyAccess, req => { + return this.handleFind(req); + }); } } diff --git a/src/Routers/AudiencesRouter.js b/src/Routers/AudiencesRouter.js index bd5fd15b6d..d16a34fb30 100644 --- a/src/Routers/AudiencesRouter.js +++ b/src/Routers/AudiencesRouter.js @@ -8,14 +8,8 @@ export class AudiencesRouter extends ClassesRouter { } handleFind(req) { - const body = Object.assign( - req.body || {}, - ClassesRouter.JSONFromQuery(req.query) - ); - const options = ClassesRouter.optionsFromBody( - body, - req.config.defaultLimit - ); + const body = Object.assign(req.body || {}, ClassesRouter.JSONFromQuery(req.query)); + const options = ClassesRouter.optionsFromBody(body, req.config.defaultLimit); return rest .find( @@ -45,14 +39,9 @@ export class AudiencesRouter extends ClassesRouter { } mountRoutes() { - this.route( - 'GET', - '/push_audiences', - middleware.promiseEnforceMasterKeyAccess, - req => { - return this.handleFind(req); - } - ); + this.route('GET', '/push_audiences', middleware.promiseEnforceMasterKeyAccess, req => { + return this.handleFind(req); + }); this.route( 'GET', '/push_audiences/:objectId', @@ -61,14 +50,9 @@ export class AudiencesRouter extends ClassesRouter { return this.handleGet(req); } ); - this.route( - 'POST', - '/push_audiences', - middleware.promiseEnforceMasterKeyAccess, - req => { - return this.handleCreate(req); - } - ); + this.route('POST', '/push_audiences', middleware.promiseEnforceMasterKeyAccess, req => { + return this.handleCreate(req); + }); this.route( 'PUT', '/push_audiences/:objectId', diff --git a/src/Routers/ClassesRouter.js b/src/Routers/ClassesRouter.js index d3e04d3acd..8b6e447757 100644 --- a/src/Routers/ClassesRouter.js +++ b/src/Routers/ClassesRouter.js @@ -19,14 +19,8 @@ export class ClassesRouter extends PromiseRouter { } handleFind(req) { - const body = Object.assign( - req.body || {}, - ClassesRouter.JSONFromQuery(req.query) - ); - const options = ClassesRouter.optionsFromBody( - body, - req.config.defaultLimit - ); + const body = Object.assign(req.body || {}, ClassesRouter.JSONFromQuery(req.query)); + const options = ClassesRouter.optionsFromBody(body, req.config.defaultLimit); if (req.config.maxLimit && body.limit > req.config.maxLimit) { // Silently replace the limit on the query with the max configured options.limit = Number(req.config.maxLimit); @@ -54,18 +48,12 @@ export class ClassesRouter extends PromiseRouter { // Returns a promise for a {response} object. handleGet(req) { - const body = Object.assign( - req.body || {}, - ClassesRouter.JSONFromQuery(req.query) - ); + const body = Object.assign(req.body || {}, ClassesRouter.JSONFromQuery(req.query)); const options = {}; for (const key of Object.keys(body)) { if (ALLOWED_GET_QUERY_KEYS.indexOf(key) === -1) { - throw new Parse.Error( - Parse.Error.INVALID_QUERY, - 'Improper encode of parameter' - ); + throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Improper encode of parameter'); } } @@ -100,10 +88,7 @@ export class ClassesRouter extends PromiseRouter { ) .then(response => { if (!response.results || response.results.length == 0) { - throw new Parse.Error( - Parse.Error.OBJECT_NOT_FOUND, - 'Object not found.' - ); + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.'); } if (this.className(req) === '_User') { @@ -126,10 +111,7 @@ export class ClassesRouter extends PromiseRouter { typeof req.body?.objectId === 'string' && req.body.objectId.startsWith('role:') ) { - throw new Parse.Error( - Parse.Error.OPERATION_FORBIDDEN, - 'Invalid object ID.' - ); + throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Invalid object ID.'); } return rest.create( req.config, @@ -156,13 +138,7 @@ export class ClassesRouter extends PromiseRouter { handleDelete(req) { return rest - .del( - req.config, - req.auth, - this.className(req), - req.params.objectId, - req.info.context - ) + .del(req.config, req.auth, this.className(req), req.params.objectId, req.info.context) .then(() => { return { response: {} }; }); @@ -202,10 +178,7 @@ export class ClassesRouter extends PromiseRouter { for (const key of Object.keys(body)) { if (allowConstraints.indexOf(key) === -1) { - throw new Parse.Error( - Parse.Error.INVALID_QUERY, - `Invalid parameter for query: ${key}` - ); + throw new Parse.Error(Parse.Error.INVALID_QUERY, `Invalid parameter for query: ${key}`); } } const options = {}; @@ -244,10 +217,7 @@ export class ClassesRouter extends PromiseRouter { if (typeof body.subqueryReadPreference === 'string') { options.subqueryReadPreference = body.subqueryReadPreference; } - if ( - body.hint && - (typeof body.hint === 'string' || typeof body.hint === 'object') - ) { + if (body.hint && (typeof body.hint === 'string' || typeof body.hint === 'object')) { options.hint = body.hint; } if (body.explain) { @@ -269,14 +239,9 @@ export class ClassesRouter extends PromiseRouter { this.route('POST', '/classes/:className', promiseEnsureIdempotency, req => { return this.handleCreate(req); }); - this.route( - 'PUT', - '/classes/:className/:objectId', - promiseEnsureIdempotency, - req => { - return this.handleUpdate(req); - } - ); + this.route('PUT', '/classes/:className/:objectId', promiseEnsureIdempotency, req => { + return this.handleUpdate(req); + }); this.route('DELETE', '/classes/:className/:objectId', req => { return this.handleDelete(req); }); diff --git a/src/Routers/CloudCodeRouter.js b/src/Routers/CloudCodeRouter.js index 292d56c994..58408151e7 100644 --- a/src/Routers/CloudCodeRouter.js +++ b/src/Routers/CloudCodeRouter.js @@ -56,28 +56,24 @@ export class CloudCodeRouter extends PromiseRouter { } static getJobs(req) { - return rest - .find(req.config, req.auth, '_JobSchedule', {}, {}) - .then(scheduledJobs => { - return { - response: scheduledJobs.results, - }; - }); + return rest.find(req.config, req.auth, '_JobSchedule', {}, {}).then(scheduledJobs => { + return { + response: scheduledJobs.results, + }; + }); } static getJobsData(req) { const config = req.config; const jobs = triggers.getJobs(config.applicationId) || {}; - return rest - .find(req.config, req.auth, '_JobSchedule', {}, {}) - .then(scheduledJobs => { - return { - response: { - in_use: scheduledJobs.results.map(job => job.jobName), - jobs: Object.keys(jobs), - }, - }; - }); + return rest.find(req.config, req.auth, '_JobSchedule', {}, {}).then(scheduledJobs => { + return { + response: { + in_use: scheduledJobs.results.map(job => job.jobName), + jobs: Object.keys(jobs), + }, + }; + }); } static createJob(req) { diff --git a/src/Routers/FeaturesRouter.js b/src/Routers/FeaturesRouter.js index a83b4d01cc..df26338955 100644 --- a/src/Routers/FeaturesRouter.js +++ b/src/Routers/FeaturesRouter.js @@ -4,64 +4,59 @@ import * as middleware from '../middlewares'; export class FeaturesRouter extends PromiseRouter { mountRoutes() { - this.route( - 'GET', - '/serverInfo', - middleware.promiseEnforceMasterKeyAccess, - req => { - const { config } = req; - const features = { - globalConfig: { - create: true, - read: true, - update: true, - delete: true, - }, - hooks: { - create: true, - read: true, - update: true, - delete: true, - }, - cloudCode: { - jobs: true, - }, - logs: { - level: true, - size: true, - order: true, - until: true, - from: true, - }, - push: { - immediatePush: config.hasPushSupport, - scheduledPush: config.hasPushScheduledSupport, - storedPushData: config.hasPushSupport, - pushAudiences: true, - localization: true, - }, - schemas: { - addField: true, - removeField: true, - addClass: true, - removeClass: true, - clearAllDataFromClass: true, - exportClass: false, - editClassLevelPermissions: true, - editPointerPermissions: true, - }, - settings: { - securityCheck: !!config.security?.enableCheck, - }, - }; + this.route('GET', '/serverInfo', middleware.promiseEnforceMasterKeyAccess, req => { + const { config } = req; + const features = { + globalConfig: { + create: true, + read: true, + update: true, + delete: true, + }, + hooks: { + create: true, + read: true, + update: true, + delete: true, + }, + cloudCode: { + jobs: true, + }, + logs: { + level: true, + size: true, + order: true, + until: true, + from: true, + }, + push: { + immediatePush: config.hasPushSupport, + scheduledPush: config.hasPushScheduledSupport, + storedPushData: config.hasPushSupport, + pushAudiences: true, + localization: true, + }, + schemas: { + addField: true, + removeField: true, + addClass: true, + removeClass: true, + clearAllDataFromClass: true, + exportClass: false, + editClassLevelPermissions: true, + editPointerPermissions: true, + }, + settings: { + securityCheck: !!config.security?.enableCheck, + }, + }; - return { - response: { - features: features, - parseServerVersion: version, - }, - }; - } - ); + return { + response: { + features: features, + parseServerVersion: version, + }, + }; + }); } } diff --git a/src/Routers/FilesRouter.js b/src/Routers/FilesRouter.js index 28b7626351..0bec64c9aa 100644 --- a/src/Routers/FilesRouter.js +++ b/src/Routers/FilesRouter.js @@ -39,9 +39,7 @@ export class FilesRouter { router.get('/files/:appId/metadata/:filename', this.metadataHandler); router.post('/files', function (req, res, next) { - next( - new Parse.Error(Parse.Error.INVALID_FILE_NAME, 'Filename not provided.') - ); + next(new Parse.Error(Parse.Error.INVALID_FILE_NAME, 'Filename not provided.')); }); router.post( @@ -71,10 +69,7 @@ export class FilesRouter { const config = Config.get(req.params.appId); if (!config) { res.status(403); - const err = new Parse.Error( - Parse.Error.OPERATION_FORBIDDEN, - 'Invalid application ID.' - ); + const err = new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Invalid application ID.'); res.json({ code: err.code, error: err.message }); return; } @@ -97,31 +92,23 @@ export class FilesRouter { } if (isFileStreamable(req, filesController)) { - filesController - .handleFileStream(config, filename, req, res, contentType) - .catch(() => { - res.status(404); - res.set('Content-Type', 'text/plain'); - res.end('File not found.'); - }); - return; - } - - let data = await filesController - .getFileData(config, filename) - .catch(() => { + filesController.handleFileStream(config, filename, req, res, contentType).catch(() => { res.status(404); res.set('Content-Type', 'text/plain'); res.end('File not found.'); }); + return; + } + + let data = await filesController.getFileData(config, filename).catch(() => { + res.status(404); + res.set('Content-Type', 'text/plain'); + res.end('File not found.'); + }); if (!data) { return; } - file = new Parse.File( - filename, - { base64: data.toString('base64') }, - contentType - ); + file = new Parse.File(filename, { base64: data.toString('base64') }, contentType); const afterFind = await triggers.maybeRunFileTrigger( triggers.Types.afterFind, { file, forceDownload: false }, @@ -138,10 +125,7 @@ export class FilesRouter { res.set('Content-Type', contentType); res.set('Content-Length', data.length); if (afterFind.forceDownload) { - res.set( - 'Content-Disposition', - `attachment;filename=${afterFind.file._name}` - ); + res.set('Content-Disposition', `attachment;filename=${afterFind.file._name}`); } res.end(data); } catch (e) { @@ -161,19 +145,11 @@ export class FilesRouter { const isLinked = user && Parse.AnonymousUtils.isLinked(user); if (!isMaster && !config.fileUpload.enableForAnonymousUser && isLinked) { next( - new Parse.Error( - Parse.Error.FILE_SAVE_ERROR, - 'File upload by anonymous user is disabled.' - ) + new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'File upload by anonymous user is disabled.') ); return; } - if ( - !isMaster && - !config.fileUpload.enableForAuthenticatedUser && - !isLinked && - user - ) { + if (!isMaster && !config.fileUpload.enableForAuthenticatedUser && !isLinked && user) { next( new Parse.Error( Parse.Error.FILE_SAVE_ERROR, @@ -183,12 +159,7 @@ export class FilesRouter { return; } if (!isMaster && !config.fileUpload.enableForPublic && !user) { - next( - new Parse.Error( - Parse.Error.FILE_SAVE_ERROR, - 'File upload by public is disabled.' - ) - ); + next(new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'File upload by public is disabled.')); return; } const filesController = config.filesController; @@ -196,9 +167,7 @@ export class FilesRouter { const contentType = req.get('Content-type'); if (!req.body || !req.body.length) { - next( - new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'Invalid file upload.') - ); + next(new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'Invalid file upload.')); return; } @@ -290,9 +259,7 @@ export class FilesRouter { // some s3-compatible providers (DigitalOcean, Linode) do not accept tags // so we do not include the tags option if it is empty. const fileTags = - Object.keys(fileObject.file._tags).length > 0 - ? { tags: fileObject.file._tags } - : {}; + Object.keys(fileObject.file._tags).length > 0 ? { tags: fileObject.file._tags } : {}; Object.assign(fileOptions, fileTags); // save file const createFileResult = await filesController.createFile( @@ -313,12 +280,7 @@ export class FilesRouter { }; } // run afterSaveFile trigger - await triggers.maybeRunFileTrigger( - triggers.Types.afterSave, - fileObject, - config, - req.auth - ); + await triggers.maybeRunFileTrigger(triggers.Types.afterSave, fileObject, config, req.auth); res.status(201); res.set('Location', saveResult.url); res.json(saveResult); @@ -338,10 +300,7 @@ export class FilesRouter { const { filename } = req.params; // run beforeDeleteFile trigger const file = new Parse.File(filename); - file._url = await filesController.adapter.getFileLocation( - req.config, - filename - ); + file._url = await filesController.adapter.getFileLocation(req.config, filename); const fileObject = { file, fileSize: null }; await triggers.maybeRunFileTrigger( triggers.Types.beforeDelete, @@ -391,7 +350,6 @@ function isFileStreamable(req, filesController) { const start = Number(range[0]); const end = Number(range[1]); return ( - (!isNaN(start) || !isNaN(end)) && - typeof filesController.adapter.handleFileStream === 'function' + (!isNaN(start) || !isNaN(end)) && typeof filesController.adapter.handleFileStream === 'function' ); } diff --git a/src/Routers/FunctionsRouter.js b/src/Routers/FunctionsRouter.js index c5ca520857..4c90ac2810 100644 --- a/src/Routers/FunctionsRouter.js +++ b/src/Routers/FunctionsRouter.js @@ -4,10 +4,7 @@ var Parse = require('parse/node').Parse, triggers = require('../triggers'); import PromiseRouter from '../PromiseRouter'; -import { - promiseEnforceMasterKeyAccess, - promiseEnsureIdempotency, -} from '../middlewares'; +import { promiseEnforceMasterKeyAccess, promiseEnsureIdempotency } from '../middlewares'; import { jobStatusHandler } from '../StatusHandler'; import _ from 'lodash'; import { logger } from '../logger'; @@ -21,11 +18,7 @@ function parseObject(obj, config) { return Object.assign(new Date(obj.iso), obj); } else if (obj && obj.__type == 'File') { return Parse.File.fromJSON(obj); - } else if ( - obj && - obj.__type == 'Pointer' && - config.encodeParseObjectInCloudFunction - ) { + } else if (obj && obj.__type == 'Pointer' && config.encodeParseObjectInCloudFunction) { return Parse.Object.fromJSON({ __type: 'Pointer', className: obj.className, @@ -130,10 +123,7 @@ export class FunctionsRouter extends PromiseRouter { const theFunction = triggers.getFunction(functionName, applicationId); if (!theFunction) { - throw new Parse.Error( - Parse.Error.SCRIPT_FAILED, - `Invalid function: "${functionName}"` - ); + throw new Parse.Error(Parse.Error.SCRIPT_FAILED, `Invalid function: "${functionName}"`); } let params = Object.assign({}, req.body, req.query); params = parseParams(params, req.config); @@ -150,18 +140,13 @@ export class FunctionsRouter extends PromiseRouter { }; return new Promise(function (resolve, reject) { - const userString = - req.auth && req.auth.user ? req.auth.user.id : undefined; + const userString = req.auth && req.auth.user ? req.auth.user.id : undefined; const { success, error } = FunctionsRouter.createResponseObject( result => { try { if (req.config.logLevels.cloudFunctionSuccess !== 'silent') { - const cleanInput = logger.truncateLogMessage( - JSON.stringify(params) - ); - const cleanResult = logger.truncateLogMessage( - JSON.stringify(result.response.result) - ); + const cleanInput = logger.truncateLogMessage(JSON.stringify(params)); + const cleanResult = logger.truncateLogMessage(JSON.stringify(result.response.result)); logger[req.config.logLevels.cloudFunctionSuccess]( `Ran cloud function ${functionName} for user ${userString} with:\n Input: ${cleanInput}\n Result: ${cleanResult}`, { @@ -179,9 +164,7 @@ export class FunctionsRouter extends PromiseRouter { error => { try { if (req.config.logLevels.cloudFunctionError !== 'silent') { - const cleanInput = logger.truncateLogMessage( - JSON.stringify(params) - ); + const cleanInput = logger.truncateLogMessage(JSON.stringify(params)); logger[req.config.logLevels.cloudFunctionError]( `Failed running cloud function ${functionName} for user ${userString} with:\n Input: ${cleanInput}\n Error: ` + JSON.stringify(error), diff --git a/src/Routers/GlobalConfigRouter.js b/src/Routers/GlobalConfigRouter.js index 04e4634cce..31d38c8569 100644 --- a/src/Routers/GlobalConfigRouter.js +++ b/src/Routers/GlobalConfigRouter.js @@ -129,14 +129,9 @@ export class GlobalConfigRouter extends PromiseRouter { this.route('GET', '/config', req => { return this.getGlobalConfig(req); }); - this.route( - 'PUT', - '/config', - middleware.promiseEnforceMasterKeyAccess, - req => { - return this.updateGlobalConfig(req); - } - ); + this.route('PUT', '/config', middleware.promiseEnforceMasterKeyAccess, req => { + return this.updateGlobalConfig(req); + }); } } diff --git a/src/Routers/GraphQLRouter.js b/src/Routers/GraphQLRouter.js index 5a328d47f3..71ca8b3ce8 100644 --- a/src/Routers/GraphQLRouter.js +++ b/src/Routers/GraphQLRouter.js @@ -28,22 +28,12 @@ export class GraphQLRouter extends PromiseRouter { } mountRoutes() { - this.route( - 'GET', - GraphQLConfigPath, - middleware.promiseEnforceMasterKeyAccess, - req => { - return this.getGraphQLConfig(req); - } - ); - this.route( - 'PUT', - GraphQLConfigPath, - middleware.promiseEnforceMasterKeyAccess, - req => { - return this.updateGraphQLConfig(req); - } - ); + this.route('GET', GraphQLConfigPath, middleware.promiseEnforceMasterKeyAccess, req => { + return this.getGraphQLConfig(req); + }); + this.route('PUT', GraphQLConfigPath, middleware.promiseEnforceMasterKeyAccess, req => { + return this.updateGraphQLConfig(req); + }); } } diff --git a/src/Routers/HooksRouter.js b/src/Routers/HooksRouter.js index 1e1f35c6ab..104ef799c2 100644 --- a/src/Routers/HooksRouter.js +++ b/src/Routers/HooksRouter.js @@ -4,15 +4,11 @@ import * as middleware from '../middlewares'; export class HooksRouter extends PromiseRouter { createHook(aHook, config) { - return config.hooksController - .createHook(aHook) - .then(hook => ({ response: hook })); + return config.hooksController.createHook(aHook).then(hook => ({ response: hook })); } updateHook(aHook, config) { - return config.hooksController - .updateHook(aHook) - .then(hook => ({ response: hook })); + return config.hooksController.updateHook(aHook).then(hook => ({ response: hook })); } handlePost(req) { @@ -22,17 +18,12 @@ export class HooksRouter extends PromiseRouter { handleGetFunctions(req) { var hooksController = req.config.hooksController; if (req.params.functionName) { - return hooksController - .getFunction(req.params.functionName) - .then(foundFunction => { - if (!foundFunction) { - throw new Parse.Error( - 143, - `no function named: ${req.params.functionName} is defined` - ); - } - return Promise.resolve({ response: foundFunction }); - }); + return hooksController.getFunction(req.params.functionName).then(foundFunction => { + if (!foundFunction) { + throw new Parse.Error(143, `no function named: ${req.params.functionName} is defined`); + } + return Promise.resolve({ response: foundFunction }); + }); } return hooksController.getFunctions().then( @@ -52,26 +43,19 @@ export class HooksRouter extends PromiseRouter { .getTrigger(req.params.className, req.params.triggerName) .then(foundTrigger => { if (!foundTrigger) { - throw new Parse.Error( - 143, - `class ${req.params.className} does not exist` - ); + throw new Parse.Error(143, `class ${req.params.className} does not exist`); } return Promise.resolve({ response: foundTrigger }); }); } - return hooksController - .getTriggers() - .then(triggers => ({ response: triggers || [] })); + return hooksController.getTriggers().then(triggers => ({ response: triggers || [] })); } handleDelete(req) { var hooksController = req.config.hooksController; if (req.params.functionName) { - return hooksController - .deleteFunction(req.params.functionName) - .then(() => ({ response: {} })); + return hooksController.deleteFunction(req.params.functionName).then(() => ({ response: {} })); } else if (req.params.className && req.params.triggerName) { return hooksController .deleteTrigger(req.params.className, req.params.triggerName) @@ -86,11 +70,7 @@ export class HooksRouter extends PromiseRouter { hook = {}; hook.functionName = req.params.functionName; hook.url = req.body.url; - } else if ( - req.params.className && - req.params.triggerName && - req.body?.url - ) { + } else if (req.params.className && req.params.triggerName && req.body?.url) { hook = {}; hook.className = req.params.className; hook.triggerName = req.params.triggerName; diff --git a/src/Routers/IAPValidationRouter.js b/src/Routers/IAPValidationRouter.js index d08cb3db9d..17eae685d7 100644 --- a/src/Routers/IAPValidationRouter.js +++ b/src/Routers/IAPValidationRouter.js @@ -61,10 +61,7 @@ function getFileForProductIdentifier(productIdentifier, req) { const products = result.results; if (!products || products.length != 1) { // Error not found or too many - throw new Parse.Error( - Parse.Error.OBJECT_NOT_FOUND, - 'Object not found.' - ); + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.'); } var download = products[0].download; @@ -79,10 +76,7 @@ export class IAPValidationRouter extends PromiseRouter { if (!receipt || !productIdentifier) { // TODO: Error, malformed request - throw new Parse.Error( - Parse.Error.INVALID_JSON, - 'missing receipt or productIdentifier' - ); + throw new Parse.Error(Parse.Error.INVALID_JSON, 'missing receipt or productIdentifier'); } // Transform the object if there diff --git a/src/Routers/InstallationsRouter.js b/src/Routers/InstallationsRouter.js index 0bb2a12e3f..7142d0fe5c 100644 --- a/src/Routers/InstallationsRouter.js +++ b/src/Routers/InstallationsRouter.js @@ -10,14 +10,8 @@ export class InstallationsRouter extends ClassesRouter { } handleFind(req) { - const body = Object.assign( - req.body || {}, - ClassesRouter.JSONFromQuery(req.query) - ); - const options = ClassesRouter.optionsFromBody( - body, - req.config.defaultLimit - ); + const body = Object.assign(req.body || {}, ClassesRouter.JSONFromQuery(req.query)); + const options = ClassesRouter.optionsFromBody(body, req.config.defaultLimit); return rest .find( req.config, @@ -43,14 +37,9 @@ export class InstallationsRouter extends ClassesRouter { this.route('POST', '/installations', promiseEnsureIdempotency, req => { return this.handleCreate(req); }); - this.route( - 'PUT', - '/installations/:objectId', - promiseEnsureIdempotency, - req => { - return this.handleUpdate(req); - } - ); + this.route('PUT', '/installations/:objectId', promiseEnsureIdempotency, req => { + return this.handleUpdate(req); + }); this.route('DELETE', '/installations/:objectId', req => { return this.handleDelete(req); }); diff --git a/src/Routers/LogsRouter.js b/src/Routers/LogsRouter.js index 0bc2e23455..182a4f1669 100644 --- a/src/Routers/LogsRouter.js +++ b/src/Routers/LogsRouter.js @@ -17,10 +17,7 @@ export class LogsRouter extends PromiseRouter { validateRequest(req) { if (!req.config || !req.config.loggerController) { - throw new Parse.Error( - Parse.Error.PUSH_MISCONFIGURED, - 'Logger adapter is not available' - ); + throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED, 'Logger adapter is not available'); } } diff --git a/src/Routers/PagesRouter.js b/src/Routers/PagesRouter.js index a8c37a4531..c172bb8970 100644 --- a/src/Routers/PagesRouter.js +++ b/src/Routers/PagesRouter.js @@ -61,8 +61,7 @@ const pageParamHeaderPrefix = 'x-parse-page-param-'; // The errors being thrown const errors = Object.freeze({ jsonFailedFileLoading: 'failed to load JSON file', - fileOutsideAllowedScope: - 'not allowed to read file outside of pages directory', + fileOutsideAllowedScope: 'not allowed to read file outside of pages directory', }); export class PagesRouter extends PromiseRouter { @@ -88,8 +87,7 @@ export class PagesRouter extends PromiseRouter { verifyEmail(req) { const config = req.config; const { token: rawToken } = req.query; - const token = - rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken; + const token = rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken; if (!config) { this.invalidRequest(); @@ -155,8 +153,7 @@ export class PagesRouter extends PromiseRouter { } const { token: rawToken } = req.query; - const token = - rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken; + const token = rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken; if (!token) { return this.goToPage(req, pages.passwordResetLinkInvalid); @@ -185,8 +182,7 @@ export class PagesRouter extends PromiseRouter { } const { new_password, token: rawToken } = req.body || {}; - const token = - rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken; + const token = rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken; if ((!token || !new_password) && req.xhr === false) { return this.goToPage(req, pages.passwordResetLinkInvalid); @@ -241,9 +237,7 @@ export class PagesRouter extends PromiseRouter { delete query[pageParams.token]; query[pageParams.token] = token; } - const page = result.success - ? pages.passwordResetSuccess - : pages.passwordReset; + const page = result.success ? pages.passwordResetSuccess : pages.passwordReset; return this.goToPage(req, page, query, false); }); @@ -306,18 +300,13 @@ export class PagesRouter extends PromiseRouter { // Send response if (config.pages.enableLocalization && locale) { - return Utils.getLocalizedPath(defaultPath, locale).then( - ({ path, subdir }) => - redirect - ? this.redirectResponse( - this.composePageUrl( - defaultFile, - config.publicServerURL, - subdir - ), - params - ) - : this.pageResponse(path, params, placeholders) + return Utils.getLocalizedPath(defaultPath, locale).then(({ path, subdir }) => + redirect + ? this.redirectResponse( + this.composePageUrl(defaultFile, config.publicServerURL, subdir), + params + ) + : this.pageResponse(path, params, placeholders) ); } else { return redirect @@ -416,10 +405,7 @@ export class PagesRouter extends PromiseRouter { */ getJsonPlaceholders(locale, params = {}) { // If localization is disabled or there is no JSON resource - if ( - !this.pagesConfig.enableLocalization || - !this.pagesConfig.localizationJsonPath - ) { + if (!this.pagesConfig.enableLocalization || !this.pagesConfig.localizationJsonPath) { return {}; } @@ -457,8 +443,7 @@ export class PagesRouter extends PromiseRouter { let configPlaceholders = typeof this.pagesConfig.placeholders === 'function' ? this.pagesConfig.placeholders(params) - : Object.prototype.toString.call(this.pagesConfig.placeholders) === - '[object Object]' + : Object.prototype.toString.call(this.pagesConfig.placeholders) === '[object Object]' ? this.pagesConfig.placeholders : {}; if (configPlaceholders instanceof Promise) { @@ -533,9 +518,7 @@ export class PagesRouter extends PromiseRouter { return; } try { - const json = require( - path.resolve('./', this.pagesConfig.localizationJsonPath) - ); + const json = require(path.resolve('./', this.pagesConfig.localizationJsonPath)); this.jsonParameters = json; } catch (e) { throw errors.jsonFailedFileLoading; diff --git a/src/Routers/PublicAPIRouter.js b/src/Routers/PublicAPIRouter.js index 1f693fe8af..98da1c4542 100644 --- a/src/Routers/PublicAPIRouter.js +++ b/src/Routers/PublicAPIRouter.js @@ -20,8 +20,7 @@ export class PublicAPIRouter extends PromiseRouter { } verifyEmail(req) { const { token: rawToken } = req.query; - const token = - rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken; + const token = rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken; const appId = req.params.appId; const config = Config.get(appId); @@ -104,22 +103,15 @@ export class PublicAPIRouter extends PromiseRouter { }); } // Should we keep the file in memory or leave like that? - fs.readFile( - path.resolve(views, 'choose_password'), - 'utf-8', - (err, data) => { - if (err) { - return reject(err); - } - data = data.replace( - 'PARSE_SERVER_URL', - `'${config.publicServerURL}'` - ); - resolve({ - text: data, - }); + fs.readFile(path.resolve(views, 'choose_password'), 'utf-8', (err, data) => { + if (err) { + return reject(err); } - ); + data = data.replace('PARSE_SERVER_URL', `'${config.publicServerURL}'`); + resolve({ + text: data, + }); + }); }); } @@ -135,8 +127,7 @@ export class PublicAPIRouter extends PromiseRouter { } const { token: rawToken } = req.query; - const token = - rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken; + const token = rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken; if (!token) { return this.invalidLink(req); @@ -172,8 +163,7 @@ export class PublicAPIRouter extends PromiseRouter { } const { new_password, token: rawToken } = req.body || {}; - const token = - rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken; + const token = rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken; if ((!token || !new_password) && req.xhr === false) { return this.invalidLink(req); diff --git a/src/Routers/PurgeRouter.js b/src/Routers/PurgeRouter.js index 6d0aca0c3e..3195d134af 100644 --- a/src/Routers/PurgeRouter.js +++ b/src/Routers/PurgeRouter.js @@ -30,14 +30,9 @@ export class PurgeRouter extends PromiseRouter { } mountRoutes() { - this.route( - 'DELETE', - '/purge/:className', - middleware.promiseEnforceMasterKeyAccess, - req => { - return this.handlePurge(req); - } - ); + this.route('DELETE', '/purge/:className', middleware.promiseEnforceMasterKeyAccess, req => { + return this.handlePurge(req); + }); } } diff --git a/src/Routers/PushRouter.js b/src/Routers/PushRouter.js index dee9968d6b..1c1c8f3b5f 100644 --- a/src/Routers/PushRouter.js +++ b/src/Routers/PushRouter.js @@ -4,12 +4,7 @@ import { Parse } from 'parse/node'; export class PushRouter extends PromiseRouter { mountRoutes() { - this.route( - 'POST', - '/push', - middleware.promiseEnforceMasterKeyAccess, - PushRouter.handlePOST - ); + this.route('POST', '/push', middleware.promiseEnforceMasterKeyAccess, PushRouter.handlePOST); } static handlePOST(req) { @@ -21,10 +16,7 @@ export class PushRouter extends PromiseRouter { } const pushController = req.config.pushController; if (!pushController) { - throw new Parse.Error( - Parse.Error.PUSH_MISCONFIGURED, - 'Push controller is not set' - ); + throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED, 'Push controller is not set'); } const where = PushRouter.getQueryCondition(req); diff --git a/src/Routers/SchemasRouter.js b/src/Routers/SchemasRouter.js index e1e77906dc..0a42123af7 100644 --- a/src/Routers/SchemasRouter.js +++ b/src/Routers/SchemasRouter.js @@ -16,9 +16,7 @@ function classNameMismatchResponse(bodyClass, pathClass) { function getAllSchemas(req) { return req.config.database .loadSchema({ clearCache: true }) - .then(schemaController => - schemaController.getAllClasses({ clearCache: true }) - ) + .then(schemaController => schemaController.getAllClasses({ clearCache: true })) .then(schemas => ({ response: { results: schemas } })); } @@ -30,15 +28,9 @@ function getOneSchema(req) { .then(schema => ({ response: schema })) .catch(error => { if (error === undefined) { - throw new Parse.Error( - Parse.Error.INVALID_CLASS_NAME, - `Class ${className} does not exist.` - ); + throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, `Class ${className} does not exist.`); } else { - throw new Parse.Error( - Parse.Error.INTERNAL_SERVER_ERROR, - 'Database adapter error.' - ); + throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Database adapter error.'); } }); } @@ -87,10 +79,7 @@ async function createSchema(req) { } if (req.params.className && req.body?.className) { if (req.params.className != req.body.className) { - return classNameMismatchResponse( - req.body.className, - req.params.className - ); + return classNameMismatchResponse(req.body.className, req.params.className); } } @@ -131,31 +120,19 @@ const deleteSchema = req => { SchemaController.invalidClassNameMessage(req.params.className) ); } - return req.config.database - .deleteSchema(req.params.className) - .then(() => ({ response: {} })); + return req.config.database.deleteSchema(req.params.className).then(() => ({ response: {} })); }; export class SchemasRouter extends PromiseRouter { mountRoutes() { - this.route( - 'GET', - '/schemas', - middleware.promiseEnforceMasterKeyAccess, - getAllSchemas - ); + this.route('GET', '/schemas', middleware.promiseEnforceMasterKeyAccess, getAllSchemas); this.route( 'GET', '/schemas/:className', middleware.promiseEnforceMasterKeyAccess, getOneSchema ); - this.route( - 'POST', - '/schemas', - middleware.promiseEnforceMasterKeyAccess, - createSchema - ); + this.route('POST', '/schemas', middleware.promiseEnforceMasterKeyAccess, createSchema); this.route( 'POST', '/schemas/:className', diff --git a/src/Routers/SecurityRouter.js b/src/Routers/SecurityRouter.js index 7eaa2216e6..c7c217a048 100644 --- a/src/Routers/SecurityRouter.js +++ b/src/Routers/SecurityRouter.js @@ -24,8 +24,7 @@ export class SecurityRouter extends PromiseRouter { if (!config.security || !config.security.enableCheck) { const error = new Error(); error.status = 409; - error.message = - 'Enable Parse Server option `security.enableCheck` to run security check.'; + error.message = 'Enable Parse Server option `security.enableCheck` to run security check.'; throw error; } } diff --git a/src/Routers/SessionsRouter.js b/src/Routers/SessionsRouter.js index 026dd9dc7f..e5275fae06 100644 --- a/src/Routers/SessionsRouter.js +++ b/src/Routers/SessionsRouter.js @@ -12,10 +12,7 @@ export class SessionsRouter extends ClassesRouter { handleMe(req) { // TODO: Verify correct behavior if (!req.info || !req.info.sessionToken) { - throw new Parse.Error( - Parse.Error.INVALID_SESSION_TOKEN, - 'Session token required.' - ); + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Session token required.'); } return rest .find( @@ -29,10 +26,7 @@ export class SessionsRouter extends ClassesRouter { ) .then(response => { if (!response.results || response.results.length == 0) { - throw new Parse.Error( - Parse.Error.INVALID_SESSION_TOKEN, - 'Session token not found.' - ); + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Session token not found.'); } return { response: response.results[0], diff --git a/src/Routers/UsersRouter.js b/src/Routers/UsersRouter.js index 16fcf31688..9724c8e4f0 100644 --- a/src/Routers/UsersRouter.js +++ b/src/Routers/UsersRouter.js @@ -79,26 +79,17 @@ export class UsersRouter extends ClassesRouter { // TODO: use the right error codes / descriptions. if (!username && !email) { - throw new Parse.Error( - Parse.Error.USERNAME_MISSING, - 'username/email is required.' - ); + throw new Parse.Error(Parse.Error.USERNAME_MISSING, 'username/email is required.'); } if (!password) { - throw new Parse.Error( - Parse.Error.PASSWORD_MISSING, - 'password is required.' - ); + throw new Parse.Error(Parse.Error.PASSWORD_MISSING, 'password is required.'); } if ( typeof password !== 'string' || (email && typeof email !== 'string') || (username && typeof username !== 'string') ) { - throw new Parse.Error( - Parse.Error.OBJECT_NOT_FOUND, - 'Invalid username/password.' - ); + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid username/password.'); } let user; @@ -115,10 +106,7 @@ export class UsersRouter extends ClassesRouter { .find('_User', query, {}, Auth.maintenance(req.config)) .then(results => { if (!results.length) { - throw new Parse.Error( - Parse.Error.OBJECT_NOT_FOUND, - 'Invalid username/password.' - ); + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid username/password.'); } if (results.length > 1) { @@ -140,67 +128,43 @@ export class UsersRouter extends ClassesRouter { }) .then(async () => { if (!isValidPassword) { - throw new Parse.Error( - Parse.Error.OBJECT_NOT_FOUND, - 'Invalid username/password.' - ); + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid username/password.'); } // Ensure the user isn't locked out // A locked out user won't be able to login // To lock a user out, just set the ACL to `masterKey` only ({}). // Empty ACL is OK - if ( - !req.auth.isMaster && - user.ACL && - Object.keys(user.ACL).length == 0 - ) { - throw new Parse.Error( - Parse.Error.OBJECT_NOT_FOUND, - 'Invalid username/password.' - ); + if (!req.auth.isMaster && user.ACL && Object.keys(user.ACL).length == 0) { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid username/password.'); } // Create request object for verification functions const request = { master: req.auth.isMaster, ip: req.config.ip, installationId: req.auth.installationId, - object: Parse.User.fromJSON( - Object.assign({ className: '_User' }, user) - ), + object: Parse.User.fromJSON(Object.assign({ className: '_User' }, user)), }; // If request doesn't use master or maintenance key with ignoring email verification - if ( - !( - (req.auth.isMaster || req.auth.isMaintenance) && - ignoreEmailVerification - ) - ) { + if (!((req.auth.isMaster || req.auth.isMaintenance) && ignoreEmailVerification)) { // Get verification conditions which can be booleans or functions; the purpose of this async/await // structure is to avoid unnecessarily executing subsequent functions if previous ones fail in the // conditional statement below, as a developer may decide to execute expensive operations in them const verifyUserEmails = async () => req.config.verifyUserEmails === true || (typeof req.config.verifyUserEmails === 'function' && - (await Promise.resolve( - req.config.verifyUserEmails(request) - )) === true); + (await Promise.resolve(req.config.verifyUserEmails(request))) === true); const preventLoginWithUnverifiedEmail = async () => req.config.preventLoginWithUnverifiedEmail === true || - (typeof req.config.preventLoginWithUnverifiedEmail === - 'function' && - (await Promise.resolve( - req.config.preventLoginWithUnverifiedEmail(request) - )) === true); + (typeof req.config.preventLoginWithUnverifiedEmail === 'function' && + (await Promise.resolve(req.config.preventLoginWithUnverifiedEmail(request))) === + true); if ( (await verifyUserEmails()) && (await preventLoginWithUnverifiedEmail()) && !user.emailVerified ) { - throw new Parse.Error( - Parse.Error.EMAIL_NOT_FOUND, - 'User email is not verified.' - ); + throw new Parse.Error(Parse.Error.EMAIL_NOT_FOUND, 'User email is not verified.'); } } @@ -216,10 +180,7 @@ export class UsersRouter extends ClassesRouter { handleMe(req) { if (!req.info || !req.info.sessionToken) { - throw new Parse.Error( - Parse.Error.INVALID_SESSION_TOKEN, - 'Invalid session token' - ); + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid session token'); } const sessionToken = req.info.sessionToken; return rest @@ -233,15 +194,8 @@ export class UsersRouter extends ClassesRouter { req.info.context ) .then(response => { - if ( - !response.results || - response.results.length == 0 || - !response.results[0].user - ) { - throw new Parse.Error( - Parse.Error.INVALID_SESSION_TOKEN, - 'Invalid session token' - ); + if (!response.results || response.results.length == 0 || !response.results[0].user) { + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid session token'); } else { const user = response.results[0].user; // Send token back on the login, because SDKs expect that. @@ -306,8 +260,7 @@ export class UsersRouter extends ClassesRouter { } // Calculate the expiry time. const expiresAt = new Date( - changedAt.getTime() + - 86400000 * req.config.passwordPolicy.maxPasswordAge + changedAt.getTime() + 86400000 * req.config.passwordPolicy.maxPasswordAge ); if (expiresAt < new Date()) { // fail of current time is past password expiry time @@ -357,9 +310,7 @@ export class UsersRouter extends ClassesRouter { await createSession(); - const afterLoginUser = Parse.User.fromJSON( - Object.assign({ className: '_User' }, user) - ); + const afterLoginUser = Parse.User.fromJSON(Object.assign({ className: '_User' }, user)); await maybeRunTrigger( TriggerTypes.afterLogin, { ...req.auth, user: afterLoginUser }, @@ -393,10 +344,7 @@ export class UsersRouter extends ClassesRouter { */ async handleLogInAs(req) { if (!req.auth.isMaster) { - throw new Parse.Error( - Parse.Error.OPERATION_FORBIDDEN, - 'master key is required' - ); + throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'master key is required'); } const userId = req.body?.userId || req.query.userId; @@ -469,9 +417,7 @@ export class UsersRouter extends ClassesRouter { await maybeRunTrigger( TriggerTypes.afterLogout, req.auth, - Parse.Session.fromJSON( - Object.assign({ className: '_Session' }, records.results[0]) - ), + Parse.Session.fromJSON(Object.assign({ className: '_Session' }, records.results[0])), null, req.config ); @@ -486,8 +432,7 @@ export class UsersRouter extends ClassesRouter { emailAdapter: req.config.userController.adapter, appName: req.config.appName, publicServerURL: req.config.publicServerURL, - emailVerifyTokenValidityDuration: - req.config.emailVerifyTokenValidityDuration, + emailVerifyTokenValidityDuration: req.config.emailVerifyTokenValidityDuration, emailVerifyTokenReuseIfValid: req.config.emailVerifyTokenReuseIfValid, }); } catch (e) { @@ -510,10 +455,7 @@ export class UsersRouter extends ClassesRouter { const token = req.body?.token; if (!email && !token) { - throw new Parse.Error( - Parse.Error.EMAIL_MISSING, - 'you must provide an email' - ); + throw new Parse.Error(Parse.Error.EMAIL_MISSING, 'you must provide an email'); } if (token) { const results = await req.config.database.find('_User', { @@ -538,10 +480,7 @@ export class UsersRouter extends ClassesRouter { }; } catch (err) { if (err.code === Parse.Error.OBJECT_NOT_FOUND) { - if ( - req.config.passwordPolicy?.resetPasswordSuccessOnInvalidEmail ?? - true - ) { + if (req.config.passwordPolicy?.resetPasswordSuccessOnInvalidEmail ?? true) { return { response: {}, }; @@ -557,10 +496,7 @@ export class UsersRouter extends ClassesRouter { const { email } = req.body || {}; if (!email) { - throw new Parse.Error( - Parse.Error.EMAIL_MISSING, - 'you must provide an email' - ); + throw new Parse.Error(Parse.Error.EMAIL_MISSING, 'you must provide an email'); } if (typeof email !== 'string') { throw new Parse.Error( @@ -576,10 +512,7 @@ export class UsersRouter extends ClassesRouter { Auth.maintenance(req.config) ); if (!results.length || results.length < 1) { - throw new Parse.Error( - Parse.Error.EMAIL_NOT_FOUND, - `No user found with email ${email}` - ); + throw new Parse.Error(Parse.Error.EMAIL_NOT_FOUND, `No user found with email ${email}`); } const user = results[0]; @@ -587,10 +520,7 @@ export class UsersRouter extends ClassesRouter { delete user.password; if (user.emailVerified) { - throw new Parse.Error( - Parse.Error.OTHER_CAUSE, - `Email ${email} is already verified.` - ); + throw new Parse.Error(Parse.Error.OTHER_CAUSE, `Email ${email} is already verified.`); } const userController = req.config.userController; @@ -607,8 +537,7 @@ export class UsersRouter extends ClassesRouter { } async handleChallenge(req) { - const { username, email, password, authData, challengeData } = - req.body || {}; + const { username, email, password, authData, challengeData } = req.body || {}; // if username or email provided with password try to authenticate the user by username let user; @@ -627,10 +556,7 @@ export class UsersRouter extends ClassesRouter { } if (typeof challengeData !== 'object') { - throw new Parse.Error( - Parse.Error.OTHER_CAUSE, - 'challengeData should be an object.' - ); + throw new Parse.Error(Parse.Error.OTHER_CAUSE, 'challengeData should be an object.'); } let request; @@ -639,10 +565,7 @@ export class UsersRouter extends ClassesRouter { // Try to find user by authData if (authData) { if (typeof authData !== 'object') { - throw new Parse.Error( - Parse.Error.OTHER_CAUSE, - 'authData should be an object.' - ); + throw new Parse.Error(Parse.Error.OTHER_CAUSE, 'authData should be an object.'); } if (user) { throw new Parse.Error( @@ -662,32 +585,17 @@ export class UsersRouter extends ClassesRouter { try { if (!results[0] || results.length > 1) { - throw new Parse.Error( - Parse.Error.OBJECT_NOT_FOUND, - 'User not found.' - ); + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.'); } // Find the provider used to find the user const provider = Object.keys(authData).find(key => authData[key].id); parseUser = Parse.User.fromJSON({ className: '_User', ...results[0] }); - request = getRequestObject( - undefined, - req.auth, - parseUser, - parseUser, - req.config - ); + request = getRequestObject(undefined, req.auth, parseUser, parseUser, req.config); request.isChallenge = true; // Validate authData used to identify the user to avoid brute-force attack on `id` - const { validator } = - req.config.authDataManager.getValidatorForProvider(provider); - const validatorResponse = await validator( - authData[provider], - req, - parseUser, - request - ); + const { validator } = req.config.authDataManager.getValidatorForProvider(provider); + const validatorResponse = await validator(authData[provider], req, parseUser, request); if (validatorResponse && validatorResponse.validator) { await validatorResponse.validator(); } @@ -699,19 +607,11 @@ export class UsersRouter extends ClassesRouter { } if (!parseUser) { - parseUser = user - ? Parse.User.fromJSON({ className: '_User', ...user }) - : undefined; + parseUser = user ? Parse.User.fromJSON({ className: '_User', ...user }) : undefined; } if (!request) { - request = getRequestObject( - undefined, - req.auth, - parseUser, - parseUser, - req.config - ); + request = getRequestObject(undefined, req.auth, parseUser, parseUser, req.config); request.isChallenge = true; } const acc = {}; @@ -719,8 +619,7 @@ export class UsersRouter extends ClassesRouter { // and to avoid to trigger others challenges if one of them fails for (const provider of Object.keys(challengeData).sort()) { try { - const authAdapter = - req.config.authDataManager.getValidatorForProvider(provider); + const authAdapter = req.config.authDataManager.getValidatorForProvider(provider); if (!authAdapter) { continue; } @@ -741,8 +640,7 @@ export class UsersRouter extends ClassesRouter { code: Parse.Error.SCRIPT_FAILED, message: 'Challenge failed. Unknown error.', }); - const userString = - req.auth && req.auth.user ? req.auth.user.id : undefined; + const userString = req.auth && req.auth.user ? req.auth.user.id : undefined; logger.error( `Failed running auth step challenge for ${provider} for user ${userString} with Error: ` + JSON.stringify(e), diff --git a/src/SchemaMigrations/DefinedSchemas.js b/src/SchemaMigrations/DefinedSchemas.js index 4c5fd94211..af5090bbbd 100644 --- a/src/SchemaMigrations/DefinedSchemas.js +++ b/src/SchemaMigrations/DefinedSchemas.js @@ -3,10 +3,7 @@ const Parse = require('parse/node'); import { logger } from '../logger'; import Config from '../Config'; -import { - internalCreateSchema, - internalUpdateSchema, -} from '../Routers/SchemasRouter'; +import { internalCreateSchema, internalUpdateSchema } from '../Routers/SchemasRouter'; import { defaultColumns, systemClasses } from '../Controllers/SchemaController'; import { ParseServerOptions } from '../Options'; import * as Migrations from './Migrations'; @@ -21,10 +18,7 @@ export class DefinedSchemas { maxRetries: number; allCloudSchemas: Parse.Schema[]; - constructor( - schemaOptions: Migrations.SchemaOptions, - config: ParseServerOptions - ) { + constructor(schemaOptions: Migrations.SchemaOptions, config: ParseServerOptions) { this.localSchemas = []; this.config = Config.get(config.appId); this.schemaOptions = schemaOptions; @@ -101,9 +95,7 @@ export class DefinedSchemas { // after the exit if (process.env.NODE_ENV === 'production') { timeout = setTimeout(() => { - logger.error( - 'Timeout occurred during execution of migrations. Exiting...' - ); + logger.error('Timeout occurred during execution of migrations. Exiting...'); process.exit(1); }, 20000); } @@ -113,11 +105,7 @@ export class DefinedSchemas { const schemaController = await this.config.database.loadSchema(); this.allCloudSchemas = await schemaController.getAllClasses(); clearTimeout(timeout); - await Promise.all( - this.localSchemas.map(async localSchema => - this.saveOrUpdate(localSchema) - ) - ); + await Promise.all(this.localSchemas.map(async localSchema => this.saveOrUpdate(localSchema))); this.checkForMissingSchemas(); await this.enforceCLPForNonProvidedClass(); @@ -154,9 +142,7 @@ export class DefinedSchemas { if (new Set(localSchemas).size !== localSchemas.length) { logger.error( - `The list of schemas provided contains duplicated "className" "${localSchemas.join( - '","' - )}"` + `The list of schemas provided contains duplicated "className" "${localSchemas.join('","')}"` ); process.exit(1); } @@ -178,9 +164,7 @@ export class DefinedSchemas { async enforceCLPForNonProvidedClass(): Promise { const nonProvidedClasses = this.allCloudSchemas.filter( cloudSchema => - !this.localSchemas.some( - localSchema => localSchema.className === cloudSchema.className - ) + !this.localSchemas.some(localSchema => localSchema.className === cloudSchema.className) ); await Promise.all( nonProvidedClasses.map(async schema => { @@ -194,24 +178,12 @@ export class DefinedSchemas { // Create a fake session since Parse do not create the _Session until // a session is created async createDeleteSession() { - const { response } = await rest.create( - this.config, - Auth.master(this.config), - '_Session', - {} - ); - await rest.del( - this.config, - Auth.master(this.config), - '_Session', - response.objectId - ); + const { response } = await rest.create(this.config, Auth.master(this.config), '_Session', {}); + await rest.del(this.config, Auth.master(this.config), '_Session', response.objectId); } async saveOrUpdate(localSchema: Migrations.JSONSchema) { - const cloudSchema = this.allCloudSchemas.find( - sc => sc.className === localSchema.className - ); + const cloudSchema = this.allCloudSchemas.find(sc => sc.className === localSchema.className); if (cloudSchema) { try { await this.updateSchema(localSchema, cloudSchema); @@ -232,9 +204,7 @@ export class DefinedSchemas { if (localSchema.fields) { // Handle fields Object.keys(localSchema.fields) - .filter( - fieldName => !this.isProtectedFields(localSchema.className, fieldName) - ) + .filter(fieldName => !this.isProtectedFields(localSchema.className, fieldName)) .forEach(fieldName => { if (localSchema.fields) { const field = localSchema.fields[fieldName]; @@ -245,10 +215,7 @@ export class DefinedSchemas { // Handle indexes if (localSchema.indexes) { Object.keys(localSchema.indexes).forEach(indexName => { - if ( - localSchema.indexes && - !this.isProtectedIndex(localSchema.className, indexName) - ) { + if (localSchema.indexes && !this.isProtectedIndex(localSchema.className, indexName)) { newLocalSchema.addIndex(indexName, localSchema.indexes[indexName]); } }); @@ -259,19 +226,14 @@ export class DefinedSchemas { return await this.saveSchemaToDB(newLocalSchema); } - async updateSchema( - localSchema: Migrations.JSONSchema, - cloudSchema: Parse.Schema - ) { + async updateSchema(localSchema: Migrations.JSONSchema, cloudSchema: Parse.Schema) { const newLocalSchema = new Parse.Schema(localSchema.className); // Handle fields // Check addition if (localSchema.fields) { Object.keys(localSchema.fields) - .filter( - fieldName => !this.isProtectedFields(localSchema.className, fieldName) - ) + .filter(fieldName => !this.isProtectedFields(localSchema.className, fieldName)) .forEach(fieldName => { // @flow-disable-next const field = localSchema.fields[fieldName]; @@ -291,9 +253,7 @@ export class DefinedSchemas { // Check deletion Object.keys(cloudSchema.fields) - .filter( - fieldName => !this.isProtectedFields(localSchema.className, fieldName) - ) + .filter(fieldName => !this.isProtectedFields(localSchema.className, fieldName)) .forEach(fieldName => { const field = cloudSchema.fields[fieldName]; if (!localSchema.fields || !localSchema.fields[fieldName]) { @@ -355,11 +315,8 @@ export class DefinedSchemas { } else if (this.schemaOptions.strict === true && fieldsToRecreate.length) { fieldsToRecreate.forEach(field => { const from = - field.from.type + - (field.from.targetClass ? ` (${field.from.targetClass})` : ''); - const to = - field.to.type + - (field.to.targetClass ? ` (${field.to.targetClass})` : ''); + field.from.type + (field.from.targetClass ? ` (${field.from.targetClass})` : ''); + const to = field.to.type + (field.to.targetClass ? ` (${field.to.targetClass})` : ''); logger.warn( `The field "${field.fieldName}" type differ between the schema and the database for "${localSchema.className}"; Schema is defined as "${to}" and current database type is "${from}"` @@ -398,10 +355,7 @@ export class DefinedSchemas { if (!localSchema.indexes || !localSchema.indexes[indexName]) { newLocalSchema.deleteIndex(indexName); } else if ( - !this.paramsAreEquals( - localSchema.indexes[indexName], - cloudSchema.indexes[indexName] - ) + !this.paramsAreEquals(localSchema.indexes[indexName], cloudSchema.indexes[indexName]) ) { newLocalSchema.deleteIndex(indexName); if (localSchema.indexes) { @@ -434,9 +388,7 @@ export class DefinedSchemas { cloudSchema: Parse.Schema ) { if (!localSchema.classLevelPermissions && !cloudSchema) { - logger.warn( - `classLevelPermissions not provided for ${localSchema.className}.` - ); + logger.warn(`classLevelPermissions not provided for ${localSchema.className}.`); } // Use spread to avoid read only issue (encountered by Moumouls using directAccess) const clp = ({ @@ -488,11 +440,7 @@ export class DefinedSchemas { return keysA.every(k => objA[k] === objB[k]); } - handleFields( - newLocalSchema: Parse.Schema, - fieldName: string, - field: Migrations.FieldType - ) { + handleFields(newLocalSchema: Parse.Schema, fieldName: string, field: Migrations.FieldType) { if (field.type === 'Relation') { newLocalSchema.addRelation(fieldName, field.targetClass); } else if (field.type === 'Pointer') { diff --git a/src/SchemaMigrations/Migrations.js b/src/SchemaMigrations/Migrations.js index 114dc27338..8768911189 100644 --- a/src/SchemaMigrations/Migrations.js +++ b/src/SchemaMigrations/Migrations.js @@ -45,13 +45,7 @@ export interface IndexesInterface { [key: string]: IndexInterface; } -export type CLPOperation = - | 'find' - | 'count' - | 'get' - | 'update' - | 'create' - | 'delete'; +export type CLPOperation = 'find' | 'count' | 'get' | 'update' | 'create' | 'delete'; // @Typescript 4.1+ // type CLPPermission = 'requiresAuthentication' | '*' | `user:${string}` | `role:${string}` type CLPValue = { [key: string]: boolean }; @@ -90,10 +84,7 @@ export class CLP { } } -export function makeSchema( - className: ClassNameType, - schema: JSONSchema -): JSONSchema { +export function makeSchema(className: ClassNameType, schema: JSONSchema): JSONSchema { // This function solve two things: // 1. It provides auto-completion to the users who are implementing schemas // 2. It allows forward-compatible point in order to allow future changes to the internal structure of JSONSchema without affecting all the users diff --git a/src/Security/CheckRunner.js b/src/Security/CheckRunner.js index 86d38e4c10..4be1c3acbf 100644 --- a/src/Security/CheckRunner.js +++ b/src/Security/CheckRunner.js @@ -18,11 +18,7 @@ class CheckRunner { */ constructor(config = {}) { this._validateParams(config); - const { - enableCheck = false, - enableCheckLog = false, - checkGroups = CheckGroups, - } = config; + const { enableCheck = false, enableCheckLog = false, checkGroups = CheckGroups } = config; this.enableCheck = enableCheck; this.enableCheckLog = enableCheckLog; this.checkGroups = checkGroups; @@ -131,9 +127,7 @@ class CheckRunner { _logReport(report) { // Determine log level depending on whether any check failed const log = - report.report.state == CheckState.success - ? s => logger.info(s) - : s => logger.warn(s); + report.report.state == CheckState.success ? s => logger.info(s) : s => logger.warn(s); // Declare output const indent = ' '; diff --git a/src/SharedRest.js b/src/SharedRest.js index da6550c2dd..0b4a07c320 100644 --- a/src/SharedRest.js +++ b/src/SharedRest.js @@ -26,10 +26,7 @@ function enforceRoleSecurity(method, className, auth) { } // readOnly masterKey is not allowed - if ( - auth.isReadOnly && - (method === 'delete' || method === 'create' || method === 'update') - ) { + if (auth.isReadOnly && (method === 'delete' || method === 'create' || method === 'update')) { const error = `read-only masterKey isn't allowed to perform the ${method} operation.`; throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, error); } diff --git a/src/StatusHandler.js b/src/StatusHandler.js index 0b8468f3d3..81b4d70c99 100644 --- a/src/StatusHandler.js +++ b/src/StatusHandler.js @@ -39,9 +39,7 @@ function statusHandler(className, database) { } function update(where, object) { - return jobPromiseQueue.enqueue(where.objectId, () => - database.update(className, where, object) - ); + return jobPromiseQueue.enqueue(where.objectId, () => database.update(className, where, object)); } return Object.freeze({ @@ -143,9 +141,7 @@ export function pushStatusHandler(config, existingObjectId) { pushTime = body.push_time; status = 'scheduled'; } else { - logger.warn( - 'Trying to schedule a push while server is not configured.' - ); + logger.warn('Trying to schedule a push while server is not configured.'); logger.warn('Push will be sent immediately'); } } @@ -203,8 +199,7 @@ export function pushStatusHandler(config, existingObjectId) { const trackSent = function ( results, UTCOffset, - cleanupInstallations = process.env - .PARSE_SERVER_CLEANUP_INVALID_INSTALLATIONS + cleanupInstallations = process.env.PARSE_SERVER_CLEANUP_INVALID_INSTALLATIONS ) { const update = { numSent: 0, @@ -253,8 +248,7 @@ export function pushStatusHandler(config, existingObjectId) { error?.code === 'messaging/registration-token-not-registered' || error?.code === 'messaging/invalid-registration-token' || (error?.code === 'messaging/invalid-argument' && - error?.message === - 'The registration token is not a valid FCM registration token') + error?.message === 'The registration token is not a valid FCM registration token') ) { devicesToRemove.push(token); } @@ -290,9 +284,7 @@ export function pushStatusHandler(config, existingObjectId) { }); if (devicesToRemove.length > 0 && cleanupInstallations) { - logger.info( - `Removing device tokens on ${devicesToRemove.length} _Installations` - ); + logger.info(`Removing device tokens on ${devicesToRemove.length} _Installations`); database.update( '_Installation', { deviceToken: { $in: devicesToRemove } }, diff --git a/src/Utils.js b/src/Utils.js index 46194c33d1..b676cbc248 100644 --- a/src/Utils.js +++ b/src/Utils.js @@ -152,12 +152,7 @@ class Utils { * @param {Object} [current={}] The current result entry being composed. * @param {Array} [results=[]] The resulting array of permutations. */ - static getObjectKeyPermutations( - object, - index = 0, - current = {}, - results = [] - ) { + static getObjectKeyPermutations(object, index = 0, current = {}, results = []) { const keys = Object.keys(object); const key = keys[index]; const values = object[key]; @@ -346,8 +341,7 @@ class Utils { * @returns {Boolean} True if a match was found, false otherwise. */ static objectContainsKeyValue(obj, key, value) { - const isMatch = (a, b) => - (typeof a === 'string' && new RegExp(b).test(a)) || a === b; + const isMatch = (a, b) => (typeof a === 'string' && new RegExp(b).test(a)) || a === b; const isKeyMatch = k => isMatch(k, key); const isValueMatch = v => isMatch(v, value); for (const [k, v] of Object.entries(obj)) { @@ -355,19 +349,10 @@ class Utils { return true; } else if (key === undefined && value !== undefined && isValueMatch(v)) { return true; - } else if ( - key !== undefined && - value !== undefined && - isKeyMatch(k) && - isValueMatch(v) - ) { + } else if (key !== undefined && value !== undefined && isKeyMatch(k) && isValueMatch(v)) { return true; } - if ( - ['[object Object]', '[object Array]'].includes( - Object.prototype.toString.call(v) - ) - ) { + if (['[object Object]', '[object Array]'].includes(Object.prototype.toString.call(v))) { return Utils.objectContainsKeyValue(v, key, value); } } @@ -378,11 +363,7 @@ class Utils { if (config?.requestKeywordDenylist) { // Scan request data for denied keywords for (const keyword of config.requestKeywordDenylist) { - const match = Utils.objectContainsKeyValue( - data, - keyword.key, - keyword.value - ); + const match = Utils.objectContainsKeyValue(data, keyword.key, keyword.value); if (match) { throw `Prohibited keyword in request data: ${JSON.stringify(keyword)}.`; } diff --git a/src/batch.js b/src/batch.js index 459e9e9f11..80fa028cc6 100644 --- a/src/batch.js +++ b/src/batch.js @@ -28,19 +28,12 @@ function makeBatchRoutingPathFunction(originalUrl, serverURL, publicServerURL) { const makeRoutablePath = function (requestPath) { // The routablePath is the path minus the api prefix if (requestPath.slice(0, apiPrefix.length) != apiPrefix) { - throw new Parse.Error( - Parse.Error.INVALID_JSON, - 'cannot route batch path ' + requestPath - ); + throw new Parse.Error(Parse.Error.INVALID_JSON, 'cannot route batch path ' + requestPath); } return path.posix.join('/', requestPath.slice(apiPrefix.length)); }; - if ( - serverURL && - publicServerURL && - serverURL.pathname != publicServerURL.pathname - ) { + if (serverURL && publicServerURL && serverURL.pathname != publicServerURL.pathname) { const localPath = serverURL.pathname; const publicPath = publicServerURL.pathname; @@ -58,12 +51,7 @@ function makeBatchRoutingPathFunction(originalUrl, serverURL, publicServerURL) { ? localPath.length : publicPath.length; - const newPath = path.posix.join( - '/', - localPath, - '/', - requestPath.slice(pathLengthToUse) - ); + const newPath = path.posix.join('/', localPath, '/', requestPath.slice(pathLengthToUse)); // Use the method for local routing return makeRoutablePath(newPath); @@ -77,10 +65,7 @@ function makeBatchRoutingPathFunction(originalUrl, serverURL, publicServerURL) { // TODO: pass along auth correctly function handleBatch(router, req) { if (!Array.isArray(req.body?.requests)) { - throw new Parse.Error( - Parse.Error.INVALID_JSON, - 'requests must be an array' - ); + throw new Parse.Error(Parse.Error.INVALID_JSON, 'requests must be an array'); } // The batch paths are all from the root of our domain. @@ -116,33 +101,27 @@ function handleBatch(router, req) { info: req.info, }; - return router - .tryRouteRequest(restRequest.method, routablePath, request) - .then( - response => { - return { success: response.response }; - }, - error => { - return { error: { code: error.code, error: error.message } }; - } - ); + return router.tryRouteRequest(restRequest.method, routablePath, request).then( + response => { + return { success: response.response }; + }, + error => { + return { error: { code: error.code, error: error.message } }; + } + ); }); return Promise.all(promises) .then(results => { if (req.body?.transaction === true) { if (results.find(result => typeof result.error === 'object')) { - return req.config.database - .abortTransactionalSession() - .then(() => { - return Promise.reject({ response: results }); - }); + return req.config.database.abortTransactionalSession().then(() => { + return Promise.reject({ response: results }); + }); } else { - return req.config.database - .commitTransactionalSession() - .then(() => { - return { response: results }; - }); + return req.config.database.commitTransactionalSession().then(() => { + return { response: results }; + }); } } else { return { response: results }; @@ -153,9 +132,7 @@ function handleBatch(router, req) { error && error.response && error.response.find( - errorItem => - typeof errorItem.error === 'object' && - errorItem.error.code === 251 + errorItem => typeof errorItem.error === 'object' && errorItem.error.code === 251 ) && transactionRetries > 0 ) { diff --git a/src/cli/parse-server.js b/src/cli/parse-server.js index 00ed5a2a34..b0c574bfea 100755 --- a/src/cli/parse-server.js +++ b/src/cli/parse-server.js @@ -15,23 +15,15 @@ const help = function () { console.log(' Usage with npm start'); console.log(''); console.log(' $ npm start -- path/to/config.json'); - console.log( - ' $ npm start -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL' - ); - console.log( - ' $ npm start -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL' - ); + console.log(' $ npm start -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL'); + console.log(' $ npm start -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL'); console.log(''); console.log(''); console.log(' Usage:'); console.log(''); console.log(' $ parse-server path/to/config.json'); - console.log( - ' $ parse-server -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL' - ); - console.log( - ' $ parse-server -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL' - ); + console.log(' $ parse-server -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL'); + console.log(' $ parse-server -- --appId APP_ID --masterKey MASTER_KEY --serverURL serverURL'); console.log(''); }; @@ -43,9 +35,7 @@ runner({ if (!options.appId || !options.masterKey) { program.outputHelp(); console.error(''); - console.error( - '\u001b[31mERROR: appId and masterKey are required\u001b[0m' - ); + console.error('\u001b[31mERROR: appId and masterKey are required\u001b[0m'); console.error(''); process.exit(1); } @@ -67,19 +57,14 @@ runner({ } if (options.cluster) { - const numCPUs = - typeof options.cluster === 'number' - ? options.cluster - : os.cpus().length; + const numCPUs = typeof options.cluster === 'number' ? options.cluster : os.cpus().length; if (cluster.isMaster) { logOptions(); for (let i = 0; i < numCPUs; i++) { cluster.fork(); } cluster.on('exit', (worker, code) => { - console.log( - `worker ${worker.process.pid} died (${code})... Restarting` - ); + console.log(`worker ${worker.process.pid} died (${code})... Restarting`); cluster.fork(); }); } else { @@ -106,9 +91,7 @@ runner({ } function printSuccessMessage() { - console.log( - '[' + process.pid + '] parse-server running on ' + options.serverURL - ); + console.log('[' + process.pid + '] parse-server running on ' + options.serverURL); if (options.mountGraphQL) { console.log( '[' + diff --git a/src/cloud-code/Parse.Cloud.js b/src/cloud-code/Parse.Cloud.js index 412510b6f6..aeaed1bce3 100644 --- a/src/cloud-code/Parse.Cloud.js +++ b/src/cloud-code/Parse.Cloud.js @@ -4,10 +4,7 @@ import { addRateLimit } from '../middlewares'; const Config = require('../Config'); function isParseObjectConstructor(object) { - return ( - typeof object === 'function' && - Object.prototype.hasOwnProperty.call(object, 'className') - ); + return typeof object === 'function' && Object.prototype.hasOwnProperty.call(object, 'className'); } function validateValidator(validator) { @@ -55,9 +52,7 @@ function validateValidator(validator) { const types = parameter.map(type => getType(type)); const type = getType(validatorParam); if (!types.includes(type) && !types.includes('Any')) { - throw `Invalid type for Cloud Function validation key ${key}. Expected ${types.join( - '|' - )}, actual ${type}`; + throw `Invalid type for Cloud Function validation key ${key}. Expected ${types.join('|')}, actual ${type}`; } }; for (const key in validator) { @@ -130,12 +125,7 @@ var ParseCloud = {}; */ ParseCloud.define = function (functionName, handler, validationHandler) { validateValidator(validationHandler); - triggers.addFunction( - functionName, - handler, - validationHandler, - Parse.applicationId - ); + triggers.addFunction(functionName, handler, validationHandler, Parse.applicationId); if (validationHandler && validationHandler.rateLimit) { addRateLimit( { @@ -291,12 +281,7 @@ ParseCloud.beforeLogin = function (handler, validationHandler) { handler = arguments[1]; validationHandler = arguments.length >= 2 ? arguments[2] : null; } - triggers.addTrigger( - triggers.Types.beforeLogin, - className, - handler, - Parse.applicationId - ); + triggers.addTrigger(triggers.Types.beforeLogin, className, handler, Parse.applicationId); if (validationHandler && validationHandler.rateLimit) { addRateLimit( { @@ -337,12 +322,7 @@ ParseCloud.afterLogin = function (handler) { className = triggers.getClassName(handler); handler = arguments[1]; } - triggers.addTrigger( - triggers.Types.afterLogin, - className, - handler, - Parse.applicationId - ); + triggers.addTrigger(triggers.Types.afterLogin, className, handler, Parse.applicationId); }; /** @@ -371,12 +351,7 @@ ParseCloud.afterLogout = function (handler) { className = triggers.getClassName(handler); handler = arguments[1]; } - triggers.addTrigger( - triggers.Types.afterLogout, - className, - handler, - Parse.applicationId - ); + triggers.addTrigger(triggers.Types.afterLogout, className, handler, Parse.applicationId); }; /** @@ -661,11 +636,7 @@ ParseCloud.onLiveQueryEvent = function (handler) { * @param {Function} func The function to run after a live query event. This function can be async and should take one parameter, a {@link Parse.Cloud.LiveQueryEventTrigger}. * @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.LiveQueryEventTrigger}, or a {@link Parse.Cloud.ValidatorObject}. */ -ParseCloud.afterLiveQueryEvent = function ( - parseClass, - handler, - validationHandler -) { +ParseCloud.afterLiveQueryEvent = function (parseClass, handler, validationHandler) { const className = triggers.getClassName(parseClass); validateValidator(validationHandler); triggers.addTrigger( diff --git a/src/cryptoUtils.js b/src/cryptoUtils.js index 1b0337ea45..f85b62c349 100644 --- a/src/cryptoUtils.js +++ b/src/cryptoUtils.js @@ -23,8 +23,7 @@ export function randomString(size: number): string { if (size === 0) { throw new Error('Zero-length randomString is useless.'); } - const chars = - 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + 'abcdefghijklmnopqrstuvwxyz' + '0123456789'; + const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + 'abcdefghijklmnopqrstuvwxyz' + '0123456789'; let objectId = ''; const bytes = randomBytes(size); for (let i = 0; i < bytes.length; ++i) { diff --git a/src/defaults.js b/src/defaults.js index ba603424a8..a2b105d8db 100644 --- a/src/defaults.js +++ b/src/defaults.js @@ -16,16 +16,13 @@ const { verbose, level } = (() => { return { verbose, level: verbose ? 'verbose' : undefined }; })(); -const DefinitionDefaults = Object.keys(ParseServerOptions).reduce( - (memo, key) => { - const def = ParseServerOptions[key]; - if (Object.prototype.hasOwnProperty.call(def, 'default')) { - memo[key] = def.default; - } - return memo; - }, - {} -); +const DefinitionDefaults = Object.keys(ParseServerOptions).reduce((memo, key) => { + const def = ParseServerOptions[key]; + if (Object.prototype.hasOwnProperty.call(def, 'default')) { + memo[key] = def.default; + } + return memo; +}, {}); const computedDefaults = { jsonLogs: process.env.JSON_LOGS || false, diff --git a/src/middlewares.js b/src/middlewares.js index 2e77b4916f..ddd2170ca9 100644 --- a/src/middlewares.js +++ b/src/middlewares.js @@ -148,8 +148,7 @@ export async function handleParseHeaders(req, res, next) { req.body && req.body._ApplicationId && AppCache.get(req.body._ApplicationId) && - (!info.masterKey || - AppCache.get(req.body._ApplicationId).masterKey === info.masterKey) + (!info.masterKey || AppCache.get(req.body._ApplicationId).masterKey === info.masterKey) ) { info.appId = req.body._ApplicationId; info.javascriptKey = req.body._JavaScriptKey || ''; @@ -179,9 +178,7 @@ export async function handleParseHeaders(req, res, next) { } else { try { info.context = JSON.parse(req.body._context); - if ( - Object.prototype.toString.call(info.context) !== '[object Object]' - ) { + if (Object.prototype.toString.call(info.context) !== '[object Object]') { throw 'Context is not an object'; } } catch (e) { @@ -232,16 +229,9 @@ export async function handleParseHeaders(req, res, next) { req.info = info; const isMaintenance = - req.config.maintenanceKey && - info.maintenanceKey === req.config.maintenanceKey; + req.config.maintenanceKey && info.maintenanceKey === req.config.maintenanceKey; if (isMaintenance) { - if ( - checkIp( - clientIp, - req.config.maintenanceKeyIps || [], - req.config.maintenanceKeyIpsStore - ) - ) { + if (checkIp(clientIp, req.config.maintenanceKeyIps || [], req.config.maintenanceKeyIpsStore)) { req.auth = new auth.Auth({ config: req.config, installationId: info.installationId, @@ -259,14 +249,7 @@ export async function handleParseHeaders(req, res, next) { const masterKey = await req.config.loadMasterKey(); let isMaster = info.masterKey === masterKey; - if ( - isMaster && - !checkIp( - clientIp, - req.config.masterKeyIps || [], - req.config.masterKeyIpsStore - ) - ) { + if (isMaster && !checkIp(clientIp, req.config.masterKeyIps || [], req.config.masterKeyIpsStore)) { const log = req.config?.loggerController || defaultLogger; log.error( `Request using master key rejected as the request IP address '${clientIp}' is not set in Parse Server option 'masterKeyIps'.` @@ -402,10 +385,7 @@ export const handleParseSession = async (req, res, next) => { return; } // TODO: Determine the correct error scenario. - req.config.loggerController.error( - 'error getting auth for sessionToken', - error - ); + req.config.loggerController.error('error getting auth for sessionToken', error); throw new Parse.Error(Parse.Error.UNKNOWN_ERROR, error); } }; @@ -467,16 +447,11 @@ export function allowCrossDomain(appId) { : (config?.allowOrigin ?? ['*']); const requestOrigin = req.headers.origin; const allowOrigins = - requestOrigin && baseOrigins.includes(requestOrigin) - ? requestOrigin - : baseOrigins[0]; + requestOrigin && baseOrigins.includes(requestOrigin) ? requestOrigin : baseOrigins[0]; res.header('Access-Control-Allow-Origin', allowOrigins); res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS'); res.header('Access-Control-Allow-Headers', allowHeaders); - res.header( - 'Access-Control-Expose-Headers', - 'X-Parse-Job-Status-Id, X-Parse-Push-Status-Id' - ); + res.header('Access-Control-Expose-Headers', 'X-Parse-Job-Status-Id, X-Parse-Push-Status-Id'); // intercept OPTIONS method if ('OPTIONS' == req.method) { res.sendStatus(200); @@ -608,9 +583,7 @@ export const addRateLimit = (route, config, cloud) => { handler: rateLimit({ windowMs: route.requestTimeWindow, max: route.requestCount, - message: - route.errorResponseMessage || - RateLimitOptions.errorResponseMessage.default, + message: route.errorResponseMessage || RateLimitOptions.errorResponseMessage.default, handler: (request, response, next, options) => { throw { code: Parse.Error.CONNECTION_FAILED, @@ -648,9 +621,7 @@ export const addRateLimit = (route, config, cloud) => { } if (route.zone === Parse.Server.RateLimitZone.user && token) { if (!request.auth) { - await new Promise(resolve => - handleParseSession(request, null, resolve) - ); + await new Promise(resolve => handleParseSession(request, null, resolve)); } if (request.auth?.user?.id && request.zone === 'user') { return request.auth.user.id; @@ -705,9 +676,7 @@ export function promiseEnsureIdempotency(req) { return Promise.resolve(); } // Try to store request - const expiryDate = new Date( - new Date().setSeconds(new Date().getSeconds() + ttl) - ); + const expiryDate = new Date(new Date().setSeconds(new Date().getSeconds() + ttl)); return rest .create(config, auth.master(config), '_Idempotency', { reqId: requestId, @@ -715,10 +684,7 @@ export function promiseEnsureIdempotency(req) { }) .catch(e => { if (e.code == Parse.Error.DUPLICATE_VALUE) { - throw new Parse.Error( - Parse.Error.DUPLICATE_REQUEST, - 'Duplicate request' - ); + throw new Parse.Error(Parse.Error.DUPLICATE_REQUEST, 'Duplicate request'); } throw e; }); diff --git a/src/request.js b/src/request.js index e0f1a04339..bc58ee40ac 100644 --- a/src/request.js +++ b/src/request.js @@ -99,18 +99,13 @@ const encodeBody = function ({ body, headers = {} }) { } else { /* istanbul ignore next */ if (contentTypeKeys.length > 1) { - log.error( - 'Parse.Cloud.httpRequest', - 'multiple content-type headers are set.' - ); + log.error('Parse.Cloud.httpRequest', 'multiple content-type headers are set.'); } // There maybe many, we'll just take the 1st one var contentType = contentTypeKeys[0]; if (headers[contentType].match(/application\/json/i)) { body = JSON.stringify(body); - } else if ( - headers[contentType].match(/application\/x-www-form-urlencoded/i) - ) { + } else if (headers[contentType].match(/application\/x-www-form-urlencoded/i)) { body = querystring.stringify(body); } } @@ -164,10 +159,7 @@ function httpRequest(options) { requestOptions.agent = options.agent; } return new Promise((resolve, reject) => { - const req = client.request( - requestOptions, - makeCallback(resolve, reject, options) - ); + const req = client.request(requestOptions, makeCallback(resolve, reject, options)); if (options.body) { req.write(options.body); } diff --git a/src/rest.js b/src/rest.js index 9282c36294..1f9dbacb73 100644 --- a/src/rest.js +++ b/src/rest.js @@ -16,31 +16,16 @@ const { enforceRoleSecurity } = require('./SharedRest'); function checkTriggers(className, config, types) { return types.some(triggerType => { - return triggers.getTrigger( - className, - triggers.Types[triggerType], - config.applicationId - ); + return triggers.getTrigger(className, triggers.Types[triggerType], config.applicationId); }); } function checkLiveQuery(className, config) { - return ( - config.liveQueryController && - config.liveQueryController.hasLiveQuery(className) - ); + return config.liveQueryController && config.liveQueryController.hasLiveQuery(className); } // Returns a promise for an object with optional keys 'results' and 'count'. -const find = async ( - config, - auth, - className, - restWhere, - restOptions, - clientSDK, - context -) => { +const find = async (config, auth, className, restWhere, restOptions, clientSDK, context) => { const query = await RestQuery({ method: RestQuery.Method.find, config, @@ -55,15 +40,7 @@ const find = async ( }; // get is just like find but only queries an objectId. -const get = async ( - config, - auth, - className, - objectId, - restOptions, - clientSDK, - context -) => { +const get = async (config, auth, className, objectId, restOptions, clientSDK, context) => { var restWhere = { objectId }; const query = await RestQuery({ method: RestQuery.Method.get, @@ -85,10 +62,7 @@ function del(config, auth, className, objectId, context) { } if (className === '_User' && auth.isUnauthenticated()) { - throw new Parse.Error( - Parse.Error.SESSION_MISSING, - 'Insufficient auth to delete user' - ); + throw new Parse.Error(Parse.Error.SESSION_MISSING, 'Insufficient auth to delete user'); } enforceRoleSecurity('delete', className, auth); @@ -98,10 +72,7 @@ function del(config, auth, className, objectId, context) { return Promise.resolve() .then(async () => { - const hasTriggers = checkTriggers(className, config, [ - 'beforeDelete', - 'afterDelete', - ]); + const hasTriggers = checkTriggers(className, config, ['beforeDelete', 'afterDelete']); const hasLiveQuery = checkLiveQuery(className, config); if (hasTriggers || hasLiveQuery || className == '_Session') { const query = await RestQuery({ @@ -115,16 +86,9 @@ function del(config, auth, className, objectId, context) { if (response && response.results && response.results.length) { const firstResult = response.results[0]; firstResult.className = className; - if ( - className === '_Session' && - !auth.isMaster && - !auth.isMaintenance - ) { + if (className === '_Session' && !auth.isMaster && !auth.isMaintenance) { if (!auth.user || firstResult.user.objectId !== auth.user.id) { - throw new Parse.Error( - Parse.Error.INVALID_SESSION_TOKEN, - 'Invalid session token' - ); + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid session token'); } } var cacheAdapter = config.cacheController; @@ -139,10 +103,7 @@ function del(config, auth, className, objectId, context) { context ); } - throw new Parse.Error( - Parse.Error.OBJECT_NOT_FOUND, - 'Object not found for delete.' - ); + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found for delete.'); }); } return Promise.resolve({}); @@ -178,12 +139,7 @@ function del(config, auth, className, objectId, context) { .then(() => { // Notify LiveQuery server if possible const perms = schemaController.getClassLevelPermissions(className); - config.liveQueryController.onAfterDelete( - className, - inflatedObject, - null, - perms - ); + config.liveQueryController.onAfterDelete(className, inflatedObject, null, perms); return triggers.maybeRunTrigger( triggers.Types.afterDelete, auth, @@ -201,39 +157,19 @@ function del(config, auth, className, objectId, context) { // Returns a promise for a {response, status, location} object. function create(config, auth, className, restObject, clientSDK, context) { enforceRoleSecurity('create', className, auth); - var write = new RestWrite( - config, - auth, - className, - null, - restObject, - null, - clientSDK, - context - ); + var write = new RestWrite(config, auth, className, null, restObject, null, clientSDK, context); return write.execute(); } // Returns a promise that contains the fields of the update that the // REST API is supposed to return. // Usually, this is just updatedAt. -function update( - config, - auth, - className, - restWhere, - restObject, - clientSDK, - context -) { +function update(config, auth, className, restWhere, restObject, clientSDK, context) { enforceRoleSecurity('update', className, auth); return Promise.resolve() .then(async () => { - const hasTriggers = checkTriggers(className, config, [ - 'beforeSave', - 'afterSave', - ]); + const hasTriggers = checkTriggers(className, config, ['beforeSave', 'afterSave']); const hasLiveQuery = checkLiveQuery(className, config); if (hasTriggers || hasLiveQuery) { // Do not use find, as it runs the before finds diff --git a/src/vendor/mongodbUrl.js b/src/vendor/mongodbUrl.js index 68b9271a26..9fdc764629 100644 --- a/src/vendor/mongodbUrl.js +++ b/src/vendor/mongodbUrl.js @@ -199,18 +199,14 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { // resolution will treat //foo/bar as host=foo,path=bar because that's // how the browser resolves relative URLs. if (slashesDenoteHost || proto || /^\/\/[^@\/]+@[^@\/]+/.test(rest)) { - var slashes = - rest.charCodeAt(0) === 47 /*/*/ && rest.charCodeAt(1) === 47; /*/*/ + var slashes = rest.charCodeAt(0) === 47 /*/*/ && rest.charCodeAt(1) === 47; /*/*/ if (slashes && !(proto && hostlessProtocol[proto])) { rest = rest.slice(2); this.slashes = true; } } - if ( - !hostlessProtocol[proto] && - (slashes || (proto && !slashedProtocol[proto])) - ) { + if (!hostlessProtocol[proto] && (slashes || (proto && !slashedProtocol[proto]))) { // there's a hostname. // the first instance of /, ?, ;, or # ends the host. // @@ -299,8 +295,7 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { // if hostname begins with [ and ends with ] // assume that it's an IPv6 address. var ipv6Hostname = - hostname.charCodeAt(0) === 91 /*[*/ && - hostname.charCodeAt(hostname.length - 1) === 93; /*]*/ + hostname.charCodeAt(0) === 91 /*[*/ && hostname.charCodeAt(hostname.length - 1) === 93; /*]*/ // validate a little. if (!ipv6Hostname) { @@ -378,9 +373,7 @@ Url.prototype.parse = function (url, parseQueryString, slashesDenoteHost) { } var firstIdx = - questionIdx !== -1 && (hashIdx === -1 || questionIdx < hashIdx) - ? questionIdx - : hashIdx; + questionIdx !== -1 && (hashIdx === -1 || questionIdx < hashIdx) ? questionIdx : hashIdx; if (firstIdx === -1) { if (rest.length > 0) { this.pathname = rest; @@ -573,8 +566,7 @@ function urlFormat(obj) { obj = urlParse(obj); } else if (typeof obj !== 'object' || obj === null) { throw new TypeError( - 'Parameter "urlObj" must be an object, not ' + - (obj === null ? 'null' : typeof obj) + 'Parameter "urlObj" must be an object, not ' + (obj === null ? 'null' : typeof obj) ); } else if (!(obj instanceof Url)) { return Url.prototype.format.call(obj); @@ -600,11 +592,7 @@ Url.prototype.format = function () { if (this.host) { host = auth + this.host; } else if (this.hostname) { - host = - auth + - (this.hostname.indexOf(':') === -1 - ? this.hostname - : '[' + this.hostname + ']'); + host = auth + (this.hostname.indexOf(':') === -1 ? this.hostname : '[' + this.hostname + ']'); if (this.port) { host += ':' + this.port; } @@ -650,10 +638,7 @@ Url.prototype.format = function () { // only the slashedProtocols get the //. Not mailto:, xmpp:, etc. // unless they had them to begin with. - if ( - this.slashes || - ((!protocol || slashedProtocol[protocol]) && host !== false) - ) { + if (this.slashes || ((!protocol || slashedProtocol[protocol]) && host !== false)) { host = '//' + (host || ''); if (pathname && pathname.charCodeAt(0) !== 47 /*/*/) { pathname = '/' + pathname; @@ -729,11 +714,7 @@ Url.prototype.resolveObject = function (relative) { } //urlParse appends trailing / to urls like http://www.example.com - if ( - slashedProtocol[result.protocol] && - result.hostname && - !result.pathname - ) { + if (slashedProtocol[result.protocol] && result.hostname && !result.pathname) { result.path = result.pathname = '/'; } @@ -808,10 +789,8 @@ Url.prototype.resolveObject = function (relative) { } var isSourceAbs = result.pathname && result.pathname.charAt(0) === '/'; - var isRelAbs = - relative.host || (relative.pathname && relative.pathname.charAt(0) === '/'); - var mustEndAbs = - isRelAbs || isSourceAbs || (result.host && relative.pathname); + var isRelAbs = relative.host || (relative.pathname && relative.pathname.charAt(0) === '/'); + var mustEndAbs = isRelAbs || isSourceAbs || (result.host && relative.pathname); var removeAllDots = mustEndAbs; var srcPath = (result.pathname && result.pathname.split('/')) || []; var relPath = (relative.pathname && relative.pathname.split('/')) || []; @@ -850,12 +829,9 @@ Url.prototype.resolveObject = function (relative) { if (isRelAbs) { // it's absolute. - result.host = - relative.host || relative.host === '' ? relative.host : result.host; + result.host = relative.host || relative.host === '' ? relative.host : result.host; result.hostname = - relative.hostname || relative.hostname === '' - ? relative.hostname - : result.hostname; + relative.hostname || relative.hostname === '' ? relative.hostname : result.hostname; result.search = relative.search; result.query = relative.query; srcPath = relPath; @@ -880,9 +856,7 @@ Url.prototype.resolveObject = function (relative) { //this especially happens in cases like //url.resolveObject('mailto:local1@domain1', 'local2@domain2') const authInHost = - result.host && result.host.indexOf('@') > 0 - ? result.host.split('@') - : false; + result.host && result.host.indexOf('@') > 0 ? result.host.split('@') : false; if (authInHost) { result.auth = authInHost.shift(); result.host = result.hostname = authInHost.shift(); @@ -892,9 +866,7 @@ Url.prototype.resolveObject = function (relative) { result.query = relative.query; //to support http.request if (result.pathname !== null || result.search !== null) { - result.path = - (result.pathname ? result.pathname : '') + - (result.search ? result.search : ''); + result.path = (result.pathname ? result.pathname : '') + (result.search ? result.search : ''); } result.href = result.format(); return result; @@ -919,8 +891,7 @@ Url.prototype.resolveObject = function (relative) { // then it must NOT get a trailing slash. var last = srcPath.slice(-1)[0]; var hasTrailingSlash = - ((result.host || relative.host || srcPath.length > 1) && - (last === '.' || last === '..')) || + ((result.host || relative.host || srcPath.length > 1) && (last === '.' || last === '..')) || last === ''; // strip single dots, resolve double dots to parent dir @@ -946,11 +917,7 @@ Url.prototype.resolveObject = function (relative) { } } - if ( - mustEndAbs && - srcPath[0] !== '' && - (!srcPath[0] || srcPath[0].charAt(0) !== '/') - ) { + if (mustEndAbs && srcPath[0] !== '' && (!srcPath[0] || srcPath[0].charAt(0) !== '/')) { srcPath.unshift(''); } @@ -958,8 +925,7 @@ Url.prototype.resolveObject = function (relative) { srcPath.push(''); } - var isAbsolute = - srcPath[0] === '' || (srcPath[0] && srcPath[0].charAt(0) === '/'); + var isAbsolute = srcPath[0] === '' || (srcPath[0] && srcPath[0].charAt(0) === '/'); // put the host back if (psychotic) { @@ -971,10 +937,7 @@ Url.prototype.resolveObject = function (relative) { //occasionally the auth can get stuck only in host //this especially happens in cases like //url.resolveObject('mailto:local1@domain1', 'local2@domain2') - const authInHost = - result.host && result.host.indexOf('@') > 0 - ? result.host.split('@') - : false; + const authInHost = result.host && result.host.indexOf('@') > 0 ? result.host.split('@') : false; if (authInHost) { result.auth = authInHost.shift(); result.host = result.hostname = authInHost.shift(); @@ -996,9 +959,7 @@ Url.prototype.resolveObject = function (relative) { //to support request.http if (result.pathname !== null || result.search !== null) { - result.path = - (result.pathname ? result.pathname : '') + - (result.search ? result.search : ''); + result.path = (result.pathname ? result.pathname : '') + (result.search ? result.search : ''); } result.auth = relative.auth || result.auth; result.slashes = result.slashes || relative.slashes; From 82fc7f54cdb0a2fc4356d4b4a5939a4621754e54 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sun, 18 May 2025 20:59:20 +1000 Subject: [PATCH 7/7] lint --- src/RestQuery.js | 18 +++++++++--------- src/Routers/PagesRouter.js | 24 ++++++++++++------------ 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/RestQuery.js b/src/RestQuery.js index ec47eb2e3d..323a83f2bf 100644 --- a/src/RestQuery.js +++ b/src/RestQuery.js @@ -53,15 +53,15 @@ async function RestQuery({ enforceRoleSecurity(method, className, auth); const result = runBeforeFind ? await triggers.maybeRunQueryTrigger( - triggers.Types.beforeFind, - className, - restWhere, - restOptions, - config, - auth, - context, - method === RestQuery.Method.get - ) + triggers.Types.beforeFind, + className, + restWhere, + restOptions, + config, + auth, + context, + method === RestQuery.Method.get + ) : Promise.resolve({ restWhere, restOptions }); return new _UnsafeRestQuery( diff --git a/src/Routers/PagesRouter.js b/src/Routers/PagesRouter.js index c172bb8970..4f49108f0b 100644 --- a/src/Routers/PagesRouter.js +++ b/src/Routers/PagesRouter.js @@ -227,11 +227,11 @@ export class PagesRouter extends PromiseRouter { const query = result.success ? {} : { - [pageParams.token]: token, - [pageParams.appId]: config.applicationId, - [pageParams.error]: result.err, - [pageParams.appName]: config.appName, - }; + [pageParams.token]: token, + [pageParams.appId]: config.applicationId, + [pageParams.error]: result.err, + [pageParams.appName]: config.appName, + }; if (result?.err === 'The password reset link has expired') { delete query[pageParams.token]; @@ -303,9 +303,9 @@ export class PagesRouter extends PromiseRouter { return Utils.getLocalizedPath(defaultPath, locale).then(({ path, subdir }) => redirect ? this.redirectResponse( - this.composePageUrl(defaultFile, config.publicServerURL, subdir), - params - ) + this.composePageUrl(defaultFile, config.publicServerURL, subdir), + params + ) : this.pageResponse(path, params, placeholders) ); } else { @@ -535,10 +535,10 @@ export class PagesRouter extends PromiseRouter { getDefaultParams(config) { return config ? { - [pageParams.appId]: config.appId, - [pageParams.appName]: config.appName, - [pageParams.publicServerUrl]: config.publicServerURL, - } + [pageParams.appId]: config.appId, + [pageParams.appName]: config.appName, + [pageParams.publicServerUrl]: config.publicServerURL, + } : {}; }